diff --git a/shared/common-adapters/avatar/util.tsx b/shared/common-adapters/avatar/util.tsx new file mode 100644 index 000000000000..6e08e360ab2f --- /dev/null +++ b/shared/common-adapters/avatar/util.tsx @@ -0,0 +1,14 @@ +import * as EngineGen from '@/actions/engine-gen-gen' +import {useAvatarState} from '@/common-adapters/avatar/store' + +export const onEngineIncoming = (action: EngineGen.Actions) => { + switch (action.type) { + case EngineGen.keybase1NotifyTeamAvatarUpdated: { + const {name} = action.payload.params + useAvatarState.getState().dispatch.updated(name) + break + } + default: + } +} + diff --git a/shared/constants/config/index.tsx b/shared/constants/config/index.tsx index 057b292034da..01a496551e42 100644 --- a/shared/constants/config/index.tsx +++ b/shared/constants/config/index.tsx @@ -2,7 +2,6 @@ import * as T from '../types' import {ignorePromise, timeoutPromise} from '../utils' import {waitingKeyConfigLogin} from '../strings' import * as EngineGen from '@/actions/engine-gen-gen' -import * as RemoteGen from '@/actions/remote-gen' import * as Stats from '@/engine/stats' import * as Z from '@/util/zustand' import {noConversationIDKey} from '../types/chat2/common' @@ -13,14 +12,9 @@ import {RPCError, convertToError, isEOFError, isErrorTransient, niceError} from import {defaultUseNativeFrame, isMobile} from '../platform' import {type CommonResponseHandler} from '@/engine/types' import {invalidPasswordErrorString} from './util' -import {navigateAppend, switchTab} from '../router2/util' +import {navigateAppend} from '../router2/util' import {storeRegistry} from '../store-registry' -import {useAvatarState} from '@/common-adapters/avatar/store' -import {useCurrentUserState} from '../current-user' -import {useFollowerState} from '../followers' -import {usePinentryState} from '../pinentry' import {useWhatsNewState} from '../whats-new' -import {getSelectedConversation} from '@/constants/chat2/common' type Store = T.Immutable<{ forceSmallNav: boolean @@ -167,7 +161,6 @@ export interface State extends Store { changedFocus: (f: boolean) => void checkForUpdate: () => void dumpLogs: (reason: string) => Promise - eventFromRemoteWindows: (action: RemoteGen.Actions) => void filePickerError: (error: Error) => void initAppUpdateLoop: () => void initNotifySound: () => void @@ -189,7 +182,7 @@ export interface State extends Store { resetState: (isDebug?: boolean) => void remoteWindowNeedsProps: (component: string, params: string) => void resetRevokedSelf: () => void - revoke: (deviceName: string) => void + revoke: (deviceName: string, wasCurrentDevice: boolean) => void setAccounts: (a: Store['configuredAccounts']) => void setAndroidShare: (s: Store['androidShare']) => void setBadgeState: (b: State['badgeState']) => void @@ -205,8 +198,9 @@ export interface State extends Store { setStartupDetails: (st: Omit) => void setOpenAtLogin: (open: boolean) => void setOutOfDate: (outOfDate: T.Config.OutOfDate) => void - setUserSwitching: (sw: boolean) => void + setUpdating: () => void setUseNativeFrame: (use: boolean) => void + setUserSwitching: (sw: boolean) => void showMain: () => void toggleRuntimeStats: () => void updateGregorCategory: (category: string, body: string, dtime?: {offset: number; time: number}) => void @@ -249,15 +243,6 @@ export const useConfigState = Z.createZustand((set, get) => { set(s => { s.gregorReachable = r }) - // Re-get info about our account if you log in/we're done handshaking/became reachable - if (r === T.RPCGen.Reachable.yes) { - // not in waiting state - if (storeRegistry.getState('daemon').handshakeWaiters.size === 0) { - ignorePromise(storeRegistry.getState('daemon').dispatch.loadDaemonBootstrapStatus()) - } - } - - storeRegistry.getState('teams').dispatch.eagerLoadTeams() } const setGregorPushState = (state: T.RPCGen.Gregor1.State) => { @@ -285,26 +270,6 @@ export const useConfigState = Z.createZustand((set, get) => { useWhatsNewState.getState().dispatch.updateLastSeen(lastSeenItem) } - const updateApp = () => { - const f = async () => { - await T.RPCGen.configStartUpdateIfNeededRpcPromise() - } - ignorePromise(f()) - // * If user choose to update: - // We'd get killed and it doesn't matter what happens here. - // * If user hits "Ignore": - // Note that we ignore the snooze here, so the state shouldn't change, - // and we'd back to where we think we still need an update. So we could - // have just unset the "updating" flag.However, in case server has - // decided to pull out the update between last time we asked the updater - // and now, we'd be in a wrong state if we didn't check with the service. - // Since user has interacted with it, we still ask the service to make - // sure. - set(s => { - s.outOfDate.updating = true - }) - } - const updateRuntimeStats = (stats?: T.RPCGen.RuntimeStats) => { set(s => { if (!stats) { @@ -324,13 +289,6 @@ export const useConfigState = Z.createZustand((set, get) => { set(s => { s.appFocused = f }) - - if (!isMobile || !f) { - return - } - const {dispatch} = storeRegistry.getConvoState(getSelectedConversation()) - dispatch.loadMoreMessages({reason: 'foregrounding'}) - dispatch.markThreadAsRead() }, checkForUpdate: () => { const f = async () => { @@ -357,154 +315,6 @@ export const useConfigState = Z.createZustand((set, get) => { showMainNative: undefined, showShareActionSheet: undefined, }, - eventFromRemoteWindows: (action: RemoteGen.Actions) => { - switch (action.type) { - case RemoteGen.resetStore: - break - case RemoteGen.openChatFromWidget: { - get().dispatch.showMain() - storeRegistry - .getConvoState(action.payload.conversationIDKey) - .dispatch.navigateToThread('inboxSmall') - break - } - case RemoteGen.inboxRefresh: { - storeRegistry.getState('chat').dispatch.inboxRefresh('widgetRefresh') - break - } - case RemoteGen.engineConnection: { - if (action.payload.connected) { - storeRegistry.getState('engine').dispatch.onEngineConnected() - } else { - storeRegistry.getState('engine').dispatch.onEngineDisconnected() - } - break - } - case RemoteGen.switchTab: { - switchTab(action.payload.tab) - break - } - case RemoteGen.setCriticalUpdate: { - storeRegistry.getState('fs').dispatch.setCriticalUpdate(action.payload.critical) - break - } - case RemoteGen.userFileEditsLoad: { - storeRegistry.getState('fs').dispatch.userFileEditsLoad() - break - } - case RemoteGen.openFilesFromWidget: { - storeRegistry.getState('fs').dispatch.dynamic.openFilesFromWidgetDesktop?.(action.payload.path) - break - } - case RemoteGen.saltpackFileOpen: { - storeRegistry.getState('deeplinks').dispatch.handleSaltPackOpen(action.payload.path) - break - } - case RemoteGen.pinentryOnCancel: { - usePinentryState.getState().dispatch.dynamic.onCancel?.() - break - } - case RemoteGen.pinentryOnSubmit: { - usePinentryState.getState().dispatch.dynamic.onSubmit?.(action.payload.password) - break - } - case RemoteGen.openPathInSystemFileManager: { - storeRegistry - .getState('fs') - .dispatch.dynamic.openPathInSystemFileManagerDesktop?.(action.payload.path) - break - } - case RemoteGen.unlockFoldersSubmitPaperKey: { - T.RPCGen.loginPaperKeySubmitRpcPromise( - {paperPhrase: action.payload.paperKey}, - 'unlock-folders:waiting' - ) - .then(() => { - get().dispatch.openUnlockFolders([]) - }) - .catch((e: unknown) => { - if (!(e instanceof RPCError)) return - set(s => { - s.unlockFoldersError = e.desc - }) - }) - break - } - case RemoteGen.closeUnlockFolders: { - T.RPCGen.rekeyRekeyStatusFinishRpcPromise() - .then(() => {}) - .catch(() => {}) - get().dispatch.openUnlockFolders([]) - break - } - case RemoteGen.stop: { - storeRegistry.getState('settings').dispatch.stop(action.payload.exitCode) - break - } - case RemoteGen.trackerChangeFollow: { - storeRegistry - .getState('tracker2') - .dispatch.changeFollow(action.payload.guiID, action.payload.follow) - break - } - case RemoteGen.trackerIgnore: { - storeRegistry.getState('tracker2').dispatch.ignore(action.payload.guiID) - break - } - case RemoteGen.trackerCloseTracker: { - storeRegistry.getState('tracker2').dispatch.closeTracker(action.payload.guiID) - break - } - case RemoteGen.trackerLoad: { - storeRegistry.getState('tracker2').dispatch.load(action.payload) - break - } - case RemoteGen.link: - { - const {link} = action.payload - storeRegistry.getState('deeplinks').dispatch.handleAppLink(link) - } - break - case RemoteGen.installerRan: - get().dispatch.installerRan() - break - case RemoteGen.updateNow: - updateApp() - break - case RemoteGen.powerMonitorEvent: - get().dispatch.powerMonitorEvent(action.payload.event) - break - case RemoteGen.showMain: - get().dispatch.showMain() - break - case RemoteGen.dumpLogs: - ignorePromise(get().dispatch.dumpLogs(action.payload.reason)) - break - case RemoteGen.remoteWindowWantsProps: - get().dispatch.remoteWindowNeedsProps(action.payload.component, action.payload.param) - break - case RemoteGen.updateWindowMaxState: - set(s => { - s.windowState.isMaximized = action.payload.max - }) - break - case RemoteGen.updateWindowState: - get().dispatch.updateWindowState(action.payload.windowState) - break - case RemoteGen.updateWindowShown: { - const win = action.payload.component - set(s => { - s.windowShownCount.set(win, (s.windowShownCount.get(win) ?? 0) + 1) - }) - break - } - case RemoteGen.previewConversation: - storeRegistry - .getState('chat') - .dispatch.previewConversation({participants: [action.payload.participant], reason: 'tracker'}) - break - } - }, filePickerError: error => { get().dispatch.dynamic.onFilePickerError?.(error) }, @@ -575,8 +385,6 @@ export const useConfigState = Z.createZustand((set, get) => { set(s => { s.installerRanCount++ }) - - storeRegistry.getState('fs').dispatch.checkKbfsDaemonRpcStatus() }, loadIsOnline: () => { const f = async () => { @@ -749,30 +557,6 @@ export const useConfigState = Z.createZustand((set, get) => { } break } - case EngineGen.keybase1NotifyTeamAvatarUpdated: { - const {name} = action.payload.params - useAvatarState.getState().dispatch.updated(name) - break - } - case EngineGen.keybase1NotifyTrackingTrackingChanged: { - const {isTracking, username} = action.payload.params - useFollowerState.getState().dispatch.updateFollowing(username, isTracking) - break - } - case EngineGen.keybase1NotifyTrackingTrackingInfo: { - const {uid, followers: _newFollowers, followees: _newFollowing} = action.payload.params - if (useCurrentUserState.getState().uid !== uid) { - return - } - const newFollowers = new Set(_newFollowers) - const newFollowing = new Set(_newFollowing) - const {following: oldFollowing, followers: oldFollowers, dispatch} = useFollowerState.getState() - const following = isEqual(newFollowing, oldFollowing) ? oldFollowing : newFollowing - const followers = isEqual(newFollowers, oldFollowers) ? oldFollowers : newFollowers - dispatch.replace(followers, following) - break - } - case EngineGen.keybase1ReachabilityReachabilityChanged: if (get().loggedIn) { setGregorReachable(action.payload.params.reachability.reachable) @@ -856,8 +640,7 @@ export const useConfigState = Z.createZustand((set, get) => { userSwitching: s.userSwitching, })) }, - revoke: name => { - const wasCurrentDevice = useCurrentUserState.getState().deviceName === name + revoke: (name, wasCurrentDevice) => { if (wasCurrentDevice) { const {configuredAccounts, defaultUsername} = get() const acc = configuredAccounts.find(n => n.username !== defaultUsername) @@ -962,11 +745,6 @@ export const useConfigState = Z.createZustand((set, get) => { if (!changed) return - if (loggedIn) { - ignorePromise(storeRegistry.getState('daemon').dispatch.loadDaemonBootstrapStatus()) - } - storeRegistry.getState('daemon').dispatch.loadDaemonAccounts() - const {loadOnStart} = get().dispatch if (loggedIn) { if (!causedByStartup) { @@ -982,14 +760,6 @@ export const useConfigState = Z.createZustand((set, get) => { } else { Z.resetAllStores() } - - if (loggedIn) { - storeRegistry.getState('fs').dispatch.checkKbfsDaemonRpcStatus() - } - - if (!causedByStartup) { - ignorePromise(storeRegistry.getState('daemon').dispatch.refreshAccounts()) - } }, setLoginError: error => { set(s => { @@ -1004,9 +774,6 @@ export const useConfigState = Z.createZustand((set, get) => { set(s => { s.mobileAppState = nextAppState }) - if (nextAppState === 'background' && storeRegistry.getState('chat').inboxSearch) { - storeRegistry.getState('chat').dispatch.toggleInboxSearch(false) - } }, setNotifySound: n => { set(s => { @@ -1046,6 +813,11 @@ export const useConfigState = Z.createZustand((set, get) => { } }) }, + setUpdating: () => { + set(s => { + s.outOfDate.updating = true + }) + }, setUseNativeFrame: use => { set(s => { s.useNativeFrame = use diff --git a/shared/constants/engine/index.tsx b/shared/constants/engine/index.tsx index e2d651083c6b..9b8a73cf31cd 100644 --- a/shared/constants/engine/index.tsx +++ b/shared/constants/engine/index.tsx @@ -1,23 +1,13 @@ -import * as Z from '@/util/zustand' -import {storeRegistry} from '../store-registry' import type * as EngineGen from '@/actions/engine-gen-gen' -import * as ArchiveUtil from '../archive/util' -import * as AutoResetUtil from '../autoreset/util' -import * as BotsUtil from '../bots/util' +import * as Z from '@/util/zustand' import * as ChatUtil from '../chat2/util' -import * as DeepLinksUtil from '../deeplinks/util' -import * as DevicesUtil from '../devices/util' -import * as FSUtil from '../fs/util' -import * as GitUtil from '../git/util' import * as NotifUtil from '../notifications/util' import * as PeopleUtil from '../people/util' import * as PinentryUtil from '../pinentry/util' -import * as SettingsUtil from '../settings/util' -import * as SignupUtil from '../signup/util' -import * as TeamsUtil from '../teams/util' +import {onEngineIncoming as onEngineIncomingShared} from '../platform-specific/shared' +import {storeRegistry} from '../store-registry' import * as TrackerUtil from '../tracker2/util' import * as UnlockFoldersUtil from '../unlock-folders/util' -import * as UsersUtil from '../users/util' type Store = object const initialStore: Store = {} @@ -50,26 +40,7 @@ export const useEngineState = Z.createZustand(set => { // defer a frame so its more like before incomingTimeout = setTimeout(() => { // we delegate to these utils so we don't need to load stores that we don't need yet - ArchiveUtil.onEngineIncoming(action) - AutoResetUtil.onEngineIncoming(action) - BotsUtil.onEngineIncoming(action) - ChatUtil.onEngineIncoming(action) - storeRegistry.getState('config').dispatch.dynamic.onEngineIncomingDesktop?.(action) - storeRegistry.getState('config').dispatch.dynamic.onEngineIncomingNative?.(action) - storeRegistry.getState('config').dispatch.onEngineIncoming(action) - DeepLinksUtil.onEngineIncoming(action) - DevicesUtil.onEngineIncoming(action) - FSUtil.onEngineIncoming(action) - GitUtil.onEngineIncoming(action) - NotifUtil.onEngineIncoming(action) - PeopleUtil.onEngineIncoming(action) - PinentryUtil.onEngineIncoming(action) - SettingsUtil.onEngineIncoming(action) - SignupUtil.onEngineIncoming(action) - TeamsUtil.onEngineIncoming(action) - TrackerUtil.onEngineIncoming(action) - UnlockFoldersUtil.onEngineIncoming(action) - UsersUtil.onEngineIncoming(action) + onEngineIncomingShared(action) }, 0) }, resetState: () => { diff --git a/shared/constants/followers/util.tsx b/shared/constants/followers/util.tsx new file mode 100644 index 000000000000..34ea577cab45 --- /dev/null +++ b/shared/constants/followers/util.tsx @@ -0,0 +1,29 @@ +import * as EngineGen from '@/actions/engine-gen-gen' +import isEqual from 'lodash/isEqual' +import {useCurrentUserState} from '../current-user' +import {useFollowerState} from '../followers' + +export const onEngineIncoming = (action: EngineGen.Actions) => { + switch (action.type) { + case EngineGen.keybase1NotifyTrackingTrackingChanged: { + const {isTracking, username} = action.payload.params + useFollowerState.getState().dispatch.updateFollowing(username, isTracking) + break + } + case EngineGen.keybase1NotifyTrackingTrackingInfo: { + const {uid, followers: _newFollowers, followees: _newFollowing} = action.payload.params + if (useCurrentUserState.getState().uid !== uid) { + return + } + const newFollowers = new Set(_newFollowers) + const newFollowing = new Set(_newFollowing) + const {following: oldFollowing, followers: oldFollowers, dispatch} = useFollowerState.getState() + const following = isEqual(newFollowing, oldFollowing) ? oldFollowing : newFollowing + const followers = isEqual(newFollowers, oldFollowers) ? oldFollowers : newFollowers + dispatch.replace(followers, following) + break + } + default: + } +} + diff --git a/shared/constants/platform-specific/index.desktop.tsx b/shared/constants/platform-specific/index.desktop.tsx index 957df7384a13..50fca882c4ef 100644 --- a/shared/constants/platform-specific/index.desktop.tsx +++ b/shared/constants/platform-specific/index.desktop.tsx @@ -5,14 +5,16 @@ import {useConfigState} from '../config' import * as ConfigConstants from '../config' import {useDaemonState} from '../daemon' import {useFSState} from '../fs' +import {usePinentryState} from '../pinentry' import {useProfileState} from '../profile' import {useRouterState} from '../router2' import * as EngineGen from '@/actions/engine-gen-gen' +import * as RemoteGen from '@/actions/remote-gen' import * as T from '../types' import InputMonitor from './input-monitor.desktop' import KB2 from '@/util/electron.desktop' import logger from '@/logger' -import type {RPCError} from '@/util/errors' +import {RPCError} from '@/util/errors' import {getEngine} from '@/engine' import {isLinux, isWindows} from '../platform.desktop' import {kbfsNotification} from './kbfs-notifications' @@ -20,7 +22,10 @@ import {skipAppFocusActions} from '@/local-debug.desktop' import NotifyPopup from '@/util/notify-popup' import {noKBFSFailReason} from '@/constants/config/util' import {initSharedSubscriptions} from './shared' +import {switchTab} from '../router2/util' +import {storeRegistry} from '../store-registry' import {wrapErrors} from '@/util/debug' +import {getSelectedConversation} from '@/constants/chat2/common' const {showMainWindow, activeChanged, requestWindowsStartService, dumpNodeLogger} = KB2.functions const {quitApp, exitApp, setOpenAtLogin, ctlQuit, copyToClipboard} = KB2.functions @@ -153,39 +158,49 @@ export const initPlatformListener = () => { }) useConfigState.subscribe((s, old) => { - if (s.loggedIn === old.loggedIn) return - s.dispatch.osNetworkStatusChanged(navigator.onLine, 'notavailable', true) - }) + if (s.loggedIn !== old.loggedIn) { + s.dispatch.osNetworkStatusChanged(navigator.onLine, 'notavailable', true) + } - useConfigState.subscribe((s, prev) => { - if (s.appFocused !== prev.appFocused) { + if (s.appFocused !== old.appFocused) { maybePauseVideos() + if (!old.appFocused && s.appFocused) { + const {dispatch} = storeRegistry.getConvoState(getSelectedConversation()) + dispatch.loadMoreMessages({reason: 'foregrounding'}) + dispatch.markThreadAsRead() + } } - }) - useDaemonState.subscribe((s, old) => { - if (s.handshakeVersion === old.handshakeVersion) return - if (!isWindows) return - - const f = async () => { - const waitKey = 'pipeCheckFail' - const version = s.handshakeVersion - const {wait} = s.dispatch - wait(waitKey, version, true) - try { - logger.info('Checking RPC ownership') - if (KB2.functions.winCheckRPCOwnership) { - await KB2.functions.winCheckRPCOwnership() + if (s.openAtLogin !== old.openAtLogin) { + const {openAtLogin} = s + const f = async () => { + if (__DEV__) { + console.log('onSetOpenAtLogin disabled for dev mode') + return + } else { + await T.RPCGen.configGuiSetValueRpcPromise({ + path: ConfigConstants.openAtLoginKey, + value: {b: openAtLogin, isNull: false}, + }) + } + if (isLinux || isWindows) { + const enabled = + (await T.RPCGen.ctlGetOnLoginStartupRpcPromise()) === T.RPCGen.OnLoginStartupStatus.enabled + if (enabled !== openAtLogin) { + try { + await T.RPCGen.ctlSetOnLoginStartupRpcPromise({enabled: openAtLogin}) + } catch (error_) { + const error = error_ as RPCError + logger.warn(`Error in sending ctlSetOnLoginStartup: ${error.message}`) + } + } + } else { + logger.info(`Login item settings changed! now ${openAtLogin ? 'on' : 'off'}`) + await setOpenAtLogin?.(openAtLogin) } - wait(waitKey, version, false) - } catch (error_) { - // error will be logged in bootstrap check - getEngine().reset() - const error = error_ as RPCError - wait(waitKey, version, false, error.message || 'windows pipe owner fail', true) } + ignorePromise(f()) } - ignorePromise(f()) }) const handleWindowFocusEvents = () => { @@ -211,46 +226,39 @@ export const initPlatformListener = () => { } setupReachabilityWatcher() - useConfigState.subscribe((s, old) => { - if (s.openAtLogin === old.openAtLogin) return - const {openAtLogin} = s - const f = async () => { - if (__DEV__) { - console.log('onSetOpenAtLogin disabled for dev mode') - return - } else { - await T.RPCGen.configGuiSetValueRpcPromise({ - path: ConfigConstants.openAtLoginKey, - value: {b: openAtLogin, isNull: false}, - }) - } - if (isLinux || isWindows) { - const enabled = - (await T.RPCGen.ctlGetOnLoginStartupRpcPromise()) === T.RPCGen.OnLoginStartupStatus.enabled - if (enabled !== openAtLogin) { - try { - await T.RPCGen.ctlSetOnLoginStartupRpcPromise({enabled: openAtLogin}) - } catch (error_) { - const error = error_ as RPCError - logger.warn(`Error in sending ctlSetOnLoginStartup: ${error.message}`) + useDaemonState.subscribe((s, old) => { + if (s.handshakeVersion !== old.handshakeVersion) { + if (!isWindows) return + + const f = async () => { + const waitKey = 'pipeCheckFail' + const version = s.handshakeVersion + const {wait} = s.dispatch + wait(waitKey, version, true) + try { + logger.info('Checking RPC ownership') + if (KB2.functions.winCheckRPCOwnership) { + await KB2.functions.winCheckRPCOwnership() } + wait(waitKey, version, false) + } catch (error_) { + // error will be logged in bootstrap check + getEngine().reset() + const error = error_ as RPCError + wait(waitKey, version, false, error.message || 'windows pipe owner fail', true) } - } else { - logger.info(`Login item settings changed! now ${openAtLogin ? 'on' : 'off'}`) - await setOpenAtLogin?.(openAtLogin) } + ignorePromise(f()) } - ignorePromise(f()) - }) - useDaemonState.subscribe((s, old) => { - if (s.handshakeState === old.handshakeState || s.handshakeState !== 'done') return - useConfigState.getState().dispatch.setStartupDetails({ - conversation: Chat.noConversationIDKey, - followUser: '', - link: '', - tab: undefined, - }) + if (s.handshakeState !== old.handshakeState && s.handshakeState === 'done') { + useConfigState.getState().dispatch.setStartupDetails({ + conversation: Chat.noConversationIDKey, + followUser: '', + link: '', + tab: undefined, + }) + } }) if (isLinux) { @@ -296,3 +304,164 @@ export const initPlatformListener = () => { ignorePromise(useFSState.getState().dispatch.setupSubscriptions()) } + +const updateApp = () => { + const f = async () => { + await T.RPCGen.configStartUpdateIfNeededRpcPromise() + } + ignorePromise(f()) + // * If user choose to update: + // We'd get killed and it doesn't matter what happens here. + // * If user hits "Ignore": + // Note that we ignore the snooze here, so the state shouldn't change, + // and we'd back to where we think we still need an update. So we could + // have just unset the "updating" flag.However, in case server has + // decided to pull out the update between last time we asked the updater + // and now, we'd be in a wrong state if we didn't check with the service. + // Since user has interacted with it, we still ask the service to make + // sure. + + useConfigState.getState().dispatch.setUpdating() +} + +export const eventFromRemoteWindows = (action: RemoteGen.Actions) => { + switch (action.type) { + case RemoteGen.resetStore: + break + case RemoteGen.openChatFromWidget: { + useConfigState.getState().dispatch.showMain() + storeRegistry.getConvoState(action.payload.conversationIDKey).dispatch.navigateToThread('inboxSmall') + break + } + case RemoteGen.inboxRefresh: { + storeRegistry.getState('chat').dispatch.inboxRefresh('widgetRefresh') + break + } + case RemoteGen.engineConnection: { + if (action.payload.connected) { + storeRegistry.getState('engine').dispatch.onEngineConnected() + } else { + storeRegistry.getState('engine').dispatch.onEngineDisconnected() + } + break + } + case RemoteGen.switchTab: { + switchTab(action.payload.tab) + break + } + case RemoteGen.setCriticalUpdate: { + storeRegistry.getState('fs').dispatch.setCriticalUpdate(action.payload.critical) + break + } + case RemoteGen.userFileEditsLoad: { + storeRegistry.getState('fs').dispatch.userFileEditsLoad() + break + } + case RemoteGen.openFilesFromWidget: { + storeRegistry.getState('fs').dispatch.dynamic.openFilesFromWidgetDesktop?.(action.payload.path) + break + } + case RemoteGen.saltpackFileOpen: { + storeRegistry.getState('deeplinks').dispatch.handleSaltPackOpen(action.payload.path) + break + } + case RemoteGen.pinentryOnCancel: { + usePinentryState.getState().dispatch.dynamic.onCancel?.() + break + } + case RemoteGen.pinentryOnSubmit: { + usePinentryState.getState().dispatch.dynamic.onSubmit?.(action.payload.password) + break + } + case RemoteGen.openPathInSystemFileManager: { + storeRegistry.getState('fs').dispatch.dynamic.openPathInSystemFileManagerDesktop?.(action.payload.path) + break + } + case RemoteGen.unlockFoldersSubmitPaperKey: { + T.RPCGen.loginPaperKeySubmitRpcPromise({paperPhrase: action.payload.paperKey}, 'unlock-folders:waiting') + .then(() => { + useConfigState.getState().dispatch.openUnlockFolders([]) + }) + .catch((e: unknown) => { + if (!(e instanceof RPCError)) return + useConfigState.setState(s => { + s.unlockFoldersError = e.desc + }) + }) + break + } + case RemoteGen.closeUnlockFolders: { + T.RPCGen.rekeyRekeyStatusFinishRpcPromise() + .then(() => {}) + .catch(() => {}) + useConfigState.getState().dispatch.openUnlockFolders([]) + break + } + case RemoteGen.stop: { + storeRegistry.getState('settings').dispatch.stop(action.payload.exitCode) + break + } + case RemoteGen.trackerChangeFollow: { + storeRegistry.getState('tracker2').dispatch.changeFollow(action.payload.guiID, action.payload.follow) + break + } + case RemoteGen.trackerIgnore: { + storeRegistry.getState('tracker2').dispatch.ignore(action.payload.guiID) + break + } + case RemoteGen.trackerCloseTracker: { + storeRegistry.getState('tracker2').dispatch.closeTracker(action.payload.guiID) + break + } + case RemoteGen.trackerLoad: { + storeRegistry.getState('tracker2').dispatch.load(action.payload) + break + } + case RemoteGen.link: + { + const {link} = action.payload + storeRegistry.getState('deeplinks').dispatch.handleAppLink(link) + } + break + case RemoteGen.installerRan: + useConfigState.getState().dispatch.installerRan() + break + case RemoteGen.updateNow: + updateApp() + break + case RemoteGen.powerMonitorEvent: + useConfigState.getState().dispatch.powerMonitorEvent(action.payload.event) + break + case RemoteGen.showMain: + useConfigState.getState().dispatch.showMain() + break + case RemoteGen.dumpLogs: + ignorePromise(useConfigState.getState().dispatch.dumpLogs(action.payload.reason)) + break + case RemoteGen.remoteWindowWantsProps: + useConfigState + .getState() + .dispatch.remoteWindowNeedsProps(action.payload.component, action.payload.param) + break + case RemoteGen.updateWindowMaxState: + useConfigState.setState(s => { + s.windowState.isMaximized = action.payload.max + }) + break + case RemoteGen.updateWindowState: + useConfigState.getState().dispatch.updateWindowState(action.payload.windowState) + break + case RemoteGen.updateWindowShown: { + const win = action.payload.component + useConfigState.setState(s => { + s.windowShownCount.set(win, (s.windowShownCount.get(win) ?? 0) + 1) + }) + break + } + case RemoteGen.previewConversation: + storeRegistry + .getState('chat') + .dispatch.previewConversation({participants: [action.payload.participant], reason: 'tracker'}) + break + } +} diff --git a/shared/constants/platform-specific/shared.tsx b/shared/constants/platform-specific/shared.tsx index 6afb0b1831e0..ebf3c4317f29 100644 --- a/shared/constants/platform-specific/shared.tsx +++ b/shared/constants/platform-specific/shared.tsx @@ -1,69 +1,147 @@ -import {ignorePromise} from '../utils' +import type * as EngineGen from '@/actions/engine-gen-gen' +import logger from '@/logger' import {serverConfigFileName} from '../platform' import * as T from '../types' -import logger from '@/logger' +import {ignorePromise} from '../utils' +import * as ArchiveUtil from '../archive/util' +import * as AutoResetUtil from '../autoreset/util' +import * as AvatarUtil from '@/common-adapters/avatar/util' +import * as BotsUtil from '../bots/util' import {useChatState} from '../chat2' +import * as ChatUtil from '../chat2/util' import {useConfigState} from '../config' import {useCurrentUserState} from '../current-user' +import * as DeepLinksUtil from '../deeplinks/util' +import * as DevicesUtil from '../devices/util' +import * as FollowerUtil from '../followers/util' +import * as FSUtil from '../fs/util' +import * as GitUtil from '../git/util' +import * as NotifUtil from '../notifications/util' +import * as PeopleUtil from '../people/util' +import * as PinentryUtil from '../pinentry/util' +import {storeRegistry} from '../store-registry' import {useSettingsContactsState} from '../settings-contacts' +import * as SettingsUtil from '../settings/util' +import * as SignupUtil from '../signup/util' import {useTeamsState} from '../teams' +import * as TeamsUtil from '../teams/util' +import * as TrackerUtil from '../tracker2/util' +import * as UnlockFoldersUtil from '../unlock-folders/util' +import * as UsersUtil from '../users/util' export const initSharedSubscriptions = () => { useConfigState.subscribe((s, old) => { - if (s.loadOnStartPhase === old.loadOnStartPhase) return + if (s.loadOnStartPhase !== old.loadOnStartPhase) { + if (s.loadOnStartPhase === 'startupOrReloginButNotInARush') { + const getFollowerInfo = () => { + const {uid} = useCurrentUserState.getState() + logger.info(`getFollowerInfo: init; uid=${uid}`) + if (uid) { + // request follower info in the background + T.RPCGen.configRequestFollowingAndUnverifiedFollowersRpcPromise() + .then(() => {}) + .catch(() => {}) + } + } - if (s.loadOnStartPhase === 'startupOrReloginButNotInARush') { - const getFollowerInfo = () => { - const {uid} = useCurrentUserState.getState() - logger.info(`getFollowerInfo: init; uid=${uid}`) - if (uid) { - // request follower info in the background - T.RPCGen.configRequestFollowingAndUnverifiedFollowersRpcPromise() - .then(() => {}) - .catch(() => {}) + const updateServerConfig = async () => { + if (s.loggedIn) { + try { + await T.RPCGen.configUpdateLastLoggedInAndServerConfigRpcPromise({ + serverConfigPath: serverConfigFileName, + }) + } catch {} + } + } + + const updateTeams = () => { + useTeamsState.getState().dispatch.getTeams() + useTeamsState.getState().dispatch.refreshTeamRoleMap() } - } - const updateServerConfig = async () => { - if (s.loggedIn) { + const updateSettings = () => { + useSettingsContactsState.getState().dispatch.loadContactImportEnabled() + } + + const updateChat = async () => { + // On login lets load the untrusted inbox. This helps make some flows easier + if (useCurrentUserState.getState().username) { + const {inboxRefresh} = useChatState.getState().dispatch + inboxRefresh('bootstrap') + } try { - await T.RPCGen.configUpdateLastLoggedInAndServerConfigRpcPromise({ - serverConfigPath: serverConfigFileName, - }) + const rows = await T.RPCGen.configGuiGetValueRpcPromise({path: 'ui.inboxSmallRows'}) + const ri = rows.i ?? -1 + if (ri > 0) { + useChatState.getState().dispatch.setInboxNumSmallRows(ri, true) + } } catch {} } - } - const updateTeams = () => { - useTeamsState.getState().dispatch.getTeams() - useTeamsState.getState().dispatch.refreshTeamRoleMap() + getFollowerInfo() + ignorePromise(updateServerConfig()) + updateTeams() + updateSettings() + ignorePromise(updateChat()) } + } - const updateSettings = () => { - useSettingsContactsState.getState().dispatch.loadContactImportEnabled() + if (s.gregorReachable !== old.gregorReachable) { + // Re-get info about our account if you log in/we're done handshaking/became reachable + if (s.gregorReachable === T.RPCGen.Reachable.yes) { + // not in waiting state + if (storeRegistry.getState('daemon').handshakeWaiters.size === 0) { + ignorePromise(storeRegistry.getState('daemon').dispatch.loadDaemonBootstrapStatus()) + } + storeRegistry.getState('teams').dispatch.eagerLoadTeams() } + } - const updateChat = async () => { - // On login lets load the untrusted inbox. This helps make some flows easier - if (useCurrentUserState.getState().username) { - const {inboxRefresh} = useChatState.getState().dispatch - inboxRefresh('bootstrap') - } - try { - const rows = await T.RPCGen.configGuiGetValueRpcPromise({path: 'ui.inboxSmallRows'}) - const ri = rows.i ?? -1 - if (ri > 0) { - useChatState.getState().dispatch.setInboxNumSmallRows(ri, true) - } - } catch {} + if (s.installerRanCount !== old.installerRanCount) { + storeRegistry.getState('fs').dispatch.checkKbfsDaemonRpcStatus() + } + + if (s.loggedIn !== old.loggedIn) { + if (s.loggedIn) { + ignorePromise(storeRegistry.getState('daemon').dispatch.loadDaemonBootstrapStatus()) + storeRegistry.getState('fs').dispatch.checkKbfsDaemonRpcStatus() + } + storeRegistry.getState('daemon').dispatch.loadDaemonAccounts() + if (!s.loggedInCausedbyStartup) { + ignorePromise(storeRegistry.getState('daemon').dispatch.refreshAccounts()) } + } - getFollowerInfo() - ignorePromise(updateServerConfig()) - updateTeams() - updateSettings() - ignorePromise(updateChat()) + if (s.mobileAppState !== old.mobileAppState) { + if (s.mobileAppState === 'background' && storeRegistry.getState('chat').inboxSearch) { + storeRegistry.getState('chat').dispatch.toggleInboxSearch(false) + } } }) } +export const onEngineIncoming = (action: EngineGen.Actions) => { + ArchiveUtil.onEngineIncoming(action) + AutoResetUtil.onEngineIncoming(action) + AvatarUtil.onEngineIncoming(action) + BotsUtil.onEngineIncoming(action) + ChatUtil.onEngineIncoming(action) + storeRegistry.getState('config').dispatch.dynamic.onEngineIncomingDesktop?.(action) + storeRegistry.getState('config').dispatch.dynamic.onEngineIncomingNative?.(action) + storeRegistry.getState('config').dispatch.onEngineIncoming(action) + DeepLinksUtil.onEngineIncoming(action) + DevicesUtil.onEngineIncoming(action) + FollowerUtil.onEngineIncoming(action) + FSUtil.onEngineIncoming(action) + GitUtil.onEngineIncoming(action) + NotifUtil.onEngineIncoming(action) + PeopleUtil.onEngineIncoming(action) + PinentryUtil.onEngineIncoming(action) + SettingsUtil.onEngineIncoming(action) + SignupUtil.onEngineIncoming(action) + TeamsUtil.onEngineIncoming(action) + TrackerUtil.onEngineIncoming(action) + UnlockFoldersUtil.onEngineIncoming(action) + UsersUtil.onEngineIncoming(action) +} + diff --git a/shared/desktop/renderer/main2.desktop.tsx b/shared/desktop/renderer/main2.desktop.tsx index 969a0917143b..1f5a194af580 100644 --- a/shared/desktop/renderer/main2.desktop.tsx +++ b/shared/desktop/renderer/main2.desktop.tsx @@ -8,12 +8,11 @@ import type * as RemoteGen from '@/actions/remote-gen' import Root from './container.desktop' import {makeEngine} from '@/engine' import {disableDragDrop} from '@/util/drag-drop.desktop' -import {dumpLogs} from '@/constants/platform-specific/index.desktop' +import {dumpLogs, eventFromRemoteWindows} from '@/constants/platform-specific/index.desktop' import {initDesktopStyles} from '@/styles/index.desktop' import {isWindows} from '@/constants/platform' import KB2 from '@/util/electron.desktop' import {debugWarning} from '@/util/debug-warning' -import {useConfigState} from '@/constants/config' import type {default as NewMainType} from '../../app/main.desktop' import {setServiceDecoration} from '@/common-adapters/markdown/react' import ServiceDecoration from '@/common-adapters/markdown/service-decoration' @@ -58,7 +57,7 @@ const setupApp = () => { ipcRendererOn?.('KBdispatchAction', (_: unknown, action: unknown) => { setTimeout(() => { try { - useConfigState.getState().dispatch.eventFromRemoteWindows(action as RemoteGen.Actions) + eventFromRemoteWindows(action as RemoteGen.Actions) } catch {} }, 0) }) diff --git a/shared/devices/device-revoke.tsx b/shared/devices/device-revoke.tsx index 374bc6d13354..0d4ead70adde 100644 --- a/shared/devices/device-revoke.tsx +++ b/shared/devices/device-revoke.tsx @@ -97,7 +97,7 @@ const useRevoke = (deviceID = '') => { try { await T.RPCGen.loginDeprovisionRpcPromise({doRevoke: true, username}, C.waitingKeyDevices) load() - useConfigState.getState().dispatch.revoke(deviceName) + useConfigState.getState().dispatch.revoke(deviceName, wasCurrentDevice) } catch {} } else { try { @@ -106,7 +106,7 @@ const useRevoke = (deviceID = '') => { C.waitingKeyDevices ) load() - useConfigState.getState().dispatch.revoke(deviceName) + useConfigState.getState().dispatch.revoke(deviceName, wasCurrentDevice) navUpToScreen( C.isMobile ? (C.isTablet ? C.Tabs.settingsTab : settingsDevicesTab) : C.Tabs.devicesTab )