From 5cfcd579873e467622c4d282ca58826e4ac435d0 Mon Sep 17 00:00:00 2001 From: Chris Nojima Date: Tue, 6 Jan 2026 21:42:36 -0500 Subject: [PATCH 01/13] WIP --- shared/constants/daemon/index.tsx | 3 --- shared/constants/platform-specific/shared.tsx | 9 +++++++++ 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/shared/constants/daemon/index.tsx b/shared/constants/daemon/index.tsx index b08dff8bedc6..1bdf5e25ac9e 100644 --- a/shared/constants/daemon/index.tsx +++ b/shared/constants/daemon/index.tsx @@ -4,7 +4,6 @@ import * as T from '../types' import * as Z from '@/util/zustand' import {storeRegistry} from '../store-registry' import {useCurrentUserState} from '../current-user' -import {useDarkModeState} from '../darkmode' import {maxHandshakeTries} from '../values' // Load accounts, this call can be slow so we attempt to continue w/o waiting if we determine we're logged in @@ -111,8 +110,6 @@ export const useDaemonState = Z.createZustand((set, get) => { wait(name, version, true) try { await get().dispatch.loadDaemonBootstrapStatus() - useDarkModeState.getState().dispatch.loadDarkPrefs() - storeRegistry.getState('chat').dispatch.loadStaticConfig() } finally { wait(name, version, false) } diff --git a/shared/constants/platform-specific/shared.tsx b/shared/constants/platform-specific/shared.tsx index ebf3c4317f29..83d24bf18db4 100644 --- a/shared/constants/platform-specific/shared.tsx +++ b/shared/constants/platform-specific/shared.tsx @@ -11,6 +11,8 @@ import {useChatState} from '../chat2' import * as ChatUtil from '../chat2/util' import {useConfigState} from '../config' import {useCurrentUserState} from '../current-user' +import {useDaemonState} from '../daemon' +import {useDarkModeState} from '../darkmode' import * as DeepLinksUtil from '../deeplinks/util' import * as DevicesUtil from '../devices/util' import * as FollowerUtil from '../followers/util' @@ -118,6 +120,13 @@ export const initSharedSubscriptions = () => { } } }) + + useDaemonState.subscribe((s, old) => { + if (s.handshakeVersion !== old.handshakeVersion) { + useDarkModeState.getState().dispatch.loadDarkPrefs() + storeRegistry.getState('chat').dispatch.loadStaticConfig() + } + }) } export const onEngineIncoming = (action: EngineGen.Actions) => { From 0bee78a2ef4a920a63f9485efb393c780f70ab20 Mon Sep 17 00:00:00 2001 From: Chris Nojima Date: Tue, 6 Jan 2026 21:49:28 -0500 Subject: [PATCH 02/13] WIP --- shared/constants/config/index.tsx | 4 ++-- shared/constants/daemon/index.tsx | 11 ++++++----- shared/constants/platform-specific/shared.tsx | 2 +- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/shared/constants/config/index.tsx b/shared/constants/config/index.tsx index 01a496551e42..1ab659205887 100644 --- a/shared/constants/config/index.tsx +++ b/shared/constants/config/index.tsx @@ -642,7 +642,7 @@ export const useConfigState = Z.createZustand((set, get) => { }, revoke: (name, wasCurrentDevice) => { if (wasCurrentDevice) { - const {configuredAccounts, defaultUsername} = get() + const {configuredAccounts, defaultUsername, loggedIn} = get() const acc = configuredAccounts.find(n => n.username !== defaultUsername) const du = acc?.username ?? '' set(s => { @@ -650,7 +650,7 @@ export const useConfigState = Z.createZustand((set, get) => { s.justRevokedSelf = name s.revokedTrigger++ }) - storeRegistry.getState('daemon').dispatch.loadDaemonAccounts() + storeRegistry.getState('daemon').dispatch.loadDaemonAccounts(configuredAccounts.length, loggedIn) } }, setAccounts: a => { diff --git a/shared/constants/daemon/index.tsx b/shared/constants/daemon/index.tsx index 1bdf5e25ac9e..9d7ccb953dfa 100644 --- a/shared/constants/daemon/index.tsx +++ b/shared/constants/daemon/index.tsx @@ -30,7 +30,7 @@ const initialStore: Store = { export interface State extends Store { dispatch: { - loadDaemonAccounts: () => void + loadDaemonAccounts: (configuredAccountsLength: number, loggedIn: boolean) => void loadDaemonBootstrapStatus: () => Promise refreshAccounts: () => Promise resetState: () => void @@ -115,15 +115,16 @@ export const useDaemonState = Z.createZustand((set, get) => { } } ignorePromise(f()) - get().dispatch.loadDaemonAccounts() + const configState = storeRegistry.getState('config') + get().dispatch.loadDaemonAccounts(configState.configuredAccounts.length, configState.loggedIn) }, daemonHandshakeDone: () => { get().dispatch.setState('done') }, - loadDaemonAccounts: () => { + loadDaemonAccounts: (configuredAccountsLength: number, loggedIn: boolean) => { const f = async () => { const version = get().handshakeVersion - if (storeRegistry.getState('config').configuredAccounts.length) { + if (configuredAccountsLength) { // bail on already loaded return } @@ -132,7 +133,7 @@ export const useDaemonState = Z.createZustand((set, get) => { const handshakeVersion = version // did we beat getBootstrapStatus? - if (!storeRegistry.getState('config').loggedIn) { + if (!loggedIn) { handshakeWait = true } diff --git a/shared/constants/platform-specific/shared.tsx b/shared/constants/platform-specific/shared.tsx index 83d24bf18db4..fb8ee1ecd280 100644 --- a/shared/constants/platform-specific/shared.tsx +++ b/shared/constants/platform-specific/shared.tsx @@ -108,7 +108,7 @@ export const initSharedSubscriptions = () => { ignorePromise(storeRegistry.getState('daemon').dispatch.loadDaemonBootstrapStatus()) storeRegistry.getState('fs').dispatch.checkKbfsDaemonRpcStatus() } - storeRegistry.getState('daemon').dispatch.loadDaemonAccounts() + storeRegistry.getState('daemon').dispatch.loadDaemonAccounts(s.configuredAccounts.length, s.loggedIn) if (!s.loggedInCausedbyStartup) { ignorePromise(storeRegistry.getState('daemon').dispatch.refreshAccounts()) } From 1adece85c718884fc45af8e56c8031b856ddf46c Mon Sep 17 00:00:00 2001 From: Chris Nojima Date: Tue, 6 Jan 2026 21:55:22 -0500 Subject: [PATCH 03/13] WIP --- shared/constants/daemon/index.tsx | 2 -- shared/constants/platform-specific/shared.tsx | 2 ++ 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/shared/constants/daemon/index.tsx b/shared/constants/daemon/index.tsx index 9d7ccb953dfa..83e4dde44e75 100644 --- a/shared/constants/daemon/index.tsx +++ b/shared/constants/daemon/index.tsx @@ -115,8 +115,6 @@ export const useDaemonState = Z.createZustand((set, get) => { } } ignorePromise(f()) - const configState = storeRegistry.getState('config') - get().dispatch.loadDaemonAccounts(configState.configuredAccounts.length, configState.loggedIn) }, daemonHandshakeDone: () => { get().dispatch.setState('done') diff --git a/shared/constants/platform-specific/shared.tsx b/shared/constants/platform-specific/shared.tsx index fb8ee1ecd280..59389489a79e 100644 --- a/shared/constants/platform-specific/shared.tsx +++ b/shared/constants/platform-specific/shared.tsx @@ -125,6 +125,8 @@ export const initSharedSubscriptions = () => { if (s.handshakeVersion !== old.handshakeVersion) { useDarkModeState.getState().dispatch.loadDarkPrefs() storeRegistry.getState('chat').dispatch.loadStaticConfig() + const configState = storeRegistry.getState('config') + s.dispatch.loadDaemonAccounts(configState.configuredAccounts.length, configState.loggedIn) } }) } From 68a72e03ac45540abe1abb5a322c57100137470d Mon Sep 17 00:00:00 2001 From: Chris Nojima Date: Wed, 7 Jan 2026 10:24:34 -0500 Subject: [PATCH 04/13] WIP --- shared/constants/daemon/index.tsx | 27 +++++++------------ shared/constants/platform-specific/shared.tsx | 23 ++++++++++++++++ 2 files changed, 32 insertions(+), 18 deletions(-) diff --git a/shared/constants/daemon/index.tsx b/shared/constants/daemon/index.tsx index 83e4dde44e75..46e2322053ba 100644 --- a/shared/constants/daemon/index.tsx +++ b/shared/constants/daemon/index.tsx @@ -3,7 +3,6 @@ import {ignorePromise} from '../utils' import * as T from '../types' import * as Z from '@/util/zustand' import {storeRegistry} from '../store-registry' -import {useCurrentUserState} from '../current-user' import {maxHandshakeTries} from '../values' // Load accounts, this call can be slow so we attempt to continue w/o waiting if we determine we're logged in @@ -11,16 +10,18 @@ import {maxHandshakeTries} from '../values' const getAccountsWaitKey = 'config.getAccounts' type Store = T.Immutable<{ + bootstrapStatus?: T.RPCGen.BootstrapStatus error?: Error - handshakeState: T.Config.DaemonHandshakeState handshakeFailedReason: string handshakeRetriesLeft: number + handshakeState: T.Config.DaemonHandshakeState + handshakeVersion: number handshakeWaiters: Map // if we ever restart handshake up this so we can ignore any waiters for old things - handshakeVersion: number }> const initialStore: Store = { + bootstrapStatus: undefined, handshakeFailedReason: '', handshakeRetriesLeft: maxHandshakeTries, handshakeState: 'starting', @@ -168,32 +169,22 @@ export const useDaemonState = Z.createZustand((set, get) => { const {wait} = get().dispatch const f = async () => { - const {setBootstrap} = useCurrentUserState.getState().dispatch - const {setDefaultUsername} = storeRegistry.getState('config').dispatch const s = await T.RPCGen.configGetBootstrapStatusRpcPromise() - const {userReacjis, deviceName, deviceID, uid, loggedIn, username} = s - setBootstrap({deviceID, deviceName, uid, username}) - if (username) { - setDefaultUsername(username) - } - if (loggedIn) { - storeRegistry.getState('config').dispatch.setUserSwitching(false) - } + set(state => { + state.bootstrapStatus = s + }) - logger.info(`[Bootstrap] loggedIn: ${loggedIn ? 1 : 0}`) - storeRegistry.getState('config').dispatch.setLoggedIn(loggedIn, false) - storeRegistry.getState('chat').dispatch.updateUserReacjis(userReacjis) + logger.info(`[Bootstrap] loggedIn: ${s.loggedIn ? 1 : 0}`) // set HTTP srv info if (s.httpSrvInfo) { logger.info(`[Bootstrap] http server: addr: ${s.httpSrvInfo.address} token: ${s.httpSrvInfo.token}`) - storeRegistry.getState('config').dispatch.setHTTPSrvInfo(s.httpSrvInfo.address, s.httpSrvInfo.token) } else { logger.info(`[Bootstrap] http server: no info given`) } // if we're logged in act like getAccounts is done already - if (loggedIn) { + if (s.loggedIn) { const {handshakeWaiters} = get() if (handshakeWaiters.get(getAccountsWaitKey)) { wait(getAccountsWaitKey, version, false) diff --git a/shared/constants/platform-specific/shared.tsx b/shared/constants/platform-specific/shared.tsx index 59389489a79e..c3551e87beb2 100644 --- a/shared/constants/platform-specific/shared.tsx +++ b/shared/constants/platform-specific/shared.tsx @@ -128,6 +128,29 @@ export const initSharedSubscriptions = () => { const configState = storeRegistry.getState('config') s.dispatch.loadDaemonAccounts(configState.configuredAccounts.length, configState.loggedIn) } + + if (s.bootstrapStatus !== old.bootstrapStatus) { + const bootstrap = s.bootstrapStatus + if (bootstrap) { + const {deviceID, deviceName, loggedIn, uid, username, userReacjis} = bootstrap + useCurrentUserState.getState().dispatch.setBootstrap({deviceID, deviceName, uid, username}) + + const configDispatch = storeRegistry.getState('config').dispatch + if (username) { + configDispatch.setDefaultUsername(username) + } + if (loggedIn) { + configDispatch.setUserSwitching(false) + } + configDispatch.setLoggedIn(loggedIn, false) + + if (bootstrap.httpSrvInfo) { + configDispatch.setHTTPSrvInfo(bootstrap.httpSrvInfo.address, bootstrap.httpSrvInfo.token) + } + + storeRegistry.getState('chat').dispatch.updateUserReacjis(userReacjis) + } + } }) } From e6aab06a66b7862f3cd877235c3316d380f8f124 Mon Sep 17 00:00:00 2001 From: chrisnojima Date: Wed, 7 Jan 2026 10:54:38 -0500 Subject: [PATCH 05/13] WIP --- shared/constants/daemon/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shared/constants/daemon/index.tsx b/shared/constants/daemon/index.tsx index 46e2322053ba..73c277e1b140 100644 --- a/shared/constants/daemon/index.tsx +++ b/shared/constants/daemon/index.tsx @@ -171,7 +171,7 @@ export const useDaemonState = Z.createZustand((set, get) => { const f = async () => { const s = await T.RPCGen.configGetBootstrapStatusRpcPromise() set(state => { - state.bootstrapStatus = s + state.bootstrapStatus = T.castDraft(s) }) logger.info(`[Bootstrap] loggedIn: ${s.loggedIn ? 1 : 0}`) From ab0dacb49e547cc8b53292c35b757fcd5ac41b2c Mon Sep 17 00:00:00 2001 From: Chris Nojima Date: Wed, 7 Jan 2026 13:24:29 -0500 Subject: [PATCH 06/13] WIP --- shared/constants/daemon/index.tsx | 8 +++----- shared/constants/platform-specific/shared.tsx | 2 +- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/shared/constants/daemon/index.tsx b/shared/constants/daemon/index.tsx index 73c277e1b140..eb34523a2e9d 100644 --- a/shared/constants/daemon/index.tsx +++ b/shared/constants/daemon/index.tsx @@ -33,7 +33,7 @@ export interface State extends Store { dispatch: { loadDaemonAccounts: (configuredAccountsLength: number, loggedIn: boolean) => void loadDaemonBootstrapStatus: () => Promise - refreshAccounts: () => Promise + refreshAccounts: (defaultUsername: string) => Promise resetState: () => void setError: (e?: Error) => void setFailed: (r: string) => void @@ -142,7 +142,7 @@ export const useDaemonState = Z.createZustand((set, get) => { wait(getAccountsWaitKey, handshakeVersion, true) } - await get().dispatch.refreshAccounts() + await get().dispatch.refreshAccounts(storeRegistry.getState('config').defaultUsername) if (handshakeWait) { // someone dismissed this already? @@ -194,10 +194,8 @@ export const useDaemonState = Z.createZustand((set, get) => { return await f() }, onRestartHandshakeNative: _onRestartHandshakeNative, - refreshAccounts: async () => { + refreshAccounts: async (defaultUsername: string) => { const configuredAccounts = (await T.RPCGen.loginGetConfiguredAccountsRpcPromise()) ?? [] - // already have one? - const {defaultUsername} = storeRegistry.getState('config') const {setAccounts, setDefaultUsername} = storeRegistry.getState('config').dispatch let existingDefaultFound = false as boolean diff --git a/shared/constants/platform-specific/shared.tsx b/shared/constants/platform-specific/shared.tsx index c3551e87beb2..216906749d78 100644 --- a/shared/constants/platform-specific/shared.tsx +++ b/shared/constants/platform-specific/shared.tsx @@ -110,7 +110,7 @@ export const initSharedSubscriptions = () => { } storeRegistry.getState('daemon').dispatch.loadDaemonAccounts(s.configuredAccounts.length, s.loggedIn) if (!s.loggedInCausedbyStartup) { - ignorePromise(storeRegistry.getState('daemon').dispatch.refreshAccounts()) + ignorePromise(storeRegistry.getState('daemon').dispatch.refreshAccounts(s.defaultUsername)) } } From 96b7c4598977514a2667454a0f663f64c1d6f690 Mon Sep 17 00:00:00 2001 From: Chris Nojima Date: Wed, 7 Jan 2026 13:45:42 -0500 Subject: [PATCH 07/13] WIP --- shared/constants/config/index.tsx | 27 ++++++++++++- shared/constants/daemon/index.tsx | 38 ++----------------- shared/constants/platform-specific/shared.tsx | 18 +++++++-- shared/constants/types/config.tsx | 1 + 4 files changed, 45 insertions(+), 39 deletions(-) diff --git a/shared/constants/config/index.tsx b/shared/constants/config/index.tsx index 1ab659205887..591f77563f9d 100644 --- a/shared/constants/config/index.tsx +++ b/shared/constants/config/index.tsx @@ -183,6 +183,7 @@ export interface State extends Store { remoteWindowNeedsProps: (component: string, params: string) => void resetRevokedSelf: () => void revoke: (deviceName: string, wasCurrentDevice: boolean) => void + refreshAccounts: () => Promise setAccounts: (a: Store['configuredAccounts']) => void setAndroidShare: (s: Store['androidShare']) => void setBadgeState: (b: State['badgeState']) => void @@ -650,9 +651,33 @@ export const useConfigState = Z.createZustand((set, get) => { s.justRevokedSelf = name s.revokedTrigger++ }) - storeRegistry.getState('daemon').dispatch.loadDaemonAccounts(configuredAccounts.length, loggedIn) + storeRegistry.getState('daemon').dispatch.loadDaemonAccounts(configuredAccounts.length, loggedIn, get().dispatch.refreshAccounts) } }, + refreshAccounts: async () => { + const defaultUsername = get().defaultUsername + const configuredAccounts = (await T.RPCGen.loginGetConfiguredAccountsRpcPromise()) ?? [] + const {setAccounts, setDefaultUsername} = get().dispatch + + let existingDefaultFound = false as boolean + let currentName = '' + const nextConfiguredAccounts: Array = [] + + configuredAccounts.forEach(account => { + const {username, isCurrent, fullname, hasStoredSecret} = account + if (username === defaultUsername) { + existingDefaultFound = true + } + if (isCurrent) { + currentName = account.username + } + nextConfiguredAccounts.push({fullname, hasStoredSecret, username}) + }) + if (!existingDefaultFound) { + setDefaultUsername(currentName) + } + setAccounts(nextConfiguredAccounts) + }, setAccounts: a => { set(s => { if (!isEqual(a, s.configuredAccounts)) { diff --git a/shared/constants/daemon/index.tsx b/shared/constants/daemon/index.tsx index eb34523a2e9d..2df6dd4a047d 100644 --- a/shared/constants/daemon/index.tsx +++ b/shared/constants/daemon/index.tsx @@ -31,9 +31,8 @@ const initialStore: Store = { export interface State extends Store { dispatch: { - loadDaemonAccounts: (configuredAccountsLength: number, loggedIn: boolean) => void + loadDaemonAccounts: (configuredAccountsLength: number, loggedIn: boolean, refreshAccounts: () => Promise) => void loadDaemonBootstrapStatus: () => Promise - refreshAccounts: (defaultUsername: string) => Promise resetState: () => void setError: (e?: Error) => void setFailed: (r: string) => void @@ -120,7 +119,7 @@ export const useDaemonState = Z.createZustand((set, get) => { daemonHandshakeDone: () => { get().dispatch.setState('done') }, - loadDaemonAccounts: (configuredAccountsLength: number, loggedIn: boolean) => { + loadDaemonAccounts: (configuredAccountsLength: number, loggedIn: boolean, refreshAccounts: () => Promise) => { const f = async () => { const version = get().handshakeVersion if (configuredAccountsLength) { @@ -142,7 +141,7 @@ export const useDaemonState = Z.createZustand((set, get) => { wait(getAccountsWaitKey, handshakeVersion, true) } - await get().dispatch.refreshAccounts(storeRegistry.getState('config').defaultUsername) + await refreshAccounts() if (handshakeWait) { // someone dismissed this already? @@ -194,37 +193,6 @@ export const useDaemonState = Z.createZustand((set, get) => { return await f() }, onRestartHandshakeNative: _onRestartHandshakeNative, - refreshAccounts: async (defaultUsername: string) => { - const configuredAccounts = (await T.RPCGen.loginGetConfiguredAccountsRpcPromise()) ?? [] - const {setAccounts, setDefaultUsername} = storeRegistry.getState('config').dispatch - - let existingDefaultFound = false as boolean - let currentName = '' - const nextConfiguredAccounts: Array = [] - const usernameToFullname: {[username: string]: string} = {} - - configuredAccounts.forEach(account => { - const {username, isCurrent, fullname, hasStoredSecret} = account - if (username === defaultUsername) { - existingDefaultFound = true - } - if (isCurrent) { - currentName = account.username - } - nextConfiguredAccounts.push({hasStoredSecret, username}) - usernameToFullname[username] = fullname - }) - if (!existingDefaultFound) { - setDefaultUsername(currentName) - } - setAccounts(nextConfiguredAccounts) - storeRegistry.getState('users').dispatch.updates( - Object.keys(usernameToFullname).map(name => ({ - info: {fullname: usernameToFullname[name]}, - name, - })) - ) - }, resetState: () => { set(s => ({ ...s, diff --git a/shared/constants/platform-specific/shared.tsx b/shared/constants/platform-specific/shared.tsx index 216906749d78..a517a6b7ede6 100644 --- a/shared/constants/platform-specific/shared.tsx +++ b/shared/constants/platform-specific/shared.tsx @@ -108,9 +108,9 @@ export const initSharedSubscriptions = () => { ignorePromise(storeRegistry.getState('daemon').dispatch.loadDaemonBootstrapStatus()) storeRegistry.getState('fs').dispatch.checkKbfsDaemonRpcStatus() } - storeRegistry.getState('daemon').dispatch.loadDaemonAccounts(s.configuredAccounts.length, s.loggedIn) + storeRegistry.getState('daemon').dispatch.loadDaemonAccounts(s.configuredAccounts.length, s.loggedIn, storeRegistry.getState('config').dispatch.refreshAccounts) if (!s.loggedInCausedbyStartup) { - ignorePromise(storeRegistry.getState('daemon').dispatch.refreshAccounts(s.defaultUsername)) + ignorePromise(storeRegistry.getState('config').dispatch.refreshAccounts()) } } @@ -126,7 +126,7 @@ export const initSharedSubscriptions = () => { useDarkModeState.getState().dispatch.loadDarkPrefs() storeRegistry.getState('chat').dispatch.loadStaticConfig() const configState = storeRegistry.getState('config') - s.dispatch.loadDaemonAccounts(configState.configuredAccounts.length, configState.loggedIn) + s.dispatch.loadDaemonAccounts(configState.configuredAccounts.length, configState.loggedIn, storeRegistry.getState('config').dispatch.refreshAccounts) } if (s.bootstrapStatus !== old.bootstrapStatus) { @@ -152,6 +152,18 @@ export const initSharedSubscriptions = () => { } } }) + + useConfigState.subscribe((s, old) => { + if (s.configuredAccounts !== old.configuredAccounts) { + const updates = s.configuredAccounts.map(account => ({ + info: {fullname: account.fullname ?? ''}, + name: account.username, + })) + if (updates.length > 0) { + storeRegistry.getState('users').dispatch.updates(updates) + } + } + }) } export const onEngineIncoming = (action: EngineGen.Actions) => { diff --git a/shared/constants/types/config.tsx b/shared/constants/types/config.tsx index e001a72201e9..fdebccf3474d 100644 --- a/shared/constants/types/config.tsx +++ b/shared/constants/types/config.tsx @@ -8,6 +8,7 @@ export type OutOfDate = { } export type DaemonHandshakeState = 'starting' | 'waitingForWaiters' | 'done' export type ConfiguredAccount = { + fullname?: string hasStoredSecret: boolean username: string } From eeedf90cdf350973a7c7383daf23ee4ae9b91de2 Mon Sep 17 00:00:00 2001 From: chrisnojima Date: Wed, 7 Jan 2026 13:55:38 -0500 Subject: [PATCH 08/13] WIP --- shared/constants/config/index.tsx | 52 ++++++++++++++++--------------- 1 file changed, 27 insertions(+), 25 deletions(-) diff --git a/shared/constants/config/index.tsx b/shared/constants/config/index.tsx index 591f77563f9d..5733c7e93d32 100644 --- a/shared/constants/config/index.tsx +++ b/shared/constants/config/index.tsx @@ -613,6 +613,30 @@ export const useConfigState = Z.createZustand((set, get) => { } ignorePromise(f()) }, + refreshAccounts: async () => { + const defaultUsername = get().defaultUsername + const configuredAccounts = (await T.RPCGen.loginGetConfiguredAccountsRpcPromise()) ?? [] + const {setAccounts, setDefaultUsername} = get().dispatch + + let existingDefaultFound = false as boolean + let currentName = '' + const nextConfiguredAccounts: Array = [] + + configuredAccounts.forEach(account => { + const {username, isCurrent, fullname, hasStoredSecret} = account + if (username === defaultUsername) { + existingDefaultFound = true + } + if (isCurrent) { + currentName = account.username + } + nextConfiguredAccounts.push({fullname, hasStoredSecret, username}) + }) + if (!existingDefaultFound) { + setDefaultUsername(currentName) + } + setAccounts(nextConfiguredAccounts) + }, remoteWindowNeedsProps: (component, params) => { set(s => { const map = s.remoteWindowNeedsProps.get(component) ?? new Map() @@ -651,33 +675,11 @@ export const useConfigState = Z.createZustand((set, get) => { s.justRevokedSelf = name s.revokedTrigger++ }) - storeRegistry.getState('daemon').dispatch.loadDaemonAccounts(configuredAccounts.length, loggedIn, get().dispatch.refreshAccounts) + storeRegistry + .getState('daemon') + .dispatch.loadDaemonAccounts(configuredAccounts.length, loggedIn, get().dispatch.refreshAccounts) } }, - refreshAccounts: async () => { - const defaultUsername = get().defaultUsername - const configuredAccounts = (await T.RPCGen.loginGetConfiguredAccountsRpcPromise()) ?? [] - const {setAccounts, setDefaultUsername} = get().dispatch - - let existingDefaultFound = false as boolean - let currentName = '' - const nextConfiguredAccounts: Array = [] - - configuredAccounts.forEach(account => { - const {username, isCurrent, fullname, hasStoredSecret} = account - if (username === defaultUsername) { - existingDefaultFound = true - } - if (isCurrent) { - currentName = account.username - } - nextConfiguredAccounts.push({fullname, hasStoredSecret, username}) - }) - if (!existingDefaultFound) { - setDefaultUsername(currentName) - } - setAccounts(nextConfiguredAccounts) - }, setAccounts: a => { set(s => { if (!isEqual(a, s.configuredAccounts)) { From 9dd9b247efe41d6d0e2cb9562cf6e651d35dce6c Mon Sep 17 00:00:00 2001 From: Chris Nojima Date: Wed, 7 Jan 2026 14:04:11 -0500 Subject: [PATCH 09/13] WIP --- shared/constants/config/index.tsx | 5 +---- shared/constants/platform-specific/shared.tsx | 4 ++++ 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/shared/constants/config/index.tsx b/shared/constants/config/index.tsx index 5733c7e93d32..7298413b89ff 100644 --- a/shared/constants/config/index.tsx +++ b/shared/constants/config/index.tsx @@ -667,7 +667,7 @@ export const useConfigState = Z.createZustand((set, get) => { }, revoke: (name, wasCurrentDevice) => { if (wasCurrentDevice) { - const {configuredAccounts, defaultUsername, loggedIn} = get() + const {configuredAccounts, defaultUsername} = get() const acc = configuredAccounts.find(n => n.username !== defaultUsername) const du = acc?.username ?? '' set(s => { @@ -675,9 +675,6 @@ export const useConfigState = Z.createZustand((set, get) => { s.justRevokedSelf = name s.revokedTrigger++ }) - storeRegistry - .getState('daemon') - .dispatch.loadDaemonAccounts(configuredAccounts.length, loggedIn, get().dispatch.refreshAccounts) } }, setAccounts: a => { diff --git a/shared/constants/platform-specific/shared.tsx b/shared/constants/platform-specific/shared.tsx index a517a6b7ede6..d3d2d19c7bce 100644 --- a/shared/constants/platform-specific/shared.tsx +++ b/shared/constants/platform-specific/shared.tsx @@ -119,6 +119,10 @@ export const initSharedSubscriptions = () => { storeRegistry.getState('chat').dispatch.toggleInboxSearch(false) } } + + if (s.revokedTrigger !== old.revokedTrigger) { + storeRegistry.getState('daemon').dispatch.loadDaemonAccounts(s.configuredAccounts.length, s.loggedIn, storeRegistry.getState('config').dispatch.refreshAccounts) + } }) useDaemonState.subscribe((s, old) => { From 20b21320d3b74c61e224d01de50a35b89fce68d0 Mon Sep 17 00:00:00 2001 From: Chris Nojima Date: Wed, 7 Jan 2026 14:07:17 -0500 Subject: [PATCH 10/13] WIP --- shared/constants/daemon/index.tsx | 8 -------- shared/constants/platform-specific/shared.tsx | 11 +++++++++++ 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/shared/constants/daemon/index.tsx b/shared/constants/daemon/index.tsx index 2df6dd4a047d..0920034a33b5 100644 --- a/shared/constants/daemon/index.tsx +++ b/shared/constants/daemon/index.tsx @@ -92,7 +92,6 @@ export const useDaemonState = Z.createZustand((set, get) => { // When there are no more waiters, we can show the actual app - let _emitStartupOnLoadDaemonConnectedOnce = false const dispatch: State['dispatch'] = { daemonHandshake: version => { get().dispatch.setState('waitingForWaiters') @@ -223,13 +222,6 @@ export const useDaemonState = Z.createZustand((set, get) => { set(s => { s.handshakeState = ds }) - - if (ds !== 'done') return - - if (!_emitStartupOnLoadDaemonConnectedOnce) { - _emitStartupOnLoadDaemonConnectedOnce = true - storeRegistry.getState('config').dispatch.loadOnStart('connectedToDaemonForFirstTime') - } }, startHandshake: () => { get().dispatch.setError() diff --git a/shared/constants/platform-specific/shared.tsx b/shared/constants/platform-specific/shared.tsx index d3d2d19c7bce..6f40376b42f5 100644 --- a/shared/constants/platform-specific/shared.tsx +++ b/shared/constants/platform-specific/shared.tsx @@ -31,6 +31,8 @@ import * as TrackerUtil from '../tracker2/util' import * as UnlockFoldersUtil from '../unlock-folders/util' import * as UsersUtil from '../users/util' +let _emitStartupOnLoadDaemonConnectedOnce = false + export const initSharedSubscriptions = () => { useConfigState.subscribe((s, old) => { if (s.loadOnStartPhase !== old.loadOnStartPhase) { @@ -155,6 +157,15 @@ export const initSharedSubscriptions = () => { storeRegistry.getState('chat').dispatch.updateUserReacjis(userReacjis) } } + + if (s.handshakeState !== old.handshakeState) { + if (s.handshakeState === 'done') { + if (!_emitStartupOnLoadDaemonConnectedOnce) { + _emitStartupOnLoadDaemonConnectedOnce = true + storeRegistry.getState('config').dispatch.loadOnStart('connectedToDaemonForFirstTime') + } + } + } }) useConfigState.subscribe((s, old) => { From 4d545b1f6b24f3de77c5c5b25afc40fae6a27834 Mon Sep 17 00:00:00 2001 From: Chris Nojima Date: Wed, 7 Jan 2026 14:07:46 -0500 Subject: [PATCH 11/13] WIP --- shared/constants/daemon/index.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/shared/constants/daemon/index.tsx b/shared/constants/daemon/index.tsx index 0920034a33b5..f87e335f757b 100644 --- a/shared/constants/daemon/index.tsx +++ b/shared/constants/daemon/index.tsx @@ -2,7 +2,6 @@ import logger from '@/logger' import {ignorePromise} from '../utils' import * as T from '../types' import * as Z from '@/util/zustand' -import {storeRegistry} from '../store-registry' import {maxHandshakeTries} from '../values' // Load accounts, this call can be slow so we attempt to continue w/o waiting if we determine we're logged in From 9878e3ee876ef7ef620fb7e65ee850e74c9dff4a Mon Sep 17 00:00:00 2001 From: Chris Nojima Date: Wed, 7 Jan 2026 14:13:14 -0500 Subject: [PATCH 12/13] WIP --- shared/constants/config/index.tsx | 9 --------- shared/constants/engine/index.tsx | 8 ++++++++ 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/shared/constants/config/index.tsx b/shared/constants/config/index.tsx index 7298413b89ff..4db04f067ae3 100644 --- a/shared/constants/config/index.tsx +++ b/shared/constants/config/index.tsx @@ -489,8 +489,6 @@ export const useConfigState = Z.createZustand((set, get) => { ignorePromise(f()) }, onEngineConnected: () => { - storeRegistry.getState('daemon').dispatch.startHandshake() - // The startReachability RPC call both starts and returns the current // reachability state. Then we'll get updates of changes from this state via reachabilityChanged. // This should be run on app start and service re-connect in case the service somehow crashed or was restarted manually. @@ -518,13 +516,6 @@ export const useConfigState = Z.createZustand((set, get) => { get().dispatch.dynamic.onEngineConnectedDesktop?.() get().dispatch.loadOnStart('initialStartupAsEarlyAsPossible') }, - onEngineDisonnected: () => { - const f = async () => { - await logger.dump() - } - ignorePromise(f()) - storeRegistry.getState('daemon').dispatch.setError(new Error('Disconnected')) - }, onEngineIncoming: action => { switch (action.type) { case EngineGen.keybase1GregorUIPushState: { diff --git a/shared/constants/engine/index.tsx b/shared/constants/engine/index.tsx index 9b8a73cf31cd..63a7ec8efa64 100644 --- a/shared/constants/engine/index.tsx +++ b/shared/constants/engine/index.tsx @@ -6,8 +6,10 @@ import * as PeopleUtil from '../people/util' import * as PinentryUtil from '../pinentry/util' import {onEngineIncoming as onEngineIncomingShared} from '../platform-specific/shared' import {storeRegistry} from '../store-registry' +import {ignorePromise} from '../utils' import * as TrackerUtil from '../tracker2/util' import * as UnlockFoldersUtil from '../unlock-folders/util' +import logger from '@/logger' type Store = object const initialStore: Store = {} @@ -27,6 +29,7 @@ export const useEngineState = Z.createZustand(set => { onEngineConnected: () => { ChatUtil.onEngineConnected() storeRegistry.getState('config').dispatch.onEngineConnected() + storeRegistry.getState('daemon').dispatch.startHandshake() NotifUtil.onEngineConnected() PeopleUtil.onEngineConnected() PinentryUtil.onEngineConnected() @@ -35,6 +38,11 @@ export const useEngineState = Z.createZustand(set => { }, onEngineDisconnected: () => { storeRegistry.getState('config').dispatch.onEngineDisonnected() + const f = async () => { + await logger.dump() + } + ignorePromise(f()) + storeRegistry.getState('daemon').dispatch.setError(new Error('Disconnected')) }, onEngineIncoming: action => { // defer a frame so its more like before From 2884d3afe2edbb0a4e51e3b44d507a7b718fec52 Mon Sep 17 00:00:00 2001 From: chrisnojima Date: Wed, 7 Jan 2026 14:27:23 -0500 Subject: [PATCH 13/13] WIP --- shared/constants/config/index.tsx | 1 - shared/constants/engine/index.tsx | 1 - 2 files changed, 2 deletions(-) diff --git a/shared/constants/config/index.tsx b/shared/constants/config/index.tsx index 4db04f067ae3..1501ce8e40d7 100644 --- a/shared/constants/config/index.tsx +++ b/shared/constants/config/index.tsx @@ -174,7 +174,6 @@ export interface State extends Store { setLoginError: (error?: RPCError) => void logoutAndTryToLogInAs: (username: string) => void onEngineConnected: () => void - onEngineDisonnected: () => void onEngineIncoming: (action: EngineGen.Actions) => void osNetworkStatusChanged: (online: boolean, type: T.Config.ConnectionType, isInit?: boolean) => void openUnlockFolders: (devices: ReadonlyArray) => void diff --git a/shared/constants/engine/index.tsx b/shared/constants/engine/index.tsx index 63a7ec8efa64..167add4ebbe4 100644 --- a/shared/constants/engine/index.tsx +++ b/shared/constants/engine/index.tsx @@ -37,7 +37,6 @@ export const useEngineState = Z.createZustand(set => { UnlockFoldersUtil.onEngineConnected() }, onEngineDisconnected: () => { - storeRegistry.getState('config').dispatch.onEngineDisonnected() const f = async () => { await logger.dump() }