diff --git a/shared/constants/config/index.tsx b/shared/constants/config/index.tsx index 01a496551e42..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 @@ -183,6 +182,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 @@ -488,8 +488,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. @@ -517,13 +515,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: { @@ -612,6 +603,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() @@ -650,7 +665,6 @@ export const useConfigState = Z.createZustand((set, get) => { s.justRevokedSelf = name s.revokedTrigger++ }) - storeRegistry.getState('daemon').dispatch.loadDaemonAccounts() } }, setAccounts: a => { diff --git a/shared/constants/daemon/index.tsx b/shared/constants/daemon/index.tsx index b08dff8bedc6..f87e335f757b 100644 --- a/shared/constants/daemon/index.tsx +++ b/shared/constants/daemon/index.tsx @@ -2,9 +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 {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 @@ -12,16 +9,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', @@ -31,9 +30,8 @@ const initialStore: Store = { export interface State extends Store { dispatch: { - loadDaemonAccounts: () => void + loadDaemonAccounts: (configuredAccountsLength: number, loggedIn: boolean, refreshAccounts: () => Promise) => void loadDaemonBootstrapStatus: () => Promise - refreshAccounts: () => Promise resetState: () => void setError: (e?: Error) => void setFailed: (r: string) => void @@ -93,7 +91,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') @@ -111,22 +108,19 @@ 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) } } ignorePromise(f()) - get().dispatch.loadDaemonAccounts() }, daemonHandshakeDone: () => { get().dispatch.setState('done') }, - loadDaemonAccounts: () => { + loadDaemonAccounts: (configuredAccountsLength: number, loggedIn: boolean, refreshAccounts: () => Promise) => { const f = async () => { const version = get().handshakeVersion - if (storeRegistry.getState('config').configuredAccounts.length) { + if (configuredAccountsLength) { // bail on already loaded return } @@ -135,7 +129,7 @@ export const useDaemonState = Z.createZustand((set, get) => { const handshakeVersion = version // did we beat getBootstrapStatus? - if (!storeRegistry.getState('config').loggedIn) { + if (!loggedIn) { handshakeWait = true } @@ -145,7 +139,7 @@ export const useDaemonState = Z.createZustand((set, get) => { wait(getAccountsWaitKey, handshakeVersion, true) } - await get().dispatch.refreshAccounts() + await refreshAccounts() if (handshakeWait) { // someone dismissed this already? @@ -172,32 +166,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 = T.castDraft(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) @@ -207,39 +191,6 @@ export const useDaemonState = Z.createZustand((set, get) => { return await f() }, onRestartHandshakeNative: _onRestartHandshakeNative, - refreshAccounts: async () => { - 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 - 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, @@ -270,13 +221,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/engine/index.tsx b/shared/constants/engine/index.tsx index 9b8a73cf31cd..167add4ebbe4 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() @@ -34,7 +37,11 @@ export const useEngineState = Z.createZustand(set => { UnlockFoldersUtil.onEngineConnected() }, 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 diff --git a/shared/constants/platform-specific/shared.tsx b/shared/constants/platform-specific/shared.tsx index ebf3c4317f29..6f40376b42f5 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' @@ -29,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) { @@ -106,9 +110,9 @@ 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, storeRegistry.getState('config').dispatch.refreshAccounts) if (!s.loggedInCausedbyStartup) { - ignorePromise(storeRegistry.getState('daemon').dispatch.refreshAccounts()) + ignorePromise(storeRegistry.getState('config').dispatch.refreshAccounts()) } } @@ -117,6 +121,63 @@ 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) => { + 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, storeRegistry.getState('config').dispatch.refreshAccounts) + } + + 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) + } + } + + if (s.handshakeState !== old.handshakeState) { + if (s.handshakeState === 'done') { + if (!_emitStartupOnLoadDaemonConnectedOnce) { + _emitStartupOnLoadDaemonConnectedOnce = true + storeRegistry.getState('config').dispatch.loadOnStart('connectedToDaemonForFirstTime') + } + } + } + }) + + 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) + } + } }) } 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 }