diff options
author | Ansh <anshnanda10@gmail.com> | 2023-04-18 09:19:37 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-04-18 11:19:37 -0500 |
commit | 10621e86e4379ff05b2262a659b8512d80203a4b (patch) | |
tree | fdc91b7db00526f945d9463b732785da6cceb5c7 /src | |
parent | 2509290fdd2b20c76c302d4962216f5d2d2b5a73 (diff) | |
download | voidsky-10621e86e4379ff05b2262a659b8512d80203a4b.tar.zst |
APP-70 give profile its own tab mobile (#469)
* add prebuild command to package.json * add ProfileTab navigator and screen * add prop to remove back button from profile * fix MyProfileTabNavigatorParams type * fix dep array for rendering ProfileHeader * just added ts-ignore * enable opening drawer in profile tab * clean up useNavigationTabState * clean up code * fix hideBackButton code flow
Diffstat (limited to 'src')
-rw-r--r-- | src/Navigation.tsx | 33 | ||||
-rw-r--r-- | src/lib/hooks/useNavigationTabState.ts | 15 | ||||
-rw-r--r-- | src/lib/hooks/useNavigationTabState.web.ts | 1 | ||||
-rw-r--r-- | src/lib/routes/helpers.ts | 3 | ||||
-rw-r--r-- | src/lib/routes/types.ts | 14 | ||||
-rw-r--r-- | src/view/com/profile/ProfileHeader.tsx | 24 | ||||
-rw-r--r-- | src/view/screens/Profile.tsx | 10 | ||||
-rw-r--r-- | src/view/shell/Drawer.tsx | 32 | ||||
-rw-r--r-- | src/view/shell/bottom-bar/BottomBar.tsx | 46 |
9 files changed, 127 insertions, 51 deletions
diff --git a/src/Navigation.tsx b/src/Navigation.tsx index 0de31a4ad..e868dd3b0 100644 --- a/src/Navigation.tsx +++ b/src/Navigation.tsx @@ -13,6 +13,8 @@ import { NotificationsTabNavigatorParams, FlatNavigatorParams, AllNavigatorParams, + MyProfileTabNavigatorParams, + BottomTabNavigatorParams, } from 'lib/routes/types' import {BottomBar} from './view/shell/bottom-bar/BottomBar' import {buildStateObject} from 'lib/routes/helpers' @@ -41,6 +43,7 @@ import {TermsOfServiceScreen} from './view/screens/TermsOfService' import {CommunityGuidelinesScreen} from './view/screens/CommunityGuidelines' import {CopyrightPolicyScreen} from './view/screens/CopyrightPolicy' import {usePalette} from 'lib/hooks/usePalette' +import {useStores} from './state' const navigationRef = createNavigationContainerRef<AllNavigatorParams>() @@ -48,8 +51,9 @@ const HomeTab = createNativeStackNavigator<HomeTabNavigatorParams>() const SearchTab = createNativeStackNavigator<SearchTabNavigatorParams>() const NotificationsTab = createNativeStackNavigator<NotificationsTabNavigatorParams>() +const MyProfileTab = createNativeStackNavigator<MyProfileTabNavigatorParams>() const Flat = createNativeStackNavigator<FlatNavigatorParams>() -const Tab = createBottomTabNavigator() +const Tab = createBottomTabNavigator<BottomTabNavigatorParams>() /** * These "common screens" are reused across stacks. @@ -100,6 +104,7 @@ function TabsNavigator() { component={NotificationsTabNavigator} /> <Tab.Screen name="SearchTab" component={SearchTabNavigator} /> + <Tab.Screen name="MyProfileTab" component={MyProfileTabNavigator} /> </Tab.Navigator> ) } @@ -158,6 +163,32 @@ function NotificationsTabNavigator() { ) } +function MyProfileTabNavigator() { + const contentStyle = useColorSchemeStyle(styles.bgLight, styles.bgDark) + const store = useStores() + return ( + <MyProfileTab.Navigator + screenOptions={{ + gestureEnabled: true, + fullScreenGestureEnabled: true, + headerShown: false, + animationDuration: 250, + contentStyle, + }}> + <MyProfileTab.Screen + name="MyProfile" + // @ts-ignore // TODO: fix this broken type in ProfileScreen + component={ProfileScreen} + initialParams={{ + name: store.me.handle, + hideBackButton: true, + }} + /> + {commonScreens(MyProfileTab as typeof HomeTab)} + </MyProfileTab.Navigator> + ) +} + /** * The FlatNavigator is used by Web to represent the routes * in a single ("flat") stack. diff --git a/src/lib/hooks/useNavigationTabState.ts b/src/lib/hooks/useNavigationTabState.ts index 8afc799eb..fb3662152 100644 --- a/src/lib/hooks/useNavigationTabState.ts +++ b/src/lib/hooks/useNavigationTabState.ts @@ -3,11 +3,24 @@ import {getTabState, TabState} from 'lib/routes/helpers' export function useNavigationTabState() { return useNavigationState(state => { - return { + const res = { isAtHome: getTabState(state, 'Home') !== TabState.Outside, isAtSearch: getTabState(state, 'Search') !== TabState.Outside, isAtNotifications: getTabState(state, 'Notifications') !== TabState.Outside, + isAtMyProfile: getTabState(state, 'MyProfile') !== TabState.Outside, } + if ( + !res.isAtHome && + !res.isAtNotifications && + !res.isAtSearch && + !res.isAtMyProfile + ) { + // HACK for some reason useNavigationState will give us pre-hydration results + // and not update after, so we force isAtHome if all came back false + // -prf + res.isAtHome = true + } + return res }) } diff --git a/src/lib/hooks/useNavigationTabState.web.ts b/src/lib/hooks/useNavigationTabState.web.ts index d0173aa0f..704424781 100644 --- a/src/lib/hooks/useNavigationTabState.web.ts +++ b/src/lib/hooks/useNavigationTabState.web.ts @@ -8,6 +8,7 @@ export function useNavigationTabState() { isAtHome: currentRoute === 'Home', isAtSearch: currentRoute === 'Search', isAtNotifications: currentRoute === 'Notifications', + isAtMyProfile: currentRoute === 'MyProfile', } }) } diff --git a/src/lib/routes/helpers.ts b/src/lib/routes/helpers.ts index be76b9669..cfa6ae53b 100644 --- a/src/lib/routes/helpers.ts +++ b/src/lib/routes/helpers.ts @@ -20,7 +20,8 @@ export function isStateAtTabRoot(state: State | undefined) { return ( isTab(currentRoute.name, 'Home') || isTab(currentRoute.name, 'Search') || - isTab(currentRoute.name, 'Notifications') + isTab(currentRoute.name, 'Notifications') || + isTab(currentRoute.name, 'MyProfile') ) } diff --git a/src/lib/routes/types.ts b/src/lib/routes/types.ts index 48edcc956..f8698f1cc 100644 --- a/src/lib/routes/types.ts +++ b/src/lib/routes/types.ts @@ -6,7 +6,7 @@ export type {NativeStackScreenProps} from '@react-navigation/native-stack' export type CommonNavigatorParams = { NotFound: undefined Settings: undefined - Profile: {name: string} + Profile: {name: string; hideBackButton?: boolean} ProfileFollowers: {name: string} ProfileFollows: {name: string} PostThread: {name: string; rkey: string} @@ -21,6 +21,13 @@ export type CommonNavigatorParams = { CopyrightPolicy: undefined } +export type BottomTabNavigatorParams = CommonNavigatorParams & { + HomeTab: undefined + SearchTab: undefined + NotificationsTab: undefined + MyProfileTab: undefined +} + export type HomeTabNavigatorParams = CommonNavigatorParams & { Home: undefined } @@ -33,6 +40,10 @@ export type NotificationsTabNavigatorParams = CommonNavigatorParams & { Notifications: undefined } +export type MyProfileTabNavigatorParams = CommonNavigatorParams & { + MyProfile: undefined +} + export type FlatNavigatorParams = CommonNavigatorParams & { Home: undefined Search: {q?: string} @@ -46,6 +57,7 @@ export type AllNavigatorParams = CommonNavigatorParams & { Search: {q?: string} NotificationsTab: undefined Notifications: undefined + MyProfileTab: undefined } // NOTE diff --git a/src/view/com/profile/ProfileHeader.tsx b/src/view/com/profile/ProfileHeader.tsx index d520a712f..101b6b833 100644 --- a/src/view/com/profile/ProfileHeader.tsx +++ b/src/view/com/profile/ProfileHeader.tsx @@ -36,8 +36,14 @@ import {FollowState} from 'state/models/cache/my-follows' const BACK_HITSLOP = {left: 30, top: 30, right: 30, bottom: 30} +interface Props { + view: ProfileModel + onRefreshAll: () => void + hideBackButton?: boolean +} + export const ProfileHeader = observer( - ({view, onRefreshAll}: {view: ProfileModel; onRefreshAll: () => void}) => { + ({view, onRefreshAll, hideBackButton = false}: Props) => { const pal = usePalette('default') // loading @@ -80,17 +86,21 @@ export const ProfileHeader = observer( // loaded // = - return <ProfileHeaderLoaded view={view} onRefreshAll={onRefreshAll} /> + return ( + <ProfileHeaderLoaded + view={view} + onRefreshAll={onRefreshAll} + hideBackButton={hideBackButton} + /> + ) }, ) const ProfileHeaderLoaded = observer(function ProfileHeaderLoaded({ view, onRefreshAll, -}: { - view: ProfileModel - onRefreshAll: () => void -}) { + hideBackButton = false, +}: Props) { const pal = usePalette('default') const store = useStores() const navigation = useNavigation<NavigationProp>() @@ -336,7 +346,7 @@ const ProfileHeaderLoaded = observer(function ProfileHeaderLoaded({ </View> ) : undefined} </View> - {!isDesktopWeb && ( + {!isDesktopWeb && !hideBackButton && ( <TouchableWithoutFeedback onPress={onPressBack} hitSlop={BACK_HITSLOP}> <View style={styles.backBtnWrapper}> <BlurView style={styles.backBtn} blurType="dark"> diff --git a/src/view/screens/Profile.tsx b/src/view/screens/Profile.tsx index dfee6f12a..3b4c47ce1 100644 --- a/src/view/screens/Profile.tsx +++ b/src/view/screens/Profile.tsx @@ -96,8 +96,14 @@ export const ProfileScreen = withAuthRequired( if (!uiState) { return <View /> } - return <ProfileHeader view={uiState.profile} onRefreshAll={onRefresh} /> - }, [uiState, onRefresh]) + return ( + <ProfileHeader + view={uiState.profile} + onRefreshAll={onRefresh} + hideBackButton={route.params.hideBackButton} + /> + ) + }, [uiState, onRefresh, route.params.hideBackButton]) const Footer = React.useMemo(() => { return uiState.showLoadingMoreFooter ? LoadingMoreFooter : undefined }, [uiState.showLoadingMoreFooter]) diff --git a/src/view/shell/Drawer.tsx b/src/view/shell/Drawer.tsx index de36463e1..74e10d6a1 100644 --- a/src/view/shell/Drawer.tsx +++ b/src/view/shell/Drawer.tsx @@ -27,6 +27,7 @@ import { MagnifyingGlassIcon2, MagnifyingGlassIcon2Solid, MoonIcon, + UserIconSolid, } from 'lib/icons' import {UserAvatar} from 'view/com/util/UserAvatar' import {Text} from 'view/com/util/text/Text' @@ -45,7 +46,8 @@ export const DrawerContent = observer(() => { const store = useStores() const navigation = useNavigation<NavigationProp>() const {track} = useAnalytics() - const {isAtHome, isAtSearch, isAtNotifications} = useNavigationTabState() + const {isAtHome, isAtSearch, isAtNotifications, isAtMyProfile} = + useNavigationTabState() // events // = @@ -56,7 +58,7 @@ export const DrawerContent = observer(() => { const state = navigation.getState() store.shell.closeDrawer() if (isWeb) { - // @ts-ignore must be Home, Search, or Notifications + // @ts-ignore must be Home, Search, Notifications, or MyProfile navigation.navigate(tab) } else { const tabState = getTabState(state, tab) @@ -65,7 +67,7 @@ export const DrawerContent = observer(() => { } else if (tabState === TabState.Inside) { navigation.dispatch(StackActions.popToTop()) } else { - // @ts-ignore must be Home, Search, or Notifications + // @ts-ignore must be Home, Search, Notifications, or MyProfile navigation.navigate(`${tab}Tab`) } } @@ -86,10 +88,8 @@ export const DrawerContent = observer(() => { ) const onPressProfile = React.useCallback(() => { - track('Menu:ItemClicked', {url: 'Profile'}) - navigation.navigate('Profile', {name: store.me.handle}) - store.shell.closeDrawer() - }, [navigation, track, store.me.handle, store.shell]) + onPressTab('MyProfile') + }, [onPressTab]) const onPressSettings = React.useCallback(() => { track('Menu:ItemClicked', {url: 'Settings'}) @@ -211,11 +211,19 @@ export const DrawerContent = observer(() => { /> <MenuItem icon={ - <UserIcon - style={pal.text as StyleProp<ViewStyle>} - size="26" - strokeWidth={1.5} - /> + isAtMyProfile ? ( + <UserIconSolid + style={pal.text as StyleProp<ViewStyle>} + size="26" + strokeWidth={1.5} + /> + ) : ( + <UserIcon + style={pal.text as StyleProp<ViewStyle>} + size="26" + strokeWidth={1.5} + /> + ) } label="Profile" onPress={onPressProfile} diff --git a/src/view/shell/bottom-bar/BottomBar.tsx b/src/view/shell/bottom-bar/BottomBar.tsx index 59b21968d..4dcaf3eb1 100644 --- a/src/view/shell/bottom-bar/BottomBar.tsx +++ b/src/view/shell/bottom-bar/BottomBar.tsx @@ -5,7 +5,7 @@ import { TouchableOpacity, View, } from 'react-native' -import {StackActions, useNavigationState} from '@react-navigation/native' +import {StackActions} from '@react-navigation/native' import {BottomTabBarProps} from '@react-navigation/bottom-tabs' import {useSafeAreaInsets} from 'react-native-safe-area-context' import {observer} from 'mobx-react-lite' @@ -21,34 +21,21 @@ import { BellIcon, BellIconSolid, UserIcon, + UserIconSolid, } from 'lib/icons' import {usePalette} from 'lib/hooks/usePalette' import {getTabState, TabState} from 'lib/routes/helpers' import {styles} from './BottomBarStyles' import {useMinimalShellMode} from 'lib/hooks/useMinimalShellMode' +import {useNavigationTabState} from 'lib/hooks/useNavigationTabState' export const BottomBar = observer(({navigation}: BottomTabBarProps) => { const store = useStores() const pal = usePalette('default') const safeAreaInsets = useSafeAreaInsets() const {track} = useAnalytics() - const {isAtHome, isAtSearch, isAtNotifications} = useNavigationState( - state => { - const res = { - isAtHome: getTabState(state, 'Home') !== TabState.Outside, - isAtSearch: getTabState(state, 'Search') !== TabState.Outside, - isAtNotifications: - getTabState(state, 'Notifications') !== TabState.Outside, - } - if (!res.isAtHome && !res.isAtNotifications && !res.isAtSearch) { - // HACK for some reason useNavigationState will give us pre-hydration results - // and not update after, so we force isAtHome if all came back false - // -prf - res.isAtHome = true - } - return res - }, - ) + const {isAtHome, isAtSearch, isAtNotifications, isAtMyProfile} = + useNavigationTabState() const {footerMinimalShellTransform} = useMinimalShellMode() @@ -77,9 +64,8 @@ export const BottomBar = observer(({navigation}: BottomTabBarProps) => { [onPressTab], ) const onPressProfile = React.useCallback(() => { - track('MobileShell:ProfileButtonPressed') - navigation.navigate('Profile', {name: store.me.handle}) - }, [navigation, track, store.me.handle]) + onPressTab('MyProfile') + }, [onPressTab]) return ( <Animated.View @@ -154,11 +140,19 @@ export const BottomBar = observer(({navigation}: BottomTabBarProps) => { testID="bottomBarProfileBtn" icon={ <View style={styles.ctrlIconSizingWrapper}> - <UserIcon - size={28} - strokeWidth={1.5} - style={[styles.ctrlIcon, pal.text, styles.profileIcon]} - /> + {isAtMyProfile ? ( + <UserIconSolid + size={28} + strokeWidth={1.5} + style={[styles.ctrlIcon, pal.text, styles.profileIcon]} + /> + ) : ( + <UserIcon + size={28} + strokeWidth={1.5} + style={[styles.ctrlIcon, pal.text, styles.profileIcon]} + /> + )} </View> } onPress={onPressProfile} |