import { create } from "zustand"; import { persist } from "zustand/middleware"; import { immer } from "zustand/middleware/immer"; import { PlayerMeta } from "@/stores/player/slices/source"; export interface ProgressItem { watched: number; duration: number; } export interface ProgressSeasonItem { title: string; number: number; id: string; } export interface ProgressEpisodeItem { title: string; number: number; id: string; seasonId: string; updatedAt: number; progress: ProgressItem; } export interface ProgressMediaItem { title: string; year?: number; poster?: string; type: "show" | "movie"; progress?: ProgressItem; updatedAt: number; seasons: Record; episodes: Record; } export interface ProgressUpdateItem { title?: string; year?: number; poster?: string; type?: "show" | "movie"; progress?: ProgressItem; tmdbId: string; id: string; episodeId?: string; seasonId?: string; episodeNumber?: number; seasonNumber?: number; action: "upsert" | "delete"; } export interface UpdateItemOptions { meta: PlayerMeta; progress: ProgressItem; } export interface ProgressStore { items: Record; updateQueue: ProgressUpdateItem[]; updateItem(ops: UpdateItemOptions): void; removeItem(id: string): void; replaceItems(items: Record): void; clear(): void; clearUpdateQueue(): void; removeUpdateItem(id: string): void; } let updateId = 0; export const useProgressStore = create( persist( immer((set) => ({ items: {}, updateQueue: [], removeItem(id) { set((s) => { updateId += 1; s.updateQueue.push({ id: updateId.toString(), action: "delete", tmdbId: id, }); delete s.items[id]; }); }, replaceItems(items: Record) { set((s) => { s.items = items; }); }, updateItem({ meta, progress }) { set((s) => { // add to updateQueue updateId += 1; s.updateQueue.push({ tmdbId: meta.tmdbId, title: meta.title, year: meta.releaseYear, poster: meta.poster, type: meta.type, progress: { ...progress }, id: updateId.toString(), episodeId: meta.episode?.tmdbId, seasonId: meta.season?.tmdbId, seasonNumber: meta.season?.number, episodeNumber: meta.episode?.number, action: "upsert", }); // add to progress store if (!s.items[meta.tmdbId]) s.items[meta.tmdbId] = { type: meta.type, episodes: {}, seasons: {}, updatedAt: 0, title: meta.title, year: meta.releaseYear, poster: meta.poster, }; const item = s.items[meta.tmdbId]; item.updatedAt = Date.now(); if (meta.type === "movie") { if (!item.progress) item.progress = { duration: 0, watched: 0, }; item.progress = { ...progress }; return; } if (!meta.episode || !meta.season) return; if (!item.seasons[meta.season.tmdbId]) item.seasons[meta.season.tmdbId] = { id: meta.season.tmdbId, number: meta.season.number, title: meta.season.title, }; if (!item.episodes[meta.episode.tmdbId]) item.episodes[meta.episode.tmdbId] = { id: meta.episode.tmdbId, number: meta.episode.number, title: meta.episode.title, seasonId: meta.season.tmdbId, updatedAt: Date.now(), progress: { duration: 0, watched: 0, }, }; item.episodes[meta.episode.tmdbId].progress = { ...progress }; }); }, clear() { set((s) => { s.items = {}; }); }, clearUpdateQueue() { set((s) => { s.updateQueue = []; }); }, removeUpdateItem(id: string) { set((s) => { s.updateQueue = [...s.updateQueue.filter((v) => v.id !== id)]; }); }, })), { name: "__MW::progress", }, ), );