import { Ionicons } from "@expo/vector-icons"; import { FlashList } from "@shopify/flash-list"; import { useRouter } from "expo-router"; import { useAtomValue } from "jotai"; import { useCallback, useMemo, useState } from "react"; import { useTranslation } from "react-i18next"; import { Platform, RefreshControl, TouchableOpacity, View } from "react-native"; import { useSafeAreaInsets } from "react-native-safe-area-context"; import { Button } from "@/components/Button"; import { Text } from "@/components/common/Text"; import { useStreamystatsEnabled, useWatchlistsQuery, } from "@/hooks/useWatchlists"; import { userAtom } from "@/providers/JellyfinProvider"; import type { StreamystatsWatchlist } from "@/utils/streamystats/types"; interface WatchlistCardProps { watchlist: StreamystatsWatchlist; isOwner: boolean; onPress: () => void; } const WatchlistCard: React.FC = ({ watchlist, isOwner, onPress, }) => { const { t } = useTranslation(); return ( {watchlist.name} {isOwner && ( {t("watchlists.you")} )} {watchlist.description && ( {watchlist.description} )} {watchlist.itemCount ?? 0}{" "} {(watchlist.itemCount ?? 0) === 1 ? t("watchlists.item") : t("watchlists.items")} {watchlist.allowedItemType && ( {watchlist.allowedItemType} )} ); }; const EmptyState: React.FC<{ onCreatePress: () => void }> = ({ onCreatePress: _onCreatePress, }) => { const { t } = useTranslation(); return ( {t("watchlists.empty_title")} {t("watchlists.empty_description")} ); }; const NotConfiguredState: React.FC = () => { const { t } = useTranslation(); const router = useRouter(); return ( {t("watchlists.not_configured_title")} {t("watchlists.not_configured_description")} ); }; export default function WatchlistsScreen() { const { t } = useTranslation(); const router = useRouter(); const insets = useSafeAreaInsets(); const user = useAtomValue(userAtom); const streamystatsEnabled = useStreamystatsEnabled(); const { data: watchlists, isLoading, refetch } = useWatchlistsQuery(); const [refreshing, setRefreshing] = useState(false); const handleRefresh = useCallback(async () => { setRefreshing(true); await refetch(); setRefreshing(false); }, [refetch]); const handleCreatePress = useCallback(() => { router.push("/(auth)/(tabs)/(watchlists)/create"); }, [router]); const handleWatchlistPress = useCallback( (watchlistId: number) => { router.push(`/(auth)/(tabs)/(watchlists)/${watchlistId}`); }, [router], ); // Separate watchlists into "mine" and "public" const { myWatchlists, publicWatchlists } = useMemo(() => { if (!watchlists) return { myWatchlists: [], publicWatchlists: [] }; const mine: StreamystatsWatchlist[] = []; const pub: StreamystatsWatchlist[] = []; for (const w of watchlists) { if (w.userId === user?.Id) { mine.push(w); } else { pub.push(w); } } return { myWatchlists: mine, publicWatchlists: pub }; }, [watchlists, user?.Id]); // Combine into sections for FlashList const sections = useMemo(() => { const result: Array< | { type: "header"; title: string } | { type: "watchlist"; data: StreamystatsWatchlist; isOwner: boolean } > = []; if (myWatchlists.length > 0) { result.push({ type: "header", title: t("watchlists.my_watchlists") }); for (const w of myWatchlists) { result.push({ type: "watchlist", data: w, isOwner: true }); } } if (publicWatchlists.length > 0) { result.push({ type: "header", title: t("watchlists.public_watchlists") }); for (const w of publicWatchlists) { result.push({ type: "watchlist", data: w, isOwner: false }); } } return result; }, [myWatchlists, publicWatchlists, t]); if (!streamystatsEnabled) { return ; } if (!isLoading && (!watchlists || watchlists.length === 0)) { return ; } return ( } renderItem={({ item }) => { if (item.type === "header") { return ( {item.title} ); } return ( handleWatchlistPress(item.data.id)} /> ); }} getItemType={(item) => item.type} /> ); }