From 6180c3b8b79bd85e646d462b2946976c6d54de15 Mon Sep 17 00:00:00 2001 From: DCCONSTRUCTIONS Date: Wed, 29 Jul 2026 15:30:35 +0300 Subject: [PATCH] fix(ui): restore background after playlist reset --- .../EnvironmentMediaPlaylistEditor.tsx | 17 ++--- .../core/environment/environmentSettings.ts | 30 +++++++++ .../test/environmentSettings.test.mjs | 62 +++++++++++++++++++ 3 files changed, 96 insertions(+), 13 deletions(-) diff --git a/apps/control-station/src/components/EnvironmentMediaPlaylistEditor.tsx b/apps/control-station/src/components/EnvironmentMediaPlaylistEditor.tsx index 5f6113f..70185bf 100644 --- a/apps/control-station/src/components/EnvironmentMediaPlaylistEditor.tsx +++ b/apps/control-station/src/components/EnvironmentMediaPlaylistEditor.tsx @@ -8,9 +8,10 @@ import { } from "@nodedc/ui-react"; import { - createEnvironmentMediaItem, + appendEnvironmentMediaItem, inferEnvironmentMediaKind, maxEnvironmentMediaItems, + removeEnvironmentMediaItem, type EnvironmentBackground, type EnvironmentMediaItem, type EnvironmentSurfaceId, @@ -135,10 +136,7 @@ export function EnvironmentMediaPlaylistEditor({ = maxEnvironmentMediaItems} - onClick={() => onChange({ - ...background, - items: [...background.items, createEnvironmentMediaItem()], - })} + onClick={() => onChange(appendEnvironmentMediaItem(background))} > @@ -202,14 +200,7 @@ export function EnvironmentMediaPlaylistEditor({ disabled={disabled || uploadingIds.has(item.id)} onClick={() => { setItemError(item.id); - const items = background.items.filter( - (candidate) => candidate.id !== item.id, - ); - onChange({ - ...background, - enabled: items.length ? background.enabled : false, - items, - }); + onChange(removeEnvironmentMediaItem(background, item.id)); }} > diff --git a/apps/control-station/src/core/environment/environmentSettings.ts b/apps/control-station/src/core/environment/environmentSettings.ts index b262834..2c2cd3d 100644 --- a/apps/control-station/src/core/environment/environmentSettings.ts +++ b/apps/control-station/src/core/environment/environmentSettings.ts @@ -75,6 +75,36 @@ export function createEnvironmentMediaItem(): EnvironmentMediaItem { }; } +export function appendEnvironmentMediaItem( + background: EnvironmentBackground, + item: EnvironmentMediaItem = createEnvironmentMediaItem(), +): EnvironmentBackground { + if ( + background.items.length >= maxEnvironmentMediaItems + || background.items.some((candidate) => candidate.id === item.id) + ) { + return background; + } + return { + ...background, + enabled: background.items.length === 0 ? true : background.enabled, + items: [...background.items, item], + }; +} + +export function removeEnvironmentMediaItem( + background: EnvironmentBackground, + itemId: string, +): EnvironmentBackground { + const items = background.items.filter((item) => item.id !== itemId); + if (items.length === background.items.length) return background; + return { + ...background, + enabled: items.length > 0 && background.enabled, + items, + }; +} + export function inferEnvironmentMediaKind(url: string): EnvironmentMediaKind { return /\.(mp4|webm|mov)(?:[?#].*)?$/i.test(url) ? "video" : "image"; } diff --git a/apps/control-station/test/environmentSettings.test.mjs b/apps/control-station/test/environmentSettings.test.mjs index efdec6b..144c3e5 100644 --- a/apps/control-station/test/environmentSettings.test.mjs +++ b/apps/control-station/test/environmentSettings.test.mjs @@ -143,3 +143,65 @@ test("editing a cloned page cannot mutate accepted settings", () => { assert.equal(accepted.pages.fleet.background.enabled, false); assert.equal(accepted.pages.fleet.background.items.length, 0); }); + +test("repopulating an emptied playlist restores its background lifecycle", () => { + const original = { + enabled: true, + imageDurationSeconds: 10, + items: [ + { + id: "media-original", + source: "url", + url: "https://example.test/original.mp4", + mediaKind: "video", + fileName: null, + }, + ], + }; + const emptied = environment.removeEnvironmentMediaItem( + original, + "media-original", + ); + const replacement = { + id: "media-replacement", + source: "file", + url: null, + mediaKind: null, + fileName: null, + }; + const repopulated = environment.appendEnvironmentMediaItem( + emptied, + replacement, + ); + + assert.equal(emptied.enabled, false); + assert.equal(emptied.items.length, 0); + assert.equal(repopulated.enabled, true); + assert.deepEqual(repopulated.items, [replacement]); +}); + +test("adding to an intentionally disabled nonempty playlist keeps it disabled", () => { + const disabled = { + enabled: false, + imageDurationSeconds: 10, + items: [ + { + id: "media-hidden", + source: "url", + url: "https://example.test/hidden.png", + mediaKind: "image", + fileName: null, + }, + ], + }; + const next = environment.appendEnvironmentMediaItem(disabled, { + id: "media-second", + source: "file", + url: null, + mediaKind: null, + fileName: null, + }); + + assert.equal(next.enabled, false); + assert.equal(next.items.length, 2); +});