summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSameera Sandakelum <[email protected]>2025-05-06 17:51:53 +0530
committerGitHub <[email protected]>2025-05-06 17:51:53 +0530
commitdb4a2034c470dec5793f3050ce460c355f4d39d8 (patch)
tree283d5f51bafb822ebc5a3e370eead7c26b75d488
parente7f8b06ae2ed012a27e8d6d3af46e964b4275ede (diff)
parent3175a9659126b1d069739254a09a9e00cd03051f (diff)
Merge pull request #15 from sameerasw/backup
Backup and restore feature #13
-rw-r--r--data-viewer/data-viewer.css58
-rw-r--r--data-viewer/data-viewer.html24
-rw-r--r--data-viewer/data-viewer.js154
3 files changed, 232 insertions, 4 deletions
diff --git a/data-viewer/data-viewer.css b/data-viewer/data-viewer.css
index 508d34f..84f0131 100644
--- a/data-viewer/data-viewer.css
+++ b/data-viewer/data-viewer.css
@@ -502,10 +502,60 @@ body {
margin-bottom: 20px;
}
-/* Responsive handling for mobile */
+/* Backup and Restore Styling */
+.backup-restore-container {
+ background-color: var(--bg-color);
+ border-radius: var(--radius-sm);
+ padding: 16px;
+ border-left: 3px solid var(--accent-color);
+}
+
+.backup-description {
+ margin-top: 0;
+ margin-bottom: 16px;
+ color: var(--text-secondary);
+ font-size: 14px;
+ line-height: 1.5;
+}
+
+.backup-actions {
+ display: flex;
+ gap: 16px;
+ margin-bottom: 16px;
+}
+
+.import-container, #export-settings {
+ flex: 1;
+}
+
+.import-label {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ cursor: pointer;
+}
+
+.import-status {
+ font-size: 14px;
+ margin-top: 8px;
+ padding: 8px 0;
+ border-radius: var(--radius-sm);
+}
+
+.status-success {
+ color: var(--success-color);
+ font-weight: 500;
+}
+
+.status-error {
+ color: var(--danger-color);
+ font-weight: 500;
+}
+
+/* Add responsive adjustments for mobile */
@media (max-width: 768px) {
- .tables-container {
- grid-template-columns: 1fr;
- gap: 16px;
+ .backup-actions {
+ flex-direction: column;
+ gap: 8px;
}
}
diff --git a/data-viewer/data-viewer.html b/data-viewer/data-viewer.html
index 716942d..5336bfe 100644
--- a/data-viewer/data-viewer.html
+++ b/data-viewer/data-viewer.html
@@ -46,6 +46,30 @@
</div>
</div>
+ <!-- New section for backup/restore -->
+ <div class="data-section">
+ <h2 class="section-title">Backup & Restore</h2>
+ <div class="backup-restore-container">
+ <p class="backup-description">
+ Export your settings to a JSON file or import settings from a previously exported file.
+ <br><strong>Note:</strong> This includes global settings, website lists, and website-specific
+ feature toggles, but not the actual style data which will be re-fetched.
+ </p>
+ <div class="backup-actions">
+ <button id="export-settings" class="action-button primary">
+ <i class="fas fa-upload"></i> Export Settings
+ </button>
+ <div class="import-container">
+ <label for="import-file" class="action-button secondary import-label">
+ <i class="fas fa-download"></i> Import Settings
+ </label>
+ <input type="file" id="import-file" accept=".json" hidden>
+ </div>
+ </div>
+ <div id="import-status" class="import-status"></div>
+ </div>
+ </div>
+
<div class="data-section">
<h2 class="section-title">Global Settings</h2>
<div id="global-settings-data" class="data-container"></div>
diff --git a/data-viewer/data-viewer.js b/data-viewer/data-viewer.js
index c006f09..55ec808 100644
--- a/data-viewer/data-viewer.js
+++ b/data-viewer/data-viewer.js
@@ -15,6 +15,11 @@ document.addEventListener("DOMContentLoaded", function () {
"disable-transparency"
);
+ // Backup & Restore Elements
+ const exportButton = document.getElementById("export-settings");
+ const importFileInput = document.getElementById("import-file");
+ const importStatusElement = document.getElementById("import-status");
+
// Load and display the data
loadAllData();
@@ -42,6 +47,10 @@ document.addEventListener("DOMContentLoaded", function () {
}
});
+ // New event listeners for export and import functionality
+ exportButton.addEventListener("click", exportSettings);
+ importFileInput.addEventListener("change", importSettings);
+
async function deleteAllData() {
try {
// Clear all storage data
@@ -83,6 +92,151 @@ document.addEventListener("DOMContentLoaded", function () {
}
}
+ // Export settings functionality
+ async function exportSettings() {
+ try {
+ // Retrieve all storage data to find site-specific settings
+ const allData = await browser.storage.local.get(null);
+
+ // Extract only the settings we want to backup
+ const settingsToBackup = {
+ [BROWSER_STORAGE_KEY]: allData[BROWSER_STORAGE_KEY] || {},
+ [SKIP_FORCE_THEMING_KEY]: allData[SKIP_FORCE_THEMING_KEY] || [],
+ [SKIP_THEMING_KEY]: allData[SKIP_THEMING_KEY] || [],
+ };
+
+ // Also extract site-specific settings (keys that start with BROWSER_STORAGE_KEY.)
+ const siteSpecificSettings = {};
+ for (const [key, value] of Object.entries(allData)) {
+ if (key.startsWith(BROWSER_STORAGE_KEY + ".")) {
+ siteSpecificSettings[key] = value;
+ }
+ }
+
+ // Add export timestamp and version
+ const manifest = browser.runtime.getManifest();
+ const exportData = {
+ exportDate: new Date().toISOString(),
+ addonVersion: manifest.version,
+ settings: settingsToBackup,
+ siteSettings: siteSpecificSettings,
+ };
+
+ // Convert to JSON
+ const jsonData = JSON.stringify(exportData, null, 2);
+
+ // Create a blob and download link
+ const blob = new Blob([jsonData], { type: "application/json" });
+ const url = URL.createObjectURL(blob);
+
+ // Create a temporary anchor and trigger download
+ const a = document.createElement("a");
+ a.href = url;
+ a.download = `zen-internet-settings-${
+ new Date().toISOString().split("T")[0]
+ }.json`;
+ document.body.appendChild(a);
+ a.click();
+
+ // Cleanup
+ setTimeout(() => {
+ document.body.removeChild(a);
+ URL.revokeObjectURL(url);
+ }, 0);
+
+ // Show success message
+ showImportStatus("Settings exported successfully!", "success");
+ } catch (error) {
+ console.error("Error exporting settings:", error);
+ showImportStatus(`Export failed: ${error.message}`, "error");
+ }
+ }
+
+ // Import settings functionality
+ async function importSettings(event) {
+ try {
+ const file = event.target.files[0];
+ if (!file) return;
+
+ const reader = new FileReader();
+
+ reader.onload = async (e) => {
+ try {
+ const importData = JSON.parse(e.target.result);
+
+ // Validate the imported data structure
+ if (
+ !importData.settings ||
+ !importData.settings[BROWSER_STORAGE_KEY]
+ ) {
+ throw new Error("Invalid settings file format");
+ }
+
+ // Confirm the import
+ if (
+ confirm(
+ `Are you sure you want to import settings from ${importData.exportDate}? This will overwrite your current settings.`
+ )
+ ) {
+ // First store the global settings and lists
+ const importOperations = {
+ [BROWSER_STORAGE_KEY]: importData.settings[BROWSER_STORAGE_KEY],
+ [SKIP_FORCE_THEMING_KEY]:
+ importData.settings[SKIP_FORCE_THEMING_KEY] || [],
+ [SKIP_THEMING_KEY]: importData.settings[SKIP_THEMING_KEY] || [],
+ };
+
+ // Then add any site-specific settings if they exist
+ if (importData.siteSettings) {
+ for (const [key, value] of Object.entries(
+ importData.siteSettings
+ )) {
+ importOperations[key] = value;
+ }
+ }
+
+ // Apply all settings at once
+ await browser.storage.local.set(importOperations);
+
+ showImportStatus(
+ "Settings imported successfully! Reloading...",
+ "success"
+ );
+
+ // Reload the page after a short delay
+ setTimeout(() => {
+ window.location.reload();
+ }, 1500);
+ } else {
+ // User cancelled
+ importFileInput.value = "";
+ showImportStatus("Import cancelled", "error");
+ }
+ } catch (parseError) {
+ console.error("Error parsing import file:", parseError);
+ showImportStatus(`Import failed: ${parseError.message}`, "error");
+ }
+ };
+
+ reader.readAsText(file);
+ } catch (error) {
+ console.error("Error handling import:", error);
+ showImportStatus(`Import failed: ${error.message}`, "error");
+ }
+ }
+
+ // Helper function to show import status messages
+ function showImportStatus(message, type) {
+ importStatusElement.textContent = message;
+ importStatusElement.className = `import-status status-${type}`;
+
+ // Clear the message after 5 seconds
+ setTimeout(() => {
+ importStatusElement.textContent = "";
+ importStatusElement.className = "import-status";
+ }, 5000);
+ }
+
async function displayAddonVersion() {
const manifest = browser.runtime.getManifest();
versionElement.textContent = `Version: ${manifest.version}`;