2 Commits d438b4daec ... 4c6b0bbd16

Tác giả SHA1 Thông báo Ngày
  Viktoriia 4c6b0bbd16 fixes 1 tuần trước cách đây
  Viktoriia f34273114d keyboard autofocus fix for the GroupChat + small ts fix 1 tuần trước cách đây

+ 0 - 1
package.json

@@ -122,7 +122,6 @@
     "baseline-browser-mapping": "^2.9.7",
     "metro-react-native-babel-transformer": "^0.77.0",
     "prettier": "^3.1.0",
-    "react-dom": "19.1.0",
     "react-native-svg-transformer": "^1.5.0",
     "typescript": "~5.9.2"
   },

+ 49 - 20
src/screens/InAppScreens/MessagesScreen/ChatScreen/index.tsx

@@ -86,7 +86,6 @@ import { database } from 'src/watermelondb';
 import { Q } from '@nozbe/watermelondb';
 import { createOptimisticMessage } from 'src/watermelondb/features/chat/data/createOptimisticMessage';
 import _ from 'lodash';
-import { Galeria } from '@nandorojo/galeria';
 
 const options = {
   enableVibrateFallback: true,
@@ -137,7 +136,8 @@ const ChatScreen = ({ route }: { route: any }) => {
     data: chatData,
     refetch,
     isFetching,
-    isFetched
+    isFetchedAfterMount,
+    isRefetching
   } = usePostGetChatWithQuery(token, id, 50, prevThenMessageId, true);
 
   const swipeableRowRef = useRef<Swipeable | null>(null);
@@ -773,13 +773,13 @@ const ChatScreen = ({ route }: { route: any }) => {
   const handleWebSocketMessage = async (data: any) => {
     switch (data.action) {
       case 'new_message':
-        if (data.conversation_with === id && data.message) {
+        if (data.conversation_with === id && data.message && data.uid !== +currentUserId) {
           await upsertMessagesIntoDB({ chatUid: id, apiMessages: [data.message] });
         }
         break;
 
       case 'new_reaction':
-        if (data.conversation_with === id && data.reaction) {
+        if (data.conversation_with === id && data.reaction && data.uid !== +currentUserId) {
           const record = await findMsgRecord(data.reaction.message_id, id);
 
           if (!record) return;
@@ -799,7 +799,11 @@ const ChatScreen = ({ route }: { route: any }) => {
         break;
 
       case 'unreact':
-        if (data.conversation_with === id && data.unreacted_message_id) {
+        if (
+          data.conversation_with === id &&
+          data.unreacted_message_id &&
+          data.uid !== +currentUserId
+        ) {
           const record = await findMsgRecord(data.unreacted_message_id, id);
 
           if (!record) return;
@@ -817,7 +821,11 @@ const ChatScreen = ({ route }: { route: any }) => {
         break;
 
       case 'delete_message':
-        if (data.conversation_with === id && data.deleted_message_id) {
+        if (
+          data.conversation_with === id &&
+          data.deleted_message_id &&
+          data.uid !== +currentUserId
+        ) {
           const record = await findMsgRecord(data.deleted_message_id, id);
 
           if (!record) return;
@@ -848,7 +856,12 @@ const ChatScreen = ({ route }: { route: any }) => {
 
       case 'messages_read':
         const readIds = data.read_messages_ids;
-        if (data.conversation_with === id && Array.isArray(readIds) && readIds.length) {
+        if (
+          data.conversation_with === id &&
+          Array.isArray(readIds) &&
+          readIds.length &&
+          data.uid !== +currentUserId
+        ) {
           const records = await database
             .get<Message>('messages')
             .query(Q.where('chat_key', 'u:' + id), Q.where('message_id', Q.oneOf(readIds)))
@@ -856,19 +869,26 @@ const ChatScreen = ({ route }: { route: any }) => {
 
           if (!records.length) return;
 
-          const prepared = records.map((r) =>
-            r.prepareUpdate((m) => {
-              m.status = 3;
-            })
-          );
-
-          await database.batch(prepared);
+          await database.write(async () => {
+            records.forEach((msg: Message) => {
+              msg.update((r) => {
+                r.status = 3;
+                (r as any)._raw._status = 'synced';
+                (r as any)._raw._changed = '';
+              });
+            });
+          });
         }
         break;
 
       case 'messages_received':
         const receivedIds = data.received_messages_ids;
-        if (data.conversation_with === id && Array.isArray(receivedIds) && receivedIds.length) {
+        if (
+          data.conversation_with === id &&
+          Array.isArray(receivedIds) &&
+          receivedIds.length &&
+          data.uid !== +currentUserId
+        ) {
           const records = await database
             .get<Message>('messages')
             .query(Q.where('chat_key', 'u:' + id), Q.where('message_id', Q.oneOf(receivedIds)))
@@ -881,6 +901,8 @@ const ChatScreen = ({ route }: { route: any }) => {
               r.update((m) => {
                 m.status = 2;
                 m.isSending = false;
+                (m as any)._raw._status = 'synced';
+                (m as any)._raw._changed = '';
               });
             });
           });
@@ -888,7 +910,7 @@ const ChatScreen = ({ route }: { route: any }) => {
         break;
 
       case 'edited_message':
-        if (data.conversation_with === id && data.message) {
+        if (data.conversation_with === id && data.message && data.uid !== +currentUserId) {
           const record = await findMsgRecord(data.message.id, id);
 
           if (!record) return;
@@ -1087,6 +1109,12 @@ const ChatScreen = ({ route }: { route: any }) => {
   }, []);
 
   useEffect(() => {
+    if (!isFetchedAfterMount) {
+      if (!isRefetching) {
+        refetch();
+      }
+      return;
+    }
     if (!chatData?.messages?.length) {
       setHasMoreMessages(false);
       return;
@@ -1101,10 +1129,10 @@ const ChatScreen = ({ route }: { route: any }) => {
     if (chatData.messages.length < 50) {
       setHasMoreMessages(false);
     }
-  }, [chatData]);
+  }, [chatData, isRefetching]);
 
   useEffect(() => {
-    if (!isFetched || isFetching) return;
+    if (!isFetchedAfterMount || isFetching) return;
     if (
       giftedMessages?.length === 0 &&
       !modalInfo.visible &&
@@ -1114,7 +1142,7 @@ const ChatScreen = ({ route }: { route: any }) => {
         textInputRef.current?.focus();
       }, 500);
     }
-  }, [isFetched]);
+  }, [isFetchedAfterMount]);
 
   const loadEarlierMessages = async () => {
     if (isLoadingEarlier || !hasMoreMessages || !giftedMessages) return;
@@ -1589,9 +1617,10 @@ const ChatScreen = ({ route }: { route: any }) => {
         { datetime: new Date().toISOString(), reaction: reaction, uid: +currentUserId }
       ];
 
+      const newReactions = JSON.stringify(updatedReactions);
       await database.write(async () => {
         existingMsg.update((r) => {
-          r.reactions = JSON.stringify(updatedReactions);
+          r.reactions = newReactions;
           addMessageDirtyAction(r, {
             type: 'reaction',
             value: reaction

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

@@ -59,7 +59,7 @@ const ReactionsListModal = () => {
             ? messageReactions?.filter((r: Reaction) => r.uid !== reactionsData.currentUserId)
             : [];
 
-          await database.write(() =>
+          await database.write(async () =>
             existingMsg.update((r) => {
               r.reactions = JSON.stringify(updatedReactions);
               addMessageDirtyAction(r, {
@@ -87,7 +87,7 @@ const ReactionsListModal = () => {
             ? messageReactions?.filter((r: Reaction) => r.uid !== reactionsData.currentUserId)
             : [];
 
-          await database.write(() =>
+          await database.write(async () =>
             existingMsg.update((r) => {
               r.reactions = JSON.stringify(updatedReactions);
               addMessageDirtyAction(r, {

+ 55 - 40
src/screens/InAppScreens/MessagesScreen/GroupChatScreen/index.tsx

@@ -46,7 +46,7 @@ import { SafeAreaView, useSafeAreaInsets } from 'react-native-safe-area-context'
 import Clipboard from '@react-native-clipboard/clipboard';
 import { trigger } from 'react-native-haptic-feedback';
 import ReactModal from 'react-native-modal';
-import { checkAndSendSavedMessages, storage, StoreType } from 'src/storage';
+import { storage, StoreType } from 'src/storage';
 import {
   usePostGetGroupChatQuery,
   usePostGetPinnedGroupMessageQuery,
@@ -151,9 +151,10 @@ const GroupChatScreen = ({ route }: { route: any }) => {
   const [visibleBeforeId, setVisibleBeforeId] = useState<number | null>(null);
   const {
     data: chatData,
-    refetch: refetch,
-    isFetching: isFetching,
-    isFetched
+    refetch,
+    isFetching,
+    isFetchedAfterMount,
+    isRefetching
   } = usePostGetGroupChatQuery(token, group_token, 50, prevThenMessageId, true);
   const [canSeeMembers, setCanSeeMembers] = useState(false);
 
@@ -331,16 +332,6 @@ const GroupChatScreen = ({ route }: { route: any }) => {
     }, [navigation])
   );
 
-  useEffect(() => {
-    if (netInfo && netInfo.isConnected !== null) {
-      setIsConnected(netInfo.isConnected);
-      if (netInfo.isConnected) {
-        checkAndSendSavedMessages();
-        refetch();
-      }
-    }
-  }, [netInfo]);
-
   const closeModal = () => {
     setModalInfo({ ...modalInfo, visible: false });
   };
@@ -768,7 +759,7 @@ const GroupChatScreen = ({ route }: { route: any }) => {
   const handleWebSocketMessage = async (data: any) => {
     switch (data.action) {
       case 'new_message':
-        if (data.group_token === group_token && data.message) {
+        if (data.group_token === group_token && data.message && data.uid !== +currentUserId) {
           await upsertMessagesIntoDB({
             groupToken: group_token,
             apiMessages: [data.message],
@@ -779,7 +770,7 @@ const GroupChatScreen = ({ route }: { route: any }) => {
         break;
 
       case 'new_reaction':
-        if (data.group_token === group_token && data.reaction) {
+        if (data.group_token === group_token && data.reaction && data.uid !== +currentUserId) {
           const record = await findGroupMsgRecord(data.reaction.message_id, group_token);
 
           if (!record) return;
@@ -799,7 +790,11 @@ const GroupChatScreen = ({ route }: { route: any }) => {
         break;
 
       case 'unreact':
-        if (data.group_token === group_token && data.unreacted_message_id) {
+        if (
+          data.group_token === group_token &&
+          data.unreacted_message_id &&
+          data.uid !== +currentUserId
+        ) {
           const record = await findGroupMsgRecord(data.unreacted_message_id, group_token);
 
           if (!record) return;
@@ -817,7 +812,11 @@ const GroupChatScreen = ({ route }: { route: any }) => {
         break;
 
       case 'delete_message':
-        if (data.group_token === group_token && data.deleted_message_id) {
+        if (
+          data.group_token === group_token &&
+          data.deleted_message_id &&
+          data.uid !== +currentUserId
+        ) {
           const record = await findGroupMsgRecord(data.deleted_message_id, group_token);
           if (!record) return;
 
@@ -847,7 +846,12 @@ const GroupChatScreen = ({ route }: { route: any }) => {
 
       case 'messages_read':
         const readIds = data.read_messages_ids;
-        if (data.group_token === group_token && Array.isArray(readIds) && readIds.length) {
+        if (
+          data.group_token === group_token &&
+          Array.isArray(readIds) &&
+          readIds.length &&
+          data.uid !== +currentUserId
+        ) {
           const records = await database
             .get<Message>('messages')
             .query(Q.where('chat_key', 'g:' + group_token), Q.where('message_id', Q.oneOf(readIds)))
@@ -855,18 +859,25 @@ const GroupChatScreen = ({ route }: { route: any }) => {
 
           if (!records.length) return;
 
-          const prepared = records.map((r) =>
-            r.prepareUpdate((m) => {
-              m.status = 3;
-            })
-          );
-
-          await database.batch(prepared);
+          await database.write(async () => {
+            records.forEach((msg: Message) => {
+              msg.update((r) => {
+                r.status = 3;
+                (r as any)._raw._status = 'synced';
+                (r as any)._raw._changed = '';
+              });
+            });
+          });
         }
         break;
       case 'messages_received':
         const receivedIds = data.received_messages_ids;
-        if (data.group_token === group_token && Array.isArray(receivedIds) && receivedIds.length) {
+        if (
+          data.group_token === group_token &&
+          Array.isArray(receivedIds) &&
+          receivedIds.length &&
+          data.uid !== +currentUserId
+        ) {
           const records = await database
             .get<Message>('messages')
             .query(
@@ -882,6 +893,8 @@ const GroupChatScreen = ({ route }: { route: any }) => {
               r.update((m) => {
                 m.status = 2;
                 m.isSending = false;
+                (m as any)._raw._status = 'synced';
+                (m as any)._raw._changed = '';
               });
             });
           });
@@ -889,7 +902,7 @@ const GroupChatScreen = ({ route }: { route: any }) => {
         break;
 
       case 'edited_message':
-        if (data.group_token === group_token && data.message) {
+        if (data.group_token === group_token && data.message && data.uid !== +currentUserId) {
           const record = await findGroupMsgRecord(data.message.id, group_token);
 
           if (!record) return;
@@ -1092,6 +1105,7 @@ const GroupChatScreen = ({ route }: { route: any }) => {
   }, [giftedMessages, unreadMessageIndex]);
 
   const reconcileDebounced = useMemo(() => _.debounce(reconcileChatRange, 300), []);
+
   useEffect(() => {
     return () => {
       reconcileDebounced.cancel();
@@ -1099,7 +1113,16 @@ const GroupChatScreen = ({ route }: { route: any }) => {
   }, []);
 
   useEffect(() => {
-    if (!chatData?.messages?.length) return;
+    if (!isFetchedAfterMount) {
+      if (!isRefetching) {
+        refetch();
+      }
+      return;
+    }
+    if (!chatData?.messages?.length) {
+      setHasMoreMessages(false);
+      return;
+    }
 
     upsertMessagesIntoDB({ groupToken: group_token, apiMessages: chatData.messages });
 
@@ -1110,7 +1133,7 @@ const GroupChatScreen = ({ route }: { route: any }) => {
     if (chatData.messages.length < 50) {
       setHasMoreMessages(false);
     }
-  }, [chatData]);
+  }, [chatData, isRefetching]);
 
   useEffect(() => {
     if (giftedMessages) {
@@ -1125,15 +1148,6 @@ const GroupChatScreen = ({ route }: { route: any }) => {
     }
   }, [giftedMessages]);
 
-  useEffect(() => {
-    if (!isFetched || isFetching) return;
-    if (giftedMessages?.length === 0 && !modalInfo.visible) {
-      setTimeout(() => {
-        textInputRef.current?.focus();
-      }, 500);
-    }
-  }, [isFetched]);
-
   const loadEarlierMessages = async () => {
     if ((isLoadingEarlier && !isSearchingMessage) || !hasMoreMessages || !giftedMessages) return;
 
@@ -1710,9 +1724,10 @@ const GroupChatScreen = ({ route }: { route: any }) => {
         }
       ];
 
+      const newReactions = JSON.stringify(updatedReactions);
       await database.write(async () => {
         existingMsg.update((r) => {
-          r.reactions = JSON.stringify(updatedReactions);
+          r.reactions = newReactions;
           addMessageDirtyAction(r, {
             type: 'reaction',
             value: reaction as string

+ 87 - 88
src/watermelondb/features/chat/data/message.sync.ts

@@ -28,7 +28,7 @@ export type MessageDirtyAction =
       ts: number;
     }
   | { type: 'edit'; value: { text: string }; ts: number }
-  | { type: 'delete'; ts: number }
+  | { type: 'delete'; value?: any; ts: number }
   | { type: 'read'; value: { messagesIds: number[] }; ts: number }
   | { type: 'reaction'; value: string; ts: number }
   | { type: 'unreaction'; value: string; ts: number };
@@ -36,10 +36,12 @@ export type MessageDirtyAction =
 export function addMessageDirtyAction(msg: Message, action: Omit<MessageDirtyAction, 'ts'>) {
   const list: MessageDirtyAction[] = msg.dirtyActions ? JSON.parse(msg.dirtyActions) : [];
 
-  list.push({ ...action, ts: now() });
+  list.push({ ...(action as MessageDirtyAction), ts: now() });
 
   msg.isDirty = true;
   msg.dirtyActions = JSON.stringify(list);
+  (msg as any)._raw._status = 'synced';
+  (msg as any)._raw._changed = '';
 }
 
 export function compactMessageActions(actions: MessageDirtyAction[]): MessageDirtyAction[] {
@@ -269,97 +271,101 @@ export async function upsertMessagesIntoDB({
         const record = existing[0];
         const hasDirty = Boolean(msg.dirtyActions);
 
-        batch.push(
-          record.prepareUpdate((r) => {
-            r.messageId = msg.id;
-            r.sentAt = msg.sent_datetime;
-            r.receivedAt = msg.received_datetime ?? r.receivedAt;
-            r.readAt = msg.read_datetime ?? r.readAt;
+        try {
+          batch.push(
+            record.prepareUpdate((r) => {
+              r.messageId = msg.id;
+              r.sentAt = msg.sent_datetime;
+              r.receivedAt = msg.received_datetime ?? r.receivedAt;
+              r.readAt = msg.read_datetime ?? r.readAt;
 
-            if (!hasDirty) {
-              r.text = msg.text;
-            }
+              if (!hasDirty) {
+                r.text = msg.text;
+              }
 
-            if (msg.attachement && msg.attachement !== -1) {
-              const prev = r.attachment ? JSON.parse(r.attachment) : {};
-              r.attachment = JSON.stringify({
-                ...msg.attachement,
-                local_uri: prev?.local_uri ?? null
-              });
-            } else {
-              r.attachment = null;
-            }
+              if (msg.attachement && msg.attachement !== -1) {
+                const prev = r.attachment ? JSON.parse(r.attachment) : {};
+                r.attachment = JSON.stringify({
+                  ...msg.attachement,
+                  local_uri: prev?.local_uri ?? null
+                });
+              } else {
+                r.attachment = null;
+              }
 
-            r.status = msg.status;
-            r.isSending = false;
-            r.replyToId = msg.reply_to_id;
-            r.replyTo = msg.reply_to ? JSON.stringify(msg.reply_to) : null;
+              r.status = msg.status;
+              r.isSending = false;
+              r.replyToId = msg.reply_to_id;
+              r.replyTo = msg.reply_to ? JSON.stringify(msg.reply_to) : null;
 
-            if (avatar && groupToken) {
-              r.senderAvatar = avatar;
-            } else {
-              r.senderAvatar = msg.sender_avatar ?? null;
-            }
-            if (name && groupToken) {
-              r.senderName = name;
-            } else {
-              r.senderName = msg.sender_name ?? '';
-            }
+              if (avatar && groupToken) {
+                r.senderAvatar = avatar;
+              } else {
+                r.senderAvatar = msg.sender_avatar ?? null;
+              }
+              if (name && groupToken) {
+                r.senderName = name;
+              } else {
+                r.senderName = msg.sender_name ?? '';
+              }
 
-            r.reactions = msg.reactions ?? '{}';
-            r.edits = msg.edits ?? '{}';
-            (r as any)._raw._status = 'synced';
-            (r as any)._raw._changed = '';
-          })
-        );
+              r.reactions = msg.reactions ?? '{}';
+              r.edits = msg.edits ?? '{}';
+              (r as any)._raw._status = 'synced';
+              (r as any)._raw._changed = '';
+            })
+          );
+        } catch (err) {}
       } else {
-        batch.push(
-          col.prepareCreate((r) => {
-            r.chatKey = chatKey;
-            r.isGroup = isGroup;
+        try {
+          batch.push(
+            col.prepareCreate((r) => {
+              r.chatKey = chatKey;
+              r.isGroup = isGroup;
 
-            r.messageId = msg.id;
-            r.compositeId = compositeId;
+              r.messageId = msg.id;
+              r.compositeId = compositeId;
 
-            r.sentAt = msg.sent_datetime;
-            r.receivedAt = msg.received_datetime ?? null;
-            r.readAt = msg.read_datetime ?? null;
+              r.sentAt = msg.sent_datetime;
+              r.receivedAt = msg.received_datetime ?? null;
+              r.readAt = msg.read_datetime ?? null;
 
-            r.senderId = msg.sender;
-            r.recipientId = msg.recipient;
+              r.senderId = msg.sender;
+              r.recipientId = msg.recipient;
 
-            r.text = msg.text;
-            r.status = msg.status;
-            r.isSending = false;
+              r.text = msg.text;
+              r.status = msg.status;
+              r.isSending = false;
 
-            r.reactions = msg.reactions ?? '{}';
-            r.edits = msg.edits ?? '{}';
+              r.reactions = msg.reactions ?? '{}';
+              r.edits = msg.edits ?? '{}';
 
-            r.attachment =
-              msg.attachement && msg.attachement !== -1 ? JSON.stringify(msg.attachement) : null;
+              r.attachment =
+                msg.attachement && msg.attachement !== -1 ? JSON.stringify(msg.attachement) : null;
 
-            r.encrypted = msg.encrypted ?? 0;
-            r.replyToId = msg.reply_to_id ?? -1;
-            r.replyTo = msg.reply_to ? JSON.stringify(msg.reply_to) : null;
+              r.encrypted = msg.encrypted ?? 0;
+              r.replyToId = msg.reply_to_id ?? -1;
+              r.replyTo = msg.reply_to ? JSON.stringify(msg.reply_to) : null;
 
-            if (avatar && groupToken) {
-              r.senderAvatar = avatar;
-            } else {
-              r.senderAvatar = msg.sender_avatar ?? null;
-            }
-            if (name && groupToken) {
-              r.senderName = name;
-            } else {
-              r.senderName = msg.sender_name ?? '';
-            }
+              if (avatar && groupToken) {
+                r.senderAvatar = avatar;
+              } else {
+                r.senderAvatar = msg.sender_avatar ?? null;
+              }
+              if (name && groupToken) {
+                r.senderName = name;
+              } else {
+                r.senderName = msg.sender_name ?? '';
+              }
 
-            r.isDirty = false;
-            r.dirtyActions = null;
+              r.isDirty = false;
+              r.dirtyActions = null;
 
-            (r as any)._raw._status = 'synced';
-            (r as any)._raw._changed = '';
-          })
-        );
+              (r as any)._raw._status = 'synced';
+              (r as any)._raw._changed = '';
+            })
+          );
+        } catch (err) {}
       }
     }
 
@@ -427,7 +433,6 @@ export async function pushMessageChanges(
   if (!dirty.length) return;
 
   for (const msg of dirty) {
-    const raw = (msg as any)._raw;
     const actions: MessageDirtyAction[] = msg.dirtyActions ? JSON.parse(msg.dirtyActions) : [];
 
     const compacted = compactMessageActions(actions);
@@ -462,21 +467,15 @@ export async function pushMessageChanges(
               ...res.attachment,
               local_uri: prev?.local_uri ?? null
             });
-
-            // if (res.attachment.filetype === 'nomadmania/location') {
-            //   const locationUri = a?.value?.attachment?.uri;
-            //   // await FileSystem.deleteAsync(locationUri);
-            // }
-          }
-          if (res?.wsEvent && onWsEvent) {
-            onWsEvent(res.wsEvent);
           }
 
-          m.dirtyActions = null;
           m.isDirty = false;
-
+          m.dirtyActions = null;
           (m as any)._raw._status = 'synced';
           (m as any)._raw._changed = '';
+          if (res?.wsEvent && onWsEvent) {
+            onWsEvent(res.wsEvent);
+          }
         });
       });
     }