diff --git a/lib/shared/messages/add-members-message-spec.js b/lib/shared/messages/add-members-message-spec.js index ca9176c9d..b32179957 100644 --- a/lib/shared/messages/add-members-message-spec.js +++ b/lib/shared/messages/add-members-message-spec.js @@ -1,156 +1,156 @@ // @flow import invariant from 'invariant'; import { messageTypes } from '../../types/message-types'; import type { MessageInfo } from '../../types/message-types'; import type { AddMembersMessageData, AddMembersMessageInfo, RawAddMembersMessageInfo, } from '../../types/messages/add-members'; import type { NotifTexts } from '../../types/notif-types'; import type { ThreadInfo } from '../../types/thread-types'; import type { RelativeUserInfo } from '../../types/user-types'; import { values } from '../../utils/objects'; import { robotextForMessageInfo, robotextToRawString, removeCreatorAsViewer, } from '../message-utils'; import type { CreateMessageInfoParams, MessageSpec, MessageTitleParam, NotificationTextsParams, RobotextParams, } from './message-spec'; import { joinResult } from './utils'; export const addMembersMessageSpec: MessageSpec< AddMembersMessageData, RawAddMembersMessageInfo, AddMembersMessageInfo, > = Object.freeze({ messageContent(data: AddMembersMessageData): string { return JSON.stringify(data.addedUserIDs); }, messageTitle({ messageInfo, threadInfo, viewerContext, }: MessageTitleParam) { let validMessageInfo: AddMembersMessageInfo = (messageInfo: AddMembersMessageInfo); if (viewerContext === 'global_viewer') { validMessageInfo = removeCreatorAsViewer(validMessageInfo); validMessageInfo = { ...validMessageInfo, addedMembers: validMessageInfo.addedMembers.map((item) => ({ ...item, isViewer: false, })), }; } return robotextToRawString( robotextForMessageInfo(validMessageInfo, threadInfo), ); }, rawMessageInfoFromRow(row: Object): RawAddMembersMessageInfo { return { type: messageTypes.ADD_MEMBERS, id: row.id.toString(), threadID: row.threadID.toString(), time: row.time, creatorID: row.creatorID.toString(), addedUserIDs: JSON.parse(row.content), }; }, createMessageInfo( rawMessageInfo: RawAddMembersMessageInfo, creator: RelativeUserInfo, params: CreateMessageInfoParams, ): AddMembersMessageInfo { const addedMembers = params.createRelativeUserInfos( rawMessageInfo.addedUserIDs, ); return { type: messageTypes.ADD_MEMBERS, id: rawMessageInfo.id, threadID: rawMessageInfo.threadID, creator, time: rawMessageInfo.time, addedMembers, }; }, rawMessageInfoFromMessageData( messageData: AddMembersMessageData, id: string, ): RawAddMembersMessageInfo { return { ...messageData, id }; }, robotext( messageInfo: AddMembersMessageInfo, creator: string, params: RobotextParams, ): string { const users = messageInfo.addedMembers; invariant(users.length !== 0, 'added who??'); const addedUsersString = params.robotextForUsers(users); return `${creator} added ${addedUsersString}`; }, notificationTexts( messageInfos: $ReadOnlyArray, threadInfo: ThreadInfo, params: NotificationTextsParams, ): NotifTexts { const addedMembersObject = {}; for (const messageInfo of messageInfos) { invariant( messageInfo.type === messageTypes.ADD_MEMBERS, 'messageInfo should be messageTypes.ADD_MEMBERS!', ); for (const member of messageInfo.addedMembers) { addedMembersObject[member.id] = member; } } const addedMembers = values(addedMembersObject); const mostRecentMessageInfo = messageInfos[0]; invariant( mostRecentMessageInfo.type === messageTypes.ADD_MEMBERS, 'messageInfo should be messageTypes.ADD_MEMBERS!', ); const mergedMessageInfo = { ...mostRecentMessageInfo, addedMembers }; const robotext = params.strippedRobotextForMessageInfo( mergedMessageInfo, threadInfo, ); const merged = `${robotext} to ${params.notifThreadName(threadInfo)}`; return { merged, title: threadInfo.uiName, body: robotext, }; }, notificationCollapseKey(rawMessageInfo: RawAddMembersMessageInfo): string { return joinResult( rawMessageInfo.type, rawMessageInfo.threadID, rawMessageInfo.creatorID, ); }, generatesNotifs: false, - userIDs(rawMessageInfo) { + userIDs(rawMessageInfo: RawAddMembersMessageInfo): $ReadOnlyArray { return rawMessageInfo.addedUserIDs; }, }); diff --git a/lib/shared/messages/create-sidebar-message-spec.js b/lib/shared/messages/create-sidebar-message-spec.js index a910203a6..6597c8ff5 100644 --- a/lib/shared/messages/create-sidebar-message-spec.js +++ b/lib/shared/messages/create-sidebar-message-spec.js @@ -1,215 +1,215 @@ // @flow import invariant from 'invariant'; import type { PlatformDetails } from '../../types/device-types'; import { messageTypes } from '../../types/message-types'; import type { MessageInfo } from '../../types/message-types'; import type { CreateSidebarMessageData, CreateSidebarMessageInfo, RawCreateSidebarMessageInfo, } from '../../types/messages/create-sidebar'; import type { RawUnsupportedMessageInfo } from '../../types/messages/unsupported'; import type { NotifTexts } from '../../types/notif-types'; import type { ThreadInfo } from '../../types/thread-types'; import type { RelativeUserInfo } from '../../types/user-types'; import { robotextToRawString, robotextForMessageInfo, removeCreatorAsViewer, } from '../message-utils'; import { stringForUser } from '../user-utils'; import { hasMinCodeVersion } from '../version-utils'; import type { CreateMessageInfoParams, MessageSpec, MessageTitleParam, RobotextParams, } from './message-spec'; import { assertSingleMessageInfo } from './utils'; export const createSidebarMessageSpec: MessageSpec< CreateSidebarMessageData, RawCreateSidebarMessageInfo, CreateSidebarMessageInfo, > = Object.freeze({ messageContent(data: CreateSidebarMessageData): string { return JSON.stringify({ ...data.initialThreadState, sourceMessageAuthorID: data.sourceMessageAuthorID, }); }, messageTitle({ messageInfo, threadInfo, viewerContext, }: MessageTitleParam) { let validMessageInfo: CreateSidebarMessageInfo = (messageInfo: CreateSidebarMessageInfo); if (viewerContext === 'global_viewer') { validMessageInfo = removeCreatorAsViewer(validMessageInfo); validMessageInfo = { ...validMessageInfo, sourceMessageAuthor: { ...validMessageInfo.sourceMessageAuthor, isViewer: false, }, initialThreadState: { ...validMessageInfo.initialThreadState, otherMembers: validMessageInfo.initialThreadState.otherMembers.map( (item) => ({ ...item, isViewer: false, }), ), }, }; } return robotextToRawString( robotextForMessageInfo(validMessageInfo, threadInfo), ); }, rawMessageInfoFromRow(row: Object): RawCreateSidebarMessageInfo { const { sourceMessageAuthorID, ...initialThreadState } = JSON.parse( row.content, ); return { type: messageTypes.CREATE_SIDEBAR, id: row.id.toString(), threadID: row.threadID.toString(), time: row.time, creatorID: row.creatorID.toString(), sourceMessageAuthorID, initialThreadState, }; }, createMessageInfo( rawMessageInfo: RawCreateSidebarMessageInfo, creator: RelativeUserInfo, params: CreateMessageInfoParams, ): ?CreateSidebarMessageInfo { const { threadInfos } = params; const parentThreadInfo = threadInfos[rawMessageInfo.initialThreadState.parentThreadID]; const sourceMessageAuthor = params.createRelativeUserInfos([ rawMessageInfo.sourceMessageAuthorID, ])[0]; if (!sourceMessageAuthor) { return null; } return { type: messageTypes.CREATE_SIDEBAR, id: rawMessageInfo.id, threadID: rawMessageInfo.threadID, creator, time: rawMessageInfo.time, sourceMessageAuthor, initialThreadState: { name: rawMessageInfo.initialThreadState.name, parentThreadInfo, color: rawMessageInfo.initialThreadState.color, otherMembers: params.createRelativeUserInfos( rawMessageInfo.initialThreadState.memberIDs.filter( (userID: string) => userID !== rawMessageInfo.creatorID, ), ), }, }; }, rawMessageInfoFromMessageData( messageData: CreateSidebarMessageData, id: string, ): RawCreateSidebarMessageInfo { return { ...messageData, id }; }, robotext( messageInfo: CreateSidebarMessageInfo, creator: string, params: RobotextParams, ): string { let text = `started ${params.encodedThreadEntity( messageInfo.threadID, `this sidebar`, )}`; const users = messageInfo.initialThreadState.otherMembers.filter( (member) => member.id !== messageInfo.sourceMessageAuthor.id, ); if (users.length !== 0) { const initialUsersString = params.robotextForUsers(users); text += ` and added ${initialUsersString}`; } return `${creator} ${text}`; }, shimUnsupportedMessageInfo( rawMessageInfo: RawCreateSidebarMessageInfo, platformDetails: ?PlatformDetails, ): RawCreateSidebarMessageInfo | RawUnsupportedMessageInfo { // TODO determine min code version if (hasMinCodeVersion(platformDetails, 75)) { 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: 'created a sidebar', unsupportedMessageInfo: rawMessageInfo, }; }, unshimMessageInfo( unwrapped: RawCreateSidebarMessageInfo, ): RawCreateSidebarMessageInfo { return unwrapped; }, notificationTexts( messageInfos: $ReadOnlyArray, threadInfo: ThreadInfo, ): NotifTexts { const messageInfo = assertSingleMessageInfo(messageInfos); invariant( messageInfo.type === messageTypes.CREATE_SIDEBAR, 'messageInfo should be messageTypes.CREATE_SIDEBAR!', ); const prefix = stringForUser(messageInfo.creator); const title = threadInfo.uiName; const sourceMessageAuthorPossessive = messageInfo.sourceMessageAuthor .isViewer ? 'your' : `${stringForUser(messageInfo.sourceMessageAuthor)}'s`; const body = `started a sidebar in response to ${sourceMessageAuthorPossessive} ` + `message "${messageInfo.initialThreadState.name ?? ''}"`; const merged = `${prefix} ${body}`; return { merged, body, title, prefix, }; }, generatesNotifs: true, - userIDs(rawMessageInfo) { + userIDs(rawMessageInfo: RawCreateSidebarMessageInfo): $ReadOnlyArray { return rawMessageInfo.initialThreadState.memberIDs; }, threadIDs(rawMessageInfo) { const { parentThreadID } = rawMessageInfo.initialThreadState; return [parentThreadID]; }, }); diff --git a/lib/shared/messages/create-thread-message-spec.js b/lib/shared/messages/create-thread-message-spec.js index db9a20c88..4e472155c 100644 --- a/lib/shared/messages/create-thread-message-spec.js +++ b/lib/shared/messages/create-thread-message-spec.js @@ -1,188 +1,188 @@ // @flow import invariant from 'invariant'; import { messageTypes } from '../../types/message-types'; import type { MessageInfo } from '../../types/message-types'; import type { CreateThreadMessageData, CreateThreadMessageInfo, RawCreateThreadMessageInfo, } from '../../types/messages/create-thread'; import type { NotifTexts } from '../../types/notif-types'; import type { ThreadInfo } from '../../types/thread-types'; import type { RelativeUserInfo } from '../../types/user-types'; import { robotextToRawString, robotextForMessageInfo, removeCreatorAsViewer, } from '../message-utils'; import { stringForUser } from '../user-utils'; import type { CreateMessageInfoParams, MessageSpec, MessageTitleParam, NotificationTextsParams, RobotextParams, } from './message-spec'; import { assertSingleMessageInfo } from './utils'; export const createThreadMessageSpec: MessageSpec< CreateThreadMessageData, RawCreateThreadMessageInfo, CreateThreadMessageInfo, > = Object.freeze({ messageContent(data: CreateThreadMessageData): string { return JSON.stringify(data.initialThreadState); }, messageTitle({ messageInfo, threadInfo, viewerContext, }: MessageTitleParam) { let validMessageInfo: CreateThreadMessageInfo = (messageInfo: CreateThreadMessageInfo); if (viewerContext === 'global_viewer') { validMessageInfo = removeCreatorAsViewer(validMessageInfo); validMessageInfo = { ...validMessageInfo, initialThreadState: { ...validMessageInfo.initialThreadState, otherMembers: validMessageInfo.initialThreadState.otherMembers.map( (item) => ({ ...item, isViewer: false, }), ), }, }; } return robotextToRawString( robotextForMessageInfo(validMessageInfo, threadInfo), ); }, rawMessageInfoFromRow(row: Object): RawCreateThreadMessageInfo { return { type: messageTypes.CREATE_THREAD, id: row.id.toString(), threadID: row.threadID.toString(), time: row.time, creatorID: row.creatorID.toString(), initialThreadState: JSON.parse(row.content), }; }, createMessageInfo( rawMessageInfo: RawCreateThreadMessageInfo, creator: RelativeUserInfo, params: CreateMessageInfoParams, ): CreateThreadMessageInfo { const initialParentThreadID = rawMessageInfo.initialThreadState.parentThreadID; const parentThreadInfo = initialParentThreadID ? params.threadInfos[initialParentThreadID] : null; return { type: messageTypes.CREATE_THREAD, id: rawMessageInfo.id, threadID: rawMessageInfo.threadID, creator, time: rawMessageInfo.time, initialThreadState: { name: rawMessageInfo.initialThreadState.name, parentThreadInfo, type: rawMessageInfo.initialThreadState.type, color: rawMessageInfo.initialThreadState.color, otherMembers: params.createRelativeUserInfos( rawMessageInfo.initialThreadState.memberIDs.filter( (userID: string) => userID !== rawMessageInfo.creatorID, ), ), }, }; }, rawMessageInfoFromMessageData( messageData: CreateThreadMessageData, id: string, ): RawCreateThreadMessageInfo { return { ...messageData, id }; }, robotext( messageInfo: CreateThreadMessageInfo, creator: string, params: RobotextParams, ): string { let text = `created ${params.encodedThreadEntity( messageInfo.threadID, `this thread`, )}`; const parentThread = messageInfo.initialThreadState.parentThreadInfo; if (parentThread) { text += ' as a child of ' + `<${encodeURI(parentThread.uiName)}|t${parentThread.id}>`; } if (messageInfo.initialThreadState.name) { text += ` with the name "${encodeURI( messageInfo.initialThreadState.name, )}"`; } const users = messageInfo.initialThreadState.otherMembers; if (users.length !== 0) { const initialUsersString = params.robotextForUsers(users); text += ` and added ${initialUsersString}`; } return `${creator} ${text}`; }, notificationTexts( messageInfos: $ReadOnlyArray, threadInfo: ThreadInfo, params: NotificationTextsParams, ): NotifTexts { const messageInfo = assertSingleMessageInfo(messageInfos); invariant( messageInfo.type === messageTypes.CREATE_THREAD, 'messageInfo should be messageTypes.CREATE_THREAD!', ); const parentThreadInfo = messageInfo.initialThreadState.parentThreadInfo; if (parentThreadInfo) { return params.notifTextForSubthreadCreation( messageInfo.creator, messageInfo.initialThreadState.type, parentThreadInfo, messageInfo.initialThreadState.name, threadInfo.uiName, ); } const prefix = stringForUser(messageInfo.creator); const body = 'created a new thread'; let merged = `${prefix} ${body}`; if (messageInfo.initialThreadState.name) { merged += ` called "${messageInfo.initialThreadState.name}"`; } return { merged, body, title: threadInfo.uiName, prefix, }; }, generatesNotifs: true, - userIDs(rawMessageInfo) { + userIDs(rawMessageInfo: RawCreateThreadMessageInfo): $ReadOnlyArray { return rawMessageInfo.initialThreadState.memberIDs; }, startsThread: true, threadIDs(rawMessageInfo) { const { parentThreadID } = rawMessageInfo.initialThreadState; return parentThreadID ? [parentThreadID] : []; }, }); diff --git a/lib/shared/messages/remove-members-message-spec.js b/lib/shared/messages/remove-members-message-spec.js index 1b3c1a76a..7e0bad9e3 100644 --- a/lib/shared/messages/remove-members-message-spec.js +++ b/lib/shared/messages/remove-members-message-spec.js @@ -1,156 +1,156 @@ // @flow import invariant from 'invariant'; import { messageTypes } from '../../types/message-types'; import type { MessageInfo } from '../../types/message-types'; import type { RawRemoveMembersMessageInfo, RemoveMembersMessageData, RemoveMembersMessageInfo, } from '../../types/messages/remove-members'; import type { NotifTexts } from '../../types/notif-types'; import type { ThreadInfo } from '../../types/thread-types'; import type { RelativeUserInfo } from '../../types/user-types'; import { values } from '../../utils/objects'; import { robotextForMessageInfo, robotextToRawString, removeCreatorAsViewer, } from '../message-utils'; import type { CreateMessageInfoParams, MessageSpec, MessageTitleParam, NotificationTextsParams, RobotextParams, } from './message-spec'; import { joinResult } from './utils'; export const removeMembersMessageSpec: MessageSpec< RemoveMembersMessageData, RawRemoveMembersMessageInfo, RemoveMembersMessageInfo, > = Object.freeze({ messageContent(data: RemoveMembersMessageData): string { return JSON.stringify(data.removedUserIDs); }, messageTitle({ messageInfo, threadInfo, viewerContext, }: MessageTitleParam) { let validMessageInfo: RemoveMembersMessageInfo = (messageInfo: RemoveMembersMessageInfo); if (viewerContext === 'global_viewer') { validMessageInfo = removeCreatorAsViewer(validMessageInfo); validMessageInfo = { ...validMessageInfo, removedMembers: validMessageInfo.removedMembers.map((item) => ({ ...item, isViewer: false, })), }; } return robotextToRawString( robotextForMessageInfo(validMessageInfo, threadInfo), ); }, rawMessageInfoFromRow(row: Object): RawRemoveMembersMessageInfo { return { type: messageTypes.REMOVE_MEMBERS, id: row.id.toString(), threadID: row.threadID.toString(), time: row.time, creatorID: row.creatorID.toString(), removedUserIDs: JSON.parse(row.content), }; }, createMessageInfo( rawMessageInfo: RawRemoveMembersMessageInfo, creator: RelativeUserInfo, params: CreateMessageInfoParams, ): RemoveMembersMessageInfo { const removedMembers = params.createRelativeUserInfos( rawMessageInfo.removedUserIDs, ); return { type: messageTypes.REMOVE_MEMBERS, id: rawMessageInfo.id, threadID: rawMessageInfo.threadID, creator, time: rawMessageInfo.time, removedMembers, }; }, rawMessageInfoFromMessageData( messageData: RemoveMembersMessageData, id: string, ): RawRemoveMembersMessageInfo { return { ...messageData, id }; }, robotext( messageInfo: RemoveMembersMessageInfo, creator: string, params: RobotextParams, ): string { const users = messageInfo.removedMembers; invariant(users.length !== 0, 'removed who??'); const removedUsersString = params.robotextForUsers(users); return `${creator} removed ${removedUsersString}`; }, notificationTexts( messageInfos: $ReadOnlyArray, threadInfo: ThreadInfo, params: NotificationTextsParams, ): NotifTexts { const removedMembersObject = {}; for (const messageInfo of messageInfos) { invariant( messageInfo.type === messageTypes.REMOVE_MEMBERS, 'messageInfo should be messageTypes.REMOVE_MEMBERS!', ); for (const member of messageInfo.removedMembers) { removedMembersObject[member.id] = member; } } const removedMembers = values(removedMembersObject); const mostRecentMessageInfo = messageInfos[0]; invariant( mostRecentMessageInfo.type === messageTypes.REMOVE_MEMBERS, 'messageInfo should be messageTypes.REMOVE_MEMBERS!', ); const mergedMessageInfo = { ...mostRecentMessageInfo, removedMembers }; const robotext = params.strippedRobotextForMessageInfo( mergedMessageInfo, threadInfo, ); const merged = `${robotext} from ${params.notifThreadName(threadInfo)}`; return { merged, title: threadInfo.uiName, body: robotext, }; }, notificationCollapseKey(rawMessageInfo: RawRemoveMembersMessageInfo): string { return joinResult( rawMessageInfo.type, rawMessageInfo.threadID, rawMessageInfo.creatorID, ); }, generatesNotifs: false, - userIDs(rawMessageInfo) { + userIDs(rawMessageInfo: RawRemoveMembersMessageInfo): $ReadOnlyArray { return rawMessageInfo.removedUserIDs; }, });