diff --git a/lib/shared/messages/add-members-message-spec.js b/lib/shared/messages/add-members-message-spec.js --- a/lib/shared/messages/add-members-message-spec.js +++ b/lib/shared/messages/add-members-message-spec.js @@ -233,14 +233,14 @@ messageInfo: AddMembersMessageInfo, params: ShowInMessagePreviewParams, ) => - threadSpecs[params.threadInfo.type].protocol + threadSpecs[params.threadInfo.type].protocol.presentationDetails .membershipChangesShownInThreadPreview, getLastUpdatedTime: ( rawMessageInfo: RawAddMembersMessageInfo, params: ShowInMessagePreviewParams, ) => - threadSpecs[params.threadInfo.type].protocol + threadSpecs[params.threadInfo.type].protocol.presentationDetails .membershipChangesShownInThreadPreview ? rawMessageInfo.time : null, diff --git a/lib/shared/messages/join-thread-message-spec.js b/lib/shared/messages/join-thread-message-spec.js --- a/lib/shared/messages/join-thread-message-spec.js +++ b/lib/shared/messages/join-thread-message-spec.js @@ -179,14 +179,14 @@ messageInfo: JoinThreadMessageInfo, params: ShowInMessagePreviewParams, ) => - threadSpecs[params.threadInfo.type].protocol + threadSpecs[params.threadInfo.type].protocol.presentationDetails .membershipChangesShownInThreadPreview, getLastUpdatedTime: ( rawMessageInfo: RawJoinThreadMessageInfo, params: ShowInMessagePreviewParams, ) => - threadSpecs[params.threadInfo.type].protocol + threadSpecs[params.threadInfo.type].protocol.presentationDetails .membershipChangesShownInThreadPreview ? rawMessageInfo.time : null, diff --git a/lib/shared/messages/leave-thread-message-spec.js b/lib/shared/messages/leave-thread-message-spec.js --- a/lib/shared/messages/leave-thread-message-spec.js +++ b/lib/shared/messages/leave-thread-message-spec.js @@ -179,14 +179,14 @@ messageInfo: LeaveThreadMessageInfo, params: ShowInMessagePreviewParams, ) => - threadSpecs[params.threadInfo.type].protocol + threadSpecs[params.threadInfo.type].protocol.presentationDetails .membershipChangesShownInThreadPreview, getLastUpdatedTime: ( rawMessageInfo: RawLeaveThreadMessageInfo, params: ShowInMessagePreviewParams, ) => - threadSpecs[params.threadInfo.type].protocol + threadSpecs[params.threadInfo.type].protocol.presentationDetails .membershipChangesShownInThreadPreview ? rawMessageInfo.time : null, diff --git a/lib/shared/messages/remove-members-message-spec.js b/lib/shared/messages/remove-members-message-spec.js --- a/lib/shared/messages/remove-members-message-spec.js +++ b/lib/shared/messages/remove-members-message-spec.js @@ -239,14 +239,14 @@ messageInfo: RemoveMembersMessageInfo, params: ShowInMessagePreviewParams, ) => - threadSpecs[params.threadInfo.type].protocol + threadSpecs[params.threadInfo.type].protocol.presentationDetails .membershipChangesShownInThreadPreview, getLastUpdatedTime: ( rawMessageInfo: RawRemoveMembersMessageInfo, params: ShowInMessagePreviewParams, ) => - threadSpecs[params.threadInfo.type].protocol + threadSpecs[params.threadInfo.type].protocol.presentationDetails .membershipChangesShownInThreadPreview ? rawMessageInfo.time : null, diff --git a/lib/shared/search-utils.js b/lib/shared/search-utils.js --- a/lib/shared/search-utils.js +++ b/lib/shared/search-utils.js @@ -9,7 +9,7 @@ userIsMember, userHasDeviceList, } from './thread-utils.js'; -import { threadTypeIsSidebar } from './threads/thread-specs.js'; +import { threadSpecs, threadTypeIsSidebar } from './threads/thread-specs.js'; import { searchMessagesActionTypes } from '../actions/message-actions.js'; import { searchUsers, @@ -37,10 +37,7 @@ import { userRelationshipStatus } from '../types/relationship-types.js'; import { threadPermissions } from '../types/thread-permission-types.js'; import type { ThreadRolePermissionsBlob } from '../types/thread-permission-types.js'; -import { - type ThreadType, - threadTypeIsThick, -} from '../types/thread-types-enum.js'; +import { type ThreadType } from '../types/thread-types-enum.js'; import type { AccountUserInfo, GlobalAccountUserInfo, @@ -161,7 +158,8 @@ } return !!( threadType && - threadTypeIsThick(threadType) && + threadSpecs[threadType].protocol.presentationDetails + .usersWithoutDeviceListExcludedFromSearchResult && !userHasDeviceList(userID, auxUserInfos) ); }, 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 @@ -14,6 +14,7 @@ import { relationshipBlockedInEitherDirection } from './relationship-utils.js'; import type { SidebarItem } from './sidebar-item-utils.js'; import { + threadSpecs, threadTypeIsCommunityRoot, threadTypeIsPersonal, threadTypeIsPrivate, @@ -1245,28 +1246,7 @@ } function threadLabel(threadType: ThreadType): string { - if ( - threadType === threadTypes.COMMUNITY_OPEN_SUBTHREAD || - threadType === threadTypes.COMMUNITY_OPEN_ANNOUNCEMENT_SUBTHREAD - ) { - return 'Open'; - } else if (threadType === threadTypes.GENESIS_PERSONAL) { - return 'Personal'; - } else if (threadTypeIsSidebar(threadType)) { - return 'Thread'; - } else if (threadType === threadTypes.GENESIS_PRIVATE) { - return 'Private'; - } else if ( - threadType === threadTypes.COMMUNITY_ROOT || - threadType === threadTypes.COMMUNITY_ANNOUNCEMENT_ROOT || - threadType === threadTypes.GENESIS - ) { - return 'Community'; - } else if (threadTypeIsThick(threadType)) { - return 'Local DM'; - } else { - return 'Secret'; - } + return threadSpecs[threadType].threadLabel; } type ExistingThreadInfoFinderParams = { diff --git a/lib/shared/threads/community-announcement-root-spec.js b/lib/shared/threads/community-announcement-root-spec.js --- a/lib/shared/threads/community-announcement-root-spec.js +++ b/lib/shared/threads/community-announcement-root-spec.js @@ -6,6 +6,7 @@ const communityAnnouncementRootSpec: ThreadSpec = Object.freeze({ traits: new Set(['community', 'announcement']), protocol: keyserverThreadProtocol, + threadLabel: 'Community', }); export { communityAnnouncementRootSpec }; diff --git a/lib/shared/threads/community-open-announcement-subthread-spec.js b/lib/shared/threads/community-open-announcement-subthread-spec.js --- a/lib/shared/threads/community-open-announcement-subthread-spec.js +++ b/lib/shared/threads/community-open-announcement-subthread-spec.js @@ -6,6 +6,7 @@ const communityOpenAnnouncementSubthreadSpec: ThreadSpec = Object.freeze({ traits: new Set(['communitySubthread', 'announcement']), protocol: keyserverThreadProtocol, + threadLabel: 'Open', }); export { communityOpenAnnouncementSubthreadSpec }; diff --git a/lib/shared/threads/community-open-subthread-spec.js b/lib/shared/threads/community-open-subthread-spec.js --- a/lib/shared/threads/community-open-subthread-spec.js +++ b/lib/shared/threads/community-open-subthread-spec.js @@ -6,6 +6,7 @@ const communityOpenSubthreadSpec: ThreadSpec = Object.freeze({ traits: new Set(['communitySubthread']), protocol: keyserverThreadProtocol, + threadLabel: 'Open', }); export { communityOpenSubthreadSpec }; diff --git a/lib/shared/threads/community-root-spec.js b/lib/shared/threads/community-root-spec.js --- a/lib/shared/threads/community-root-spec.js +++ b/lib/shared/threads/community-root-spec.js @@ -6,6 +6,7 @@ const communityRootSpec: ThreadSpec = Object.freeze({ traits: new Set(['community']), protocol: keyserverThreadProtocol, + threadLabel: 'Community', }); export { communityRootSpec }; diff --git a/lib/shared/threads/community-secret-announcement-subthread-spec.js b/lib/shared/threads/community-secret-announcement-subthread-spec.js --- a/lib/shared/threads/community-secret-announcement-subthread-spec.js +++ b/lib/shared/threads/community-secret-announcement-subthread-spec.js @@ -6,6 +6,7 @@ const communitySecretAnnouncementSubthreadSpec: ThreadSpec = Object.freeze({ traits: new Set(['communitySubthread', 'announcement']), protocol: keyserverThreadProtocol, + threadLabel: 'Secret', }); export { communitySecretAnnouncementSubthreadSpec }; diff --git a/lib/shared/threads/community-secret-subthread-spec.js b/lib/shared/threads/community-secret-subthread-spec.js --- a/lib/shared/threads/community-secret-subthread-spec.js +++ b/lib/shared/threads/community-secret-subthread-spec.js @@ -6,6 +6,7 @@ const communitySecretSubthreadSpec: ThreadSpec = Object.freeze({ traits: new Set(['communitySubthread']), protocol: keyserverThreadProtocol, + threadLabel: 'Secret', }); export { communitySecretSubthreadSpec }; diff --git a/lib/shared/threads/genesis-personal-spec.js b/lib/shared/threads/genesis-personal-spec.js --- a/lib/shared/threads/genesis-personal-spec.js +++ b/lib/shared/threads/genesis-personal-spec.js @@ -6,6 +6,7 @@ const genesisPersonalSpec: ThreadSpec = Object.freeze({ traits: new Set(['personal']), protocol: keyserverThreadProtocol, + threadLabel: 'Personal', }); export { genesisPersonalSpec }; diff --git a/lib/shared/threads/genesis-private-spec.js b/lib/shared/threads/genesis-private-spec.js --- a/lib/shared/threads/genesis-private-spec.js +++ b/lib/shared/threads/genesis-private-spec.js @@ -6,6 +6,7 @@ const genesisPrivateSpec: ThreadSpec = Object.freeze({ traits: new Set(['private']), protocol: keyserverThreadProtocol, + threadLabel: 'Private', }); export { genesisPrivateSpec }; diff --git a/lib/shared/threads/genesis-spec.js b/lib/shared/threads/genesis-spec.js --- a/lib/shared/threads/genesis-spec.js +++ b/lib/shared/threads/genesis-spec.js @@ -6,6 +6,7 @@ const genesisSpec: ThreadSpec = Object.freeze({ traits: new Set(['community', 'announcement']), protocol: keyserverThreadProtocol, + threadLabel: 'Community', }); export { genesisSpec }; diff --git a/lib/shared/threads/local-spec.js b/lib/shared/threads/local-spec.js --- a/lib/shared/threads/local-spec.js +++ b/lib/shared/threads/local-spec.js @@ -6,6 +6,7 @@ const localSpec: ThreadSpec = Object.freeze({ traits: new Set(), protocol: dmThreadProtocol, + threadLabel: 'Local DM', }); export { localSpec }; diff --git a/lib/shared/threads/personal-spec.js b/lib/shared/threads/personal-spec.js --- a/lib/shared/threads/personal-spec.js +++ b/lib/shared/threads/personal-spec.js @@ -6,6 +6,7 @@ const personalSpec: ThreadSpec = Object.freeze({ traits: new Set(['personal']), protocol: dmThreadProtocol, + threadLabel: 'Local DM', }); export { personalSpec }; diff --git a/lib/shared/threads/private-spec.js b/lib/shared/threads/private-spec.js --- a/lib/shared/threads/private-spec.js +++ b/lib/shared/threads/private-spec.js @@ -6,6 +6,7 @@ const privateSpec: ThreadSpec = Object.freeze({ traits: new Set(['private']), protocol: dmThreadProtocol, + threadLabel: 'Local DM', }); export { privateSpec }; 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 @@ -430,9 +430,15 @@ }; }, - membershipChangesShownInThreadPreview: true, - allowsDeletingSidebarSource: false, + + presentationDetails: { + membershipChangesShownInThreadPreview: true, + usersWithoutDeviceListExcludedFromSearchResult: true, + supportsMediaGallery: false, + chatThreadListIcon: 'lock', + breadCrumbs: () => 'Local DM', + }, }); export { dmThreadProtocol }; 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 @@ -1,6 +1,7 @@ // @flow import invariant from 'invariant'; +import * as React from 'react'; import type { ProcessHolders } from '../../../actions/holder-actions.js'; import { sendEditMessageActionTypes } from '../../../actions/message-actions.js'; @@ -213,9 +214,15 @@ return utils.keyserverSetThreadUnreadStatus(rest); }, - membershipChangesShownInThreadPreview: false, - allowsDeletingSidebarSource: true, + + presentationDetails: { + membershipChangesShownInThreadPreview: false, + usersWithoutDeviceListExcludedFromSearchResult: false, + supportsMediaGallery: true, + chatThreadListIcon: 'server', + breadCrumbs: (ancestorPath: React.Node) => ancestorPath, + }, }); function mediaIDIsKeyserverID(mediaID: string): boolean { diff --git a/lib/shared/threads/sidebar-spec.js b/lib/shared/threads/sidebar-spec.js --- a/lib/shared/threads/sidebar-spec.js +++ b/lib/shared/threads/sidebar-spec.js @@ -6,6 +6,7 @@ const sidebarSpec: ThreadSpec = Object.freeze({ traits: new Set(['sidebar']), protocol: keyserverThreadProtocol, + threadLabel: 'Thread', }); export { sidebarSpec }; diff --git a/lib/shared/threads/thick-sidebar-spec.js b/lib/shared/threads/thick-sidebar-spec.js --- a/lib/shared/threads/thick-sidebar-spec.js +++ b/lib/shared/threads/thick-sidebar-spec.js @@ -6,6 +6,7 @@ const thickSidebarSpec: ThreadSpec = Object.freeze({ traits: new Set(['sidebar']), protocol: dmThreadProtocol, + threadLabel: 'Thread', }); export { thickSidebarSpec }; 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 @@ -1,5 +1,7 @@ // @flow +import * as React from 'react'; + import type { UseSetThreadUnreadStatusInput } from '../../actions/activity-actions.js'; import type { UseCreateEntryInput, @@ -184,11 +186,18 @@ input: ProtocolSetThreadUnreadStatusInput, utils: SetThreadUnreadStatusUtils, ) => Promise, - +membershipChangesShownInThreadPreview: boolean, +allowsDeletingSidebarSource: boolean, + +presentationDetails: { + +membershipChangesShownInThreadPreview: boolean, + +usersWithoutDeviceListExcludedFromSearchResult: boolean, + +supportsMediaGallery: boolean, + +chatThreadListIcon: string, + +breadCrumbs: (ancestorPath: React.Node) => React.Node, + }, }; export type ThreadSpec = { +traits: $ReadOnlySet, +protocol: ThreadProtocol, + +threadLabel: string, }; diff --git a/native/chat/chat-thread-list-item.react.js b/native/chat/chat-thread-list-item.react.js --- a/native/chat/chat-thread-list-item.react.js +++ b/native/chat/chat-thread-list-item.react.js @@ -5,8 +5,8 @@ import { Text, View } from 'react-native'; import type { ChatThreadItem } from 'lib/selectors/chat-selectors.js'; +import { threadSpecs } from 'lib/shared/threads/thread-specs.js'; import type { ThreadInfo } from 'lib/types/minimally-encoded-thread-permissions-types.js'; -import { threadTypeIsThick } from 'lib/types/thread-types-enum.js'; import type { UserInfo } from 'lib/types/user-types.js'; import { shortAbsoluteDate } from 'lib/utils/date-utils.js'; import { useResolvedThreadInfo } from 'lib/utils/entity-helpers.js'; @@ -132,12 +132,12 @@ [data.threadInfo, styles.avatarContainer], ); - const isThick = threadTypeIsThick(data.threadInfo.type); const iconStyle = data.threadInfo.currentUser.unread ? styles.iconUnread : styles.iconRead; - - const iconName = isThick ? 'lock' : 'server'; + const iconName = + threadSpecs[data.threadInfo.type].protocol.presentationDetails + .chatThreadListIcon; const threadDetails = React.useMemo( () => ( diff --git a/web/chat/chat-thread-list-item.react.js b/web/chat/chat-thread-list-item.react.js --- a/web/chat/chat-thread-list-item.react.js +++ b/web/chat/chat-thread-list-item.react.js @@ -11,6 +11,7 @@ import SWMansionIcon from 'lib/components/swmansion-icon.react.js'; import type { ChatThreadItem } from 'lib/selectors/chat-selectors.js'; import { useAncestorThreads } from 'lib/shared/ancestor-threads.js'; +import { threadSpecs } from 'lib/shared/threads/thread-specs.js'; import { threadTypeIsThick } from 'lib/types/thread-types-enum.js'; import { shortAbsoluteDate } from 'lib/utils/date-utils.js'; import { @@ -132,7 +133,10 @@ const iconClass = unread ? css.iconUnread : css.iconRead; const icon = isThick ? lock : server; - const breadCrumbs = isThick ? 'Local DM' : ancestorPath; + const breadCrumbs = + threadSpecs[threadInfo.type].protocol.presentationDetails.breadCrumbs( + ancestorPath, + ); return ( <> diff --git a/web/chat/thread-menu.react.js b/web/chat/thread-menu.react.js --- a/web/chat/thread-menu.react.js +++ b/web/chat/thread-menu.react.js @@ -23,6 +23,7 @@ viewerIsMember, } from 'lib/shared/thread-utils.js'; import { + threadSpecs, threadTypeIsPersonal, threadTypeIsPrivate, threadTypeIsSidebar, @@ -113,7 +114,10 @@ ); const threadMediaGalleryItem = React.useMemo(() => { - if (threadTypeIsThick(threadInfo.type)) { + if ( + !threadSpecs[threadInfo.type].protocol.presentationDetails + .supportsMediaGallery + ) { return null; } return (