improvements to the sleep timer
🏗️ Build and Test JellyTau / Run Tests (push) Failing after 14s
Traceability Validation / Check Requirement Traces (push) Failing after 2s
🏗️ Build and Test JellyTau / Build Android APK (push) Has been skipped

This commit is contained in:
2026-02-28 20:33:22 +01:00
parent e8e37649fa
commit c5be9eb18c
19 changed files with 475 additions and 57 deletions
@@ -5,24 +5,32 @@
sleepTimerActive,
} from "$lib/stores/sleepTimer";
import { currentQueueItem } from "$lib/stores/queue";
import ScrollPicker from "$lib/components/common/ScrollPicker.svelte";
interface Props {
isOpen?: boolean;
onClose?: () => void;
mediaType?: string; // Override queue-based detection (e.g. for video player)
}
let { isOpen = false, onClose }: Props = $props();
let { isOpen = false, onClose, mediaType }: Props = $props();
const timePickerItems = [
{ value: 15, label: "15 min" },
{ value: 30, label: "30 min" },
{ value: 45, label: "45 min" },
{ value: 60, label: "60 min" },
];
let selectedMinutes = $state(30);
const timePresets = [15, 30, 45, 60];
const episodePresets = [1, 2, 3];
const isEpisode = $derived($currentQueueItem?.type === "Episode");
const isVideo = $derived(
$currentQueueItem?.type === "Episode" || $currentQueueItem?.type === "Movie"
);
const effectiveType = $derived(mediaType ?? $currentQueueItem?.type);
const isEpisode = $derived(effectiveType === "Episode");
function handleTimePreset(minutes: number) {
sleepTimer.setTimeTimer(minutes);
function handleSetTimer() {
sleepTimer.setTimeTimer(selectedMinutes);
onClose?.();
}
@@ -62,7 +70,7 @@
}
function getEndOfTrackLabel(): string {
const type = $currentQueueItem?.type;
const type = effectiveType;
if (type === "Episode") return "End of current episode";
if (type === "Movie") return "End of current film";
return "End of current track";
@@ -73,7 +81,7 @@
<div
class="fixed inset-0 bg-black/60 z-[60] flex items-end sm:items-center justify-center p-0 sm:p-4"
onclick={handleBackdropClick}
onkeydown={(e) => { if (e.key === 'Escape') handleBackdropClick(); }}
onkeydown={(e) => { if (e.key === 'Escape') onClose?.(); }}
role="dialog"
aria-modal="true"
aria-labelledby="sleep-timer-title"
@@ -144,19 +152,23 @@
</div>
{/if}
<!-- Time presets -->
<!-- Time roller -->
<div class="mb-6">
<h3 class="text-sm font-medium text-gray-400 mb-3">Stop after time</h3>
<div class="grid grid-cols-2 gap-2">
{#each timePresets as minutes}
<button
onclick={() => handleTimePreset(minutes)}
class="p-4 rounded-lg border border-gray-800 hover:border-[var(--color-jellyfin)]/50 hover:bg-[var(--color-jellyfin)]/5 transition-all text-center"
>
<span class="text-lg font-medium text-white">{minutes}</span>
<span class="text-sm text-gray-400 ml-1">min</span>
</button>
{/each}
<div class="flex flex-col items-center gap-3">
<ScrollPicker
items={timePickerItems}
selectedValue={selectedMinutes}
visibleCount={3}
itemHeight={56}
onSelect={(val) => { selectedMinutes = val as number; }}
/>
<button
onclick={handleSetTimer}
class="w-full py-3 rounded-lg bg-[var(--color-jellyfin)] text-white font-semibold hover:opacity-90 transition-opacity"
>
Set {selectedMinutes} min timer
</button>
</div>
</div>