diff --git a/lib/shared/thread-utils.js b/lib/shared/thread-utils.js --- a/lib/shared/thread-utils.js +++ b/lib/shared/thread-utils.js @@ -9,7 +9,6 @@ import * as React from 'react'; import { getUserAvatarForThread } from './avatar-utils.js'; -import { generatePendingThreadColor } from './color-utils.js'; import { extractUserMentionsFromText } from './mention-utils.js'; import { relationshipBlockedInEitherDirection } from './relationship-utils.js'; import type { SidebarItem } from './sidebar-item-utils.js'; @@ -63,10 +62,8 @@ } from '../types/minimally-encoded-thread-permissions-types.js'; import { decodeMinimallyEncodedRoleInfo, - minimallyEncodeMemberInfo, minimallyEncodeRawThreadInfoWithMemberPermissions, minimallyEncodeRoleInfo, - minimallyEncodeThreadCurrentUserInfo, } from '../types/minimally-encoded-thread-permissions-types.js'; import { userRelationshipStatus } from '../types/relationship-types.js'; import { defaultThreadSubscription } from '../types/subscription-types.js'; @@ -86,15 +83,12 @@ threadTypes, assertThreadType, threadTypeIsThick, - assertThinThreadType, - assertThickThreadType, type ThinThreadType, } from '../types/thread-types-enum.js'; import type { LegacyRawThreadInfo, ClientLegacyRoleInfo, ServerThreadInfo, - ThickMemberInfo, UserProfileThreadInfo, MixedRawThreadInfos, LegacyThinRawThreadInfo, @@ -462,7 +456,7 @@ +username: ?string, ... }; -type CreatePendingThreadArgs = { +export type CreatePendingThreadArgs = { +viewerID: string, +threadType: ThreadType, +members: $ReadOnlyArray, @@ -472,15 +466,8 @@ +sourceMessageID?: string, }; -function createPendingThread({ - viewerID, - threadType, - members, - parentThreadInfo, - threadColor, - name, - sourceMessageID, -}: CreatePendingThreadArgs): ThreadInfo { +function createPendingThread(args: CreatePendingThreadArgs): ThreadInfo { + const { viewerID, threadType, members, sourceMessageID } = args; const now = Date.now(); if (!members.some(member => member.id === viewerID)) { throw new Error( @@ -511,82 +498,14 @@ specialRole: specialRoles.DEFAULT_ROLE, }; - let rawThreadInfo: RawThreadInfo; - if (threadTypeIsThick(threadType)) { - const thickThreadType = assertThickThreadType(threadType); - rawThreadInfo = { - minimallyEncoded: true, - thick: true, - id: threadID, - type: thickThreadType, - name: name ?? null, - description: null, - color: threadColor ?? generatePendingThreadColor(memberIDs), - creationTime: now, - parentThreadID: parentThreadInfo?.id ?? null, - containingThreadID: getContainingThreadID( - parentThreadInfo, - thickThreadType, - ), - members: members.map(member => - minimallyEncodeMemberInfo({ - id: member.id, - role: role.id, - permissions: membershipPermissions, - isSender: false, - subscription: defaultThreadSubscription, - }), - ), - roles: { - [role.id]: role, - }, - currentUser: minimallyEncodeThreadCurrentUserInfo({ - role: role.id, - permissions: membershipPermissions, - subscription: defaultThreadSubscription, - unread: false, - }), - repliesCount: 0, - sourceMessageID, - pinnedCount: 0, - timestamps: createThreadTimestamps(now, memberIDs), - }; - } else { - const thinThreadType = assertThinThreadType(threadType); - rawThreadInfo = { - minimallyEncoded: true, - id: threadID, - type: thinThreadType, - name: name ?? null, - description: null, - color: threadColor ?? generatePendingThreadColor(memberIDs), - creationTime: now, - parentThreadID: parentThreadInfo?.id ?? null, - containingThreadID: getContainingThreadID( - parentThreadInfo, - thinThreadType, - ), - community: getCommunity(parentThreadInfo), - members: members.map(member => ({ - id: member.id, - role: role.id, - minimallyEncoded: true, - isSender: false, - })), - roles: { - [role.id]: role, - }, - currentUser: minimallyEncodeThreadCurrentUserInfo({ - role: role.id, - permissions: membershipPermissions, - subscription: defaultThreadSubscription, - unread: false, - }), - repliesCount: 0, - sourceMessageID, - pinnedCount: 0, - }; - } + const rawThreadInfo = threadSpecs[threadType].protocol.createPendingThread({ + createPendingThreadArgs: args, + creationTime: now, + membershipPermissions, + role, + threadID, + memberIDs, + }); const userInfos: { [string]: UserInfo } = {}; for (const member of members) { diff --git a/lib/shared/threads/protocols/dm-thread-protocol.js b/lib/shared/threads/protocols/dm-thread-protocol.js --- a/lib/shared/threads/protocols/dm-thread-protocol.js +++ b/lib/shared/threads/protocols/dm-thread-protocol.js @@ -24,6 +24,11 @@ ThickRawThreadInfo, MinimallyEncodedThickMemberInfo, } from '../../../types/minimally-encoded-thread-permissions-types.js'; +import { + minimallyEncodeMemberInfo, + minimallyEncodeThreadCurrentUserInfo, +} from '../../../types/minimally-encoded-thread-permissions-types.js'; +import { defaultThreadSubscription } from '../../../types/subscription-types.js'; import { assertThickThreadType, thickThreadTypes, @@ -31,6 +36,7 @@ import type { ChangeThreadSettingsPayload, ClientDBThreadInfo, + ThickMemberInfo, } from '../../../types/thread-types.js'; import { threadTimestampsValidator } from '../../../types/thread-types.js'; import { dateString as stringFromDate } from '../../../utils/date-utils.js'; @@ -39,11 +45,16 @@ assertWithValidator, pendingThickSidebarURLPrefix, } from '../../../utils/validation-utils.js'; +import { generatePendingThreadColor } from '../../color-utils.js'; import { dmOperationSpecificationTypes, type OutboundDMOperationSpecification, } from '../../dm-ops/dm-op-types.js'; import { getIDFromLocalID } from '../../id-utils.js'; +import { + createThreadTimestamps, + getContainingThreadID, +} from '../../thread-utils.js'; import type { ProtocolSendTextMessageInput, SendMultimediaMessageUtils, @@ -72,6 +83,7 @@ ProtocolLeaveThreadInput, FetchMessageUtils, ProtocolFetchMessageInput, + ProtocolCreatePendingThreadInput, } from '../thread-spec.js'; const dmThreadProtocol: ThreadProtocol = @@ -659,6 +671,62 @@ await promise; }, + createPendingThread: (input: ProtocolCreatePendingThreadInput) => { + const { + threadID, + creationTime, + membershipPermissions, + role, + memberIDs, + createPendingThreadArgs: { + threadType, + name, + threadColor, + parentThreadInfo, + members, + sourceMessageID, + }, + } = input; + const thickThreadType = assertThickThreadType(threadType); + return { + minimallyEncoded: true, + thick: true, + id: threadID, + type: thickThreadType, + name: name ?? null, + description: null, + color: threadColor ?? generatePendingThreadColor(memberIDs), + creationTime, + parentThreadID: parentThreadInfo?.id ?? null, + containingThreadID: getContainingThreadID( + parentThreadInfo, + thickThreadType, + ), + members: members.map(member => + minimallyEncodeMemberInfo({ + id: member.id, + role: role.id, + permissions: membershipPermissions, + isSender: false, + subscription: defaultThreadSubscription, + }), + ), + roles: { + [role.id]: role, + }, + currentUser: minimallyEncodeThreadCurrentUserInfo({ + role: role.id, + permissions: membershipPermissions, + subscription: defaultThreadSubscription, + unread: false, + }), + repliesCount: 0, + sourceMessageID, + pinnedCount: 0, + timestamps: createThreadTimestamps(creationTime, memberIDs), + }; + }, + allowsDeletingSidebarSource: false, presentationDetails: { diff --git a/lib/shared/threads/protocols/keyserver-thread-protocol.js b/lib/shared/threads/protocols/keyserver-thread-protocol.js --- a/lib/shared/threads/protocols/keyserver-thread-protocol.js +++ b/lib/shared/threads/protocols/keyserver-thread-protocol.js @@ -44,7 +44,9 @@ ThinRawThreadInfo, MemberInfoSansPermissions, } from '../../../types/minimally-encoded-thread-permissions-types.js'; +import { minimallyEncodeThreadCurrentUserInfo } from '../../../types/minimally-encoded-thread-permissions-types.js'; import type { Dispatch } from '../../../types/redux-types.js'; +import { defaultThreadSubscription } from '../../../types/subscription-types.js'; import { assertThinThreadType, thinThreadTypes, @@ -59,7 +61,9 @@ SendMessageError, } from '../../../utils/errors.js'; import { pendingSidebarURLPrefix } from '../../../utils/validation-utils.js'; +import { generatePendingThreadColor } from '../../color-utils.js'; import { getNextLocalID } from '../../id-utils.js'; +import { getCommunity, getContainingThreadID } from '../../thread-utils.js'; import { identifyInvalidatedThreads } from '../../updates/utils.js'; import type { ThreadProtocol, @@ -89,6 +93,7 @@ LeaveThreadUtils, FetchMessageUtils, ProtocolFetchMessageInput, + ProtocolCreatePendingThreadInput, } from '../thread-spec.js'; const keyserverThreadProtocol: ThreadProtocol = @@ -433,6 +438,58 @@ } }, + createPendingThread: (input: ProtocolCreatePendingThreadInput) => { + const { + threadID, + creationTime, + membershipPermissions, + role, + memberIDs, + createPendingThreadArgs: { + threadType, + name, + threadColor, + parentThreadInfo, + members, + sourceMessageID, + }, + } = input; + const thinThreadType = assertThinThreadType(threadType); + return { + minimallyEncoded: true, + id: threadID, + type: thinThreadType, + name: name ?? null, + description: null, + color: threadColor ?? generatePendingThreadColor(memberIDs), + creationTime, + parentThreadID: parentThreadInfo?.id ?? null, + containingThreadID: getContainingThreadID( + parentThreadInfo, + thinThreadType, + ), + community: getCommunity(parentThreadInfo), + members: members.map(member => ({ + id: member.id, + role: role.id, + minimallyEncoded: true, + isSender: false, + })), + roles: { + [role.id]: role, + }, + currentUser: minimallyEncodeThreadCurrentUserInfo({ + role: role.id, + permissions: membershipPermissions, + subscription: defaultThreadSubscription, + unread: false, + }), + repliesCount: 0, + sourceMessageID, + pinnedCount: 0, + }; + }, + allowsDeletingSidebarSource: true, presentationDetails: { diff --git a/lib/shared/threads/thread-spec.js b/lib/shared/threads/thread-spec.js --- a/lib/shared/threads/thread-spec.js +++ b/lib/shared/threads/thread-spec.js @@ -62,6 +62,7 @@ SubscriptionUpdateRequest, SubscriptionUpdateResult, } from '../../types/subscription-types.js'; +import type { ThreadPermissionsInfo } from '../../types/thread-permission-types.js'; import type { ThreadType } from '../../types/thread-types-enum.js'; import type { ChangeThreadSettingsPayload, @@ -75,6 +76,7 @@ OutboundDMOperationSpecification, } from '../dm-ops/dm-op-types.js'; import type { FetchThickMessagesType } from '../message-utils.js'; +import type { CreatePendingThreadArgs } from '../thread-utils.js'; export type ThreadTrait = | 'sidebar' @@ -228,6 +230,15 @@ +dispatchActionPromise: DispatchActionPromise, }; +export type ProtocolCreatePendingThreadInput = { + +createPendingThreadArgs: CreatePendingThreadArgs, + +creationTime: number, + +membershipPermissions: ThreadPermissionsInfo, + +role: RoleInfo, + +threadID: string, + +memberIDs: $ReadOnlyArray, +}; + export type ThreadProtocol< RawThreadMemberType: | MemberInfoSansPermissions @@ -296,6 +307,9 @@ input: ProtocolFetchMessageInput, utils: FetchMessageUtils, ) => Promise, + +createPendingThread: ( + input: ProtocolCreatePendingThreadInput, + ) => RawThreadInfo, +allowsDeletingSidebarSource: boolean, +presentationDetails: { +membershipChangesShownInThreadPreview: boolean,