From 417a012c93a72a557c0bb7a73f1eed1af561ac80 Mon Sep 17 00:00:00 2001 From: space Date: Sun, 1 Mar 2026 19:06:34 +0100 Subject: [PATCH] feat: add disabled state to ImageRotatorCard and handle paused state in ImageRotatorWidget --- mobile/src/pages/datacards.tsx | 21 ++++++++++++++++++++- tv/src/components/DataCardWidget.tsx | 10 +++++++++- tv/src/types.ts | 2 ++ 3 files changed, 31 insertions(+), 2 deletions(-) diff --git a/mobile/src/pages/datacards.tsx b/mobile/src/pages/datacards.tsx index b075555..69361f0 100644 --- a/mobile/src/pages/datacards.tsx +++ b/mobile/src/pages/datacards.tsx @@ -68,6 +68,7 @@ interface ClockCard { interface ImageRotatorCard { id: string; type: "image_rotator"; name: string; config: { images: string[]; interval: number; fit: "cover" | "contain" }; + disabled?: boolean; layout?: CardLayout; } @@ -85,6 +86,7 @@ export type DataCard = CustomJsonCard | StaticTextCard | ClockCard | ImageRotato interface FormState { type: CardType; name: string; + disabled: boolean; // layout grid_col: number; grid_row: number; col_span: number; row_span: number; // display (custom_json / static_text / clock) @@ -112,6 +114,7 @@ interface FormState { const EMPTY_FORM: FormState = { type: "custom_json", name: "", + disabled: false, grid_col: 1, grid_row: 1, col_span: 1, row_span: 1, font_size: "16", text_color: "#ffffff", @@ -186,6 +189,7 @@ function cardToForm(card: DataCard): FormState { image_urls: card.config.images ?? [], image_interval: String(card.config.interval ?? 10), image_fit: card.config.fit ?? "cover", + disabled: (card as ImageRotatorCard).disabled ?? false, }; } if (card.type === "spotify") { @@ -246,6 +250,7 @@ function formToCard(form: FormState, id: string): DataCard { fit: form.image_fit, }, layout, + disabled: form.disabled, }; } if (form.type === "spotify") { @@ -967,6 +972,17 @@ function FormView({ form, onChange, onBoolChange, onLayoutChange, onTypeChange, onChange("name", v)} /> + {form.type === "image_rotator" && ( + + Paused + onBoolChange("disabled", v)} + trackColor={{ true: colors.accent, false: colors.border }} + thumbColor="#fff" + /> + + )} {form.type === "custom_json" && } @@ -1030,7 +1046,10 @@ function cardSubtitle(card: DataCard): string { function cardMeta(card: DataCard): string { if (card.type === "custom_json") return `Refresh: ${(card as CustomJsonCard).config.refresh_interval}s`; - if (card.type === "image_rotator") return `Fit: ${card.config.fit}`; + if (card.type === "image_rotator") { + const paused = (card as ImageRotatorCard).disabled ? "Paused · " : ""; + return `${paused}Fit: ${card.config.fit}`; + } if (card.type === "clock") return card.config.mode === "time" ? "Mode: live time" : "Mode: countdown"; if (card.type === "spotify") return `Refresh: ${(card as SpotifyCard).config.refresh_interval}s`; return ""; diff --git a/tv/src/components/DataCardWidget.tsx b/tv/src/components/DataCardWidget.tsx index 350da50..82c2ac1 100644 --- a/tv/src/components/DataCardWidget.tsx +++ b/tv/src/components/DataCardWidget.tsx @@ -242,6 +242,7 @@ function ImageRotatorWidget({ card, isNightMode, nightMessage }: { card: ImageRo useEffect(() => { if (isNightMode) return; + if (card.disabled) return; if (images.length <= 1) return; const ms = Math.max(2000, (card.config.interval ?? 10) * 1000); const timer = setInterval(() => { @@ -252,7 +253,7 @@ function ImageRotatorWidget({ card, isNightMode, nightMessage }: { card: ImageRo }, 400); }, ms); return () => clearInterval(timer); - }, [images.length, card.config.interval, isNightMode]); + }, [images.length, card.config.interval, isNightMode, card.disabled]); // ── Night mode overlay ──────────────────────────────────────────────────── if (isNightMode) { @@ -292,6 +293,13 @@ function ImageRotatorWidget({ card, isNightMode, nightMessage }: { card: ImageRo display: "block", }} /> + {/* Paused overlay when disabled */} + {card.disabled && ( +
+ + Paused +
+ )} {/* Name overlay */}
{card.name} diff --git a/tv/src/types.ts b/tv/src/types.ts index 56355ea..2d14dae 100644 --- a/tv/src/types.ts +++ b/tv/src/types.ts @@ -115,6 +115,8 @@ export interface ImageRotatorCard { name: string; config: ImageRotatorConfig; layout?: CardLayout; + /** When true the rotator is temporarily paused on the TV */ + disabled?: boolean; } // ─── spotify ──────────────────────────────────────────────────────────────────