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.

at offline-detection 237 lines 7.1 kB view raw
1import React from 'react' 2import { 3 FontAwesomeIcon, 4 FontAwesomeIconStyle, 5} from '@fortawesome/react-native-fontawesome' 6import {useNavigation} from '@react-navigation/native' 7import {useAnalytics} from 'lib/analytics/analytics' 8import {useQueryClient} from '@tanstack/react-query' 9import {RQKEY as FEED_RQKEY} from '#/state/queries/post-feed' 10import {MainScrollProvider} from '../util/MainScrollProvider' 11import {usePalette} from 'lib/hooks/usePalette' 12import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries' 13import {useSetMinimalShellMode} from '#/state/shell' 14import {FeedDescriptor, FeedParams} from '#/state/queries/post-feed' 15import {ComposeIcon2} from 'lib/icons' 16import {colors, s} from 'lib/styles' 17import {View, useWindowDimensions} from 'react-native' 18import {ListMethods} from '../util/List' 19import {Feed} from '../posts/Feed' 20import {TextLink} from '../util/Link' 21import {FAB} from '../util/fab/FAB' 22import {LoadLatestBtn} from '../util/load-latest/LoadLatestBtn' 23import {msg} from '@lingui/macro' 24import {useLingui} from '@lingui/react' 25import {useSession} from '#/state/session' 26import {useComposerControls} from '#/state/shell/composer' 27import {listenSoftReset, emitSoftReset} from '#/state/events' 28import {truncateAndInvalidate} from '#/state/queries/util' 29import {TabState, getTabState, getRootNavigation} from '#/lib/routes/helpers' 30import {isNative} from '#/platform/detection' 31 32const POLL_FREQ = 60e3 // 60sec 33 34export function FeedPage({ 35 testID, 36 isPageFocused, 37 feed, 38 feedParams, 39 renderEmptyState, 40 renderEndOfFeed, 41}: { 42 testID?: string 43 feed: FeedDescriptor 44 feedParams?: FeedParams 45 isPageFocused: boolean 46 renderEmptyState: () => JSX.Element 47 renderEndOfFeed?: () => JSX.Element 48}) { 49 const {isSandbox, hasSession} = useSession() 50 const pal = usePalette('default') 51 const {_} = useLingui() 52 const navigation = useNavigation() 53 const {isDesktop} = useWebMediaQueries() 54 const queryClient = useQueryClient() 55 const {openComposer} = useComposerControls() 56 const [isScrolledDown, setIsScrolledDown] = React.useState(false) 57 const setMinimalShellMode = useSetMinimalShellMode() 58 const {screen, track} = useAnalytics() 59 const headerOffset = useHeaderOffset() 60 const scrollElRef = React.useRef<ListMethods>(null) 61 const [hasNew, setHasNew] = React.useState(false) 62 63 const scrollToTop = React.useCallback(() => { 64 scrollElRef.current?.scrollToOffset({ 65 animated: isNative, 66 offset: -headerOffset, 67 }) 68 setMinimalShellMode(false) 69 }, [headerOffset, setMinimalShellMode]) 70 71 const onSoftReset = React.useCallback(() => { 72 const isScreenFocused = 73 getTabState(getRootNavigation(navigation).getState(), 'Home') === 74 TabState.InsideAtRoot 75 if (isScreenFocused && isPageFocused) { 76 scrollToTop() 77 truncateAndInvalidate(queryClient, FEED_RQKEY(feed)) 78 setHasNew(false) 79 } 80 }, [navigation, isPageFocused, scrollToTop, queryClient, feed, setHasNew]) 81 82 // fires when page within screen is activated/deactivated 83 React.useEffect(() => { 84 if (!isPageFocused) { 85 return 86 } 87 screen('Feed') 88 return listenSoftReset(onSoftReset) 89 }, [onSoftReset, screen, isPageFocused]) 90 91 const onPressCompose = React.useCallback(() => { 92 track('HomeScreen:PressCompose') 93 openComposer({}) 94 }, [openComposer, track]) 95 96 const onPressLoadLatest = React.useCallback(() => { 97 scrollToTop() 98 truncateAndInvalidate(queryClient, FEED_RQKEY(feed)) 99 setHasNew(false) 100 }, [scrollToTop, feed, queryClient, setHasNew]) 101 102 const ListHeaderComponent = React.useCallback(() => { 103 if (isDesktop) { 104 return ( 105 <View 106 style={[ 107 pal.view, 108 { 109 flexDirection: 'row', 110 alignItems: 'center', 111 justifyContent: 'space-between', 112 paddingHorizontal: 18, 113 paddingVertical: 12, 114 }, 115 ]}> 116 <TextLink 117 type="title-lg" 118 href="/" 119 style={[pal.text, {fontWeight: 'bold'}]} 120 text={ 121 <> 122 {isSandbox ? 'SANDBOX' : 'Bluesky'}{' '} 123 {hasNew && ( 124 <View 125 style={{ 126 top: -8, 127 backgroundColor: colors.blue3, 128 width: 8, 129 height: 8, 130 borderRadius: 4, 131 }} 132 /> 133 )} 134 </> 135 } 136 onPress={emitSoftReset} 137 /> 138 {hasSession && ( 139 <TextLink 140 type="title-lg" 141 href="/settings/home-feed" 142 style={{fontWeight: 'bold'}} 143 accessibilityLabel={_(msg`Feed Preferences`)} 144 accessibilityHint="" 145 text={ 146 <FontAwesomeIcon 147 icon="sliders" 148 style={pal.textLight as FontAwesomeIconStyle} 149 /> 150 } 151 /> 152 )} 153 </View> 154 ) 155 } 156 return <></> 157 }, [ 158 isDesktop, 159 pal.view, 160 pal.text, 161 pal.textLight, 162 hasNew, 163 _, 164 isSandbox, 165 hasSession, 166 ]) 167 168 return ( 169 <View testID={testID} style={s.h100pct}> 170 <MainScrollProvider> 171 <Feed 172 testID={testID ? `${testID}-feed` : undefined} 173 enabled={isPageFocused} 174 feed={feed} 175 feedParams={feedParams} 176 pollInterval={POLL_FREQ} 177 scrollElRef={scrollElRef} 178 onScrolledDownChange={setIsScrolledDown} 179 onHasNew={setHasNew} 180 renderEmptyState={renderEmptyState} 181 renderEndOfFeed={renderEndOfFeed} 182 ListHeaderComponent={ListHeaderComponent} 183 headerOffset={headerOffset} 184 /> 185 </MainScrollProvider> 186 {(isScrolledDown || hasNew) && ( 187 <LoadLatestBtn 188 onPress={onPressLoadLatest} 189 label={_(msg`Load new posts`)} 190 showIndicator={hasNew} 191 /> 192 )} 193 194 {hasSession && ( 195 <FAB 196 testID="composeFAB" 197 onPress={onPressCompose} 198 icon={<ComposeIcon2 strokeWidth={1.5} size={29} style={s.white} />} 199 accessibilityRole="button" 200 accessibilityLabel={_(msg`New post`)} 201 accessibilityHint="" 202 /> 203 )} 204 </View> 205 ) 206} 207 208function useHeaderOffset() { 209 const {isDesktop, isTablet} = useWebMediaQueries() 210 const {fontScale} = useWindowDimensions() 211 const {hasSession} = useSession() 212 213 if (isDesktop) { 214 return 0 215 } 216 if (isTablet) { 217 if (hasSession) { 218 return 50 219 } else { 220 return 0 221 } 222 } 223 224 if (hasSession) { 225 const navBarPad = 16 226 const navBarText = 21 * fontScale 227 const tabBarPad = 20 + 3 // nav bar padding + border 228 const tabBarText = 16 * fontScale 229 const magic = 7 * fontScale 230 return navBarPad + navBarText + tabBarPad + tabBarText + magic 231 } else { 232 const navBarPad = 16 233 const navBarText = 21 * fontScale 234 const magic = 4 * fontScale 235 return navBarPad + navBarText + magic 236 } 237}