A music player that connects to your cloud/distributed storage.
1import { BroadcastableDiffuseElement } from "~/common/element.js";
2import { signal } from "~/common/signal.js";
3
4////////////////////////////////////////////
5// ELEMENT
6////////////////////////////////////////////
7
8class ScopeEngine extends BroadcastableDiffuseElement {
9 static NAME = "diffuse/engine/scope";
10
11 // SIGNALS
12
13 #groupBy = signal(/** @type {string | undefined} */ (undefined));
14 #playlist = signal(/** @type {string | undefined} */ (undefined));
15 #searchTerm = signal(/** @type {string | undefined} */ (undefined));
16 #sortBy = signal(/** @type {string[]} */ (["createdAt"]));
17 #sortDirection = signal(/** @type {"asc" | "desc" | undefined} */ ("desc"));
18
19 groupBy = this.#groupBy.get;
20 playlist = this.#playlist.get;
21 searchTerm = this.#searchTerm.get;
22 sortBy = this.#sortBy.get;
23 sortDirection = this.#sortDirection.get;
24
25 // LIFECYCLE
26
27 /**
28 * @override
29 */
30 connectedCallback() {
31 // Broadcast if needed
32 if (this.hasAttribute("group")) {
33 const actions = this.broadcast(this.identifier, {
34 revertToDefaultSort: {
35 strategy: "replicate",
36 fn: this.revertToDefaultSort,
37 },
38 setGroupBy: { strategy: "replicate", fn: this.setGroupBy },
39 setPlaylist: { strategy: "replicate", fn: this.setPlaylist },
40 setSearchTerm: { strategy: "replicate", fn: this.setSearchTerm },
41 setSortBy: { strategy: "replicate", fn: this.setSortBy },
42 setSortDirection: { strategy: "replicate", fn: this.setSortDirection },
43 });
44
45 if (actions) {
46 this.revertToDefaultSort = actions.revertToDefaultSort;
47 this.setGroupBy = actions.setGroupBy;
48 this.setPlaylist = actions.setPlaylist;
49 this.setSearchTerm = actions.setSearchTerm;
50 this.setSortBy = actions.setSortBy;
51 this.setSortDirection = actions.setSortDirection;
52 }
53 }
54
55 // Super
56 super.connectedCallback();
57
58 // Signals
59 const storagePrefix =
60 `${this.constructor.prototype.constructor.NAME}/${this.group}`;
61
62 this.#groupBy.value = localStorage.getItem(`${storagePrefix}/groupBy`) ??
63 undefined;
64 this.#playlist.value =
65 localStorage.getItem(`${storagePrefix}/playlistId`) ?? undefined;
66 this.#searchTerm.value =
67 localStorage.getItem(`${storagePrefix}/searchTerm`) ?? undefined;
68 this.#sortBy.value = JSON.parse(
69 localStorage.getItem(`${storagePrefix}/sortBy`) ?? `["createdAt"]`,
70 );
71 this.#sortDirection.value =
72 /** @type {"desc" | "asc"} */ (localStorage.getItem(
73 `${storagePrefix}/sortDirection`,
74 ) ?? "desc");
75
76 // Effects
77 this.effect(() => {
78 const key = `${storagePrefix}/groupBy`;
79 const val = this.#groupBy.value;
80
81 if (val) localStorage.setItem(key, val);
82 else localStorage.removeItem(key);
83 });
84
85 this.effect(() => {
86 const key = `${storagePrefix}/playlistId`;
87 const val = this.#playlist.value;
88
89 if (val) localStorage.setItem(key, val);
90 else localStorage.removeItem(key);
91 });
92
93 this.effect(() => {
94 const key = `${storagePrefix}/searchTerm`;
95 const val = this.#searchTerm.value;
96
97 if (val) localStorage.setItem(key, val);
98 else localStorage.removeItem(key);
99 });
100
101 this.effect(() => {
102 const key = `${storagePrefix}/sortBy`;
103 const val = this.#sortBy.value;
104
105 if (val.length) localStorage.setItem(key, JSON.stringify(val));
106 else localStorage.removeItem(key);
107 });
108
109 this.effect(() => {
110 const key = `${storagePrefix}/sortDirection`;
111 const val = this.#sortDirection.value;
112
113 if (val) localStorage.setItem(key, val);
114 else localStorage.removeItem(key);
115 });
116 }
117
118 // ACTIONS
119
120 revertToDefaultSort = async () => {
121 this.#sortBy.value = ["createdAt"];
122 this.#sortDirection.value = "desc";
123 };
124
125 /** @param {string | undefined} val */
126 setGroupBy = async (val) => this.#groupBy.value = val;
127
128 /** @param {string | undefined} val */
129 setPlaylist = async (val) => this.#playlist.value = val;
130
131 /** @param {string | undefined} val */
132 setSearchTerm = async (val) => this.#searchTerm.value = val;
133
134 /** @param {string[]} val */
135 setSortBy = async (val) => this.#sortBy.value = val;
136
137 /** @param {"asc" | "desc" | undefined} val */
138 setSortDirection = async (val) => this.#sortDirection.value = val;
139}
140
141export default ScopeEngine;
142
143////////////////////////////////////////////
144// REGISTER
145////////////////////////////////////////////
146
147export const CLASS = ScopeEngine;
148export const NAME = "de-scope";
149
150customElements.define(NAME, CLASS);