import React, {ComponentProps} from 'react' import {Linking, ScrollView, TouchableOpacity, View} from 'react-native' import {useSafeAreaInsets} from 'react-native-safe-area-context' import {msg, Plural, plural, Trans} from '@lingui/macro' import {useLingui} from '@lingui/react' import {StackActions, useNavigation} from '@react-navigation/native' import {FEEDBACK_FORM_URL, HELP_DESK_URL} from '#/lib/constants' import {PressableScale} from '#/lib/custom-animations/PressableScale' import {useNavigationTabState} from '#/lib/hooks/useNavigationTabState' import {getTabState, TabState} from '#/lib/routes/helpers' import {NavigationProp} from '#/lib/routes/types' import {sanitizeHandle} from '#/lib/strings/handles' import {colors} from '#/lib/styles' import {isWeb} from '#/platform/detection' import {emitSoftReset} from '#/state/events' import {useKawaiiMode} from '#/state/preferences/kawaii' import {useUnreadNotifications} from '#/state/queries/notifications/unread' import {useProfileQuery} from '#/state/queries/profile' import {SessionAccount, useSession} from '#/state/session' import {useSetDrawerOpen} from '#/state/shell' import {formatCount} from '#/view/com/util/numeric/format' import {UserAvatar} from '#/view/com/util/UserAvatar' import {NavSignupCard} from '#/view/shell/NavSignupCard' import {atoms as a, tokens, useTheme, web} from '#/alf' import {Button, ButtonIcon, ButtonText} from '#/components/Button' import {Divider} from '#/components/Divider' import { Bell_Filled_Corner0_Rounded as BellFilled, Bell_Stroke2_Corner0_Rounded as Bell, } from '#/components/icons/Bell' import {BulletList_Stroke2_Corner0_Rounded as List} from '#/components/icons/BulletList' import { Hashtag_Filled_Corner0_Rounded as HashtagFilled, Hashtag_Stroke2_Corner0_Rounded as Hashtag, } from '#/components/icons/Hashtag' import { HomeOpen_Filled_Corner0_Rounded as HomeFilled, HomeOpen_Stoke2_Corner0_Rounded as Home, } from '#/components/icons/HomeOpen' import {MagnifyingGlass_Filled_Stroke2_Corner0_Rounded as MagnifyingGlassFilled} from '#/components/icons/MagnifyingGlass' import {MagnifyingGlass2_Stroke2_Corner0_Rounded as MagnifyingGlass} from '#/components/icons/MagnifyingGlass2' import { Message_Stroke2_Corner0_Rounded as Message, Message_Stroke2_Corner0_Rounded_Filled as MessageFilled, } from '#/components/icons/Message' import {SettingsGear2_Stroke2_Corner0_Rounded as Settings} from '#/components/icons/SettingsGear2' import { UserCircle_Filled_Corner0_Rounded as UserCircleFilled, UserCircle_Stroke2_Corner0_Rounded as UserCircle, } from '#/components/icons/UserCircle' import {InlineLinkText} from '#/components/Link' import {Text} from '#/components/Typography' const iconWidth = 26 let DrawerProfileCard = ({ account, onPressProfile, }: { account: SessionAccount onPressProfile: () => void }): React.ReactNode => { const {_, i18n} = useLingui() const t = useTheme() const {data: profile} = useProfileQuery({did: account.did}) return ( {profile?.displayName || account.handle} {sanitizeHandle(account.handle, '@')} {formatCount(i18n, profile?.followersCount ?? 0)} {' '} {' '} ·{' '} {formatCount(i18n, profile?.followsCount ?? 0)} {' '} ) } DrawerProfileCard = React.memo(DrawerProfileCard) export {DrawerProfileCard} let DrawerContent = ({}: React.PropsWithoutRef<{}>): React.ReactNode => { const t = useTheme() const insets = useSafeAreaInsets() const setDrawerOpen = useSetDrawerOpen() const navigation = useNavigation() const { isAtHome, isAtSearch, isAtFeeds, isAtNotifications, isAtMyProfile, isAtMessages, } = useNavigationTabState() const {hasSession, currentAccount} = useSession() // events // = const onPressTab = React.useCallback( (tab: string) => { const state = navigation.getState() setDrawerOpen(false) if (isWeb) { // hack because we have flat navigator for web and MyProfile does not exist on the web navigator -ansh if (tab === 'MyProfile') { navigation.navigate('Profile', {name: currentAccount!.handle}) } else { // @ts-ignore must be Home, Search, Notifications, or MyProfile navigation.navigate(tab) } } else { const tabState = getTabState(state, tab) if (tabState === TabState.InsideAtRoot) { emitSoftReset() } else if (tabState === TabState.Inside) { navigation.dispatch(StackActions.popToTop()) } else { // @ts-ignore must be Home, Search, Notifications, or MyProfile navigation.navigate(`${tab}Tab`) } } }, [navigation, setDrawerOpen, currentAccount], ) const onPressHome = React.useCallback(() => onPressTab('Home'), [onPressTab]) const onPressSearch = React.useCallback( () => onPressTab('Search'), [onPressTab], ) const onPressMessages = React.useCallback( () => onPressTab('Messages'), [onPressTab], ) const onPressNotifications = React.useCallback( () => onPressTab('Notifications'), [onPressTab], ) const onPressProfile = React.useCallback(() => { onPressTab('MyProfile') }, [onPressTab]) const onPressMyFeeds = React.useCallback(() => { navigation.navigate('Feeds') setDrawerOpen(false) }, [navigation, setDrawerOpen]) const onPressLists = React.useCallback(() => { navigation.navigate('Lists') setDrawerOpen(false) }, [navigation, setDrawerOpen]) const onPressSettings = React.useCallback(() => { navigation.navigate('Settings') setDrawerOpen(false) }, [navigation, setDrawerOpen]) const onPressFeedback = React.useCallback(() => { Linking.openURL( FEEDBACK_FORM_URL({ email: currentAccount?.email, handle: currentAccount?.handle, }), ) }, [currentAccount]) const onPressHelp = React.useCallback(() => { Linking.openURL(HELP_DESK_URL) }, []) // rendering // = return ( {hasSession && currentAccount ? ( ) : ( )} {hasSession ? ( <> ) : ( <> )} ) } DrawerContent = React.memo(DrawerContent) export {DrawerContent} let DrawerFooter = ({ onPressFeedback, onPressHelp, }: { onPressFeedback: () => void onPressHelp: () => void }): React.ReactNode => { const {_} = useLingui() const insets = useSafeAreaInsets() return ( ) } DrawerFooter = React.memo(DrawerFooter) interface MenuItemProps extends ComponentProps { icon: JSX.Element label: string count?: string bold?: boolean } let SearchMenuItem = ({ isActive, onPress, }: { isActive: boolean onPress: () => void }): React.ReactNode => { const {_} = useLingui() const t = useTheme() return ( ) : ( ) } label={_(msg`Search`)} bold={isActive} onPress={onPress} /> ) } SearchMenuItem = React.memo(SearchMenuItem) let HomeMenuItem = ({ isActive, onPress, }: { isActive: boolean onPress: () => void }): React.ReactNode => { const {_} = useLingui() const t = useTheme() return ( ) : ( ) } label={_(msg`Home`)} bold={isActive} onPress={onPress} /> ) } HomeMenuItem = React.memo(HomeMenuItem) let ChatMenuItem = ({ isActive, onPress, }: { isActive: boolean onPress: () => void }): React.ReactNode => { const {_} = useLingui() const t = useTheme() return ( ) : ( ) } label={_(msg`Chat`)} bold={isActive} onPress={onPress} /> ) } ChatMenuItem = React.memo(ChatMenuItem) let NotificationsMenuItem = ({ isActive, onPress, }: { isActive: boolean onPress: () => void }): React.ReactNode => { const {_} = useLingui() const t = useTheme() const numUnreadNotifications = useUnreadNotifications() return ( ) : ( ) } label={_(msg`Notifications`)} accessibilityHint={ numUnreadNotifications === '' ? '' : _( msg`${plural(numUnreadNotifications ?? 0, { one: '# unread item', other: '# unread items', })}` || '', ) } count={numUnreadNotifications} bold={isActive} onPress={onPress} /> ) } NotificationsMenuItem = React.memo(NotificationsMenuItem) let FeedsMenuItem = ({ isActive, onPress, }: { isActive: boolean onPress: () => void }): React.ReactNode => { const {_} = useLingui() const t = useTheme() return ( ) : ( ) } label={_(msg`Feeds`)} bold={isActive} onPress={onPress} /> ) } FeedsMenuItem = React.memo(FeedsMenuItem) let ListsMenuItem = ({onPress}: {onPress: () => void}): React.ReactNode => { const {_} = useLingui() const t = useTheme() return ( } label={_(msg`Lists`)} onPress={onPress} /> ) } ListsMenuItem = React.memo(ListsMenuItem) let ProfileMenuItem = ({ isActive, onPress, }: { isActive: boolean onPress: () => void }): React.ReactNode => { const {_} = useLingui() const t = useTheme() return ( ) : ( ) } label={_(msg`Profile`)} onPress={onPress} /> ) } ProfileMenuItem = React.memo(ProfileMenuItem) let SettingsMenuItem = ({onPress}: {onPress: () => void}): React.ReactNode => { const {_} = useLingui() const t = useTheme() return ( } label={_(msg`Settings`)} onPress={onPress} /> ) } SettingsMenuItem = React.memo(SettingsMenuItem) function MenuItem({icon, label, count, bold, onPress}: MenuItemProps) { const t = useTheme() return ( ) } function ExtraLinks() { const {_} = useLingui() const t = useTheme() const kawaii = useKawaiiMode() return ( Terms of Service Privacy Policy {kawaii && ( Logo by{' '} @sawaratsuki.bsky.social )} ) }