Add nav:click event to all main nav items (#10540)
Co-authored-by: Eric Bailey <git@esb.lol>
This commit is contained in:
@@ -60,6 +60,20 @@ export type Events = {
|
||||
'router:navigate': {
|
||||
from?: string
|
||||
}
|
||||
'nav:click': {
|
||||
item:
|
||||
| 'home'
|
||||
| 'search'
|
||||
| 'chat'
|
||||
| 'notifications'
|
||||
| 'profile'
|
||||
| 'feeds'
|
||||
| 'lists'
|
||||
| 'saved'
|
||||
| 'settings'
|
||||
| 'menu'
|
||||
surface: 'bottomBar' | 'drawer' | 'drawerHeader' | 'topBar' | 'leftNav'
|
||||
}
|
||||
'deepLink:referrerReceived': {
|
||||
to: string
|
||||
referrer: string
|
||||
|
||||
@@ -30,6 +30,7 @@ import {
|
||||
} from '#/components/Layout/const'
|
||||
import {ScrollbarOffsetContext} from '#/components/Layout/context'
|
||||
import {Text} from '#/components/Typography'
|
||||
import {useAnalytics} from '#/analytics'
|
||||
import {IS_IOS} from '#/env'
|
||||
|
||||
export function Outer({
|
||||
@@ -150,13 +151,15 @@ export function BackButton({onPress, style, ...props}: Partial<ButtonProps>) {
|
||||
|
||||
export function MenuButton() {
|
||||
const {_} = useLingui()
|
||||
const ax = useAnalytics()
|
||||
const setDrawerOpen = useSetDrawerOpen()
|
||||
const {gtMobile} = useBreakpoints()
|
||||
|
||||
const onPress = useCallback(() => {
|
||||
ax.metric('nav:click', {item: 'menu', surface: 'topBar'})
|
||||
Keyboard.dismiss()
|
||||
setDrawerOpen(true)
|
||||
}, [setDrawerOpen])
|
||||
}, [setDrawerOpen, ax])
|
||||
|
||||
return gtMobile ? null : (
|
||||
<Slot>
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
import {type Events} from '#/analytics/metrics/types'
|
||||
|
||||
export type SharedNavTab =
|
||||
| 'Home'
|
||||
| 'Search'
|
||||
| 'Messages'
|
||||
| 'Notifications'
|
||||
| 'MyProfile'
|
||||
|
||||
export const TAB_TO_NAV_ITEM: Record<
|
||||
SharedNavTab,
|
||||
Events['nav:click']['item']
|
||||
> = {
|
||||
Home: 'home',
|
||||
Search: 'search',
|
||||
Messages: 'chat',
|
||||
Notifications: 'notifications',
|
||||
MyProfile: 'profile',
|
||||
}
|
||||
@@ -190,7 +190,7 @@ const GalleryItem = ({
|
||||
return (
|
||||
<View
|
||||
ref={altBtnRef}
|
||||
style={imageStyle as ViewStyle}
|
||||
style={imageStyle}
|
||||
// Fixes ALT and icons appearing with half opacity when the post is inactive
|
||||
renderToHardwareTextureAndroid>
|
||||
<TouchableOpacity
|
||||
|
||||
@@ -14,6 +14,7 @@ import {ButtonIcon} from '#/components/Button'
|
||||
import {Hashtag_Stroke2_Corner0_Rounded as FeedsIcon} from '#/components/icons/Hashtag'
|
||||
import * as Layout from '#/components/Layout'
|
||||
import {Link} from '#/components/Link'
|
||||
import {useAnalytics} from '#/analytics'
|
||||
|
||||
export function HomeHeaderLayout(props: {
|
||||
children: React.ReactNode
|
||||
@@ -38,6 +39,7 @@ function HomeHeaderLayoutDesktopAndTablet({
|
||||
const {headerHeight} = useShellLayout()
|
||||
const {hasSession} = useSession()
|
||||
const {_} = useLingui()
|
||||
const ax = useAnalytics()
|
||||
const kawaii = useKawaiiMode()
|
||||
const gutters = useGutters([0, 'base'])
|
||||
|
||||
@@ -59,6 +61,9 @@ function HomeHeaderLayoutDesktopAndTablet({
|
||||
variant="ghost"
|
||||
color="secondary"
|
||||
shape="square"
|
||||
onPress={() => {
|
||||
ax.metric('nav:click', {item: 'feeds', surface: 'topBar'})
|
||||
}}
|
||||
style={[a.justify_center]}>
|
||||
<ButtonIcon icon={FeedsIcon} size="lg" />
|
||||
</Link>
|
||||
|
||||
@@ -19,6 +19,7 @@ import {ButtonIcon} from '#/components/Button'
|
||||
import {Hashtag_Stroke2_Corner0_Rounded as FeedsIcon} from '#/components/icons/Hashtag'
|
||||
import * as Layout from '#/components/Layout'
|
||||
import {Link} from '#/components/Link'
|
||||
import {useAnalytics} from '#/analytics'
|
||||
import {IS_DEV, IS_LIQUID_GLASS} from '#/env'
|
||||
|
||||
export function HomeHeaderLayoutMobile({
|
||||
@@ -29,6 +30,7 @@ export function HomeHeaderLayoutMobile({
|
||||
}) {
|
||||
const t = useTheme()
|
||||
const {_} = useLingui()
|
||||
const ax = useAnalytics()
|
||||
const {headerHeight} = useShellLayout()
|
||||
const insets = useSafeAreaInsets()
|
||||
const headerMinimalShellTransform = useHomeHeaderTransform()
|
||||
@@ -84,6 +86,9 @@ export function HomeHeaderLayoutMobile({
|
||||
variant="ghost"
|
||||
color="secondary"
|
||||
shape="square"
|
||||
onPress={() => {
|
||||
ax.metric('nav:click', {item: 'feeds', surface: 'topBar'})
|
||||
}}
|
||||
style={[
|
||||
a.justify_center,
|
||||
{marginRight: -Layout.BUTTON_VISUAL_ALIGNMENT_OFFSET},
|
||||
|
||||
@@ -10,6 +10,7 @@ import {FEEDBACK_FORM_URL, HELP_DESK_URL} from '#/lib/constants'
|
||||
import {type PressableScale} from '#/lib/custom-animations/PressableScale'
|
||||
import {useNavigationTabState} from '#/lib/hooks/useNavigationTabState'
|
||||
import {getTabState, TabState} from '#/lib/routes/helpers'
|
||||
import {type SharedNavTab, TAB_TO_NAV_ITEM} from '#/lib/routes/tab-to-nav-item'
|
||||
import {type NavigationProp} from '#/lib/routes/types'
|
||||
import {sanitizeHandle} from '#/lib/strings/handles'
|
||||
import {colors} from '#/lib/styles'
|
||||
@@ -55,6 +56,7 @@ import {
|
||||
import {InlineLinkText} from '#/components/Link'
|
||||
import {ProfileBadges} from '#/components/ProfileBadges'
|
||||
import {Text} from '#/components/Typography'
|
||||
import {useAnalytics} from '#/analytics'
|
||||
import {IS_WEB} from '#/env'
|
||||
import {useActorStatus} from '#/features/liveNow'
|
||||
|
||||
@@ -138,6 +140,7 @@ let DrawerContent = ({}: React.PropsWithoutRef<{}>): React.ReactNode => {
|
||||
const insets = useSafeAreaInsets()
|
||||
const setDrawerOpen = useSetDrawerOpen()
|
||||
const navigation = useNavigation<NavigationProp>()
|
||||
const ax = useAnalytics()
|
||||
const {
|
||||
isAtHome,
|
||||
isAtSearch,
|
||||
@@ -153,7 +156,11 @@ let DrawerContent = ({}: React.PropsWithoutRef<{}>): React.ReactNode => {
|
||||
// =
|
||||
|
||||
const onPressTab = useCallback(
|
||||
(tab: 'Home' | 'Search' | 'Messages' | 'Notifications' | 'MyProfile') => {
|
||||
(tab: SharedNavTab, surface: 'drawer' | 'drawerHeader' = 'drawer') => {
|
||||
ax.metric('nav:click', {
|
||||
item: TAB_TO_NAV_ITEM[tab],
|
||||
surface,
|
||||
})
|
||||
const state = navigation.getState()
|
||||
setDrawerOpen(false)
|
||||
if (IS_WEB) {
|
||||
@@ -190,7 +197,7 @@ let DrawerContent = ({}: React.PropsWithoutRef<{}>): React.ReactNode => {
|
||||
}
|
||||
}
|
||||
},
|
||||
[navigation, setDrawerOpen, currentAccount],
|
||||
[navigation, setDrawerOpen, currentAccount, ax],
|
||||
)
|
||||
|
||||
const onPressHome = useCallback(() => onPressTab('Home'), [onPressTab])
|
||||
@@ -211,25 +218,33 @@ let DrawerContent = ({}: React.PropsWithoutRef<{}>): React.ReactNode => {
|
||||
onPressTab('MyProfile')
|
||||
}, [onPressTab])
|
||||
|
||||
const onPressDrawerHeaderProfile = useCallback(() => {
|
||||
onPressTab('MyProfile', 'drawerHeader')
|
||||
}, [onPressTab])
|
||||
|
||||
const onPressMyFeeds = useCallback(() => {
|
||||
ax.metric('nav:click', {item: 'feeds', surface: 'drawer'})
|
||||
navigation.navigate('Feeds')
|
||||
setDrawerOpen(false)
|
||||
}, [navigation, setDrawerOpen])
|
||||
}, [navigation, setDrawerOpen, ax])
|
||||
|
||||
const onPressLists = useCallback(() => {
|
||||
ax.metric('nav:click', {item: 'lists', surface: 'drawer'})
|
||||
navigation.navigate('Lists')
|
||||
setDrawerOpen(false)
|
||||
}, [navigation, setDrawerOpen])
|
||||
}, [navigation, setDrawerOpen, ax])
|
||||
|
||||
const onPressBookmarks = useCallback(() => {
|
||||
ax.metric('nav:click', {item: 'saved', surface: 'drawer'})
|
||||
navigation.navigate('Bookmarks')
|
||||
setDrawerOpen(false)
|
||||
}, [navigation, setDrawerOpen])
|
||||
}, [navigation, setDrawerOpen, ax])
|
||||
|
||||
const onPressSettings = useCallback(() => {
|
||||
ax.metric('nav:click', {item: 'settings', surface: 'drawer'})
|
||||
navigation.navigate('Settings')
|
||||
setDrawerOpen(false)
|
||||
}, [navigation, setDrawerOpen])
|
||||
}, [navigation, setDrawerOpen, ax])
|
||||
|
||||
const onPressFeedback = useCallback(() => {
|
||||
Linking.openURL(
|
||||
@@ -265,7 +280,7 @@ let DrawerContent = ({}: React.PropsWithoutRef<{}>): React.ReactNode => {
|
||||
{hasSession && currentAccount ? (
|
||||
<DrawerProfileCard
|
||||
account={currentAccount}
|
||||
onPressProfile={onPressProfile}
|
||||
onPressProfile={onPressDrawerHeaderProfile}
|
||||
/>
|
||||
) : (
|
||||
<View style={[a.pr_xl]}>
|
||||
|
||||
@@ -17,6 +17,7 @@ import {useMinimalShellFooterTransform} from '#/lib/hooks/useMinimalShellTransfo
|
||||
import {useNavigationTabState} from '#/lib/hooks/useNavigationTabState'
|
||||
import {clamp} from '#/lib/numbers'
|
||||
import {getTabState, TabState} from '#/lib/routes/helpers'
|
||||
import {type SharedNavTab, TAB_TO_NAV_ITEM} from '#/lib/routes/tab-to-nav-item'
|
||||
import {emitSoftReset} from '#/state/events'
|
||||
import {useUnreadMessageCount} from '#/state/queries/messages/list-conversations'
|
||||
import {useUnreadNotifications} from '#/state/queries/notifications/unread'
|
||||
@@ -50,16 +51,16 @@ import {
|
||||
} from '#/components/icons/Message'
|
||||
import {Text} from '#/components/Typography'
|
||||
import {useAgeAssurance} from '#/ageAssurance'
|
||||
import {useAnalytics} from '#/analytics'
|
||||
import {useActorStatus} from '#/features/liveNow'
|
||||
import {useDemoMode} from '#/storage/hooks/demo-mode'
|
||||
import {styles} from './BottomBarStyles'
|
||||
|
||||
type TabOptions = 'Home' | 'Search' | 'Messages' | 'Notifications' | 'MyProfile'
|
||||
|
||||
export function BottomBar({navigation}: BottomTabBarProps) {
|
||||
const {hasSession, currentAccount} = useSession()
|
||||
const t = useTheme()
|
||||
const {_} = useLingui()
|
||||
const ax = useAnalytics()
|
||||
const safeAreaInsets = useSafeAreaInsets()
|
||||
const {footerHeight} = useShellLayout()
|
||||
const {isAtHome, isAtSearch, isAtNotifications, isAtMyProfile, isAtMessages} =
|
||||
@@ -89,7 +90,11 @@ export function BottomBar({navigation}: BottomTabBarProps) {
|
||||
}, [requestSwitchToAccount, closeAllActiveElements])
|
||||
|
||||
const onPressTab = useCallback(
|
||||
(tab: TabOptions) => {
|
||||
(tab: SharedNavTab) => {
|
||||
ax.metric('nav:click', {
|
||||
item: TAB_TO_NAV_ITEM[tab],
|
||||
surface: 'bottomBar',
|
||||
})
|
||||
const state = navigation.getState()
|
||||
const tabState = getTabState(state, tab)
|
||||
if (tabState === TabState.InsideAtRoot) {
|
||||
@@ -117,7 +122,7 @@ export function BottomBar({navigation}: BottomTabBarProps) {
|
||||
dedupe(() => navigation.navigate(`${tab}Tab`))
|
||||
}
|
||||
},
|
||||
[navigation, dedupe],
|
||||
[navigation, dedupe, ax],
|
||||
)
|
||||
const onPressHome = useCallback(() => onPressTab('Home'), [onPressTab])
|
||||
const onPressSearch = useCallback(() => onPressTab('Search'), [onPressTab])
|
||||
|
||||
@@ -44,8 +44,11 @@ import {
|
||||
} from '#/components/icons/Message'
|
||||
import {Text} from '#/components/Typography'
|
||||
import {useAgeAssurance} from '#/ageAssurance'
|
||||
import {useAnalytics} from '#/analytics'
|
||||
import {styles} from './BottomBarStyles'
|
||||
|
||||
type NavItemValue = 'home' | 'search' | 'chat' | 'notifications' | 'profile'
|
||||
|
||||
export function BottomBarWeb() {
|
||||
const {_} = useLingui()
|
||||
const {hasSession, currentAccount} = useSession()
|
||||
@@ -96,7 +99,7 @@ export function BottomBarWeb() {
|
||||
onLayout={event => footerHeight.set(event.nativeEvent.layout.height)}>
|
||||
{hasSession ? (
|
||||
<>
|
||||
<NavItem routeName="Home" href="/">
|
||||
<NavItem routeName="Home" href="/" navItem="home">
|
||||
{({isActive}) => {
|
||||
const Icon = isActive ? HomeFilled : Home
|
||||
return (
|
||||
@@ -108,7 +111,7 @@ export function BottomBarWeb() {
|
||||
)
|
||||
}}
|
||||
</NavItem>
|
||||
<NavItem routeName="Search" href="/search">
|
||||
<NavItem routeName="Search" href="/search" navItem="search">
|
||||
{({isActive}) => {
|
||||
const Icon = isActive ? MagnifyingGlassFilled : MagnifyingGlass
|
||||
return (
|
||||
@@ -126,6 +129,7 @@ export function BottomBarWeb() {
|
||||
<NavItem
|
||||
routeName="Messages"
|
||||
href="/messages"
|
||||
navItem="chat"
|
||||
notificationCount={
|
||||
aa.flags.chatDisabled
|
||||
? undefined
|
||||
@@ -152,6 +156,7 @@ export function BottomBarWeb() {
|
||||
<NavItem
|
||||
routeName="Notifications"
|
||||
href="/notifications"
|
||||
navItem="notifications"
|
||||
notificationCount={notificationCountStr}>
|
||||
{({isActive}) => {
|
||||
const Icon = isActive ? BellFilled : Bell
|
||||
@@ -174,6 +179,7 @@ export function BottomBarWeb() {
|
||||
})
|
||||
: '/'
|
||||
}
|
||||
navItem="profile"
|
||||
onLongPress={onLongPressProfile}>
|
||||
{({isActive}) => (
|
||||
<View style={styles.ctrlIconSizingWrapper}>
|
||||
@@ -257,12 +263,22 @@ const NavItem: React.FC<{
|
||||
children: (props: {isActive: boolean}) => React.ReactNode
|
||||
href: string
|
||||
routeName: string
|
||||
navItem: NavItemValue
|
||||
hasNew?: boolean
|
||||
notificationCount?: string
|
||||
onLongPress?: () => void
|
||||
}> = ({children, href, routeName, hasNew, notificationCount, onLongPress}) => {
|
||||
}> = ({
|
||||
children,
|
||||
href,
|
||||
routeName,
|
||||
navItem,
|
||||
hasNew,
|
||||
notificationCount,
|
||||
onLongPress,
|
||||
}) => {
|
||||
const t = useTheme()
|
||||
const {_} = useLingui()
|
||||
const ax = useAnalytics()
|
||||
const {currentAccount} = useSession()
|
||||
const currentRoute = useNavigationState(state => {
|
||||
if (!state) {
|
||||
@@ -271,6 +287,10 @@ const NavItem: React.FC<{
|
||||
return getCurrentRoute(state)
|
||||
})
|
||||
|
||||
const onBeforePress = useCallback(() => {
|
||||
ax.metric('nav:click', {item: navItem, surface: 'bottomBar'})
|
||||
}, [ax, navItem])
|
||||
|
||||
// Checks whether we're on someone else's profile
|
||||
const isOnDifferentProfile =
|
||||
currentRoute.name === 'Profile' &&
|
||||
@@ -295,6 +315,7 @@ const NavItem: React.FC<{
|
||||
aria-role="link"
|
||||
aria-label={routeName}
|
||||
accessible={true}
|
||||
onBeforePress={onBeforePress}
|
||||
onLongPress={onLongPress}>
|
||||
{children({isActive})}
|
||||
{notificationCount ? (
|
||||
|
||||
@@ -83,6 +83,8 @@ import * as Menu from '#/components/Menu'
|
||||
import * as Prompt from '#/components/Prompt'
|
||||
import {Text} from '#/components/Typography'
|
||||
import {useAgeAssurance} from '#/ageAssurance'
|
||||
import {useAnalytics} from '#/analytics'
|
||||
import {type Events} from '#/analytics/metrics/types'
|
||||
import {useActorStatus} from '#/features/liveNow'
|
||||
import {router} from '#/routes'
|
||||
import {PlatformInfo} from '../../../../modules/expo-bluesky-swiss-army'
|
||||
@@ -389,10 +391,20 @@ interface NavItemProps {
|
||||
}
|
||||
label: string
|
||||
minimal: boolean
|
||||
navItem: Events['nav:click']['item']
|
||||
}
|
||||
function NavItem({count, hasNew, href, icons, label, minimal}: NavItemProps) {
|
||||
function NavItem({
|
||||
count,
|
||||
hasNew,
|
||||
href,
|
||||
icons,
|
||||
label,
|
||||
minimal,
|
||||
navItem,
|
||||
}: NavItemProps) {
|
||||
const t = useTheme()
|
||||
const {t: l} = useLingui()
|
||||
const ax = useAnalytics()
|
||||
const {currentAccount} = useSession()
|
||||
|
||||
const [pathName] = useMemo(() => router.matchPath(href), [href])
|
||||
@@ -412,6 +424,7 @@ function NavItem({count, hasNew, href, icons, label, minimal}: NavItemProps) {
|
||||
const navigation = useNavigation<NavigationProp>()
|
||||
const onPressWrapped = useCallback(
|
||||
(e: React.MouseEvent<HTMLAnchorElement, MouseEvent>) => {
|
||||
ax.metric('nav:click', {item: navItem, surface: 'leftNav'})
|
||||
if (e.ctrlKey || e.metaKey || e.altKey) {
|
||||
return
|
||||
}
|
||||
@@ -424,7 +437,7 @@ function NavItem({count, hasNew, href, icons, label, minimal}: NavItemProps) {
|
||||
navigation.navigate(screen, params, {pop: true})
|
||||
}
|
||||
},
|
||||
[navigation, href, isCurrent],
|
||||
[navigation, href, isCurrent, ax, navItem],
|
||||
)
|
||||
|
||||
const Icon = isCurrent || isRelated ? icons.active : icons.inactive
|
||||
@@ -652,6 +665,7 @@ export function DesktopLeftNav({routeName}: {routeName: string}) {
|
||||
<NavItem
|
||||
label={l`Home`}
|
||||
href="/"
|
||||
navItem="home"
|
||||
minimal={leftNavMinimal}
|
||||
icons={{
|
||||
inactive: HomeIcon,
|
||||
@@ -661,6 +675,7 @@ export function DesktopLeftNav({routeName}: {routeName: string}) {
|
||||
<NavItem
|
||||
label={l`Explore`}
|
||||
href="/search"
|
||||
navItem="search"
|
||||
minimal={leftNavMinimal}
|
||||
icons={{
|
||||
inactive: MagnifyingGlassIcon,
|
||||
@@ -670,6 +685,7 @@ export function DesktopLeftNav({routeName}: {routeName: string}) {
|
||||
<NavItem
|
||||
label={l`Notifications`}
|
||||
href="/notifications"
|
||||
navItem="notifications"
|
||||
minimal={leftNavMinimal}
|
||||
count={numUnreadNotifications}
|
||||
icons={{
|
||||
@@ -680,6 +696,7 @@ export function DesktopLeftNav({routeName}: {routeName: string}) {
|
||||
<NavItem
|
||||
label={l`Chat`}
|
||||
href="/messages"
|
||||
navItem="chat"
|
||||
minimal={leftNavMinimal}
|
||||
count={
|
||||
aa.flags.chatDisabled ? undefined : numUnreadMessages.numUnread
|
||||
@@ -693,6 +710,7 @@ export function DesktopLeftNav({routeName}: {routeName: string}) {
|
||||
<NavItem
|
||||
label={l`Feeds`}
|
||||
href="/feeds"
|
||||
navItem="feeds"
|
||||
minimal={leftNavMinimal}
|
||||
icons={{
|
||||
inactive: HashtagIcon,
|
||||
@@ -702,6 +720,7 @@ export function DesktopLeftNav({routeName}: {routeName: string}) {
|
||||
<NavItem
|
||||
label={l`Lists`}
|
||||
href="/lists"
|
||||
navItem="lists"
|
||||
minimal={leftNavMinimal}
|
||||
icons={{
|
||||
inactive: ListIcon,
|
||||
@@ -714,6 +733,7 @@ export function DesktopLeftNav({routeName}: {routeName: string}) {
|
||||
context: 'link to bookmarks screen',
|
||||
})}
|
||||
href="/saved"
|
||||
navItem="saved"
|
||||
minimal={leftNavMinimal}
|
||||
icons={{
|
||||
inactive: BookmarkIcon,
|
||||
@@ -723,6 +743,7 @@ export function DesktopLeftNav({routeName}: {routeName: string}) {
|
||||
<NavItem
|
||||
label={l`Profile`}
|
||||
href={makeProfileLink(currentAccount!)}
|
||||
navItem="profile"
|
||||
minimal={leftNavMinimal}
|
||||
icons={{
|
||||
inactive: UserCircleIcon,
|
||||
@@ -732,6 +753,7 @@ export function DesktopLeftNav({routeName}: {routeName: string}) {
|
||||
<NavItem
|
||||
label={l`Settings`}
|
||||
href="/settings"
|
||||
navItem="settings"
|
||||
minimal={leftNavMinimal}
|
||||
icons={{
|
||||
inactive: SettingsIcon,
|
||||
|
||||
Reference in New Issue
Block a user