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