mirror of
https://github.com/PR0M3TH3AN/bitvid.git
synced 2026-10-27 07:52:24 +00:00
Fix video modal stacking and playback readiness
This commit is contained in:
1 parent
6ecbef207f
commit
ad0713b4cb
6 files changed
+1024
-679
No files matched your search
@@ -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
File diff suppressed because it is too large.
Load diff
+16
-1
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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.");
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user