about summary refs log tree commit diff
path: root/src/Navigation.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'src/Navigation.tsx')
-rw-r--r--src/Navigation.tsx172
1 files changed, 113 insertions, 59 deletions
diff --git a/src/Navigation.tsx b/src/Navigation.tsx
index 897d86e40..ab40ff422 100644
--- a/src/Navigation.tsx
+++ b/src/Navigation.tsx
@@ -1,82 +1,86 @@
 import * as React from 'react'
-import {
-  NavigationContainer,
-  createNavigationContainerRef,
-  CommonActions,
-  StackActions,
-  DefaultTheme,
-  DarkTheme,
-} from '@react-navigation/native'
+import {JSX} from 'react/jsx-runtime'
+import {i18n, MessageDescriptor} from '@lingui/core'
+import {msg} from '@lingui/macro'
 import {
   BottomTabBarProps,
   createBottomTabNavigator,
 } from '@react-navigation/bottom-tabs'
 import {
-  HomeTabNavigatorParams,
-  SearchTabNavigatorParams,
+  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,
   FeedsTabNavigatorParams,
-  NotificationsTabNavigatorParams,
   FlatNavigatorParams,
-  AllNavigatorParams,
+  HomeTabNavigatorParams,
   MyProfileTabNavigatorParams,
-  BottomTabNavigatorParams,
+  NotificationsTabNavigatorParams,
+  SearchTabNavigatorParams,
 } from 'lib/routes/types'
-import {BottomBar} from './view/shell/bottom-bar/BottomBar'
-import {buildStateObject} from 'lib/routes/helpers'
-import {State, RouteParams} from 'lib/routes/types'
+import {RouteParams, State} from 'lib/routes/types'
+import {bskyTitle} from 'lib/strings/headings'
 import {isAndroid, isNative} from 'platform/detection'
-import {useColorSchemeStyle} from 'lib/hooks/useColorSchemeStyle'
+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'
 import {router} from './routes'
-import {usePalette} from 'lib/hooks/usePalette'
-import {bskyTitle} from 'lib/strings/headings'
-import {JSX} from 'react/jsx-runtime'
-import {timeout} from 'lib/async/timeout'
+import {useModalControls} from './state/modals'
 import {useUnreadNotifications} from './state/queries/notifications/unread'
 import {useSession} from './state/session'
-import {useModalControls} from './state/modals'
 import {
-  shouldRequestEmailConfirmation,
   setEmailConfirmationRequested,
+  shouldRequestEmailConfirmation,
 } from './state/shell/reminders'
-import {init as initAnalytics} from './lib/analytics/analytics'
-import {useWebScrollRestoration} from './lib/hooks/useWebScrollRestoration'
-
-import {HomeScreen} from './view/screens/Home'
-import {SearchScreen} from './view/screens/Search'
+import {CommunityGuidelinesScreen} from './view/screens/CommunityGuidelines'
+import {CopyrightPolicyScreen} from './view/screens/CopyrightPolicy'
+import {DebugModScreen} from './view/screens/DebugMod'
 import {FeedsScreen} from './view/screens/Feeds'
-import {NotificationsScreen} from './view/screens/Notifications'
+import {HomeScreen} from './view/screens/Home'
+import {LanguageSettingsScreen} from './view/screens/LanguageSettings'
 import {ListsScreen} from './view/screens/Lists'
-import {ModerationScreen} from './view/screens/Moderation'
+import {LogScreen} from './view/screens/Log'
 import {ModerationModlistsScreen} from './view/screens/ModerationModlists'
 import {NotFoundScreen} from './view/screens/NotFound'
-import {SettingsScreen} from './view/screens/Settings'
-import {LanguageSettingsScreen} from './view/screens/LanguageSettings'
+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'
 import {ProfileScreen} from './view/screens/Profile'
-import {ProfileFollowersScreen} from './view/screens/ProfileFollowers'
-import {ProfileFollowsScreen} from './view/screens/ProfileFollows'
 import {ProfileFeedScreen} from './view/screens/ProfileFeed'
 import {ProfileFeedLikedByScreen} from './view/screens/ProfileFeedLikedBy'
+import {ProfileFollowersScreen} from './view/screens/ProfileFollowers'
+import {ProfileFollowsScreen} from './view/screens/ProfileFollows'
 import {ProfileListScreen} from './view/screens/ProfileList'
-import {PostThreadScreen} from './view/screens/PostThread'
-import {PostLikedByScreen} from './view/screens/PostLikedBy'
-import {PostRepostedByScreen} from './view/screens/PostRepostedBy'
+import {SearchScreen} from './view/screens/Search'
+import {SettingsScreen} from './view/screens/Settings'
 import {Storybook} from './view/screens/Storybook'
-import {LogScreen} from './view/screens/Log'
 import {SupportScreen} from './view/screens/Support'
-import {PrivacyPolicyScreen} from './view/screens/PrivacyPolicy'
 import {TermsOfServiceScreen} from './view/screens/TermsOfService'
