Add ability to edit group clip clop name (#10275)
This commit is contained in:
@@ -20,6 +20,7 @@ import {type Shadow} from '#/state/cache/types'
|
|||||||
import {ConvoProvider, useConvo} from '#/state/messages/convo'
|
import {ConvoProvider, useConvo} from '#/state/messages/convo'
|
||||||
import {ConvoStatus} from '#/state/messages/convo/types'
|
import {ConvoStatus} from '#/state/messages/convo/types'
|
||||||
import {useModerationOpts} from '#/state/preferences/moderation-opts'
|
import {useModerationOpts} from '#/state/preferences/moderation-opts'
|
||||||
|
import {useEditGroupName} from '#/state/queries/messages/edit-group-name'
|
||||||
import {useGetConvoAvailabilityQuery} from '#/state/queries/messages/get-convo-availability'
|
import {useGetConvoAvailabilityQuery} from '#/state/queries/messages/get-convo-availability'
|
||||||
import {useGetConvoForMembers} from '#/state/queries/messages/get-convo-for-members'
|
import {useGetConvoForMembers} from '#/state/queries/messages/get-convo-for-members'
|
||||||
import {useLeaveConvo} from '#/state/queries/messages/leave-conversation'
|
import {useLeaveConvo} from '#/state/queries/messages/leave-conversation'
|
||||||
@@ -717,11 +718,26 @@ function SettingsHeader({
|
|||||||
const convoState = useConvo()
|
const convoState = useConvo()
|
||||||
const {currentAccount} = useSession()
|
const {currentAccount} = useSession()
|
||||||
|
|
||||||
|
const groupName = convoState.getGroupInfo?.()?.name ?? ''
|
||||||
|
const [newGroupName, setNewGroupName] = useState(groupName)
|
||||||
|
|
||||||
|
const [isLocked, setIsLocked] = useState(false)
|
||||||
|
|
||||||
const isOwner =
|
const isOwner =
|
||||||
currentAccount?.did == null
|
currentAccount?.did == null
|
||||||
? false
|
? false
|
||||||
: convoState.getPrimaryMember?.()?.did === currentAccount.did
|
: convoState.getPrimaryMember?.()?.did === currentAccount.did
|
||||||
|
|
||||||
|
const {mutate: editGroupName} = useEditGroupName(convo.id, {
|
||||||
|
onError: e => {
|
||||||
|
setNewGroupName(groupName)
|
||||||
|
logger.error('Failed to edit group chat name', {message: e})
|
||||||
|
Toast.show(l`Failed to edit group chat name`, {
|
||||||
|
type: 'error',
|
||||||
|
})
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
const {mutate: muteConvo} = useMuteConvo(convo.id, {
|
const {mutate: muteConvo} = useMuteConvo(convo.id, {
|
||||||
onSuccess: data => {
|
onSuccess: data => {
|
||||||
if (data.convo.muted) {
|
if (data.convo.muted) {
|
||||||
@@ -755,13 +771,6 @@ function SettingsHeader({
|
|||||||
const lockChatPrompt = Prompt.usePromptControl()
|
const lockChatPrompt = Prompt.usePromptControl()
|
||||||
const leaveChatPrompt = Prompt.usePromptControl()
|
const leaveChatPrompt = Prompt.usePromptControl()
|
||||||
|
|
||||||
const [groupName, setGroupName] = useState(
|
|
||||||
convoState.getGroupInfo?.()?.name ?? '',
|
|
||||||
)
|
|
||||||
const [newGroupName, setNewGroupName] = useState(groupName)
|
|
||||||
|
|
||||||
const [isLocked, setIsLocked] = useState(false)
|
|
||||||
|
|
||||||
const handleToggleMute = () => {
|
const handleToggleMute = () => {
|
||||||
muteConvo({mute: !convo?.muted})
|
muteConvo({mute: !convo?.muted})
|
||||||
}
|
}
|
||||||
@@ -777,7 +786,7 @@ function SettingsHeader({
|
|||||||
}
|
}
|
||||||
|
|
||||||
const handleEditName = () => {
|
const handleEditName = () => {
|
||||||
setGroupName(newGroupName)
|
editGroupName({name: newGroupName})
|
||||||
editNamePrompt.close()
|
editNamePrompt.close()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1045,6 +1054,7 @@ function EditNamePrompt({
|
|||||||
autoCapitalize="none"
|
autoCapitalize="none"
|
||||||
autoComplete="off"
|
autoComplete="off"
|
||||||
autoCorrect={false}
|
autoCorrect={false}
|
||||||
|
autoFocus
|
||||||
onSubmitEditing={onConfirm}
|
onSubmitEditing={onConfirm}
|
||||||
/>
|
/>
|
||||||
</TextField.Root>
|
</TextField.Root>
|
||||||
|
|||||||
@@ -116,6 +116,7 @@ export class Convo {
|
|||||||
this.isGroup = this.isGroup.bind(this)
|
this.isGroup = this.isGroup.bind(this)
|
||||||
this.getGroupInfo = this.getGroupInfo.bind(this)
|
this.getGroupInfo = this.getGroupInfo.bind(this)
|
||||||
this.getPrimaryMember = this.getPrimaryMember.bind(this)
|
this.getPrimaryMember = this.getPrimaryMember.bind(this)
|
||||||
|
this.updateGroupName = this.updateGroupName.bind(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
private commit() {
|
private commit() {
|
||||||
@@ -655,7 +656,7 @@ export class Convo {
|
|||||||
|
|
||||||
const nextCursor = this.oldestRev // for TS
|
const nextCursor = this.oldestRev // for TS
|
||||||
const response = await networkRetry(2, () => {
|
const response = await networkRetry(2, () => {
|
||||||
return this.agent.api.chat.bsky.convo.getMessages(
|
return this.agent.chat.bsky.convo.getMessages(
|
||||||
{
|
{
|
||||||
cursor: nextCursor,
|
cursor: nextCursor,
|
||||||
convoId: this.convoId,
|
convoId: this.convoId,
|
||||||
@@ -889,6 +890,25 @@ export class Convo {
|
|||||||
this.commit()
|
this.commit()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updateGroupName(name: string) {
|
||||||
|
if (
|
||||||
|
this.convo &&
|
||||||
|
bsky.dangerousIsType<ChatBskyConvoDefs.GroupConvo>(
|
||||||
|
this.convo.kind,
|
||||||
|
ChatBskyConvoDefs.isGroupConvo,
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
this.convo = {
|
||||||
|
...this.convo,
|
||||||
|
kind: {
|
||||||
|
...this.convo.kind,
|
||||||
|
name,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.commit()
|
||||||
|
}
|
||||||
|
|
||||||
async processPendingMessages() {
|
async processPendingMessages() {
|
||||||
logger.debug(
|
logger.debug(
|
||||||
`processing messages (${this.pendingMessages.size} remaining)`,
|
`processing messages (${this.pendingMessages.size} remaining)`,
|
||||||
@@ -1388,14 +1408,14 @@ export class Convo {
|
|||||||
|
|
||||||
getPrimaryMember(): ChatBskyActorDefs.ProfileViewBasic | undefined {
|
getPrimaryMember(): ChatBskyActorDefs.ProfileViewBasic | undefined {
|
||||||
if (this.isGroup()) {
|
if (this.isGroup()) {
|
||||||
return this.recipients?.find(r => {
|
return this.convo?.members.find(m => {
|
||||||
if (
|
if (
|
||||||
bsky.dangerousIsType<ChatBskyActorDefs.GroupConvoMember>(
|
bsky.dangerousIsType<ChatBskyActorDefs.GroupConvoMember>(
|
||||||
r.kind,
|
m.kind,
|
||||||
ChatBskyActorDefs.isGroupConvoMember,
|
ChatBskyActorDefs.isGroupConvoMember,
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
return r.kind.role === 'owner'
|
return m.kind.role === 'owner'
|
||||||
} else {
|
} else {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
'Expected a GroupConvoMember, got an unknown kind of member',
|
'Expected a GroupConvoMember, got an unknown kind of member',
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import {
|
|||||||
useState,
|
useState,
|
||||||
useSyncExternalStore,
|
useSyncExternalStore,
|
||||||
} from 'react'
|
} from 'react'
|
||||||
import {type ChatBskyConvoDefs} from '@atproto/api'
|
import {ChatBskyConvoDefs} from '@atproto/api'
|
||||||
import {useFocusEffect} from '@react-navigation/native'
|
import {useFocusEffect} from '@react-navigation/native'
|
||||||
import {useQueryClient} from '@tanstack/react-query'
|
import {useQueryClient} from '@tanstack/react-query'
|
||||||
|
|
||||||
@@ -130,6 +130,15 @@ export function ConvoProvider({
|
|||||||
if (data && convo.convo && data.muted !== convo.convo.muted) {
|
if (data && convo.convo && data.muted !== convo.convo.muted) {
|
||||||
convo.updateMuted(data.muted)
|
convo.updateMuted(data.muted)
|
||||||
}
|
}
|
||||||
|
if (
|
||||||
|
data &&
|
||||||
|
convo.convo &&
|
||||||
|
ChatBskyConvoDefs.isGroupConvo(data.kind) &&
|
||||||
|
ChatBskyConvoDefs.isGroupConvo(convo.convo.kind) &&
|
||||||
|
data.kind.name !== convo.convo.kind.name
|
||||||
|
) {
|
||||||
|
convo.updateGroupName(data.kind.name)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}, [convo, convoId, queryClient])
|
}, [convo, convoId, queryClient])
|
||||||
|
|||||||
@@ -0,0 +1,108 @@
|
|||||||
|
import {
|
||||||
|
ChatBskyConvoDefs,
|
||||||
|
type ChatBskyConvoListConvos,
|
||||||
|
type ChatBskyGroupEditGroup,
|
||||||
|
} 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 useEditGroupName(
|
||||||
|
convoId: string | undefined,
|
||||||
|
{
|
||||||
|
onSuccess,
|
||||||
|
onError,
|
||||||
|
}: {
|
||||||
|
onSuccess?: (data: ChatBskyGroupEditGroup.OutputSchema) => void
|
||||||
|
onError?: (error: Error) => void
|
||||||
|
},
|
||||||
|
) {
|
||||||
|
const queryClient = useQueryClient()
|
||||||
|
const agent = useAgent()
|
||||||
|
|
||||||
|
return useMutation({
|
||||||
|
mutationFn: async ({name: groupName}: {name: string}) => {
|
||||||
|
if (!convoId) throw new Error('No convoId provided')
|
||||||
|
const {data} = await agent.chat.bsky.group.editGroup(
|
||||||
|
{convoId, name: groupName},
|
||||||
|
{headers: DM_SERVICE_HEADERS, encoding: 'application/json'},
|
||||||
|
)
|
||||||
|
return data
|
||||||
|
},
|
||||||
|
onMutate: ({name: groupName}) => {
|
||||||
|
if (!convoId) return
|
||||||
|
|
||||||
|
const prevConvo = queryClient.getQueryData<ChatBskyConvoDefs.ConvoView>(
|
||||||
|
CONVO_KEY(convoId),
|
||||||
|
)
|
||||||
|
const prevListEntries = queryClient.getQueriesData<
|
||||||
|
InfiniteData<ChatBskyConvoListConvos.OutputSchema>
|
||||||
|
>({queryKey: [CONVO_LIST_KEY]})
|
||||||
|
|
||||||
|
// Update for a single chat thread
|
||||||
|
queryClient.setQueryData<ChatBskyConvoDefs.ConvoView>(
|
||||||
|
CONVO_KEY(convoId),
|
||||||
|
prev => {
|
||||||
|
if (!prev) return
|
||||||
|
if (!ChatBskyConvoDefs.isGroupConvo(prev.kind)) return prev
|
||||||
|
return {
|
||||||
|
...prev,
|
||||||
|
kind: {
|
||||||
|
...prev.kind,
|
||||||
|
name: groupName,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
// Update for the chat list
|
||||||
|
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 => {
|
||||||
|
if (convo.id !== convoId) return convo
|
||||||
|
if (!ChatBskyConvoDefs.isGroupConvo(convo.kind)) return convo
|
||||||
|
return {
|
||||||
|
...convo,
|
||||||
|
kind: {
|
||||||
|
...convo.kind,
|
||||||
|
name: groupName,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
})),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return {prevConvo, prevListEntries}
|
||||||
|
},
|
||||||
|
onSuccess: data => {
|
||||||
|
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