Merge pull request #907 from PR0M3TH3AN/codex/add-moderation-handlers-to-subscriptions

Forward subscriptions moderation badge actions to app
This commit is contained in:
thePR0M3TH3AN authored and GitHub committed 2025-10-25 20:29:51 -04:00
commit 2bc77c829d
2 files changed
+188

No files matched your search

+54
View File
@@ -1140,6 +1140,46 @@ class SubscriptionsManager {
listView.setPopularTagsContainer(null);
}
const buildModerationPayload = (detail = {}) => {
const event = detail?.event || null;
const trigger =
detail?.trigger ||
(event && (event.currentTarget || event.target)) ||
null;
const card = detail?.card || null;
const video = detail?.video || null;
const datasetContext = (() => {
const detailContext =
typeof detail?.context === "string" ? detail.context.trim() : "";
if (detailContext) {
return detailContext;
}
const datasetSource =
(detail?.dataset && typeof detail.dataset === "object"
? detail.dataset
: null) ||
(trigger && trigger.dataset) ||
(card && card.root && card.root.dataset) ||
null;
if (
datasetSource &&
typeof datasetSource.context === "string" &&
datasetSource.context.trim()
) {
return datasetSource.context.trim();
}
return "";
})();
return {
...detail,
video,
card,
trigger,
context: datasetContext || "subscriptions",
};
};
listView.setPlaybackHandler((detail) => {
if (!detail) {
return;
@@ -1207,6 +1247,20 @@ class SubscriptionsManager {
app?.handleMoreMenuAction?.("blacklist-author", detail);
});
listView.setModerationOverrideHandler((detail = {}) => {
if (typeof app?.handleModerationOverride !== "function") {
return false;
}
return app.handleModerationOverride(buildModerationPayload(detail));
});
listView.setModerationHideHandler((detail = {}) => {
if (typeof app?.handleModerationHide !== "function") {
return false;
}
return app.handleModerationHide(buildModerationPayload(detail));
});
listView.addEventListener("video:share", (event) => {
const detail = event?.detail || {};
const dataset = {
+134
View File
@@ -677,3 +677,137 @@ test("renderSameGridStyle shows empty state message", async () => {
}
});
test(
"renderSameGridStyle forwards moderation badge actions to the application",
async () => {
const SubscriptionsManager = subscriptions.constructor;
const originalWindow = globalThis.window;
const originalDocument = globalThis.document;
const originalNavigator = globalThis.navigator;
const dom = new JSDOM(
"<!doctype html><div id=\"subscriptionsVideoList\"></div>",
{ url: "https://example.test/" },
);
globalThis.window = dom.window;
globalThis.document = dom.window.document;
globalThis.navigator = dom.window.navigator;
const manager = new SubscriptionsManager();
const overrideCalls = [];
const hideCalls = [];
const app = {
videosMap: new Map(),
handleModerationOverride(detail) {
overrideCalls.push(detail);
return true;
},
handleModerationHide(detail) {
hideCalls.push(detail);
return true;
},
ensureGlobalMoreMenuHandlers() {},
closeAllMoreMenus() {},
handleMoreMenuAction() {},
};
setApplication(app);
const video = {
id: "video-moderated-1",
pubkey: "author-1",
title: "Moderated clip",
created_at: 1,
moderation: {
original: { hidden: true },
trustedMuted: true,
trustedMuteCount: 2,
summary: { types: { nudity: { trusted: 1 } } },
},
};
manager.renderSameGridStyle(
{ items: [{ video }] },
"subscriptionsVideoList",
);
const container = dom.window.document.getElementById(
"subscriptionsVideoList",
);
const overrideButton = container.querySelector(
'[data-moderation-action="override"]',
);
assert.ok(overrideButton, "override button should render for moderated video");
overrideButton.click();
await Promise.resolve();
assert.equal(
overrideCalls.length,
1,
"app.handleModerationOverride should receive one call",
);
const overrideDetail = overrideCalls[0];
assert.equal(overrideDetail.video, video);
assert.equal(overrideDetail.card?.video, video);
assert.equal(
overrideDetail.context,
"subscriptions",
"override payload should include the subscriptions context",
);
assert.equal(overrideDetail.trigger, overrideButton);
const hideButton = container.querySelector('[data-moderation-action="hide"]');
assert.ok(hideButton, "hide button should render for moderated video");
hideButton.click();
await Promise.resolve();
assert.equal(
hideCalls.length,
1,
"app.handleModerationHide should receive one call",
);
const hideDetail = hideCalls[0];
assert.equal(hideDetail.video, video);
assert.equal(hideDetail.card?.video, video);
assert.equal(
hideDetail.context,
"subscriptions",
"hide payload should include the subscriptions context",
);
assert.equal(hideDetail.trigger, hideButton);
if (manager.subscriptionListView?.destroy) {
manager.subscriptionListView.destroy();
manager.subscriptionListView = null;
}
dom.window.close();
if (typeof originalWindow === "undefined") {
delete globalThis.window;
} else {
globalThis.window = originalWindow;
}
if (typeof originalDocument === "undefined") {
delete globalThis.document;
} else {
globalThis.document = originalDocument;
}
if (typeof originalNavigator === "undefined") {
delete globalThis.navigator;
} else {
globalThis.navigator = originalNavigator;
}
setApplication(null);
},
);