Bladeren bron

blocked list & timeout refresh

Viktoriia 8 maanden geleden
bovenliggende
commit
9da3311d04

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

@@ -98,6 +98,15 @@ export interface PostSetSettings {
   conversation_with_user: number;
 }
 
+export interface PostGetBlockedReturn extends ResponseType {
+  blocked: {
+    id: number;
+    first_name: string;
+    last_name: string;
+    avatar: string | null;
+  }[];
+}
+
 export const chatApi = {
   searchUsers: (token: string, search: string) =>
     request.postForm<PostSearchUsersReturn>(API.SEARCH_USERS, { token, search }),
@@ -130,5 +139,6 @@ export const chatApi = {
   setMute: (data: PostSetSettings) => request.postForm<ResponseType>(API.SET_MUTE, data),
   deleteChat: (data: PostDeleteChat) => request.postForm<ResponseType>(API.DELETE_CHAT, data),
   unreactToMessage: (data: PostUnreactToMessage) =>
-    request.postForm<ResponseType>(API.UNREACT_TO_MESSAGE, data)
+    request.postForm<ResponseType>(API.UNREACT_TO_MESSAGE, data),
+  getBlocked: (token: string) => request.postForm<PostGetBlockedReturn>(API.GET_BLOCKED, { token })
 };

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

@@ -17,5 +17,6 @@ export const chatQueryKeys = {
   setBlock: () => ['setBlock'] as const,
   setMute: () => ['setMute'] as const,
   deleteChat: () => ['deleteChat'] as const,
-  unreactToMessage: () => ['unreactToMessage'] as const
+  unreactToMessage: () => ['unreactToMessage'] as const,
+  getBlocked: (token: string) => ['getBlocked', token] as const
 };

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

@@ -12,3 +12,4 @@ export * from './use-post-set-mute';
 export * from './use-post-set-block';
 export * from './use-post-delete-conversation';
 export * from './use-post-unreact-to-message';
+export * from './use-post-get-blocked';

+ 17 - 0
src/modules/api/chat/queries/use-post-get-blocked.tsx

@@ -0,0 +1,17 @@
+import { useQuery } from '@tanstack/react-query';
+
+import { chatQueryKeys } from '../chat-query-keys';
+import { chatApi, type PostGetBlockedReturn } from '../chat-api';
+
+import type { BaseAxiosError } from '../../../../types';
+
+export const usePostGetBlockedQuery = (token: string, enabled: boolean) => {
+  return useQuery<PostGetBlockedReturn, BaseAxiosError>({
+    queryKey: chatQueryKeys.getBlocked(token),
+    queryFn: async () => {
+      const response = await chatApi.getBlocked(token);
+      return response.data;
+    },
+    enabled
+  });
+};

+ 3 - 0
src/screens/InAppScreens/MessagesScreen/Components/MoreModal.tsx

@@ -27,6 +27,7 @@ const MoreModal = () => {
     | (ChatProps & {
         token: string;
         refetch: () => void;
+        refetchBlocked: () => void;
       })
     | null
   >(null);
@@ -41,6 +42,7 @@ const MoreModal = () => {
       | (ChatProps & {
           token: string;
           refetch: () => void;
+          refetchBlocked: () => void;
         })
       | null
   ) => {
@@ -80,6 +82,7 @@ const MoreModal = () => {
         });
 
         chatData.refetch();
+        chatData.refetchBlocked();
       }
     });
 

+ 105 - 0
src/screens/InAppScreens/MessagesScreen/Components/SwipeableBlockedRow.tsx

