import React, {type ComponentProps, type JSX} 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 {useActorStatus} from '#/lib/actor-status'
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 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 {type 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 {Bookmark, BookmarkFilled} from '#/components/icons/Bookmark'
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'
import {useSimpleVerificationState} from '#/components/verification'
import {VerificationCheck} from '#/components/verification/VerificationCheck'
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})
const verification = useSimpleVerificationState({profile})
const {isActive: live} = useActorStatus(profile)
return (
{profile?.displayName || account.handle}
{verification.showBadge && (
)}
{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,
isAtBookmarks,
isAtNotifications,
isAtMyProfile,
isAtMessages,
} = useNavigationTabState()
const {hasSession, currentAccount} = useSession()
// events
// =
const onPressTab = React.useCallback(
(tab: 'Home' | 'Search' | 'Messages' | 'Notifications' | 'MyProfile') => {
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-expect-error struggles with string unions, apparently
navigation.navigate(tab)
}
} else {
const tabState = getTabState(state, tab)
if (tabState === TabState.InsideAtRoot) {
emitSoftReset()
} else if (tabState === TabState.Inside) {
// find the correct navigator in which to pop-to-top
const target = state.routes.find(route => route.name === `${tab}Tab`)
?.state?.key
if (target) {
// if we found it, trigger pop-to-top
navigation.dispatch({
...StackActions.popToTop(),
target,
})
} else {
// fallback: reset navigation
navigation.reset({
index: 0,
routes: [{name: `${tab}Tab`}],
})
}
} else {
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 onPressBookmarks = React.useCallback(() => {
navigation.navigate('Bookmarks')
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`Explore`)}
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 BookmarksMenuItem = ({
isActive,
onPress,
}: {
isActive: boolean
onPress: () => void
}): React.ReactNode => {
const {_} = useLingui()
const t = useTheme()
return (
) : (
)
}
label={_(msg({message: 'Saved', context: 'link to bookmarks screen'}))}
onPress={onPress}
/>
)
}
BookmarksMenuItem = React.memo(BookmarksMenuItem)
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
)}
)
}