about summary refs log tree commit diff
path: root/src/screens/Messages/Temp
diff options
context:
space:
mode:
Diffstat (limited to 'src/screens/Messages/Temp')
-rw-r--r--src/screens/Messages/Temp/query/query.ts219
-rw-r--r--src/screens/Messages/Temp/useDmServiceUrlStorage.tsx64
2 files changed, 283 insertions, 0 deletions
diff --git a/src/screens/Messages/Temp/query/query.ts b/src/screens/Messages/Temp/query/query.ts
new file mode 100644
index 000000000..2477dc569
--- /dev/null
+++ b/src/screens/Messages/Temp/query/query.ts
@@ -0,0 +1,219 @@
+import {useMutation, useQuery, useQueryClient} from '@tanstack/react-query'
+
+import {useSession} from 'state/session'
+import {useDmServiceUrlStorage} from '#/screens/Messages/Temp/useDmServiceUrlStorage'
+import * as TempDmChatDefs from '#/temp/dm/defs'
+import * as TempDmChatGetChat from '#/temp/dm/getChat'
+import * as TempDmChatGetChatLog from '#/temp/dm/getChatLog'
+import * as TempDmChatGetChatMessages from '#/temp/dm/getChatMessages'
+
+/**
+ * TEMPORARY, PLEASE DO NOT JUDGE ME REACT QUERY OVERLORDS 🙏
+ * (and do not try this at home)
+ */
+
+function createHeaders(did: string) {
+  return {
+    Authorization: did,
+  }
+}
+
+type Chat = {
+  chatId: string
+  messages: TempDmChatGetChatMessages.OutputSchema['messages']
+  lastCursor?: string
+  lastRev?: string
+}
+
+export function useChat(chatId: string) {
+  const queryClient = useQueryClient()
+
+  const {serviceUrl} = useDmServiceUrlStorage()
+  const {currentAccount} = useSession()
+  const did = currentAccount?.did ?? ''
+
+  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: createHeaders(did),
+        },
+      )
+
+      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: createHeaders(did),
+        },
+      )
+
+      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 {serviceUrl} = useDmServiceUrlStorage()
+  const {currentAccount} = useSession()
+  const did = currentAccount?.did ?? ''
+
+  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: {
+            ...createHeaders(did),
+            '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: [
+            {
+              id: variables.tempId,
+              text: variables.message,
+            },
+            ...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 {serviceUrl} = useDmServiceUrlStorage()
+  const {currentAccount} = useSession()
+  const did = currentAccount?.did ?? ''
+
+  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: createHeaders(did),
+          },
+        )
+
+        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.isLogDeleteMessage(log)) {
+            queryClient.setQueryData(['chat', log.chatId], (prev: Chat) => {
+              // What to do in this case
+              if (!prev) return
+
+              // HACK we don't know who the creator of a message is, so just filter by id for now
+              if (prev.messages.find(m => m.id === log.message.id)) return prev
+
+              return {
+                ...prev,
+                messages: [log.message, ...prev.messages],
+              }
+            })
+          }
+        }
+
+        return json
+      } catch (e) {
+        console.log(e)
+      }
+    },
+    refetchInterval: 5000,
+  })
+}
diff --git a/src/screens/Messages/Temp/useDmServiceUrlStorage.tsx b/src/screens/Messages/Temp/useDmServiceUrlStorage.tsx
new file mode 100644
index 000000000..3679858f4
--- /dev/null
+++ b/src/screens/Messages/Temp/useDmServiceUrlStorage.tsx
@@ -0,0 +1,64 @@
+import React from 'react'
+import {useAsyncStorage} from '@react-native-async-storage/async-storage'
+
+/**
+ * TEMP: REMOVE BEFORE RELEASE
+ *
+ * Clip clop trivia:
+ *
+ * A little known fact about the term "clip clop" is that it may refer to a unit of time. It is unknown what the exact
+ * length of a clip clop is, but it is generally agreed that it is approximately 9 minutes and 30 seconds, or 570
+ * seconds.
+ *
+ * The term "clip clop" may also be used in other contexts, although it is unknown what all of these contexts may be.
+ * Recently, the term has been used among many young adults to refer to a type of social media functionality, although
+ * the exact nature of this functionality is also unknown. It is believed that the term may have originated from a
+ * popular video game, but this has not been confirmed.
+ *
+ */
+
+const DmServiceUrlStorageContext = React.createContext<{
+  serviceUrl: string
+  setServiceUrl: (value: string) => void
+}>({
+  serviceUrl: '',
+  setServiceUrl: () => {},
+})
+
+export const useDmServiceUrlStorage = () =>
+  React.useContext(DmServiceUrlStorageContext)
+
+export function DmServiceUrlProvider({children}: {children: React.ReactNode}) {
+  const [serviceUrl, setServiceUrl] = React.useState<string>('')
+  const {getItem, setItem: setItemInner} = useAsyncStorage('dmServiceUrl')
+
+  React.useEffect(() => {
+    ;(async () => {
+      const v = await getItem()
+      console.log(v)
+      setServiceUrl(v ?? '')
+    })()
+  }, [getItem])
+
+  const setItem = React.useCallback(
+    (v: string) => {
+      setItemInner(v)
+      setServiceUrl(v)
+    },
+    [setItemInner],
+  )
+
+  const value = React.useMemo(
+    () => ({
+      serviceUrl,
+      setServiceUrl: setItem,
+    }),
+    [serviceUrl, setItem],
+  )
+
+  return (
+    <DmServiceUrlStorageContext.Provider value={value}>
+      {children}
+    </DmServiceUrlStorageContext.Provider>
+  )
+}