mirror of https://git.lenooby09.tech/LeNooby09/social-app.git
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Merge branch 'CooperEdmunds-2011-link-in-feeds-tab-bar' into main

+101 -14
+21 -4
__e2e__/tests/home-screen.test.ts
··· 4 4 5 5 describe('Home screen', () => { 6 6 beforeAll(async () => { 7 - await createServer('?users&follows&posts') 7 + await createServer('?users&follows&posts&feeds') 8 8 await openApp({permissions: {notifications: 'YES'}}) 9 9 }) 10 10 11 11 it('Login', async () => { 12 12 await loginAsAlice() 13 13 await element(by.id('homeScreenFeedTabs-Following')).tap() 14 + }) 15 + 16 + it('Can go to feeds page using feeds button in tab bar', async () => { 17 + await element(by.id('homeScreenFeedTabs-Feeds ✨')).tap() 18 + await expect(element(by.text('Discover new feeds'))).toBeVisible() 19 + }) 20 + 21 + it('Feeds button disappears after pinning a feed', async () => { 22 + await element(by.id('bottomBarProfileBtn')).tap() 23 + await element(by.id('profilePager-selector')).swipe('left') 24 + await element(by.id('profilePager-selector-4')).tap() 25 + await element(by.id('feed-alice-favs')).tap() 26 + await element(by.id('pinBtn')).tap() 27 + await element(by.id('bottomBarHomeBtn')).tap() 28 + await expect( 29 + element(by.id('homeScreenFeedTabs-Feeds ✨')), 30 + ).not.toBeVisible() 14 31 }) 15 32 16 33 it('Can like posts', async () => { ··· 65 82 66 83 it('Can swipe between feeds', async () => { 67 84 await element(by.id('homeScreen')).swipe('left', 'fast', 0.75) 68 - await expect(element(by.id('whatshotFeedPage'))).toBeVisible() 85 + await expect(element(by.id('customFeedPage'))).toBeVisible() 69 86 await element(by.id('homeScreen')).swipe('right', 'fast', 0.75) 70 87 await expect(element(by.id('followingFeedPage'))).toBeVisible() 71 88 }) 72 89 73 90 it('Can tap between feeds', async () => { 74 - await element(by.id("homeScreenFeedTabs-What's hot")).tap() 75 - await expect(element(by.id('whatshotFeedPage'))).toBeVisible() 91 + await element(by.id('homeScreenFeedTabs-alice-favs')).tap() 92 + await expect(element(by.id('customFeedPage'))).toBeVisible() 76 93 await element(by.id('homeScreenFeedTabs-Following')).tap() 77 94 await expect(element(by.id('followingFeedPage'))).toBeVisible() 78 95 })
+2 -2
src/lib/constants.ts
··· 116 116 } else { 117 117 // production 118 118 return { 119 - pinned: [PROD_DEFAULT_FEED('whats-hot')], 120 - saved: [PROD_DEFAULT_FEED('whats-hot')], 119 + pinned: [], 120 + saved: [], 121 121 } 122 122 } 123 123 }
+9 -2
src/state/queries/feed.ts
··· 246 246 likeUri: '', 247 247 } 248 248 249 - export function usePinnedFeedsInfos(): FeedSourceInfo[] { 249 + export function usePinnedFeedsInfos(): { 250 + feeds: FeedSourceInfo[] 251 + hasPinnedCustom: boolean 252 + } { 250 253 const queryClient = useQueryClient() 251 254 const [tabs, setTabs] = React.useState<FeedSourceInfo[]>([ 252 255 FOLLOWING_FEED_STUB, 253 256 ]) 254 257 const {data: preferences} = usePreferencesQuery() 258 + 259 + const hasPinnedCustom = React.useMemo<boolean>(() => { 260 + return tabs.some(tab => tab !== FOLLOWING_FEED_STUB) 261 + }, [tabs]) 255 262 256 263 React.useEffect(() => { 257 264 if (!preferences?.feeds?.pinned) return ··· 300 307 fetchFeedInfo() 301 308 }, [queryClient, setTabs, preferences?.feeds?.pinned]) 302 309 303 - return tabs 310 + return {feeds: tabs, hasPinnedCustom} 304 311 }
+31 -2
src/view/com/pager/FeedsTabBar.web.tsx
··· 12 12 import {useSession} from '#/state/session' 13 13 import {TextLink} from '#/view/com/util/Link' 14 14 import {CenteredView} from '../util/Views' 15 + import {isWeb} from 'platform/detection' 16 + import {useNavigation} from '@react-navigation/native' 17 + import {NavigationProp} from 'lib/routes/types' 15 18 16 19 export function FeedsTabBar( 17 20 props: RenderTabBarFnProps & {testID?: string; onPressSelected: () => void}, ··· 79 82 function FeedsTabBarTablet( 80 83 props: RenderTabBarFnProps & {testID?: string; onPressSelected: () => void}, 81 84 ) { 82 - const feeds = usePinnedFeedsInfos() 85 + const {feeds, hasPinnedCustom} = usePinnedFeedsInfos() 83 86 const pal = usePalette('default') 84 87 const {hasSession} = useSession() 88 + const navigation = useNavigation<NavigationProp>() 85 89 const {headerMinimalShellTransform} = useMinimalShellMode() 86 90 const {headerHeight} = useShellLayout() 87 - const items = hasSession ? feeds.map(f => f.displayName) : [] 91 + const pinnedDisplayNames = hasSession ? feeds.map(f => f.displayName) : [] 92 + const showFeedsLinkInTabBar = hasSession && !hasPinnedCustom 93 + const items = showFeedsLinkInTabBar 94 + ? pinnedDisplayNames.concat('Feeds ✨') 95 + : pinnedDisplayNames 96 + 97 + const onPressDiscoverFeeds = React.useCallback(() => { 98 + if (isWeb) { 99 + navigation.navigate('Feeds') 100 + } else { 101 + navigation.navigate('FeedsTab') 102 + navigation.popToTop() 103 + } 104 + }, [navigation]) 105 + 106 + const onSelect = React.useCallback( 107 + (index: number) => { 108 + if (showFeedsLinkInTabBar && index === items.length - 1) { 109 + onPressDiscoverFeeds() 110 + } else if (props.onSelect) { 111 + props.onSelect(index) 112 + } 113 + }, 114 + [items.length, onPressDiscoverFeeds, props, showFeedsLinkInTabBar], 115 + ) 88 116 89 117 return ( 90 118 // @ts-ignore the type signature for transform wrong here, translateX and translateY need to be in separate objects -prf ··· 96 124 <TabBar 97 125 key={items.join(',')} 98 126 {...props} 127 + onSelect={onSelect} 99 128 items={items} 100 129 indicatorColor={pal.colors.link} 101 130 />
+31 -3
src/view/com/pager/FeedsTabBarMobile.tsx
··· 18 18 import {useShellLayout} from '#/state/shell/shell-layout' 19 19 import {useSession} from '#/state/session' 20 20 import {usePinnedFeedsInfos} from '#/state/queries/feed' 21 + import {isWeb} from 'platform/detection' 22 + import {useNavigation} from '@react-navigation/native' 23 + import {NavigationProp} from 'lib/routes/types' 21 24 22 25 export function FeedsTabBar( 23 26 props: RenderTabBarFnProps & {testID?: string; onPressSelected: () => void}, ··· 26 29 const {isSandbox, hasSession} = useSession() 27 30 const {_} = useLingui() 28 31 const setDrawerOpen = useSetDrawerOpen() 29 - const feeds = usePinnedFeedsInfos() 32 + const navigation = useNavigation<NavigationProp>() 33 + const {feeds, hasPinnedCustom} = usePinnedFeedsInfos() 30 34 const brandBlue = useColorSchemeStyle(s.brandBlue, s.blue3) 31 35 const {headerHeight} = useShellLayout() 32 36 const {headerMinimalShellTransform} = useMinimalShellMode() 33 - const items = hasSession ? feeds.map(f => f.displayName) : [] 37 + const pinnedDisplayNames = hasSession ? feeds.map(f => f.displayName) : [] 38 + const showFeedsLinkInTabBar = hasSession && !hasPinnedCustom 39 + const items = showFeedsLinkInTabBar 40 + ? pinnedDisplayNames.concat('Feeds ✨') 41 + : pinnedDisplayNames 42 + 43 + const onPressFeedsLink = React.useCallback(() => { 44 + if (isWeb) { 45 + navigation.navigate('Feeds') 46 + } else { 47 + navigation.navigate('FeedsTab') 48 + navigation.popToTop() 49 + } 50 + }, [navigation]) 51 + 52 + const onSelect = React.useCallback( 53 + (index: number) => { 54 + if (showFeedsLinkInTabBar && index === items.length - 1) { 55 + onPressFeedsLink() 56 + } else if (props.onSelect) { 57 + props.onSelect(index) 58 + } 59 + }, 60 + [items.length, onPressFeedsLink, props, showFeedsLinkInTabBar], 61 + ) 34 62 35 63 const onPressAvi = React.useCallback(() => { 36 64 setDrawerOpen(true) ··· 84 112 key={items.join(',')} 85 113 onPressSelected={props.onPressSelected} 86 114 selectedPage={props.selectedPage} 87 - onSelect={props.onSelect} 115 + onSelect={onSelect} 88 116 testID={props.testID} 89 117 items={items} 90 118 indicatorColor={pal.colors.link}
+2
src/view/com/pager/PagerWithHeader.tsx
··· 108 108 pointerEvents: isHeaderReady ? 'auto' : 'none', 109 109 }}> 110 110 <TabBar 111 + testID={testID} 111 112 items={items} 112 113 selectedPage={currentPage} 113 114 onSelect={props.onSelect} ··· 127 128 isMobile, 128 129 onTabBarLayout, 129 130 onHeaderOnlyLayout, 131 + testID, 130 132 ], 131 133 ) 132 134
+2
src/view/com/pager/TabBar.tsx
··· 68 68 return ( 69 69 <View testID={testID} style={[pal.view, styles.outer]}> 70 70 <DraggableScrollView 71 + testID={`${testID}-selector`} 71 72 horizontal={true} 72 73 showsHorizontalScrollIndicator={false} 73 74 ref={scrollElRef} ··· 76 77 const selected = i === selectedPage 77 78 return ( 78 79 <PressableWithHover 80 + testID={`${testID}-selector-${i}`} 79 81 key={item} 80 82 onLayout={e => onItemLayout(e, i)} 81 83 style={[styles.item, selected && indicatorStyle]}
+1
src/view/screens/Profile.tsx
··· 267 267 screenDescription="profile" 268 268 moderation={moderation.account}> 269 269 <PagerWithHeader 270 + testID="profilePager" 270 271 isHeaderReady={true} 271 272 items={sectionTitles} 272 273 onPageSelected={onPageSelected}
+1
src/view/screens/ProfileFeed.tsx
··· 353 353 style={styles.btn} 354 354 /> 355 355 <Button 356 + testID={isPinned ? 'unpinBtn' : 'pinBtn'} 356 357 disabled={isPinPending || isUnpinPending} 357 358 type={isPinned ? 'default' : 'inverted'} 358 359 label={isPinned ? 'Unpin' : 'Pin to home'}
+1 -1
src/view/shell/desktop/Feeds.tsx
··· 11 11 export function DesktopFeeds() { 12 12 const pal = usePalette('default') 13 13 const {_} = useLingui() 14 - const feeds = usePinnedFeedsInfos() 14 + const {feeds} = usePinnedFeedsInfos() 15 15 16 16 const route = useNavigationState(state => { 17 17 if (!state) {