From 549891a3d6f211cbff83cb5f5793dd70728e828b Mon Sep 17 00:00:00 2001
From: DS Boyce <260543580+ds-boyce@users.noreply.github.com>
Date: Wed, 8 Apr 2026 10:00:42 -0700
Subject: [PATCH] Add more group clip clop settings (#10199)
---
src/screens/Messages/ConversationSettings.tsx | 186 ++++++++++++++----
1 file changed, 148 insertions(+), 38 deletions(-)
diff --git a/src/screens/Messages/ConversationSettings.tsx b/src/screens/Messages/ConversationSettings.tsx
index 001b032a41..0b3bfa8b8e 100644
--- a/src/screens/Messages/ConversationSettings.tsx
+++ b/src/screens/Messages/ConversationSettings.tsx
@@ -1,5 +1,5 @@
import {useMemo, useState} from 'react'
-import {Pressable, View} from 'react-native'
+import {Pressable, type StyleProp, View, type ViewStyle} from 'react-native'
import {moderateProfile} from '@atproto/api'
import {plural} from '@lingui/core/macro'
import {Trans, useLingui} from '@lingui/react/macro'
@@ -7,10 +7,13 @@ import {useNavigation} from '@react-navigation/native'
import {useBottomBarOffset} from '#/lib/hooks/useBottomBarOffset'
import {useInitialNumToRender} from '#/lib/hooks/useInitialNumToRender'
+import {useRequireEmailVerification} from '#/lib/hooks/useRequireEmailVerification'
import {type NavigationProp} from '#/lib/routes/types'
import {sanitizeDisplayName} from '#/lib/strings/display-names'
import {sanitizeHandle} from '#/lib/strings/handles'
import {useModerationOpts} from '#/state/preferences/moderation-opts'
+import {useGetConvoAvailabilityQuery} from '#/state/queries/messages/get-convo-availability'
+import {useGetConvoForMembers} from '#/state/queries/messages/get-convo-for-members'
import {List} from '#/view/com/util/List'
import {PreviewableUserAvatar} from '#/view/com/util/UserAvatar'
import {atoms as a, useBreakpoints, useTheme, web} from '#/alf'
@@ -39,9 +42,12 @@ import {PlusLarge_Stroke2_Corner0_Rounded as PlusIcon} from '#/components/icons/
import * as Layout from '#/components/Layout'
import {InlineLinkText} from '#/components/Link'
import * as Menu from '#/components/Menu'
+import {type TriggerChildProps} from '#/components/Menu/types'
import * as Prompt from '#/components/Prompt'
import {SubtleHover} from '#/components/SubtleHover'
+import * as Toast from '#/components/Toast'
import {Text} from '#/components/Typography'
+import {useAnalytics} from '#/analytics'
import {IS_NATIVE} from '#/env'
import type * as bsky from '#/types/bsky'
@@ -135,7 +141,7 @@ function SettingsInner() {
desktopFixedHeight
initialNumToRender={initialNumToRender}
keyExtractor={keyExtractor}
- ListHeaderComponent={}
+ ListHeaderComponent={}
renderItem={renderItem}
sideBorders={false}
windowSize={11}
@@ -277,7 +283,9 @@ function Member({
{l`Added by ${invitedByDisplayName}`}
)
- let statusBadge: React.ReactNode | null =
+ let statusBadge: React.ReactNode | null = (
+
+ )
switch (status) {
case 'admin':
invitedBy = null
@@ -289,7 +297,7 @@ function Member({
{l`Invited by ${invitedByDisplayName}`}
)
- statusBadge =
+ statusBadge =
break
}
@@ -367,10 +375,84 @@ function StatusBadge({label}: {label: string}) {
)
}
-function MemberMenu({profile}: {profile: bsky.profile.AnyProfileView}) {
+function StatusButton({
+ label,
+ style,
+ ...rest
+}: {
+ label: string
+ style?: StyleProp
+} & TriggerChildProps['props']) {
+ const t = useTheme()
+
+ return (
+
+
+ {label}
+
+
+ )
+}
+
+function MemberMenu({
+ profile,
+ type,
+}: {
+ profile: bsky.profile.AnyProfileView
+ type: 'member' | 'invited'
+}) {
const navigation = useNavigation()
const t = useTheme()
const {t: l} = useLingui()
+ const ax = useAnalytics()
+ const requireEmailVerification = useRequireEmailVerification()
+
+ const {data: convoAvailability} = useGetConvoAvailabilityQuery(profile.did)
+ const {mutate: initiateConvo} = useGetConvoForMembers({
+ onSuccess: ({convo}) => {
+ ax.metric('chat:open', {logContext: 'ProfileHeader'})
+ navigation.navigate('MessagesConversation', {conversation: convo.id})
+ },
+ onError: () => {
+ Toast.show(l`Failed to create conversation`)
+ },
+ })
+
+ const onPress = () => {
+ if (!convoAvailability?.canChat) {
+ return
+ }
+
+ if (convoAvailability.convo) {
+ ax.metric('chat:open', {logContext: 'ProfileHeader'})
+ navigation.navigate('MessagesConversation', {
+ conversation: convoAvailability.convo.id,
+ })
+ } else {
+ ax.metric('chat:create', {logContext: 'ProfileHeader'})
+ initiateConvo([profile.did])
+ }
+ }
+
+ const wrappedOnPress = requireEmailVerification(onPress, {
+ instructions: [
+
+ Before you can message another user, you must first verify your email.
+ ,
+ ],
+ })
const moderationOpts = useModerationOpts()
const moderation = useMemo(
@@ -392,21 +474,35 @@ function MemberMenu({profile}: {profile: bsky.profile.AnyProfileView}) {
return (
- {({props, state}) => (
-
-
-
- )}
+ {({props, state}) =>
+ type === 'invited' ? (
+
+ ) : (
+
+
+
+ )
+ }
@@ -420,7 +516,7 @@ function MemberMenu({profile}: {profile: bsky.profile.AnyProfileView}) {
- {}}>
+
Message
@@ -429,27 +525,41 @@ function MemberMenu({profile}: {profile: bsky.profile.AnyProfileView}) {
- {}}>
-
- Block
-
-
-
- {}}>
-
- Remove from chat
-
-
-
+ {type === 'member' ? (
+ <>
+ {}}>
+
+ Block
+
+
+
+ {}}>
+
+ Remove from chat
+
+
+
+ >
+ ) : null}
+ {type === 'invited' ? (
+ {}}>
+
+ Uninvite
+
+
+
+ ) : null}
)
}
-function SettingsHeading() {
+function SettingsHeader({profiles}: {profiles: bsky.profile.AnyProfileView[]}) {
const t = useTheme()
const {t: l} = useLingui()
const editNamePrompt = Prompt.usePromptControl()
@@ -457,7 +567,7 @@ function SettingsHeading() {
const lockChatPrompt = Prompt.usePromptControl()
const [groupName, setGroupName] = useState('Work in Progress')
- const [newGroupName, setNewGroupName] = useState('Work in Progress')
+ const [newGroupName, setNewGroupName] = useState(groupName)
const [isMuted, setIsMuted] = useState(false)
const [isLocked, setIsLocked] = useState(false)
@@ -500,7 +610,7 @@ function SettingsHeading() {
-
+