import React, {ComponentProps} from 'react' import { Linking, SafeAreaView, ScrollView, StyleProp, StyleSheet, TouchableOpacity, View, ViewStyle, } from 'react-native' import {useNavigation, StackActions} from '@react-navigation/native' import { FontAwesomeIcon, FontAwesomeIconStyle, } from '@fortawesome/react-native-fontawesome' import {s, colors} from 'lib/styles' import {FEEDBACK_FORM_URL, HELP_DESK_URL} from 'lib/constants' import { HomeIcon, HomeIconSolid, BellIcon, BellIconSolid, UserIcon, CogIcon, MagnifyingGlassIcon2, MagnifyingGlassIcon2Solid, UserIconSolid, HashtagIcon, ListIcon, HandIcon, } from 'lib/icons' import {UserAvatar} from 'view/com/util/UserAvatar' import {Text} from 'view/com/util/text/Text' import {useTheme} from 'lib/ThemeContext' import {usePalette} from 'lib/hooks/usePalette' import {useAnalytics} from 'lib/analytics/analytics' import {pluralize} from 'lib/strings/helpers' import {getTabState, TabState} from 'lib/routes/helpers' import {NavigationProp} from 'lib/routes/types' import {useNavigationTabState} from 'lib/hooks/useNavigationTabState' import {isWeb} from 'platform/detection' import {formatCountShortOnly} from 'view/com/util/numeric/format' import {Trans, msg} from '@lingui/macro' import {useLingui} from '@lingui/react' import {useSetDrawerOpen} from '#/state/shell' import {useSession, SessionAccount} from '#/state/session' import {useProfileQuery} from '#/state/queries/profile' import {useUnreadNotifications} from '#/state/queries/notifications/unread' import {emitSoftReset} from '#/state/events' import {NavSignupCard} from '#/view/shell/NavSignupCard' import {TextLink} from '../com/util/Link' import {useTheme as useAlfTheme} from '#/alf' let DrawerProfileCard = ({ account, onPressProfile, }: { account: SessionAccount onPressProfile: () => void }): React.ReactNode => { const {_} = useLingui() const pal = usePalette('default') const {data: profile} = useProfileQuery({did: account.did}) return ( {profile?.displayName || account.handle} @{account.handle} {formatCountShortOnly(profile?.followersCount ?? 0)} {' '} {pluralize(profile?.followersCount || 0, 'follower')} ·{' '} {formatCountShortOnly(profile?.followsCount ?? 0)} {' '} following ) } DrawerProfileCard = React.memo(DrawerProfileCard) export {DrawerProfileCard} let DrawerContent = ({}: {}): React.ReactNode => { const theme = useTheme() const t = useAlfTheme() const pal = usePalette('default') const {_} = useLingui() const setDrawerOpen = useSetDrawerOpen() const navigation = useNavigation() const {track} = useAnalytics() const {isAtHome, isAtSearch, isAtFeeds, isAtNotifications, isAtMyProfile} = useNavigationTabState() const {hasSession, currentAccount} = useSession() // events // = const onPressTab = React.useCallback( (tab: string) => { track('Menu:ItemClicked', {url: tab}) 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`) } } }, [track, navigation, setDrawerOpen, currentAccount], ) const onPressHome = React.useCallback(() => onPressTab('Home'), [onPressTab]) const onPressSearch = React.useCallback( () => onPressTab('Search'), [onPressTab], ) const onPressNotifications = React.useCallback( () => onPressTab('Notifications'), [onPressTab], ) const onPressProfile = React.useCallback(() => { onPressTab('MyProfile') }, [onPressTab]) const onPressMyFeeds = React.useCallback( () => onPressTab('Feeds'), [onPressTab], ) const onPressLists = React.useCallback(() => { track('Menu:ItemClicked', {url: 'Lists'}) navigation.navigate('Lists') setDrawerOpen(false) }, [navigation, track, setDrawerOpen]) const onPressModeration = React.useCallback(() => { track('Menu:ItemClicked', {url: 'Moderation'}) navigation.navigate('Moderation') setDrawerOpen(false) }, [navigation, track, setDrawerOpen]) const onPressSettings = React.useCallback(() => { track('Menu:ItemClicked', {url: 'Settings'}) navigation.navigate('Settings') setDrawerOpen(false) }, [navigation, track, setDrawerOpen]) const onPressFeedback = React.useCallback(() => { track('Menu:FeedbackClicked') Linking.openURL( FEEDBACK_FORM_URL({ email: currentAccount?.email, handle: currentAccount?.handle, }), ) }, [track, currentAccount]) const onPressHelp = React.useCallback(() => { track('Menu:HelpClicked') Linking.openURL(HELP_DESK_URL) }, [track]) // rendering // = return ( {hasSession && currentAccount ? ( ) : ( )} {hasSession ? ( <> ) : ( )} ) } DrawerContent = React.memo(DrawerContent) export {DrawerContent} let DrawerFooter = ({ onPressFeedback, onPressHelp, }: { onPressFeedback: () => void onPressHelp: () => void }): React.ReactNode => { const theme = useTheme() const pal = usePalette('default') const {_} = useLingui() return ( Feedback Help ) } 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 pal = usePalette('default') return ( } size={24} strokeWidth={1.7} /> ) : ( } size={24} strokeWidth={1.7} /> ) } label={_(msg`Search`)} accessibilityLabel={_(msg`Search`)} accessibilityHint="" bold={isActive} onPress={onPress} /> ) } SearchMenuItem = React.memo(SearchMenuItem) let HomeMenuItem = ({ isActive, onPress, }: { isActive: boolean onPress: () => void }): React.ReactNode => { const {_} = useLingui() const pal = usePalette('default') return ( } size="24" strokeWidth={3.25} /> ) : ( } size="24" strokeWidth={3.25} /> ) } label={_(msg`Home`)} accessibilityLabel={_(msg`Home`)} accessibilityHint="" bold={isActive} onPress={onPress} /> ) } HomeMenuItem = React.memo(HomeMenuItem) let NotificationsMenuItem = ({ isActive, onPress, }: { isActive: boolean onPress: () => void }): React.ReactNode => { const {_} = useLingui() const pal = usePalette('default') const numUnreadNotifications = useUnreadNotifications() return ( } size="24" strokeWidth={1.7} /> ) : ( } size="24" strokeWidth={1.7} /> ) } label={_(msg`Notifications`)} accessibilityLabel={_(msg`Notifications`)} accessibilityHint={ numUnreadNotifications === '' ? '' : _(msg`${numUnreadNotifications} unread`) } count={numUnreadNotifications} bold={isActive} onPress={onPress} /> ) } NotificationsMenuItem = React.memo(NotificationsMenuItem) let FeedsMenuItem = ({ isActive, onPress, }: { isActive: boolean onPress: () => void }): React.ReactNode => { const {_} = useLingui() const pal = usePalette('default') return ( ) : ( ) } label={_(msg`Feeds`)} accessibilityLabel={_(msg`Feeds`)} accessibilityHint="" bold={isActive} onPress={onPress} /> ) } FeedsMenuItem = React.memo(FeedsMenuItem) let ListsMenuItem = ({onPress}: {onPress: () => void}): React.ReactNode => { const {_} = useLingui() const pal = usePalette('default') return ( } label={_(msg`Lists`)} accessibilityLabel={_(msg`Lists`)} accessibilityHint="" onPress={onPress} /> ) } ListsMenuItem = React.memo(ListsMenuItem) let ModerationMenuItem = ({ onPress, }: { onPress: () => void }): React.ReactNode => { const {_} = useLingui() const pal = usePalette('default') return ( } label={_(msg`Moderation`)} accessibilityLabel={_(msg`Moderation`)} accessibilityHint="" onPress={onPress} /> ) } ModerationMenuItem = React.memo(ModerationMenuItem) let ProfileMenuItem = ({ isActive, onPress, }: { isActive: boolean onPress: () => void }): React.ReactNode => { const {_} = useLingui() const pal = usePalette('default') return ( } size="26" strokeWidth={1.5} /> ) : ( } size="26" strokeWidth={1.5} /> ) } label={_(msg`Profile`)} accessibilityLabel={_(msg`Profile`)} accessibilityHint="" onPress={onPress} /> ) } ProfileMenuItem = React.memo(ProfileMenuItem) let SettingsMenuItem = ({onPress}: {onPress: () => void}): React.ReactNode => { const {_} = useLingui() const pal = usePalette('default') return ( } size="26" strokeWidth={1.75} /> } label={_(msg`Settings`)} accessibilityLabel={_(msg`Settings`)} accessibilityHint="" onPress={onPress} /> ) } SettingsMenuItem = React.memo(SettingsMenuItem) function MenuItem({ icon, label, accessibilityLabel, count, bold, onPress, }: MenuItemProps) { const pal = usePalette('default') return ( {icon} {count ? ( 2 ? styles.menuItemCountHundreds : count.length > 1 ? styles.menuItemCountTens : undefined, ]}> {count} ) : undefined} {label} ) } const styles = StyleSheet.create({ view: { flex: 1, paddingBottom: 50, maxWidth: 300, }, viewDarkMode: { backgroundColor: '#1B1919', }, main: { paddingLeft: 20, paddingTop: 20, }, smallSpacer: { height: 20, }, profileCardDisplayName: { marginTop: 20, paddingRight: 30, }, profileCardHandle: { marginTop: 4, paddingRight: 30, }, profileCardFollowers: { marginTop: 16, paddingRight: 10, }, menuItem: { flexDirection: 'row', alignItems: 'center', paddingVertical: 16, paddingRight: 10, }, menuItemIconWrapper: { width: 24, height: 24, alignItems: 'center', justifyContent: 'center', marginRight: 12, }, menuItemCount: { position: 'absolute', width: 'auto', right: -6, top: -4, backgroundColor: colors.blue3, paddingHorizontal: 4, paddingBottom: 1, borderRadius: 6, }, menuItemCountTens: { width: 25, }, menuItemCountHundreds: { right: -12, width: 34, }, menuItemCountLabel: { fontSize: 12, fontWeight: 'bold', fontVariant: ['tabular-nums'], color: colors.white, }, inviteCodes: { paddingLeft: 0, paddingVertical: 8, flexDirection: 'row', }, inviteCodesIcon: { marginRight: 6, flexShrink: 0, marginTop: 2, }, footer: { flexWrap: 'wrap', flexDirection: 'row', gap: 8, paddingRight: 20, paddingTop: 20, paddingLeft: 20, }, footerBtn: { flexDirection: 'row', alignItems: 'center', padding: 10, borderRadius: 25, }, footerBtnFeedback: { paddingHorizontal: 20, }, footerBtnFeedbackLight: { backgroundColor: '#DDEFFF', }, footerBtnFeedbackDark: { backgroundColor: colors.blue6, }, })