Page MenuHomePhorge

D15306.1765047950.diff
No OneTemporary

Size
29 KB
Referenced Files
None
Subscribers
None

D15306.1765047950.diff

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

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)

Event Timeline