Viktoriia 1 mesiac pred
rodič
commit
b0d33d4f98

+ 5 - 0
Route.tsx

@@ -103,6 +103,7 @@ import GroupSettingScreen from 'src/screens/InAppScreens/MessagesScreen/GroupSet
 import CreateEventScreen from 'src/screens/InAppScreens/TravelsScreen/CreateEvent';
 import MembersListScreen from 'src/screens/InAppScreens/MessagesScreen/MembersListScreen';
 import AllEventPhotosScreen from 'src/screens/InAppScreens/TravelsScreen/AllEventPhotosScreen';
+import ParticipantsListScreen from 'src/screens/InAppScreens/TravelsScreen/ParticipantsListScreen';
 
 enableScreens();
 
@@ -324,6 +325,10 @@ const Route = () => {
             <ScreenStack.Screen name={NAVIGATION_PAGES.ADD_FIXER} component={AddNewFixerScreen} />
             <ScreenStack.Screen name={NAVIGATION_PAGES.EVENTS} component={EventsScreen} />
             <ScreenStack.Screen name={NAVIGATION_PAGES.EVENT} component={EventScreen} />
+            <ScreenStack.Screen
+              name={NAVIGATION_PAGES.PARTICIPANTS_LIST}
+              component={ParticipantsListScreen}
+            />
             <ScreenStack.Screen
               name={NAVIGATION_PAGES.CREATE_EVENT}
               component={CreateEventScreen}

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

@@ -48,6 +48,7 @@ export interface PostGetGroupSettingsReturn extends ResponseType {
     admin: 0 | 1;
     member_count: number;
     muted: 0 | 1;
+    description_html?: string;
   };
 }
 

+ 7 - 0
src/modules/api/events/events-api.ts

@@ -2,6 +2,7 @@ import { request } from '../../../utils';
 import { API } from '../../../types';
 import { ResponseType } from '../response-type';
 import { AxiosRequestConfig } from 'axios';
