ソースを参照

sending attachments

Viktoriia 5 ヶ月 前
コミット
a9d6c8b099

+ 21 - 2
src/modules/api/chat/chat-api.ts

@@ -61,10 +61,12 @@ export interface PostSendMessage {
   to_uid: number;
   text: string;
   reply_to_id?: number;
+  attachment?: { uri: string; type: string; name?: string } | -1;
 }
 
 export interface PostSendMessageReturn extends ResponseType {
   message_id: number;
+  attachment?: any;
 }
 
 export interface PostMessagesReceivedOrRead {
@@ -138,8 +140,25 @@ export const chatApi = {
       no_of_messages,
       previous_than_message_id
     }),
-  sendMessage: (data: PostSendMessage) =>
-    request.postForm<PostSendMessageReturn>(API.SEND_MESSAGE, data),
+  sendMessage: (data: PostSendMessage) => {
+    const formData = new FormData();
+    formData.append('token', data.token);
+    formData.append('to_uid', data.to_uid.toString());
+    formData.append('text', data.text);
+    if (data.reply_to_id && data.reply_to_id !== undefined) {
+      formData.append('reply_to_id', data.reply_to_id.toString());
+    }
+    if (data.attachment && data.attachment !== -1) {
+      const { uri, type, name } = data.attachment;
+      formData.append('attachment', {
+        uri,
+        type: type === 'image' ? type + '/' + uri.split('.').pop()! : type,
+        name: name || 'file'
+      } as unknown as Blob);
+    }
+
+    return request.postForm<PostSendMessageReturn>(API.SEND_MESSAGE, formData);
+  },
   messagesReceived: (data: PostMessagesReceivedOrRead) =>
     request.postForm<ResponseType>(API.MESSAGES_RECEIVED, data),
   messagesRead: (data: PostMessagesReceivedOrRead) =>

+ 181 - 50
src/screens/InAppScreens/MessagesScreen/ChatScreen/index.tsx

@@ -176,63 +176,193 @@ const ChatScreen = ({ route }: { route: any }) => {
     });
   }, []);
 
