mirror of
https://github.com/PR0M3TH3AN/bitvid.git
synced 2026-10-27 07:52:24 +00:00
### Motivation - Prevent a browser-policy muted-autoplay retry from overwriting a viewer's explicit mute/unmute preference and ensure fallback mutes are transient. - Ensure that when a user logs in while a video comment modal is open, the open thread is reloaded (not just refreshed) so comments continue to display correctly after relay-backed services are reconfigured. ### Description - Add `js/utils/autoplayPreference.js` with `muteForAutoplayFallback` and `consumeAutoplayFallbackMute` helpers to mark and consume transient fallback mutes. - Update `js/services/playbackService.js` to treat sound as default unless the viewer explicitly chose mute, persist explicit choices via `localStorage`, ignore fallback-generated volume changes using `consumeAutoplayFallbackMute`, and expose `muteForAutoplayFallback` delegating to the new util. - Replace direct `videoElement.muted = true` calls with `playbackService.muteForAutoplayFallback` in `js/app/playbackCoordinator.js` and `js/webtorrent.js` so fallback mutes are marked as transient. - Change `js/app/authSessionCoordinator.js` to reload the currently open video comment thread with `commentController.load(activeModalVideo)` after login if a modal video is active, instead of only calling `refreshAuthState`. - Update tests in `tests/services/playbackService.test.mjs` and add a test in `tests/app/login-defers-dms.test.mjs` to cover the login behavior that reloads an open comment thread. ### Testing - Ran the `PlaybackService` unit tests (`tests/services/playbackService.test.mjs`) which assert `prepareVideoElement` behavior, explicit mute persistence, and the transient nature of fallback mute, and they passed. - Ran the auth login unit tests including `tests/app/login-defers-dms.test.mjs` which verifies DM deferral and reload of an open comment thread, and they passed.
74 lines
2.4 KiB
JavaScript
74 lines
2.4 KiB
JavaScript
import test from "node:test";
|
|
import assert from "node:assert/strict";
|
|
|
|
import { createWhitelistedAuthorsNostrSource } from "../js/feedEngine/sources.js";
|
|
|
|
test("Most Zapped discovery fetches only whitelisted creators and caches the catalog", async () => {
|
|
const calls = [];
|
|
const approved = "a".repeat(64);
|
|
const unapproved = "b".repeat(64);
|
|
let now = 1_000;
|
|
const source = createWhitelistedAuthorsNostrSource({
|
|
now: () => now,
|
|
service: {
|
|
async fetchVideosByAuthors(authors) {
|
|
calls.push(authors);
|
|
return [
|
|
{ id: "approved", pubkey: approved, created_at: 20 },
|
|
{ id: "unapproved", pubkey: unapproved, created_at: 30 },
|
|
];
|
|
},
|
|
},
|
|
});
|
|
|
|
const runtime = { whitelistedAuthors: [approved.toUpperCase()] };
|
|
const first = await source({ runtime });
|
|
const rerank = await source({ runtime });
|
|
|
|
assert.deepEqual(calls, [[approved]], "relay lookup is author-scoped once");
|
|
assert.deepEqual(first.map((entry) => entry.video.id), ["approved"]);
|
|
assert.deepEqual(rerank.map((entry) => entry.video.id), ["approved"]);
|
|
|
|
now += 2 * 60 * 1000;
|
|
await source({ runtime });
|
|
assert.equal(calls.length, 2, "catalog refreshes after its bounded TTL");
|
|
});
|
|
|
|
test("Most Zapped discovery is empty without approved creators and never queries globally", async () => {
|
|
let calls = 0;
|
|
const source = createWhitelistedAuthorsNostrSource({
|
|
service: {
|
|
async fetchVideosByAuthors() {
|
|
calls += 1;
|
|
return [{ id: "must-not-leak", pubkey: "c".repeat(64) }];
|
|
},
|
|
},
|
|
});
|
|
|
|
const result = await source({ runtime: { whitelistedAuthors: [] } });
|
|
assert.deepEqual(result, []);
|
|
assert.equal(calls, 0);
|
|
});
|
|
|
|
test("Most Zapped never leaks a removed creator while an earlier lookup is in flight", async () => {
|
|
const removed = "d".repeat(64);
|
|
const remaining = "e".repeat(64);
|
|
let resolveFetch;
|
|
const source = createWhitelistedAuthorsNostrSource({
|
|
service: {
|
|
fetchVideosByAuthors() {
|
|
return new Promise((resolve) => {
|
|
resolveFetch = resolve;
|
|
});
|
|
},
|
|
},
|
|
});
|
|
|
|
const first = source({ runtime: { whitelistedAuthors: [removed] } });
|
|
const afterRemoval = source({ runtime: { whitelistedAuthors: [remaining] } });
|
|
resolveFetch([{ id: "removed-video", pubkey: removed }]);
|
|
|
|
assert.deepEqual((await first).map((entry) => entry.video.id), ["removed-video"]);
|
|
assert.deepEqual(await afterRemoval, []);
|
|
});
|