-import {CommunityGuidelinesScreen} from './view/screens/CommunityGuidelines'
-import {CopyrightPolicyScreen} from './view/screens/CopyrightPolicy'
-import {AppPasswords} from 'view/screens/AppPasswords'
-import {ModerationMutedAccounts} from 'view/screens/ModerationMutedAccounts'
-import {ModerationBlockedAccounts} from 'view/screens/ModerationBlockedAccounts'
-import {SavedFeeds} from 'view/screens/SavedFeeds'
-import {PreferencesHomeFeed} from 'view/screens/PreferencesHomeFeed'
-import {PreferencesThreads} from 'view/screens/PreferencesThreads'
-import {PreferencesExternalEmbeds} from '#/view/screens/PreferencesExternalEmbeds'
+import {BottomBar} from './view/shell/bottom-bar/BottomBar'
 import {createNativeStackNavigatorWithAuth} from './view/shell/createNativeStackNavigatorWithAuth'
-import {msg} from '@lingui/macro'
-import {i18n, MessageDescriptor} from '@lingui/core'
 
 const navigationRef = createNavigationContainerRef<AllNavigatorParams>()
 
@@ -197,11 +201,21 @@ function commonScreens(Stack: typeof HomeTab, unreadCountLabel?: string) {
         options={{title: title(msg`Liked by`)}}
       />
       <Stack.Screen
+        name="ProfileLabelerLikedBy"
+        getComponent={() => ProfileLabelerLikedByScreen}
+        options={{title: title(msg`Liked by`)}}
+      />
+      <Stack.Screen
         name="Debug"
         getComponent={() => Storybook}
         options={{title: title(msg`Storybook`), requireAuth: true}}
       />
       <Stack.Screen
+        name="DebugMod"
+        getComponent={() => DebugModScreen}
+        options={{title: title(msg`Moderation states`), requireAuth: true}}
+      />
+      <Stack.Screen
         name="Log"
         getComponent={() => LogScreen}
         options={{title: title(msg`Log`), requireAuth: true}}
@@ -242,9 +256,12 @@ function commonScreens(Stack: typeof HomeTab, unreadCountLabel?: string) {
         options={{title: title(msg`Edit My Feeds`), requireAuth: true}}
       />
       <Stack.Screen
-        name="PreferencesHomeFeed"
-        getComponent={() => PreferencesHomeFeed}
-        options={{title: title(msg`Home Feed Preferences`), requireAuth: true}}
+        name="PreferencesFollowingFeed"
+        getComponent={() => PreferencesFollowingFeed}
+        options={{
+          title: title(msg`Following Feed Preferences`),
+          requireAuth: true,
+        }}
       />
       <Stack.Screen
         name="PreferencesThreads"
@@ -259,6 +276,11 @@ function commonScreens(Stack: typeof HomeTab, unreadCountLabel?: string) {
           requireAuth: true,
         }}
       />
+      <Stack.Screen
+        name="Hashtag"
+        getComponent={() => HashtagScreen}
+        options={{title: title(msg`Hashtag`)}}
+      />
     </>
   )
 }
@@ -457,7 +479,8 @@ const FlatNavigator = () => {
  */
 
 const LINKING = {
-  prefixes: ['bsky://', 'https://bsky.app'],
+  // TODO figure out what we are going to use
+  prefixes: ['bsky://', 'bluesky://', 'https://bsky.app'],
 
   getPathFromState(state: State) {
     // find the current node in the navigation tree
@@ -476,6 +499,18 @@ const LINKING = {
 
   getStateFromPath(path: string) {
     const [name, params] = router.matchPath(path)
+
+    // 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
+    // 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)
+    }
+
     if (isNative) {
       if (name === 'Search') {
         return buildStateObject('SearchTab', 'Search', params)
@@ -494,7 +529,8 @@ const LINKING = {
         },
       ])
     } else {
-      return buildStateObject('Flat', name, params)
+      const res = buildStateObject('Flat', name, params)
+      return res
     }
   },
 }
@@ -518,15 +554,28 @@ function RoutesContainer({children}: React.PropsWithChildren<{}>) {
       ref={navigationRef}
       linking={LINKING}
       theme={theme}
+      onStateChange={() => {
+        logEvent('router:navigate', {})
+      }}
       onReady={() => {
+        attachRouteToLogEvents(getCurrentRouteName)
         logModuleInitTime()
         onReady()
+        logEvent('router:navigate', {})
       }}>
       {children}
     </NavigationContainer>
   )
 }
 
+function getCurrentRouteName() {
+  if (navigationRef.isReady()) {
+    return navigationRef.getCurrentRoute()?.name
+  } else {
+    return undefined
+  }
+}
+
 /**
  * These helpers can be used from outside of the RoutesContainer
  * (eg in the state models).
@@ -626,11 +675,16 @@ function logModuleInitTime() {
     return
   }
   didInit = true
+
   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`)
+  logEvent('init', {
+    initMs,
+  })
+
   if (__DEV__) {
     // This log is noisy, so keep false committed
     const shouldLog = false
@@ -643,11 +697,11 @@ function logModuleInitTime() {
 }
 
 export {
+  FlatNavigator,
+  handleLink,
   navigate,
-  resetToTab,
   reset,
-  handleLink,
-  TabsNavigator,
-  FlatNavigator,
+  resetToTab,
   RoutesContainer,
+  TabsNavigator,
 }