[Chat] Fix assumption that every chat has an owner (#10373)

This commit is contained in:
Samuel Newman
2026-05-06 14:11:07 +01:00
committed by GitHub
parent 2aa544fade
commit a7ed3ee3cc
12 changed files with 163 additions and 90 deletions
@@ -14,7 +14,11 @@ import {useHaptics} from '#/lib/haptics'
import {createSanitizedDisplayName} from '#/lib/moderation/create-sanitized-display-name'
import {decrementBadgeCount} from '#/lib/notifications/notifications'
import {sanitizeHandle} from '#/lib/strings/handles'
import {type Shadow, useProfileShadow} from '#/state/cache/profile-shadow'
import {
type Shadow,
useMaybeProfileShadow,
useProfileShadow,
} from '#/state/cache/profile-shadow'
import {useModerationOpts} from '#/state/preferences/moderation-opts'
import {
precacheConvoQuery,
@@ -120,7 +124,7 @@ function DirectChatItem({
return (
<BaseChatItem
convo={convo.view}
convo={convo}
avatar={
<PreviewableUserAvatar
profile={profile}
@@ -167,10 +171,11 @@ function GroupChatItem({
children?: React.ReactNode
}) {
const {t: l} = useLingui()
const groupOwner = useProfileShadow(convo.primaryMember)
const groupOwner = useMaybeProfileShadow(convo.primaryMember)
const moderation = useMemo(
() => moderateProfile(groupOwner, moderationOpts),
() =>
groupOwner ? moderateProfile(groupOwner, moderationOpts) : undefined,
[groupOwner, moderationOpts],
)
@@ -178,7 +183,7 @@ function GroupChatItem({
return (
<BaseChatItem
convo={convo.view}
convo={convo}
avatar={<AvatarBubbles profiles={convo.members} size={52} />}
title={chatName}
accessibilityHint={l`Go to the group chat named "${chatName}"`}
@@ -208,15 +213,15 @@ function BaseChatItem({
postAlerts,
children,
}: {
convo: ChatBskyConvoDefs.ConvoView
convo: ConvoWithDetails
avatar: React.ReactNode
title: string
subtitle?: string
accessibilityHint: string
isDeletedAccount: boolean
isBlockedAccount: boolean
primaryProfile: Shadow<bsky.profile.AnyProfileView>
primaryProfileModeration: ModerationDecision
primaryProfile?: Shadow<bsky.profile.AnyProfileView>
primaryProfileModeration?: ModerationDecision
showMenu?: boolean
showProfileBadges: boolean
postAlerts?: React.ReactNode
@@ -233,9 +238,16 @@ function BaseChatItem({
const playHaptic = useHaptics()
const queryClient = useQueryClient()
const hasUnread = convo.unreadCount > 0 && !isDeletedAccount
const hasUnread =
convo.view.unreadCount > 0 &&
!isDeletedAccount &&
!(
convo.kind === 'group' &&
convo.details.lockStatus === 'locked-permanently'
)
const blockInfo = useMemo(() => {
if (!primaryProfileModeration) return {listBlocks: [], userBlock: undefined}
const modui = primaryProfileModeration.ui('profileView')
const blocks = modui.alerts.filter(alert => alert.type === 'blocking')
const listBlocks = blocks.filter(alert => alert.source.type === 'list')
@@ -246,7 +258,11 @@ function BaseChatItem({
}
}, [primaryProfileModeration])
const isDimStyle = convo.muted || isBlockedAccount || isDeletedAccount
const isDimStyle =
convo.view.muted ||
isBlockedAccount ||
isDeletedAccount ||
(convo.kind === 'group' && convo.details.lockStatus !== 'unlocked')
const {lastMessage, lastMessageSentAt, latestReportableMessage} =
useMemo(() => {
@@ -257,8 +273,8 @@ function BaseChatItem({
let latestReportableMessage: ChatBskyConvoDefs.MessageView | undefined
// Deleted message
if (ChatBskyConvoDefs.isDeletedMessageView(convo.lastMessage)) {
lastMessageSentAt = convo.lastMessage.sentAt
if (ChatBskyConvoDefs.isDeletedMessageView(convo.view.lastMessage)) {
lastMessageSentAt = convo.view.lastMessage.sentAt
lastMessage = isDeletedAccount
? l`Conversation deleted`
@@ -266,9 +282,9 @@ function BaseChatItem({
}
// Message
if (ChatBskyConvoDefs.isMessageView(convo.lastMessage)) {
if (ChatBskyConvoDefs.isMessageView(convo.view.lastMessage)) {
const info = getMessageInfo({
convo,
convo: convo.view,
currentAccountDid: currentAccount?.did,
i18n,
})
@@ -280,9 +296,9 @@ function BaseChatItem({
}
// Reaction
if (ChatBskyConvoDefs.isMessageAndReactionView(convo.lastReaction)) {
if (ChatBskyConvoDefs.isMessageAndReactionView(convo.view.lastReaction)) {
const info = getReactionInfo({
convo,
convo: convo.view,
currentAccountDid: currentAccount?.did,
i18n,
})
@@ -297,14 +313,14 @@ function BaseChatItem({
}
// System message
if (ChatBskyConvoDefs.isSystemMessageView(convo.lastMessage)) {
if (ChatBskyConvoDefs.isSystemMessageView(convo.view.lastMessage)) {
const info = getSystemMessageInfo(
convo.lastMessage.data,
new Map(convo.members.map(m => [m.did, m])),
convo.view.lastMessage.data,
new Map(convo.view.members.map(m => [m.did, m])),
)
if (info) {
lastMessage = i18n._(info.message)
lastMessageSentAt = convo.lastMessage.sentAt
lastMessageSentAt = convo.view.lastMessage.sentAt
}
}
@@ -332,11 +348,11 @@ function BaseChatItem({
const onPress = useCallback(
(e: GestureResponderEvent) => {
for (const member of convo.members) {
for (const member of convo.view.members) {
unstableCacheProfileView(queryClient, member)
}
precacheConvoQuery(queryClient, convo)
void decrementBadgeCount(convo.unreadCount)
precacheConvoQuery(queryClient, convo.view)
void decrementBadgeCount(convo.view.unreadCount)
if (isDeletedAccount) {
e.preventDefault()
menuControl.open()
@@ -359,7 +375,7 @@ function BaseChatItem({
icon: EnvelopeOpen,
action: () => {
markAsRead({
convoId: convo.id,
convoId: convo.view.id,
})
},
}
@@ -402,7 +418,7 @@ function BaseChatItem({
</View>
<Link
to={`/messages/${convo.id}`}
to={`/messages/${convo.view.id}`}
label={title}
accessibilityHint={accessibilityHint}
accessibilityActions={
@@ -419,7 +435,7 @@ function BaseChatItem({
]
: undefined
}
onPressIn={() => precacheConvoQuery(queryClient, convo)}
onPressIn={() => precacheConvoQuery(queryClient, convo.view)}
onPress={onPress}
onLongPress={showMenu && IS_NATIVE ? onLongPress : undefined}
onAccessibilityAction={showMenu ? onLongPress : undefined}>
@@ -455,7 +471,7 @@ function BaseChatItem({
</Text>
</View>
{showProfileBadges && (
{showProfileBadges && primaryProfile && (
<ProfileBadges
profile={primaryProfile}
size="md"
@@ -480,7 +496,7 @@ function BaseChatItem({
</TimeElapsed>
</View>
)}
{(convo.muted || isBlockedAccount) && (
{(convo.view.muted || isBlockedAccount) && (
<Text
style={[
a.text_sm,
@@ -550,13 +566,14 @@ function BaseChatItem({
<ChatListItemPortal.Outlet />
{showMenu && (
{/* TODO: Allow showing menu for groups where the owner has left! */}
{showMenu && primaryProfile && (
<ConvoMenu
convo={convo}
convo={convo.view}
profile={primaryProfile}
control={menuControl}
currentScreen="list"
showMarkAsRead={convo.unreadCount > 0}
showMarkAsRead={convo.view.unreadCount > 0}
hideTrigger={IS_NATIVE}
blockInfo={blockInfo}
style={[
@@ -576,7 +593,7 @@ function BaseChatItem({
<LeaveConvoPrompt
control={leaveConvoControl}
convoId={convo.id}
convoId={convo.view.id}
currentScreen="list"
/>
</View>