diff --git a/src/components/dms/ConvoMenu.tsx b/src/components/dms/ConvoMenu.tsx index 587a25e95b..8ff4cf55bc 100644 --- a/src/components/dms/ConvoMenu.tsx +++ b/src/components/dms/ConvoMenu.tsx @@ -190,7 +190,7 @@ function MenuContent({ const isDeletedAccount = profile.handle === 'missing.invalid' const convoId = initialConvo.id - const {data: convo} = useConvoQuery(initialConvo) + const {data: convo} = useConvoQuery({convoId}) const onNavigateToProfile = useCallback(() => { navigation.navigate('Profile', {name: profile.did}) diff --git a/src/components/dms/MessagesListHeader.tsx b/src/components/dms/MessagesListHeader.tsx index 13e747231f..0806f3a116 100644 --- a/src/components/dms/MessagesListHeader.tsx +++ b/src/components/dms/MessagesListHeader.tsx @@ -1,10 +1,9 @@ import {useMemo} from 'react' import {View} from 'react-native' import { - type AppBskyActorDefs, ChatBskyConvoDefs, - type ModerationCause, - type ModerationDecision, + moderateProfile, + type ModerationOpts, } from '@atproto/api' import {useLingui} from '@lingui/react/macro' import {useNavigation} from '@react-navigation/native' @@ -12,7 +11,8 @@ import {useNavigation} from '@react-navigation/native' import {createSanitizedDisplayName} from '#/lib/moderation/create-sanitized-display-name' import {makeProfileLink} from '#/lib/routes/links' import {type NavigationProp} from '#/lib/routes/types' -import {type Shadow} from '#/state/cache/profile-shadow' +import {useProfileShadow} from '#/state/cache/profile-shadow' +import {useModerationOpts} from '#/state/preferences/moderation-opts' import {useSession} from '#/state/session' import {PreviewableUserAvatar} from '#/view/com/util/UserAvatar' import {atoms as a, useTheme} from '#/alf' @@ -30,30 +30,9 @@ import {type ConvoWithDetails} from './util' const PFP_SIZE = IS_WEB ? 40 : Layout.HEADER_SLOT_SIZE -export function MessagesListHeader({ - convo, - profile, - moderation, -}: { - convo?: ConvoWithDetails | null - profile?: Shadow - moderation?: ModerationDecision | null -}) { +export function MessagesListHeader({convo}: {convo?: ConvoWithDetails | null}) { const t = useTheme() - - const isGroupChat = convo?.kind === 'group' - - const blockInfo = useMemo(() => { - if (!moderation) return - const modui = moderation.ui('profileView') - const blocks = modui.alerts.filter(alert => alert.type === 'blocking') - const listBlocks = blocks.filter(alert => alert.source.type === 'list') - const userBlock = blocks.find(alert => alert.source.type === 'user') - return { - listBlocks, - userBlock, - } - }, [moderation]) + const moderationOpts = useModerationOpts() return ( @@ -61,20 +40,11 @@ export function MessagesListHeader({ - {convo ? ( - moderation && blockInfo && profile && !isGroupChat ? ( - + {convo && moderationOpts ? ( + convo.kind === 'direct' ? ( + ) : ( - + ) ) : ( <> @@ -108,20 +78,27 @@ export function MessagesListHeader({ function ProfileHeaderReady({ convo, - profile, - moderation, - blockInfo, + moderationOpts, }: { - convo: ConvoWithDetails - profile: Shadow - moderation: ModerationDecision - blockInfo: { - listBlocks: ModerationCause[] - userBlock?: ModerationCause - } + convo: Extract + moderationOpts: ModerationOpts }) { const {t: l} = useLingui() const {currentAccount} = useSession() + const profile = useProfileShadow(convo.primaryMember) + + const moderation = moderateProfile(profile, moderationOpts) + + const blockInfo = useMemo(() => { + const modui = moderation.ui('profileView') + const blocks = modui.alerts.filter(alert => alert.type === 'blocking') + const listBlocks = blocks.filter(alert => alert.source.type === 'list') + const userBlock = blocks.find(alert => alert.source.type === 'user') + return { + listBlocks, + userBlock, + } + }, [moderation]) const isDeletedAccount = profile?.handle === 'missing.invalid' const displayName = isDeletedAccount @@ -171,29 +148,13 @@ function ProfileHeaderReady({ function GroupHeaderReady({ convo, - profile, - moderation, }: { - convo: ConvoWithDetails - profile?: Shadow - moderation?: ModerationDecision | null + convo: Extract }) { const {t: l} = useLingui() const navigation = useNavigation() - const groupInfo = convo.kind === 'group' ? convo.details : undefined - - const isDeletedAccount = profile?.handle === 'missing.invalid' - const displayName = isDeletedAccount - ? l`Deleted Account` - : profile - ? createSanitizedDisplayName(profile, true, moderation?.ui('displayName')) - : undefined - const groupName = - groupInfo?.name ?? - (displayName ? l`${displayName}’s group chat` : l`Group chat`) - const handleNavigateToSettings = () => { navigation.navigate('MessagesConversationSettings', { conversation: convo.view.id, @@ -206,7 +167,7 @@ function GroupHeaderReady({ <> - {groupName} + {convo.details.name} } diff --git a/src/components/dms/util.ts b/src/components/dms/util.ts index 491023cf2f..64e6bb16c6 100644 --- a/src/components/dms/util.ts +++ b/src/components/dms/util.ts @@ -56,12 +56,12 @@ export function hasReachedReactionLimit( return myReactions.length >= EMOJI_REACTION_LIMIT } -type GroupConvoMember = ChatBskyActorDefs.ProfileViewBasic & { +export type GroupConvoMember = ChatBskyActorDefs.ProfileViewBasic & { // can be missing if account deleted kind?: $Typed } -type DirectConvoMember = ChatBskyActorDefs.ProfileViewBasic & { +export type DirectConvoMember = ChatBskyActorDefs.ProfileViewBasic & { kind: $Typed } diff --git a/src/screens/Messages/Conversation.tsx b/src/screens/Messages/Conversation.tsx index 5e390f7a0c..0926770eb3 100644 --- a/src/screens/Messages/Conversation.tsx +++ b/src/screens/Messages/Conversation.tsx @@ -1,11 +1,7 @@ import {useCallback, useEffect, useMemo, useState} from 'react' import {type LayoutChangeEvent, View} from 'react-native' import {useSafeAreaInsets} from 'react-native-safe-area-context' -import { - type AppBskyActorDefs, - moderateProfile, - type ModerationDecision, -} from '@atproto/api' +import {moderateProfile} from '@atproto/api' import { ScrollEdgeEffect, ScrollEdgeEffectProvider, @@ -28,13 +24,13 @@ import { type CommonNavigatorParams, type NavigationProp, } from '#/lib/routes/types' -import {type Shadow, useMaybeProfileShadow} from '#/state/cache/profile-shadow' +import {useMaybeProfileShadow} from '#/state/cache/profile-shadow' import {useEmail} from '#/state/email-verification' import {ConvoProvider, isConvoActive, useConvo} from '#/state/messages/convo' import {ConvoStatus} from '#/state/messages/convo/types' import {useCurrentConvoId} from '#/state/messages/current-convo-id' import {useModerationOpts} from '#/state/preferences/moderation-opts' -import {useProfileQuery} from '#/state/queries/profile' +import {useConvoQuery} from '#/state/queries/messages/conversation' import {useSession} from '#/state/session' import {useSetMinimalShellMode} from '#/state/shell' import {MessagesList} from '#/screens/Messages/components/MessagesList' @@ -52,6 +48,7 @@ import {Error} from '#/components/Error' import * as Layout from '#/components/Layout' import {Loader} from '#/components/Loader' import {IS_LIQUID_GLASS, IS_WEB} from '#/env' +import {ChatDisabled} from './components/ChatDisabled' type Props = NativeStackScreenProps< CommonNavigatorParams, @@ -95,36 +92,26 @@ export function MessagesConversationScreenInner({route}: Props) { style={web([{minHeight: 0}, a.flex_1])}> - + ) } -function Inner() { +function Inner({convoId}: {convoId: string}) { const t = useTheme() const convoState = useConvo() const {_} = useLingui() const {currentAccount} = useSession() const isFocused = useIsFocused() const {top: topInset} = useSafeAreaInsets() + const {data: convoData} = useConvoQuery({convoId}) - const convo = convoState.convo - ? parseConvoView(convoState.convo, currentAccount?.did) + const convo = convoData + ? parseConvoView(convoData, currentAccount?.did) : null - const moderationOpts = useModerationOpts() - const {data: recipientUnshadowed} = useProfileQuery({ - did: convoState.getPrimaryMember?.()?.did, - }) - const recipient = useMaybeProfileShadow(recipientUnshadowed) - - const moderation = useMemo(() => { - if (!recipient || !moderationOpts) return null - return moderateProfile(recipient, moderationOpts) - }, [recipient, moderationOpts]) - // Because we want to give the list a chance to asynchronously scroll to the end before it is visible to the user, // we use `hasScrolled` to determine when to render. With that said however, there is a chance that the chat will be // empty. So, we also check for that possible state as well and render once we can. @@ -150,15 +137,7 @@ function Inner() { <> - {moderation ? ( - - ) : ( - - )} + } {!readyToShow && ( - {moderation ? ( - - ) : ( - - )} + )} 0} /> {!readyToShow && ( @@ -219,20 +189,18 @@ function Inner() { } function InnerReady({ - moderation, - recipient, hasScrolled, setHasScrolled, convo, isActive, + isDisabled, hasMessages, }: { - moderation: ModerationDecision | null - recipient: Shadow | undefined hasScrolled: boolean setHasScrolled: React.Dispatch> convo: ConvoWithDetails | null isActive: boolean + isDisabled: boolean hasMessages: boolean }) { const navigation = useNavigation() @@ -284,13 +252,14 @@ function InnerReady({ maybeBlockForEmailVerification() }, [maybeBlockForEmailVerification]) - const header = ( - - ) + const primaryMember = useMaybeProfileShadow(convo?.primaryMember) + const moderationOpts = useModerationOpts() + const primaryMemberModeration = useMemo(() => { + if (!primaryMember || !moderationOpts) return null + return moderateProfile(primaryMember, moderationOpts) + }, [primaryMember, moderationOpts]) + + const header = return ( <> @@ -308,16 +277,17 @@ function InnerReady({ + ) : convo && primaryMember && primaryMemberModeration?.blocked ? ( ) : null } diff --git a/src/screens/Messages/components/ChatListItem.tsx b/src/screens/Messages/components/ChatListItem.tsx index 1f51656b1c..eaa0694822 100644 --- a/src/screens/Messages/components/ChatListItem.tsx +++ b/src/screens/Messages/components/ChatListItem.tsx @@ -482,6 +482,7 @@ function BaseChatItem({ ] : undefined } + onPressIn={() => precacheConvoQuery(queryClient, convo)} onPress={onPress} onLongPress={showMenu && IS_NATIVE ? onLongPress : undefined} onAccessibilityAction={showMenu ? onLongPress : undefined}> diff --git a/src/screens/Messages/components/MessagesList.tsx b/src/screens/Messages/components/MessagesList.tsx index bdff2412c1..e7121602ef 100644 --- a/src/screens/Messages/components/MessagesList.tsx +++ b/src/screens/Messages/components/MessagesList.tsx @@ -50,7 +50,6 @@ import { import {useGetPost} from '#/state/queries/post' import {useAgent} from '#/state/session' import {List, type ListMethods} from '#/view/com/util/List' -import {ChatDisabled} from '#/screens/Messages/components/ChatDisabled' import {MessageComposer} from '#/screens/Messages/components/MessageComposer' import {MessageInput} from '#/screens/Messages/components/MessageInput' import {MessageListError} from '#/screens/Messages/components/MessageListError' @@ -93,14 +92,12 @@ function onScrollToIndexFailed() { export function MessagesList({ hasScrolled, setHasScrolled, - blocked, footer, hasAcceptOverride, transparentHeaderHeight, }: { hasScrolled: boolean setHasScrolled: React.Dispatch> - blocked?: boolean footer?: React.ReactNode hasAcceptOverride?: boolean transparentHeaderHeight?: number @@ -489,11 +486,7 @@ export function MessagesList({ }), opened: 0, }}> - {convoState.status === ConvoStatus.Disabled ? ( - - ) : blocked ? ( - footer - ) : ( + {footer ?? ( diff --git a/src/state/queries/messages/conversation.ts b/src/state/queries/messages/conversation.ts index 393bf9e520..b8f26cc88c 100644 --- a/src/state/queries/messages/conversation.ts +++ b/src/state/queries/messages/conversation.ts @@ -19,19 +19,18 @@ import { const RQKEY_ROOT = 'convo' export const RQKEY = (convoId: string) => [RQKEY_ROOT, convoId] -export function useConvoQuery(convo: ChatBskyConvoDefs.ConvoView) { +export function useConvoQuery({convoId}: {convoId: string}) { const agent = useAgent() return useQuery({ - queryKey: RQKEY(convo.id), + queryKey: RQKEY(convoId), queryFn: async () => { const {data} = await agent.chat.bsky.convo.getConvo( - {convoId: convo.id}, + {convoId}, {headers: DM_SERVICE_HEADERS}, ) return data.convo }, - initialData: convo, staleTime: STALE.INFINITY, }) } @@ -58,7 +57,7 @@ export function useMarkAsReadMutation() { }) => { if (!convoId) throw new Error('No convoId provided') - await agent.api.chat.bsky.convo.updateRead( + await agent.chat.bsky.convo.updateRead( { convoId, messageId,