瀏覽代碼

chat list api for group

Viktoriia 4 月之前
父節點
當前提交
d888a52247

+ 3 - 0
assets/icons/messages/exit.svg

@@ -0,0 +1,3 @@
+<svg width="16" height="14" viewBox="0 0 16 14" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M11.8094 2.30938L15.6469 6.14687C15.8719 6.37187 16 6.68125 16 7C16 7.31875 15.8719 7.62812 15.6469 7.85312L11.8094 11.6906C11.6094 11.8906 11.3406 12 11.0594 12C10.475 12 10 11.525 10 10.9406V9H6C5.44688 9 5 8.55313 5 8V6C5 5.44688 5.44688 5 6 5H10V3.05937C10 2.475 10.475 2 11.0594 2C11.3406 2 11.6094 2.1125 11.8094 2.30938ZM5 2H3C2.44688 2 2 2.44688 2 3V11C2 11.5531 2.44688 12 3 12H5C5.55312 12 6 12.4469 6 13C6 13.5531 5.55312 14 5 14H3C1.34375 14 0 12.6562 0 11V3C0 1.34375 1.34375 0 3 0H5C5.55312 0 6 0.446875 6 1C6 1.55313 5.55312 2 5 2Z" fill="#EF5B5B"/>
+</svg>

+ 25 - 1
src/modules/api/chat/chat-api.ts

@@ -162,6 +162,12 @@ export interface PostDeleteMessage {
   conversation_with_user: number;
 }
 
