Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F32164267
D15306.1765047950.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Size
29 KB
Referenced Files
None
Subscribers
None
D15306.1765047950.diff
View Options
diff --git a/lib/components/farcaster-data-handler.react.js b/lib/components/farcaster-data-handler.react.js
--- a/lib/components/farcaster-data-handler.react.js
+++ b/lib/components/farcaster-data-handler.react.js
@@ -9,17 +9,13 @@
import { useIsLoggedInToIdentityAndAuthoritativeKeyserver } from '../hooks/account-hooks.js';
import { useDeviceKind } from '../hooks/primary-device-hooks.js';
import { useUpdateRelationships } from '../hooks/relationship-hooks.js';
-import { useFarcasterConversationsSync } from '../shared/farcaster/farcaster-hooks.js';
import { IdentityClientContext } from '../shared/identity-client-context.js';
-import { useTunnelbroker } from '../tunnelbroker/tunnelbroker-context.js';
import { relationshipActions } from '../types/relationship-types.js';
import {
useCurrentUserFID,
useUnlinkFID,
useSetLocalFID,
useSetLocalCurrentUserSupportsDCs,
- useCurrentUserSupportsDCs,
- useFarcasterDCsLoaded,
} from '../utils/farcaster-utils.js';
import { useDispatchActionPromise } from '../utils/redux-promise-utils.js';
import { useSelector, useDispatch } from '../utils/redux-utils.js';
@@ -241,46 +237,6 @@
return children;
}, [children, fidLoaded]);
- const syncFarcasterConversations = useFarcasterConversationsSync();
- const currentUserSupportsDCs = useCurrentUserSupportsDCs();
- const farcasterDCsLoaded = useFarcasterDCsLoaded();
- const [syncType, setSyncType] = React.useState<'none' | 'partial' | 'full'>(
- farcasterDCsLoaded ? 'partial' : 'full',
- );
- const tunnelbroker = useTunnelbroker();
-
- const prevSupport = React.useRef(currentUserSupportsDCs);
- React.useEffect(() => {
- if (currentUserSupportsDCs && !prevSupport.current) {
- setSyncType('full');
- }
- prevSupport.current = currentUserSupportsDCs;
- }, [currentUserSupportsDCs]);
-
- React.useEffect(() => {
- if (
- !currentUserSupportsDCs ||
- syncType === 'none' ||
- !tunnelbroker.socketState.connected
- ) {
- return;
- }
- setSyncType('none');
- const limit = syncType === 'partial' ? 20 : Number.POSITIVE_INFINITY;
- void syncFarcasterConversations(limit);
- }, [
- currentUserSupportsDCs,
- syncType,
- syncFarcasterConversations,
- tunnelbroker.socketState.connected,
- ]);
-
- React.useEffect(() => {
- if (!currentUserSupportsDCs && syncType === 'none') {
- setSyncType('full');
- }
- }, [currentUserSupportsDCs, syncType]);
-
return farcasterDataHandler;
}
diff --git a/lib/shared/farcaster/farcaster-hooks.js b/lib/shared/farcaster/farcaster-hooks.js
--- a/lib/shared/farcaster/farcaster-hooks.js
+++ b/lib/shared/farcaster/farcaster-hooks.js
@@ -8,14 +8,15 @@
useFetchFarcasterConversation,
useFetchFarcasterInbox,
useFetchFarcasterMessages,
- useSendFarcasterTextMessage,
} from './farcaster-api.js';
import type { FarcasterConversation } from './farcaster-conversation-types.js';
import {
type FarcasterMessage,
farcasterMessageValidator,
} from './farcaster-messages-types.js';
+import { useIsUserDataReady } from '../../hooks/backup-hooks.js';
import { useGetCommFCUsersForFIDs } from '../../hooks/user-identities-hooks.js';
+import { isLoggedIn } from '../../selectors/user-selectors.js';
import type { RawMessageInfo } from '../../types/message-types.js';
import { messageTruncationStatus } from '../../types/message-types.js';
import type { Dispatch } from '../../types/redux-types.js';
@@ -24,10 +25,13 @@
import { extractFarcasterIDsFromPayload } from '../../utils/conversion-utils.js';
import { convertFarcasterMessageToCommMessages } from '../../utils/convert-farcaster-message-to-comm-messages.js';
import { createFarcasterRawThreadInfo } from '../../utils/create-farcaster-raw-thread-info.js';
-import { useSetFarcasterDCsLoaded } from '../../utils/farcaster-utils.js';
-import { useDispatch } from '../../utils/redux-utils.js';
+import {
+ useCurrentUserSupportsDCs,
+ useFarcasterDCsLoaded,
+ useSetFarcasterDCsLoaded,
+} from '../../utils/farcaster-utils.js';
+import { useDispatch, useSelector } from '../../utils/redux-utils.js';
import sleep from '../../utils/sleep.js';
-import { useSendDMOperationUtils } from '../dm-ops/dm-op-utils.js';
import { userIDFromFID } from '../id-utils.js';
const FARCASTER_DATA_BATCH_SIZE = 20;
@@ -340,111 +344,101 @@
}
function useFarcasterConversationsSync(): (limit: number) => Promise<void> {
- const [fullInbox, setFullInbox] = React.useState(false);
- const [conversations, setConversations] = React.useState<
- $ReadOnlyArray<string>,
- >([]);
- const [messagesNumberLimit, setMessagesNumberLimit] = React.useState(20);
-
const fetchFarcasterInbox = useFetchFarcasterInbox();
- const sendFarcasterTextMessage = useSendFarcasterTextMessage();
const dispatch = useDispatch();
- const utils = useSendDMOperationUtils();
const fetchConversation = useFetchConversationWithBatching();
const fetchMessagesForConversation = useFetchMessagesForConversation();
+ const setFarcasterDCsLoaded = useSetFarcasterDCsLoaded();
+
+ const fetchInboxes = React.useCallback(
+ async (cursor: ?string): Promise<$ReadOnlyArray<string>> => {
+ const allConversations: Array<string> = [];
+ let currentCursor = cursor;
+
+ while (true) {
+ try {
+ let input = { limit: 20 };
+ if (currentCursor) {
+ input = {
+ ...input,
+ cursor: currentCursor,
+ };
+ }
+ const { result, next } = await withRetry(
+ () => fetchFarcasterInbox(input),
+ MAX_RETRIES,
+ RETRY_DELAY_MS,
+ );
- const fetchInboxes: (cursor: ?string) => Promise<void> = React.useCallback(
- async (cursor: ?string) => {
- try {
- let input = { limit: 20 };
- if (cursor) {
- input = {
- ...input,
- cursor,
- };
- }
- const { result, next } = await withRetry(
- () => fetchFarcasterInbox(input),
- MAX_RETRIES,
- RETRY_DELAY_MS,
- );
- setConversations(prev => {
const ids = result.conversations.map(
conversation => conversation.conversationId,
);
- return [...prev, ...ids];
- });
- if (next?.cursor) {
- void fetchInboxes(next.cursor);
- } else {
- setFullInbox(true);
+ allConversations.push(...ids);
+
+ if (next?.cursor) {
+ currentCursor = next.cursor;
+ } else {
+ break;
+ }
+ } catch (e) {
+ console.error('Error fetching inbox', e);
+ break;
}
- } catch (e) {
- console.error('Error fetching inbox', e);
- setFullInbox(true);
}
+
+ return allConversations;
},
[fetchFarcasterInbox],
);
- const [inProgress, setInProgress] = React.useState(false);
- const setFarcasterDCsLoaded = useSetFarcasterDCsLoaded();
- React.useEffect(() => {
- if (!fullInbox || conversations.length === 0 || inProgress) {
- return;
- }
- setInProgress(true);
-
- void (async () => {
- const farcasterConversations: Array<FarcasterConversation> = [];
-
- const conversationResults = await processInBatchesWithReduxBatching(
- conversations,
- FARCASTER_DATA_BATCH_SIZE,
- (conversationID, batchedUpdates) =>
- fetchConversation(conversationID, batchedUpdates),
- dispatch,
- );
-
- const successfulConversations = conversationResults.filter(Boolean);
- farcasterConversations.push(...successfulConversations);
-
- await processInBatchesWithReduxBatching(
- farcasterConversations,
- FARCASTER_DATA_BATCH_SIZE,
- (conversation, batchedUpdates) =>
- fetchMessagesForConversation(
- conversation.conversationId,
- messagesNumberLimit,
- batchedUpdates,
- ),
- dispatch,
- );
-
- setConversations([]);
- setInProgress(false);
- setFarcasterDCsLoaded(true);
- })();
- }, [
- conversations,
- dispatch,
- fetchConversation,
- fetchMessagesForConversation,
- fullInbox,
- inProgress,
- messagesNumberLimit,
- sendFarcasterTextMessage,
- utils,
- setFarcasterDCsLoaded,
- ]);
-
return React.useCallback(
async (limit: number) => {
- setMessagesNumberLimit(limit);
- setFullInbox(false);
- void fetchInboxes(null);
+ try {
+ const conversations = await fetchInboxes(null);
+
+ if (conversations.length === 0) {
+ setFarcasterDCsLoaded(true);
+ return;
+ }
+
+ const farcasterConversations: Array<FarcasterConversation> = [];
+
+ const conversationResults = await processInBatchesWithReduxBatching(
+ conversations,
+ FARCASTER_DATA_BATCH_SIZE,
+ (conversationID, batchedUpdates) =>
+ fetchConversation(conversationID, batchedUpdates),
+ dispatch,
+ );
+
+ const successfulConversations = conversationResults.filter(Boolean);
+ farcasterConversations.push(...successfulConversations);
+
+ await processInBatchesWithReduxBatching(
+ farcasterConversations,
+ FARCASTER_DATA_BATCH_SIZE,
+ (conversation, batchedUpdates) =>
+ fetchMessagesForConversation(
+ conversation.conversationId,
+ limit,
+ batchedUpdates,
+ ),
+ dispatch,
+ );
+
+ setFarcasterDCsLoaded(true);
+ } catch (e) {
+ console.error('Error syncing Farcaster conversations', e);
+ throw e;
+ }
},
- [fetchInboxes],
+ [
+ fetchInboxes,
+ fetchConversation,
+ fetchMessagesForConversation,
+ dispatch,
+ setFarcasterDCsLoaded,
+ ],
);
}
@@ -506,6 +500,45 @@
);
}
+function useFarcasterSync(onComplete?: () => void): { +inProgress: boolean } {
+ const syncFarcasterConversations = useFarcasterConversationsSync();
+ const currentUserSupportsDCs = useCurrentUserSupportsDCs();
+ const farcasterDCsLoaded = useFarcasterDCsLoaded();
+ const isUserLoggedIn = useSelector(isLoggedIn);
+ const userDataReady = useIsUserDataReady();
+ const fullyLoggedIn = isUserLoggedIn && userDataReady;
+ const [inProgress, setInProgress] = React.useState(false);
+
+ React.useEffect(() => {
+ if (
+ inProgress ||
+ farcasterDCsLoaded !== false ||
+ !fullyLoggedIn ||
+ !currentUserSupportsDCs
+ ) {
+ return;
+ }
+ setInProgress(true);
+ void (async () => {
+ try {
+ await syncFarcasterConversations(Number.POSITIVE_INFINITY);
+ } finally {
+ setInProgress(false);
+ onComplete?.();
+ }
+ })();
+ }, [
+ currentUserSupportsDCs,
+ farcasterDCsLoaded,
+ fullyLoggedIn,
+ inProgress,
+ onComplete,
+ syncFarcasterConversations,
+ ]);
+
+ return { inProgress };
+}
+
export {
useFarcasterConversationsSync,
useFetchConversationWithBatching,
@@ -513,4 +546,5 @@
useFetchMessagesForConversation,
useRefreshFarcasterConversation,
useAddNewFarcasterMessage,
+ useFarcasterSync,
};
diff --git a/lib/tunnelbroker/use-peer-to-peer-message-handler.js b/lib/tunnelbroker/use-peer-to-peer-message-handler.js
--- a/lib/tunnelbroker/use-peer-to-peer-message-handler.js
+++ b/lib/tunnelbroker/use-peer-to-peer-message-handler.js
@@ -34,10 +34,7 @@
} from '../shared/device-list-utils.js';
import { dmOperationSpecificationTypes } from '../shared/dm-ops/dm-op-types.js';
import { useProcessDMOperation } from '../shared/dm-ops/process-dm-ops.js';
-import {
- useAddNewFarcasterMessage,
- useFarcasterConversationsSync,
-} from '../shared/farcaster/farcaster-hooks.js';
+import { useAddNewFarcasterMessage } from '../shared/farcaster/farcaster-hooks.js';
import { IdentityClientContext } from '../shared/identity-client-context.js';
import type {
IdentityServiceClient,
@@ -61,6 +58,7 @@
import { getMessageForException } from '../utils/errors.js';
import {
useClearFarcasterThreads,
+ useSetFarcasterDCsLoaded,
useSetLocalCurrentUserSupportsDCs,
useSetLocalFID,
} from '../utils/farcaster-utils.js';
@@ -115,7 +113,6 @@
const setLocalFID = useSetLocalFID();
const setLocalDCsSupport = useSetLocalCurrentUserSupportsDCs();
const clearFarcasterThreads = useClearFarcasterThreads();
- const syncFarcasterConversations = useFarcasterConversationsSync();
const confirmPeerToPeerMessage = useConfirmPeerToPeerMessage();
const removeAndConfirmMessage = React.useCallback(
@@ -134,6 +131,7 @@
);
const fullBackupSupport = useFullBackupSupportEnabled();
+ const setFarcasterDCsLoaded = useSetFarcasterDCsLoaded();
return React.useCallback(
async (
@@ -265,9 +263,8 @@
try {
if (!userActionMessage.hasDCsToken) {
clearFarcasterThreads();
- } else {
- await syncFarcasterConversations(Number.POSITIVE_INFINITY);
}
+ setFarcasterDCsLoaded(false);
} finally {
await removeAndConfirmMessage(messageID, senderInfo.deviceID);
}
@@ -286,15 +283,15 @@
getAuthMetadata,
reBroadcastAccountDeletion,
dispatch,
+ removeAndConfirmMessage,
fullBackupSupport,
restoreBackupState.status,
restoreBackupState.payload.forced,
userDataRestore,
setLocalFID,
setLocalDCsSupport,
+ setFarcasterDCsLoaded,
clearFarcasterThreads,
- syncFarcasterConversations,
- removeAndConfirmMessage,
],
);
}
diff --git a/lib/utils/farcaster-utils.js b/lib/utils/farcaster-utils.js
--- a/lib/utils/farcaster-utils.js
+++ b/lib/utils/farcaster-utils.js
@@ -52,15 +52,20 @@
return currentUserFIDDCs === 'true';
}
-function useFarcasterDCsLoaded(): boolean {
+function useFarcasterDCsLoaded(): ?boolean {
const farcasterDCsLoaded = useSelector(
state =>
state.syncedMetadataStore.syncedMetadata[
syncedMetadataNames.FARCASTER_DCS_LOADED
- ] ?? 'false',
+ ],
);
- return farcasterDCsLoaded === 'true';
+ if (farcasterDCsLoaded === 'true') {
+ return true;
+ } else if (farcasterDCsLoaded === 'false') {
+ return false;
+ }
+ return undefined;
}
function useSetLocalFID(): (fid: ?string) => void {
diff --git a/native/components/farcaster-sync-handler.react.js b/native/components/farcaster-sync-handler.react.js
new file mode 100644
--- /dev/null
+++ b/native/components/farcaster-sync-handler.react.js
@@ -0,0 +1,60 @@
+// @flow
+
+import { CommonActions } from '@react-navigation/core';
+import * as React from 'react';
+
+import { useIsUserDataReady } from 'lib/hooks/backup-hooks.js';
+import { isLoggedIn } from 'lib/selectors/user-selectors.js';
+import {
+ useCurrentUserSupportsDCs,
+ useFarcasterDCsLoaded,
+} from 'lib/utils/farcaster-utils.js';
+import { useSelector } from 'lib/utils/redux-utils.js';
+
+import { useCurrentLeafRouteName } from '../navigation/nav-selectors.js';
+import { NavContext } from '../navigation/navigation-context.js';
+import { FarcasterSyncScreenRouteName } from '../navigation/route-names.js';
+
+function FarcasterSyncHandler(): React.Node {
+ const navContext = React.useContext(NavContext);
+
+ const isUserLoggedIn = useSelector(isLoggedIn);
+ const userDataReady = useIsUserDataReady();
+ const fullyLoggedIn = isUserLoggedIn && userDataReady;
+
+ const currentUserSupportsDCs = useCurrentUserSupportsDCs();
+ const farcasterDCsLoaded = useFarcasterDCsLoaded();
+ const currentRouteName = useCurrentLeafRouteName();
+
+ React.useEffect(() => {
+ if (!navContext) {
+ return;
+ }
+
+ const { dispatch } = navContext;
+
+ if (
+ fullyLoggedIn &&
+ currentUserSupportsDCs &&
+ farcasterDCsLoaded === false &&
+ currentRouteName !== FarcasterSyncScreenRouteName
+ ) {
+ dispatch(
+ CommonActions.navigate({
+ name: FarcasterSyncScreenRouteName,
+ }),
+ );
+ }
+ }, [
+ navContext,
+ userDataReady,
+ currentUserSupportsDCs,
+ farcasterDCsLoaded,
+ currentRouteName,
+ fullyLoggedIn,
+ ]);
+
+ return null;
+}
+
+export default FarcasterSyncHandler;
diff --git a/native/farcaster/farcaster-sync-loading-screen.react.js b/native/farcaster/farcaster-sync-loading-screen.react.js
new file mode 100644
--- /dev/null
+++ b/native/farcaster/farcaster-sync-loading-screen.react.js
@@ -0,0 +1,78 @@
+// @flow
+
+import * as React from 'react';
+import { Text, View } from 'react-native';
+import * as Progress from 'react-native-progress';
+import { SafeAreaView } from 'react-native-safe-area-context';
+
+import { useFarcasterSync } from 'lib/shared/farcaster/farcaster-hooks.js';
+
+import type { RootNavigationProp } from '../navigation/root-navigator.react.js';
+import type { NavigationRoute } from '../navigation/route-names.js';
+import { useColors, useStyles } from '../themes/colors.js';
+
+type Props = {
+ +navigation: RootNavigationProp<'FarcasterSyncScreen'>,
+ +route: NavigationRoute<'FarcasterSyncScreen'>,
+};
+
+function FarcasterSyncLoadingScreen(props: Props): React.Node {
+ const styles = useStyles(unboundStyles);
+ const colors = useColors();
+
+ const handleComplete = React.useCallback(() => {
+ props.navigation.goBack();
+ }, [props.navigation]);
+
+ useFarcasterSync(handleComplete);
+
+ return (
+ <SafeAreaView edges={safeAreaEdges} style={styles.container}>
+ <Text style={styles.header}>Fetching Farcaster threads</Text>
+ <Text style={styles.section}>
+ We’re fetching all your Farcaster threads and messages.
+ </Text>
+ <Text style={styles.section}>
+ This could take a while depending on how many conversations you have.
+ </Text>
+ <View style={styles.progressContainer}>
+ <Progress.CircleSnail
+ indeterminate
+ color={colors.panelForegroundIcon}
+ size={100}
+ strokeCap="round"
+ />
+ </View>
+ </SafeAreaView>
+ );
+}
+
+const safeAreaEdges = ['bottom', 'top'];
+
+const unboundStyles = {
+ container: {
+ flex: 1,
+ backgroundColor: 'panelBackground',
+ justifyContent: 'space-between',
+ padding: 16,
+ },
+ header: {
+ fontSize: 24,
+ color: 'panelForegroundLabel',
+ paddingBottom: 16,
+ },
+ section: {
+ fontFamily: 'Arial',
+ fontSize: 15,
+ lineHeight: 20,
+ color: 'panelForegroundSecondaryLabel',
+ paddingBottom: 16,
+ },
+ progressContainer: {
+ flexGrow: 1,
+ alignItems: 'center',
+ justifyContent: 'center',
+ },
+};
+
+export default FarcasterSyncLoadingScreen;
diff --git a/native/navigation/root-navigator.react.js b/native/navigation/root-navigator.react.js
--- a/native/navigation/root-navigator.react.js
+++ b/native/navigation/root-navigator.react.js
@@ -58,6 +58,7 @@
LinkedDevicesBottomSheetRouteName,
QRAuthNavigatorRouteName,
CommunityJoinerModalRouteName,
+ FarcasterSyncScreenRouteName,
} from './route-names.js';
import LoggedOutModal from '../account/logged-out-modal.react.js';
import QRAuthNavigator from '../account/qr-auth/qr-auth-navigator.react.js';
@@ -77,6 +78,7 @@
import CommunityJoinerModal from '../components/community-joiner-modal.react.js';
import ConnectFarcasterBottomSheet from '../components/connect-farcaster-bottom-sheet.react.js';
import DirectoryPromptBottomSheet from '../components/directory-prompt-bottom-sheet.react.js';
+import FarcasterSyncLoadingScreen from '../farcaster/farcaster-sync-loading-screen.react.js';
import InviteLinksNavigator from '../invite-links/invite-links-navigator.react.js';
import CustomServerModal from '../profile/custom-server-modal.react.js';
import KeyserverSelectionBottomSheet from '../profile/keyserver-selection-bottom-sheet.react.js';
@@ -215,6 +217,11 @@
component={RegistrationNavigator}
options={disableGesturesScreenOptions}
/>
+ <Root.Screen
+ name={FarcasterSyncScreenRouteName}
+ component={FarcasterSyncLoadingScreen}
+ options={disableGesturesScreenOptions}
+ />
<Root.Screen
name={CommunityCreationRouteName}
component={CommunityCreationNavigator}
diff --git a/native/navigation/route-names.js b/native/navigation/route-names.js
--- a/native/navigation/route-names.js
+++ b/native/navigation/route-names.js
@@ -164,6 +164,7 @@
'RestorePasswordAccountScreen';
export const RestoreBackupScreenRouteName = 'RestoreBackupScreen';
export const RestoreBackupErrorScreenRouteName = 'RestoreBackupErrorScreen';
+export const FarcasterSyncScreenRouteName = 'FarcasterSyncScreen';
export const UserProfileBottomSheetNavigatorRouteName =
'UserProfileBottomSheetNavigator';
export const UserProfileBottomSheetRouteName = 'UserProfileBottomSheet';
@@ -226,6 +227,7 @@
+TagFarcasterChannelNavigator: void,
+CreateMissingSIWEBackupMessage: void,
+QRAuthNavigator: void,
+ +FarcasterSyncScreen: void,
};
export type NUXTipRouteNames =
diff --git a/native/profile/profile-screen.react.js b/native/profile/profile-screen.react.js
--- a/native/profile/profile-screen.react.js
+++ b/native/profile/profile-screen.react.js
@@ -637,7 +637,7 @@
}, [checkIfPrimaryDevice]);
const usingRestoreFlow = useIsRestoreFlowEnabled();
- const farcasterConversationsSync = useFarcasterConversationsSync();
+ const syncFarcasterConversations = useFarcasterConversationsSync();
return (
<ProfileScreen
@@ -657,7 +657,7 @@
onCreateDMThread={onCreateDMThread}
currentUserFID={currentUserID}
usingRestoreFlow={usingRestoreFlow}
- farcasterConversationsSync={farcasterConversationsSync}
+ farcasterConversationsSync={syncFarcasterConversations}
/>
);
},
diff --git a/native/root.react.js b/native/root.react.js
--- a/native/root.react.js
+++ b/native/root.react.js
@@ -68,6 +68,7 @@
import ColdStartTracker from './components/cold-start-tracker.react.js';
import ConnectFarcasterAlertHandler from './components/connect-farcaster-alert-handler.react.js';
import DisplayCommunityDirectoryPromptHandler from './components/display-community-directory-prompt.react.js';
+import FarcasterSyncHandler from './components/farcaster-sync-handler.react.js';
import { FeatureFlagsProvider } from './components/feature-flags-provider.react.js';
import NonKeyserverActivityHandler from './components/non-keyserver-activity-handler.react.js';
import { NUXTipsContextProvider } from './components/nux-tips-context.react.js';
@@ -322,6 +323,7 @@
<HoldersHandler />
</>
);
+
let navigation;
if (initialState) {
navigation = (
@@ -423,6 +425,7 @@
<PrekeysHandler />
<ReportHandler />
<FarcasterChannelPrefetchHandler />
+ <FarcasterSyncHandler />
<AutoJoinCommunityHandler />
<SyncCommunityStoreHandler />
<InitialStateSharingHandler />
diff --git a/web/app.react.js b/web/app.react.js
--- a/web/app.react.js
+++ b/web/app.react.js
@@ -66,6 +66,7 @@
import { MemberListSidebarProvider } from './chat/member-list-sidebar/member-list-sidebar-provider.react.js';
import { AutoJoinCommunityHandler } from './components/auto-join-community-handler.react.js';
import CommunitiesRefresher from './components/communities-refresher.react.js';
+import FarcasterSyncOverlay from './components/farcaster-sync-overlay.react.js';
import LogOutIfMissingCSATHandler from './components/log-out-if-missing-csat-handler.react.js';
import NavigationArrows from './components/navigation-arrows.react.js';
import NonKeyserverActivityHandler from './components/non-keyserver-activity-handler.react.js';
@@ -228,16 +229,18 @@
if (this.props.loggedIn) {
content = (
<>
- <WebEditThreadAvatarProvider>
- <EditUserAvatarProvider>
- <StaffContextProvider>
- <MemberListSidebarProvider>
- {this.renderMainContent()}
- {this.props.modals}
- </MemberListSidebarProvider>
- </StaffContextProvider>
- </EditUserAvatarProvider>
- </WebEditThreadAvatarProvider>
+ <FarcasterSyncOverlay>
+ <WebEditThreadAvatarProvider>
+ <EditUserAvatarProvider>
+ <StaffContextProvider>
+ <MemberListSidebarProvider>
+ {this.renderMainContent()}
+ {this.props.modals}
+ </MemberListSidebarProvider>
+ </StaffContextProvider>
+ </EditUserAvatarProvider>
+ </WebEditThreadAvatarProvider>
+ </FarcasterSyncOverlay>
</>
);
} else {
diff --git a/web/components/farcaster-sync-overlay.react.js b/web/components/farcaster-sync-overlay.react.js
new file mode 100644
--- /dev/null
+++ b/web/components/farcaster-sync-overlay.react.js
@@ -0,0 +1,46 @@
+// @flow
+
+import * as React from 'react';
+
+import {
+ useCurrentUserSupportsDCs,
+ useFarcasterDCsLoaded,
+} from 'lib/utils/farcaster-utils.js';
+
+import FarcasterSyncLoadingScreen from '../farcaster/farcaster-sync-loading-screen.react.js';
+
+type Props = {
+ +children: React.Node,
+};
+
+function FarcasterSyncOverlay(props: Props): React.Node {
+ const { children } = props;
+ const farcasterDCsLoaded = useFarcasterDCsLoaded();
+ const currentUserSupportsDCs = useCurrentUserSupportsDCs();
+
+ const isFullSyncInProgress =
+ currentUserSupportsDCs && farcasterDCsLoaded === false;
+
+ const style = React.useMemo(() => {
+ if (!isFullSyncInProgress) {
+ return {};
+ }
+ return { opacity: 0 };
+ }, [isFullSyncInProgress]);
+
+ const loadingScreen = React.useMemo(() => {
+ if (!isFullSyncInProgress) {
+ return null;
+ }
+ return <FarcasterSyncLoadingScreen />;
+ }, [isFullSyncInProgress]);
+
+ return (
+ <>
+ <div style={style}>{children}</div>
+ {loadingScreen}
+ </>
+ );
+}
+
+export default FarcasterSyncOverlay;
diff --git a/web/farcaster/farcaster-sync-loading-screen.css b/web/farcaster/farcaster-sync-loading-screen.css
new file mode 100644
--- /dev/null
+++ b/web/farcaster/farcaster-sync-loading-screen.css
@@ -0,0 +1,66 @@
+div.loadingContainer {
+ position: fixed;
+ top: 0;
+ left: 0;
+ width: 100vw;
+ height: 100vh;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ background-color: rgba(0, 0, 0, 0.5);
+ z-index: 10000;
+}
+
+div.loading {
+ width: 380px;
+ min-height: 438px;
+ background: var(--auth-modal-bg);
+ border: var(--auth-modal-border-color) solid 1px;
+ border-radius: 12px;
+ display: flex;
+ justify-content: center;
+ padding: 19px 17px;
+ box-sizing: border-box;
+}
+
+div.innerLoading {
+ flex: 1;
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ gap: 32px;
+}
+
+div.content {
+ text-align: left;
+ width: 100%;
+}
+
+h2.header {
+ font-size: var(--xl-font-20);
+ font-weight: 700;
+ color: var(--fg);
+ margin: 0;
+ line-height: var(--line-height-display);
+}
+
+p.description {
+ font-size: var(--s-font-14);
+ line-height: var(--line-height-text);
+ color: var(--fg);
+ margin: 5px 0;
+ font-weight: var(--normal);
+}
+
+div.separator {
+ width: 100%;
+ height: 1px;
+ background: var(--border);
+ margin: 8px 0;
+}
+
+div.spinner {
+ display: flex;
+ justify-content: center;
+ margin: auto;
+}
diff --git a/web/farcaster/farcaster-sync-loading-screen.react.js b/web/farcaster/farcaster-sync-loading-screen.react.js
new file mode 100644
--- /dev/null
+++ b/web/farcaster/farcaster-sync-loading-screen.react.js
@@ -0,0 +1,37 @@
+// @flow
+
+import * as React from 'react';
+
+import { useFarcasterSync } from 'lib/shared/farcaster/farcaster-hooks.js';
+
+import css from './farcaster-sync-loading-screen.css';
+import LoadingIndicator from '../loading-indicator.react.js';
+
+function FarcasterSyncLoadingScreen(): React.Node {
+ useFarcasterSync();
+
+ return (
+ <div className={css.loadingContainer}>
+ <div className={css.loading}>
+ <div className={css.innerLoading}>
+ <div className={css.content}>
+ <h2 className={css.header}>Fetching Farcaster threads</h2>
+ <div className={css.separator} />
+ <p className={css.description}>
+ We’re fetching all your Farcaster threads and messages.
+ </p>
+ <p className={css.description}>
+ This could take a while depending on how many conversations you
+ have.
+ </p>
+ </div>
+ <div className={css.spinner}>
+ <LoadingIndicator status="loading" size="x-large" />
+ </div>
+ </div>
+ </div>
+ </div>
+ );
+}
+
+export default FarcasterSyncLoadingScreen;
diff --git a/web/settings/account-settings.react.js b/web/settings/account-settings.react.js
--- a/web/settings/account-settings.react.js
+++ b/web/settings/account-settings.react.js
@@ -182,10 +182,10 @@
[pushModal],
);
- const farcasterConversationsSync = useFarcasterConversationsSync();
+ const syncFarcasterConversations = useFarcasterConversationsSync();
const syncConversations = React.useCallback(() => {
- return farcasterConversationsSync(Number.POSITIVE_INFINITY);
- }, [farcasterConversationsSync]);
+ return syncFarcasterConversations(Number.POSITIVE_INFINITY);
+ }, [syncFarcasterConversations]);
if (!currentUserInfo || currentUserInfo.anonymous) {
return null;
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Sat, Dec 6, 7:05 PM (19 h, 31 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
5840297
Default Alt Text
D15306.1765047950.diff (29 KB)
Attached To
Mode
D15306: [native][web] Show a loading screen when initially fetching the Farcaster threads
Attached
Detach File
Event Timeline
Log In to Comment