Skip to content

Commit 9308a57

Browse files
committed
WIP
1 parent 73cb2aa commit 9308a57

9 files changed

Lines changed: 38 additions & 49 deletions

File tree

shared/chat/routes.tsx

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,6 @@ import {defineRouteMap} from '@/constants/types/router'
1515
import type {BlockModalContext} from './blocking/block-modal'
1616
const Convo = React.lazy(async () => import('./conversation/container'))
1717

18-
type ChatAddToChannelRouteParams = {
19-
conversationIDKey?: T.Chat.ConversationIDKey
20-
teamID: T.Teams.TeamID
21-
}
2218
type ChatBlockingRouteParams = {
2319
blockUserByDefault?: boolean
2420
filterUserByDefault?: boolean
@@ -30,16 +26,6 @@ type ChatBlockingRouteParams = {
3026
team?: string
3127
username?: string
3228
}
33-
type ChatConfirmRemoveBotRouteParams = {
34-
botUsername: string
35-
teamID?: T.Teams.TeamID
36-
conversationIDKey?: T.Chat.ConversationIDKey
37-
}
38-
type ChatInstallBotRouteParams = {
39-
botUsername: string
40-
conversationIDKey?: T.Chat.ConversationIDKey
41-
teamID?: T.Teams.TeamID
42-
}
4329
type ChatSearchBotsRouteParams = {
4430
teamID?: T.Teams.TeamID
4531
conversationIDKey?: T.Chat.ConversationIDKey

shared/constants/router.tsx

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import {
1212
} from '@react-navigation/core'
1313
import type {StaticScreenProps} from '@react-navigation/core'
1414
import type {
15+
AllOptionalParamRouteKeys,
1516
NoParamRouteKeys,
1617
ParamRouteKeys,
1718
RouteKeys,
@@ -24,11 +25,13 @@ import {shallowEqual} from './utils'
2425
import {registerDebugClear} from '@/util/debug'
2526
import {makeUUID} from '@/util/uuid'
2627

27-
type IsExactlyRecord<T> = [T] extends [Record<string, unknown>]
28-
? [Record<string, unknown>] extends [T]
29-
? true
30-
: false
31-
: false
28+
// Detects the unconstrained Record<string,unknown> index-signature type.
29+
// We can't use bidirectional assignability ([Record] extends [T] && [T] extends [Record])
30+
// because TypeScript allows Record<string,unknown> to be assigned to any all-optional-property
31+
// type, making the check incorrectly return true for {x?: string} etc.
32+
// Instead, check for an index signature: string extends keyof T is true only for
33+
// Record<string,unknown>-like types (index signatures), not for specific property types.
34+
type IsExactlyRecord<T> = string extends keyof T ? true : false
3235

3336
type NavigatorParamsFromProps<P> = P extends Record<string, unknown>
3437
? IsExactlyRecord<P> extends true
@@ -248,7 +251,7 @@ export const navUpToScreen = (name: RouteKeys) => {
248251
n.dispatch(StackActions.popTo(typeof name === 'string' ? name : String(name)))
249252
}
250253

251-
export function navigateAppend<RouteName extends NoParamRouteKeys>(path: RouteName, replace?: boolean): void
254+
export function navigateAppend<RouteName extends NoParamRouteKeys | AllOptionalParamRouteKeys>(path: RouteName, replace?: boolean): void
252255
export function navigateAppend<RouteName extends ParamRouteKeys>(
253256
path: {name: RouteName; params: KBRootParamList[RouteName]},
254257
replace?: boolean

shared/constants/types/router.tsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ export type GetOptionsRet =
7272
})
7373
| undefined
7474

75-
type AnyScreen = React.ComponentType<any>
75+
type AnyScreen = React.ComponentType<any> | React.LazyExoticComponent<any>
7676
type ScreenRouteParams<Screen extends AnyScreen> =
7777
React.ComponentProps<Screen> extends {route: {params: infer Params}}
7878
? Params
@@ -86,7 +86,10 @@ export type GetOptions<Screen extends AnyScreen = AnyScreen> =
8686

8787
export type RouteDef<Screen extends AnyScreen = AnyScreen, Params = ScreenRouteParams<Screen>> = {
8888
__routeParams?: Params
89-
getOptions?: GetOptions<Screen>
89+
// Use `any` for the function param to avoid RouteDef being contravariant in Screen.
90+
// GetOptions<Screen> would cause RouteDef<SpecificScreen> to not be assignable to RouteDef<AnyScreen>
91+
// because of function parameter contravariance. Typed getOptions are used in makeScreen / makeChatScreen.
92+
getOptions?: GetOptionsRet | ((p: any) => GetOptionsRet)
9093
initialParams?: Params
9194
screen: Screen
9295
}

shared/router-v2/route-params.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,11 @@ export type NoParamRouteKeys = {
4646
[K in RouteKeys]: RootParamList[K] extends undefined ? K : never
4747
}[RouteKeys]
4848
export type ParamRouteKeys = Exclude<RouteKeys, NoParamRouteKeys>
49+
// Routes with required params would break if navigated to without params.
50+
// Routes where all params are optional can be safely navigated to with just a name string.
51+
export type AllOptionalParamRouteKeys = {
52+
[K in ParamRouteKeys]: {} extends NonNullable<RootParamList[K]> ? K : never
53+
}[ParamRouteKeys]
4954
export type NavigateAppendArg<RouteName extends RouteKeys> = RootParamList[RouteName] extends undefined
5055
? RouteName
5156
: {name: RouteName; params: RootParamList[RouteName]}

shared/stores/chat.tsx

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2014,11 +2014,8 @@ export const useChatState = Z.createZustand<State>('chat', (set, get) => {
20142014
}
20152015
})
20162016

2017-
type IsExactlyRecord<T> = [T] extends [Record<string, unknown>]
2018-
? [Record<string, unknown>] extends [T]
2019-
? true
2020-
: false
2021-
: false
2017+
// See constants/router.tsx IsExactlyRecord for explanation
2018+
type IsExactlyRecord<T> = string extends keyof T ? true : false
20222019

20232020
type NavigatorParamsFromProps<P> = P extends Record<string, unknown>
20242021
? IsExactlyRecord<P> extends true

shared/stores/convostate.tsx

Lines changed: 4 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3632,23 +3632,13 @@ export function ProviderScreen(p: {children: React.ReactNode; rp: RouteParams; c
36323632
)
36333633
}
36343634

3635-
import type {NoParamRouteKeys, ParamRouteKeys, RootParamList} from '@/router-v2/route-params'
3635+
import type {NavigateAppendType} from '@/router-v2/route-params'
36363636
export const useChatNavigateAppend = () => {
36373637
const cid = useChatContext(s => s.id)
3638-
function chatNavigateAppend<RouteName extends NoParamRouteKeys>(
3639-
makePath: (cid: T.Chat.ConversationIDKey) => RouteName,
3638+
const chatNavigateAppend = (
3639+
makePath: (cid: T.Chat.ConversationIDKey) => NavigateAppendType,
36403640
replace?: boolean
3641-
): void
3642-
function chatNavigateAppend<RouteName extends ParamRouteKeys>(
3643-
makePath: (cid: T.Chat.ConversationIDKey) => {name: RouteName; params: RootParamList[RouteName]},
3644-
replace?: boolean
3645-
): void
3646-
function chatNavigateAppend(
3647-
makePath:
3648-
| ((cid: T.Chat.ConversationIDKey) => NoParamRouteKeys)
3649-
| ((cid: T.Chat.ConversationIDKey) => {name: ParamRouteKeys; params: object | undefined}),
3650-
replace?: boolean
3651-
) {
3641+
) => {
36523642
navigateAppend(makePath(cid) as never, replace)
36533643
}
36543644
return chatNavigateAppend

shared/teams/routes.tsx

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,6 @@ import {useModalHeaderState} from '@/stores/modal-header'
1212
import teamsRootGetOptions from './get-options'
1313
import {defineRouteMap} from '@/constants/types/router'
1414

15-
type TeamRouteParams = {
16-
teamID: T.Teams.TeamID
17-
initialTab?: T.Teams.TabKey
18-
}
19-
2015
const AddToChannelsHeaderTitle = ({teamID}: {teamID: T.Teams.TeamID}) => {
2116
const title = useModalHeaderState(s => s.title)
2217
return <ModalTitle teamID={teamID} title={title || 'Browse all channels'} />

shared/util/safe-navigation.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,18 @@
11
import * as C from '@/constants'
22
import {useIsFocused} from '@react-navigation/core'
3-
import type {NoParamRouteKeys, ParamRouteKeys, RootParamList} from '@/router-v2/route-params'
3+
import type {AllOptionalParamRouteKeys, NoParamRouteKeys, ParamRouteKeys, RootParamList} from '@/router-v2/route-params'
44

55
export const useSafeNavigation = () => {
66
const isFocused = useIsFocused()
77
const navigateUp = C.Router2.navigateUp
88
const navigateAppend = C.Router2.navigateAppend
9-
function safeNavigateAppend<RouteName extends NoParamRouteKeys>(path: RouteName, replace?: boolean): void
9+
function safeNavigateAppend<RouteName extends NoParamRouteKeys | AllOptionalParamRouteKeys>(path: RouteName, replace?: boolean): void
1010
function safeNavigateAppend<RouteName extends ParamRouteKeys>(
1111
path: {name: RouteName; params: RootParamList[RouteName]},
1212
replace?: boolean
1313
): void
1414
function safeNavigateAppend(
15-
path: NoParamRouteKeys | {name: ParamRouteKeys; params: object | undefined},
15+
path: NoParamRouteKeys | AllOptionalParamRouteKeys | {name: ParamRouteKeys; params: object | undefined},
1616
replace?: boolean
1717
) {
1818
return isFocused && navigateAppend(path as never, replace)

skills-lock.json

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,16 @@
1515
"source": "react-navigation/skills",
1616
"sourceType": "github",
1717
"computedHash": "49e493a5e878c7ef4e77dd7b5ed98c477c6a815617351c337c9cd374414669f3"
18+
},
19+
"vercel-react-best-practices": {
20+
"source": "vercel-labs/agent-skills",
21+
"sourceType": "github",
22+
"computedHash": "1be892a3ea68b74654326ef836a58dd528d99ae0ab8d879764e2519c39068e67"
23+
},
24+
"vercel-react-native-skills": {
25+
"source": "vercel-labs/agent-skills",
26+
"sourceType": "github",
27+
"computedHash": "2e9088a7333666d8c2833b8ff58bd51b955501c42b4c7244f72b4cbf22dafcc4"
1828
}
1929
}
2030
}

0 commit comments

Comments
 (0)