A music player that connects to your cloud/distributed storage.
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

feat: output constituents

+146 -17
+41 -1
src/components/configurator/output/element.js
··· 2 2 import { batch, computed, signal } from "@common/signal.js"; 3 3 4 4 /** 5 - * @import {Track} from "@definitions/types.d.ts" 5 + * @import {Constituent, Track} from "@definitions/types.d.ts" 6 6 * @import {OutputManagerDeputy, OutputElement} from "@components/output/types.d.ts" 7 7 */ 8 8 ··· 27 27 28 28 /** @type {OutputManagerDeputy} */ 29 29 const manager = { 30 + constituents: { 31 + collection: computed(() => { 32 + const out = this.#selectedOutput.value; 33 + if (out) return out.constituents.collection(); 34 + 35 + const def = this.#defaultOutput.value; 36 + if (def) return def.constituents.collection(); 37 + 38 + return this.#memory.constituents.value; 39 + }), 40 + reload: () => { 41 + const def = this.#defaultOutput.value; 42 + if (def) def.constituents.reload(); 43 + 44 + const out = this.#selectedOutput.value; 45 + if (out) return out.constituents.reload(); 46 + 47 + return Promise.resolve(); 48 + }, 49 + save: async (newConstituents) => { 50 + const def = this.#defaultOutput.value; 51 + if (def) await def.constituents.save(newConstituents); 52 + 53 + const out = this.#selectedOutput.value; 54 + if (out) return await out.constituents.save(newConstituents); 55 + 56 + this.#memory.constituents.value = newConstituents; 57 + }, 58 + state: computed(() => { 59 + const out = this.#selectedOutput.value; 60 + if (out) return out.constituents.state(); 61 + 62 + const def = this.#defaultOutput.value; 63 + if (def) return def.constituents.state(); 64 + 65 + return this.#setupFinished.value ? "loaded" : "sleeping"; 66 + }), 67 + }, 30 68 tracks: { 31 69 collection: computed(() => { 32 70 const out = this.#selectedOutput.value; ··· 68 106 }; 69 107 70 108 // Assign manager properties to class 109 + this.constituents = manager.constituents; 71 110 this.tracks = manager.tracks; 72 111 } 73 112 ··· 78 117 ); 79 118 80 119 #memory = { 120 + constituents: signal(/** @type {Constituent[]} */ ([])), 81 121 tracks: signal(/** @type {Track[]} */ ([])), 82 122 }; 83 123
+31 -2
src/components/output/common.js
··· 1 1 import { computed, signal, untracked } from "@common/signal.js"; 2 2 3 3 /** 4 - * @import {Track} from "@definitions/types.d.ts" 4 + * @import {Constituent, Track} from "@definitions/types.d.ts" 5 5 * @import {OutputManager, OutputManagerProperties} from "./types.d.ts" 6 6 */ 7 7 ··· 10 10 * @param {OutputManagerProperties<Encoding>} _ 11 11 * @returns {OutputManager<Encoding>} 12 12 */ 13 - export function outputManager({ init, tracks }) { 13 + export function outputManager({ init, constituents, tracks }) { 14 + const c = signal( 15 + /** @type {Encoding extends null ? Constituent[] : Encoding} */ (constituents 16 + .empty()), 17 + ); 18 + const cs = signal( 19 + /** @type {"loading" | "loaded" | "sleeping"} */ ("sleeping"), 20 + ); 21 + 14 22 const t = signal( 15 23 /** @type {Encoding extends null ? Track[] : Encoding} */ (tracks.empty()), 16 24 ); ··· 18 26 /** @type {"loading" | "loaded" | "sleeping"} */ ("sleeping"), 19 27 ); 20 28 29 + async function loadConstituents() { 30 + if (init && (await init()) === false) return; 31 + cs.value = "loading"; 32 + c.value = await constituents.get(); 33 + cs.value = "loaded"; 34 + } 35 + 21 36 async function loadTracks() { 22 37 if (init && (await init()) === false) return; 23 38 ts.value = "loading"; ··· 26 41 } 27 42 28 43 return { 44 + constituents: { 45 + collection: computed(() => { 46 + if (untracked(() => cs.value === "sleeping")) loadConstituents(); 47 + return c.value; 48 + }), 49 + reload: loadConstituents, 50 + save: async (newConstituents) => { 51 + if (untracked(() => cs.value === "sleeping")) loadConstituents(); 52 + c.value = newConstituents; 53 + await constituents.put(newConstituents); 54 + }, 55 + state: cs.get, 56 + }, 29 57 tracks: { 30 58 collection: computed(() => { 31 59 if (untracked(() => ts.value === "sleeping")) loadTracks(); ··· 40 68 state: ts.get, 41 69 }, 42 70 signals: { 71 + constituents: c, 43 72 tracks: t, 44 73 }, 45 74 };
+7 -1
src/components/output/polymorphic/indexed-db/element.js
··· 12 12 //////////////////////////////////////////// 13 13 14 14 /** 15 - * @implements {OutputElement<any>} 15 + * @implements {OutputElement<SupportedDataTypes>} 16 16 */ 17 17 class IndexedDBOutput extends BroadcastableDiffuseElement { 18 18 static NAME = "diffuse/output/polymorphic/indexed-db"; ··· 28 28 29 29 /** @type {OutputManager<SupportedDataTypes>} */ 30 30 this.#manager = outputManager({ 31 + constituents: { 32 + empty: () => undefined, 33 + get: () => this.#get("constituents"), 34 + put: (data) => this.#put("constituents", data), 35 + }, 31 36 init: () => this.whenConnected(), 32 37 tracks: { 33 38 empty: () => undefined, ··· 36 41 }, 37 42 }); 38 43 44 + this.constituents = this.#manager.constituents; 39 45 this.tracks = this.#manager.tracks; 40 46 } 41 47
+17 -1
src/components/output/types.d.ts
··· 1 1 import type { Signal, SignalReader } from "@common/signal.d.ts"; 2 2 import type { DiffuseElement } from "@common/element.js"; 3 - import type { Track } from "@definitions/types.d.ts"; 3 + import type { Constituent, Track } from "@definitions/types.d.ts"; 4 4 5 5 export type OutputElement<Encoding = null> = 6 6 & DiffuseElement ··· 12 12 >; 13 13 14 14 export type OutputManager<Encoding = null> = { 15 + constituents: { 16 + collection: SignalReader<Encoding extends null ? Constituent[] : Encoding>; 17 + reload: () => Promise<void>; 18 + save: ( 19 + constituents: Encoding extends null ? Constituent[] : Encoding, 20 + ) => Promise<void>; 21 + state: SignalReader<"loading" | "loaded" | "sleeping">; 22 + }; 15 23 signals: { 24 + constituents: Signal<Encoding extends null ? Constituent[] : Encoding>; 16 25 tracks: Signal<Encoding extends null ? Track[] : Encoding>; 17 26 }; 18 27 tracks: { ··· 24 33 }; 25 34 26 35 export type OutputManagerProperties<Encoding = null> = { 36 + constituents: { 37 + empty(): Encoding extends null ? Constituent[] : Encoding; 38 + get(): Promise<Encoding extends null ? Constituent[] : Encoding>; 39 + put( 40 + constituents: Encoding extends null ? Constituent[] : Encoding, 41 + ): Promise<void>; 42 + }; 27 43 init?: () => Promise<boolean>; 28 44 tracks: { 29 45 empty(): Encoding extends null ? Track[] : Encoding;
+17
src/components/transformer/output/base.js
··· 42 42 base() { 43 43 /** @type {OutputManagerDeputy<T | undefined>} */ 44 44 const m = { 45 + constituents: { 46 + collection: computed(() => { 47 + return this.output.signal()?.constituents?.collection(); 48 + }), 49 + reload: () => { 50 + return this.output.signal()?.constituents?.reload() ?? 51 + Promise.resolve(); 52 + }, 53 + save: async (newConstituents) => { 54 + if (newConstituents === undefined) return; 55 + await this.output.whenDefined; 56 + await this.output.signal()?.constituents.save(newConstituents); 57 + }, 58 + state: computed(() => { 59 + return this.output.signal()?.constituents.state() ?? "sleeping"; 60 + }), 61 + }, 45 62 tracks: { 46 63 collection: computed(() => { 47 64 return this.output.signal()?.tracks?.collection();
+7
src/components/transformer/output/refiner/default/element.js
··· 17 17 18 18 /** @type {OutputManagerDeputy} */ 19 19 const manager = { 20 + constituents: { 21 + ...base.constituents, 22 + collection: computed(() => { 23 + return base.constituents.collection() ?? []; 24 + }), 25 + }, 20 26 tracks: { 21 27 ...base.tracks, 22 28 collection: computed(() => { ··· 30 36 }; 31 37 32 38 // Assign manager properties to class 39 + this.constituents = manager.constituents; 33 40 this.tracks = manager.tracks; 34 41 } 35 42 }
+26 -12
src/components/transformer/output/string/json/element.js
··· 17 17 18 18 /** @type {OutputManagerDeputy} */ 19 19 const manager = { 20 + constituents: { 21 + ...base.constituents, 22 + collection: computed(() => { 23 + const json = base.constituents.collection(); 24 + return typeof json === "string" ? parseArray(json) : []; 25 + }), 26 + save: async (newConstituents) => { 27 + const json = JSON.stringify(newConstituents); 28 + await base.constituents.save(json); 29 + }, 30 + }, 20 31 tracks: { 21 32 ...base.tracks, 22 33 collection: computed(() => { 23 - let json = base.tracks.collection(); 24 - if (typeof json !== "string") json = "[]"; 25 - 26 - // Try parsing JSON 27 - try { 28 - return JSON.parse(json); 29 - } catch (err) { 30 - console.error( 31 - "components/transformer/output/string/json: Failed to parse JSON", 32 - ); 33 - return []; 34 - } 34 + const json = base.tracks.collection(); 35 + return typeof json === "string" ? parseArray(json) : []; 35 36 }), 36 37 save: async (newTracks) => { 37 38 const json = JSON.stringify(newTracks); ··· 41 42 }; 42 43 43 44 // Assign manager properties to class 45 + this.constituents = manager.constituents; 44 46 this.tracks = manager.tracks; 47 + } 48 + } 49 + 50 + /** 51 + * @param {string} json 52 + */ 53 + function parseArray(json) { 54 + try { 55 + return JSON.parse(json); 56 + } catch (err) { 57 + console.error(err); 58 + return []; 45 59 } 46 60 } 47 61