{"version":3,"sources":["../../src/hooks/useWallScenes.ts"],"names":[],"mappings":"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","file":"useWallScenes.mjs","sourcesContent":["import { useCallback, useEffect, useRef, useState } from 'react';\nimport type { WallScene } from '../types/wall';\n\nexport interface UseWallScenesOptions {\n  /** Endpoint that returns `{ scenes: WallScene[] }` newest-first. Defaults to '/api/wall-scenes'. */\n  scenesEndpoint?: string;\n  /** Public scene poll interval. 0 disables polling. Default 10000. */\n  pollMs?: number;\n  /** Auto-rotation interval. 0 disables rotation. Default 10000. */\n  rotationMs?: number;\n}\n\nexport interface UseWallScenesResult {\n  scenes: WallScene[];\n  activeScene: WallScene | null;\n  /** Index of the active scene in the (newest-first) `scenes` array — useful for thumbnail highlight. */\n  activeSceneIndex: number;\n  /** Jump rotation to a specific scene; resets the rotation timer. */\n  setActiveScene: (id: string) => void;\n  /** Re-fetch scenes (used after creating one). */\n  refresh: () => Promise<void>;\n}\n\n/**\n * Manages the wall scene list, public polling, and the auto-rotating slideshow\n * with new-arrival interrupt logic for TV/live wall mode.\n *\n * Behaviour:\n * - `scenes` is the raw list (newest-first) returned by `scenesEndpoint`. Updated on poll.\n * - On hard reload: rotation order = scenes order; `activeScene` starts at scenes[0].\n * - On 10s rotation tick: advance to the next id in `playOrder`, wrapping at the end.\n * - When poll detects new ids: insert them right after the currently displayed scene\n *   in `playOrder`, jump display to the first new scene (interrupt), and reset the\n *   rotation timer so the new scene gets a full cycle. Thumbnail order (= scenes\n *   array) keeps showing newest first — the rotation order is independent.\n * - Deleted scenes are pruned from `playOrder`.\n */\nexport function useWallScenes(opts?: UseWallScenesOptions): UseWallScenesResult {\n  const scenesEndpoint = opts?.scenesEndpoint ?? '/api/wall-scenes';\n  const pollMs = opts?.pollMs ?? 10000;\n  const rotationMs = opts?.rotationMs ?? 10000;\n\n  const [scenes, setScenes] = useState<WallScene[]>([]);\n  const [activeSceneId, setActiveSceneId] = useState<string | null>(null);\n\n  const playOrderRef = useRef<string[]>([]);\n  const seenIdsRef = useRef<Set<string>>(new Set());\n  const activeSceneIdRef = useRef<string | null>(null);\n  const rotationTimerRef = useRef<ReturnType<typeof setInterval> | null>(null);\n  const scenePollRef = useRef<ReturnType<typeof setInterval> | null>(null);\n\n  const setActiveSceneIdSync = useCallback((id: string | null) => {\n    activeSceneIdRef.current = id;\n    setActiveSceneId(id);\n  }, []);\n\n  const startRotation = useCallback(() => {\n    if (rotationMs <= 0) return;\n    if (rotationTimerRef.current) clearInterval(rotationTimerRef.current);\n    rotationTimerRef.current = setInterval(() => {\n      const order = playOrderRef.current;\n      if (order.length <= 1) return;\n      const current = activeSceneIdRef.current;\n      const idx = current ? order.indexOf(current) : -1;\n      const next = order[(idx + 1) % order.length];\n      activeSceneIdRef.current = next;\n      setActiveSceneId(next);\n    }, rotationMs);\n  }, [rotationMs]);\n\n  const refresh = useCallback(async () => {\n    try {\n      const res = await fetch(scenesEndpoint, { cache: 'no-store' });\n      const data: { scenes?: WallScene[] } = await res.json();\n      const newScenes = data.scenes ?? [];\n      setScenes(newScenes);\n\n      const allIds = newScenes.map(s => s.id);\n      const existingIdSet = new Set(allIds);\n\n      if (seenIdsRef.current.size === 0) {\n        // First load: rotation order = scenes order (newest-first).\n        playOrderRef.current = allIds.slice();\n        seenIdsRef.current = new Set(allIds);\n        if (allIds.length > 0) {\n          setActiveSceneIdSync(allIds[0]);\n          startRotation();\n        }\n      } else {\n        const newIds = allIds.filter(id => !seenIdsRef.current.has(id));\n        const cleanedOrder = playOrderRef.current.filter(id =>\n          existingIdSet.has(id),\n        );\n\n        if (newIds.length > 0) {\n          // Insert new ids right after the currently displayed scene, then\n          // interrupt rotation by jumping to the first new id.\n          const currentId = activeSceneIdRef.current;\n          const insertAfter = currentId\n            ? cleanedOrder.indexOf(currentId)\n            : -1;\n          const head = cleanedOrder.slice(0, insertAfter + 1);\n          const tail = cleanedOrder.slice(insertAfter + 1);\n          playOrderRef.current = [...head, ...newIds, ...tail];\n          newIds.forEach(id => seenIdsRef.current.add(id));\n          setActiveSceneIdSync(newIds[0]);\n          startRotation(); // reset timer so the new scene gets a full cycle\n        } else {\n          playOrderRef.current = cleanedOrder;\n          // If the active scene was deleted, fall back to the first one.\n          if (\n            activeSceneIdRef.current &&\n            !existingIdSet.has(activeSceneIdRef.current)\n          ) {\n            const first = cleanedOrder[0] || null;\n            setActiveSceneIdSync(first);\n            startRotation();\n          }\n        }\n      }\n    } catch {\n      /* network errors are silent — we'll retry on the next poll */\n    }\n  }, [scenesEndpoint, setActiveSceneIdSync, startRotation]);\n\n  // Initial fetch + public scene poll.\n  useEffect(() => {\n    refresh();\n    if (pollMs > 0) {\n      scenePollRef.current = setInterval(() => {\n        refresh();\n      }, pollMs);\n    }\n    return () => {\n      if (scenePollRef.current) clearInterval(scenePollRef.current);\n    };\n  }, [refresh, pollMs]);\n\n  // Cleanup rotation timer on unmount.\n  useEffect(() => {\n    return () => {\n      if (rotationTimerRef.current) clearInterval(rotationTimerRef.current);\n    };\n  }, []);\n\n  const setActiveScene = useCallback(\n    (id: string) => {\n      setActiveSceneIdSync(id);\n      startRotation();\n    },\n    [setActiveSceneIdSync, startRotation],\n  );\n\n  const activeScene =\n    scenes.find(s => s.id === activeSceneId) ?? scenes[0] ?? null;\n  const activeSceneIndex = activeScene\n    ? scenes.findIndex(s => s.id === activeScene.id)\n    : 0;\n\n  return {\n    scenes,\n    activeScene,\n    activeSceneIndex,\n    setActiveScene,\n    refresh,\n  };\n}\n"]}