diff --git a/keyserver/src/responders/website-responders.js b/keyserver/src/responders/website-responders.js --- a/keyserver/src/responders/website-responders.js +++ b/keyserver/src/responders/website-responders.js @@ -277,8 +277,15 @@ currentUserInfo.id ) { const { userInfos } = userStore; - const members = pendingThreadData.memberIDs - .map(id => userInfos[id]) + const members = [...pendingThreadData.memberIDs, currentUserInfo.id] + .map(id => { + const userInfo = userInfos[id]; + if (!userInfo || !userInfo.username) { + return undefined; + } + const { username } = userInfo; + return { id, username }; + }) .filter(Boolean); const newPendingThread = createPendingThread({ viewerID: currentUserInfo.id, diff --git a/lib/reducers/message-reducer.test.js b/lib/reducers/message-reducer.test.js --- a/lib/reducers/message-reducer.test.js +++ b/lib/reducers/message-reducer.test.js @@ -295,6 +295,7 @@ [88471]: createPendingThread({ viewerID: '', threadType: threadTypes.LOCAL, + members: [{ id: '', username: '' }], }), }, ); 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 @@ -71,7 +71,6 @@ import type { GlobalAccountUserInfo, UserInfos, - UserInfo, AccountUserInfo, LoggedInUserInfo, } from '../types/user-types.js'; @@ -323,10 +322,15 @@ }; } +type UserIDAndUsername = { + +id: string, + +username: string, + ... +}; type CreatePendingThreadArgs = { +viewerID: string, +threadType: ThreadType, - +members?: $ReadOnlyArray, + +members: $ReadOnlyArray, +parentThreadInfo?: ?ThreadInfo, +threadColor?: ?string, +name?: ?string, @@ -343,11 +347,13 @@ sourceMessageID, }: CreatePendingThreadArgs): ThreadInfo { const now = Date.now(); - const nonViewerMembers = members - ? members.filter(member => member.id !== viewerID) - : []; - const nonViewerMemberIDs = nonViewerMembers.map(member => member.id); - const memberIDs = [...nonViewerMemberIDs, viewerID]; + if (!members.some(member => member.id === viewerID)) { + throw new Error( + 'createPendingThread should be called with the viewer as a member', + ); + } + + const memberIDs = members.map(member => member.id); const threadID = getPendingThreadID(threadType, memberIDs, sourceMessageID); const permissions = { @@ -377,20 +383,12 @@ parentThreadID: parentThreadInfo?.id ?? null, containingThreadID: getContainingThreadID(parentThreadInfo, threadType), community: getCommunity(parentThreadInfo), - members: [ - { - id: viewerID, - role: role.id, - permissions: membershipPermissions, - isSender: false, - }, - ...nonViewerMembers.map(member => ({ - id: member.id, - role: role.id, - permissions: membershipPermissions, - isSender: false, - })), - ], + members: members.map(member => ({ + id: member.id, + role: role.id, + permissions: membershipPermissions, + isSender: false, + })), roles: { [role.id]: role, }, @@ -408,19 +406,22 @@ }; const userInfos = {}; - nonViewerMembers.forEach(member => (userInfos[member.id] = member)); + for (const member of members) { + const { id, username } = member; + userInfos[id] = { id, username }; + } return threadInfoFromRawThreadInfo(rawThreadInfo, viewerID, userInfos); } function createPendingThreadItem( loggedInUserInfo: LoggedInUserInfo, - user: GlobalAccountUserInfo, + user: UserIDAndUsername, ): ChatThreadItem { const threadInfo = createPendingThread({ viewerID: loggedInUserInfo.id, threadType: threadTypes.PERSONAL, - members: [user], + members: [loggedInUserInfo, user], }); return { @@ -462,7 +463,7 @@ const initialMembers = new Map(); const { id: viewerID, username } = loggedInUserInfo; - initialMembers.set(viewerID, ({ id: viewerID, username }: UserInfo)); + initialMembers.set(viewerID, { id: viewerID, username }); if (userIsMember(parentThreadInfo, sourceMessageInfo.creator.id)) { const { id: sourceAuthorID, username: sourceAuthorUsername } = sourceMessageInfo.creator; @@ -470,7 +471,7 @@ sourceAuthorUsername, 'sourceAuthorUsername should be set in createPendingSidebar', ); - const initialMemberUserInfo: GlobalAccountUserInfo = { + const initialMemberUserInfo = { id: sourceAuthorID, username: sourceAuthorUsername, }; @@ -485,7 +486,7 @@ singleOtherUsername, 'singleOtherUsername should be set in createPendingSidebar', ); - const singleOtherUserInfo: GlobalAccountUserInfo = { + const singleOtherUserInfo = { id: singleOtherUser, username: singleOtherUsername, }; @@ -1164,7 +1165,7 @@ ? createPendingThread({ viewerID, threadType: pendingThreadType(userInfoInputArray.length), - members: userInfoInputArray, + members: [loggedInUserInfo, ...userInfoInputArray], }) : baseThreadInfo; return { diff --git a/native/chat/chat-thread-list.react.js b/native/chat/chat-thread-list.react.js --- a/native/chat/chat-thread-list.react.js +++ b/native/chat/chat-thread-list.react.js @@ -568,6 +568,7 @@ const threadInfo = createPendingThread({ viewerID: loggedInUserInfo.id, threadType: threadTypes.PRIVATE, + members: [loggedInUserInfo], }); this.props.navigateToThread({ threadInfo, searching: true }); }; diff --git a/native/chat/compose-thread-button.react.js b/native/chat/compose-thread-button.react.js --- a/native/chat/compose-thread-button.react.js +++ b/native/chat/compose-thread-button.react.js @@ -3,6 +3,7 @@ import * as React from 'react'; import { StyleSheet } from 'react-native'; +import { useLoggedInUserInfo } from 'lib/hooks/account-hooks.js'; import { createPendingThread } from 'lib/shared/thread-utils.js'; import { threadTypes } from 'lib/types/thread-types.js'; @@ -10,7 +11,6 @@ import Button from '../components/button.react.js'; import SWMansionIcon from '../components/swmansion-icon.react.js'; import { MessageListRouteName } from '../navigation/route-names.js'; -import { useSelector } from '../redux/redux-utils.js'; import { useColors } from '../themes/colors.js'; type Props = { @@ -18,24 +18,23 @@ }; function ComposeThreadButton(props: Props) { const { navigate } = props; - const viewerID = useSelector( - state => state.currentUserInfo && state.currentUserInfo.id, - ); + const loggedInUserInfo = useLoggedInUserInfo(); const onPress = React.useCallback(() => { - if (!viewerID) { + if (!loggedInUserInfo) { return; } navigate<'MessageList'>({ name: MessageListRouteName, params: { threadInfo: createPendingThread({ - viewerID, + viewerID: loggedInUserInfo.id, threadType: threadTypes.PRIVATE, + members: [loggedInUserInfo], }), searching: true, }, }); - }, [navigate, viewerID]); + }, [navigate, loggedInUserInfo]); const { listForegroundSecondaryLabel } = useColors(); return ( diff --git a/web/chat/chat-message-list-container.react.js b/web/chat/chat-message-list-container.react.js --- a/web/chat/chat-message-list-container.react.js +++ b/web/chat/chat-message-list-container.react.js @@ -8,6 +8,7 @@ import { NativeTypes } from 'react-dnd-html5-backend'; import { useDispatch } from 'react-redux'; +import { useLoggedInUserInfo } from 'lib/hooks/account-hooks.js'; import { threadInfoSelector } from 'lib/selectors/thread-selectors.js'; import { userInfoSelectorForPotentialMembers } from 'lib/selectors/user-selectors.js'; import { @@ -43,13 +44,15 @@ () => selectedUserIDs?.map(id => otherUserInfos[id]).filter(Boolean) ?? [], [otherUserInfos, selectedUserIDs], ); - const viewerID = useSelector(state => state.currentUserInfo?.id); - invariant(viewerID, 'should be set'); + + const loggedInUserInfo = useLoggedInUserInfo(); + invariant(loggedInUserInfo, 'loggedInUserInfo should be set'); const pendingPrivateThread = React.useRef( createPendingThread({ - viewerID, + viewerID: loggedInUserInfo.id, threadType: threadTypes.PRIVATE, + members: [loggedInUserInfo], }), ); @@ -57,13 +60,14 @@ const pendingNewThread = React.useMemo( () => ({ ...createPendingThread({ - viewerID, + viewerID: loggedInUserInfo.id, threadType: threadTypes.PRIVATE, + members: [loggedInUserInfo], name: 'New thread', }), id: newThreadID, }), - [viewerID], + [loggedInUserInfo], ); const existingThreadInfoFinderForCreatingThread = useExistingThreadInfoFinder(