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: only load output elements when needed

+133 -69
+59 -49
src/components/configurator/output/element.js
··· 1 - import { 2 - BroadcastableDiffuseElement, 3 - DiffuseElement, 4 - } from "@common/element.js"; 5 - import { batch, computed, signal } from "@common/signal.js"; 1 + import { BroadcastableDiffuseElement } from "@common/element.js"; 2 + import { batch, computed, signal, trigger } from "@common/signal.js"; 6 3 7 4 /** 5 + * @import {DiffuseElement} from "@common/element.js" 8 6 * @import {Facet, PlaylistItem, Theme, Track} from "@definitions/types.d.ts" 9 7 * @import {OutputManagerDeputy, OutputElement} from "@components/output/types.d.ts" 10 8 * ··· 34 32 const manager = { 35 33 facets: { 36 34 collection: computed(() => { 37 - const out = this.#selectedOutput.value; 35 + const out = this.#selected.value; 38 36 if (out) return out.facets.collection(); 39 37 40 38 const def = this.#defaultOutput.value; ··· 46 44 const def = this.#defaultOutput.value; 47 45 if (def) def.facets.reload(); 48 46 49 - const out = this.#selectedOutput.value; 47 + const out = this.#selected.value; 50 48 if (out) return out.facets.reload(); 51 49 52 50 return Promise.resolve(); 53 51 }, 54 52 save: async (newFacets) => { 55 - const out = this.#selectedOutput.value; 53 + const out = this.#selected.value; 56 54 if (out) return await out.facets.save(newFacets); 57 55 58 56 const def = this.#defaultOutput.value; ··· 61 59 this.#memory.facets.value = newFacets; 62 60 }, 63 61 state: computed(() => { 64 - const out = this.#selectedOutput.value; 62 + const out = this.#selected.value; 65 63 if (out) return out.facets.state(); 66 64 67 65 const def = this.#defaultOutput.value; ··· 72 70 }, 73 71 playlistItems: { 74 72 collection: computed(() => { 75 - const out = this.#selectedOutput.value; 73 + const out = this.#selected.value; 76 74 if (out) return out.playlistItems.collection(); 77 75 78 76 const def = this.#defaultOutput.value; ··· 84 82 const def = this.#defaultOutput.value; 85 83 if (def) def.playlistItems.reload(); 86 84 87 - const out = this.#selectedOutput.value; 85 + const out = this.#selected.value; 88 86 if (out) return out.playlistItems.reload(); 89 87 90 88 return Promise.resolve(); 91 89 }, 92 90 save: async (newPlaylistItems) => { 93 - const out = this.#selectedOutput.value; 91 + const out = this.#selected.value; 94 92 if (out) return await out.playlistItems.save(newPlaylistItems); 95 93 96 94 const def = this.#defaultOutput.value; ··· 99 97 this.#memory.playlistItems.value = newPlaylistItems; 100 98 }, 101 99 state: computed(() => { 102 - const out = this.#selectedOutput.value; 100 + const out = this.#selected.value; 103 101 if (out) return out.playlistItems.state(); 104 102 105 103 const def = this.#defaultOutput.value; ··· 110 108 }, 111 109 themes: { 112 110 collection: computed(() => { 113 - const out = this.#selectedOutput.value; 111 + const out = this.#selected.value; 114 112 if (out) return out.themes.collection(); 115 113 116 114 const def = this.#defaultOutput.value; ··· 122 120 const def = this.#defaultOutput.value; 123 121 if (def) def.themes.reload(); 124 122 125 - const out = this.#selectedOutput.value; 123 + const out = this.#selected.value; 126 124 if (out) return out.themes.reload(); 127 125 128 126 return Promise.resolve(); 129 127 }, 130 128 save: async (newThemes) => { 131 - const out = this.#selectedOutput.value; 129 + const out = this.#selected.value; 132 130 if (out) return await out.themes.save(newThemes); 133 131 134 132 const def = this.#defaultOutput.value; ··· 137 135 this.#memory.themes.value = newThemes; 138 136 }, 139 137 state: computed(() => { 140 - const out = this.#selectedOutput.value; 138 + const out = this.#selected.value; 141 139 if (out) return out.themes.state(); 142 140 143 141 const def = this.#defaultOutput.value; ··· 148 146 }, 149 147 tracks: { 150 148 collection: computed(() => { 151 - const out = this.#selectedOutput.value; 149 + const out = this.#selected.value; 152 150 if (out) return out.tracks.collection(); 153 151 154 152 const def = this.#defaultOutput.value; ··· 160 158 const def = this.#defaultOutput.value; 161 159 if (def) def.tracks.reload(); 162 160 163 - const out = this.#selectedOutput.value; 161 + const out = this.#selected.value; 164 162 if (out) return out.tracks.reload(); 165 163 166 164 return Promise.resolve(); 167 165 }, 168 166 save: async (newTracks) => { 169 - const out = this.#selectedOutput.value; 167 + const out = this.#selected.value; 170 168 if (out) return await out.tracks.save(newTracks); 171 169 172 170 const def = this.#defaultOutput.value; ··· 175 173 this.#memory.tracks.value = newTracks; 176 174 }, 177 175 state: computed(() => { 178 - const out = this.#selectedOutput.value; 176 + const out = this.#selected.value; 179 177 if (out) return out.tracks.state(); 180 178 181 179 const def = this.#defaultOutput.value; ··· 187 185 188 186 // Other 189 187 ready: computed(() => { 190 - const out = this.#selectedOutput.value; 188 + const out = this.#selected.value; 191 189 if (out) return out.ready(); 192 190 193 191 const def = this.#defaultOutput.value; ··· 207 205 208 206 // SIGNALS 209 207 208 + #activated = signal(/** @type {Set<string>} */ (new Set()), { eager: true }); 209 + 210 210 #defaultOutput = signal( 211 211 /** @type {Output | null | undefined} */ (undefined), 212 212 ); ··· 218 218 tracks: signal(/** @type {Track[]} */ ([])), 219 219 }; 220 220 221 - #selectedOutput = signal( 221 + #selected = signal( 222 222 /** @type {Output | null | undefined} */ (undefined), 223 223 ); 224 224 ··· 226 226 227 227 // STATE 228 228 229 - selectedOutput = computed(() => this.#selectedOutput.value ?? null); 229 + activated = this.#activated.get; 230 + selected = computed(() => this.#selected.value ?? null); 230 231 231 232 // LIFECYCLE 232 233 ··· 251 252 // Super 252 253 super.connectedCallback(); 253 254 254 - /** @type {Output | null | undefined} */ 255 - let defaultOutput = undefined; 256 - 255 + // Outputs 257 256 const def_ault = this.getAttribute("default"); 258 - if (def_ault) { 259 - defaultOutput = await this.#findOutput(def_ault); 260 - } 257 + const selectedOutputId = localStorage.getItem( 258 + `${STORAGE_PREFIX}/selected/id`, 259 + ); 260 + 261 + batch(() => { 262 + /** @type {Set<string>} */ 263 + const activated = new Set(); 264 + 265 + if (def_ault) { 266 + activated.add(def_ault); 267 + } 268 + 269 + if (selectedOutputId) { 270 + activated.add(selectedOutputId); 271 + } 272 + 273 + this.#activated.value = activated; 274 + }); 261 275 262 - const selectedOutput = await this.#findSelectedOutput(); 276 + /** @type {Output | null} */ 277 + const defaultOutput = def_ault ? await this.#findOutput(def_ault) : null; 278 + const selectedOutput = await this.#findOutput(selectedOutputId); 263 279 264 280 batch(() => { 265 - this.#selectedOutput.value = selectedOutput; 281 + this.#selected.value = selectedOutput; 266 282 this.#defaultOutput.value = defaultOutput; 267 283 this.#setupFinished.value = true; 268 284 }); ··· 273 289 /** 274 290 * @param {string | null} id 275 291 */ 276 - #selectOutput = async (id) => { 277 - this.#selectedOutput.value = await this.#findOutput(id); 278 - }; 279 - 280 - /** 281 - * @param {string | null} id 282 - */ 283 292 async #findOutput(id) { 284 293 const el = id ? this.root().querySelector(`#${id}`) : null; 285 294 if (!el) return null; ··· 296 305 return /** @type {Output} */ (/** @type {unknown} */ (el)); 297 306 } 298 307 299 - async #findSelectedOutput() { 300 - const id = localStorage.getItem(`${STORAGE_PREFIX}/selected/id`); 301 - if (id) return this.#findOutput(id); 302 - return undefined; 303 - } 308 + /** 309 + * @param {string | null} id 310 + */ 311 + #selectOutput = async (id) => { 312 + if (id) { 313 + this.#activated.value = new Set([...this.#activated.value.values(), id]); 314 + } 315 + 316 + this.#selected.value = await this.#findOutput(id); 317 + }; 304 318 305 319 /** 306 320 * @override ··· 333 347 const deps = this.dependencies(); 334 348 const entries = Object.entries(deps); 335 349 336 - await Promise.all( 337 - entries.map(([_k, v]) => customElements.whenDefined(v.localName)), 338 - ); 339 - 340 350 return entries.map(([k, v]) => { 341 351 return { 342 352 id: k, 343 - label: v.label, 353 + label: v.label ?? v.getAttribute("label"), 344 354 element: /** @type {OutputElement} */ (v), 345 355 }; 346 356 });
+6 -1
src/components/configurator/output/types.d.ts
··· 7 7 deselect: () => Promise<void>; 8 8 options: () => Promise<Array<OutputOption<ElementType>>>; 9 9 select: (id: string) => Promise<void>; 10 - selectedOutput: SignalReader<ElementType | null>; 10 + 11 + /** Output-element ids that have been selected earlier. */ 12 + activated: SignalReader<Set<string>>; 13 + 14 + /** Selected output element. */ 15 + selected: SignalReader<ElementType | null>; 11 16 }; 12 17 13 18 export type OutputOption<ElementType = OutputElement> = {
+52 -8
src/components/orchestrator/output/element.js
··· 2 2 import { DEFAULT_GROUP, DiffuseElement } from "@common/element.js"; 3 3 4 4 import "@components/configurator/output/element.js"; 5 - import "@components/output/bytes/s3/element.js"; 6 - import "@components/output/polymorphic/indexed-db/element.js"; 7 - import "@components/output/raw/atproto/element.js"; 8 - import "@components/transformer/output/bytes/automerge/element.js"; 9 - import "@components/transformer/output/raw/atproto-sync/element.js"; 10 5 import "@components/transformer/output/refiner/default/element.js"; 11 6 import "@components/transformer/output/replicator/broadcast/element.js"; 12 - import "@components/transformer/output/string/json/element.js"; 13 7 14 8 /** 15 9 * @import {RenderArg} from "@common/element.d.ts" ··· 29 23 class OutputOrchestrator extends DiffuseElement { 30 24 static NAME = "diffuse/orchestrator/output"; 31 25 26 + // LIFECYCLE 27 + 28 + /** 29 + * @override 30 + */ 31 + async connectedCallback() { 32 + super.connectedCallback(); 33 + 34 + /** @type {Set<string>} */ 35 + let previouslyActivated = new Set(); 36 + 37 + this.effect(() => { 38 + const set = this.outputConfigurator.activated(); 39 + const newlyActicated = set.difference(previouslyActivated); 40 + 41 + console.log(newlyActicated); 42 + 43 + newlyActicated.forEach((id) => { 44 + switch (id) { 45 + case "do-output__dc-output__local": { 46 + import("@components/output/polymorphic/indexed-db/element.js"); 47 + import("@components/transformer/output/string/json/element.js"); 48 + break; 49 + } 50 + case "do-output__dc-output__atproto": { 51 + import("@components/output/raw/atproto/element.js"); 52 + import( 53 + "@components/transformer/output/raw/atproto-sync/element.js" 54 + ); 55 + break; 56 + } 57 + case "do-output__dc-output__s3": { 58 + import("@components/output/bytes/s3/element.js"); 59 + import("@components/transformer/output/bytes/automerge/element.js"); 60 + break; 61 + } 62 + } 63 + }); 64 + 65 + previouslyActivated = set; 66 + }); 67 + } 68 + 69 + // ELEMENT GETTERS 70 + 32 71 /** 33 72 * @returns {OutputElement} 34 73 */ ··· 52 91 if (!outputConfigurator) { 53 92 throw new Error("Output orchestrator did not render yet."); 54 93 } 94 + 55 95 return outputConfigurator; 56 96 } 57 97 ··· 79 119 80 120 // PROXY ADDITIONAL OUTPUT CONFIGURATOR ACTIONS 81 121 122 + get activated() { 123 + return this.outputConfigurator.activated; 124 + } 125 + 82 126 get deselect() { 83 127 return this.outputConfigurator.deselect; 84 128 } ··· 91 135 return this.outputConfigurator.select; 92 136 } 93 137 94 - get selectedOutput() { 95 - return this.outputConfigurator.selectedOutput; 138 + get selected() { 139 + return this.outputConfigurator.selected; 96 140 } 97 141 98 142 // RENDER
+16 -11
src/themes/webamp/configurators/output/element.js
··· 11 11 * @import {S3OutputElement} from "@components/output/bytes/s3/types.d.ts" 12 12 * 13 13 * @import {OutputElement} from "@components/output/types.d.ts" 14 - * @import {OutputConfiguratorElement, OutputOption} from "@components/configurator/output/types.d.ts" 15 - * @import {OutputFallbackConfiguratorElement} from "@components/configurator/output-fallback/types.d.ts" 14 + * @import {OutputConfiguratorElement} from "@components/configurator/output/types.d.ts" 16 15 * @import {RenderArg} from "@common/element.d.ts" 17 16 */ 18 17 ··· 100 99 await atproto.logout(); 101 100 }; 102 101 103 - #handleAtprotoActivate = async () => { 102 + /** @param {Event} event */ 103 + #handleAtprotoActivate = async (event) => { 104 + event.preventDefault(); 105 + 104 106 const output = this.$output.value; 105 107 if (!output || !("select" in output)) return; 106 108 ··· 175 177 await s3.unsetBucket(); 176 178 }; 177 179 178 - #handleS3Activate = async () => { 180 + /** @param {Event} event */ 181 + #handleS3Activate = async (event) => { 182 + event.preventDefault(); 183 + 179 184 const output = this.$output.value; 180 185 if (!output || !("select" in output)) return; 181 186 ··· 291 296 */ 292 297 #renderOverviewTab(html) { 293 298 const selectedOutput = 294 - this.$output.value && "selectedOutput" in this.$output.value 295 - ? this.$output.value.selectedOutput() 299 + this.$output.value && "selected" in this.$output.value 300 + ? this.$output.value.selected() 296 301 : undefined; 297 302 298 303 return html` ··· 334 339 /> 335 340 <div> 336 341 ${this.$output.value && 337 - "selectedOutput" in this.$output.value 342 + "selected" in this.$output.value 338 343 ? selectedOutput 339 344 ? html` 340 345 <p> ··· 361 366 #renderAtprotoTab(html) { 362 367 const did = this.$atproto.value?.did() ?? null; 363 368 const selectedOutput = 364 - this.$output.value && "selectedOutput" in this.$output.value 365 - ? this.$output.value.selectedOutput() 369 + this.$output.value && "selected" in this.$output.value 370 + ? this.$output.value.selected() 366 371 : undefined; 367 372 368 373 const authenticated = () => { ··· 428 433 const s3 = this.$s3.value; 429 434 const ready = s3?.ready() ?? false; 430 435 const selectedOutput = 431 - this.$output.value && "selectedOutput" in this.$output.value 432 - ? this.$output.value.selectedOutput() 436 + this.$output.value && "selected" in this.$output.value 437 + ? this.$output.value.selected() 433 438 : undefined; 434 439 435 440 const configured = () => {