+export interface PostDeleteGroupMessage {
+  token: string;
+  message_id: number;
+  group_token: string;
+}
+
 export interface PostDeleteChat {
   token: string;
   conversation_with_user: number;
@@ -178,6 +184,12 @@ export interface PostSetSettings {
   conversation_with_user: number;
 }
 
+export interface PostSetGroupSettings {
+  token: string;
+  value: 0 | 1;
+  group_token: string;
+}
+
 export interface PostGetBlockedReturn extends ResponseType {
   blocked: {
     id: number;
@@ -329,5 +341,17 @@ export const chatApi = {
   reactToGroupMessage: (data: PostReactToGroupMessage) =>
     request.postForm<ResponseType>(API.REACT_TO_GROUP_MESSAGE, data),
   unreactToGroupMessage: (data: PostUnreactToGroupMessage) =>
-    request.postForm<ResponseType>(API.UNREACT_TO_GROUP_MESSAGE, data)
+    request.postForm<ResponseType>(API.UNREACT_TO_GROUP_MESSAGE, data),
+  deleteGroupMessage: (data: PostDeleteGroupMessage) =>
+    request.postForm<ResponseType>(API.DELETE_GROUP_MESSAGE, data),
+  setPinForGroup: (data: PostSetGroupSettings) =>
+    request.postForm<ResponseType>(API.SET_PIN_FOR_GROUP, data),
+  setArchiveForGroup: (data: PostSetGroupSettings) =>
+    request.postForm<ResponseType>(API.SET_ARCHIVE_FOR_GROUP, data),
+  setMuteForGroup: (data: PostSetGroupSettings) =>
+    request.postForm<ResponseType>(API.SET_MUTE_FOR_GROUP, data),
+  leaveGroup: (token: string, group_token: string) =>
+    request.postForm<ResponseType>(API.LEAVE_GROUP, { token, group_token }),
+  addToGroup: (token: string, uid: number, group_token: string) =>
+    request.postForm<ResponseType>(API.ADD_TO_GROUP, { token, uid, group_token })
 };

+ 7 - 1
src/modules/api/chat/chat-query-keys.tsx

@@ -32,5 +32,11 @@ export const chatQueryKeys = {
   groupMessagesReceived: () => ['groupMessagesReceived'] as const,
   groupMessagesRead: () => ['groupMessagesRead'] as const,
   reactToGroupMessage: () => ['reactToGroupMessage'] as const,
-  unreactToGroupMessage: () => ['unreactToGroupMessage'] as const
+  unreactToGroupMessage: () => ['unreactToGroupMessage'] as const,
+  deleteGroupMessage: () => ['deleteGroupMessage'] as const,
+  setPinForGroup: () => ['setPinForGroup'] as const,
+  setArchiveForGroup: () => ['setArchiveForGroup'] as const,
+  setMuteForGroup: () => ['setMuteForGroup'] as const,
+  leaveGroup: () => ['leaveGroup'] as const,
+  addToGroup: () => ['addToGroup'] as const
 };

+ 6 - 0
src/modules/api/chat/queries/index.ts

@@ -22,3 +22,9 @@ export * from './use-post-group-messages-received';
 export * from './use-post-group-messages-read';
 export * from './use-post-react-to-group-message';
 export * from './use-post-unreact-to-group-message';
+export * from './use-post-delete-group-message';
+export * from './use-post-set-pin-for-group';
+export * from './use-post-set-archive-for-group';
+export * from './use-post-set-mute-for-group';
+export * from './use-post-leave-group';
+export * from './use-post-add-to-group';

+ 22 - 0
src/modules/api/chat/queries/use-post-add-to-group.tsx

@@ -0,0 +1,22 @@
+import { useMutation } from '@tanstack/react-query';
+
+import { chatQueryKeys } from '../chat-query-keys';
+import { chatApi } from '../chat-api';
+
+import type { BaseAxiosError } from '../../../../types';
+import { ResponseType } from '@api/response-type';
+
+export const usePostAddToGroupMutation = () => {
+  return useMutation<
+    ResponseType,
+    BaseAxiosError,
+    { token: string; uid: number; group_token: string },
+    ResponseType
+  >({
+    mutationKey: chatQueryKeys.addToGroup(),
+    mutationFn: async (data) => {
+      const response = await chatApi.addToGroup(data.token, data.uid, data.group_token);
+      return response.data;
+    }
+  });
+};

+ 17 - 0
src/modules/api/chat/queries/use-post-delete-group-message.tsx

@@ -0,0 +1,17 @@
+import { useMutation } from '@tanstack/react-query';
+
+import { chatQueryKeys } from '../chat-query-keys';
+import { chatApi, type PostDeleteGroupMessage } from '../chat-api';
+
+import type { BaseAxiosError } from '../../../../types';
+import { ResponseType } from '@api/response-type';
+
+export const usePostDeleteGroupMessageMutation = () => {
+  return useMutation<ResponseType, BaseAxiosError, PostDeleteGroupMessage, ResponseType>({
+    mutationKey: chatQueryKeys.deleteGroupMessage(),
+    mutationFn: async (data) => {
+      const response = await chatApi.deleteGroupMessage(data);
+      return response.data;
+    }
+  });
+};

+ 22 - 0
src/modules/api/chat/queries/use-post-leave-group.tsx

@@ -0,0 +1,22 @@
+import { useMutation } from '@tanstack/react-query';
+
+import { chatQueryKeys } from '../chat-query-keys';
+import { chatApi } from '../chat-api';
+
+import type { BaseAxiosError } from '../../../../types';
+import { ResponseType } from '@api/response-type';
+
+export const usePostLeaveGroupMutation = () => {
+  return useMutation<
+    ResponseType,
+    BaseAxiosError,
+    { token: string; group_token: string },
+    ResponseType
+  >({
+    mutationKey: chatQueryKeys.leaveGroup(),
+    mutationFn: async (data) => {
+      const response = await chatApi.leaveGroup(data.token, data.group_token);
+      return response.data;
+    }
+  });
+};

+ 17 - 0
src/modules/api/chat/queries/use-post-set-archive-for-group.tsx

@@ -0,0 +1,17 @@
+import { useMutation } from '@tanstack/react-query';
+
+import { chatQueryKeys } from '../chat-query-keys';
+import { chatApi, type PostSetGroupSettings } from '../chat-api';
+
+import type { BaseAxiosError } from '../../../../types';
+import { ResponseType } from '@api/response-type';
+
+export const usePostSetArchiveForGroupMutation = () => {
+  return useMutation<ResponseType, BaseAxiosError, PostSetGroupSettings, ResponseType>({
+    mutationKey: chatQueryKeys.setArchiveForGroup(),
+    mutationFn: async (data) => {
+      const response = await chatApi.setArchiveForGroup(data);
+      return response.data;
+    }
+  });
+};

+ 17 - 0
src/modules/api/chat/queries/use-post-set-mute-for-group.tsx

@@ -0,0 +1,17 @@
+import { useMutation } from '@tanstack/react-query';
+
+import { chatQueryKeys } from '../chat-query-keys';
+import { chatApi, type PostSetGroupSettings } from '../chat-api';
+
+import type { BaseAxiosError } from '../../../../types';
+import { ResponseType } from '@api/response-type';
+
+export const usePostSetMuteForGroupMutation = () => {
+  return useMutation<ResponseType, BaseAxiosError, PostSetGroupSettings, ResponseType>({
+    mutationKey: chatQueryKeys.setMuteForGroup(),
+    mutationFn: async (data) => {
+      const response = await chatApi.setMuteForGroup(data);
+      return response.data;
+    }
+  });
+};

+ 17 - 0
src/modules/api/chat/queries/use-post-set-pin-for-group.tsx

@@ -0,0 +1,17 @@
+import { useMutation } from '@tanstack/react-query';
+
+import { chatQueryKeys } from '../chat-query-keys';
+import { chatApi, type PostSetGroupSettings } from '../chat-api';
+
+import type { BaseAxiosError } from '../../../../types';
+import { ResponseType } from '@api/response-type';
+
+export const usePostSetPinForGroupMutation = () => {
+  return useMutation<ResponseType, BaseAxiosError, PostSetGroupSettings, ResponseType>({
+    mutationKey: chatQueryKeys.setPinForGroup(),
+    mutationFn: async (data) => {
+      const response = await chatApi.setPinForGroup(data);
+      return response.data;
+    }
+  });
+};

+ 77 - 15
src/screens/InAppScreens/MessagesScreen/Components/MoreModal.tsx

@@ -12,7 +12,9 @@ import {
   usePostDeleteChatMutation,
   usePostReportConversationMutation,
   usePostSetBlockMutation,
-  usePostSetMuteMutation
+  usePostSetMuteMutation,
+  usePostSetMuteForGroupMutation,
+  usePostLeaveGroupMutation
 } from '@api/chat';
 import { useChatStore } from 'src/stores/chatStore';
 import TrashIcon from 'assets/icons/travels-screens/trash-solid.svg';
@@ -20,6 +22,8 @@ import BanIcon from 'assets/icons/messages/ban.svg';
 import BellSlashIcon from 'assets/icons/messages/bell-slash.svg';
 import { AvatarWithInitials } from 'src/components';
 import MegaphoneIcon from 'assets/icons/messages/megaphone.svg';
+import ExitIcon from 'assets/icons/messages/exit.svg';
+import GroupIcon from 'assets/icons/messages/group-chat.svg';
 
 const MoreModal = () => {
   const insets = useSafeAreaInsets();
@@ -39,6 +43,8 @@ const MoreModal = () => {
   const { mutateAsync: blockUser } = usePostSetBlockMutation();
   const { mutateAsync: deleteChat } = usePostDeleteChatMutation();
   const { mutateAsync: reportUser } = usePostReportConversationMutation();
+  const { mutateAsync: muteGroup } = usePostSetMuteForGroupMutation();
+  const { mutateAsync: leaveGroup } = usePostLeaveGroupMutation();
 
   const [shouldOpenWarningModal, setShouldOpenWarningModal] = useState<WarningProps | null>(null);
 
@@ -64,19 +70,32 @@ const MoreModal = () => {
   const handleMute = async () => {
     if (!chatData) return;
 
-    chatData.uid &&
-      (await muteUser(
-        {
-          token: chatData.token,
-          value: chatData.muted === 1 ? 0 : 1,
-          conversation_with_user: chatData.uid
-        },
-        {
-          onSuccess: () => {
-            setChatData({ ...chatData, muted: chatData.muted === 1 ? 0 : 1 });
+    chatData.uid
+      ? await muteUser(
+          {
+            token: chatData.token,
+            value: chatData.muted === 1 ? 0 : 1,
+            conversation_with_user: chatData.uid
+          },
+          {
+            onSuccess: () => {
+              setChatData({ ...chatData, muted: chatData.muted === 1 ? 0 : 1 });
+            }
           }
-        }
-      ));
+        )
+      : await muteGroup(
+          {
+            token: chatData.token,
+            value: chatData.muted === 1 ? 0 : 1,
+            group_token: chatData.groupToken as string
+          },
+          {
+            onSuccess: () => {
+              setChatData({ ...chatData, muted: chatData.muted === 1 ? 0 : 1 });
+            }
+          }
+        );
+
     chatData.refetch();
   };
 
@@ -151,6 +170,37 @@ const MoreModal = () => {
     }, 300);
   };
 
+  const handleLeaveGroup = async () => {
+    if (!chatData) return;
+
+    setShouldOpenWarningModal({
+      title: `Leave group ${name}`,
+      buttonTitle: 'Leave',
+      message: `Are you sure you want to leave ${name}?`,
+      action: async () => {
+        chatData.groupToken &&
+          (await leaveGroup(
+            {
+              token: chatData.token,
+              group_token: chatData.groupToken
+            },
+            {
+              onSuccess: (res) => {
+                console.log(res);
+              }
+            }
+          ));
+
+        chatData.refetch();
+      }
+    });
+
+    setTimeout(() => {
+      SheetManager.hide('more-modal');
+      setShouldOpenWarningModal(null);
+    }, 300);
+  };
+
   return (
     <ActionSheet
       id="more-modal"
@@ -182,9 +232,9 @@ const MoreModal = () => {
             }}
             disabled={chatData?.userType !== 'normal'}
           >
-            {chatData?.avatar && chatData?.userType === 'normal' ? (
+            {chatData?.avatar && (chatData.userType === 'normal' || !chatData.userType) ? (
               <Image source={{ uri: API_HOST + chatData.avatar }} style={styles.avatar} />
-            ) : chatData?.userType === 'normal' ? (
+            ) : chatData.uid && (chatData.userType === 'normal' || !chatData.userType) ? (
               <AvatarWithInitials
                 text={
                   chatData.name
@@ -196,6 +246,8 @@ const MoreModal = () => {
                 size={32}
                 fontSize={12}
               />
+            ) : chatData.userType === 'normal' || !chatData.userType ? (
+              <GroupIcon fill={Colors.DARK_BLUE} width={32} height={32} />
             ) : (
               <BanIcon fill={Colors.RED} width={32} height={32} />
             )}
@@ -218,6 +270,16 @@ const MoreModal = () => {
           <View style={[styles.optionsContainer, { paddingVertical: 0, gap: 0 }]}>
             {chatData?.userType === 'normal' && (
               <>
+                {chatData?.groupToken && (
+                  <TouchableOpacity
+                    style={[styles.option, styles.dangerOption]}
+                    onPress={handleLeaveGroup}
+                  >
+                    <Text style={[styles.optionText, styles.dangerText]}>Leave group</Text>
+                    <ExitIcon fill={Colors.RED} width={16} />
+                  </TouchableOpacity>
+                )}
+
                 <TouchableOpacity
                   style={[styles.option, styles.dangerOption]}
                   onPress={handleReport}

+ 30 - 11
src/screens/InAppScreens/MessagesScreen/Components/SwipeableRow.tsx

@@ -10,7 +10,12 @@ import ArchiveIcon from 'assets/icons/messages/archive.svg';
 import DotsIcon from 'assets/icons/messages/dots.svg';
 import UnpinIcon from 'assets/icons/messages/unpin.svg';
 import { ChatProps } from '../types';
-import { usePostSetArchiveMutation, usePostSetPinMutation } from '@api/chat';
+import {
+  usePostSetArchiveForGroupMutation,
+  usePostSetArchiveMutation,
+  usePostSetPinForGroupMutation,
+  usePostSetPinMutation
+} from '@api/chat';
 import { useChatStore } from 'src/stores/chatStore';
 
 interface AppleStyleSwipeableRowProps extends PropsWithChildren<unknown> {
@@ -34,6 +39,8 @@ const SwipeableRow: React.FC<AppleStyleSwipeableRowProps> = ({
 
   const { mutateAsync: pinChat } = usePostSetPinMutation();
   const { mutateAsync: archiveChat } = usePostSetArchiveMutation();
+  const { mutateAsync: pinGroupChat } = usePostSetPinForGroupMutation();
+  const { mutateAsync: archiveGroupChat } = usePostSetArchiveForGroupMutation();
 
   const close = () => {
     swipeableRow.current?.close();
@@ -68,11 +75,17 @@ const SwipeableRow: React.FC<AppleStyleSwipeableRowProps> = ({
           } as any
         });
       } else {
-        chat.uid && await archiveChat({
-          token,
-          value: chat.archive === 1 ? 0 : 1,
-          conversation_with_user: chat.uid
-        });
+        chat.uid
+          ? await archiveChat({
+              token,
+              value: chat.archive === 1 ? 0 : 1,
+              conversation_with_user: chat.uid
+            })
+          : await archiveGroupChat({
+              token,
+              value: chat.archive === 1 ? 0 : 1,
+              group_token: chat.groupToken as string
+            });
         refetch();
       }
     };
@@ -104,11 +117,17 @@ const SwipeableRow: React.FC<AppleStyleSwipeableRowProps> = ({
 
     const pressHandler = async () => {
       close();
-      chat.uid && await pinChat({
-        token,
-        value: chat.pin === 1 ? 0 : 1,
-        conversation_with_user: chat.uid
-      });
+      chat.uid
+        ? await pinChat({
+            token,
+            value: chat.pin === 1 ? 0 : 1,
+            conversation_with_user: chat.uid
+          })
+        : await pinGroupChat({
+            token,
+            value: chat.pin === 1 ? 0 : 1,
+            group_token: chat.groupToken as string
+          });
       refetch();
     };
 

+ 33 - 8
src/screens/InAppScreens/MessagesScreen/GroupChatScreen/index.tsx

@@ -26,7 +26,8 @@ import {
   MessageProps,
   Actions,
   isSameUser,
-  isSameDay
+  isSameDay,
+  SystemMessage
 } from 'react-native-gifted-chat';
 import { MaterialCommunityIcons } from '@expo/vector-icons';
 import { GestureHandlerRootView, Swipeable } from 'react-native-gesture-handler';
@@ -44,11 +45,11 @@ import { trigger } from 'react-native-haptic-feedback';
 import ReactModal from 'react-native-modal';
 import { storage, StoreType } from 'src/storage';
 import {
-  usePostDeleteMessageMutation,
   usePostGetGroupChatQuery,
   usePostSendGroupMessageMutation,
   usePostReactToGroupMessageMutation,
-  usePostGroupMessagesReadMutation
+  usePostGroupMessagesReadMutation,
+  usePostDeleteGroupMessageMutation
 } from '@api/chat';
 import { CustomMessage, GroupMessage, Reaction } from '../types';
 import { API_HOST, WEBSOCKET_URL } from 'src/constants';
@@ -142,7 +143,7 @@ const GroupChatScreen = ({ route }: { route: any }) => {
   const [isModalVisible, setIsModalVisible] = useState(false);
   const [unreadMessageIndex, setUnreadMessageIndex] = useState<number | null>(null);
   const { mutateAsync: markMessagesAsRead } = usePostGroupMessagesReadMutation();
-  const { mutateAsync: deleteMessage } = usePostDeleteMessageMutation();
+  const { mutateAsync: deleteMessage } = usePostDeleteGroupMessageMutation();
   const { mutateAsync: reactToMessage } = usePostReactToGroupMessageMutation();
 
   const [highlightedMessageId, setHighlightedMessageId] = useState<number | null>(null);
@@ -862,7 +863,7 @@ const GroupChatScreen = ({ route }: { route: any }) => {
               : undefined
       },
       replyMessage:
-        message.reply_to_id !== -1
+        message.reply_to_id && message.reply_to_id > 0
           ? {
               text: message.reply_to.text,
               id: message.reply_to.id,
@@ -884,7 +885,8 @@ const GroupChatScreen = ({ route }: { route: any }) => {
       image:
         message.attachement !== -1 && message.attachement?.filetype?.startsWith('image')
           ? API_HOST + message.attachement?.attachment_small_url
-          : null
+          : null,
+      system: message.sender === -1
     };
   };
 
@@ -1012,6 +1014,30 @@ const GroupChatScreen = ({ route }: { route: any }) => {
           <Text style={styles.unreadMessagesText}>{props.currentMessage.text}</Text>
         </View>
       );
+    } else if (props.currentMessage.user._id === -1) {
+      return (
+        <SystemMessage
+          currentMessage={props.currentMessage}
+          containerStyle={{
+            marginTop: 0,
+            marginBottom: 0,
+            paddingVertical: 2,
+            paddingHorizontal: 12
+          }}
+          wrapperStyle={{
+            backgroundColor: Colors.FILL_LIGHT,
+            paddingHorizontal: 6,
+            paddingVertical: 4,
+            borderRadius: 10
+          }}
+          textStyle={{
+            color: Colors.DARK_BLUE,
+            fontStyle: 'italic',
+            fontSize: 12,
+            textAlign: 'center'
+          }}
+        />
+      );
     }
     return null;
   };
@@ -1080,13 +1106,12 @@ const GroupChatScreen = ({ route }: { route: any }) => {
     addReaction(messageId, emoji);
   };
 
-  // todo: delete api
   const handleDeleteMessage = (messageId: number) => {
     deleteMessage(
       {
         token,
         message_id: messageId,
-        conversation_with_user: group_token
+        group_token
       },
       {
         onSuccess: () => {

+ 14 - 2
src/types/api.ts

@@ -166,7 +166,13 @@ export enum API_ENDPOINT {
   GROUP_MESSAGES_RECEIVED = 'group-messages-received',
   GROUP_MESSAGES_READ = 'group-messages-read',
   REACT_TO_GROUP_MESSAGE = 'react-to-group-message',
-  UNREACT_TO_GROUP_MESSAGE = 'unreact-to-group-message'
+  UNREACT_TO_GROUP_MESSAGE = 'unreact-to-group-message',
+  DELETE_GROUP_MESSAGE = 'delete-group-message',
+  SET_PIN_FOR_GROUP = 'set-pin-for-group',
+  SET_ARCHIVE_FOR_GROUP = 'set-archive-for-group',
+  SET_MUTE_FOR_GROUP = 'set-mute-for-group',
+  LEAVE_GROUP = 'leave-group',
+  ADD_TO_GROUP = 'add-to-group'
 }
 
 export enum API {
@@ -307,7 +313,13 @@ export enum API {
   GROUP_MESSAGES_RECEIVED = `${API_ROUTE.CHAT}/${API_ENDPOINT.GROUP_MESSAGES_RECEIVED}`,
   GROUP_MESSAGES_READ = `${API_ROUTE.CHAT}/${API_ENDPOINT.GROUP_MESSAGES_READ}`,
   REACT_TO_GROUP_MESSAGE = `${API_ROUTE.CHAT}/${API_ENDPOINT.REACT_TO_GROUP_MESSAGE}`,
-  UNREACT_TO_GROUP_MESSAGE = `${API_ROUTE.CHAT}/${API_ENDPOINT.UNREACT_TO_GROUP_MESSAGE}`
+  UNREACT_TO_GROUP_MESSAGE = `${API_ROUTE.CHAT}/${API_ENDPOINT.UNREACT_TO_GROUP_MESSAGE}`,
+  DELETE_GROUP_MESSAGE = `${API_ROUTE.CHAT}/${API_ENDPOINT.DELETE_GROUP_MESSAGE}`,
+  SET_PIN_FOR_GROUP = `${API_ROUTE.CHAT}/${API_ENDPOINT.SET_PIN_FOR_GROUP}`,
+  SET_ARCHIVE_FOR_GROUP = `${API_ROUTE.CHAT}/${API_ENDPOINT.SET_ARCHIVE_FOR_GROUP}`,
+  SET_MUTE_FOR_GROUP = `${API_ROUTE.CHAT}/${API_ENDPOINT.SET_MUTE_FOR_GROUP}`,
+  LEAVE_GROUP = `${API_ROUTE.CHAT}/${API_ENDPOINT.LEAVE_GROUP}`,
+  ADD_TO_GROUP = `${API_ROUTE.CHAT}/${API_ENDPOINT.ADD_TO_GROUP}`
 }
 
 export type BaseAxiosError = AxiosError;