Fix video modal stacking and playback readiness

This commit is contained in:
thePR0M3TH3AN committed 2025-10-07 21:51:12 -04:00
1 parent 6ecbef207f
commit ad0713b4cb
6 files changed
+1024 -679

No files matched your search

+243 -325
View File
@@ -291,11 +291,7 @@ class Application {
this.profileAvatar = document.getElementById("profileAvatar") || null;
// Profile modal controller state
this.activeProfilePane = null;
this.isWalletPaneBusy = false;
this.profileModalCachedSelection = null;
this.profileModalController = null;
this.profileSwitcherSelectionPubkey = null;
this.profileController = null;
this.currentUserNpub = null;
// Upload modal component
@@ -388,6 +384,118 @@ class Application {
this.boundRevertConfirmHandler
);
try {
const profileModalContainer = document.getElementById("modalContainer") || null;
if (profileModalContainer) {
const profileModalServices = {
normalizeHexPubkey: (value) => this.normalizeHexPubkey(value),
safeEncodeNpub: (pubkey) => this.safeEncodeNpub(pubkey),
safeDecodeNpub: (npub) => this.safeDecodeNpub(npub),
truncateMiddle: (value, maxLength) => truncateMiddle(value, maxLength),
getProfileCacheEntry: (pubkey) => this.getProfileCacheEntry(pubkey),
batchFetchProfiles: (authorSet) => this.batchFetchProfiles(authorSet),
switchProfile: (pubkey) => this.authService.switchProfile(pubkey),
removeSavedProfile: (pubkey) =>
this.authService.removeSavedProfile(pubkey),
relayManager,
userBlocks,
nostrClient,
accessControl,
getCurrentUserNpub: () => this.getCurrentUserNpub(),
getActiveNwcSettings: () => this.getActiveNwcSettings(),
updateActiveNwcSettings: (partial) =>
this.updateActiveNwcSettings(partial),
hydrateNwcSettingsForPubkey: (pubkey) =>
this.hydrateNwcSettingsForPubkey(pubkey),
createDefaultNwcSettings: () => createDefaultNwcSettings(),
ensureWallet: (options) => this.ensureWallet(options),
loadVideos: (forceFetch, context) =>
this.loadVideos(forceFetch, context),
onVideosShouldRefresh: (context) =>
this.onVideosShouldRefresh(context),
describeAdminError: (code) => this.describeAdminError(code),
describeNotificationError: (code) =>
this.describeNotificationError(code),
onAccessControlUpdated: () => this.onAccessControlUpdated(),
persistSavedProfiles: (options) => persistSavedProfiles(options),
watchHistoryService,
authService: this.authService,
log: (...args) => this.log(...args),
closeAllMoreMenus: () => this.closeAllMoreMenus(),
};
const profileModalState = {
getSavedProfiles: () => getSavedProfiles(),
setSavedProfiles: (profiles, options) =>
setSavedProfiles(Array.isArray(profiles) ? profiles : [], options),
persistSavedProfiles: (options) => persistSavedProfiles(options),
getActivePubkey: () => getActiveProfilePubkey(),
setActivePubkey: (pubkey, options) => {
const normalized =
typeof pubkey === "string" && pubkey.trim()
? pubkey.trim()
: null;
setStoredActiveProfilePubkey(normalized, options);
return getActiveProfilePubkey();
},
};
const profileModalCallbacks = {
onClose: () => this.handleProfileModalClosed(),
onLogout: async () => this.authService.logout(),
onChannelLink: (element) => this.handleProfileChannelLink(element),
onAddAccount: (controller) => this.handleAddProfile(controller),
onRequestSwitchProfile: (payload) =>
this.handleProfileSwitchRequest(payload),
onRelayOperation: (payload) =>
this.handleProfileRelayOperation(payload),
onRelayModeToggle: (payload) =>
this.handleProfileRelayModeToggle(payload),
onRelayRestore: (payload) =>
this.handleProfileRelayRestore(payload),
onBlocklistMutation: (payload) =>
this.handleProfileBlocklistMutation(payload),
onWalletPersist: (payload) =>
this.handleProfileWalletPersist(payload),
onWalletTestRequest: (payload) =>
this.handleProfileWalletTest(payload),
onWalletDisconnectRequest: (payload) =>
this.handleProfileWalletDisconnect(payload),
onAdminMutation: (payload) =>
this.handleProfileAdminMutation(payload),
onAdminNotifyError: (payload) =>
this.handleProfileAdminNotifyError(payload),
onHistoryReady: (payload) =>
this.handleProfileHistoryEvent(payload),
};
this.profileController = new ProfileModalController({
modalContainer: profileModalContainer,
removeTrackingScripts,
createWatchHistoryRenderer,
setGlobalModalState,
showError: (message) => this.showError(message),
showSuccess: (message) => this.showSuccess(message),
showStatus: (message) => this.showStatus(message),
constants: {
MAX_WALLET_DEFAULT_ZAP,
ADMIN_SUPER_NPUB,
ADMIN_DM_IMAGE_URL,
BITVID_WEBSITE_URL,
},
services: profileModalServices,
state: profileModalState,
callbacks: profileModalCallbacks,
});
} else {
console.warn(
"[Application] Profile modal controller disabled: modal container not found.",
);
}
} catch (error) {
console.error("Failed to initialize profile modal controller:", error);
}
// Optional small inline player stats
this.status = document.getElementById("status") || null;
@@ -478,11 +586,11 @@ class Application {
this.boundVideoModalZapCommentHandler
);
this.boundVideoModalZapWalletHandler = () => {
if (!this.profileModalController) {
if (!this.profileController) {
return;
}
this.profileModalController
this.profileController
.showWalletPane()
.catch((error) => {
console.error("Failed to open wallet pane:", error);
@@ -894,7 +1002,34 @@ class Application {
this.showError(`Failed to initialize edit modal: ${error.message}`);
});
const profileModalPromise = this.initProfileModal();
const profileModalPromise = this.profileController
? this.profileController
.load()
.then(() => {
try {
this.renderSavedProfiles();
} catch (error) {
console.warn(
"[profileModal] Failed to render saved profiles after load:",
error,
);
}
try {
this.profileController.refreshWalletPaneState();
} catch (error) {
console.warn(
"[profileModal] Failed to refresh wallet pane after load:",
error,
);
}
return true;
})
.catch((error) => {
console.error("Failed to load profile modal:", error);
return false;
})
: Promise.resolve(false);
const modalBootstrapPromise = Promise.all([
videoModalPromise,
@@ -930,12 +1065,16 @@ class Application {
);
});
const adminPanePromise = this.refreshAdminPaneState().catch((error) => {
console.warn(
"Failed to update admin pane after connecting to Nostr:",
error
);
});
const adminPanePromise = this.profileController
? Promise.resolve()
.then(() => this.profileController.refreshAdminPaneState())
.catch((error) => {
console.warn(
"Failed to update admin pane after connecting to Nostr:",
error,
);
})
: Promise.resolve(null);
await Promise.all([accessControlPromise, adminPanePromise]);
@@ -1217,187 +1356,25 @@ class Application {
}
}
async initProfileModal() {
try {
const modalContainer = document.getElementById("modalContainer");
if (!modalContainer) {
throw new Error("Modal container element not found!");
}
if (!this.profileModalController) {
const profileModalServices = {
normalizeHexPubkey: (value) => this.normalizeHexPubkey(value),
safeEncodeNpub: (pubkey) => this.safeEncodeNpub(pubkey),
safeDecodeNpub: (npub) => this.safeDecodeNpub(npub),
truncateMiddle: (value, maxLength) => truncateMiddle(value, maxLength),
getProfileCacheEntry: (pubkey) => this.getProfileCacheEntry(pubkey),
batchFetchProfiles: (authorSet) => this.batchFetchProfiles(authorSet),
switchProfile: (pubkey) => this.authService.switchProfile(pubkey),
removeSavedProfile: (pubkey) =>
this.authService.removeSavedProfile(pubkey),
relayManager,
userBlocks,
nostrClient,
accessControl,
getCurrentUserNpub: () => this.getCurrentUserNpub(),
getActiveNwcSettings: () => this.getActiveNwcSettings(),
updateActiveNwcSettings: (partial) =>
this.updateActiveNwcSettings(partial),
hydrateNwcSettingsForPubkey: (pubkey) =>
this.hydrateNwcSettingsForPubkey(pubkey),
createDefaultNwcSettings: () => createDefaultNwcSettings(),
ensureWallet: (options) => this.ensureWallet(options),
loadVideos: (forceFetch) => this.loadVideos(forceFetch),
onVideosShouldRefresh: (context) => this.onVideosShouldRefresh(context),
describeAdminError: (code) => this.describeAdminError(code),
describeNotificationError: (code) =>
this.describeNotificationError(code),
onAccessControlUpdated: () => this.onAccessControlUpdated(),
persistSavedProfiles: (options) => persistSavedProfiles(options),
};
const profileModalState = {
getSavedProfiles: () => getSavedProfiles(),
setSavedProfiles: (profiles, options) =>
setSavedProfiles(Array.isArray(profiles) ? profiles : [], options),
persistSavedProfiles: (options) => persistSavedProfiles(options),
getActivePubkey: () => this.activeProfilePubkey,
setActivePubkey: (pubkey, options) => {
this.activeProfilePubkey =
typeof pubkey === "string" && pubkey.trim()
? pubkey.trim()
: null;
setStoredActiveProfilePubkey(this.activeProfilePubkey, options);
return this.activeProfilePubkey;
},
getCachedSelection: () => this.profileModalCachedSelection || null,
setCachedSelection: (value) => {
const normalized =
typeof value === "string" && value.trim()
? value.trim()
: null;
this.profileModalCachedSelection = normalized;
return this.profileModalCachedSelection;
},
getActivePane: () => {
const pane =
typeof this.activeProfilePane === "string" && this.activeProfilePane
? this.activeProfilePane
: "account";
return pane;
},
setActivePane: (pane) => {
if (pane === null) {
this.activeProfilePane = null;
return this.activeProfilePane;
}
const normalized =
typeof pane === "string" && pane.trim()
? pane.trim().toLowerCase()
: "account";
this.activeProfilePane = normalized;
return this.activeProfilePane;
},
getWalletBusy: () => Boolean(this.isWalletPaneBusy),
setWalletBusy: (flag) => {
this.isWalletPaneBusy = Boolean(flag);
return this.isWalletPaneBusy;
},
};
const profileModalCallbacks = {
onClose: () => this.handleProfileModalClosed(),
onLogout: async () => this.authService.logout(),
onChannelLink: (element) => this.handleProfileChannelLink(element),
onAddAccount: (controller) => this.handleAddProfile(controller),
onSelectPane: (pane) => {
this.activeProfilePane = pane;
},
onPaneShown: (pane) => {
this.activeProfilePane = pane;
},
onRequestSwitchProfile: (payload) =>
this.handleProfileSwitchRequest(payload),
onRelayOperation: (payload) =>
this.handleProfileRelayOperation(payload),
onRelayModeToggle: (payload) =>
this.handleProfileRelayModeToggle(payload),
onRelayRestore: (payload) =>
this.handleProfileRelayRestore(payload),
onBlocklistMutation: (payload) =>
this.handleProfileBlocklistMutation(payload),
onWalletPersist: (payload) =>
this.handleProfileWalletPersist(payload),
onWalletTestRequest: (payload) =>
this.handleProfileWalletTest(payload),
onWalletDisconnectRequest: (payload) =>
this.handleProfileWalletDisconnect(payload),
onAdminMutation: (payload) =>
this.handleProfileAdminMutation(payload),
onAdminNotifyError: (payload) =>
this.handleProfileAdminNotifyError(payload),
onHistoryReady: (payload) =>
this.handleProfileHistoryEvent(payload),
};
this.profileModalController = new ProfileModalController({
modalContainer,
removeTrackingScripts,
createWatchHistoryRenderer,
setGlobalModalState,
showError: (message) => this.showError(message),
showSuccess: (message) => this.showSuccess(message),
showStatus: (message) => this.showStatus(message),
constants: {
MAX_WALLET_DEFAULT_ZAP,
ADMIN_SUPER_NPUB,
ADMIN_DM_IMAGE_URL,
BITVID_WEBSITE_URL,
},
services: profileModalServices,
state: profileModalState,
callbacks: profileModalCallbacks,
});
}
const alreadyLoaded =
this.profileModalController.profileModal instanceof HTMLElement;
if (!alreadyLoaded) {
const loaded = await this.profileModalController.load();
if (!loaded) {
return false;
}
} else {
this.profileModalController.cacheDomReferences();
}
this.syncProfileModalState();
this.renderSavedProfiles();
return true;
} catch (error) {
console.error("initProfileModal failed:", error);
return false;
}
}
syncProfileModalState({
includeSavedProfiles = true,
includeActivePubkey = true,
} = {}) {
if (!this.profileModalController) {
if (!this.profileController) {
return;
}
if (includeSavedProfiles) {
try {
const entries = Array.isArray(this.savedProfiles)
? this.savedProfiles.slice()
: [];
this.profileModalController.setSavedProfiles(entries, {
persist: false,
persistActive: false,
});
if (typeof this.profileController.setSavedProfiles === "function") {
const entries = Array.isArray(this.savedProfiles)
? this.savedProfiles.slice()
: [];
this.profileController.setSavedProfiles(entries, {
persist: false,
persistActive: false,
});
}
} catch (error) {
console.warn(
"[profileModal] Failed to synchronize saved profiles with controller:",
@@ -1408,10 +1385,12 @@ class Application {
if (includeActivePubkey) {
try {
this.profileModalController.setActivePubkey(
this.activeProfilePubkey || null,
{ persist: false },
);
if (typeof this.profileController.setActivePubkey === "function") {
this.profileController.setActivePubkey(
this.activeProfilePubkey || null,
{ persist: false },
);
}
} catch (error) {
console.warn(
"[profileModal] Failed to synchronize active profile with controller:",
@@ -1422,44 +1401,22 @@ class Application {
}
renderSavedProfiles() {
if (!this.profileModalController) {
if (!this.profileController) {
return;
}
this.syncProfileModalState();
this.profileModalController.renderSavedProfiles();
}
selectProfilePane(name = "account") {
if (!this.profileModalController) {
return;
try {
this.profileController.renderSavedProfiles();
} catch (error) {
console.warn(
"[profileModal] Failed to render saved profiles:",
error,
);
}
this.syncProfileModalState();
this.profileModalController.selectPane(name);
}
setWalletPaneBusy(isBusy = false) {
this.isWalletPaneBusy = Boolean(isBusy);
if (!this.profileModalController) {
return;
}
this.profileModalController.setWalletPaneBusy(this.isWalletPaneBusy);
}
refreshWalletPaneState() {
if (!this.profileModalController) {
return;
}
this.syncProfileModalState({ includeSavedProfiles: false });
this.profileModalController.refreshWalletPaneState();
}
updateWalletStatus(message, variant = "info") {
this.profileModalController?.updateWalletStatus(message, variant);
}
handleProfileModalClosed() {
this.closeAllMoreMenus();
this.activeProfilePane = null;
}
handleProfileChannelLink(element) {
@@ -1471,8 +1428,8 @@ class Application {
typeof element.dataset.targetNpub === "string"
? element.dataset.targetNpub
: "";
if (this.profileModalController) {
this.profileModalController.hide();
if (this.profileController) {
this.profileController.hide();
}
if (targetNpub) {
window.location.hash = `#view=channel-profile&npub=${encodeURIComponent(
@@ -1481,74 +1438,10 @@ class Application {
}
}
populateProfileRelays(relayEntries = null) {
if (!this.profileModalController) {
return;
}
this.syncProfileModalState({ includeSavedProfiles: false });
this.profileModalController.populateProfileRelays(relayEntries);
}
populateBlockedList(blocked = null) {
if (!this.profileModalController) {
return;
}
this.syncProfileModalState({ includeSavedProfiles: false });
this.profileModalController.populateBlockedList(blocked);
}
refreshAdminPaneState() {
if (!this.profileModalController) {
return null;
}
this.syncProfileModalState();
return this.profileModalController.refreshAdminPaneState();
}
populateAdminLists() {
if (!this.profileModalController) {
return;
}
this.syncProfileModalState();
this.profileModalController.populateAdminLists();
}
renderAdminList(...args) {
if (!this.profileModalController) {
return null;
}
this.syncProfileModalState();
return this.profileModalController.renderAdminList(...args);
}
handleAdminListMutation(...args) {
if (!this.profileModalController) {
return null;
}
this.syncProfileModalState();
return this.profileModalController.handleAdminListMutation(...args);
}
handleAddModerator(...args) {
if (!this.profileModalController) {
return null;
}
this.syncProfileModalState();
return this.profileModalController.handleAddModerator(...args);
}
handleRemoveModerator(...args) {
if (!this.profileModalController) {
return null;
}
this.syncProfileModalState();
return this.profileModalController.handleRemoveModerator(...args);
}
async handleAddProfile(controller) {
const button =
(controller && controller.addAccountButton) ||
this.profileModalController?.addAccountButton ||
this.profileController?.addAccountButton ||
null;
if (!(button instanceof HTMLElement)) {
return;
@@ -1725,10 +1618,12 @@ class Application {
}
async onAccessControlUpdated() {
try {
await this.refreshAdminPaneState();
} catch (error) {
console.error("Failed to refresh admin pane after update:", error);
if (this.profileController) {
try {
await this.profileController.refreshAdminPaneState();
} catch (error) {
console.error("Failed to refresh admin pane after update:", error);
}
}
this.loadVideos(true).catch((error) => {
@@ -1765,11 +1660,11 @@ class Application {
// 2) Profile button
if (this.profileButton) {
this.profileButton.addEventListener("click", () => {
if (!this.profileModalController) {
if (!this.profileController) {
return;
}
this.profileModalController
this.profileController
.show()
.catch((error) => {
console.error("Failed to open profile modal:", error);
@@ -2706,8 +2601,8 @@ class Application {
}
updateActiveProfileUI(pubkey, profile = {}) {
if (this.profileModalController) {
this.profileModalController.handleProfileUpdated({
if (this.profileController) {
this.profileController.handleProfileUpdated({
pubkey,
profile,
});
@@ -2905,20 +2800,17 @@ class Application {
await this.hydrateNwcSettingsForPubkey(normalizedActive);
}
if (this.profileModalController) {
await this.profileModalController.handleAuthLogin(detail);
if (this.profileController) {
try {
await this.profileController.handleAuthLogin(detail);
} catch (error) {
console.error(
"Failed to process login within the profile controller:",
error,
);
}
} else {
this.renderSavedProfiles();
try {
await this.refreshAdminPaneState();
} catch (error) {
console.warn("Failed to refresh admin pane after login:", error);
}
this.populateBlockedList();
this.populateProfileRelays();
this.refreshWalletPaneState();
}
if (this.loginButton) {
@@ -2978,20 +2870,17 @@ class Application {
}
this.nwcSettings.clear();
if (this.profileModalController) {
await this.profileModalController.handleAuthLogout(detail);
if (this.profileController) {
try {
await this.profileController.handleAuthLogout(detail);
} catch (error) {
console.error(
"Failed to process logout within the profile controller:",
error,
);
}
} else {
this.renderSavedProfiles();
try {
await this.refreshAdminPaneState();
} catch (error) {
console.warn("Failed to refresh admin pane after logout:", error);
}
this.populateBlockedList();
this.populateProfileRelays();
this.refreshWalletPaneState();
}
if (this.loginButton) {
@@ -3040,8 +2929,8 @@ class Application {
}
handleProfileUpdated(detail = {}) {
if (this.profileModalController) {
this.profileModalController.handleProfileUpdated(detail);
if (this.profileController) {
this.profileController.handleProfileUpdated(detail);
} else if (Array.isArray(detail?.savedProfiles)) {
this.renderSavedProfiles();
}
@@ -3054,7 +2943,7 @@ class Application {
if (normalizedPubkey && profile) {
this.updateProfileInDOM(normalizedPubkey, profile);
if (
!this.profileModalController &&
!this.profileController &&
this.normalizeHexPubkey(this.pubkey) === normalizedPubkey
) {
this.updateActiveProfileUI(normalizedPubkey, profile);
@@ -4190,8 +4079,8 @@ class Application {
typeof settings?.nwcUri === "string" ? settings.nwcUri.trim() : "";
if (!normalizedUri) {
this.showError("Connect a Lightning wallet to send zaps.");
if (this.profileModalController) {
this.profileModalController
if (this.profileController) {
this.profileController
.showWalletPane()
.catch((error) => {
console.error("Failed to open wallet pane:", error);
@@ -6211,7 +6100,16 @@ class Application {
);
}
this.populateBlockedList();
if (this.profileController) {
try {
this.profileController.populateBlockedList();
} catch (error) {
console.warn(
"[profileModal] Failed to refresh blocked list after update:",
error,
);
}
}
await this.loadVideos();
subscriptions
.refreshActiveFeed({ reason: "user-block-update" })
@@ -7038,6 +6936,9 @@ class Application {
this.cancelPendingViewLogging();
let modalVideoEl = this.modalVideo;
if (!modalVideoEl && this.videoModal) {
modalVideoEl = this.videoModal.getVideoElement();
}
if (!modalVideoEl && this.videoModal) {
const maybeLoader = this.videoModal.load;
if (typeof maybeLoader === "function") {
@@ -7052,6 +6953,9 @@ class Application {
return { source: null, error };
}
modalVideoEl = this.modalVideo;
if (!modalVideoEl && this.videoModal) {
modalVideoEl = this.videoModal.getVideoElement();
}
}
}
@@ -7948,6 +7852,20 @@ class Application {
}
}
if (this.profileController) {
if (typeof this.profileController.destroy === "function") {
try {
this.profileController.destroy();
} catch (error) {
console.warn(
"[Application] Failed to destroy profile controller:",
error,
);
}
}
this.profileController = null;
}
this.videoList = null;
}
+660 -349
View File
File diff suppressed because it is too large. Load diff
+16 -1
View File
@@ -800,7 +800,22 @@ function sanitizeRelayList(list) {
if (!/^wss?:\/\//i.test(trimmed)) {
return;
}
const normalized = trimmed.replace(/\/+$/, "");
if (/\s/.test(trimmed)) {
return;
}
let normalized = trimmed.replace(/\/+$/, "");
try {
const parsed = new URL(trimmed);
if (!parsed.hostname) {
return;
}
const pathname = parsed.pathname.replace(/\/+$/, "");
normalized = `${parsed.protocol}//${parsed.host}${pathname}${parsed.search || ""}`;
} catch (error) {
// Ignore URL parsing failures and fall back to the trimmed string.
}
if (seen.has(normalized)) {
return;
}
+21 -2
View File
@@ -65,6 +65,11 @@ const HEX64_REGEX = /^[0-9a-f]{64}$/i;
const FALLBACK_PROFILE = {
name: "Unknown",
picture: "assets/svg/default-profile.svg",
about: "",
website: "",
banner: "",
lud16: "",
lud06: "",
};
export default class AuthService {
@@ -556,8 +561,15 @@ export default class AuthService {
try {
const data = JSON.parse(events[0].content);
profile = {
name: data.name || data.display_name || FALLBACK_PROFILE.name,
name: data.display_name || data.name || FALLBACK_PROFILE.name,
picture: data.picture || FALLBACK_PROFILE.picture,
about: typeof data.about === "string" ? data.about : FALLBACK_PROFILE.about,
website:
typeof data.website === "string" ? data.website : FALLBACK_PROFILE.website,
banner:
typeof data.banner === "string" ? data.banner : FALLBACK_PROFILE.banner,
lud16: typeof data.lud16 === "string" ? data.lud16 : FALLBACK_PROFILE.lud16,
lud06: typeof data.lud06 === "string" ? data.lud06 : FALLBACK_PROFILE.lud06,
};
} catch (error) {
this.log("[AuthService] Failed to parse profile metadata", error);
@@ -618,8 +630,15 @@ export default class AuthService {
if (newest?.content) {
const data = JSON.parse(newest.content);
const profile = {
name: data.name || data.display_name || FALLBACK_PROFILE.name,
name: data.display_name || data.name || FALLBACK_PROFILE.name,
picture: data.picture || FALLBACK_PROFILE.picture,
about: typeof data.about === "string" ? data.about : FALLBACK_PROFILE.about,
website:
typeof data.website === "string" ? data.website : FALLBACK_PROFILE.website,
banner:
typeof data.banner === "string" ? data.banner : FALLBACK_PROFILE.banner,
lud16: typeof data.lud16 === "string" ? data.lud16 : FALLBACK_PROFILE.lud16,
lud06: typeof data.lud06 === "string" ? data.lud06 : FALLBACK_PROFILE.lud06,
};
this.setProfileCacheEntry(normalized, profile, {
persist: true,
+77
View File
@@ -18,6 +18,15 @@ const URL_PROBE_TIMEOUT_RETRY_MS = 15 * 1000; // 15 seconds
const HEX64_REGEX = /^[0-9a-f]{64}$/i;
function sanitizeProfileString(value) {
if (typeof value !== "string") {
return "";
}
const trimmed = value.trim();
return trimmed;
}
let savedProfiles = [];
let activeProfilePubkey = null;
const profileCache = new Map();
@@ -444,6 +453,40 @@ export function loadProfileCacheFromStorage() {
picture: profile.picture || "assets/svg/default-profile.svg",
};
const about = sanitizeProfileString(profile.about || profile.aboutMe);
if (about) {
normalized.about = about;
}
const website = sanitizeProfileString(profile.website || profile.url);
if (website) {
normalized.website = website;
}
const banner = sanitizeProfileString(profile.banner || profile.header);
if (banner) {
normalized.banner = banner;
}
const lud16 = sanitizeProfileString(profile.lud16);
if (lud16) {
normalized.lud16 = lud16;
}
const lud06 = sanitizeProfileString(profile.lud06);
if (lud06) {
normalized.lud06 = lud06;
}
const lightningCandidates = [
sanitizeProfileString(profile.lightningAddress),
lud16,
lud06,
].filter(Boolean);
if (lightningCandidates.length) {
normalized.lightningAddress = lightningCandidates[0];
}
profileCache.set(pubkey, {
profile: normalized,
timestamp,
@@ -536,6 +579,40 @@ export function setProfileCacheEntry(pubkey, profile, { persist = true } = {}) {
picture: profile.picture || "assets/svg/default-profile.svg",
};
const about = sanitizeProfileString(profile.about || profile.aboutMe);
if (about) {
normalized.about = about;
}
const website = sanitizeProfileString(profile.website || profile.url);
if (website) {
normalized.website = website;
}
const banner = sanitizeProfileString(profile.banner || profile.header);
if (banner) {
normalized.banner = banner;
}
const lud16 = sanitizeProfileString(profile.lud16);
if (lud16) {
normalized.lud16 = lud16;
}
const lud06 = sanitizeProfileString(profile.lud06);
if (lud06) {
normalized.lud06 = lud06;
}
const lightningCandidates = [
sanitizeProfileString(profile.lightningAddress),
lud16,
lud06,
].filter(Boolean);
if (lightningCandidates.length) {
normalized.lightningAddress = lightningCandidates[0];
}
const entry = {
profile: normalized,
timestamp: Date.now(),
+7 -2
View File
@@ -151,9 +151,14 @@ export class VideoModal {
const wrapper = this.document.createElement("div");
wrapper.innerHTML = html;
this.removeTrackingScripts(wrapper);
container.appendChild(wrapper);
const playerModal = wrapper.querySelector("#playerModal");
const fragment = this.document.createDocumentFragment();
while (wrapper.firstChild) {
fragment.appendChild(wrapper.firstChild);
}
container.appendChild(fragment);
const playerModal = container.querySelector("#playerModal");
if (!playerModal) {
throw new Error("Player modal root not found in markup.");
}