Files
bitvid/js/ui/profileModal/ProfileModerationController.js
T
thePR0M3TH3ANandClaude Opus 4.8 27808533d2 Mobile: same stacked treatment for the Admin + Moderation account cards
Extends the .profile-account-card pattern to the remaining compact account
lists so they all behave consistently on phones:

- Admin lists (whitelist/blacklist/editors): use the shared
  .profile-account-card / __actions classes (was bespoke responsive utilities;
  now full-width equal-width action buttons on mobile like the others).
- Moderation overrides: the summary+actions row (a nested flex inside an
  already-padded card) uses a new padding-less .profile-account-card__row
  variant so it stacks on phones too.

The account switcher (single tap-button with a column meta-stack) and DM
conversation rows (tap-to-open, no action buttons) are already mobile-friendly
and structurally different, so they're intentionally left as-is.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-07-08 07:35:05 -04:00

933 lines
29 KiB
JavaScript

import { devLogger, userLogger } from "../../utils/logger.js";
import { RUNTIME_FLAGS } from "../../constants.js";
import {
DEFAULT_INTERNAL_MODERATION_SETTINGS,
createInternalDefaultModerationSettings,
} from "../profileModalContract.js";
const noop = () => {};
const TRUSTED_MUTE_HIDE_HELPER_TEXT =
"Reaching this count hides cards (with “Show anyway”); lower signals only blur thumbnails or block autoplay.";
const FALLBACK_PROFILE_AVATAR = "assets/svg/default-profile.svg";
export class ProfileModerationController {
constructor(mainController) {
this.mainController = mainController;
this.moderationSettingsCard = null;
this.moderationBlurInput = null;
this.moderationAutoplayInput = null;
this.moderationMuteHideInput = null;
this.moderationSpamHideInput = null;
this.moderationSaveButton = null;
this.moderationResetButton = null;
this.moderationStatusText = null;
this.moderationOverridesList = null;
this.moderationOverridesEmpty = null;
this.moderationTrustedContactsCount = null;
this.moderationTrustedMuteCount = null;
this.moderationTrustedReportCount = null;
this.moderationSeedOnlyIndicator = null;
this.moderationHideControlsGroup = null;
this.moderationHideControlElements = [];
this.boundModerationOverridesUpdate = null;
this.moderationSettingsDefaults = createInternalDefaultModerationSettings();
this.currentModerationSettings = createInternalDefaultModerationSettings();
}
cacheDomReferences() {
this.moderationSettingsCard = document.getElementById("profileModerationSettings") || null;
this.moderationBlurInput = document.getElementById("profileModerationBlurThreshold") || null;
this.moderationAutoplayInput = document.getElementById("profileModerationAutoplayThreshold") || null;
this.moderationMuteHideInput = document.getElementById("profileModerationMuteHideThreshold") || null;
this.moderationSpamHideInput = document.getElementById("profileModerationSpamHideThreshold") || null;
this.moderationSaveButton = document.getElementById("profileModerationSave") || null;
this.moderationResetButton = document.getElementById("profileModerationReset") || null;
this.moderationStatusText = document.getElementById("profileModerationStatus") || null;
this.moderationOverridesList = document.getElementById("profileModerationOverridesList") || null;
this.moderationOverridesEmpty = document.getElementById("profileModerationOverridesEmpty") || null;
this.moderationTrustedContactsCount = document.getElementById("profileModerationTrustedContactsCount") || null;
this.moderationTrustedMuteCount = document.getElementById("profileModerationTrustedMuteCount") || null;
this.moderationTrustedReportCount = document.getElementById("profileModerationTrustedReportCount") || null;
this.moderationSeedOnlyIndicator = document.getElementById("profileModerationSeedOnlyIndicator") || null;
this.moderationHideControlsGroup =
this.moderationSettingsCard?.querySelector(
"[data-role=\"trusted-hide-controls\"]",
) || null;
this.moderationHideControlElements = Array.from(
this.moderationSettingsCard?.querySelectorAll(
"[data-role=\"trusted-hide-control\"]",
) || [],
);
this.updateTrustedMuteHideHelperCopy();
}
registerEventListeners() {
if (this.moderationBlurInput instanceof HTMLElement) {
this.moderationBlurInput.addEventListener("input", () => {
this.applyModerationSettingsControlState();
});
}
if (this.moderationAutoplayInput instanceof HTMLElement) {
this.moderationAutoplayInput.addEventListener("input", () => {
this.applyModerationSettingsControlState();
});
}
if (this.moderationMuteHideInput instanceof HTMLElement) {
this.moderationMuteHideInput.addEventListener("input", () => {
this.applyModerationSettingsControlState();
});
}
if (this.moderationSpamHideInput instanceof HTMLElement) {
this.moderationSpamHideInput.addEventListener("input", () => {
this.applyModerationSettingsControlState();
});
}
if (this.moderationSaveButton instanceof HTMLElement) {
this.moderationSaveButton.addEventListener("click", () => {
void this.handleModerationSettingsSave();
});
}
if (this.moderationResetButton instanceof HTMLElement) {
this.moderationResetButton.addEventListener("click", () => {
void this.handleModerationSettingsReset();
});
}
if (!this.boundModerationOverridesUpdate && typeof document !== "undefined") {
this.boundModerationOverridesUpdate = () => {
this.refreshModerationOverridesUi();
};
document.addEventListener(
"video:moderation-override",
this.boundModerationOverridesUpdate,
);
document.addEventListener(
"video:moderation-hide",
this.boundModerationOverridesUpdate,
);
document.addEventListener(
"video:moderation-block",
this.boundModerationOverridesUpdate,
);
}
}
getModerationSettingsService() {
const service = this.mainController.services.moderationSettings;
if (!service || typeof service !== "object") {
return null;
}
return service;
}
getModerationSettingsDefaults() {
const service = this.getModerationSettingsService();
let defaults = null;
if (service && typeof service.getDefaultModerationSettings === "function") {
try {
defaults = service.getDefaultModerationSettings();
} catch (error) {
devLogger.info("[profileModal] moderation defaults fallback used", error);
}
}
if (!defaults || typeof defaults !== "object") {
defaults = createInternalDefaultModerationSettings();
}
const sanitized = {
blurThreshold: Math.max(
0,
Math.floor(
Number(
defaults.blurThreshold ?? DEFAULT_INTERNAL_MODERATION_SETTINGS.blurThreshold,
),
),
),
autoplayBlockThreshold: Math.max(
0,
Math.floor(
Number(
defaults.autoplayBlockThreshold ??
DEFAULT_INTERNAL_MODERATION_SETTINGS.autoplayBlockThreshold,
),
),
),
trustedMuteHideThreshold: Math.max(
0,
Math.floor(
Number(
defaults.trustedMuteHideThreshold ??
DEFAULT_INTERNAL_MODERATION_SETTINGS.trustedMuteHideThreshold,
),
),
),
trustedSpamHideThreshold: Math.max(
0,
Math.floor(
Number(
defaults.trustedSpamHideThreshold ??
DEFAULT_INTERNAL_MODERATION_SETTINGS.trustedSpamHideThreshold,
),
),
),
};
return sanitized;
}
normalizeModerationSettings(settings = null) {
const defaults = this.getModerationSettingsDefaults();
const blur = Number.isFinite(settings?.blurThreshold)
? Math.max(0, Math.floor(settings.blurThreshold))
: defaults.blurThreshold;
const autoplay = Number.isFinite(settings?.autoplayBlockThreshold)
? Math.max(0, Math.floor(settings.autoplayBlockThreshold))
: defaults.autoplayBlockThreshold;
const muteHide = Number.isFinite(settings?.trustedMuteHideThreshold)
? Math.max(0, Math.floor(settings.trustedMuteHideThreshold))
: defaults.trustedMuteHideThreshold;
const spamHide = Number.isFinite(settings?.trustedSpamHideThreshold)
? Math.max(0, Math.floor(settings.trustedSpamHideThreshold))
: defaults.trustedSpamHideThreshold;
return {
blurThreshold: blur,
autoplayBlockThreshold: autoplay,
trustedMuteHideThreshold: muteHide,
trustedSpamHideThreshold: spamHide,
};
}
readModerationInputs() {
const defaults = this.getModerationSettingsDefaults();
const parse = (input, fallback) => {
if (!(input instanceof HTMLInputElement)) {
return { value: fallback, override: null, valid: true };
}
const raw = typeof input.value === "string" ? input.value.trim() : "";
if (!raw) {
return { value: fallback, override: null, valid: true };
}
const numeric = Number(raw);
if (!Number.isFinite(numeric)) {
return { value: fallback, override: null, valid: false };
}
const sanitized = Math.max(0, Math.floor(numeric));
return { value: sanitized, override: sanitized, valid: true };
};
const blur = parse(this.moderationBlurInput, defaults.blurThreshold);
const autoplay = parse(
this.moderationAutoplayInput,
defaults.autoplayBlockThreshold,
);
const muteHide = parse(
this.moderationMuteHideInput,
defaults.trustedMuteHideThreshold,
);
const spamHide = parse(
this.moderationSpamHideInput,
defaults.trustedSpamHideThreshold,
);
const valid = blur.valid && autoplay.valid && muteHide.valid && spamHide.valid;
const values = {
blurThreshold: blur.value,
autoplayBlockThreshold: autoplay.value,
trustedMuteHideThreshold: muteHide.value,
trustedSpamHideThreshold: spamHide.value,
};
const overrides = {
blurThreshold: blur.override,
autoplayBlockThreshold: autoplay.override,
trustedMuteHideThreshold: muteHide.override,
trustedSpamHideThreshold: spamHide.override,
};
return { defaults, values, overrides, valid };
}
applyModerationSettingsControlState({ resetStatus = false } = {}) {
const result = this.readModerationInputs();
const button = this.moderationSaveButton;
if (button instanceof HTMLElement) {
const baseline = this.currentModerationSettings || this.normalizeModerationSettings();
const isDirty =
result.valid &&
(baseline.blurThreshold !== result.values.blurThreshold ||
baseline.autoplayBlockThreshold !== result.values.autoplayBlockThreshold ||
baseline.trustedMuteHideThreshold !==
result.values.trustedMuteHideThreshold ||
baseline.trustedSpamHideThreshold !==
result.values.trustedSpamHideThreshold);
button.disabled = !(result.valid && isDirty);
if (button.disabled) {
button.setAttribute("aria-disabled", "true");
} else {
button.removeAttribute("aria-disabled");
}
}
if (resetStatus) {
this.updateModerationSettingsStatus("", "info");
}
return result;
}
areTrustedHideControlsEnabled() {
if (
RUNTIME_FLAGS &&
typeof RUNTIME_FLAGS === "object" &&
RUNTIME_FLAGS.FEATURE_TRUSTED_HIDE_CONTROLS === false
) {
return false;
}
return true;
}
updateTrustedHideControlsVisibility() {
const shouldShow = this.areTrustedHideControlsEnabled();
const targets = new Set();
if (this.moderationHideControlsGroup instanceof HTMLElement) {
targets.add(this.moderationHideControlsGroup);
}
if (Array.isArray(this.moderationHideControlElements)) {
for (const element of this.moderationHideControlElements) {
if (element instanceof HTMLElement) {
targets.add(element);
}
}
}
targets.forEach((element) => {
if (!(element instanceof HTMLElement)) {
return;
}
if (shouldShow) {
element.classList.remove("hidden");
element.removeAttribute("hidden");
element.removeAttribute("aria-hidden");
} else {
element.classList.add("hidden");
element.setAttribute("hidden", "");
element.setAttribute("aria-hidden", "true");
}
});
}
updateModerationSettingsStatus(message = "", variant = "info") {
if (!(this.moderationStatusText instanceof HTMLElement)) {
return;
}
const text = typeof message === "string" ? message : "";
this.moderationStatusText.textContent = text;
if (text) {
this.moderationStatusText.dataset.variant = variant || "info";
} else if (this.moderationStatusText.dataset.variant) {
delete this.moderationStatusText.dataset.variant;
}
}
updateTrustedMuteHideHelperCopy() {
if (!(this.moderationMuteHideInput instanceof HTMLInputElement)) {
return;
}
const label = this.moderationMuteHideInput.closest("label");
if (!(label instanceof HTMLElement)) {
return;
}
const helper = label.querySelector("span.text-xs");
if (!(helper instanceof HTMLElement)) {
return;
}
helper.textContent = TRUSTED_MUTE_HIDE_HELPER_TEXT;
}
getModerationOverrideEntries() {
if (typeof this.mainController.services.getModerationOverrides !== "function") {
return [];
}
try {
const entries = this.mainController.services.getModerationOverrides();
return Array.isArray(entries) ? entries : [];
} catch (error) {
devLogger.info(
"[profileModal] moderation overrides fallback used",
error,
);
return [];
}
}
normalizeModerationOverrideEntries(entries = []) {
const normalized = [];
const seen = new Set();
entries.forEach((entry) => {
if (!entry || typeof entry !== "object") {
return;
}
const eventId =
typeof entry.eventId === "string"
? entry.eventId.trim().toLowerCase()
: "";
if (!eventId) {
return;
}
const author =
typeof entry.authorPubkey === "string"
? entry.authorPubkey.trim()
: "";
const normalizedAuthor = author ? this.mainController.normalizeHexPubkey(author) || author : "";
const key = `${normalizedAuthor || ""}:${eventId}`;
if (seen.has(key)) {
return;
}
seen.add(key);
normalized.push({
eventId,
authorPubkey: normalizedAuthor || "",
updatedAt: Number.isFinite(entry.updatedAt)
? Math.floor(entry.updatedAt)
: 0,
});
});
normalized.sort((a, b) => (b.updatedAt || 0) - (a.updatedAt || 0));
return normalized;
}
formatModerationOverrideTimestamp(updatedAt) {
const numeric = Number(updatedAt);
if (!Number.isFinite(numeric) || numeric <= 0) {
return { display: "", iso: "" };
}
try {
const date = new Date(numeric);
return {
display: date.toLocaleString(undefined, {
year: "numeric",
month: "short",
day: "numeric",
hour: "numeric",
minute: "2-digit",
}),
iso: date.toISOString(),
};
} catch (error) {
return { display: "", iso: "" };
}
}
async handleModerationOverrideReset(entry) {
if (!entry || typeof entry !== "object") {
return false;
}
if (typeof this.mainController.services.clearModerationOverride !== "function") {
return false;
}
try {
await this.mainController.services.clearModerationOverride({
eventId: entry.eventId,
authorPubkey: entry.authorPubkey,
});
this.refreshModerationOverridesUi();
this.mainController.showSuccess("Moderation override reset.");
return true;
} catch (error) {
this.mainController.showError("Unable to reset this moderation override.");
return false;
}
}
refreshModerationOverridesUi() {
if (
!(this.moderationOverridesList instanceof HTMLElement) ||
!(this.moderationOverridesEmpty instanceof HTMLElement)
) {
return;
}
const entries = this.normalizeModerationOverrideEntries(
this.getModerationOverrideEntries(),
);
this.moderationOverridesList.textContent = "";
if (!entries.length) {
this.moderationOverridesEmpty.classList.remove("hidden");
this.moderationOverridesList.classList.add("hidden");
return;
}
this.moderationOverridesEmpty.classList.add("hidden");
this.moderationOverridesList.classList.remove("hidden");
const entriesNeedingFetch = new Set();
entries.forEach((entry) => {
const item = document.createElement("li");
item.className = "card space-y-2 p-4";
const row = document.createElement("div");
row.className = "profile-account-card__row";
const authorKey = entry.authorPubkey;
let profileSummary = null;
if (authorKey) {
const cacheEntry = this.mainController.services.getProfileCacheEntry(authorKey);
if (!cacheEntry) {
entriesNeedingFetch.add(authorKey);
}
}
const summaryData = this.mainController.dmController.resolveProfileSummaryForPubkey(authorKey);
profileSummary = this.mainController.dmController.createCompactProfileSummary(summaryData);
const actions = document.createElement("div");
actions.className = "profile-account-card__actions";
const resetButton = this.mainController.createRemoveButton({
label: "Reset",
onRemove: () => this.handleModerationOverrideReset(entry),
});
if (resetButton) {
actions.appendChild(resetButton);
}
if (profileSummary) {
row.appendChild(profileSummary);
}
if (actions.childElementCount > 0) {
row.appendChild(actions);
}
const meta = document.createElement("div");
meta.className = "flex flex-wrap items-center gap-3 text-2xs text-muted";
const contentId = document.createElement("span");
contentId.className = "font-mono text-2xs text-muted";
const shortId =
typeof this.mainController.truncateMiddle === "function"
? this.mainController.truncateMiddle(entry.eventId, 16)
: entry.eventId;
contentId.textContent = `Content ${shortId}`;
contentId.title = entry.eventId;
meta.appendChild(contentId);
const timestamp = this.formatModerationOverrideTimestamp(entry.updatedAt);
if (timestamp.display) {
const time = document.createElement("time");
time.className = "text-2xs text-muted";
time.dateTime = timestamp.iso;
time.textContent = `Updated ${timestamp.display}`;
meta.appendChild(time);
}
item.appendChild(row);
item.appendChild(meta);
this.moderationOverridesList.appendChild(item);
});
if (
entriesNeedingFetch.size &&
typeof this.mainController.services.batchFetchProfiles === "function"
) {
this.mainController.services.batchFetchProfiles(entriesNeedingFetch);
}
}
refreshModerationSettingsUi() {
const service = this.getModerationSettingsService();
if (!service) {
this.moderationSettingsDefaults = createInternalDefaultModerationSettings();
this.currentModerationSettings = createInternalDefaultModerationSettings();
this.updateTrustedHideControlsVisibility();
this.updateModerationTrustStats();
this.refreshModerationOverridesUi();
this.applyModerationSettingsControlState({ resetStatus: true });
return;
}
let active = null;
if (typeof service.getActiveModerationSettings === "function") {
try {
active = service.getActiveModerationSettings();
} catch (error) {
devLogger.info("[profileModal] moderation settings fallback used", error);
}
}
const defaults = this.getModerationSettingsDefaults();
this.moderationSettingsDefaults = defaults;
const normalized = this.normalizeModerationSettings(active);
this.currentModerationSettings = normalized;
if (this.moderationBlurInput instanceof HTMLInputElement) {
this.moderationBlurInput.value = String(normalized.blurThreshold);
}
if (this.moderationAutoplayInput instanceof HTMLInputElement) {
this.moderationAutoplayInput.value = String(
normalized.autoplayBlockThreshold,
);
}
if (this.moderationMuteHideInput instanceof HTMLInputElement) {
this.moderationMuteHideInput.value = String(
normalized.trustedMuteHideThreshold,
);
}
if (this.moderationSpamHideInput instanceof HTMLInputElement) {
this.moderationSpamHideInput.value = String(
normalized.trustedSpamHideThreshold,
);
}
this.updateTrustedHideControlsVisibility();
this.updateModerationTrustStats();
this.refreshModerationOverridesUi();
this.applyModerationSettingsControlState({ resetStatus: true });
}
getModerationTrustStats() {
const summary = {
trustedContactsCount: 0,
trustedMuteContributors: 0,
trustedReportContributors: 0,
trustedSeedOnly: false,
};
const service = this.mainController.moderationService;
if (!service) {
return summary;
}
if (typeof service.isTrustedSeedOnly === "function") {
summary.trustedSeedOnly = service.isTrustedSeedOnly();
} else if (typeof service.trustedSeedOnly === "boolean") {
summary.trustedSeedOnly = service.trustedSeedOnly;
}
const trustedContacts =
service.trustedContacts instanceof Set
? service.trustedContacts
: Array.isArray(service.trustedContacts)
? new Set(service.trustedContacts)
: new Set();
summary.trustedContactsCount = trustedContacts.size;
const adminSnapshot =
typeof service.getAdminListSnapshot === "function"
? service.getAdminListSnapshot()
: null;
const resolveStatus = (candidate) => {
if (typeof service.getAccessControlStatus === "function") {
return service.getAccessControlStatus(candidate, adminSnapshot);
}
return {
hex: this.mainController.normalizeHexPubkey(candidate),
whitelisted: false,
blacklisted: false,
};
};
const isBlocked = (pubkey) =>
typeof service.isPubkeyBlockedByViewer === "function"
? service.isPubkeyBlockedByViewer(pubkey)
: false;
const isTrustedCandidate = (status) => {
if (!status || !status.hex) {
return false;
}
if (status.blacklisted) {
return false;
}
if (isBlocked(status.hex)) {
return false;
}
return Boolean(status.whitelisted || trustedContacts.has(status.hex));
};
if (service.trustedMuteLists instanceof Map) {
const trustedMuteOwners = new Set();
for (const owner of service.trustedMuteLists.keys()) {
const status = resolveStatus(owner);
if (isTrustedCandidate(status)) {
trustedMuteOwners.add(status.hex);
}
}
summary.trustedMuteContributors = trustedMuteOwners.size;
}
if (service.reportEvents instanceof Map) {
const trustedReporters = new Set();
for (const eventReports of service.reportEvents.values()) {
if (!(eventReports instanceof Map)) {
continue;
}
for (const reporter of eventReports.keys()) {
const status = resolveStatus(reporter);
if (!isTrustedCandidate(status)) {
continue;
}
trustedReporters.add(status.hex);
}
}
summary.trustedReportContributors = trustedReporters.size;
}
return summary;
}
updateModerationTrustStats() {
if (
!(this.moderationTrustedContactsCount instanceof HTMLElement) &&
!(this.moderationTrustedMuteCount instanceof HTMLElement) &&
!(this.moderationTrustedReportCount instanceof HTMLElement) &&
!(this.moderationSeedOnlyIndicator instanceof HTMLElement)
) {
return;
}
const summary = this.getModerationTrustStats();
if (this.moderationTrustedContactsCount instanceof HTMLElement) {
this.moderationTrustedContactsCount.textContent = String(
summary.trustedContactsCount,
);
}
if (this.moderationTrustedMuteCount instanceof HTMLElement) {
this.moderationTrustedMuteCount.textContent = String(
summary.trustedMuteContributors,
);
}
if (this.moderationTrustedReportCount instanceof HTMLElement) {
this.moderationTrustedReportCount.textContent = String(
summary.trustedReportContributors,
);
}
if (this.moderationSeedOnlyIndicator instanceof HTMLElement) {
this.moderationSeedOnlyIndicator.hidden = !summary.trustedSeedOnly;
}
}
async handleModerationSettingsSave() {
const service = this.getModerationSettingsService();
const context = {
success: false,
reason: null,
error: null,
settings: null,
};
if (!service) {
return context;
}
const inputState = this.applyModerationSettingsControlState();
if (!inputState.valid) {
const message =
"Enter non-negative whole numbers for moderation thresholds.";
this.updateModerationSettingsStatus(message, "error");
this.mainController.showError(message);
context.reason = "invalid-input";
context.error = message;
return context;
}
const payload = {};
if (Object.prototype.hasOwnProperty.call(inputState.overrides, "blurThreshold")) {
payload.blurThreshold = inputState.overrides.blurThreshold;
}
if (
Object.prototype.hasOwnProperty.call(
inputState.overrides,
"autoplayBlockThreshold",
)
) {
payload.autoplayBlockThreshold = inputState.overrides.autoplayBlockThreshold;
}
if (
Object.prototype.hasOwnProperty.call(
inputState.overrides,
"trustedMuteHideThreshold",
)
) {
payload.trustedMuteHideThreshold =
inputState.overrides.trustedMuteHideThreshold;
}
if (
Object.prototype.hasOwnProperty.call(
inputState.overrides,
"trustedSpamHideThreshold",
)
) {
payload.trustedSpamHideThreshold =
inputState.overrides.trustedSpamHideThreshold;
}
try {
const updated =
typeof service.updateModerationSettings === "function"
? await service.updateModerationSettings(payload)
: inputState.values;
const normalized = this.normalizeModerationSettings(updated);
this.currentModerationSettings = normalized;
if (this.moderationBlurInput instanceof HTMLInputElement) {
this.moderationBlurInput.value = String(normalized.blurThreshold);
}
if (this.moderationAutoplayInput instanceof HTMLInputElement) {
this.moderationAutoplayInput.value = String(
normalized.autoplayBlockThreshold,
);
}
if (this.moderationMuteHideInput instanceof HTMLInputElement) {
this.moderationMuteHideInput.value = String(
normalized.trustedMuteHideThreshold,
);
}
if (this.moderationSpamHideInput instanceof HTMLInputElement) {
this.moderationSpamHideInput.value = String(
normalized.trustedSpamHideThreshold,
);
}
this.applyModerationSettingsControlState();
this.updateModerationSettingsStatus("Moderation settings saved.", "success");
this.mainController.showSuccess("Moderation settings saved.");
context.success = true;
context.reason = "saved";
context.settings = normalized;
this.mainController.callbacks.onModerationSettingsChange({
settings: normalized,
controller: this.mainController,
reason: "saved",
});
} catch (error) {
const fallbackMessage = "Failed to update moderation settings.";
const detail =
error && typeof error.message === "string" && error.message.trim()
? error.message.trim()
: fallbackMessage;
this.updateModerationSettingsStatus(detail, "error");
this.mainController.showError(detail);
context.error = detail;
context.reason = error?.code || "service-error";
}
return context;
}
async handleModerationSettingsReset() {
const service = this.getModerationSettingsService();
const context = {
success: false,
reason: null,
error: null,
settings: null,
};
if (!service) {
return context;
}
try {
const updated =
typeof service.resetModerationSettings === "function"
? await service.resetModerationSettings()
: createInternalDefaultModerationSettings();
const normalized = this.normalizeModerationSettings(updated);
this.currentModerationSettings = normalized;
if (this.moderationBlurInput instanceof HTMLInputElement) {
this.moderationBlurInput.value = String(normalized.blurThreshold);
}
if (this.moderationAutoplayInput instanceof HTMLInputElement) {
this.moderationAutoplayInput.value = String(
normalized.autoplayBlockThreshold,
);
}
if (this.moderationMuteHideInput instanceof HTMLInputElement) {
this.moderationMuteHideInput.value = String(
normalized.trustedMuteHideThreshold,
);
}
if (this.moderationSpamHideInput instanceof HTMLInputElement) {
this.moderationSpamHideInput.value = String(
normalized.trustedSpamHideThreshold,
);
}
this.updateTrustedHideControlsVisibility();
this.applyModerationSettingsControlState({ resetStatus: true });
this.updateModerationSettingsStatus(
"Moderation defaults restored.",
"success",
);
this.mainController.showSuccess("Moderation defaults restored.");
context.success = true;
context.reason = "reset";
context.settings = normalized;
this.mainController.callbacks.onModerationSettingsChange({
settings: normalized,
controller: this.mainController,
reason: "reset",
});
} catch (error) {
const fallbackMessage = "Failed to restore moderation defaults.";
const detail =
error && typeof error.message === "string" && error.message.trim()
? error.message.trim()
: fallbackMessage;
this.updateModerationSettingsStatus(detail, "error");
this.mainController.showError(detail);
context.error = detail;
context.reason = error?.code || "service-error";
}
return context;
}
}