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