this repo has no description
1import WebpackRequire from "@moonlight-mod/types/discord/require";
2import {
3 Config,
4 DetectedExtension,
5 ExtensionLoadSource
6} from "@moonlight-mod/types";
7import {
8 ExtensionState,
9 MoonbaseExtension,
10 MoonbaseNatives,
11 RepositoryManifest
12} from "./types";
13
14export const stores = (require: typeof WebpackRequire) => {
15 const Flux = require("common_flux");
16 const Dispatcher = require("common_fluxDispatcher");
17 const natives: MoonbaseNatives = moonlight.getNatives("moonbase");
18
19 class MoonbaseSettingsStore extends Flux.Store<any> {
20 private origConfig: Config;
21 private config: Config;
22
23 modified: boolean;
24 submitting: boolean;
25 installing: boolean;
26
27 extensions: { [id: string]: MoonbaseExtension };
28 updates: { [id: string]: { version: string; download: string } };
29
30 constructor() {
31 super(Dispatcher);
32
33 // Fucking Electron making it immutable
34 this.origConfig = moonlightNode.config;
35 this.config = JSON.parse(JSON.stringify(this.origConfig));
36
37 this.modified = false;
38 this.submitting = false;
39 this.installing = false;
40
41 this.extensions = {};
42 this.updates = {};
43 for (const ext of moonlightNode.extensions) {
44 const existingExtension = this.extensions[ext.id];
45 if (existingExtension != null) continue;
46
47 this.extensions[ext.id] = {
48 ...ext,
49 state: moonlight.enabledExtensions.has(ext.id)
50 ? ExtensionState.Enabled
51 : ExtensionState.Disabled
52 };
53 }
54
55 natives.fetchRepositories(this.config.repositories).then((ret) => {
56 for (const [repo, exts] of Object.entries(ret)) {
57 try {
58 for (const ext of exts) {
59 try {
60 const existingExtension = this.extensions[ext.id];
61 if (existingExtension != null) {
62 if (this.hasUpdate(repo, ext, existingExtension)) {
63 this.updates[ext.id] = {
64 version: ext.version!,
65 download: ext.download
66 };
67 }
68 continue;
69 }
70
71 this.extensions[ext.id] = {
72 id: ext.id,
73 manifest: ext,
74 source: { type: ExtensionLoadSource.Normal, url: repo },
75 state: ExtensionState.NotDownloaded
76 };
77 } catch (e) {
78 console.error(`Error processing extension ${ext.id}`, e);
79 }
80 }
81 } catch (e) {
82 console.error(`Error processing repository ${repo}`, e);
83 }
84 }
85
86 this.emitChange();
87 });
88 }
89
90 // this logic sucks so bad lol
91 private hasUpdate(
92 repo: string,
93 repoExt: RepositoryManifest,
94 existing: MoonbaseExtension
95 ) {
96 return (
97 existing.source.type === ExtensionLoadSource.Normal &&
98 existing.source.url != null &&
99 existing.source.url === repo &&
100 repoExt.version != null &&
101 existing.manifest.version != repoExt.version
102 );
103 }
104
105 // Jank
106 private isModified() {
107 const orig = JSON.stringify(this.origConfig);
108 const curr = JSON.stringify(this.config);
109 return orig !== curr;
110 }
111
112 get busy() {
113 return this.submitting || this.installing;
114 }
115
116 showNotice() {
117 return this.modified;
118 }
119
120 getExtension(id: string) {
121 return this.extensions[id];
122 }
123
124 getExtensionName(id: string) {
125 return this.extensions.hasOwnProperty(id)
126 ? this.extensions[id].manifest.meta?.name ?? id
127 : id;
128 }
129
130 getExtensionUpdate(id: string) {
131 return this.updates.hasOwnProperty(id) ? this.updates[id] : null;
132 }
133
134 getExtensionEnabled(id: string) {
135 const val = this.config.extensions[id];
136 if (val == null) return false;
137 return typeof val === "boolean" ? val : val.enabled;
138 }
139
140 getExtensionConfig<T>(id: string, key: string): T | undefined {
141 const defaultValue =
142 this.extensions[id].manifest.settings?.[key]?.default;
143 const cfg = this.config.extensions[id];
144
145 if (cfg == null || typeof cfg === "boolean") return defaultValue;
146 return cfg.config?.[key] ?? defaultValue;
147 }
148
149 getExtensionConfigName(id: string, key: string) {
150 return this.extensions[id].manifest.settings?.[key]?.displayName ?? key;
151 }
152
153 setExtensionConfig(id: string, key: string, value: any) {
154 const oldConfig = this.config.extensions[id];
155 const newConfig =
156 typeof oldConfig === "boolean"
157 ? {
158 enabled: oldConfig,
159 config: { [key]: value }
160 }
161 : {
162 ...oldConfig,
163 config: { ...(oldConfig?.config ?? {}), [key]: value }
164 };
165
166 this.config.extensions[id] = newConfig;
167 this.modified = this.isModified();
168 this.emitChange();
169 }
170
171 setExtensionEnabled(id: string, enabled: boolean) {
172 let val = this.config.extensions[id];
173
174 if (val == null) {
175 this.config.extensions[id] = { enabled };
176 this.emitChange();
177 return;
178 }
179
180 if (typeof val === "boolean") {
181 val = enabled;
182 } else {
183 val.enabled = enabled;
184 }
185
186 this.config.extensions[id] = val;
187 this.modified = this.isModified();
188 this.emitChange();
189 }
190
191 async installExtension(id: string) {
192 const ext = this.getExtension(id);
193 if (!("download" in ext.manifest)) {
194 throw new Error("Extension has no download URL");
195 }
196
197 this.installing = true;
198 try {
199 const url = this.updates[id]?.download ?? ext.manifest.download;
200 await natives.installExtension(ext.manifest, url, ext.source.url!);
201 if (ext.state === ExtensionState.NotDownloaded) {
202 this.extensions[id].state = ExtensionState.Disabled;
203 }
204
205 delete this.updates[id];
206 } catch (e) {
207 console.error("Error installing extension:", e);
208 }
209
210 this.installing = false;
211 this.emitChange();
212 }
213
214 async deleteExtension(id: string) {
215 const ext = this.getExtension(id);
216 if (ext == null) return;
217
218 this.installing = true;
219 try {
220 await natives.deleteExtension(ext.id);
221 this.extensions[id].state = ExtensionState.NotDownloaded;
222 } catch (e) {
223 console.error("Error deleting extension:", e);
224 }
225
226 this.installing = false;
227 this.emitChange();
228 }
229
230 writeConfig() {
231 this.submitting = true;
232
233 try {
234 moonlightNode.writeConfig(this.config);
235 // I love jank cloning
236 this.origConfig = JSON.parse(JSON.stringify(this.config));
237 } catch (e) {
238 console.error("Error writing config", e);
239 }
240
241 this.submitting = false;
242 this.modified = false;
243 this.emitChange();
244 }
245
246 reset() {
247 this.submitting = false;
248 this.modified = false;
249 this.config = JSON.parse(JSON.stringify(this.origConfig));
250 this.emitChange();
251 }
252 }
253
254 return {
255 MoonbaseSettingsStore: new MoonbaseSettingsStore()
256 };
257};