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 (11, 31, 101) days, this will increase your SLOW score, each longer stay should include at least 11 days countinuous stay in a country.`}
  221. />
  222. </PageWrapper>
  223. );
  224. };
  225. export default CountriesScreen;