forked from Ivasoft/mattermost-mobile
[Gekidou MM-39707] CRT DB (#5948)
* Database init * Naming fix * naming misc * Fix test * Added Thread Tab columns, Team Threads Count table and other changes * Test case fix * Test cases fix ...... AGAIN * TS fix * Removed loaded_in_all_threads_tab, loaded_in_unreads_tab * Removed TeamThreadsCount table, mention & message root counts & added loadedInGlobalThreads flag * Type changes, added delete thread with post * Removed unused type * Reverted relationshio of post with thread * Calling thread destroyPermanently from post * Removed unused table name variables * added THREAD constant table in post model and fixed a few comments * Misc typo fix and code clean up * Added test case and related to participant in user model * test cases fix Co-authored-by: Mattermod <mattermod@users.noreply.github.com> Co-authored-by: Elias Nahum <nahumhbl@gmail.com>
This commit is contained in:
committed by
GitHub
parent
e93c570562
commit
9dbdae22fd
@@ -36,7 +36,6 @@ export const sendAddToChannelEphemeralPost = async (serverUrl: string, user: Use
|
||||
pending_post_id: '',
|
||||
reply_count: 0,
|
||||
metadata: {},
|
||||
participants: null,
|
||||
root_id: postRootId,
|
||||
props: {
|
||||
username: user.username,
|
||||
|
||||
@@ -34,6 +34,8 @@ export const MM_TABLES = {
|
||||
TEAM_MEMBERSHIP: 'TeamMembership',
|
||||
TEAM_SEARCH_HISTORY: 'TeamSearchHistory',
|
||||
TERMS_OF_SERVICE: 'TermsOfService',
|
||||
THREAD: 'Thread',
|
||||
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, UserModel,
|
||||
TermsOfServiceModel, ThreadModel, ThreadParticipantModel, 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, UserModel,
|
||||
TermsOfServiceModel, ThreadModel, ThreadParticipantModel, 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, UserModel,
|
||||
TermsOfServiceModel, ThreadModel, ThreadParticipantModel, 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, UserModel,
|
||||
TermsOfServiceModel, ThreadModel, ThreadParticipantModel, UserModel,
|
||||
];
|
||||
|
||||
this.databaseDirectory = Platform.OS === 'ios' ? getIOSAppGroupDetails().appGroupDatabase : `${FileSystem.documentDirectory}databases/`;
|
||||
|
||||
@@ -25,4 +25,6 @@ export {default as TeamMembershipModel} from './team_membership';
|
||||
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 ThreadParticipantModel} from './thread_participant';
|
||||
export {default as UserModel} from './user';
|
||||
|
||||
@@ -13,9 +13,10 @@ import type DraftModel from '@typings/database/models/servers/draft';
|
||||
import type FileModel from '@typings/database/models/servers/file';
|
||||
import type PostInThreadModel from '@typings/database/models/servers/posts_in_thread';
|
||||
import type ReactionModel from '@typings/database/models/servers/reaction';
|
||||
import type ThreadModel from '@typings/database/models/servers/thread';
|
||||
import type UserModel from '@typings/database/models/servers/user';
|
||||
|
||||
const {CHANNEL, DRAFT, FILE, POST, POSTS_IN_THREAD, REACTION, USER} = MM_TABLES.SERVER;
|
||||
const {CHANNEL, DRAFT, FILE, POST, POSTS_IN_THREAD, REACTION, THREAD, USER} = MM_TABLES.SERVER;
|
||||
|
||||
/**
|
||||
* The Post model is the building block of communication in the Mattermost app.
|
||||
@@ -42,6 +43,9 @@ export default class PostModel extends Model {
|
||||
/** A POST can have multiple REACTION. (relationship is 1:N)*/
|
||||
[REACTION]: {type: 'has_many', foreignKey: 'post_id'},
|
||||
|
||||
/** A POST can have an associated thread. (relationship is 1:1) */
|
||||
[THREAD]: {type: 'has_many', foreignKey: 'id'},
|
||||
|
||||
/** A USER can have multiple POST. A user can author several posts. (relationship is 1:N)*/
|
||||
[USER]: {type: 'belongs_to', key: 'user_id'},
|
||||
};
|
||||
@@ -115,6 +119,9 @@ export default class PostModel extends Model {
|
||||
/** channel: The channel which is presenting this Post */
|
||||
@immutableRelation(CHANNEL, 'channel_id') channel!: Relation<ChannelModel>;
|
||||
|
||||
/** thread : The thread data for the post */
|
||||
@immutableRelation(THREAD, 'id') thread!: Relation<ThreadModel>;
|
||||
|
||||
async destroyPermanently() {
|
||||
await this.reactions.destroyAllPermanently();
|
||||
await this.files.destroyAllPermanently();
|
||||
@@ -122,6 +129,11 @@ export default class PostModel extends Model {
|
||||
await this.collections.get(POSTS_IN_THREAD).query(
|
||||
Q.where('root_id', this.id),
|
||||
).destroyAllPermanently();
|
||||
try {
|
||||
(await this.thread.fetch())?.destroyPermanently();
|
||||
} catch {
|
||||
// there is no thread record for this post
|
||||
}
|
||||
super.destroyPermanently();
|
||||
}
|
||||
|
||||
|
||||
63
app/database/models/server/thread.ts
Normal file
63
app/database/models/server/thread.ts
Normal file
@@ -0,0 +1,63 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {Query, Relation} from '@nozbe/watermelondb';
|
||||
import {children, field, immutableRelation} from '@nozbe/watermelondb/decorators';
|
||||
import Model, {Associations} from '@nozbe/watermelondb/Model';
|
||||
|
||||
import {MM_TABLES} from '@constants/database';
|
||||
|
||||
import type PostModel from '@typings/database/models/servers/post';
|
||||
import type ThreadParticipantModel from '@typings/database/models/servers/thread_participant';
|
||||
|
||||
const {POST, THREAD, THREAD_PARTICIPANT} = MM_TABLES.SERVER;
|
||||
|
||||
/**
|
||||
* The Thread model contains thread information of a post.
|
||||
*/
|
||||
export default class ThreadModel extends Model {
|
||||
/** table (name) : Thread */
|
||||
static table = THREAD;
|
||||
|
||||
/** associations : Describes every relationship to this table. */
|
||||
static associations: Associations = {
|
||||
|
||||
/** A THREAD is associated to one POST (relationship is 1:1) */
|
||||
[POST]: {type: 'belongs_to', key: 'id'},
|
||||
|
||||
/** A THREAD can have multiple THREAD_PARTICIPANT. (relationship is 1:N)*/
|
||||
[THREAD_PARTICIPANT]: {type: 'has_many', foreignKey: 'thread_id'},
|
||||
};
|
||||
|
||||
/** last_reply_at : The timestamp of when user last replied to the thread. */
|
||||
@field('last_reply_at') lastReplyAt!: number;
|
||||
|
||||
/** last_viewed_at : The timestamp of when user last viewed the thread. */
|
||||
@field('last_viewed_at') lastViewedAt!: number;
|
||||
|
||||
/** reply_count : The total replies to the thread by all the participants. */
|
||||
@field('reply_count') replyCount!: number;
|
||||
|
||||
/** is_following: If user is following the thread or not */
|
||||
@field('is_following') isFollowing!: boolean;
|
||||
|
||||
/** unread_replies : The number of replies that have not been read by the user. */
|
||||
@field('unread_replies') unreadReplies!: number;
|
||||
|
||||
/** 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>;
|
||||
|
||||
/** post : The root post of this thread */
|
||||
@immutableRelation(POST, 'id') post!: Relation<PostModel>;
|
||||
|
||||
async destroyPermanently() {
|
||||
await this.participants.destroyAllPermanently();
|
||||
super.destroyPermanently();
|
||||
}
|
||||
}
|
||||
43
app/database/models/server/thread_participant.ts
Normal file
43
app/database/models/server/thread_participant.ts
Normal file
@@ -0,0 +1,43 @@
|
||||
// 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 ThreadModel from '@typings/database/models/servers/thread';
|
||||
import type UserModel from '@typings/database/models/servers/user';
|
||||
|
||||
const {THREAD, THREAD_PARTICIPANT, USER} = MM_TABLES.SERVER;
|
||||
|
||||
/**
|
||||
* The Thread Participants model contains participants data of a thread.
|
||||
*/
|
||||
export default class ThreadParticipantModel extends Model {
|
||||
/** table (name) : ThreadParticipant */
|
||||
static table = THREAD_PARTICIPANT;
|
||||
|
||||
/** associations : Describes every relationship to this table. */
|
||||
static associations: Associations = {
|
||||
|
||||
/** A THREAD can have multiple PARTICIPANTS. (relationship is 1:N) */
|
||||
[THREAD]: {type: 'belongs_to', key: 'thread_id'},
|
||||
|
||||
/** A USER can participate in multiple THREADS. (relationship is 1:N) */
|
||||
[USER]: {type: 'belongs_to', key: 'user_id'},
|
||||
};
|
||||
|
||||
/** thread_id : thread id to which participant belong to. */
|
||||
@field('thread_id') threadId!: string;
|
||||
|
||||
/** user_id : user id of the participant. */
|
||||
@field('user_id') userId!: number;
|
||||
|
||||
/** thread : The related record of the Thread model */
|
||||
@immutableRelation(THREAD, 'thread_id') thread!: Relation<ThreadModel>;
|
||||
|
||||
/** user : The related record of the User model */
|
||||
@immutableRelation(USER, 'user_id') user!: Relation<UserModel>;
|
||||
}
|
||||
@@ -5,6 +5,7 @@ import {children, field, json} from '@nozbe/watermelondb/decorators';
|
||||
import Model, {Associations} from '@nozbe/watermelondb/Model';
|
||||
|
||||
import {MM_TABLES} from '@constants/database';
|
||||
import ThreadParticipantsModel from '@typings/database/models/servers/thread_participant';
|
||||
import {safeParseJSON} from '@utils/helpers';
|
||||
|
||||
import type ChannelModel from '@typings/database/models/servers/channel';
|
||||
@@ -22,6 +23,7 @@ const {
|
||||
PREFERENCE,
|
||||
REACTION,
|
||||
TEAM_MEMBERSHIP,
|
||||
THREAD_PARTICIPANT,
|
||||
USER,
|
||||
} = MM_TABLES.SERVER;
|
||||
|
||||
@@ -53,6 +55,9 @@ export default class UserModel extends Model {
|
||||
|
||||
/** USER has a 1:N relationship with TEAM_MEMBERSHIP. A user can join multiple teams */
|
||||
[TEAM_MEMBERSHIP]: {type: 'has_many', foreignKey: 'user_id'},
|
||||
|
||||
/** USER has a 1:N relationship with THREAD_PARTICIPANT. A user can participante in multiple threads */
|
||||
[THREAD_PARTICIPANT]: {type: 'has_many', foreignKey: 'user_id'},
|
||||
};
|
||||
|
||||
/** auth_service : The type of authentication service registered to that user */
|
||||
@@ -129,6 +134,9 @@ export default class UserModel extends Model {
|
||||
/** teams : All the team that this user is part of */
|
||||
@children(TEAM_MEMBERSHIP) teams!: TeamMembershipModel[];
|
||||
|
||||
/** threadParticipations : All the thread participations this user is part of */
|
||||
@children(THREAD_PARTICIPANT) threadParticipations!: ThreadParticipantsModel[];
|
||||
|
||||
prepareStatus = (status: string) => {
|
||||
this.prepareUpdate((u) => {
|
||||
u.status = status;
|
||||
|
||||
@@ -22,6 +22,7 @@ import type TeamChannelHistoryModel from '@typings/database/models/servers/team_
|
||||
import type TeamMembershipModel from '@typings/database/models/servers/team_membership';
|
||||
import type TeamSearchHistoryModel from '@typings/database/models/servers/team_search_history';
|
||||
import type TermsOfServiceModel from '@typings/database/models/servers/terms_of_service';
|
||||
import type ThreadModel from '@typings/database/models/servers/thread';
|
||||
import type UserModel from '@typings/database/models/servers/user';
|
||||
|
||||
/**
|
||||
@@ -122,3 +123,7 @@ export const isRecordMyChannelEqualToRaw = (record: MyChannelModel, raw: Channel
|
||||
export const isRecordFileEqualToRaw = (record: FileModel, raw: FileInfo) => {
|
||||
return raw.id === record.id;
|
||||
};
|
||||
|
||||
export const isRecordThreadEqualToRaw = (record: ThreadModel, raw: Thread) => {
|
||||
return raw.id === record.id;
|
||||
};
|
||||
|
||||
@@ -75,6 +75,7 @@ describe('*** Operator: Post Handlers tests ***', () => {
|
||||
edit_at: 0,
|
||||
delete_at: 0,
|
||||
is_pinned: false,
|
||||
is_following: false,
|
||||
user_id: 'q3mzxua9zjfczqakxdkowc6u6yy',
|
||||
channel_id: 'xxoq1p6bqg7dkxb3kj1mcjoungw',
|
||||
root_id: '',
|
||||
@@ -169,6 +170,7 @@ describe('*** Operator: Post Handlers tests ***', () => {
|
||||
edit_at: 0,
|
||||
delete_at: 0,
|
||||
is_pinned: false,
|
||||
is_following: false,
|
||||
user_id: 'hy5sq51sebfh58ktrce5ijtcwyy',
|
||||
channel_id: 'xxoq1p6bqg7dkxb3kj1mcjoungw',
|
||||
root_id: '8swgtrrdiff89jnsiwiip3y1eoe',
|
||||
@@ -195,6 +197,7 @@ describe('*** Operator: Post Handlers tests ***', () => {
|
||||
edit_at: 0,
|
||||
delete_at: 0,
|
||||
is_pinned: false,
|
||||
is_following: false,
|
||||
user_id: '44ud4m9tqwby3mphzzdwm7h31sr',
|
||||
channel_id: 'xxoq1p6bqg7dkxb3kj1mcjoungw',
|
||||
root_id: '8swgtrrdiff89jnsiwiip3y1eoe',
|
||||
|
||||
@@ -0,0 +1,100 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
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 ServerDataOperator from '..';
|
||||
|
||||
jest.mock('@database/operator/utils/thread', () => {
|
||||
return {
|
||||
sanitizeThreadParticipants: ({rawParticipants}: {rawParticipants: ThreadParticipant[]}) => {
|
||||
return {
|
||||
createParticipants: rawParticipants.map((participant) => ({
|
||||
raw: participant,
|
||||
})),
|
||||
};
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
describe('*** Operator: Thread Handlers tests ***', () => {
|
||||
let operator: ServerDataOperator;
|
||||
|
||||
beforeAll(async () => {
|
||||
await DatabaseManager.init(['baseHandler.test.com']);
|
||||
operator = DatabaseManager.serverDatabases['baseHandler.test.com'].operator;
|
||||
});
|
||||
|
||||
it('=> HandleThreads: should write to the the Thread & ThreadParticipant table', async () => {
|
||||
const spyOnBatchOperation = jest.spyOn(operator, 'batchRecords');
|
||||
const spyOnHandleRecords = jest.spyOn(operator, 'handleRecords');
|
||||
const spyOnHandleThreadParticipants = jest.spyOn(operator, 'handleThreadParticipants');
|
||||
|
||||
const threads = [
|
||||
{
|
||||
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,
|
||||
},
|
||||
] as Thread[];
|
||||
|
||||
await operator.handleThreads({threads, prepareRecordsOnly: false});
|
||||
|
||||
expect(spyOnHandleRecords).toHaveBeenCalledWith({
|
||||
findMatchingRecordBy: isRecordThreadEqualToRaw,
|
||||
fieldName: 'id',
|
||||
transformer: transformThreadRecord,
|
||||
createOrUpdateRawValues: threads,
|
||||
tableName: 'Thread',
|
||||
prepareRecordsOnly: true,
|
||||
});
|
||||
|
||||
// Should handle participants
|
||||
expect(spyOnHandleThreadParticipants).toHaveBeenCalledWith({
|
||||
threadsParticipants: threads.map((thread) => ({
|
||||
thread_id: thread.id,
|
||||
participants: thread.participants.map((participant) => ({
|
||||
id: participant.id,
|
||||
thread_id: thread.id,
|
||||
})),
|
||||
})),
|
||||
prepareRecordsOnly: true,
|
||||
});
|
||||
|
||||
// Only one batch operation for both tables
|
||||
expect(spyOnBatchOperation).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('=> HandleThreadParticipants: should write to the the ThreadParticipant table', async () => {
|
||||
const spyOnPrepareRecords = jest.spyOn(operator, 'prepareRecords');
|
||||
|
||||
const threadsParticipants = [
|
||||
{
|
||||
thread_id: 'thread-1',
|
||||
participants: [{
|
||||
id: 'user-1',
|
||||
thread_id: 'thread-1',
|
||||
}],
|
||||
},
|
||||
];
|
||||
|
||||
await operator.handleThreadParticipants({threadsParticipants, prepareRecordsOnly: false});
|
||||
|
||||
expect(spyOnPrepareRecords).toHaveBeenCalledWith({
|
||||
createRaws: [{
|
||||
raw: threadsParticipants[0].participants[0],
|
||||
}],
|
||||
transformer: transformThreadParticipantRecord,
|
||||
tableName: 'ThreadParticipant',
|
||||
});
|
||||
});
|
||||
});
|
||||
144
app/database/operator/server_data_operator/handlers/thread.ts
Normal file
144
app/database/operator/server_data_operator/handlers/thread.ts
Normal file
@@ -0,0 +1,144 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import Model from '@nozbe/watermelondb/Model';
|
||||
|
||||
import {Database} from '@constants';
|
||||
import DataOperatorException from '@database/exceptions/data_operator_exception';
|
||||
import {isRecordThreadEqualToRaw} from '@database/operator/server_data_operator/comparators';
|
||||
import {
|
||||
transformThreadRecord,
|
||||
transformThreadParticipantRecord,
|
||||
} from '@database/operator/server_data_operator/transformers/thread';
|
||||
import {getUniqueRawsBy} from '@database/operator/utils/general';
|
||||
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 ThreadParticipantModel from '@typings/database/models/servers/thread_participant';
|
||||
|
||||
const {
|
||||
THREAD,
|
||||
THREAD_PARTICIPANT,
|
||||
} = Database.MM_TABLES.SERVER;
|
||||
|
||||
export interface ThreadHandlerMix {
|
||||
handleThreads: ({threads, prepareRecordsOnly}: HandleThreadsArgs) => Promise<Model[]>;
|
||||
handleThreadParticipants: ({threadsParticipants, prepareRecordsOnly}: HandleThreadParticipantsArgs) => Promise<ThreadParticipantModel[]>;
|
||||
}
|
||||
|
||||
const ThreadHandler = (superclass: any) => class extends superclass {
|
||||
/**
|
||||
* handleThreads: Handler responsible for the Create/Update operations occurring on the Thread table from the 'Server' schema
|
||||
* @param {HandleThreadsArgs} handleThreads
|
||||
* @param {Thread[]} handleThreads.threads
|
||||
* @param {boolean | undefined} handleThreads.prepareRecordsOnly
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
handleThreads = async ({threads, prepareRecordsOnly = false}: HandleThreadsArgs): Promise<Model[]> => {
|
||||
if (!threads.length) {
|
||||
throw new DataOperatorException(
|
||||
'An empty "threads" array has been passed to the handleThreads method',
|
||||
);
|
||||
}
|
||||
|
||||
// Get unique threads in case they are duplicated
|
||||
const uniqueThreads = getUniqueRawsBy({
|
||||
raws: threads,
|
||||
key: 'id',
|
||||
}) as Thread[];
|
||||
|
||||
const threadsParticipants: ParticipantsPerThread[] = [];
|
||||
|
||||
// Let's process the thread data
|
||||
for (const thread of uniqueThreads) {
|
||||
threadsParticipants.push({
|
||||
thread_id: thread.id,
|
||||
participants: (thread.participants || []).map((participant) => ({
|
||||
id: participant.id,
|
||||
thread_id: thread.id,
|
||||
})),
|
||||
});
|
||||
}
|
||||
|
||||
// Get thread models to be created and updated
|
||||
const preparedThreads = await this.handleRecords({
|
||||
fieldName: 'id',
|
||||
findMatchingRecordBy: isRecordThreadEqualToRaw,
|
||||
transformer: transformThreadRecord,
|
||||
prepareRecordsOnly: true,
|
||||
createOrUpdateRawValues: uniqueThreads,
|
||||
tableName: THREAD,
|
||||
}) as ThreadModel[];
|
||||
|
||||
// Add the models to be batched here
|
||||
const batch: Model[] = [...preparedThreads];
|
||||
|
||||
// calls handler for Thread Participants
|
||||
const threadParticipants = (await this.handleThreadParticipants({threadsParticipants, prepareRecordsOnly: true})) as ThreadParticipantModel[];
|
||||
batch.push(...threadParticipants);
|
||||
|
||||
if (batch.length && !prepareRecordsOnly) {
|
||||
await this.batchRecords(batch);
|
||||
}
|
||||
|
||||
return batch;
|
||||
};
|
||||
|
||||
/**
|
||||
* handleThreadParticipants: Handler responsible for the Create/Update operations occurring on the ThreadParticipants table from the 'Server' schema
|
||||
* @param {HandleThreadParticipantsArgs} handleThreadParticipants
|
||||
* @param {ParticipantsPerThread[]} handleThreadParticipants.threadsParticipants
|
||||
* @param {boolean} handleThreadParticipants.prepareRecordsOnly
|
||||
* @throws DataOperatorException
|
||||
* @returns {Promise<Array<ThreadParticipantModel>>}
|
||||
*/
|
||||
handleThreadParticipants = async ({threadsParticipants, prepareRecordsOnly}: HandleThreadParticipantsArgs): Promise<ThreadParticipantModel[]> => {
|
||||
const batchRecords: ThreadParticipantModel[] = [];
|
||||
|
||||
if (!threadsParticipants.length) {
|
||||
throw new DataOperatorException(
|
||||
'An empty "thread participants" array has been passed to the handleThreadParticipants method',
|
||||
);
|
||||
}
|
||||
|
||||
for await (const threadParticipant of threadsParticipants) {
|
||||
const {thread_id, participants} = threadParticipant;
|
||||
const rawValues = getUniqueRawsBy({raws: participants, key: 'id'}) as ThreadParticipant[];
|
||||
const {
|
||||
createParticipants,
|
||||
deleteParticipants,
|
||||
} = await sanitizeThreadParticipants({
|
||||
database: this.database,
|
||||
thread_id,
|
||||
rawParticipants: rawValues,
|
||||
});
|
||||
|
||||
if (createParticipants?.length) {
|
||||
// Prepares record for model ThreadParticipants
|
||||
const participantsRecords = (await this.prepareRecords({
|
||||
createRaws: createParticipants,
|
||||
transformer: transformThreadParticipantRecord,
|
||||
tableName: THREAD_PARTICIPANT,
|
||||
})) as ThreadParticipantModel[];
|
||||
batchRecords.push(...participantsRecords);
|
||||
}
|
||||
|
||||
if (deleteParticipants?.length) {
|
||||
batchRecords.push(...deleteParticipants);
|
||||
}
|
||||
}
|
||||
|
||||
if (prepareRecordsOnly) {
|
||||
return batchRecords;
|
||||
}
|
||||
|
||||
if (batchRecords?.length) {
|
||||
await this.batchRecords(batchRecords);
|
||||
}
|
||||
|
||||
return batchRecords;
|
||||
};
|
||||
};
|
||||
|
||||
export default ThreadHandler;
|
||||
@@ -9,13 +9,14 @@ import PostsInChannelHandler, {PostsInChannelHandlerMix} from '@database/operato
|
||||
import PostsInThreadHandler, {PostsInThreadHandlerMix} from '@database/operator/server_data_operator/handlers/posts_in_thread';
|
||||
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 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 {}
|
||||
PostsInThreadHandlerMix, ReactionHandlerMix, UserHandlerMix, ChannelHandlerMix, CategoryHandlerMix, TeamHandlerMix, ThreadHandlerMix {}
|
||||
|
||||
class ServerDataOperator extends mix(ServerDataOperatorBase).with(
|
||||
CategoryHandler,
|
||||
@@ -25,6 +26,7 @@ class ServerDataOperator extends mix(ServerDataOperatorBase).with(
|
||||
PostsInThreadHandler,
|
||||
ReactionHander,
|
||||
TeamHandler,
|
||||
ThreadHandler,
|
||||
UserHandler,
|
||||
) {
|
||||
// eslint-disable-next-line no-useless-constructor
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {MM_TABLES} from '@constants/database';
|
||||
import {prepareBaseRecord} from '@database/operator/server_data_operator/transformers/index';
|
||||
import {OperationType} from '@typings/database/enums';
|
||||
|
||||
import type {TransformerArgs} from '@typings/database/database';
|
||||
import type ThreadModel from '@typings/database/models/servers/thread';
|
||||
import type ThreadParticipantModel from '@typings/database/models/servers/thread_participant';
|
||||
|
||||
const {
|
||||
THREAD,
|
||||
THREAD_PARTICIPANT,
|
||||
} = MM_TABLES.SERVER;
|
||||
|
||||
/**
|
||||
* transformThreadRecord: Prepares a record of the SERVER database 'Thread' table for update or create actions.
|
||||
* @param {TransformerArgs} operator
|
||||
* @param {Database} operator.database
|
||||
* @param {RecordPair} operator.value
|
||||
* @returns {Promise<ThreadModel>}
|
||||
*/
|
||||
export const transformThreadRecord = ({action, database, value}: TransformerArgs): Promise<ThreadModel> => {
|
||||
const raw = value.raw as Thread;
|
||||
const record = value.record as ThreadModel;
|
||||
const isCreateAction = action === OperationType.CREATE;
|
||||
|
||||
// If isCreateAction is true, we will use the id (API response) from the RAW, else we shall use the existing record id from the database
|
||||
const fieldsMapper = (thread: ThreadModel) => {
|
||||
thread._raw.id = isCreateAction ? (raw?.id ?? thread.id) : record.id;
|
||||
thread.lastReplyAt = raw.last_reply_at;
|
||||
thread.lastViewedAt = raw.last_viewed_at;
|
||||
thread.replyCount = raw.reply_count;
|
||||
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({
|
||||
action,
|
||||
database,
|
||||
tableName: THREAD,
|
||||
value,
|
||||
fieldsMapper,
|
||||
}) as Promise<ThreadModel>;
|
||||
};
|
||||
|
||||
/**
|
||||
* transformThreadParticipantRecord: Prepares a record of the SERVER database 'ThreadParticipant' table for update or create actions.
|
||||
* @param {TransformerArgs} operator
|
||||
* @param {Database} operator.database
|
||||
* @param {RecordPair} operator.value
|
||||
* @returns {Promise<ThreadParticipantModel>}
|
||||
*/
|
||||
export const transformThreadParticipantRecord = ({action, database, value}: TransformerArgs): Promise<ThreadParticipantModel> => {
|
||||
const raw = value.raw as ThreadParticipant;
|
||||
|
||||
// id of participant comes from server response
|
||||
const fieldsMapper = (participant: ThreadParticipantModel) => {
|
||||
participant.threadId = raw.thread_id;
|
||||
participant.userId = raw.id;
|
||||
};
|
||||
|
||||
return prepareBaseRecord({
|
||||
action,
|
||||
database,
|
||||
tableName: THREAD_PARTICIPANT,
|
||||
value,
|
||||
fieldsMapper,
|
||||
}) as Promise<ThreadParticipantModel>;
|
||||
};
|
||||
@@ -28,7 +28,6 @@ export const mockedPosts = {
|
||||
pending_post_id: '',
|
||||
reply_count: 4,
|
||||
last_reply_at: 0,
|
||||
participants: null,
|
||||
metadata: {},
|
||||
},
|
||||
'8fcnk3p1jt8mmkaprgajoxz115a': {
|
||||
@@ -55,7 +54,6 @@ export const mockedPosts = {
|
||||
pending_post_id: '',
|
||||
reply_count: 0,
|
||||
last_reply_at: 0,
|
||||
participants: null,
|
||||
metadata: {},
|
||||
},
|
||||
'3y3w3a6gkbg73bnj3xund9o5ic': {
|
||||
@@ -77,7 +75,6 @@ export const mockedPosts = {
|
||||
pending_post_id: '',
|
||||
reply_count: 4,
|
||||
last_reply_at: 0,
|
||||
participants: null,
|
||||
metadata: {},
|
||||
},
|
||||
'4btbnmticjgw7ewd3qopmpiwqw': {
|
||||
@@ -101,7 +98,6 @@ export const mockedPosts = {
|
||||
pending_post_id: '',
|
||||
reply_count: 0,
|
||||
last_reply_at: 0,
|
||||
participants: null,
|
||||
metadata: {},
|
||||
},
|
||||
'4r9jmr7eqt8dxq3f9woypzurry': {
|
||||
@@ -124,7 +120,6 @@ export const mockedPosts = {
|
||||
has_reactions: true,
|
||||
reply_count: 7,
|
||||
last_reply_at: 0,
|
||||
participants: null,
|
||||
metadata: {
|
||||
reactions: [
|
||||
{
|
||||
@@ -165,7 +160,6 @@ export const mockedPosts = {
|
||||
pending_post_id: '',
|
||||
reply_count: 7,
|
||||
last_reply_at: 0,
|
||||
participants: null,
|
||||
metadata: {
|
||||
emojis: [
|
||||
{
|
||||
|
||||
51
app/database/operator/utils/thread.ts
Normal file
51
app/database/operator/utils/thread.ts
Normal file
@@ -0,0 +1,51 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
import {Q} from '@nozbe/watermelondb';
|
||||
|
||||
import {MM_TABLES} from '@constants/database';
|
||||
|
||||
import type {RecordPair, SanitizeThreadParticipantsArgs} from '@typings/database/database';
|
||||
import type ThreadParticipantModel from '@typings/database/models/servers/thread_participant';
|
||||
|
||||
const {THREAD_PARTICIPANT} = MM_TABLES.SERVER;
|
||||
|
||||
/**
|
||||
* sanitizeThreadParticipants: Treats participants in a Thread. For example, a user can participate/not. Hence, this function
|
||||
* tell us which participants to create/delete in the ThreadParticipants table.
|
||||
* @param {SanitizeThreadParticipantsArgs} sanitizeThreadParticipants
|
||||
* @param {Database} sanitizeThreadParticipants.database
|
||||
* @param {string} sanitizeThreadParticipants.thread_id
|
||||
* @param {UserProfile[]} sanitizeThreadParticipants.rawParticipants
|
||||
* @returns {Promise<{createParticipants: ThreadParticipant[], deleteParticipants: ThreadParticipantModel[]}>}
|
||||
*/
|
||||
export const sanitizeThreadParticipants = async ({database, thread_id, rawParticipants}: SanitizeThreadParticipantsArgs) => {
|
||||
const participants = (await database.collections.
|
||||
get(THREAD_PARTICIPANT).
|
||||
query(Q.where('thread_id', thread_id)).
|
||||
fetch()) as ThreadParticipantModel[];
|
||||
|
||||
// similarObjects: Contains objects that are in both the RawParticipant array and in the ThreadParticipant table
|
||||
const similarObjects: ThreadParticipantModel[] = [];
|
||||
|
||||
const createParticipants: RecordPair[] = [];
|
||||
|
||||
for (let i = 0; i < rawParticipants.length; i++) {
|
||||
const rawParticipant = rawParticipants[i];
|
||||
|
||||
// If the participant is not present let's add them to the db
|
||||
const exists = participants.find((participant) => participant.userId === rawParticipant.id);
|
||||
|
||||
if (exists) {
|
||||
similarObjects.push(exists);
|
||||
} else {
|
||||
createParticipants.push({raw: rawParticipant});
|
||||
}
|
||||
}
|
||||
|
||||
// finding out elements to delete using array subtract
|
||||
const deleteParticipants = participants.
|
||||
filter((participant) => !similarObjects.includes(participant)).
|
||||
map((outCast) => outCast.prepareDestroyPermanently());
|
||||
|
||||
return {createParticipants, deleteParticipants};
|
||||
};
|
||||
@@ -28,6 +28,8 @@ import {
|
||||
TeamSchema,
|
||||
TeamSearchHistorySchema,
|
||||
TermsOfServiceSchema,
|
||||
ThreadSchema,
|
||||
ThreadParticipantSchema,
|
||||
UserSchema,
|
||||
} from './table_schemas';
|
||||
|
||||
@@ -58,6 +60,8 @@ export const serverSchema: AppSchema = appSchema({
|
||||
TeamSchema,
|
||||
TeamSearchHistorySchema,
|
||||
TermsOfServiceSchema,
|
||||
ThreadSchema,
|
||||
ThreadParticipantSchema,
|
||||
UserSchema,
|
||||
],
|
||||
});
|
||||
|
||||
@@ -25,4 +25,6 @@ export {default as TeamMembershipSchema} from './team_membership';
|
||||
export {default as TeamSchema} from './team';
|
||||
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 UserSchema} from './user';
|
||||
|
||||
21
app/database/schema/server/table_schemas/thread.ts
Normal file
21
app/database/schema/server/table_schemas/thread.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
// 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 {THREAD} = MM_TABLES.SERVER;
|
||||
|
||||
export default tableSchema({
|
||||
name: THREAD,
|
||||
columns: [
|
||||
{name: 'last_reply_at', type: 'number'},
|
||||
{name: 'last_viewed_at', type: 'number'},
|
||||
{name: 'is_following', type: 'boolean'},
|
||||
{name: 'reply_count', type: 'number'},
|
||||
{name: 'unread_replies', type: 'number'},
|
||||
{name: 'unread_mentions', type: 'number'},
|
||||
{name: 'loaded_in_global_threads', type: 'boolean'},
|
||||
],
|
||||
});
|
||||
@@ -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 {THREAD_PARTICIPANT} = MM_TABLES.SERVER;
|
||||
|
||||
export default tableSchema({
|
||||
name: THREAD_PARTICIPANT,
|
||||
columns: [
|
||||
{name: 'thread_id', type: 'string', isIndexed: true},
|
||||
{name: 'user_id', type: 'string', isIndexed: true},
|
||||
],
|
||||
});
|
||||
@@ -32,6 +32,8 @@ const {
|
||||
TEAM_MEMBERSHIP,
|
||||
TEAM_SEARCH_HISTORY,
|
||||
TERMS_OF_SERVICE,
|
||||
THREAD,
|
||||
THREAD_PARTICIPANT,
|
||||
USER,
|
||||
} = MM_TABLES.SERVER;
|
||||
|
||||
@@ -459,6 +461,40 @@ describe('*** Test schema for SERVER database ***', () => {
|
||||
},
|
||||
columnArray: [{name: 'accepted_at', type: 'number'}],
|
||||
},
|
||||
[THREAD]: {
|
||||
name: THREAD,
|
||||
unsafeSql: undefined,
|
||||
columns: {
|
||||
last_reply_at: {name: 'last_reply_at', type: 'number'},
|
||||
last_viewed_at: {name: 'last_viewed_at', type: 'number'},
|
||||
is_following: {name: 'is_following', type: 'boolean'},
|
||||
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'},
|
||||
{name: 'last_viewed_at', type: 'number'},
|
||||
{name: 'is_following', type: 'boolean'},
|
||||
{name: 'reply_count', type: 'number'},
|
||||
{name: 'unread_replies', type: 'number'},
|
||||
{name: 'unread_mentions', type: 'number'},
|
||||
{name: 'loaded_in_global_threads', type: 'boolean'},
|
||||
],
|
||||
},
|
||||
[THREAD_PARTICIPANT]: {
|
||||
name: THREAD_PARTICIPANT,
|
||||
unsafeSql: undefined,
|
||||
columns: {
|
||||
thread_id: {name: 'thread_id', type: 'string', isIndexed: true},
|
||||
user_id: {name: 'user_id', type: 'string', isIndexed: true},
|
||||
},
|
||||
columnArray: [
|
||||
{name: 'thread_id', type: 'string', isIndexed: true},
|
||||
{name: 'user_id', type: 'string', isIndexed: true},
|
||||
],
|
||||
},
|
||||
[USER]: {
|
||||
name: USER,
|
||||
unsafeSql: undefined,
|
||||
|
||||
@@ -326,7 +326,6 @@ export function generateCombinedPost(combinedId: string, systemPosts: PostModel[
|
||||
type: Post.POST_TYPES.COMBINED_USER_ACTIVITY as PostType,
|
||||
user_id: '',
|
||||
metadata: {},
|
||||
participants: null,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
2
types/api/config.d.ts
vendored
2
types/api/config.d.ts
vendored
@@ -22,6 +22,7 @@ interface ClientConfig {
|
||||
BuildHashEnterprise: string;
|
||||
BuildNumber: string;
|
||||
CloseUnusedDirectMessages: string;
|
||||
CollapsedThreads: string;
|
||||
CustomBrandText: string;
|
||||
CustomDescriptionText: string;
|
||||
CustomTermsOfServiceId: string;
|
||||
@@ -115,6 +116,7 @@ interface ClientConfig {
|
||||
ExperimentalViewArchivedChannels: string;
|
||||
ExtendSessionLengthWithActivity: string;
|
||||
FeatureFlagAppsEnabled?: string;
|
||||
FeatureFlagCollapsedThreads?: string;
|
||||
GfycatApiKey: string;
|
||||
GfycatApiSecret: string;
|
||||
GoogleDeveloperKey: string;
|
||||
|
||||
2
types/api/posts.d.ts
vendored
2
types/api/posts.d.ts
vendored
@@ -47,6 +47,7 @@ type Post = {
|
||||
update_at: number;
|
||||
edit_at: number;
|
||||
delete_at: number;
|
||||
is_following?: boolean;
|
||||
is_pinned: boolean;
|
||||
user_id: string;
|
||||
channel_id: string;
|
||||
@@ -54,6 +55,7 @@ type Post = {
|
||||
original_id: string;
|
||||
message: string;
|
||||
type: PostType;
|
||||
participants?: null | UserProfile[];
|
||||
props: Record<string, any>;
|
||||
hashtags: string;
|
||||
pending_post_id: string;
|
||||
|
||||
20
types/api/threads.d.ts
vendored
Normal file
20
types/api/threads.d.ts
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
type Thread = {
|
||||
id: string;
|
||||
reply_count: number;
|
||||
last_reply_at: number;
|
||||
last_viewed_at: number;
|
||||
participants: UserProfile[];
|
||||
post: Post;
|
||||
is_following?: boolean;
|
||||
unread_replies: number;
|
||||
unread_mentions: number;
|
||||
loaded_in_global_threads: boolean;
|
||||
};
|
||||
|
||||
type ThreadParticipant = {
|
||||
id: $ID<User>;
|
||||
thread_id: $ID<Thread>;
|
||||
};
|
||||
16
types/database/database.d.ts
vendored
16
types/database/database.d.ts
vendored
@@ -88,6 +88,16 @@ export type HandlePostsArgs = {
|
||||
prepareRecordsOnly?: boolean;
|
||||
};
|
||||
|
||||
export type HandleThreadsArgs = {
|
||||
threads: Thread[];
|
||||
prepareRecordsOnly?: boolean;
|
||||
};
|
||||
|
||||
export type HandleThreadParticipantsArgs = {
|
||||
prepareRecordsOnly: boolean;
|
||||
threadsParticipants: ParticipantsPerThread[];
|
||||
};
|
||||
|
||||
export type SanitizeReactionsArgs = {
|
||||
database: Database;
|
||||
post_id: string;
|
||||
@@ -95,6 +105,12 @@ export type SanitizeReactionsArgs = {
|
||||
skipSync?: boolean;
|
||||
};
|
||||
|
||||
export type SanitizeThreadParticipantsArgs = {
|
||||
database: Database;
|
||||
thread_id: $ID<Thread>;
|
||||
rawParticipants: ThreadParticipant[];
|
||||
}
|
||||
|
||||
export type ChainPostsArgs = {
|
||||
order: string[];
|
||||
previousPostId: string;
|
||||
|
||||
3
types/database/models/servers/post.d.ts
vendored
3
types/database/models/servers/post.d.ts
vendored
@@ -79,6 +79,9 @@ export default class PostModel extends Model {
|
||||
/** channel: The channel which is presenting this Post */
|
||||
channel: Relation<ChannelModel>;
|
||||
|
||||
/** thread : the related thread for the post */
|
||||
thread: Relation<ThreadModel>;
|
||||
|
||||
/** hasReplies: Async function to determine if the post is part of a thread */
|
||||
hasReplies: () => Promise<boolean>;
|
||||
}
|
||||
|
||||
43
types/database/models/servers/thread.d.ts
vendored
Normal file
43
types/database/models/servers/thread.d.ts
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {Query, Relation} from '@nozbe/watermelondb';
|
||||
import Model, {Associations} from '@nozbe/watermelondb/Model';
|
||||
|
||||
/**
|
||||
* The Thread model contains thread information of a post.
|
||||
*/
|
||||
export default class ThreadModel extends Model {
|
||||
/** table (name) : Thread */
|
||||
static table: string;
|
||||
|
||||
/** associations : Describes every relationship to this table. */
|
||||
static associations: Associations;
|
||||
|
||||
/** lastReplyAt : The timestamp of when user last replied to the thread. */
|
||||
lastReplyAt: number;
|
||||
|
||||
/** lastViewedAt : The timestamp of when user last viewed the thread. */
|
||||
lastViewedAt: number;
|
||||
|
||||
/** reply_count : The total replies to the thread by all the participants. */
|
||||
replyCount: number;
|
||||
|
||||
/** isFollowing: If user is following this thread or not */
|
||||
isFollowing: boolean;
|
||||
|
||||
/** unread_replies : The number of replies that are not read by the user. */
|
||||
unreadReplies: number;
|
||||
|
||||
/** 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>;
|
||||
|
||||
/** post : Query returning the post data for the current thread */
|
||||
post: Relation<PostModel>;
|
||||
}
|
||||
28
types/database/models/servers/thread_participant.d.ts
vendored
Normal file
28
types/database/models/servers/thread_participant.d.ts
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
// 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';
|
||||
|
||||
/**
|
||||
* The Thread Participants Model is used to show the participants of a thread
|
||||
*/
|
||||
export default class ThreadParticipantsModel extends Model {
|
||||
/** table (name) : ThreadParticipants */
|
||||
static table: string;
|
||||
|
||||
/** associations : Describes every relationship to this table. */
|
||||
static associations: Associations;
|
||||
|
||||
/** thread_id : The related Thread's foreign key to which this participant belongs */
|
||||
threadId: string;
|
||||
|
||||
/** user_id : The user id of the user participating in the thread */
|
||||
userId: string;
|
||||
|
||||
/** thread : The related record to the Thread model */
|
||||
thread: Relation<ThreadModel>;
|
||||
|
||||
/** user : The related record to the User model */
|
||||
user: Relation<UserModel>;
|
||||
}
|
||||
3
types/database/models/servers/user.d.ts
vendored
3
types/database/models/servers/user.d.ts
vendored
@@ -88,6 +88,9 @@ export default class UserModel extends Model {
|
||||
/** teams : All the team that this user is part of */
|
||||
teams: Query<TeamMembershipModel>;
|
||||
|
||||
/** threadParticipations : All the thread participations this user is part of */
|
||||
threadParticipations: Query<ThreadParticipantsModel>;
|
||||
|
||||
/** prepareStatus: Prepare the model to update the user status in a batch operation */
|
||||
prepareStatus: (status: string) => void;
|
||||
|
||||
|
||||
7
types/database/raw_values.d.ts
vendored
7
types/database/raw_values.d.ts
vendored
@@ -57,6 +57,11 @@ type IdValue = {
|
||||
value: unknown;
|
||||
};
|
||||
|
||||
type ParticipantsPerThread = {
|
||||
thread_id: string;
|
||||
participants: ThreadParticipant[];
|
||||
};
|
||||
|
||||
type TeamChannelHistory = {
|
||||
id: string;
|
||||
channel_ids: string[];
|
||||
@@ -103,5 +108,7 @@ type RawValue =
|
||||
| TeamMembership
|
||||
| TeamSearchHistory
|
||||
| TermsOfService
|
||||
| Thread
|
||||
| ThreadParticipant
|
||||
| UserProfile
|
||||
| Pick<ChannelMembership, 'channel_id' | 'user_id'>
|
||||
|
||||
Reference in New Issue
Block a user