diff --git a/src/webui/www/private/index.html b/src/webui/www/private/index.html index 0243723eb8d8..976ff6c3378c 100644 --- a/src/webui/www/private/index.html +++ b/src/webui/www/private/index.html @@ -29,6 +29,7 @@ + diff --git a/src/webui/www/private/scripts/addtorrent.js b/src/webui/www/private/scripts/addtorrent.js index 7252a97ae0a0..a3b87ccdf587 100644 --- a/src/webui/www/private/scripts/addtorrent.js +++ b/src/webui/www/private/scripts/addtorrent.js @@ -45,10 +45,10 @@ window.qBittorrent.AddTorrent ??= (() => { let source = ""; let downloader = ""; - const localPreferences = new window.qBittorrent.LocalPreferences.LocalPreferences(); + const clientData = window.parent.qBittorrent.ClientData; const getCategories = () => { - const defaultCategory = localPreferences.get("add_torrent_default_category", ""); + const defaultCategory = clientData.get("add_torrent_default_category") ?? ""; const categorySelect = document.getElementById("categorySelect"); for (const name of window.parent.qBittorrent.Client.categoryMap.keys()) { const option = document.createElement("option"); @@ -320,10 +320,7 @@ window.qBittorrent.AddTorrent ??= (() => { if (document.getElementById("setDefaultCategory").checked) { const category = document.getElementById("category").value.trim(); - if (category.length === 0) - localPreferences.remove("add_torrent_default_category"); - else - localPreferences.set("add_torrent_default_category", category); + clientData.set({ add_torrent_default_category: (category.length > 0) ? category : null }).catch(console.error); } }; diff --git a/src/webui/www/private/scripts/client-data.js b/src/webui/www/private/scripts/client-data.js new file mode 100644 index 000000000000..74b3a0dd56b9 --- /dev/null +++ b/src/webui/www/private/scripts/client-data.js @@ -0,0 +1,129 @@ +/* + * Bittorrent Client using Qt and libtorrent. + * Copyright (C) 2025 Thomas Piccirello + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * In addition, as a special exception, the copyright holders give permission to + * link this program with the OpenSSL project's "OpenSSL" library (or with + * modified versions of it that use the same license as the "OpenSSL" library), + * and distribute the linked executables. You must obey the GNU General Public + * License in all respects for all of the code used other than "OpenSSL". If you + * modify file(s), you may extend this exception to your version of the file(s), + * but you are not obligated to do so. If you do not wish to do so, delete this + * exception statement from your version. + */ + +"use strict"; + +window.qBittorrent ??= {}; +window.qBittorrent.ClientData ??= (() => { + const exports = () => { + return new ClientData(); + }; + + // this is exposed as a singleton + class ClientData { + /** + * @type Map + */ + #cache = new Map(); + + #keyPrefix = "qbt_"; + + #addKeyPrefix(data) { + return Object.fromEntries(Object.entries(data).map(([key, value]) => ([`${this.#keyPrefix}${key}`, value]))); + } + + #removeKeyPrefix(data) { + return Object.fromEntries(Object.entries(data).map(([key, value]) => ([key.substring(this.#keyPrefix.length), value]))); + } + + /** + * @param {string[]} keys + * @returns {Record} + */ + async #fetch(keys) { + keys = keys.map(key => `${this.#keyPrefix}${key}`); + return await fetch("api/v2/clientdata/load", { + method: "POST", + body: new URLSearchParams({ + keys: JSON.stringify(keys) + }) + }) + .then(async (response) => { + if (!response.ok) + return; + + const data = await response.json(); + return this.#removeKeyPrefix(data); + }); + } + + /** + * @param {Record} data + */ + async #set(data) { + data = this.#addKeyPrefix(data); + await fetch("api/v2/clientdata/store", { + method: "POST", + body: new URLSearchParams({ + data: JSON.stringify(data) + }) + }) + .then((response) => { + if (!response.ok) + throw new Error("Failed to store client data"); + }); + } + + /** + * @param {string} key + * @returns {any} + */ + get(key) { + return this.#cache.get(key); + } + + /** + * @param {string[]} keys + * @returns {Record} + */ + async fetch(keys = []) { + const keysToFetch = keys.filter((key) => !this.#cache.has(key)); + if (keysToFetch.length > 0) { + const fetchedData = await this.#fetch(keysToFetch); + for (const [key, value] of Object.entries(fetchedData)) + this.#cache.set(key, value); + } + + return Object.fromEntries(keys.map((key) => ([key, this.#cache.get(key)]))); + } + + /** + * @param {Record} data + */ + async set(data) { + await this.#set(data); + + // update cache + for (const [key, value] of Object.entries(data)) + this.#cache.set(key, value); + } + } + + return exports(); +})(); +Object.freeze(window.qBittorrent.ClientData); diff --git a/src/webui/www/private/scripts/client.js b/src/webui/www/private/scripts/client.js index 3aecd340905e..f5414cb286d6 100644 --- a/src/webui/www/private/scripts/client.js +++ b/src/webui/www/private/scripts/client.js @@ -31,6 +31,7 @@ window.qBittorrent.Client ??= (() => { return { setup: setup, initializeCaches: initializeCaches, + initializeClientData: initializeClientData, closeWindow: closeWindow, closeFrameWindow: closeFrameWindow, getSyncMainDataInterval: getSyncMainDataInterval, @@ -56,15 +57,47 @@ window.qBittorrent.Client ??= (() => { const tagMap = new Map(); let cacheAllSettled; + let clientDataPromise; const setup = () => { // fetch various data and store it in memory + clientDataPromise = window.qBittorrent.ClientData.fetch([ + "show_search_engine", + "show_rss_reader", + "show_log_viewer", + "speed_in_browser_title_bar", + "show_top_toolbar", + "show_status_bar", + "show_filters_sidebar", + "hide_zero_status_filters", + "color_scheme", + "full_url_tracker_column", + "use_alt_row_colors", + "use_virtual_list", + "dblclick_complete", + "dblclick_download", + "dblclick_filter", + "search_in_filter", + "qbt_selected_log_levels", + "add_torrent_default_category", + ]); + cacheAllSettled = Promise.allSettled([ window.qBittorrent.Cache.buildInfo.init(), window.qBittorrent.Cache.preferences.init(), - window.qBittorrent.Cache.qbtVersion.init() + window.qBittorrent.Cache.qbtVersion.init(), + clientDataPromise, ]); }; + const initializeClientData = async () => { + try { + await clientDataPromise; + } + catch (error) { + console.error(`Failed to initialize client data. Reason: "${error}".`); + } + }; + const initializeCaches = async () => { const results = await cacheAllSettled; for (const [idx, result] of results.entries()) { @@ -223,8 +256,8 @@ let queueing_enabled = true; let serverSyncMainDataInterval = 1500; let customSyncMainDataInterval = null; let useSubcategories = true; -const useAutoHideZeroStatusFilters = localPreferences.get("hide_zero_status_filters", "false") === "true"; -const displayFullURLTrackerColumn = localPreferences.get("full_url_tracker_column", "false") === "true"; +let useAutoHideZeroStatusFilters = false; +let displayFullURLTrackerColumn = false; /* Categories filter */ const CATEGORIES_ALL = "b4af0e4c-e76d-4bac-a392-46cbc18d9655"; @@ -250,6 +283,8 @@ const TRACKERS_WARNING = "82a702c5-210c-412b-829f-97632d7557e9"; // Map> const trackerMap = new Map(); +const clientData = window.qBittorrent.ClientData; + let selectedTracker = localPreferences.get("selected_tracker", TRACKERS_ALL); let setTrackerFilter = () => {}; @@ -258,7 +293,13 @@ let selectedStatus = localPreferences.get("selected_filter", "all"); let setStatusFilter = () => {}; let toggleFilterDisplay = () => {}; -window.addEventListener("DOMContentLoaded", (event) => { +window.addEventListener("DOMContentLoaded", async (event) => { + await window.qBittorrent.Client.initializeClientData(); + window.qBittorrent.ColorScheme.update(); + + useAutoHideZeroStatusFilters = clientData.get("hide_zero_status_filters") === true; + displayFullURLTrackerColumn = clientData.get("full_url_tracker_column") === true; + window.qBittorrent.LocalPreferences.upgrade(); let isSearchPanelLoaded = false; @@ -405,6 +446,13 @@ window.addEventListener("DOMContentLoaded", (event) => { localPreferences.set(`filter_${filterListID.replace("FilterList", "")}_collapsed`, filterList.classList.toggle("invisible").toString()); }; + const highlightSelectedStatus = () => { + const statusFilter = document.getElementById("statusFilterList"); + const filterID = `${selectedStatus}_filter`; + for (const status of statusFilter.children) + status.classList.toggle("selectedFilter", (status.id === filterID)); + }; + new MochaUI.Panel({ id: "Filters", title: "Panel", @@ -426,35 +474,35 @@ window.addEventListener("DOMContentLoaded", (event) => { initializeWindows(); // Show Top Toolbar is enabled by default - let showTopToolbar = localPreferences.get("show_top_toolbar", "true") === "true"; + let showTopToolbar = clientData.get("show_top_toolbar") !== false; if (!showTopToolbar) { document.getElementById("showTopToolbarLink").firstElementChild.style.opacity = "0"; document.getElementById("mochaToolbar").classList.add("invisible"); } // Show Status Bar is enabled by default - let showStatusBar = localPreferences.get("show_status_bar", "true") === "true"; + let showStatusBar = clientData.get("show_status_bar") !== false; if (!showStatusBar) { document.getElementById("showStatusBarLink").firstElementChild.style.opacity = "0"; document.getElementById("desktopFooterWrapper").classList.add("invisible"); } // Show Filters Sidebar is enabled by default - let showFiltersSidebar = localPreferences.get("show_filters_sidebar", "true") === "true"; + let showFiltersSidebar = clientData.get("show_filters_sidebar") !== false; if (!showFiltersSidebar) { document.getElementById("showFiltersSidebarLink").firstElementChild.style.opacity = "0"; document.getElementById("filtersColumn").classList.add("invisible"); document.getElementById("filtersColumn_handle").classList.add("invisible"); } - let speedInTitle = localPreferences.get("speed_in_browser_title_bar") === "true"; + let speedInTitle = clientData.get("speed_in_browser_title_bar") === true; if (!speedInTitle) document.getElementById("speedInBrowserTitleBarLink").firstElementChild.style.opacity = "0"; // After showing/hiding the toolbar + status bar - window.qBittorrent.Client.showSearchEngine(localPreferences.get("show_search_engine") !== "false"); - window.qBittorrent.Client.showRssReader(localPreferences.get("show_rss_reader") !== "false"); - window.qBittorrent.Client.showLogViewer(localPreferences.get("show_log_viewer") === "true"); + window.qBittorrent.Client.showSearchEngine(clientData.get("show_search_engine") !== false); + window.qBittorrent.Client.showRssReader(clientData.get("show_rss_reader") !== false); + window.qBittorrent.Client.showLogViewer(clientData.get("show_log_viewer") === true); // After Show Top Toolbar MochaUI.Desktop.setDesktopSize(); @@ -571,13 +619,6 @@ window.addEventListener("DOMContentLoaded", (event) => { window.qBittorrent.Filters.clearStatusFilter(); }; - const highlightSelectedStatus = () => { - const statusFilter = document.getElementById("statusFilterList"); - const filterID = `${selectedStatus}_filter`; - for (const status of statusFilter.children) - status.classList.toggle("selectedFilter", (status.id === filterID)); - }; - const updateCategoryList = () => { const categoryList = document.getElementById("categoryFilterList"); if (!categoryList) @@ -1223,7 +1264,7 @@ window.addEventListener("DOMContentLoaded", (event) => { document.getElementById("showTopToolbarLink").addEventListener("click", (e) => { showTopToolbar = !showTopToolbar; - localPreferences.set("show_top_toolbar", showTopToolbar.toString()); + clientData.set({ show_top_toolbar: showTopToolbar }).catch(console.error); if (showTopToolbar) { document.getElementById("showTopToolbarLink").firstElementChild.style.opacity = "1"; document.getElementById("mochaToolbar").classList.remove("invisible"); @@ -1237,7 +1278,7 @@ window.addEventListener("DOMContentLoaded", (event) => { document.getElementById("showStatusBarLink").addEventListener("click", (e) => { showStatusBar = !showStatusBar; - localPreferences.set("show_status_bar", showStatusBar.toString()); + clientData.set({ show_status_bar: showStatusBar }).catch(console.error); if (showStatusBar) { document.getElementById("showStatusBarLink").firstElementChild.style.opacity = "1"; document.getElementById("desktopFooterWrapper").classList.remove("invisible"); @@ -1274,7 +1315,7 @@ window.addEventListener("DOMContentLoaded", (event) => { document.getElementById("showFiltersSidebarLink").addEventListener("click", (e) => { showFiltersSidebar = !showFiltersSidebar; - localPreferences.set("show_filters_sidebar", showFiltersSidebar.toString()); + clientData.set({ show_filters_sidebar: showFiltersSidebar }).catch(console.error); if (showFiltersSidebar) { document.getElementById("showFiltersSidebarLink").firstElementChild.style.opacity = "1"; document.getElementById("filtersColumn").classList.remove("invisible"); @@ -1290,7 +1331,7 @@ window.addEventListener("DOMContentLoaded", (event) => { document.getElementById("speedInBrowserTitleBarLink").addEventListener("click", (e) => { speedInTitle = !speedInTitle; - localPreferences.set("speed_in_browser_title_bar", speedInTitle.toString()); + clientData.set({ speed_in_browser_title_bar: speedInTitle }).catch(console.error); if (speedInTitle) document.getElementById("speedInBrowserTitleBarLink").firstElementChild.style.opacity = "1"; else @@ -1300,19 +1341,19 @@ window.addEventListener("DOMContentLoaded", (event) => { document.getElementById("showSearchEngineLink").addEventListener("click", (e) => { window.qBittorrent.Client.showSearchEngine(!window.qBittorrent.Client.isShowSearchEngine()); - localPreferences.set("show_search_engine", window.qBittorrent.Client.isShowSearchEngine().toString()); + clientData.set({ show_search_engine: window.qBittorrent.Client.isShowSearchEngine() }).catch(console.error); updateTabDisplay(); }); document.getElementById("showRssReaderLink").addEventListener("click", (e) => { window.qBittorrent.Client.showRssReader(!window.qBittorrent.Client.isShowRssReader()); - localPreferences.set("show_rss_reader", window.qBittorrent.Client.isShowRssReader().toString()); + clientData.set({ show_rss_reader: window.qBittorrent.Client.isShowRssReader() }).catch(console.error); updateTabDisplay(); }); document.getElementById("showLogViewerLink").addEventListener("click", (e) => { window.qBittorrent.Client.showLogViewer(!window.qBittorrent.Client.isShowLogViewer()); - localPreferences.set("show_log_viewer", window.qBittorrent.Client.isShowLogViewer().toString()); + clientData.set({ show_log_viewer: window.qBittorrent.Client.isShowLogViewer() }).catch(console.error); updateTabDisplay(); }); @@ -1369,7 +1410,7 @@ window.addEventListener("DOMContentLoaded", (event) => { // main window tabs const showTransfersTab = () => { - const showFiltersSidebar = localPreferences.get("show_filters_sidebar", "true") === "true"; + const showFiltersSidebar = clientData.get("show_filters_sidebar") !== false; if (showFiltersSidebar) { document.getElementById("filtersColumn").classList.remove("invisible"); document.getElementById("filtersColumn_handle").classList.remove("invisible"); diff --git a/src/webui/www/private/scripts/color-scheme.js b/src/webui/www/private/scripts/color-scheme.js index 3e41ddc523fe..15d277a56cf1 100644 --- a/src/webui/www/private/scripts/color-scheme.js +++ b/src/webui/www/private/scripts/color-scheme.js @@ -36,12 +36,12 @@ window.qBittorrent.ColorScheme ??= (() => { }; }; - const localPreferences = new window.qBittorrent.LocalPreferences.LocalPreferences(); const colorSchemeQuery = window.matchMedia("(prefers-color-scheme: dark)"); + const clientData = window.parent.qBittorrent.ClientData; const update = () => { const root = document.documentElement; - const colorScheme = localPreferences.get("color_scheme"); + const colorScheme = clientData.get("color_scheme"); const validScheme = (colorScheme === "light") || (colorScheme === "dark"); const isDark = colorSchemeQuery.matches; root.classList.toggle("dark", ((!validScheme && isDark) || (colorScheme === "dark"))); @@ -52,5 +52,3 @@ window.qBittorrent.ColorScheme ??= (() => { return exports(); })(); Object.freeze(window.qBittorrent.ColorScheme); - -window.qBittorrent.ColorScheme.update(); diff --git a/src/webui/www/private/scripts/dynamicTable.js b/src/webui/www/private/scripts/dynamicTable.js index 13480ab24cf9..18b173aa6a0e 100644 --- a/src/webui/www/private/scripts/dynamicTable.js +++ b/src/webui/www/private/scripts/dynamicTable.js @@ -66,6 +66,7 @@ window.qBittorrent.DynamicTable ??= (() => { }; const localPreferences = new window.qBittorrent.LocalPreferences.LocalPreferences(); + const clientData = window.qBittorrent.ClientData ?? window.parent.qBittorrent.ClientData; class DynamicTable { #DynamicTableHeaderContextMenuClass = null; @@ -74,7 +75,7 @@ window.qBittorrent.DynamicTable ??= (() => { this.dynamicTableDivId = dynamicTableDivId; this.dynamicTableFixedHeaderDivId = dynamicTableFixedHeaderDivId; this.dynamicTableDiv = document.getElementById(dynamicTableDivId); - this.useVirtualList = useVirtualList && (localPreferences.get("use_virtual_list", "false") === "true"); + this.useVirtualList = useVirtualList && (clientData.get("use_virtual_list") === true); this.fixedTableHeader = document.querySelector(`#${dynamicTableFixedHeaderDivId} thead tr`); this.hiddenTableHeader = this.dynamicTableDiv.querySelector("thead tr"); this.table = this.dynamicTableDiv.querySelector("table"); @@ -725,7 +726,7 @@ window.qBittorrent.DynamicTable ??= (() => { } setupAltRow() { - const useAltRowColors = (localPreferences.get("use_alt_row_colors", "true") === "true"); + const useAltRowColors = clientData.get("use_alt_row_colors") !== false; if (useAltRowColors) document.getElementById(this.dynamicTableDivId).classList.add("altRowColors"); } @@ -1800,7 +1801,7 @@ window.qBittorrent.DynamicTable ??= (() => { ? "dblclick_download" : "dblclick_complete"; - if (localPreferences.get(prefKey, "1") !== "1") + if (clientData.get(prefKey) === "0") return true; if (state.includes("stopped")) diff --git a/src/webui/www/private/scripts/search.js b/src/webui/www/private/scripts/search.js index 7525e763328e..fb06c4a781a4 100644 --- a/src/webui/www/private/scripts/search.js +++ b/src/webui/www/private/scripts/search.js @@ -109,8 +109,7 @@ window.qBittorrent.Search ??= (() => { }); const init = () => { - // load "Search in" preference from local storage - document.getElementById("searchInTorrentName").value = (localPreferences.get("search_in_filter") === "names") ? "names" : "everywhere"; + document.getElementById("searchInTorrentName").value = (window.qBittorrent.ClientData.get("search_in_filter") === "names") ? "names" : "everywhere"; const searchResultsTableContextMenu = new window.qBittorrent.ContextMenu.ContextMenu({ targets: "#searchResultsTableDiv tbody tr", menu: "searchResultsTableMenu", @@ -802,7 +801,8 @@ window.qBittorrent.Search ??= (() => { }; const searchInTorrentName = () => { - localPreferences.set("search_in_filter", getSearchInTorrentName()); + // don't await this + window.qBittorrent.ClientData.set({ search_in_filter: getSearchInTorrentName() }).catch(console.error); searchFilterChanged(); }; diff --git a/src/webui/www/private/views/createtorrent.html b/src/webui/www/private/views/createtorrent.html index c5f1a3bc4ab7..781205b6566e 100644 --- a/src/webui/www/private/views/createtorrent.html +++ b/src/webui/www/private/views/createtorrent.html @@ -127,8 +127,6 @@ }; }; - const localPreferences = new window.qBittorrent.LocalPreferences.LocalPreferences(); - const formatUrls = (urls) => { return urls.split("\n").map(encodeURIComponent).join("|"); }; @@ -140,7 +138,7 @@ return option; }; - const init = () => { + const init = async () => { const pieceSizeSelect = document.getElementById("pieceSize"); pieceSizeSelect.appendChild(createSizeOption(0)); for (let i = 4; i <= 17; ++i) @@ -165,7 +163,7 @@ submit(); }); - loadPreference(); + await loadPreference(); window.qBittorrent.pathAutofill.attachPathAutofill(); }; @@ -209,11 +207,13 @@ comments: document.getElementById("comments").value, source: document.getElementById("source").value, }; - localPreferences.set("torrent_creator", JSON.stringify(preference)); + window.parent.qBittorrent.ClientData.set({ torrent_creator: preference }).catch(console.error); }; - const loadPreference = () => { - const preference = JSON.parse(localPreferences.get("torrent_creator") ?? "{}"); + const loadPreference = async () => { + const clientData = await window.parent.qBittorrent.ClientData.fetch(["torrent_creator"]); + const preference = clientData.torrent_creator ?? {}; + document.getElementById("sourcePath").value = preference.sourcePath ?? ""; document.getElementById("torrentFormat").value = preference.torrentFormat ?? "hybrid"; document.getElementById("pieceSize").value = preference.pieceSize ?? 0; diff --git a/src/webui/www/private/views/filters.html b/src/webui/www/private/views/filters.html index 5c8f0404a0bb..578d66a13fc1 100644 --- a/src/webui/www/private/views/filters.html +++ b/src/webui/www/private/views/filters.html @@ -263,7 +263,7 @@ }); document.getElementById("Filters_pad").addEventListener("dblclick", (event) => { - if (localPreferences.get("dblclick_filter", "1") !== "1") + if (window.qBittorrent.ClientData.get("dblclick_filter") === "0") return; const filterItem = event.target.closest("li"); diff --git a/src/webui/www/private/views/log.html b/src/webui/www/private/views/log.html index 2b51b5643f25..e3530469a705 100644 --- a/src/webui/www/private/views/log.html +++ b/src/webui/www/private/views/log.html @@ -182,12 +182,10 @@ } }; - const localPreferences = new window.qBittorrent.LocalPreferences.LocalPreferences(); - let logFilterTimer = -1; let inputtedFilterText = ""; let selectBox; - let selectedLogLevels = JSON.parse(localPreferences.get("qbt_selected_log_levels")) || ["1", "2", "4", "8"]; + let selectedLogLevels = window.qBittorrent.ClientData.get("qbt_selected_log_levels") ?? ["1", "2", "4", "8"]; const init = () => { for (const option of document.getElementById("logLevelSelect").options) @@ -277,7 +275,8 @@ if (selectedLogLevels !== value) { tableInfo[currentSelectedTab].last_id = -1; selectedLogLevels = value; - localPreferences.set("qbt_selected_log_levels", JSON.stringify(selectedLogLevels)); + // don't await this + window.qBittorrent.ClientData.set({ qbt_selected_log_levels: selectedLogLevels }).catch(console.error); logFilterChanged(); } }; diff --git a/src/webui/www/private/views/preferences.html b/src/webui/www/private/views/preferences.html index 7325197cf2e0..2ace34e45c2e 100644 --- a/src/webui/www/private/views/preferences.html +++ b/src/webui/www/private/views/preferences.html @@ -1797,8 +1797,6 @@ }; }; - const localPreferences = new window.qBittorrent.LocalPreferences.LocalPreferences(); - // Behavior tab const numberInputLimiter = (input) => { const inputNumber = Number(input.value); @@ -2217,10 +2215,8 @@ document.getElementById("locale_select").value = selected; }; - const updateColoSchemeSelect = () => { + const updateColoSchemeSelect = (colorScheme) => { const combobox = document.getElementById("colorSchemeSelect"); - const colorScheme = localPreferences.get("color_scheme"); - if (colorScheme === "light") combobox.options[1].selected = true; else if (colorScheme === "dark") @@ -2232,20 +2228,31 @@ const loadPreferences = () => { window.parent.qBittorrent.Cache.preferences.init() .then((pref) => { + const clientData = window.parent.qBittorrent.ClientData; + const colorScheme = clientData.get("color_scheme"); + const fullUrlTrackerColumn = clientData.get("full_url_tracker_column"); + const useVirtualList = clientData.get("use_virtual_list"); + const hideZeroStatusFilters = clientData.get("hide_zero_status_filters"); + const dblclickDownload = clientData.get("dblclick_download"); + const dblclickComplete = clientData.get("dblclick_complete"); + const dblclickFilter = clientData.get("dblclick_filter"); + const useAltRowColors = clientData.get("use_alt_row_colors"); + // Behavior tab // Language updateWebuiLocaleSelect(pref.locale); - updateColoSchemeSelect(); + updateColoSchemeSelect(colorScheme); + document.getElementById("statusBarExternalIP").checked = pref.status_bar_external_ip; document.getElementById("performanceWarning").checked = pref.performance_warning; - document.getElementById("displayFullURLTrackerColumn").checked = (localPreferences.get("full_url_tracker_column", "false") === "true"); - document.getElementById("useVirtualList").checked = (localPreferences.get("use_virtual_list", "false") === "true"); - document.getElementById("hideZeroFiltersCheckbox").checked = (localPreferences.get("hide_zero_status_filters", "false") === "true"); - document.getElementById("dblclickDownloadSelect").value = localPreferences.get("dblclick_download", "1"); - document.getElementById("dblclickCompleteSelect").value = localPreferences.get("dblclick_complete", "1"); - document.getElementById("dblclickFiltersSelect").value = localPreferences.get("dblclick_filter", "1"); + document.getElementById("displayFullURLTrackerColumn").checked = fullUrlTrackerColumn === true; + document.getElementById("useVirtualList").checked = useVirtualList === true; + document.getElementById("hideZeroFiltersCheckbox").checked = hideZeroStatusFilters === true; + document.getElementById("dblclickDownloadSelect").value = dblclickDownload ?? "1"; + document.getElementById("dblclickCompleteSelect").value = dblclickComplete ?? "1"; + document.getElementById("dblclickFiltersSelect").value = dblclickFilter ?? "1"; document.getElementById("confirmTorrentDeletion").checked = pref.confirm_torrent_deletion; - document.getElementById("useAltRowColorsInput").checked = (localPreferences.get("use_alt_row_colors", "true") === "true"); + document.getElementById("useAltRowColorsInput").checked = useAltRowColors !== false; document.getElementById("filelog_checkbox").checked = pref.file_log_enabled; document.getElementById("filelog_save_path_input").value = pref.file_log_path; document.getElementById("filelog_backup_checkbox").checked = pref.file_log_backup_enabled; @@ -2658,8 +2665,9 @@ }); }; - const applyPreferences = () => { + const applyPreferences = async () => { const settings = {}; + const clientData = {}; // Validate form data // Behavior tab @@ -2667,21 +2675,21 @@ settings["locale"] = document.getElementById("locale_select").value; const colorScheme = Number(document.getElementById("colorSchemeSelect").value); if (colorScheme === 0) - localPreferences.remove("color_scheme"); + clientData.color_scheme = null; else if (colorScheme === 1) - localPreferences.set("color_scheme", "light"); + clientData.color_scheme = "light"; else - localPreferences.set("color_scheme", "dark"); + clientData.color_scheme = "dark"; settings["status_bar_external_ip"] = document.getElementById("statusBarExternalIP").checked; settings["performance_warning"] = document.getElementById("performanceWarning").checked; - localPreferences.set("full_url_tracker_column", document.getElementById("displayFullURLTrackerColumn").checked.toString()); - localPreferences.set("use_virtual_list", document.getElementById("useVirtualList").checked.toString()); - localPreferences.set("hide_zero_status_filters", document.getElementById("hideZeroFiltersCheckbox").checked.toString()); - localPreferences.set("dblclick_download", document.getElementById("dblclickDownloadSelect").value); - localPreferences.set("dblclick_complete", document.getElementById("dblclickCompleteSelect").value); - localPreferences.set("dblclick_filter", document.getElementById("dblclickFiltersSelect").value); + clientData.full_url_tracker_column = document.getElementById("displayFullURLTrackerColumn").checked; + clientData.use_virtual_list = document.getElementById("useVirtualList").checked; + clientData.hide_zero_status_filters = document.getElementById("hideZeroFiltersCheckbox").checked; + clientData.dblclick_download = document.getElementById("dblclickDownloadSelect").value; + clientData.dblclick_complete = document.getElementById("dblclickCompleteSelect").value; + clientData.dblclick_filter = document.getElementById("dblclickFiltersSelect").value; settings["confirm_torrent_deletion"] = document.getElementById("confirmTorrentDeletion").checked; - localPreferences.set("use_alt_row_colors", document.getElementById("useAltRowColorsInput").checked.toString()); + clientData.use_alt_row_colors = document.getElementById("useAltRowColorsInput").checked; settings["file_log_enabled"] = document.getElementById("filelog_checkbox").checked; settings["file_log_path"] = document.getElementById("filelog_save_path_input").value; settings["file_log_backup_enabled"] = document.getElementById("filelog_backup_checkbox").checked; @@ -3172,16 +3180,19 @@ settings["i2p_inbound_length"] = Number(document.getElementById("i2pInboundLength").value); settings["i2p_outbound_length"] = Number(document.getElementById("i2pOutboundLength").value); - // Send it to qBT - window.parent.qBittorrent.Cache.preferences.set(settings) - .then(() => { - // Close window - window.parent.location.reload(); - window.parent.qBittorrent.Client.closeWindow(document.getElementById("preferencesPage")); - }).catch((error) => { - alert("QBT_TR(Unable to save program preferences, qBittorrent is probably unreachable.)QBT_TR[CONTEXT=HttpServer]"); - window.parent.qBittorrent.Client.closeWindow(document.getElementById("preferencesPage")); - }); + // Send data to qBT + const results = await Promise.allSettled([ + window.parent.qBittorrent.ClientData.set(clientData), + window.parent.qBittorrent.Cache.preferences.set(settings) + ]); + if (results.every((result) => result.status === "fulfilled")) { + window.parent.location.reload(); + window.parent.qBittorrent.Client.closeWindow(document.getElementById("preferencesPage")); + } + else { + // keep window open so user can reattempt saving + alert("QBT_TR(Unable to save preferences, qBittorrent is probably unreachable.)QBT_TR[CONTEXT=HttpServer]"); + } }; const setup = () => { diff --git a/src/webui/www/webui.qrc b/src/webui/www/webui.qrc index f17da4d50677..cc0af3384aa6 100644 --- a/src/webui/www/webui.qrc +++ b/src/webui/www/webui.qrc @@ -390,6 +390,7 @@ private/scripts/addtorrent.js private/scripts/cache.js private/scripts/client.js + private/scripts/client-data.js private/scripts/color-scheme.js private/scripts/contextmenu.js private/scripts/dynamicTable.js