index.tsx 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247
  1. import React, { useCallback, useEffect, useState } from 'react';
  2. import { View, Text, TouchableOpacity, ScrollView, FlatList, Platform } from 'react-native';
  3. import ReactModal from 'react-native-modal';
  4. import * as Progress from 'react-native-progress';
  5. import { useFocusEffect, useNavigation } from '@react-navigation/native';
  6. import CountryItem from '../Components/CountryItem';
  7. import EditModal from '../Components/EditSlowModal';
  8. import { Header, Input, PageWrapper, WarningModal } from 'src/components';
  9. import { useGetSlowQuery, usePostSetSlowMutation } from '@api/countries';
  10. import { StoreType, storage } from 'src/storage';
  11. import { SlowData } from '../utils/types';
  12. import { Colors } from 'src/theme';
  13. import { styles } from './styles';
  14. import ChevronIcon from 'assets/icons/travels-screens/down-arrow.svg';
  15. import IngoSvg from 'assets/icons/travels-screens/info.svg';
  16. import SearchIcon from '../../../../../assets/icons/search.svg';
  17. import InfoIcon from 'assets/icons/info-solid.svg';
  18. import { NAVIGATION_PAGES } from 'src/types';
  19. import { useRegion } from 'src/contexts/RegionContext';
  20. const CountriesScreen = () => {
  21. const token = storage.get('token', StoreType.STRING) as string;
  22. const { data, refetch } = useGetSlowQuery(String(token));
  23. const [megaSelectorVisible, setMegaSelectorVisible] = useState(false);
  24. const [selectedMega, setSelectedMega] = useState<{ name: string; id: number }>({
  25. name: 'ALL MEGAREGIONS',
  26. id: -1
  27. });
  28. const [total, setTotal] = useState({ slow: 0, visited: 0 });
  29. const [isEditModalVisible, setIsEditModalVisible] = useState(false);
  30. const [currentItem, setCurrentItem] = useState<SlowData | null>(null);
  31. const [infoModalVisible, setInfoModalVisible] = useState(false);
  32. const [search, setSearch] = useState<string>('');
  33. const [filteredSlow, setFilteredSlow] = useState<SlowData[] | null>(null);
  34. const navigation = useNavigation();
  35. const { handleUpdateSlowList: handleUpdateSlow, slow, setSlow, setUserData } = useRegion();
  36. const handleOpenEditModal = (item: SlowData) => {
  37. setCurrentItem(item);
  38. setIsEditModalVisible(true);
  39. };
  40. useEffect(() => {
  41. if (slow && slow.length) {
  42. token && calcTotalScore();
  43. let newSlowData = slow;
  44. if (search) {
  45. newSlowData =
  46. slow?.filter((item: any) => {
  47. const itemData = item.country ? item.country.toLowerCase() : ''.toLowerCase();
  48. const textData = search.toLowerCase();
  49. return itemData.indexOf(textData) > -1;
  50. }) ?? [];
  51. }
  52. setFilteredSlow(newSlowData);
  53. }
  54. }, [slow]);
  55. useEffect(() => {
  56. const refetchData = async () => {
  57. await refetch().then((res) => {
  58. if (res.data) {
  59. setSlow(res.data.slow);
  60. }
  61. });
  62. };
  63. if (data && data.result === 'OK') {
  64. setSearch('');
  65. if (selectedMega.id === -1) {
  66. refetchData();
  67. } else {
  68. setSlow(data?.slow?.filter((item) => item.mega.includes(selectedMega.id)));
  69. }
  70. }
  71. }, [selectedMega]);
  72. useEffect(() => {
  73. if (data && data.result === 'OK') {
  74. setSlow(data?.slow);
  75. }
  76. }, [data]);
  77. const searchFilter = (text: string) => {
  78. if (text) {
  79. const newData: SlowData[] | null =
  80. slow?.filter((item: any) => {
  81. const itemData = item.country ? item.country.toLowerCase() : ''.toLowerCase();
  82. const textData = text.toLowerCase();
  83. return itemData.indexOf(textData) > -1;
  84. }) ?? [];
  85. setFilteredSlow(newData);
  86. setSearch(text);
  87. } else {
  88. setFilteredSlow(slow);
  89. setSearch(text);
  90. }
  91. };
  92. const calcTotalScore = () => {
  93. let visited = 0;
  94. let slow11 = 0;
  95. let slow31 = 0;
  96. let slow101 = 0;
  97. slow?.forEach((item: SlowData) => {
  98. visited += item.visited;
  99. slow11 += item.slow11;
  100. slow31 += item.slow31;
  101. slow101 += item.slow101;
  102. });
  103. setTotal({ slow: slow11 + slow31 + slow101, visited });
  104. };
  105. const renderCountryItem = ({ item }: { item: SlowData }) => (
  106. <CountryItem
  107. item={item}
  108. updateSlow={handleUpdateSlow}
  109. openEditModal={handleOpenEditModal}
  110. token={token}
  111. setUserData={setUserData}
  112. navigation={navigation}
  113. />
  114. );
  115. return (
  116. <PageWrapper>
  117. <Header
  118. label="Countries"
  119. // rightElement={
  120. // <TouchableOpacity
  121. // onPress={() => navigation.navigate(NAVIGATION_PAGES.COUNTRIES_INFO as never)}
  122. // style={{ width: 30 }}
  123. // >
  124. // <InfoIcon />
  125. // </TouchableOpacity>
  126. // }
  127. />
  128. <TouchableOpacity style={styles.megaSelector} onPress={() => setMegaSelectorVisible(true)}>
  129. <Text style={styles.megaButtonText}>{selectedMega.name}</Text>
  130. <ChevronIcon width={18} height={18} />
  131. </TouchableOpacity>
  132. <View style={{ marginBottom: 16 }}>
  133. <Input
  134. inputMode={'search'}
  135. placeholder={'Search'}
  136. onChange={(text) => searchFilter(text)}
  137. value={search}
  138. icon={<SearchIcon fill={'#C8C8C8'} width={14} height={14} />}
  139. height={34}
  140. />
  141. </View>
  142. <View style={styles.progressHeader}>
  143. <Text style={styles.visitedCountriesCount}>Visited countries</Text>
  144. <Text style={styles.visitedCountriesCount}>
  145. {slow?.length
  146. ? `${total.visited}/${slow.length} • ${((total.visited * 100) / slow.length).toFixed(2)}%`
  147. : '0/0 • 100%'}
  148. </Text>
  149. </View>
  150. <Progress.Bar
  151. progress={slow?.length ? total.visited / slow.length : 1}
  152. width={null}
  153. height={4}
  154. color={Colors.DARK_BLUE}
  155. borderWidth={0}
  156. borderRadius={5}
  157. unfilledColor={Colors.FILL_LIGHT}
  158. />
  159. <View style={styles.slowScoreSection}>
  160. <Text style={styles.visitedCountriesCount}>SLOW score: {total.slow}</Text>
  161. <TouchableOpacity style={styles.infoBtn} onPress={() => setInfoModalVisible(true)}>
  162. <IngoSvg />
  163. </TouchableOpacity>
  164. </View>
  165. {filteredSlow && filteredSlow?.length ? (
  166. <FlatList
  167. data={filteredSlow}
  168. renderItem={renderCountryItem}
  169. keyExtractor={(item) => item.country_id.toString()}
  170. showsVerticalScrollIndicator={false}
  171. style={{ paddingTop: 8 }}
  172. />
  173. ) : null}
  174. <ReactModal
  175. isVisible={megaSelectorVisible}
  176. onBackdropPress={() => setMegaSelectorVisible(false)}
  177. style={styles.modal}
  178. statusBarTranslucent={true}
  179. presentationStyle="overFullScreen"
  180. >
  181. <View style={styles.wrapper}>
  182. <ScrollView style={{ paddingBottom: 16 }} showsVerticalScrollIndicator={false}>
  183. <TouchableOpacity
  184. style={styles.btnOption}
  185. onPress={() => {
  186. setMegaSelectorVisible(false);
  187. setSelectedMega({ name: 'ALL MEGAREGIONS', id: -1 });
  188. }}
  189. >
  190. <Text style={styles.btnOptionText}>ALL MEGAREGIONS</Text>
  191. </TouchableOpacity>
  192. {data?.megaregions?.map((mega) => (
  193. <TouchableOpacity
  194. key={mega.id}
  195. style={styles.btnOption}
  196. onPress={() => {
  197. setMegaSelectorVisible(false);
  198. setSelectedMega(mega);
  199. }}
  200. >
  201. <Text style={styles.btnOptionText}>{mega.name}</Text>
  202. </TouchableOpacity>
  203. ))}
  204. </ScrollView>
  205. </View>
  206. </ReactModal>
  207. {currentItem && (
  208. <EditModal
  209. isVisible={isEditModalVisible}
  210. onClose={() => setIsEditModalVisible(false)}
  211. item={currentItem}
  212. updateSlow={(id, v, s11, s31, s101) => handleUpdateSlow(id, v, s11, s31, s101)}
  213. />
  214. )}
  215. <WarningModal
  216. isVisible={infoModalVisible}
  217. onClose={() => setInfoModalVisible(false)}
  218. type="success"
  219. title="SLOW score"
  220. message={`Mark countries as visited.\nTell us if you stayed longer (7, 31, 101) days, this will increase your SLOW score, each longer stay should include at least 7 days countinuous stay in a country.`}
  221. />
  222. </PageWrapper>
  223. );
  224. };
  225. export default CountriesScreen;