mirror of
https://github.com/PR0M3TH3AN/bitvid.git
synced 2026-11-06 12:52:26 +00:00
[profile] Refresh the profile modal on account switch (no manual reload)
Switching accounts with the profile modal open (e.g. NIP-07 -> a saved nsec) left the PREVIOUS account's DMs, subscriptions, hashtags and lists on screen until the user refreshed. Two problems: 1. handleAuthLogin ran its identity-reset steps unguarded, so if any one threw (renderSavedProfiles, a warm-list call, etc.) the steps after it were skipped — including handleActiveDmIdentityChanged, which is what clears the previous account's conversation. Result: stale DMs after a switch. Each reset step now runs in isolation via runIdentityResetStep, and the DM reset runs first. 2. The switch-with-modal-open refresh called populateOpenProfilePanels(), which only paints from current service state and omits lazily-loaded panes (messages, watch history). It also fired before the new identity's lists had loaded, with no reliable refresh afterward. Replaced with refreshOpenPanesForActiveIdentity(), which re-runs the ACTIVE pane through selectPane() — the same load+populate path as opening it fresh — so DMs reload, subscriptions/hashtags refetch, etc. It runs immediately, again once profile/wallet data settles, and once more from the auth coordinator when the new identity's blocks/subscriptions/hashtags finish loading (backfill for slow sync). Non-active panes stay lazy and repopulate on select. Per-user list caches (blocks/subscriptions/hashtags are already keyed per pubkey) mean the active pane shows cached data instantly on switch, then updates when the new identity's fetch settles — no manual refresh. Tests (tests/profile-modal-controller.test.mjs +3): a throwing reset step is isolated; a throwing DM reset no longer skips the saved-profiles reset; and refreshOpenPanesForActiveIdentity re-runs the active pane only when the modal is open. Existing profile-modal / switch-coordinator / stored-nsec-switch suites green. Co-Authored-By: Claude Opus 4.8 <[email protected]>
This commit is contained in:
1 parent
b8ac09ef7e
commit
87af650b38
3 files changed
+196
-27
No files matched your search
@@ -1258,6 +1258,39 @@ export class ProfileModalController {
|
||||
void this.refreshDmRelayPreferences({ force: true });
|
||||
}
|
||||
|
||||
// Run one identity-switch reset step in isolation. A failure in a single step
|
||||
// must not skip the ones after it — notably the DM reset that clears the
|
||||
// previous account's conversation on a switch.
|
||||
runIdentityResetStep(label, fn) {
|
||||
try {
|
||||
fn();
|
||||
} catch (error) {
|
||||
userLogger.warn(
|
||||
`[profileModal] Identity reset step "${label}" failed:`,
|
||||
error,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Refresh the profile modal for the ACTIVE identity while it is open (e.g. right
|
||||
// after switching accounts). Re-runs the current pane's own lazy load+populate —
|
||||
// the SAME path as opening the pane fresh — so DMs reload, subscriptions and
|
||||
// hashtags refetch for the new account, blocked/relays/storage repopulate, etc.
|
||||
// Non-active panes stay lazy and repopulate when the user selects them.
|
||||
refreshOpenPanesForActiveIdentity() {
|
||||
if (!this.isProfileModalOpen()) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
this.selectPane(this.getActivePane());
|
||||
} catch (error) {
|
||||
userLogger.warn(
|
||||
"[profileModal] Failed to refresh the active pane for the current identity:",
|
||||
error,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
setMessagesUnreadIndicator(visible) {
|
||||
if (!(this.dmController.profileMessagesUnreadDot instanceof HTMLElement)) {
|
||||
return;
|
||||
@@ -5223,46 +5256,57 @@ export class ProfileModalController {
|
||||
this.setActivePubkey(activePubkey);
|
||||
}
|
||||
|
||||
// Reset identity-scoped UI state up front, each step isolated so one throwing
|
||||
// can't skip the others. Regression this guards: switching from NIP-07 to a
|
||||
// saved nsec (modal open) left the PREVIOUS account's DMs, subscriptions and
|
||||
// hashtags on screen because a sibling call threw before the DM reset ran.
|
||||
// DM identity setup also feeds the background unread indicator + DM
|
||||
// subscription, so it stays eager.
|
||||
this.runIdentityResetStep("dm-identity", () =>
|
||||
this.handleActiveDmIdentityChanged(activePubkey),
|
||||
);
|
||||
this.runIdentityResetStep("saved-profiles", () => this.renderSavedProfiles());
|
||||
|
||||
const walletPromise = this.hydrateActiveWalletSettings(activePubkey);
|
||||
const adminPromise = this.refreshAdminPaneState().catch((error) => {
|
||||
userLogger.warn("Failed to refresh admin pane after login:", error);
|
||||
});
|
||||
|
||||
this.renderSavedProfiles();
|
||||
|
||||
// Warm blocks + subscriptions, but go through ensureLoaded() (not the raw
|
||||
// loadBlocks/loadSubscriptions) so these COALESCE with the auth coordinator's
|
||||
// login-time list loads instead of issuing a second, ungated fetch. The
|
||||
// direct calls here each kicked off an independent relay round-trip,
|
||||
// doubling the cold-login kind-30000/10000 REQ burst (KNOWN_BUGS #0).
|
||||
if (activePubkey) {
|
||||
if (this.services.userBlocks?.ensureLoaded) {
|
||||
this.services.userBlocks.ensureLoaded(activePubkey).catch(noop);
|
||||
} else if (this.services.userBlocks?.loadBlocks) {
|
||||
this.services.userBlocks.loadBlocks(activePubkey).catch(noop);
|
||||
}
|
||||
if (this.subscriptionsService?.ensureLoaded) {
|
||||
this.subscriptionsService
|
||||
.ensureLoaded(activePubkey, { allowPermissionPrompt: false })
|
||||
.catch(noop);
|
||||
} else if (this.subscriptionsService?.loadSubscriptions) {
|
||||
this.subscriptionsService
|
||||
.loadSubscriptions(activePubkey, { allowPermissionPrompt: false })
|
||||
.catch(noop);
|
||||
}
|
||||
this.runIdentityResetStep("warm-lists", () => {
|
||||
if (this.services.userBlocks?.ensureLoaded) {
|
||||
this.services.userBlocks.ensureLoaded(activePubkey).catch(noop);
|
||||
} else if (this.services.userBlocks?.loadBlocks) {
|
||||
this.services.userBlocks.loadBlocks(activePubkey).catch(noop);
|
||||
}
|
||||
if (this.subscriptionsService?.ensureLoaded) {
|
||||
this.subscriptionsService
|
||||
.ensureLoaded(activePubkey, { allowPermissionPrompt: false })
|
||||
.catch(noop);
|
||||
} else if (this.subscriptionsService?.loadSubscriptions) {
|
||||
this.subscriptionsService
|
||||
.loadSubscriptions(activePubkey, { allowPermissionPrompt: false })
|
||||
.catch(noop);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// DM identity is cheap state setup needed for the background unread
|
||||
// indicator + DM subscription, so it stays eager.
|
||||
this.handleActiveDmIdentityChanged(activePubkey);
|
||||
|
||||
// Profile-PANEL rendering is deferred: each pane lazily populates when it's
|
||||
// shown (selectPane), so eagerly rendering all of them at login paints into
|
||||
// CLOSED panels — friends-list avatars, subscriptions, blocks, storage,
|
||||
// relays — and jams the main thread right after login (the ~10-15s "profile
|
||||
// panel won't open / site frozen" report). Only do the eager pass when the
|
||||
// modal is already open (e.g. switching accounts with it visible);
|
||||
// otherwise open()/selectPane handles it on demand.
|
||||
// panel won't open / site frozen" report). Only refresh when the modal is
|
||||
// already open (e.g. switching accounts with it visible); otherwise
|
||||
// open()/selectPane handles it on demand. On a switch we re-run the ACTIVE
|
||||
// pane's own load+populate (refreshOpenPanesForActiveIdentity) so the pane the
|
||||
// user is viewing reloads for the new identity, then again once profile/wallet
|
||||
// data settles (list panes also backfill via the auth coordinator once the new
|
||||
// identity's lists finish loading).
|
||||
const settleLoginData = Promise.all([
|
||||
walletPromise,
|
||||
adminPromise,
|
||||
@@ -5270,12 +5314,10 @@ export class ProfileModalController {
|
||||
]);
|
||||
|
||||
if (this.isProfileModalOpen()) {
|
||||
this.populateOpenProfilePanels();
|
||||
this.refreshOpenPanesForActiveIdentity();
|
||||
settleLoginData
|
||||
.then(() => {
|
||||
if (this.isProfileModalOpen()) {
|
||||
this.populateOpenProfilePanels();
|
||||
}
|
||||
this.refreshOpenPanesForActiveIdentity();
|
||||
})
|
||||
.catch((error) => {
|
||||
userLogger.warn(
|
||||
|
||||
Reference in new issue
Block a user