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,
+ })
+}