-  const onSendMedia = useCallback((files: { uri: string; type: 'image' | 'video' }[]) => {
-    const newMsgs = files.map((file) => {
-      const msg: IMessage = {
-        _id: Date.now() + Math.random(),
-        text: '',
-        createdAt: new Date(),
-        user: { _id: +currentUserId, name: 'Me' }
-      };
+  const onSendMedia = useCallback(
+    (files: { uri: string; type: 'image' | 'video' }[]) => {
+      const newMsgs = files.map((file) => {
+        const msg: IMessage = {
+          _id: Date.now() + Math.random(),
+          text: '',
+          createdAt: new Date(),
+          user: { _id: +currentUserId, name: 'Me' }
+        };
 
-      if (file.type === 'image') {
-        msg.image = file.uri;
-      } else if (file.type === 'video') {
-        msg.video = file.uri;
-      }
-      return msg;
-    });
+        if (file.type === 'image') {
+          msg.image = file.uri;
+        } else if (file.type === 'video') {
+          msg.video = file.uri;
+        }
 
-    setMessages((prev) => GiftedChat.append(prev, newMsgs));
-  }, []);
+        const messageData = {
+          token,
+          to_uid: id,
+          text: '',
+          reply_to_id: replyMessage ? (replyMessage._id as number) : -1,
+          attachment: {
+            uri: file.uri,
+            type: file.type,
+            name: file.uri.split('/').pop()
+          }
+        };
 
-  const onSendLocation = useCallback((coords: { latitude: number; longitude: number }) => {
-    const locMsg: IMessage = {
-      _id: Date.now() + Math.random(),
-      text: `Location: lat=${coords.latitude}, lon=${coords.longitude}`,
-      createdAt: new Date(),
-      user: { _id: +currentUserId, name: 'Me' },
-      location: coords
-    };
-    setMessages((prev) => GiftedChat.append(prev, [locMsg]));
-  }, []);
+        console.log('messageData', messageData);
 
-  const onSendFile = useCallback((files: { uri: string; type: string; name?: string }[]) => {
-    const newMsgs = files.map((file) => {
-      const msg: IMessage = {
+        sendMessage(messageData, {
+          onSuccess: (res) => {
+            console.log('res', res);
+            const newMessage = {
+              _id: res.message_id,
+              text: '',
+              attachment: res.attachment,
+              replyMessage: replyMessage
+                ? { text: replyMessage.text, id: replyMessage._id, sender: replyMessage.user?._id }
+                : undefined
+            };
+
+            // setMessages((previousMessages) =>
+            //   (previousMessages ?? []).map((msg) =>
+            //     msg._id === msg._id ? { ...msg, _id: res.message_id } : msg
+            //   )
+            // );
+            sendWebSocketMessage('new_message', newMessage as unknown as CustomMessage);
+          },
+          onError: (err) => console.log('err', err)
+        });
+
+        return msg;
+      });
+      clearReplyMessage();
+
+      setMessages((prev) => GiftedChat.append(prev, newMsgs));
+    },
+    [replyMessage]
+  );
+
+  const toBase64 = (str: string) => {
+    return global.btoa(unescape(encodeURIComponent(str)));
+  };
+
+  const onSendLocation = useCallback(
+    (coords: { latitude: number; longitude: number }) => {
+      const locMsg: IMessage = {
         _id: Date.now() + Math.random(),
-        text: '',
+        text: `Location: lat=${coords.latitude}, lon=${coords.longitude}`,
         createdAt: new Date(),
-        user: { _id: +currentUserId, name: 'Me' }
+        user: { _id: +currentUserId, name: 'Me' },
+        location: coords
       };
 
-      if (file.type.includes('image')) {
-        msg.image = file.uri;
-      } else if (file.type.includes('video')) {
-        msg.video = file.uri;
-      } else {
-        msg.attachment = {
-          uri: file.uri,
-          type: file.type,
-          name: file.name || 'Attachment'
+      const locationData = JSON.stringify({ lat: coords.latitude, lng: coords.longitude });
+
+      const locationFile = {
+        uri: `data:application/json;base64,${toBase64(locationData)}`,
+        type: 'application/json',
+        name: 'location.json'
+      };
+
+      const messageData = {
+        token,
+        to_uid: id,
+        text: locMsg.text,
+        reply_to_id: replyMessage ? (replyMessage._id as number) : -1,
+        attachment: locationFile
+      };
+
+      console.log('messageData', messageData);
+
+      sendMessage(messageData, {
+        onSuccess: (res) => {
+          console.log('res', res);
+          const newMessage = {
+            _id: res.message_id,
+            text: locMsg.text,
+            attachment: res.attachment,
+            replyMessage: replyMessage
+              ? { text: replyMessage.text, id: replyMessage._id, sender: replyMessage.user?._id }
+              : undefined
+          };
+
+          // setMessages((previousMessages) =>
+          //   (previousMessages ?? []).map((msg) =>
+          //     msg._id === locMsg._id ? { ...msg, _id: res.message_id } : msg
+          //   )
+          // );
+          // sendWebSocketMessage('new_message', newMessage as unknown as CustomMessage);
+        },
+        onError: (err) => console.log('err', err)
+      });
+      clearReplyMessage();
+
+      setMessages((prev) => GiftedChat.append(prev, [locMsg]));
+    },
+    [replyMessage]
+  );
+
+  const onSendFile = useCallback(
+    (files: { uri: string; type: string; name?: string }[]) => {
+      const newMsgs = files.map((file) => {
+        const msg: IMessage = {
+          _id: Date.now() + Math.random(),
+          text: '',
+          createdAt: new Date(),
+          user: { _id: +currentUserId, name: 'Me' }
         };
-      }
 
-      return msg;
-    });
+        if (file.type.includes('image')) {
+          msg.image = file.uri;
+        } else if (file.type.includes('video')) {
+          msg.video = file.uri;
+        } else {
+          msg.attachment = {
+            uri: file.uri,
+            type: file.type,
+            name: file.name || 'Attachment'
+          };
+        }
 
-    setMessages((prev) => GiftedChat.append(prev, newMsgs));
-  }, []);
+        const messageData = {
+          token,
+          to_uid: id,
+          text: '',
+          reply_to_id: replyMessage ? (replyMessage._id as number) : -1,
+          attachment: {
+            uri: file.uri,
+            type: file.type,
+            name: file.name || file.uri.split('/').pop()
+          }
+        };
+
+        console.log('messageData', messageData);
+
+        sendMessage(messageData, {
+          onSuccess: (res) => {
+            console.log('res', res);
+            const newMessage = {
+              _id: res.message_id,
+              text: '',
+              attachment: res.attachment,
+              replyMessage: replyMessage
+                ? { text: replyMessage.text, id: replyMessage._id, sender: replyMessage.user?._id }
+                : undefined
+            };
+
+            // setMessages((previousMessages) =>
+            //   (previousMessages ?? []).map((msg) =>
+            //     msg._id === msg._id ? { ...msg, _id: res.message_id } : msg
+            //   )
+            // );
+            sendWebSocketMessage('new_message', newMessage as unknown as CustomMessage);
+          },
+          onError: (err) => console.log('err', err)
+        });
+
+        return msg;
+      });
+
+      clearReplyMessage();
+      setMessages((prev) => GiftedChat.append(prev, newMsgs));
+    },
+    [replyMessage]
+  );
 
   async function openFileInApp(uri: string, fileName: string) {
     try {
@@ -269,8 +399,9 @@ const ChatScreen = ({ route }: { route: any }) => {
     const leftMessage = currentMessage?.user?._id !== +currentUserId;
     if (!currentMessage?.attachment) return null;
 
-    const { uri, type, name } = currentMessage.attachment;
-    const fileName = name ?? 'Attachment';
+    // const { uri, type, name } = currentMessage.attachment;
+    // const fileName = name ?? 'Attachment';
+    const fileName = 'Attachment';
 
     return (
       <TouchableOpacity
@@ -278,8 +409,8 @@ const ChatScreen = ({ route }: { route: any }) => {
           styles.fileContainer,
           { backgroundColor: leftMessage ? 'rgba(15, 63, 79, 0.2)' : 'rgba(244, 244, 244, 0.2)' }
         ]}
-        onPress={() => openFileInApp(uri, fileName)}
-        onLongPress={() => downloadFileToDevice(uri, fileName)}
+        // onPress={() => openFileInApp(uri, fileName)}
+        // onLongPress={() => downloadFileToDevice(uri, fileName)}
       >
         <MaterialCommunityIcons
           name="file"

+ 19 - 25
src/screens/InAppScreens/MessagesScreen/Components/AttachmentsModal.tsx

@@ -1,4 +1,4 @@
-import React, { useCallback, useState } from 'react';
+import React, { useCallback, useRef, useState } from 'react';
 import { StyleSheet, TouchableOpacity, View, Text, Button } from 'react-native';
 import ActionSheet, { Route, SheetManager, useSheetRouter } from 'react-native-actions-sheet';
 import { getFontSize } from 'src/utils';
@@ -15,27 +15,17 @@ import RouteB from './RouteB';
 
 const AttachmentsModal = () => {
   const insets = useSafeAreaInsets();
-  const [chatData, setChatData] = useState<any>(null);
   const [shouldOpenWarningModal, setShouldOpenWarningModal] = useState<WarningProps | null>(null);
   const { mutateAsync: reportUser } = usePostReportConversationMutation();
 
-  const router = useSheetRouter('sheet-router');
-
-  const [currentLocation, setCurrentLocation] = useState<{
-    latitude: number;
-    longitude: number;
-  } | null>(null);
-  const [selectedLocation, setSelectedLocation] = useState<{
-    latitude: number;
-    longitude: number;
-  } | null>(null);
-  const [isLoading, setIsLoading] = useState(true);
+  const chatDataRef = useRef<any>(null);
 
   const handleSheetOpen = (payload: any) => {
-    setChatData(payload);
+    chatDataRef.current = payload;
   };
 
   const handleReport = async () => {
+    const chatData = chatDataRef.current;
     if (!chatData) return;
 
     setShouldOpenWarningModal({
@@ -56,7 +46,8 @@ const AttachmentsModal = () => {
     }, 300);
   };
 
-  const handleOpenGallery = useCallback(async () => {
+  const handleOpenGallery = async () => {
+    const chatData = chatDataRef.current;
     if (!chatData) return;
     try {
       const perm = await ImagePicker.requestMediaLibraryPermissionsAsync();
@@ -83,9 +74,10 @@ const AttachmentsModal = () => {
     } catch (err) {
       console.warn('Gallery error: ', err);
     }
-  }, [chatData?.onSendMedia, chatData?.closeOptions]);
+  };
 
-  const handleOpenCamera = useCallback(async () => {
+  const handleOpenCamera = async () => {
+    const chatData = chatDataRef.current;
     if (!chatData) return;
     try {
       const perm = await ImagePicker.requestCameraPermissionsAsync();
@@ -110,15 +102,17 @@ const AttachmentsModal = () => {
     } catch (err) {
       console.warn('Camera error: ', err);
     }
-  }, [chatData?.onSendMedia, chatData?.closeOptions]);
+  };
 
-  const handleShareLiveLocation = useCallback(() => {
+  const handleShareLiveLocation = () => {
+    const chatData = chatDataRef.current;
     if (!chatData) return;
     chatData.onShareLiveLocation();
     SheetManager.hide('chat-attachments');
-  }, [chatData?.onShareLiveLocation, chatData?.closeOptions]);
+  };
 
-  const handleSendFile = useCallback(async () => {
+  const handleSendFile = async () => {
+    const chatData = chatDataRef.current;
     if (!chatData) return;
 
     try {
@@ -145,10 +139,10 @@ const AttachmentsModal = () => {
     }
 
     SheetManager.hide('chat-attachments');
-  }, [chatData?.onSendFile, chatData?.closeOptions]);
+  };
 
   const RouteA = () => {
-    const router = useSheetRouter('sheet-router');
+    const router = useSheetRouter('chat-attachments');
     return (
       <View
         style={[
@@ -205,7 +199,7 @@ const AttachmentsModal = () => {
     {
       name: 'route-b',
       component: RouteB,
-      params: { onSendLocation: chatData?.onSendLocation, insetsBottom: insets.bottom }
+      params: { onSendLocation: chatDataRef.current?.onSendLocation, insetsBottom: insets.bottom }
     }
   ];
 
@@ -227,7 +221,7 @@ const AttachmentsModal = () => {
       }}
       onClose={() => {
         if (shouldOpenWarningModal) {
-          chatData?.setModalInfo({
+          chatDataRef.current?.setModalInfo({
             visible: true,
             type: 'delete',
             title: shouldOpenWarningModal.title,

+ 2 - 2
src/screens/InAppScreens/MessagesScreen/Components/RouteB.tsx

@@ -9,8 +9,8 @@ import { ButtonVariants } from 'src/types/components';
 import { Button } from 'src/components';
 
 const RouteB = () => {
-  const router = useSheetRouter('sheet-router');
-  const params = useSheetRouteParams('sheet-router', 'route-b');
+  const router = useSheetRouter('chat-attachments');
+  const params = useSheetRouteParams('chat-attachments', 'route-b');
   const {
     onSendLocation,
     insetsBottom

+ 5 - 0
src/utils/request.ts

@@ -17,6 +17,11 @@ export const setupInterceptors = ({
     (config) => {
       config.headers['App-Version'] = APP_VERSION;
       config.headers['Platform'] = Platform.OS;
+
+      if (config.data instanceof FormData) {
+        config.timeout = 0;
+      }
+
       return config;
     },
     (error) => {