From 9ba11baab7bb79800a6162bbcc49f74c2b4c7fea Mon Sep 17 00:00:00 2001 From: DS Boyce <260543580+ds-boyce@users.noreply.github.com> Date: Wed, 22 Apr 2026 12:31:38 -0700 Subject: [PATCH] Display actual number of group clip clop requests (#10333) --- src/screens/Messages/ConversationSettings.tsx | 50 ++++++++++---- .../queries/messages/list-join-requests.ts | 65 +++++++++++++++++++ 2 files changed, 103 insertions(+), 12 deletions(-) create mode 100644 src/state/queries/messages/list-join-requests.ts diff --git a/src/screens/Messages/ConversationSettings.tsx b/src/screens/Messages/ConversationSettings.tsx index e63ccc3ef0..b5384b6c87 100644 --- a/src/screens/Messages/ConversationSettings.tsx +++ b/src/screens/Messages/ConversationSettings.tsx @@ -24,6 +24,7 @@ import {useEditGroupName} from '#/state/queries/messages/edit-group-name' import {useGetConvoAvailabilityQuery} from '#/state/queries/messages/get-convo-availability' import {useGetConvoForMembers} from '#/state/queries/messages/get-convo-for-members' import {useLeaveConvo} from '#/state/queries/messages/leave-conversation' +import {useListJoinRequestsQuery} from '#/state/queries/messages/list-join-requests' import {useMuteConvo} from '#/state/queries/messages/mute-conversation' import {useProfileBlockMutationQueue} from '#/state/queries/profile' import {useSession} from '#/state/session' @@ -109,7 +110,7 @@ export function MessagesConversationSettingsScreen({route}: Props) { - + ) @@ -119,7 +120,7 @@ function keyExtractor(item: Item) { return item.type === 'CHAT_MEMBER' ? item.profile.did : item.type } -function SettingsInner() { +function SettingsInner({convoId}: {convoId: string}) { const {t: l} = useLingui() const initialNumToRender = useInitialNumToRender({minItemHeight: 68}) @@ -137,6 +138,17 @@ function SettingsInner() { const data: bsky.profile.AnyProfileView[] = convo?.members ?? [] const invites: string[] = [] + const {data: joinRequestsData, hasNextPage: hasMoreRequests} = + useListJoinRequestsQuery({ + convoId, + enabled: isOwner, + }) + const requestCount = + joinRequestsData?.pages.reduce( + (sum, page) => sum + page.requests.length, + 0, + ) ?? 0 + const items = [ { type: 'MEMBERS_AND_REQUESTS', @@ -172,7 +184,8 @@ function SettingsInner() { return ( ) @@ -229,10 +242,12 @@ function SettingsInner() { function MembersAndRequests({ memberCount, requestCount, + hasMoreRequests, isOwner, }: { memberCount: number requestCount: number + hasMoreRequests: boolean isOwner: boolean }) { const t = useTheme() @@ -245,21 +260,32 @@ function MembersAndRequests({ Members{' '} {l`${memberCount}/${MEMBER_LIMIT}`} + style={[a.text_xs, a.font_medium, {color: t.palette.contrast_500}]}> + {l({ + message: `${memberCount}/${MEMBER_LIMIT}`, + comment: + 'The number of group chat members out of the total number of permitted users.', + })} + {isOwner && requestCount > 0 ? ( - {l`${plural(requestCount, { - one: '# request', - other: '# requests', - })}`} + {hasMoreRequests + ? l({ + message: `${requestCount}+ requests`, + comment: + 'Displayed when there are more than 50 requests to join a group chat', + }) + : l({ + message: `${plural(requestCount, { + one: '# request', + other: '# requests', + })}`, + comment: 'The number of requests to join a group chat.', + })} ) : null} diff --git a/src/state/queries/messages/list-join-requests.ts b/src/state/queries/messages/list-join-requests.ts new file mode 100644 index 0000000000..d95a181ecb --- /dev/null +++ b/src/state/queries/messages/list-join-requests.ts @@ -0,0 +1,65 @@ +import {useEffect} from 'react' +import {ChatBskyConvoDefs} from '@atproto/api' +import {useInfiniteQuery, useQueryClient} from '@tanstack/react-query' + +import {DM_SERVICE_HEADERS} from '#/lib/constants' +import {useMessagesEventBus} from '#/state/messages/events' +import {createQueryKey} from '#/state/queries/util' +import {useAgent} from '#/state/session' +import {STALE} from '..' + +const listJoinRequestsQueryKeyRoot = 'list-join-requests' + +export const createListJoinRequestsQueryKey = (args: {convoId: string}) => + createQueryKey(listJoinRequestsQueryKeyRoot, args) + +export function useListJoinRequestsQuery({ + convoId, + enabled, +}: { + convoId: string | undefined + enabled?: boolean +}) { + const agent = useAgent() + const queryClient = useQueryClient() + const messagesBus = useMessagesEventBus() + const isEnabled = enabled !== false && !!convoId + + useEffect(() => { + if (!isEnabled || !convoId) return + + return messagesBus.on( + event => { + if (event.type !== 'logs') return + for (const log of event.logs) { + if ( + ChatBskyConvoDefs.isLogIncomingJoinRequest(log) || + ChatBskyConvoDefs.isLogApproveJoinRequest(log) || + ChatBskyConvoDefs.isLogRejectJoinRequest(log) + ) { + void queryClient.invalidateQueries({ + queryKey: createListJoinRequestsQueryKey({convoId}), + }) + return + } + } + }, + {convoId}, + ) + }, [isEnabled, convoId, messagesBus, queryClient]) + + return useInfiniteQuery({ + enabled: isEnabled, + queryKey: createListJoinRequestsQueryKey({convoId: convoId ?? ''}), + queryFn: async ({pageParam}) => { + const {data} = await agent.chat.bsky.group.listJoinRequests( + {convoId: convoId!, cursor: pageParam, limit: 50}, + {headers: DM_SERVICE_HEADERS}, + ) + return data + }, + initialPageParam: undefined as string | undefined, + getNextPageParam: page => page.cursor, + staleTime: STALE.MINUTES.ONE, + }) +}