From 6334ce2794f1031281f46e3bb159161d84eded85 Mon Sep 17 00:00:00 2001 From: araujobarret Date: Mon, 18 Nov 2024 17:42:57 +0100 Subject: [PATCH] feat: add preloaded query to collections by category screen --- src/app/Scenes/CollectionsByCategory/Body.tsx | 48 +++---- .../CollectionsByCategory/CollectionRail.tsx | 36 ++---- .../CollectionsByCategory.tsx | 3 + .../__tests__/Body.tests.tsx | 6 +- .../Components/HomeViewSectionCardsCard.tsx | 121 ++++++++++++++++++ .../Sections/HomeViewSectionCards.tsx | 88 +++---------- .../__tests__/HomeViewSectionCards.tests.tsx | 11 +- 7 files changed, 180 insertions(+), 133 deletions(-) create mode 100644 src/app/Scenes/HomeView/Components/HomeViewSectionCardsCard.tsx diff --git a/src/app/Scenes/CollectionsByCategory/Body.tsx b/src/app/Scenes/CollectionsByCategory/Body.tsx index a1b9260a27b..3c385b67f9e 100644 --- a/src/app/Scenes/CollectionsByCategory/Body.tsx +++ b/src/app/Scenes/CollectionsByCategory/Body.tsx @@ -1,8 +1,7 @@ import { Flex, Separator, Skeleton, SkeletonText, Text } from "@artsy/palette-mobile" import { useRoute } from "@react-navigation/native" import { FlashList } from "@shopify/flash-list" -import { BodyCollectionsByCategoryQuery } from "__generated__/BodyCollectionsByCategoryQuery.graphql" -import { BodyCollectionsByCategory_viewer$key } from "__generated__/BodyCollectionsByCategory_viewer.graphql" +import { BodyCollectionsByCategory_marketingCollections$key } from "__generated__/BodyCollectionsByCategory_marketingCollections.graphql" import { CollectionRailPlaceholder, CollectionRailWithSuspense, @@ -12,19 +11,20 @@ import { CollectionsChips, CollectionsChipsPlaceholder, } from "app/Scenes/CollectionsByCategory/CollectionsChips" +import { marketingCollectionsQuery } from "app/Scenes/HomeView/Components/HomeViewSectionCardsCard" import { NoFallback, withSuspense } from "app/utils/hooks/withSuspense" -import { graphql, useLazyLoadQuery, useFragment } from "react-relay" +import { graphql, useFragment, usePreloadedQuery } from "react-relay" interface BodyProps { - viewer: BodyCollectionsByCategory_viewer$key + marketingCollections: BodyCollectionsByCategory_marketingCollections$key } -export const Body: React.FC = ({ viewer }) => { - const data = useFragment(fragment, viewer) +export const Body: React.FC = ({ marketingCollections }) => { + const data = useFragment(fragment, marketingCollections) const { params } = useRoute() const category = params.category - if (!data?.marketingCollections) { + if (!data) { return null } @@ -36,20 +36,20 @@ export const Body: React.FC = ({ viewer }) => { Explore collections with {category} {/* TODO: fix typings broken by some unknown reason here, prob related to @plural */} - + `artwork_rail_${item?.slug}`} renderItem={({ item, index }) => { return ( ) }} @@ -61,13 +61,9 @@ export const Body: React.FC = ({ viewer }) => { const ESTIMATED_ITEM_SIZE = 390 const fragment = graphql` - fragment BodyCollectionsByCategory_viewer on Viewer - @argumentDefinitions(category: { type: "String" }) { - marketingCollections(category: $category, sort: CURATED, first: 20) { - ...CollectionsChips_marketingCollections - - slug @required(action: NONE) - } + fragment BodyCollectionsByCategory_marketingCollections on MarketingCollection + @relay(plural: true) { + slug @required(action: NONE) } ` @@ -91,30 +87,16 @@ const BodyPlaceholder: React.FC = () => { ) } -const query = graphql` - query BodyCollectionsByCategoryQuery($category: String!) @cacheable { - viewer { - ...BodyCollectionsByCategory_viewer @arguments(category: $category) - } - } -` - export const BodyWithSuspense = withSuspense({ Component: () => { const { params } = useRoute() - const data = useLazyLoadQuery( - query, - { - category: params.entityID, - }, - { fetchPolicy: "store-and-network" } - ) + const data = usePreloadedQuery(marketingCollectionsQuery, params.queryRef) if (!data.viewer) { return } - return + return }, LoadingFallback: BodyPlaceholder, ErrorFallback: NoFallback, diff --git a/src/app/Scenes/CollectionsByCategory/CollectionRail.tsx b/src/app/Scenes/CollectionsByCategory/CollectionRail.tsx index 00a7a9f5461..c6f75cfab32 100644 --- a/src/app/Scenes/CollectionsByCategory/CollectionRail.tsx +++ b/src/app/Scenes/CollectionsByCategory/CollectionRail.tsx @@ -5,7 +5,6 @@ import { Skeleton, SkeletonText, Spacer, - useScreenDimensions, } from "@artsy/palette-mobile" import { ArtworkRail_artworks$data } from "__generated__/ArtworkRail_artworks.graphql" import { CollectionRailCollectionsByCategoryQuery } from "__generated__/CollectionRailCollectionsByCategoryQuery.graphql" @@ -14,10 +13,9 @@ import { ArtworkRail, ArtworkRailPlaceholder } from "app/Components/ArtworkRail/ import { SectionTitle } from "app/Components/SectionTitle" import { useCollectionByCategoryTracking } from "app/Scenes/CollectionsByCategory/hooks/useCollectionByCategoryTracking" import { navigate } from "app/system/navigation/navigate" -import { ElementInView } from "app/utils/ElementInView" import { extractNodes } from "app/utils/extractNodes" import { withSuspense, NoFallback } from "app/utils/hooks/withSuspense" -import { FC, useState } from "react" +import { FC } from "react" import { graphql, useFragment, useLazyLoadQuery } from "react-relay" interface CollectionRailProps { @@ -128,8 +126,8 @@ export const CollectionRailPlaceholder: FC> = ({ la } const query = graphql` - query CollectionRailCollectionsByCategoryQuery($slug: String!, $isVisible: Boolean!) { - marketingCollection(slug: $slug) @include(if: $isVisible) { + query CollectionRailCollectionsByCategoryQuery($slug: String!) { + marketingCollection(slug: $slug) { ...CollectionRail_marketingCollection title @@ -146,26 +144,14 @@ export const CollectionRailWithSuspense = withSuspense< CollectionRailWithSuspenseProps & Partial >({ Component: ({ slug, lastElement }) => { - const { height } = useScreenDimensions() - const [isVisible, setIsVisible] = useState(false) - const data = useLazyLoadQuery(query, { - slug, - isVisible, - }) - - const handleOnVisible = () => { - if (!isVisible) { - setIsVisible(true) - } - } - - if (!data?.marketingCollection || !isVisible) { - // We don't need to overfetch all rails at once, fetch as they become closer to be visible - return ( - - - - ) + const data = useLazyLoadQuery( + query, + { slug }, + { fetchPolicy: "store-and-network" } + ) + + if (!data?.marketingCollection) { + return } return diff --git a/src/app/Scenes/CollectionsByCategory/CollectionsByCategory.tsx b/src/app/Scenes/CollectionsByCategory/CollectionsByCategory.tsx index b3ff42e5318..33a4759705c 100644 --- a/src/app/Scenes/CollectionsByCategory/CollectionsByCategory.tsx +++ b/src/app/Scenes/CollectionsByCategory/CollectionsByCategory.tsx @@ -1,18 +1,21 @@ import { OwnerType } from "@artsy/cohesion" import { Flex, Screen } from "@artsy/palette-mobile" import { RouteProp, useRoute } from "@react-navigation/native" +import { HomeViewSectionCardsCardQuery } from "__generated__/HomeViewSectionCardsCardQuery.graphql" import { BodyWithSuspense } from "app/Scenes/CollectionsByCategory/Body" import { FooterWithSuspense } from "app/Scenes/CollectionsByCategory/Footer" import { goBack } from "app/system/navigation/navigate" import { ProvideScreenTrackingWithCohesionSchema } from "app/utils/track" import { screen } from "app/utils/track/helpers" import { FC } from "react" +import { PreloadedQuery } from "react-relay" type CollectionsByCategoriesNavigationRoutes = { collections: { category: string entityID: string homeViewSectionId: string + queryRef: PreloadedQuery } } diff --git a/src/app/Scenes/CollectionsByCategory/__tests__/Body.tests.tsx b/src/app/Scenes/CollectionsByCategory/__tests__/Body.tests.tsx index c63155b1da1..756e998da32 100644 --- a/src/app/Scenes/CollectionsByCategory/__tests__/Body.tests.tsx +++ b/src/app/Scenes/CollectionsByCategory/__tests__/Body.tests.tsx @@ -17,11 +17,13 @@ jest.mock("app/Scenes/CollectionsByCategory/CollectionRail", () => ({ describe("Body", () => { const { renderWithRelay } = setupTestWrapper({ - Component: Body, + Component: ({ viewer }) => , query: graphql` query BodyHomeViewSectionCardsTestQuery { viewer @required(action: NONE) { - ...BodyCollectionsByCategory_viewer @arguments(category: "category") + marketingCollections(first: 2) { + ...BodyCollectionsByCategory_marketingCollections + } } } `, diff --git a/src/app/Scenes/HomeView/Components/HomeViewSectionCardsCard.tsx b/src/app/Scenes/HomeView/Components/HomeViewSectionCardsCard.tsx new file mode 100644 index 00000000000..6137a69c473 --- /dev/null +++ b/src/app/Scenes/HomeView/Components/HomeViewSectionCardsCard.tsx @@ -0,0 +1,121 @@ +import { ContextModule, OwnerType, ScreenOwnerType } from "@artsy/cohesion" +import { Flex, Image, SkeletonBox, Text, Touchable, useSpace } from "@artsy/palette-mobile" +import { HomeViewSectionCardsCardQuery } from "__generated__/HomeViewSectionCardsCardQuery.graphql" +import { HomeViewSectionCardsCard_card$key } from "__generated__/HomeViewSectionCardsCard_card.graphql" +import { HomeViewSectionCardsCard_section$key } from "__generated__/HomeViewSectionCardsCard_section.graphql" +import { useHomeViewTracking } from "app/Scenes/HomeView/hooks/useHomeViewTracking" +import { navigate } from "app/system/navigation/navigate" +import { FC, useEffect } from "react" +import { graphql, useFragment, useQueryLoader } from "react-relay" + +const IMAGE_RATIO = 0.85 + +interface HomeViewSectionCardsCardProps { + card: HomeViewSectionCardsCard_card$key + section: HomeViewSectionCardsCard_section$key + index: number + imageWidth: number +} + +export const HomeViewSectionCardsCard: FC = ({ + card: _card, + section: _section, + index, + imageWidth, +}) => { + const space = useSpace() + const card = useFragment(cardFragment, _card) + const section = useFragment(sectionFragment, _section) + const [queryRef, loadQuery] = + useQueryLoader(marketingCollectionsQuery) + const tracking = useHomeViewTracking() + + useEffect(() => { + if (!queryRef && card?.entityID) { + loadQuery({ category: card.entityID }, { fetchPolicy: "store-or-network" }) + } + }, [queryRef, card?.entityID]) + + if (!card || !section) { + return null + } + + if (!queryRef) { + return + } + + const handleCardPress = () => { + const href = + card?.entityType === OwnerType.collectionsCategory + ? `/collections-by-category/${card.title}?homeViewSectionId=${section.internalID}&entityID=${card.entityID}` + : card?.href + + if (href) { + tracking.tappedCardGroup( + card.entityID, + card.entityType as ScreenOwnerType, + href, + section.contextModule as ContextModule, + index + ) + navigate(href, { passProps: { queryRef } }) + } + } + + return ( + + + + + + {card.title} + + + + ) +} + +const cardFragment = graphql` + fragment HomeViewSectionCardsCard_card on HomeViewCard { + entityID @required(action: NONE) + title @required(action: NONE) + entityType + href + image { + url + } + } +` +const sectionFragment = graphql` + fragment HomeViewSectionCardsCard_section on HomeViewSectionCards { + internalID + contextModule + } +` + +export const marketingCollectionsQuery = graphql` + query HomeViewSectionCardsCardQuery($category: String!) @cacheable { + viewer { + marketingCollections(category: $category, sort: CURATED, first: 20) { + ...BodyCollectionsByCategory_marketingCollections + ...CollectionsChips_marketingCollections + } + } + } +` + +export const HomeViewSectionCardsCardPlaceholder: FC< + Pick +> = ({ imageWidth, index }) => { + return ( + + + + ) +} diff --git a/src/app/Scenes/HomeView/Sections/HomeViewSectionCards.tsx b/src/app/Scenes/HomeView/Sections/HomeViewSectionCards.tsx index e2c68c13dfa..bf0caa4d2a9 100644 --- a/src/app/Scenes/HomeView/Sections/HomeViewSectionCards.tsx +++ b/src/app/Scenes/HomeView/Sections/HomeViewSectionCards.tsx @@ -1,21 +1,20 @@ -import { ContextModule, OwnerType, ScreenOwnerType } from "@artsy/cohesion" +import { ContextModule } from "@artsy/cohesion" import { Flex, - Image, Skeleton, - SkeletonBox, SkeletonText, Text, - Touchable, useScreenDimensions, useSpace, } from "@artsy/palette-mobile" import { HomeViewSectionCardsQuery } from "__generated__/HomeViewSectionCardsQuery.graphql" import { HomeViewSectionCards_section$key } from "__generated__/HomeViewSectionCards_section.graphql" +import { + HomeViewSectionCardsCard, + HomeViewSectionCardsCardPlaceholder, +} from "app/Scenes/HomeView/Components/HomeViewSectionCardsCard" import { HomeViewSectionSentinel } from "app/Scenes/HomeView/Components/HomeViewSectionSentinel" import { SectionSharedProps } from "app/Scenes/HomeView/Sections/Section" -import { useHomeViewTracking } from "app/Scenes/HomeView/hooks/useHomeViewTracking" -import { navigate } from "app/system/navigation/navigate" import { extractNodes } from "app/utils/extractNodes" import { useFeatureFlag } from "app/utils/hooks/useFeatureFlag" import { NoFallback, withSuspense } from "app/utils/hooks/withSuspense" @@ -25,19 +24,14 @@ import { graphql, useFragment, useLazyLoadQuery } from "react-relay" interface HomeViewSectionCardsProps { section: HomeViewSectionCards_section$key - homeViewSectionId: string index: number } -const IMAGE_RATIO = 0.85 - export const HomeViewSectionCards: React.FC = ({ section: _section, - homeViewSectionId, index, }) => { const { width } = useScreenDimensions() - const tracking = useHomeViewTracking() const space = useSpace() const section = useFragment(fragment, _section) @@ -51,50 +45,19 @@ export const HomeViewSectionCards: React.FC = ({ const imageWidth = width / columns - space(2) - imageColumnGaps const cards = extractNodes(section.cardsConnection) - const handleCardPress = (card: (typeof cards)[number], index: number) => { - const href = - card.entityType === OwnerType.collectionsCategory - ? `/collections-by-category/${card.title}?homeViewSectionId=${homeViewSectionId}&entityID=${card.entityID}` - : card.href - - if (href) { - tracking.tappedCardGroup( - card.entityID, - card.entityType as ScreenOwnerType, - href, - section.contextModule as ContextModule, - index - ) - navigate(href) - } - } - return ( {section.component?.title} {cards.map((card, index) => { - const src = card.image?.url - if (!src) { - return null - } - return ( - handleCardPress(card, index)}> - - - - - {card.title} - - - + ) })} @@ -109,7 +72,8 @@ export const HomeViewSectionCards: React.FC = ({ const fragment = graphql` fragment HomeViewSectionCards_section on HomeViewSectionCards { - internalID + ...HomeViewSectionCardsCard_section + component { title } @@ -117,13 +81,7 @@ const fragment = graphql` cardsConnection(first: 6) { edges { node { - entityID @required(action: NONE) - title @required(action: NONE) - entityType - href - image { - url - } + ...HomeViewSectionCardsCard_card } } } @@ -155,9 +113,11 @@ const HomeViewCardsPlaceholder: React.FC = () => { <> {Array.from({ length: 6 }).map((_, index) => ( - - - + ))} @@ -177,13 +137,7 @@ export const HomeViewSectionCardsQueryRenderer = withSuspense< return null } - return ( - - ) + return }, LoadingFallback: HomeViewCardsPlaceholder, ErrorFallback: NoFallback, diff --git a/src/app/Scenes/HomeView/Sections/__tests__/HomeViewSectionCards.tests.tsx b/src/app/Scenes/HomeView/Sections/__tests__/HomeViewSectionCards.tests.tsx index 6a5b4ba41a2..2a92583551b 100644 --- a/src/app/Scenes/HomeView/Sections/__tests__/HomeViewSectionCards.tests.tsx +++ b/src/app/Scenes/HomeView/Sections/__tests__/HomeViewSectionCards.tests.tsx @@ -11,11 +11,7 @@ describe("HomeViewSectionCards", () => { const { renderWithRelay } = setupTestWrapper({ Component: (props) => ( - + ), query: graphql` @@ -98,6 +94,9 @@ describe("HomeViewSectionCards", () => { }) ) - expect(navigate).toHaveBeenCalledWith(expect.stringMatching("/collections-by-category/Card 1")) + expect(navigate).toHaveBeenCalledWith( + expect.stringMatching("/collections-by-category/Card 1"), + expect.any(Object) + ) }) })