Merge pull request #940 from PR0M3TH3AN/codex/refactor-videomodal-into-submodules

Refactor video modal into domain controllers
This commit is contained in:
thePR0M3TH3AN authored and GitHub committed 2025-10-26 18:03:57 -04:00
commit bf70f7c6e6
7 files changed
+609 -245

No files matched your search

+41 -245
View File
@@ -4,6 +4,15 @@ import { createVideoMoreMenuPanel } from "./videoMenuRenderers.js";
import { attachAmbientBackground } from "../ambientBackground.js";
import { applyDesignSystemAttributes } from "../../designSystem.js";
import { devLogger } from "../../utils/logger.js";
import { CommentsController } from "./video-modal/commentsController.js";
import { ReactionsController } from "./video-modal/reactionsController.js";
import { SimilarContentController } from "./video-modal/similarContentController.js";
import { ModerationController } from "./video-modal/moderationController.js";
import {
normalizeCommentAvatarKey as normalizeCommentAvatarKeyUtil,
resolveCommentAvatarAsset as resolveCommentAvatarAssetUtil,
registerCommentAvatarFailure as registerCommentAvatarFailureUtil,
} from "./video-modal/utils/commentAvatar.js";
import {
renderTagPillStrip,
applyTagPreferenceState,
@@ -188,6 +197,11 @@ export class VideoModal {
canManageBlacklist: false,
};
this.commentsController = new CommentsController({ modal: this });
this.reactionsController = new ReactionsController({ modal: this });
this.similarContentController = new SimilarContentController({ modal: this });
this.moderationController = new ModerationController({ modal: this });
this.commentsRoot = null;
this.commentsContainer = null;
this.commentsCountLabel = null;
@@ -479,6 +493,11 @@ export class VideoModal {
);
}
this.commentsController.destroy();
this.reactionsController.destroy();
this.similarContentController.destroy();
this.moderationController.destroy();
this.clearSimilarContent({ preservePending: true });
this.playerModal = playerModal;
@@ -486,7 +505,6 @@ export class VideoModal {
this.modalBackdrop = playerModal.querySelector("[data-dismiss]") || null;
this.scrollRegion = this.modalPanel || playerModal;
this.teardownSimilarContentMediaQuery();
const nextVideoTagsRoot = playerModal.querySelector("#videoTags") || null;
if (this.videoTagsRoot && this.videoTagsRoot !== nextVideoTagsRoot) {
this.cleanupVideoTags(this.videoTagsRoot);
@@ -496,26 +514,8 @@ export class VideoModal {
this.cleanupVideoTags(this.videoTagsRoot);
}
const similarHeading =
playerModal.querySelector("#playerModalSimilarContentHeading") || null;
const similarList =
playerModal.querySelector("#playerModalSimilarContentList") || null;
const similarSection =
similarList?.closest(
"[aria-labelledby='playerModalSimilarContentHeading']"
) || similarHeading?.closest(
"[aria-labelledby='playerModalSimilarContentHeading']"
) || null;
const similarContainer =
similarList?.closest(".watch-container") ||
similarHeading?.closest(".watch-container") ||
similarSection?.closest(".watch-container") ||
similarSection ||
null;
this.similarContentHeading = similarHeading;
this.similarContentList = similarList;
this.similarContentContainer = similarContainer;
const { container: similarContainer } =
this.similarContentController.initialize({ playerModal }) || {};
const statsContainerCandidate =
playerModal.querySelector("[data-video-stats-container]") ||
@@ -540,65 +540,12 @@ export class VideoModal {
statsContainer: statsContainerCandidate,
});
this.setupSimilarContentMediaQuery();
if (similarList && similarList.children.length) {
this.toggleSimilarContentVisibility(true);
} else {
this.toggleSimilarContentVisibility(false);
}
const previousTags = Array.isArray(this.videoTagsData)
? [...this.videoTagsData]
: [];
this.videoTagsData = null;
this.videoStage = playerModal.querySelector(".video-modal__video") || null;
this.moderationOverlay =
this.videoStage?.querySelector("[data-moderation-overlay]") || null;
this.moderationBadge =
this.moderationOverlay?.querySelector("[data-moderation-badge='true']") || null;
this.moderationBadgeText =
this.moderationOverlay?.querySelector("[data-moderation-text]") || null;
if (this.moderationPrimaryButton) {
if (this.moderationPrimaryMode === "override") {
this.moderationPrimaryButton.removeEventListener(
"click",
this.handleModerationOverrideClick,
);
} else if (this.moderationPrimaryMode === "hide") {
this.moderationPrimaryButton.removeEventListener(
"click",
this.handleModerationHideClick,
);
}
}
this.moderationActionsContainer =
this.moderationOverlay?.querySelector("[data-moderation-actions]") ||
this.moderationBadge?.querySelector(".moderation-badge__actions") ||
null;
const overrideButton =
this.moderationActionsContainer?.querySelector(
"[data-moderation-action='override']",
) || null;
if (overrideButton) {
overrideButton.addEventListener(
"click",
this.handleModerationOverrideClick,
);
this.moderationPrimaryButton = overrideButton;
this.moderationPrimaryMode = "override";
} else {
this.moderationPrimaryButton = null;
this.moderationPrimaryMode = "";
}
if (this.moderationBlockButton) {
this.moderationBlockButton.removeEventListener(
"click",
this.handleModerationBlockClick,
);
}
this.moderationBlockButton = null;
this.moderationController.initialize({ playerModal });
this.modalVideo = playerModal.querySelector("#modalVideo") || null;
this.modalStatus = playerModal.querySelector("#modalStatus") || null;
@@ -635,22 +582,7 @@ export class VideoModal {
this.shareBtn = playerModal.querySelector("#shareBtn") || null;
this.modalZapBtn = playerModal.querySelector("#modalZapBtn") || null;
this.modalMoreBtn = playerModal.querySelector("#modalMoreBtn") || null;
this.reactionButtons["+"] =
playerModal.querySelector("#modalLikeBtn") || null;
this.reactionButtons["-"] =
playerModal.querySelector("#modalDislikeBtn") || null;
this.reactionMeter =
playerModal.querySelector("[data-reaction-meter]") || null;
this.reactionMeterFill =
playerModal.querySelector("[data-reaction-meter-fill]") || null;
this.reactionMeterLabel =
playerModal.querySelector("[data-reaction-meter-label]") || null;
this.reactionMeterAssistive =
playerModal.querySelector("[data-reaction-meter-sr]") || null;
this.reactionCountLabels["+"] =
playerModal.querySelector("[data-reaction-like-count]") || null;
this.reactionCountLabels["-"] =
playerModal.querySelector("[data-reaction-dislike-count]") || null;
this.reactionsController.initialize({ playerModal });
this.renderVideoTags(previousTags);
@@ -688,90 +620,7 @@ export class VideoModal {
this.refreshActiveVideoModeration({ video: this.activeVideo });
this.detachCommentEventHandlers();
const commentsRoot =
playerModal.querySelector("[data-comments-root]") || null;
this.commentsRoot = commentsRoot;
this.commentsContainer = commentsRoot;
this.commentsCountLabel =
commentsRoot?.querySelector("[data-comments-count]") || null;
this.commentsEmptyState =
commentsRoot?.querySelector("[data-comments-empty]") || null;
this.commentsList =
commentsRoot?.querySelector("[data-comments-list]") || null;
this.commentsLoadMoreButton =
commentsRoot?.querySelector("[data-comments-load-more]") || null;
this.commentsDisabledPlaceholder =
playerModal.querySelector("[data-comments-disabled-placeholder]") ||
commentsRoot?.querySelector("[data-comments-disabled]") ||
null;
this.commentsComposer =
commentsRoot?.querySelector("[data-comments-composer]") || null;
this.commentsInput =
this.commentsComposer?.querySelector("[data-comments-input]") || null;
this.commentsSubmitButton =
this.commentsComposer?.querySelector("[data-comments-submit]") || null;
this.commentsStatusMessage =
this.commentsComposer?.querySelector("[data-comments-status]") ||
commentsRoot?.querySelector("[data-comments-status]") ||
null;
this.commentsCharCount =
this.commentsComposer?.querySelector("[data-comments-char-count]") ||
null;
this.commentComposerHint =
this.commentsComposer?.querySelector("#commentComposerHelp") ||
this.commentsComposer?.querySelector(".comment-composer__hint") ||
this.commentComposerHint ||
null;
if (this.commentComposerHint) {
const hintText = this.commentComposerHint.textContent || "";
if (hintText) {
this.commentComposerDefaultHint = hintText;
}
}
if (this.commentsCharCount) {
const countText = this.commentsCharCount.textContent || "";
if (countText) {
this.commentComposerDefaultCountText = countText;
}
}
if (this.commentsStatusMessage) {
const statusText = this.commentsStatusMessage.textContent || "";
if (statusText) {
this.commentStatusDefaultText = statusText;
}
}
if (this.commentsInput) {
const parsedMax = Number(this.commentsInput.maxLength);
if (Number.isFinite(parsedMax) && parsedMax > 0) {
this.commentComposerMaxLength = parsedMax;
}
}
if (this.commentsDisabledPlaceholder) {
const disabledText = this.commentsDisabledPlaceholder.textContent || "";
if (disabledText) {
this.commentsDisabledPlaceholderDefaultText = disabledText;
}
this.commentsDisabledPlaceholder.setAttribute("hidden", "");
}
if (this.commentsComposer) {
this.commentsComposer.removeAttribute("hidden");
}
this.clearComments();
this.resetCommentComposer();
this.attachCommentEventHandlers();
Object.values(this.reactionButtons).forEach((button) => {
if (button) {
button.addEventListener("click", this.handleReactionClick);
}
});
this.syncReactionButtons();
this.updateReactionMeterDisplay();
this.commentsController.initialize({ playerModal });
const closeButton = playerModal.querySelector("#closeModal");
if (closeButton) {
@@ -1773,80 +1622,25 @@ export class VideoModal {
}
resolveCommentAvatarAsset(pubkey, sanitizedPicture) {
const normalizedPubkey = this.normalizeCommentAvatarKey(pubkey);
const normalizedSource =
typeof sanitizedPicture === "string" && sanitizedPicture
? sanitizedPicture
: "";
if (normalizedSource && this.commentAvatarFailures.has(normalizedSource)) {
return { url: this.DEFAULT_PROFILE_AVATAR, source: "" };
}
if (normalizedPubkey && this.commentAvatarCache.has(normalizedPubkey)) {
const cached = this.commentAvatarCache.get(normalizedPubkey);
if (cached) {
const cachedSource =
typeof cached.source === "string" ? cached.source : "";
const cachedUrl =
typeof cached.url === "string" && cached.url
? cached.url
: this.DEFAULT_PROFILE_AVATAR;
if (!normalizedSource) {
return { url: cachedUrl, source: cachedSource };
}
if (cachedSource === normalizedSource) {
return { url: cachedUrl, source: cachedSource };
}
}
}
const resolvedUrl = normalizedSource || this.DEFAULT_PROFILE_AVATAR;
if (normalizedPubkey) {
this.commentAvatarCache.set(normalizedPubkey, {
url: resolvedUrl,
source: normalizedSource,
});
}
return { url: resolvedUrl, source: normalizedSource };
return resolveCommentAvatarAssetUtil({
cache: this.commentAvatarCache,
failures: this.commentAvatarFailures,
defaultAvatar: this.DEFAULT_PROFILE_AVATAR,
pubkey,
sanitizedPicture,
});
}
normalizeCommentAvatarKey(value) {
if (typeof value !== "string") {
return "";
}
const trimmed = value.trim();
if (!trimmed) {
return "";
}
return trimmed.toLowerCase();
return normalizeCommentAvatarKeyUtil(value);
}
registerCommentAvatarFailure(sourceUrl) {
if (typeof sourceUrl !== "string") {
return;
}
const trimmed = sourceUrl.trim();
if (!trimmed || trimmed === this.DEFAULT_PROFILE_AVATAR) {
return;
}
if (this.commentAvatarFailures.has(trimmed)) {
return;
}
this.commentAvatarFailures.add(trimmed);
this.commentAvatarCache.forEach((entry, key) => {
if (entry && entry.source === trimmed) {
this.commentAvatarCache.set(key, {
url: this.DEFAULT_PROFILE_AVATAR,
source: "",
});
}
registerCommentAvatarFailureUtil({
cache: this.commentAvatarCache,
failures: this.commentAvatarFailures,
defaultAvatar: this.DEFAULT_PROFILE_AVATAR,
sourceUrl,
});
}
@@ -2136,8 +1930,10 @@ export class VideoModal {
destroy() {
this.close();
this.detachCommentEventHandlers();
this.removeCommentRetryButton();
this.commentsController.destroy();
this.reactionsController.destroy();
this.similarContentController.destroy();
this.moderationController.destroy();
this.invokeCommentTeardown();
if (this.document) {
@@ -0,0 +1,141 @@
export class CommentsController {
constructor({ modal } = {}) {
this.modal = modal;
this.root = null;
}
initialize({ playerModal } = {}) {
if (!this.modal) {
return;
}
const root = playerModal?.querySelector("[data-comments-root]") || null;
this.root = root;
this.modal.commentsRoot = root;
this.modal.commentsContainer = root;
this.modal.commentsCountLabel =
root?.querySelector("[data-comments-count]") || null;
this.modal.commentsEmptyState =
root?.querySelector("[data-comments-empty]") || null;
this.modal.commentsList =
root?.querySelector("[data-comments-list]") || null;
this.modal.commentsLoadMoreButton =
root?.querySelector("[data-comments-load-more]") || null;
this.modal.commentsDisabledPlaceholder =
playerModal?.querySelector("[data-comments-disabled-placeholder]") ||
root?.querySelector("[data-comments-disabled]") ||
null;
this.modal.commentsComposer =
root?.querySelector("[data-comments-composer]") || null;
this.modal.commentsInput =
this.modal.commentsComposer?.querySelector("[data-comments-input]") || null;
this.modal.commentsSubmitButton =
this.modal.commentsComposer?.querySelector("[data-comments-submit]") || null;
this.modal.commentsStatusMessage =
this.modal.commentsComposer?.querySelector("[data-comments-status]") ||
root?.querySelector("[data-comments-status]") ||
null;
this.modal.commentsCharCount =
this.modal.commentsComposer?.querySelector("[data-comments-char-count]") ||
null;
this.modal.commentComposerHint =
this.modal.commentsComposer?.querySelector("#commentComposerHelp") ||
this.modal.commentsComposer?.querySelector(".comment-composer__hint") ||
this.modal.commentComposerHint ||
null;
if (this.modal.commentComposerHint) {
const hintText = this.modal.commentComposerHint.textContent || "";
if (hintText) {
this.modal.commentComposerDefaultHint = hintText;
}
}
if (this.modal.commentsCharCount) {
const countText = this.modal.commentsCharCount.textContent || "";
if (countText) {
this.modal.commentComposerDefaultCountText = countText;
}
}
if (this.modal.commentsStatusMessage) {
const statusText = this.modal.commentsStatusMessage.textContent || "";
if (statusText) {
this.modal.commentStatusDefaultText = statusText;
}
}
if (this.modal.commentsInput) {
const parsedMax = Number(this.modal.commentsInput.maxLength);
if (Number.isFinite(parsedMax) && parsedMax > 0) {
this.modal.commentComposerMaxLength = parsedMax;
}
}
if (this.modal.commentsDisabledPlaceholder) {
const disabledText =
this.modal.commentsDisabledPlaceholder.textContent || "";
if (disabledText) {
this.modal.commentsDisabledPlaceholderDefaultText = disabledText;
}
this.modal.commentsDisabledPlaceholder.setAttribute("hidden", "");
}
if (this.modal.commentsComposer) {
this.modal.commentsComposer.removeAttribute("hidden");
}
this.modal.clearComments();
this.modal.resetCommentComposer();
this.modal.attachCommentEventHandlers();
}
update(action = {}) {
if (!this.modal) {
return;
}
const { type } = action;
switch (type) {
case "set-visibility":
this.modal.setCommentsVisibility(action.visible);
break;
case "render":
this.modal.renderComments(action.snapshot);
break;
case "set-composer-state":
this.modal.setCommentComposerState(action.state);
break;
case "set-thread-context":
this.modal.setCommentThreadContext(action.context);
break;
case "clear":
this.modal.clearComments();
break;
default:
break;
}
}
destroy() {
if (!this.modal) {
return;
}
this.modal.detachCommentEventHandlers();
this.modal.removeCommentRetryButton();
this.modal.commentsRoot = null;
this.modal.commentsContainer = null;
this.modal.commentsCountLabel = null;
this.modal.commentsEmptyState = null;
this.modal.commentsList = null;
this.modal.commentsLoadMoreButton = null;
this.modal.commentsDisabledPlaceholder = null;
this.modal.commentsComposer = null;
this.modal.commentsInput = null;
this.modal.commentsSubmitButton = null;
this.modal.commentsStatusMessage = null;
this.modal.commentsCharCount = null;
this.modal.commentComposerHint = null;
this.root = null;
}
}
@@ -0,0 +1,118 @@
export class ModerationController {
constructor({ modal } = {}) {
this.modal = modal;
}
initialize({ playerModal } = {}) {
if (!this.modal) {
return;
}
this.modal.videoStage =
playerModal?.querySelector(".video-modal__video") || null;
this.modal.moderationOverlay =
this.modal.videoStage?.querySelector("[data-moderation-overlay]") || null;
this.modal.moderationBadge =
this.modal.moderationOverlay?.querySelector(
"[data-moderation-badge='true']",
) || null;
this.modal.moderationBadgeText =
this.modal.moderationOverlay?.querySelector("[data-moderation-text]") ||
null;
this.teardownPrimaryButton();
this.modal.moderationActionsContainer =
this.modal.moderationOverlay?.querySelector("[data-moderation-actions]") ||
this.modal.moderationBadge?.querySelector(".moderation-badge__actions") ||
null;
const overrideButton =
this.modal.moderationActionsContainer?.querySelector(
"[data-moderation-action='override']",
) || null;
if (overrideButton) {
overrideButton.addEventListener(
"click",
this.modal.handleModerationOverrideClick,
);
this.modal.moderationPrimaryButton = overrideButton;
this.modal.moderationPrimaryMode = "override";
} else {
this.modal.moderationPrimaryButton = null;
this.modal.moderationPrimaryMode = "";
}
this.teardownBlockButton();
const blockButton =
this.modal.moderationActionsContainer?.querySelector(
"[data-moderation-action='block']",
) || null;
if (blockButton) {
blockButton.addEventListener(
"click",
this.modal.handleModerationBlockClick,
);
this.modal.moderationBlockButton = blockButton;
} else {
this.modal.moderationBlockButton = null;
}
}
update(action = {}) {
if (!this.modal) {
return;
}
const { type } = action;
switch (type) {
case "set-context":
this.modal.refreshActiveVideoModeration?.(action.context);
break;
case "set-overlay":
this.modal.applyModerationOverlay?.(action.overlay);
break;
default:
break;
}
}
destroy() {
if (!this.modal) {
return;
}
this.teardownPrimaryButton();
this.teardownBlockButton();
this.modal.moderationOverlay = null;
this.modal.moderationBadge = null;
this.modal.moderationBadgeText = null;
this.modal.moderationActionsContainer = null;
this.modal.moderationPrimaryButton = null;
this.modal.moderationPrimaryMode = "";
this.modal.moderationBlockButton = null;
}
teardownPrimaryButton() {
if (!this.modal?.moderationPrimaryButton) {
return;
}
const button = this.modal.moderationPrimaryButton;
if (this.modal.moderationPrimaryMode === "override") {
button.removeEventListener(
"click",
this.modal.handleModerationOverrideClick,
);
} else if (this.modal.moderationPrimaryMode === "hide") {
button.removeEventListener("click", this.modal.handleModerationHideClick);
}
}
teardownBlockButton() {
if (!this.modal?.moderationBlockButton) {
return;
}
this.modal.moderationBlockButton.removeEventListener(
"click",
this.modal.handleModerationBlockClick,
);
}
}
@@ -0,0 +1,76 @@
export class ReactionsController {
constructor({ modal } = {}) {
this.modal = modal;
this.boundHandler = (event) => {
this.modal?.handleReactionClick?.(event);
};
this.buttons = [];
}
initialize({ playerModal } = {}) {
if (!this.modal) {
return;
}
const likeButton = playerModal?.querySelector("#modalLikeBtn") || null;
const dislikeButton = playerModal?.querySelector("#modalDislikeBtn") || null;
this.modal.reactionButtons = { "+": likeButton, "-": dislikeButton };
this.modal.reactionMeter =
playerModal?.querySelector("[data-reaction-meter]") || null;
this.modal.reactionMeterFill =
playerModal?.querySelector("[data-reaction-meter-fill]") || null;
this.modal.reactionMeterLabel =
playerModal?.querySelector("[data-reaction-meter-label]") || null;
this.modal.reactionMeterAssistive =
playerModal?.querySelector("[data-reaction-meter-sr]") || null;
this.modal.reactionCountLabels = {
"+": playerModal?.querySelector("[data-reaction-like-count]") || null,
"-": playerModal?.querySelector("[data-reaction-dislike-count]") || null,
};
this.buttons = [likeButton, dislikeButton].filter(Boolean);
this.buttons.forEach((button) => {
button.removeEventListener("click", this.boundHandler);
button.addEventListener("click", this.boundHandler);
});
this.modal.syncReactionButtons?.();
this.modal.updateReactionMeterDisplay?.();
}
update(action = {}) {
if (!this.modal) {
return;
}
const { type } = action;
switch (type) {
case "set-summary":
this.modal.updateReactionSummary?.(action.summary);
break;
case "set-user-reaction":
this.modal.setUserReaction?.(action.reaction);
break;
case "reset":
this.modal.resetReactions?.();
break;
default:
break;
}
}
destroy() {
this.buttons.forEach((button) => {
button.removeEventListener("click", this.boundHandler);
});
this.buttons = [];
if (this.modal) {
this.modal.reactionButtons = { "+": null, "-": null };
this.modal.reactionMeter = null;
this.modal.reactionMeterFill = null;
this.modal.reactionMeterLabel = null;
this.modal.reactionMeterAssistive = null;
this.modal.reactionCountLabels = { "+": null, "-": null };
}
}
}
@@ -0,0 +1,73 @@
export class SimilarContentController {
constructor({ modal } = {}) {
this.modal = modal;
}
initialize({ playerModal } = {}) {
if (!this.modal) {
return { container: null, heading: null, list: null };
}
this.modal.teardownSimilarContentMediaQuery?.();
const heading =
playerModal?.querySelector("#playerModalSimilarContentHeading") || null;
const list =
playerModal?.querySelector("#playerModalSimilarContentList") || null;
const section =
list?.closest("[aria-labelledby='playerModalSimilarContentHeading']") ||
heading?.closest("[aria-labelledby='playerModalSimilarContentHeading']") ||
null;
const container =
list?.closest(".watch-container") ||
heading?.closest(".watch-container") ||
section?.closest(".watch-container") ||
section ||
null;
this.modal.similarContentHeading = heading;
this.modal.similarContentList = list;
this.modal.similarContentContainer = container;
this.modal.setupSimilarContentMediaQuery?.();
if (list && list.children.length) {
this.modal.toggleSimilarContentVisibility?.(true);
} else {
this.modal.toggleSimilarContentVisibility?.(false);
}
return { container, heading, list };
}
update(action = {}) {
if (!this.modal) {
return;
}
const { type } = action;
switch (type) {
case "set-items":
this.modal.setSimilarContent?.(action.items, action.options);
break;
case "set-visibility":
this.modal.toggleSimilarContentVisibility?.(action.visible);
break;
case "clear":
this.modal.clearSimilarContent?.(action.options);
break;
default:
break;
}
}
destroy() {
if (!this.modal) {
return;
}
this.modal.teardownSimilarContentMediaQuery?.();
this.modal.similarContentHeading = null;
this.modal.similarContentList = null;
this.modal.similarContentContainer = null;
}
}
@@ -0,0 +1,81 @@
export function normalizeCommentAvatarKey(value) {
if (typeof value !== "string") {
return "";
}
const trimmed = value.trim();
if (!trimmed) {
return "";
}
return trimmed.toLowerCase();
}
export function resolveCommentAvatarAsset({
cache,
failures,
defaultAvatar,
pubkey,
sanitizedPicture,
}) {
const normalizedPubkey = normalizeCommentAvatarKey(pubkey);
const normalizedSource =
typeof sanitizedPicture === "string" && sanitizedPicture
? sanitizedPicture
: "";
if (normalizedSource && failures?.has?.(normalizedSource)) {
return { url: defaultAvatar, source: "" };
}
if (normalizedPubkey && cache?.has?.(normalizedPubkey)) {
const cached = cache.get(normalizedPubkey);
if (cached) {
const cachedSource =
typeof cached.source === "string" ? cached.source : "";
const cachedUrl =
typeof cached.url === "string" && cached.url
? cached.url
: defaultAvatar;
if (!normalizedSource || cachedSource === normalizedSource) {
return { url: cachedUrl, source: cachedSource };
}
}
}
const resolvedUrl = normalizedSource || defaultAvatar;
if (normalizedPubkey && cache) {
cache.set(normalizedPubkey, { url: resolvedUrl, source: normalizedSource });
}
return { url: resolvedUrl, source: normalizedSource };
}
export function registerCommentAvatarFailure({
cache,
failures,
defaultAvatar,
sourceUrl,
}) {
if (typeof sourceUrl !== "string") {
return;
}
const trimmed = sourceUrl.trim();
if (!trimmed || trimmed === defaultAvatar) {
return;
}
if (!failures?.has?.(trimmed)) {
failures?.add?.(trimmed);
}
if (!cache?.forEach) {
return;
}
cache.forEach((entry, key) => {
if (entry && entry.source === trimmed) {
cache.set(key, { url: defaultAvatar, source: "" });
}
});
}
+79
View File
@@ -0,0 +1,79 @@
import test from "node:test";
import assert from "node:assert/strict";
import { setupModal } from "./video-modal-accessibility.test.mjs";
test("CommentsController lifecycle manages comment references", async (t) => {
const { modal, cleanup } = await setupModal();
t.after(cleanup);
assert.ok(modal.commentsRoot, "comments should be initialized after hydrate");
modal.commentsController.destroy();
assert.equal(modal.commentsRoot, null, "destroy should clear comment root");
modal.commentsController.initialize({ playerModal: modal.getRoot() });
assert.ok(modal.commentsRoot, "initialize should restore comment root");
});
test("ReactionsController delegates reaction updates", async (t) => {
const { modal, cleanup } = await setupModal();
t.after(cleanup);
modal.reactionsController.update({ type: "set-user-reaction", reaction: "+" });
assert.equal(
modal.reactionState.userReaction,
"+",
"user reaction should be updated via controller",
);
modal.reactionsController.destroy();
assert.equal(
modal.reactionButtons["+"],
null,
"destroy should clear reaction button references",
);
});
test("SimilarContentController toggles section visibility", async (t) => {
const { modal, cleanup } = await setupModal();
t.after(cleanup);
const container = modal.similarContentContainer;
assert.ok(container, "similar content container should exist");
modal.similarContentController.update({ type: "set-visibility", visible: false });
assert.equal(
container.hasAttribute("hidden"),
true,
"controller should hide similar content when requested",
);
modal.similarContentController.update({ type: "set-visibility", visible: true });
assert.equal(
container.hasAttribute("hidden"),
false,
"controller should reveal similar content",
);
});
test("ModerationController reset clears moderation overlay references", async (t) => {
const { modal, cleanup } = await setupModal();
t.after(cleanup);
assert.ok(modal.moderationOverlay, "moderation overlay should be present");
modal.moderationController.destroy();
assert.equal(
modal.moderationOverlay,
null,
"destroy should clear moderation overlay references",
);
modal.moderationController.initialize({ playerModal: modal.getRoot() });
assert.ok(
modal.moderationOverlay,
"initialize should restore moderation overlay references",
);
});