mirror of
https://github.com/PR0M3TH3AN/bitvid.git
synced 2026-11-06 21:02:28 +00:00
Merge branch 'unstable' into codex/extend-tag-pill-rendering-and-add-menu-component
This commit is contained in:
14 files changed
+801
-39
No files matched your search
@@ -144,6 +144,7 @@ import {
|
||||
getInFlightUrlProbe,
|
||||
getModerationOverride,
|
||||
setModerationOverride,
|
||||
clearModerationOverride,
|
||||
loadModerationOverridesFromStorage,
|
||||
getModerationSettings,
|
||||
getDefaultModerationSettings,
|
||||
@@ -1350,6 +1351,9 @@ class Application {
|
||||
this.videoListView.setModerationOverrideHandler((detail = {}) =>
|
||||
this.handleModerationOverride(detail)
|
||||
);
|
||||
this.videoListView.setModerationHideHandler((detail = {}) =>
|
||||
this.handleModerationHide(detail)
|
||||
);
|
||||
|
||||
if (this.moreMenuController) {
|
||||
this.moreMenuController.attachVideoListView(this.videoListView);
|
||||
@@ -6258,7 +6262,9 @@ class Application {
|
||||
|
||||
autoplayModalVideo() {
|
||||
if (this.currentVideo?.moderation?.blockAutoplay) {
|
||||
this.log("[moderation] Skipping autoplay due to trusted reports.");
|
||||
this.log(
|
||||
"[moderation] Skipping autoplay due to trusted reports or trusted mutes.",
|
||||
);
|
||||
return;
|
||||
}
|
||||
if (!this.modalVideo) return;
|
||||
@@ -7226,8 +7232,11 @@ class Application {
|
||||
: this.deriveModerationTrustedCount(summary, reportType);
|
||||
|
||||
const thresholds = this.getActiveModerationThresholds();
|
||||
const computedBlockAutoplay = trustedCount >= thresholds.autoplayBlockThreshold;
|
||||
const computedBlurThumbnail = trustedCount >= thresholds.blurThreshold;
|
||||
const computedBlockAutoplay =
|
||||
trustedCount >= thresholds.autoplayBlockThreshold || trustedMuted;
|
||||
const blurFromReports = trustedCount >= thresholds.blurThreshold;
|
||||
let computedBlurThumbnail = blurFromReports;
|
||||
let computedBlurReason = computedBlurThumbnail ? "trusted-report" : "";
|
||||
|
||||
const muteHideThreshold = Number.isFinite(thresholds.trustedMuteHideThreshold)
|
||||
? Math.max(0, Math.floor(thresholds.trustedMuteHideThreshold))
|
||||
@@ -7263,6 +7272,23 @@ class Application {
|
||||
hideTriggered = true;
|
||||
}
|
||||
|
||||
if (!computedBlurThumbnail && (trustedMuted || hideTriggered)) {
|
||||
computedBlurThumbnail = true;
|
||||
if (hideTriggered) {
|
||||
computedBlurReason = hideReason || "trusted-hide";
|
||||
} else if (trustedMuted) {
|
||||
computedBlurReason = "trusted-mute";
|
||||
}
|
||||
} else if (computedBlurThumbnail) {
|
||||
if (hideTriggered) {
|
||||
computedBlurReason = hideReason || "trusted-hide";
|
||||
} else if (trustedMuted && !blurFromReports) {
|
||||
computedBlurReason = "trusted-mute";
|
||||
} else if (!computedBlurReason && blurFromReports) {
|
||||
computedBlurReason = "trusted-report";
|
||||
}
|
||||
}
|
||||
|
||||
const hideCounts = hideTriggered
|
||||
? {
|
||||
trustedMuteCount,
|
||||
@@ -7294,6 +7320,7 @@ class Application {
|
||||
hideCounts: originalHideCounts,
|
||||
hideBypass,
|
||||
hideTriggered,
|
||||
blurReason: computedBlurThumbnail ? computedBlurReason : "",
|
||||
};
|
||||
|
||||
const decoratedModeration = {
|
||||
@@ -7307,6 +7334,7 @@ class Application {
|
||||
trustedMuters: rawTrustedMuters,
|
||||
trustedMuteCount,
|
||||
trustedMuterDisplayNames,
|
||||
blurReason: computedBlurThumbnail ? computedBlurReason : "",
|
||||
original: {
|
||||
blockAutoplay: originalState.blockAutoplay,
|
||||
blurThumbnail: originalState.blurThumbnail,
|
||||
@@ -7315,9 +7343,14 @@ class Application {
|
||||
hideCounts: originalState.hideCounts,
|
||||
hideBypass: originalState.hideBypass,
|
||||
hideTriggered: originalState.hideTriggered,
|
||||
blurReason: originalState.blurReason,
|
||||
},
|
||||
};
|
||||
|
||||
if (!computedBlurThumbnail && decoratedModeration.blurReason) {
|
||||
delete decoratedModeration.blurReason;
|
||||
}
|
||||
|
||||
if (overrideActive) {
|
||||
decoratedModeration.blockAutoplay = false;
|
||||
decoratedModeration.blurThumbnail = false;
|
||||
@@ -7428,6 +7461,61 @@ class Application {
|
||||
return true;
|
||||
}
|
||||
|
||||
handleModerationHide({ video, card }) {
|
||||
if (!video || typeof video !== "object" || !video.id) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
clearModerationOverride(video.id);
|
||||
} catch (error) {
|
||||
devLogger.warn("[Application] Failed to clear moderation override:", error);
|
||||
}
|
||||
|
||||
const storedVideo =
|
||||
this.videosMap instanceof Map && video.id ? this.videosMap.get(video.id) : null;
|
||||
const target = storedVideo || video;
|
||||
|
||||
const resetModerationState = (subject) => {
|
||||
if (!subject || typeof subject !== "object") {
|
||||
return;
|
||||
}
|
||||
const moderation =
|
||||
subject.moderation && typeof subject.moderation === "object"
|
||||
? subject.moderation
|
||||
: null;
|
||||
if (!moderation) {
|
||||
return;
|
||||
}
|
||||
if (moderation.viewerOverride) {
|
||||
delete moderation.viewerOverride;
|
||||
}
|
||||
if (moderation.hideBypass) {
|
||||
delete moderation.hideBypass;
|
||||
}
|
||||
};
|
||||
|
||||
if (target) {
|
||||
resetModerationState(target);
|
||||
this.decorateVideoModeration(target);
|
||||
}
|
||||
|
||||
if (this.currentVideo && this.currentVideo.id === video.id) {
|
||||
resetModerationState(this.currentVideo);
|
||||
this.decorateVideoModeration(this.currentVideo);
|
||||
}
|
||||
|
||||
if (card && typeof card.refreshModerationUi === "function") {
|
||||
try {
|
||||
card.refreshModerationUi();
|
||||
} catch (error) {
|
||||
devLogger.warn("[Application] Failed to refresh moderation UI after hide:", error);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
getVideoAddressPointer(video) {
|
||||
if (
|
||||
this.discussionCountService &&
|
||||
|
||||
@@ -56,6 +56,82 @@ let currentChannelNpub = null;
|
||||
let currentChannelLightningAddress = "";
|
||||
let channelZapPendingOpen = false;
|
||||
|
||||
function normalizeHex(value) {
|
||||
if (typeof value !== "string") {
|
||||
return "";
|
||||
}
|
||||
const trimmed = value.trim().toLowerCase();
|
||||
return /^[0-9a-f]{64}$/.test(trimmed) ? trimmed : "";
|
||||
}
|
||||
|
||||
function shouldBlurChannelMedia(pubkey) {
|
||||
const normalized = normalizeHex(pubkey);
|
||||
if (!normalized) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
if (moderationService.isAuthorMutedByViewer(normalized) === true) {
|
||||
return true;
|
||||
}
|
||||
} catch (error) {
|
||||
devLogger.warn("[ChannelProfile] Failed to resolve viewer mute state", error);
|
||||
}
|
||||
|
||||
const app = getApp();
|
||||
const videos =
|
||||
app?.videosMap instanceof Map ? Array.from(app.videosMap.values()) : [];
|
||||
|
||||
for (const video of videos) {
|
||||
if (!video || typeof video !== "object") {
|
||||
continue;
|
||||
}
|
||||
const videoPubkey =
|
||||
typeof video.pubkey === "string" ? video.pubkey.trim().toLowerCase() : "";
|
||||
if (videoPubkey !== normalized) {
|
||||
continue;
|
||||
}
|
||||
const moderation =
|
||||
video.moderation && typeof video.moderation === "object"
|
||||
? video.moderation
|
||||
: null;
|
||||
if (!moderation) {
|
||||
continue;
|
||||
}
|
||||
if (moderation.hidden === true) {
|
||||
return true;
|
||||
}
|
||||
if (moderation.blurThumbnail === true) {
|
||||
const reason =
|
||||
typeof moderation.blurReason === "string"
|
||||
? moderation.blurReason.trim().toLowerCase()
|
||||
: "";
|
||||
if (reason.startsWith("trusted-")) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function applyChannelVisualBlur({ bannerEl = null, avatarEl = null, pubkey = "" } = {}) {
|
||||
const shouldBlur = shouldBlurChannelMedia(pubkey);
|
||||
const applyState = (element) => {
|
||||
if (!element) {
|
||||
return;
|
||||
}
|
||||
if (shouldBlur) {
|
||||
element.dataset.visualState = "blurred";
|
||||
} else if (element.dataset.visualState) {
|
||||
delete element.dataset.visualState;
|
||||
}
|
||||
};
|
||||
|
||||
applyState(bannerEl);
|
||||
applyState(avatarEl);
|
||||
}
|
||||
|
||||
const summarizeZapTracker = (tracker) =>
|
||||
Array.isArray(tracker)
|
||||
? tracker.map((entry) => ({
|
||||
@@ -3283,6 +3359,22 @@ function renderChannelVideosFromList({
|
||||
startPlayback(detail);
|
||||
};
|
||||
|
||||
videoCard.onModerationOverride = ({ video: overrideVideo, card: overrideCard }) =>
|
||||
typeof app?.handleModerationOverride === "function"
|
||||
? app.handleModerationOverride({
|
||||
video: overrideVideo,
|
||||
card: overrideCard,
|
||||
})
|
||||
: false;
|
||||
|
||||
videoCard.onModerationHide = ({ video: hideVideo, card: hideCard }) =>
|
||||
typeof app?.handleModerationHide === "function"
|
||||
? app.handleModerationHide({
|
||||
video: hideVideo,
|
||||
card: hideCard,
|
||||
})
|
||||
: false;
|
||||
|
||||
videoCard.onEdit = ({ video: editVideo, index: editIndex }) => {
|
||||
app?.handleEditVideo?.({
|
||||
eventId: editVideo?.id || "",
|
||||
@@ -3468,6 +3560,9 @@ function applyChannelProfileMetadata({
|
||||
avatarEl.src = normalized.picture || FALLBACK_CHANNEL_AVATAR;
|
||||
}
|
||||
|
||||
const blurPubkey = pubkey || currentChannelHex || "";
|
||||
applyChannelVisualBlur({ bannerEl, avatarEl, pubkey: blurPubkey });
|
||||
|
||||
const nameEl = document.getElementById("channelName");
|
||||
if (nameEl) {
|
||||
nameEl.textContent = normalized.name || "Unknown User";
|
||||
|
||||
+35
-4
@@ -583,8 +583,11 @@ export function createModerationStage({
|
||||
trustedMuters = [];
|
||||
}
|
||||
|
||||
const blockAutoplay = trustedCount >= normalizedAutoplayThreshold;
|
||||
const blurThumbnail = trustedCount >= normalizedBlurThreshold;
|
||||
const blockAutoplay =
|
||||
trustedCount >= normalizedAutoplayThreshold || trustedMuted;
|
||||
const blurFromReports = trustedCount >= normalizedBlurThreshold;
|
||||
let blurThumbnail = blurFromReports;
|
||||
let blurReason = blurThumbnail ? "trusted-report" : "";
|
||||
const adminWhitelist = adminStatus?.whitelisted === true;
|
||||
const adminWhitelistBypass = false;
|
||||
|
||||
@@ -598,7 +601,6 @@ export function createModerationStage({
|
||||
: {};
|
||||
|
||||
metadataModeration.blockAutoplay = blockAutoplay;
|
||||
metadataModeration.blurThumbnail = blurThumbnail;
|
||||
metadataModeration.summary = summary;
|
||||
metadataModeration.trustedCount = trustedCount;
|
||||
metadataModeration.reportType = normalizedReportType;
|
||||
@@ -616,7 +618,6 @@ export function createModerationStage({
|
||||
}
|
||||
|
||||
video.moderation.blockAutoplay = blockAutoplay;
|
||||
video.moderation.blurThumbnail = blurThumbnail;
|
||||
video.moderation.trustedCount = trustedCount;
|
||||
video.moderation.reportType = normalizedReportType;
|
||||
video.moderation.adminWhitelist = adminWhitelist;
|
||||
@@ -713,6 +714,36 @@ export function createModerationStage({
|
||||
}
|
||||
}
|
||||
|
||||
if (!blurThumbnail && (trustedMuted || hideTriggered)) {
|
||||
blurThumbnail = true;
|
||||
if (hideTriggered) {
|
||||
blurReason = hideReason || "trusted-hide";
|
||||
} else if (trustedMuted) {
|
||||
blurReason = "trusted-mute";
|
||||
}
|
||||
} else if (blurThumbnail) {
|
||||
if (hideTriggered) {
|
||||
blurReason = hideReason || "trusted-hide";
|
||||
} else if (trustedMuted && !blurFromReports) {
|
||||
blurReason = "trusted-mute";
|
||||
} else if (!blurReason && blurFromReports) {
|
||||
blurReason = "trusted-report";
|
||||
}
|
||||
}
|
||||
|
||||
metadataModeration.blurThumbnail = blurThumbnail;
|
||||
if (blurThumbnail) {
|
||||
metadataModeration.blurReason = blurReason;
|
||||
} else if (metadataModeration.blurReason) {
|
||||
delete metadataModeration.blurReason;
|
||||
}
|
||||
video.moderation.blurThumbnail = blurThumbnail;
|
||||
if (blurThumbnail) {
|
||||
video.moderation.blurReason = blurReason;
|
||||
} else if (video.moderation.blurReason) {
|
||||
delete video.moderation.blurReason;
|
||||
}
|
||||
|
||||
item.metadata.moderation = metadataModeration;
|
||||
|
||||
if (IS_DEV_MODE) {
|
||||
|
||||
+257
-28
@@ -114,15 +114,20 @@ export class VideoCard {
|
||||
onRequestSettingsMenu: null,
|
||||
onCloseSettingsMenu: null,
|
||||
onModerationOverride: null,
|
||||
onModerationHide: null,
|
||||
};
|
||||
|
||||
this.moderationBadgeEl = null;
|
||||
this.moderationBadgeTextEl = null;
|
||||
this.moderationBadgeIconWrapper = null;
|
||||
this.moderationBadgeIconSvg = null;
|
||||
this.moderationActionButton = null;
|
||||
this.moderationHideButton = null;
|
||||
this.moderationBadgeId = "";
|
||||
this.badgesContainerEl = null;
|
||||
this.hiddenSummaryEl = null;
|
||||
this.boundShowAnywayHandler = (event) => this.handleShowAnywayClick(event);
|
||||
this.boundModerationHideHandler = (event) => this.handleModerationHideClick(event);
|
||||
|
||||
this.root = null;
|
||||
this.anchorEl = null;
|
||||
@@ -198,6 +203,10 @@ export class VideoCard {
|
||||
this.callbacks.onModerationOverride = typeof fn === "function" ? fn : null;
|
||||
}
|
||||
|
||||
set onModerationHide(fn) {
|
||||
this.callbacks.onModerationHide = typeof fn === "function" ? fn : null;
|
||||
}
|
||||
|
||||
set onMoreAction(fn) {
|
||||
this.callbacks.onMoreAction = typeof fn === "function" ? fn : null;
|
||||
}
|
||||
@@ -1074,6 +1083,11 @@ export class VideoCard {
|
||||
const effectiveHideReason = hideReasonActive || originalHideReason;
|
||||
const effectiveHideCounts = activeHideCounts || originalHideCounts;
|
||||
|
||||
const blurReason =
|
||||
typeof moderation?.blurReason === "string" ? moderation.blurReason.trim() : "";
|
||||
const originalBlurReason =
|
||||
typeof original?.blurReason === "string" ? original.blurReason.trim() : "";
|
||||
|
||||
const context = {
|
||||
reportType,
|
||||
friendlyType: reportType ? reportType.replace(/[_-]+/g, " ").trim() : "",
|
||||
@@ -1082,6 +1096,8 @@ export class VideoCard {
|
||||
trustedMuted,
|
||||
trustedMuteCount,
|
||||
trustedMuteDisplayNames,
|
||||
blurReason,
|
||||
originalBlurReason,
|
||||
originalBlur: original.blurThumbnail === true,
|
||||
originalBlockAutoplay: original.blockAutoplay === true,
|
||||
activeBlur: moderation?.blurThumbnail === true,
|
||||
@@ -1222,16 +1238,18 @@ export class VideoCard {
|
||||
return "Hidden";
|
||||
}
|
||||
|
||||
const blurSource = (context.originalBlurReason || context.blurReason || "").toLowerCase();
|
||||
const suppressBlurLabel =
|
||||
blurSource === "trusted-mute" || blurSource === "trusted-mute-hide";
|
||||
|
||||
const parts = [];
|
||||
if (context.originalBlur) {
|
||||
if (context.originalBlur && !suppressBlurLabel) {
|
||||
parts.push("Blurred");
|
||||
}
|
||||
if (context.originalBlockAutoplay) {
|
||||
parts.push("Autoplay blocked");
|
||||
}
|
||||
if (context.trustedMuted && !context.originalBlur && !context.originalBlockAutoplay) {
|
||||
parts.push(reason || "Muted by trusted contacts");
|
||||
} else if (reason) {
|
||||
if (reason) {
|
||||
parts.push(reason);
|
||||
}
|
||||
|
||||
@@ -1240,28 +1258,7 @@ export class VideoCard {
|
||||
|
||||
createModerationOverrideButton() {
|
||||
const button = this.createElement("button", {
|
||||
classNames: [
|
||||
"inline-flex",
|
||||
"items-center",
|
||||
"rounded-full",
|
||||
"border",
|
||||
"border-status-warning-border",
|
||||
"px-3",
|
||||
"py-1",
|
||||
"text-2xs",
|
||||
"font-semibold",
|
||||
"uppercase",
|
||||
"tracking-extra-wide",
|
||||
"text-status-warning-on",
|
||||
"bg-transparent",
|
||||
"hover:bg-status-warning-surface",
|
||||
"transition",
|
||||
"duration-150",
|
||||
"focus-visible:outline",
|
||||
"focus-visible:outline-2",
|
||||
"focus-visible:outline-offset-2",
|
||||
"focus-visible:outline-status-warning-border",
|
||||
],
|
||||
classNames: ["moderation-badge__action", "flex-shrink-0"],
|
||||
attrs: {
|
||||
type: "button",
|
||||
"data-moderation-action": "override",
|
||||
@@ -1274,6 +1271,80 @@ export class VideoCard {
|
||||
return button;
|
||||
}
|
||||
|
||||
createModerationHideButton() {
|
||||
const button = this.createElement("button", {
|
||||
classNames: ["moderation-badge__action", "flex-shrink-0"],
|
||||
attrs: {
|
||||
type: "button",
|
||||
"data-moderation-action": "hide",
|
||||
"aria-describedby": this.getModerationBadgeId(),
|
||||
},
|
||||
textContent: "Hide",
|
||||
});
|
||||
button.addEventListener("click", this.boundModerationHideHandler);
|
||||
return button;
|
||||
}
|
||||
|
||||
handleModerationHideClick(event) {
|
||||
if (event) {
|
||||
if (typeof event.preventDefault === "function") {
|
||||
event.preventDefault();
|
||||
}
|
||||
if (typeof event.stopPropagation === "function") {
|
||||
event.stopPropagation();
|
||||
}
|
||||
}
|
||||
|
||||
const button = this.moderationHideButton;
|
||||
if (button) {
|
||||
button.disabled = true;
|
||||
button.setAttribute("aria-busy", "true");
|
||||
}
|
||||
|
||||
if (!this.callbacks.onModerationHide) {
|
||||
if (button) {
|
||||
button.disabled = false;
|
||||
button.removeAttribute("aria-busy");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
let result;
|
||||
try {
|
||||
result = this.callbacks.onModerationHide({
|
||||
event,
|
||||
video: this.video,
|
||||
card: this,
|
||||
});
|
||||
} catch (error) {
|
||||
userLogger.warn("[VideoCard] onModerationHide callback threw", error);
|
||||
if (button) {
|
||||
button.disabled = false;
|
||||
button.removeAttribute("aria-busy");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
Promise.resolve(result)
|
||||
.then((handled) => {
|
||||
if (handled === false) {
|
||||
if (button) {
|
||||
button.disabled = false;
|
||||
button.removeAttribute("aria-busy");
|
||||
}
|
||||
return;
|
||||
}
|
||||
this.refreshModerationUi();
|
||||
})
|
||||
.catch((error) => {
|
||||
userLogger.warn("[VideoCard] Moderation hide failed", error);
|
||||
if (button) {
|
||||
button.disabled = false;
|
||||
button.removeAttribute("aria-busy");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
handleShowAnywayClick(event) {
|
||||
if (event) {
|
||||
if (typeof event.preventDefault === "function") {
|
||||
@@ -1334,10 +1405,87 @@ export class VideoCard {
|
||||
});
|
||||
}
|
||||
|
||||
getModerationBadgeIconShape(state) {
|
||||
if (state === "override") {
|
||||
return {
|
||||
d: "M10 18a8 8 0 100-16 8 8 0 000 16zm3.78-9.72a.75.75 0 00-1.06-1.06L9 11.94l-1.72-1.72a.75.75 0 10-1.06 1.06l2.25 2.25a.75.75 0 001.06 0l3.25-3.25z",
|
||||
fillRule: "evenodd",
|
||||
clipRule: "evenodd",
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
d: "M10 18a8 8 0 100-16 8 8 0 000 16zm-.75-11.75a.75.75 0 011.5 0v4.5a.75.75 0 01-1.5 0v-4.5zm.75 8.5a1 1 0 100-2 1 1 0 000 2z",
|
||||
fillRule: "evenodd",
|
||||
clipRule: "evenodd",
|
||||
};
|
||||
}
|
||||
|
||||
updateModerationBadgeIcon(state) {
|
||||
if (!this.moderationBadgeIconSvg) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.moderationBadgeIconSvg.dataset.iconState = state;
|
||||
const path = this.moderationBadgeIconSvg.firstElementChild;
|
||||
if (!path) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { d, fillRule, clipRule } = this.getModerationBadgeIconShape(state);
|
||||
path.setAttribute("d", d);
|
||||
if (fillRule) {
|
||||
path.setAttribute("fill-rule", fillRule);
|
||||
} else {
|
||||
path.removeAttribute("fill-rule");
|
||||
}
|
||||
if (clipRule) {
|
||||
path.setAttribute("clip-rule", clipRule);
|
||||
} else {
|
||||
path.removeAttribute("clip-rule");
|
||||
}
|
||||
}
|
||||
|
||||
createModerationBadgeIcon(state) {
|
||||
const wrapper = this.createElement("span", {
|
||||
classNames: ["moderation-badge__icon"],
|
||||
attrs: { "aria-hidden": "true" },
|
||||
});
|
||||
|
||||
const svg = this.document.createElementNS(SVG_NAMESPACE, "svg");
|
||||
svg.setAttribute("viewBox", "0 0 20 20");
|
||||
svg.setAttribute("focusable", "false");
|
||||
svg.setAttribute("aria-hidden", "true");
|
||||
svg.classList.add("moderation-badge__icon-mark");
|
||||
|
||||
const path = this.document.createElementNS(SVG_NAMESPACE, "path");
|
||||
path.setAttribute("fill", "currentColor");
|
||||
svg.appendChild(path);
|
||||
wrapper.appendChild(svg);
|
||||
|
||||
this.moderationBadgeIconWrapper = wrapper;
|
||||
this.moderationBadgeIconSvg = svg;
|
||||
this.updateModerationBadgeIcon(state);
|
||||
|
||||
return wrapper;
|
||||
}
|
||||
|
||||
shouldShowModerationHideButton(context = this.getModerationContext()) {
|
||||
if (!context || !context.trustedMuted) {
|
||||
return false;
|
||||
}
|
||||
if (context.activeHidden && !context.overrideActive) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
buildModerationBadge(context = this.getModerationContext()) {
|
||||
if (!context.shouldShow) {
|
||||
this.moderationBadgeEl = null;
|
||||
this.moderationBadgeTextEl = null;
|
||||
this.moderationBadgeIconWrapper = null;
|
||||
this.moderationBadgeIconSvg = null;
|
||||
if (this.moderationActionButton) {
|
||||
this.moderationActionButton.removeEventListener(
|
||||
"click",
|
||||
@@ -1345,11 +1493,18 @@ export class VideoCard {
|
||||
);
|
||||
}
|
||||
this.moderationActionButton = null;
|
||||
if (this.moderationHideButton) {
|
||||
this.moderationHideButton.removeEventListener(
|
||||
"click",
|
||||
this.boundModerationHideHandler,
|
||||
);
|
||||
}
|
||||
this.moderationHideButton = null;
|
||||
return null;
|
||||
}
|
||||
|
||||
const badge = this.createElement("div", {
|
||||
classNames: ["badge", "flex", "flex-wrap", "items-center", "gap-sm"],
|
||||
classNames: ["moderation-badge", "flex", "flex-wrap", "items-center", "gap-sm"],
|
||||
});
|
||||
badge.dataset.variant = context.overrideActive ? "neutral" : "warning";
|
||||
badge.dataset.moderationBadge = "true";
|
||||
@@ -1362,6 +1517,7 @@ export class VideoCard {
|
||||
? "trusted-mute"
|
||||
: "blocked";
|
||||
badge.dataset.moderationState = state;
|
||||
this.updateModerationBadgeIcon(state);
|
||||
if (hiddenActive && context.effectiveHideReason) {
|
||||
badge.dataset.moderationHideReason = context.effectiveHideReason;
|
||||
}
|
||||
@@ -1372,8 +1528,13 @@ export class VideoCard {
|
||||
badge.setAttribute("aria-live", "polite");
|
||||
badge.setAttribute("aria-atomic", "true");
|
||||
|
||||
const icon = this.createModerationBadgeIcon(state);
|
||||
if (icon) {
|
||||
badge.appendChild(icon);
|
||||
}
|
||||
|
||||
const text = this.createElement("span", {
|
||||
classNames: ["whitespace-nowrap"],
|
||||
classNames: ["moderation-badge__text", "whitespace-nowrap"],
|
||||
textContent: this.buildModerationBadgeText(context),
|
||||
});
|
||||
badge.appendChild(text);
|
||||
@@ -1427,6 +1588,24 @@ export class VideoCard {
|
||||
this.moderationActionButton = null;
|
||||
}
|
||||
|
||||
if (this.shouldShowModerationHideButton(context)) {
|
||||
if (!this.moderationHideButton) {
|
||||
const hideButton = this.createModerationHideButton();
|
||||
badge.appendChild(hideButton);
|
||||
this.moderationHideButton = hideButton;
|
||||
} else {
|
||||
this.moderationHideButton.disabled = false;
|
||||
this.moderationHideButton.removeAttribute("aria-busy");
|
||||
}
|
||||
} else if (this.moderationHideButton) {
|
||||
this.moderationHideButton.removeEventListener(
|
||||
"click",
|
||||
this.boundModerationHideHandler,
|
||||
);
|
||||
this.moderationHideButton.remove();
|
||||
this.moderationHideButton = null;
|
||||
}
|
||||
|
||||
return badge;
|
||||
}
|
||||
|
||||
@@ -1434,6 +1613,21 @@ export class VideoCard {
|
||||
const badge = this.moderationBadgeEl;
|
||||
const hiddenActive = context.activeHidden && !context.overrideActive;
|
||||
|
||||
if (badge) {
|
||||
if (!this.moderationBadgeIconWrapper) {
|
||||
const iconWrapper = badge.querySelector(".moderation-badge__icon");
|
||||
if (iconWrapper) {
|
||||
this.moderationBadgeIconWrapper = iconWrapper;
|
||||
}
|
||||
}
|
||||
if (!this.moderationBadgeIconSvg) {
|
||||
const iconSvg = badge.querySelector(".moderation-badge__icon svg");
|
||||
if (iconSvg) {
|
||||
this.moderationBadgeIconSvg = iconSvg;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!context.shouldShow) {
|
||||
if (badge && badge.parentElement) {
|
||||
badge.parentElement.removeChild(badge);
|
||||
@@ -1444,8 +1638,17 @@ export class VideoCard {
|
||||
this.boundShowAnywayHandler,
|
||||
);
|
||||
}
|
||||
if (this.moderationHideButton) {
|
||||
this.moderationHideButton.removeEventListener(
|
||||
"click",
|
||||
this.boundModerationHideHandler,
|
||||
);
|
||||
this.moderationHideButton = null;
|
||||
}
|
||||
this.moderationBadgeEl = null;
|
||||
this.moderationBadgeTextEl = null;
|
||||
this.moderationBadgeIconWrapper = null;
|
||||
this.moderationBadgeIconSvg = null;
|
||||
this.moderationActionButton = null;
|
||||
if (this.hiddenSummaryEl && this.hiddenSummaryEl.parentElement === this.root) {
|
||||
this.hiddenSummaryEl.remove();
|
||||
@@ -1590,6 +1793,24 @@ export class VideoCard {
|
||||
this.moderationActionButton.disabled = false;
|
||||
this.moderationActionButton.removeAttribute("aria-busy");
|
||||
}
|
||||
|
||||
if (this.shouldShowModerationHideButton(context)) {
|
||||
if (!this.moderationHideButton) {
|
||||
const hideButton = this.createModerationHideButton();
|
||||
badge.appendChild(hideButton);
|
||||
this.moderationHideButton = hideButton;
|
||||
} else {
|
||||
this.moderationHideButton.disabled = false;
|
||||
this.moderationHideButton.removeAttribute("aria-busy");
|
||||
}
|
||||
} else if (this.moderationHideButton) {
|
||||
this.moderationHideButton.removeEventListener(
|
||||
"click",
|
||||
this.boundModerationHideHandler,
|
||||
);
|
||||
this.moderationHideButton.remove();
|
||||
this.moderationHideButton = null;
|
||||
}
|
||||
}
|
||||
|
||||
ensureHiddenSummaryContainer() {
|
||||
@@ -2175,6 +2396,14 @@ export class VideoCard {
|
||||
}
|
||||
}
|
||||
|
||||
if (this.authorPicEl && !this.shouldMaskNsfwForOwner) {
|
||||
if (context.activeBlur) {
|
||||
this.authorPicEl.dataset.visualState = "blurred";
|
||||
} else if (this.authorPicEl.dataset.visualState) {
|
||||
delete this.authorPicEl.dataset.visualState;
|
||||
}
|
||||
}
|
||||
|
||||
const reportCount = Math.max(0, Number(context.trustedCount) || 0);
|
||||
if (reportCount > 0) {
|
||||
this.root.dataset.moderationReportCount = String(reportCount);
|
||||
|
||||
@@ -601,6 +601,19 @@ export class VideoListView {
|
||||
});
|
||||
};
|
||||
|
||||
videoCard.onModerationHide = ({ event: hideEvent }) => {
|
||||
if (!this.handlers.moderationHide) {
|
||||
return false;
|
||||
}
|
||||
const trigger = hideEvent?.currentTarget || hideEvent?.target || null;
|
||||
return this.handlers.moderationHide({
|
||||
event: hideEvent,
|
||||
video,
|
||||
card: videoCard,
|
||||
trigger,
|
||||
});
|
||||
};
|
||||
|
||||
videoCard.onEdit = ({ event: editEvent, video: editVideo, index: editIndex }) => {
|
||||
if (this.handlers.edit) {
|
||||
const trigger = editEvent?.currentTarget || editEvent?.target || null;
|
||||
|
||||
Reference in new issue
Block a user