diff options
author | Ansh <anshnanda10@gmail.com> | 2023-10-13 18:54:35 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-10-13 18:54:35 -0700 |
commit | 8e9cf182c2e247203b6b5ea9ae701c039945d6a0 (patch) | |
tree | 1dbf0c69fe209fccdb7841b29fc03bf8e311eac3 /src/view/com/feeds/FeedPage.tsx | |
parent | 9042f503c2533deff535de75b190c26ed1ae59ec (diff) | |
download | voidsky-8e9cf182c2e247203b6b5ea9ae701c039945d6a0.tar.zst |
Performance optimization (#1676)
* upgrade sentry to support profiling monitoring * remove console logs in production builds * feeds tab bar and bottom bar animation centralized * refactor FeedPage out of Home * add script to start in production mode * move FAB inner to reanimated * move FABInner back to `Animated` RN animation * add perf commands * add testing with Maestro and perf with Flashlight * fix merge conflicts * fix resourceClass name in eas.json * fix onEndReachedThreshold in Feed * memoize styles * go back to old styling for LoadLatestBtn * remove reanimated code from useMinimalShellMode * move shell animations to hook/reanimated for perf * fix empty state issue * make shell animation feel smoother * make shell animation more smooth * run animation with autorun * specify keys for tab bar properly * remove comments * remove already imported dep * fix lint * add testing instructions * mock sentry-expo for jest * fix jest mocks * Fix the load-latest button on desktop and tablet * Fix: don't move the FAB in tablet mode * Fix type error * Fix tabs bar positioning on tablet * Fix types --------- Co-authored-by: Paul Frazee <pfrazee@gmail.com>
Diffstat (limited to 'src/view/com/feeds/FeedPage.tsx')
-rw-r--r-- | src/view/com/feeds/FeedPage.tsx | 210 |
1 files changed, 210 insertions, 0 deletions
diff --git a/src/view/com/feeds/FeedPage.tsx b/src/view/com/feeds/FeedPage.tsx new file mode 100644 index 000000000..725106d59 --- /dev/null +++ b/src/view/com/feeds/FeedPage.tsx @@ -0,0 +1,210 @@ +import { + FontAwesomeIcon, + FontAwesomeIconStyle, +} from '@fortawesome/react-native-fontawesome' +import {useIsFocused} from '@react-navigation/native' +import {useAnalytics} from '@segment/analytics-react-native' +import {useOnMainScroll} from 'lib/hooks/useOnMainScroll' +import {usePalette} from 'lib/hooks/usePalette' +import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries' +import {ComposeIcon2} from 'lib/icons' +import {colors, s} from 'lib/styles' +import {observer} from 'mobx-react-lite' +import React from 'react' +import {FlatList, View} from 'react-native' +import {useStores} from 'state/index' +import {PostsFeedModel} from 'state/models/feeds/posts' +import {useHeaderOffset, POLL_FREQ} from 'view/screens/Home' +import {Feed} from '../posts/Feed' +import {TextLink} from '../util/Link' +import {FAB} from '../util/fab/FAB' +import {LoadLatestBtn} from '../util/load-latest/LoadLatestBtn' +import useAppState from 'react-native-appstate-hook' + +export const FeedPage = observer(function FeedPageImpl({ + testID, + isPageFocused, + feed, + renderEmptyState, + renderEndOfFeed, +}: { + testID?: string + feed: PostsFeedModel + isPageFocused: boolean + renderEmptyState: () => JSX.Element + renderEndOfFeed?: () => JSX.Element +}) { + const store = useStores() + const pal = usePalette('default') + const {isDesktop} = useWebMediaQueries() + const [onMainScroll, isScrolledDown, resetMainScroll] = useOnMainScroll(store) + const {screen, track} = useAnalytics() + const headerOffset = useHeaderOffset() + const scrollElRef = React.useRef<FlatList>(null) + const {appState} = useAppState({ + onForeground: () => doPoll(true), + }) + const isScreenFocused = useIsFocused() + const hasNew = feed.hasNewLatest && !feed.isRefreshing + + React.useEffect(() => { + // called on first load + if (!feed.hasLoaded && isPageFocused) { + feed.setup() + } + }, [isPageFocused, feed]) + + const doPoll = React.useCallback( + (knownActive = false) => { + if ( + (!knownActive && appState !== 'active') || + !isScreenFocused || + !isPageFocused + ) { + return + } + if (feed.isLoading) { + return + } + store.log.debug('HomeScreen: Polling for new posts') + feed.checkForLatest() + }, + [appState, isScreenFocused, isPageFocused, store, feed], + ) + + const scrollToTop = React.useCallback(() => { + scrollElRef.current?.scrollToOffset({offset: -headerOffset}) + resetMainScroll() + }, [headerOffset, resetMainScroll]) + + const onSoftReset = React.useCallback(() => { + if (isPageFocused) { + scrollToTop() + feed.refresh() + } + }, [isPageFocused, scrollToTop, feed]) + + // fires when page within screen is activated/deactivated + // - check for latest + React.useEffect(() => { + if (!isPageFocused || !isScreenFocused) { + return + } + + const softResetSub = store.onScreenSoftReset(onSoftReset) + const feedCleanup = feed.registerListeners() + const pollInterval = setInterval(doPoll, POLL_FREQ) + + screen('Feed') + store.log.debug('HomeScreen: Updating feed') + feed.checkForLatest() + + return () => { + clearInterval(pollInterval) + softResetSub.remove() + feedCleanup() + } + }, [store, doPoll, onSoftReset, screen, feed, isPageFocused, isScreenFocused]) + + const onPressCompose = React.useCallback(() => { + track('HomeScreen:PressCompose') + store.shell.openComposer({}) + }, [store, track]) + + const onPressTryAgain = React.useCallback(() => { + feed.refresh() + }, [feed]) + + const onPressLoadLatest = React.useCallback(() => { + scrollToTop() + feed.refresh() + }, [feed, scrollToTop]) + + const ListHeaderComponent = React.useCallback(() => { + if (isDesktop) { + return ( + <View + style={[ + pal.view, + { + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'space-between', + paddingHorizontal: 18, + paddingVertical: 12, + }, + ]}> + <TextLink + type="title-lg" + href="/" + style={[pal.text, {fontWeight: 'bold'}]} + text={ + <> + {store.session.isSandbox ? 'SANDBOX' : 'Bluesky'}{' '} + {hasNew && ( + <View + style={{ + top: -8, + backgroundColor: colors.blue3, + width: 8, + height: 8, + borderRadius: 4, + }} + /> + )} + </> + } + onPress={() => store.emitScreenSoftReset()} + /> + <TextLink + type="title-lg" + href="/settings/home-feed" + style={{fontWeight: 'bold'}} + accessibilityLabel="Feed Preferences" + accessibilityHint="" + text={ + <FontAwesomeIcon + icon="sliders" + style={pal.textLight as FontAwesomeIconStyle} + /> + } + /> + </View> + ) + } + return <></> + }, [isDesktop, pal, store, hasNew]) + + return ( + <View testID={testID} style={s.h100pct}> + <Feed + testID={testID ? `${testID}-feed` : undefined} + key="default" + feed={feed} + scrollElRef={scrollElRef} + onPressTryAgain={onPressTryAgain} + onScroll={onMainScroll} + scrollEventThrottle={100} + renderEmptyState={renderEmptyState} + renderEndOfFeed={renderEndOfFeed} + ListHeaderComponent={ListHeaderComponent} + headerOffset={headerOffset} + /> + {(isScrolledDown || hasNew) && ( + <LoadLatestBtn + onPress={onPressLoadLatest} + label="Load new posts" + showIndicator={hasNew} + /> + )} + <FAB + testID="composeFAB" + onPress={onPressCompose} + icon={<ComposeIcon2 strokeWidth={1.5} size={29} style={s.white} />} + accessibilityRole="button" + accessibilityLabel="New post" + accessibilityHint="" + /> + </View> + ) +}) |