Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(DIA-1062): refetch infinite discovery #11395

Merged
merged 3 commits into from
Jan 16, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
90 changes: 2 additions & 88 deletions src/app/Components/FancySwiper/__tests__/FancySwiper.tests.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { fireEvent, screen, waitFor } from "@testing-library/react-native"
import { waitFor } from "@testing-library/react-native"
import { FancySwiper } from "app/Components/FancySwiper/FancySwiper"
import { Card } from "app/Components/FancySwiper/FancySwiperCard"
import { swipeLeft, swipeRight } from "app/Components/FancySwiper/__tests__/utils"
import { renderWithWrappers } from "app/utils/tests/renderWithWrappers"

describe("FancySwiper", () => {
Expand All @@ -19,93 +20,6 @@ describe("FancySwiper", () => {
})
})

const swipeLeft = () => {
swipe(-100)
}

const swipeRight = () => {
swipe(100)
}

const swipe = (moveX: number) => {
const topCard = screen.getByTestId("top-fancy-swiper-card")

const startX = 0
const startY = 0
const startTimeStamp = Date.now()
const moveTimeStamp = Date.now()
const releaseTimeStamp = Date.now()

fireEvent(topCard, "responderStart", {
touchHistory: {
indexOfSingleActiveTouch: 1,
mostRecentTimeStamp: startTimeStamp,
numberActiveTouches: 1,
touchBank: [
undefined,
{
currentPageX: startX,
currentPageY: startY,
currentTimeStamp: startTimeStamp,
previousPageX: startX,
previousPageY: startY,
previousTimeStamp: startTimeStamp,
startPageX: startX,
startPageY: startY,
startTimeStamp: startTimeStamp,
touchActive: true,
},
],
},
})

fireEvent(topCard, "responderMove", {
touchHistory: {
indexOfSingleActiveTouch: 1,
mostRecentTimeStamp: moveTimeStamp,
numberActiveTouches: 1,
touchBank: [
undefined,
{
currentPageX: moveX,
currentPageY: startY,
currentTimeStamp: moveTimeStamp,
previousPageX: startX,
previousPageY: startY,
previousTimeStamp: startTimeStamp,
startPageX: startX,
startPageY: startY,
startTimeStamp: startTimeStamp,
touchActive: true,
},
],
},
})

fireEvent(topCard, "responderRelease", {
touchHistory: {
indexOfSingleActiveTouch: 1,
mostRecentTimeStamp: releaseTimeStamp,
numberActiveTouches: 0,
touchBank: [
undefined,
{
currentPageX: moveX,
currentPageY: startY,
currentTimeStamp: releaseTimeStamp,
previousPageX: moveX,
previousPageY: startY,
previousTimeStamp: moveTimeStamp,
startPageX: startX,
startPageY: startY,
startTimeStamp: startTimeStamp,
touchActive: false,
},
],
},
})
}

