mirror of
https://github.com/PR0M3TH3AN/bitvid.git
synced 2026-10-27 07:52:24 +00:00
fix(watch-history): allow logged-out (local) removal instead of "disabled"
The logged-out delete error: handleWatchHistoryRemoval threw "watch-history-disabled" whenever the service wasn't synced (isEnabled() false) — which is the NORMAL state for a logged-out user — so local deletes errored with "Watch history sync is not available right now" and never touched the local queue. It conflated "not synced to relays" with "disabled". Now it only rejects when neither synced NOR local history is supported. For a local-only actor it proceeds with the snapshot (replace), which rewrites the local queue (replaceLocalQueue), and SKIPS the relay publish (gated on synced) so a session-actor list is never pushed to relays. Success copy reflects local vs synced. Tests + mutation-verified: local-only removal isn't rejected, snapshots with replace, and does NOT publish to relays; synced removal still publishes; truly disabled (no sync, no local) is still rejected. Reverting the gate fails the local cases. Build + full lint green. Co-Authored-By: Claude Opus 4.8 <[email protected]>
This commit is contained in:
1 parent
12440d2f48
commit
0756dd36d8
2 files changed
+104
-2
No files matched your search
@@ -222,7 +222,13 @@ export default class WatchHistoryController {
|
||||
}
|
||||
|
||||
async handleWatchHistoryRemoval(payload = {}) {
|
||||
if (!this.watchHistoryService?.isEnabled?.()) {
|
||||
const synced = this.watchHistoryService?.isEnabled?.() === true;
|
||||
// Logged-out / session actors aren't synced to relays, but their history
|
||||
// lives in the LOCAL queue — removal must still work there. Only reject when
|
||||
// neither synced nor local history is available.
|
||||
const localOnly =
|
||||
!synced && this.watchHistoryService?.supportsLocalHistory?.() === true;
|
||||
if (!synced && !localOnly) {
|
||||
const error = new Error("watch-history-disabled");
|
||||
error.handled = true;
|
||||
this.showError("Watch history sync is not available right now.");
|
||||
@@ -332,6 +338,10 @@ export default class WatchHistoryController {
|
||||
skipped: snapshotResult?.skipped,
|
||||
});
|
||||
|
||||
// Only push to relays when synced. For local-only actors the snapshot
|
||||
// above already rewrote the local queue (replaceLocalQueue); publishing a
|
||||
// session-actor list to relays would be wrong.
|
||||
if (synced) {
|
||||
try {
|
||||
const updateResult = await this.nostrClient?.updateWatchHistoryList?.(
|
||||
normalizedItems,
|
||||
@@ -354,9 +364,12 @@ export default class WatchHistoryController {
|
||||
error: updateError?.message || String(updateError),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
this.showSuccess(
|
||||
"Removed from encrypted history. Relay sync may take a moment.",
|
||||
synced
|
||||
? "Removed from encrypted history. Relay sync may take a moment."
|
||||
: "Removed from your local history.",
|
||||
);
|
||||
|
||||
return { handledToasts: true, snapshot: snapshotResult };
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
// Logged-out / local-only watch-history removal must NOT be rejected as
|
||||
// "watch-history-disabled". handleWatchHistoryRemoval previously threw whenever
|
||||
// the service wasn't synced (isEnabled() false), which is the normal state for a
|
||||
// logged-out user — so local deletes errored out. It must instead perform the
|
||||
// local removal (snapshot with replace, which rewrites the local queue) and NOT
|
||||
// publish to relays.
|
||||
|
||||
import "./test-helpers/setup-localstorage.mjs";
|
||||
import assert from "node:assert/strict";
|
||||
import test from "node:test";
|
||||
import WatchHistoryController from "../js/ui/watchHistoryController.js";
|
||||
|
||||
const ACTOR = "f".repeat(64);
|
||||
|
||||
function makeController({ synced }) {
|
||||
const calls = { snapshot: [], relayPublish: [], errors: [], success: [] };
|
||||
const controller = new WatchHistoryController({
|
||||
watchHistoryService: {
|
||||
isEnabled: () => synced,
|
||||
supportsLocalHistory: () => true,
|
||||
snapshot: async (items, opts) => {
|
||||
calls.snapshot.push({ items, opts });
|
||||
return { ok: true, local: !synced };
|
||||
},
|
||||
loadLatest: async () => [],
|
||||
},
|
||||
nostrClient: {
|
||||
updateWatchHistoryList: async (items, opts) => {
|
||||
calls.relayPublish.push({ items, opts });
|
||||
return { ok: true, items };
|
||||
},
|
||||
},
|
||||
showError: (m) => calls.errors.push(m),
|
||||
showSuccess: (m) => calls.success.push(m),
|
||||
getActivePubkey: () => "",
|
||||
});
|
||||
return { controller, calls };
|
||||
}
|
||||
|
||||
test("local-only removal is NOT rejected and rewrites the local list", async () => {
|
||||
const { controller, calls } = makeController({ synced: false });
|
||||
await controller.handleWatchHistoryRemoval({
|
||||
actor: ACTOR,
|
||||
items: [{ type: "e", value: "keep-1" }, { type: "e", value: "keep-2" }],
|
||||
removed: { pointerKey: "e:gone" },
|
||||
});
|
||||
|
||||
assert.equal(calls.errors.length, 0, "must not show the 'disabled' error for local-only");
|
||||
assert.equal(calls.snapshot.length, 1, "must snapshot the reduced list");
|
||||
assert.equal(calls.snapshot[0].opts.replace, true, "local removal must replace, not merge");
|
||||
assert.equal(
|
||||
calls.relayPublish.length,
|
||||
0,
|
||||
"must NOT publish a local-only list to relays",
|
||||
);
|
||||
});
|
||||
|
||||
test("synced removal still publishes to relays", async () => {
|
||||
const { controller, calls } = makeController({ synced: true });
|
||||
await controller.handleWatchHistoryRemoval({
|
||||
actor: ACTOR,
|
||||
items: [{ type: "e", value: "keep-1" }],
|
||||
removed: { pointerKey: "e:gone" },
|
||||
});
|
||||
|
||||
assert.equal(calls.errors.length, 0);
|
||||
assert.equal(calls.snapshot.length, 1);
|
||||
assert.equal(calls.relayPublish.length, 1, "synced removal must push to relays");
|
||||
assert.equal(calls.relayPublish[0].opts.replace, true);
|
||||
});
|
||||
|
||||
test("truly disabled (no sync AND no local) is still rejected", async () => {
|
||||
const calls = { errors: [] };
|
||||
const controller = new WatchHistoryController({
|
||||
watchHistoryService: {
|
||||
isEnabled: () => false,
|
||||
supportsLocalHistory: () => false,
|
||||
snapshot: async () => ({ ok: true }),
|
||||
},
|
||||
nostrClient: {},
|
||||
showError: (m) => calls.errors.push(m),
|
||||
getActivePubkey: () => "",
|
||||
});
|
||||
await assert.rejects(
|
||||
() => controller.handleWatchHistoryRemoval({ actor: ACTOR, items: [] }),
|
||||
/watch-history-disabled/,
|
||||
);
|
||||
assert.equal(calls.errors.length, 1);
|
||||
});
|
||||
Reference in new issue
Block a user