Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F32140805
D15487.1765029637.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Size
43 KB
Referenced Files
None
Subscribers
None
D15487.1765029637.diff
View Options
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
Details
Attached
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)
Attached To
Mode
D15487: [lib] Introduce the option to cancel Farcaster sync
Attached
Detach File
Event Timeline
Log In to Comment