Add ability to add members to an existing group clip clops (#10344)
This commit is contained in:
@@ -20,6 +20,7 @@ import {type Shadow} from '#/state/cache/types'
|
||||
import {ConvoProvider, useConvo} from '#/state/messages/convo'
|
||||
import {ConvoStatus} from '#/state/messages/convo/types'
|
||||
import {useModerationOpts} from '#/state/preferences/moderation-opts'
|
||||
import {useAddGroupMembers} from '#/state/queries/messages/add-group-members'
|
||||
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'
|
||||
@@ -82,7 +83,7 @@ type Item =
|
||||
| {
|
||||
type: 'CHAT_MEMBER'
|
||||
profile: Shadow<bsky.profile.AnyProfileView>
|
||||
status: 'owner' | 'member' | 'invited'
|
||||
status: 'owner' | 'standard' | 'invited'
|
||||
}
|
||||
|
||||
type Props = NativeStackScreenProps<
|
||||
@@ -136,7 +137,6 @@ function SettingsInner({convoId}: {convoId: string}) {
|
||||
const isOwner = !!primaryMember && primaryMember.did === currentAccount?.did
|
||||
|
||||
const data: bsky.profile.AnyProfileView[] = convo?.members ?? []
|
||||
const invites: string[] = []
|
||||
|
||||
const {data: joinRequestsData, hasNextPage: hasMoreRequests} =
|
||||
useListJoinRequestsQuery({
|
||||
@@ -169,12 +169,7 @@ function SettingsInner({convoId}: {convoId: string}) {
|
||||
.map(profile => ({
|
||||
type: 'CHAT_MEMBER',
|
||||
profile,
|
||||
status:
|
||||
primaryMember?.did === profile.did
|
||||
? 'owner'
|
||||
: invites.includes(profile.did)
|
||||
? 'invited'
|
||||
: 'member',
|
||||
status: primaryMember?.did === profile.did ? 'owner' : 'standard',
|
||||
})),
|
||||
]
|
||||
|
||||
@@ -190,7 +185,7 @@ function SettingsInner({convoId}: {convoId: string}) {
|
||||
/>
|
||||
)
|
||||
case 'ADD_MEMBERS_LINK':
|
||||
return <AddMembersLink isOwner={isOwner} />
|
||||
return <AddMembersLink convo={convo} isOwner={isOwner} />
|
||||
case 'CHAT_MEMBER':
|
||||
return (
|
||||
<Member
|
||||
@@ -292,12 +287,26 @@ function MembersAndRequests({
|
||||
)
|
||||
}
|
||||
|
||||
function AddMembersLink({isOwner}: {isOwner: boolean}) {
|
||||
function AddMembersLink({
|
||||
convo,
|
||||
isOwner,
|
||||
}: {
|
||||
convo: ConvoWithDetails | null
|
||||
isOwner: boolean
|
||||
}) {
|
||||
const t = useTheme()
|
||||
const {t: l} = useLingui()
|
||||
|
||||
const addMembersControl = Dialog.useDialogControl()
|
||||
|
||||
const convoId = convo?.view.id
|
||||
const {mutate: addGroupMembers} = useAddGroupMembers(convoId, {
|
||||
onError: e => {
|
||||
logger.error('Failed to add group chat members', {message: e})
|
||||
Toast.show(l`Failed to add members`, {type: 'error'})
|
||||
},
|
||||
})
|
||||
|
||||
if (!isOwner) {
|
||||
return null
|
||||
}
|
||||
@@ -371,8 +380,8 @@ function AddMembersLink({isOwner}: {isOwner: boolean}) {
|
||||
<Dialog.Handle />
|
||||
<AddMembersFlow
|
||||
title={l`Add members`}
|
||||
onAddMembers={(_dids: string[]) => {
|
||||
// TODO Add members here
|
||||
onAddMembers={members => {
|
||||
addGroupMembers({members})
|
||||
addMembersControl.close()
|
||||
}}
|
||||
/>
|
||||
@@ -387,7 +396,7 @@ function Member({
|
||||
isOwner,
|
||||
}: {
|
||||
profile: Shadow<bsky.profile.AnyProfileView>
|
||||
status: 'owner' | 'member' | 'invited'
|
||||
status: 'owner' | 'standard' | 'invited'
|
||||
isOwner: boolean
|
||||
}) {
|
||||
const navigation = useNavigation<NavigationProp>()
|
||||
@@ -533,7 +542,7 @@ function MemberMenu({
|
||||
isOwner,
|
||||
}: {
|
||||
profile: Shadow<bsky.profile.AnyProfileView>
|
||||
type: 'owner' | 'member' | 'invited'
|
||||
type: 'owner' | 'standard' | 'invited'
|
||||
isOwner: boolean
|
||||
}) {
|
||||
const navigation = useNavigation<NavigationProp>()
|
||||
@@ -688,7 +697,7 @@ function MemberMenu({
|
||||
</Menu.Group>
|
||||
<Menu.Divider />
|
||||
<Menu.Group>
|
||||
{type === 'owner' || type === 'member' ? (
|
||||
{type === 'owner' || type === 'standard' ? (
|
||||
<Menu.Item
|
||||
label={
|
||||
profile.viewer?.blocking
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import {View} from 'react-native'
|
||||
import {Plural, Trans, useLingui} from '@lingui/react/macro'
|
||||
|
||||
import {logger} from '#/logger'
|
||||
import {type ConvoState} from '#/state/messages/convo/types'
|
||||
import {useAddGroupMembers} from '#/state/queries/messages/add-group-members'
|
||||
import {useSession} from '#/state/session'
|
||||
import {atoms as a, useTheme} from '#/alf'
|
||||
import {AvatarBubbles} from '#/components/AvatarBubbles'
|
||||
@@ -10,6 +12,7 @@ import * as Dialog from '#/components/Dialog'
|
||||
import {AddMembersFlow} from '#/components/dms/AddMembersFlow'
|
||||
import {ChainLink_Stroke2_Corner0_Rounded as ChainLinkIcon} from '#/components/icons/ChainLink'
|
||||
import {PersonPlus_Stroke2_Corner0_Rounded as PersonPlusIcon} from '#/components/icons/Person'
|
||||
import * as Toast from '#/components/Toast'
|
||||
import {Text} from '#/components/Typography'
|
||||
|
||||
export function MessagesListInfoPanel({convoState}: {convoState: ConvoState}) {
|
||||
@@ -20,6 +23,14 @@ export function MessagesListInfoPanel({convoState}: {convoState: ConvoState}) {
|
||||
|
||||
const {currentAccount} = useSession()
|
||||
|
||||
const convoId = convoState.convo?.id
|
||||
const {mutate: addGroupMembers} = useAddGroupMembers(convoId, {
|
||||
onError: e => {
|
||||
logger.error('Failed to add group chat members', {message: e})
|
||||
Toast.show(l`Failed to add members`, {type: 'error'})
|
||||
},
|
||||
})
|
||||
|
||||
const isOwner =
|
||||
currentAccount?.did == null
|
||||
? false
|
||||
@@ -124,8 +135,8 @@ export function MessagesListInfoPanel({convoState}: {convoState: ConvoState}) {
|
||||
<Dialog.Handle />
|
||||
<AddMembersFlow
|
||||
title={l`Add people`}
|
||||
onAddMembers={(_dids: string[]) => {
|
||||
// TODO Add members here
|
||||
onAddMembers={(members: string[]) => {
|
||||
addGroupMembers({members})
|
||||
addMembersControl.close()
|
||||
}}
|
||||
/>
|
||||
|
||||
@@ -145,6 +145,7 @@ export class Convo {
|
||||
this.getGroupInfo = this.getGroupInfo.bind(this)
|
||||
this.getPrimaryMember = this.getPrimaryMember.bind(this)
|
||||
this.updateGroupName = this.updateGroupName.bind(this)
|
||||
this.updateGroupMembers = this.updateGroupMembers.bind(this)
|
||||
}
|
||||
|
||||
private commit() {
|
||||
@@ -959,6 +960,18 @@ export class Convo {
|
||||
this.commit()
|
||||
}
|
||||
|
||||
updateGroupMembers(members: ChatBskyActorDefs.ProfileViewBasic[]) {
|
||||
if (this.convo) {
|
||||
this.convo = {
|
||||
...this.convo,
|
||||
members,
|
||||
}
|
||||
this.sender = members.find(m => m.did === this.senderUserDid)
|
||||
this.recipients = members.filter(m => m.did !== this.senderUserDid)
|
||||
}
|
||||
this.commit()
|
||||
}
|
||||
|
||||
async processPendingMessages() {
|
||||
logger.debug(
|
||||
`processing messages (${this.pendingMessages.size} remaining)`,
|
||||
|
||||
@@ -32,6 +32,15 @@ import {useAgent} from '#/state/session'
|
||||
|
||||
export * from '#/state/messages/convo/util'
|
||||
|
||||
function membersChanged(
|
||||
a: ChatBskyConvoDefs.ConvoView['members'],
|
||||
b: ChatBskyConvoDefs.ConvoView['members'],
|
||||
) {
|
||||
if (a.length !== b.length) return true
|
||||
const aDids = new Set(a.map(m => m.did))
|
||||
return b.some(m => !aDids.has(m.did))
|
||||
}
|
||||
|
||||
const ChatContext = createContext<ConvoState | null>(null)
|
||||
ChatContext.displayName = 'ChatContext'
|
||||
|
||||
@@ -139,6 +148,13 @@ export function ConvoProvider({
|
||||
) {
|
||||
convo.updateGroupName(data.kind.name)
|
||||
}
|
||||
if (
|
||||
data &&
|
||||
convo.convo &&
|
||||
membersChanged(data.members, convo.convo.members)
|
||||
) {
|
||||
convo.updateGroupMembers(data.members)
|
||||
}
|
||||
}
|
||||
})
|
||||
}, [convo, convoId, queryClient])
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
import {
|
||||
type ChatBskyConvoDefs,
|
||||
type ChatBskyConvoListConvos,
|
||||
type ChatBskyGroupAddMembers,
|
||||
} from '@atproto/api'
|
||||
import {
|
||||
type InfiniteData,
|
||||
useMutation,
|
||||
useQueryClient,
|
||||
} from '@tanstack/react-query'
|
||||
|
||||
import {DM_SERVICE_HEADERS} from '#/lib/constants'
|
||||
import {logger} from '#/logger'
|
||||
import {useAgent} from '#/state/session'
|
||||
import {RQKEY as CONVO_KEY} from './conversation'
|
||||
import {RQKEY_ROOT as CONVO_LIST_KEY} from './list-conversations'
|
||||
|
||||
export function useAddGroupMembers(
|
||||
convoId: string | undefined,
|
||||
{
|
||||
onSuccess,
|
||||
onError,
|
||||
}: {
|
||||
onSuccess?: (data: ChatBskyGroupAddMembers.OutputSchema) => void
|
||||
onError?: (error: Error) => void
|
||||
},
|
||||
) {
|
||||
const queryClient = useQueryClient()
|
||||
const agent = useAgent()
|
||||
|
||||
return useMutation({
|
||||
mutationFn: async ({members}: {members: string[]}) => {
|
||||
if (!convoId) throw new Error('No convoId provided')
|
||||
const {data} = await agent.chat.bsky.group.addMembers(
|
||||
{convoId, members},
|
||||
{headers: DM_SERVICE_HEADERS, encoding: 'application/json'},
|
||||
)
|
||||
return data
|
||||
},
|
||||
onMutate: () => {
|
||||
if (!convoId) return
|
||||
|
||||
const prevConvo = queryClient.getQueryData<ChatBskyConvoDefs.ConvoView>(
|
||||
CONVO_KEY(convoId),
|
||||
)
|
||||
const prevListEntries = queryClient.getQueriesData<
|
||||
InfiniteData<ChatBskyConvoListConvos.OutputSchema>
|
||||
>({queryKey: [CONVO_LIST_KEY]})
|
||||
|
||||
return {prevConvo, prevListEntries}
|
||||
},
|
||||
onSuccess: data => {
|
||||
if (convoId) {
|
||||
queryClient.setQueryData<ChatBskyConvoDefs.ConvoView>(
|
||||
CONVO_KEY(convoId),
|
||||
data.convo,
|
||||
)
|
||||
|
||||
queryClient.setQueriesData<
|
||||
InfiniteData<ChatBskyConvoListConvos.OutputSchema>
|
||||
>({queryKey: [CONVO_LIST_KEY]}, prev => {
|
||||
if (!prev?.pages) return
|
||||
return {
|
||||
...prev,
|
||||
pages: prev.pages.map(page => ({
|
||||
...page,
|
||||
convos: page.convos.map(convo =>
|
||||
convo.id === convoId ? data.convo : convo,
|
||||
),
|
||||
})),
|
||||
}
|
||||
})
|
||||
}
|
||||
onSuccess?.(data)
|
||||
},
|
||||
onError: (e, _variables, context) => {
|
||||
logger.error(e)
|
||||
if (context?.prevConvo && convoId) {
|
||||
queryClient.setQueryData(CONVO_KEY(convoId), context.prevConvo)
|
||||
}
|
||||
if (context?.prevListEntries) {
|
||||
for (const [key, data] of context.prevListEntries) {
|
||||
queryClient.setQueryData(key, data)
|
||||
}
|
||||
}
|
||||
onError?.(e)
|
||||
},
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user