/
githubmirror
/
client
Обзор
Документация
Войти
/
githubmirror
/
client
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
shared/settings/advanced.tsx
460 строк
14 KB
chrisnojima
refactor(styles): theme via hooks, no android remount (#29515)
08 авг 2026, 02:24
Не верифицирован
08 авг 2026, 02:24
a41d38e
Код
Авторство
О чём код?
import * as C from '@/constants' import * as Kb from '@/common-adapters' import * as T from '@/constants/types' import * as React from 'react' import * as TestIDs from '@/tests/e2e/shared/test-ids' import {ProxySettings} from './proxy' import {processorProfileInProgressKey, traceInProgressKey} from '@/constants/settings' import {useConfigState} from '@/stores/config' import {useShellState} from '@/stores/shell' import {ignorePromise, timeoutPromise} from '@/constants/utils' import {pprofDir} from '@/constants/platform' import {clearLocalLogs} from '@/util/misc' import {useWaitingState} from '@/stores/waiting' import {useRandomPWState} from './use-random-pw' import {useRPCLoad} from '@/util/use-rpc-load' let initialUseNativeFrame: boolean | undefined const showMakeIcons = __DEV__ && (false as boolean) const runPprofAction = ( rpc: () => Promise<void>, waitingKey: string, durationSeconds: number ) => { const f = async () => { await rpc() const {decrement, increment} = useWaitingState.getState().dispatch increment(waitingKey) await timeoutPromise(durationSeconds * 1_000) decrement(waitingKey) } ignorePromise(f()) } const useLockdownMode = () => { const [lockdownModeEnabled, setLockdownModeEnabled] = React.useState<boolean | undefined>(undefined) const loadLockdownModeRPC = C.useRPC(T.RPCGen.accountGetLockdownModeRpcPromise) const setLockdownModeRPC = C.useRPC(T.RPCGen.accountSetLockdownModeRpcPromise) const loadLockdownMode = React.useCallback(() => { if (!useConfigState.getState().loggedIn) { return } loadLockdownModeRPC( [undefined], result => { setLockdownModeEnabled(result.status) }, () => { setLockdownModeEnabled(undefined) } ) }, [loadLockdownModeRPC]) const setLockdownMode = React.useCallback( (enabled: boolean) => { if (!useConfigState.getState().loggedIn) { return } setLockdownModeRPC( [{enabled}, C.waitingKeySettingsSetLockdownMode], () => { setLockdownModeEnabled(enabled) }, () => { setLockdownModeEnabled(undefined) } ) }, [setLockdownModeRPC] ) return {loadLockdownMode, lockdownModeEnabled, setLockdownMode} } const UseNativeFrame = () => { const styles = useStyles() const {onChangeUseNativeFrame, useNativeFrame} = useShellState( C.useShallow(s => ({ onChangeUseNativeFrame: s.dispatch.setUseNativeFrame, useNativeFrame: s.useNativeFrame, })) ) React.useEffect(() => { if (initialUseNativeFrame === undefined) { initialUseNativeFrame = useNativeFrame } }, [useNativeFrame]) return isMobile ? null : ( <> <Kb.Checkbox checked={!useNativeFrame} label="Hide system window frame" onCheck={x => onChangeUseNativeFrame(!x)} /> {initialUseNativeFrame !== useNativeFrame && ( <Kb.Text type="BodySmall" style={styles.error}> Keybase needs to restart for this change to take effect. </Kb.Text> )} </> ) } const LockdownCheckbox = (p: { hasRandomPW: boolean loaded: boolean lockdownModeEnabled?: boolean setLockdownMode: (enabled: boolean) => void settingLockdownMode: boolean }) => { const theme = Kb.Styles.useTheme() const {hasRandomPW, loaded, lockdownModeEnabled, setLockdownMode, settingLockdownMode} = p const readMoreUrlProps = Kb.useClickURL('https://keybase.io/docs/lockdown/index') const label = 'Enable account lockdown mode' + (hasRandomPW ? ' (you need to set a password first)' : '') const checked = hasRandomPW || !!lockdownModeEnabled const disabled = !loaded || hasRandomPW || settingLockdownMode return ( <Kb.Checkbox checked={checked} disabled={disabled} onCheck={setLockdownMode} labelComponent={ <Kb.Box2 direction="vertical" alignItems="flex-start" style={Kb.Styles.globalStyles.flexOne}> <Kb.Text type="Body">{label}</Kb.Text> <Kb.Text type="BodySmall">Prevent making account changes from the website.</Kb.Text> <Kb.Text type="BodySmall"> With this setting on you will not be able to reset your account, even from the app. Protect your account by installing Keybase on several devices, or by keeping a paper key in a safe place. </Kb.Text> <Kb.Text type="BodySmallPrimaryLink" {...readMoreUrlProps}> Read more{' '} <Kb.Icon type="iconfont-open-browser" sizeType="Tiny" color={theme.blueDark} /> </Kb.Text> </Kb.Box2> } /> ) } // app-launch value, module level so a remount keeps showing "(restart required)" let disableSpellCheckInitialValue: boolean | undefined const stampDisableSpellCheckInitialValue = (value: boolean) => { if (disableSpellCheckInitialValue === undefined) { disableSpellCheckInitialValue = value } } const Advanced = () => { const styles = useStyles() const settingLockdownMode = C.Waiting.useAnyWaiting(C.waitingKeySettingsSetLockdownMode) const {loaded: randomPWLoaded, randomPW} = useRandomPWState() const hasRandomPW = !!randomPW const {onSetOpenAtLogin, openAtLogin} = useShellState( C.useShallow(s => ({ onSetOpenAtLogin: s.dispatch.setOpenAtLogin, openAtLogin: s.openAtLogin, })) ) const {loadLockdownMode, lockdownModeEnabled, setLockdownMode} = useLockdownMode() const setLockdownModeError = C.Waiting.useAnyErrors(C.waitingKeySettingsSetLockdownMode)?.message || '' const submitRememberPassword = C.useRPC(T.RPCGen.configSetRememberPassphraseRpcPromise) const {data: disableSpellCheck, setData: setDisableSpellcheck} = useRPCLoad( T.RPCGen.configGuiGetValueRpcPromise, [{path: 'ui.disableSpellCheck'}], { map: result => { const res = result.b ?? false stampDisableSpellCheckInitialValue(res) return res }, onError: () => { stampDisableSpellCheckInitialValue(false) setDisableSpellcheck(false) }, } ) const {data: rememberPassword, setData: setRememberPassword} = useRPCLoad( T.RPCGen.configGetRememberPassphraseRpcPromise, [undefined], { map: remember => remember, onError: () => setRememberPassword(true), } ) const submitDisableSpellcheck = C.useRPC(T.RPCGen.configGuiSetValueRpcPromise) const onChangeRememberPassword = (remember: boolean) => { const previous = rememberPassword setRememberPassword(remember) submitRememberPassword( [{remember}], () => {}, () => { setRememberPassword(previous) } ) } const onToggleDisableSpellcheck = () => { const next = !disableSpellCheck setDisableSpellcheck(next) submitDisableSpellcheck( [ { path: 'ui.disableSpellCheck', value: {b: next, isNull: false}, }, ], () => {}, () => { console.log('cant save spell check?') setDisableSpellcheck(!next) } ) } React.useEffect(() => { loadLockdownMode() }, [loadLockdownMode]) return ( <Kb.KeyboardAvoidingView2> <Kb.ScrollView style={Kb.Styles.globalStyles.fullWidth} testID={TestIDs.SETTINGS_ADVANCED}> <Kb.Box2 direction="vertical" fullWidth={true}> <Kb.Box2 direction="vertical" gap="tiny" fullWidth={true} style={styles.section}> {settingLockdownMode && <Kb.ProgressIndicator />} <LockdownCheckbox hasRandomPW={hasRandomPW} loaded={randomPWLoaded} lockdownModeEnabled={lockdownModeEnabled} setLockdownMode={setLockdownMode} settingLockdownMode={settingLockdownMode} /> {!!setLockdownModeError && ( <Kb.Text type="BodySmall" style={styles.error}> {setLockdownModeError} </Kb.Text> )} {randomPWLoaded && !hasRandomPW && ( <Kb.Checkbox checked={!!rememberPassword} disabled={rememberPassword === undefined} labelComponent={ <Kb.Box2 direction="vertical" style={Kb.Styles.globalStyles.flexOne}> <Kb.Text type="Body">Always stay logged in</Kb.Text> <Kb.Text type="BodySmall"> {"You won't be asked for your password when restarting the app or your device."} </Kb.Text> </Kb.Box2> } onCheck={onChangeRememberPassword} /> )} {C.isLinux ? <UseNativeFrame /> : null} {!isMobile && ( <Kb.Checkbox label="Open Keybase on startup" checked={openAtLogin} onCheck={onSetOpenAtLogin} /> )} {!isMobile && ( <Kb.Checkbox label={ 'Disable spellchecking' + (disableSpellCheckInitialValue !== undefined && disableSpellCheckInitialValue !== disableSpellCheck ? ' (restart required)' : '') } disabled={disableSpellCheck === undefined} checked={!!disableSpellCheck} onCheck={onToggleDisableSpellcheck} /> )} </Kb.Box2> <Kb.Divider style={styles.proxyDivider} /> <Kb.Box2 direction="vertical" fullWidth={true} style={Kb.Styles.collapseStyles([styles.section, {paddingTop: 0}])} > <ProxySettings /> </Kb.Box2> <Developer /> </Kb.Box2> </Kb.ScrollView> </Kb.KeyboardAvoidingView2> ) } const clickThreshold = 7 const traceDurationSeconds = 30 const processorProfileDurationSeconds = 30 const Developer = () => { const styles = useStyles() const [clickCount, setClickCount] = React.useState(0) const onExtraKBFSLogging = () => { const f = async () => { await T.RPCGen.SimpleFSSimpleFSSetDebugLevelRpcPromise({level: 'vlog2'}) } ignorePromise(f()) } const onToggleRuntimeStats = useConfigState(s => s.dispatch.toggleRuntimeStats) const onLabelClick = () => setClickCount(s => { const next = s + 1 if (next < clickThreshold) { console.log( `clickCount = ${clickCount} (${clickThreshold - clickCount} away from showing developer controls)` ) } return next }) const showPprofControls = clickCount >= clickThreshold const traceInProgress = C.Waiting.useAnyWaiting(traceInProgressKey) const onProcessorProfile = () => { runPprofAction( async () => T.RPCGen.pprofLogProcessorProfileRpcPromise({ logDirForMobile: pprofDir, profileDurationSeconds: processorProfileDurationSeconds, }), processorProfileInProgressKey, processorProfileDurationSeconds ) } const onTrace = () => { runPprofAction( async () => T.RPCGen.pprofLogTraceRpcPromise({ logDirForMobile: pprofDir, traceDurationSeconds, }), traceInProgressKey, traceDurationSeconds ) } const processorProfileInProgress = C.Waiting.useAnyWaiting(processorProfileInProgressKey) const navigateAppend = C.Router2.navigateAppend const onDBNuke = () => navigateAppend({name: 'dbNukeConfirm', params: {}}) const onMakeIcons = () => navigateAppend({name: 'makeIcons', params: {}}) const onClearLogs = () => { const f = async () => { await clearLocalLogs() } ignorePromise(f()) } return ( <Kb.Box2 direction="vertical" fullWidth={true} alignItems="center" flex={1} style={styles.developerContainer}> <Kb.Text center={true} type="BodySmallSemibold" onClick={onLabelClick} style={styles.text}> {"Please don't do anything below here unless instructed to by a developer."} </Kb.Text> <Kb.Divider style={styles.divider} /> <Kb.Button style={styles.developerButtons} type="Danger" label="DB Nuke" onClick={onDBNuke} /> {isIOS ? ( <Kb.Button style={styles.developerButtons} mode="Secondary" label="Clear Logs" onClick={onClearLogs} /> ) : null} <Kb.Button style={styles.developerButtons} mode="Secondary" label="Enable Detailed Logging" onClick={onExtraKBFSLogging} /> {showMakeIcons && ( <Kb.Button style={styles.developerButtons} mode="Secondary" label="Make Icons" onClick={onMakeIcons} /> )} {showPprofControls && ( <> <Kb.Button label="Toggle Runtime Stats" onClick={onToggleRuntimeStats} mode="Secondary" style={styles.developerButtons} /> <Kb.Button waiting={traceInProgress} style={{marginTop: Kb.Styles.globalMargins.small}} type="Danger" label={`Trace (${traceDurationSeconds}s)`} onClick={onTrace} /> <Kb.Button waiting={processorProfileInProgress} style={{marginTop: Kb.Styles.globalMargins.small}} type="Danger" label={`CPU Profile (${processorProfileDurationSeconds}s)`} onClick={onProcessorProfile} /> <Kb.Text center={true} type="BodySmallSemibold" style={styles.text}> Trace and profile files are included in logs sent with feedback. </Kb.Text> </> )} <Kb.Box2 direction="vertical" flex={1} /> </Kb.Box2> ) } const useStyles = Kb.Styles.createStyleHook( theme => ({ developerButtons: { marginTop: Kb.Styles.globalMargins.small, }, developerContainer: { paddingBottom: Kb.Styles.globalMargins.medium, }, divider: { marginTop: Kb.Styles.globalMargins.xsmall, width: '100%', }, error: { color: theme.redDark, }, proxyDivider: { ...Kb.Styles.marginV(Kb.Styles.globalMargins.small), width: '100%', }, section: Kb.Styles.platformStyles({ common: { ...Kb.Styles.padding( Kb.Styles.globalMargins.small, Kb.Styles.globalMargins.mediumLarge, Kb.Styles.globalMargins.medium, Kb.Styles.globalMargins.small ), }, isElectron: { maxWidth: 600, }, }), text: Kb.Styles.platformStyles({ isElectron: { cursor: 'default', }, }), }) as const ) export default Advanced