diff --git a/CHANGELOG.md b/CHANGELOG.md index 4e12bb5f..a591f7ec 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,16 +18,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Added support for touch-swipe and mouse-wheel gestures on the search engine icon to switch search engines when they are hidden ([@prem-k-r](https://github.com/prem-k-r)) ([#145](https://github.com/prem-k-r/MaterialYouNewTab/pull/145)) +- Migrated storage from `localStorage` to browser Storage API (`chrome.storage.sync` on Chrome, `browser.storage.local` on Firefox) with automatic one-time migration of existing user data ([@basupatil1213](https://github.com/basupatil1213)) ### Improved - Introduced collapsible sections in the settings menu for improved organization and easier navigation ([@prem-k-r](https://github.com/prem-k-r)) ([#109](https://github.com/prem-k-r/MaterialYouNewTab/pull/109)) +- Settings now sync across devices via `chrome.storage.sync`; large/ephemeral data (quotes, weather cache) kept local to respect sync quotas ([@basupatil1213](https://github.com/basupatil1213)) - Redesigned the theme selector from dropdown to buttons for easier switching between Light, Dark, and System modes ([@prem-k-r](https://github.com/prem-k-r)) ([#112](https://github.com/prem-k-r/MaterialYouNewTab/pull/112)) - Added expanding animations to todo and Google apps panels and some other minor UI changes ([@prem-k-r](https://github.com/prem-k-r)) ([#118](https://github.com/prem-k-r/MaterialYouNewTab/pull/118)) ### Fixed - Fixed an issue where rapid clicks on the AI Tools icon caused race conditions, leading to inconsistent shortcuts panel visibility. ([#118](https://github.com/prem-k-r/MaterialYouNewTab/pull/118)) +- Fixed flash of wrong theme color on page load by awaiting storage initialization before applying theme ([@basupatil1213](https://github.com/basupatil1213)) ### Localized diff --git a/index.html b/index.html index 2819962b..99ff992c 100644 --- a/index.html +++ b/index.html @@ -20,6 +20,7 @@ + diff --git a/manifest(firefox).json b/manifest(firefox).json index 4f8e3e54..9612b858 100644 --- a/manifest(firefox).json +++ b/manifest(firefox).json @@ -5,6 +5,7 @@ "description": "A Simple New Tab (browser's home page) inspired by Google's 'Material You' design.", "permissions": [ "search", + "storage", "bookmarks", "https://www.google.com/complete/search?client=*", "https://duckduckgo.com/ac/?q=*", diff --git a/manifest.json b/manifest.json index b13b9d12..37728bb6 100644 --- a/manifest.json +++ b/manifest.json @@ -3,7 +3,7 @@ "name": "MYNT: Material You New Tab", "version": "3.3.6", "description": "A Simple New Tab (browser's home page) inspired by Google's 'Material You' design.", - "permissions": ["search"], + "permissions": ["search", "storage"], "optional_permissions": ["bookmarks", "favicon"], "optional_host_permissions": [ "https://www.google.com/complete/search?client=*", diff --git a/scripts/ai-tools.js b/scripts/ai-tools.js index c113d736..28d6d9ae 100644 --- a/scripts/ai-tools.js +++ b/scripts/ai-tools.js @@ -122,19 +122,19 @@ function saveAIToolsSettings() { } }); - // Save settings to localStorage - localStorage.setItem("aiToolsSettings", JSON.stringify(aiToolsSettings)); + // Save settings to Storage + Storage.setItem("aiToolsSettings", JSON.stringify(aiToolsSettings)); } // Function to apply saved settings (visibility and order) function applyAIToolsSettings() { - const savedSettings = JSON.parse(localStorage.getItem("aiToolsSettings") || "null"); + const savedSettings = JSON.parse(Storage.getItem("aiToolsSettings") || "null"); let settingsToApply; if (!savedSettings || !Array.isArray(savedSettings)) { // Initialize with default values if no settings exist settingsToApply = aiTools.map(tool => tool.visible ? tool.id : { [tool.id]: false }); - localStorage.setItem("aiToolsSettings", JSON.stringify(settingsToApply)); + Storage.setItem("aiToolsSettings", JSON.stringify(settingsToApply)); } else { settingsToApply = savedSettings; } @@ -243,7 +243,7 @@ function showAIToolsSettings() { aiToolsForm.innerHTML = ""; // Load saved tool order and visibility or initialize from defaults - let savedSettings = JSON.parse(localStorage.getItem("aiToolsSettings") || "null"); + let savedSettings = JSON.parse(Storage.getItem("aiToolsSettings") || "null"); // If no settings exist, create from aiTools if (!savedSettings || !Array.isArray(savedSettings)) { @@ -444,8 +444,8 @@ document.addEventListener("DOMContentLoaded", function () { } }); - // Save settings to localStorage - localStorage.setItem("aiToolsSettings", JSON.stringify(newSettings)); + // Save settings to Storage + Storage.setItem("aiToolsSettings", JSON.stringify(newSettings)); // Apply new settings applyAIToolsSettings(); diff --git a/scripts/backup-restore.js b/scripts/backup-restore.js index d7af8214..37e230d1 100644 --- a/scripts/backup-restore.js +++ b/scripts/backup-restore.js @@ -11,18 +11,21 @@ document.getElementById("backupBtn").addEventListener("click", backupData); document.getElementById("restoreBtn").addEventListener("click", () => document.getElementById("fileInput").click()); document.getElementById("fileInput").addEventListener("change", validateAndRestoreData); -// Backup data from localStorage and IndexedDB +// Backup data from Storage API, localStorage (local-only keys) and IndexedDB async function backupData() { try { - const backup = { localStorage: {}, indexedDB: {} }; + const backup = { localStorage: {}, chromeStorage: {}, indexedDB: {} }; - // Backup localStorage + // Backup local-only keys from localStorage (quotes, weather cache, etc.) for (let key in localStorage) { if (localStorage.hasOwnProperty(key)) { backup.localStorage[key] = localStorage.getItem(key); } } + // Backup all synced settings from chrome.storage via Storage wrapper + backup.chromeStorage = await Storage.getAll(); + // Backup IndexedDB (ImageDB) backup.indexedDB = await backupIndexedDB(); @@ -150,16 +153,57 @@ async function restoreIndexedDB(data) { }); } -// Restore data for both localStorage and IndexedDB +// Restore data for Storage API, localStorage and IndexedDB. +// Supports both the old (localStorage-only) and new (chromeStorage) backup formats. async function restoreData(backup) { - // Clear localStorage before restoring - localStorage.clear(); + // Clear all synced settings from chrome.storage + await Storage.clearForRestore(); + + // Clear local-only localStorage data (quotes, weather cache) + // We'll selectively restore only what's in the backup + const localOnlyKeys = Object.keys(localStorage); + localOnlyKeys.forEach(key => localStorage.removeItem(key)); - // Restore localStorage from backup + // --- Restore local-only keys (quotes, weather cache, etc.) --- if (backup.localStorage) { Object.keys(backup.localStorage).forEach(key => { - localStorage.setItem(key, backup.localStorage[key]); + // Restore only keys that belong in localStorage + // (local-only keys: quotes_*, LoadingScreenColor, weatherParsed*) + if ( + key.startsWith("quotes_") || + key === "LoadingScreenColor" || + key === "weatherParsedData" || + key === "weatherParsedTime" || + key === "weatherParsedLocation" || + key === "weatherParsedLang" + ) { + localStorage.setItem(key, backup.localStorage[key]); + } + }); + } + + // --- Restore synced settings --- + if (backup.chromeStorage && Object.keys(backup.chromeStorage).length > 0) { + // New backup format: restore from chromeStorage field + await Storage.setAll(backup.chromeStorage); + } else if (backup.localStorage) { + // Old backup format: migrate non-local-only keys to chrome.storage + const settingsToRestore = {}; + Object.keys(backup.localStorage).forEach(key => { + if ( + !key.startsWith("quotes_") && + key !== "LoadingScreenColor" && + key !== "weatherParsedData" && + key !== "weatherParsedTime" && + key !== "weatherParsedLocation" && + key !== "weatherParsedLang" + ) { + settingsToRestore[key] = backup.localStorage[key]; + } }); + if (Object.keys(settingsToRestore).length > 0) { + await Storage.setAll(settingsToRestore); + } } // Restore IndexedDB from backup @@ -183,13 +227,19 @@ function base64ToBlob(base64) { // ------------------- Reset Settings ---------------------------- const resetbtn = document.getElementById("resetsettings"); -// Clear localStorage and reload the page +// Clear all settings and reload the page resetbtn.addEventListener("click", async () => { const confirmationMessage = translations[currentLanguage]?.confirmRestore || translations["en"].confirmRestore; if (await confirmPrompt(confirmationMessage)) { - localStorage.clear(); + await Storage.reset(); + // Also clear local-only localStorage data + const localKeys = Object.keys(localStorage).filter(k => + !k.startsWith("quotes_") && k !== "LoadingScreenColor" && + k !== "weatherParsedData" && k !== "weatherParsedTime" && + k !== "weatherParsedLocation" && k !== "weatherParsedLang" + ); + localKeys.forEach(k => localStorage.removeItem(k)); location.reload(); } }); - diff --git a/scripts/bookmarks.js b/scripts/bookmarks.js index 5b0d0e56..0fbcaa18 100644 --- a/scripts/bookmarks.js +++ b/scripts/bookmarks.js @@ -27,7 +27,7 @@ let currentBookmarkId = null; const sortAlphabetical = document.getElementById("sortAlphabetical"); const sortTimeAdded = document.getElementById("sortTimeAdded"); -let currentSortMethod = localStorage.getItem("bookmarkSortMethod") || 'title'; +let currentSortMethod = Storage.getItem("bookmarkSortMethod") || 'title'; var bookmarksAPI; if (isFirefox) { @@ -135,7 +135,7 @@ bookmarkSearch.addEventListener("input", function () { sortAlphabetical.addEventListener("click", function () { if (!this.classList.contains("active")) { currentSortMethod = 'title'; - localStorage.setItem("bookmarkSortMethod", "title"); + Storage.setItem("bookmarkSortMethod", "title"); updateSortButtons(); loadBookmarks(); } @@ -144,7 +144,7 @@ sortAlphabetical.addEventListener("click", function () { sortTimeAdded.addEventListener("click", function () { if (!this.classList.contains("active")) { currentSortMethod = 'date'; - localStorage.setItem("bookmarkSortMethod", "date"); + Storage.setItem("bookmarkSortMethod", "date"); updateSortButtons(); loadBookmarks(); } diff --git a/scripts/clock.js b/scripts/clock.js index 2f108a13..1ccf38df 100644 --- a/scripts/clock.js +++ b/scripts/clock.js @@ -45,8 +45,8 @@ function handleClockVisibility() { rightDiv.classList.remove("clock-padding-adjusted"); } else { - // Retrieve saved state from localStorage - const isClockHidden = localStorage.getItem("hideClockVisible") === "true"; + // Retrieve saved state from Storage + const isClockHidden = Storage.getItem("hideClockVisible") === "true"; hideClockCheckbox.checked = isClockHidden; // Apply initial state @@ -59,7 +59,7 @@ function handleClockVisibility() { hideClockCheckbox.addEventListener("change", function () { const isChecked = hideClockCheckbox.checked; - localStorage.setItem("hideClockVisible", isChecked); + Storage.setItem("hideClockVisible", isChecked); applyClockState(isChecked); toggleHideState(isChecked); @@ -113,9 +113,9 @@ async function initializeClock() { document.getElementById("hour").style.transform = `rotate(${cumulativeHourRotation}deg)`; function initializeClockType() { - const savedClockType = localStorage.getItem("clocktype"); + const savedClockType = Storage.getItem("clocktype"); clocktype = savedClockType ? savedClockType : "analog"; // Default to "analog" if nothing is saved - localStorage.setItem("clocktype", clocktype); // Ensure it's set in local storage + Storage.setItem("clocktype", clocktype); // Ensure it's set in local storage } // Call this function to initialize the clock type @@ -258,10 +258,10 @@ async function initializeClock() { } function updatedigiClock() { - const hourformatstored = localStorage.getItem("hourformat"); + const hourformatstored = Storage.getItem("hourformat"); let hourformat = hourformatstored === "true"; // Default to false if null const greetingCheckbox = document.getElementById("greetingcheckbox"); - const isGreetingEnabled = localStorage.getItem("greetingEnabled") === "true"; + const isGreetingEnabled = Storage.getItem("greetingEnabled") === "true"; greetingCheckbox.checked = isGreetingEnabled; const now = new Date(); @@ -389,7 +389,7 @@ async function initializeClock() { document.getElementById("amPm").textContent = ""; // Clear AM/PM for 24-hour format } - const clocktype1 = localStorage.getItem("clocktype"); + const clocktype1 = Storage.getItem("clocktype"); const dateElement = document.getElementById("date"); if (clocktype1 === "digital" && isGreetingEnabled) { const newGreeting = getGreeting(); @@ -496,12 +496,12 @@ async function initializeClock() { const greetingCheckbox = document.getElementById("greetingcheckbox"); const greetingField = document.getElementById("greetingField"); - if (localStorage.getItem("greetingEnabled") === null) { - localStorage.setItem("greetingEnabled", "true"); + if (Storage.getItem("greetingEnabled") === null) { + Storage.setItem("greetingEnabled", "true"); } - greetingCheckbox.checked = localStorage.getItem("greetingEnabled") === "true"; - greetingCheckbox.disabled = localStorage.getItem("clocktype") !== "digital"; + greetingCheckbox.checked = Storage.getItem("greetingEnabled") === "true"; + greetingCheckbox.disabled = Storage.getItem("clocktype") !== "digital"; digitalCheckbox.addEventListener("change", function () { saveCheckboxState("digitalCheckboxState", digitalCheckbox); @@ -509,8 +509,8 @@ async function initializeClock() { timeformatField.classList.remove("inactive"); greetingField.classList.remove("inactive"); greetingCheckbox.disabled = false; // Enable greeting toggle - localStorage.setItem("clocktype", "digital"); - clocktype = localStorage.getItem("clocktype"); + Storage.setItem("clocktype", "digital"); + clocktype = Storage.getItem("clocktype"); displayClock(); stopAnalogClock(); startDigitalClock(); @@ -521,8 +521,8 @@ async function initializeClock() { timeformatField.classList.add("inactive"); greetingField.classList.add("inactive"); greetingCheckbox.disabled = true; // Disable greeting toggle - localStorage.setItem("clocktype", "analog"); - clocktype = localStorage.getItem("clocktype"); + Storage.setItem("clocktype", "analog"); + clocktype = Storage.getItem("clocktype"); stopDigitalClock(); startAnalogClock(); updateanalogclock(); @@ -536,14 +536,14 @@ async function initializeClock() { hourcheckbox.addEventListener("change", function () { saveCheckboxState("hourcheckboxState", hourcheckbox); if (hourcheckbox.checked) { - localStorage.setItem("hourformat", "true"); + Storage.setItem("hourformat", "true"); } else { - localStorage.setItem("hourformat", "false"); + Storage.setItem("hourformat", "false"); } }); greetingCheckbox.addEventListener("change", () => { - localStorage.setItem("greetingEnabled", greetingCheckbox.checked); + Storage.setItem("greetingEnabled", greetingCheckbox.checked); updatedigiClock(); }); diff --git a/scripts/custom-text.js b/scripts/custom-text.js index 8f405677..105619b1 100644 --- a/scripts/custom-text.js +++ b/scripts/custom-text.js @@ -12,7 +12,7 @@ document.addEventListener("DOMContentLoaded", () => { const userTextCheckbox = document.getElementById("userTextCheckbox"); // Load and apply the checkbox state - const isUserTextVisible = localStorage.getItem("userTextVisible") !== "false"; + const isUserTextVisible = Storage.getItem("userTextVisible") !== "false"; userTextCheckbox.checked = isUserTextVisible; userTextDiv.style.display = isUserTextVisible ? "block" : "none"; @@ -20,15 +20,15 @@ document.addEventListener("DOMContentLoaded", () => { userTextCheckbox.addEventListener("change", () => { const isVisible = userTextCheckbox.checked; userTextDiv.style.display = isVisible ? "block" : "none"; - localStorage.setItem("userTextVisible", isVisible); + Storage.setItem("userTextVisible", isVisible); }); // Set the default language to English if no language is saved - const savedLang = localStorage.getItem("selectedLanguage") || "en"; + const savedLang = Storage.getItem("selectedLanguage") || "en"; applyLanguage(savedLang); // Load the stored text if it exists - const storedValue = localStorage.getItem("userText"); + const storedValue = Storage.getItem("userText"); if (storedValue) { userTextDiv.textContent = storedValue; } else { @@ -39,7 +39,7 @@ document.addEventListener("DOMContentLoaded", () => { // Handle input event userTextDiv.addEventListener("input", function () { - localStorage.setItem("userText", userTextDiv.textContent); + Storage.setItem("userText", userTextDiv.textContent); }); // Remove placeholder text when the user starts editing diff --git a/scripts/languages.js b/scripts/languages.js index 39c3f315..4d6ca2ae 100644 --- a/scripts/languages.js +++ b/scripts/languages.js @@ -302,8 +302,8 @@ function applyLanguage(lang) { if (translations[lang]) { const placeholder = translations[lang]?.userText || translations["en"].userText; userTextDiv.dataset.placeholder = placeholder; // Update the placeholder in data attribute - // Only set the text content if there's nothing in localStorage - if (!localStorage.getItem("userText")) { + // Only set the text content if there's nothing in Storage + if (!Storage.getItem("userText")) { userTextDiv.innerText = placeholder; } } @@ -383,7 +383,7 @@ function applyLanguage(lang) { // quotesText.style.textAlign = isRTL ? "right" : "left"; quotesText.style.fontFamily = commonFontStack; - // Save the selected language in localStorage + // Save the selected language in Storage document.documentElement.lang = currentLanguage; saveLanguageStatus("selectedLanguage", lang); } @@ -401,12 +401,12 @@ window.onload = function () { applyLanguage(savedLanguage); }; -// Function to save the language status in localStorage +// Function to save the language status to Storage function saveLanguageStatus(key, languageStatus) { - localStorage.setItem(key, languageStatus); + Storage.setItem(key, languageStatus); } -// Function to get the language status from localStorage +// Function to get the language status from Storage function getLanguageStatus(key) { - return localStorage.getItem(key); + return Storage.getItem(key); } diff --git a/scripts/menu-shortcut-page.js b/scripts/menu-shortcut-page.js index ff27854c..070effa6 100644 --- a/scripts/menu-shortcut-page.js +++ b/scripts/menu-shortcut-page.js @@ -7,8 +7,8 @@ */ -// Get the current language from localStorage -const currentLanguage = localStorage.getItem("selectedLanguage") || "en"; +// Get the current language from Storage +const currentLanguage = Storage.getItem("selectedLanguage") || "en"; const isRTL = rtlLanguages.includes(currentLanguage); // ------------Showing & Hiding Menu-bar --------------- diff --git a/scripts/save-load-states.js b/scripts/save-load-states.js index 1d90ad41..341a25b9 100644 --- a/scripts/save-load-states.js +++ b/scripts/save-load-states.js @@ -8,15 +8,15 @@ /* ------ Helper functions for saving and loading states ------ */ -// Function to save checkbox state to localStorage +// Function to save checkbox state to Storage function saveCheckboxState(key, checkbox) { - localStorage.setItem(key, checkbox.checked ? "checked" : "unchecked"); + Storage.setItem(key, checkbox.checked ? "checked" : "unchecked"); } const bookmarkGridCheckbox = document.getElementById("bookmarkGridCheckbox"); -// Function to load and apply checkbox state from localStorage +// Function to load and apply checkbox state from Storage function loadCheckboxState(key, checkbox) { - const savedState = localStorage.getItem(key); + const savedState = Storage.getItem(key); checkbox.checked = savedState === "checked"; if (key === "bookmarkGridCheckboxState") { if (!savedState) { @@ -28,14 +28,14 @@ function loadCheckboxState(key, checkbox) { } } -// Function to save display status to localStorage +// Function to save display status to Storage function saveDisplayStatus(key, displayStatus) { - localStorage.setItem(key, displayStatus); + Storage.setItem(key, displayStatus); } -// Function to load and apply display status from localStorage +// Function to load and apply display status from Storage function loadDisplayStatus(key, element) { - const savedStatus = localStorage.getItem(key); + const savedStatus = Storage.getItem(key); if (savedStatus === "flex") { element.style.display = "flex"; } else { @@ -43,14 +43,14 @@ function loadDisplayStatus(key, element) { } } -// Function to save activeness status to localStorage +// Function to save activeness status to Storage function saveActiveStatus(key, activeStatus) { - localStorage.setItem(key, activeStatus); + Storage.setItem(key, activeStatus); } -// Function to load and apply activeness status from localStorage +// Function to load and apply activeness status from Storage function loadActiveStatus(key, element) { - const savedStatus = localStorage.getItem(key); + const savedStatus = Storage.getItem(key); if (savedStatus === "active") { element.classList.remove("inactive"); } else { diff --git a/scripts/script.js b/scripts/script.js index 65c5e4cf..8f207a19 100644 --- a/scripts/script.js +++ b/scripts/script.js @@ -45,14 +45,14 @@ document.addEventListener("DOMContentLoaded", function () { const dontShowButton = document.getElementById("dontShowTips"); // Check if the user has previously disabled tips - if (localStorage.getItem("hideTips") === "true") { + if (Storage.getItem("hideTips") === "true") { tips.style.display = "none"; } // Hide tips and save preference when button is clicked dontShowButton.addEventListener("click", function () { tips.style.display = "none"; - localStorage.setItem("hideTips", "true"); // Save preference + Storage.setItem("hideTips", "true"); // Save preference }); }); @@ -75,11 +75,11 @@ document.addEventListener("DOMContentLoaded", function () { function showToast() { // Check if toast has been shown before - const hasShown = localStorage.getItem(STORAGE_KEY); + const hasShown = Storage.getItem(STORAGE_KEY); if (hasShown) return; // Mark as shown - localStorage.setItem(STORAGE_KEY, 'true'); + Storage.setItem(STORAGE_KEY, 'true'); // Show toast after brief delay setTimeout(() => { diff --git a/scripts/search-suggestions.js b/scripts/search-suggestions.js index 539aa9c3..6a46cae8 100644 --- a/scripts/search-suggestions.js +++ b/scripts/search-suggestions.js @@ -11,7 +11,7 @@ let proxyurl; document.addEventListener("DOMContentLoaded", () => { const userProxyInput = document.getElementById("userproxy"); const saveProxyButton = document.getElementById("saveproxy"); - const savedProxy = localStorage.getItem("proxy"); + const savedProxy = Storage.getItem("proxy"); const defaultProxyURL = "https://mynt-proxy.rhythmcorehq.com/proxy?url="; //Default proxy url @@ -26,7 +26,7 @@ document.addEventListener("DOMContentLoaded", () => { } }); - // Save the proxy to localStorage + // Save the proxy to Storage saveProxyButton.addEventListener("click", () => { proxyurl = userProxyInput.value.trim(); @@ -39,8 +39,8 @@ document.addEventListener("DOMContentLoaded", () => { proxyurl = "https://" + proxyurl; } } - // Set the proxy in localStorage, clear the input, and reload the page - localStorage.setItem("proxy", proxyurl); + // Set the proxy in Storage, clear the input, and reload the page + Storage.setItem("proxy", proxyurl); userProxyInput.value = ""; location.reload(); }); diff --git a/scripts/search.js b/scripts/search.js index 3c600972..88f6c00d 100644 --- a/scripts/search.js +++ b/scripts/search.js @@ -9,7 +9,7 @@ const searchbar = document.getElementById("searchbar"); const searchInput = document.getElementById("searchQ"); -const languageCode = (localStorage.getItem("selectedLanguage") || "en").slice(0, 2); +const languageCode = (Storage.getItem("selectedLanguage") || "en").slice(0, 2); const searchQueryURLs = { engine1: "https://www.google.com/search?q=", engine2: "https://duckduckgo.com/?q=", @@ -43,7 +43,7 @@ document.addEventListener("click", function (event) { const searchWith = document.getElementById("searchWithHint"); const searchEngines = document.querySelectorAll(".searchEnginesContainer .search-engine"); const searchEnginesContainer = document.querySelector(".searchEnginesContainer"); -let activeSearchMode = localStorage.getItem("activeSearchMode") || "search-with"; +let activeSearchMode = Storage.getItem("activeSearchMode") || "search-with"; searchWith.addEventListener("click", function (event) { activeSearchMode = (activeSearchMode === "search-with") ? "search-on" : "search-with"; @@ -64,7 +64,7 @@ function toggleSearchEngines(category) { "search-with": "engine0", "search-on": "engine5", }; - const checkeditem = localStorage.getItem(`selectedSearchEngine-${category}`) || defaultItems[category]; + const checkeditem = Storage.getItem(`selectedSearchEngine-${category}`) || defaultItems[category]; const searchModeName = category === "search-with" ? "searchWithHint" : "searchOnHint"; searchWith.innerText = translations[currentLanguage][searchModeName] || translations["en"][searchModeName]; @@ -153,8 +153,8 @@ searchDropdowns.forEach(element => { swapDropdown(selector); sortDropdown() - localStorage.setItem(`selectedSearchEngine-${radioButton.parentElement.dataset.category}`, radioButton.value); - localStorage.setItem(`activeSearchMode`, radioButton.parentElement.dataset.category); + Storage.setItem(`selectedSearchEngine-${radioButton.parentElement.dataset.category}`, radioButton.value); + Storage.setItem(`activeSearchMode`, radioButton.parentElement.dataset.category); }); }); @@ -174,8 +174,8 @@ document.querySelectorAll(".search-engine").forEach((engineDiv) => { swapDropdown(selector); sortDropdown(); - localStorage.setItem(`selectedSearchEngine-${radioButton.parentElement.dataset.category}`, radioButton.value); - localStorage.setItem(`activeSearchMode`, radioButton.parentElement.dataset.category); + Storage.setItem(`selectedSearchEngine-${radioButton.parentElement.dataset.category}`, radioButton.value); + Storage.setItem(`activeSearchMode`, radioButton.parentElement.dataset.category); searchInput.focus(); searchbar.classList.add("active"); @@ -232,7 +232,7 @@ enterBTN.addEventListener("click", () => performSearch()); // Enter key handling is managed in the search suggestions keydown listener // Set selected search engine from local storage -const storedSearchEngine = localStorage.getItem(`selectedSearchEngine-${activeSearchMode}`); +const storedSearchEngine = Storage.getItem(`selectedSearchEngine-${activeSearchMode}`); toggleSearchEngines(activeSearchMode); @@ -310,7 +310,7 @@ document.querySelector(".dropdown").addEventListener("keydown", function (event) swapDropdown(`*[data-engine="${engine}"]`); sortDropdown(); - localStorage.setItem("selectedSearchEngine", radioButton.value); + Storage.setItem("selectedSearchEngine", radioButton.value); // Close the dropdown after selection dropdown.classList.remove("show"); @@ -327,8 +327,8 @@ updateSelection(); searchEngineRadio.forEach((radio) => { radio.addEventListener("change", () => { const selectedOption = document.querySelector('input[name="search-engine"]:checked'); - localStorage.setItem(`selectedSearchEngine-${selectedOption.parentElement.dataset.category}`, selectedOption.value); - localStorage.setItem(`activeSearchMode`, selectedOption.parentElement.dataset.category); + Storage.setItem(`selectedSearchEngine-${selectedOption.parentElement.dataset.category}`, selectedOption.value); + Storage.setItem(`activeSearchMode`, selectedOption.parentElement.dataset.category); }); }); @@ -350,10 +350,10 @@ const hideEngineContainer = () => { const initShortCutSwitch = (element) => { if (element.checked) { hideEngineContainer(); - localStorage.setItem("showShortcutSwitch", true) + Storage.setItem("showShortcutSwitch", true) } else { showEngineContainer(); - localStorage.setItem("showShortcutSwitch", false) + Storage.setItem("showShortcutSwitch", false) } } @@ -363,11 +363,11 @@ hideSearchWith.addEventListener("change", (e) => { initShortCutSwitch(e.target); // Fetch active search mode from storage - let activeSearchMode = localStorage.getItem("activeSearchMode") || "search-with"; + let activeSearchMode = Storage.getItem("activeSearchMode") || "search-with"; toggleSearchEngines(activeSearchMode); - // Get the selected search engine from localStorage - const storedSearchEngine = localStorage.getItem(`selectedSearchEngine-${activeSearchMode}`); + // Get the selected search engine from Storage + const storedSearchEngine = Storage.getItem(`selectedSearchEngine-${activeSearchMode}`); // Find the corresponding radio button const selectedRadioButton = document.querySelector(`input[name="search-engine"][value="${storedSearchEngine}"]`); @@ -382,8 +382,8 @@ hideSearchWith.addEventListener("change", (e) => { }); // Intialize shortcut switch -if (localStorage.getItem("showShortcutSwitch")) { - const isShortCutSwitchEnabled = localStorage.getItem("showShortcutSwitch").toString() === "true"; +if (Storage.getItem("showShortcutSwitch")) { + const isShortCutSwitchEnabled = Storage.getItem("showShortcutSwitch").toString() === "true"; document.getElementById("shortcut_switchcheckbox").checked = isShortCutSwitchEnabled; if (isShortCutSwitchEnabled) { @@ -392,7 +392,7 @@ if (localStorage.getItem("showShortcutSwitch")) { showEngineContainer() } } else { - localStorage.setItem("showShortcutSwitch", false); + Storage.setItem("showShortcutSwitch", false); } initShortCutSwitch(hideSearchWith); @@ -455,8 +455,8 @@ function switchEngine(direction) { swapDropdown(selector); sortDropdown(); - localStorage.setItem(`selectedSearchEngine-${radioButton.parentElement.dataset.category}`, radioButton.value); - localStorage.setItem(`activeSearchMode`, radioButton.parentElement.dataset.category); + Storage.setItem(`selectedSearchEngine-${radioButton.parentElement.dataset.category}`, radioButton.value); + Storage.setItem(`activeSearchMode`, radioButton.parentElement.dataset.category); // Update the search mode hint text const newCategory = radioButton.parentElement.dataset.category; diff --git a/scripts/shortcuts.js b/scripts/shortcuts.js index 362241fd..fb7dec0f 100644 --- a/scripts/shortcuts.js +++ b/scripts/shortcuts.js @@ -81,7 +81,7 @@ document.addEventListener("DOMContentLoaded", function () { setupEventListeners(); loadShortcuts(); - // Loads all settings from localStorage and applies them + // Loads all settings from Storage and applies them function loadSettings() { loadCheckboxState("shortcutsCheckboxState", dom.shortcutsCheckbox); loadCheckboxState("adaptiveIconToggle", dom.adaptiveIconToggle); @@ -113,7 +113,7 @@ document.addEventListener("DOMContentLoaded", function () { function handleNewShortcutClick() { if (this.classList.contains("inactive")) return; - const currentAmount = parseInt(localStorage.getItem("shortcutAmount")) || shortcutsCache.length; + const currentAmount = parseInt(Storage.getItem("shortcutAmount")) || shortcutsCache.length; if (currentAmount >= MAX_SHORTCUTS) return; addNewShortcut(); @@ -133,16 +133,16 @@ document.addEventListener("DOMContentLoaded", function () { }, 800); } - // Loads shortcuts from localStorage or uses presets if none exist + // Loads shortcuts from Storage or uses presets if none exist function loadShortcuts() { - const amount = localStorage.getItem("shortcutAmount") || presets.length; + const amount = Storage.getItem("shortcutAmount") || presets.length; const deleteInactive = amount <= 1; shortcutsCache = []; for (let i = 0; i < amount; i++) { - const name = localStorage.getItem(`shortcutName${i}`) || (presets[i] ? presets[i].name : PLACEHOLDER.name); - const url = localStorage.getItem(`shortcutURL${i}`) || (presets[i] ? presets[i].url : PLACEHOLDER.url); + const name = Storage.getItem(`shortcutName${i}`) || (presets[i] ? presets[i].name : PLACEHOLDER.name); + const url = Storage.getItem(`shortcutURL${i}`) || (presets[i] ? presets[i].url : PLACEHOLDER.url); shortcutsCache.push({ name, url }); @@ -559,7 +559,7 @@ document.addEventListener("DOMContentLoaded", function () { }); } - // Saves the new shortcut order to localStorage + // Saves the new shortcut order to Storage function saveShortcutOrder() { const entries = dom.shortcutSettingsContainer.querySelectorAll(".shortcutSettingsEntry"); const newOrder = Array.from(entries).map(entry => ({ @@ -569,10 +569,10 @@ document.addEventListener("DOMContentLoaded", function () { // Only save if order has changed if (hasOrderChanged(newOrder)) { - localStorage.setItem("shortcutAmount", newOrder.length.toString()); + Storage.setItem("shortcutAmount", newOrder.length.toString()); newOrder.forEach((item, index) => { - localStorage.setItem(`shortcutName${index}`, item.name); - localStorage.setItem(`shortcutURL${index}`, item.url); + Storage.setItem(`shortcutName${index}`, item.name); + Storage.setItem(`shortcutURL${index}`, item.url); }); shortcutsCache = newOrder; @@ -629,11 +629,11 @@ document.addEventListener("DOMContentLoaded", function () { // Adds a new shortcut function addNewShortcut() { - const currentAmount = parseInt(localStorage.getItem("shortcutAmount")) || shortcutsCache.length; + const currentAmount = parseInt(Storage.getItem("shortcutAmount")) || shortcutsCache.length; if (currentAmount >= MAX_SHORTCUTS) return; const newAmount = currentAmount + 1; - localStorage.setItem("shortcutAmount", newAmount.toString()); + Storage.setItem("shortcutAmount", newAmount.toString()); if (currentAmount >= 1) { document.querySelectorAll(".delete button.inactive").forEach(b => { @@ -654,22 +654,22 @@ document.addEventListener("DOMContentLoaded", function () { // Deletes a shortcut function deleteShortcut(entry) { - const currentAmount = parseInt(localStorage.getItem("shortcutAmount")) || shortcutsCache.length; + const currentAmount = parseInt(Storage.getItem("shortcutAmount")) || shortcutsCache.length; if (currentAmount <= 1) return; const index = entry._index; entry.remove(); dom.shortcutsContainer.removeChild(dom.shortcutsContainer.children[index]); - // Update localStorage - localStorage.setItem("shortcutAmount", (currentAmount - 1).toString()); + // Update Storage + Storage.setItem("shortcutAmount", (currentAmount - 1).toString()); for (let i = index; i < currentAmount - 1; i++) { const nextEntry = dom.shortcutSettingsContainer.children[i]; nextEntry._index = i; saveShortcut(nextEntry); } - localStorage.removeItem(`shortcutName${currentAmount - 1}`); - localStorage.removeItem(`shortcutURL${currentAmount - 1}`); + Storage.removeItem(`shortcutName${currentAmount - 1}`); + Storage.removeItem(`shortcutURL${currentAmount - 1}`); if (currentAmount - 1 === 1) { document.querySelectorAll(".delete button").forEach(b => { @@ -694,11 +694,11 @@ document.addEventListener("DOMContentLoaded", function () { svg.classList.add("rotate-animation"); // Clear storage - for (let i = 0; i < (localStorage.getItem("shortcutAmount") || 0); i++) { - localStorage.removeItem(`shortcutName${i}`); - localStorage.removeItem(`shortcutURL${i}`); + for (let i = 0; i < (Storage.getItem("shortcutAmount") || 0); i++) { + Storage.removeItem(`shortcutName${i}`); + Storage.removeItem(`shortcutURL${i}`); } - localStorage.removeItem("shortcutAmount"); + Storage.removeItem("shortcutAmount"); // Wait for animations of shortcut elements to complete await new Promise(resolve => setTimeout(resolve, 300)); @@ -713,9 +713,9 @@ document.addEventListener("DOMContentLoaded", function () { loadShortcuts(); } - // Saves a single shortcut to localStorage + // Saves a single shortcut to Storage function saveShortcut(entry) { - localStorage.setItem(`shortcutName${entry._index}`, entry.querySelector(".shortcutName").value); - localStorage.setItem(`shortcutURL${entry._index}`, entry.querySelector(".URL").value); + Storage.setItem(`shortcutName${entry._index}`, entry.querySelector(".shortcutName").value); + Storage.setItem(`shortcutURL${entry._index}`, entry.querySelector(".URL").value); } }); diff --git a/scripts/storage.js b/scripts/storage.js new file mode 100644 index 00000000..9d2872ff --- /dev/null +++ b/scripts/storage.js @@ -0,0 +1,177 @@ +/* + * Material You NewTab + * Copyright (c) 2023-2025 XengShi + * Licensed under the GNU General Public License v3.0 (GPL-3.0) + * You should have received a copy of the GNU General Public License along with this program. + * If not, see . + */ + +// --------------- Centralized Storage Wrapper --------------- +// Uses chrome.storage.sync on Chrome, browser.storage.local on Firefox. +// Falls back to localStorage if chrome.storage is unavailable (e.g., website preview). + +(function () { + // Keys that must remain in localStorage (not synced): + // quotes_* : large quote arrays, exceed chrome.storage.sync item quota + // LoadingScreenColor : computed value used by preload.js for fast synchronous read + // weatherParsed* : ephemeral API cache, not worth syncing across devices + function _isLocalOnly(key) { + return key.startsWith("quotes_") + || key === "LoadingScreenColor" + || key === "weatherParsedData" + || key === "weatherParsedTime" + || key === "weatherParsedLocation" + || key === "weatherParsedLang"; + } + + // Firefox detection (browser-utils.js has not loaded yet at this point) + const _isFirefox = typeof browser !== "undefined" && /firefox/i.test(navigator.userAgent); + + // Return the appropriate storage area for settings + function _getStorageArea() { + if (_isFirefox) { + return (typeof browser !== "undefined" && browser.storage && browser.storage.local) || null; + } + if (typeof chrome !== "undefined" && chrome.storage) { + return chrome.storage.sync || chrome.storage.local || null; + } + return null; + } + + // Internal key to track whether migration from localStorage has been done + const MIGRATION_KEY = "_storage_migrated"; + + // In-memory cache: synchronously pre-populated from localStorage so that + // defer scripts can read settings immediately before chrome.storage resolves. + const _cache = {}; + for (let i = 0; i < localStorage.length; i++) { + const key = localStorage.key(i); + if (!_isLocalOnly(key)) _cache[key] = localStorage.getItem(key); + } + + // One-time migration: copy all non-local-only localStorage keys to chrome.storage, + // then remove them from localStorage so there is a single source of truth. + async function _migrate(storageArea) { + const result = await storageArea.get(MIGRATION_KEY); + if (result[MIGRATION_KEY]) return; // Already migrated + + const toMigrate = { [MIGRATION_KEY]: true }; + for (let i = 0; i < localStorage.length; i++) { + const key = localStorage.key(i); + if (!_isLocalOnly(key)) toMigrate[key] = localStorage.getItem(key); + } + + await storageArea.set(toMigrate); + + // Remove migrated keys from localStorage (preserves local-only keys) + Object.keys(toMigrate).forEach(key => { + if (key !== MIGRATION_KEY) localStorage.removeItem(key); + }); + } + + // Promise that resolves once chrome.storage is loaded and cache is up-to-date. + // DOMContentLoaded handlers that need accurate settings should await this. + window.storageReady = (async () => { + const storageArea = _getStorageArea(); + if (!storageArea) return; // No chrome.storage available; cache serves as localStorage proxy + + await _migrate(storageArea); + + // Load all settings from chrome.storage into the in-memory cache + const items = await storageArea.get(null); + Object.assign(_cache, items); + + // Listen for external changes (e.g., sync data arriving from another device) + const onChanged = _isFirefox + ? (typeof browser !== "undefined" && browser.storage && browser.storage.onChanged) + : (typeof chrome !== "undefined" && chrome.storage && chrome.storage.onChanged); + + if (onChanged) { + onChanged.addListener((changes) => { + for (const [key, { newValue }] of Object.entries(changes)) { + if (newValue !== undefined) _cache[key] = newValue; + else delete _cache[key]; + } + }); + } + })(); + + // ---------- Public Storage API ---------- + + window.Storage = { + // Synchronous read from cache (or localStorage for local-only keys). + // Safe to call at any time; returns null if key does not exist. + getItem(key) { + if (_isLocalOnly(key)) return localStorage.getItem(key); + return Object.prototype.hasOwnProperty.call(_cache, key) ? _cache[key] : null; + }, + + // Write to cache immediately and persist to chrome.storage (fire-and-forget). + // Callers do not need to await this. + setItem(key, value) { + if (_isLocalOnly(key)) { localStorage.setItem(key, value); return; } + _cache[key] = value; + const storageArea = _getStorageArea(); + if (storageArea) storageArea.set({ [key]: value }); + else localStorage.setItem(key, value); + }, + + // Remove from cache and from chrome.storage (fire-and-forget). + removeItem(key) { + if (_isLocalOnly(key)) { localStorage.removeItem(key); return; } + delete _cache[key]; + const storageArea = _getStorageArea(); + if (storageArea) storageArea.remove(key); + else localStorage.removeItem(key); + }, + + // Clear all synced settings but keep the migration marker so re-migration + // is not triggered on next load. Used by the reset-settings button. + reset() { + const storageArea = _getStorageArea(); + const keysToRemove = Object.keys(_cache).filter(k => k !== MIGRATION_KEY); + keysToRemove.forEach(key => delete _cache[key]); + if (storageArea) return storageArea.remove(keysToRemove); + keysToRemove.forEach(key => localStorage.removeItem(key)); + }, + + // Clear ALL chrome.storage including the migration marker. Used before + // restoring a backup so old data does not bleed through. + clearForRestore() { + const storageArea = _getStorageArea(); + Object.keys(_cache).forEach(key => delete _cache[key]); + if (storageArea) return storageArea.clear(); + // Fallback: clear only non-local-only keys from localStorage + const toKeep = {}; + for (let i = 0; i < localStorage.length; i++) { + const k = localStorage.key(i); + if (_isLocalOnly(k)) toKeep[k] = localStorage.getItem(k); + } + localStorage.clear(); + Object.entries(toKeep).forEach(([k, v]) => localStorage.setItem(k, v)); + }, + + // Return all synced settings as a plain object (used for backup). + async getAll() { + const storageArea = _getStorageArea(); + let items; + if (storageArea) { + items = await storageArea.get(null); + } else { + items = { ..._cache }; + } + const result = { ...items }; + delete result[MIGRATION_KEY]; + return result; + }, + + // Write a batch of settings and mark migration as done (used for restore). + async setAll(obj) { + const dataWithMarker = { ...obj, [MIGRATION_KEY]: true }; + Object.entries(dataWithMarker).forEach(([key, val]) => { _cache[key] = val; }); + const storageArea = _getStorageArea(); + if (storageArea) return storageArea.set(dataWithMarker); + Object.entries(dataWithMarker).forEach(([key, val]) => localStorage.setItem(key, val)); + } + }; +})(); diff --git a/scripts/theme.js b/scripts/theme.js index 365ab938..0e187e78 100644 --- a/scripts/theme.js +++ b/scripts/theme.js @@ -7,8 +7,6 @@ const themeStorageKey = "selectedTheme"; const customThemeStorageKey = "customThemeColor"; const darkModeCheckboxKey = "enableDarkModeCheckboxState"; -const storedTheme = localStorage.getItem(themeStorageKey); -const storedCustomColor = localStorage.getItem(customThemeStorageKey); const radioButtons = document.querySelectorAll(".colorPlate"); const colorPicker = document.getElementById("colorPicker"); const colorPickerLabel = document.getElementById("rangColor"); @@ -24,7 +22,9 @@ const syncThemeChange = (MediaQuery) => { syncThemeChange(systemTheme); // ===== Segmented Control for Dark Mode ===== -(function () { +(async function () { + await window.storageReady; + const preferredThemeKey = "preferredTheme"; const segment = document.getElementById("themeSegment"); const indicator = segment.querySelector(".themeIndicator"); @@ -40,7 +40,7 @@ syncThemeChange(systemTheme); // Apply theme mode (light/dark/system) function applyThemeMode(theme) { - localStorage.setItem(preferredThemeKey, theme); + Storage.setItem(preferredThemeKey, theme); segment.dataset.active = theme; moveIndicator(theme); @@ -61,16 +61,16 @@ syncThemeChange(systemTheme); systemTheme.addEventListener('change', syncThemeChange); function initializeThemeMode() { - const darkModeCheckboxState = localStorage.getItem(darkModeCheckboxKey); - const savedPreferredTheme = localStorage.getItem(preferredThemeKey); + const darkModeCheckboxState = Storage.getItem(darkModeCheckboxKey); + const savedPreferredTheme = Storage.getItem(preferredThemeKey); let initialTheme; if (darkModeCheckboxState === "checked") { // Migrate old checkbox users to "dark" mode initialTheme = "dark"; - localStorage.removeItem(darkModeCheckboxKey); - localStorage.setItem(preferredThemeKey, "dark"); + Storage.removeItem(darkModeCheckboxKey); + Storage.setItem(preferredThemeKey, "dark"); } else if (savedPreferredTheme) { initialTheme = savedPreferredTheme; // Use saved preference } else { @@ -84,9 +84,11 @@ syncThemeChange(systemTheme); initializeThemeMode(); })(); -document.addEventListener("DOMContentLoaded", () => { +document.addEventListener("DOMContentLoaded", async () => { + await window.storageReady; + // Check for custom color - const storedCustomColor = localStorage.getItem(customThemeStorageKey); + const storedCustomColor = Storage.getItem(customThemeStorageKey); if (storedCustomColor) { applyCustomTheme(storedCustomColor); // Uncheck all radio buttons @@ -96,7 +98,7 @@ document.addEventListener("DOMContentLoaded", () => { } // Check for regular theme else { - const storedTheme = localStorage.getItem(themeStorageKey); + const storedTheme = Storage.getItem(themeStorageKey); if (storedTheme) { applySelectedTheme(storedTheme); const selectedRadioButton = document.querySelector(`.colorPlate[value="${storedTheme}"]`); @@ -115,10 +117,10 @@ document.addEventListener("DOMContentLoaded", () => { }, 25); }); -// Function to load background color +// Function to load background color function ApplyLoadingColor() { let LoadingScreenColor = getComputedStyle(document.body).getPropertyValue("background-color"); - localStorage.setItem("LoadingScreenColor", LoadingScreenColor); + Storage.setItem("LoadingScreenColor", LoadingScreenColor); } const resetDarkTheme = () => { @@ -258,8 +260,8 @@ const applyCustomTheme = (color) => { const handleThemeChange = function () { if (this.checked) { const colorValue = this.value; - localStorage.setItem(themeStorageKey, colorValue); - localStorage.removeItem(customThemeStorageKey); // Clear custom theme + Storage.setItem(themeStorageKey, colorValue); + Storage.removeItem(customThemeStorageKey); // Clear custom theme applySelectedTheme(colorValue); } }; @@ -274,8 +276,8 @@ radioButtons.forEach(radioButton => { const handleColorPickerChange = function (event) { const selectedColor = event.target.value; resetDarkTheme(); // Clear dark theme if active - localStorage.setItem(customThemeStorageKey, selectedColor); // Save custom color - localStorage.removeItem(themeStorageKey); // Clear predefined theme + Storage.setItem(customThemeStorageKey, selectedColor); // Save custom color + Storage.removeItem(themeStorageKey); // Clear predefined theme applyCustomTheme(selectedColor); // Uncheck all radio buttons @@ -306,4 +308,4 @@ const throttle = (func, limit) => { // Add listeners for color picker colorPicker.removeEventListener("input", handleColorPickerChange); // Ensure no duplicate listeners -colorPicker.addEventListener("input", throttle(handleColorPickerChange, 10)); \ No newline at end of file +colorPicker.addEventListener("input", throttle(handleColorPickerChange, 10)); diff --git a/scripts/todo-list.js b/scripts/todo-list.js index 8e22aabd..02315397 100644 --- a/scripts/todo-list.js +++ b/scripts/todo-list.js @@ -181,13 +181,13 @@ todoulList.addEventListener("click", (event) => { // Save JSON to local Storage function SaveToDoData() { - localStorage.setItem("todoList", JSON.stringify(todoList)); + Storage.setItem("todoList", JSON.stringify(todoList)); } // Fetch saved JSON and create list items using it function ShowToDoList() { try { - todoList = JSON.parse(localStorage.getItem("todoList")) || {}; // Parse stored data or initialize empty + todoList = JSON.parse(Storage.getItem("todoList")) || {}; // Parse stored data or initialize empty const fragment = document.createDocumentFragment(); // Create a DocumentFragment for (let id in todoList) { @@ -198,21 +198,21 @@ function ShowToDoList() { todoulList.appendChild(fragment); // Append all `li` to the `ul` at once } catch (error) { - console.error("Error loading from localStorage:", error); - localStorage.setItem("todoList", "{}"); // Reset corrupted data + console.error("Error loading from Storage:", error); + Storage.setItem("todoList", "{}"); // Reset corrupted data } } // Code to reset the List on the Next Day -let todoLastUpdateDate = localStorage.getItem("todoLastUpdateDate"); // Get the date of last update +let todoLastUpdateDate = Storage.getItem("todoLastUpdateDate"); // Get the date of last update let todoCurrentDate = new Date().toLocaleDateString(); // Get current date if (todoLastUpdateDate === todoCurrentDate) { ShowToDoList(); } else { // Modify the list when last update date and the current date does not match - localStorage.setItem("todoLastUpdateDate", todoCurrentDate); - todoList = JSON.parse(localStorage.getItem("todoList")) || {}; + Storage.setItem("todoLastUpdateDate", todoCurrentDate); + todoList = JSON.parse(Storage.getItem("todoList")) || {}; for (let id in todoList) { if (todoList[id].pinned === false) { diff --git a/scripts/voice-search.js b/scripts/voice-search.js index 3e4f09ff..91d53355 100644 --- a/scripts/voice-search.js +++ b/scripts/voice-search.js @@ -15,8 +15,8 @@ function isSupportedBrowser() { const micIcon = document.getElementById("micIcon"); const micIconCheckbox = document.getElementById("micIconCheckbox"); -// Check if there's a saved state in localStorage -const savedState = localStorage.getItem("micIconVisible"); +// Check if there's a saved state in Storage +const savedState = Storage.getItem("micIconVisible"); let isMicIconVisible; // If saved state exists, use it; otherwise, fallback to initial state based on browser support @@ -26,7 +26,7 @@ if (savedState !== null) { // Default state: Hide mic icon if browser is not supported isMicIconVisible = isSupportedBrowser(); // Save the initial state based on the user agent - localStorage.setItem("micIconVisible", isMicIconVisible); + Storage.setItem("micIconVisible", isMicIconVisible); } // Set the checkbox state based on the saved or default state @@ -40,7 +40,7 @@ if (isMicIconVisible) { // Function to toggle mic icon visibility function toggleMicIconVisibility(isVisible) { micIcon.style.display = isVisible ? "block" : "none"; - localStorage.setItem("micIconVisible", isVisible); // Save to localStorage + Storage.setItem("micIconVisible", isVisible); // Save to Storage } // Toggle mic icon display based on checkbox state diff --git a/scripts/weather.js b/scripts/weather.js index b2992f5a..78acbb12 100644 --- a/scripts/weather.js +++ b/scripts/weather.js @@ -13,7 +13,7 @@ document.addEventListener("DOMContentLoaded", function () { const weatherOptions = document.querySelector(".weatherOptions"); // Retrieve saved state - const isHidden = localStorage.getItem("hideWeatherVisible") === "true"; + const isHidden = Storage.getItem("hideWeatherVisible") === "true"; hideWeatherCheckbox.checked = isHidden; function applyWeatherState(hidden) { @@ -38,7 +38,7 @@ document.addEventListener("DOMContentLoaded", function () { hideWeatherCheckbox.addEventListener("change", () => { const hidden = hideWeatherCheckbox.checked; - localStorage.setItem("hideWeatherVisible", hidden); + Storage.setItem("hideWeatherVisible", hidden); applyWeatherState(hidden); @@ -63,16 +63,16 @@ async function getWeatherData() { const locationCont = document.getElementById("locationCont"); const locationSuggestions = document.getElementById("locationSuggestions"); - // Load saved data from localStorage - const savedApiKey = localStorage.getItem("weatherApiKey"); - const savedLocation = localStorage.getItem("weatherLocation"); + // Load saved data from Storage + const savedApiKey = Storage.getItem("weatherApiKey"); + const savedLocation = Storage.getItem("weatherLocation"); // Pre-fill input fields with saved data if (savedLocation) userLocInput.value = savedLocation; if (savedApiKey) userAPIInput.value = savedApiKey; const minMaxTempCheckbox = document.getElementById("minMaxTempCheckbox"); - const isMinMaxEnabled = localStorage.getItem("minMaxTempEnabled") === "true"; + const isMinMaxEnabled = Storage.getItem("minMaxTempEnabled") === "true"; minMaxTempCheckbox.checked = isMinMaxEnabled; document.getElementById("feelsLike").textContent = isMinMaxEnabled @@ -90,10 +90,10 @@ async function getWeatherData() { userAPIInput.addEventListener("keydown", (event) => handleEnterPress(event, "saveAPI")); userLocInput.addEventListener("keydown", (event) => handleEnterPress(event, "saveLoc")); - // Save API key to localStorage + // Save API key to Storage saveAPIButton.addEventListener("click", () => { const apiKey = userAPIInput.value.trim(); - localStorage.setItem("weatherApiKey", apiKey); + Storage.setItem("weatherApiKey", apiKey); userAPIInput.value = ""; location.reload(); }); @@ -108,10 +108,10 @@ async function getWeatherData() { gpsToggle.checked = false; // Revert toggle if user cancels return; } - localStorage.setItem("useGPS", true); + Storage.setItem("useGPS", true); locationCont.classList.add("inactive"); } else { - localStorage.setItem("useGPS", false); + Storage.setItem("useGPS", false); locationCont.classList.remove("inactive"); } location.reload(); @@ -120,8 +120,8 @@ async function getWeatherData() { // Handle manual location input saveLocButton.addEventListener("click", () => { const userLocation = userLocInput.value.trim(); - localStorage.setItem("weatherLocation", userLocation); - localStorage.setItem("useGPS", false); + Storage.setItem("weatherLocation", userLocation); + Storage.setItem("useGPS", false); userLocInput.value = ""; fetchWeather(); location.reload(); @@ -248,7 +248,7 @@ async function getWeatherData() { userLocInput.value = locationText; locationSuggestions.style.display = "none"; - localStorage.setItem("weatherLocation", JSON.stringify(selectedLocation)); + Storage.setItem("weatherLocation", JSON.stringify(selectedLocation)); saveLocButton.click(); suggestions = []; toggleAutocomplete(); @@ -300,8 +300,8 @@ async function getWeatherData() { // Determine the location to use let currentUserLocation = savedLocation; - // Load the saved GPS state from localStorage - const useGPS = JSON.parse(localStorage.getItem("useGPS")) || false; + // Load the saved GPS state from Storage + const useGPS = JSON.parse(Storage.getItem("useGPS")) || false; gpsToggle.checked = useGPS; if (useGPS) locationCont.classList.add("inactive"); @@ -356,10 +356,10 @@ async function getWeatherData() { // Fetch weather data based on a location async function fetchWeather() { try { - let parsedData = JSON.parse(localStorage.getItem("weatherParsedData")); - const weatherParsedTime = parseInt(localStorage.getItem("weatherParsedTime")); - const weatherParsedLocation = localStorage.getItem("weatherParsedLocation"); - const weatherParsedLang = localStorage.getItem("weatherParsedLang"); + let parsedData = JSON.parse(Storage.getItem("weatherParsedData")); + const weatherParsedTime = parseInt(Storage.getItem("weatherParsedTime")); + const weatherParsedLocation = Storage.getItem("weatherParsedLocation"); + const weatherParsedLang = Storage.getItem("weatherParsedLang"); const retentionTime = savedApiKey ? 435000 : 960000; // 7.25 min for user-entered API key, 16 min otherwise @@ -407,11 +407,11 @@ async function getWeatherData() { } }; - // Save filtered weather data to localStorage - localStorage.setItem("weatherParsedData", JSON.stringify(filteredData)); - localStorage.setItem("weatherParsedTime", Date.now()); // Save time of last fetching - localStorage.setItem("weatherParsedLocation", currentUserLocation); // Save user location - localStorage.setItem("weatherParsedLang", currentLanguage); // Save language preference + // Save filtered weather data to Storage + Storage.setItem("weatherParsedData", JSON.stringify(filteredData)); + Storage.setItem("weatherParsedTime", Date.now()); // Save time of last fetching + Storage.setItem("weatherParsedLocation", currentUserLocation); // Save user location + Storage.setItem("weatherParsedLang", currentLanguage); // Save language preference } } @@ -538,7 +538,7 @@ async function getWeatherData() { // Update location let city = parsedData.location.name; let maxLength = 10; - let isLocationHidden = localStorage.getItem("locationHidden") === "true"; + let isLocationHidden = Storage.getItem("locationHidden") === "true"; const locationTile = document.querySelector(".tiles.location"); const locationIcon = locationTile.querySelector(".location-icon"); @@ -580,7 +580,7 @@ async function getWeatherData() { locationIcon.addEventListener("click", (e) => { e.stopPropagation(); isLocationHidden = !isLocationHidden; - localStorage.setItem("locationHidden", isLocationHidden); + Storage.setItem("locationHidden", isLocationHidden); updateLocationText(); // Update icon immediately @@ -613,6 +613,6 @@ loadCheckboxState("fahrenheitCheckboxState", fahrenheitCheckbox); // Handle min-max temp checkbox state change minMaxTempCheckbox.addEventListener("change", () => { const isChecked = minMaxTempCheckbox.checked; - localStorage.setItem("minMaxTempEnabled", isChecked); + Storage.setItem("minMaxTempEnabled", isChecked); location.reload(); }); diff --git a/scripts/widgets-transparency.js b/scripts/widgets-transparency.js index b7cc8ff1..1f3b33fb 100644 --- a/scripts/widgets-transparency.js +++ b/scripts/widgets-transparency.js @@ -18,7 +18,7 @@ function setSliderPosition(percentage) { slider.style.width = `${posPercent}%`; opacityLevel.textContent = `${localizeNumbers(Math.round(posPercent).toString(), currentLanguage)}%`; document.documentElement.style.setProperty("--transparency", `${Math.round(posPercent)}%`); - localStorage.setItem("bgOpacity", posPercent); + Storage.setItem("bgOpacity", posPercent); } // Handle drag or click interaction on opacity bar @@ -55,5 +55,5 @@ function startDrag() { }); // Initialize with saved opacity value or default to 90% -const savedOpacity = localStorage.getItem("bgOpacity") || 90; +const savedOpacity = Storage.getItem("bgOpacity") || 90; setSliderPosition(Number(savedOpacity));