fix react-hooks/exhaustive-deps warnings
This commit is contained in:
@@ -32,16 +32,14 @@ export default function PersonalSection({ itemId, showRewatchToggle }: Props) {
|
|||||||
// notes correctly without leaking state).
|
// notes correctly without leaking state).
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setLocalNote(entry?.note || '')
|
setLocalNote(entry?.note || '')
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
}, [itemId, entry?.note])
|
||||||
}, [itemId])
|
|
||||||
|
|
||||||
// Debounce writes by 400ms.
|
// Debounce writes by 400ms.
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (localNote === (entry?.note || '')) return
|
if (localNote === (entry?.note || '')) return
|
||||||
const id = setTimeout(() => setNote(itemId, localNote), 400)
|
const id = setTimeout(() => setNote(itemId, localNote), 400)
|
||||||
return () => clearTimeout(id)
|
return () => clearTimeout(id)
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
}, [localNote, entry?.note, itemId, setNote])
|
||||||
}, [localNote])
|
|
||||||
|
|
||||||
const rating = entry?.rating ?? 0
|
const rating = entry?.rating ?? 0
|
||||||
const rewatchCount = entry?.rewatchCount ?? 0
|
const rewatchCount = entry?.rewatchCount ?? 0
|
||||||
|
|||||||
@@ -95,21 +95,21 @@ function RouletteModal({
|
|||||||
|
|
||||||
const pick = pool[pickIndex % Math.max(1, pool.length)] || null
|
const pick = pool[pickIndex % Math.max(1, pool.length)] || null
|
||||||
|
|
||||||
function spin() {
|
const spin = useCallback(() => {
|
||||||
if (pool.length < 2) return
|
if (pool.length < 2) return
|
||||||
let next = pickIndex
|
let next = pickIndex
|
||||||
// Avoid landing on the same pick consecutively.
|
// Avoid landing on the same pick consecutively.
|
||||||
while (next === pickIndex) next = Math.floor(Math.random() * pool.length)
|
while (next === pickIndex) next = Math.floor(Math.random() * pool.length)
|
||||||
setPickIndex(next)
|
setPickIndex(next)
|
||||||
setSpinNonce(n => n + 1)
|
setSpinNonce(n => n + 1)
|
||||||
}
|
}, [pool.length, pickIndex])
|
||||||
|
|
||||||
function open() {
|
const open = useCallback(() => {
|
||||||
if (!pick) return
|
if (!pick) return
|
||||||
const mediaType = pick.media_type === 'tv' || pick.first_air_date ? 'tv' : 'movie'
|
const mediaType = pick.media_type === 'tv' || pick.first_air_date ? 'tv' : 'movie'
|
||||||
navigate(`/item/tmdb-${mediaType}-${pick.id}`)
|
navigate(`/item/tmdb-${mediaType}-${pick.id}`)
|
||||||
onClose()
|
onClose()
|
||||||
}
|
}, [pick, navigate, onClose])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
function onKey(e: KeyboardEvent) {
|
function onKey(e: KeyboardEvent) {
|
||||||
@@ -121,8 +121,7 @@ function RouletteModal({
|
|||||||
}
|
}
|
||||||
window.addEventListener('keydown', onKey)
|
window.addEventListener('keydown', onKey)
|
||||||
return () => window.removeEventListener('keydown', onKey)
|
return () => window.removeEventListener('keydown', onKey)
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
}, [pickIndex, pool.length, onClose, spin])
|
||||||
}, [pickIndex, pool.length])
|
|
||||||
|
|
||||||
const title = pick?.title || pick?.name || ''
|
const title = pick?.title || pick?.name || ''
|
||||||
const year = (pick?.release_date || pick?.first_air_date || '').slice(0, 4)
|
const year = (pick?.release_date || pick?.first_air_date || '').slice(0, 4)
|
||||||
|
|||||||
@@ -113,8 +113,7 @@ export default function RequestModal({ open, onClose, tmdbId, kind, tmdbData }:
|
|||||||
const init: Record<number, boolean> = {}
|
const init: Record<number, boolean> = {}
|
||||||
for (const s of tvSeasons) init[s.season_number ?? 0] = true
|
for (const s of tvSeasons) init[s.season_number ?? 0] = true
|
||||||
setSeasonsRequested(init)
|
setSeasonsRequested(init)
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
}, [kind, tvSeasons])
|
||||||
}, [tmdbData?.id])
|
|
||||||
|
|
||||||
// Esc closes.
|
// Esc closes.
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|||||||
@@ -133,6 +133,5 @@ export function usePlaybackReporting(args: Args) {
|
|||||||
debugLog('[playback] stop report failed', err),
|
debugLog('[playback] stop report failed', err),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
}, [itemId, mediaSourceId, startTimeTicks, progressRef])
|
||||||
}, [itemId])
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -76,6 +76,5 @@ export function usePrebuffer(item: BaseItemDto | null | undefined, armed: boolea
|
|||||||
}).catch(() => { /* warm-only; ignore */ })
|
}).catch(() => { /* warm-only; ignore */ })
|
||||||
|
|
||||||
return () => { cancelled = true }
|
return () => { cancelled = true }
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
}, [armed, item?.Id, item?.Type, qc])
|
||||||
}, [armed, item?.Id])
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -92,8 +92,7 @@ export default function LibraryPage({ type }: Props) {
|
|||||||
}, [selected.size, clearSelection])
|
}, [selected.size, clearSelection])
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
return () => clearSelection()
|
return () => clearSelection()
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
}, [clearSelection])
|
||||||
}, [])
|
|
||||||
|
|
||||||
const { data: libraries } = useLibraries()
|
const { data: libraries } = useLibraries()
|
||||||
const collectionType = COLLECTION_TYPE_MAP[type]
|
const collectionType = COLLECTION_TYPE_MAP[type]
|
||||||
|
|||||||
+13
-14
@@ -332,18 +332,22 @@ export default function PlayerPage() {
|
|||||||
|
|
||||||
/* Resume prompt: show on first mount when there's a saved position
|
/* Resume prompt: show on first mount when there's a saved position
|
||||||
* past the threshold AND the user wants the prompt AND the URL didn't
|
* past the threshold AND the user wants the prompt AND the URL didn't
|
||||||
* already specify ?resume=true (queue navigation path). */
|
* already specify ?resume=true (queue navigation path).
|
||||||
|
* Intentionally scoped to item?.Id only - we only want to evaluate
|
||||||
|
* the resume condition once per item, not re-trigger when item data
|
||||||
|
* refreshes or prefs change mid-playback. */
|
||||||
|
const resumePromptShownRef = useRef<string | null>(null)
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!item) return
|
if (!item || resumePromptShownRef.current === item.Id) return
|
||||||
const pos = Number(item.UserData?.PlaybackPositionTicks ?? 0)
|
const pos = Number(item.UserData?.PlaybackPositionTicks ?? 0)
|
||||||
const thresholdSec = usePreferencesStore.getState().resumeThresholdSec ?? 5
|
const thresholdSec = usePreferencesStore.getState().resumeThresholdSec ?? 5
|
||||||
const threshold = thresholdSec * 10_000_000
|
const threshold = thresholdSec * 10_000_000
|
||||||
const fromQueue = searchParams.get('resume') === 'true'
|
const fromQueue = searchParams.get('resume') === 'true'
|
||||||
if (showResumePromptPref && !fromQueue && pos > threshold) {
|
if (showResumePromptPref && !fromQueue && pos > threshold) {
|
||||||
setResumePromptOpen(true)
|
setResumePromptOpen(true)
|
||||||
|
resumePromptShownRef.current = item.Id
|
||||||
}
|
}
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
}, [item?.Id, searchParams, showResumePromptPref])
|
||||||
}, [item?.Id])
|
|
||||||
|
|
||||||
/* Auto-rewatch counter: when an already-played item starts playing
|
/* Auto-rewatch counter: when an already-played item starts playing
|
||||||
* again, record the rewatch. We trip it at most once per item-mount
|
* again, record the rewatch. We trip it at most once per item-mount
|
||||||
@@ -358,8 +362,7 @@ export default function PlayerPage() {
|
|||||||
if (rewatchedItemIdRef.current === item.Id) return
|
if (rewatchedItemIdRef.current === item.Id) return
|
||||||
rewatchedItemIdRef.current = item.Id
|
rewatchedItemIdRef.current = item.Id
|
||||||
usePersonalData.getState().incrementRewatch(item.Id)
|
usePersonalData.getState().incrementRewatch(item.Id)
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
}, [item])
|
||||||
}, [item?.Id])
|
|
||||||
|
|
||||||
/* Auto-recap trigger: decide once per item. Recap card waits for the
|
/* Auto-recap trigger: decide once per item. Recap card waits for the
|
||||||
* resume prompt (if any) to resolve before appearing. */
|
* resume prompt (if any) to resolve before appearing. */
|
||||||
@@ -376,8 +379,7 @@ export default function PlayerPage() {
|
|||||||
} else {
|
} else {
|
||||||
setRecapPending(false)
|
setRecapPending(false)
|
||||||
}
|
}
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
}, [item?.Id, recapTrigger.shouldShow, showRecapCardPref, item])
|
||||||
}, [item?.Id, recapTrigger.shouldShow])
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (recapPending && !resumePromptOpen) {
|
if (recapPending && !resumePromptOpen) {
|
||||||
setRecapCardOpen(true)
|
setRecapCardOpen(true)
|
||||||
@@ -429,8 +431,7 @@ export default function PlayerPage() {
|
|||||||
qc.removeQueries({ queryKey: ['jellyfin', 'episodes', evictId], exact: false })
|
qc.removeQueries({ queryKey: ['jellyfin', 'episodes', evictId], exact: false })
|
||||||
} catch { /* ignore */ }
|
} catch { /* ignore */ }
|
||||||
}
|
}
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
}, [id, qc])
|
||||||
}, [id])
|
|
||||||
|
|
||||||
/* Aggressive teardown on player unmount.
|
/* Aggressive teardown on player unmount.
|
||||||
*
|
*
|
||||||
@@ -638,8 +639,7 @@ export default function PlayerPage() {
|
|||||||
p.currentTime = target
|
p.currentTime = target
|
||||||
if (seriesId) recordSkippedSeconds(seriesId, 'credits', target - from)
|
if (seriesId) recordSkippedSeconds(seriesId, 'credits', target - from)
|
||||||
}
|
}
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
}, [currentMarker?.type, currentMarker?.startSec, skipIntros, skipCredits, duration, seriesId])
|
||||||
}, [currentMarker?.type, currentMarker?.startSec, skipIntros, skipCredits])
|
|
||||||
|
|
||||||
/* Imperatively switch the active subtitle track. We use 'hidden' rather
|
/* Imperatively switch the active subtitle track. We use 'hidden' rather
|
||||||
* than 'showing' so the browser doesn't paint its own caption UI over our
|
* than 'showing' so the browser doesn't paint its own caption UI over our
|
||||||
@@ -720,8 +720,7 @@ export default function PlayerPage() {
|
|||||||
// default: prefer a language match, then default-flagged, then off
|
// default: prefer a language match, then default-flagged, then off
|
||||||
const match = pickSubtitle(subs, subtitleLanguage)
|
const match = pickSubtitle(subs, subtitleLanguage)
|
||||||
setSubtitleIndex(match?.Index ?? null)
|
setSubtitleIndex(match?.Index ?? null)
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
}, [id, mediaSourceId, subtitleStreams.length, subtitleMode, subtitleLanguage, item])
|
||||||
}, [id, mediaSourceId, subtitleStreams.length, subtitleMode, subtitleLanguage])
|
|
||||||
|
|
||||||
/* ── Playback reporting ──────────────────────────────────── */
|
/* ── Playback reporting ──────────────────────────────────── */
|
||||||
const playSessionId = playbackInfo?.PlaySessionId || undefined
|
const playSessionId = playbackInfo?.PlaySessionId || undefined
|
||||||
|
|||||||
@@ -110,8 +110,7 @@ export function SurpriseMeModal({
|
|||||||
} else if (!open) {
|
} else if (!open) {
|
||||||
setPick(null)
|
setPick(null)
|
||||||
}
|
}
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
}, [open, items])
|
||||||
}, [open, items.length])
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AnimatePresence>
|
<AnimatePresence>
|
||||||
|
|||||||
@@ -117,8 +117,7 @@ export function ShortcutsSection() {
|
|||||||
}
|
}
|
||||||
window.addEventListener('keydown', onKey, true)
|
window.addEventListener('keydown', onKey, true)
|
||||||
return () => window.removeEventListener('keydown', onKey, true)
|
return () => window.removeEventListener('keydown', onKey, true)
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
}, [capturingId, overrides, commit])
|
||||||
}, [capturingId, overrides])
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Section
|
<Section
|
||||||
|
|||||||
Reference in New Issue
Block a user