Defer moderated playback until user override

This commit is contained in:
thePR0M3TH3AN committed 2025-10-25 18:58:28 -04:00
1 parent 1e19b2466e
commit e7566b5a4f
3 files changed
+140 -3

No files matched your search

+102 -3
View File
@@ -273,6 +273,7 @@ class Application {
this.modalCommentLimit = 40;
this.modalCommentLoadPromise = null;
this.modalCommentPublishPromise = null;
this.pendingModeratedPlayback = null;
this.nostrService = services.nostrService || nostrService;
this.r2Service = services.r2Service || r2Service;
@@ -6260,6 +6261,82 @@ class Application {
}
}
shouldDeferModeratedPlayback(video) {
if (!video || typeof video !== "object") {
return false;
}
const moderation =
video.moderation && typeof video.moderation === "object"
? video.moderation
: null;
if (!moderation) {
return false;
}
if (moderation.viewerOverride?.showAnyway === true) {
return false;
}
const blurActive = moderation.blurThumbnail === true;
const hiddenActive = moderation.hidden === true;
return blurActive || hiddenActive;
}
resumePendingModeratedPlayback(video) {
const pending = this.pendingModeratedPlayback;
if (!pending) {
return;
}
const activeVideo = this.currentVideo || null;
const targetVideo = video && typeof video === "object" ? video : activeVideo;
if (!targetVideo) {
return;
}
const pendingId =
typeof pending.videoId === "string" && pending.videoId ? pending.videoId : "";
const targetId =
typeof targetVideo.id === "string" && targetVideo.id ? targetVideo.id : "";
const matchesId = pendingId && targetId && pendingId === targetId;
const matchesActive = !pendingId && !targetId && targetVideo === activeVideo;
if (!matchesId && !matchesActive) {
return;
}
this.pendingModeratedPlayback = null;
if (typeof this.playVideoWithFallback !== "function") {
return;
}
const playbackOptions = {
url: pending.url || "",
magnet: pending.magnet || "",
};
if (pending.triggerProvided) {
playbackOptions.trigger = Object.prototype.hasOwnProperty.call(pending, "trigger")
? pending.trigger
: this.lastModalTrigger || null;
}
const playbackPromise = this.playVideoWithFallback(playbackOptions);
if (playbackPromise && typeof playbackPromise.catch === "function") {
playbackPromise.catch((error) => {
devLogger.error(
"[Application] Failed to resume moderated playback:",
error,
);
});
}
}
buildShareUrlFromNevent(nevent) {
if (!nevent) {
return "";
@@ -6376,6 +6453,7 @@ class Application {
this.clearActiveIntervals();
this.teardownModalViewCountSubscription();
this.teardownModalReactionSubscription();
this.pendingModeratedPlayback = null;
if (
this.videoModal &&
typeof this.videoModal.clearSimilarContent === "function"
@@ -7523,6 +7601,8 @@ class Application {
}
}
this.resumePendingModeratedPlayback(target);
return true;
}
@@ -9658,6 +9738,7 @@ class Application {
this.currentVideoPointer = null;
this.currentVideoPointerKey = null;
this.pendingModeratedPlayback = null;
if (this.blacklistedEventIds.has(eventId)) {
this.showError("This content has been removed or is not allowed.");
@@ -9818,11 +9899,28 @@ class Application {
await this.showModalWithPoster(this.currentVideo);
const playbackPromise = this.playVideoWithFallback({
const playbackOptions = {
url: trimmedUrl,
magnet: magnetInput,
trigger: hasTrigger ? this.lastModalTrigger : null,
});
};
if (hasTrigger) {
playbackOptions.trigger = this.lastModalTrigger;
}
let playbackPromise = null;
if (this.shouldDeferModeratedPlayback(this.currentVideo)) {
const pendingVideoId =
(this.currentVideo && typeof this.currentVideo.id === "string" && this.currentVideo.id)
? this.currentVideo.id
: eventId || null;
this.pendingModeratedPlayback = {
...playbackOptions,
triggerProvided: hasTrigger,
videoId: pendingVideoId,
};
} else {
playbackPromise = this.playVideoWithFallback(playbackOptions);
}
let lightningAddress = "";
let creatorProfile = {
@@ -10103,6 +10201,7 @@ class Application {
this.subscribeModalViewCount(null, null);
this.subscribeModalReactions(null, null);
this.subscribeModalComments(null);
this.pendingModeratedPlayback = null;
const sanitizedUrl = typeof url === "string" ? url.trim() : "";
const trimmedMagnet = typeof magnet === "string" ? magnet.trim() : "";
+37
View File
@@ -71,6 +71,43 @@ test("handleModerationOverride decorates stored and current videos then refreshe
assert.equal(currentVideo.moderation.blurThumbnail, false);
});
test("handleModerationOverride resumes deferred playback", async (t) => {
withMockedNostrTools(t);
const app = await createModerationAppHarness();
const videoId = "d".repeat(64);
const storedVideo = { id: videoId, moderation: buildModerationState() };
const incomingVideo = { id: videoId, moderation: buildModerationState() };
const currentVideo = { id: videoId, moderation: buildModerationState() };
app.videosMap.set(videoId, storedVideo);
app.currentVideo = currentVideo;
const playbackCalls = [];
app.playVideoWithFallback = (options) => {
playbackCalls.push(options);
return Promise.resolve({ source: "hosted" });
};
app.pendingModeratedPlayback = {
url: "https://example.com/video.mp4",
magnet: "",
triggerProvided: false,
videoId,
};
const result = app.handleModerationOverride({ video: incomingVideo });
assert.equal(result, true);
assert.equal(app.pendingModeratedPlayback, null);
assert.equal(playbackCalls.length, 1);
assert.deepEqual(playbackCalls[0], {
url: "https://example.com/video.mp4",
magnet: "",
});
});
test("handleModerationHide clears overrides and re-applies hidden state", async (t) => {
withMockedNostrTools(t);
@@ -201,6 +201,7 @@ export async function createModerationAppHarness() {
app.videosMap = new Map();
app.currentVideo = null;
app.pendingModeratedPlayback = null;
return app;
}