import React, { useCallback, useEffect, useRef, useState } from 'react'; import { useFocusEffect } from '@react-navigation/native'; import { View, Text, FlatList, Alert } from 'react-native'; import { Button, Header, Input, Loading, Modal, PageWrapper } from 'src/components'; import SearchIcon from '../../../../../assets/icons/search.svg'; import { fetchItemsForSeries, usePostSetToggleItem } from '@api/series'; import { Colors } from 'src/theme'; import { ButtonVariants } from 'src/types/components'; import { AccordionListItem } from '../Components/AccordionListItem'; import { styles } from './styles'; import TabViewWrapper from 'src/components/TabViewWrapper'; import { SafeAreaView } from 'react-native-safe-area-context'; interface SeriesItem { series_id: number; item_id: number; icon: string | null; name: string; readonly: boolean; info: string; new: boolean; checked: boolean; checked_double: boolean; double: boolean; } interface SeriesGroup { name: string; icon: string; series_id: number; series_name: string; items: SeriesItem[]; } interface FilteredData { [key: string]: SeriesGroup[]; } interface Route { key: string; title: string; } export const SeriesItemScreen = ({ route }: { route: any }) => { const { id, name, token, scrollToItemId } = route.params; const { mutate: updateSeriesItem } = usePostSetToggleItem(); const flatListRef = useRef(null); const scrolledRef = useRef(false); const [search, setSearch] = useState(''); const [filteredData, setFilteredData] = useState({}); const [seriesData, setSeriesData] = useState([]); const [index, setIndex] = useState(0); const [isLoading, setIsLoading] = useState(true); const [isInfoModalVisible, setIsInfoModalVisible] = useState(false); const [infoItem, setInfoItem] = useState(null); const [activeFilteredData, setActiveFilteredData] = useState([]); useFocusEffect( useCallback(() => { const fetchGroups = async () => { const data = await fetchItemsForSeries(String(token), id); if (!data) { setIsLoading(false); return; } const allItems = data.groups; const newItems = data.groups .map((group) => ({ ...group, items: group.items.filter((item) => item.new) })) .filter((group) => group.items.length > 0); setFilteredData({ all: allItems, new: newItems, unchecked: [], checked: [] }); setSeriesData(data.groups); setIsLoading(false); }; fetchGroups(); }, []) ); const [routes] = useState([ { key: 'all', title: 'All items' }, { key: 'checked', title: 'Ticked' }, { key: 'unchecked', title: 'Unticked' }, { key: 'new', title: 'New' } ]); const handleIndexChange = (index: number) => { let dataToFilter = [...seriesData]; switch (index) { case 3: dataToFilter = dataToFilter .map((group) => ({ ...group, items: group.items.filter((item) => item.new) })) .filter((group) => group.items.length > 0); break; case 2: dataToFilter = dataToFilter .map((group) => ({ ...group, items: group.items.filter( (item) => !item.checked || (item.double && !item.checked_double) ) })) .filter((group) => group.items.length > 0); break; case 1: dataToFilter = dataToFilter .map((group) => ({ ...group, items: group.items.filter( (item) => item.checked || (item.double && item.checked_double) ) })) .filter((group) => group.items.length > 0); break; default: break; } setActiveFilteredData(dataToFilter); setFilteredData((prevState) => ({ ...prevState, [routes[index].key]: dataToFilter })); }; useEffect(() => { handleIndexChange(index); }, [seriesData, index]); useEffect(() => { setActiveFilteredData(filteredData[routes[index].key]); }, [filteredData]); const [subItemY, setSubItemY] = useState(null); const [groupY, setGroupY] = useState(null); useEffect(() => { if (!scrollToItemId || scrolledRef.current || !activeFilteredData?.length) return; const targetId = String(scrollToItemId); const groupIndex = activeFilteredData.findIndex((group) => group.items.some((item) => String(item.item_id) === targetId) ); if (groupIndex !== -1) { if (subItemY !== null && groupY !== null) { scrolledRef.current = true; const scroll = () => { flatListRef.current?.scrollToIndex({ index: groupIndex, animated: true, viewPosition: 0, viewOffset: -subItemY - 60 + 50 // -subItemY to push group up, -60 for header, +50 buffer }); }; const timer1 = setTimeout(scroll, 300); const timer2 = setTimeout(scroll, 800); return () => { clearTimeout(timer1); clearTimeout(timer2); }; } else { const roughScroll = () => { flatListRef.current?.scrollToIndex({ index: groupIndex, animated: false, viewPosition: 0 }); }; const timer = setTimeout(roughScroll, 600); return () => clearTimeout(timer); } } else { scrolledRef.current = true; } }, [scrollToItemId, activeFilteredData, subItemY, groupY]); const renderScene = ({ route: tabRoute }: { route: Route }) => { return isLoading ? ( ) : ( index.toString()} showsVerticalScrollIndicator={false} initialNumToRender={15} windowSize={11} style={{ paddingTop: 10 }} contentContainerStyle={{ paddingBottom: 16 }} data={activeFilteredData} onScrollToIndexFailed={(info) => { const offset = info.averageItemLength * info.index; flatListRef.current?.scrollToOffset({ offset, animated: false }); setTimeout(() => { flatListRef.current?.scrollToIndex({ index: info.index, animated: false, viewPosition: 0 }); }, 150); }} renderItem={({ item, index }) => { const targetId = String(scrollToItemId); const isTargetGroup = item.items.some((subItem) => String(subItem.item_id) === targetId); return ( { if (isTargetGroup) { const y = e.nativeEvent.layout.y; setGroupY((prev) => (prev === null ? y : prev)); } }} > { setSubItemY((prev) => (prev === null ? y : prev)); }} /> ); }} /> ); }; useEffect(() => { const searchText = search.toLowerCase(); const searchData = filteredData[routes[index].key] ?.map((group) => { const groupNameMatch = group.name.toLowerCase().includes(searchText); if (groupNameMatch) { return group; } else { const filteredItems = group.items.filter((item) => item.name.toLowerCase().includes(searchText) ); return filteredItems.length > 0 ? { ...group, items: filteredItems } : null; } }) .filter((group) => group !== null); setActiveFilteredData(searchData as SeriesGroup[]); }, [search]); const handleCheckboxChange = useCallback( async (item: SeriesItem, groupName: string, double?: boolean) => { setSeriesData((currentData) => { const newData = currentData.map((group) => { const hasItem = group.items.some((subItem) => subItem.item_id === item.item_id); if (!hasItem) return group; return { ...group, items: group.items.map((subItem) => subItem.item_id === item.item_id ? { ...subItem, ...(double ? { checked_double: !subItem.checked_double } : { checked: !subItem.checked }) } : subItem ) }; }); return newData; }); const itemData = { token: token, series_id: item.series_id, item_id: item.item_id, checked: double ? (item.checked_double ? 0 : 1) : (item.checked ? 0 : 1) as 0 | 1, double: (double ? 1 : 0) as 0 | 1 }; try { updateSeriesItem(itemData); } catch (error) { console.error('Failed to update checkbox state', error); } }, [token, updateSeriesItem] ); return (
setSearch(text)} value={search} icon={} /> {infoItem?.name} {infoItem?.info}