@@ -0,0 +1,105 @@
+import React, { PropsWithChildren, useRef } from 'react';
+import { Animated, StyleSheet, Text, View } from 'react-native';
+import { RectButton, Swipeable } from 'react-native-gesture-handler';
+import { Colors } from 'src/theme';
+import { getFontSize } from 'src/utils';
+
+import { Blocked } from '../types';
+import { usePostSetBlockMutation } from '@api/chat';
+import BanIcon from 'assets/icons/messages/ban.svg';
+
+interface AppleStyleSwipeableRowProps extends PropsWithChildren<unknown> {
+  data: Blocked;
+  token: string;
+  onRowOpen: (ref: any) => void;
+  refetchBlocked: () => void;
+}
+
+const SwipeableBlockedRow: React.FC<AppleStyleSwipeableRowProps> = ({
+  children,
+  data,
+  token,
+  onRowOpen,
+  refetchBlocked
+}) => {
+  const swipeableRow = useRef<Swipeable>(null);
+  const { mutateAsync: unBlockUser } = usePostSetBlockMutation();
+
+  const close = () => {
+    swipeableRow.current?.close();
+  };
+
+  const renderRightAction = (
+    text: string,
+    color: string,
+    x: number,
+    progress: Animated.AnimatedInterpolation<number>
+  ) => {
+    const trans = progress.interpolate({
+      inputRange: [0, 1],
+      outputRange: [x, 0]
+    });
+
+    const pressHandler = async () => {
+      close();
+      await unBlockUser({
+        token,
+        value: 0,
+        conversation_with_user: data.id
+      });
+      refetchBlocked();
+    };
+
+    return (
+      <Animated.View style={{ flex: 1, transform: [{ translateX: trans }] }}>
+        <RectButton style={[styles.rightAction, { backgroundColor: color }]} onPress={pressHandler}>
+          <BanIcon height={18} width={18} fill={Colors.WHITE} />
+          <Text style={styles.actionText}>{text}</Text>
+        </RectButton>
+      </Animated.View>
+    );
+  };
+
+  const renderRightActions = (progress: Animated.AnimatedInterpolation<number>) => (
+    <View
+      style={{
+        width: 84,
+        flexDirection: 'row'
+      }}
+    >
+      {renderRightAction('Unblock', Colors.RED, 84, progress)}
+    </View>
+  );
+
+  return (
+    <Swipeable
+      ref={swipeableRow}
+      friction={2}
+      enableTrackpadTwoFingerGesture
+      rightThreshold={40}
+      renderRightActions={renderRightActions}
+      onSwipeableOpenStartDrag={() => {
+        onRowOpen(swipeableRow.current);
+      }}
+    >
+      {children}
+    </Swipeable>
+  );
+};
+
+const styles = StyleSheet.create({
+  actionText: {
+    color: Colors.WHITE,
+    fontSize: getFontSize(12),
+    fontWeight: '600',
+    backgroundColor: 'transparent'
+  },
+  rightAction: {
+    alignItems: 'center',
+    flex: 1,
+    justifyContent: 'center',
+    gap: 8
+  }
+});
+
+export default SwipeableBlockedRow;

+ 4 - 1
src/screens/InAppScreens/MessagesScreen/Components/SwipeableRow.tsx

