From b80019598ec0c66d57870f5494d059183a77f4f2 Mon Sep 17 00:00:00 2001 From: utopiaeh Date: Thu, 20 Aug 2026 19:27:42 +0300 Subject: [PATCH] feat: Added a backup export/import, protected lists and updated the api calling --- components/options/BackupSection.tsx | 64 ++++++++++++ components/options/ClearLogSection.tsx | 48 +++++++++ components/options/GlobalSection.tsx | 3 + components/options/PerSiteSection.tsx | 7 ++ components/options/ProtectedSitesSection.tsx | 24 +++++ components/options/VisitedSitesSection.tsx | 12 +++ entrypoints/background.ts | 56 ++++++++++ entrypoints/options/App.tsx | 97 ++++++++++++----- entrypoints/popup/App.tsx | 40 ++++--- store/clearLog.test.ts | 44 ++++++++ store/clearLog.ts | 44 ++++++++ store/settings.test.ts | 16 +++ store/settings.ts | 10 +- utils/browser-info.test.ts | 38 +++++++ utils/browser-info.ts | 14 +++ utils/clearing.firefox.test.ts | 48 +++++---- utils/clearing.test.ts | 48 +++++++++ utils/clearing.ts | 72 ++++++++++--- utils/i18n.ts | 103 ++++++++++++++++++- utils/settings-transfer.test.ts | 53 ++++++++++ utils/settings-transfer.ts | 40 +++++++ utils/storage-adapter.test.ts | 82 +++++++++++++++ utils/storage-adapter.ts | 85 +++++++++++++++ 23 files changed, 971 insertions(+), 77 deletions(-) create mode 100644 components/options/BackupSection.tsx create mode 100644 components/options/ClearLogSection.tsx create mode 100644 components/options/ProtectedSitesSection.tsx create mode 100644 entrypoints/background.ts create mode 100644 store/clearLog.test.ts create mode 100644 store/clearLog.ts create mode 100644 utils/browser-info.test.ts create mode 100644 utils/settings-transfer.test.ts create mode 100644 utils/settings-transfer.ts create mode 100644 utils/storage-adapter.test.ts diff --git a/components/options/BackupSection.tsx b/components/options/BackupSection.tsx new file mode 100644 index 0000000..91ace85 --- /dev/null +++ b/components/options/BackupSection.tsx @@ -0,0 +1,64 @@ +import { useRef, useState } from 'react'; +import { useTranslation } from '../../hooks/useTranslation'; +import { useSettingsStore } from '../../store/settings'; +import { exportSettingsJson, parseImportedSettings } from '../../utils/settings-transfer'; + +export function BackupSection() { + const t = useTranslation(); + const settings = useSettingsStore(); + const [importError, setImportError] = useState(false); + const fileInputRef = useRef(null); + + function handleExport() { + const json = exportSettingsJson(settings); + const url = URL.createObjectURL(new Blob([json], { type: 'application/json' })); + const a = document.createElement('a'); + a.href = url; + a.download = 'bleep-settings.json'; + a.click(); + URL.revokeObjectURL(url); + } + + async function handleImportFile(e: React.ChangeEvent) { + const file = e.target.files?.[0]; + e.target.value = ''; + if (!file) return; + try { + const data = parseImportedSettings(await file.text()); + settings.importSettings(data); + setImportError(false); + } catch (err) { + console.error('Bleep: settings import failed', err); + setImportError(true); + } + } + + return ( +
+ +

{t('backupHint')}

+
+ + + +
+ {importError &&

{t('importInvalidFile')}

} +
+ ); +} diff --git a/components/options/ClearLogSection.tsx b/components/options/ClearLogSection.tsx new file mode 100644 index 0000000..048734f --- /dev/null +++ b/components/options/ClearLogSection.tsx @@ -0,0 +1,48 @@ +import { useTranslation } from '../../hooks/useTranslation'; +import { useClearLogStore } from '../../store/clearLog'; +import type { TranslationKey } from '../../utils/i18n'; + +export function ClearLogSection() { + const t = useTranslation(); + const entries = useClearLogStore((s) => s.entries); + const clearLog = useClearLogStore((s) => s.clearLog); + + return ( +
+
+

+ {t('clearLogTitle')} +

+ {entries.length > 0 && ( + + )} +
+ {entries.length === 0 ? ( +

{t('clearLogEmpty')}

+ ) : ( +
    + {entries.map((entry) => ( +
  • +
    + {entry.hostname} + + {new Date(entry.timestamp).toLocaleString()} + +
    +

    + {entry.types.map((id) => t(`dt_${id}` as TranslationKey)).join(', ')} + {entry.linkedTargets.length > 0 && + ` + ${t('clearLogLinkedCount', { count: entry.linkedTargets.length })}`} +

    +
  • + ))} +
+ )} +
+ ); +} diff --git a/components/options/GlobalSection.tsx b/components/options/GlobalSection.tsx index 47cdb82..9420bda 100644 --- a/components/options/GlobalSection.tsx +++ b/components/options/GlobalSection.tsx @@ -18,6 +18,7 @@ interface GlobalSectionProps { historyStatus: ClearStatus; onClearAllHistory: () => void; onDeleteHistoryItem: (url: string) => void; + hasProtectedSites: boolean; } export function GlobalSection({ @@ -32,6 +33,7 @@ export function GlobalSection({ historyStatus, onClearAllHistory, onDeleteHistoryItem, + hasProtectedSites, }: GlobalSectionProps) { const t = useTranslation(); @@ -51,6 +53,7 @@ export function GlobalSection({ onConfirm={onClear} /> + {hasProtectedSites &&

{t('protectedSitesGlobalNote')}

} {showHistory && ( void; linkedOrigins: string; onLinkedOriginsChange: (value: string) => void; + protectedSites: string; + onProtectedSitesChange: (value: string) => void; siteFilter: string; onSiteFilterChange: (value: string) => void; bulkStatus: ClearStatus; @@ -48,6 +51,8 @@ export function PerSiteSection({ onUseOriginMappingsChange, linkedOrigins, onLinkedOriginsChange, + protectedSites, + onProtectedSitesChange, siteFilter, onSiteFilterChange, bulkStatus, @@ -104,6 +109,8 @@ export function PerSiteSection({ + +
{!isGeckoBased() && ( diff --git a/components/options/ProtectedSitesSection.tsx b/components/options/ProtectedSitesSection.tsx new file mode 100644 index 0000000..ea50eb7 --- /dev/null +++ b/components/options/ProtectedSitesSection.tsx @@ -0,0 +1,24 @@ +import { useTranslation } from '../../hooks/useTranslation'; + +interface ProtectedSitesSectionProps { + value: string; + onChange: (value: string) => void; +} + +export function ProtectedSitesSection({ value, onChange }: ProtectedSitesSectionProps) { + const t = useTranslation(); + + return ( +
+ +