[Gekidou DB]: Adds threads in team database table and handlers (#6090)
* Adds threads in team database table and handlers DM/GM channels have no team ID. This makes it troublesome to paginate threads in a team. The issue is that whenever a DM/GM thread is fetched from pagination it will be added in all teams in that server, potentially creating gaps in between threads for those teams. This PR inserts a new table in the DB ThreadsInTeam which will hold references of threads loaded in which server. Thread lists then would have to rely on that table to show threads. Co-authored-by: Elias Nahum <nahumhbl@gmail.com> Co-authored-by: Avinash Lingaloo <avinashlng1080@gmail.com>
This commit is contained in:
parent
86ae1fc9cc
commit
f2484297a8
19 changed files with 331 additions and 12 deletions
|
|
@ -35,6 +35,7 @@ export const MM_TABLES = {
|
|||
TEAM_SEARCH_HISTORY: 'TeamSearchHistory',
|
||||
TERMS_OF_SERVICE: 'TermsOfService',
|
||||
THREAD: 'Thread',
|
||||
THREADS_IN_TEAM: 'ThreadsInTeam',
|
||||
THREAD_PARTICIPANT: 'ThreadParticipant',
|
||||
USER: 'User',
|
||||
},
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ import {CategoryModel, CategoryChannelModel, ChannelModel, ChannelInfoModel, Cha
|
|||
MyChannelModel, MyChannelSettingsModel, MyTeamModel,
|
||||
PostModel, PostsInChannelModel, PostsInThreadModel, PreferenceModel, ReactionModel, RoleModel,
|
||||
SlashCommandModel, SystemModel, TeamModel, TeamChannelHistoryModel, TeamMembershipModel, TeamSearchHistoryModel,
|
||||
TermsOfServiceModel, ThreadModel, ThreadParticipantModel, UserModel,
|
||||
TermsOfServiceModel, ThreadModel, ThreadParticipantModel, ThreadInTeamModel, UserModel,
|
||||
} from '@database/models/server';
|
||||
import AppDataOperator from '@database/operator/app_data_operator';
|
||||
import ServerDataOperator from '@database/operator/server_data_operator';
|
||||
|
|
@ -51,7 +51,7 @@ class DatabaseManager {
|
|||
MyChannelModel, MyChannelSettingsModel, MyTeamModel,
|
||||
PostModel, PostsInChannelModel, PostsInThreadModel, PreferenceModel, ReactionModel, RoleModel,
|
||||
SlashCommandModel, SystemModel, TeamModel, TeamChannelHistoryModel, TeamMembershipModel, TeamSearchHistoryModel,
|
||||
TermsOfServiceModel, ThreadModel, ThreadParticipantModel, UserModel,
|
||||
TermsOfServiceModel, ThreadModel, ThreadParticipantModel, ThreadInTeamModel, UserModel,
|
||||
];
|
||||
this.databaseDirectory = '';
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ import {CategoryModel, CategoryChannelModel, ChannelModel, ChannelInfoModel, Cha
|
|||
MyChannelModel, MyChannelSettingsModel, MyTeamModel,
|
||||
PostModel, PostsInChannelModel, PostsInThreadModel, PreferenceModel, ReactionModel, RoleModel,
|
||||
SlashCommandModel, SystemModel, TeamModel, TeamChannelHistoryModel, TeamMembershipModel, TeamSearchHistoryModel,
|
||||
TermsOfServiceModel, ThreadModel, ThreadParticipantModel, UserModel,
|
||||
TermsOfServiceModel, ThreadModel, ThreadParticipantModel, ThreadInTeamModel, UserModel,
|
||||
} from '@database/models/server';
|
||||
import AppDataOperator from '@database/operator/app_data_operator';
|
||||
import ServerDataOperator from '@database/operator/server_data_operator';
|
||||
|
|
@ -46,7 +46,7 @@ class DatabaseManager {
|
|||
MyChannelModel, MyChannelSettingsModel, MyTeamModel,
|
||||
PostModel, PostsInChannelModel, PostsInThreadModel, PreferenceModel, ReactionModel, RoleModel,
|
||||
SlashCommandModel, SystemModel, TeamModel, TeamChannelHistoryModel, TeamMembershipModel, TeamSearchHistoryModel,
|
||||
TermsOfServiceModel, ThreadModel, ThreadParticipantModel, UserModel,
|
||||
TermsOfServiceModel, ThreadModel, ThreadParticipantModel, ThreadInTeamModel, UserModel,
|
||||
];
|
||||
|
||||
this.databaseDirectory = Platform.OS === 'ios' ? getIOSAppGroupDetails().appGroupDatabase : `${FileSystem.documentDirectory}databases/`;
|
||||
|
|
|
|||
|
|
@ -26,5 +26,6 @@ export {default as TeamModel} from './team';
|
|||
export {default as TeamSearchHistoryModel} from './team_search_history';
|
||||
export {default as TermsOfServiceModel} from './terms_of_service';
|
||||
export {default as ThreadModel} from './thread';
|
||||
export {default as ThreadInTeamModel} from './thread_in_team';
|
||||
export {default as ThreadParticipantModel} from './thread_participant';
|
||||
export {default as UserModel} from './user';
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {Relation} from '@nozbe/watermelondb';
|
||||
import {children, field, immutableRelation} from '@nozbe/watermelondb/decorators';
|
||||
import {Q, Relation} from '@nozbe/watermelondb';
|
||||
import {children, field, immutableRelation, lazy} from '@nozbe/watermelondb/decorators';
|
||||
import Model, {Associations} from '@nozbe/watermelondb/Model';
|
||||
|
||||
import {MM_TABLES} from '@constants/database';
|
||||
|
|
@ -14,6 +14,7 @@ import type SlashCommandModel from '@typings/database/models/servers/slash_comma
|
|||
import type TeamChannelHistoryModel from '@typings/database/models/servers/team_channel_history';
|
||||
import type TeamMembershipModel from '@typings/database/models/servers/team_membership';
|
||||
import type TeamSearchHistoryModel from '@typings/database/models/servers/team_search_history';
|
||||
import type ThreadModel from '@typings/database/models/servers/thread';
|
||||
|
||||
const {
|
||||
CATEGORY,
|
||||
|
|
@ -24,6 +25,8 @@ const {
|
|||
TEAM_CHANNEL_HISTORY,
|
||||
TEAM_MEMBERSHIP,
|
||||
TEAM_SEARCH_HISTORY,
|
||||
THREADS_IN_TEAM,
|
||||
THREAD,
|
||||
} = MM_TABLES.SERVER;
|
||||
|
||||
/**
|
||||
|
|
@ -53,6 +56,9 @@ export default class TeamModel extends Model {
|
|||
|
||||
/** A TEAM has a 1:N relationship with TEAM_SEARCH_HISTORY. A TEAM can possess multiple search histories*/
|
||||
[TEAM_SEARCH_HISTORY]: {type: 'has_many', foreignKey: 'team_id'},
|
||||
|
||||
/** A TEAM has a 1:N relationship with THREADS_IN_TEAM. A TEAM can possess multiple threads */
|
||||
[THREADS_IN_TEAM]: {type: 'has_many', foreignKey: 'team_id'},
|
||||
};
|
||||
|
||||
/** is_allow_open_invite : Boolean flag indicating if this team is open to the public */
|
||||
|
|
@ -102,4 +108,21 @@ export default class TeamModel extends Model {
|
|||
|
||||
/** teamSearchHistories : All the searches performed on this team */
|
||||
@children(TEAM_SEARCH_HISTORY) teamSearchHistories!: 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),
|
||||
Q.sortBy('last_reply_at', Q.desc),
|
||||
);
|
||||
|
||||
/** unreadThreadsList : Unread threads list belonging to a team */
|
||||
@lazy unreadThreadsList = this.threads.extend(
|
||||
Q.where('unread_replies', Q.gt(0)),
|
||||
Q.sortBy('last_reply_at', Q.desc),
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,9 +8,10 @@ import Model, {Associations} from '@nozbe/watermelondb/Model';
|
|||
import {MM_TABLES} from '@constants/database';
|
||||
|
||||
import type PostModel from '@typings/database/models/servers/post';
|
||||
import type ThreadInTeamModel from '@typings/database/models/servers/thread_in_team';
|
||||
import type ThreadParticipantModel from '@typings/database/models/servers/thread_participant';
|
||||
|
||||
const {POST, THREAD, THREAD_PARTICIPANT} = MM_TABLES.SERVER;
|
||||
const {POST, THREAD, THREAD_PARTICIPANT, THREADS_IN_TEAM} = MM_TABLES.SERVER;
|
||||
|
||||
/**
|
||||
* The Thread model contains thread information of a post.
|
||||
|
|
@ -27,6 +28,9 @@ export default class ThreadModel extends Model {
|
|||
|
||||
/** A THREAD can have multiple THREAD_PARTICIPANT. (relationship is 1:N)*/
|
||||
[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'},
|
||||
};
|
||||
|
||||
/** last_reply_at : The timestamp of when user last replied to the thread. */
|
||||
|
|
@ -53,11 +57,15 @@ export default class ThreadModel extends Model {
|
|||
/** participants : All the participants associated with this Thread */
|
||||
@children(THREAD_PARTICIPANT) participants!: Query<ThreadParticipantModel>;
|
||||
|
||||
/** threadsInTeam : All the threadsInTeam associated with this Thread */
|
||||
@children(THREADS_IN_TEAM) threadsInTeam!: Query<ThreadInTeamModel>;
|
||||
|
||||
/** post : The root post of this thread */
|
||||
@immutableRelation(POST, 'id') post!: Relation<PostModel>;
|
||||
|
||||
async destroyPermanently() {
|
||||
await this.participants.destroyAllPermanently();
|
||||
await this.threadsInTeam.destroyAllPermanently();
|
||||
super.destroyPermanently();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
42
app/database/models/server/thread_in_team.ts
Normal file
42
app/database/models/server/thread_in_team.ts
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {Relation} from '@nozbe/watermelondb';
|
||||
import {field, immutableRelation} from '@nozbe/watermelondb/decorators';
|
||||
import Model, {Associations} from '@nozbe/watermelondb/Model';
|
||||
|
||||
import {MM_TABLES} from '@constants/database';
|
||||
|
||||
import type TeamModel from '@typings/database/models/servers/team';
|
||||
import type ThreadModel from '@typings/database/models/servers/thread';
|
||||
|
||||
const {TEAM, THREAD, THREADS_IN_TEAM} = MM_TABLES.SERVER;
|
||||
|
||||
/**
|
||||
* ThreadInTeam model helps us to combine adjacent threads together without leaving
|
||||
* gaps in between for an efficient user reading experience for threads.
|
||||
*/
|
||||
export default class ThreadInTeamModel extends Model {
|
||||
/** table (name) : ThreadsInTeam */
|
||||
static table = THREADS_IN_TEAM;
|
||||
|
||||
/** associations : Describes every relationship to this table. */
|
||||
static associations: Associations = {
|
||||
|
||||
/** A TEAM can have many THREADS_IN_TEAM. (relationship is N:1)*/
|
||||
[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_id: Associated thread identifier */
|
||||
@field('thread_id') threadId!: string;
|
||||
|
||||
/** team_id: Associated team identifier */
|
||||
@field('team_id') teamId!: string;
|
||||
|
||||
@immutableRelation(THREAD, 'thread_id') thread!: Relation<ThreadModel>;
|
||||
|
||||
@immutableRelation(TEAM, 'team_id') team!: Relation<TeamModel>;
|
||||
}
|
||||
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
import DatabaseManager from '@database/manager';
|
||||
import {isRecordThreadEqualToRaw} from '@database/operator/server_data_operator/comparators';
|
||||
import {transformThreadRecord, transformThreadParticipantRecord} from '@database/operator/server_data_operator/transformers/thread';
|
||||
import {transformThreadRecord, transformThreadParticipantRecord, transformThreadInTeamRecord} from '@database/operator/server_data_operator/transformers/thread';
|
||||
|
||||
import ServerDataOperator from '..';
|
||||
|
||||
|
|
@ -27,10 +27,13 @@ describe('*** Operator: Thread Handlers tests ***', () => {
|
|||
operator = DatabaseManager.serverDatabases['baseHandler.test.com'].operator;
|
||||
});
|
||||
|
||||
it('=> HandleThreads: should write to the the Thread & ThreadParticipant table', async () => {
|
||||
it('=> HandleThreads: should write to the the Thread & ThreadParticipant & ThreadsInTeam tables', async () => {
|
||||
expect.assertions(4);
|
||||
|
||||
const spyOnBatchOperation = jest.spyOn(operator, 'batchRecords');
|
||||
const spyOnHandleRecords = jest.spyOn(operator, 'handleRecords');
|
||||
const spyOnHandleThreadParticipants = jest.spyOn(operator, 'handleThreadParticipants');
|
||||
const spyOnHandleThreadInTeam = jest.spyOn(operator, 'handleThreadInTeam');
|
||||
|
||||
const threads = [
|
||||
{
|
||||
|
|
@ -47,7 +50,8 @@ describe('*** Operator: Thread Handlers tests ***', () => {
|
|||
},
|
||||
] as Thread[];
|
||||
|
||||
await operator.handleThreads({threads, prepareRecordsOnly: false});
|
||||
const threadsMap = {team_id_1: threads};
|
||||
await operator.handleThreads({threads, prepareRecordsOnly: false, teamId: 'team_id_1'});
|
||||
|
||||
expect(spyOnHandleRecords).toHaveBeenCalledWith({
|
||||
findMatchingRecordBy: isRecordThreadEqualToRaw,
|
||||
|
|
@ -70,11 +74,18 @@ describe('*** Operator: Thread Handlers tests ***', () => {
|
|||
prepareRecordsOnly: true,
|
||||
});
|
||||
|
||||
expect(spyOnHandleThreadInTeam).toHaveBeenCalledWith({
|
||||
threadsMap,
|
||||
prepareRecordsOnly: true,
|
||||
});
|
||||
|
||||
// Only one batch operation for both tables
|
||||
expect(spyOnBatchOperation).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('=> HandleThreadParticipants: should write to the the ThreadParticipant table', async () => {
|
||||
expect.assertions(1);
|
||||
|
||||
const spyOnPrepareRecords = jest.spyOn(operator, 'prepareRecords');
|
||||
|
||||
const threadsParticipants = [
|
||||
|
|
@ -97,4 +108,70 @@ describe('*** Operator: Thread Handlers tests ***', () => {
|
|||
tableName: 'ThreadParticipant',
|
||||
});
|
||||
});
|
||||
|
||||
it('=> HandleThreadInTeam: should write to the the ThreadsInTeam table', async () => {
|
||||
expect.assertions(1);
|
||||
|
||||
const spyOnPrepareRecords = jest.spyOn(operator, 'prepareRecords');
|
||||
|
||||
const team1Threads = [
|
||||
{
|
||||
id: 'thread-1',
|
||||
reply_count: 2,
|
||||
last_reply_at: 123,
|
||||
last_viewed_at: 123,
|
||||
participants: [{
|
||||
id: 'user-1',
|
||||
}],
|
||||
is_following: true,
|
||||
unread_replies: 0,
|
||||
unread_mentions: 0,
|
||||
},
|
||||
{
|
||||
id: 'thread-2',
|
||||
reply_count: 2,
|
||||
last_reply_at: 123,
|
||||
last_viewed_at: 123,
|
||||
participants: [{
|
||||
id: 'user-1',
|
||||
}],
|
||||
is_following: true,
|
||||
unread_replies: 0,
|
||||
unread_mentions: 0,
|
||||
},
|
||||
] as Thread[];
|
||||
|
||||
const team2Threads = [
|
||||
{
|
||||
id: 'thread-2',
|
||||
reply_count: 2,
|
||||
last_reply_at: 123,
|
||||
last_viewed_at: 123,
|
||||
participants: [{
|
||||
id: 'user-1',
|
||||
}],
|
||||
is_following: true,
|
||||
unread_replies: 0,
|
||||
unread_mentions: 0,
|
||||
},
|
||||
] as Thread[];
|
||||
|
||||
const threadsMap = {
|
||||
team_id_1: team1Threads,
|
||||
team_id_2: team2Threads,
|
||||
};
|
||||
|
||||
await operator.handleThreadInTeam({threadsMap, prepareRecordsOnly: false});
|
||||
|
||||
expect(spyOnPrepareRecords).toHaveBeenCalledWith({
|
||||
createRaws: [{
|
||||
raw: {team_id: 'team_id_1', thread_id: 'thread-2'},
|
||||
}, {
|
||||
raw: {team_id: 'team_id_2', thread_id: 'thread-2'},
|
||||
}],
|
||||
transformer: transformThreadInTeamRecord,
|
||||
updateRaws: [],
|
||||
tableName: 'ThreadsInTeam',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ import {sanitizeThreadParticipants} from '@database/operator/utils/thread';
|
|||
|
||||
import type {HandleThreadsArgs, HandleThreadParticipantsArgs} from '@typings/database/database';
|
||||
import type ThreadModel from '@typings/database/models/servers/thread';
|
||||
import type ThreadInTeamModel from '@typings/database/models/servers/thread_in_team';
|
||||
import type ThreadParticipantModel from '@typings/database/models/servers/thread_participant';
|
||||
|
||||
const {
|
||||
|
|
@ -35,7 +36,7 @@ const ThreadHandler = (superclass: any) => class extends superclass {
|
|||
* @param {boolean | undefined} handleThreads.prepareRecordsOnly
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
handleThreads = async ({threads, prepareRecordsOnly = false}: HandleThreadsArgs): Promise<Model[]> => {
|
||||
handleThreads = async ({threads, teamId, prepareRecordsOnly = false}: HandleThreadsArgs): Promise<Model[]> => {
|
||||
if (!threads.length) {
|
||||
throw new DataOperatorException(
|
||||
'An empty "threads" array has been passed to the handleThreads method',
|
||||
|
|
@ -78,6 +79,12 @@ const ThreadHandler = (superclass: any) => class extends superclass {
|
|||
const threadParticipants = (await this.handleThreadParticipants({threadsParticipants, prepareRecordsOnly: true})) as ThreadParticipantModel[];
|
||||
batch.push(...threadParticipants);
|
||||
|
||||
const threadsInTeam = await this.handleThreadInTeam({
|
||||
threadsMap: {[teamId]: threads},
|
||||
prepareRecordsOnly: true,
|
||||
}) as ThreadInTeamModel[];
|
||||
batch.push(...threadsInTeam);
|
||||
|
||||
if (batch.length && !prepareRecordsOnly) {
|
||||
await this.batchRecords(batch);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,62 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
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 type {HandleThreadInTeamArgs} from '@typings/database/database';
|
||||
import type ThreadInTeamModel from '@typings/database/models/servers/thread_in_team';
|
||||
|
||||
export interface ThreadInTeamHandlerMix {
|
||||
handleThreadInTeam: ({threadsMap, prepareRecordsOnly}: HandleThreadInTeamArgs) => Promise<ThreadInTeamModel[]>;
|
||||
}
|
||||
|
||||
const {THREADS_IN_TEAM} = MM_TABLES.SERVER;
|
||||
|
||||
const ThreadInTeamHandler = (superclass: any) => class extends superclass {
|
||||
handleThreadInTeam = async ({threadsMap, prepareRecordsOnly = false}: HandleThreadInTeamArgs): Promise<ThreadInTeamModel[]> => {
|
||||
if (!Object.keys(threadsMap).length) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const create: ThreadInTeam[] = [];
|
||||
const teamIds = Object.keys(threadsMap);
|
||||
for await (const teamId of teamIds) {
|
||||
const chunks = await (this.database as Database).get<ThreadInTeamModel>(THREADS_IN_TEAM).query(
|
||||
Q.where('team_id', teamId),
|
||||
).fetch();
|
||||
|
||||
for (const thread of threadsMap[teamId]) {
|
||||
const exists = chunks.some((threadInTeam) => {
|
||||
return threadInTeam.threadId === thread.id;
|
||||
});
|
||||
|
||||
if (!exists) {
|
||||
// create chunk
|
||||
create.push({
|
||||
thread_id: thread.id,
|
||||
team_id: teamId,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const threadsInTeam = (await this.prepareRecords({
|
||||
createRaws: getRawRecordPairs(create),
|
||||
updateRaws: [],
|
||||
transformer: transformThreadInTeamRecord,
|
||||
tableName: THREADS_IN_TEAM,
|
||||
})) as ThreadInTeamModel[];
|
||||
|
||||
if (threadsInTeam?.length && !prepareRecordsOnly) {
|
||||
await this.batchRecords(threadsInTeam);
|
||||
}
|
||||
|
||||
return threadsInTeam;
|
||||
};
|
||||
};
|
||||
|
||||
export default ThreadInTeamHandler;
|
||||
|
|
@ -10,13 +10,14 @@ import PostsInThreadHandler, {PostsInThreadHandlerMix} from '@database/operator/
|
|||
import ReactionHander, {ReactionHandlerMix} from '@database/operator/server_data_operator/handlers/reaction';
|
||||
import TeamHandler, {TeamHandlerMix} from '@database/operator/server_data_operator/handlers/team';
|
||||
import ThreadHandler, {ThreadHandlerMix} from '@database/operator/server_data_operator/handlers/thread';
|
||||
import ThreadInTeamHandler, {ThreadInTeamHandlerMix} from '@database/operator/server_data_operator/handlers/thread_in_team';
|
||||
import UserHandler, {UserHandlerMix} from '@database/operator/server_data_operator/handlers/user';
|
||||
import mix from '@utils/mix';
|
||||
|
||||
import type {Database} from '@nozbe/watermelondb';
|
||||
|
||||
interface ServerDataOperator extends ServerDataOperatorBase, PostHandlerMix, PostsInChannelHandlerMix,
|
||||
PostsInThreadHandlerMix, ReactionHandlerMix, UserHandlerMix, ChannelHandlerMix, CategoryHandlerMix, TeamHandlerMix, ThreadHandlerMix {}
|
||||
PostsInThreadHandlerMix, ReactionHandlerMix, UserHandlerMix, ChannelHandlerMix, CategoryHandlerMix, TeamHandlerMix, ThreadHandlerMix, ThreadInTeamHandlerMix {}
|
||||
|
||||
class ServerDataOperator extends mix(ServerDataOperatorBase).with(
|
||||
CategoryHandler,
|
||||
|
|
@ -27,6 +28,7 @@ class ServerDataOperator extends mix(ServerDataOperatorBase).with(
|
|||
ReactionHander,
|
||||
TeamHandler,
|
||||
ThreadHandler,
|
||||
ThreadInTeamHandler,
|
||||
UserHandler,
|
||||
) {
|
||||
// eslint-disable-next-line no-useless-constructor
|
||||
|
|
|
|||
|
|
@ -7,11 +7,13 @@ import {OperationType} from '@typings/database/enums';
|
|||
|
||||
import type {TransformerArgs} from '@typings/database/database';
|
||||
import type ThreadModel from '@typings/database/models/servers/thread';
|
||||
import type ThreadInTeamModel from '@typings/database/models/servers/thread_in_team';
|
||||
import type ThreadParticipantModel from '@typings/database/models/servers/thread_participant';
|
||||
|
||||
const {
|
||||
THREAD,
|
||||
THREAD_PARTICIPANT,
|
||||
THREADS_IN_TEAM,
|
||||
} = MM_TABLES.SERVER;
|
||||
|
||||
/**
|
||||
|
|
@ -71,3 +73,20 @@ export const transformThreadParticipantRecord = ({action, database, value}: Tran
|
|||
fieldsMapper,
|
||||
}) as Promise<ThreadParticipantModel>;
|
||||
};
|
||||
|
||||
export const transformThreadInTeamRecord = ({action, database, value}: TransformerArgs): Promise<ThreadInTeamModel> => {
|
||||
const raw = value.raw as ThreadInTeam;
|
||||
|
||||
const fieldsMapper = (threadInTeam: ThreadInTeamModel) => {
|
||||
threadInTeam.threadId = raw.thread_id;
|
||||
threadInTeam.teamId = raw.team_id;
|
||||
};
|
||||
|
||||
return prepareBaseRecord({
|
||||
action,
|
||||
database,
|
||||
tableName: THREADS_IN_TEAM,
|
||||
value,
|
||||
fieldsMapper,
|
||||
}) as Promise<ThreadInTeamModel>;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ import {
|
|||
TeamSearchHistorySchema,
|
||||
TermsOfServiceSchema,
|
||||
ThreadSchema,
|
||||
ThreadInTeamSchema,
|
||||
ThreadParticipantSchema,
|
||||
UserSchema,
|
||||
} from './table_schemas';
|
||||
|
|
@ -61,6 +62,7 @@ export const serverSchema: AppSchema = appSchema({
|
|||
TeamSearchHistorySchema,
|
||||
TermsOfServiceSchema,
|
||||
ThreadSchema,
|
||||
ThreadInTeamSchema,
|
||||
ThreadParticipantSchema,
|
||||
UserSchema,
|
||||
],
|
||||
|
|
|
|||
|
|
@ -27,4 +27,5 @@ export {default as TeamSearchHistorySchema} from './team_search_history';
|
|||
export {default as TermsOfServiceSchema} from './terms_of_service';
|
||||
export {default as ThreadSchema} from './thread';
|
||||
export {default as ThreadParticipantSchema} from './thread_participant';
|
||||
export {default as ThreadInTeamSchema} from './thread_in_team';
|
||||
export {default as UserSchema} from './user';
|
||||
|
|
|
|||
16
app/database/schema/server/table_schemas/thread_in_team.ts
Normal file
16
app/database/schema/server/table_schemas/thread_in_team.ts
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {tableSchema} from '@nozbe/watermelondb';
|
||||
|
||||
import {MM_TABLES} from '@constants/database';
|
||||
|
||||
const {THREADS_IN_TEAM} = MM_TABLES.SERVER;
|
||||
|
||||
export default tableSchema({
|
||||
name: THREADS_IN_TEAM,
|
||||
columns: [
|
||||
{name: 'team_id', type: 'string', isIndexed: true},
|
||||
{name: 'thread_id', type: 'string', isIndexed: true},
|
||||
],
|
||||
});
|
||||
|
|
@ -34,6 +34,7 @@ const {
|
|||
TERMS_OF_SERVICE,
|
||||
THREAD,
|
||||
THREAD_PARTICIPANT,
|
||||
THREADS_IN_TEAM,
|
||||
USER,
|
||||
} = MM_TABLES.SERVER;
|
||||
|
||||
|
|
@ -495,6 +496,18 @@ describe('*** Test schema for SERVER database ***', () => {
|
|||
{name: 'user_id', type: 'string', isIndexed: true},
|
||||
],
|
||||
},
|
||||
[THREADS_IN_TEAM]: {
|
||||
name: THREADS_IN_TEAM,
|
||||
unsafeSql: undefined,
|
||||
columns: {
|
||||
team_id: {name: 'team_id', type: 'string', isIndexed: true},
|
||||
thread_id: {name: 'thread_id', type: 'string', isIndexed: true},
|
||||
},
|
||||
columnArray: [
|
||||
{name: 'team_id', type: 'string', isIndexed: true},
|
||||
{name: 'thread_id', type: 'string', isIndexed: true},
|
||||
],
|
||||
},
|
||||
[USER]: {
|
||||
name: USER,
|
||||
unsafeSql: undefined,
|
||||
|
|
|
|||
6
types/database/database.d.ts
vendored
6
types/database/database.d.ts
vendored
|
|
@ -91,6 +91,7 @@ export type HandlePostsArgs = {
|
|||
export type HandleThreadsArgs = {
|
||||
threads: Thread[];
|
||||
prepareRecordsOnly?: boolean;
|
||||
teamId: string;
|
||||
};
|
||||
|
||||
export type HandleThreadParticipantsArgs = {
|
||||
|
|
@ -98,6 +99,11 @@ export type HandleThreadParticipantsArgs = {
|
|||
threadsParticipants: ParticipantsPerThread[];
|
||||
};
|
||||
|
||||
export type HandleThreadInTeamArgs = {
|
||||
threadsMap: Record<string, Thread[]>;
|
||||
prepareRecordsOnly?: boolean;
|
||||
};
|
||||
|
||||
export type SanitizeReactionsArgs = {
|
||||
database: Database;
|
||||
post_id: string;
|
||||
|
|
|
|||
33
types/database/models/servers/thread_in_team.d.ts
vendored
Normal file
33
types/database/models/servers/thread_in_team.d.ts
vendored
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {Relation} from '@nozbe/watermelondb';
|
||||
import Model, {Associations} from '@nozbe/watermelondb/Model';
|
||||
|
||||
import type TeamModel from './team';
|
||||
import type ThreadModel from './thread';
|
||||
|
||||
/**
|
||||
* ThreadInTeam model helps us to determine for which team we have loaded which threads
|
||||
* this was done mostly because of threads belonging in DM/GM channels,
|
||||
* so they belong in multiple teams.
|
||||
*/
|
||||
export default class ThreadInTeamModel extends Model {
|
||||
/** table (name) : ThreadsInTeam */
|
||||
static table: string;
|
||||
|
||||
/** associations : Describes every relationship to this table. */
|
||||
static associations: Associations;
|
||||
|
||||
/** thread_id: Associated thread identifier */
|
||||
threadId: string;
|
||||
|
||||
/** teamId: Associated thread identifier */
|
||||
teamId: string;
|
||||
|
||||
/** thread : The related record to the parent Thread model */
|
||||
thread: Relation<ThreadModel>;
|
||||
|
||||
/** team : The related record to the parent Team model */
|
||||
team: Relation<TeamModel>;
|
||||
}
|
||||
6
types/database/raw_values.d.ts
vendored
6
types/database/raw_values.d.ts
vendored
|
|
@ -82,6 +82,11 @@ type TermsOfService = {
|
|||
text: string;
|
||||
};
|
||||
|
||||
type ThreadInTeam = {
|
||||
thread_id: string;
|
||||
team_id: string;
|
||||
};
|
||||
|
||||
type RawValue =
|
||||
| AppInfo
|
||||
| Category
|
||||
|
|
@ -109,6 +114,7 @@ type RawValue =
|
|||
| TeamSearchHistory
|
||||
| TermsOfService
|
||||
| Thread
|
||||
| ThreadInTeam
|
||||
| ThreadParticipant
|
||||
| UserProfile
|
||||
| Pick<ChannelMembership, 'channel_id' | 'user_id'>
|
||||
|
|
|
|||
Loading…
Reference in a new issue