mirror of
https://github.com/PR0M3TH3AN/bitvid.git
synced 2026-10-27 16:02:26 +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.
(cherry picked from commit ce14004725)
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, []);
|
|
});
|