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);
+});