diff --git a/lib/selectors/chat-selectors.js b/lib/selectors/chat-selectors.js --- a/lib/selectors/chat-selectors.js +++ b/lib/selectors/chat-selectors.js @@ -39,6 +39,7 @@ type MessageStore, type RobotextMessageInfo, } from '../types/message-types.js'; +import type { CompoundReactionInfo } from '../types/messages/compound-reaction.js'; import type { ThreadInfo, RawThreadInfo, @@ -355,6 +356,7 @@ type MessageReactionInfo = { +viewerReacted: boolean, +users: $ReadOnlyArray, + +count: number, }; type TargetMessageReactions = Map>; @@ -381,6 +383,10 @@ : threadMessageInfos; const targetMessageReactionsMap = new Map(); + const compoundReactionsMap = new Map< + string, + { +[reaction: string]: CompoundReactionInfo }, + >(); // We need to iterate backwards to put the order of messages in chronological // order, starting with the oldest. This avoids the scenario where the most @@ -389,6 +395,15 @@ // to be skipped. for (let i = messages.length - 1; i >= 0; i--) { const messageInfo = messages[i]; + + if (messageInfo.type === messageTypes.COMPOUND_REACTION) { + compoundReactionsMap.set( + messageInfo.targetMessageID, + messageInfo.reactions, + ); + continue; + } + if (messageInfo.type !== messageTypes.REACTION) { continue; } @@ -470,7 +485,8 @@ if ( messageInfo.type === messageTypes.REACTION || messageInfo.type === messageTypes.EDIT_MESSAGE || - messageInfo.type === messageTypes.DELETE_MESSAGE + messageInfo.type === messageTypes.DELETE_MESSAGE || + messageInfo.type === messageTypes.COMPOUND_REACTION ) { continue; } @@ -537,6 +553,20 @@ const renderedReactions: ReactionInfo = (() => { const result: { [string]: MessageReactionInfo } = {}; + const compoundReaction = originalMessageInfo.id + ? compoundReactionsMap.get(originalMessageInfo.id) + : null; + if (compoundReaction) { + for (const reaction of Object.keys(compoundReaction)) { + result[reaction] = { + users: [], + viewerReacted: compoundReaction[reaction].viewerReacted, + count: compoundReaction[reaction].count, + }; + } + return result; + } + let messageReactsMap; if (originalMessageInfo.id) { messageReactsMap = targetMessageReactionsMap.get( @@ -561,6 +591,7 @@ const messageReactionInfo = { users: reactionUserInfos, viewerReacted: reactionUsersInfoMap.has(viewerID), + count: reactionUserInfos.length, }; result[reaction] = messageReactionInfo; diff --git a/lib/shared/id-utils.js b/lib/shared/id-utils.js --- a/lib/shared/id-utils.js +++ b/lib/shared/id-utils.js @@ -10,6 +10,7 @@ RawComposableMessageInfo, RawMessageInfo, } from '../types/message-types.js'; +import type { RawCompoundReactionMessageInfo } from '../types/messages/compound-reaction.js'; import type { RawEditMessageInfo } from '../types/messages/edit.js'; import type { RawReactionMessageInfo } from '../types/messages/reaction.js'; @@ -79,7 +80,8 @@ rawMessageInfo: | RawComposableMessageInfo | RawReactionMessageInfo - | RawEditMessageInfo, + | RawEditMessageInfo + | RawCompoundReactionMessageInfo, ) { const { localID, ...rest } = rawMessageInfo; return rest; diff --git a/lib/shared/message-utils.js b/lib/shared/message-utils.js --- a/lib/shared/message-utils.js +++ b/lib/shared/message-utils.js @@ -42,6 +42,7 @@ type ValidRawSidebarSourceMessageInfo, type ValidSidebarSourceMessageInfo, } from '../types/message-types.js'; +import type { CompoundReactionMessageInfo } from '../types/messages/compound-reaction.js'; import type { DeleteMessageInfo } from '../types/messages/delete.js'; import type { EditMessageInfo } from '../types/messages/edit.js'; import type { ImagesMessageData } from '../types/messages/images.js'; @@ -266,7 +267,8 @@ | RobotextMessageInfo | ReactionMessageInfo | EditMessageInfo - | DeleteMessageInfo, + | DeleteMessageInfo + | CompoundReactionMessageInfo, threadInfo: ThreadInfo, parentThreadInfo: ?ThreadInfo, markdownRules: ParserRules, @@ -282,7 +284,8 @@ messageInfo.type !== messageTypes.MULTIMEDIA && messageInfo.type !== messageTypes.REACTION && messageInfo.type !== messageTypes.EDIT_MESSAGE && - messageInfo.type !== messageTypes.DELETE_MESSAGE, + messageInfo.type !== messageTypes.DELETE_MESSAGE && + messageInfo.type !== messageTypes.COMPOUND_REACTION, 'messageTitle can only be auto-generated for RobotextMessageInfo', ); return robotextForMessageInfo(messageInfo, threadInfo, parentThreadInfo); @@ -515,7 +518,8 @@ message.type === messageTypes.EDIT_MESSAGE || message.type === messageTypes.SIDEBAR_SOURCE || message.type === messageTypes.TOGGLE_PIN || - message.type === messageTypes.DELETE_MESSAGE) && + message.type === messageTypes.DELETE_MESSAGE || + message.type === messageTypes.COMPOUND_REACTION) && !messageSpecs[message.type].canBeSidebarSource ); } @@ -529,6 +533,7 @@ message.type !== messageTypes.SIDEBAR_SOURCE && message.type !== messageTypes.TOGGLE_PIN && message.type !== messageTypes.DELETE_MESSAGE && + message.type !== messageTypes.COMPOUND_REACTION && messageSpecs[message.type].canBeSidebarSource ); } @@ -542,6 +547,7 @@ message.type !== messageTypes.SIDEBAR_SOURCE && message.type !== messageTypes.TOGGLE_PIN && message.type !== messageTypes.DELETE_MESSAGE && + message.type !== messageTypes.COMPOUND_REACTION && messageSpecs[message.type].canBeSidebarSource ); } diff --git a/lib/shared/messages/compound-reaction-message-spec.js b/lib/shared/messages/compound-reaction-message-spec.js new file mode 100644 --- /dev/null +++ b/lib/shared/messages/compound-reaction-message-spec.js @@ -0,0 +1,284 @@ +// @flow + +import invariant from 'invariant'; + +import { + type GeneratesNotifsParams, + type MessageSpec, + type MessageTitleParam, + messageNotifyTypes, + type ShowInMessagePreviewParams, +} from './message-spec.js'; +import { assertSingleMessageInfo, joinResult } from './utils.js'; +import type { PlatformDetails } from '../../types/device-types.js'; +import { + assertMessageType, + messageTypes, +} from '../../types/message-types-enum.js'; +import type { + ClientDBMessageInfo, + MessageInfo, +} from '../../types/message-types.js'; +import { + type RawCompoundReactionMessageInfo, + rawCompoundReactionMessageInfoValidator, + type CompoundReactionMessageData, + type CompoundReactionMessageInfo, +} from '../../types/messages/compound-reaction.js'; +import type { RawUnsupportedMessageInfo } from '../../types/messages/unsupported.js'; +import type { ThreadInfo } from '../../types/minimally-encoded-thread-permissions-types.js'; +import type { NotifTexts } from '../../types/notif-types.js'; +import type { RelativeUserInfo } from '../../types/user-types.js'; +import { ET } from '../../utils/entity-text.js'; +import { threadIsGroupChat } from '../thread-utils.js'; +import { hasMinCodeVersion, NEXT_CODE_VERSION } from '../version-utils.js'; + +export const compoundReactionMessageSpec: MessageSpec< + CompoundReactionMessageData, + RawCompoundReactionMessageInfo, + CompoundReactionMessageInfo, +> = Object.freeze({ + messageContentForServerDB( + data: CompoundReactionMessageData | RawCompoundReactionMessageInfo, + ): string { + return JSON.stringify({ + reactions: data.reactions, + }); + }, + + messageContentForClientDB(data: RawCompoundReactionMessageInfo): string { + return JSON.stringify({ + targetMessageID: data.targetMessageID, + reactions: data.reactions, + }); + }, + + messageTitle({ + messageInfo, + }: MessageTitleParam) { + const reactionCount = `${Object.keys(messageInfo.reactions).length}`; + const hasViewerReacted = Object.values(messageInfo.reactions).some( + info => info.viewerReacted, + ); + + if (hasViewerReacted) { + return ET`You and others reacted to a message`; + } else { + return ET`${reactionCount} reactions on a message`; + } + }, + + rawMessageInfoFromServerDBRow(row: Object): RawCompoundReactionMessageInfo { + invariant( + row.targetMessageID !== null && row.targetMessageID !== undefined, + 'targetMessageID should be set', + ); + + const content = JSON.parse(row.content); + + return { + type: messageTypes.COMPOUND_REACTION, + id: row.id.toString(), + threadID: row.threadID.toString(), + time: row.time, + creatorID: row.creatorID.toString(), + targetMessageID: row.targetMessageID.toString(), + reactions: content.reactions, + }; + }, + + rawMessageInfoFromClientDB( + clientDBMessageInfo: ClientDBMessageInfo, + ): RawCompoundReactionMessageInfo { + const messageType = assertMessageType(parseInt(clientDBMessageInfo.type)); + invariant( + messageType === messageTypes.COMPOUND_REACTION, + 'message must be of type COMPOUND_REACTION', + ); + invariant( + clientDBMessageInfo.content !== undefined && + clientDBMessageInfo.content !== null, + 'content must be defined for CompoundReaction', + ); + const content = JSON.parse(clientDBMessageInfo.content); + + const rawCompoundReactionMessageInfo: RawCompoundReactionMessageInfo = { + type: messageTypes.COMPOUND_REACTION, + id: clientDBMessageInfo.id, + threadID: clientDBMessageInfo.thread, + time: parseInt(clientDBMessageInfo.time), + creatorID: clientDBMessageInfo.user, + targetMessageID: content.targetMessageID, + reactions: content.reactions, + }; + + return rawCompoundReactionMessageInfo; + }, + + createMessageInfo( + rawMessageInfo: RawCompoundReactionMessageInfo, + creator: RelativeUserInfo, + ): CompoundReactionMessageInfo { + return { + type: messageTypes.COMPOUND_REACTION, + id: rawMessageInfo.id, + threadID: rawMessageInfo.threadID, + time: rawMessageInfo.time, + targetMessageID: rawMessageInfo.targetMessageID, + reactions: rawMessageInfo.reactions, + creator, + }; + }, + + rawMessageInfoFromMessageData( + messageData: CompoundReactionMessageData, + id: ?string, + ): RawCompoundReactionMessageInfo { + invariant(id, 'RawCompoundReactionMessageInfo needs id'); + return { ...messageData, id }; + }, + + shimUnsupportedMessageInfo( + rawMessageInfo: RawCompoundReactionMessageInfo, + platformDetails: ?PlatformDetails, + ): RawCompoundReactionMessageInfo | RawUnsupportedMessageInfo { + if ( + hasMinCodeVersion(platformDetails, { + native: NEXT_CODE_VERSION, + web: NEXT_CODE_VERSION, + }) + ) { + return rawMessageInfo; + } + const { id } = rawMessageInfo; + invariant(id !== null && id !== undefined, 'id should be set on server'); + + return { + type: messageTypes.UNSUPPORTED, + id, + threadID: rawMessageInfo.threadID, + creatorID: rawMessageInfo.creatorID, + time: rawMessageInfo.time, + robotext: 'sent reactions to a message', + unsupportedMessageInfo: rawMessageInfo, + }; + }, + + unshimMessageInfo( + unwrapped: RawCompoundReactionMessageInfo, + ): RawCompoundReactionMessageInfo { + return unwrapped; + }, + + async notificationTexts( + messageInfos: $ReadOnlyArray, + threadInfo: ThreadInfo, + ): Promise { + const messageInfo = assertSingleMessageInfo(messageInfos); + invariant( + messageInfo.type === messageTypes.COMPOUND_REACTION, + 'messageInfo should be compound reaction type', + ); + + const reactionCount = Object.keys(messageInfo.reactions).length; + const body = `${reactionCount} reactions to your message`; + + let merged = ET`${body}`; + if (threadInfo.name || threadIsGroupChat(threadInfo)) { + const thread = ET.thread({ display: 'shortName', threadInfo }); + merged = ET`${merged} in ${thread}`; + } + + return { + merged, + body, + title: threadInfo.uiName, + prefix: ET``, + }; + }, + + notificationCollapseKey( + rawMessageInfo: RawCompoundReactionMessageInfo, + ): string { + return joinResult( + rawMessageInfo.type, + rawMessageInfo.threadID, + rawMessageInfo.creatorID, + rawMessageInfo.targetMessageID, + ); + }, + + getMessageNotifyType: async ( + rawMessageInfo: RawCompoundReactionMessageInfo, + params: GeneratesNotifsParams, + ) => { + const { notifTargetUserID, fetchMessageInfoByID } = params; + + const targetMessageInfo = await fetchMessageInfoByID( + rawMessageInfo.targetMessageID, + ); + + if (targetMessageInfo?.creatorID !== notifTargetUserID) { + return messageNotifyTypes.NONE; + } + + // Check if any reaction has a positive count + const hasActiveReactions = Object.values(rawMessageInfo.reactions).some( + reaction => reaction.count > 0, + ); + + return hasActiveReactions + ? messageNotifyTypes.NOTIF_AND_SET_UNREAD + : messageNotifyTypes.RESCIND; + }, + + canBeSidebarSource: false, + + canBePinned: false, + + canBeRenderedIndependently: false, + + validator: rawCompoundReactionMessageInfoValidator, + + showInMessagePreview: ( + messageInfo: CompoundReactionMessageInfo, + params: ShowInMessagePreviewParams, + ) => { + const getOriginalMessageAuthorResult = params.getMessageAuthor( + messageInfo.targetMessageID, + ); + if (!getOriginalMessageAuthorResult) { + return false; + } + if (typeof getOriginalMessageAuthorResult === 'string') { + return getOriginalMessageAuthorResult === params.viewerID; + } + return (async () => { + const originalMessageAuthor = await getOriginalMessageAuthorResult; + return originalMessageAuthor === params.viewerID; + })(); + }, + + getLastUpdatedTime: + ( + rawMessageInfo: RawCompoundReactionMessageInfo, + params: ShowInMessagePreviewParams, + ) => + async () => { + const getOriginalMessageAuthorResult = params.getMessageAuthor( + rawMessageInfo.targetMessageID, + ); + if (!getOriginalMessageAuthorResult) { + return null; + } + if (typeof getOriginalMessageAuthorResult === 'string') { + return getOriginalMessageAuthorResult === params.viewerID + ? rawMessageInfo.time + : null; + } + const originalMessageAuthor = await getOriginalMessageAuthorResult; + return originalMessageAuthor === params.viewerID + ? rawMessageInfo.time + : null; + }, +}); diff --git a/lib/shared/messages/message-specs.js b/lib/shared/messages/message-specs.js --- a/lib/shared/messages/message-specs.js +++ b/lib/shared/messages/message-specs.js @@ -3,6 +3,7 @@ import { addMembersMessageSpec } from './add-members-message-spec.js'; import { changeRoleMessageSpec } from './change-role-message-spec.js'; import { changeSettingsMessageSpec } from './change-settings-message-spec.js'; +import { compoundReactionMessageSpec } from './compound-reaction-message-spec.js'; import { createEntryMessageSpec } from './create-entry-message-spec.js'; import { createSidebarMessageSpec } from './create-sidebar-message-spec.js'; import { createSubThreadMessageSpec } from './create-sub-thread-message-spec.js'; @@ -55,4 +56,5 @@ [messageTypes.TOGGLE_PIN]: togglePinMessageSpec, [messageTypes.UPDATE_RELATIONSHIP]: updateRelationshipMessageSpec, [messageTypes.DELETE_MESSAGE]: deleteMessageSpec, + [messageTypes.COMPOUND_REACTION]: compoundReactionMessageSpec, }); diff --git a/lib/types/message-types-enum.js b/lib/types/message-types-enum.js --- a/lib/types/message-types-enum.js +++ b/lib/types/message-types-enum.js @@ -38,6 +38,7 @@ TOGGLE_PIN: 21, UPDATE_RELATIONSHIP: 22, DELETE_MESSAGE: 23, + COMPOUND_REACTION: 24, }); export type MessageType = $Values; export function assertMessageType(ourMessageType: number): MessageType { @@ -65,7 +66,8 @@ ourMessageType === 20 || ourMessageType === 21 || ourMessageType === 22 || - ourMessageType === 23, + ourMessageType === 23 || + ourMessageType === 24, 'number is not MessageType enum', ); return ourMessageType; diff --git a/lib/types/message-types.js b/lib/types/message-types.js --- a/lib/types/message-types.js +++ b/lib/types/message-types.js @@ -28,6 +28,12 @@ type RawChangeSettingsMessageInfo, rawChangeSettingsMessageInfoValidator, } from './messages/change-settings.js'; +import { + type RawCompoundReactionMessageInfo, + rawCompoundReactionMessageInfoValidator, + type CompoundReactionMessageData, + type CompoundReactionMessageInfo, +} from './messages/compound-reaction.js'; import { type CreateEntryMessageData, type CreateEntryMessageInfo, @@ -193,7 +199,8 @@ messageData.type !== messageTypes.TEXT && messageData.type !== messageTypes.IMAGES && messageData.type !== messageTypes.MULTIMEDIA && - messageData.type !== messageTypes.REACTION + messageData.type !== messageTypes.REACTION && + messageData.type !== messageTypes.COMPOUND_REACTION ) { return null; } @@ -267,6 +274,7 @@ | SidebarSourceMessageData | CreateSidebarMessageData | ReactionMessageData + | CompoundReactionMessageData | EditMessageData | TogglePinMessageData | UpdateRelationshipMessageData @@ -368,6 +376,7 @@ | RawRobotextMessageInfo | RawSidebarSourceMessageInfo | RawReactionMessageInfo + | RawCompoundReactionMessageInfo | RawEditMessageInfo | RawDeleteMessageInfo; export const rawMessageInfoValidator: TUnion = t.union([ @@ -375,6 +384,7 @@ rawRobotextMessageInfoValidator, rawSidebarSourceMessageInfoValidator, rawReactionMessageInfoValidator, + rawCompoundReactionMessageInfoValidator, rawEditMessageInfoValidator, rawDeleteMessageInfoValidator, ]); @@ -395,7 +405,11 @@ | ({ ...RawReactionMessageInfo, +localID: string, - } & RawReactionMessageInfo); + } & RawReactionMessageInfo) + | ({ + ...RawCompoundReactionMessageInfo, + +localID: string, + } & RawCompoundReactionMessageInfo); export type MultimediaMessageInfo = ImagesMessageInfo | MediaMessageInfo; export type ComposableMessageInfo = TextMessageInfo | MultimediaMessageInfo; @@ -421,7 +435,8 @@ export type PreviewableMessageInfo = | RobotextMessageInfo | MultimediaMessageInfo - | ReactionMessageInfo; + | ReactionMessageInfo + | CompoundReactionMessageInfo; export type ValidSidebarSourceMessageInfo = | TextMessageInfo @@ -458,6 +473,7 @@ | RobotextMessageInfo | SidebarSourceMessageInfo | ReactionMessageInfo + | CompoundReactionMessageInfo | EditMessageInfo | DeleteMessageInfo; diff --git a/lib/types/messages/compound-reaction.js b/lib/types/messages/compound-reaction.js new file mode 100644 --- /dev/null +++ b/lib/types/messages/compound-reaction.js @@ -0,0 +1,56 @@ +// @flow + +import t, { type TInterface } from 'tcomb'; + +import { tID, tNumber, tShape, tUserID } from '../../utils/validation-utils.js'; +import { messageTypes } from '../message-types-enum.js'; +import type { RelativeUserInfo } from '../user-types.js'; + +export type CompoundReactionInfo = { + +count: number, + +viewerReacted: boolean, +}; + +export type CompoundReactionMessageData = { + +type: 24, + +localID?: string, // for optimistic creations. included by new clients + +threadID: string, + +creatorID: string, // doesn't make too much sense, but our types require it + +time: number, + +targetMessageID: string, + +reactions: { +[reaction: string]: CompoundReactionInfo }, +}; + +export type RawCompoundReactionMessageInfo = $ReadOnly<{ + ...CompoundReactionMessageData, + +id?: string, // null if local copy without ID yet +}>; + +export const rawCompoundReactionMessageInfoValidator: TInterface = + tShape({ + type: tNumber(messageTypes.COMPOUND_REACTION), + localID: t.maybe(t.String), + threadID: tID, + time: t.Number, + targetMessageID: tID, + reactions: t.dict( + t.String, + tShape({ + count: t.Number, + viewerReacted: t.Boolean, + }), + ), + id: t.maybe(tID), + creatorID: tUserID, + }); + +export type CompoundReactionMessageInfo = { + +type: 24, + +id?: string, // null if local copy without ID yet + +localID?: string, // for optimistic creations + +threadID: string, + +creator: RelativeUserInfo, + +time: number, + +targetMessageID: string, + +reactions: { +[reaction: string]: CompoundReactionInfo }, +}; diff --git a/lib/types/messages/unsupported.js b/lib/types/messages/unsupported.js --- a/lib/types/messages/unsupported.js +++ b/lib/types/messages/unsupported.js @@ -6,6 +6,8 @@ rawChangeRoleMessageInfoValidator, type RawChangeRoleMessageInfo, } from './change-role.js'; +import type { RawCompoundReactionMessageInfo } from './compound-reaction.js'; +import { rawCompoundReactionMessageInfoValidator } from './compound-reaction.js'; import type { RawDeleteMessageInfo } from './delete.js'; import { rawDeleteMessageInfoValidator } from './delete.js'; import { @@ -47,7 +49,8 @@ | RawMediaMessageInfo | RawReactionMessageInfo | RawTogglePinMessageInfo - | RawDeleteMessageInfo, + | RawDeleteMessageInfo + | RawCompoundReactionMessageInfo, }; export const rawUnsupportedMessageInfoValidator: TInterface = @@ -69,6 +72,7 @@ rawReactionMessageInfoValidator, rawTogglePinMessageInfoValidator, rawDeleteMessageInfoValidator, + rawCompoundReactionMessageInfoValidator, t.Object, ]), }); @@ -88,5 +92,6 @@ | RawMediaMessageInfo | RawReactionMessageInfo | RawTogglePinMessageInfo - | RawDeleteMessageInfo, + | RawDeleteMessageInfo + | RawCompoundReactionMessageInfo, }; diff --git a/native/cpp/CommonCpp/NativeModules/PersistentStorageUtilities/MessageOperationsUtilities/MessageSpecs.h b/native/cpp/CommonCpp/NativeModules/PersistentStorageUtilities/MessageOperationsUtilities/MessageSpecs.h --- a/native/cpp/CommonCpp/NativeModules/PersistentStorageUtilities/MessageOperationsUtilities/MessageSpecs.h +++ b/native/cpp/CommonCpp/NativeModules/PersistentStorageUtilities/MessageOperationsUtilities/MessageSpecs.h @@ -3,6 +3,7 @@ #include "MessageSpecs/AddMembersMessageSpec.h" #include "MessageSpecs/ChangeRoleMessageSpec.h" #include "MessageSpecs/ChangeSettingsMessageSpec.h" +#include "MessageSpecs/CompoundReactionMessageSpec.h" #include "MessageSpecs/CreateEntryMessageSpec.h" #include "MessageSpecs/CreateSidebarMessageSpec.h" #include "MessageSpecs/CreateSubThreadMessageSpec.h" @@ -97,6 +98,9 @@ std::make_unique()}); message_specs_initializer.insert( {MessageType::DELETE_MESSAGE, std::make_unique()}); + message_specs_initializer.insert( + {MessageType::COMPOUND_REACTION, + std::make_unique()}); return message_specs_initializer; }(); diff --git a/native/cpp/CommonCpp/NativeModules/PersistentStorageUtilities/MessageOperationsUtilities/MessageSpecs/CompoundReactionMessageSpec.h b/native/cpp/CommonCpp/NativeModules/PersistentStorageUtilities/MessageOperationsUtilities/MessageSpecs/CompoundReactionMessageSpec.h new file mode 100644 --- /dev/null +++ b/native/cpp/CommonCpp/NativeModules/PersistentStorageUtilities/MessageOperationsUtilities/MessageSpecs/CompoundReactionMessageSpec.h @@ -0,0 +1,15 @@ +#pragma once + +#include "MessageSpec.h" + +namespace comm { +class CompoundReactionMessageSpec : public MessageSpec { + virtual std::unique_ptr + messageContentForClientDB(const folly::dynamic &rawMessageInfo) override { + folly::dynamic reactionData = folly::dynamic::object( + "targetMessageID", rawMessageInfo["targetMessageID"])( + "reactions", rawMessageInfo["reactions"]); + return std::make_unique(folly::toJson(reactionData)); + } +}; +} // namespace comm diff --git a/native/cpp/CommonCpp/NativeModules/PersistentStorageUtilities/MessageOperationsUtilities/MessageTypeEnum.h b/native/cpp/CommonCpp/NativeModules/PersistentStorageUtilities/MessageOperationsUtilities/MessageTypeEnum.h --- a/native/cpp/CommonCpp/NativeModules/PersistentStorageUtilities/MessageOperationsUtilities/MessageTypeEnum.h +++ b/native/cpp/CommonCpp/NativeModules/PersistentStorageUtilities/MessageOperationsUtilities/MessageTypeEnum.h @@ -28,6 +28,7 @@ TOGGLE_PIN = 21, UPDATE_RELATIONSHIP = 22, DELETE_MESSAGE = 23, + COMPOUND_REACTION = 24, }; } // namespace comm diff --git a/native/ios/Comm.xcodeproj/project.pbxproj b/native/ios/Comm.xcodeproj/project.pbxproj --- a/native/ios/Comm.xcodeproj/project.pbxproj +++ b/native/ios/Comm.xcodeproj/project.pbxproj @@ -169,6 +169,7 @@ 0E02677C2D81ED6600788249 /* DMOperationStore.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = DMOperationStore.cpp; path = PersistentStorageUtilities/DataStores/DMOperationStore.cpp; sourceTree = ""; }; 0E02677E2D81ED7B00788249 /* DMOperationStoreOperations.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DMOperationStoreOperations.h; sourceTree = ""; }; 0E0267862D970E9500788249 /* DeleteMessageSpec.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = DeleteMessageSpec.h; path = PersistentStorageUtilities/MessageOperationsUtilities/MessageSpecs/DeleteMessageSpec.h; sourceTree = ""; }; + 0EE91AA82E7497A8006684F8 /* CompoundReactionMessageSpec.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = CompoundReactionMessageSpec.h; path = PersistentStorageUtilities/MessageOperationsUtilities/MessageSpecs/CompoundReactionMessageSpec.h; sourceTree = ""; }; 13B07F961A680F5B00A75B9A /* Comm.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Comm.app; sourceTree = BUILT_PRODUCTS_DIR; }; 13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = Comm/Images.xcassets; sourceTree = ""; }; 13B07FB61A68108700A75B9A /* Info.release.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.release.plist; path = Comm/Info.release.plist; sourceTree = ""; }; @@ -879,6 +880,7 @@ CB38F2B9286C6C970010535C /* UnsupportedMessageSpec.h */, CB38F2BF286C6C980010535C /* UpdateRelationshipMessageSpec.h */, 0E0267862D970E9500788249 /* DeleteMessageSpec.h */, + 0EE91AA82E7497A8006684F8 /* CompoundReactionMessageSpec.h */, ); name = MessageSpecs; sourceTree = "";