import { useMutation } from "@tanstack/react-query"; import { useAtomValue } from "jotai"; import { useCallback } from "react"; import { toast } from "sonner-native"; import { useNetworkAwareQueryClient } from "@/hooks/useNetworkAwareQueryClient"; import { apiAtom } from "@/providers/JellyfinProvider"; import { useSettings } from "@/utils/atoms/settings"; import { createStreamystatsApi } from "@/utils/streamystats/api"; import type { CreateWatchlistRequest, StreamystatsWatchlist, UpdateWatchlistRequest, } from "@/utils/streamystats/types"; /** * Hook to create a new watchlist */ export const useCreateWatchlist = () => { const api = useAtomValue(apiAtom); const { settings } = useSettings(); const queryClient = useNetworkAwareQueryClient(); const mutation = useMutation({ mutationFn: async ( data: CreateWatchlistRequest, ): Promise => { if (!settings?.streamyStatsServerUrl || !api?.accessToken) { throw new Error("Streamystats not configured"); } const streamystatsApi = createStreamystatsApi({ serverUrl: settings.streamyStatsServerUrl, jellyfinToken: api.accessToken, }); const response = await streamystatsApi.createWatchlist(data); if (response.error) { throw new Error(response.error); } return response.data; }, onSuccess: () => { queryClient.invalidateQueries({ queryKey: ["streamystats", "watchlists"], }); toast.success("Watchlist created"); }, onError: (error: Error) => { toast.error(error.message || "Failed to create watchlist"); }, }); return mutation; }; /** * Hook to update a watchlist */ export const useUpdateWatchlist = () => { const api = useAtomValue(apiAtom); const { settings } = useSettings(); const queryClient = useNetworkAwareQueryClient(); const mutation = useMutation({ mutationFn: async ({ watchlistId, data, }: { watchlistId: number; data: UpdateWatchlistRequest; }): Promise => { if (!settings?.streamyStatsServerUrl || !api?.accessToken) { throw new Error("Streamystats not configured"); } const streamystatsApi = createStreamystatsApi({ serverUrl: settings.streamyStatsServerUrl, jellyfinToken: api.accessToken, }); const response = await streamystatsApi.updateWatchlist(watchlistId, data); if (response.error) { throw new Error(response.error); } return response.data; }, onSuccess: (_data, variables) => { queryClient.invalidateQueries({ queryKey: ["streamystats", "watchlists"], }); queryClient.invalidateQueries({ queryKey: ["streamystats", "watchlist", variables.watchlistId], }); toast.success("Watchlist updated"); }, onError: (error: Error) => { toast.error(error.message || "Failed to update watchlist"); }, }); return mutation; }; /** * Hook to delete a watchlist */ export const useDeleteWatchlist = () => { const api = useAtomValue(apiAtom); const { settings } = useSettings(); const queryClient = useNetworkAwareQueryClient(); const mutation = useMutation({ mutationFn: async (watchlistId: number): Promise => { if (!settings?.streamyStatsServerUrl || !api?.accessToken) { throw new Error("Streamystats not configured"); } const streamystatsApi = createStreamystatsApi({ serverUrl: settings.streamyStatsServerUrl, jellyfinToken: api.accessToken, }); const response = await streamystatsApi.deleteWatchlist(watchlistId); if (response.error) { throw new Error(response.error); } }, onSuccess: (_data, watchlistId) => { queryClient.invalidateQueries({ queryKey: ["streamystats", "watchlists"], }); queryClient.removeQueries({ queryKey: ["streamystats", "watchlist", watchlistId], }); toast.success("Watchlist deleted"); }, onError: (error: Error) => { toast.error(error.message || "Failed to delete watchlist"); }, }); return mutation; }; /** * Hook to add an item to a watchlist with optimistic update */ export const useAddToWatchlist = () => { const api = useAtomValue(apiAtom); const { settings } = useSettings(); const queryClient = useNetworkAwareQueryClient(); const mutation = useMutation({ mutationFn: async ({ watchlistId, itemId, }: { watchlistId: number; itemId: string; watchlistName?: string; }): Promise => { if (!settings?.streamyStatsServerUrl || !api?.accessToken) { throw new Error("Streamystats not configured"); } const streamystatsApi = createStreamystatsApi({ serverUrl: settings.streamyStatsServerUrl, jellyfinToken: api.accessToken, }); const response = await streamystatsApi.addWatchlistItem( watchlistId, itemId, ); if (response.error) { throw new Error(response.error); } }, onSuccess: (_data, variables) => { queryClient.invalidateQueries({ queryKey: ["streamystats", "watchlist", variables.watchlistId], }); queryClient.invalidateQueries({ queryKey: ["streamystats", "watchlistItems", variables.watchlistId], }); queryClient.invalidateQueries({ queryKey: ["streamystats", "itemInWatchlists", variables.itemId], }); if (variables.watchlistName) { toast.success(`Added to ${variables.watchlistName}`); } else { toast.success("Added to watchlist"); } }, onError: (error: Error) => { toast.error(error.message || "Failed to add to watchlist"); }, }); return mutation; }; /** * Hook to remove an item from a watchlist with optimistic update */ export const useRemoveFromWatchlist = () => { const api = useAtomValue(apiAtom); const { settings } = useSettings(); const queryClient = useNetworkAwareQueryClient(); const mutation = useMutation({ mutationFn: async ({ watchlistId, itemId, }: { watchlistId: number; itemId: string; watchlistName?: string; }): Promise => { if (!settings?.streamyStatsServerUrl || !api?.accessToken) { throw new Error("Streamystats not configured"); } const streamystatsApi = createStreamystatsApi({ serverUrl: settings.streamyStatsServerUrl, jellyfinToken: api.accessToken, }); const response = await streamystatsApi.removeWatchlistItem( watchlistId, itemId, ); if (response.error) { throw new Error(response.error); } }, onSuccess: (_data, variables) => { queryClient.invalidateQueries({ queryKey: ["streamystats", "watchlist", variables.watchlistId], }); queryClient.invalidateQueries({ queryKey: ["streamystats", "watchlistItems", variables.watchlistId], }); queryClient.invalidateQueries({ queryKey: ["streamystats", "itemInWatchlists", variables.itemId], }); if (variables.watchlistName) { toast.success(`Removed from ${variables.watchlistName}`); } else { toast.success("Removed from watchlist"); } }, onError: (error: Error) => { toast.error(error.message || "Failed to remove from watchlist"); }, }); return mutation; }; /** * Hook to toggle an item in a watchlist */ export const useToggleWatchlistItem = () => { const addMutation = useAddToWatchlist(); const removeMutation = useRemoveFromWatchlist(); const toggle = useCallback( async (params: { watchlistId: number; itemId: string; isInWatchlist: boolean; watchlistName?: string; }) => { if (params.isInWatchlist) { await removeMutation.mutateAsync({ watchlistId: params.watchlistId, itemId: params.itemId, watchlistName: params.watchlistName, }); } else { await addMutation.mutateAsync({ watchlistId: params.watchlistId, itemId: params.itemId, watchlistName: params.watchlistName, }); } }, [addMutation, removeMutation], ); return { toggle, isLoading: addMutation.isPending || removeMutation.isPending, }; };