2023-03-13 22:01:43 +01:00
import * as React from 'react'
2024-03-21 12:04:02 +01:00
import { JSX } from 'react/jsx-runtime'
import { i18n , MessageDescriptor } from '@lingui/core'
import { msg } from '@lingui/macro'
2023-06-22 18:40:32 +02:00
import {
BottomTabBarProps ,
createBottomTabNavigator ,
} from '@react-navigation/bottom-tabs'
2023-03-13 22:01:43 +01:00
import {
2024-03-21 12:04:02 +01:00
CommonActions ,
createNavigationContainerRef ,
DarkTheme ,
DefaultTheme ,
NavigationContainer ,
StackActions ,
} from '@react-navigation/native'
import { timeout } from 'lib/async/timeout'
import { useColorSchemeStyle } from 'lib/hooks/useColorSchemeStyle'
import { usePalette } from 'lib/hooks/usePalette'
import { buildStateObject } from 'lib/routes/helpers'
import {
AllNavigatorParams ,
BottomTabNavigatorParams ,
2023-05-26 03:02:37 +02:00
FeedsTabNavigatorParams ,
2023-03-13 22:01:43 +01:00
FlatNavigatorParams ,
2024-03-21 12:04:02 +01:00
HomeTabNavigatorParams ,
2024-04-27 06:54:18 +02:00
MessagesTabNavigatorParams ,
2023-04-18 18:19:37 +02:00
MyProfileTabNavigatorParams ,
2024-03-21 12:04:02 +01:00
NotificationsTabNavigatorParams ,
SearchTabNavigatorParams ,
2023-03-13 22:01:43 +01:00
} from 'lib/routes/types'
2024-03-21 12:04:02 +01:00
import { RouteParams , State } from 'lib/routes/types'
import { bskyTitle } from 'lib/strings/headings'
2023-12-29 00:46:50 +01:00
import { isAndroid , isNative } from 'platform/detection'
2024-03-21 12:04:02 +01:00
import { PreferencesExternalEmbeds } from '#/view/screens/PreferencesExternalEmbeds'
import { AppPasswords } from 'view/screens/AppPasswords'
import { ModerationBlockedAccounts } from 'view/screens/ModerationBlockedAccounts'
import { ModerationMutedAccounts } from 'view/screens/ModerationMutedAccounts'
import { PreferencesFollowingFeed } from 'view/screens/PreferencesFollowingFeed'
import { PreferencesThreads } from 'view/screens/PreferencesThreads'
import { SavedFeeds } from 'view/screens/SavedFeeds'
import HashtagScreen from '#/screens/Hashtag'
import { ModerationScreen } from '#/screens/Moderation'
import { ProfileLabelerLikedByScreen } from '#/screens/Profile/ProfileLabelerLikedBy'
import { init as initAnalytics } from './lib/analytics/analytics'
import { useWebScrollRestoration } from './lib/hooks/useWebScrollRestoration'
import { attachRouteToLogEvents , logEvent } from './lib/statsig/statsig'
2023-03-13 22:01:43 +01:00
import { router } from './routes'
2024-04-27 06:54:18 +02:00
import { MessagesConversationScreen } from './screens/Messages/Conversation'
2024-05-02 22:02:45 +02:00
import { MessagesScreen } from './screens/Messages/List'
2024-04-27 06:54:18 +02:00
import { MessagesSettingsScreen } from './screens/Messages/Settings'
2024-03-21 12:04:02 +01:00
import { useModalControls } from './state/modals'
2023-11-13 03:13:11 +01:00
import { useUnreadNotifications } from './state/queries/notifications/unread'
2023-12-06 01:28:11 +01:00
import { useSession } from './state/session'
import {
setEmailConfirmationRequested ,
2024-03-21 12:04:02 +01:00
shouldRequestEmailConfirmation ,
2023-12-06 01:28:11 +01:00
} from './state/shell/reminders'
2024-04-19 23:10:37 +02:00
import { AccessibilitySettingsScreen } from './view/screens/AccessibilitySettings'
2024-03-21 12:04:02 +01:00
import { CommunityGuidelinesScreen } from './view/screens/CommunityGuidelines'
import { CopyrightPolicyScreen } from './view/screens/CopyrightPolicy'
import { DebugModScreen } from './view/screens/DebugMod'
2023-05-26 03:02:37 +02:00
import { FeedsScreen } from './view/screens/Feeds'
2024-03-21 12:04:02 +01:00
import { HomeScreen } from './view/screens/Home'
import { LanguageSettingsScreen } from './view/screens/LanguageSettings'
2023-11-02 00:15:40 +01:00
import { ListsScreen } from './view/screens/Lists'
2024-03-21 12:04:02 +01:00
import { LogScreen } from './view/screens/Log'
2023-11-02 00:15:40 +01:00
import { ModerationModlistsScreen } from './view/screens/ModerationModlists'
2023-03-13 22:01:43 +01:00
import { NotFoundScreen } from './view/screens/NotFound'
2024-03-21 12:04:02 +01:00
import { NotificationsScreen } from './view/screens/Notifications'
import { PostLikedByScreen } from './view/screens/PostLikedBy'
import { PostRepostedByScreen } from './view/screens/PostRepostedBy'
import { PostThreadScreen } from './view/screens/PostThread'
import { PrivacyPolicyScreen } from './view/screens/PrivacyPolicy'
2023-03-13 22:01:43 +01:00
import { ProfileScreen } from './view/screens/Profile'
2023-11-02 00:15:40 +01:00
import { ProfileFeedScreen } from './view/screens/ProfileFeed'
import { ProfileFeedLikedByScreen } from './view/screens/ProfileFeedLikedBy'
2024-03-21 12:04:02 +01:00
import { ProfileFollowersScreen } from './view/screens/ProfileFollowers'
import { ProfileFollowsScreen } from './view/screens/ProfileFollows'
2023-05-11 23:08:21 +02:00
import { ProfileListScreen } from './view/screens/ProfileList'
2024-03-21 12:04:02 +01:00
import { SearchScreen } from './view/screens/Search'
import { SettingsScreen } from './view/screens/Settings'
New component library based on ALF (#2459)
* Install on native as well
* Add button and link components
* Comments
* Use new prop
* Add some form elements
* Add labels to input
* Fix line height, add suffix
* Date inputs
* Autofill styles
* Clean up InputDate types
* Improve types for InputText, value handling
* Enforce a11y props on buttons
* Add Dialog, Portal
* Dialog contents
* Native dialog
* Clean up
* Fix animations
* Improvements to web modal, exiting still broken
* Clean up dialog types
* Add Prompt, Dialog refinement, mobile refinement
* Integrate new design tokens, reorg storybook
* Button colors
* Dim mode
* Reorg
* Some styles
* Toggles
* Improve a11y
* Autosize dialog, handle max height, Dialog.ScrolLView not working
* Try to use BottomSheet's own APIs
* Scrollable dialogs
* Add web shadow
* Handle overscroll
* Styles
* Dialog text input
* Shadows
* Button focus states
* Button pressed states
* Gradient poc
* Gradient colors and hovers
* Add hrefAttrs to Link
* Some more a11y
* Toggle invalid states
* Update dialog descriptions for demo
* Icons
* WIP Toggle cleanup
* Refactor toggle to not rely on immediate children
* Make Toggle controlled
* Clean up Toggles storybook
* ToggleButton styles
* Improve a11y labels
* ToggleButton hover darkmode
* Some i18n
* Refactor input
* Allow extension of input
* Remove old input
* Improve icons, add CalendarDays
* Refactor DateField, web done
* Add label example
* Clean up old InputDate, DateField android, text area example
* Consistent imports
* Button context, icons
* Add todo
* Add closeAllDialogs control
* Alignment
* Expand color palette
* Hitslops, add shortcut to Storybook in dev
* Fix multiline on ios
* Mark dialog close button as unused
2024-01-19 03:28:04 +01:00
import { Storybook } from './view/screens/Storybook'
2023-03-14 02:34:01 +01:00
import { SupportScreen } from './view/screens/Support'
2023-04-07 05:53:58 +02:00
import { TermsOfServiceScreen } from './view/screens/TermsOfService'
2024-03-21 12:04:02 +01:00
import { BottomBar } from './view/shell/bottom-bar/BottomBar'
2023-11-24 23:31:33 +01:00
import { createNativeStackNavigatorWithAuth } from './view/shell/createNativeStackNavigatorWithAuth'
2023-03-13 22:01:43 +01:00
const navigationRef = createNavigationContainerRef < AllNavigatorParams > ( )
2023-11-24 23:31:33 +01:00
const HomeTab = createNativeStackNavigatorWithAuth < HomeTabNavigatorParams > ( )
const SearchTab = createNativeStackNavigatorWithAuth < SearchTabNavigatorParams > ( )
const FeedsTab = createNativeStackNavigatorWithAuth < FeedsTabNavigatorParams > ( )
2023-03-13 22:01:43 +01:00
const NotificationsTab =
2023-11-24 23:31:33 +01:00
createNativeStackNavigatorWithAuth < NotificationsTabNavigatorParams > ( )
const MyProfileTab =
createNativeStackNavigatorWithAuth < MyProfileTabNavigatorParams > ( )
2024-04-27 06:54:18 +02:00
const MessagesTab =
createNativeStackNavigatorWithAuth < MessagesTabNavigatorParams > ( )
2023-11-24 23:31:33 +01:00
const Flat = createNativeStackNavigatorWithAuth < FlatNavigatorParams > ( )
2023-04-18 18:19:37 +02:00
const Tab = createBottomTabNavigator < BottomTabNavigatorParams > ( )
2023-03-13 22:01:43 +01:00
/ * *
* These "common screens" are reused across stacks .
* /
2023-05-16 20:13:05 +02:00
function commonScreens ( Stack : typeof HomeTab , unreadCountLabel? : string ) {
2024-01-09 23:37:15 +01:00
const title = ( page : MessageDescriptor ) = >
bskyTitle ( i18n . _ ( page ) , unreadCountLabel )
2023-05-16 20:13:05 +02:00
2023-03-13 22:01:43 +01:00
return (
< >
2023-05-16 20:13:05 +02:00
< Stack.Screen
name = "NotFound"
2023-10-27 20:06:28 +02:00
getComponent = { ( ) = > NotFoundScreen }
2024-01-09 23:37:15 +01:00
options = { { title : title ( msg ` Not Found ` ) } }
2023-05-16 20:13:05 +02:00
/ >
2023-11-02 00:15:40 +01:00
< Stack.Screen
name = "Lists"
component = { ListsScreen }
2024-01-09 23:37:15 +01:00
options = { { title : title ( msg ` Lists ` ) , requireAuth : true } }
2023-11-02 00:15:40 +01:00
/ >
2023-05-16 20:13:05 +02:00
< Stack.Screen
name = "Moderation"
2023-10-27 20:06:28 +02:00
getComponent = { ( ) = > ModerationScreen }
2024-01-09 23:37:15 +01:00
options = { { title : title ( msg ` Moderation ` ) , requireAuth : true } }
2023-05-16 20:13:05 +02:00
/ >
2023-05-11 23:08:21 +02:00
< Stack.Screen
2023-11-02 00:15:40 +01:00
name = "ModerationModlists"
getComponent = { ( ) = > ModerationModlistsScreen }
2024-01-09 23:37:15 +01:00
options = { { title : title ( msg ` Moderation Lists ` ) , requireAuth : true } }
2023-05-11 23:08:21 +02:00
/ >
< Stack.Screen
name = "ModerationMutedAccounts"
2023-10-27 20:06:28 +02:00
getComponent = { ( ) = > ModerationMutedAccounts }
2024-01-09 23:37:15 +01:00
options = { { title : title ( msg ` Muted Accounts ` ) , requireAuth : true } }
2023-05-11 23:08:21 +02:00
/ >
< Stack.Screen
name = "ModerationBlockedAccounts"
2023-10-27 20:06:28 +02:00
getComponent = { ( ) = > ModerationBlockedAccounts }
2024-01-09 23:37:15 +01:00
options = { { title : title ( msg ` Blocked Accounts ` ) , requireAuth : true } }
2023-05-16 20:13:05 +02:00
/ >
< Stack.Screen
name = "Settings"
2023-10-27 20:06:28 +02:00
getComponent = { ( ) = > SettingsScreen }
2024-01-09 23:37:15 +01:00
options = { { title : title ( msg ` Settings ` ) , requireAuth : true } }
2023-05-16 20:13:05 +02:00
/ >
2023-09-21 20:33:19 +02:00
< Stack.Screen
name = "LanguageSettings"
2023-10-27 20:06:28 +02:00
getComponent = { ( ) = > LanguageSettingsScreen }
2024-01-09 23:37:15 +01:00
options = { { title : title ( msg ` Language Settings ` ) , requireAuth : true } }
2023-09-21 20:33:19 +02:00
/ >
2023-05-16 20:13:05 +02:00
< Stack.Screen
name = "Profile"
2023-10-27 20:06:28 +02:00
getComponent = { ( ) = > ProfileScreen }
2023-08-03 19:25:17 +02:00
options = { ( { route } ) = > ( {
2024-01-19 06:23:12 +01:00
title : bskyTitle ( ` @ ${ route . params . name } ` , unreadCountLabel ) ,
2023-08-03 19:25:17 +02:00
} ) }
2023-05-11 23:08:21 +02:00
/ >
2023-03-13 22:01:43 +01:00
< Stack.Screen
name = "ProfileFollowers"
2023-10-27 20:06:28 +02:00
getComponent = { ( ) = > ProfileFollowersScreen }
2023-05-16 20:13:05 +02:00
options = { ( { route } ) = > ( {
2024-01-09 23:37:15 +01:00
title : title ( msg ` People following @ ${ route . params . name } ` ) ,
2023-05-16 20:13:05 +02:00
} ) }
/ >
< Stack.Screen
name = "ProfileFollows"
2023-10-27 20:06:28 +02:00
getComponent = { ( ) = > ProfileFollowsScreen }
2023-05-16 20:13:05 +02:00
options = { ( { route } ) = > ( {
2024-01-09 23:37:15 +01:00
title : title ( msg ` People followed by @ ${ route . params . name } ` ) ,
2023-05-16 20:13:05 +02:00
} ) }
/ >
< Stack.Screen
name = "ProfileList"
2023-10-27 20:06:28 +02:00
getComponent = { ( ) = > ProfileListScreen }
2024-01-09 23:37:15 +01:00
options = { { title : title ( msg ` List ` ) , requireAuth : true } }
2023-05-16 20:13:05 +02:00
/ >
< Stack.Screen
name = "PostThread"
2023-10-27 20:06:28 +02:00
getComponent = { ( ) = > PostThreadScreen }
2024-01-09 23:37:15 +01:00
options = { ( { route } ) = > ( {
title : title ( msg ` Post by @ ${ route . params . name } ` ) ,
} ) }
2023-05-16 20:13:05 +02:00
/ >
< Stack.Screen
name = "PostLikedBy"
2023-10-27 20:06:28 +02:00
getComponent = { ( ) = > PostLikedByScreen }
2024-01-09 23:37:15 +01:00
options = { ( { route } ) = > ( {
title : title ( msg ` Post by @ ${ route . params . name } ` ) ,
} ) }
2023-05-16 20:13:05 +02:00
/ >
< Stack.Screen
name = "PostRepostedBy"
2023-10-27 20:06:28 +02:00
getComponent = { ( ) = > PostRepostedByScreen }
2024-01-09 23:37:15 +01:00
options = { ( { route } ) = > ( {
title : title ( msg ` Post by @ ${ route . params . name } ` ) ,
} ) }
2023-05-16 20:13:05 +02:00
/ >
2023-05-18 06:33:59 +02:00
< Stack.Screen
2023-11-02 00:15:40 +01:00
name = "ProfileFeed"
getComponent = { ( ) = > ProfileFeedScreen }
2024-04-12 23:13:13 +02:00
options = { { title : title ( msg ` Feed ` ) } }
2023-05-18 06:33:59 +02:00
/ >
2023-05-18 05:12:14 +02:00
< Stack.Screen
2023-11-02 00:15:40 +01:00
name = "ProfileFeedLikedBy"
getComponent = { ( ) = > ProfileFeedLikedByScreen }
2024-01-09 23:37:15 +01:00
options = { { title : title ( msg ` Liked by ` ) } }
2023-05-18 05:12:14 +02:00
/ >
2024-03-18 20:46:28 +01:00
< Stack.Screen
name = "ProfileLabelerLikedBy"
getComponent = { ( ) = > ProfileLabelerLikedByScreen }
options = { { title : title ( msg ` Liked by ` ) } }
/ >
2023-05-16 20:13:05 +02:00
< Stack.Screen
name = "Debug"
New component library based on ALF (#2459)
* Install on native as well
* Add button and link components
* Comments
* Use new prop
* Add some form elements
* Add labels to input
* Fix line height, add suffix
* Date inputs
* Autofill styles
* Clean up InputDate types
* Improve types for InputText, value handling
* Enforce a11y props on buttons
* Add Dialog, Portal
* Dialog contents
* Native dialog
* Clean up
* Fix animations
* Improvements to web modal, exiting still broken
* Clean up dialog types
* Add Prompt, Dialog refinement, mobile refinement
* Integrate new design tokens, reorg storybook
* Button colors
* Dim mode
* Reorg
* Some styles
* Toggles
* Improve a11y
* Autosize dialog, handle max height, Dialog.ScrolLView not working
* Try to use BottomSheet's own APIs
* Scrollable dialogs
* Add web shadow
* Handle overscroll
* Styles
* Dialog text input
* Shadows
* Button focus states
* Button pressed states
* Gradient poc
* Gradient colors and hovers
* Add hrefAttrs to Link
* Some more a11y
* Toggle invalid states
* Update dialog descriptions for demo
* Icons
* WIP Toggle cleanup
* Refactor toggle to not rely on immediate children
* Make Toggle controlled
* Clean up Toggles storybook
* ToggleButton styles
* Improve a11y labels
* ToggleButton hover darkmode
* Some i18n
* Refactor input
* Allow extension of input
* Remove old input
* Improve icons, add CalendarDays
* Refactor DateField, web done
* Add label example
* Clean up old InputDate, DateField android, text area example
* Consistent imports
* Button context, icons
* Add todo
* Add closeAllDialogs control
* Alignment
* Expand color palette
* Hitslops, add shortcut to Storybook in dev
* Fix multiline on ios
* Mark dialog close button as unused
2024-01-19 03:28:04 +01:00
getComponent = { ( ) = > Storybook }
options = { { title : title ( msg ` Storybook ` ) , requireAuth : true } }
2023-05-16 20:13:05 +02:00
/ >
2024-03-18 20:46:28 +01:00
< Stack.Screen
name = "DebugMod"
getComponent = { ( ) = > DebugModScreen }
options = { { title : title ( msg ` Moderation states ` ) , requireAuth : true } }
/ >
2023-05-16 20:13:05 +02:00
< Stack.Screen
name = "Log"
2023-10-27 20:06:28 +02:00
getComponent = { ( ) = > LogScreen }
2024-01-09 23:37:15 +01:00
options = { { title : title ( msg ` Log ` ) , requireAuth : true } }
2023-05-16 20:13:05 +02:00
/ >
< Stack.Screen
name = "Support"
2023-10-27 20:06:28 +02:00
getComponent = { ( ) = > SupportScreen }
2024-01-09 23:37:15 +01:00
options = { { title : title ( msg ` Support ` ) } }
2023-05-16 20:13:05 +02:00
/ >
< Stack.Screen
name = "PrivacyPolicy"
2023-10-27 20:06:28 +02:00
getComponent = { ( ) = > PrivacyPolicyScreen }
2024-01-09 23:37:15 +01:00
options = { { title : title ( msg ` Privacy Policy ` ) } }
2023-05-16 20:13:05 +02:00
/ >
< Stack.Screen
name = "TermsOfService"
2023-10-27 20:06:28 +02:00
getComponent = { ( ) = > TermsOfServiceScreen }
2024-01-09 23:37:15 +01:00
options = { { title : title ( msg ` Terms of Service ` ) } }
2023-03-13 22:01:43 +01:00
/ >
2023-04-07 05:53:58 +02:00
< Stack.Screen
name = "CommunityGuidelines"
2023-10-27 20:06:28 +02:00
getComponent = { ( ) = > CommunityGuidelinesScreen }
2024-01-09 23:37:15 +01:00
options = { { title : title ( msg ` Community Guidelines ` ) } }
2023-05-16 20:13:05 +02:00
/ >
< Stack.Screen
name = "CopyrightPolicy"
2023-10-27 20:06:28 +02:00
getComponent = { ( ) = > CopyrightPolicyScreen }
2024-01-09 23:37:15 +01:00
options = { { title : title ( msg ` Copyright Policy ` ) } }
2023-05-16 20:13:05 +02:00
/ >
< Stack.Screen
name = "AppPasswords"
2023-10-27 20:06:28 +02:00
getComponent = { ( ) = > AppPasswords }
2024-01-09 23:37:15 +01:00
options = { { title : title ( msg ` App Passwords ` ) , requireAuth : true } }
2023-04-07 05:53:58 +02:00
/ >
2023-05-18 06:33:59 +02:00
< Stack.Screen
name = "SavedFeeds"
2023-10-27 20:06:28 +02:00
getComponent = { ( ) = > SavedFeeds }
2024-01-09 23:37:15 +01:00
options = { { title : title ( msg ` Edit My Feeds ` ) , requireAuth : true } }
2023-05-18 06:33:59 +02:00
/ >
2023-08-31 00:21:12 +02:00
< Stack.Screen
2024-02-22 17:51:11 +01:00
name = "PreferencesFollowingFeed"
getComponent = { ( ) = > PreferencesFollowingFeed }
options = { {
title : title ( msg ` Following Feed Preferences ` ) ,
requireAuth : true ,
} }
2023-08-31 00:21:12 +02:00
/ >
2023-09-19 21:24:58 +02:00
< Stack.Screen
name = "PreferencesThreads"
2023-10-27 20:06:28 +02:00
getComponent = { ( ) = > PreferencesThreads }
2024-01-09 23:37:15 +01:00
options = { { title : title ( msg ` Threads Preferences ` ) , requireAuth : true } }
2023-09-19 21:24:58 +02:00
/ >
2024-01-05 02:37:36 +01:00
< Stack.Screen
name = "PreferencesExternalEmbeds"
getComponent = { ( ) = > PreferencesExternalEmbeds }
options = { {
2024-01-09 23:37:15 +01:00
title : title ( msg ` External Media Preferences ` ) ,
2024-01-05 02:37:36 +01:00
requireAuth : true ,
} }
/ >
2024-04-19 23:10:37 +02:00
< Stack.Screen
name = "AccessibilitySettings"
getComponent = { ( ) = > AccessibilitySettingsScreen }
options = { {
title : title ( msg ` Accessibility Settings ` ) ,
requireAuth : true ,
} }
/ >
2024-03-01 02:56:29 +01:00
< Stack.Screen
name = "Hashtag"
getComponent = { ( ) = > HashtagScreen }
options = { { title : title ( msg ` Hashtag ` ) } }
/ >
2024-04-27 06:54:18 +02:00
< Stack.Screen
name = "MessagesConversation"
getComponent = { ( ) = > MessagesConversationScreen }
options = { { title : title ( msg ` Chat ` ) , requireAuth : true } }
/ >
< Stack.Screen
name = "MessagesSettings"
getComponent = { ( ) = > MessagesSettingsScreen }
options = { { title : title ( msg ` Messaging settings ` ) , requireAuth : true } }
/ >
2023-03-13 22:01:43 +01:00
< / >
)
}
/ * *
* The TabsNavigator is used by native mobile to represent the routes
* in 3 distinct tab - stacks with a different root screen on each .
* /
function TabsNavigator() {
2023-06-22 18:40:32 +02:00
const tabBar = React . useCallback (
( props : JSX.IntrinsicAttributes & BottomTabBarProps ) = > (
< BottomBar { ...props } / >
) ,
[ ] ,
)
2023-09-28 21:41:44 +02:00
2023-03-13 22:01:43 +01:00
return (
< Tab.Navigator
initialRouteName = "HomeTab"
backBehavior = "initialRoute"
2023-05-31 03:16:30 +02:00
screenOptions = { { headerShown : false , lazy : true } }
2023-03-13 22:01:43 +01:00
tabBar = { tabBar } >
2023-10-27 20:06:28 +02:00
< Tab.Screen name = "HomeTab" getComponent = { ( ) = > HomeTabNavigator } / >
< Tab.Screen name = "SearchTab" getComponent = { ( ) = > SearchTabNavigator } / >
< Tab.Screen name = "FeedsTab" getComponent = { ( ) = > FeedsTabNavigator } / >
2023-03-13 22:01:43 +01:00
< Tab.Screen
name = "NotificationsTab"
2023-10-27 20:06:28 +02:00
getComponent = { ( ) = > NotificationsTabNavigator }
/ >
< Tab.Screen
name = "MyProfileTab"
getComponent = { ( ) = > MyProfileTabNavigator }
2023-03-13 22:01:43 +01:00
/ >
2024-04-27 06:54:18 +02:00
< Tab.Screen
name = "MessagesTab"
getComponent = { ( ) = > MessagesTabNavigator }
/ >
2023-03-13 22:01:43 +01:00
< / Tab.Navigator >
)
}
function HomeTabNavigator() {
2024-01-20 01:13:29 +01:00
const pal = usePalette ( 'default' )
2023-08-28 22:37:44 +02:00
2023-03-13 22:01:43 +01:00
return (
< HomeTab.Navigator
screenOptions = { {
2023-12-29 00:46:50 +01:00
animation : isAndroid ? 'none' : undefined ,
2023-03-13 22:01:43 +01:00
gestureEnabled : true ,
fullScreenGestureEnabled : true ,
headerShown : false ,
animationDuration : 250 ,
2024-01-20 01:13:29 +01:00
contentStyle : pal.view ,
2023-03-13 22:01:43 +01:00
} } >
2024-04-12 23:13:13 +02:00
< HomeTab.Screen name = "Home" getComponent = { ( ) = > HomeScreen } / >
2023-03-13 22:01:43 +01:00
{ commonScreens ( HomeTab ) }
< / HomeTab.Navigator >
)
}
function SearchTabNavigator() {
2024-01-20 01:13:29 +01:00
const pal = usePalette ( 'default' )
2023-03-13 22:01:43 +01:00
return (
< SearchTab.Navigator
screenOptions = { {
2023-12-29 00:46:50 +01:00
animation : isAndroid ? 'none' : undefined ,
2023-03-13 22:01:43 +01:00
gestureEnabled : true ,
fullScreenGestureEnabled : true ,
headerShown : false ,
animationDuration : 250 ,
2024-01-20 01:13:29 +01:00
contentStyle : pal.view ,
2023-03-13 22:01:43 +01:00
} } >
2023-10-27 20:06:28 +02:00
< SearchTab.Screen name = "Search" getComponent = { ( ) = > SearchScreen } / >
2023-03-13 22:01:43 +01:00
{ commonScreens ( SearchTab as typeof HomeTab ) }
< / SearchTab.Navigator >
)
}
2023-05-26 03:02:37 +02:00
function FeedsTabNavigator() {
2024-01-20 01:13:29 +01:00
const pal = usePalette ( 'default' )
2023-05-26 03:02:37 +02:00
return (
< FeedsTab.Navigator
screenOptions = { {
2023-12-29 00:46:50 +01:00
animation : isAndroid ? 'none' : undefined ,
2023-05-26 03:02:37 +02:00
gestureEnabled : true ,
fullScreenGestureEnabled : true ,
headerShown : false ,
animationDuration : 250 ,
2024-01-20 01:13:29 +01:00
contentStyle : pal.view ,
2023-05-26 03:02:37 +02:00
} } >
2024-04-12 23:13:13 +02:00
< FeedsTab.Screen name = "Feeds" getComponent = { ( ) = > FeedsScreen } / >
2023-05-26 03:02:37 +02:00
{ commonScreens ( FeedsTab as typeof HomeTab ) }
< / FeedsTab.Navigator >
)
}
2023-03-13 22:01:43 +01:00
function NotificationsTabNavigator() {
2024-01-20 01:13:29 +01:00
const pal = usePalette ( 'default' )
2023-03-13 22:01:43 +01:00
return (
< NotificationsTab.Navigator
screenOptions = { {
2023-12-29 00:46:50 +01:00
animation : isAndroid ? 'none' : undefined ,
2023-03-13 22:01:43 +01:00
gestureEnabled : true ,
fullScreenGestureEnabled : true ,
headerShown : false ,
animationDuration : 250 ,
2024-01-20 01:13:29 +01:00
contentStyle : pal.view ,
2023-03-13 22:01:43 +01:00
} } >
< NotificationsTab.Screen
name = "Notifications"
2023-10-27 20:06:28 +02:00
getComponent = { ( ) = > NotificationsScreen }
2023-11-24 23:31:33 +01:00
options = { { requireAuth : true } }
2023-03-13 22:01:43 +01:00
/ >
{ commonScreens ( NotificationsTab as typeof HomeTab ) }
< / NotificationsTab.Navigator >
)
}
2023-11-13 03:13:11 +01:00
function MyProfileTabNavigator() {
2024-01-20 01:13:29 +01:00
const pal = usePalette ( 'default' )
2023-04-18 18:19:37 +02:00
return (
< MyProfileTab.Navigator
screenOptions = { {
2023-12-29 00:46:50 +01:00
animation : isAndroid ? 'none' : undefined ,
2023-04-18 18:19:37 +02:00
gestureEnabled : true ,
fullScreenGestureEnabled : true ,
headerShown : false ,
animationDuration : 250 ,
2024-01-20 01:13:29 +01:00
contentStyle : pal.view ,
2023-04-18 18:19:37 +02:00
} } >
< MyProfileTab.Screen
// @ts-ignore // TODO: fix this broken type in ProfileScreen
2023-11-24 23:31:33 +01:00
name = "MyProfile"
2023-10-27 20:06:28 +02:00
getComponent = { ( ) = > ProfileScreen }
2023-04-18 18:19:37 +02:00
initialParams = { {
2023-11-03 03:54:43 +01:00
name : 'me' ,
2023-04-18 18:19:37 +02:00
} }
/ >
{ commonScreens ( MyProfileTab as typeof HomeTab ) }
< / MyProfileTab.Navigator >
)
2023-11-13 03:13:11 +01:00
}
2023-04-18 18:19:37 +02:00
2024-04-27 06:54:18 +02:00
function MessagesTabNavigator() {
const pal = usePalette ( 'default' )
return (
< MessagesTab.Navigator
screenOptions = { {
animation : isAndroid ? 'none' : undefined ,
gestureEnabled : true ,
fullScreenGestureEnabled : true ,
headerShown : false ,
animationDuration : 250 ,
contentStyle : pal.view ,
} } >
< MessagesTab.Screen
2024-05-02 22:02:45 +02:00
name = "Messages"
getComponent = { ( ) = > MessagesScreen }
2024-04-27 06:54:18 +02:00
options = { { requireAuth : true } }
/ >
{ commonScreens ( MessagesTab as typeof HomeTab ) }
< / MessagesTab.Navigator >
)
}
2023-03-13 22:01:43 +01:00
/ * *
* The FlatNavigator is used by Web to represent the routes
* in a single ( "flat" ) stack .
* /
2023-11-13 03:13:11 +01:00
const FlatNavigator = ( ) = > {
2023-04-13 03:49:40 +02:00
const pal = usePalette ( 'default' )
2023-11-13 03:13:11 +01:00
const numUnread = useUnreadNotifications ( )
2024-01-22 23:46:32 +01:00
const screenListeners = useWebScrollRestoration ( )
2024-01-09 23:37:15 +01:00
const title = ( page : MessageDescriptor ) = > bskyTitle ( i18n . _ ( page ) , numUnread )
2024-01-22 23:46:32 +01:00
2023-03-13 22:01:43 +01:00
return (
< Flat.Navigator
2024-01-22 23:46:32 +01:00
screenListeners = { screenListeners }
2023-03-13 22:01:43 +01:00
screenOptions = { {
gestureEnabled : true ,
fullScreenGestureEnabled : true ,
headerShown : false ,
animationDuration : 250 ,
2024-01-20 01:13:29 +01:00
contentStyle : pal.view ,
2023-03-13 22:01:43 +01:00
} } >
2023-05-16 20:13:05 +02:00
< Flat.Screen
name = "Home"
2023-10-27 20:06:28 +02:00
getComponent = { ( ) = > HomeScreen }
2024-04-12 23:13:13 +02:00
options = { { title : title ( msg ` Home ` ) } }
2023-05-16 20:13:05 +02:00
/ >
< Flat.Screen
name = "Search"
2023-10-27 20:06:28 +02:00
getComponent = { ( ) = > SearchScreen }
2024-01-09 23:37:15 +01:00
options = { { title : title ( msg ` Search ` ) } }
2023-05-16 20:13:05 +02:00
/ >
2023-05-26 03:02:37 +02:00
< Flat.Screen
name = "Feeds"
2023-10-27 20:06:28 +02:00
getComponent = { ( ) = > FeedsScreen }
2024-04-12 23:13:13 +02:00
options = { { title : title ( msg ` Feeds ` ) } }
2023-05-26 03:02:37 +02:00
/ >
2023-05-16 20:13:05 +02:00
< Flat.Screen
name = "Notifications"
2023-10-27 20:06:28 +02:00
getComponent = { ( ) = > NotificationsScreen }
2024-01-09 23:37:15 +01:00
options = { { title : title ( msg ` Notifications ` ) , requireAuth : true } }
2023-05-16 20:13:05 +02:00
/ >
2024-04-27 06:54:18 +02:00
< Flat.Screen
2024-05-02 22:02:45 +02:00
name = "Messages"
getComponent = { ( ) = > MessagesScreen }
2024-04-27 06:54:18 +02:00
options = { { title : title ( msg ` Messages ` ) , requireAuth : true } }
/ >
2023-11-13 03:13:11 +01:00
{ commonScreens ( Flat as typeof HomeTab , numUnread ) }
2023-03-13 22:01:43 +01:00
< / Flat.Navigator >
)
2023-11-13 03:13:11 +01:00
}
2023-03-13 22:01:43 +01:00
/ * *
* The RoutesContainer should wrap all components which need access
* to the navigation context .
* /
const LINKING = {
2024-02-27 19:35:38 +01:00
// TODO figure out what we are going to use
prefixes : [ 'bsky://' , 'bluesky://' , 'https://bsky.app' ] ,
2023-03-13 22:01:43 +01:00
getPathFromState ( state : State ) {
// find the current node in the navigation tree
let node = state . routes [ state . index || 0 ]
while ( node . state ? . routes && typeof node . state ? . index === 'number' ) {
node = node . state ? . routes [ node . state ? . index ]
}
// build the path
const route = router . matchName ( node . name )
if ( typeof route === 'undefined' ) {
return '/' // default to home
}
return route . build ( ( node . params || { } ) as RouteParams )
} ,
getStateFromPath ( path : string ) {
2024-03-02 01:04:06 +01:00
const [ name , params ] = router . matchPath ( path )
2024-02-27 19:35:38 +01:00
// Any time we receive a url that starts with `intent/` we want to ignore it here. It will be handled in the
// intent handler hook. We should check for the trailing slash, because if there isn't one then it isn't a valid
// intent
2024-03-02 01:04:06 +01:00
// On web, there is no route state that's created by default, so we should initialize it as the home route. On
// native, since the home tab and the home screen are defined as initial routes, we don't need to return a state
// since it will be created by react-navigation.
if ( path . includes ( 'intent/' ) ) {
if ( isNative ) return
return buildStateObject ( 'Flat' , 'Home' , params )
}
2024-02-27 19:35:38 +01:00
2023-03-13 22:01:43 +01:00
if ( isNative ) {
if ( name === 'Search' ) {
return buildStateObject ( 'SearchTab' , 'Search' , params )
}
if ( name === 'Notifications' ) {
return buildStateObject ( 'NotificationsTab' , 'Notifications' , params )
}
2023-05-31 04:16:29 +02:00
if ( name === 'Home' ) {
return buildStateObject ( 'HomeTab' , 'Home' , params )
}
2024-04-27 06:54:18 +02:00
if ( name === 'Messages' ) {
2024-05-02 22:02:45 +02:00
return buildStateObject ( 'MessagesTab' , 'Messages' , params )
2024-04-27 06:54:18 +02:00
}
2023-05-31 04:16:29 +02:00
// if the path is something else, like a post, profile, or even settings, we need to initialize the home tab as pre-existing state otherwise the back button will not work
return buildStateObject ( 'HomeTab' , name , params , [
{
name : 'Home' ,
params : { } ,
} ,
] )
2023-03-13 22:01:43 +01:00
} else {
2024-02-27 05:33:48 +01:00
const res = buildStateObject ( 'Flat' , name , params )
return res
2023-03-13 22:01:43 +01:00
}
} ,
}
function RoutesContainer ( { children } : React . PropsWithChildren < { } > ) {
2023-04-24 23:36:05 +02:00
const theme = useColorSchemeStyle ( DefaultTheme , DarkTheme )
2023-12-06 01:28:11 +01:00
const { currentAccount } = useSession ( )
const { openModal } = useModalControls ( )
2024-04-10 00:09:53 +02:00
const prevLoggedRouteName = React . useRef < string | undefined > ( undefined )
2023-12-06 01:28:11 +01:00
function onReady() {
2024-04-10 00:09:53 +02:00
prevLoggedRouteName . current = getCurrentRouteName ( )
2023-12-06 21:04:05 +01:00
initAnalytics ( currentAccount )
2023-12-06 01:28:11 +01:00
if ( currentAccount && shouldRequestEmailConfirmation ( currentAccount ) ) {
openModal ( { name : 'verify-email' , showReminder : true } )
setEmailConfirmationRequested ( )
}
}
2023-03-13 22:01:43 +01:00
return (
2023-05-01 21:42:31 +02:00
< NavigationContainer
ref = { navigationRef }
linking = { LINKING }
theme = { theme }
2024-03-21 12:04:02 +01:00
onStateChange = { ( ) = > {
2024-04-10 00:09:53 +02:00
logEvent ( 'router:navigate' , {
from : prevLoggedRouteName . current ,
} )
prevLoggedRouteName . current = getCurrentRouteName ( )
2024-03-21 12:04:02 +01:00
} }
2023-05-01 21:42:31 +02:00
onReady = { ( ) = > {
2024-03-13 04:29:03 +01:00
attachRouteToLogEvents ( getCurrentRouteName )
2023-12-06 01:28:11 +01:00
logModuleInitTime ( )
onReady ( )
2024-03-21 12:04:02 +01:00
logEvent ( 'router:navigate' , { } )
2023-05-01 21:42:31 +02:00
} } >
2023-03-13 22:01:43 +01:00
{ children }
< / NavigationContainer >
)
}
2024-03-13 04:29:03 +01:00
function getCurrentRouteName() {
2024-03-20 04:24:05 +01:00
if ( navigationRef . isReady ( ) ) {
return navigationRef . getCurrentRoute ( ) ? . name
} else {
return undefined
}
2024-03-13 04:29:03 +01:00
}
2023-03-13 22:01:43 +01:00
/ * *
* These helpers can be used from outside of the RoutesContainer
* ( eg in the state models ) .
* /
function navigate < K extends keyof AllNavigatorParams > (
name : K ,
params? : AllNavigatorParams [ K ] ,
) {
if ( navigationRef . isReady ( ) ) {
2023-10-13 21:10:15 +02:00
return Promise . race ( [
new Promise < void > ( resolve = > {
const handler = ( ) = > {
resolve ( )
navigationRef . removeListener ( 'state' , handler )
}
navigationRef . addListener ( 'state' , handler )
// @ts-ignore I dont know what would make typescript happy but I have a life -prf
navigationRef . navigate ( name , params )
} ) ,
timeout ( 1 e3 ) ,
] )
2023-03-13 22:01:43 +01:00
}
2023-10-13 21:10:15 +02:00
return Promise . resolve ( )
2023-03-13 22:01:43 +01:00
}
function resetToTab ( tabName : 'HomeTab' | 'SearchTab' | 'NotificationsTab' ) {
if ( navigationRef . isReady ( ) ) {
navigate ( tabName )
2023-05-04 23:18:27 +02:00
if ( navigationRef . canGoBack ( ) ) {
navigationRef . dispatch ( StackActions . popToTop ( ) ) //we need to check .canGoBack() before calling it
}
2023-03-13 22:01:43 +01:00
}
}
2023-07-20 08:50:42 +02:00
// returns a promise that resolves after the state reset is complete
function reset ( ) : Promise < void > {
2023-04-21 19:21:38 +02:00
if ( navigationRef . isReady ( ) ) {
navigationRef . dispatch (
CommonActions . reset ( {
index : 0 ,
routes : [ { name : isNative ? 'HomeTab' : 'Home' } ] ,
} ) ,
)
2023-07-20 08:50:42 +02:00
return Promise . race ( [
timeout ( 1 e3 ) ,
new Promise < void > ( resolve = > {
const handler = ( ) = > {
resolve ( )
navigationRef . removeListener ( 'state' , handler )
}
navigationRef . addListener ( 'state' , handler )
} ) ,
] )
} else {
return Promise . resolve ( )
2023-04-21 19:21:38 +02:00
}
}
2023-03-13 22:01:43 +01:00
function handleLink ( url : string ) {
let path
if ( url . startsWith ( '/' ) ) {
path = url
} else if ( url . startsWith ( 'http' ) ) {
try {
path = new URL ( url ) . pathname
} catch ( e ) {
console . error ( 'Invalid url' , url , e )
return
}
} else {
console . error ( 'Invalid url' , url )
return
}
const [ name , params ] = router . matchPath ( path )
if ( isNative ) {
if ( name === 'Search' ) {
resetToTab ( 'SearchTab' )
} else if ( name === 'Notifications' ) {
resetToTab ( 'NotificationsTab' )
} else {
resetToTab ( 'HomeTab' )
// @ts-ignore matchPath doesnt give us type-checked output -prf
navigate ( name , params )
}
} else {
// @ts-ignore matchPath doesnt give us type-checked output -prf
navigate ( name , params )
}
}
2023-12-06 01:28:11 +01:00
let didInit = false
function logModuleInitTime() {
if ( didInit ) {
return
}
didInit = true
2024-03-08 05:33:42 +01:00
2023-12-06 01:28:11 +01:00
const initMs = Math . round (
// @ts-ignore Emitted by Metro in the bundle prelude
performance . now ( ) - global . __BUNDLE_START_TIME__ ,
)
console . log ( ` Time to first paint: ${ initMs } ms ` )
2024-03-13 04:29:03 +01:00
logEvent ( 'init' , {
initMs ,
} )
2024-03-08 05:33:42 +01:00
2023-11-01 16:31:33 +01:00
if ( __DEV__ ) {
// This log is noisy, so keep false committed
const shouldLog = false
// Relies on our patch to polyfill.js in metro-runtime
const initLogs = ( global as any ) . __INIT_LOGS__
if ( shouldLog && Array . isArray ( initLogs ) ) {
console . log ( initLogs . join ( '\n' ) )
}
}
}
2023-03-13 22:01:43 +01:00
export {
2024-03-21 12:04:02 +01:00
FlatNavigator ,
handleLink ,
2023-03-13 22:01:43 +01:00
navigate ,
2023-04-21 19:21:38 +02:00
reset ,
2024-03-21 12:04:02 +01:00
resetToTab ,
2023-03-13 22:01:43 +01:00
RoutesContainer ,
2024-03-21 12:04:02 +01:00
TabsNavigator ,
2023-03-13 22:01:43 +01:00
}