diff --git a/src/components/dms/MessagesListHeader.tsx b/src/components/dms/MessagesListHeader.tsx index 3f7694a342..9178652457 100644 --- a/src/components/dms/MessagesListHeader.tsx +++ b/src/components/dms/MessagesListHeader.tsx @@ -5,18 +5,19 @@ import { type ModerationCause, type ModerationDecision, } from '@atproto/api' -import {msg} from '@lingui/core/macro' -import {useLingui} from '@lingui/react' +import {useLingui} from '@lingui/react/macro' import {makeProfileLink} from '#/lib/routes/links' import {sanitizeDisplayName} from '#/lib/strings/display-names' import {type Shadow} from '#/state/cache/profile-shadow' import {isConvoActive, useConvo} from '#/state/messages/convo' import {type ConvoItem} from '#/state/messages/convo/types' +import {useSession} from '#/state/session' import {PreviewableUserAvatar} from '#/view/com/util/UserAvatar' -import {atoms as a, useTheme, web} from '#/alf' +import {atoms as a, useTheme} from '#/alf' +import {AvatarBubbles} from '#/components/AvatarBubbles' import {ConvoMenu} from '#/components/dms/ConvoMenu' -import {Bell2Off_Filled_Corner0_Rounded as BellStroke} from '#/components/icons/Bell2' +import {Bell2Off_Filled_Corner0_Rounded as BellOffIcon} from '#/components/icons/Bell2' import * as Layout from '#/components/Layout' import {Link} from '#/components/Link' import {PostAlerts} from '#/components/moderation/PostAlerts' @@ -34,6 +35,7 @@ export function MessagesListHeader({ moderation?: ModerationDecision }) { const t = useTheme() + const convoState = useConvo() const blockInfo = useMemo(() => { if (!moderation) return @@ -78,13 +80,15 @@ export function MessagesListHeader({ a.mt_xs, ]} /> - + {!convoState.isGroup?.() && ( + + )} @@ -108,22 +112,28 @@ function HeaderReady({ userBlock?: ModerationCause } }) { - const {_} = useLingui() + const {t: l} = useLingui() const t = useTheme() const convoState = useConvo() + const {currentAccount} = useSession() + + const groupInfo = convoState.getGroupInfo?.() + const isGroupChat = groupInfo != null const isDeletedAccount = profile?.handle === 'missing.invalid' - const displayName = isDeletedAccount - ? _(msg`Deleted Account`) - : sanitizeDisplayName( - profile.displayName || profile.handle, - moderation.ui('displayName'), - ) + const displayName = isGroupChat + ? (groupInfo.name ?? l`${profile.handle}'s group chat`) + : isDeletedAccount + ? l`Deleted Account` + : sanitizeDisplayName( + profile.displayName || profile.handle, + moderation.ui('displayName'), + ) - // @ts-ignore findLast is polyfilled - esb const latestMessageFromOther = convoState.items.findLast( (item: ConvoItem) => - item.type === 'message' && item.message.sender.did === profile.did, + item.type === 'message' && + item.message.sender.did !== currentAccount?.did, ) const latestReportableMessage = @@ -134,54 +144,58 @@ function HeaderReady({ return ( - - - - - - {displayName} - - - - {!isDeletedAccount && ( - - @{profile.handle} - {convoState.convo?.muted && ( - <> - {' '} - ·{' '} - - - )} - - )} + {isGroupChat ? ( + + + + {displayName} + - + ) : ( + + + + + + {displayName} + + + + {!isDeletedAccount && ( + + @{profile.handle} + {convoState.convo?.muted && ( + <> + {' '} + ·{' '} + + + )} + + )} + + + )} diff --git a/src/screens/Messages/Conversation.tsx b/src/screens/Messages/Conversation.tsx index 509907b331..9582386a96 100644 --- a/src/screens/Messages/Conversation.tsx +++ b/src/screens/Messages/Conversation.tsx @@ -101,7 +101,7 @@ function Inner() { const moderationOpts = useModerationOpts() const {data: recipientUnshadowed} = useProfileQuery({ - did: convoState.recipients?.[0].did, + did: convoState.getPrimaryMember?.()?.did, }) const recipient = useMaybeProfileShadow(recipientUnshadowed) @@ -135,7 +135,7 @@ function Inner() { <> {moderation ? ( - + ) : ( )} @@ -156,7 +156,7 @@ function Inner() { {isFocused && IS_WEB && } {!readyToShow && (moderation ? ( - + ) : ( ))} diff --git a/src/state/messages/convo/agent.ts b/src/state/messages/convo/agent.ts index 93726833f1..4e0530b050 100644 --- a/src/state/messages/convo/agent.ts +++ b/src/state/messages/convo/agent.ts @@ -37,6 +37,7 @@ import { import {type MessagesEventBus} from '#/state/messages/events/agent' import {type MessagesEventBusError} from '#/state/messages/events/types' import {IS_NATIVE} from '#/env' +import * as bsky from '#/types/bsky' const logger = Logger.create(Logger.Context.ConversationAgent) @@ -112,6 +113,9 @@ export class Convo { this.markConvoAccepted = this.markConvoAccepted.bind(this) this.addReaction = this.addReaction.bind(this) this.removeReaction = this.removeReaction.bind(this) + this.isGroup = this.isGroup.bind(this) + this.getGroupInfo = this.getGroupInfo.bind(this) + this.getPrimaryMember = this.getPrimaryMember.bind(this) } private commit() { @@ -155,6 +159,9 @@ export class Convo { markConvoAccepted: undefined, addReaction: undefined, removeReaction: undefined, + isGroup: this.isGroup, + getGroupInfo: this.getGroupInfo, + getPrimaryMember: this.getPrimaryMember, } } case ConvoStatus.Disabled: @@ -175,6 +182,9 @@ export class Convo { markConvoAccepted: this.markConvoAccepted, addReaction: this.addReaction, removeReaction: this.removeReaction, + isGroup: this.isGroup, + getGroupInfo: this.getGroupInfo, + getPrimaryMember: this.getPrimaryMember, } } case ConvoStatus.Error: { @@ -192,6 +202,9 @@ export class Convo { markConvoAccepted: undefined, addReaction: undefined, removeReaction: undefined, + isGroup: undefined, + getGroupInfo: undefined, + getPrimaryMember: undefined, } } default: { @@ -209,6 +222,9 @@ export class Convo { markConvoAccepted: undefined, addReaction: undefined, removeReaction: undefined, + isGroup: this.isGroup, + getGroupInfo: this.getGroupInfo, + getPrimaryMember: this.getPrimaryMember, } } } @@ -1332,4 +1348,34 @@ export class Convo { throw error } } + + // Group utilities + + isGroup(): boolean | undefined { + if (!this.convo) return undefined + return this.convo.kind === 'group' + } + + getGroupInfo(): ChatBskyConvoDefs.GroupConvoData | undefined { + if ( + this.convo && + bsky.dangerousIsType( + this.convo.kindData, + ChatBskyConvoDefs.isGroupConvoData, + ) + ) { + return this.convo.kindData + } + return undefined + } + + getPrimaryMember(): ChatBskyActorDefs.ProfileViewBasic | undefined { + if (this.convo?.kind === 'group') { + return ( + this.recipients?.find(r => r.role === 'owner') ?? this.recipients?.[0] + ) + } else { + return this.recipients?.find(r => r.did !== this.senderUserDid) + } + } } diff --git a/src/state/messages/convo/types.ts b/src/state/messages/convo/types.ts index 7053877935..3ea9443543 100644 --- a/src/state/messages/convo/types.ts +++ b/src/state/messages/convo/types.ts @@ -144,6 +144,9 @@ type FetchMessageHistory = () => Promise type MarkConvoAccepted = () => void type AddReaction = (messageId: string, reaction: string) => Promise type RemoveReaction = (messageId: string, reaction: string) => Promise +type IsGroup = () => boolean | undefined +type GetGroupInfo = () => ChatBskyConvoDefs.GroupConvoData | undefined +type GetPrimaryMember = () => ChatBskyActorDefs.ProfileViewBasic | undefined export type ConvoStateUninitialized = { status: ConvoStatus.Uninitialized @@ -159,6 +162,9 @@ export type ConvoStateUninitialized = { markConvoAccepted: undefined addReaction: undefined removeReaction: undefined + isGroup: IsGroup + getGroupInfo: GetGroupInfo + getPrimaryMember: GetPrimaryMember } export type ConvoStateInitializing = { status: ConvoStatus.Initializing @@ -174,6 +180,9 @@ export type ConvoStateInitializing = { markConvoAccepted: undefined addReaction: undefined removeReaction: undefined + isGroup: IsGroup + getGroupInfo: GetGroupInfo + getPrimaryMember: GetPrimaryMember } export type ConvoStateReady = { status: ConvoStatus.Ready @@ -189,6 +198,9 @@ export type ConvoStateReady = { markConvoAccepted: MarkConvoAccepted addReaction: AddReaction removeReaction: RemoveReaction + isGroup: IsGroup + getGroupInfo: GetGroupInfo + getPrimaryMember: GetPrimaryMember } export type ConvoStateBackgrounded = { status: ConvoStatus.Backgrounded @@ -204,6 +216,9 @@ export type ConvoStateBackgrounded = { markConvoAccepted: MarkConvoAccepted addReaction: AddReaction removeReaction: RemoveReaction + isGroup: IsGroup + getGroupInfo: GetGroupInfo + getPrimaryMember: GetPrimaryMember } export type ConvoStateSuspended = { status: ConvoStatus.Suspended @@ -219,6 +234,9 @@ export type ConvoStateSuspended = { markConvoAccepted: MarkConvoAccepted addReaction: AddReaction removeReaction: RemoveReaction + isGroup: IsGroup + getGroupInfo: GetGroupInfo + getPrimaryMember: GetPrimaryMember } export type ConvoStateError = { status: ConvoStatus.Error @@ -234,6 +252,9 @@ export type ConvoStateError = { markConvoAccepted: undefined addReaction: undefined removeReaction: undefined + isGroup: undefined + getGroupInfo: undefined + getPrimaryMember: undefined } export type ConvoStateDisabled = { status: ConvoStatus.Disabled @@ -249,6 +270,9 @@ export type ConvoStateDisabled = { markConvoAccepted: MarkConvoAccepted addReaction: AddReaction removeReaction: RemoveReaction + isGroup: IsGroup + getGroupInfo: GetGroupInfo + getPrimaryMember: GetPrimaryMember } export type ConvoState = | ConvoStateUninitialized