@@ -18,6 +18,7 @@ interface AppleStyleSwipeableRowProps extends PropsWithChildren<unknown> {
   token: string;
   onRowOpen: (ref: any) => void;
   refetch: () => void;
+  refetchBlocked: () => void;
 }
 
 const SwipeableRow: React.FC<AppleStyleSwipeableRowProps> = ({
@@ -25,7 +26,8 @@ const SwipeableRow: React.FC<AppleStyleSwipeableRowProps> = ({
   chat,
   token,
   onRowOpen,
-  refetch
+  refetch,
+  refetchBlocked
 }) => {
   const swipeableRow = useRef<Swipeable>(null);
   const { setSelectedChat } = useChatStore();
@@ -60,6 +62,7 @@ const SwipeableRow: React.FC<AppleStyleSwipeableRowProps> = ({
             muted: chat.muted,
             token: token,
             refetch,
+            refetchBlocked
           } as any
         });
       } else {

+ 108 - 17
src/screens/InAppScreens/MessagesScreen/index.tsx

@@ -23,8 +23,8 @@ import { SheetManager } from 'react-native-actions-sheet';
 import MoreModal from './Components/MoreModal';
 import SearchIcon from 'assets/icons/search.svg';
 import { storage, StoreType } from 'src/storage';
-import { usePostGetChatsListQuery } from '@api/chat';
-import { Chat } from './types';
+import { usePostGetBlockedQuery, usePostGetChatsListQuery } from '@api/chat';
+import { Blocked, Chat } from './types';
 
 import PinIcon from 'assets/icons/messages/pin.svg';
 import { formatDate } from './utils';
@@ -32,6 +32,8 @@ import { routes } from './constants';
 import { styles } from './styles';
 import { useChatStore } from 'src/stores/chatStore';
 import BellSlashIcon from 'assets/icons/messages/bell-slash.svg';
+import BanIcon from 'assets/icons/messages/ban.svg';
+import SwipeableBlockedRow from './Components/SwipeableBlockedRow';
 
 const MessagesScreen = () => {
   const navigation = useNavigation();
@@ -39,12 +41,15 @@ const MessagesScreen = () => {
   const [chats, setChats] = useState<Chat[]>([]);
   const [index, setIndex] = useState(0);
   const { data: chatsData, refetch } = usePostGetChatsListQuery(token, index === 2 ? 1 : 0, true);
+  const { data: blockedData, refetch: refetchBlocked } = usePostGetBlockedQuery(token, true);
+  const [blocked, setBlocked] = useState<Blocked[]>([]);
 
   const [filteredChats, setFilteredChats] = useState<{
     all: Chat[];
     unread: Chat[];
     archived: Chat[];
-  }>({ all: [], unread: [], archived: [] });
+    blocked: Blocked[];
+  }>({ all: [], unread: [], archived: [], blocked: [] });
   const [search, setSearch] = useState('');
   const openRowRef = useRef<any>(null);
   const { isWarningModalVisible, setIsWarningModalVisible } = useChatStore();
@@ -75,15 +80,34 @@ const MessagesScreen = () => {
     }
   }, [chatsData]);
 
+  useEffect(() => {
+    if (blockedData && blockedData.blocked) {
+      setBlocked(blockedData.blocked);
+    }
+  }, [blockedData]);
+
   useFocusEffect(
     useCallback(() => {
       refetch();
     }, [])
   );
 
+  useEffect(() => {
+    const intervalId = setInterval(() => {
+      refetch();
+    }, 5000);
+
+    return () => clearInterval(intervalId);
+  }, [refetch]);
+
   const filterChatsByTab = () => {
     let filteredList = chats;
 
+    if (index === 3) {
+      setFilteredChats((prev) => ({ ...prev, blocked }));
+      return;
+    }
+
     if (index === 1) {
       filteredList = chats.filter((chat) => chat.unread_count > 0);
     }
@@ -103,7 +127,7 @@ const MessagesScreen = () => {
 
   useEffect(() => {
     filterChatsByTab();
-  }, [chats, index]);
+  }, [chats, index, blocked]);
 
   const searchFilter = (text: string) => {
     if (text) {
@@ -135,6 +159,7 @@ const MessagesScreen = () => {
         token={token}
         onRowOpen={handleRowOpen}
         refetch={refetch}
+        refetchBlocked={refetchBlocked}
       >
         <TouchableHighlight
           activeOpacity={0.8}
@@ -205,6 +230,58 @@ const MessagesScreen = () => {
     );
   };
 
+  const renderBlockedItem = ({ item }: { item: Blocked }) => {
+    return (
+      <SwipeableBlockedRow
+        data={{
+          id: item.id,
+          first_name: item.first_name,
+          last_name: item.last_name,
+          avatar: item.avatar
+        }}
+        token={token}
+        onRowOpen={handleRowOpen}
+        refetchBlocked={refetchBlocked}
+      >
+        <TouchableHighlight
+          activeOpacity={0.8}
+          onPress={() =>
+            navigation.navigate(
+              ...([NAVIGATION_PAGES.PUBLIC_PROFILE_VIEW, { userId: item.id }] as never)
+            )
+          }
+          underlayColor={Colors.FILL_LIGHT}
+        >
+          <View style={[styles.chatItem, { alignItems: 'center' }]}>
+            {item.avatar ? (
+              <Image
+                source={{ uri: API_HOST + item.avatar }}
+                style={[styles.avatar, { width: 30, height: 30, borderRadius: 15 }]}
+              />
+            ) : (
+              <AvatarWithInitials
+                text={item.first_name[0] + item.last_name[0]}
+                flag={API_HOST + item?.flag}
+                size={30}
+                fontSize={12}
+              />
+            )}
+
+            <View style={{ flex: 1, gap: 6 }}>
+              <View style={[styles.rowContainer, { alignItems: 'center' }]}>
+                <Text style={styles.chatName}>{item.first_name + ' ' + item.last_name}</Text>
+
+                <View style={{ flexDirection: 'row', alignItems: 'center', gap: 6 }}>
+                  <BanIcon height={12} fill={Colors.RED} />
+                </View>
+              </View>
+            </View>
+          </View>
+        </TouchableHighlight>
+      </SwipeableBlockedRow>
+    );
+  };
+
   return (
     <PageWrapper style={{ flex: 1, marginLeft: 0, marginRight: 0, gap: 12 }}>
       <View style={styles.header}>
@@ -234,19 +311,33 @@ const MessagesScreen = () => {
         setIndex={setIndex}
         routes={routes}
         tabBarStyle={{ paddingHorizontal: '4%' }}
-        renderScene={({ route }: { route: { key: keyof typeof filteredChats } }) => (
-          <FlashList
-            viewabilityConfig={{
-              waitForInteraction: true,
-              itemVisiblePercentThreshold: 50,
-              minimumViewTime: 1000
-            }}
-            data={filteredChats[route.key]}
-            renderItem={renderChatItem}
-            keyExtractor={(item, index) => `${item.uid}-${index}`}
-            estimatedItemSize={78}
-          />
-        )}
+        renderScene={({ route }: { route: { key: keyof typeof filteredChats } }) =>
+          route.key === 'blocked' ? (
+            <FlashList
+              viewabilityConfig={{
+                waitForInteraction: true,
+                itemVisiblePercentThreshold: 50,
+                minimumViewTime: 1000
+              }}
+              data={filteredChats[route.key]}
+              renderItem={renderBlockedItem}
+              keyExtractor={(item, index) => `${item.id}-${index}`}
+              estimatedItemSize={50}
+            />
+          ) : (
+            <FlashList
+              viewabilityConfig={{
+                waitForInteraction: true,
+                itemVisiblePercentThreshold: 50,
+                minimumViewTime: 1000
+              }}
+              data={filteredChats[route.key]}
+              renderItem={renderChatItem}
+              keyExtractor={(item, index) => `${item.uid}-${index}`}
+              estimatedItemSize={78}
+            />
+          )
+        }
       />
 
       <SearchModal />

+ 7 - 0
src/screens/InAppScreens/MessagesScreen/types.ts

@@ -19,6 +19,13 @@ export type Chat = {
   muted: 0 | 1;
 };
 
+export type Blocked = {
+  id: number;
+  first_name: string;
+  last_name: string;
+  avatar: string | null;
+};
+
 export type ChatProps = {
   uid: number;
   name: string;

+ 4 - 2
src/types/api.ts

@@ -138,7 +138,8 @@ export enum API_ENDPOINT {
   SET_BLOCK = 'set-block',
   SET_MUTE = 'set-mute',
   DELETE_CHAT = 'delete-conversation',
-  UNREACT_TO_MESSAGE = 'unreact-to-message'
+  UNREACT_TO_MESSAGE = 'unreact-to-message',
+  GET_BLOCKED = 'get-blocked'
 }
 
 export enum API {
@@ -253,7 +254,8 @@ export enum API {
   SET_BLOCK = `${API_ROUTE.CHAT}/${API_ENDPOINT.SET_BLOCK}`,
   SET_MUTE = `${API_ROUTE.CHAT}/${API_ENDPOINT.SET_MUTE}`,
   DELETE_CHAT = `${API_ROUTE.CHAT}/${API_ENDPOINT.DELETE_CHAT}`,
-  UNREACT_TO_MESSAGE = `${API_ROUTE.CHAT}/${API_ENDPOINT.UNREACT_TO_MESSAGE}`
+  UNREACT_TO_MESSAGE = `${API_ROUTE.CHAT}/${API_ENDPOINT.UNREACT_TO_MESSAGE}`,
+  GET_BLOCKED = `${API_ROUTE.CHAT}/${API_ENDPOINT.GET_BLOCKED}`
 }
 
 export type BaseAxiosError = AxiosError;