From f815f6b3e5bd376e79202b2a45320b71fda3d5ac Mon Sep 17 00:00:00 2001 From: Jason Frerich Date: Thu, 3 Feb 2022 04:14:08 -0600 Subject: [PATCH] [Gekidou MM-3972] Websocket Events - Reaction and emojis (#5920) * Wip * update name so it references changing the reaction on a post * handleAddEmoji -> handleAddCustomEmoji * add remove reaction logic * sanitizeReactions * use skipSync to return early if not deleting any records that are not in the server db * return the filtered delete results and have the handler destroy the records * fix lint * use forEach instead of map. * PR feedback * PR feedback * Update app/actions/websocket/reactions.ts * remove console.log Co-authored-by: Elias Nahum --- app/actions/websocket/index.ts | 7 +- app/actions/websocket/reactions.ts | 69 +++++++++++++++++++ .../server_data_operator/handlers/user.ts | 2 + app/database/operator/utils/reaction.ts | 9 ++- types/database/database.d.ts | 1 + 5 files changed, 82 insertions(+), 6 deletions(-) create mode 100644 app/actions/websocket/reactions.ts diff --git a/app/actions/websocket/index.ts b/app/actions/websocket/index.ts index 3715560ada..4f72ed73f5 100644 --- a/app/actions/websocket/index.ts +++ b/app/actions/websocket/index.ts @@ -22,6 +22,7 @@ import {queryCurrentUser} from '@queries/servers/user'; import {handleChannelDeletedEvent, handleUserAddedToChannelEvent, handleUserRemovedFromChannelEvent} from './channel'; import {handleNewPostEvent, handlePostDeleted, handlePostEdited, handlePostUnread} from './posts'; import {handlePreferenceChangedEvent, handlePreferencesChangedEvent, handlePreferencesDeletedEvent} from './preferences'; +import {handleAddCustomEmoji, handleReactionRemovedFromPostEvent, handleReactionAddedToPostEvent} from './reactions'; import {handleUserRoleUpdatedEvent, handleTeamMemberRoleUpdatedEvent, handleRoleUpdatedEvent} from './roles'; import {handleLeaveTeamEvent, handleUserAddedToTeamEvent, handleUpdateTeamEvent} from './teams'; import {handleUserUpdatedEvent} from './users'; @@ -260,17 +261,17 @@ export async function handleEvent(serverUrl: string, msg: WebSocketMessage) { // handleHelloEvent(msg); // break; case WebsocketEvents.REACTION_ADDED: + handleReactionAddedToPostEvent(serverUrl, msg); break; - // return dispatch(handleReactionAddedEvent(msg)); case WebsocketEvents.REACTION_REMOVED: + handleReactionRemovedFromPostEvent(serverUrl, msg); break; - // return dispatch(handleReactionRemovedEvent(msg)); case WebsocketEvents.EMOJI_ADDED: + handleAddCustomEmoji(serverUrl, msg); break; - // return dispatch(handleAddEmoji(msg)); case WebsocketEvents.LICENSE_CHANGED: break; diff --git a/app/actions/websocket/reactions.ts b/app/actions/websocket/reactions.ts new file mode 100644 index 0000000000..0922fe9b61 --- /dev/null +++ b/app/actions/websocket/reactions.ts @@ -0,0 +1,69 @@ +// 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 DatabaseManager from '@database/manager'; + +export async function handleAddCustomEmoji(serverUrl: string, msg: WebSocketMessage): Promise { + const operator = DatabaseManager.serverDatabases[serverUrl]?.operator; + if (!operator) { + return; + } + + try { + const emoji = JSON.parse(msg.data.emoji) as CustomEmoji; + await operator.handleCustomEmojis({ + prepareRecordsOnly: false, + emojis: [emoji], + }); + } catch { + // Do nothing + } +} + +export async function handleReactionAddedToPostEvent(serverUrl: string, msg: WebSocketMessage): Promise { + const operator = DatabaseManager.serverDatabases[serverUrl]?.operator; + if (!operator) { + return; + } + + try { + const reaction = JSON.parse(msg.data.reaction) as Reaction; + await operator.handleReactions({ + prepareRecordsOnly: false, + skipSync: true, + postsReactions: [{ + post_id: reaction.post_id, + reactions: [reaction], + }], + }); + } catch { + // Do nothing + } +} + +export async function handleReactionRemovedFromPostEvent(serverUrl: string, msg: WebSocketMessage): Promise { + const database = DatabaseManager.serverDatabases[serverUrl]?.database; + if (!database) { + return; + } + + try { + const msgReaction = JSON.parse(msg.data.reaction) as Reaction; + const reaction = await database.get(MM_TABLES.SERVER.REACTION).query( + Q.where('emoji_name', msgReaction.emoji_name), + Q.where('post_id', msgReaction.post_id), + Q.where('user_id', msgReaction.user_id), + ).fetch(); + + if (reaction.length) { + await database.write(async () => { + await reaction[0].destroyPermanently(); + }); + } + } catch { + // Do nothing + } +} diff --git a/app/database/operator/server_data_operator/handlers/user.ts b/app/database/operator/server_data_operator/handlers/user.ts index 33995cc73e..804ca97963 100644 --- a/app/database/operator/server_data_operator/handlers/user.ts +++ b/app/database/operator/server_data_operator/handlers/user.ts @@ -152,6 +152,7 @@ const UserHandler = (superclass: any) => class extends superclass { database: this.database, post_id, rawReactions: rawValues, + skipSync, }); if (createReactions?.length) { @@ -165,6 +166,7 @@ const UserHandler = (superclass: any) => class extends superclass { } if (deleteReactions?.length && !skipSync) { + deleteReactions.forEach((outCast) => outCast.prepareDestroyPermanently()); batchRecords.push(...deleteReactions); } } diff --git a/app/database/operator/utils/reaction.ts b/app/database/operator/utils/reaction.ts index 72233f8691..3c03675c5c 100644 --- a/app/database/operator/utils/reaction.ts +++ b/app/database/operator/utils/reaction.ts @@ -19,7 +19,7 @@ const {REACTION} = MM_TABLES.SERVER; * @param {RawReaction[]} sanitizeReactions.rawReactions * @returns {Promise<{createReactions: RawReaction[], deleteReactions: Reaction[]}>} */ -export const sanitizeReactions = async ({database, post_id, rawReactions}: SanitizeReactionsArgs) => { +export const sanitizeReactions = async ({database, post_id, rawReactions, skipSync}: SanitizeReactionsArgs) => { const reactions = (await database.collections. get(REACTION). query(Q.where('post_id', post_id)). @@ -45,10 +45,13 @@ export const sanitizeReactions = async ({database, post_id, rawReactions}: Sanit } } + if (skipSync) { + return {createReactions, deleteReactions: []}; + } + // finding out elements to delete using array subtract const deleteReactions = reactions. - filter((reaction) => !similarObjects.includes(reaction)). - map((outCast) => outCast.prepareDestroyPermanently()); + filter((reaction) => !similarObjects.includes(reaction)); return {createReactions, deleteReactions}; }; diff --git a/types/database/database.d.ts b/types/database/database.d.ts index d4b9f41921..5cb85ae931 100644 --- a/types/database/database.d.ts +++ b/types/database/database.d.ts @@ -92,6 +92,7 @@ export type SanitizeReactionsArgs = { database: Database; post_id: string; rawReactions: Reaction[]; + skipSync?: boolean; }; export type ChainPostsArgs = {