mirror of
https://github.com/PR0M3TH3AN/bitvid.git
synced 2026-11-06 12:52:26 +00:00
Preserve autoplay preference on fallback; reload open comment thread after login
### 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)
This commit is contained in:
1 parent
d8e5113efd
commit
f0a3ede3e5
7 files changed
+241
-23
No files matched your search
@@ -62,6 +62,89 @@ export function createActiveNostrSource({ service } = {}) {
|
||||
};
|
||||
}
|
||||
|
||||
// Bounded discovery source for curated surfaces such as Most Zapped. It never
|
||||
// falls back to an unscoped relay request: the caller must provide approved
|
||||
// creator pubkeys in runtime.whitelistedAuthors. The successful remote result
|
||||
// is retained briefly so an asynchronous ranking update does not turn into a
|
||||
// second full relay scan for the same author set.
|
||||
export function createWhitelistedAuthorsNostrSource({
|
||||
service,
|
||||
refreshTtlMs = 2 * 60 * 1000,
|
||||
now = () => Date.now(),
|
||||
} = {}) {
|
||||
const resolvedService = resolveService(service, nostrService);
|
||||
let cachedAuthorsKey = "";
|
||||
let cachedVideos = [];
|
||||
let fetchedAt = 0;
|
||||
let inFlight = null;
|
||||
|
||||
const normalizeAuthors = (input) =>
|
||||
Array.from(
|
||||
new Set(
|
||||
toArray(input)
|
||||
.map(normalizeAuthor)
|
||||
.filter(Boolean),
|
||||
),
|
||||
).sort();
|
||||
|
||||
return async function whitelistedAuthorsNostrSource(context = {}) {
|
||||
const authors = normalizeAuthors(context?.runtime?.whitelistedAuthors);
|
||||
const authorsKey = authors.join(",");
|
||||
if (!authors.length) {
|
||||
cachedAuthorsKey = "";
|
||||
cachedVideos = [];
|
||||
fetchedAt = 0;
|
||||
return [];
|
||||
}
|
||||
|
||||
const cacheIsCurrent =
|
||||
authorsKey === cachedAuthorsKey &&
|
||||
now() - fetchedAt < Math.max(0, Number(refreshTtlMs) || 0);
|
||||
|
||||
if (!cacheIsCurrent && !inFlight) {
|
||||
const fetchByAuthors = resolvedService?.fetchVideosByAuthors;
|
||||
inFlight = Promise.resolve(
|
||||
typeof fetchByAuthors === "function"
|
||||
? fetchByAuthors.call(resolvedService, authors)
|
||||
: [],
|
||||
)
|
||||
.then((videos) => {
|
||||
const allowed = new Set(authors);
|
||||
cachedAuthorsKey = authorsKey;
|
||||
cachedVideos = (Array.isArray(videos) ? videos : []).filter(
|
||||
(video) => allowed.has(normalizeAuthor(video?.pubkey)),
|
||||
);
|
||||
fetchedAt = now();
|
||||
return cachedVideos;
|
||||
})
|
||||
.catch((error) => {
|
||||
context?.log?.(
|
||||
"[whitelisted-authors-source] Failed to fetch approved creators",
|
||||
error,
|
||||
);
|
||||
return authorsKey === cachedAuthorsKey ? cachedVideos : [];
|
||||
})
|
||||
.finally(() => {
|
||||
inFlight = null;
|
||||
});
|
||||
}
|
||||
|
||||
const videos = cacheIsCurrent ? cachedVideos : await inFlight;
|
||||
// The whitelist may change while a prior author-scoped request is still in
|
||||
// flight. Filter again at return time so that stale results can never
|
||||
// briefly render after an administrator removes a creator.
|
||||
const allowed = new Set(authors);
|
||||
return (Array.isArray(videos) ? videos : [])
|
||||
.filter((video) => allowed.has(normalizeAuthor(video?.pubkey)))
|
||||
.map((video) => ({
|
||||
video,
|
||||
metadata: {
|
||||
source: "nostr:whitelisted-authors",
|
||||
},
|
||||
}));
|
||||
};
|
||||
}
|
||||
|
||||
export function createSubscriptionAuthorsSource({ service } = {}) {
|
||||
const resolvedService = resolveService(service, nostrService);
|
||||
|
||||
|
||||
Reference in new issue
Block a user