OptionsModal.tsx
⎇
Raw
1import { type Accessor, createEffect, type Setter, on } from "solid-js";
2import type { AppOptions } from "../types";
3import "./OptionsModal.css";
4import "./Dialog.css";
5import "toastify-js/src/toastify.css";
6import { toast } from "../utils";
7import { MediaFormat } from "music-server-shared/types";
8import { createStore } from "solid-js/store";
9
10interface OptionsModalProps {
11 showOptions: Accessor<boolean>;
12 setShowOptions: Setter<boolean>;
13 options: AppOptions;
14 setOptions: (options: AppOptions) => void;
15}
16
17export default function OptionsModal(props: OptionsModalProps) {
18 //clone, otherwise it shares the reference
19 const [newOptions, setNewOptions] = createStore<AppOptions>({ ...props.options });
20 let dialogElement!: HTMLDialogElement;
21 let formElement!: HTMLFormElement;
22
23 createEffect(
24 on(props.showOptions, () => {
25 if (props.showOptions()) {
26 dialogElement.showModal();
27 setNewOptions(props.options);
28 }
29 }),
30 );
31
32 function handleSave() {
33 if (!formElement.reportValidity()) return;
34 toast("Saved changes", "success");
35 props.setOptions(newOptions);
36 handleClose();
37 }
38
39 function handleClose() {
40 dialogElement.close();
41 props.setShowOptions(false);
42 }
43
44 async function deleteServerCache() {
45 const response = await fetch("/reset-cache", { method: "POST" }).catch(() => {});
46 if (!response || response.status !== 200) {
47 toast("Clearing server cache failed", "error");
48 } else {
49 toast("Server cache cleared", "info");
50 }
51 }
52
53 return (
54 <dialog ref={dialogElement} onClose={handleClose}>
55 <form class="option-content" ref={formElement}>
56 <div class="title">Player Options</div>
57 <hr />
58 <div>
59 <label for="remember-dir-checkbox">Remember last directory:</label>
60 <input
61 id="remember-dir-checkbox"
62 type="checkbox"
63 checked={newOptions.rememberDir}
64 onChange={(e) => setNewOptions("rememberDir", e.target.checked)}
65 />
66 </div>
67 <div>
68 <label for="transcoding-checkbox">Enable transcoding:</label>
69 <input
70 id="transcoding-checkbox"
71 type="checkbox"
72 checked={newOptions.enableTranscoding}
73 onChange={(e) => setNewOptions("enableTranscoding", e.target.checked)}
74 />
75 </div>
76 <div>
77 <label for="transcoding-buffer-checkbox">Disable chunked transcoding:</label>
78 <input
79 id="transcoding-buffer-checkbox"
80 type="checkbox"
81 checked={newOptions.disableChunkedTranscoding}
82 onChange={(e) => setNewOptions("disableChunkedTranscoding", e.target.checked)}
83 disabled={!newOptions.enableTranscoding}
84 />
85 </div>
86 <div>
87 <label for="bitrate-select">Bitrate:</label>
88 <select
89 id="bitrate-select"
90 value={newOptions.bitrate}
91 onChange={(e) => setNewOptions("bitrate", Number.parseInt(e.target.value))}
92 disabled={!newOptions.enableTranscoding}
93 >
94 <option value="32000">32 kbps</option>
95 <option value="48000">48 kbps</option>
96 <option value="64000">64 kbps</option>
97 <option value="96000">96 kbps</option>
98 <option value="128000">128 kbps</option>
99 <option value="192000">192 kbps</option>
100 <option value="320000">320 kbps</option>
101 </select>
102 </div>
103 <div>
104 <label for="codec-select">Format:</label>
105 <select
106 id="codec-select"
107 value={newOptions.format}
108 onChange={(e) => setNewOptions("format", e.target.value as MediaFormat)}
109 disabled={!newOptions.enableTranscoding}
110 >
111 <option>{MediaFormat["opus/ogg"]}</option>
112 <option>{MediaFormat["opus/webm"]}</option>
113 <option>{MediaFormat.mp3}</option>
114 <option>{MediaFormat["vorbis/ogg"]}</option>
115 <option>{MediaFormat["vorbis/webm"]}</option>
116 </select>
117 </div>
118 <div>
119 <label for="max-downloads-input">Max concurrent downloads:</label>
120 <input
121 id="max-downloads-input"
122 type="number"
123 min={1}
124 required
125 value={newOptions.maxDownloads}
126 onChange={(e) => setNewOptions("maxDownloads", e.target.valueAsNumber)}
127 />
128 </div>
129 <div>
130 <label for="max-transcodes-input">Max concurrent transcodes:</label>
131 <input
132 id="max-transcodes-input"
133 type="number"
134 min={1}
135 required
136 value={newOptions.maxTranscodes}
137 onChange={(e) => setNewOptions("maxTranscodes", e.target.valueAsNumber)}
138 />
139 </div>
140 <div>
141 <label for="hide-volume-checkbox">Hide volume:</label>
142 <input
143 id="hide-volume-checkbox"
144 type="checkbox"
145 checked={newOptions.hideVolume}
146 onChange={(e) => setNewOptions("hideVolume", e.target.checked)}
147 />
148 </div>
149 <div>
150 <label for="duplicate-checkbox">Warn when adding duplicate to playlist:</label>
151 <input
152 id="duplicate-checkbox"
153 type="checkbox"
154 checked={newOptions.duplicateWarning}
155 onChange={(e) => setNewOptions("duplicateWarning", e.target.checked)}
156 />
157 </div>
158 <div class="option-buttons">
159 <button title="Clear server caches" type="button" class="cache-button" onclick={deleteServerCache}>
160 Clear server caches
161 </button>
162 <button title="Save settings" type="button" onClick={handleSave}>
163 Save
164 </button>
165 <button title="Cancel changes settings" type="button" class="cancel-button" onClick={handleClose}>
166 Cancel
167 </button>
168 </div>
169 </form>
170 </dialog>
171 );
172}
173