Skip to content

Commit

Permalink
feat: add preloaded query to collections by category screen
Browse files Browse the repository at this point in the history
  • Loading branch information
araujobarret committed Nov 21, 2024
1 parent 714f5df commit 6334ce2
Show file tree
Hide file tree
Showing 7 changed files with 180 additions and 133 deletions.
48 changes: 15 additions & 33 deletions src/app/Scenes/CollectionsByCategory/Body.tsx
Original file line number Diff line number Diff line change
@@ -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,
Expand All @@ -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<BodyProps> = ({ viewer }) => {
const data = useFragment(fragment, viewer)
export const Body: React.FC<BodyProps> = ({ marketingCollections }) => {
const data = useFragment(fragment, marketingCollections)
const { params } = useRoute<CollectionsByCategoriesRouteProp>()
const category = params.category

if (!data?.marketingCollections) {
if (!data) {
return null
}

Expand All @@ -36,20 +36,20 @@ export const Body: React.FC<BodyProps> = ({ viewer }) => {
</Text>
<Text px={2}>Explore collections with {category}</Text>
{/* TODO: fix typings broken by some unknown reason here, prob related to @plural */}
<CollectionsChips marketingCollections={data.marketingCollections as any} />
<CollectionsChips marketingCollections={marketingCollections as any} />
</Flex>

<Separator borderColor="black10" />

<FlashList
estimatedItemSize={ESTIMATED_ITEM_SIZE}
data={data.marketingCollections}
data={data}
keyExtractor={(item) => `artwork_rail_${item?.slug}`}
renderItem={({ item, index }) => {
return (
<CollectionRailWithSuspense
slug={item?.slug ?? ""}
lastElement={index === data.marketingCollections.length - 1}
lastElement={index === data.length - 1}
/>
)
}}
Expand All @@ -61,13 +61,9 @@ export const Body: React.FC<BodyProps> = ({ 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)
}
`

Expand All @@ -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<CollectionsByCategoriesRouteProp>()
const data = useLazyLoadQuery<BodyCollectionsByCategoryQuery>(
query,
{
category: params.entityID,
},
{ fetchPolicy: "store-and-network" }
)
const data = usePreloadedQuery(marketingCollectionsQuery, params.queryRef)

if (!data.viewer) {
return <BodyPlaceholder />
}

return <Body viewer={data.viewer} />
return <Body marketingCollections={data.viewer.marketingCollections} />
},
LoadingFallback: BodyPlaceholder,
ErrorFallback: NoFallback,
Expand Down
36 changes: 11 additions & 25 deletions src/app/Scenes/CollectionsByCategory/CollectionRail.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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 {
Expand Down Expand Up @@ -128,8 +126,8 @@ export const CollectionRailPlaceholder: FC<Partial<CollectionRailProps>> = ({ 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
Expand All @@ -146,26 +144,14 @@ export const CollectionRailWithSuspense = withSuspense<
CollectionRailWithSuspenseProps & Partial<CollectionRailProps>
>({
Component: ({ slug, lastElement }) => {
const { height } = useScreenDimensions()
const [isVisible, setIsVisible] = useState(false)
const data = useLazyLoadQuery<CollectionRailCollectionsByCategoryQuery>(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 (
<ElementInView onVisible={handleOnVisible} visibilityMargin={-height}>
<CollectionRailPlaceholder lastElement={lastElement} />
</ElementInView>
)
const data = useLazyLoadQuery<CollectionRailCollectionsByCategoryQuery>(
query,
{ slug },
{ fetchPolicy: "store-and-network" }
)

if (!data?.marketingCollection) {
return <CollectionRailPlaceholder lastElement={lastElement} />
}

return <CollectionRail collection={data.marketingCollection} lastElement={lastElement} />
Expand Down
Original file line number Diff line number Diff line change
@@ -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<HomeViewSectionCardsCardQuery>
}
}

Expand Down
6 changes: 4 additions & 2 deletions src/app/Scenes/CollectionsByCategory/__tests__/Body.tests.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,13 @@ jest.mock("app/Scenes/CollectionsByCategory/CollectionRail", () => ({

describe("Body", () => {
const { renderWithRelay } = setupTestWrapper<BodyHomeViewSectionCardsTestQuery>({
Component: Body,
Component: ({ viewer }) => <Body marketingCollections={viewer.marketingCollections} />,
query: graphql`
query BodyHomeViewSectionCardsTestQuery {
viewer @required(action: NONE) {
...BodyCollectionsByCategory_viewer @arguments(category: "category")
marketingCollections(first: 2) {
...BodyCollectionsByCategory_marketingCollections
}
}
}
`,
Expand Down
121 changes: 121 additions & 0 deletions src/app/Scenes/HomeView/Components/HomeViewSectionCardsCard.tsx
Original file line number Diff line number Diff line change
@@ -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<HomeViewSectionCardsCardProps> = ({
card: _card,
section: _section,
index,
imageWidth,
}) => {
const space = useSpace()
const card = useFragment(cardFragment, _card)
const section = useFragment(sectionFragment, _section)
const [queryRef, loadQuery] =
useQueryLoader<HomeViewSectionCardsCardQuery>(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 <HomeViewSectionCardsCardPlaceholder imageWidth={imageWidth} index={index} />
}

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 (
<Touchable onPress={handleCardPress}>
<Flex borderRadius={5} overflow="hidden">
<Image src={card.image?.url as string} width={imageWidth} aspectRatio={IMAGE_RATIO} />

<Flex
position="absolute"
top={space(1)}
left={space(1)}
backgroundColor="white100"
px={0.5}
>
<Text variant="md">{card.title}</Text>
</Flex>
</Flex>
</Touchable>
)
}

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<HomeViewSectionCardsCardProps, "imageWidth" | "index">
> = ({ imageWidth, index }) => {
return (
<Flex key={index} borderRadius={5}>
<SkeletonBox width={imageWidth} height={imageWidth / IMAGE_RATIO} />
</Flex>
)
}
Loading

0 comments on commit 6334ce2

Please sign in to comment.