Release v0.1.2: site-backup persistence + PNG toolbar icons

- Mirror dismissals to window.localStorage on autotrader.co.uk ("atm:..."
  key). If browser.storage.local comes up empty after a quirky reinstall,
  the content script rehydrates from the site backup on the next page load.
- Replace the SVG toolbar icon with PNG renders (16/32/48/96/128) — some
  Firefox installs don't render extension SVG icons reliably in the
  toolbar / extensions flyout.
- Sharp added as a devDependency to rebuild icons from icon.svg.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-22 13:31:46 +00:00
parent 066ec44584
commit ed92298b79
11 changed files with 644 additions and 11 deletions
+49 -3
View File
@@ -20,22 +20,67 @@ const state = {
};
// --- storage ---
//
// Primary: browser.storage.local (per-extension, syncs across tabs via
// storage.onChanged). Secondary: window.localStorage scoped to
// autotrader.co.uk — survives extension reinstall, because it's the site's
// storage, not the extension's. On load, if extension storage is empty but
// the site backup has entries, we re-hydrate. On every write we mirror to the
// site backup so the two stay in lockstep.
const BACKUP_KEY = "atm:dismissedListings:v1";
function readSiteBackup() {
try {
const raw = window.localStorage.getItem(BACKUP_KEY);
return raw ? JSON.parse(raw) : null;
} catch {
return null;
}
}
function writeSiteBackup(dismissed) {
try {
window.localStorage.setItem(BACKUP_KEY, JSON.stringify(dismissed));
} catch {
// localStorage full/disabled — nothing to do
}
}
async function loadState() {
const res = await api.storage.local.get([STORAGE_KEY, SETTINGS_KEY]);
state.dismissed = res[STORAGE_KEY] || {};
let dismissed = res[STORAGE_KEY] || {};
// Re-hydrate from site backup if extension storage lost its entries
// (e.g. after an unlisted reinstall that didn't upgrade in place).
const backup = readSiteBackup();
if (backup && Object.keys(dismissed).length === 0 && Object.keys(backup).length > 0) {
dismissed = backup;
await api.storage.local.set({ [STORAGE_KEY]: dismissed });
console.log("[ATM] restored", Object.keys(dismissed).length, "dismissals from site backup");
} else if (Object.keys(dismissed).length > 0) {
// Keep site backup fresh even if we didn't need it this load
writeSiteBackup(dismissed);
}
state.dismissed = dismissed;
state.hideDismissed = !!(res[SETTINGS_KEY] && res[SETTINGS_KEY].hideDismissed);
applyHideMode();
}
async function persistDismissed() {
await api.storage.local.set({ [STORAGE_KEY]: state.dismissed });
writeSiteBackup(state.dismissed);
}
async function setDismissed(id, title) {
state.dismissed[id] = { ts: Date.now(), title: title || "" };
await api.storage.local.set({ [STORAGE_KEY]: state.dismissed });
await persistDismissed();
}
async function clearDismissed(id) {
delete state.dismissed[id];
await api.storage.local.set({ [STORAGE_KEY]: state.dismissed });
await persistDismissed();
}
function applyHideMode() {
@@ -263,6 +308,7 @@ api.storage.onChanged.addListener((changes, area) => {
if (area !== "local") return;
if (changes[STORAGE_KEY]) {
state.dismissed = changes[STORAGE_KEY].newValue || {};
writeSiteBackup(state.dismissed); // keep site backup in sync with popup-driven changes
document.querySelectorAll(".atm-card[data-atm-id]").forEach((card) => {
refreshCardState(card, card.dataset.atmId);
});