Page MenuHomePhorge

D15487.1765029637.diff
No OneTemporary

Size
43 KB
Referenced Files
None
Subscribers
None

D15487.1765029637.diff

diff --git a/lib/actions/synced-metadata-actions.js b/lib/actions/synced-metadata-actions.js
--- a/lib/actions/synced-metadata-actions.js
+++ b/lib/actions/synced-metadata-actions.js
@@ -2,7 +2,14 @@
const setSyncedMetadataEntryActionType = 'SET_SYNCED_METADATA_ENTRY' as const;
+const setSyncedMetadataEntriesActionType =
+ 'SET_SYNCED_METADATA_ENTRIES' as const;
+
const clearSyncedMetadataEntryActionType =
'CLEAR_SYNCED_METADATA_ENTRY' as const;
-export { setSyncedMetadataEntryActionType, clearSyncedMetadataEntryActionType };
+export {
+ setSyncedMetadataEntryActionType,
+ setSyncedMetadataEntriesActionType,
+ clearSyncedMetadataEntryActionType,
+};
diff --git a/lib/reducers/synced-metadata-reducer.js b/lib/reducers/synced-metadata-reducer.js
--- a/lib/reducers/synced-metadata-reducer.js
+++ b/lib/reducers/synced-metadata-reducer.js
@@ -3,6 +3,7 @@
import { setClientDBStoreActionType } from '../actions/client-db-store-actions.js';
import {
setSyncedMetadataEntryActionType,
+ setSyncedMetadataEntriesActionType,
clearSyncedMetadataEntryActionType,
} from '../actions/synced-metadata-actions.js';
import {
@@ -34,6 +35,17 @@
syncedMetadataStore: processStoreOps(state, [replaceOperation]),
syncedMetadataStoreOperations: [replaceOperation],
};
+ } else if (action.type === setSyncedMetadataEntriesActionType) {
+ const replaceOperations: Array<ReplaceSyncedMetadataEntryOperation> =
+ action.payload.entries.map(entry => ({
+ type: 'replace_synced_metadata_entry',
+ payload: entry,
+ }));
+
+ return {
+ syncedMetadataStore: processStoreOps(state, replaceOperations),
+ syncedMetadataStoreOperations: replaceOperations,
+ };
} else if (action.type === clearSyncedMetadataEntryActionType) {
const removeOperation: RemoveSyncedMetadataOperation = {
type: 'remove_synced_metadata',
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
@@ -67,7 +67,9 @@
useCurrentUserFID,
useCurrentUserSupportsDCs,
useFarcasterDCsLoaded,
- useSetFarcasterDCsLoaded,
+ useFarcasterDCsSyncCanceled,
+ useFarcasterDCsSyncCancellationRef,
+ useSetFarcasterDCsSyncStatus,
} from '../../utils/farcaster-utils.js';
import { values } from '../../utils/objects.js';
import { useDispatch, useSelector } from '../../utils/redux-utils.js';
@@ -640,6 +642,7 @@
conversationID: string,
messagesLimit: number,
batchedUpdates: BatchedUpdates,
+ canBeCanceled: boolean,
increaseMessagesCount?: (number) => mixed,
) => Promise<?FarcasterConversation> {
const fetchUsersByFIDs = useGetCommFCUsersForFIDs();
@@ -648,14 +651,19 @@
useFetchFarcasterConversationInvites();
const fetchFarcasterMessages = useFetchMessagesForConversation();
const { addLog } = useDebugLogs();
+ const canceledRef = useFarcasterDCsSyncCancellationRef();
return React.useCallback(
async (
conversationID: string,
messagesLimit: number,
batchedUpdates: BatchedUpdates,
+ canBeCanceled: boolean,
increaseMessagesCount?: number => mixed,
): Promise<?FarcasterConversation> => {
+ if (canBeCanceled && canceledRef.current) {
+ return null;
+ }
try {
const result = await fetchAndProcessConversation(
conversationID,
@@ -686,12 +694,13 @@
threadMembers.forEach(member => batchedUpdates.addUserID(member.id));
}
- const messagesResult = await fetchFarcasterMessages(
+ const messagesResult = await fetchFarcasterMessages({
conversationID,
- messagesLimit,
- undefined,
- increaseMessagesCount,
- );
+ messagesNumberLimit: messagesLimit,
+ cursor: undefined,
+ canBeCanceled,
+ increaseMessageCount: increaseMessagesCount,
+ });
if (messagesResult.messages.length === 0) {
addLog(
@@ -742,20 +751,26 @@
}
},
[
- addLog,
+ canceledRef,
fetchFarcasterConversation,
- fetchFarcasterMessages,
fetchFarcasterConversationInvites,
fetchUsersByFIDs,
+ addLog,
+ fetchFarcasterMessages,
],
);
}
+type FetchMessagesForConversationInput = {
+ +conversationID: string,
+ +messagesNumberLimit?: number,
+ +cursor?: ?string,
+ +canBeCanceled?: boolean,
+ +increaseMessageCount?: number => mixed,
+};
+
function useFetchMessagesForConversation(): (
- conversationID: string,
- messagesNumberLimit?: number,
- cursor?: ?string,
- increaseMessageCount?: (number) => mixed,
+ input: FetchMessagesForConversationInput,
) => Promise<{
+messages: Array<RawMessageInfo>,
+userIDs: Array<string>,
@@ -765,21 +780,34 @@
const fetchUsersByFIDs = useGetCommFCUsersForFIDs();
const { addLog } = useDebugLogs();
+ const canceledRef = useFarcasterDCsSyncCancellationRef();
+
return React.useCallback(
async (
- conversationID: string,
- messagesNumberLimit: number = 20,
- cursor?: ?string,
- increaseMessageCount?: number => mixed,
+ input: FetchMessagesForConversationInput,
): Promise<{
+messages: Array<RawMessageInfo>,
+userIDs: Array<string>,
+newCursor?: ?string,
}> => {
+ const {
+ conversationID,
+ messagesNumberLimit = 20,
+ cursor: initialCursor,
+ canBeCanceled = false,
+ increaseMessageCount,
+ } = input;
+ if (canBeCanceled && canceledRef.current) {
+ return {
+ messages: [],
+ userIDs: [],
+ };
+ }
const result: Array<RawMessageInfo> = [];
const messageIDs = new Set<string>();
const identityCache = new Map<string, FarcasterUser | void>();
let batchNumber = 0;
+ let cursor = initialCursor;
try {
let totalMessagesFetched = 0;
@@ -892,7 +920,11 @@
}
// This should help with the app responsiveness
await sleep(0);
- } while (cursor && totalMessagesFetched < messagesNumberLimit);
+ } while (
+ cursor &&
+ totalMessagesFetched < messagesNumberLimit &&
+ (!canBeCanceled || !canceledRef.current)
+ );
} catch (e) {
addLog(
'Farcaster: Failed to fetch messages',
@@ -916,7 +948,7 @@
newCursor: cursor,
};
},
- [addLog, fetchFarcasterMessages, fetchUsersByFIDs],
+ [addLog, canceledRef, fetchFarcasterMessages, fetchUsersByFIDs],
);
}
@@ -935,6 +967,7 @@
conversationID,
messagesLimit ?? 20,
batchedUpdates,
+ false,
);
if (!batchedUpdates.isEmpty()) {
@@ -1098,7 +1131,7 @@
) => Promise<void> {
const dispatch = useDispatch();
const fetchConversationWithMessages = useFetchConversationWithMessages();
- const setFarcasterDCsLoaded = useSetFarcasterDCsLoaded();
+ const { setLoaded } = useSetFarcasterDCsSyncStatus();
const { addLog } = useDebugLogs();
const fetchInboxes = useFetchInboxIDs();
const removeDeadThreads = useRemoveDeadThreads();
@@ -1128,7 +1161,7 @@
JSON.stringify({}),
new Set([logTypes.FARCASTER]),
);
- setFarcasterDCsLoaded(true);
+ setLoaded(true);
return;
}
@@ -1154,6 +1187,7 @@
conversationID,
Number.POSITIVE_INFINITY,
batchedUpdates,
+ true,
increaseMessagesCount,
),
dispatch,
@@ -1161,7 +1195,7 @@
addLog,
);
- setFarcasterDCsLoaded(true);
+ setLoaded(true);
} catch (e) {
addLog(
'Farcaster: Failed to sync conversations (full sync)',
@@ -1179,12 +1213,14 @@
fetchConversationWithMessages,
fetchInboxes,
removeDeadThreads,
- setFarcasterDCsLoaded,
+ setLoaded,
],
);
}
-function useLightweightFarcasterConversationsSync(): () => Promise<void> {
+function useLightweightFarcasterConversationsSync(): (
+ withMessages: boolean,
+) => Promise<void> {
const dispatch = useDispatch();
const fetchConversationWithMessages = useFetchConversationWithMessages();
const { addLog } = useDebugLogs();
@@ -1194,132 +1230,140 @@
const currentUserFID = useCurrentUserFID();
const auxUserStore = useSelector(state => state.auxUserStore);
- return React.useCallback(async () => {
- try {
- invariant(currentUserFID, 'currentUserFID is not defined');
- const inboxResults = await Promise.all([
- fetchInboxes(),
- fetchInboxes('request'),
- fetchInboxes('archived'),
- ]);
- const allFetchSuccessful = inboxResults.every(
- result => result.fetchSuccessful,
- );
- const conversations = inboxResults.flatMap(
- result => result.conversations,
- );
- const conversationIDs = conversations.map(
- conversation => conversation.conversationId,
- );
+ return React.useCallback(
+ async (withMessages: boolean) => {
+ try {
+ invariant(currentUserFID, 'currentUserFID is not defined');
+ const inboxResults = await Promise.all([
+ fetchInboxes(),
+ fetchInboxes('request'),
+ fetchInboxes('archived'),
+ ]);
+ const allFetchSuccessful = inboxResults.every(
+ result => result.fetchSuccessful,
+ );
+ const conversations = inboxResults.flatMap(
+ result => result.conversations,
+ );
+ const conversationIDs = conversations.map(
+ conversation => conversation.conversationId,
+ );
- if (allFetchSuccessful) {
- removeDeadThreads(conversationIDs);
- }
+ if (allFetchSuccessful) {
+ removeDeadThreads(conversationIDs);
+ }
- const threadIDs = new Set(Object.keys(threadInfos));
- const newConversationIDs = [];
- const existingConversationIDs = [];
+ const threadIDs = new Set(Object.keys(threadInfos));
+ const newConversationIDs = [];
- for (const conversationID of conversationIDs) {
- if (
- threadIDs.has(farcasterThreadIDFromConversationID(conversationID))
- ) {
- existingConversationIDs.push(conversationID);
- } else {
- newConversationIDs.push(conversationID);
+ for (const conversationID of conversationIDs) {
+ if (
+ !threadIDs.has(farcasterThreadIDFromConversationID(conversationID))
+ ) {
+ newConversationIDs.push(conversationID);
+ }
}
- }
- const updateResults = conversations
- .map(conversation => {
- const threadID = farcasterThreadIDFromConversationID(
- conversation.conversationId,
- );
- const thread = threadInfos[threadID];
- if (thread && thread.farcaster) {
- return createUpdatedThread(
- thread,
- conversation,
- currentUserFID,
- auxUserStore,
+ const updateResults = conversations
+ .map(conversation => {
+ const threadID = farcasterThreadIDFromConversationID(
+ conversation.conversationId,
);
- }
- return null;
- })
- .filter(Boolean);
-
- const updates = updateResults
- .map(result =>
- result && result.result === 'updated' ? result.threadInfo : null,
- )
- .filter(Boolean)
- .map(thread => ({
- type: updateTypes.UPDATE_THREAD,
- time: thread.creationTime,
- threadInfo: thread,
- id: uuid.v4(),
- }));
- dispatch({
- type: processFarcasterOpsActionType,
- payload: {
- rawMessageInfos: [],
- updateInfos: updates,
- },
- });
+ const thread = threadInfos[threadID];
+ if (thread && thread.farcaster) {
+ return createUpdatedThread(
+ thread,
+ conversation,
+ currentUserFID,
+ auxUserStore,
+ );
+ }
+ return null;
+ })
+ .filter(Boolean);
+
+ const updates = updateResults
+ .map(result =>
+ result && result.result === 'updated' ? result.threadInfo : null,
+ )
+ .filter(Boolean)
+ .map(thread => ({
+ type: updateTypes.UPDATE_THREAD,
+ time: thread.creationTime,
+ threadInfo: thread,
+ id: uuid.v4(),
+ }));
+ dispatch({
+ type: processFarcasterOpsActionType,
+ payload: {
+ rawMessageInfos: [],
+ updateInfos: updates,
+ },
+ });
- const needRefetch = updateResults
- .map(result =>
- result && result.result === 'refetch' ? result.conversationID : null,
- )
- .filter(Boolean);
+ const needRefetch = updateResults
+ .map(result =>
+ result && result.result === 'refetch'
+ ? result.conversationID
+ : null,
+ )
+ .filter(Boolean);
- if (needRefetch.length > 0) {
- await processInBatchesWithReduxBatching(
- needRefetch,
- FARCASTER_DATA_BATCH_SIZE,
- (conversationID, batchedUpdates) =>
- fetchConversationWithMessages(conversationID, 0, batchedUpdates),
- dispatch,
- undefined,
- addLog,
- );
- }
+ if (needRefetch.length > 0) {
+ await processInBatchesWithReduxBatching(
+ needRefetch,
+ FARCASTER_DATA_BATCH_SIZE,
+ (conversationID, batchedUpdates) =>
+ fetchConversationWithMessages(
+ conversationID,
+ 0,
+ batchedUpdates,
+ false,
+ ),
+ dispatch,
+ undefined,
+ addLog,
+ );
+ }
- if (newConversationIDs.length > 0) {
- await processInBatchesWithReduxBatching(
- newConversationIDs,
- FARCASTER_DATA_BATCH_SIZE,
- (conversationID, batchedUpdates) =>
- fetchConversationWithMessages(
- conversationID,
- Number.POSITIVE_INFINITY,
- batchedUpdates,
- ),
- dispatch,
- undefined,
- addLog,
+ if (newConversationIDs.length > 0) {
+ await processInBatchesWithReduxBatching(
+ newConversationIDs,
+ FARCASTER_DATA_BATCH_SIZE,
+ (conversationID, batchedUpdates) =>
+ fetchConversationWithMessages(
+ conversationID,
+ withMessages ? Number.POSITIVE_INFINITY : 20,
+ batchedUpdates,
+ false,
+ ),
+ dispatch,
+ undefined,
+ addLog,
+ );
+ }
+ } catch (e) {
+ addLog(
+ 'Farcaster: Failed to sync conversations (lightweight)',
+ JSON.stringify({
+ error: getMessageForException(e),
+ }),
+ new Set([logTypes.FARCASTER]),
);
+ throw e;
}
- } catch (e) {
- addLog(
- 'Farcaster: Failed to sync conversations (lightweight)',
- JSON.stringify({
- error: getMessageForException(e),
- }),
- new Set([logTypes.FARCASTER]),
- );
- throw e;
- }
- }, [
- currentUserFID,
- fetchInboxes,
- removeDeadThreads,
- threadInfos,
- dispatch,
- auxUserStore,
- addLog,
- fetchConversationWithMessages,
- ]);
+ },
+ [
+ currentUserFID,
+ fetchInboxes,
+ removeDeadThreads,
+ threadInfos,
+ dispatch,
+ auxUserStore,
+ addLog,
+ fetchConversationWithMessages,
+ ],
+ );
}
function useAddNewFarcasterMessage(): FarcasterMessage => Promise<void> {
@@ -1337,6 +1381,7 @@
farcasterMessage.conversationId,
withMessages ? Number.POSITIVE_INFINITY : 0,
updates,
+ true,
);
dispatch({
type: processFarcasterOpsActionType,
@@ -1358,6 +1403,7 @@
const { addLog } = useDebugLogs();
const currentUserSupportsDCs = useCurrentUserSupportsDCs();
const farcasterDCsLoaded = useFarcasterDCsLoaded();
+ const isFarcasterSyncCanceled = useFarcasterDCsSyncCanceled();
return React.useCallback(
async (farcasterMessage: FarcasterMessage) => {
@@ -1375,7 +1421,7 @@
farcasterMessage.conversationId,
)
) {
- if (farcasterDCsLoaded) {
+ if (farcasterDCsLoaded && !isFarcasterSyncCanceled) {
// If DCs have already been synced, then this missing conversation is
// unexpected, and we should fetch all messages for it. No need to
// proceed afterwards since this message will be fetched below
@@ -1448,15 +1494,16 @@
});
},
[
+ currentUserSupportsDCs,
+ threadInfos,
+ fetchUsersByFIDs,
addLog,
+ viewerID,
dispatch,
+ farcasterDCsLoaded,
+ isFarcasterSyncCanceled,
fetchConversation,
fetchMessage,
- fetchUsersByFIDs,
- threadInfos,
- viewerID,
- currentUserSupportsDCs,
- farcasterDCsLoaded,
],
);
}
@@ -1487,16 +1534,16 @@
}
function useFarcasterSync(onComplete?: () => void): {
- +inProgress: boolean,
+progress: ?FarcasterSyncProgress,
} {
const syncFarcasterConversations = useFarcasterConversationsSync();
const currentUserSupportsDCs = useCurrentUserSupportsDCs();
const farcasterDCsLoaded = useFarcasterDCsLoaded();
+ const isFarcasterSyncCanceled = useFarcasterDCsSyncCanceled();
const isUserLoggedIn = useSelector(isLoggedIn);
const userDataReady = useIsUserDataReady();
const fullyLoggedIn = isUserLoggedIn && userDataReady;
- const [inProgress, setInProgress] = React.useState(false);
+ const inProgress = React.useRef(false);
const [progress, setProgress] = React.useState<?FarcasterSyncProgress>(null);
const { socketState } = useTunnelbroker();
@@ -1519,23 +1566,26 @@
React.useEffect(() => {
if (
- inProgress ||
+ inProgress.current ||
farcasterDCsLoaded !== false ||
!fullyLoggedIn ||
!currentUserSupportsDCs ||
- !socketState.isAuthorized
+ !socketState.isAuthorized ||
+ isFarcasterSyncCanceled
) {
return;
}
- setInProgress(true);
+ inProgress.current = true;
setProgress(null);
void (async () => {
try {
await syncFarcasterConversations(handleProgress);
} finally {
- setInProgress(false);
+ if (inProgress.current) {
+ onComplete?.();
+ }
+ inProgress.current = false;
setProgress(null);
- onComplete?.();
}
})();
}, [
@@ -1547,9 +1597,17 @@
handleProgress,
syncFarcasterConversations,
socketState.isAuthorized,
+ isFarcasterSyncCanceled,
]);
- return { inProgress, progress };
+ React.useEffect(() => {
+ if (isFarcasterSyncCanceled && inProgress.current) {
+ onComplete?.();
+ inProgress.current = false;
+ }
+ }, [isFarcasterSyncCanceled, onComplete]);
+
+ return { progress };
}
function useFarcasterThreadRefresher(
@@ -1594,6 +1652,7 @@
const fullyLoggedIn = isUserLoggedIn && userDataReady;
const currentUserSupportsDCs = useCurrentUserSupportsDCs();
const farcasterDCsLoaded = useFarcasterDCsLoaded();
+ const isFarcasterSyncCanceled = useFarcasterDCsSyncCanceled();
const lightweightSync = useLightweightFarcasterConversationsSync();
const { socketState } = useTunnelbroker();
const started = React.useRef(false);
@@ -1612,14 +1671,15 @@
// If we're here, it means that the full sync is not yet done. In that
// case, we don't want to perform the lightweight sync during this run
// of the app.
- if (!farcasterDCsLoaded) {
+ if (!farcasterDCsLoaded && !isFarcasterSyncCanceled) {
return;
}
- void lightweightSync();
+ void lightweightSync(!isFarcasterSyncCanceled);
}, [
currentUserSupportsDCs,
farcasterDCsLoaded,
fullyLoggedIn,
+ isFarcasterSyncCanceled,
lightweightSync,
socketState.isAuthorized,
]);
@@ -1631,10 +1691,11 @@
) => Promise<void> {
const dispatch = useDispatch();
const threadInfos = useSelector(state => state.threadStore.threadInfos);
- const fetchConversationWithBatching = useFetchConversationWithBatching();
+ const fetchConversationWithMessages = useFetchConversationWithMessages();
const { addLog } = useDebugLogs();
const currentUserSupportsDCs = useCurrentUserSupportsDCs();
const farcasterDCsLoaded = useFarcasterDCsLoaded();
+ const isFarcasterSyncCanceled = useFarcasterDCsSyncCanceled();
return React.useCallback(
async (
@@ -1678,12 +1739,22 @@
}
}
+ const shouldFetchMessages =
+ farcasterDCsLoaded && !isFarcasterSyncCanceled;
+ const messagesNumberLimit = shouldFetchMessages
+ ? Number.POSITIVE_INFINITY
+ : 20;
if (farcasterDCsLoaded && unknownConversationIds.length > 0) {
await processInBatchesWithReduxBatching(
unknownConversationIds,
FARCASTER_DATA_BATCH_SIZE,
(conversationID, batchedUpdates) =>
- fetchConversationWithBatching(conversationID, batchedUpdates),
+ fetchConversationWithMessages(
+ conversationID,
+ messagesNumberLimit,
+ batchedUpdates,
+ false,
+ ),
dispatch,
undefined,
addLog,
@@ -1715,10 +1786,11 @@
[
currentUserSupportsDCs,
farcasterDCsLoaded,
- addLog,
- dispatch,
- fetchConversationWithBatching,
+ isFarcasterSyncCanceled,
threadInfos,
+ dispatch,
+ addLog,
+ fetchConversationWithMessages,
],
);
}
diff --git a/lib/shared/farcaster/farcaster-message-fetching-context.js b/lib/shared/farcaster/farcaster-message-fetching-context.js
--- a/lib/shared/farcaster/farcaster-message-fetching-context.js
+++ b/lib/shared/farcaster/farcaster-message-fetching-context.js
@@ -149,11 +149,12 @@
: Promise.resolve(null);
const farcasterPromise = !state.farcasterExhausted
- ? fetchFarcasterMessagesForConversation(
+ ? fetchFarcasterMessagesForConversation({
conversationID,
- numMessages,
- state.farcasterCursor ?? null,
- )
+ messagesNumberLimit: numMessages,
+ cursor: state.farcasterCursor ?? null,
+ canBeCanceled: false,
+ })
: Promise.resolve(null);
const [dbResult, farcasterResult] = await Promise.allSettled([
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
@@ -61,7 +61,7 @@
import { getMessageForException } from '../utils/errors.js';
import {
useClearFarcasterThreads,
- useSetFarcasterDCsLoaded,
+ useSetFarcasterDCsSyncStatus,
useSetLocalCurrentUserSupportsDCs,
useSetLocalFID,
} from '../utils/farcaster-utils.js';
@@ -134,7 +134,7 @@
);
const fullBackupSupport = useFullBackupSupportEnabled();
- const setFarcasterDCsLoaded = useSetFarcasterDCsLoaded();
+ const { setLoaded } = useSetFarcasterDCsSyncStatus();
return React.useCallback(
async (
@@ -267,7 +267,7 @@
if (!userActionMessage.hasDCsToken) {
clearFarcasterThreads();
}
- setFarcasterDCsLoaded(false);
+ setLoaded(false);
} finally {
await removeAndConfirmMessage(messageID, senderInfo.deviceID);
}
@@ -293,7 +293,7 @@
userDataRestore,
setLocalFID,
setLocalDCsSupport,
- setFarcasterDCsLoaded,
+ setLoaded,
clearFarcasterThreads,
],
);
diff --git a/lib/types/redux-types.js b/lib/types/redux-types.js
--- a/lib/types/redux-types.js
+++ b/lib/types/redux-types.js
@@ -143,6 +143,7 @@
import type {
SyncedMetadataStore,
SetSyncedMetadataEntryPayload,
+ SetSyncedMetadataEntriesPayload,
ClearSyncedMetadataEntryPayload,
} from './synced-metadata-types.js';
import type { GlobalThemeInfo } from './theme-types.js';
@@ -1388,6 +1389,10 @@
+type: 'SET_SYNCED_METADATA_ENTRY',
+payload: SetSyncedMetadataEntryPayload,
}
+ | {
+ +type: 'SET_SYNCED_METADATA_ENTRIES',
+ +payload: SetSyncedMetadataEntriesPayload,
+ }
| {
+type: 'CLEAR_SYNCED_METADATA_ENTRY',
+payload: ClearSyncedMetadataEntryPayload,
diff --git a/lib/types/synced-metadata-types.js b/lib/types/synced-metadata-types.js
--- a/lib/types/synced-metadata-types.js
+++ b/lib/types/synced-metadata-types.js
@@ -10,6 +10,7 @@
CURRENT_USER_FID: 'current_user_fid',
CURRENT_USER_SUPPORTS_DCS: 'current_user_supports_dcs',
FARCASTER_DCS_LOADED: 'farcaster_dcs_loaded',
+ FARCASTER_DCS_SYNC_CANCELED: 'farcaster_dcs_sync_canceled',
STORE_VERSION: 'store_version',
ENABLED_APPS: 'enabled_apps',
GLOBAL_THEME_INFO: 'global_theme_info',
@@ -23,6 +24,13 @@
+data: string,
};
+export type SetSyncedMetadataEntriesPayload = {
+ +entries: $ReadOnlyArray<{
+ +name: SyncedMetadataName,
+ +data: string,
+ }>,
+};
+
export type ClearSyncedMetadataEntryPayload = {
+name: SyncedMetadataName,
};
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
@@ -7,7 +7,10 @@
import { getConfig } from './config.js';
import { getContentSigningKey } from './crypto-utils.js';
import { useDispatch, useSelector } from './redux-utils.js';
-import { setSyncedMetadataEntryActionType } from '../actions/synced-metadata-actions.js';
+import {
+ setSyncedMetadataEntryActionType,
+ setSyncedMetadataEntriesActionType,
+} from '../actions/synced-metadata-actions.js';
import { useUserIdentityCache } from '../components/user-identity-cache.react.js';
import { getOwnPeerDevices } from '../selectors/user-selectors.js';
import { processFarcasterOpsActionType } from '../shared/farcaster/farcaster-actions.js';
@@ -68,6 +71,17 @@
return undefined;
}
+function useFarcasterDCsSyncCanceled(): boolean {
+ const farcasterDCsSyncCanceled = useSelector(
+ state =>
+ state.syncedMetadataStore.syncedMetadata[
+ syncedMetadataNames.FARCASTER_DCS_SYNC_CANCELED
+ ],
+ );
+
+ return farcasterDCsSyncCanceled === 'true';
+}
+
function useSetLocalFID(): (fid: ?string) => void {
const dispatch = useDispatch();
const { invalidateCacheForUser } = useUserIdentityCache();
@@ -114,20 +128,69 @@
);
}
-function useSetFarcasterDCsLoaded(): (loaded: boolean) => void {
+function useSetFarcasterDCsSyncStatus(): {
+ +setLoaded: (loaded: boolean) => void,
+ +setCanceled: (canceled: boolean) => void,
+} {
const dispatch = useDispatch();
- return React.useCallback(
+
+ const setLoaded = React.useCallback(
(loaded: boolean) => {
- dispatch({
- type: setSyncedMetadataEntryActionType,
- payload: {
+ const entries: Array<{ +name: string, +data: string }> = [
+ {
name: syncedMetadataNames.FARCASTER_DCS_LOADED,
data: String(loaded),
},
+ ];
+ if (!loaded) {
+ entries.push({
+ name: syncedMetadataNames.FARCASTER_DCS_SYNC_CANCELED,
+ data: String(false),
+ });
+ }
+ dispatch({
+ type: setSyncedMetadataEntriesActionType,
+ payload: { entries },
});
},
[dispatch],
);
+
+ const setCanceled = React.useCallback(
+ (canceled: boolean) => {
+ const entries: Array<{ +name: string, +data: string }> = [
+ {
+ name: syncedMetadataNames.FARCASTER_DCS_SYNC_CANCELED,
+ data: String(canceled),
+ },
+ ];
+ if (canceled) {
+ entries.push({
+ name: syncedMetadataNames.FARCASTER_DCS_LOADED,
+ data: String(true),
+ });
+ }
+ dispatch({
+ type: setSyncedMetadataEntriesActionType,
+ payload: { entries },
+ });
+ },
+ [dispatch],
+ );
+
+ return React.useMemo(
+ () => ({ setLoaded, setCanceled }),
+ [setLoaded, setCanceled],
+ );
+}
+
+function useFarcasterDCsSyncCancellationRef(): { +current: boolean } {
+ const isCanceled = useFarcasterDCsSyncCanceled();
+ const canceledRef = React.useRef(isCanceled);
+ React.useEffect(() => {
+ canceledRef.current = isCanceled;
+ }, [isCanceled]);
+ return canceledRef;
}
function useLinkFID(): (fid: string) => Promise<void> {
@@ -154,9 +217,9 @@
function useClearFarcasterThreads(): () => void {
const threads = useSelector(state => state.threadStore.threadInfos);
const dispatch = useDispatch();
- const setFarcasterDCsLoaded = useSetFarcasterDCsLoaded();
+ const { setLoaded } = useSetFarcasterDCsSyncStatus();
return React.useCallback(() => {
- setFarcasterDCsLoaded(false);
+ setLoaded(false);
const farcasterThreadIDs = Object.values(threads)
.filter(thread => thread.farcaster)
.map(thread => thread.id);
@@ -174,7 +237,7 @@
updateInfos: updates,
},
});
- }, [dispatch, setFarcasterDCsLoaded, threads]);
+ }, [dispatch, setLoaded, threads]);
}
function useUnlinkFID(): () => Promise<void> {
@@ -218,19 +281,19 @@
const setLocalDCsSupport = useSetLocalCurrentUserSupportsDCs();
const broadcastConnectionStatus =
useBroadcastUpdateFarcasterConnectionStatus();
- const setFarcasterDCsLoaded = useSetFarcasterDCsLoaded();
+ const { setLoaded } = useSetFarcasterDCsSyncStatus();
return React.useCallback(
async (fid: string, farcasterDCsToken: string) => {
await linkFarcasterDCsAccount(fid, farcasterDCsToken);
setLocalDCsSupport(true);
- setFarcasterDCsLoaded(false);
+ setLoaded(false);
await broadcastConnectionStatus(fid, true);
},
[
linkFarcasterDCsAccount,
setLocalDCsSupport,
- setFarcasterDCsLoaded,
+ setLoaded,
broadcastConnectionStatus,
],
);
@@ -301,18 +364,47 @@
);
}
+function useCancelFarcasterDCsSync(): () => void {
+ const { setCanceled } = useSetFarcasterDCsSyncStatus();
+ const { showAlert } = getConfig();
+
+ return React.useCallback(() => {
+ showAlert(
+ 'Cancel Farcaster sync?',
+ 'You can restart it later in the Profile tab.',
+ [
+ {
+ text: 'No',
+ style: 'cancel',
+ },
+ {
+ text: 'Yes',
+ style: 'destructive',
+ onPress: () => setCanceled(true),
+ },
+ ],
+ {
+ cancelable: true,
+ },
+ );
+ }, [setCanceled, showAlert]);
+}
+
export {
DISABLE_CONNECT_FARCASTER_ALERT,
NO_FID_METADATA,
useCurrentUserFID,
useCurrentUserSupportsDCs,
useFarcasterDCsLoaded,
+ useFarcasterDCsSyncCanceled,
useSetLocalFID,
useSetLocalCurrentUserSupportsDCs,
- useSetFarcasterDCsLoaded,
+ useSetFarcasterDCsSyncStatus,
useLinkFID,
useUnlinkFID,
useLinkFarcasterDCs,
createFarcasterDCsAuthMessage,
useClearFarcasterThreads,
+ useFarcasterDCsSyncCancellationRef,
+ useCancelFarcasterDCsSync,
};
diff --git a/native/account/registration/registration-terms.react.js b/native/account/registration/registration-terms.react.js
--- a/native/account/registration/registration-terms.react.js
+++ b/native/account/registration/registration-terms.react.js
@@ -5,7 +5,7 @@
import { Text, View, Image, Linking } from 'react-native';
import type { SignedMessage } from 'lib/types/siwe-types.js';
-import { useSetFarcasterDCsLoaded } from 'lib/utils/farcaster-utils.js';
+import { useSetFarcasterDCsSyncStatus } from 'lib/utils/farcaster-utils.js';
import type { AuthNavigationProp } from './auth-navigator.react.js';
import { RegistrationContext } from './registration-context.js';
@@ -105,7 +105,7 @@
);
}, [setCachedSelections, navigateToConnectEthereum]);
- const setFarcasterDCsLoaded = useSetFarcasterDCsLoaded();
+ const { setLoaded } = useSetFarcasterDCsSyncStatus();
const onProceed = React.useCallback(async () => {
setRegistrationInProgress(true);
try {
@@ -115,7 +115,7 @@
onNonceExpired,
});
if (userSelections.farcasterDCsToken) {
- setFarcasterDCsLoaded(false);
+ setLoaded(false);
}
} finally {
setRegistrationInProgress(false);
@@ -125,7 +125,7 @@
userSelections,
clearCachedSelections,
onNonceExpired,
- setFarcasterDCsLoaded,
+ setLoaded,
]);
usePreventUserFromLeavingScreen(registrationInProgress);
diff --git a/native/components/farcaster-sync-handler.react.js b/native/components/farcaster-sync-handler.react.js
--- a/native/components/farcaster-sync-handler.react.js
+++ b/native/components/farcaster-sync-handler.react.js
@@ -9,6 +9,7 @@
import {
useCurrentUserSupportsDCs,
useFarcasterDCsLoaded,
+ useFarcasterDCsSyncCanceled,
} from 'lib/utils/farcaster-utils.js';
import { useSelector } from 'lib/utils/redux-utils.js';
@@ -25,6 +26,7 @@
const currentUserSupportsDCs = useCurrentUserSupportsDCs();
const farcasterDCsLoaded = useFarcasterDCsLoaded();
+ const isFarcasterSyncCanceled = useFarcasterDCsSyncCanceled();
const currentRouteName = useCurrentLeafRouteName();
useLightweightSyncOnAppStart();
@@ -40,7 +42,8 @@
fullyLoggedIn &&
currentUserSupportsDCs &&
farcasterDCsLoaded === false &&
- currentRouteName !== FarcasterSyncScreenRouteName
+ currentRouteName !== FarcasterSyncScreenRouteName &&
+ !isFarcasterSyncCanceled
) {
dispatch(
CommonActions.navigate({
@@ -55,6 +58,7 @@
farcasterDCsLoaded,
currentRouteName,
fullyLoggedIn,
+ isFarcasterSyncCanceled,
]);
return null;
diff --git a/native/farcaster/farcaster-sync-loading-screen.react.js b/native/farcaster/farcaster-sync-loading-screen.react.js
--- a/native/farcaster/farcaster-sync-loading-screen.react.js
+++ b/native/farcaster/farcaster-sync-loading-screen.react.js
@@ -6,7 +6,9 @@
import { SafeAreaView } from 'react-native-safe-area-context';
import { useFarcasterSync } from 'lib/shared/farcaster/farcaster-hooks.js';
+import { useCancelFarcasterDCsSync } from 'lib/utils/farcaster-utils.js';
+import PrimaryButton from '../components/primary-button.react.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';
@@ -25,6 +27,7 @@
}, [props.navigation]);
const { progress } = useFarcasterSync(handleComplete);
+ const cancelSync = useCancelFarcasterDCsSync();
const progressValue = progress
? progress.completedConversations / progress.totalNumberOfConversations
@@ -32,67 +35,76 @@
return (
<SafeAreaView edges={safeAreaEdges} style={styles.container}>
- <Text style={styles.header}>Fetching Farcaster conversations</Text>
- <View style={styles.listContainer}>
- <View style={styles.listItem}>
- <Text style={styles.listNumber}>1.</Text>
- <Text style={styles.listText}>
- <Text style={styles.bold}>Fetching in progress</Text>: Comm is
- fetching all of your Farcaster messages so they can be backed up.
- This can take a while, depending on how many chats you have.
- </Text>
+ <View style={styles.contentContainer}>
+ <Text style={styles.header}>Fetching Farcaster conversations</Text>
+ <View style={styles.listContainer}>
+ <View style={styles.listItem}>
+ <Text style={styles.listNumber}>1.</Text>
+ <Text style={styles.listText}>
+ <Text style={styles.bold}>Fetching in progress</Text>: Comm is
+ fetching all of your Farcaster messages so they can be backed up.
+ This can take a while, depending on how many chats you have.
+ </Text>
+ </View>
+ <View style={styles.listItem}>
+ <Text style={styles.listNumber}>2.</Text>
+ <Text style={styles.listText}>
+ <Text style={styles.bold}>No E2E encryption</Text>: Please note
+ that Farcaster messages are not end-to-end encrypted, which means
+ the Farcaster team can see them. For better security, consider
+ using Comm DMs.
+ </Text>
+ </View>
+ <View style={styles.listItem}>
+ <Text style={styles.listNumber}>3.</Text>
+ <Text style={styles.listText}>
+ <Text style={styles.bold}>Manual refresh</Text>: If you ever
+ notice any missing messages, you can manually refresh all
+ Farcaster chats from your profile screen, or refresh an individual
+ chat from its settings.
+ </Text>
+ </View>
</View>
- <View style={styles.listItem}>
- <Text style={styles.listNumber}>2.</Text>
- <Text style={styles.listText}>
- <Text style={styles.bold}>No E2E encryption</Text>: Please note that
- Farcaster messages are not end-to-end encrypted, which means the
- Farcaster team can see them. For better security, consider using
- Comm DMs.
- </Text>
- </View>
- <View style={styles.listItem}>
- <Text style={styles.listNumber}>3.</Text>
- <Text style={styles.listText}>
- <Text style={styles.bold}>Manual refresh</Text>: If you ever notice
- any missing messages, you can manually refresh all Farcaster chats
- from your profile screen, or refresh an individual chat from its
- settings.
- </Text>
- </View>
- </View>
- <View style={styles.progressContainer}>
- {progress ? (
- <>
- <Progress.Circle
- progress={progressValue}
- size={100}
+ <View style={styles.progressContainer}>
+ {progress ? (
+ <>
+ <Progress.Circle
+ progress={progressValue}
+ size={100}
+ color={colors.panelForegroundIcon}
+ strokeCap="round"
+ showsText
+ />
+ <View>
+ <Text style={styles.progressText}>
+ {progress.completedConversations} of{' '}
+ {progress.totalNumberOfConversations} conversations fetched
+ </Text>
+ </View>
+ <View>
+ <Text style={styles.progressText}>
+ {progress.completedMessages
+ ? `${progress.completedMessages.toLocaleString()} messages fetched`
+ : null}
+ </Text>
+ </View>
+ </>
+ ) : (
+ <Progress.CircleSnail
+ indeterminate
color={colors.panelForegroundIcon}
+ size={100}
strokeCap="round"
- showsText
/>
- <View>
- <Text style={styles.progressText}>
- {progress.completedConversations} of{' '}
- {progress.totalNumberOfConversations} conversations fetched
- </Text>
- </View>
- <View>
- <Text style={styles.progressText}>
- {progress.completedMessages
- ? `${progress.completedMessages.toLocaleString()} messages fetched`
- : null}
- </Text>
- </View>
- </>
- ) : (
- <Progress.CircleSnail
- indeterminate
- color={colors.panelForegroundIcon}
- size={100}
- strokeCap="round"
- />
- )}
+ )}
+ </View>
+ </View>
+ <View style={styles.buttonContainer}>
+ <PrimaryButton
+ onPress={cancelSync}
+ label="Cancel fetching"
+ variant="outline"
+ />
</View>
</SafeAreaView>
);
@@ -104,8 +116,11 @@
container: {
flex: 1,
backgroundColor: 'panelBackground',
- justifyContent: 'space-between',
+ },
+ contentContainer: {
+ flex: 1,
padding: 16,
+ justifyContent: 'space-between',
},
header: {
fontSize: 24,
@@ -148,6 +163,10 @@
color: 'panelForegroundSecondaryLabel',
textAlign: 'center',
},
+ buttonContainer: {
+ marginVertical: 8,
+ marginHorizontal: 16,
+ },
};
export default FarcasterSyncLoadingScreen;
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
@@ -26,7 +26,7 @@
import { type CurrentUserInfo } from 'lib/types/user-types.js';
import {
useCurrentUserFID,
- useSetFarcasterDCsLoaded,
+ useSetFarcasterDCsSyncStatus,
} from 'lib/utils/farcaster-utils.js';
import {
useDispatchActionPromise,
@@ -640,10 +640,10 @@
}, [checkIfPrimaryDevice]);
const usingRestoreFlow = useIsRestoreFlowEnabled();
- const setDCsLoaded = useSetFarcasterDCsLoaded();
+ const { setLoaded } = useSetFarcasterDCsSyncStatus();
const syncFarcasterConversations = React.useCallback(() => {
- setDCsLoaded(false);
- }, [setDCsLoaded]);
+ setLoaded(false);
+ }, [setLoaded]);
const supportsFarcasterDCs = useIsFarcasterDCsIntegrationEnabled();
return (
diff --git a/web/components/farcaster-sync-overlay.react.js b/web/components/farcaster-sync-overlay.react.js
--- a/web/components/farcaster-sync-overlay.react.js
+++ b/web/components/farcaster-sync-overlay.react.js
@@ -6,6 +6,7 @@
import {
useCurrentUserSupportsDCs,
useFarcasterDCsLoaded,
+ useFarcasterDCsSyncCanceled,
} from 'lib/utils/farcaster-utils.js';
import FarcasterSyncLoadingScreen from '../farcaster/farcaster-sync-loading-screen.react.js';
@@ -17,10 +18,13 @@
function FarcasterSyncOverlay(props: Props): React.Node {
const { children } = props;
const farcasterDCsLoaded = useFarcasterDCsLoaded();
+ const isFarcasterSyncCanceled = useFarcasterDCsSyncCanceled();
const currentUserSupportsDCs = useCurrentUserSupportsDCs();
const isFullSyncInProgress =
- currentUserSupportsDCs && farcasterDCsLoaded === false;
+ currentUserSupportsDCs &&
+ farcasterDCsLoaded === false &&
+ !isFarcasterSyncCanceled;
useLightweightSyncOnAppStart();
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
@@ -26,7 +26,7 @@
createOlmSessionsWithOwnDevices,
getContentSigningKey,
} from 'lib/utils/crypto-utils.js';
-import { useSetFarcasterDCsLoaded } from 'lib/utils/farcaster-utils.js';
+import { useSetFarcasterDCsSyncStatus } from 'lib/utils/farcaster-utils.js';
import { useDispatchActionPromise } from 'lib/utils/redux-promise-utils.js';
import {
useIsFarcasterDCsIntegrationEnabled,
@@ -183,10 +183,10 @@
[pushModal],
);
- const setDCsLoaded = useSetFarcasterDCsLoaded();
+ const { setLoaded } = useSetFarcasterDCsSyncStatus();
const syncFarcasterConversations = React.useCallback(() => {
- setDCsLoaded(false);
- }, [setDCsLoaded]);
+ setLoaded(false);
+ }, [setLoaded]);
if (!currentUserInfo || currentUserInfo.anonymous) {
return null;

File Metadata

Mime Type
text/plain
Expires
Sat, Dec 6, 2:00 PM (19 h, 43 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
5829734
Default Alt Text
D15487.1765029637.diff (43 KB)

Event Timeline