diff options
| author | Sameera Sandakelum <[email protected]> | 2025-05-06 17:51:53 +0530 | 
|---|---|---|
| committer | GitHub <[email protected]> | 2025-05-06 17:51:53 +0530 | 
| commit | db4a2034c470dec5793f3050ce460c355f4d39d8 (patch) | |
| tree | 283d5f51bafb822ebc5a3e370eead7c26b75d488 | |
| parent | e7f8b06ae2ed012a27e8d6d3af46e964b4275ede (diff) | |
| parent | 3175a9659126b1d069739254a09a9e00cd03051f (diff) | |
Merge pull request #15 from sameerasw/backup
Backup and restore feature #13
| -rw-r--r-- | data-viewer/data-viewer.css | 58 | ||||
| -rw-r--r-- | data-viewer/data-viewer.html | 24 | ||||
| -rw-r--r-- | data-viewer/data-viewer.js | 154 | 
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}`;  | 
