[Gekidou DB]: Refactors thread and threads_in_team tables (#6100)

* Refactors thread and threads_in_team tables

The convention is that only threads being in the ThreadsInTeam will be shown in a thread list,
and that only threads marked as loaded_in_global_threads are being shown
in the All threads tab in the list.

So when a thread arrives through different means, whether it's a WS event,
or just fetching a (*new) thread by opening it in a channel, etc...
we'll need to check if it's newer than any of the existing threads in the all threads list.
If it is it will be added in the ThreadsInTeam and will be marked as loaded_in_global_threads: true.
If it's not newer but it is an unread thread it will still be added in the ThreadsInTeam
but marked as loaded_in_global_threads: false.

This commit refactors `loaded_in_global_threads` field from the Thread table
to the ThreadsInTeam table so that the above is possible.

* Update tests

* Addresses review comments
This commit is contained in:
Kyriakos Z
2022-03-31 15:18:35 +03:00
committed by GitHub
parent e2e54b3bca
commit 567141a60e
12 changed files with 53 additions and 31 deletions

View File

@@ -110,19 +110,18 @@ export default class TeamModel extends Model implements TeamModelInterface {
/** teamSearchHistories : All the searches performed on this team */
@children(TEAM_SEARCH_HISTORY) teamSearchHistories!: Query<TeamSearchHistoryModel>;
/** threads : All threads belonging to a team */
@lazy threads = this.collections.get<ThreadModel>(THREAD).query(
Q.on(THREADS_IN_TEAM, 'team_id', this.id),
);
/** threads : Threads list belonging to a team */
@lazy threadsList = this.threads.extend(
Q.where('loadedInGlobalThreads', true),
@lazy threadsList = this.collections.get<ThreadModel>(THREAD).query(
Q.on(THREADS_IN_TEAM, Q.and(
Q.where('team_id', this.id),
Q.where('loadedInGlobalThreads', true),
)),
Q.sortBy('last_reply_at', Q.desc),
);
/** unreadThreadsList : Unread threads list belonging to a team */
@lazy unreadThreadsList = this.threads.extend(
@lazy unreadThreadsList = this.collections.get<ThreadModel>(THREAD).query(
Q.on(THREADS_IN_TEAM, 'team_id', this.id),
Q.where('unread_replies', Q.gt(0)),
Q.sortBy('last_reply_at', Q.desc),
);

View File

@@ -52,9 +52,6 @@ export default class ThreadModel extends Model implements ThreadModelInterface {
/** unread_mentions : The number of mentions that have not been read by the user. */
@field('unread_mentions') unreadMentions!: number;
/** loaded_in_global_threads : Flag to differentiate the unread threads loaded for showing unread counts/mentions */
@field('loaded_in_global_threads') loadedInGlobalThreads!: boolean;
/** participants : All the participants associated with this Thread */
@children(THREAD_PARTICIPANT) participants!: Query<ThreadParticipantModel>;

View File

@@ -37,6 +37,9 @@ export default class ThreadInTeamModel extends Model implements ThreadInTeamMode
/** team_id: Associated team identifier */
@field('team_id') teamId!: string;
/** loaded_in_global_threads : Flag to differentiate the unread threads loaded for showing unread counts/mentions */
@field('loaded_in_global_threads') loadedInGlobalThreads!: boolean;
@immutableRelation(THREAD, 'thread_id') thread!: Relation<ThreadModel>;
@immutableRelation(TEAM, 'team_id') team!: Relation<TeamModel>;

View File

@@ -47,6 +47,7 @@ describe('*** Operator: Thread Handlers tests ***', () => {
is_following: true,
unread_replies: 0,
unread_mentions: 0,
loaded_in_global_threads: false,
},
] as Thread[];
@@ -126,6 +127,7 @@ describe('*** Operator: Thread Handlers tests ***', () => {
is_following: true,
unread_replies: 0,
unread_mentions: 0,
loaded_in_global_threads: true,
},
{
id: 'thread-2',
@@ -138,6 +140,7 @@ describe('*** Operator: Thread Handlers tests ***', () => {
is_following: true,
unread_replies: 0,
unread_mentions: 0,
loaded_in_global_threads: true,
},
] as Thread[];
@@ -151,8 +154,9 @@ describe('*** Operator: Thread Handlers tests ***', () => {
id: 'user-1',
}],
is_following: true,
unread_replies: 0,
unread_replies: 2,
unread_mentions: 0,
loaded_in_global_threads: false,
},
] as Thread[];
@@ -165,12 +169,16 @@ describe('*** Operator: Thread Handlers tests ***', () => {
expect(spyOnPrepareRecords).toHaveBeenCalledWith({
createRaws: [{
raw: {team_id: 'team_id_1', thread_id: 'thread-2'},
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'},
raw: {team_id: 'team_id_2', thread_id: 'thread-2', loaded_in_global_threads: false},
}],
transformer: transformThreadInTeamRecord,
updateRaws: [],
updateRaws: [
expect.objectContaining({
raw: {team_id: 'team_id_1', thread_id: 'thread-1', loaded_in_global_threads: true},
}),
],
tableName: 'ThreadsInTeam',
});
});

View File

@@ -5,9 +5,9 @@ import {Q, Database} from '@nozbe/watermelondb';
import {MM_TABLES} from '@constants/database';
import {transformThreadInTeamRecord} from '@database/operator/server_data_operator/transformers/thread';
import {getRawRecordPairs} from '@database/operator/utils/general';
import {getRawRecordPairs, getValidRecordsForUpdate} from '@database/operator/utils/general';
import type {HandleThreadInTeamArgs} from '@typings/database/database';
import type {HandleThreadInTeamArgs, RecordPair} from '@typings/database/database';
import type ThreadInTeamModel from '@typings/database/models/servers/thread_in_team';
export interface ThreadInTeamHandlerMix {
@@ -22,6 +22,7 @@ const ThreadInTeamHandler = (superclass: any) => class extends superclass {
return [];
}
const update: RecordPair[] = [];
const create: ThreadInTeam[] = [];
const teamIds = Object.keys(threadsMap);
for await (const teamId of teamIds) {
@@ -30,23 +31,33 @@ const ThreadInTeamHandler = (superclass: any) => class extends superclass {
).fetch();
for (const thread of threadsMap[teamId]) {
const exists = chunks.some((threadInTeam) => {
const chunk = chunks.find((threadInTeam) => {
return threadInTeam.threadId === thread.id;
});
if (!exists) {
const newValue = {
thread_id: thread.id,
team_id: teamId,
loaded_in_global_threads: thread.loaded_in_global_threads,
};
// update record if loaded_in_global_threads is true
if (chunk && thread.loaded_in_global_threads) {
update.push(getValidRecordsForUpdate({
tableName: THREADS_IN_TEAM,
newValue,
existingRecord: chunk,
}));
} else {
// create chunk
create.push({
thread_id: thread.id,
team_id: teamId,
});
create.push(newValue);
}
}
}
const threadsInTeam = (await this.prepareRecords({
createRaws: getRawRecordPairs(create),
updateRaws: [],
updateRaws: update,
transformer: transformThreadInTeamRecord,
tableName: THREADS_IN_TEAM,
})) as ThreadInTeamModel[];

View File

@@ -37,7 +37,6 @@ export const transformThreadRecord = ({action, database, value}: TransformerArgs
thread.isFollowing = raw.is_following ?? record?.isFollowing;
thread.unreadReplies = raw.unread_replies;
thread.unreadMentions = raw.unread_mentions;
thread.loadedInGlobalThreads = raw.loaded_in_global_threads || record?.loadedInGlobalThreads;
};
return prepareBaseRecord({
@@ -76,10 +75,14 @@ export const transformThreadParticipantRecord = ({action, database, value}: Tran
export const transformThreadInTeamRecord = ({action, database, value}: TransformerArgs): Promise<ThreadInTeamModel> => {
const raw = value.raw as ThreadInTeam;
const record = value.record as ThreadInTeamModel;
const fieldsMapper = (threadInTeam: ThreadInTeamModel) => {
threadInTeam.threadId = raw.thread_id;
threadInTeam.teamId = raw.team_id;
// if it's already loaded don't change it
threadInTeam.loadedInGlobalThreads = record?.loadedInGlobalThreads || raw.loaded_in_global_threads;
};
return prepareBaseRecord({

View File

@@ -16,6 +16,5 @@ export default tableSchema({
{name: 'reply_count', type: 'number'},
{name: 'unread_replies', type: 'number'},
{name: 'unread_mentions', type: 'number'},
{name: 'loaded_in_global_threads', type: 'boolean'},
],
});

View File

@@ -12,5 +12,6 @@ export default tableSchema({
columns: [
{name: 'team_id', type: 'string', isIndexed: true},
{name: 'thread_id', type: 'string', isIndexed: true},
{name: 'loaded_in_global_threads', type: 'boolean', isIndexed: true},
],
});

View File

@@ -472,7 +472,6 @@ describe('*** Test schema for SERVER database ***', () => {
reply_count: {name: 'reply_count', type: 'number'},
unread_replies: {name: 'unread_replies', type: 'number'},
unread_mentions: {name: 'unread_mentions', type: 'number'},
loaded_in_global_threads: {name: 'loaded_in_global_threads', type: 'boolean'},
},
columnArray: [
{name: 'last_reply_at', type: 'number'},
@@ -481,7 +480,6 @@ describe('*** Test schema for SERVER database ***', () => {
{name: 'reply_count', type: 'number'},
{name: 'unread_replies', type: 'number'},
{name: 'unread_mentions', type: 'number'},
{name: 'loaded_in_global_threads', type: 'boolean'},
],
},
[THREAD_PARTICIPANT]: {
@@ -502,10 +500,12 @@ describe('*** Test schema for SERVER database ***', () => {
columns: {
team_id: {name: 'team_id', type: 'string', isIndexed: true},
thread_id: {name: 'thread_id', type: 'string', isIndexed: true},
loaded_in_global_threads: {name: 'loaded_in_global_threads', type: 'boolean', isIndexed: true},
},
columnArray: [
{name: 'team_id', type: 'string', isIndexed: true},
{name: 'thread_id', type: 'string', isIndexed: true},
{name: 'loaded_in_global_threads', type: 'boolean', isIndexed: true},
],
},
[USER]: {

View File

@@ -35,9 +35,6 @@ export default class ThreadModel extends Model {
/** unread_mentions : The number of mentions that are not read by the user. */
unreadMentions: number;
/** loaded_in_global_threads : Flag to differentiate the unread threads loaded for showing unread counts/mentions */
loadedInGlobalThreads: boolean;
/** participants: All the participants of the thread */
participants: Query<ThreadParticipantsModel>;

View File

@@ -25,6 +25,9 @@ export default class ThreadInTeamModel extends Model {
/** teamId: Associated thread identifier */
teamId: string;
/** loaded_in_global_threads : Flag to differentiate the unread threads loaded for showing unread counts/mentions */
loadedInGlobalThreads: boolean;
/** thread : The related record to the parent Thread model */
thread: Relation<ThreadModel>;

View File

@@ -85,6 +85,7 @@ type TermsOfService = {
type ThreadInTeam = {
thread_id: string;
team_id: string;
loaded_in_global_threads: boolean;
};
type RawValue =