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