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 @@ -486,7 +486,9 @@ messageInfo.type === messageTypes.REACTION || messageInfo.type === messageTypes.EDIT_MESSAGE || messageInfo.type === messageTypes.DELETE_MESSAGE || - messageInfo.type === messageTypes.COMPOUND_REACTION + messageInfo.type === messageTypes.COMPOUND_REACTION || + (messageInfo.type === messageTypes.UNSUPPORTED && + messageInfo.unsupportedMessageInfo.type === messageTypes.PLAIN) ) { continue; } diff --git a/lib/shared/farcaster/farcaster-hooks.js b/lib/shared/farcaster/farcaster-hooks.js --- a/lib/shared/farcaster/farcaster-hooks.js +++ b/lib/shared/farcaster/farcaster-hooks.js @@ -500,7 +500,11 @@ const fcUserInfos = await fetchUsersByFIDs(userFIDs); const rawMessageInfos = farcasterMessages.flatMap(message => - convertFarcasterMessageToCommMessages(message, fcUserInfos), + convertFarcasterMessageToCommMessages( + message, + fcUserInfos, + addLog, + ), ); userIDs.push( @@ -880,6 +884,7 @@ const threadInfos = useSelector(state => state.threadStore.threadInfos); const fetchConversationWithMessages = useFetchConversationWithMessages(); const currentlyFetchedConversations = React.useRef>(new Set()); + const { addLog } = useDebugLogs(); return React.useCallback( async (farcasterMessage: FarcasterMessage) => { @@ -918,6 +923,7 @@ const rawMessageInfos = convertFarcasterMessageToCommMessages( farcasterMessage, fcUserInfos, + addLog, ); const userIDs = userFIDs.map(fid => userIDFromFID(`${fid}`)); @@ -942,6 +948,7 @@ }); }, [ + addLog, dispatch, fetchConversationWithMessages, fetchMessage, 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 @@ -16,6 +16,7 @@ import { leaveThreadMessageSpec } from './leave-thread-message-spec.js'; import { type MessageSpec } from './message-spec.js'; import { multimediaMessageSpec } from './multimedia-message-spec.js'; +import { plainMessageSpec } from './plain-message-spec.js'; import { reactionMessageSpec } from './reaction-message-spec.js'; import { removeMembersMessageSpec } from './remove-members-message-spec.js'; import { restoreEntryMessageSpec } from './restore-entry-message-spec.js'; @@ -57,4 +58,5 @@ [messageTypes.UPDATE_RELATIONSHIP]: updateRelationshipMessageSpec, [messageTypes.DELETE_MESSAGE]: deleteMessageSpec, [messageTypes.COMPOUND_REACTION]: compoundReactionMessageSpec, + [messageTypes.PLAIN]: plainMessageSpec, }); diff --git a/lib/shared/messages/plain-message-spec.js b/lib/shared/messages/plain-message-spec.js new file mode 100644 --- /dev/null +++ b/lib/shared/messages/plain-message-spec.js @@ -0,0 +1,75 @@ +// @flow + +import invariant from 'invariant'; + +import { messageNotifyTypes, type MessageSpec } from './message-spec.js'; +import { messageTypes } from '../../types/message-types-enum.js'; +import type { ClientDBMessageInfo } from '../../types/message-types.js'; +import { + type PlainMessageInfo, + type RawPlainMessageInfo, + rawPlainMessageInfoValidator, +} from '../../types/messages/plain.js'; +import type { RelativeUserInfo } from '../../types/user-types.js'; +import { type EntityText, ET } from '../../utils/entity-text.js'; + +export const plainMessageSpec: MessageSpec< + null, + RawPlainMessageInfo, + PlainMessageInfo, +> = Object.freeze({ + messageContentForClientDB(data: RawPlainMessageInfo): string { + return JSON.stringify({ + rawContent: data.rawContent, + }); + }, + + rawMessageInfoFromClientDB( + clientDBMessageInfo: ClientDBMessageInfo, + ): RawPlainMessageInfo { + invariant( + clientDBMessageInfo.content !== undefined && + clientDBMessageInfo.content !== null, + 'content must be defined for Raw', + ); + const content = JSON.parse(clientDBMessageInfo.content); + return { + type: messageTypes.PLAIN, + id: clientDBMessageInfo.id, + threadID: clientDBMessageInfo.thread, + time: parseInt(clientDBMessageInfo.time), + creatorID: clientDBMessageInfo.user, + rawContent: content.rawContent ?? '', + }; + }, + + createMessageInfo( + rawMessageInfo: RawPlainMessageInfo, + creator: RelativeUserInfo, + ): PlainMessageInfo { + const messageInfo: PlainMessageInfo = { + type: messageTypes.PLAIN, + threadID: rawMessageInfo.threadID, + creator, + time: rawMessageInfo.time, + rawContent: rawMessageInfo.rawContent, + }; + if (rawMessageInfo.id) { + return { ...messageInfo, id: rawMessageInfo.id }; + } + return messageInfo; + }, + + robotext(messageInfo: PlainMessageInfo): EntityText { + const creator = ET.user({ userInfo: messageInfo.creator }); + return ET`${creator} sent a message`; + }, + + getMessageNotifyType: async () => messageNotifyTypes.NOTIF_AND_SET_UNREAD, + + canBeSidebarSource: false, + + canBePinned: false, + + validator: rawPlainMessageInfoValidator, +}); diff --git a/lib/shared/messages/unsupported-message-spec.js b/lib/shared/messages/unsupported-message-spec.js --- a/lib/shared/messages/unsupported-message-spec.js +++ b/lib/shared/messages/unsupported-message-spec.js @@ -79,4 +79,7 @@ canBePinned: false, validator: rawUnsupportedMessageInfoValidator, + + showInMessagePreview: (messageInfo: UnsupportedMessageInfo) => + messageInfo.unsupportedMessageInfo.type !== messageTypes.PLAIN, }); diff --git a/lib/shared/unshim-utils.js b/lib/shared/unshim-utils.js --- a/lib/shared/unshim-utils.js +++ b/lib/shared/unshim-utils.js @@ -29,6 +29,11 @@ if (!unshimTypes.has(messageInfo.unsupportedMessageInfo.type)) { return messageInfo; } + // Unshimming the plain messages should always consist of converting it, + // so we can't use the existing mechanism where we just return the message + if (messageInfo.unsupportedMessageInfo.type === messageTypes.PLAIN) { + return messageInfo; + } const unwrapped = messageInfo.unsupportedMessageInfo; const { unshimMessageInfo } = messageSpecs[unwrapped.type]; const unshimmed = unshimMessageInfo?.(unwrapped, messageInfo); 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 @@ -39,6 +39,7 @@ UPDATE_RELATIONSHIP: 22, DELETE_MESSAGE: 23, COMPOUND_REACTION: 24, + PLAIN: 25, }); export type MessageType = $Values; export function assertMessageType(ourMessageType: number): MessageType { @@ -67,7 +68,8 @@ ourMessageType === 21 || ourMessageType === 22 || ourMessageType === 23 || - ourMessageType === 24, + ourMessageType === 24 || + ourMessageType === 25, 'number is not MessageType enum', ); return ourMessageType; diff --git a/lib/types/messages/plain.js b/lib/types/messages/plain.js new file mode 100644 --- /dev/null +++ b/lib/types/messages/plain.js @@ -0,0 +1,35 @@ +// @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 RawPlainMessageInfo = { + +type: 25, + +id?: string, + +threadID: string, + +creatorID: string, + +time: number, + +rawContent: string, +}; + +export const rawPlainMessageInfoValidator: TInterface = + tShape({ + type: tNumber(messageTypes.PLAIN), + id: t.maybe(tID), + threadID: tID, + creatorID: tUserID, + time: t.Number, + rawContent: t.String, + }); + +export type PlainMessageInfo = { + +type: 25, + +id?: string, + +threadID: string, + +creator: RelativeUserInfo, + +time: number, + +rawContent: string, +}; 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 @@ -18,6 +18,8 @@ rawMediaMessageInfoValidator, type RawMediaMessageInfo, } from './media.js'; +import type { RawPlainMessageInfo } from './plain.js'; +import { rawPlainMessageInfoValidator } from './plain.js'; import { rawReactionMessageInfoValidator, type RawReactionMessageInfo, @@ -50,7 +52,8 @@ | RawReactionMessageInfo | RawTogglePinMessageInfo | RawDeleteMessageInfo - | RawCompoundReactionMessageInfo, + | RawCompoundReactionMessageInfo + | RawPlainMessageInfo, }; export const rawUnsupportedMessageInfoValidator: TInterface = @@ -73,6 +76,7 @@ rawTogglePinMessageInfoValidator, rawDeleteMessageInfoValidator, rawCompoundReactionMessageInfoValidator, + rawPlainMessageInfoValidator, t.Object, ]), }); @@ -93,5 +97,6 @@ | RawReactionMessageInfo | RawTogglePinMessageInfo | RawDeleteMessageInfo - | RawCompoundReactionMessageInfo, + | RawCompoundReactionMessageInfo + | RawPlainMessageInfo, }; diff --git a/lib/utils/convert-farcaster-message-to-comm-messages.js b/lib/utils/convert-farcaster-message-to-comm-messages.js --- a/lib/utils/convert-farcaster-message-to-comm-messages.js +++ b/lib/utils/convert-farcaster-message-to-comm-messages.js @@ -2,6 +2,8 @@ import uuid from 'uuid'; import { makeFarcasterBlobMediaURI } from './blob-service.js'; +import type { AddLogCallback } from '../components/debug-logs-context.js'; +import { logTypes } from '../components/debug-logs-context.js'; import type { FCUserInfos } from '../hooks/user-identities-hooks.js'; import type { FarcasterMessage } from '../shared/farcaster/farcaster-messages-types.js'; import { @@ -21,6 +23,7 @@ function convertFarcasterMessageToCommMessages( farcasterMessage: FarcasterMessage, fcUserInfos: FCUserInfos, + addLog: AddLogCallback, ): $ReadOnlyArray { const senderFid = farcasterMessage.senderFid.toString(); const creatorID = @@ -180,10 +183,27 @@ removedUserIDs: [removedUserID], }); } else { - console.log( - 'UNSUPPORTED FARCASTER MESSAGE', - JSON.stringify(farcasterMessage, null, 2), + addLog( + 'Unsupported Farcaster message', + `Unsupported message type: ${farcasterMessage.type}`, + new Set([logTypes.FARCASTER]), ); + result.push({ + type: messageTypes.UNSUPPORTED, + id: `${farcasterMessage.messageId}/unsupported`, + threadID, + creatorID, + time: parseInt(farcasterMessage.serverTimestamp, 10), + robotext: 'Unsupported message type', + unsupportedMessageInfo: { + type: messageTypes.PLAIN, + id: farcasterMessage.messageId, + threadID, + creatorID, + time: parseInt(farcasterMessage.serverTimestamp, 10), + rawContent: JSON.stringify(farcasterMessage), + }, + }); } return result; 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 @@ -16,6 +16,7 @@ #include "MessageSpecs/LeaveThreadMessageSpec.h" #include "MessageSpecs/MessageSpec.h" #include "MessageSpecs/MultimediaMessageSpec.h" +#include "MessageSpecs/PlainMessageSpec.h" #include "MessageSpecs/ReactionMessageSpec.h" #include "MessageSpecs/RemoveMembersMessageSpec.h" #include "MessageSpecs/RestoreEntryMessageSpec.h" @@ -101,6 +102,8 @@ message_specs_initializer.insert( {MessageType::COMPOUND_REACTION, std::make_unique()}); + message_specs_initializer.insert( + {MessageType::PLAIN, std::make_unique()}); return message_specs_initializer; }(); diff --git a/native/cpp/CommonCpp/NativeModules/PersistentStorageUtilities/MessageOperationsUtilities/MessageSpecs/PlainMessageSpec.h b/native/cpp/CommonCpp/NativeModules/PersistentStorageUtilities/MessageOperationsUtilities/MessageSpecs/PlainMessageSpec.h new file mode 100644 --- /dev/null +++ b/native/cpp/CommonCpp/NativeModules/PersistentStorageUtilities/MessageOperationsUtilities/MessageSpecs/PlainMessageSpec.h @@ -0,0 +1,14 @@ +#pragma once + +#include "MessageSpec.h" + +namespace comm { +class PlainMessageSpec : public MessageSpec { + virtual std::unique_ptr + messageContentForClientDB(const folly::dynamic &rawMessageInfo) override { + folly::dynamic content = folly::dynamic::object; + content["rawContent"] = rawMessageInfo["rawContent"]; + return std::make_unique(folly::toJson(content)); + } +}; +} // 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 @@ -29,6 +29,7 @@ UPDATE_RELATIONSHIP = 22, DELETE_MESSAGE = 23, COMPOUND_REACTION = 24, + PLAIN = 25, }; } // 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 @@ -364,6 +364,7 @@ CB38F2BD286C6C970010535C /* MultimediaMessageSpec.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MultimediaMessageSpec.h; path = PersistentStorageUtilities/MessageOperationsUtilities/MessageSpecs/MultimediaMessageSpec.h; sourceTree = ""; }; CB38F2BE286C6C980010535C /* DeleteEntryMessageSpec.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DeleteEntryMessageSpec.h; path = PersistentStorageUtilities/MessageOperationsUtilities/MessageSpecs/DeleteEntryMessageSpec.h; sourceTree = ""; }; CB38F2BF286C6C980010535C /* UpdateRelationshipMessageSpec.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = UpdateRelationshipMessageSpec.h; path = PersistentStorageUtilities/MessageOperationsUtilities/MessageSpecs/UpdateRelationshipMessageSpec.h; sourceTree = ""; }; + CB38F2C0286C6C980010535C /* PlainMessageSpec.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PlainMessageSpec.h; path = PersistentStorageUtilities/MessageOperationsUtilities/MessageSpecs/PlainMessageSpec.h; sourceTree = ""; }; CB3C621327CE66540054F24C /* libEXSecureStore.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; path = libEXSecureStore.a; sourceTree = BUILT_PRODUCTS_DIR; }; CB3CCAFF2B7246F400793640 /* NativeSQLiteConnectionManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = NativeSQLiteConnectionManager.h; sourceTree = ""; }; CB3CCB002B7246F400793640 /* NativeSQLiteConnectionManager.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = NativeSQLiteConnectionManager.cpp; sourceTree = ""; }; @@ -874,6 +875,7 @@ CB38F2BB286C6C970010535C /* EditEntryMessageSpec.h */, CB38F2B7286C6C970010535C /* MessageSpec.h */, CB38F2BD286C6C970010535C /* MultimediaMessageSpec.h */, + CB38F2C0286C6C980010535C /* PlainMessageSpec.h */, 7F446E2229C3AF3800670288 /* ReactionMessageSpec.h */, CB38F2B6286C6C970010535C /* RestoreEntryMessageSpec.h */, CB38F2B3286C6C970010535C /* TextMessageSpec.h */,