diff --git a/app/actions/local/thread.ts b/app/actions/local/thread.ts index 40c97b2a2c..579de4db06 100644 --- a/app/actions/local/thread.ts +++ b/app/actions/local/thread.ts @@ -140,7 +140,7 @@ export const createThreadFromNewPost = async (serverUrl: string, post: Post, pre }; // On receiving threads, Along with the "threads" & "thread participants", extract and save "posts" & "users" -export const processReceivedThreads = async (serverUrl: string, threads: Thread[], teamId: string, prepareRecordsOnly = false) => { +export const processReceivedThreads = async (serverUrl: string, threads: Thread[], teamId: string, prepareRecordsOnly = false, loadedInGlobalThreads = false) => { const operator = DatabaseManager.serverDatabases[serverUrl]?.operator; if (!operator) { return {error: `${serverUrl} database not found`}; @@ -173,6 +173,7 @@ export const processReceivedThreads = async (serverUrl: string, threads: Thread[ threads, teamId, prepareRecordsOnly: true, + loadedInGlobalThreads, }); if (threadModels.length) { diff --git a/app/actions/remote/thread.ts b/app/actions/remote/thread.ts index be2fae1332..7ceb3b5090 100644 --- a/app/actions/remote/thread.ts +++ b/app/actions/remote/thread.ts @@ -100,7 +100,7 @@ export const fetchThreads = async ( thread.is_following = true; }); - await processReceivedThreads(serverUrl, threads, teamId); + await processReceivedThreads(serverUrl, threads, teamId, false, !unread); } return {data}; @@ -121,7 +121,7 @@ export const fetchThread = async (serverUrl: string, teamId: string, threadId: s try { const thread = await client.getThread('me', teamId, threadId, extended); - await processReceivedThreads(serverUrl, [thread], teamId); + await processReceivedThreads(serverUrl, [thread], teamId, false, false); return {data: thread}; } catch (error) { diff --git a/app/database/models/server/team.ts b/app/database/models/server/team.ts index 10793354a2..1b1e826082 100644 --- a/app/database/models/server/team.ts +++ b/app/database/models/server/team.ts @@ -106,15 +106,23 @@ export default class TeamModel extends Model implements TeamModelInterface { @lazy threadsList = this.collections.get(THREAD).query( Q.on(THREADS_IN_TEAM, Q.and( Q.where('team_id', this.id), - Q.where('loadedInGlobalThreads', true), + Q.where('loaded_in_global_threads', true), )), + Q.and( + Q.where('reply_count', Q.gt(0)), + Q.where('is_following', true), + ), Q.sortBy('last_reply_at', Q.desc), ); /** unreadThreadsList : Unread threads list belonging to a team */ @lazy unreadThreadsList = this.collections.get(THREAD).query( Q.on(THREADS_IN_TEAM, 'team_id', this.id), - Q.where('unread_replies', Q.gt(0)), + Q.and( + Q.where('reply_count', Q.gt(0)), + Q.where('is_following', true), + Q.where('unread_replies', Q.gt(0)), + ), Q.sortBy('last_reply_at', Q.desc), ); } diff --git a/app/database/models/server/thread.ts b/app/database/models/server/thread.ts index 599eb0ebeb..ad3dd26dd3 100644 --- a/app/database/models/server/thread.ts +++ b/app/database/models/server/thread.ts @@ -31,7 +31,7 @@ export default class ThreadModel extends Model implements ThreadModelInterface { [THREAD_PARTICIPANT]: {type: 'has_many', foreignKey: 'thread_id'}, /** A THREAD can have multiple THREADS_IN_TEAM. (relationship is 1:N)*/ - [THREADS_IN_TEAM]: {type: 'has_many', foreignKey: 'team_id'}, + [THREADS_IN_TEAM]: {type: 'has_many', foreignKey: 'thread_id'}, }; /** last_reply_at : The timestamp of when user last replied to the thread. */ diff --git a/app/database/models/server/thread_in_team.ts b/app/database/models/server/thread_in_team.ts index 6bc8531d30..d12fc0eff5 100644 --- a/app/database/models/server/thread_in_team.ts +++ b/app/database/models/server/thread_in_team.ts @@ -28,7 +28,7 @@ export default class ThreadInTeamModel extends Model implements ThreadInTeamMode [TEAM]: {type: 'belongs_to', key: 'team_id'}, /** A THREAD can have many THREADS_IN_TEAM. (relationship is N:1)*/ - [THREAD]: {type: 'belongs_to', key: 'team_id'}, + [THREAD]: {type: 'belongs_to', key: 'thread_id'}, }; /** thread_id: Associated thread identifier */ diff --git a/app/database/operator/server_data_operator/handlers/thread.test.ts b/app/database/operator/server_data_operator/handlers/thread.test.ts index 3ba74c3f22..ac526a7b3a 100644 --- a/app/database/operator/server_data_operator/handlers/thread.test.ts +++ b/app/database/operator/server_data_operator/handlers/thread.test.ts @@ -46,12 +46,11 @@ describe('*** Operator: Thread Handlers tests ***', () => { is_following: true, unread_replies: 0, unread_mentions: 0, - loaded_in_global_threads: false, }, ] as Thread[]; const threadsMap = {team_id_1: threads}; - await operator.handleThreads({threads, prepareRecordsOnly: false, teamId: 'team_id_1'}); + await operator.handleThreads({threads, loadedInGlobalThreads: false, prepareRecordsOnly: false, teamId: 'team_id_1'}); expect(spyOnHandleRecords).toHaveBeenCalledWith({ fieldName: 'id', @@ -76,6 +75,7 @@ describe('*** Operator: Thread Handlers tests ***', () => { expect(spyOnHandleThreadInTeam).toHaveBeenCalledWith({ threadsMap, prepareRecordsOnly: true, + loadedInGlobalThreads: false, }); // Only one batch operation for both tables @@ -125,7 +125,6 @@ describe('*** Operator: Thread Handlers tests ***', () => { is_following: true, unread_replies: 0, unread_mentions: 0, - loaded_in_global_threads: true, }, { id: 'thread-2', @@ -138,7 +137,6 @@ describe('*** Operator: Thread Handlers tests ***', () => { is_following: true, unread_replies: 0, unread_mentions: 0, - loaded_in_global_threads: true, }, ] as Thread[]; @@ -154,7 +152,6 @@ describe('*** Operator: Thread Handlers tests ***', () => { is_following: true, unread_replies: 2, unread_mentions: 0, - loaded_in_global_threads: false, }, ] as Thread[]; @@ -163,13 +160,13 @@ describe('*** Operator: Thread Handlers tests ***', () => { team_id_2: team2Threads, }; - await operator.handleThreadInTeam({threadsMap, prepareRecordsOnly: false}); + await operator.handleThreadInTeam({threadsMap, loadedInGlobalThreads: true, prepareRecordsOnly: false}); expect(spyOnPrepareRecords).toHaveBeenCalledWith({ createRaws: [{ raw: {team_id: 'team_id_1', thread_id: 'thread-2', loaded_in_global_threads: true}, }, { - raw: {team_id: 'team_id_2', thread_id: 'thread-2', loaded_in_global_threads: false}, + raw: {team_id: 'team_id_2', thread_id: 'thread-2', loaded_in_global_threads: true}, }], transformer: transformThreadInTeamRecord, updateRaws: [ diff --git a/app/database/operator/server_data_operator/handlers/thread.ts b/app/database/operator/server_data_operator/handlers/thread.ts index bd236b111c..35793f4ff8 100644 --- a/app/database/operator/server_data_operator/handlers/thread.ts +++ b/app/database/operator/server_data_operator/handlers/thread.ts @@ -23,7 +23,7 @@ const { } = Database.MM_TABLES.SERVER; export interface ThreadHandlerMix { - handleThreads: ({threads, teamId, prepareRecordsOnly}: HandleThreadsArgs) => Promise; + handleThreads: ({threads, teamId, prepareRecordsOnly, loadedInGlobalThreads}: HandleThreadsArgs) => Promise; handleThreadParticipants: ({threadsParticipants, prepareRecordsOnly}: HandleThreadParticipantsArgs) => Promise; } @@ -35,7 +35,7 @@ const ThreadHandler = (superclass: any) => class extends superclass { * @param {boolean | undefined} handleThreads.prepareRecordsOnly * @returns {Promise} */ - handleThreads = async ({threads, teamId, prepareRecordsOnly = false}: HandleThreadsArgs): Promise => { + handleThreads = async ({threads, teamId, loadedInGlobalThreads, prepareRecordsOnly = false}: HandleThreadsArgs): Promise => { if (!threads.length) { throw new DataOperatorException( 'An empty "threads" array has been passed to the handleThreads method', @@ -81,6 +81,7 @@ const ThreadHandler = (superclass: any) => class extends superclass { const threadsInTeam = await this.handleThreadInTeam({ threadsMap: {[teamId]: threads}, prepareRecordsOnly: true, + loadedInGlobalThreads, }) as ThreadInTeamModel[]; batch.push(...threadsInTeam); } diff --git a/app/database/operator/server_data_operator/handlers/thread_in_team.ts b/app/database/operator/server_data_operator/handlers/thread_in_team.ts index 4a7cfb5181..a9118e2e8c 100644 --- a/app/database/operator/server_data_operator/handlers/thread_in_team.ts +++ b/app/database/operator/server_data_operator/handlers/thread_in_team.ts @@ -11,13 +11,13 @@ import type {HandleThreadInTeamArgs, RecordPair} from '@typings/database/databas import type ThreadInTeamModel from '@typings/database/models/servers/thread_in_team'; export interface ThreadInTeamHandlerMix { - handleThreadInTeam: ({threadsMap, prepareRecordsOnly}: HandleThreadInTeamArgs) => Promise; + handleThreadInTeam: ({threadsMap, loadedInGlobalThreads, prepareRecordsOnly}: HandleThreadInTeamArgs) => Promise; } const {THREADS_IN_TEAM} = MM_TABLES.SERVER; const ThreadInTeamHandler = (superclass: any) => class extends superclass { - handleThreadInTeam = async ({threadsMap, prepareRecordsOnly = false}: HandleThreadInTeamArgs): Promise => { + handleThreadInTeam = async ({threadsMap, loadedInGlobalThreads, prepareRecordsOnly = false}: HandleThreadInTeamArgs): Promise => { if (!Object.keys(threadsMap).length) { return []; } @@ -40,11 +40,11 @@ const ThreadInTeamHandler = (superclass: any) => class extends superclass { const newValue = { thread_id: thread.id, team_id: teamId, - loaded_in_global_threads: thread.loaded_in_global_threads, + loaded_in_global_threads: Boolean(loadedInGlobalThreads), }; - // update record if loaded_in_global_threads is true - if (chunk && thread.loaded_in_global_threads) { + // update record only if loaded_in_global_threads is true + if (chunk && loadedInGlobalThreads) { update.push(getValidRecordsForUpdate({ tableName: THREADS_IN_TEAM, newValue, diff --git a/app/database/operator/server_data_operator/transformers/thread.ts b/app/database/operator/server_data_operator/transformers/thread.ts index 2a4d2bd2a9..96aeddb6e5 100644 --- a/app/database/operator/server_data_operator/transformers/thread.ts +++ b/app/database/operator/server_data_operator/transformers/thread.ts @@ -35,8 +35,8 @@ export const transformThreadRecord = ({action, database, value}: TransformerArgs thread.lastViewedAt = raw.last_viewed_at ?? record?.lastViewedAt ?? 0; thread.replyCount = raw.reply_count; thread.isFollowing = raw.is_following ?? record?.isFollowing; - thread.unreadReplies = raw.unread_replies ?? record?.lastViewedAt ?? 0; - thread.unreadMentions = raw.unread_mentions ?? record?.lastViewedAt ?? 0; + thread.unreadReplies = raw.unread_replies ?? record?.unreadReplies ?? 0; + thread.unreadMentions = raw.unread_mentions ?? record?.unreadMentions ?? 0; thread.viewedAt = record?.viewedAt || 0; }; diff --git a/types/api/threads.d.ts b/types/api/threads.d.ts index 0619d4dd63..d6087817d4 100644 --- a/types/api/threads.d.ts +++ b/types/api/threads.d.ts @@ -11,7 +11,6 @@ type Thread = { is_following?: boolean; unread_replies: number; unread_mentions: number; - loaded_in_global_threads: boolean; }; type ThreadParticipant = { diff --git a/types/database/database.d.ts b/types/database/database.d.ts index 7d4ad0c1e4..45bc3150ae 100644 --- a/types/database/database.d.ts +++ b/types/database/database.d.ts @@ -92,6 +92,7 @@ export type HandleThreadsArgs = { threads: Thread[]; prepareRecordsOnly?: boolean; teamId?: string; + loadedInGlobalThreads?: boolean; }; export type HandleThreadParticipantsArgs = { @@ -103,6 +104,7 @@ export type HandleThreadParticipantsArgs = { export type HandleThreadInTeamArgs = { threadsMap: Record; prepareRecordsOnly?: boolean; + loadedInGlobalThreads?: boolean; }; export type SanitizeReactionsArgs = {