about summary refs log tree commit diff
path: root/src/screens
diff options
context:
space:
mode:
Diffstat (limited to 'src/screens')
-rw-r--r--src/screens/Messages/Conversation/MessageItem.tsx12
-rw-r--r--src/screens/Messages/Conversation/MessagesList.tsx9
-rw-r--r--src/screens/Messages/List/index.tsx43
-rw-r--r--src/screens/Messages/Temp/query/query.ts79
4 files changed, 99 insertions, 44 deletions
diff --git a/src/screens/Messages/Conversation/MessageItem.tsx b/src/screens/Messages/Conversation/MessageItem.tsx
index 74e65488e..822b17804 100644
--- a/src/screens/Messages/Conversation/MessageItem.tsx
+++ b/src/screens/Messages/Conversation/MessageItem.tsx
@@ -1,12 +1,16 @@
 import React from 'react'
 import {View} from 'react-native'
 
+import {useAgent} from '#/state/session'
 import {atoms as a, useTheme} from '#/alf'
 import {Text} from '#/components/Typography'
 import * as TempDmChatDefs from '#/temp/dm/defs'
 
 export function MessageItem({item}: {item: TempDmChatDefs.MessageView}) {
   const t = useTheme()
+  const {getAgent} = useAgent()
+
+  const fromMe = item.sender?.did === getAgent().session?.did
 
   return (
     <View
@@ -15,13 +19,17 @@ export function MessageItem({item}: {item: TempDmChatDefs.MessageView}) {
         a.px_md,
         a.my_xs,
         a.rounded_md,
+        fromMe ? a.self_end : a.self_start,
         {
-          backgroundColor: t.palette.primary_500,
+          backgroundColor: fromMe
+            ? t.palette.primary_500
+            : t.palette.contrast_50,
           maxWidth: '65%',
           borderRadius: 17,
         },
       ]}>
-      <Text style={[a.text_md, {lineHeight: 1.2, color: 'white'}]}>
+      <Text
+        style={[a.text_md, a.leading_snug, fromMe && {color: t.palette.white}]}>
         {item.text}
       </Text>
     </View>
diff --git a/src/screens/Messages/Conversation/MessagesList.tsx b/src/screens/Messages/Conversation/MessagesList.tsx
index aafed42af..e3b518f65 100644
--- a/src/screens/Messages/Conversation/MessagesList.tsx
+++ b/src/screens/Messages/Conversation/MessagesList.tsx
@@ -1,5 +1,6 @@
 import React, {useCallback, useMemo, useRef, useState} from 'react'
-import {Alert, FlatList, View, ViewToken} from 'react-native'
+import {FlatList, View, ViewToken} from 'react-native'
+import {Alert} from 'react-native'
 import {KeyboardAvoidingView} from 'react-native-keyboard-controller'
 
 import {isWeb} from 'platform/detection'
@@ -64,6 +65,7 @@ export function MessagesList({chatId}: {chatId: string}) {
   const totalMessages = useRef(10)
 
   // TODO later
+
   const [_, setShowSpinner] = useState(false)
 
   // Query Data
@@ -147,6 +149,8 @@ export function MessagesList({chatId}: {chatId: string}) {
       },
     )
     totalMessages.current = filtered.length
+
+    return filtered
   }, [chat])
 
   return (
@@ -162,7 +166,7 @@ export function MessagesList({chatId}: {chatId: string}) {
         contentContainerStyle={{paddingHorizontal: 10}}
         // In the future, we might want to adjust this value. Not very concerning right now as long as we are only
         // dealing with text. But whenever we have images or other media and things are taller, we will want to lower
-        // this...probably
+        // this...probably.
         initialNumToRender={20}
         // Same with the max to render per batch. Let's be safe for now though.
         maxToRenderPerBatch={25}
@@ -175,7 +179,6 @@ export function MessagesList({chatId}: {chatId: string}) {
         maintainVisibleContentPosition={{
           minIndexForVisible: 0,
         }}
-        // This is actually a header since we are inverted!
         ListFooterComponent={<MaybeLoader isLoading={false} />}
         removeClippedSubviews={true}
         ref={flatListRef}
diff --git a/src/screens/Messages/List/index.tsx b/src/screens/Messages/List/index.tsx
index b13ddd291..ff4e8e83e 100644
--- a/src/screens/Messages/List/index.tsx
+++ b/src/screens/Messages/List/index.tsx
@@ -1,4 +1,4 @@
-import React, {useCallback, useState} from 'react'
+import React, {useCallback, useMemo, useState} from 'react'
 import {View} from 'react-native'
 import {msg} from '@lingui/macro'
 import {useLingui} from '@lingui/react'
@@ -20,10 +20,11 @@ import {SettingsSliderVertical_Stroke2_Corner0_Rounded as SettingsSlider} from '
 import {Link} from '#/components/Link'
 import {ListFooter, ListMaybePlaceholder} from '#/components/Lists'
 import {Text} from '#/components/Typography'
+import {NewChat} from '../../../components/dms/NewChat'
 import {ClipClopGate} from '../gate'
 
 type Props = NativeStackScreenProps<MessagesTabNavigatorParams, 'MessagesList'>
-export function MessagesListScreen({}: Props) {
+export function MessagesListScreen({navigation}: Props) {
   const {_} = useLingui()
   const t = useTheme()
 
@@ -53,14 +54,14 @@ export function MessagesListScreen({}: Props) {
 
   const isError = !!error
 
-  const conversations = React.useMemo(() => {
+  const conversations = useMemo(() => {
     if (data?.pages) {
       return data.pages.flat()
     }
     return []
   }, [data])
 
-  const onRefresh = React.useCallback(async () => {
+  const onRefresh = useCallback(async () => {
     setIsPTRing(true)
     try {
       await refetch()
@@ -70,7 +71,7 @@ export function MessagesListScreen({}: Props) {
     setIsPTRing(false)
   }, [refetch, setIsPTRing])
 
-  const onEndReached = React.useCallback(async () => {
+  const onEndReached = useCallback(async () => {
     if (isFetchingNextPage || !hasNextPage || isError) return
     try {
       await fetchNextPage()
@@ -79,26 +80,35 @@ export function MessagesListScreen({}: Props) {
     }
   }, [isFetchingNextPage, hasNextPage, isError, fetchNextPage])
 
+  const onNewChat = useCallback(
+    (conversation: string) =>
+      navigation.navigate('MessagesConversation', {conversation}),
+    [navigation],
+  )
+
   const gate = useGate()
   if (!gate('dms')) return <ClipClopGate />
 
   if (conversations.length < 1) {
     return (
-      <ListMaybePlaceholder
-        isLoading={isLoading}
-        isError={isError}
-        emptyType="results"
-        emptyMessage={_(
-          msg`You have no messages yet. Start a conversation with someone!`,
-        )}
-        errorMessage={cleanError(error)}
-        onRetry={isError ? refetch : undefined}
-      />
+      <>
+        <ListMaybePlaceholder
+          isLoading={isLoading}
+          isError={isError}
+          emptyType="results"
+          emptyMessage={_(
+            msg`You have no messages yet. Start a conversation with someone!`,
+          )}
+          errorMessage={cleanError(error)}
+          onRetry={isError ? refetch : undefined}
+        />
+        <NewChat onNewChat={onNewChat} />
+      </>
     )
   }
 
   return (
-    <View>
+    <View style={a.flex_1}>
       <ViewHeader
         title={_(msg`Messages`)}
         showOnDesktop
@@ -106,6 +116,7 @@ export function MessagesListScreen({}: Props) {
         showBorder
         canGoBack={false}
       />
+      <NewChat onNewChat={onNewChat} />
       <List
         data={conversations}
         renderItem={({item}) => {
diff --git a/src/screens/Messages/Temp/query/query.ts b/src/screens/Messages/Temp/query/query.ts
index 2477dc569..26f9e625f 100644
--- a/src/screens/Messages/Temp/query/query.ts
+++ b/src/screens/Messages/Temp/query/query.ts
@@ -1,20 +1,24 @@
 import {useMutation, useQuery, useQueryClient} from '@tanstack/react-query'
 
-import {useSession} from 'state/session'
-import {useDmServiceUrlStorage} from '#/screens/Messages/Temp/useDmServiceUrlStorage'
+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)
  */
 
-function createHeaders(did: string) {
+const useHeaders = () => {
+  const {getAgent} = useAgent()
   return {
-    Authorization: did,
+    get Authorization() {
+      return getAgent().session!.did
+    },
   }
 }
 
@@ -27,10 +31,8 @@ type Chat = {
 
 export function useChat(chatId: string) {
   const queryClient = useQueryClient()
-
+  const headers = useHeaders()
   const {serviceUrl} = useDmServiceUrlStorage()
-  const {currentAccount} = useSession()
-  const did = currentAccount?.did ?? ''
 
   return useQuery({
     queryKey: ['chat', chatId],
@@ -44,7 +46,7 @@ export function useChat(chatId: string) {
       const messagesResponse = await fetch(
         `${serviceUrl}/xrpc/temp.dm.getChatMessages?chatId=${chatId}`,
         {
-          headers: createHeaders(did),
+          headers,
         },
       )
 
@@ -56,7 +58,7 @@ export function useChat(chatId: string) {
       const chatResponse = await fetch(
         `${serviceUrl}/xrpc/temp.dm.getChat?chatId=${chatId}`,
         {
-          headers: createHeaders(did),
+          headers,
         },
       )
 
@@ -90,10 +92,8 @@ export function createTempId() {
 
 export function useSendMessageMutation(chatId: string) {
   const queryClient = useQueryClient()
-
+  const headers = useHeaders()
   const {serviceUrl} = useDmServiceUrlStorage()
-  const {currentAccount} = useSession()
-  const did = currentAccount?.did ?? ''
 
   return useMutation<
     TempDmChatDefs.Message,
@@ -108,7 +108,7 @@ export function useSendMessageMutation(chatId: string) {
         {
           method: 'POST',
           headers: {
-            ...createHeaders(did),
+            ...headers,
             'Content-Type': 'application/json',
           },
           body: JSON.stringify({
@@ -130,8 +130,10 @@ export function useSendMessageMutation(chatId: string) {
           ...prev,
           messages: [
             {
+              $type: 'temp.dm.defs#messageView',
               id: variables.tempId,
               text: variables.message,
+              sender: {did: headers.Authorization}, // TODO a real DID get
             },
             ...prev.messages,
           ],
@@ -165,10 +167,8 @@ export function useSendMessageMutation(chatId: string) {
 
 export function useChatLogQuery() {
   const queryClient = useQueryClient()
-
+  const headers = useHeaders()
   const {serviceUrl} = useDmServiceUrlStorage()
-  const {currentAccount} = useSession()
-  const did = currentAccount?.did ?? ''
 
   return useQuery({
     queryKey: ['chatLog'],
@@ -183,7 +183,7 @@ export function useChatLogQuery() {
             prevLog?.cursor ?? ''
           }`,
           {
-            headers: createHeaders(did),
+            headers,
           },
         )
 
@@ -193,13 +193,10 @@ export function useChatLogQuery() {
           (await response.json()) as TempDmChatGetChatLog.OutputSchema
 
         for (const log of json.logs) {
-          if (TempDmChatDefs.isLogDeleteMessage(log)) {
+          if (TempDmChatDefs.isLogCreateMessage(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
+              // TODO hack filter out duplicates
+              if (prev?.messages.find(m => m.id === log.message.id)) return
 
               return {
                 ...prev,
@@ -217,3 +214,39 @@ export function useChatLogQuery() {
     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,
+  })
+}