Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
- Do not add new exported functions, types, or constants unless they are required outside the file. Prefer file-local helpers for one-off implementation details and tests.
- Do not edit lockfiles by hand. They are generated artifacts. If you cannot regenerate one locally, leave it unchanged.
- Components must not mutate Zustand stores directly with `useXState.setState`, `getState()`-based writes, or similar ad hoc store mutation. If a component needs to affect store state, route it through a store dispatch action or move the state out of the store.
- When a Zustand store already uses `resetState: Z.defaultReset`, prefer calling `dispatch.resetState()` for full resets instead of manually reassigning each initial field in another dispatch action.
- During refactors, do not delete existing guards, conditionals, or platform/test-specific behavior unless you have proven they are dead and the user asked for that behavior change. Port checks like `androidIsTestDevice` forward into the new code path instead of silently dropping them.
- When addressing PR or review feedback, including bot or lint-style suggestions, do not apply it mechanically. Verify that the reported issue is real in this codebase and that the proposed fix is consistent with repo rules and improves correctness, behavior, or maintainability before making changes.
- When working from a repo plan or checklist such as `PLAN.md`, update the checklist in the same change and mark implemented items done before you finish.
3 changes: 2 additions & 1 deletion shared/app/index.native.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/// <reference types="webpack-env" />
import * as C from '@/constants'
import {useConfigState} from '@/stores/config'
import {useShellState} from '@/stores/shell'
import * as Kb from '@/common-adapters'
import * as React from 'react'
import Main from './main.native'
Expand Down Expand Up @@ -35,7 +36,7 @@ const useDarkHookup = () => {
const initedRef = React.useRef(false)
const appStateRef = React.useRef('active')
const setSystemDarkMode = DarkMode.useDarkModeState(s => s.dispatch.setSystemDarkMode)
const setMobileAppState = useConfigState(s => s.dispatch.setMobileAppState)
const setMobileAppState = useShellState(s => s.dispatch.setMobileAppState)
const setSystemSupported = DarkMode.useDarkModeState(s => s.dispatch.setSystemSupported)
const setDarkModePreference = DarkMode.useDarkModeState(s => s.dispatch.setDarkModePreference)

Expand Down
6 changes: 3 additions & 3 deletions shared/chat/conversation/normal/container.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import * as C from '@/constants'
import * as ConvoState from '@/stores/convostate'
import {useConfigState} from '@/stores/config'
import {useShellState} from '@/stores/shell'
import * as React from 'react'
import Normal from '.'
import * as T from '@/constants/types'
Expand Down Expand Up @@ -61,15 +61,15 @@ const useOrangeLine = () => {

// just use the rpc for orange line if we're not active
// if we are active we want to keep whatever state we had so it is maintained
const active = useConfigState(s => s.active)
const active = useShellState(s => s.active)
React.useEffect(() => {
if (!active) {
loadOrangeLine()
}
}, [maxVisibleMsgID, active])

// mobile backgrounded us
const mobileAppState = useConfigState(s => s.mobileAppState)
const mobileAppState = useShellState(s => s.mobileAppState)
const lastMobileAppStateRef = React.useRef(mobileAppState)
React.useEffect(() => {
if (mobileAppState !== lastMobileAppStateRef.current) {
Expand Down
4 changes: 2 additions & 2 deletions shared/chat/inbox/use-inbox-search.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import * as C from '@/constants'
import {ignorePromise} from '@/constants/utils'
import * as T from '@/constants/types'
import logger from '@/logger'
import {useConfigState} from '@/stores/config'
import {useShellState} from '@/stores/shell'
import {RPCError} from '@/util/errors'
import {isMobile} from '@/constants/platform'
import * as ConvoState from '@/stores/convostate'
Expand Down Expand Up @@ -151,7 +151,7 @@ export type InboxSearchController = {
}

export function useInboxSearch(): InboxSearchController {
const mobileAppState = useConfigState(s => s.mobileAppState)
const mobileAppState = useShellState(s => s.mobileAppState)
const [isSearching, setIsSearching] = React.useState(false)
const [searchInfo, setSearchInfo] = React.useState(makeInboxSearchInfo)
const activeSearchIDRef = React.useRef(0)
Expand Down
4 changes: 2 additions & 2 deletions shared/constants/chat/common.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import * as T from '../types'
import {getVisibleScreen} from '@/constants/router'
import {useConfigState} from '@/stores/config'
import {useShellState} from '@/stores/shell'
import {isSplit, threadRouteName} from './layout'

export const explodingModeGregorKeyPrefix = 'exploding:'
Expand Down Expand Up @@ -28,7 +28,7 @@ export const isUserActivelyLookingAtThisThread = (conversationIDKey: T.Chat.Conv
(maybeVisibleScreen === undefined ? undefined : maybeVisibleScreen.name) === threadRouteName
}

const {appFocused, active: userActive} = useConfigState.getState()
const {appFocused, active: userActive} = useShellState.getState()

return (
appFocused && // app focused?
Expand Down
22 changes: 12 additions & 10 deletions shared/constants/init/index.desktop.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@
import * as Chat from '@/stores/chat'
import {ignorePromise} from '@/constants/utils'
import {useConfigState} from '@/stores/config'
import * as ConfigConstants from '@/stores/config'
import {useDaemonState} from '@/stores/daemon'
import {useFSState} from '@/stores/fs'
import {openAtLoginKey, useShellState} from '@/stores/shell'
import type * as EngineGen from '@/constants/rpc'
import * as T from '@/constants/types'
import InputMonitor from '@/util/platform-specific/input-monitor.desktop'
Expand All @@ -24,7 +24,7 @@ const {activeChanged, requestWindowsStartService} = KB2.functions
const {quitApp, exitApp, setOpenAtLogin} = KB2.functions

const maybePauseVideos = () => {
const {appFocused} = useConfigState.getState()
const {appFocused} = useShellState.getState()
const videos = document.querySelectorAll('video')
const allVideos = Array.from(videos)

Expand Down Expand Up @@ -142,16 +142,18 @@ export const initPlatformListener = () => {
for (const unsub of _platformUnsubs) unsub()
_platformUnsubs.length = 0

_platformUnsubs.push(useConfigState.subscribe((s, old) => {
_platformUnsubs.push(useShellState.subscribe((s, old) => {
if (s.appFocused === old.appFocused) return
useFSState.getState().dispatch.onChangedFocus(s.appFocused)
}))

_platformUnsubs.push(useConfigState.subscribe((s, old) => {
if (s.loggedIn !== old.loggedIn) {
s.dispatch.osNetworkStatusChanged(navigator.onLine, 'notavailable', true)
useShellState.getState().dispatch.osNetworkStatusChanged(navigator.onLine, 'notavailable', true)
}
}))

_platformUnsubs.push(useShellState.subscribe((s, old) => {
if (s.appFocused !== old.appFocused) {
maybePauseVideos()
}
Expand All @@ -164,7 +166,7 @@ export const initPlatformListener = () => {
return
} else {
await T.RPCGen.configGuiSetValueRpcPromise({
path: ConfigConstants.openAtLoginKey,
path: openAtLoginKey,
value: {b: openAtLogin, isNull: false},
})
}
Expand Down Expand Up @@ -198,7 +200,7 @@ export const initPlatformListener = () => {
if (skipAppFocusActions) {
console.log('Skipping app focus actions!')
} else {
useConfigState.getState().dispatch.changedFocus(appFocused)
useShellState.getState().dispatch.changedFocus(appFocused)
}
}
window.addEventListener('focus', () => handle(true))
Expand All @@ -208,16 +210,16 @@ export const initPlatformListener = () => {

const setupReachabilityWatcher = () => {
window.addEventListener('online', () =>
useConfigState.getState().dispatch.osNetworkStatusChanged(true, 'notavailable')
useShellState.getState().dispatch.osNetworkStatusChanged(true, 'notavailable')
)
window.addEventListener('offline', () =>
useConfigState.getState().dispatch.osNetworkStatusChanged(false, 'notavailable')
useShellState.getState().dispatch.osNetworkStatusChanged(false, 'notavailable')
)
}
setupReachabilityWatcher()

if (isLinux) {
useConfigState.getState().dispatch.initUseNativeFrame()
useShellState.getState().dispatch.initUseNativeFrame()
}

const initializeInputMonitor = () => {
Expand All @@ -226,7 +228,7 @@ export const initPlatformListener = () => {
if (skipAppFocusActions) {
console.log('Skipping app focus actions!')
} else {
useConfigState.getState().dispatch.setActive(userActive)
useShellState.getState().dispatch.setActive(userActive)
// let node thread save file
activeChanged?.(Date.now(), userActive)
}
Expand Down
15 changes: 10 additions & 5 deletions shared/constants/init/index.native.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {useDaemonState} from '@/stores/daemon'
import {useDarkModeState} from '@/stores/darkmode'
import {useFSState} from '@/stores/fs'
import {useRouterState} from '@/stores/router'
import {useShellState} from '@/stores/shell'
import {useSettingsContactsState} from '@/stores/settings-contacts'
import * as T from '@/constants/types'
import type * as EngineGen from '@/constants/rpc'
Expand Down Expand Up @@ -243,7 +244,7 @@ export const onEngineIncoming = (action: EngineGen.Actions) => {
}

export const initPlatformListener = () => {
useConfigState.subscribe((s, old) => {
useShellState.subscribe((s, old) => {
if (s.mobileAppState === old.mobileAppState) return
let appFocused: boolean
let logState: T.RPCGen.MobileAppState
Expand Down Expand Up @@ -311,12 +312,16 @@ export const initPlatformListener = () => {
if (s.loggedIn === old.loggedIn) return
const f = async () => {
const {type} = await NetInfo.fetch()
s.dispatch.osNetworkStatusChanged(type !== NetInfo.NetInfoStateType.none, type, true)
useShellState.getState().dispatch.osNetworkStatusChanged(
type !== NetInfo.NetInfoStateType.none,
type,
true
)
}
ignorePromise(f())
})

useConfigState.subscribe((s, old) => {
useShellState.subscribe((s, old) => {
if (s.networkStatus === old.networkStatus) return
const type = s.networkStatus?.type
if (!type) return
Expand All @@ -330,7 +335,7 @@ export const initPlatformListener = () => {
ignorePromise(f())
})

useConfigState.subscribe((s, old) => {
useShellState.subscribe((s, old) => {
if (s.mobileAppState === old.mobileAppState) return
if (s.mobileAppState === 'active') {
// only reload on foreground
Expand Down Expand Up @@ -382,7 +387,7 @@ export const initPlatformListener = () => {
initPushListener()

NetInfo.addEventListener(({type}) => {
useConfigState.getState().dispatch.osNetworkStatusChanged(type !== NetInfo.NetInfoStateType.none, type)
useShellState.getState().dispatch.osNetworkStatusChanged(type !== NetInfo.NetInfoStateType.none, type)
})

const initAudioModes = () => {
Expand Down
3 changes: 2 additions & 1 deletion shared/constants/init/push-listener.native.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {useConfigState} from '@/stores/config'
import {useCurrentUserState} from '@/stores/current-user'
import {useLogoutState} from '@/stores/logout'
import {usePushState} from '@/stores/push'
import {useShellState} from '@/stores/shell'

type DataCommon = {
userInteraction: boolean
Expand Down Expand Up @@ -172,7 +173,7 @@ const getStartupDetailsFromInitialPush = async () => {

export const initPushListener = () => {
// Permissions
useConfigState.subscribe((s, old) => {
useShellState.subscribe((s, old) => {
if (s.mobileAppState === old.mobileAppState) return
// Only recheck on foreground, not background
if (s.mobileAppState !== 'active') {
Expand Down
58 changes: 42 additions & 16 deletions shared/constants/init/shared.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ import {useFSState} from '@/stores/fs'
import {useModalHeaderState} from '@/stores/modal-header'
import {usePeopleState} from '@/stores/people'
import {useProvisionState} from '@/stores/provision'
import {useShellState} from '@/stores/shell'
import {useSettingsEmailState} from '@/stores/settings-email'
import {useSettingsPhoneState} from '@/stores/settings-phone'
import {useSettingsContactsState} from '@/stores/settings-contacts'
Expand All @@ -70,6 +71,39 @@ import {clearSignupDeviceNameDraft} from '@/signup/device-name-draft'
let _emitStartupOnLoadDaemonConnectedOnce: boolean = __DEV__ ? (globalThis.__hmr_startupOnce ?? false) : false

const _sharedUnsubs: Array<() => void> = __DEV__ ? (globalThis.__hmr_sharedUnsubs ??= []) : []
const getAccountsWaitKey = 'config.getAccounts'

const loadConfiguredAccountsForBootstrap = () => {
const configState = useConfigState.getState()
if (configState.configuredAccounts.length) {
return
}

const version = useDaemonState.getState().handshakeVersion
const handshakeWait = !configState.loggedIn
const refreshAccounts = configState.dispatch.refreshAccounts
const {wait} = useDaemonState.getState().dispatch

const f = async () => {
try {
if (handshakeWait) {
wait(getAccountsWaitKey, version, true)
}

await refreshAccounts()

if (handshakeWait && useDaemonState.getState().handshakeWaiters.get(getAccountsWaitKey)) {
wait(getAccountsWaitKey, version, false)
}
} catch {
if (handshakeWait && useDaemonState.getState().handshakeWaiters.get(getAccountsWaitKey)) {
wait(getAccountsWaitKey, version, false, "Can't get accounts")
}
}
}

ignorePromise(f())
}

export const onEngineConnected = () => {
{
Expand Down Expand Up @@ -204,22 +238,14 @@ export const initSharedSubscriptions = () => {
clearSignupEmail()
clearSignupDeviceNameDraft()
}
useDaemonState.getState().dispatch.loadDaemonAccounts(
s.configuredAccounts.length,
s.loggedIn,
useConfigState.getState().dispatch.refreshAccounts
)
loadConfiguredAccountsForBootstrap()
if (!s.loggedInCausedbyStartup) {
ignorePromise(useConfigState.getState().dispatch.refreshAccounts())
}
}

if (s.revokedTrigger !== old.revokedTrigger) {
useDaemonState.getState().dispatch.loadDaemonAccounts(
s.configuredAccounts.length,
s.loggedIn,
useConfigState.getState().dispatch.refreshAccounts
)
loadConfiguredAccountsForBootstrap()
}

if (s.configuredAccounts !== old.configuredAccounts) {
Expand All @@ -232,6 +258,11 @@ export const initSharedSubscriptions = () => {
}
}

})
)

_sharedUnsubs.push(
useShellState.subscribe((s, old) => {
if (s.active !== old.active) {
const cs = getConvoState(getSelectedConversation())
cs.dispatch.markThreadAsRead()
Expand All @@ -244,12 +275,7 @@ export const initSharedSubscriptions = () => {
if (s.handshakeVersion !== old.handshakeVersion) {
useDarkModeState.getState().dispatch.loadDarkPrefs()
useChatState.getState().dispatch.loadStaticConfig()
const configState = useConfigState.getState()
s.dispatch.loadDaemonAccounts(
configState.configuredAccounts.length,
configState.loggedIn,
useConfigState.getState().dispatch.refreshAccounts
)
loadConfiguredAccountsForBootstrap()
}

if (s.bootstrapStatus !== old.bootstrapStatus) {
Expand Down
7 changes: 4 additions & 3 deletions shared/desktop/renderer/main2.desktop.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {initDesktopStyles} from '@/styles/index.desktop'
import {isWindows} from '@/constants/platform'
import KB2 from '@/util/electron.desktop'
import {useConfigState} from '@/stores/config'
import {useShellState} from '@/stores/shell'
import {setServiceDecoration} from '@/common-adapters/markdown/react'
import ServiceDecoration from '@/common-adapters/markdown/service-decoration'
import {useDarkModeState} from '@/stores/darkmode'
Expand Down Expand Up @@ -72,9 +73,9 @@ const setupApp = async () => {
// issuing RPCs on a renderer reload.
await appStartedUp?.()

useConfigState.getState().dispatch.initNotifySound()
useConfigState.getState().dispatch.initForceSmallNav()
useConfigState.getState().dispatch.initOpenAtLogin()
useShellState.getState().dispatch.initNotifySound()
useShellState.getState().dispatch.initForceSmallNav()
useShellState.getState().dispatch.initOpenAtLogin()
useConfigState.getState().dispatch.initAppUpdateLoop()
eng.listenersAreReady()

Expand Down
Loading