|
@@ -10,6 +10,9 @@ import { Loading, WarningModal } from 'src/components';
|
|
|
import MessagesDot from 'src/components/MessagesDot';
|
|
import MessagesDot from 'src/components/MessagesDot';
|
|
|
import ActionSheet, { ActionSheetRef } from 'react-native-actions-sheet';
|
|
import ActionSheet, { ActionSheetRef } from 'react-native-actions-sheet';
|
|
|
import TabViewWrapper from 'src/components/TabViewWrapper';
|
|
import TabViewWrapper from 'src/components/TabViewWrapper';
|
|
|
|
|
+import LocationIcon from 'assets/icons/travels-screens/map-location.svg';
|
|
|
|
|
+import { usePostSetToggleItem } from '@api/series';
|
|
|
|
|
+import { Colors } from 'src/theme';
|
|
|
|
|
|
|
|
export function TabViewDelayed({
|
|
export function TabViewDelayed({
|
|
|
children,
|
|
children,
|
|
@@ -34,6 +37,7 @@ const SearchModal = ({
|
|
|
searchVisible,
|
|
searchVisible,
|
|
|
handleCloseModal,
|
|
handleCloseModal,
|
|
|
handleFindRegion,
|
|
handleFindRegion,
|
|
|
|
|
+ handleFindSeries,
|
|
|
index,
|
|
index,
|
|
|
searchData,
|
|
searchData,
|
|
|
setIndex,
|
|
setIndex,
|
|
@@ -42,6 +46,7 @@ const SearchModal = ({
|
|
|
searchVisible: boolean;
|
|
searchVisible: boolean;
|
|
|
handleCloseModal: () => void;
|
|
handleCloseModal: () => void;
|
|
|
handleFindRegion: (id: number, type: 'regions' | 'countries' | 'places') => void;
|
|
handleFindRegion: (id: number, type: 'regions' | 'countries' | 'places') => void;
|
|
|
|
|
+ handleFindSeries?: (item: any) => void;
|
|
|
index: number;
|
|
index: number;
|
|
|
searchData: any;
|
|
searchData: any;
|
|
|
setIndex: (index: number) => void;
|
|
setIndex: (index: number) => void;
|
|
@@ -54,13 +59,43 @@ const SearchModal = ({
|
|
|
const [routes] = useState([
|
|
const [routes] = useState([
|
|
|
{ key: 'users', title: 'Nomads' },
|
|
{ key: 'users', title: 'Nomads' },
|
|
|
{ key: 'regions', title: 'NM regions' },
|
|
{ key: 'regions', title: 'NM regions' },
|
|
|
- { key: 'dare', title: 'DARE places' }
|
|
|
|
|
|
|
+ { key: 'dare', title: 'DARE places' },
|
|
|
|
|
+ { key: 'series', title: 'Series' }
|
|
|
]);
|
|
]);
|
|
|
const [shouldOpenModal, setShouldOpenModal] = useState<{
|
|
const [shouldOpenModal, setShouldOpenModal] = useState<{
|
|
|
id: number;
|
|
id: number;
|
|
|
type: 'regions' | 'countries' | 'places';
|
|
type: 'regions' | 'countries' | 'places';
|
|
|
} | null>(null);
|
|
} | null>(null);
|
|
|
const [warningVisible, setWarningVisible] = useState(false);
|
|
const [warningVisible, setWarningVisible] = useState(false);
|
|
|
|
|
+ const { mutate: updateSeriesItem } = usePostSetToggleItem();
|
|
|
|
|
+ const [localSeriesStatus, setLocalSeriesStatus] = useState<Record<number, boolean>>({});
|
|
|
|
|
+
|
|
|
|
|
+ const toggleSeries = useCallback(
|
|
|
|
|
+ async (item: any) => {
|
|
|
|
|
+ if (!token) {
|
|
|
|
|
+ setWarningVisible(true);
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ const isCompleted = localSeriesStatus[item.item_id] !== undefined ? localSeriesStatus[item.item_id] : item.visited === 1;
|
|
|
|
|
+
|
|
|
|
|
+ const itemData = {
|
|
|
|
|
+ token,
|
|
|
|
|
+ series_id: item.series_id,
|
|
|
|
|
+ item_id: item.item_id,
|
|
|
|
|
+ checked: (isCompleted ? 0 : 1) as 0 | 1,
|
|
|
|
|
+ double: 0 as 0 | 1
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ try {
|
|
|
|
|
+ updateSeriesItem(itemData);
|
|
|
|
|
+ setLocalSeriesStatus(prev => ({ ...prev, [item.item_id]: !isCompleted }));
|
|
|
|
|
+ } catch (error) {
|
|
|
|
|
+ console.error('Failed to update series state', error);
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+ [token, updateSeriesItem, localSeriesStatus]
|
|
|
|
|
+ );
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
useEffect(() => {
|
|
|
if (searchVisible) {
|
|
if (searchVisible) {
|
|
@@ -81,17 +116,80 @@ const SearchModal = ({
|
|
|
setIndex(1);
|
|
setIndex(1);
|
|
|
} else if (searchData['dare'] && searchData['dare'].length > 0) {
|
|
} else if (searchData['dare'] && searchData['dare'].length > 0) {
|
|
|
setIndex(2);
|
|
setIndex(2);
|
|
|
|
|
+ } else if (searchData['series'] && searchData['series'].length > 0) {
|
|
|
|
|
+ setIndex(3);
|
|
|
} else {
|
|
} else {
|
|
|
setIndex(0);
|
|
setIndex(0);
|
|
|
}
|
|
}
|
|
|
}, [searchData]);
|
|
}, [searchData]);
|
|
|
|
|
|
|
|
- const renderItem = useCallback(
|
|
|
|
|
- ({ item }: { item: any }) => {
|
|
|
|
|
- const [name, ...rest] = item.name?.split(/ – | - /);
|
|
|
|
|
|
|
+ const renderItem = useCallback(
|
|
|
|
|
+ ({ item, routeKey }: { item: any; routeKey: string }) => {
|
|
|
|
|
+ if (routeKey === 'series') {
|
|
|
|
|
+ const isCompleted = localSeriesStatus[item.item_id] !== undefined ? localSeriesStatus[item.item_id] : item.visited === 1;
|
|
|
|
|
+ return (
|
|
|
|
|
+ <View style={styles.seriesOption}>
|
|
|
|
|
+ <View style={styles.seriesImageContainer}>
|
|
|
|
|
+ <Image
|
|
|
|
|
+ source={{ uri: API_HOST + item.series_icon }}
|
|
|
|
|
+ style={styles.seriesIcon}
|
|
|
|
|
+ resizeMode="contain"
|
|
|
|
|
+ />
|
|
|
|
|
+ </View>
|
|
|
|
|
+
|
|
|
|
|
+ <View style={{ flex: 1, gap: 8 }}>
|
|
|
|
|
+ <View style={{ justifyContent: 'space-between', flex: 1 }}>
|
|
|
|
|
+ <Text style={styles.seriesName}>{item.series_name}</Text>
|
|
|
|
|
+ <Text style={styles.seriesDescription}>{item.item_name}</Text>
|
|
|
|
|
+ </View>
|
|
|
|
|
+
|
|
|
|
|
+ <View
|
|
|
|
|
+ style={{
|
|
|
|
|
+ flexDirection: 'row',
|
|
|
|
|
+ justifyContent: 'space-between',
|
|
|
|
|
+ alignItems: 'center',
|
|
|
|
|
+ flex: 1
|
|
|
|
|
+ }}
|
|
|
|
|
+ >
|
|
|
|
|
+ {item.location_geojson ? (
|
|
|
|
|
+ <TouchableOpacity
|
|
|
|
|
+ onPress={() => {
|
|
|
|
|
+ handleCloseModal();
|
|
|
|
|
+ handleFindSeries && handleFindSeries({
|
|
|
|
|
+ ...item,
|
|
|
|
|
+ visited: isCompleted ? 1 : 0
|
|
|
|
|
+ });
|
|
|
|
|
+ }}
|
|
|
|
|
+ style={styles.navButton}
|
|
|
|
|
+ >
|
|
|
|
|
+ <LocationIcon fill={Colors.DARK_BLUE} width={16} height={16} />
|
|
|
|
|
+ </TouchableOpacity>
|
|
|
|
|
+ ) : <View/>}
|
|
|
|
|
+
|
|
|
|
|
+ <TouchableOpacity
|
|
|
|
|
+ onPress={() => toggleSeries(item)}
|
|
|
|
|
+ style={[styles.markButton, isCompleted && styles.visitedButton]}
|
|
|
|
|
+ >
|
|
|
|
|
+ {isCompleted ? (
|
|
|
|
|
+ <View style={styles.completedContainer}>
|
|
|
|
|
+ <Text style={[styles.calloutButtonText, { color: Colors.DARK_BLUE }]}>
|
|
|
|
|
+ Completed
|
|
|
|
|
+ </Text>
|
|
|
|
|
+ </View>
|
|
|
|
|
+ ) : (
|
|
|
|
|
+ <Text style={styles.calloutButtonText}>To Complete</Text>
|
|
|
|
|
+ )}
|
|
|
|
|
+ </TouchableOpacity>
|
|
|
|
|
+ </View>
|
|
|
|
|
+ </View>
|
|
|
|
|
+ </View>
|
|
|
|
|
+ );
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ const [name, ...rest] = item.name?.split(/ – | - /) || [];
|
|
|
const subname = rest?.join(' - ');
|
|
const subname = rest?.join(' - ');
|
|
|
|
|
|
|
|
- return index === 0 ? (
|
|
|
|
|
|
|
+ return routeKey === 'users' ? (
|
|
|
<TouchableOpacity
|
|
<TouchableOpacity
|
|
|
style={{ paddingVertical: 12 }}
|
|
style={{ paddingVertical: 12 }}
|
|
|
onPress={() => {
|
|
onPress={() => {
|
|
@@ -138,7 +236,7 @@ const SearchModal = ({
|
|
|
style={{ paddingVertical: 12 }}
|
|
style={{ paddingVertical: 12 }}
|
|
|
onPress={() => {
|
|
onPress={() => {
|
|
|
handleCloseModal();
|
|
handleCloseModal();
|
|
|
- if (index === 1) {
|
|
|
|
|
|
|
+ if (routeKey === 'regions') {
|
|
|
setShouldOpenModal({ id: item.id, type: 'regions' });
|
|
setShouldOpenModal({ id: item.id, type: 'regions' });
|
|
|
} else {
|
|
} else {
|
|
|
setShouldOpenModal({ id: item.id, type: 'places' });
|
|
setShouldOpenModal({ id: item.id, type: 'places' });
|
|
@@ -166,7 +264,7 @@ const SearchModal = ({
|
|
|
</TouchableOpacity>
|
|
</TouchableOpacity>
|
|
|
);
|
|
);
|
|
|
},
|
|
},
|
|
|
- [index]
|
|
|
|
|
|
|
+ [index, token, handleCloseModal, handleFindSeries, navigation, toggleSeries, localSeriesStatus]
|
|
|
);
|
|
);
|
|
|
|
|
|
|
|
const renderScene = ({ route }: { route: any }) => {
|
|
const renderScene = ({ route }: { route: any }) => {
|
|
@@ -180,10 +278,11 @@ const SearchModal = ({
|
|
|
minimumViewTime: 1000
|
|
minimumViewTime: 1000
|
|
|
}}
|
|
}}
|
|
|
data={searchData?.[route.key]}
|
|
data={searchData?.[route.key]}
|
|
|
- renderItem={renderItem}
|
|
|
|
|
- keyExtractor={(item) => item.id.toString()}
|
|
|
|
|
|
|
+ renderItem={({ item }) => renderItem({ item, routeKey: route.key })}
|
|
|
|
|
+ keyExtractor={(item: any, idx) => `${item?.id || item?.item_id}-${idx}`}
|
|
|
showsVerticalScrollIndicator={false}
|
|
showsVerticalScrollIndicator={false}
|
|
|
contentContainerStyle={{ paddingVertical: 16, paddingHorizontal: 8 }}
|
|
contentContainerStyle={{ paddingVertical: 16, paddingHorizontal: 8 }}
|
|
|
|
|
+ extraData={localSeriesStatus}
|
|
|
/>
|
|
/>
|
|
|
) : (
|
|
) : (
|
|
|
<Loading />
|
|
<Loading />
|