Basic typing for react-navigation

This commit is contained in:
Zhiyuan Zheng 2021-01-07 22:18:14 +01:00
parent 4b99813bb7
commit 4a6229514f
No known key found for this signature in database
GPG Key ID: 078A93AB607D85E0
17 changed files with 229 additions and 151 deletions

View File

@ -75,6 +75,7 @@
"@babel/preset-typescript": "^7.12.7",
"@expo/config": "^3.3.15",
"@jest/types": "^26.6.2",
"@react-navigation/stack": "^5.12.8",
"@testing-library/jest-native": "^3.4.3",
"@testing-library/react-hooks": "^3.7.0",
"@testing-library/react-native": "^7.1.0",

65
src/@types/react-navigation.d.ts vendored Normal file
View File

@ -0,0 +1,65 @@
declare namespace Nav {
type RootStackParamList = {
'Screen-Local': undefined
'Screen-Public': undefined
'Screen-Post': undefined
'Screen-Notifications': undefined
'Screen-Me': undefined
}
type SharedStackParamList = {
'Screen-Shared-Account': {
account: Pick<Mastodon.Account, 'id' | 'username' | 'acct' | 'url'>
}
'Screen-Shared-Announcements': { showAll?: boolean }
'Screen-Shared-Compose':
| {
type?: 'reply' | 'conversation' | 'edit'
incomingStatus: Mastodon.Status
}
| undefined
'Screen-Shared-Hashtag': {
hashtag: Mastodon.Tag['name']
}
'Screen-Shared-ImagesViewer': {
imageUrls: (IImageInfo & {
preview_url: Mastodon.AttachmentImage['preview_url']
remote_url: Mastodon.AttachmentImage['remote_url']
imageIndex: number
})[]
imageIndex: number
}
'Screen-Shared-Relationships': {
account: Mastodon.Account
initialType: 'following' | 'followers'
}
'Screen-Shared-Search': undefined
'Screen-Shared-Toot': {
toot: Mastodon.Status
}
}
type LocalStackParamList = {
'Screen-Local-Root': undefined
} & SharedStackParamList
type RemoteStackParamList = {
'Screen-Remote-Root': undefined
} & SharedStackParamList
type NotificationsStackParamList = {
'Screen-Notifications-Root': undefined
} & SharedStackParamList
type MeStackParamList = {
'Screen-Me-Root': undefined
'Screen-Me-Bookmarks': undefined
'Screen-Me-Conversations': undefined
'Screen-Me-Favourites': undefined
'Screen-Me-Lists': undefined
'Screen-Me-Lists-List': undefined
'Screen-Me-Settings': undefined
'Screen-Me-Settings-UpdateRemote': undefined
'Screen-Me-Switch': undefined
} & SharedStackParamList
}

View File

