diff options
Diffstat (limited to 'src/view/shell/desktop/LeftNav.tsx')
-rw-r--r-- | src/view/shell/desktop/LeftNav.tsx | 295 |
1 files changed, 169 insertions, 126 deletions
diff --git a/src/view/shell/desktop/LeftNav.tsx b/src/view/shell/desktop/LeftNav.tsx index 39271605c..2ed294501 100644 --- a/src/view/shell/desktop/LeftNav.tsx +++ b/src/view/shell/desktop/LeftNav.tsx @@ -1,5 +1,4 @@ import React from 'react' -import {observer} from 'mobx-react-lite' import {StyleSheet, TouchableOpacity, View} from 'react-native' import {PressableWithHover} from 'view/com/util/PressableWithHover' import { @@ -16,7 +15,6 @@ import {UserAvatar} from 'view/com/util/UserAvatar' import {Link} from 'view/com/util/Link' import {LoadingPlaceholder} from 'view/com/util/LoadingPlaceholder' import {usePalette} from 'lib/hooks/usePalette' -import {useStores} from 'state/index' import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries' import {s, colors} from 'lib/styles' import { @@ -39,18 +37,36 @@ import {getCurrentRoute, isTab, isStateAtTabRoot} from 'lib/routes/helpers' import {NavigationProp, CommonNavigatorParams} from 'lib/routes/types' import {router} from '../../../routes' import {makeProfileLink} from 'lib/routes/links' +import {useLingui} from '@lingui/react' +import {Trans, msg} from '@lingui/macro' +import {useProfileQuery} from '#/state/queries/profile' +import {useSession} from '#/state/session' +import {useUnreadNotifications} from '#/state/queries/notifications/unread' +import {useComposerControls} from '#/state/shell/composer' +import {useFetchHandle} from '#/state/queries/handle' +import {emitSoftReset} from '#/state/events' +import {useQueryClient} from '@tanstack/react-query' +import {RQKEY as NOTIFS_RQKEY} from '#/state/queries/notifications/feed' +import {NavSignupCard} from '#/view/shell/NavSignupCard' +import {truncateAndInvalidate} from '#/state/queries/util' -const ProfileCard = observer(function ProfileCardImpl() { - const store = useStores() +function ProfileCard() { + const {currentAccount} = useSession() + const {isLoading, data: profile} = useProfileQuery({did: currentAccount!.did}) const {isDesktop} = useWebMediaQueries() + const {_} = useLingui() const size = 48 - return store.me.handle ? ( + + return !isLoading && profile ? ( <Link - href={makeProfileLink(store.me)} + href={makeProfileLink({ + did: currentAccount!.did, + handle: currentAccount!.handle, + })} style={[styles.profileCard, !isDesktop && styles.profileCardTablet]} - title="My Profile" + title={_(msg`My Profile`)} asAnchor> - <UserAvatar avatar={store.me.avatar} size={size} /> + <UserAvatar avatar={profile.avatar} size={size} /> </Link> ) : ( <View style={[styles.profileCard, !isDesktop && styles.profileCardTablet]}> @@ -61,12 +77,13 @@ const ProfileCard = observer(function ProfileCardImpl() { /> </View> ) -}) +} function BackBtn() { const {isTablet} = useWebMediaQueries() const pal = usePalette('default') const navigation = useNavigation<NavigationProp>() + const {_} = useLingui() const shouldShow = useNavigationState(state => !isStateAtTabRoot(state)) const onPressBack = React.useCallback(() => { @@ -86,7 +103,7 @@ function BackBtn() { onPress={onPressBack} style={styles.backBtn} accessibilityRole="button" - accessibilityLabel="Go back" + accessibilityLabel={_(msg`Go back`)} accessibilityHint=""> <FontAwesomeIcon size={24} @@ -104,15 +121,10 @@ interface NavItemProps { iconFilled: JSX.Element label: string } -const NavItem = observer(function NavItemImpl({ - count, - href, - icon, - iconFilled, - label, -}: NavItemProps) { +function NavItem({count, href, icon, iconFilled, label}: NavItemProps) { const pal = usePalette('default') - const store = useStores() + const queryClient = useQueryClient() + const {currentAccount} = useSession() const {isDesktop, isTablet} = useWebMediaQueries() const [pathName] = React.useMemo(() => router.matchPath(href), [href]) const currentRouteInfo = useNavigationState(state => { @@ -125,7 +137,7 @@ const NavItem = observer(function NavItemImpl({ currentRouteInfo.name === 'Profile' ? isTab(currentRouteInfo.name, pathName) && (currentRouteInfo.params as CommonNavigatorParams['Profile']).name === - store.me.handle + currentAccount?.handle : isTab(currentRouteInfo.name, pathName) const {onPress} = useLinkProps({to: href}) const onPressWrapped = React.useCallback( @@ -135,12 +147,16 @@ const NavItem = observer(function NavItemImpl({ } e.preventDefault() if (isCurrent) { - store.emitScreenSoftReset() + emitSoftReset() } else { + if (href === '/notifications') { + // fetch new notifs on view + truncateAndInvalidate(queryClient, NOTIFS_RQKEY()) + } onPress() } }, - [onPress, isCurrent, store], + [onPress, isCurrent, queryClient, href], ) return ( @@ -179,12 +195,16 @@ const NavItem = observer(function NavItemImpl({ )} </PressableWithHover> ) -}) +} function ComposeBtn() { - const store = useStores() + const {currentAccount} = useSession() const {getState} = useNavigation() + const {openComposer} = useComposerControls() + const {_} = useLingui() const {isTablet} = useWebMediaQueries() + const [isFetchingHandle, setIsFetchingHandle] = React.useState(false) + const fetchHandle = useFetchHandle() const getProfileHandle = async () => { const {routes} = getState() @@ -196,13 +216,21 @@ function ComposeBtn() { ).name if (handle.startsWith('did:')) { - const cached = await store.profiles.cache.get(handle) - const profile = cached ? cached.data : undefined - // if we can't resolve handle, set to undefined - handle = profile?.handle || undefined + try { + setIsFetchingHandle(true) + handle = await fetchHandle(handle) + } catch (e) { + handle = undefined + } finally { + setIsFetchingHandle(false) + } } - if (!handle || handle === store.me.handle || handle === 'handle.invalid') + if ( + !handle || + handle === currentAccount?.handle || + handle === 'handle.invalid' + ) return undefined return handle @@ -212,17 +240,18 @@ function ComposeBtn() { } const onPressCompose = async () => - store.shell.openComposer({mention: await getProfileHandle()}) + openComposer({mention: await getProfileHandle()}) if (isTablet) { return null } return ( <TouchableOpacity + disabled={isFetchingHandle} style={[styles.newPostBtn]} onPress={onPressCompose} accessibilityRole="button" - accessibilityLabel="New post" + accessibilityLabel={_(msg`New post`)} accessibilityHint=""> <View style={styles.newPostBtnIconWrapper}> <ComposeIcon2 @@ -232,16 +261,18 @@ function ComposeBtn() { /> </View> <Text type="button" style={styles.newPostBtnLabel}> - New Post + <Trans>New Post</Trans> </Text> </TouchableOpacity> ) } -export const DesktopLeftNav = observer(function DesktopLeftNav() { - const store = useStores() +export function DesktopLeftNav() { + const {hasSession, currentAccount} = useSession() const pal = usePalette('default') + const {_} = useLingui() const {isDesktop, isTablet} = useWebMediaQueries() + const numUnread = useUnreadNotifications() return ( <View @@ -251,8 +282,16 @@ export const DesktopLeftNav = observer(function DesktopLeftNav() { pal.view, pal.border, ]}> - {store.session.hasSession && <ProfileCard />} + {hasSession ? ( + <ProfileCard /> + ) : isDesktop ? ( + <View style={{paddingHorizontal: 12}}> + <NavSignupCard /> + </View> + ) : null} + <BackBtn /> + <NavItem href="/" icon={<HomeIcon size={isDesktop ? 24 : 28} style={pal.text} />} @@ -263,7 +302,7 @@ export const DesktopLeftNav = observer(function DesktopLeftNav() { style={pal.text} /> } - label="Home" + label={_(msg`Home`)} /> <NavItem href="/search" @@ -281,7 +320,7 @@ export const DesktopLeftNav = observer(function DesktopLeftNav() { style={pal.text} /> } - label="Search" + label={_(msg`Search`)} /> <NavItem href="/feeds" @@ -299,105 +338,109 @@ export const DesktopLeftNav = observer(function DesktopLeftNav() { size={isDesktop ? 24 : 28} /> } - label="Feeds" + label={_(msg`Feeds`)} /> - <NavItem - href="/notifications" - count={store.me.notifications.unreadCountLabel} - icon={ - <BellIcon - strokeWidth={2} - size={isDesktop ? 24 : 26} - style={pal.text} + + {hasSession && ( + <> + <NavItem + href="/notifications" + count={numUnread} + icon={ + <BellIcon + strokeWidth={2} + size={isDesktop ? 24 : 26} + style={pal.text} + /> + } + iconFilled={ + <BellIconSolid + strokeWidth={1.5} + size={isDesktop ? 24 : 26} + style={pal.text} + /> + } + label={_(msg`Notifications`)} /> - } - iconFilled={ - <BellIconSolid - strokeWidth={1.5} - size={isDesktop ? 24 : 26} - style={pal.text} + <NavItem + href="/lists" + icon={ + <ListIcon + style={pal.text} + size={isDesktop ? 26 : 30} + strokeWidth={2} + /> + } + iconFilled={ + <ListIcon + style={pal.text} + size={isDesktop ? 26 : 30} + strokeWidth={3} + /> + } + label={_(msg`Lists`)} /> - } - label="Notifications" - /> - <NavItem - href="/lists" - icon={ - <ListIcon - style={pal.text} - size={isDesktop ? 26 : 30} - strokeWidth={2} + <NavItem + href="/moderation" + icon={ + <HandIcon + style={pal.text} + size={isDesktop ? 24 : 27} + strokeWidth={5.5} + /> + } + iconFilled={ + <FontAwesomeIcon + icon="hand" + style={pal.text as FontAwesomeIconStyle} + size={isDesktop ? 20 : 26} + /> + } + label={_(msg`Moderation`)} /> - } - iconFilled={ - <ListIcon - style={pal.text} - size={isDesktop ? 26 : 30} - strokeWidth={3} + <NavItem + href={currentAccount ? makeProfileLink(currentAccount) : '/'} + icon={ + <UserIcon + strokeWidth={1.75} + size={isDesktop ? 28 : 30} + style={pal.text} + /> + } + iconFilled={ + <UserIconSolid + strokeWidth={1.75} + size={isDesktop ? 28 : 30} + style={pal.text} + /> + } + label="Profile" /> - } - label="Lists" - /> - <NavItem - href="/moderation" - icon={ - <HandIcon - style={pal.text} - size={isDesktop ? 24 : 27} - strokeWidth={5.5} + <NavItem + href="/settings" + icon={ + <CogIcon + strokeWidth={1.75} + size={isDesktop ? 28 : 32} + style={pal.text} + /> + } + iconFilled={ + <CogIconSolid + strokeWidth={1.5} + size={isDesktop ? 28 : 32} + style={pal.text} + /> + } + label={_(msg`Settings`)} /> - } - iconFilled={ - <FontAwesomeIcon - icon="hand" - style={pal.text as FontAwesomeIconStyle} - size={isDesktop ? 20 : 26} - /> - } - label="Moderation" - /> - {store.session.hasSession && ( - <NavItem - href={makeProfileLink(store.me)} - icon={ - <UserIcon - strokeWidth={1.75} - size={isDesktop ? 28 : 30} - style={pal.text} - /> - } - iconFilled={ - <UserIconSolid - strokeWidth={1.75} - size={isDesktop ? 28 : 30} - style={pal.text} - /> - } - label="Profile" - /> + + <ComposeBtn /> + </> )} - <NavItem - href="/settings" - icon={ - <CogIcon - strokeWidth={1.75} - size={isDesktop ? 28 : 32} - style={pal.text} - /> - } - iconFilled={ - <CogIconSolid - strokeWidth={1.5} - size={isDesktop ? 28 : 32} - style={pal.text} - /> - } - label="Settings" - /> - {store.session.hasSession && <ComposeBtn />} </View> ) -}) +} const styles = StyleSheet.create({ leftNav: { |