* add new routes with placeholder screens * add clops list * add a clop input * add some better padding to the clops * some more adjustments * add rnkc * implement rnkc * implement rnkc * be a little less weird about it * rename clop stuff * rename more clop * one more * add codegenerated lexicon * replace hailey's types * use codegen'd types in components * fix error + throw if fetch failed * remove bad imports * update messageslist and messageitem * import useState * replace hailey's types * use codegen'd types in components * add FAB * new chat dialog * error + default search term * fix typo * fix web styles * optimistically set chat data * use cursor instead of last rev * [Clipclops] Temp codegenerated lexicon (#3749) * add codegenerated lexicon * replace hailey's types * use codegen'd types in components * fix error + throw if fetch failed * remove bad imports * update messageslist and messageitem * import useState * add clop service URL hook * add dm service url storage * use context * use context for service url (temp) * remove log * cleanup merge * fix merge error * disable hack * sender-based message styles * temporary filter * merge cleanup * add `hideBackButton` * rm unneeded return * tried to be smart * hide go back button * use `searchActorTypeahead` instead --------- Co-authored-by: Hailey <me@haileyok.com>
252 lines
6.4 KiB
TypeScript
252 lines
6.4 KiB
TypeScript
import {useMutation, useQuery, useQueryClient} from '@tanstack/react-query'
|
|
|
|
import {useAgent} from '#/state/session'
|
|
import * as TempDmChatDefs from '#/temp/dm/defs'
|
|
import * as TempDmChatGetChat from '#/temp/dm/getChat'
|
|
import * as TempDmChatGetChatForMembers from '#/temp/dm/getChatForMembers'
|
|
import * as TempDmChatGetChatLog from '#/temp/dm/getChatLog'
|
|
import * as TempDmChatGetChatMessages from '#/temp/dm/getChatMessages'
|
|
import {useDmServiceUrlStorage} from '../useDmServiceUrlStorage'
|
|
|
|
/**
|
|
* TEMPORARY, PLEASE DO NOT JUDGE ME REACT QUERY OVERLORDS 🙏
|
|
* (and do not try this at home)
|
|
*/
|
|
|
|
const useHeaders = () => {
|
|
const {getAgent} = useAgent()
|
|
return {
|
|
get Authorization() {
|
|
return getAgent().session!.did
|
|
},
|
|
}
|
|
}
|
|
|
|
type Chat = {
|
|
chatId: string
|
|
messages: TempDmChatGetChatMessages.OutputSchema['messages']
|
|
lastCursor?: string
|
|
lastRev?: string
|
|
}
|
|
|
|
export function useChat(chatId: string) {
|
|
const queryClient = useQueryClient()
|
|
const headers = useHeaders()
|
|
const {serviceUrl} = useDmServiceUrlStorage()
|
|
|
|
return useQuery({
|
|
queryKey: ['chat', chatId],
|
|
queryFn: async () => {
|
|
const currentChat = queryClient.getQueryData(['chat', chatId])
|
|
|
|
if (currentChat) {
|
|
return currentChat as Chat
|
|
}
|
|
|
|
const messagesResponse = await fetch(
|
|
`${serviceUrl}/xrpc/temp.dm.getChatMessages?chatId=${chatId}`,
|
|
{
|
|
headers,
|
|
},
|
|
)
|
|
|
|
if (!messagesResponse.ok) throw new Error('Failed to fetch messages')
|
|
|
|
const messagesJson =
|
|
(await messagesResponse.json()) as TempDmChatGetChatMessages.OutputSchema
|
|
|
|
const chatResponse = await fetch(
|
|
`${serviceUrl}/xrpc/temp.dm.getChat?chatId=${chatId}`,
|
|
{
|
|
headers,
|
|
},
|
|
)
|
|
|
|
if (!chatResponse.ok) throw new Error('Failed to fetch chat')
|
|
|
|
const chatJson =
|
|
(await chatResponse.json()) as TempDmChatGetChat.OutputSchema
|
|
|
|
const newChat = {
|
|
chatId,
|
|
messages: messagesJson.messages,
|
|
lastCursor: messagesJson.cursor,
|
|
lastRev: chatJson.chat.rev,
|
|
} satisfies Chat
|
|
|
|
queryClient.setQueryData(['chat', chatId], newChat)
|
|
|
|
return newChat
|
|
},
|
|
})
|
|
}
|
|
|
|
interface SendMessageMutationVariables {
|
|
message: string
|
|
tempId: string
|
|
}
|
|
|
|
export function createTempId() {
|
|
return Math.random().toString(36).substring(7).toString()
|
|
}
|
|
|
|
export function useSendMessageMutation(chatId: string) {
|
|
const queryClient = useQueryClient()
|
|
const headers = useHeaders()
|
|
const {serviceUrl} = useDmServiceUrlStorage()
|
|
|
|
return useMutation<
|
|
TempDmChatDefs.Message,
|
|
Error,
|
|
SendMessageMutationVariables,
|
|
unknown
|
|
>({
|
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
mutationFn: async ({message, tempId}) => {
|
|
const response = await fetch(
|
|
`${serviceUrl}/xrpc/temp.dm.sendMessage?chatId=${chatId}`,
|
|
{
|
|
method: 'POST',
|
|
headers: {
|
|
...headers,
|
|
'Content-Type': 'application/json',
|
|
},
|
|
body: JSON.stringify({
|
|
chatId,
|
|
message: {
|
|
text: message,
|
|
},
|
|
}),
|
|
},
|
|
)
|
|
|
|
if (!response.ok) throw new Error('Failed to send message')
|
|
|
|
return response.json()
|
|
},
|
|
onMutate: async variables => {
|
|
queryClient.setQueryData(['chat', chatId], (prev: Chat) => {
|
|
return {
|
|
...prev,
|
|
messages: [
|
|
{
|
|
$type: 'temp.dm.defs#messageView',
|
|
id: variables.tempId,
|
|
text: variables.message,
|
|
sender: {did: headers.Authorization}, // TODO a real DID get
|
|
},
|
|
...prev.messages,
|
|
],
|
|
}
|
|
})
|
|
},
|
|
onSuccess: (result, variables) => {
|
|
queryClient.setQueryData(['chat', chatId], (prev: Chat) => {
|
|
return {
|
|
...prev,
|
|
messages: prev.messages.map(m =>
|
|
m.id === variables.tempId
|
|
? {
|
|
...m,
|
|
id: result.id,
|
|
}
|
|
: m,
|
|
),
|
|
}
|
|
})
|
|
},
|
|
onError: (_, variables) => {
|
|
console.log(_)
|
|
queryClient.setQueryData(['chat', chatId], (prev: Chat) => ({
|
|
...prev,
|
|
messages: prev.messages.filter(m => m.id !== variables.tempId),
|
|
}))
|
|
},
|
|
})
|
|
}
|
|
|
|
export function useChatLogQuery() {
|
|
const queryClient = useQueryClient()
|
|
const headers = useHeaders()
|
|
const {serviceUrl} = useDmServiceUrlStorage()
|
|
|
|
return useQuery({
|
|
queryKey: ['chatLog'],
|
|
queryFn: async () => {
|
|
const prevLog = queryClient.getQueryData([
|
|
'chatLog',
|
|
]) as TempDmChatGetChatLog.OutputSchema
|
|
|
|
try {
|
|
const response = await fetch(
|
|
`${serviceUrl}/xrpc/temp.dm.getChatLog?cursor=${
|
|
prevLog?.cursor ?? ''
|
|
}`,
|
|
{
|
|
headers,
|
|
},
|
|
)
|
|
|
|
if (!response.ok) throw new Error('Failed to fetch chat log')
|
|
|
|
const json =
|
|
(await response.json()) as TempDmChatGetChatLog.OutputSchema
|
|
|
|
for (const log of json.logs) {
|
|
if (TempDmChatDefs.isLogCreateMessage(log)) {
|
|
queryClient.setQueryData(['chat', log.chatId], (prev: Chat) => {
|
|
// TODO hack filter out duplicates
|
|
if (prev?.messages.find(m => m.id === log.message.id)) return
|
|
|
|
return {
|
|
...prev,
|
|
messages: [log.message, ...prev.messages],
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
return json
|
|
} catch (e) {
|
|
console.log(e)
|
|
}
|
|
},
|
|
refetchInterval: 5000,
|
|
})
|
|
}
|
|
|
|
export function useGetChatFromMembers({
|
|
onSuccess,
|
|
onError,
|
|
}: {
|
|
onSuccess?: (data: TempDmChatGetChatForMembers.OutputSchema) => void
|
|
onError?: (error: Error) => void
|
|
}) {
|
|
const queryClient = useQueryClient()
|
|
const headers = useHeaders()
|
|
const {serviceUrl} = useDmServiceUrlStorage()
|
|
|
|
return useMutation({
|
|
mutationFn: async (members: string[]) => {
|
|
const response = await fetch(
|
|
`${serviceUrl}/xrpc/temp.dm.getChatForMembers?members=${members.join(
|
|
',',
|
|
)}`,
|
|
{headers},
|
|
)
|
|
|
|
if (!response.ok) throw new Error('Failed to fetch chat')
|
|
|
|
return (await response.json()) as TempDmChatGetChatForMembers.OutputSchema
|
|
},
|
|
onSuccess: data => {
|
|
queryClient.setQueryData(['chat', data.chat.id], {
|
|
chatId: data.chat.id,
|
|
messages: [],
|
|
lastRev: data.chat.rev,
|
|
})
|
|
onSuccess?.(data)
|
|
},
|
|
onError,
|
|
})
|
|
}
|