Skip to content

Commit d0ba1ca

Browse files
authored
simpler native router (#28466)
1 parent 835b476 commit d0ba1ca

File tree

17 files changed

+622
-745
lines changed

17 files changed

+622
-745
lines changed

shared/common-adapters/header-hoc/index.native.tsx

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -272,15 +272,12 @@ export const HeaderLeftArrow = React.memo(function HeaderLeftArrow(hp: {
272272
) : null
273273
})
274274

275-
export const HeaderLeftArrowCanGoBack = (hp: {
276-
canGoBack?: boolean
277-
tintColor?: string
278-
onPress?: () => void
279-
badgeNumber?: number
280-
}) => {
281-
const canGoBack = useNavigation().canGoBack()
282-
return <HeaderLeftArrow {...hp} canGoBack={canGoBack} />
283-
}
275+
export const HeaderLeftArrowCanGoBack = React.memo(
276+
(hp: {canGoBack?: boolean; tintColor?: string; onPress?: () => void; badgeNumber?: number}) => {
277+
const canGoBack = useNavigation().canGoBack()
278+
return <HeaderLeftArrow {...hp} canGoBack={canGoBack} />
279+
}
280+
)
284281

285282
export const HeaderLeftCancel = React.memo(function HeaderLeftCancel(hp: {
286283
canGoBack?: boolean

shared/constants/chat2/convostate.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3168,6 +3168,10 @@ const createSlice: Z.ImmerStateCreator<ConvoState> = (set, get) => {
31683168
dispatch,
31693169
getConvID: () => {
31703170
const id = get().id
3171+
if (!T.Chat.isValidConversationIDKey(id)) {
3172+
return new Uint8Array(0)
3173+
}
3174+
31713175
const cached = convIDCache.get(id)
31723176
if (cached) return cached
31733177
const cid = T.Chat.keyToConversationID(id)

shared/constants/chat2/index.tsx

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1785,19 +1785,17 @@ export const useState_ = Z.createZustand<State>((set, get) => {
17851785
}
17861786

17871787
// Get valid keys that we aren't already loading or have loaded
1788-
const conversationIDKeys = force
1789-
? ids
1790-
: ids.reduce((arr: Array<string>, id) => {
1791-
if (id && T.Chat.isValidConversationIDKey(id)) {
1792-
const cs = C.getConvoState(id)
1793-
const trustedState = cs.meta.trustedState
1794-
if (trustedState !== 'requesting' && trustedState !== 'trusted') {
1795-
arr.push(id)
1796-
cs.dispatch.updateMeta({trustedState: 'requesting'})
1797-
}
1798-
}
1799-
return arr
1800-
}, [])
1788+
const conversationIDKeys = ids.reduce((arr: Array<string>, id) => {
1789+
if (id && T.Chat.isValidConversationIDKey(id)) {
1790+
const cs = C.getConvoState(id)
1791+
const trustedState = cs.meta.trustedState
1792+
if (force || (trustedState !== 'requesting' && trustedState !== 'trusted')) {
1793+
arr.push(id)
1794+
cs.dispatch.updateMeta({trustedState: 'requesting'})
1795+
}
1796+
}
1797+
return arr
1798+
}, [])
18011799

18021800
if (!conversationIDKeys.length) {
18031801
return

shared/constants/types/router2.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,5 @@ export type RouteDef = {
3232
getScreen?: () => React.ComponentType<any>
3333
getOptions?: GetOptions
3434
screen?: React.ComponentType
35-
skipShim?: boolean
3635
}
3736
export type RouteMap = {[K in string]?: RouteDef}

shared/crypto/sub-nav/index.desktop.tsx

Lines changed: 4 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import decryptIO from './decrypt.inout.page'
1212
import encryptIO from './encrypt.inout.page'
1313
import signIO from './sign.inout.page'
1414
import verifyIO from './verify.inout.page'
15-
import {getOptions, shim} from '@/router-v2/shim'
15+
import {makeNavScreens, type Screen} from '@/router-v2/shim'
1616

1717
/* Desktop SubNav */
1818
const cryptoSubRoutes = {
@@ -68,30 +68,11 @@ const styles = Kb.Styles.styleSheetCreate(() => ({
6868
}))
6969

7070
const createLeftTabNavigator = createNavigatorFactory(LeftTabNavigator)
71-
// eslint-disable-next-line
72-
const TabNavigator = createLeftTabNavigator()
73-
74-
const shimmed = shim(cryptoSubRoutes, false, false)
75-
const shimKeys = Object.keys(shimmed) as Array<keyof typeof shimmed>
76-
71+
const TabNavigator = createLeftTabNavigator() as {Screen: Screen; Navigator: any}
72+
const cryptoScreens = makeNavScreens(cryptoSubRoutes, TabNavigator.Screen, false, false)
7773
const CryptoSubNavigator = () => (
7874
<TabNavigator.Navigator initialRouteName={Constants.encryptTab} backBehavior="none">
79-
{shimKeys.map(name => (
80-
<TabNavigator.Screen
81-
key={name}
82-
name={name}
83-
getComponent={shimmed[name].getScreen}
84-
options={
85-
// @ts-ignore
86-
({route, navigation}) => {
87-
const no = getOptions(cryptoSubRoutes[name])
88-
// eslint-disable-next-line
89-
const opt = typeof no === 'function' ? no({navigation, route}) : no
90-
return {...opt}
91-
}
92-
}
93-
/>
94-
))}
75+
{cryptoScreens}
9576
</TabNavigator.Navigator>
9677
)
9778

shared/crypto/sub-nav/page.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,5 +10,5 @@ const Screen = () => (
1010
</React.Suspense>
1111
)
1212

13-
const Page = {getOptions, getScreen: () => Screen, skipShim: !C.isMobile}
13+
const Page = {getOptions, getScreen: () => Screen}
1414
export default Page

shared/people/container.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,20 +12,20 @@ const PeopleReloadable = () => {
1212
const oldItems = C.usePeopleState(s => s.oldItems)
1313
const signupEmail = C.useSignupState(s => s.justSignedUpEmail)
1414
const waiting = C.Waiting.useAnyWaiting(C.People.getPeopleDataWaitingKey)
15-
const [lastRefresh, setLastRefresh] = React.useState<number>(0)
15+
const lastRefreshRef = React.useRef<number>(0)
1616

1717
const loadPeople = C.usePeopleState(s => s.dispatch.loadPeople)
1818
// const wotUpdates = Container.useSelector(state => state.people.wotUpdates)
1919

2020
const getData = React.useCallback(
2121
(markViewed = true, force = false) => {
2222
const now = Date.now()
23-
if (force || !lastRefresh || lastRefresh + waitToRefresh < now) {
24-
setLastRefresh(now)
23+
if (force || !lastRefreshRef.current || lastRefreshRef.current + waitToRefresh < now) {
24+
lastRefreshRef.current = now
2525
loadPeople(markViewed, 10)
2626
}
2727
},
28-
[loadPeople, lastRefresh]
28+
[loadPeople]
2929
)
3030

3131
const showUserProfile = C.useProfileState(s => s.dispatch.showUserProfile)

shared/router-v2/hooks.native.tsx

Lines changed: 202 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,202 @@
1+
import * as C from '@/constants'
2+
import * as Kb from '@/common-adapters'
3+
import * as Tabs from '@/constants/tabs'
4+
import * as React from 'react'
5+
import {Linking} from 'react-native'
6+
7+
type InitialStateState = 'init' | 'loading' | 'loaded'
8+
9+
const argArrayGood = (arr: Array<string>, len: number) => {
10+
return arr.length === len && arr.every(p => !!p.length)
11+
}
12+
const isValidLink = (link: string) => {
13+
const urlPrefix = 'https://keybase.io/'
14+
if (link.startsWith(urlPrefix)) {
15+
if (link.substring(urlPrefix.length).split('/').length === 1) {
16+
return true
17+
}
18+
}
19+
const prefix = 'keybase://'
20+
if (!link.startsWith(prefix)) {
21+
return false
22+
}
23+
const path = link.substring(prefix.length)
24+
const [root, ...parts] = path.split('/')
25+
26+
switch (root) {
27+
case 'profile':
28+
switch (parts[0]) {
29+
case 'new-proof':
30+
return argArrayGood(parts, 2) || argArrayGood(parts, 3)
31+
case 'show':
32+
return argArrayGood(parts, 2)
33+
default:
34+
}
35+
return false
36+
case 'private':
37+
return true
38+
case 'public':
39+
return true
40+
case 'team':
41+
return true
42+
case 'convid':
43+
return argArrayGood(parts, 1)
44+
case 'chat':
45+
return argArrayGood(parts, 1) || argArrayGood(parts, 2)
46+
case 'team-page':
47+
return argArrayGood(parts, 3)
48+
case 'incoming-share':
49+
return true
50+
case 'team-invite-link':
51+
return argArrayGood(parts, 1)
52+
case 'settingsPushPrompt':
53+
return true
54+
default:
55+
return false
56+
}
57+
}
58+
59+
export const useInitialState = (loggedInLoaded: boolean) => {
60+
const config = C.useConfigState(
61+
C.useShallow(s => {
62+
const {androidShare, loggedIn, startup} = s
63+
return {androidShare, loggedIn, startup}
64+
})
65+
)
66+
const {androidShare, loggedIn, startup} = config
67+
const {tab: startupTab, followUser: startupFollowUser, loaded: startupLoaded} = startup
68+
let {conversation: startupConversation} = startup
69+
70+
if (!C.Chat.isValidConversationIDKey(startupConversation)) {
71+
startupConversation = ''
72+
}
73+
74+
const showMonster = C.usePushState(s => {
75+
const {hasPermissions, justSignedUp, showPushPrompt} = s
76+
return loggedIn && !justSignedUp && showPushPrompt && !hasPermissions
77+
})
78+
79+
const [initialState, setInitialState] = React.useState<undefined | object>(undefined)
80+
const [initialStateState, setInitialStateState] = React.useState<InitialStateState>('init')
81+
82+
React.useEffect(() => {
83+
if (!startupLoaded) return
84+
85+
if (!loggedInLoaded) {
86+
return
87+
}
88+
if (initialStateState !== 'init') {
89+
return
90+
}
91+
setInitialStateState('loading')
92+
const loadInitialURL = async () => {
93+
let url = await Linking.getInitialURL()
94+
95+
// don't try and resume or follow links if we're signed out
96+
if (!loggedIn) {
97+
return
98+
}
99+
100+
if (!url && showMonster) {
101+
url = 'keybase://settingsPushPrompt'
102+
}
103+
if (!url && androidShare) {
104+
url = `keybase://incoming-share`
105+
}
106+
107+
if (url && isValidLink(url)) {
108+
setTimeout(() => url && C.useDeepLinksState.getState().dispatch.handleAppLink(url), 1)
109+
} else if (startupFollowUser && !startupConversation) {
110+
url = `keybase://profile/show/${startupFollowUser}`
111+
112+
if (isValidLink(url)) {
113+
const initialTabState = {
114+
state: {
115+
index: 1,
116+
routes: [{name: 'peopleRoot'}, {name: 'profile', params: {username: startupFollowUser}}],
117+
},
118+
}
119+
setInitialState({
120+
index: 0,
121+
routes: [
122+
{
123+
name: 'loggedIn',
124+
state: {
125+
index: 0,
126+
routeNames: [Tabs.peopleTab],
127+
routes: [{name: Tabs.peopleTab, ...initialTabState}],
128+
},
129+
},
130+
],
131+
})
132+
}
133+
} else if (startupTab || startupConversation) {
134+
try {
135+
const tab = startupConversation ? Tabs.chatTab : startupTab
136+
C.Chat.useState_.getState().dispatch.unboxRows([startupConversation])
137+
C.Chat.getConvoState_(startupConversation).dispatch.loadMoreMessages({
138+
reason: 'savedLastState',
139+
})
140+
141+
const initialTabState = startupConversation
142+
? {
143+
state: {
144+
index: 1,
145+
routes: [
146+
{name: 'chatRoot'},
147+
{name: 'chatConversation', params: {conversationIDKey: startupConversation}},
148+
],
149+
},
150+
}
151+
: {}
152+
153+
setInitialState({
154+
index: 0,
155+
routes: [
156+
{
157+
name: 'loggedIn',
158+
state: {
159+
index: 0,
160+
routeNames: [tab],
161+
routes: [{name: tab, ...initialTabState}],
162+
},
163+
},
164+
],
165+
})
166+
} catch {}
167+
}
168+
}
169+
170+
const f = async () => {
171+
await loadInitialURL()
172+
setInitialStateState('loaded')
173+
}
174+
175+
C.ignorePromise(f())
176+
}, [
177+
androidShare,
178+
initialState,
179+
initialStateState,
180+
loggedIn,
181+
loggedInLoaded,
182+
showMonster,
183+
startupConversation,
184+
startupFollowUser,
185+
startupLoaded,
186+
startupTab,
187+
])
188+
189+
return {initialState, initialStateState}
190+
}
191+
192+
// on android we rerender everything on dark mode changes
193+
export const useRootKey = () => {
194+
const [rootKey, setRootKey] = React.useState('')
195+
const isDarkMode = Kb.Styles.useIsDarkMode()
196+
React.useEffect(() => {
197+
if (!C.isAndroid) return
198+
setRootKey(isDarkMode ? 'android-dark' : 'android-light')
199+
}, [isDarkMode])
200+
201+
return rootKey
202+
}

0 commit comments

Comments
 (0)