Enable sharing a post to a new group chat (#11191)

This commit is contained in:
DS Boyce
2026-07-22 15:54:46 -07:00
committed by GitHub
parent 8d7a7369aa
commit f38f84e1a3
7 changed files with 367 additions and 69 deletions
-13
View File
@@ -282,19 +282,6 @@
"count": 1
}
},
"src/components/PostControls/ShareMenu/ShareMenuItems.tsx": {
"typescript/no-floating-promises": {
"count": 3
},
"typescript/no-misused-promises": {
"count": 1
}
},
"src/components/PostControls/ShareMenu/ShareMenuItems.web.tsx": {
"typescript/no-floating-promises": {
"count": 3
}
},
"src/components/PostControls/ShareMenu/index.tsx": {
"typescript/no-floating-promises": {
"count": 1
+1 -1
View File
@@ -605,7 +605,7 @@ export type Events = {
// Group chat adoption
'groupchat:create': {
logContext: 'NewChatDialog'
logContext: 'NewChatDialog' | 'SendViaChatDialog'
}
'groupchat:landingPage:view': {
hasSession: boolean
@@ -1,9 +1,7 @@
import {memo, useMemo} from 'react'
import * as ExpoClipboard from 'expo-clipboard'
import {AtUri} from '@atproto/api'
import {msg} from '@lingui/core/macro'
import {useLingui} from '@lingui/react'
import {Trans} from '@lingui/react/macro'
import {Trans, useLingui} from '@lingui/react/macro'
import {useNavigation} from '@react-navigation/native'
import {useQueryClient} from '@tanstack/react-query'
@@ -37,7 +35,7 @@ let ShareMenuItems = ({
}: ShareMenuItemsProps): React.ReactNode => {
const ax = useAnalytics()
const {hasSession} = useSession()
const {_} = useLingui()
const {t: l} = useLingui()
const navigation = useNavigation<NavigationProp>()
const sendViaChatControl = useDialogControl()
const [devModeEnabled] = useDevMode()
@@ -61,7 +59,7 @@ let ShareMenuItems = ({
const onSharePost = () => {
ax.metric('share:press:nativeShare', {})
const url = toShareUrl(href)
shareUrl(url)
void shareUrl(url)
onShareProp()
}
@@ -74,7 +72,7 @@ let ShareMenuItems = ({
} else {
await ExpoClipboard.setStringAsync(url)
}
Toast.show(_(msg`Copied to clipboard`), {
Toast.show(l`Copied to clipboard`, {
type: 'success',
})
onShareProp()
@@ -93,11 +91,11 @@ let ShareMenuItems = ({
}
const onShareATURI = () => {
shareText(postUri)
void shareText(postUri)
}
const onShareAuthorDID = () => {
shareText(postAuthor.did)
void shareText(postAuthor.did)
}
return (
@@ -113,13 +111,13 @@ let ShareMenuItems = ({
</Menu.ContainerItem>
<Menu.Item
testID="postDropdownSendViaDMBtn"
label={_(msg`Send via direct message`)}
label={l`Send via chat`}
onPress={() => {
ax.metric('share:press:openDmSearch', {})
sendViaChatControl.open()
}}>
<Menu.ItemText>
<Trans>Send via direct message</Trans>
<Trans>Send via chat</Trans>
</Menu.ItemText>
<Menu.ItemIcon icon={PaperPlaneIcon} position="right" />
</Menu.Item>
@@ -129,7 +127,7 @@ let ShareMenuItems = ({
<Menu.Group>
<Menu.Item
testID="postDropdownShareBtn"
label={_(msg`Share via...`)}
label={l`Share via...`}
onPress={onSharePost}>
<Menu.ItemText>
<Trans>Share via...</Trans>
@@ -139,8 +137,8 @@ let ShareMenuItems = ({
<Menu.Item
testID="postDropdownShareBtn"
label={_(msg`Copy link to post`)}
onPress={onCopyLink}>
label={l`Copy link to post`}
onPress={() => void onCopyLink()}>
<Menu.ItemText>
<Trans>Copy link to post</Trans>
</Menu.ItemText>
@@ -164,7 +162,7 @@ let ShareMenuItems = ({
<Menu.Group>
<Menu.Item
testID="postAtUriShareBtn"
label={_(msg`Share post at:// URI`)}
label={l`Share post at:// URI`}
onPress={onShareATURI}>
<Menu.ItemText>
<Trans>Share post at:// URI</Trans>
@@ -173,7 +171,7 @@ let ShareMenuItems = ({
</Menu.Item>
<Menu.Item
testID="postAuthorDIDShareBtn"
label={_(msg`Share author DID`)}
label={l`Share author DID`}
onPress={onShareAuthorDID}>
<Menu.ItemText>
<Trans>Share author DID</Trans>
@@ -183,7 +181,6 @@ let ShareMenuItems = ({
</Menu.Group>
)}
</Menu.Outer>
<SendViaChatDialog
control={sendViaChatControl}
onSelectChat={onSelectChatToShareTo}
@@ -1,8 +1,6 @@
import {memo, useMemo} from 'react'
import {AtUri} from '@atproto/api'
import {msg} from '@lingui/core/macro'
import {useLingui} from '@lingui/react'
import {Trans} from '@lingui/react/macro'
import {Trans, useLingui} from '@lingui/react/macro'
import {useNavigation} from '@react-navigation/native'
import {makeProfileLink} from '#/lib/routes/links'
@@ -35,7 +33,7 @@ let ShareMenuItems = ({
const ax = useAnalytics()
const {hasSession} = useSession()
const {gtMobile} = useBreakpoints()
const {_} = useLingui()
const {t: l} = useLingui()
const navigation = useNavigation<NavigationProp>()
const embedPostControl = useDialogControl()
const sendViaChatControl = useDialogControl()
@@ -60,7 +58,7 @@ let ShareMenuItems = ({
const onCopyLink = () => {
ax.metric('share:press:copyLink', {})
const url = toShareUrl(href)
shareUrl(url)
void shareUrl(url)
onShareProp()
}
@@ -75,17 +73,17 @@ let ShareMenuItems = ({
const canEmbed = IS_WEB && gtMobile && !hideInPWI
const onShareATURI = () => {
shareText(postUri)
void shareText(postUri)
}
const onShareAuthorDID = () => {
shareText(postAuthor.did)
void shareText(postAuthor.did)
}
const copyLinkItem = (
<Menu.Item
testID="postDropdownShareBtn"
label={_(msg`Copy link to post`)}
label={l`Copy link to post`}
onPress={onCopyLink}>
<Menu.ItemText>
<Trans>Copy link to post</Trans>
@@ -102,13 +100,13 @@ let ShareMenuItems = ({
{hasSession && aa.state.access === aa.Access.Full && (
<Menu.Item
testID="postDropdownSendViaDMBtn"
label={_(msg`Send via direct message`)}
label={l`Send via chat`}
onPress={() => {
ax.metric('share:press:openDmSearch', {})
sendViaChatControl.open()
}}>
<Menu.ItemText>
<Trans>Send via direct message</Trans>
<Trans>Send via chat</Trans>
</Menu.ItemText>
<Menu.ItemIcon icon={Send} position="right" />
</Menu.Item>
@@ -117,12 +115,12 @@ let ShareMenuItems = ({
{canEmbed && (
<Menu.Item
testID="postDropdownEmbedBtn"
label={_(msg`Embed post`)}
label={l`Embed post`}
onPress={() => {
ax.metric('share:press:embed', {})
embedPostControl.open()
}}>
<Menu.ItemText>{_(msg`Embed post`)}</Menu.ItemText>
<Menu.ItemText>{l`Embed post`}</Menu.ItemText>
<Menu.ItemIcon icon={CodeBracketsIcon} position="right" />
</Menu.Item>
)}
@@ -142,7 +140,7 @@ let ShareMenuItems = ({
<Menu.Divider />
<Menu.Item
testID="postAtUriShareBtn"
label={_(msg`Copy post at:// URI`)}
label={l`Copy post at:// URI`}
onPress={onShareATURI}>
<Menu.ItemText>
<Trans>Copy post at:// URI</Trans>
@@ -151,7 +149,7 @@ let ShareMenuItems = ({
</Menu.Item>
<Menu.Item
testID="postAuthorDIDShareBtn"
label={_(msg`Copy author DID`)}
label={l`Copy author DID`}
onPress={onShareAuthorDID}>
<Menu.ItemText>
<Trans>Copy author DID</Trans>
@@ -161,7 +159,6 @@ let ShareMenuItems = ({
</>
)}
</Menu.Outer>
{canEmbed && (
<EmbedDialog
control={embedPostControl}
@@ -172,7 +169,6 @@ let ShareMenuItems = ({
timestamp={timestamp}
/>
)}
<SendViaChatDialog
control={sendViaChatControl}
onSelectChat={onSelectChatToShareTo}
+225 -12
View File
@@ -11,19 +11,33 @@ import {moderateProfile, type ModerationOpts} from '@atproto/api'
import {Plural, Trans, useLingui} from '@lingui/react/macro'
import {MAX_GROUP_NAME_GRAPHEME_LENGTH} from '#/lib/constants'
import {createSanitizedDisplayName} from '#/lib/moderation/create-sanitized-display-name'
import {sanitizeDisplayName} from '#/lib/strings/display-names'
import {sanitizeHandle} from '#/lib/strings/handles'
import {isOverMaxGraphemeCount} from '#/lib/strings/helpers'
import {useModerationOpts} from '#/state/preferences/moderation-opts'
import {useActorAutocompleteQuery} from '#/state/queries/actor-autocomplete'
import {useChatActorStatusQuery} from '#/state/queries/messages/get-status'
import {useListConvosQuery} from '#/state/queries/messages/list-conversations'
import {useProfileFollowsQuery} from '#/state/queries/profile-follows'
import {useSession} from '#/state/session'
import {type ListMethods} from '#/view/com/util/List'
import {android, atoms as a, native, useTheme, web} from '#/alf'
import {AvatarBubbles} from '#/components/AvatarBubbles'
import {Button, ButtonIcon, ButtonText} from '#/components/Button'
import * as Dialog from '#/components/Dialog'
import {canBeAddedToGroup, canBeMessaged} from '#/components/dms/util'
import {ChatProfileTabs} from '#/components/dms/ChatProfileTabs'
import {EmptyMemberList} from '#/components/dms/components/EmptyMemberList'
import {GroupChatProfileCard} from '#/components/dms/components/GroupChatProfileCard'
import {ProfileCardSkeleton} from '#/components/dms/components/ProfileCardSkeleton'
import {UserLabel} from '#/components/dms/components/UserLabel'
import {UserSearchInput} from '#/components/dms/components/UserSearchInput'
import {
canBeAddedToGroup,
canBeMessaged,
type ConvoWithDetails,
parseConvoView,
} from '#/components/dms/util'
import * as TextField from '#/components/forms/TextField'
import * as Toggle from '#/components/forms/Toggle'
import {
@@ -33,18 +47,13 @@ import {
import {ChevronRight_Stroke2_Corner0_Rounded as ChevronRightIcon} from '#/components/icons/Chevron'
import {PersonGroup_Stroke2_Corner2_Rounded as PersonGroupIcon} from '#/components/icons/Person'
import {TimesLarge_Stroke2_Corner0_Rounded as XIcon} from '#/components/icons/Times'
import {ProfileBadges} from '#/components/ProfileBadges'
import * as ProfileCard from '#/components/ProfileCard'
import * as Prompt from '#/components/Prompt'
import {Text} from '#/components/Typography'
import {useAgeAssurance} from '#/ageAssurance'
import {IS_NATIVE, IS_WEB} from '#/env'
import type * as bsky from '#/types/bsky'
import {ChatProfileTabs} from './ChatProfileTabs'
import {EmptyMemberList} from './components/EmptyMemberList'
import {GroupChatProfileCard} from './components/GroupChatProfileCard'
import {ProfileCardSkeleton} from './components/ProfileCardSkeleton'
import {UserLabel} from './components/UserLabel'
import {UserSearchInput} from './components/UserSearchInput'
type NewGroupChatItem = {
type: 'newGroupChat'
@@ -63,6 +72,12 @@ type ProfileItem = {
profile: bsky.profile.AnyProfileView
}
type ExistingChatItem = {
type: 'existingChat'
key: string
convo: ConvoWithDetails
}
type EmptyItem = {
type: 'empty'
key: string
@@ -83,6 +98,7 @@ type Item =
| NewGroupChatItem
| LabelItem
| ProfileItem
| ExistingChatItem
| EmptyItem
| PlaceholderItem
| ErrorItem
@@ -212,11 +228,17 @@ export function InitiateChatFlow({
onSelectChat,
onSelectGroupChat,
startInGroupChat = false,
showRecentConvos = false,
onSelectExistingChat,
sortByMessageDeclaration = false,
}: {
title: string
onSelectChat: (did: string) => void
onSelectGroupChat: (dids: string[], groupName: string) => void
startInGroupChat?: boolean
showRecentConvos?: boolean
onSelectExistingChat?: (convoId: string) => void
sortByMessageDeclaration?: boolean
}) {
const t = useTheme()
const {t: l} = useLingui()
@@ -230,6 +252,12 @@ export function InitiateChatFlow({
const inputRef = useRef<TextInput>(null)
const accountTooNewPromptControl = Dialog.useDialogControl()
const {data: convos} = useListConvosQuery({
enabled: showRecentConvos,
status: 'accepted',
lockStatus: 'unlocked',
})
const {data: chatStatus} = useChatActorStatusQuery()
const canCreateGroups = chatStatus?.canCreateGroups ?? true
const groupMemberLimit = chatStatus?.groupMemberLimit
@@ -281,6 +309,10 @@ export function InitiateChatFlow({
let _items: Item[] = []
const checker =
chatState === ChatState.NEW_GROUP_CHAT ? canBeAddedToGroup : canBeMessaged
const messageDeclarationRank = (item: Item) =>
item.type === 'profile' && checker(item.profile) ? 0 : 1
const compareByMessageDeclaration = (a: Item, b: Item) =>
messageDeclarationRank(a) - messageDeclarationRank(b)
if (isError) {
_items.push({
@@ -310,9 +342,9 @@ export function InitiateChatFlow({
})
}
_items = _items.sort(item => {
return item.type === 'profile' && checker(item.profile) ? -1 : 1
})
if (sortByMessageDeclaration) {
_items = _items.sort(compareByMessageDeclaration)
}
}
} else {
const placeholders: Item[] = Array(10)
@@ -322,7 +354,57 @@ export function InitiateChatFlow({
key: i + '',
}))
if (follows) {
if (
chatState === ChatState.NEW_CHAT &&
showRecentConvos &&
convos &&
follows
) {
const usedDids = new Set()
for (const page of convos.pages) {
for (const convoView of page.convos) {
const convo = parseConvoView(convoView, currentAccount?.did)
if (!convo) continue
if (convo.kind === 'group') {
_items.push({
type: 'existingChat',
key: convo.view.id,
convo,
})
} else {
if (convo.primaryMember.handle === 'missing.invalid') continue
if (usedDids.has(convo.primaryMember.did)) continue
usedDids.add(convo.primaryMember.did)
_items.push({
type: 'existingChat',
key: convo.view.id,
convo,
})
}
}
}
let followsItems: ProfileItem[] = []
for (const page of follows.pages) {
for (const profile of page.follows) {
if (usedDids.has(profile.did)) continue
if (!checker(profile)) continue
followsItems.push({
type: 'profile',
key: profile.did,
profile,
})
}
}
_items.push(...followsItems)
} else if (follows) {
for (const page of follows.pages) {
for (const profile of page.follows) {
if (!checker(profile)) continue
@@ -359,10 +441,19 @@ export function InitiateChatFlow({
_items.unshift({type: 'newGroupChat', key: 'newGroupChat'})
}
return _items
const profileDids = new Set<string>()
return _items.filter(item => {
if (item.type !== 'profile') return true
if (profileDids.has(item.profile.did)) return false
profileDids.add(item.profile.did)
return true
})
}, [
isError,
chatState,
convos,
searchText,
l,
groupChatProfiles,
@@ -370,6 +461,8 @@ export function InitiateChatFlow({
currentAccount?.did,
follows,
aa.flags.groupChatDisabled,
showRecentConvos,
sortByMessageDeclaration,
])
if (searchText && !isFetching && !items.length && !isError) {
@@ -429,6 +522,16 @@ export function InitiateChatFlow({
case 'label': {
return <UserLabel key={item.key} message={item.message} />
}
case 'existingChat': {
return showRecentConvos && onSelectExistingChat ? (
<ExistingChatCard
key={item.key}
convo={item.convo}
moderationOpts={moderationOpts!}
onPress={onSelectExistingChat}
/>
) : null
}
case 'profile': {
switch (chatState) {
case ChatState.NEW_CHAT:
@@ -474,6 +577,8 @@ export function InitiateChatFlow({
handlePressNewGroupChat,
moderationOpts,
onSelectChat,
onSelectExistingChat,
showRecentConvos,
],
)
@@ -845,6 +950,114 @@ function NewGroupChatButton({
)
}
function ExistingChatCard({
convo,
moderationOpts,
onPress,
}: {
convo: ConvoWithDetails
moderationOpts: ModerationOpts
onPress: (convoId: string) => void
}) {
const t = useTheme()
const {t: l} = useLingui()
const enabled =
convo.kind === 'group' ? convo.details.lockStatus === 'unlocked' : true
const name =
convo.kind === 'group'
? convo.details.name
: createSanitizedDisplayName(
convo.primaryMember,
true,
moderateProfile(convo.primaryMember, moderationOpts).ui(
'displayName',
),
)
const handleOnPress = useCallback(() => {
onPress(convo.view.id)
}, [onPress, convo.view.id])
return (
<Button
disabled={!enabled}
label={l`Select chat "${name}"`}
onPress={handleOnPress}>
{({hovered, pressed, focused}) => (
<View
style={[
a.flex_1,
a.py_sm,
a.px_lg,
!enabled
? {opacity: 0.5}
: pressed || focused || hovered
? t.atoms.bg_contrast_25
: t.atoms.bg,
]}>
<ProfileCard.Header>
{convo.kind === 'group' ? (
<AvatarBubbles profiles={convo.members} size={40} />
) : (
<ProfileCard.Avatar
profile={convo.primaryMember}
moderationOpts={moderationOpts}
disabledPreview
/>
)}
<View style={[a.flex_1]}>
<View style={[a.flex_row, a.align_center, a.max_w_full]}>
<Text
emoji
style={[
a.text_md,
a.font_semi_bold,
a.leading_snug,
a.self_start,
a.flex_shrink,
]}
numberOfLines={1}>
{name}
</Text>
{convo.kind === 'direct' && (
<ProfileBadges
profile={convo.primaryMember}
size="md"
style={[a.pl_xs]}
/>
)}
</View>
{convo.kind === 'direct' ? (
<ProfileCard.Handle profile={convo.primaryMember} />
) : (
<>
{enabled ? (
<Text
style={[a.leading_snug, t.atoms.text_contrast_medium]}
numberOfLines={2}>
<Plural
value={convo.details.memberCount}
one="# member"
other="# members"
/>
</Text>
) : (
<Text
style={[a.leading_snug, t.atoms.text_contrast_high]}
numberOfLines={2}>
<Trans>Group is locked</Trans>
</Text>
)}
</>
)}
</View>
</ProfileCard.Header>
</View>
)}
</Button>
)
}
function DefaultProfileCard({
profile,
moderationOpts,
+2 -1
View File
@@ -89,7 +89,7 @@ export function NewChat({
},
onError: error => {
logger.error('Failed to create groupchat', {safeMessage: error})
let errorMessage = l`An issue occurred creating the group chat, please try again.`
let errorMessage = l`An issue occurred starting the group chat, please try again.`
if (isNetworkError(error)) {
errorMessage = l`A network error occurred. Please check your internet connection.`
} else if (
@@ -184,6 +184,7 @@ export function NewChat({
title={l`New chat`}
onSelectChat={onCreateChat}
onSelectGroupChat={onCreateGroupChat}
sortByMessageDeclaration
startInGroupChat={startInGroupChat}
/>
) : (
+114 -10
View File
@@ -1,11 +1,17 @@
import {useCallback} from 'react'
import {msg} from '@lingui/core/macro'
import {useLingui} from '@lingui/react'
import {useCallback, useState} from 'react'
import {
ChatBskyConvoGetConvoForMembers,
ChatBskyGroupCreateGroup,
} from '@atproto/api'
import {useLingui} from '@lingui/react/macro'
import {isNetworkError} from '#/lib/strings/errors'
import {logger} from '#/logger'
import {useCreateGroupChat} from '#/state/queries/messages/create-group-chat'
import {useGetConvoForMembers} from '#/state/queries/messages/get-convo-for-members'
import * as Dialog from '#/components/Dialog'
import {SearchablePeopleList} from '#/components/dialogs/SearchablePeopleList'
import {InitiateChatFlow} from '#/components/dms/InitiateChatFlow'
import * as Toast from '#/components/Toast'
import {useAnalytics} from '#/analytics'
@@ -16,26 +22,39 @@ export function SendViaChatDialog({
control: Dialog.DialogControlProps
onSelectChat: (chatId: string) => void
}) {
const [flowKey, setFlowKey] = useState(0)
const onClose = useCallback(() => setFlowKey(key => key + 1), [])
return (
<Dialog.Outer
control={control}
testID="sendViaChatChatDialog"
nativeOptions={{fullHeight: true}}>
nativeOptions={{fullHeight: true}}
onClose={onClose}>
<Dialog.Handle />
<SendViaChatDialogInner control={control} onSelectChat={onSelectChat} />
<SendViaChatDialogInner
control={control}
flowKey={flowKey}
onSelectChat={onSelectChat}
/>
</Dialog.Outer>
)
}
function SendViaChatDialogInner({
control,
flowKey,
onSelectChat,
}: {
control: Dialog.DialogControlProps
flowKey: number
onSelectChat: (chatId: string) => void
}) {
const {_} = useLingui()
const {t: l} = useLingui()
const ax = useAnalytics()
const isGroupChatEnabled = !ax.features.enabled(ax.features.GroupChatsDisable)
const {mutate: createChat} = useGetConvoForMembers({
onSuccess: data => {
onSelectChat(data.convo.id)
@@ -46,8 +65,74 @@ function SendViaChatDialogInner({
ax.metric('chat:open', {logContext: 'SendViaChatDialog'})
},
onError: error => {
logger.error('Failed to share post to chat', {message: error})
Toast.show(_(msg`An issue occurred while trying to open the chat`), {
logger.error('Failed to share post to chat', {safeMessage: error})
let errorMessage = l`An issue occurred starting the chat, please try again.`
if (isNetworkError(error)) {
errorMessage = l`A network error occurred. Please check your internet connection.`
} else if (
error instanceof ChatBskyConvoGetConvoForMembers.AccountSuspendedError
) {
errorMessage = l`Suspended accounts cannot participate in chat.`
} else if (
error instanceof ChatBskyConvoGetConvoForMembers.BlockedActorError
) {
errorMessage = l`This user has blocked you and cannot be messaged.`
} else if (
error instanceof ChatBskyConvoGetConvoForMembers.MessagesDisabledError
) {
errorMessage = l`This user has disabled chat and cannot be messaged.`
} else if (
error instanceof
ChatBskyConvoGetConvoForMembers.NotFollowedBySenderError
) {
errorMessage = l`Chat recipient is not followed by the sender.`
} else if (
error instanceof ChatBskyConvoGetConvoForMembers.RecipientNotFoundError
) {
errorMessage = l`Unable to find the selected recipient.`
}
Toast.show(errorMessage, {
type: 'error',
})
},
})
const {mutate: createGroupChat} = useCreateGroupChat({
onSuccess: data => {
onSelectChat(data.convo.id)
ax.metric('groupchat:create', {logContext: 'SendViaChatDialog'})
},
onError: error => {
logger.error('Failed to share post to group chat', {safeMessage: error})
let errorMessage = l`An issue occurred starting the group chat, please try again.`
if (isNetworkError(error)) {
errorMessage = l`A network error occurred. Please check your internet connection.`
} else if (
error instanceof ChatBskyGroupCreateGroup.AccountSuspendedError
) {
errorMessage = l`Suspended accounts cannot participate in a group chat.`
} else if (error instanceof ChatBskyGroupCreateGroup.BlockedActorError) {
errorMessage = l`One of the selected recipients has blocked you and cannot be messaged.`
} else if (
error instanceof
ChatBskyGroupCreateGroup.NewAccountCannotCreateGroupError
) {
errorMessage = l`You cannot create a group chat yet.`
} else if (
error instanceof ChatBskyGroupCreateGroup.NotFollowedBySenderError
) {
errorMessage = l`A selected recipient is not followed by the sender.`
} else if (
error instanceof ChatBskyGroupCreateGroup.RecipientNotFoundError
) {
errorMessage = l`Unable to find a selected recipient.`
} else if (
error instanceof ChatBskyGroupCreateGroup.UserForbidsGroupsError
) {
errorMessage = l`One of the selected recipients does not allow group chats.`
}
Toast.show(errorMessage, {
type: 'error',
})
},
@@ -67,9 +152,28 @@ function SendViaChatDialogInner({
[control, createChat],
)
return (
const onCreateGroupChat = useCallback(
(members: string[], name: string) => {
control.close(() => {
createGroupChat({members, name})
})
},
[control, createGroupChat],
)
return isGroupChatEnabled ? (
<InitiateChatFlow
key={flowKey}
title={l`Send post to...`}
onSelectChat={onCreateChat}
onSelectExistingChat={onSelectExistingChat}
onSelectGroupChat={onCreateGroupChat}
showRecentConvos
sortByMessageDeclaration
/>
) : (
<SearchablePeopleList
title={_(msg`Send post to...`)}
title={l`Send post to...`}
onSelectChat={chat => {
if (chat.kind === 'user') {
onCreateChat(chat.did)