Refactor feed header components (#2964)
* Move home-related files to view/com/home * Add HomeHeader in front of FeedTabBar * Move isDekstop check outside FeedsTabBar * Remove PWI logic from tabbar * Separate platform-specific layout from shared logic
This commit is contained in:
		
							parent
							
								
									93b5eff4d7
								
							
						
					
					
						commit
						1ccb3be961
					
				
					 7 changed files with 142 additions and 211 deletions
				
			
		
							
								
								
									
										71
									
								
								src/view/com/home/HomeHeader.tsx
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										71
									
								
								src/view/com/home/HomeHeader.tsx
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,71 @@ | |||
| import React from 'react' | ||||
| import {RenderTabBarFnProps} from 'view/com/pager/Pager' | ||||
| import {HomeHeaderLayout} from './HomeHeaderLayout' | ||||
| import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries' | ||||
| import {usePinnedFeedsInfos} from '#/state/queries/feed' | ||||
| import {useNavigation} from '@react-navigation/native' | ||||
| import {NavigationProp} from 'lib/routes/types' | ||||
| import {isWeb} from 'platform/detection' | ||||
| import {TabBar} from '../pager/TabBar' | ||||
| import {usePalette} from '#/lib/hooks/usePalette' | ||||
| 
 | ||||
| export function HomeHeader( | ||||
|   props: RenderTabBarFnProps & {testID?: string; onPressSelected: () => void}, | ||||
| ) { | ||||
|   const {isDesktop} = useWebMediaQueries() | ||||
|   if (isDesktop) { | ||||
|     return null | ||||
|   } | ||||
|   return <HomeHeaderInner {...props} /> | ||||
| } | ||||
| 
 | ||||
| export function HomeHeaderInner( | ||||
|   props: RenderTabBarFnProps & {testID?: string; onPressSelected: () => void}, | ||||
| ) { | ||||
|   const navigation = useNavigation<NavigationProp>() | ||||
|   const {feeds, hasPinnedCustom} = usePinnedFeedsInfos() | ||||
|   const pal = usePalette('default') | ||||
| 
 | ||||
|   const items = React.useMemo(() => { | ||||
|     const pinnedNames = feeds.map(f => f.displayName) | ||||
| 
 | ||||
|     if (!hasPinnedCustom) { | ||||
|       return pinnedNames.concat('Feeds ✨') | ||||
|     } | ||||
|     return pinnedNames | ||||
|   }, [hasPinnedCustom, feeds]) | ||||
| 
 | ||||
|   const onPressFeedsLink = React.useCallback(() => { | ||||
|     if (isWeb) { | ||||
|       navigation.navigate('Feeds') | ||||
|     } else { | ||||
|       navigation.navigate('FeedsTab') | ||||
|       navigation.popToTop() | ||||
|     } | ||||
|   }, [navigation]) | ||||
| 
 | ||||
|   const onSelect = React.useCallback( | ||||
|     (index: number) => { | ||||
|       if (!hasPinnedCustom && index === items.length - 1) { | ||||
|         onPressFeedsLink() | ||||
|       } else if (props.onSelect) { | ||||
|         props.onSelect(index) | ||||
|       } | ||||
|     }, | ||||
|     [items.length, onPressFeedsLink, props, hasPinnedCustom], | ||||
|   ) | ||||
| 
 | ||||
|   return ( | ||||
|     <HomeHeaderLayout> | ||||
|       <TabBar | ||||
|         key={items.join(',')} | ||||
|         onPressSelected={props.onPressSelected} | ||||
|         selectedPage={props.selectedPage} | ||||
|         onSelect={onSelect} | ||||
|         testID={props.testID} | ||||
|         items={items} | ||||
|         indicatorColor={pal.colors.link} | ||||
|       /> | ||||
|     </HomeHeaderLayout> | ||||
|   ) | ||||
| } | ||||
							
								
								
									
										1
									
								
								src/view/com/home/HomeHeaderLayout.tsx
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								src/view/com/home/HomeHeaderLayout.tsx
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1 @@ | |||
| export {HomeHeaderLayoutMobile as HomeHeaderLayout} from './HomeHeaderLayoutMobile' | ||||
							
								
								
									
										50
									
								
								src/view/com/home/HomeHeaderLayout.web.tsx
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								src/view/com/home/HomeHeaderLayout.web.tsx
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,50 @@ | |||
| import React from 'react' | ||||
| import {StyleSheet} from 'react-native' | ||||
| import Animated from 'react-native-reanimated' | ||||
| import {usePalette} from 'lib/hooks/usePalette' | ||||
| import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries' | ||||
| import {HomeHeaderLayoutMobile} from './HomeHeaderLayoutMobile' | ||||
| import {useMinimalShellMode} from 'lib/hooks/useMinimalShellMode' | ||||
| import {useShellLayout} from '#/state/shell/shell-layout' | ||||
| 
 | ||||
| export function HomeHeaderLayout({children}: {children: React.ReactNode}) { | ||||
|   const {isMobile} = useWebMediaQueries() | ||||
|   if (isMobile) { | ||||
|     return <HomeHeaderLayoutMobile>{children}</HomeHeaderLayoutMobile> | ||||
|   } else { | ||||
|     return <HomeHeaderLayoutTablet>{children}</HomeHeaderLayoutTablet> | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| function HomeHeaderLayoutTablet({children}: {children: React.ReactNode}) { | ||||
|   const pal = usePalette('default') | ||||
|   const {headerMinimalShellTransform} = useMinimalShellMode() | ||||
|   const {headerHeight} = useShellLayout() | ||||
| 
 | ||||
|   return ( | ||||
|     // @ts-ignore the type signature for transform wrong here, translateX and translateY need to be in separate objects -prf
 | ||||
|     <Animated.View | ||||
|       style={[pal.view, pal.border, styles.tabBar, headerMinimalShellTransform]} | ||||
|       onLayout={e => { | ||||
|         headerHeight.value = e.nativeEvent.layout.height | ||||
|       }}> | ||||
|       {children} | ||||
|     </Animated.View> | ||||
|   ) | ||||
| } | ||||
| 
 | ||||
| const styles = StyleSheet.create({ | ||||
|   tabBar: { | ||||
|     // @ts-ignore Web only
 | ||||
|     position: 'sticky', | ||||
|     zIndex: 1, | ||||
|     // @ts-ignore Web only -prf
 | ||||
|     left: 'calc(50% - 300px)', | ||||
|     width: 600, | ||||
|     top: 0, | ||||
|     flexDirection: 'row', | ||||
|     alignItems: 'center', | ||||
|     borderLeftWidth: 1, | ||||
|     borderRightWidth: 1, | ||||
|   }, | ||||
| }) | ||||
							
								
								
									
										119
									
								
								src/view/com/home/HomeHeaderLayoutMobile.tsx
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										119
									
								
								src/view/com/home/HomeHeaderLayoutMobile.tsx
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,119 @@ | |||
| import React from 'react' | ||||
| import {StyleSheet, TouchableOpacity, View} from 'react-native' | ||||
| import {usePalette} from 'lib/hooks/usePalette' | ||||
| import {Link} from '../util/Link' | ||||
| import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' | ||||
| import {FontAwesomeIconStyle} from '@fortawesome/react-native-fontawesome' | ||||
| import {HITSLOP_10} from 'lib/constants' | ||||
| import Animated from 'react-native-reanimated' | ||||
| import {msg} from '@lingui/macro' | ||||
| import {useLingui} from '@lingui/react' | ||||
| import {useMinimalShellMode} from 'lib/hooks/useMinimalShellMode' | ||||
| import {useSetDrawerOpen} from '#/state/shell/drawer-open' | ||||
| import {useShellLayout} from '#/state/shell/shell-layout' | ||||
| import {isWeb} from 'platform/detection' | ||||
| import {Logo} from '#/view/icons/Logo' | ||||
| 
 | ||||
| import {IS_DEV} from '#/env' | ||||
| import {atoms} from '#/alf' | ||||
| import {Link as Link2} from '#/components/Link' | ||||
| import {ColorPalette_Stroke2_Corner0_Rounded as ColorPalette} from '#/components/icons/ColorPalette' | ||||
| 
 | ||||
| export function HomeHeaderLayoutMobile({ | ||||
|   children, | ||||
| }: { | ||||
|   children: React.ReactNode | ||||
| }) { | ||||
|   const pal = usePalette('default') | ||||
|   const {_} = useLingui() | ||||
|   const setDrawerOpen = useSetDrawerOpen() | ||||
|   const {headerHeight} = useShellLayout() | ||||
|   const {headerMinimalShellTransform} = useMinimalShellMode() | ||||
| 
 | ||||
|   const onPressAvi = React.useCallback(() => { | ||||
|     setDrawerOpen(true) | ||||
|   }, [setDrawerOpen]) | ||||
| 
 | ||||
|   return ( | ||||
|     <Animated.View | ||||
|       style={[pal.view, pal.border, styles.tabBar, headerMinimalShellTransform]} | ||||
|       onLayout={e => { | ||||
|         headerHeight.value = e.nativeEvent.layout.height | ||||
|       }}> | ||||
|       <View style={[pal.view, styles.topBar]}> | ||||
|         <View style={[pal.view, {width: 100}]}> | ||||
|           <TouchableOpacity | ||||
|             testID="viewHeaderDrawerBtn" | ||||
|             onPress={onPressAvi} | ||||
|             accessibilityRole="button" | ||||
|             accessibilityLabel={_(msg`Open navigation`)} | ||||
|             accessibilityHint={_( | ||||
|               msg`Access profile and other navigation links`, | ||||
|             )} | ||||
|             hitSlop={HITSLOP_10}> | ||||
|             <FontAwesomeIcon | ||||
|               icon="bars" | ||||
|               size={18} | ||||
|               color={pal.colors.textLight} | ||||
|             /> | ||||
|           </TouchableOpacity> | ||||
|         </View> | ||||
|         <View> | ||||
|           <Logo width={30} /> | ||||
|         </View> | ||||
|         <View | ||||
|           style={[ | ||||
|             atoms.flex_row, | ||||
|             atoms.justify_end, | ||||
|             atoms.align_center, | ||||
|             atoms.gap_md, | ||||
|             pal.view, | ||||
|             {width: 100}, | ||||
|           ]}> | ||||
|           {IS_DEV && ( | ||||
|             <Link2 to="/sys/debug"> | ||||
|               <ColorPalette size="md" /> | ||||
|             </Link2> | ||||
|           )} | ||||
|           <Link | ||||
|             testID="viewHeaderHomeFeedPrefsBtn" | ||||
|             href="/settings/home-feed" | ||||
|             hitSlop={HITSLOP_10} | ||||
|             accessibilityRole="button" | ||||
|             accessibilityLabel={_(msg`Home Feed Preferences`)} | ||||
|             accessibilityHint=""> | ||||
|             <FontAwesomeIcon | ||||
|               icon="sliders" | ||||
|               style={pal.textLight as FontAwesomeIconStyle} | ||||
|             /> | ||||
|           </Link> | ||||
|         </View> | ||||
|       </View> | ||||
|       {children} | ||||
|     </Animated.View> | ||||
|   ) | ||||
| } | ||||
| 
 | ||||
| const styles = StyleSheet.create({ | ||||
|   tabBar: { | ||||
|     // @ts-ignore web-only
 | ||||
|     position: isWeb ? 'fixed' : 'absolute', | ||||
|     zIndex: 1, | ||||
|     left: 0, | ||||
|     right: 0, | ||||
|     top: 0, | ||||
|     flexDirection: 'column', | ||||
|     borderBottomWidth: 1, | ||||
|   }, | ||||
|   topBar: { | ||||
|     flexDirection: 'row', | ||||
|     justifyContent: 'space-between', | ||||
|     alignItems: 'center', | ||||
|     paddingHorizontal: 18, | ||||
|     paddingVertical: 8, | ||||
|     width: '100%', | ||||
|   }, | ||||
|   title: { | ||||
|     fontSize: 21, | ||||
|   }, | ||||
| }) | ||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue