119 lines
4.3 KiB
JavaScript
119 lines
4.3 KiB
JavaScript
import { api } from "./api.js";
|
|
import { element } from "./dom.js";
|
|
import { notificationsEnabled, setNotificationsEnabled } from "./notify.js";
|
|
|
|
const themeStorageKey = "ddc-theme";
|
|
|
|
const fields = {
|
|
dialog: document.getElementById("settings-dialog"),
|
|
form: document.getElementById("settings-form"),
|
|
closeButton: document.getElementById("settings-close"),
|
|
cancelButton: document.getElementById("settings-cancel"),
|
|
error: document.getElementById("settings-error"),
|
|
concurrent: document.getElementById("setting-concurrent"),
|
|
connections: document.getElementById("setting-connections"),
|
|
notifications: document.getElementById("setting-notifications"),
|
|
minFree: document.getElementById("setting-min-free"),
|
|
ports: document.getElementById("setting-ports"),
|
|
autoExtract: document.getElementById("setting-auto-extract"),
|
|
cleanup: document.getElementById("setting-cleanup"),
|
|
downloadPaths: document.getElementById("setting-download-paths"),
|
|
theme: document.getElementById("setting-theme"),
|
|
downloadDir: document.getElementById("info-download-dir"),
|
|
roots: document.getElementById("info-roots"),
|
|
};
|
|
|
|
export function setupSettings(onSaved) {
|
|
fields.closeButton.addEventListener("click", () => fields.dialog.close());
|
|
fields.cancelButton.addEventListener("click", () => fields.dialog.close());
|
|
fields.form.addEventListener("submit", (event) => saveSettings(event, onSaved));
|
|
fields.theme.addEventListener("change", () => applyTheme(fields.theme.value));
|
|
fields.dialog.addEventListener("close", () => applyTheme(readTheme()));
|
|
}
|
|
|
|
export async function loadSettings() {
|
|
const data = await api.settings();
|
|
return data.settings;
|
|
}
|
|
|
|
export async function openSettings() {
|
|
fields.error.textContent = "";
|
|
const data = await api.settings();
|
|
fillForm(data);
|
|
fields.dialog.showModal();
|
|
}
|
|
|
|
function fillForm(data) {
|
|
const settings = data.settings;
|
|
fields.concurrent.value = settings.maxConcurrentDownloads;
|
|
fields.connections.value = settings.connectionsPerDownload;
|
|
fields.notifications.checked = notificationsEnabled();
|
|
fields.minFree.value = settings.minFreeGB;
|
|
fields.ports.value = settings.allowedPorts.length > 0 ? settings.allowedPorts.join(", ") : "*";
|
|
fields.autoExtract.checked = settings.autoExtract;
|
|
fields.cleanup.checked = settings.cleanupAfterMove;
|
|
fields.downloadPaths.value = (settings.downloadPaths || []).join("\n");
|
|
fields.theme.value = readTheme();
|
|
fields.downloadDir.textContent = data.downloadDir;
|
|
fields.roots.replaceChildren(...data.libraryRoots.map((root) => element("div", "mono", root)));
|
|
}
|
|
|
|
async function saveSettings(event, onSaved) {
|
|
event.preventDefault();
|
|
fields.error.textContent = "";
|
|
try {
|
|
const payload = {
|
|
maxConcurrentDownloads: Number(fields.concurrent.value),
|
|
connectionsPerDownload: Number(fields.connections.value),
|
|
minFreeGB: Number(fields.minFree.value),
|
|
allowedPorts: parsePorts(fields.ports.value),
|
|
autoExtract: fields.autoExtract.checked,
|
|
cleanupAfterMove: fields.cleanup.checked,
|
|
downloadPaths: parseDownloadPaths(fields.downloadPaths.value),
|
|
};
|
|
await setNotificationsEnabled(fields.notifications.checked);
|
|
const data = await api.saveSettings(payload);
|
|
storeTheme(fields.theme.value);
|
|
fields.dialog.close();
|
|
onSaved(data.settings);
|
|
} catch (error) {
|
|
fields.error.textContent = error.message;
|
|
}
|
|
}
|
|
|
|
function parsePorts(text) {
|
|
const trimmed = text.trim();
|
|
if (trimmed === "*") return [];
|
|
const ports = trimmed.split(/[\s,]+/).filter(Boolean).map(Number);
|
|
const invalid = ports.length === 0 || ports.some((port) => !Number.isInteger(port) || port < 1 || port > 65535);
|
|
if (invalid) throw new Error("Allowed ports must be numbers between 1 and 65535, or * for any port.");
|
|
return ports;
|
|
}
|
|
|
|
function parseDownloadPaths(text) {
|
|
return text.split("\n").map((line) => line.trim()).filter(Boolean);
|
|
}
|
|
|
|
function readTheme() {
|
|
try {
|
|
return localStorage.getItem(themeStorageKey) || "system";
|
|
} catch {
|
|
return "system";
|
|
}
|
|
}
|
|
|
|
function storeTheme(theme) {
|
|
try {
|
|
localStorage.setItem(themeStorageKey, theme);
|
|
} catch {
|
|
return;
|
|
} finally {
|
|
applyTheme(theme);
|
|
}
|
|
}
|
|
|
|
function applyTheme(theme) {
|
|
if (theme === "light" || theme === "dark") document.documentElement.dataset.theme = theme;
|
|
else delete document.documentElement.dataset.theme;
|
|
}
|