+import { User } from '@api/regions';
 
 export interface PostGetEventsListReturn extends ResponseType {
   data: SingleEvent[];
@@ -30,6 +31,8 @@ export type SingleEvent = {
   time?: string;
   time_from?: string;
   time_to?: string;
+  flag: string | null;
+  country: string | null;
 };
 
 export type Participants = {
@@ -67,6 +70,7 @@ export type EventSettings = {
     avatar: string | null;
     nm: number | null;
     un: number | null;
+    flag: string;
   };
   nm_region?: number;
 };
@@ -95,6 +99,7 @@ export type EventData = SingleEvent & {
   event_chat: string | null;
   settings: EventSettings;
   participants_data: Participants[];
+  participants_full_data: User[];
   joined: 0 | 1;
   attachments: EventAttachments[];
   files: EventAttachments[];
@@ -103,6 +108,8 @@ export type EventData = SingleEvent & {
   photo_available: 0 | 1;
   photo_main: 0 | 1;
   photos_left: number;
+  flag: string | null;
+  country: string | null;
 };
 
 export interface PostGetEventReturn extends ResponseType {

+ 175 - 6
src/screens/InAppScreens/MessagesScreen/GroupSettingsScreen/index.tsx

@@ -1,7 +1,23 @@
 import React, { FC, useCallback, useEffect, useState } from 'react';
-import { ScrollView, Text, TouchableOpacity, View, Image, StyleSheet } from 'react-native';
-import { NavigationProp, useFocusEffect } from '@react-navigation/native';
+import {
+  ScrollView,
+  Text,
+  TouchableOpacity,
+  View,
+  Image,
+  StyleSheet,
+  Linking,
+  useWindowDimensions
+} from 'react-native';
+import {
+  CommonActions,
+  NavigationProp,
+  useFocusEffect,
+  useNavigation
+} from '@react-navigation/native';
 import ImageView from 'react-native-image-viewing';
+import RenderHtml, { defaultHTMLElementModels } from 'react-native-render-html';
+
 import { storage, StoreType } from 'src/storage';
 import {
   PageWrapper,
@@ -219,7 +235,23 @@ const GroupSettingScreen: FC<Props> = ({ navigation, route }) => {
             </Text>
           </View>
 
-          {data.settings.description && (
+          {data.settings?.description_html ? (
+            <View>
+              <Text
+                style={{
+                  color: Colors.DARK_BLUE,
+                  fontSize: getFontSize(14),
+                  fontFamily: 'redhat-700',
+                  marginBottom: 8
+                }}
+              >
+                Description
+              </Text>
+              <View style={{ borderRadius: 6, backgroundColor: Colors.FILL_LIGHT, padding: 10 }}>
+                <WebDisplay html={data.settings.description_html} />
+              </View>
+            </View>
+          ) : data.settings.description ? (
             <Input
               editable={false}
               value={data.settings.description}
@@ -227,7 +259,7 @@ const GroupSettingScreen: FC<Props> = ({ navigation, route }) => {
               multiline
               height={58}
             />
-          )}
+          ) : null}
 
           <View style={{ gap: 8, marginBottom: 16 }}>
             <View
@@ -258,7 +290,15 @@ const GroupSettingScreen: FC<Props> = ({ navigation, route }) => {
                   ? members.settings.slice(0, 4)
                   : (members.settings ?? [])
                 ).map((member, index) => (
-                  <View key={index} style={styles.userItem}>
+                  <TouchableOpacity
+                    key={index}
+                    onPress={() =>
+                      navigation.navigate(
+                        ...([NAVIGATION_PAGES.PUBLIC_PROFILE_VIEW, { userId: member.uid }] as never)
+                      )
+                    }
+                    style={styles.userItem}
+                  >
                     {member.avatar ? (
                       <Image source={{ uri: API_HOST + member.avatar }} style={styles.avatar} />
                     ) : (
@@ -314,7 +354,7 @@ const GroupSettingScreen: FC<Props> = ({ navigation, route }) => {
                         <DotsIcon />
                       </TouchableOpacity>
                     )}
-                  </View>
+                  </TouchableOpacity>
                 ))}
                 {data.settings.member_count > 4 ? (
                   <TouchableOpacity
@@ -398,6 +438,135 @@ const GroupSettingScreen: FC<Props> = ({ navigation, route }) => {
   );
 };
 
+const WebDisplay = React.memo(function WebDisplay({ html }: { html: string }) {
+  const { width: windowWidth } = useWindowDimensions();
+  const contentWidth = windowWidth * 0.9;
+
+  const navigation = useNavigation();
+  const token = storage.get('token', StoreType.STRING) as string;
+
+  const processedHtml = React.useMemo(() => {
+    let updatedHtml = html;
+    const hrefRegex = /href="((?!http)[^"]+)"/g;
+
+    const normalizePath = (path: string): string => {
+      const segments = path.split('/').filter(Boolean);
+      const resolved: string[] = [];
+
+      for (const segment of segments) {
+        if (segment === '..') resolved.pop();
+        else if (segment !== '.') resolved.push(segment);
+      }
+
+      return '/' + resolved.join('/');
+    };
+
+    updatedHtml = updatedHtml.replace(hrefRegex, (match, rawPath) => {
+      const normalizedPath = normalizePath(rawPath);
+      const fullUrl = `${API_HOST}${normalizedPath}`;
+      if (normalizedPath.includes('shop')) {
+        const separator = fullUrl.includes('?') ? '&' : '?';
+        return `href="${fullUrl}${separator}token=${encodeURIComponent(token)}"`;
+      }
+      return `href="${fullUrl}"`;
+    });
+
+    return updatedHtml;
+  }, [html, token]);
+
+  return (
+    <RenderHtml
+      contentWidth={contentWidth}
+      source={{ html: processedHtml }}
+      renderersProps={{
+        a: {
+          onPress: (_event, href) => {
+            if (!href) return;
+
+            const eventMatch = href.match(/event\/([^/?#]+)/);
+            if (eventMatch?.[1]) {
+              navigation.dispatch(
+                CommonActions.reset({
+                  index: 1,
+                  routes: [
+                    {
+                      name: 'DrawerApp',
+                      state: {
+                        routes: [
+                          {
+                            name: NAVIGATION_PAGES.IN_APP_TRAVELS_TAB,
+                            state: {
+                              routes: [
+                                { name: NAVIGATION_PAGES.EVENTS },
+                                {
+                                  name: NAVIGATION_PAGES.EVENT,
+                                  params: { url: eventMatch[1] }
+                                }
+                              ]
+                            }
+                          }
+                        ]
+                      }
+                    }
+                  ]
+                })
+              );
+              return;
+            }
+
+            if (href.includes(`${API_HOST}/map/`)) {
+              const lonMatch = href.match(/lon=([0-9.-]+)/);
+              const latMatch = href.match(/lat=([0-9.-]+)/);
+              if (lonMatch && latMatch) {
+                const lon = parseFloat(lonMatch[1]);
+                const lat = parseFloat(latMatch[1]);
+
+                navigation.dispatch(
+                  CommonActions.reset({
+                    index: 1,
+                    routes: [
+                      {
+                        name: 'DrawerApp',
+                        state: {
+                          routes: [
+                            {
+                              name: NAVIGATION_PAGES.IN_APP_MAP_TAB,
+                              state: {
+                                routes: [
+                                  { name: NAVIGATION_PAGES.EVENTS },
+                                  {
+                                    name: NAVIGATION_PAGES.MAP_TAB,
+                                    params: { lon, lat }
+                                  }
+                                ]
+                              }
+                            }
+                          ]
+                        }
+                      }
+                    ]
+                  })
+                );
+                return;
+              }
+            }
+
+            Linking.openURL(href);
+          }
+        }
+      }}
+      tagsStyles={{
+        a: {
+          color: '#ED9334',
+          textDecorationLine: 'none',
+          fontWeight: '500'
+        }
+      }}
+      customHTMLElementModels={defaultHTMLElementModels}
+    />
+  );
+});
+
 const styles = StyleSheet.create({
   photoContainer: {
     alignItems: 'center',

+ 10 - 2
src/screens/InAppScreens/MessagesScreen/MembersListScreen/index.tsx

@@ -14,6 +14,7 @@ import { FlashList } from '@shopify/flash-list';
 
 import SearchIcon from 'assets/icons/search.svg';
 import DotsIcon from 'assets/icons/messages/dots-vertical.svg';
+import { NAVIGATION_PAGES } from 'src/types';
 
 type Props = {
   navigation: NavigationProp<any>;
@@ -72,7 +73,14 @@ const MembersListScreen: FC<Props> = ({ navigation, route }) => {
   };
 
   const renderItem = ({ item }: { item: any }) => (
-    <View style={styles.userItem}>
+    <TouchableOpacity
+      onPress={() =>
+        navigation.navigate(
+          ...([NAVIGATION_PAGES.PUBLIC_PROFILE_VIEW, { userId: item.uid }] as never)
+        )
+      }
+      style={styles.userItem}
+    >
       {item.avatar ? (
         <Image source={{ uri: API_HOST + item.avatar }} style={chatStyles.avatar} />
       ) : (
@@ -122,7 +130,7 @@ const MembersListScreen: FC<Props> = ({ navigation, route }) => {
           <DotsIcon />
         </TouchableOpacity>
       )}
-    </View>
+    </TouchableOpacity>
   );
 
   return (

+ 164 - 60
src/screens/InAppScreens/TravelsScreen/EventScreen/index.tsx

@@ -68,10 +68,10 @@ import { Dropdown } from 'react-native-searchable-dropdown-kj';
 import { KeyboardAwareScrollView } from 'react-native-keyboard-aware-scroll-view';
 import Tooltip from 'react-native-walkthrough-tooltip';
 import WebView from 'react-native-webview';
-import ClockIcon from 'assets/icons/events/clock.svg';
 import { PhotosData } from '../../MapScreen/RegionViewScreen/types';
 import ImageCarousel from '../../MapScreen/RegionViewScreen/ImageCarousel';
 import EditSvg from 'assets/icons/events/edit.svg';
+import ChatIcon from 'assets/icons/bottom-navigation/messages.svg';
 
 type TempFile = {
   filetype: string;
@@ -126,6 +126,7 @@ const EventScreen = ({ route }: { route: any }) => {
   const [isImageModalVisible, setIsImageModalVisible] = useState(false);
   const [currentImageIndex, setCurrentImageIndex] = useState(0);
   const [nmId, setNmId] = useState<number | null>(null);
+  const [tooltipVisible, setTooltipVisible] = useState(false);
 
   const { data: photosData } = useGetPhotosForRegionQuery(nmId ?? 0, nmId !== null);
 
@@ -621,7 +622,6 @@ const EventScreen = ({ route }: { route: any }) => {
           </Text>
           {event.time_from && event.time_to ? (
             <View style={{ flexDirection: 'row', alignItems: 'center', gap: 4 }}>
-              <ClockIcon fill={Colors.DARK_BLUE} height={12} width={12} />
               <Text
                 style={{
                   fontSize: getFontSize(12),
@@ -634,7 +634,6 @@ const EventScreen = ({ route }: { route: any }) => {
             </View>
           ) : event.time ? (
             <View style={{ flexDirection: 'row', alignItems: 'center', gap: 4 }}>
-              <ClockIcon fill={Colors.DARK_BLUE} height={12} width={12} />
               <Text
                 style={{
                   fontSize: getFontSize(12),
@@ -822,12 +821,32 @@ const EventScreen = ({ route }: { route: any }) => {
 
             <View style={{ flexDirection: 'row', alignItems: 'center', gap: 4 }}>
               <View style={{ flexDirection: 'row', alignItems: 'center', gap: 4, flex: 1 }}>
-                <CalendarIcon fill={Colors.DARK_BLUE} height={20} width={20} />
-                {formatEventDate(event)}
-              </View>
-
-              <View style={{ flexDirection: 'row', alignItems: 'center', gap: 4, flex: 1 }}>
-                <EarthIcon fill={Colors.DARK_BLUE} height={20} width={20} />
+                {event.settings.type === 1 && event?.flag ? (
+                  <Tooltip
+                    isVisible={tooltipVisible}
+                    content={<Text>{event.country}</Text>}
+                    contentStyle={{ backgroundColor: Colors.FILL_LIGHT }}
+                    placement="top"
+                    onClose={() => setTooltipVisible(false)}
+                    backgroundColor="transparent"
+                    allowChildInteraction={false}
+                  >
+                    <TouchableOpacity onPress={() => setTooltipVisible(true)}>
+                      <Image
+                        source={{ uri: API_HOST + event.flag }}
+                        style={{
+                          width: 20,
+                          height: 20,
+                          borderRadius: 10,
+                          borderWidth: 0.5,
+                          borderColor: Colors.DARK_LIGHT
+                        }}
+                      />
+                    </TouchableOpacity>
+                  </Tooltip>
+                ) : (
+                  <EarthIcon fill={Colors.DARK_BLUE} height={20} width={20} />
+                )}
                 <Text
                   style={{
                     fontSize: getFontSize(12),
@@ -839,6 +858,11 @@ const EventScreen = ({ route }: { route: any }) => {
                   {event.settings.address1}
                 </Text>
               </View>
+
+              <View style={{ flexDirection: 'row', alignItems: 'center', gap: 4, flex: 1 }}>
+                <CalendarIcon fill={Colors.DARK_BLUE} height={20} width={20} />
+                {formatEventDate(event)}
+              </View>
             </View>
 
             <View style={{ flexDirection: 'row', alignItems: 'center', gap: 4 }}>
@@ -905,7 +929,19 @@ const EventScreen = ({ route }: { route: any }) => {
                           }}
                           style={[styles.userImage, { marginLeft: 0 }]}
                         />
-                      ) : null}
+                      ) : (
+                        <AvatarWithInitials
+                          text={
+                            event.settings.host_data.first_name[0] +
+                            event.settings.host_data.last_name[0]
+                          }
+                          flag={API_HOST + event.settings.host_data?.flag}
+                          size={28}
+                          fontSize={12}
+                          borderColor={Colors.DARK_LIGHT}
+                          borderWidth={1}
+                        />
+                      )}
 
                       <View style={{ justifyContent: 'space-between' }}>
                         <Text
@@ -915,17 +951,16 @@ const EventScreen = ({ route }: { route: any }) => {
                             color: Colors.DARK_BLUE
                           }}
                         >
-                          {event.settings.host_data.first_name} {event.settings.host_data.last_name}
+                          {event.settings.host_data.first_name}
                         </Text>
-                        <Text style={{ fontWeight: '600', fontSize: 12, color: Colors.DARK_BLUE }}>
-                          NM:{' '}
-                          <Text style={{ fontFamily: 'montserrat-700' }}>
-                            {event.settings.host_data.nm}
-                          </Text>{' '}
-                          / UN:{' '}
-                          <Text style={{ fontFamily: 'montserrat-700' }}>
-                            {event.settings.host_data.un}
-                          </Text>
+                        <Text
+                          style={{
+                            fontFamily: 'montserrat-700',
+                            fontSize: 12,
+                            color: Colors.DARK_BLUE
+                          }}
+                        >
+                          {event.settings.host_data.last_name}
                         </Text>
                       </View>
                     </View>
@@ -937,7 +972,9 @@ const EventScreen = ({ route }: { route: any }) => {
 
               {filteredParticipants.length > 0 ? (
                 <View style={{ gap: 8, flex: 1 }}>
-                  <Text style={[styles.travelSeriesTitle, { fontSize: 12 }]}>Participants</Text>
+                  <Text style={[styles.travelSeriesTitle, { fontSize: 12 }]}>
+                    Participants{event.participants ? ` (${event.participants})` : ''}
+                  </Text>
 
                   <View style={[styles.statItem, { justifyContent: 'flex-start' }]}>
                     <View style={styles.userImageContainer}>
@@ -968,19 +1005,15 @@ const EventScreen = ({ route }: { route: any }) => {
                           key={index}
                           backgroundColor="transparent"
                         >
-                          <TouchableOpacity onPress={() => setTooltipUser(index)}>
+                          <TouchableOpacity
+                            style={index !== 0 ? { marginLeft: -10 } : {}}
+                            onPress={() => setTooltipUser(index)}
+                          >
                             {user.avatar ? (
                               <Image
                                 key={index}
                                 source={{ uri: API_HOST + user.avatar }}
-                                style={[
-                                  styles.userImage,
-                                  (filteredParticipants.length > maxVisibleParticipantsWithGap ||
-                                    filteredParticipants.length < (event.participants ?? 0)) &&
-                                  index !== 0
-                                    ? { marginLeft: -10 }
-                                    : {}
-                                ]}
+                                style={[styles.userImage]}
                               />
                             ) : (
                               <AvatarWithInitials
@@ -997,12 +1030,23 @@ const EventScreen = ({ route }: { route: any }) => {
                           </TouchableOpacity>
                         </Tooltip>
                       ))}
-                      {maxVisibleParticipants < filteredParticipants.length ||
-                      filteredParticipants.length < (event.participants ?? 0) ? (
-                        <View style={styles.userCountContainer}>
-                          <Text style={styles.userCount}>{event.participants}</Text>
-                        </View>
-                      ) : null}
+                      <TouchableOpacity
+                        style={styles.userCountContainer}
+                        onPress={() =>
+                          navigation.navigate(
+                            ...([
+                              NAVIGATION_PAGES.PARTICIPANTS_LIST,
+                              {
+                                participants: event.participants_full_data
+                              }
+                            ] as never)
+                          )
+                        }
+                      >
+                        <View style={styles.dots}></View>
+                        <View style={styles.dots}></View>
+                        <View style={styles.dots}></View>
+                      </TouchableOpacity>
                     </View>
                   </View>
                 </View>
@@ -1062,32 +1106,92 @@ const EventScreen = ({ route }: { route: any }) => {
             ) : null}
             {event.settings.host_profile === +currentUserId ||
             event.archived === 1 ? null : joined ? (
-              <TouchableOpacity
-                style={{
-                  flexDirection: 'row',
-                  alignItems: 'center',
-                  justifyContent: 'center',
-                  paddingVertical: 8,
-                  paddingHorizontal: 12,
-                  borderRadius: 20,
-                  backgroundColor: Colors.WHITE,
-                  gap: 6,
-                  borderWidth: 1,
-                  borderColor: Colors.DARK_BLUE
-                }}
-                onPress={handleUnjoinEvent}
-              >
-                <CalendarCrossedIcon fill={Colors.DARK_BLUE} width={16} height={16} />
-                <Text
+              <View style={{ flexDirection: 'row', gap: 8 }}>
+                <TouchableOpacity
                   style={{
-                    color: Colors.DARK_BLUE,
-                    fontSize: getFontSize(14),
-                    fontFamily: 'montserrat-700'
+                    flex: 1,
+                    flexDirection: 'row',
+                    alignItems: 'center',
+                    justifyContent: 'center',
+                    paddingVertical: 8,
+                    paddingHorizontal: 12,
+                    borderRadius: 20,
+                    backgroundColor: Colors.WHITE,
+                    gap: 6,
+                    borderWidth: 1,
+                    borderColor: Colors.DARK_BLUE
                   }}
+                  onPress={handleUnjoinEvent}
                 >
-                  Cancel
-                </Text>
-              </TouchableOpacity>
+                  <CalendarCrossedIcon fill={Colors.DARK_BLUE} width={16} height={16} />
+                  <Text
+                    style={{
+                      color: Colors.DARK_BLUE,
+                      fontSize: getFontSize(14),
+                      fontFamily: 'montserrat-700'
+                    }}
+                  >
+                    Cancel
+                  </Text>
+                </TouchableOpacity>
+
+                {event.settings?.chat_token && (
+                  <TouchableOpacity
+                    style={{
+                      flex: 1,
+                      flexDirection: 'row',
+                      alignItems: 'center',
+                      justifyContent: 'center',
+                      paddingVertical: 8,
+                      paddingHorizontal: 12,
+                      borderRadius: 20,
+                      backgroundColor: Colors.ORANGE,
+                      gap: 6,
+                      borderWidth: 1,
+                      borderColor: Colors.ORANGE
+                    }}
+                    onPress={() =>
+                      navigation.dispatch(
+                        CommonActions.reset({
+                          index: 1,
+                          routes: [
+                            {
+                              name: 'DrawerApp',
+                              state: {
+                                routes: [
+                                  {
+                                    name: NAVIGATION_PAGES.IN_APP_MESSAGES_TAB,
+                                    state: {
+                                      routes: [
+                                        { name: NAVIGATION_PAGES.CHATS_LIST },
+                                        {
+                                          name: NAVIGATION_PAGES.GROUP_CHAT,
+                                          params: { group_token: event.settings?.chat_token }
+                                        }
+                                      ]
+                                    }
+                                  }
+                                ]
+                              }
+                            }
+                          ]
+                        })
+                      )
+                    }
+                  >
+                    <ChatIcon fill={Colors.WHITE} width={16} height={16} />
+                    <Text
+                      style={{
+                        color: Colors.WHITE,
+                        fontSize: getFontSize(14),
+                        fontFamily: 'montserrat-700'
+                      }}
+                    >
+                      Chat
+                    </Text>
+                  </TouchableOpacity>
+                )}
+              </View>
             ) : !event.full ? (
               <TouchableOpacity
                 style={{
@@ -1104,7 +1208,7 @@ const EventScreen = ({ route }: { route: any }) => {
                 }}
                 onPress={handleJoinEvent}
               >
-                {event.settings.free ? (
+                {event.settings.type === 1 ? (
                   <>
                     <GigtIcon fill={Colors.WHITE} width={16} height={16} />
                     <Text

+ 13 - 5
src/screens/InAppScreens/TravelsScreen/EventScreen/styles.tsx

@@ -6,7 +6,7 @@ export const styles = StyleSheet.create({
   container: {
     flex: 1,
     backgroundColor: 'white',
-    position: 'relative',
+    position: 'relative'
   },
   chevronWrapper: {
     width: 42,
@@ -84,7 +84,7 @@ export const styles = StyleSheet.create({
   },
   stats: {
     flexDirection: 'row',
-    justifyContent: 'space-between',
+    justifyContent: 'space-between'
   },
   icon: {
     width: 28,
@@ -128,19 +128,27 @@ export const styles = StyleSheet.create({
     resizeMode: 'cover'
   },
   userCountContainer: {
+    flexDirection: 'row',
+    alignItems: 'center',
+    justifyContent: 'center',
     width: 28,
     height: 28,
     borderRadius: 14,
     backgroundColor: Colors.DARK_LIGHT,
-    alignItems: 'center',
-    justifyContent: 'center',
-    marginLeft: -10
+    marginLeft: -10,
+    gap: 2
   },
   userCount: {
     fontSize: 12,
     color: Colors.DARK_BLUE,
     lineHeight: 24
   },
+  dots: {
+    width: 3,
+    height: 3,
+    borderRadius: 3 / 2,
+    backgroundColor: Colors.DARK_BLUE
+  },
   modalView: {
     paddingHorizontal: 8,
     paddingVertical: 24,

+ 47 - 7
src/screens/InAppScreens/TravelsScreen/EventsScreen/index.tsx

@@ -30,6 +30,7 @@ import { API_HOST } from 'src/constants';
 import { Grayscale } from 'react-native-color-matrix-image-filters';
 import { renderSpotsText } from './utils';
 import ChevronIcon from 'assets/icons/chevron-left.svg';
+import Tooltip from 'react-native-walkthrough-tooltip';
 
 const EventsScreen = () => {
   const token = (storage.get('token', StoreType.STRING) as string) ?? null;
@@ -42,6 +43,7 @@ const EventsScreen = () => {
   const [pastEvents, setPastEvents] = useState<SingleEvent[]>([]);
   const [filteredEvents, setFilteredEvents] = useState<SingleEvent[]>([]);
   const [filteredPastEvents, setFilteredPastEvents] = useState<SingleEvent[]>([]);
+  const [tooltipStates, setTooltipStates] = useState<Record<number, boolean>>({});
   const date = new Date();
 
   const [isExpanded, setIsExpanded] = useState(false);
@@ -176,6 +178,50 @@ const EventsScreen = () => {
               {item.name}
             </Text>
 
+            <View style={styles.row}>
+              {item.type === 1 && item?.flag ? (
+                <Tooltip
+                  key={item.id}
+                  isVisible={!!tooltipStates[item.id]}
+                  content={<Text style={{ color: Colors.BLACK }}>{item.country}</Text>}
+                  contentStyle={{ backgroundColor: Colors.FILL_LIGHT }}
+                  tooltipStyle={{
+                    position: 'absolute',
+                    zIndex: 1000
+                  }}
+                  arrowStyle={{
+                    width: 16,
+                    height: 8
+                  }}
+                  placement="top"
+                  onClose={() => setTooltipStates((prev) => ({ ...prev, [item.id]: false }))}
+                  backgroundColor="transparent"
+                  allowChildInteraction={false}
+                >
+                  <TouchableOpacity
+                    onPress={() => setTooltipStates((prev) => ({ ...prev, [item.id]: true }))}
+                    hitSlop={{ top: 10, bottom: 10, left: 10, right: 10 }}
+                  >
+                    <Image
+                      source={{ uri: API_HOST + item.flag }}
+                      style={{
+                        width: 14,
+                        height: 14,
+                        borderRadius: 7,
+                        borderWidth: 0.5,
+                        borderColor: Colors.DARK_LIGHT
+                      }}
+                    />
+                  </TouchableOpacity>
+                </Tooltip>
+              ) : (
+                <EarthIcon fill={Colors.DARK_BLUE} height={14} width={14} />
+              )}
+              <Text style={styles.dateAndLocation} numberOfLines={1}>
+                {item.address1}
+              </Text>
+            </View>
+
             <View style={[styles.row]}>
               <View style={styles.row}>
                 <CalendarIcon fill={Colors.DARK_BLUE} height={14} width={14} />
@@ -185,13 +231,6 @@ const EventsScreen = () => {
               </View>
             </View>
 
-            <View style={styles.row}>
-              <EarthIcon fill={Colors.DARK_BLUE} height={14} width={14} />
-              <Text style={styles.dateAndLocation} numberOfLines={1}>
-                {item.address1}
-              </Text>
-            </View>
-
             {item.registrations_info !== 1 && (
               <View style={styles.row}>
                 <NomadsIcon fill={Colors.DARK_BLUE} height={14} width={14} />
@@ -255,6 +294,7 @@ const EventsScreen = () => {
           estimatedItemSize={100}
           contentContainerStyle={styles.listContainer}
           showsVerticalScrollIndicator={false}
+          extraData={tooltipStates}
         />
 
         {filteredPastEvents && filteredPastEvents.length ? (

+ 149 - 0
src/screens/InAppScreens/TravelsScreen/ParticipantsListScreen/index.tsx

@@ -0,0 +1,149 @@
+import React, { FC, useEffect, useState } from 'react';
+import { View, StyleSheet } from 'react-native';
+import { NavigationProp } from '@react-navigation/native';
+import { PageWrapper, Header, Input } from 'src/components';
+import { FlashList } from '@shopify/flash-list';
+
+import SearchIcon from 'assets/icons/search.svg';
+import { applyModalSort, dataRanking } from '../../TravellersScreen/utils';
+import { RankingDropdown } from '../../TravellersScreen/utils/types';
+import { Profile } from '../../MapScreen/UsersListScreen/Profile';
+import { FilterButton, FilterModal } from '../../TravellersScreen/Components/FilterModal';
+import { usePostGetCountriesRanking } from '@api/ranking';
+
+type Props = {
+  navigation: NavigationProp<any>;
+  route: any;
+};
+
+const ParticipantsListScreen: FC<Props> = ({ navigation, route }) => {
+  const { participants } = route.params;
+  const { data: masterCountries } = usePostGetCountriesRanking();
+
+  const [searchQuery, setSearchQuery] = useState('');
+  const [filteredData, setFilteredData] = useState<any[]>([]);
+  const [confirmedValueRanking, setConfirmedValueRanking] = useState<RankingDropdown | null>();
+  const [isModalVisible, setModalVisible] = useState(false);
+
+  useEffect(() => {
+    if (participants) {
+      setFilteredData(participants);
+    }
+  }, [participants]);
+
+  const handleSearch = (text: string) => {
+    if (text && participants) {
+      const searchData =
+        participants?.filter((item: any) => {
+          const itemData = item.first_name
+            ? item.first_name.toLowerCase() + ' ' + item.last_name?.toLowerCase()
+            : ''.toLowerCase();
+          const textData = text.toLowerCase();
+          return itemData.indexOf(textData) > -1;
+        }) ?? [];
+      setFilteredData(searchData);
+      setSearchQuery(text);
+    } else {
+      setFilteredData(participants ?? []);
+      setSearchQuery(text);
+    }
+  };
+
+  return (
+    <PageWrapper>
+      <Header
+        label="Participants"
+        rightElement={<FilterButton onPress={() => setModalVisible(!isModalVisible)} />}
+      />
+      <View style={styles.container}>
+        <Input
+          inputMode={'search'}
+          placeholder={'Search nomads'}
+          onChange={handleSearch}
+          value={searchQuery}
+          icon={<SearchIcon fill={'#C8C8C8'} width={14} height={14} />}
+        />
+        <FlashList
+          viewabilityConfig={{
+            waitForInteraction: true,
+            itemVisiblePercentThreshold: 50,
+            minimumViewTime: 1000
+          }}
+          estimatedItemSize={50}
+          data={filteredData || []}
+          renderItem={({ item, index }) => (
+            <Profile
+              userId={item.user_id}
+              key={index}
+              index={index}
+              first_name={item.first_name}
+              last_name={item.last_name}
+              avatar={item.avatar ? '/img/avatars/' + item.avatar : null}
+              date_of_birth={item.age}
+              homebase_flag={'/img/flags_new/' + item.flag1}
+              homebase2_flag={item.flag2 ? '/img/flags_new/' + item.flag2 : null}
+              score={[
+                item.score_nm,
+                item.score_un,
+                item.score_unp,
+                item.score_dare,
+                item.score_tcc,
+                item.score_deep,
+                item.score_slow,
+                item.score_yes,
+                item.score_kye,
+                item.score_whs,
+                item.score_tbt
+              ]}
+              active_score={
+                confirmedValueRanking ? confirmedValueRanking.value - 1 : dataRanking[0].value - 1
+              }
+              tbt_score={item.score_tbt}
+              tbt_rank={item.rank_tbt}
+              badge_tbt={item.badge_tbt}
+              badge_1281={item.badge_1281}
+              badge_un={item.badge_un}
+              badge_un_25={item.badge_un_25}
+              badge_un_50={item.badge_un_50}
+              badge_un_75={item.badge_un_75}
+              badge_un_100={item.badge_un_100}
+              badge_un_150={item.badge_un_150}
+              auth={item.auth}
+            />
+          )}
+          keyExtractor={(item) => item.user_id.toString()}
+          contentContainerStyle={{ paddingBottom: 16, paddingTop: 8 }}
+          showsVerticalScrollIndicator={false}
+        />
+      </View>
+
+      <FilterModal
+        isModalVisible={isModalVisible}
+        setModalVisible={(value) => setModalVisible(value)}
+        applyFilter={(filterAge, filterRanking, filterCountry) => {
+          setConfirmedValueRanking(filterRanking);
+          setFilteredData(
+            applyModalSort(
+              participants ?? [],
+              filterAge,
+              filterRanking ?? dataRanking[0],
+              filterCountry
+            )
+          );
+          setModalVisible(false);
+        }}
+        countriesData={masterCountries ? masterCountries.data : []}
+      />
+    </PageWrapper>
+  );
+};
+
+const styles = StyleSheet.create({
+  container: {
+    backgroundColor: 'white',
+    gap: 16,
+    flex: 1
+  }
+});
+
+export default ParticipantsListScreen;

+ 2 - 1
src/types/navigation.ts

@@ -78,5 +78,6 @@ export enum NAVIGATION_PAGES {
   GROUP_SETTINGS = 'inAppGroupSettings',
   CREATE_EVENT = 'inAppCreateEvent',
   MEMBERS_LIST = 'inAppMembersList',
-  ALL_EVENT_PHOTOS = 'inAppAllEventPhotos'
+  ALL_EVENT_PHOTOS = 'inAppAllEventPhotos',
+  PARTICIPANTS_LIST = 'inAppParticipantsList'
 }