diff --git a/lib/shared/messages/text-message-spec.js b/lib/shared/messages/text-message-spec.js index 650afc47e..2ec145a48 100644 --- a/lib/shared/messages/text-message-spec.js +++ b/lib/shared/messages/text-message-spec.js @@ -1,188 +1,190 @@ // @flow import invariant from 'invariant'; import * as SimpleMarkdown from 'simple-markdown'; import { messageTypes } from '../../types/message-types'; import type { MessageInfo, ClientDBMessageInfo, } from '../../types/message-types'; import type { RawTextMessageInfo, TextMessageData, TextMessageInfo, } from '../../types/messages/text'; import type { NotifTexts } from '../../types/notif-types'; import type { ThreadInfo } from '../../types/thread-types'; import type { RelativeUserInfo } from '../../types/user-types'; import { type ASTNode, type SingleASTNode } from '../markdown'; import { threadIsGroupChat } from '../thread-utils'; import { stringForUser } from '../user-utils'; import type { MessageSpec, NotificationTextsParams, RawMessageInfoFromServerDBRowParams, } from './message-spec'; import { assertSingleMessageInfo } from './utils'; /** * most of the markdown leaves contain `content` field (it is an array or a string) * apart from lists, which have `items` field (that holds an array) */ const rawTextFromMarkdownAST = (node: ASTNode): string => { if (Array.isArray(node)) { return node.map(rawTextFromMarkdownAST).join(''); } const { content, items } = node; if (content && typeof content === 'string') { return content; } else if (items) { return rawTextFromMarkdownAST(items); } else if (content) { return rawTextFromMarkdownAST(content); } return ''; }; const getFirstNonQuotedRawLine = ( nodes: $ReadOnlyArray, ): string => { let result = 'message'; for (const node of nodes) { if (node.type === 'blockQuote') { result = 'quoted message'; } else { const rawText = rawTextFromMarkdownAST(node); if (!rawText || !rawText.replace(/\s/g, '')) { // handles the case of an empty(or containing only white spaces) // new line that usually occurs between a quote and the rest // of the message(we don't want it as a title, thus continue) continue; } return rawText; } } return result; }; export const textMessageSpec: MessageSpec< TextMessageData, RawTextMessageInfo, TextMessageInfo, > = Object.freeze({ messageContentForServerDB( data: TextMessageData | RawTextMessageInfo, ): string { return data.text; }, messageContentForClientDB(data: RawTextMessageInfo): string { return this.messageContentForServerDB(data); }, messageTitle({ messageInfo, markdownRules }) { const { text } = messageInfo; const parser = SimpleMarkdown.parserFor(markdownRules); const ast = parser(text, { disableAutoBlockNewlines: true }); return getFirstNonQuotedRawLine(ast).trim(); }, rawMessageInfoFromServerDBRow( row: Object, params: RawMessageInfoFromServerDBRowParams, ): RawTextMessageInfo { const rawTextMessageInfo: RawTextMessageInfo = { type: messageTypes.TEXT, id: row.id.toString(), threadID: row.threadID.toString(), time: row.time, creatorID: row.creatorID.toString(), text: row.content, }; if (params.localID) { rawTextMessageInfo.localID = params.localID; } return rawTextMessageInfo; }, rawMessageInfoFromClientDB( clientDBMessageInfo: ClientDBMessageInfo, ): RawTextMessageInfo { const rawTextMessageInfo: RawTextMessageInfo = { type: messageTypes.TEXT, - id: clientDBMessageInfo.id, threadID: clientDBMessageInfo.thread, time: parseInt(clientDBMessageInfo.time), creatorID: clientDBMessageInfo.user, text: clientDBMessageInfo.content ?? '', }; if (clientDBMessageInfo.local_id) { rawTextMessageInfo.localID = clientDBMessageInfo.local_id; } + if (clientDBMessageInfo.id !== clientDBMessageInfo.local_id) { + rawTextMessageInfo.id = clientDBMessageInfo.id; + } return rawTextMessageInfo; }, createMessageInfo( rawMessageInfo: RawTextMessageInfo, creator: RelativeUserInfo, ): TextMessageInfo { const messageInfo: TextMessageInfo = { type: messageTypes.TEXT, threadID: rawMessageInfo.threadID, creator, time: rawMessageInfo.time, text: rawMessageInfo.text, }; if (rawMessageInfo.id) { messageInfo.id = rawMessageInfo.id; } if (rawMessageInfo.localID) { messageInfo.localID = rawMessageInfo.localID; } return messageInfo; }, rawMessageInfoFromMessageData( messageData: TextMessageData, id: ?string, ): RawTextMessageInfo { if (id) { return { ...messageData, id }; } else { return { ...messageData }; } }, notificationTexts( messageInfos: $ReadOnlyArray, threadInfo: ThreadInfo, params: NotificationTextsParams, ): NotifTexts { const messageInfo = assertSingleMessageInfo(messageInfos); invariant( messageInfo.type === messageTypes.TEXT, 'messageInfo should be messageTypes.TEXT!', ); if (!threadInfo.name && !threadIsGroupChat(threadInfo)) { return { merged: `${threadInfo.uiName}: ${messageInfo.text}`, body: messageInfo.text, title: threadInfo.uiName, }; } else { const userString = stringForUser(messageInfo.creator); const threadName = params.notifThreadName(threadInfo); return { merged: `${userString} to ${threadName}: ${messageInfo.text}`, body: messageInfo.text, title: threadInfo.uiName, prefix: `${userString}:`, }; } }, generatesNotifs: true, includedInRepliesCount: true, }); diff --git a/lib/utils/message-ops-utils.test.js b/lib/utils/message-ops-utils.test.js index 50b46f5bc..8d2442d0c 100644 --- a/lib/utils/message-ops-utils.test.js +++ b/lib/utils/message-ops-utils.test.js @@ -1,338 +1,355 @@ // @flow import type { RawSidebarSourceMessageInfo } from '../types/message-types'; import type { RawAddMembersMessageInfo } from '../types/messages/add-members'; import type { RawChangeSettingsMessageInfo } from '../types/messages/change-settings'; import type { RawCreateEntryMessageInfo } from '../types/messages/create-entry'; import type { RawCreateSidebarMessageInfo } from '../types/messages/create-sidebar'; import type { RawCreateSubthreadMessageInfo } from '../types/messages/create-subthread'; import type { RawCreateThreadMessageInfo } from '../types/messages/create-thread.js'; import type { RawDeleteEntryMessageInfo } from '../types/messages/delete-entry'; import type { RawEditEntryMessageInfo } from '../types/messages/edit-entry'; import type { RawImagesMessageInfo } from '../types/messages/images'; import type { RawJoinThreadMessageInfo } from '../types/messages/join-thread'; import type { RawLeaveThreadMessageInfo } from '../types/messages/leave-thread'; import type { RawRemoveMembersMessageInfo } from '../types/messages/remove-members'; import type { RawRestoreEntryMessageInfo } from '../types/messages/restore-entry'; import type { RawTextMessageInfo } from '../types/messages/text'; import type { RawUpdateRelationshipMessageInfo } from '../types/messages/update-relationship'; import { translateRawMessageInfoToClientDBMessageInfo, translateClientDBMessageInfoToRawMessageInfo, } from './message-ops-utils'; test('TEXT: rawMessageInfo -> clientDBMessageInfo -> rawMessageInfo', () => { const rawTextMessageInfo: RawTextMessageInfo = { type: 0, localID: 'local7', threadID: '85466', text: 'Hello world', creatorID: '85435', time: 1637788332565, id: '85551', }; expect( translateClientDBMessageInfoToRawMessageInfo( translateRawMessageInfoToClientDBMessageInfo(rawTextMessageInfo), ), ).toStrictEqual(rawTextMessageInfo); }); +test('TEXT (local): rawMessageInfo -> clientDBMessageInfo -> rawMessageInfo', () => { + const localRawTextMessageInfo: RawTextMessageInfo = { + type: 0, + localID: 'local7', + threadID: '85466', + text: 'Hello world', + creatorID: '85435', + time: 1637788332565, + }; + + expect( + translateClientDBMessageInfoToRawMessageInfo( + translateRawMessageInfoToClientDBMessageInfo(localRawTextMessageInfo), + ), + ).toStrictEqual(localRawTextMessageInfo); +}); + test('CREATE_THREAD: rawMessageInfo -> clientDBMessageInfo -> rawMessageInfo', () => { const rawCreateThreadMessageInfo: RawCreateThreadMessageInfo = { type: 1, threadID: '85466', creatorID: '85435', time: 1637778853178, initialThreadState: { type: 6, name: null, parentThreadID: '1', color: '648CAA', memberIDs: ['256', '85435'], }, id: '85482', }; expect( translateClientDBMessageInfoToRawMessageInfo( translateRawMessageInfoToClientDBMessageInfo(rawCreateThreadMessageInfo), ), ).toStrictEqual(rawCreateThreadMessageInfo); }); test('ADD_MEMBER: rawMessageInfo -> clientDBMessageInfo -> rawMessageInfo', () => { const rawAddMemberMessageInfo: RawAddMembersMessageInfo = { type: 2, threadID: '85946', creatorID: '83809', time: 1638236346010, addedUserIDs: ['256'], id: '85986', }; expect( translateClientDBMessageInfoToRawMessageInfo( translateRawMessageInfoToClientDBMessageInfo(rawAddMemberMessageInfo), ), ).toStrictEqual(rawAddMemberMessageInfo); }); test('CREATE_SUB_THREAD: rawMessageInfo -> clientDBMessageInfo -> rawMessageInfo', () => { const rawCreateSubthreadMessageInfo: RawCreateSubthreadMessageInfo = { type: 3, threadID: '85946', creatorID: '83809', time: 1638237345553, childThreadID: '85990', id: '85997', }; expect( translateClientDBMessageInfoToRawMessageInfo( translateRawMessageInfoToClientDBMessageInfo( rawCreateSubthreadMessageInfo, ), ), ).toStrictEqual(rawCreateSubthreadMessageInfo); }); test('CHANGE_SETTINGS: rawMessageInfo -> clientDBMessageInfo -> rawMessageInfo', () => { const rawChangeSettingsMessageInfo: RawChangeSettingsMessageInfo = { type: 4, threadID: '85946', creatorID: '83809', time: 1638236125774, field: 'color', value: '009cc8', id: '85972', }; expect( translateClientDBMessageInfoToRawMessageInfo( translateRawMessageInfoToClientDBMessageInfo( rawChangeSettingsMessageInfo, ), ), ).toStrictEqual(rawChangeSettingsMessageInfo); }); test('REMOVE_MEMBERS: rawMessageInfo -> clientDBMessageInfo -> rawMessageInfo', () => { const rawRemoveMembersMessageInfo: RawRemoveMembersMessageInfo = { type: 5, threadID: '85990', creatorID: '83809', time: 1638237832234, removedUserIDs: ['85435'], id: '86014', }; expect( translateClientDBMessageInfoToRawMessageInfo( translateRawMessageInfoToClientDBMessageInfo(rawRemoveMembersMessageInfo), ), ).toStrictEqual(rawRemoveMembersMessageInfo); }); test('LEAVE_THREAD: rawMessageInfo -> clientDBMessageInfo -> rawMessageInfo', () => { const rawLeaveThreadMessageInfo: RawLeaveThreadMessageInfo = { type: 7, id: '86088', threadID: '85946', time: 1638238389038, creatorID: '85435', }; expect( translateClientDBMessageInfoToRawMessageInfo( translateRawMessageInfoToClientDBMessageInfo(rawLeaveThreadMessageInfo), ), ).toStrictEqual(rawLeaveThreadMessageInfo); }); test('JOIN_THREAD: rawMessageInfo -> clientDBMessageInfo -> rawMessageInfo', () => { const rawJoinThreadMessageInfo: RawJoinThreadMessageInfo = { type: 8, threadID: '86125', creatorID: '85435', time: 1638239691665, id: '86149', }; expect( translateClientDBMessageInfoToRawMessageInfo( translateRawMessageInfoToClientDBMessageInfo(rawJoinThreadMessageInfo), ), ).toStrictEqual(rawJoinThreadMessageInfo); }); test('CREATE_ENTRY: rawMessageInfo -> clientDBMessageInfo -> rawMessageInfo', () => { const rawCreateEntryMessageInfo: RawCreateEntryMessageInfo = { type: 9, threadID: '85630', creatorID: '85435', time: 1638239928303, entryID: '86151', date: '2021-11-29', text: 'Hello world', id: '86154', }; expect( translateClientDBMessageInfoToRawMessageInfo( translateRawMessageInfoToClientDBMessageInfo(rawCreateEntryMessageInfo), ), ).toStrictEqual(rawCreateEntryMessageInfo); }); test('EDIT_ENTRY: rawMessageInfo -> clientDBMessageInfo -> rawMessageInfo', () => { const rawEditEntryMessageInfo: RawEditEntryMessageInfo = { type: 10, threadID: '85630', creatorID: '85435', time: 1638240110661, entryID: '86151', date: '2021-11-29', text: 'Hello universe', id: '86179', }; expect( translateClientDBMessageInfoToRawMessageInfo( translateRawMessageInfoToClientDBMessageInfo(rawEditEntryMessageInfo), ), ).toStrictEqual(rawEditEntryMessageInfo); }); test('DELETE_ENTRY: rawMessageInfo -> clientDBMessageInfo -> rawMessageInfo', () => { const rawDeleteEntryMessageInfo: RawDeleteEntryMessageInfo = { type: 11, threadID: '85630', creatorID: '85435', time: 1638240286574, entryID: '86151', date: '2021-11-29', text: 'Hello universe', id: '86189', }; expect( translateClientDBMessageInfoToRawMessageInfo( translateRawMessageInfoToClientDBMessageInfo(rawDeleteEntryMessageInfo), ), ).toStrictEqual(rawDeleteEntryMessageInfo); }); test('RESTORE_ENTRY: rawMessageInfo -> clientDBMessageInfo -> rawMessageInfo', () => { const rawRestoreEntryMessageInfo: RawRestoreEntryMessageInfo = { type: 12, threadID: '85630', creatorID: '83809', time: 1638240605195, entryID: '86151', date: '2021-11-29', text: 'Hello universe', id: '86211', }; expect( translateClientDBMessageInfoToRawMessageInfo( translateRawMessageInfoToClientDBMessageInfo(rawRestoreEntryMessageInfo), ), ).toStrictEqual(rawRestoreEntryMessageInfo); }); test('IMAGES: rawMessageInfo -> clientDBMessageInfo -> rawMessageInfo', () => { const rawImagesMessageInfo: RawImagesMessageInfo = { type: 14, threadID: '85466', creatorID: '85435', time: 1637779260087, media: [ { id: '85504', type: 'photo', uri: 'http://localhost/comm/upload/85504/ba36cea2b5a796f6', dimensions: { width: 1920, height: 1281, }, }, ], id: '85505', }; expect( translateClientDBMessageInfoToRawMessageInfo( translateRawMessageInfoToClientDBMessageInfo(rawImagesMessageInfo), ), ).toStrictEqual(rawImagesMessageInfo); }); test('UPDATE_RELATIONSHIP: rawMessageInfo -> clientDBMessageInfo -> rawMessageInfo', () => { const rawUpdateRelationshipMessageInfo: RawUpdateRelationshipMessageInfo = { type: 16, id: '85651', threadID: '85630', time: 1638235869690, creatorID: '83809', targetID: '85435', operation: 'request_accepted', }; expect( translateClientDBMessageInfoToRawMessageInfo( translateRawMessageInfoToClientDBMessageInfo( rawUpdateRelationshipMessageInfo, ), ), ).toStrictEqual(rawUpdateRelationshipMessageInfo); }); test('SIDEBAR_SOURCE: rawMessageInfo -> clientDBMessageInfo -> rawMessageInfo', () => { const rawSidebarSourceMessageInfo: RawSidebarSourceMessageInfo = { type: 17, threadID: '86219', creatorID: '85435', time: 1638250532831, sourceMessage: { type: 0, id: '85486', threadID: '85466', time: 1637778853216, creatorID: '256', text: 'as you inevitably discover bugs, have feature requests, or design suggestions, feel free to message them to me in the app.', }, id: '86223', }; expect( translateClientDBMessageInfoToRawMessageInfo( translateRawMessageInfoToClientDBMessageInfo(rawSidebarSourceMessageInfo), ), ).toStrictEqual(rawSidebarSourceMessageInfo); }); test('CREATE_SIDEBAR: rawMessageInfo -> clientDBMessageInfo -> rawMessageInfo', () => { const rawCreateSidebarMessageInfo: RawCreateSidebarMessageInfo = { type: 18, threadID: '86219', creatorID: '85435', time: 1638250532831, sourceMessageAuthorID: '256', initialThreadState: { name: 'as you inevitably discover ...', parentThreadID: '85466', color: 'ffffff', memberIDs: ['256', '85435'], }, id: '86224', }; expect( translateClientDBMessageInfoToRawMessageInfo( translateRawMessageInfoToClientDBMessageInfo(rawCreateSidebarMessageInfo), ), ).toStrictEqual(rawCreateSidebarMessageInfo); });