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 @@ -1046,9 +1046,7 @@ ); } -function useLightweightFarcasterConversationsSync(): ( - onProgress?: (completed: number, total: number) => void, -) => Promise { +function useLightweightFarcasterConversationsSync(): () => Promise { const dispatch = useDispatch(); const fetchConversationWithMessages = useFetchConversationWithMessages(); const { addLog } = useDebugLogs(); @@ -1056,116 +1054,135 @@ const fetchInboxes = useFetchInboxes(); const removeDeadThreads = useRemoveDeadThreads(); const currentUserFID = useCurrentUserFID(); + const auxUserStore = useSelector(state => state.auxUserStore); - return React.useCallback( - async (onProgress?: (completed: number, total: number) => void) => { - 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 () => { + 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); - } + 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 = []; + const existingConversationIDs = []; - 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)) + ) { + existingConversationIDs.push(conversationID); + } else { + newConversationIDs.push(conversationID); } + } - onProgress?.(0, conversations.length); - - const updates = conversations - .map(conversation => { - const threadID = farcasterThreadIDFromConversationID( - conversation.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 thread = threadInfos[threadID]; - if (thread && thread.farcaster) { - return createUpdatedThread(thread, conversation, currentUserFID); - } - return null; - }) - .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, - }, - }); - onProgress?.(existingConversationIDs.length, conversations.length); + } + return null; + }) + .filter(Boolean); - if (newConversationIDs.length > 0) { - await processInBatchesWithReduxBatching( - newConversationIDs, - FARCASTER_DATA_BATCH_SIZE, - (conversationID, batchedUpdates) => - fetchConversationWithMessages( - conversationID, - Number.POSITIVE_INFINITY, - batchedUpdates, - ), - dispatch, - completed => - onProgress?.( - existingConversationIDs.length + completed, - conversations.length, - ), - addLog, - ); - } - } catch (e) { - addLog( - 'Farcaster: Failed to sync conversations (lightweight)', - JSON.stringify({ - error: getMessageForException(e), - }), - new Set([logTypes.FARCASTER]), + 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); + + if (needRefetch.length > 0) { + await processInBatchesWithReduxBatching( + needRefetch, + FARCASTER_DATA_BATCH_SIZE, + (conversationID, batchedUpdates) => + fetchConversationWithMessages(conversationID, 0, batchedUpdates), + dispatch, + undefined, + addLog, ); - throw e; } - }, - [ - addLog, - dispatch, - fetchConversationWithMessages, - fetchInboxes, - removeDeadThreads, - threadInfos, - currentUserFID, - ], - ); + + if (newConversationIDs.length > 0) { + await processInBatchesWithReduxBatching( + newConversationIDs, + FARCASTER_DATA_BATCH_SIZE, + (conversationID, batchedUpdates) => + fetchConversationWithMessages( + conversationID, + Number.POSITIVE_INFINITY, + batchedUpdates, + ), + dispatch, + undefined, + addLog, + ); + } + } 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, + ]); } function useAddNewFarcasterMessage(): FarcasterMessage => Promise { diff --git a/lib/utils/create-farcaster-raw-thread-info.js b/lib/utils/create-farcaster-raw-thread-info.js --- a/lib/utils/create-farcaster-raw-thread-info.js +++ b/lib/utils/create-farcaster-raw-thread-info.js @@ -17,8 +17,12 @@ FarcasterInboxConversation, FarcasterConversationInvitee, } from '../shared/farcaster/farcaster-conversation-types.js'; -import { farcasterThreadIDFromConversationID } from '../shared/id-utils.js'; +import { + farcasterThreadIDFromConversationID, + getFIDFromUserID, +} from '../shared/id-utils.js'; import { stringForUserExplicit } from '../shared/user-utils.js'; +import type { AuxUserStore } from '../types/aux-user-types.js'; import type { ClientAvatar } from '../types/avatar-types.js'; import type { FarcasterRawThreadInfo, @@ -31,10 +35,7 @@ minimallyEncodeThreadCurrentUserInfo, } from '../types/minimally-encoded-thread-permissions-types.js'; import type { ThreadRolePermissionsBlob } from '../types/thread-permission-types.js'; -import { - threadTypes, - farcasterThreadTypes, -} from '../types/thread-types-enum.js'; +import { farcasterThreadTypes } from '../types/thread-types-enum.js'; import type { FarcasterThreadType } from '../types/thread-types-enum.js'; function createPermissionsInfo( @@ -320,8 +321,10 @@ threadInfo: FarcasterRawThreadInfo, conversation: FarcasterInboxConversation, currentUserFID: string, + auxUserStore: AuxUserStore, ): | { +result: 'unchanged' } + | { +result: 'refetch', +conversationID: string } | { +result: 'updated', +threadInfo: FarcasterRawThreadInfo } { let updatedThreadInfo = threadInfo; @@ -364,40 +367,17 @@ const adminsRole = values(threadInfo.roles).find( role => role.specialRole === specialRoles.ADMIN_ROLE, ); - const threadAdminUserIDs = threadInfo.members + const threadAdminUserFIDs = threadInfo.members .filter(member => member.role === adminsRole?.id) - .map(member => member.id); - //TODO: update this condition and compute proper `inviteeIDs` + .map(member => getFIDFromUserID(member.id, auxUserStore.auxUserInfos)) + .filter(Boolean); + if ( adminsRole && - (adminIDs.size !== threadAdminUserIDs.length || - threadAdminUserIDs.some(id => !adminIDs.has(id))) + (adminIDs.size !== threadAdminUserFIDs.length || + threadAdminUserFIDs.some(id => !adminIDs.has(id))) ) { - const permissionBlobs = getFarcasterRolePermissionsBlobs( - threadTypes.FARCASTER_GROUP, - false, - false, - ); - const { members, roles, currentUser } = createMembersAndCurrentUser({ - threadID: threadInfo.id, - permissionBlobs, - memberIDs: threadInfo.members.map(member => member.id), - adminIDs, - inviteeIDs: new Set(), - currentUserOptions: { - isAdmin: adminIDs.has(currentUserFID), - unread: conversationIsUnread, - muted: conversation.viewerContext.muted, - }, - threadType: threadTypes.FARCASTER_GROUP, - category: conversation.viewerContext.category, - }); - updatedThreadInfo = { - ...updatedThreadInfo, - members, - roles, - currentUser, - }; + return { result: 'refetch', conversationID: conversation.conversationId }; } }