diff --git a/meshchatx/src/frontend/components/App.vue b/meshchatx/src/frontend/components/App.vue index 9f82239..0b6cfee 100644 --- a/meshchatx/src/frontend/components/App.vue +++ b/meshchatx/src/frontend/components/App.vue @@ -704,6 +704,9 @@ export default { wsReconnectedBanner: false, wsDisconnectTickTimer: null, wsReconnectedHideTimer: null, + + identitySwitchDedupeHash: null, + identitySwitchDedupeAt: 0, }; }, computed: { @@ -842,6 +845,7 @@ export default { WebSocketConnection.on("disconnected", this.onWsShellDisconnected); WebSocketConnection.on("connected", this.onWsShellConnected); GlobalEmitter.on("identity-switching-start", this.onIdentitySwitchingStartShell); + GlobalEmitter.on("identity-switched-apply", this.onIdentitySwitchedApplyShell); GlobalEmitter.on("sync-propagation-node", this.onSyncPropagationNodeShell); GlobalEmitter.on("config-updated", this.onConfigUpdatedExternally); GlobalEmitter.on("keyboard-shortcut", this.onKeyboardShortcutShell); @@ -878,6 +882,7 @@ export default { WebSocketConnection.off("disconnected", this.onWsShellDisconnected); WebSocketConnection.off("connected", this.onWsShellConnected); GlobalEmitter.off("identity-switching-start", this.onIdentitySwitchingStartShell); + GlobalEmitter.off("identity-switched-apply", this.onIdentitySwitchedApplyShell); GlobalEmitter.off("sync-propagation-node", this.onSyncPropagationNodeShell); GlobalEmitter.off("config-updated", this.onConfigUpdatedExternally); GlobalEmitter.off("keyboard-shortcut", this.onKeyboardShortcutShell); @@ -990,6 +995,33 @@ export default { } }, 45000); }, + onIdentitySwitchedApplyShell(payload) { + this.applyIdentitySwitched(payload).catch(() => {}); + }, + async applyIdentitySwitched(json) { + const hash = json?.identity_hash; + if (hash == null || hash === "") { + return; + } + const now = Date.now(); + if (this.identitySwitchDedupeHash === hash && now - this.identitySwitchDedupeAt < 10000) { + return; + } + this.identitySwitchDedupeHash = hash; + this.identitySwitchDedupeAt = now; + + ToastUtils.success(this.$t("identities.switched")); + + GlobalState.unreadConversationsCount = 0; + + await this.getConfig(); + await this.updateRingtonePlayer(); + await this.getAppInfo(); + + this.isSwitchingIdentity = false; + + GlobalEmitter.emit("identity-switched", json); + }, onSyncPropagationNodeShell() { this.syncPropagationNode(); }, @@ -1177,21 +1209,7 @@ export default { break; } case "identity_switched": { - ToastUtils.success(`Switched to identity: ${json.display_name}`); - - // reset global state - GlobalState.unreadConversationsCount = 0; - - // update local state - await this.getConfig(); - await this.updateRingtonePlayer(); - await this.getAppInfo(); - - // hide loading overlay - this.isSwitchingIdentity = false; - - // if we are on identities page, we might want to refresh it - GlobalEmitter.emit("identity-switched", json); + await this.applyIdentitySwitched(json); break; } case "rncp.receive.completed": { diff --git a/meshchatx/src/frontend/components/settings/IdentitiesPage.vue b/meshchatx/src/frontend/components/settings/IdentitiesPage.vue index c016553..9ac1a9a 100644 --- a/meshchatx/src/frontend/components/settings/IdentitiesPage.vue +++ b/meshchatx/src/frontend/components/settings/IdentitiesPage.vue @@ -564,8 +564,10 @@ export default { }); if (response.data.hotswapped) { - // Success is handled by GlobalEmitter "identity-switched" which we listen to - ToastUtils.success(this.$t("identities.switched") || "Identity switched successfully"); + GlobalEmitter.emit("identity-switched-apply", { + identity_hash: response.data.identity_hash ?? identity.hash, + display_name: response.data.display_name ?? identity.display_name ?? "", + }); } else { ToastUtils.info(this.$t("identities.switch_scheduled")); setTimeout(() => { diff --git a/tests/frontend/AppIdentitySwitch.test.js b/tests/frontend/AppIdentitySwitch.test.js index b5ecb24..39d0f59 100644 --- a/tests/frontend/AppIdentitySwitch.test.js +++ b/tests/frontend/AppIdentitySwitch.test.js @@ -23,8 +23,8 @@ vi.mock("../../meshchatx/src/frontend/js/GlobalEmitter", () => ({ function makeCtx() { return { - _identitySwitchDedupeHash: null, - _identitySwitchDedupeAt: 0, + identitySwitchDedupeHash: null, + identitySwitchDedupeAt: 0, isSwitchingIdentity: true, getConfig: vi.fn().mockResolvedValue(undefined), updateRingtonePlayer: vi.fn().mockResolvedValue(undefined),