Skip to content

Commit

Permalink
fix: home view performance (#11276)
Browse files Browse the repository at this point in the history
* chore: use flashlist for artwork rails on iOS

* chore: memoise home view sections

* chore: bring down windowSize on the HomeView

* chore: memoise tabs

* chore: add comment

* chore: remove intialNumToRender

* fix: unstable tests - increase timeout
  • Loading branch information
MounirDhahri authored Dec 12, 2024
1 parent ea64dba commit 01b0e2f
Show file tree
Hide file tree
Showing 26 changed files with 400 additions and 336 deletions.
151 changes: 82 additions & 69 deletions src/app/Components/ArtworkRail/ArtworkRail.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,12 @@ import {
ARTWORK_RAIL_CARD_MIN_WIDTH,
} from "app/Components/ArtworkRail/ArtworkRailCardImage"
import { BrowseMoreRailCard } from "app/Components/BrowseMoreRailCard"
import { PrefetchFlashList } from "app/Components/PrefetchFlashList"
import { PrefetchFlatList } from "app/Components/PrefetchFlatList"
import { RandomWidthPlaceholderText } from "app/utils/placeholders"
import { ArtworkActionTrackingProps } from "app/utils/track/ArtworkActions"
import React, { ReactElement, useCallback } from "react"
import { FlatList, ListRenderItem, ViewabilityConfig } from "react-native"
import React, { memo, ReactElement, useCallback } from "react"
import { FlatList, ListRenderItem, Platform, ViewabilityConfig } from "react-native"
import { isTablet } from "react-native-device-info"
import { graphql, useFragment } from "react-relay"

Expand All @@ -40,74 +41,86 @@ export interface ArtworkRailProps extends ArtworkActionTrackingProps {
viewabilityConfig?: ViewabilityConfig | undefined
}

export const ArtworkRail: React.FC<ArtworkRailProps> = ({
listRef,
onPress,
onEndReached,
onEndReachedThreshold,
ListHeaderComponent = <Spacer x={2} />,
ListFooterComponent = <Spacer x={2} />,
hideArtistName = false,
showPartnerName = true,
dark = false,
showSaveIcon = false,
viewabilityConfig,
onViewableItemsChanged,
onMorePress,
hideIncreasedInterestSignal,
hideCuratorsPickSignal,
...otherProps
}) => {
const artworks = useFragment(artworksFragment, otherProps.artworks)
export const ArtworkRail: React.FC<ArtworkRailProps> = memo(
({
listRef,
onPress,
onEndReached,
onEndReachedThreshold,
ListHeaderComponent = <Spacer x={2} />,
ListFooterComponent = <Spacer x={2} />,
hideArtistName = false,
showPartnerName = true,
dark = false,
showSaveIcon = false,
viewabilityConfig,
onViewableItemsChanged,
onMorePress,
hideIncreasedInterestSignal,
hideCuratorsPickSignal,
...otherProps
}) => {
const artworks = useFragment(artworksFragment, otherProps.artworks)

const renderItem: ListRenderItem<Artwork> = useCallback(
({ item, index }) => {
return (
<Box pr={2}>
<ArtworkRailCard
testID={`artwork-${item.slug}`}
artwork={item}
showPartnerName={showPartnerName}
hideArtistName={hideArtistName}
dark={dark}
onPress={() => {
onPress?.(item, index)
}}
showSaveIcon={showSaveIcon}
hideIncreasedInterestSignal={hideIncreasedInterestSignal}
hideCuratorsPickSignal={hideCuratorsPickSignal}
{...otherProps}
/>
</Box>
)
},
[hideArtistName, onPress, showPartnerName]
)
return (
<PrefetchFlatList
data={artworks}
horizontal
keyExtractor={(item) => item.internalID}
ListFooterComponent={
<>
{!!onMorePress && (
<BrowseMoreRailCard dark={dark} onPress={onMorePress} text="Browse All Artworks" />
)}
{ListFooterComponent}
</>
}
ListHeaderComponent={ListHeaderComponent}
listRef={listRef}
onEndReached={onEndReached}
onEndReachedThreshold={onEndReachedThreshold}
onViewableItemsChanged={onViewableItemsChanged}
prefetchUrlExtractor={(item) => item?.href || undefined}
renderItem={renderItem}
showsHorizontalScrollIndicator={false}
viewabilityConfig={viewabilityConfig}
/>
)
}
const renderItem: ListRenderItem<Artwork> = useCallback(
({ item, index }) => {
return (
<Box pr={2}>
<ArtworkRailCard
testID={`artwork-${item.slug}`}
artwork={item}
showPartnerName={showPartnerName}
hideArtistName={hideArtistName}
dark={dark}
onPress={() => {
onPress?.(item, index)
}}
showSaveIcon={showSaveIcon}
hideIncreasedInterestSignal={hideIncreasedInterestSignal}
hideCuratorsPickSignal={hideCuratorsPickSignal}
{...otherProps}
/>
</Box>
)
},
[hideArtistName, onPress, showPartnerName]
)

// On android we are using a flatlist to fix some image issues
// Context https://github.com/artsy/eigen/pull/11207
const Wrapper =
Platform.OS === "ios"
? (props: any) => (
<PrefetchFlashList estimatedItemSize={ARTWORK_RAIL_CARD_MIN_WIDTH} {...props} />
)
: PrefetchFlatList

return (
<Wrapper
data={artworks}
horizontal
keyExtractor={(item) => item.internalID}
ListFooterComponent={
<>
{!!onMorePress && (
<BrowseMoreRailCard dark={dark} onPress={onMorePress} text="Browse All Artworks" />
)}
{ListFooterComponent}
</>
}
ListHeaderComponent={ListHeaderComponent}
listRef={listRef}
onEndReached={onEndReached}
onEndReachedThreshold={onEndReachedThreshold}
onViewableItemsChanged={onViewableItemsChanged}
prefetchUrlExtractor={(item) => item?.href || undefined}
renderItem={renderItem}
showsHorizontalScrollIndicator={false}
viewabilityConfig={viewabilityConfig}
/>
)
}
)

const artworksFragment = graphql`
fragment ArtworkRail_artworks on Artwork @relay(plural: true) {
Expand Down
6 changes: 3 additions & 3 deletions src/app/Components/Containers/Inbox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import { PlaceholderBox, PlaceholderText } from "app/utils/placeholders"
import { renderWithPlaceholder } from "app/utils/renderWithPlaceholder"
import { track } from "app/utils/track"
import { ActionNames, ActionTypes } from "app/utils/track/schema"
import React from "react"
import React, { memo } from "react"
import { EmitterSubscription } from "react-native"
import { createRefetchContainer, graphql, QueryRenderer, RelayRefetchProp } from "react-relay"

Expand Down Expand Up @@ -150,7 +150,7 @@ interface InboxQueryRendererProps extends StackScreenProps<any> {
isVisible?: boolean
}

export const InboxQueryRenderer: React.FC<InboxQueryRendererProps> = (props) => {
export const InboxQueryRenderer: React.FC<InboxQueryRendererProps> = memo((props) => {
return (
<Screen>
<QueryRenderer<InboxQuery>
Expand All @@ -167,7 +167,7 @@ export const InboxQueryRenderer: React.FC<InboxQueryRendererProps> = (props) =>
/>
</Screen>
)
}
})

export const InboxPlaceholder = () => {
return (
Expand Down
40 changes: 20 additions & 20 deletions src/app/Navigation/AuthenticatedRoutes/StackNavigator.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { RetryErrorBoundary } from "app/Components/RetryErrorBoundary"
import { AuthenticatedRoutesParams } from "app/Navigation/AuthenticatedRoutes/Tabs"
import { isModalScreen } from "app/Navigation/Utils/isModalScreen"
import { goBack } from "app/system/navigation/navigate"
import { memo } from "react"
import { Platform } from "react-native"
import { isTablet } from "react-native-device-info"

Expand Down Expand Up @@ -84,24 +85,23 @@ export interface ScreenWrapperProps {
readonly hidesBottomTabs?: boolean
}

export const ScreenWrapper: React.FC<ScreenWrapperProps> = ({
hidesBottomTabs = false,
children,
}) => {
// We don't have the bottom tabs context on modal screens
// eslint-disable-next-line react-hooks/rules-of-hooks
const tabBarHeight = hidesBottomTabs ? 0 : useBottomTabBarHeight()
export const ScreenWrapper: React.FC<ScreenWrapperProps> = memo(
({ hidesBottomTabs = false, children }) => {
// We don't have the bottom tabs context on modal screens
// eslint-disable-next-line react-hooks/rules-of-hooks
const tabBarHeight = hidesBottomTabs ? 0 : useBottomTabBarHeight()

return (
<RetryErrorBoundary>
<Flex
flex={1}
style={{
paddingBottom: hidesBottomTabs ? 0 : tabBarHeight,
}}
>
{children}
</Flex>
</RetryErrorBoundary>
)
}
return (
<RetryErrorBoundary>
<Flex
flex={1}
style={{
paddingBottom: hidesBottomTabs ? 0 : tabBarHeight,
}}
>
{children}
</Flex>
</RetryErrorBoundary>
)
}
)
4 changes: 3 additions & 1 deletion src/app/Scenes/Activity/Activity.tests.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@ describe("ActivityScreen", () => {
resolveHeaderLastOperation({})
})

await waitForElementToBeRemoved(() => screen.queryByTestId("activity-content-placeholder"))
await waitForElementToBeRemoved(() => screen.queryByTestId("activity-content-placeholder"), {
timeout: 15000,
})

expect(screen.getByText("Notification One")).toBeOnTheScreen()
expect(screen.getByText("Notification Two")).toBeOnTheScreen()
Expand Down
4 changes: 3 additions & 1 deletion src/app/Scenes/Articles/Articles.tests.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,9 @@ describe("Articles", () => {
}),
})

await waitForElementToBeRemoved(() => screen.queryByTestId("articles-screen-placeholder"))
await waitForElementToBeRemoved(() => screen.queryByTestId("articles-screen-placeholder"), {
timeout: 15000,
})

expect(screen.getByText("Sebastián Meltz-Collazo")).toBeTruthy()
expect(
Expand Down
9 changes: 5 additions & 4 deletions src/app/Scenes/HomeView/HomeView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ import { usePrefetch } from "app/utils/queryPrefetching"
import { requestPushNotificationsPermission } from "app/utils/requestPushNotificationsPermission"
import { useMaybePromptForReview } from "app/utils/useMaybePromptForReview"
import { useSwitchStatusBarStyle } from "app/utils/useStatusBarStyle"
import { RefObject, Suspense, useCallback, useEffect, useState } from "react"
import { memo, RefObject, Suspense, useCallback, useEffect, useState } from "react"
import { FlatList, Linking, RefreshControl } from "react-native"
import { fetchQuery, graphql, useLazyLoadQuery, usePaginationFragment } from "react-relay"

Expand All @@ -41,7 +41,7 @@ export const homeViewScreenQueryVariables = () => ({
count: NUMBER_OF_SECTIONS_TO_LOAD,
})

export const HomeView: React.FC = () => {
export const HomeView: React.FC = memo(() => {
const flashlistRef = useBottomTabsScrollToTop()
const [isRefreshing, setIsRefreshing] = useState(false)

Expand Down Expand Up @@ -180,12 +180,13 @@ export const HomeView: React.FC = () => {
refreshControl={<RefreshControl refreshing={isRefreshing} onRefresh={handleRefresh} />}
onEndReachedThreshold={2}
stickyHeaderIndices={[0]}
windowSize={15}
/>
{!!data?.me && <EmailConfirmationBannerFragmentContainer me={data.me} />}
</Screen.Body>
</Screen>
)
}
})

const HomeViewScreenComponent: React.FC = () => {
const artQuizState = GlobalStore.useAppState((state) => state.auth.onboardingArtQuizState)
Expand Down Expand Up @@ -234,7 +235,7 @@ const HomeViewScreenComponent: React.FC = () => {
)
}

export const HomeViewScreen = Sentry.withProfiler(HomeViewScreenComponent)
export const HomeViewScreen = memo(Sentry.withProfiler(HomeViewScreenComponent))

const sectionsFragment = graphql`
fragment HomeViewSectionsConnection_viewer on Viewer
Expand Down
31 changes: 18 additions & 13 deletions src/app/Scenes/HomeView/Sections/HomeViewSectionActivity.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import { extractNodes } from "app/utils/extractNodes"
import { NoFallback, withSuspense } from "app/utils/hooks/withSuspense"
import { useMemoizedRandom } from "app/utils/placeholders"
import { times } from "lodash"
import { memo } from "react"
import { FlatList } from "react-native"
import { graphql, useFragment, useLazyLoadQuery } from "react-relay"

Expand Down Expand Up @@ -192,18 +193,22 @@ const HomeViewSectionActivityPlaceholder: React.FC<FlexProps> = (flexProps) => {
)
}

export const HomeViewSectionActivityQueryRenderer: React.FC<SectionSharedProps> = withSuspense({
Component: ({ sectionID, index, ...flexProps }) => {
const data = useLazyLoadQuery<HomeViewSectionActivityQuery>(homeViewSectionActivityQuery, {
id: sectionID,
})
export const HomeViewSectionActivityQueryRenderer: React.FC<SectionSharedProps> = memo(
withSuspense({
Component: ({ sectionID, index, ...flexProps }) => {
const data = useLazyLoadQuery<HomeViewSectionActivityQuery>(homeViewSectionActivityQuery, {
id: sectionID,
})

if (!data.homeView.section) {
return null
}
if (!data.homeView.section) {
return null
}

return <HomeViewSectionActivity section={data.homeView.section} index={index} {...flexProps} />
},
LoadingFallback: HomeViewSectionActivityPlaceholder,
ErrorFallback: NoFallback,
})
return (
<HomeViewSectionActivity section={data.homeView.section} index={index} {...flexProps} />
)
},
LoadingFallback: HomeViewSectionActivityPlaceholder,
ErrorFallback: NoFallback,
})
)
Loading

0 comments on commit 01b0e2f

Please sign in to comment.