Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
d66deba
move simple stores out of registry
chrisnojima-zoom Jan 6, 2026
f34ebb3
WIP
chrisnojima Jan 6, 2026
e73bc17
more subs (#28758)
chrisnojima-zoom Jan 6, 2026
951deb7
Merge remote-tracking branch 'origin/master' into nojima/ZCLIENT-stor…
chrisnojima Jan 7, 2026
7c4bfb1
WIP: shift 8 (#28771)
chrisnojima-zoom Jan 7, 2026
d4442ad
Merge remote-tracking branch 'origin/master' into nojima/ZCLIENT-stor…
chrisnojima-zoom Jan 7, 2026
9dca46e
WIP
chrisnojima-zoom Jan 7, 2026
10bd236
WIP
chrisnojima Jan 7, 2026
90f2d32
WIP
chrisnojima Jan 7, 2026
77bcd53
WIP2: shift 9 (#28776)
chrisnojima-zoom Jan 9, 2026
e39f691
Merge remote-tracking branch 'origin/master' into nojima/ZCLIENT-stor…
chrisnojima Jan 9, 2026
b76e1d1
WIP: shift 10 (#28787)
chrisnojima-zoom Jan 9, 2026
a9b4317
WIP shift 11 (#28788)
chrisnojima-zoom Jan 9, 2026
17f1fcc
WIP: shift 12 (#28794)
chrisnojima-zoom Jan 12, 2026
b7fab69
WIP: shift 13 (#28795)
chrisnojima-zoom Jan 12, 2026
4c78cb3
Merge remote-tracking branch 'origin/master' into nojima/ZCLIENT-stor…
chrisnojima Jan 12, 2026
28f5bd3
WIP: shift 14 (#28797)
chrisnojima-zoom Jan 12, 2026
29bf146
WIP: shift 15 (#28799)
chrisnojima-zoom Jan 12, 2026
035010e
WIP: shift 16 (#28800)
chrisnojima-zoom Jan 13, 2026
a196949
Merge remote-tracking branch 'origin/master' into nojima/ZCLIENT-stor…
chrisnojima Jan 14, 2026
6c42322
WIP: shift 17 (#28803)
chrisnojima-zoom Jan 15, 2026
4eb50cd
Merge remote-tracking branch 'origin/master' into nojima/ZCLIENT-stor…
chrisnojima Jan 15, 2026
ad88d4e
Merge remote-tracking branch 'origin/master' into nojima/ZCLIENT-stor…
chrisnojima Jan 15, 2026
45bbb80
Merge remote-tracking branch 'origin/master' into nojima/ZCLIENT-stor…
chrisnojima Jan 16, 2026
7ac34f2
WIP: shift 18 (#28811)
chrisnojima-zoom Jan 16, 2026
34745ea
WIP
chrisnojima-zoom Jan 27, 2026
1666328
WIP: shift 20 defer dynamic (#28837)
chrisnojima-zoom Jan 28, 2026
9f5001e
WIP: shift 21 (#28841)
chrisnojima-zoom Feb 10, 2026
78cdea5
Merge master into nojima/ZCLIENT-store-shift-3
chrisnojima-zoom Feb 10, 2026
51a8e7d
Merge remote-tracking branch 'origin/master' into nojima/ZCLIENT-stor…
chrisnojima Feb 12, 2026
4ed0562
WIP
chrisnojima Feb 12, 2026
6e2444c
WIP
chrisnojima Feb 12, 2026
192f1af
WIP
chrisnojima Feb 12, 2026
14669b5
cross env
chrisnojima Feb 13, 2026
4fe27d2
deps and remove qr (#28896)
chrisnojima Feb 13, 2026
522fd8c
WIP
chrisnojima Feb 13, 2026
f92dc4a
merge global errors into one (#28897)
chrisnojima Feb 13, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
2 changes: 1 addition & 1 deletion go/libkb/version.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@
package libkb

// Version is the current version (should be MAJOR.MINOR.PATCH)
const Version = "6.6.0"
const Version = "6.7.0"
2 changes: 1 addition & 1 deletion shared/android/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ apply plugin: "com.facebook.react"
apply plugin: 'com.github.triplet.play'

// KB: app version
def VERSION_NAME = "6.6.0"
def VERSION_NAME = "6.7.0"

// KB: Number of commits, like ios
Integer getVersionCode() {
Expand Down
332 changes: 332 additions & 0 deletions shared/app/global-errors.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,332 @@
import * as C from '@/constants'
import * as Kb from '@/common-adapters'
import * as React from 'react'
import logger from '@/logger'
import {ignoreDisconnectOverlay} from '@/local-debug'
import {useConfigState} from '@/stores/config'
import type {RPCError} from '@/util/errors'
import {settingsFeedbackTab} from '@/constants/settings'
import {useDaemonState} from '@/stores/daemon'

type Size = 'Closed' | 'Small' | 'Big'

const summaryForError = (err?: Error | RPCError) => err?.message ?? ''
const detailsForError = (err?: Error | RPCError) => err?.stack ?? ''

const maxHeightForSize = (size: Size) => {
return {
Big: 900,
Closed: 0,
Small: 35,
}[size]
}

const useData = () => {
const loggedIn = useConfigState(s => s.loggedIn)
const daemonError = useDaemonState(s => s.error)
const error = useConfigState(s => s.globalError)
const setGlobalError = useConfigState(s => s.dispatch.setGlobalError)
const clearModals = C.useRouterState(s => s.dispatch.clearModals)
const navigateAppend = C.useRouterState(s => s.dispatch.navigateAppend)
const onFeedback = React.useCallback(() => {
setGlobalError()
if (loggedIn) {
clearModals()
navigateAppend(settingsFeedbackTab)
} else {
navigateAppend('feedback')
}
}, [navigateAppend, clearModals, loggedIn, setGlobalError])
const onDismiss = React.useCallback(() => {
setGlobalError()
}, [setGlobalError])

const [cachedSummary, setSummary] = React.useState(summaryForError(error))
const [cachedDetails, setDetails] = React.useState(detailsForError(error))
const [size, setSize] = React.useState<Size>('Closed')
const countdownTimerRef = React.useRef<undefined | ReturnType<typeof setTimeout>>(undefined)

const clearCountdown = React.useCallback(() => {
countdownTimerRef.current && clearTimeout(countdownTimerRef.current)
countdownTimerRef.current = undefined
}, [countdownTimerRef])

const onExpandClick = React.useCallback(() => {
setSize('Big')
if (!C.isMobile) {
clearCountdown()
}
}, [clearCountdown])

const resetError = React.useCallback(
(newError: boolean) => {
setSize(newError ? 'Small' : 'Closed')
if (!C.isMobile) {
clearCountdown()
if (newError) {
countdownTimerRef.current = setTimeout(() => {
onDismiss()
}, 10000)
}
}
},
[clearCountdown, onDismiss]
)

C.useOnUnMountOnce(() => {
clearCountdown()
})

C.useOnMountOnce(() => {
resetError(!!error)
})

React.useEffect(() => {
const id = setTimeout(
() => {
setDetails(detailsForError(error))
if (!C.isMobile) {
setSummary(summaryForError(error))
}
},
error ? 0 : 7000
) // if it's set, do it immediately, if it's cleared set it in a bit
resetError(!!error)
return () => {
clearTimeout(id)
}
}, [error, resetError])

return {
cachedDetails,
cachedSummary,
daemonError,
error,
onDismiss,
onExpandClick,
onFeedback,
size,
}
}

const GlobalError = () => {
const d = useData()
const {daemonError, error, onDismiss, onFeedback} = d
const {cachedDetails, cachedSummary, size, onExpandClick} = d

if (size === 'Closed') {
return null
}

if (!daemonError && !error) {
return null
}

if (daemonError) {
if (C.isMobile) {
return null
}
if (ignoreDisconnectOverlay) {
logger.warn('Ignoring disconnect overlay')
return null
}

const message = daemonError.message || 'Keybase is currently unreachable. Trying to reconnect you…'
return (
<Kb.Box2 direction="vertical" style={styles.containerOverlay}>
<Kb.Box2 direction="horizontal" style={styles.overlayRow}>
<Kb.Text center={true} type="BodySmallSemibold" style={styles.message}>
{message}
</Kb.Text>
</Kb.Box2>
<Kb.Box2 direction="vertical" style={styles.overlayFill}>
<Kb.Animation animationType="disconnected" height={175} width={600} />
</Kb.Box2>
</Kb.Box2>
)
}

if (C.isMobile) {
return (
<Kb.Box2
direction="vertical"
style={Kb.Styles.collapseStyles([
styles.mobileContainer,
size === 'Big' && Kb.Styles.globalStyles.fillAbsolute,
])}
>
<Kb.SafeAreaViewTop style={styles.mobileSafeAreaView} />
<Kb.Box2 direction="vertical">
<Kb.Box2
direction="horizontal"
style={Kb.Styles.collapseStyles([styles.mobileSummaryRow, styles.mobileErrorTextContainer])}
>
<Kb.Text
center={true}
type="BodySmallSemibold"
style={styles.mobileErrorText}
onClick={onExpandClick}
>
{size !== 'Big' && (
<Kb.Icon
type="iconfont-caret-right"
color={Kb.Styles.globalColors.white_75}
sizeType="Tiny"
/>
)}
{' '}
An error occurred.
</Kb.Text>
<Kb.Icon
type="iconfont-close"
onClick={onDismiss}
color={Kb.Styles.globalColors.white_75}
fontSize={21}
/>
</Kb.Box2>
<Kb.Box2 direction="horizontal" style={styles.mobileSummaryRow}>
<Kb.Button fullWidth={true} label="Please tell us" onClick={onFeedback} small={true} type="Dim" />
</Kb.Box2>
</Kb.Box2>
{size === 'Big' && (
<Kb.ScrollView>
<Kb.Text type="BodySmall" selectable={true} style={styles.mobileDetails}>
{error?.message}
{'\n\n'}
{cachedDetails}
</Kb.Text>
</Kb.ScrollView>
)}
</Kb.Box2>
)
}

const summary = cachedSummary
const details = cachedDetails

let stylesContainer: Kb.Styles.StylesCrossPlatform
switch (size) {
case 'Big':
stylesContainer = styles.containerBig
break
case 'Small':
stylesContainer = styles.containerSmall
break
}

return (
<Kb.ClickableBox style={stylesContainer} onClick={onExpandClick}>
<Kb.Box2 direction="horizontal" style={styles.innerContainer}>
<Kb.Text center={true} type="BodyBig" style={styles.summary}>
{summary}
</Kb.Text>
<Kb.Button
label="Please tell us"
onClick={onFeedback}
small={true}
type="Dim"

/>
{summary && (
<Kb.Icon
color={Kb.Styles.globalColors.white_75}
hoverColor={Kb.Styles.globalColors.white}
onClick={onDismiss}
type="iconfont-close"
/>
)}
</Kb.Box2>
<Kb.ScrollView>
<Kb.Text center={true} type="BodyBig" selectable={true} style={styles.details}>
{details}
</Kb.Text>
</Kb.ScrollView>
</Kb.ClickableBox>
)
}

const styles = Kb.Styles.styleSheetCreate(() => {
const containerBase = {
left: 0,
overflow: 'hidden' as const,
position: 'absolute' as const,
right: 0,
top: 40,
zIndex: 1000,
...Kb.Styles.transition('max-height'),
}

return {
containerBig: Kb.Styles.platformStyles({
isElectron: {...containerBase, maxHeight: maxHeightForSize('Big')},
}),
containerOverlay: {
...Kb.Styles.globalStyles.fillAbsolute,
zIndex: 1000,
},
containerSmall: Kb.Styles.platformStyles({
isElectron: {...containerBase, maxHeight: maxHeightForSize('Small')},
}),
details: {
backgroundColor: Kb.Styles.globalColors.black,
color: Kb.Styles.globalColors.white_75,
...Kb.Styles.padding(8, Kb.Styles.globalMargins.xlarge),
},
innerContainer: {
...Kb.Styles.globalStyles.flexBoxCenter,
backgroundColor: Kb.Styles.globalColors.black,
flex: 1,
gap: Kb.Styles.globalMargins.small,
minHeight: maxHeightForSize('Small'),
...Kb.Styles.padding(Kb.Styles.globalMargins.xtiny, Kb.Styles.globalMargins.small),
},
message: {
color: Kb.Styles.globalColors.white,
},
mobileContainer: {
backgroundColor: Kb.Styles.globalColors.black,
position: 'absolute',
top: 0,
},
mobileDetails: {
color: Kb.Styles.globalColors.white_75,
fontSize: 14,
lineHeight: 19,
...Kb.Styles.padding(Kb.Styles.globalMargins.tiny, Kb.Styles.globalMargins.xtiny, Kb.Styles.globalMargins.xtiny),
},
mobileErrorText: {
color: Kb.Styles.globalColors.white,
flex: 1,
},
mobileErrorTextContainer: {
paddingBottom: Kb.Styles.globalMargins.xtiny,
position: 'relative',
},
mobileSafeAreaView: {
backgroundColor: Kb.Styles.globalColors.transparent,
flexGrow: 0,
},
mobileSummaryRow: {
alignItems: 'center',
flexShrink: 0,
justifyContent: 'center',
...Kb.Styles.padding(Kb.Styles.globalMargins.tiny, Kb.Styles.globalMargins.xsmall),
},
overlayFill: {
...Kb.Styles.globalStyles.flexBoxCenter,
backgroundColor: Kb.Styles.globalColors.white,
flex: 1,
},
overlayRow: {
...Kb.Styles.globalStyles.flexBoxCenter,
backgroundColor: Kb.Styles.globalColors.blue,
padding: 8,
},
summary: {
color: Kb.Styles.globalColors.white,
flex: 1,
},
} as const
})

export default GlobalError
Loading