mirror of
https://github.com/PR0M3TH3AN/bitvid.git
synced 2026-10-27 07:52:24 +00:00
Wallet restore popup: re-offer on login when missing locally (like storage)
The wallet already rode the same login "restore from Nostr" offer as storage, but a once-per-device "offered" flag suppressed it — so on a device without your wallet you fell back to the manual Sync button. Now: - encryptedSyncItem: add hasLocal(pubkey) (non-null payload => item present locally, e.g. a connected wallet / configured storage). - settingsRestorePrompt: re-offer an item whenever it's MISSING locally and a Nostr backup exists — bypassing the "offered once" flag — so it surfaces exactly when you'd want it. Only respect the once-per-device flag when the item IS present locally (no nagging). A new per-device "dismissed" flag, set when you decline, keeps it quiet after an explicit "no". - Accepting enables sync, so it auto-pulls silently thereafter (the "both" behavior: silent auto-pull when enabled + popup when missing & off). - Unknown services (no hasLocal) keep the legacy behavior. - tests: re-offer when missing despite prior offer; no re-offer when present; decline stays quiet. Co-Authored-By: Claude Opus 4.8 <[email protected]>
This commit is contained in:
1 parent
c562d9ee65
commit
4f56a2bd26
3 files changed
+134
-5
No files matched your search
@@ -15,8 +15,13 @@ import { isSyncEnabled } from "../js/services/settingsSyncFlags.js";
|
||||
|
||||
const PUBKEY = "c".repeat(64);
|
||||
|
||||
function makeSyncStub({ available = true, enabled = false, remote = false } = {}) {
|
||||
return {
|
||||
function makeSyncStub({
|
||||
available = true,
|
||||
enabled = false,
|
||||
remote = false,
|
||||
local = undefined,
|
||||
} = {}) {
|
||||
const stub = {
|
||||
pulls: 0,
|
||||
isAvailable: () => available,
|
||||
isEnabled: () => enabled,
|
||||
@@ -26,8 +31,16 @@ function makeSyncStub({ available = true, enabled = false, remote = false } = {}
|
||||
return { found: true, imported: true };
|
||||
},
|
||||
};
|
||||
// Only real services expose hasLocal; omitting it (local === undefined) keeps
|
||||
// the legacy "assume present" behavior the older tests rely on.
|
||||
if (local !== undefined) {
|
||||
stub.hasLocal = async () => local;
|
||||
}
|
||||
return stub;
|
||||
}
|
||||
|
||||
const OFFERED_KEY = "bitvid:settings-sync:offered:v1";
|
||||
|
||||
test("offers and restores items that have a remote copy and aren't already enabled", async () => {
|
||||
localStorage.clear();
|
||||
const storageSync = makeSyncStub({ remote: true });
|
||||
@@ -51,6 +64,57 @@ test("offers and restores items that have a remote copy and aren't already enabl
|
||||
assert.deepEqual(restoredCalls, [["storage"]]);
|
||||
});
|
||||
|
||||
test("re-offers a MISSING item even after it was already offered once (restore-like-storage)", async () => {
|
||||
localStorage.clear();
|
||||
// Simulate a prior offer for wallet on this device (the old blocker).
|
||||
localStorage.setItem(OFFERED_KEY, JSON.stringify({ [PUBKEY]: { wallet: true } }));
|
||||
const walletSync = makeSyncStub({ remote: true, local: false });
|
||||
const prompt = createSettingsRestorePrompt({
|
||||
storageSync: makeSyncStub({ remote: false, local: true }),
|
||||
walletSync,
|
||||
confirm: () => true,
|
||||
});
|
||||
|
||||
const result = await prompt.maybeOffer(PUBKEY);
|
||||
assert.equal(result.accepted, true);
|
||||
assert.deepEqual(result.restored, ["wallet"]);
|
||||
assert.equal(walletSync.pulls, 1, "missing wallet is re-surfaced despite the prior offer");
|
||||
});
|
||||
|
||||
test("does NOT re-offer an item that is present locally once prompted", async () => {
|
||||
localStorage.clear();
|
||||
localStorage.setItem(OFFERED_KEY, JSON.stringify({ [PUBKEY]: { wallet: true } }));
|
||||
const walletSync = makeSyncStub({ remote: true, local: true });
|
||||
const prompt = createSettingsRestorePrompt({
|
||||
storageSync: makeSyncStub({ remote: false, local: true }),
|
||||
walletSync,
|
||||
confirm: () => true,
|
||||
});
|
||||
|
||||
const result = await prompt.maybeOffer(PUBKEY);
|
||||
assert.equal(result.offered, false, "present + already offered → no nag");
|
||||
assert.equal(walletSync.pulls, 0);
|
||||
});
|
||||
|
||||
test("dismissing a missing item stops it re-offering every login", async () => {
|
||||
localStorage.clear();
|
||||
const walletSync = makeSyncStub({ remote: true, local: false });
|
||||
const prompt = createSettingsRestorePrompt({
|
||||
storageSync: makeSyncStub({ remote: false, local: true }),
|
||||
walletSync,
|
||||
confirm: () => false, // decline
|
||||
});
|
||||
|
||||
const first = await prompt.maybeOffer(PUBKEY);
|
||||
assert.equal(first.offered, true);
|
||||
assert.equal(first.accepted, false);
|
||||
|
||||
// Next login: still missing locally, but the decline is remembered.
|
||||
const second = await prompt.maybeOffer(PUBKEY);
|
||||
assert.equal(second.offered, false, "declined → stays quiet even though still missing");
|
||||
assert.equal(walletSync.pulls, 0);
|
||||
});
|
||||
|
||||
test("declining the prompt restores nothing", async () => {
|
||||
localStorage.clear();
|
||||
const storageSync = makeSyncStub({ remote: true });
|
||||
|
||||
Reference in new issue
Block a user