@ -2,7 +2,10 @@ import client from '@api/client'
import haptics from '@components/haptics'
import Icon from '@components/Icon'
import { toast, toastConfig } from '@components/toast'
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs'
import {
BottomTabNavigationOptions,
createBottomTabNavigator
} from '@react-navigation/bottom-tabs'
import {
NavigationContainer,
NavigationContainerRef
@ -32,7 +35,7 @@ import { StatusBar } from 'react-native'
import Toast from 'react-native-toast-message'
import { useDispatch, useSelector } from 'react-redux'
const Tab = createBottomTabNavigator()
const Tab = createBottomTabNavigator<Nav.RootStackParamList>()
export interface Props {
localCorrupt?: string
@ -50,7 +53,6 @@ const Index: React.FC<Props> = ({ localCorrupt }) => {
}
const routeNameRef = useRef<string | undefined>()
// const navigationRef = useRef<NavigationContainerRef>(null)
// const isConnected = useNetInfo().isConnected
// const [firstRender, setFirstRender] = useState(false)
@ -114,7 +116,7 @@ const Index: React.FC<Props> = ({ localCorrupt }) => {
...d?.toots
])
const latestNotificationTime = flattenData.length
? flattenData[0].created_at
? (flattenData[0] as Mastodon.Notification).created_at
: undefined
if (!prevNotification || !prevNotification.latestTime) {
@ -161,7 +163,7 @@ const Index: React.FC<Props> = ({ localCorrupt }) => {
routeNameRef.current = currentRouteName
}, [])
const tabNavigatorScreenOptions = useCallback(
({ route }) => ({
({ route }): BottomTabNavigationOptions => ({
tabBarIcon: ({
focused,
color,

View File

@ -11,10 +11,12 @@ import { createNativeStackNavigator } from 'react-native-screens/native-stack'
import { TabView } from 'react-native-tab-view'
import { useSelector } from 'react-redux'
const Stack = createNativeStackNavigator()
const Stack = createNativeStackNavigator<
Nav.LocalStackParamList | Nav.RemoteStackParamList
>()
export interface Props {
name: 'Local' | 'Public'
name: 'Screen-Local-Root' | 'Screen-Public-Root'
content: { title: string; page: App.Pages }[]
}
@ -68,9 +70,11 @@ const Timelines: React.FC<Props> = ({ name, content }) => {
return (
<Stack.Navigator screenOptions={{ headerHideShadow: true }}>
<Stack.Screen
name={`Screen-${name}-Root`}
// @ts-ignore
name={name}
component={screenComponent}
options={{
headerTitle: name === 'Public' ? publicDomain : '',
headerTitle: name === 'Screen-Public-Root' ? publicDomain : '',
...(localActiveIndex !== null && {
headerCenter: () => (
<View style={styles.segmentsContainer}>
@ -89,9 +93,7 @@ const Timelines: React.FC<Props> = ({ name, content }) => {
)
})
}}
>
{screenComponent}
</Stack.Screen>
/>
{sharedScreens(Stack)}
</Stack.Navigator>

View File

@ -1,14 +1,13 @@
import Timelines from '@components/Timelines'
import React from 'react'
import { useTranslation } from 'react-i18next'
import Timelines from '@components/Timelines'
const ScreenLocal: React.FC = () => {
const { t } = useTranslation()
return (
<Timelines
name='Local'
name='Screen-Local-Root'
content={[
{ title: t('local:heading.segments.left'), page: 'Following' },
{ title: t('local:heading.segments.right'), page: 'Local' }

View File

@ -1,22 +1,19 @@
import React from 'react'
import { createNativeStackNavigator } from 'react-native-screens/native-stack'
import { useTranslation } from 'react-i18next'
import ScreenMeRoot from '@screens/Me/Root'
import ScreenMeConversations from '@screens/Me/Cconversations'
import { HeaderLeft } from '@components/Header'
import ScreenMeBookmarks from '@screens/Me/Bookmarks'
import ScreenMeConversations from '@screens/Me/Cconversations'
import ScreenMeFavourites from '@screens/Me/Favourites'
import ScreenMeLists from '@screens/Me/Lists'
import sharedScreens from '@screens/Shared/sharedScreens'
import ScreenMeRoot from '@screens/Me/Root'
import ScreenMeListsList from '@screens/Me/Root/Lists/List'
import ScreenMeSettings from '@screens/Me/Settings'
import UpdateRemote from '@screens/Me/Settings/UpdateRemote'
import ScreenMeSwitch from '@screens/Me/Switch'
import sharedScreens from '@screens/Shared/sharedScreens'
import React from 'react'
import { useTranslation } from 'react-i18next'
import { createNativeStackNavigator } from 'react-native-screens/native-stack'
import { HeaderLeft } from '@root/components/Header'
import UpdateRemote from './Me/Settings/UpdateRemote'
import { CommonActions } from '@react-navigation/native'
import ScreenMeSwitch from './Me/Switch'
const Stack = createNativeStackNavigator()
const Stack = createNativeStackNavigator<Nav.MeStackParamList>()
const ScreenMe: React.FC = () => {
const { t } = useTranslation()
@ -32,14 +29,6 @@ const ScreenMe: React.FC = () => {
headerCenter: () => null
}}
/>
<Stack.Screen
name='Screen-Me-Conversations'
component={ScreenMeConversations}
options={({ navigation }: any) => ({
headerTitle: t('meConversations:heading'),
headerLeft: () => <HeaderLeft onPress={() => navigation.pop(1)} />
})}
/>
<Stack.Screen
name='Screen-Me-Bookmarks'
component={ScreenMeBookmarks}
@ -48,6 +37,14 @@ const ScreenMe: React.FC = () => {
headerLeft: () => <HeaderLeft onPress={() => navigation.pop(1)} />
})}
/>
<Stack.Screen
name='Screen-Me-Conversations'
component={ScreenMeConversations}
options={({ navigation }: any) => ({
headerTitle: t('meConversations:heading'),
headerLeft: () => <HeaderLeft onPress={() => navigation.pop(1)} />
})}
/>
<Stack.Screen
name='Screen-Me-Favourites'
component={ScreenMeFavourites}

View File

@ -6,7 +6,7 @@ import { useTranslation } from 'react-i18next'
import { createNativeStackNavigator } from 'react-native-screens/native-stack'
import { useSelector } from 'react-redux'
const Stack = createNativeStackNavigator()
const Stack = createNativeStackNavigator<Nav.NotificationsStackParamList>()
const ScreenNotifications: React.FC = () => {
const { t } = useTranslation()

View File

@ -1,14 +1,13 @@
import Timelines from '@components/Timelines'
import React from 'react'
import { useTranslation } from 'react-i18next'
import Timelines from '@components/Timelines'
const ScreenPublic: React.FC = () => {
const { t } = useTranslation()
return (
<Timelines
name='Public'
name='Screen-Public-Root'
content={[
{ title: t('public:heading.segments.left'), page: 'LocalPublic' },
{ title: t('public:heading.segments.right'), page: 'RemotePublic' }

View File

@ -3,7 +3,7 @@ import { HeaderRight } from '@components/Header'
import HeaderDefaultActionsAccount from '@components/Timelines/Timeline/Shared/HeaderDefault/ActionsAccount'
import hookAccount from '@utils/queryHooks/account'
import { getLocalAccount } from '@utils/slices/instancesSlice'
import React, { useEffect, useReducer, useRef, useState } from 'react'
import React, { useEffect, useReducer, useState } from 'react'
import Animated, {
useAnimatedScrollHandler,
useSharedValue
@ -17,19 +17,11 @@ import AccountToots from './Account/Toots'
import AccountContext from './Account/utils/createContext'
import accountInitialState from './Account/utils/initialState'
import accountReducer from './Account/utils/reducer'
import { SharedAccountProp } from './sharedScreens'
// Moved account example: https://m.cmx.im/web/accounts/27812
export interface Props {
route: {
params: {
account: Pick<Mastodon.Account, 'id' | 'username' | 'acct' | 'url'>
}
}
navigation: any
}
const ScreenSharedAccount: React.FC<Props> = ({
const ScreenSharedAccount: React.FC<SharedAccountProp> = ({
route: {
params: { account }
},

View File

@ -20,6 +20,7 @@ import {
import { FlatList, ScrollView } from 'react-native-gesture-handler'
import { SafeAreaView } from 'react-native-safe-area-context'
import { useMutation } from 'react-query'
import { SharedAnnouncementsProp } from './sharedScreens'
const fireMutation = async ({
announcementId,
@ -48,9 +49,9 @@ const fireMutation = async ({
}
}
const ScreenSharedAnnouncements: React.FC = ({
const ScreenSharedAnnouncements: React.FC<SharedAnnouncementsProp> = ({
route: {
params: { showAll }
params: { showAll = false }
},
navigation
}) => {

View File

@ -24,22 +24,14 @@ import composeInitialState from './Compose/utils/initialState'
import composeParseState from './Compose/utils/parseState'
import composePost from './Compose/utils/post'
import composeReducer from './Compose/utils/reducer'
import { SharedComposeProp } from './sharedScreens'
const Stack = createNativeStackNavigator()
export interface Props {
route: {
params:
| {
type?: 'reply' | 'conversation' | 'edit'
incomingStatus: Mastodon.Status
}
| undefined
}
navigation: any
}
const Compose: React.FC<Props> = ({ route: { params }, navigation }) => {
const Compose: React.FC<SharedComposeProp> = ({
route: { params },
navigation
}) => {
const { theme } = useTheme()
const queryClient = useQueryClient()

View File

@ -1,18 +1,10 @@
import React from 'react'
import Timeline from '@components/Timelines/Timeline'
import React from 'react'
import { SharedHashtagProp } from './sharedScreens'
// Show remote hashtag? Only when private, show local version?
export interface Props {
route: {
params: {
hashtag: string
}
}
}
const ScreenSharedHashtag: React.FC<Props> = ({
const ScreenSharedHashtag: React.FC<SharedHashtagProp> = ({
route: {
params: { hashtag }
}

View File

@ -8,23 +8,10 @@ import ImageViewer from 'react-native-image-zoom-viewer'
import { IImageInfo } from 'react-native-image-zoom-viewer/built/image-viewer.type'
import { useSafeAreaInsets } from 'react-native-safe-area-context'
import { createNativeStackNavigator } from 'react-native-screens/native-stack'
import { SharedImagesViewerProp } from './sharedScreens'
const Stack = createNativeStackNavigator()
export interface Props {
route: {
params: {
imageUrls: (IImageInfo & {
preview_url: Mastodon.AttachmentImage['preview_url']
remote_url: Mastodon.AttachmentImage['remote_url']
imageIndex: number
})[]
imageIndex: number
}
}
navigation: any
}
const TheImage = ({
style,
source,
@ -52,7 +39,7 @@ const TheImage = ({
)
}
const ScreenSharedImagesViewer: React.FC<Props> = ({
const ScreenSharedImagesViewer: React.FC<SharedImagesViewerProp> = ({
route: {
params: { imageUrls, imageIndex }
},

View File

@ -5,17 +5,9 @@ import React, { useEffect, useState } from 'react'
import { Dimensions, StyleSheet, View } from 'react-native'
import { TabView } from 'react-native-tab-view'
import RelationshipsList from './Relationships/List'
import { SharedRelationshipsProp } from './sharedScreens'
export interface Props {
route: {
params: {
account: Mastodon.Account
initialType: 'following' | 'followers'
}
}
}
const ScreenSharedRelationships: React.FC<Props> = ({
const ScreenSharedRelationships: React.FC<SharedRelationshipsProp> = ({
route: {
params: { account, initialType }
}
@ -43,16 +35,15 @@ const ScreenSharedRelationships: React.FC<Props> = ({
return updateHeaderRight()
}, [])
const routes: { key: Props['route']['params']['initialType'] }[] = [
{ key: 'following' },
{ key: 'followers' }
]
const routes: {
key: SharedRelationshipsProp['route']['params']['initialType']
}[] = [{ key: 'following' }, { key: 'followers' }]
const renderScene = ({
route
}: {
route: {
key: Props['route']['params']['initialType']
key: SharedRelationshipsProp['route']['params']['initialType']
}
}) => {
return <RelationshipsList id={account.id} type={route.key} />

View File

@ -1,16 +1,8 @@
import React from 'react'
import Timeline from '@components/Timelines/Timeline'
import React from 'react'
import { SharedTootProp } from './sharedScreens'
export interface Props {
route: {
params: {
toot: Mastodon.Status
}
}
}
const ScreenSharedToot: React.FC<Props> = ({
const ScreenSharedToot: React.FC<SharedTootProp> = ({
route: {
params: { toot }
}

View File

@ -1,16 +1,74 @@
import { HeaderLeft } from '@components/Header'
import { StackNavigationState, TypedNavigator } from '@react-navigation/native'
import { StackScreenProps } from '@react-navigation/stack'
import ScreenSharedAccount from '@screens/Shared/Account'
import ScreenSharedAnnouncements from '@screens/Shared/Announcements'
import Compose from '@screens/Shared/Compose'
import ScreenSharedHashtag from '@screens/Shared/Hashtag'
import ScreenSharedImagesViewer from '@screens/Shared/ImagesViewer'
import ScreenSharedRelationships from '@screens/Shared/Relationships'
import ScreenSharedToot from '@screens/Shared/Toot'
import Compose from '@screens/Shared/Compose'
import ScreenSharedSearch from '@screens/Shared/Search'
import ScreenSharedToot from '@screens/Shared/Toot'
import React from 'react'
import { useTranslation } from 'react-i18next'
import { NativeStackNavigationOptions } from 'react-native-screens/lib/typescript'
import {
NativeStackNavigationEventMap,
NativeStackNavigatorProps
} from 'react-native-screens/lib/typescript/types'
const sharedScreens = (Stack: any) => {
type BaseScreens =
| Nav.LocalStackParamList
| Nav.RemoteStackParamList
| Nav.NotificationsStackParamList
| Nav.MeStackParamList
export type SharedAccountProp = StackScreenProps<
BaseScreens,
'Screen-Shared-Account'
>
export type SharedAnnouncementsProp = StackScreenProps<
BaseScreens,
'Screen-Shared-Announcements'
>
export type SharedComposeProp = StackScreenProps<
BaseScreens,
'Screen-Shared-Compose'
>
export type SharedHashtagProp = StackScreenProps<
BaseScreens,
'Screen-Shared-Hashtag'
>
export type SharedImagesViewerProp = StackScreenProps<
BaseScreens,
'Screen-Shared-ImagesViewer'
>
export type SharedRelationshipsProp = StackScreenProps<
BaseScreens,
'Screen-Shared-Relationships'
>
export type SharedTootProp = StackScreenProps<BaseScreens, 'Screen-Shared-Toot'>
const sharedScreens = (
Stack: TypedNavigator<
BaseScreens,
StackNavigationState<Record<string, object | undefined>>,
NativeStackNavigationOptions,
NativeStackNavigationEventMap,
({
initialRouteName,
children,
screenOptions,
...rest
}: NativeStackNavigatorProps) => JSX.Element
>
) => {
const { t } = useTranslation()
return [
@ -18,7 +76,7 @@ const sharedScreens = (Stack: any) => {
key='Screen-Shared-Account'
name='Screen-Shared-Account'
component={ScreenSharedAccount}
options={({ navigation }: any) => {
options={({ navigation }: SharedAccountProp) => {
return {
headerTranslucent: true,
headerStyle: {
@ -30,22 +88,13 @@ const sharedScreens = (Stack: any) => {
}}
/>,
<Stack.Screen
key='Screen-Shared-Hashtag'
name='Screen-Shared-Hashtag'
component={ScreenSharedHashtag}
options={({ route, navigation }: any) => ({
title: `#${decodeURIComponent(route.params.hashtag)}`,
headerLeft: () => <HeaderLeft onPress={() => navigation.goBack()} />
})}
/>,
<Stack.Screen
key='Screen-Shared-Toot'
name='Screen-Shared-Toot'
component={ScreenSharedToot}
options={({ navigation }: any) => ({
title: t('sharedToot:heading'),
headerLeft: () => <HeaderLeft onPress={() => navigation.goBack()} />
})}
key='Screen-Shared-Announcements'
name='Screen-Shared-Announcements'
component={ScreenSharedAnnouncements}
options={{
stackPresentation: 'transparentModal',
stackAnimation: 'fade'
}}
/>,
<Stack.Screen
key='Screen-Shared-Compose'
@ -56,22 +105,14 @@ const sharedScreens = (Stack: any) => {
}}
/>,
<Stack.Screen
key='Screen-Shared-Search'
name='Screen-Shared-Search'
component={ScreenSharedSearch}
options={({ navigation }: any) => ({
key='Screen-Shared-Hashtag'
name='Screen-Shared-Hashtag'
component={ScreenSharedHashtag}
options={({ route, navigation }: any) => ({
title: `#${decodeURIComponent(route.params.hashtag)}`,
headerLeft: () => <HeaderLeft onPress={() => navigation.goBack()} />
})}
/>,
<Stack.Screen
key='Screen-Shared-Announcements'
name='Screen-Shared-Announcements'
component={ScreenSharedAnnouncements}
options={{
stackPresentation: 'transparentModal',
stackAnimation: 'fade'
}}
/>,
<Stack.Screen
key='Screen-Shared-ImagesViewer'
name='Screen-Shared-ImagesViewer'
@ -89,6 +130,23 @@ const sharedScreens = (Stack: any) => {
title: route.params.account.display_name || route.params.account.name,
headerLeft: () => <HeaderLeft onPress={() => navigation.goBack()} />
})}
/>,
<Stack.Screen
key='Screen-Shared-Search'
name='Screen-Shared-Search'
component={ScreenSharedSearch}
options={({ navigation }: any) => ({
headerLeft: () => <HeaderLeft onPress={() => navigation.goBack()} />
})}
/>,
<Stack.Screen
key='Screen-Shared-Toot'
name='Screen-Shared-Toot'
component={ScreenSharedToot}
options={({ navigation }: any) => ({
title: t('sharedToot:heading'),
headerLeft: () => <HeaderLeft onPress={() => navigation.goBack()} />
})}
/>
]
}

View File

@ -1772,6 +1772,14 @@
dependencies:
nanoid "^3.1.15"
"@react-navigation/stack@^5.12.8":
version "5.12.8"
resolved "https://registry.yarnpkg.com/@react-navigation/stack/-/stack-5.12.8.tgz#31e54e05d8a3ffaaa3e39a1a9b7969f8316a35bf"
integrity sha512-wUJFbU0v606RBXOUxHToCXJNmiwxtFYhN2TFvjxCZ3PJU+OWWx8HTmn99pT3rVH4Ax2cfO5BDUy9v+r74ZrIWw==
dependencies:
color "^3.1.3"
react-native-iphone-x-helper "^1.3.0"
"@reduxjs/toolkit@^1.5.0":
version "1.5.0"
resolved "https://registry.yarnpkg.com/@reduxjs/toolkit/-/toolkit-1.5.0.tgz#1025c1ccb224d1fc06d8d98a61f6717d57e6d477"