const cards: Card[] = [
{
id: "1",
Expand Down
88 changes: 88 additions & 0 deletions src/app/Components/FancySwiper/__tests__/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import { fireEvent, screen } from "@testing-library/react-native"

export const swipeLeft = () => {
swipe(-100)
}

export const swipeRight = () => {
swipe(100)
}

const swipe = (moveX: number) => {
const topCard = screen.getByTestId("top-fancy-swiper-card")

const startX = 0
const startY = 0
const startTimeStamp = Date.now()
const moveTimeStamp = Date.now()
const releaseTimeStamp = Date.now()

fireEvent(topCard, "responderStart", {
touchHistory: {
indexOfSingleActiveTouch: 1,
mostRecentTimeStamp: startTimeStamp,
numberActiveTouches: 1,
touchBank: [
undefined,
{
currentPageX: startX,
currentPageY: startY,
currentTimeStamp: startTimeStamp,
previousPageX: startX,
previousPageY: startY,
previousTimeStamp: startTimeStamp,
startPageX: startX,
startPageY: startY,
startTimeStamp: startTimeStamp,
touchActive: true,
},
],
},
})

fireEvent(topCard, "responderMove", {
touchHistory: {
indexOfSingleActiveTouch: 1,
mostRecentTimeStamp: moveTimeStamp,
numberActiveTouches: 1,
touchBank: [
undefined,
{
currentPageX: moveX,
currentPageY: startY,
currentTimeStamp: moveTimeStamp,
previousPageX: startX,
previousPageY: startY,
previousTimeStamp: startTimeStamp,
startPageX: startX,
startPageY: startY,
startTimeStamp: startTimeStamp,
touchActive: true,
},
],
},
})

fireEvent(topCard, "responderRelease", {
touchHistory: {
indexOfSingleActiveTouch: 1,
mostRecentTimeStamp: releaseTimeStamp,
numberActiveTouches: 0,
touchBank: [
undefined,
{
currentPageX: moveX,
currentPageY: startY,
currentTimeStamp: releaseTimeStamp,
previousPageX: moveX,
previousPageY: startY,
previousTimeStamp: moveTimeStamp,
startPageX: startX,
startPageY: startY,
startTimeStamp: startTimeStamp,
touchActive: false,
},
],
},
})
}
117 changes: 84 additions & 33 deletions src/app/Scenes/InfiniteDiscovery/InfiniteDiscovery.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,25 +10,51 @@ import {
useScreenDimensions,
useTheme,
} from "@artsy/palette-mobile"
import { InfiniteDiscoveryRefetchQuery } from "__generated__/InfiniteDiscoveryRefetchQuery.graphql"
import { InfiniteDiscovery_Fragment$key } from "__generated__/InfiniteDiscovery_Fragment.graphql"
import { FancySwiper } from "app/Components/FancySwiper/FancySwiper"
import { InfiniteDiscoveryBottomSheet } from "app/Scenes/InfiniteDiscovery/Components/InfiniteDiscoveryBottomSheet"
import { goBack } from "app/system/navigation/navigate"
import { extractNodes } from "app/utils/extractNodes"
import { NoFallback, withSuspense } from "app/utils/hooks/withSuspense"
import { NoFallback, SpinnerFallback, withSuspense } from "app/utils/hooks/withSuspense"
import { sizeToFit } from "app/utils/useSizeToFit"
import { useMemo, useState } from "react"
import { graphql, useLazyLoadQuery } from "react-relay"
import type { InfiniteDiscoveryQuery } from "__generated__/InfiniteDiscoveryQuery.graphql"
import { useEffect, useState } from "react"
import { graphql, useLazyLoadQuery, useRefetchableFragment } from "react-relay"
import type {
InfiniteDiscoveryQuery,
InfiniteDiscoveryQuery$data,
} from "__generated__/InfiniteDiscoveryQuery.graphql"
import type { Card } from "app/Components/FancySwiper/FancySwiperCard"

export const InfiniteDiscovery: React.FC = () => {
const data = useLazyLoadQuery<InfiniteDiscoveryQuery>(infiniteDiscoveryQuery, {})
const artworks = useMemo(() => extractNodes(data.marketingCollection?.artworksConnection), [data])
interface InfiniteDiscoveryProps {
artworks: InfiniteDiscoveryQuery$data
}

export const InfiniteDiscovery: React.FC<InfiniteDiscoveryProps> = ({ artworks: _artworks }) => {
const [data, refetch] = useRefetchableFragment<
InfiniteDiscoveryRefetchQuery,
InfiniteDiscovery_Fragment$key
>(infiniteDiscoveryFragment, _artworks)

const REFETCH_BUFFER = 2

const { color } = useTheme()
const { width: screenWidth } = useScreenDimensions()

const [index, setIndex] = useState(0)
const [artworks, setArtworks] = useState(extractNodes(data.discoverArtworks))

useEffect(() => {
setArtworks((previousArtworks) => {
// only add new artworks to the list by filtering-out existing artworks
const newArtworks = extractNodes(data.discoverArtworks).filter(
(newArtwork) =>
!previousArtworks.some((artwork) => artwork.internalID === newArtwork.internalID)
)

return [...previousArtworks, ...newArtworks]
})
}, [data, extractNodes, setArtworks])

const goToPrevious = () => {
if (index > 0) {
Expand All @@ -40,6 +66,16 @@ export const InfiniteDiscovery: React.FC = () => {
if (index < artworks.length - 1) {
setIndex(index + 1)
}

// fetch more artworks when the user is about to reach the end of the list
if (index === artworks.length - REFETCH_BUFFER) {
refetch(
{ excludeArtworkIds: artworks.map((artwork) => artwork.internalID) },
{
fetchPolicy: "network-only",
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is the correct fetch policy because these we are fetching artworks that should not have been "discovered" before.

}
)
}
}

const handleBackPressed = () => {
Expand Down Expand Up @@ -74,13 +110,14 @@ export const InfiniteDiscovery: React.FC = () => {
Follow
</Button>
}
p={1}
/>
<Spacer y={2} />

<Flex alignItems="center" backgroundColor={color("purple60")}>
{!!src && <Image src={src} height={size.height} width={size.width} />}
</Flex>
<Flex flexDirection="row" justifyContent="space-between">
<Flex flexDirection="row" justifyContent="space-between" p={1}>
<Flex>
<Flex flexDirection="row" maxWidth={screenWidth - 200}>
{/* TODO: maxWidth above and ellipsizeMode + numberOfLines below are used to */}
Expand Down Expand Up @@ -137,39 +174,53 @@ export const InfiniteDiscovery: React.FC = () => {
}

export const InfiniteDiscoveryQueryRenderer = withSuspense({
Component: InfiniteDiscovery,
LoadingFallback: () => <Text>Loading...</Text>,
Component: () => {
const initialData = useLazyLoadQuery<InfiniteDiscoveryQuery>(infiniteDiscoveryQuery, {})

if (!initialData) {
return null
}

return <InfiniteDiscovery artworks={initialData} />
},
LoadingFallback: SpinnerFallback,
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This LoadingFallback is displayed in two situations: when the component first loads, and when the component refetches its data. However, I want two different things to happen in those situations:

  1. When the component first loads, I want to show a skeleton loader
  2. When the component refetches its data, I don't want the component to redraw (because that should happen in the background while the user is viewing the second-to-last artwork.

cc: @araujobarret @damassi

ErrorFallback: NoFallback,
})

export const infiniteDiscoveryQuery = graphql`
query InfiniteDiscoveryQuery {
marketingCollection(slug: "curators-picks") {
artworksConnection(first: 10) {
edges {
node {
artistNames
artists(shallow: true) {
coverArtwork {
images {
url(version: "small")
}
const infiniteDiscoveryFragment = graphql`
fragment InfiniteDiscovery_Fragment on Query
@refetchable(queryName: "InfiniteDiscoveryRefetchQuery")
@argumentDefinitions(excludeArtworkIds: { type: "[String!]" }) {
discoverArtworks(excludeArtworkIds: $excludeArtworkIds) {
edges {
node {
artistNames
artists(shallow: true) {
coverArtwork {
images {
url(version: "small")
}
formattedNationalityAndBirthday
initials
}
date
internalID @required(action: NONE)
images {
url(version: "large")
width
height
}
saleMessage
title
formattedNationalityAndBirthday
initials
}
date
internalID @required(action: NONE)
images {
url(version: "large")
width
height
}
saleMessage
title
}
}
}
}
`

export const infiniteDiscoveryQuery = graphql`
query InfiniteDiscoveryQuery {
...InfiniteDiscovery_Fragment
}
`
Loading