diff --git a/lib/shared/avatar-utils.js b/lib/shared/avatar-utils.js --- a/lib/shared/avatar-utils.js +++ b/lib/shared/avatar-utils.js @@ -1,9 +1,17 @@ // @flow +import invariant from 'invariant'; import stringHash from 'string-hash'; import { selectedThreadColors } from './color-utils.js'; +import { threadOtherMembers } from './thread-utils.js'; import type { ClientEmojiAvatar, ClientAvatar } from '../types/avatar-types.js'; +import { + type RawThreadInfo, + type ThreadInfo, + threadTypes, +} from '../types/thread-types.js'; +import type { UserInfos } from '../types/user-types.js'; const defaultAnonymousUserEmojiAvatar: ClientEmojiAvatar = { color: selectedThreadColors[4], @@ -57,4 +65,30 @@ return getDefaultAvatar(user.username); } -export { getAvatarForUser }; +function getUserAvatarForThread( + threadInfo: RawThreadInfo | ThreadInfo, + viewerID: ?string, + userInfos: UserInfos, +): ClientAvatar { + if (threadInfo.type === threadTypes.PRIVATE) { + invariant(viewerID, 'viewerID should be set for PRIVATE threads'); + return getAvatarForUser(userInfos[viewerID]); + } + + invariant( + threadInfo.type === threadTypes.PERSONAL, + 'threadInfo should be a PERSONAL type', + ); + + const memberInfos = threadOtherMembers(threadInfo.members, viewerID) + .map(member => userInfos[member.id] && userInfos[member.id]) + .filter(Boolean); + + if (memberInfos.length === 0) { + return defaultAnonymousUserEmojiAvatar; + } + + return getAvatarForUser(memberInfos[0]); +} + +export { getAvatarForUser, getUserAvatarForThread }; 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 @@ -41,6 +41,7 @@ getRelativeMemberInfos, usersWithPersonalThreadSelector, } from '../selectors/user-selectors.js'; +import { getUserAvatarForThread } from '../shared/avatar-utils.js'; import type { CalendarQuery } from '../types/entry-types.js'; import { type RobotextMessageInfo, @@ -867,10 +868,22 @@ ...threadInfo, uiName: threadUIName(threadInfo), }; - const { sourceMessageID } = rawThreadInfo; + const { sourceMessageID, avatar } = rawThreadInfo; if (sourceMessageID) { threadInfo = { ...threadInfo, sourceMessageID }; } + + if (avatar) { + threadInfo = { ...threadInfo, avatar }; + } else if ( + rawThreadInfo.type === threadTypes.PERSONAL || + rawThreadInfo.type === threadTypes.PRIVATE + ) { + threadInfo = { + ...threadInfo, + avatar: getUserAvatarForThread(rawThreadInfo, viewerID, userInfos), + }; + } return threadInfo; } diff --git a/lib/types/thread-types.js b/lib/types/thread-types.js --- a/lib/types/thread-types.js +++ b/lib/types/thread-types.js @@ -2,6 +2,7 @@ import invariant from 'invariant'; +import type { ClientAvatar } from './avatar-types.js'; import type { Shape } from './core.js'; import type { CalendarQuery, RawEntryInfo } from './entry-types.js'; import type { Media } from './media-types.js'; @@ -194,6 +195,7 @@ +id: string, +type: ThreadType, +name: ?string, + +avatar?: ?ClientAvatar, +description: ?string, +color: string, // hex, without "#" or "0x" +creationTime: number, // millisecond timestamp @@ -212,6 +214,7 @@ +type: ThreadType, +name: ?string, +uiName: string | ThreadEntity, + +avatar?: ?ClientAvatar, +description: ?string, +color: string, // hex, without "#" or "0x" +creationTime: number, // millisecond timestamp @@ -230,6 +233,7 @@ +type: ThreadType, +name: ?string, +uiName: string, + +avatar?: ?ClientAvatar, +description: ?string, +color: string, // hex, without "#" or "0x" +creationTime: number, // millisecond timestamp @@ -296,6 +300,7 @@ +id: string, +type: number, +name: ?string, + +avatar?: ?ClientAvatar, +description: ?string, +color: string, +creationTime: string,