Bluesky app fork with some witchin' additions 💫
0
fork

Configure Feed

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

Allow profile header to overscroll (#5457)

* add allowoverscroll prop

* ensure spinner is visible

* more generic prop for `<List>`

* rename to allowHeaderOverScroll

authored by

Samuel Newman and committed by
GitHub
bd393b1b 850cfc1c

+72 -47
+9 -8
src/components/StarterPack/ProfileStarterPacks.tsx
··· 12 12 import {useNavigation} from '@react-navigation/native' 13 13 import {InfiniteData, UseInfiniteQueryResult} from '@tanstack/react-query' 14 14 15 + import {useGenerateStarterPackMutation} from '#/lib/generate-starterpack' 16 + import {useBottomBarOffset} from '#/lib/hooks/useBottomBarOffset' 17 + import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries' 18 + import {NavigationProp} from '#/lib/routes/types' 19 + import {parseStarterPackUri} from '#/lib/strings/starter-pack' 15 20 import {logger} from '#/logger' 16 - import {useGenerateStarterPackMutation} from 'lib/generate-starterpack' 17 - import {useBottomBarOffset} from 'lib/hooks/useBottomBarOffset' 18 - import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries' 19 - import {NavigationProp} from 'lib/routes/types' 20 - import {parseStarterPackUri} from 'lib/strings/starter-pack' 21 - import {List, ListRef} from 'view/com/util/List' 22 - import {Text} from 'view/com/util/text/Text' 23 - import {atoms as a, useTheme} from '#/alf' 21 + import {List, ListRef} from '#/view/com/util/List' 22 + import {Text} from '#/view/com/util/text/Text' 23 + import {atoms as a, ios, useTheme} from '#/alf' 24 24 import {Button, ButtonIcon, ButtonText} from '#/components/Button' 25 25 import {useDialogControl} from '#/components/Dialog' 26 26 import {LinearGradientBackground} from '#/components/LinearGradientBackground' ··· 132 132 keyExtractor={keyExtractor} 133 133 refreshing={isPTRing} 134 134 headerOffset={headerOffset} 135 + progressViewOffset={ios(0)} 135 136 contentContainerStyle={{paddingBottom: headerOffset + bottomBarOffset}} 136 137 indicatorStyle={t.name === 'light' ? 'black' : 'white'} 137 138 removeClippedSubviews={true}
+9 -5
src/screens/Profile/Header/index.tsx
··· 7 7 RichText as RichTextAPI, 8 8 } from '@atproto/api' 9 9 10 - import {usePalette} from 'lib/hooks/usePalette' 11 - import {LoadingPlaceholder} from 'view/com/util/LoadingPlaceholder' 10 + import {LoadingPlaceholder} from '#/view/com/util/LoadingPlaceholder' 11 + import {useTheme} from '#/alf' 12 12 import {ProfileHeaderLabeler} from './ProfileHeaderLabeler' 13 13 import {ProfileHeaderStandard} from './ProfileHeaderStandard' 14 14 15 15 let ProfileHeaderLoading = (_props: {}): React.ReactNode => { 16 - const pal = usePalette('default') 16 + const t = useTheme() 17 17 return ( 18 - <View style={pal.view}> 18 + <View style={t.atoms.bg}> 19 19 <LoadingPlaceholder width="100%" height={150} style={{borderRadius: 0}} /> 20 20 <View 21 - style={[pal.view, {borderColor: pal.colors.background}, styles.avi]}> 21 + style={[ 22 + t.atoms.bg, 23 + {borderColor: t.atoms.bg.backgroundColor}, 24 + styles.avi, 25 + ]}> 22 26 <LoadingPlaceholder width={90} height={90} style={styles.br45} /> 23 27 </View> 24 28 <View style={styles.content}>
+8 -6
src/screens/Profile/Sections/Feed.tsx
··· 4 4 import {useLingui} from '@lingui/react' 5 5 import {useQueryClient} from '@tanstack/react-query' 6 6 7 + import {useInitialNumToRender} from '#/lib/hooks/useInitialNumToRender' 8 + import {usePalette} from '#/lib/hooks/usePalette' 7 9 import {isNative} from '#/platform/detection' 8 10 import {FeedDescriptor} from '#/state/queries/post-feed' 9 11 import {RQKEY as FEED_RQKEY} from '#/state/queries/post-feed' 10 12 import {truncateAndInvalidate} from '#/state/queries/util' 11 - import {useInitialNumToRender} from 'lib/hooks/useInitialNumToRender' 12 - import {usePalette} from 'lib/hooks/usePalette' 13 + import {Feed} from '#/view/com/posts/Feed' 14 + import {EmptyState} from '#/view/com/util/EmptyState' 15 + import {ListRef} from '#/view/com/util/List' 16 + import {LoadLatestBtn} from '#/view/com/util/load-latest/LoadLatestBtn' 13 17 import {Text} from '#/view/com/util/text/Text' 14 - import {Feed} from 'view/com/posts/Feed' 15 - import {EmptyState} from 'view/com/util/EmptyState' 16 - import {ListRef} from 'view/com/util/List' 17 - import {LoadLatestBtn} from 'view/com/util/load-latest/LoadLatestBtn' 18 + import {ios} from '#/alf' 18 19 import {SectionRef} from './types' 19 20 20 21 interface FeedSectionProps { ··· 82 83 onScrolledDownChange={setIsScrolledDown} 83 84 renderEmptyState={renderPostsEmpty} 84 85 headerOffset={headerHeight} 86 + progressViewOffset={ios(0)} 85 87 renderEndOfFeed={ProfileEndOfFeed} 86 88 ignoreFilterFor={ignoreFilterFor} 87 89 initialNumToRender={
+3 -2
src/view/com/feeds/ProfileFeedgens.tsx
··· 15 15 import {isNative, isWeb} from '#/platform/detection' 16 16 import {usePreferencesQuery} from '#/state/queries/preferences' 17 17 import {RQKEY, useProfileFeedgensQuery} from '#/state/queries/profile-feedgens' 18 + import {EmptyState} from '#/view/com/util/EmptyState' 18 19 import {FeedLoadingPlaceholder} from '#/view/com/util/LoadingPlaceholder' 19 - import {EmptyState} from 'view/com/util/EmptyState' 20 - import {atoms as a, useTheme} from '#/alf' 20 + import {atoms as a, ios, useTheme} from '#/alf' 21 21 import * as FeedCard from '#/components/FeedCard' 22 22 import {ErrorMessage} from '../util/error/ErrorMessage' 23 23 import {List, ListRef} from '../util/List' ··· 191 191 refreshing={isPTRing} 192 192 onRefresh={onRefresh} 193 193 headerOffset={headerOffset} 194 + progressViewOffset={ios(0)} 194 195 contentContainerStyle={isNative && {paddingBottom: headerOffset + 100}} 195 196 indicatorStyle={t.name === 'light' ? 'black' : 'white'} 196 197 removeClippedSubviews={true}
+4 -3
src/view/com/lists/ProfileLists.tsx
··· 10 10 import {useLingui} from '@lingui/react' 11 11 import {useQueryClient} from '@tanstack/react-query' 12 12 13 + import {useAnalytics} from '#/lib/analytics/analytics' 13 14 import {cleanError} from '#/lib/strings/errors' 14 15 import {logger} from '#/logger' 15 16 import {isNative, isWeb} from '#/platform/detection' 16 17 import {RQKEY, useProfileListsQuery} from '#/state/queries/profile-lists' 17 - import {useAnalytics} from 'lib/analytics/analytics' 18 + import {EmptyState} from '#/view/com/util/EmptyState' 18 19 import {FeedLoadingPlaceholder} from '#/view/com/util/LoadingPlaceholder' 19 - import {EmptyState} from 'view/com/util/EmptyState' 20 - import {atoms as a, useTheme} from '#/alf' 20 + import {atoms as a, ios, useTheme} from '#/alf' 21 21 import * as ListCard from '#/components/ListCard' 22 22 import {ErrorMessage} from '../util/error/ErrorMessage' 23 23 import {List, ListRef} from '../util/List' ··· 192 192 refreshing={isPTRing} 193 193 onRefresh={onRefresh} 194 194 headerOffset={headerOffset} 195 + progressViewOffset={ios(0)} 195 196 contentContainerStyle={ 196 197 isNative && {paddingBottom: headerOffset + 100} 197 198 }
+20 -9
src/view/com/pager/PagerWithHeader.tsx
··· 19 19 20 20 import {useNonReactiveCallback} from '#/lib/hooks/useNonReactiveCallback' 21 21 import {ScrollProvider} from '#/lib/ScrollContext' 22 - import {isIOS} from 'platform/detection' 23 - import {Pager, PagerRef, RenderTabBarFnProps} from 'view/com/pager/Pager' 22 + import {isIOS} from '#/platform/detection' 23 + import {Pager, PagerRef, RenderTabBarFnProps} from '#/view/com/pager/Pager' 24 24 import {ListMethods} from '../util/List' 25 25 import {TabBar} from './TabBar' 26 26 ··· 41 41 initialPage?: number 42 42 onPageSelected?: (index: number) => void 43 43 onCurrentPageSelected?: (index: number) => void 44 + allowHeaderOverScroll?: boolean 44 45 } 45 46 export const PagerWithHeader = React.forwardRef<PagerRef, PagerWithHeaderProps>( 46 47 function PageWithHeaderImpl( ··· 53 54 initialPage, 54 55 onPageSelected, 55 56 onCurrentPageSelected, 57 + allowHeaderOverScroll, 56 58 }: PagerWithHeaderProps, 57 59 ref, 58 60 ) { ··· 92 94 onSelect={props.onSelect} 93 95 scrollY={scrollY} 94 96 testID={testID} 97 + allowHeaderOverScroll={allowHeaderOverScroll} 95 98 /> 96 99 ) 97 100 }, ··· 106 109 onHeaderOnlyLayout, 107 110 scrollY, 108 111 testID, 112 + allowHeaderOverScroll, 109 113 ], 110 114 ) 111 115 ··· 216 220 onTabBarLayout, 217 221 onCurrentPageSelected, 218 222 onSelect, 223 + allowHeaderOverScroll, 219 224 }: { 220 225 currentPage: number 221 226 headerOnlyHeight: number ··· 228 233 onTabBarLayout: (e: LayoutChangeEvent) => void 229 234 onCurrentPageSelected?: (index: number) => void 230 235 onSelect?: (index: number) => void 236 + allowHeaderOverScroll?: boolean 231 237 }): React.ReactNode => { 232 - const headerTransform = useAnimatedStyle(() => ({ 233 - transform: [ 234 - { 235 - translateY: Math.min(Math.min(scrollY.value, headerOnlyHeight) * -1, 0), 236 - }, 237 - ], 238 - })) 238 + const headerTransform = useAnimatedStyle(() => { 239 + const translateY = Math.min(scrollY.value, headerOnlyHeight) * -1 240 + return { 241 + transform: [ 242 + { 243 + translateY: allowHeaderOverScroll 244 + ? translateY 245 + : Math.min(translateY, 0), 246 + }, 247 + ], 248 + } 249 + }) 239 250 const headerRef = React.useRef(null) 240 251 return ( 241 252 <Animated.View
+1 -1
src/view/com/pager/PagerWithHeader.web.tsx
··· 4 4 5 5 import {usePalette} from '#/lib/hooks/usePalette' 6 6 import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries' 7 - import {Pager, PagerRef, RenderTabBarFnProps} from 'view/com/pager/Pager' 7 + import {Pager, PagerRef, RenderTabBarFnProps} from '#/view/com/pager/Pager' 8 8 import {ListMethods} from '../util/List' 9 9 import {TabBar} from './TabBar' 10 10
+6 -3
src/view/com/posts/Feed.tsx
··· 14 14 import {useLingui} from '@lingui/react' 15 15 import {useQueryClient} from '@tanstack/react-query' 16 16 17 + import {useAnalytics} from '#/lib/analytics/analytics' 17 18 import {DISCOVER_FEED_URI, KNOWN_SHUTDOWN_FEEDS} from '#/lib/constants' 19 + import {useInitialNumToRender} from '#/lib/hooks/useInitialNumToRender' 18 20 import {logEvent, useGate} from '#/lib/statsig/statsig' 21 + import {useTheme} from '#/lib/ThemeContext' 19 22 import {logger} from '#/logger' 20 23 import {isWeb} from '#/platform/detection' 21 24 import {listenPostCreated} from '#/state/events' ··· 30 33 usePostFeedQuery, 31 34 } from '#/state/queries/post-feed' 32 35 import {useSession} from '#/state/session' 33 - import {useAnalytics} from 'lib/analytics/analytics' 34 - import {useInitialNumToRender} from 'lib/hooks/useInitialNumToRender' 35 - import {useTheme} from 'lib/ThemeContext' 36 36 import { 37 37 ProgressGuide, 38 38 SuggestedFeeds, ··· 167 167 renderEndOfFeed, 168 168 testID, 169 169 headerOffset = 0, 170 + progressViewOffset, 170 171 desktopFixedHeightOffset, 171 172 ListHeaderComponent, 172 173 extraData, ··· 187 188 renderEndOfFeed?: () => JSX.Element 188 189 testID?: string 189 190 headerOffset?: number 191 + progressViewOffset?: number 190 192 desktopFixedHeightOffset?: number 191 193 ListHeaderComponent?: () => JSX.Element 192 194 extraData?: any ··· 548 550 refreshing={isPTRing} 549 551 onRefresh={onRefresh} 550 552 headerOffset={headerOffset} 553 + progressViewOffset={progressViewOffset} 551 554 contentContainerStyle={{ 552 555 minHeight: Dimensions.get('window').height * 1.5, 553 556 }}
+9 -8
src/view/com/util/List.tsx
··· 4 4 import {updateActiveVideoViewAsync} from '@haileyok/bluesky-video' 5 5 6 6 import {useAnimatedScrollHandler} from '#/lib/hooks/useAnimatedScrollHandler_FIXED' 7 - import {usePalette} from '#/lib/hooks/usePalette' 7 + import {useDedupe} from '#/lib/hooks/useDedupe' 8 8 import {useScrollHandlers} from '#/lib/ScrollContext' 9 - import {useDedupe} from 'lib/hooks/useDedupe' 10 - import {addStyle} from 'lib/styles' 11 - import {isIOS} from 'platform/detection' 9 + import {addStyle} from '#/lib/styles' 10 + import {isIOS} from '#/platform/detection' 11 + import {useTheme} from '#/alf' 12 12 import {FlatList_INTERNAL} from './Views' 13 13 14 14 export type ListMethods = FlatList_INTERNAL ··· 44 44 onItemSeen, 45 45 headerOffset, 46 46 style, 47 + progressViewOffset, 47 48 ...props 48 49 }: ListProps<ItemT>, 49 50 ref: React.Ref<ListMethods>, 50 51 ) { 51 52 const isScrolledDown = useSharedValue(false) 52 - const pal = usePalette('default') 53 + const t = useTheme() 53 54 const dedupe = useDedupe(400) 54 55 55 56 function handleScrolledDownChange(didScrollDown: boolean) { ··· 120 121 <RefreshControl 121 122 refreshing={refreshing ?? false} 122 123 onRefresh={onRefresh} 123 - tintColor={pal.colors.text} 124 - titleColor={pal.colors.text} 125 - progressViewOffset={headerOffset} 124 + tintColor={t.atoms.text.color} 125 + titleColor={t.atoms.text.color} 126 + progressViewOffset={progressViewOffset ?? headerOffset} 126 127 /> 127 128 ) 128 129 }
+3 -2
src/view/screens/Profile.tsx
··· 37 37 import {useComposerControls} from '#/state/shell/composer' 38 38 import {ProfileFeedgens} from '#/view/com/feeds/ProfileFeedgens' 39 39 import {ProfileLists} from '#/view/com/lists/ProfileLists' 40 + import {PagerWithHeader} from '#/view/com/pager/PagerWithHeader' 40 41 import {ErrorScreen} from '#/view/com/util/error/ErrorScreen' 41 42 import {FAB} from '#/view/com/util/fab/FAB' 42 43 import {ListRef} from '#/view/com/util/List' 43 44 import {CenteredView} from '#/view/com/util/Views' 44 - import {PagerWithHeader} from 'view/com/pager/PagerWithHeader' 45 45 import {ProfileHeader, ProfileHeaderLoading} from '#/screens/Profile/Header' 46 46 import {ProfileFeedSection} from '#/screens/Profile/Sections/Feed' 47 47 import {ProfileLabelsSection} from '#/screens/Profile/Sections/Labels' ··· 363 363 items={sectionTitles} 364 364 onPageSelected={onPageSelected} 365 365 onCurrentPageSelected={onCurrentPageSelected} 366 - renderHeader={renderHeader}> 366 + renderHeader={renderHeader} 367 + allowHeaderOverScroll> 367 368 {showFiltersTab 368 369 ? ({headerHeight, isFocused, scrollElRef}) => ( 369 370 <ProfileLabelsSection