|
@@ -0,0 +1,384 @@
|
|
|
+import React, { useCallback, useEffect, useState } from 'react';
|
|
|
+import { View, StyleSheet, TouchableOpacity, Text, Image, ActivityIndicator } from 'react-native';
|
|
|
+import ActionSheet, { SheetManager } from 'react-native-actions-sheet';
|
|
|
+import { useNavigation } from '@react-navigation/native';
|
|
|
+import { chatStyles } from './styles';
|
|
|
+import { Colors } from 'src/theme';
|
|
|
+import { AvatarWithInitials, Input } from 'src/components';
|
|
|
+import { API_HOST } from 'src/constants';
|
|
|
+import { NAVIGATION_PAGES } from 'src/types';
|
|
|
+import { FlashList } from '@shopify/flash-list';
|
|
|
+
|
|
|
+import SearchIcon from 'assets/icons/search.svg';
|
|
|
+import { getFontSize } from 'src/utils';
|
|
|
+import { useGetMyFriendsMutation } from '@api/friends';
|
|
|
+import { usePostAddToGroupMutation, usePostSearchUsers } from '@api/chat';
|
|
|
+import { storage, StoreType } from 'src/storage';
|
|
|
+
|
|
|
+import CheckSvg from 'assets/icons/travels-screens/circle-check.svg';
|
|
|
+import CloseIcon from 'assets/icons/close.svg';
|
|
|
+
|
|
|
+const AddNomadsModal = () => {
|
|
|
+ const token = storage.get('token', StoreType.STRING) as string;
|
|
|
+ const [searchQuery, setSearchQuery] = useState('');
|
|
|
+ const [data, setData] = useState<any>(null);
|
|
|
+ const [selectedUsers, setSelectedUsers] = useState<any[]>([]);
|
|
|
+
|
|
|
+ const [friends, setFriends] = useState<any[]>([]);
|
|
|
+ const [page, setPage] = useState(0);
|
|
|
+ const [isLoadingMore, setIsLoadingMore] = useState(false);
|
|
|
+ const [maxPages, setMaxPages] = useState(1);
|
|
|
+ const [isLoading, setIsLoading] = useState(false);
|
|
|
+
|
|
|
+ const { mutateAsync: getMyFriends } = useGetMyFriendsMutation();
|
|
|
+ const { mutate: addToGroup } = usePostAddToGroupMutation();
|
|
|
+ const { data: searchResult, isFetching } = usePostSearchUsers(
|
|
|
+ token,
|
|
|
+ searchQuery,
|
|
|
+ searchQuery.length > 1
|
|
|
+ );
|
|
|
+
|
|
|
+ const handleSheetOpen = (payload: any) => {
|
|
|
+ setData(payload);
|
|
|
+ };
|
|
|
+
|
|
|
+ useEffect(() => {
|
|
|
+ getMyFriends(
|
|
|
+ {
|
|
|
+ token,
|
|
|
+ type: 'friends',
|
|
|
+ page: 0
|
|
|
+ },
|
|
|
+ {
|
|
|
+ onSuccess: (data) => {
|
|
|
+ setFriends(data.friends.users);
|
|
|
+ setMaxPages(data.friends.max_pages);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ );
|
|
|
+ }, []);
|
|
|
+
|
|
|
+ useEffect(() => {
|
|
|
+ const getNextPage = async () => {
|
|
|
+ await getMyFriends(
|
|
|
+ {
|
|
|
+ token,
|
|
|
+ type: 'friends',
|
|
|
+ page
|
|
|
+ },
|
|
|
+ {
|
|
|
+ onSuccess: (data) => {
|
|
|
+ setIsLoadingMore(false);
|
|
|
+ setFriends((prevState) => [...prevState, ...data['friends'].users]);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ );
|
|
|
+ };
|
|
|
+ if (page !== 0) {
|
|
|
+ getNextPage();
|
|
|
+ }
|
|
|
+ }, [page]);
|
|
|
+
|
|
|
+ const handleEndReached = useCallback(() => {
|
|
|
+ if (friends && page < maxPages && !isLoadingMore && maxPages > 1) {
|
|
|
+ setIsLoadingMore(true);
|
|
|
+ setPage((prevPage) => prevPage + 1);
|
|
|
+ }
|
|
|
+ }, [friends, page]);
|
|
|
+
|
|
|
+ const toggleUserSelection = (user: any) => {
|
|
|
+ const isSelected = selectedUsers.some((selected) => selected.user_id === user.user_id);
|
|
|
+ if (isSelected) {
|
|
|
+ setSelectedUsers((prev) => prev.filter((selected) => selected.user_id !== user.user_id));
|
|
|
+ } else {
|
|
|
+ setSelectedUsers((prev) => [...prev, user]);
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ const handleAddToGroup = async () => {
|
|
|
+ if (!data) return;
|
|
|
+ setIsLoadingMore(true);
|
|
|
+
|
|
|
+ await Promise.all(
|
|
|
+ selectedUsers.map((user) =>
|
|
|
+ addToGroup(
|
|
|
+ {
|
|
|
+ token,
|
|
|
+ uid: user.user_id,
|
|
|
+ group_token: data?.groupToken
|
|
|
+ },
|
|
|
+ {
|
|
|
+ onSuccess: () => {
|
|
|
+ data && data?.refetch();
|
|
|
+ data && data?.refetchMembers();
|
|
|
+ },
|
|
|
+ onError: () => {
|
|
|
+ setIsLoading(false);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ )
|
|
|
+ )
|
|
|
+ );
|
|
|
+
|
|
|
+ setIsLoading(false);
|
|
|
+ SheetManager.hide('add-nomads-modal');
|
|
|
+ setSelectedUsers([]);
|
|
|
+ setSearchQuery('');
|
|
|
+ };
|
|
|
+
|
|
|
+ const renderSelectedUser = ({ item }: { item: any }) => (
|
|
|
+ <View style={styles.selectedUserContainer}>
|
|
|
+ <TouchableOpacity
|
|
|
+ onPress={() =>
|
|
|
+ setSelectedUsers((prev) => prev.filter((selected) => selected.user_id !== item.user_id))
|
|
|
+ }
|
|
|
+ style={styles.removeIcon}
|
|
|
+ >
|
|
|
+ <CloseIcon width={10} height={10} fill={Colors.WHITE} />
|
|
|
+ </TouchableOpacity>
|
|
|
+ {item.avatar ? (
|
|
|
+ <Image source={{ uri: API_HOST + item.avatar }} style={styles.selectedAvatar} />
|
|
|
+ ) : (
|
|
|
+ <AvatarWithInitials
|
|
|
+ text={`${item.first_name[0] ?? ''}${item.last_name[0] ?? ''}`}
|
|
|
+ flag={API_HOST + item.flag1}
|
|
|
+ size={60}
|
|
|
+ fontSize={21}
|
|
|
+ borderColor={Colors.LIGHT_GRAY}
|
|
|
+ borderWidth={1}
|
|
|
+ />
|
|
|
+ )}
|
|
|
+ </View>
|
|
|
+ );
|
|
|
+
|
|
|
+ const renderUserItem = ({ item }: { item: any }) => {
|
|
|
+ const isSelected = selectedUsers.some((selected) => selected.user_id === item.user_id);
|
|
|
+
|
|
|
+ return (
|
|
|
+ <TouchableOpacity style={styles.userItem} onPress={() => toggleUserSelection(item)}>
|
|
|
+ {item.avatar ? (
|
|
|
+ <Image source={{ uri: API_HOST + item.avatar }} style={chatStyles.avatar} />
|
|
|
+ ) : (
|
|
|
+ <AvatarWithInitials
|
|
|
+ text={`${item.first_name[0] ?? ''}${item.last_name[0] ?? ''}`}
|
|
|
+ flag={API_HOST + item?.flag1}
|
|
|
+ size={36}
|
|
|
+ fontSize={12}
|
|
|
+ borderColor={Colors.LIGHT_GRAY}
|
|
|
+ borderWidth={1}
|
|
|
+ />
|
|
|
+ )}
|
|
|
+
|
|
|
+ <View style={chatStyles.textContainer}>
|
|
|
+ <Text style={chatStyles.name}>
|
|
|
+ {item.first_name} {item.last_name}
|
|
|
+ </Text>
|
|
|
+ </View>
|
|
|
+ {/* {item.admin === 1 && (
|
|
|
+ <Text
|
|
|
+ style={{
|
|
|
+ fontSize: getFontSize(10),
|
|
|
+ fontWeight: '600',
|
|
|
+ color: Colors.LIGHT_GRAY
|
|
|
+ }}
|
|
|
+ >
|
|
|
+ Admin
|
|
|
+ </Text>
|
|
|
+ )} */}
|
|
|
+ <View style={styles.unselectedCircle}>
|
|
|
+ {isSelected && <CheckSvg fill={Colors.DARK_BLUE} height={20} width={20} />}
|
|
|
+ </View>
|
|
|
+ </TouchableOpacity>
|
|
|
+ );
|
|
|
+ };
|
|
|
+
|
|
|
+ return (
|
|
|
+ <ActionSheet
|
|
|
+ id="add-nomads-modal"
|
|
|
+ containerStyle={styles.sheetContainer}
|
|
|
+ defaultOverlayOpacity={0.5}
|
|
|
+ closeOnTouchBackdrop={true}
|
|
|
+ keyboardHandlerEnabled={true}
|
|
|
+ onBeforeShow={(sheetRef) => {
|
|
|
+ const payload = sheetRef || null;
|
|
|
+ handleSheetOpen(payload);
|
|
|
+ }}
|
|
|
+ >
|
|
|
+ <View style={styles.container}>
|
|
|
+ <View style={chatStyles.header}>
|
|
|
+ <TouchableOpacity
|
|
|
+ style={{
|
|
|
+ paddingTop: 16,
|
|
|
+ paddingBottom: 6,
|
|
|
+ paddingHorizontal: 6
|
|
|
+ }}
|
|
|
+ onPress={() => {
|
|
|
+ SheetManager.hide('add-nomads-modal');
|
|
|
+ setSelectedUsers([]);
|
|
|
+ setSearchQuery('');
|
|
|
+ }}
|
|
|
+ >
|
|
|
+ <Text style={chatStyles.cancelText}>Cancel</Text>
|
|
|
+ </TouchableOpacity>
|
|
|
+
|
|
|
+ {isLoading ? (
|
|
|
+ <ActivityIndicator size="small" color={Colors.DARK_BLUE} style={{ padding: 10 }} />
|
|
|
+ ) : (
|
|
|
+ <TouchableOpacity
|
|
|
+ onPress={handleAddToGroup}
|
|
|
+ style={{
|
|
|
+ paddingTop: 16,
|
|
|
+ paddingBottom: 6,
|
|
|
+ paddingHorizontal: 6
|
|
|
+ }}
|
|
|
+ >
|
|
|
+ <Text style={chatStyles.headerText}>Save</Text>
|
|
|
+ </TouchableOpacity>
|
|
|
+ )}
|
|
|
+ </View>
|
|
|
+
|
|
|
+ <Input
|
|
|
+ inputMode={'search'}
|
|
|
+ placeholder={'Search nomads'}
|
|
|
+ onChange={(text) => {
|
|
|
+ setSearchQuery(text);
|
|
|
+ }}
|
|
|
+ value={searchQuery}
|
|
|
+ icon={<SearchIcon fill={'#C8C8C8'} width={14} height={14} />}
|
|
|
+ />
|
|
|
+
|
|
|
+ {selectedUsers.length > 0 && (
|
|
|
+ <View style={styles.usersRow}>
|
|
|
+ <FlashList
|
|
|
+ horizontal
|
|
|
+ data={selectedUsers}
|
|
|
+ renderItem={renderSelectedUser}
|
|
|
+ keyExtractor={(item) => item.user_id.toString()}
|
|
|
+ showsHorizontalScrollIndicator={false}
|
|
|
+ contentContainerStyle={styles.selectedUsersList}
|
|
|
+ estimatedItemSize={50}
|
|
|
+ />
|
|
|
+ </View>
|
|
|
+ )}
|
|
|
+
|
|
|
+ {isFetching ? (
|
|
|
+ <ActivityIndicator size="large" color={Colors.DARK_BLUE} />
|
|
|
+ ) : searchQuery ? (
|
|
|
+ <FlashList
|
|
|
+ viewabilityConfig={{
|
|
|
+ waitForInteraction: true,
|
|
|
+ itemVisiblePercentThreshold: 50,
|
|
|
+ minimumViewTime: 1000
|
|
|
+ }}
|
|
|
+ data={searchResult?.data || []}
|
|
|
+ renderItem={renderUserItem}
|
|
|
+ keyExtractor={(item) => item.user_id.toString()}
|
|
|
+ estimatedItemSize={100}
|
|
|
+ extraData={selectedUsers}
|
|
|
+ showsVerticalScrollIndicator={false}
|
|
|
+ refreshing={isFetching}
|
|
|
+ contentContainerStyle={{ paddingBottom: 16 }}
|
|
|
+ />
|
|
|
+ ) : (
|
|
|
+ <FlashList
|
|
|
+ viewabilityConfig={{
|
|
|
+ waitForInteraction: true,
|
|
|
+ itemVisiblePercentThreshold: 50,
|
|
|
+ minimumViewTime: 1000
|
|
|
+ }}
|
|
|
+ data={friends || []}
|
|
|
+ renderItem={renderUserItem}
|
|
|
+ keyExtractor={(item) => item.user_id.toString()}
|
|
|
+ estimatedItemSize={100}
|
|
|
+ extraData={selectedUsers}
|
|
|
+ showsVerticalScrollIndicator={false}
|
|
|
+ refreshing={isFetching}
|
|
|
+ onEndReached={handleEndReached}
|
|
|
+ onEndReachedThreshold={0.2}
|
|
|
+ contentContainerStyle={{ paddingBottom: 16 }}
|
|
|
+ />
|
|
|
+ )}
|
|
|
+ </View>
|
|
|
+ </ActionSheet>
|
|
|
+ );
|
|
|
+};
|
|
|
+
|
|
|
+const styles = StyleSheet.create({
|
|
|
+ container: {
|
|
|
+ backgroundColor: 'white',
|
|
|
+ gap: 16,
|
|
|
+ height: '100%'
|
|
|
+ },
|
|
|
+ sheetContainer: {
|
|
|
+ height: '95%',
|
|
|
+ borderTopLeftRadius: 15,
|
|
|
+ borderTopRightRadius: 15,
|
|
|
+ paddingHorizontal: 16
|
|
|
+ },
|
|
|
+ avatar: {
|
|
|
+ width: 30,
|
|
|
+ height: 30,
|
|
|
+ borderRadius: 15,
|
|
|
+ borderWidth: 1,
|
|
|
+ borderColor: Colors.LIGHT_GRAY
|
|
|
+ },
|
|
|
+ header: {
|
|
|
+ paddingTop: 16,
|
|
|
+ paddingHorizontal: 6,
|
|
|
+ width: 68
|
|
|
+ },
|
|
|
+ userItem: {
|
|
|
+ flexDirection: 'row',
|
|
|
+ alignItems: 'center',
|
|
|
+ paddingVertical: 8,
|
|
|
+ paddingHorizontal: 12,
|
|
|
+ backgroundColor: Colors.FILL_LIGHT,
|
|
|
+ gap: 8,
|
|
|
+ borderRadius: 8,
|
|
|
+ marginBottom: 6
|
|
|
+ },
|
|
|
+ unselectedCircle: {
|
|
|
+ width: 20,
|
|
|
+ height: 20,
|
|
|
+ borderRadius: 10,
|
|
|
+ borderWidth: 1,
|
|
|
+ borderColor: Colors.LIGHT_GRAY,
|
|
|
+ justifyContent: 'center',
|
|
|
+ alignItems: 'center'
|
|
|
+ },
|
|
|
+ usersRow: {
|
|
|
+ backgroundColor: Colors.FILL_LIGHT,
|
|
|
+ borderRadius: 8
|
|
|
+ },
|
|
|
+ selectedUsersList: {
|
|
|
+ paddingHorizontal: 12,
|
|
|
+ paddingVertical: 10
|
|
|
+ },
|
|
|
+ selectedUserContainer: {
|
|
|
+ position: 'relative',
|
|
|
+ marginRight: 12
|
|
|
+ },
|
|
|
+ selectedAvatar: {
|
|
|
+ width: 60,
|
|
|
+ height: 60,
|
|
|
+ borderRadius: 30,
|
|
|
+ borderWidth: 1,
|
|
|
+ borderColor: Colors.LIGHT_GRAY
|
|
|
+ },
|
|
|
+ removeIcon: {
|
|
|
+ position: 'absolute',
|
|
|
+ top: -4,
|
|
|
+ right: -4,
|
|
|
+ width: 22,
|
|
|
+ height: 22,
|
|
|
+ borderRadius: 11,
|
|
|
+ borderWidth: 1,
|
|
|
+ borderColor: Colors.WHITE,
|
|
|
+ backgroundColor: Colors.RED,
|
|
|
+ justifyContent: 'center',
|
|
|
+ alignItems: 'center',
|
|
|
+ zIndex: 1
|
|
|
+ }
|
|
|
+});
|
|
|
+
|
|
|
+export default AddNomadsModal;
|