Skip to content

Commit

Permalink
(PC-33308) feat(Home): use releaseDate to display date on cine offers (
Browse files Browse the repository at this point in the history
…#7296)

* feat: new hook to centralize logic and avoid code repetition

* feat: update algolia query to retrieve releaseDate attribute

* feat: new component to avoid jsx duplication

* feat: use new hook and new component to render properly formatted dates

* refactor: add releaseDate to offerAttributesToRetreive

* fix: update fetchOffersByGTL
  • Loading branch information
imouandjolobe-pass authored Nov 29, 2024
1 parent c0d7c70 commit 4a19556
Show file tree
Hide file tree
Showing 24 changed files with 267 additions and 252 deletions.
2 changes: 1 addition & 1 deletion scripts/noUncheckedIndexedAccess_snapshot.txt
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
./src/features/deeplinks/helpers/getScreenFromDeeplink.ts:24
./src/features/home/api/helpers/mapVenuesDataAndModules.ts:13
./src/features/home/api/helpers/mapVenuesDataAndModules.ts:15
./src/features/home/components/modules/OffersModule.tsx:78
./src/features/home/components/modules/OffersModule.tsx:70
./src/features/home/components/modules/video/OldVideoModuleDesktop.tsx:96
./src/features/home/components/modules/video/OldVideoModuleMobile.tsx:70
./src/features/home/components/modules/video/VideoModuleDesktop.tsx:86
Expand Down
54 changes: 30 additions & 24 deletions src/features/gtlPlaylist/components/GtlPlaylist.native.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { venueDataTest } from 'features/venue/fixtures/venueDataTest'
import { analytics } from 'libs/analytics'
import * as useFeatureFlagAPI from 'libs/firebase/firestore/featureFlags/useFeatureFlag'
import { reactQueryProviderHOC } from 'tests/reactQueryProviderHOC'
import { act, fireEvent, render, screen } from 'tests/utils'
import { act, userEvent, render, screen } from 'tests/utils'

jest.mock('libs/subcategories/useSubcategories')

Expand Down Expand Up @@ -60,24 +60,29 @@ jest.mock('@shopify/flash-list', () => {
}
})

jest.useFakeTimers()

const user = userEvent.setup()

describe('GtlPlaylist', () => {
describe('on venue page', () => {
it('should log ConsultOffer when pressing an item', () => {
it('should log ConsultOffer when pressing an item', async () => {
renderGtlPlaylistOnVenuePage()

const result = screen.queryAllByText('Mon abonnement bibliothèque')[0]
const item = await screen.findByText('Mon abonnement bibliothèque')

if (result) {
fireEvent.press(result)
}
await act(async () => user.press(item))

expect(analytics.logConsultOffer).toHaveBeenNthCalledWith(1, {
from: 'venue',
index: 0,
moduleId: '2xUlLBRfxdk6jeYyJszunX',
offerId: 12,
venueId: 5543,
})
expect(analytics.logConsultOffer).toHaveBeenNthCalledWith(
1,
expect.objectContaining({
from: 'venue',
index: 0,
moduleId: '2xUlLBRfxdk6jeYyJszunX',
offerId: 12,
venueId: 5543,
})
)
})

it('should log AllTilesSeen only once when scrolling to the end of the playlist', async () => {
Expand Down Expand Up @@ -125,21 +130,22 @@ describe('GtlPlaylist', () => {
})

describe('on ThematicSearch page', () => {
it('should log ConsultOffer when pressing an item', () => {
it('should log ConsultOffer when pressing an item', async () => {
renderGtlPlaylistOnThematicSearch()

const result = screen.queryAllByText('Mon abonnement bibliothèque')[0]
const item = await screen.findByText('Mon abonnement bibliothèque')

if (result) {
fireEvent.press(result)
}
await act(async () => user.press(item))

expect(analytics.logConsultOffer).toHaveBeenNthCalledWith(1, {
from: 'thematicsearch',
index: 0,
moduleId: '2xUlLBRfxdk6jeYyJszunX',
offerId: 12,
})
expect(analytics.logConsultOffer).toHaveBeenNthCalledWith(
1,
expect.objectContaining({
from: 'thematicsearch',
index: 0,
moduleId: '2xUlLBRfxdk6jeYyJszunX',
offerId: 12,
})
)
})

it('should log AllTilesSeen only once when scrolling to the end of the playlist', async () => {
Expand Down
4 changes: 1 addition & 3 deletions src/features/gtlPlaylist/components/GtlPlaylist.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import { VenueResponse } from 'api/gen'
import { GtlPlaylistData } from 'features/gtlPlaylist/types'
import { Referrals, ScreenNames } from 'features/navigation/RootNavigator/types'
import { useLogScrollHandler } from 'features/offer/helpers/useLogScrolHandler/useLogScrollHandler'
import { useTransformOfferHits } from 'libs/algolia/fetchAlgolia/transformOfferHit'
import { analytics } from 'libs/analytics'
import { usePlaylistItemDimensionsFromLayout } from 'libs/contentful/usePlaylistItemDimensionsFromLayout'
import { useFunctionOnce } from 'libs/hooks'
Expand All @@ -22,7 +21,6 @@ export interface GtlPlaylistProps {
}

export function GtlPlaylist({ venue, playlist, analyticsFrom, route }: Readonly<GtlPlaylistProps>) {
const transformOfferHits = useTransformOfferHits()
const entryId = playlist.entryId

const logHasSeenAllTilesOnce = useFunctionOnce(() => {
Expand Down Expand Up @@ -57,7 +55,7 @@ export function GtlPlaylist({ venue, playlist, analyticsFrom, route }: Readonly<
itemWidth={itemWidth}
itemHeight={itemHeight}
renderItem={renderPassPlaylist}
keyExtractor={(item: Hit<Offer>) => transformOfferHits(item).objectID}
keyExtractor={(item: Hit<Offer>) => item.objectID}
title={playlist.title}
onEndReached={logHasSeenAllTilesOnce}
/>
Expand Down
20 changes: 12 additions & 8 deletions src/features/gtlPlaylist/hooks/useGTLPlaylists.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { GtlPlaylistData } from 'features/gtlPlaylist/types'
import { useIsUserUnderage } from 'features/profile/helpers/useIsUserUnderage'
import { useAdaptOffersPlaylistParameters } from 'libs/algolia/fetchAlgolia/fetchMultipleOffers/helpers/useAdaptOffersPlaylistParameters'
import { fetchOffersByGTL } from 'libs/algolia/fetchAlgolia/fetchOffersByGTL'
import { useTransformOfferHits } from 'libs/algolia/fetchAlgolia/transformOfferHit'
import { useLocation } from 'libs/location'
import { useNetInfoContext } from 'libs/network/NetInfoWrapper'
import { QueryKeys } from 'libs/queryKeys'
Expand All @@ -25,6 +26,7 @@ export function useGTLPlaylists({
const { userLocation, selectedLocationMode } = useLocation()
const isUserUnderage = useIsUserUnderage()
const adaptPlaylistParameters = useAdaptOffersPlaylistParameters()
const transformHits = useTransformOfferHits()

const { data: gtlPlaylists, isLoading } = useQuery({
queryKey: [gtlPlaylistsQueryKey, venue?.id, userLocation, selectedLocationMode],
Expand Down Expand Up @@ -58,13 +60,15 @@ export function useGTLPlaylists({
searchIndex
)

return gtlPlaylistsConfig.map((item, index) => ({
title: item.displayParameters.title,
offers: offers[index] ?? { hits: [] },
layout: item.displayParameters.layout,
minNumberOfOffers: item.displayParameters.minOffers,
entryId: item.id,
}))
return gtlPlaylistsConfig.map((item, index) => {
return {
title: item.displayParameters.title,
offers: { hits: offers[index]?.hits.map(transformHits) ?? [] },
layout: item.displayParameters.layout,
minNumberOfOffers: item.displayParameters.minOffers,
entryId: item.id,
}
})
},
enabled: !!netInfo.isConnected,
staleTime: 5 * 60 * 1000, // 5 minutes, as the GTL playlists are not often updated
Expand All @@ -76,7 +80,7 @@ export function useGTLPlaylists({

return {
gtlPlaylists: gtlPlaylists.filter(
(playlist) => playlist.offers.hits.length >= Math.max(playlist.minNumberOfOffers, 1)
(playlist) => playlist?.offers?.hits.length >= Math.max(playlist.minNumberOfOffers, 1)
),
isLoading,
}
Expand Down
4 changes: 2 additions & 2 deletions src/features/gtlPlaylist/types.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { OffersModuleParameters } from 'features/home/types'
import { AlgoliaHit } from 'libs/algolia/types'
import { Layout, DisplayParametersFields } from 'libs/contentful/types'
import { Offer } from 'shared/offer/types'

export type GtlPlaylistRequest = {
id: string
Expand All @@ -10,7 +10,7 @@ export type GtlPlaylistRequest = {

export type GtlPlaylistData = {
title: string
offers: { hits: Offer[] }
offers: { hits: AlgoliaHit[] }
layout: Layout
minNumberOfOffers: number
entryId: string
Expand Down
10 changes: 0 additions & 10 deletions src/features/home/components/HomeOfferTile.tsx

This file was deleted.

37 changes: 6 additions & 31 deletions src/features/home/components/modules/OffersModule.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,22 @@ import React, { useCallback, useEffect, useMemo } from 'react'

import { useAuthContext } from 'features/auth/context/AuthContext'
import { useHomeRecommendedOffers } from 'features/home/api/useHomeRecommendedOffers'
import { HomeOfferTile } from 'features/home/components/HomeOfferTile'
import {
ModuleData,
OffersModule as OffersModuleType,
RecommendedOffersModule,
} from 'features/home/types'
import { getSearchStackConfig } from 'features/navigation/SearchStackNavigator/helpers'
import { OfferTileWrapper } from 'features/offer/components/OfferTile/OfferTileWrapper'
import { useAdaptOffersPlaylistParameters } from 'libs/algolia/fetchAlgolia/fetchMultipleOffers/helpers/useAdaptOffersPlaylistParameters'
import { analytics } from 'libs/analytics'
import { ContentTypes } from 'libs/contentful/types'
import { usePlaylistItemDimensionsFromLayout } from 'libs/contentful/usePlaylistItemDimensionsFromLayout'
import { useGetPacificFrancToEuroRate } from 'libs/firebase/firestore/exchangeRates/useGetPacificFrancToEuroRate'
import { useFeatureFlag } from 'libs/firebase/firestore/featureFlags/useFeatureFlag'
import { RemoteStoreFeatureFlags } from 'libs/firebase/firestore/types'
import useFunctionOnce from 'libs/hooks/useFunctionOnce'
import { useLocation } from 'libs/location'
import { formatDates } from 'libs/parsers/formatDates'
import { getDisplayPrice } from 'libs/parsers/getDisplayPrice'
import { useCategoryHomeLabelMapping, useCategoryIdMapping } from 'libs/subcategories'
import { useGetCurrencyToDisplay } from 'shared/currency/useGetCurrencyToDisplay'
import { Offer } from 'shared/offer/types'
import { PassPlaylist } from 'ui/components/PassPlaylist'
import { CustomListRenderItem, ItemDimensions, RenderFooterItem } from 'ui/components/Playlist'
Expand Down Expand Up @@ -50,11 +46,7 @@ export const OffersModule = (props: OffersModuleProps) => {
data,
recommendationParameters,
} = props
const currency = useGetCurrencyToDisplay()
const euroToPacificFrancRate = useGetPacificFrancToEuroRate()
const adaptedPlaylistParameters = useAdaptOffersPlaylistParameters()
const mapping = useCategoryIdMapping()
const labelMapping = useCategoryHomeLabelMapping()
const { user } = useAuthContext()
const { userLocation } = useLocation()

Expand Down Expand Up @@ -110,38 +102,21 @@ export const OffersModule = (props: OffersModuleProps) => {
({ item, width, height }) => {
const timestampsInMillis = item.offer.dates?.map((timestampInSec) => timestampInSec * 1000)
return (
<HomeOfferTile
offerLocation={item._geoloc}
categoryLabel={labelMapping[item.offer.subcategoryId]}
categoryId={mapping[item.offer.subcategoryId]}
subcategoryId={item.offer.subcategoryId}
offerId={+item.objectID}
name={item.offer.name}
<OfferTileWrapper
item={item}
date={formatDates(timestampsInMillis)}
isDuo={item.offer.isDuo}
thumbUrl={item.offer.thumbUrl}
price={getDisplayPrice(item.offer.prices, currency, euroToPacificFrancRate)}
isBeneficiary={user?.isBeneficiary}
moduleName={moduleName}
moduleId={moduleId}
homeEntryId={homeEntryId}
width={width}
height={height}
variant={isNewOfferTileDisplayed ? 'new' : 'default'}
analyticsFrom="home"
/>
)
},
[
labelMapping,
mapping,
currency,
euroToPacificFrancRate,
user?.isBeneficiary,
moduleName,
moduleId,
homeEntryId,
isNewOfferTileDisplayed,
]

[moduleName, moduleId, homeEntryId, isNewOfferTileDisplayed]
)

const { itemWidth, itemHeight } = usePlaylistItemDimensionsFromLayout(displayParameters.layout)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,11 @@ describe('RecommendationModule', () => {
mockServer.getApi<SubcategoriesResponseModelv2>('/v1/subcategories/v2', subcategoriesDataTest)
})

it('should display V2 playlist when FF activated', () => {
activateFeatureFlags([RemoteStoreFeatureFlags.WIP_NEW_OFFER_TILE])
it('should display V2 playlist when FF activated', async () => {
useFeatureFlagSpy.mockReturnValueOnce(true)
renderRecommendationModule()

expect(screen.getAllByTestId('playlist-card-offer-v2')).toBeTruthy()
expect(await screen.findByTestId('playlist-card-offer-v2')).toBeOnTheScreen()
})

it('should NOT display V2 playlist when FF deactivated', () => {
Expand Down
59 changes: 14 additions & 45 deletions src/features/home/components/modules/RecommendationModule.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,15 @@ import React, { useCallback, useEffect } from 'react'

import { useAuthContext } from 'features/auth/context/AuthContext'
import { useHomeRecommendedOffers } from 'features/home/api/useHomeRecommendedOffers'
import { HomeOfferTile } from 'features/home/components/HomeOfferTile'
import { RecommendedOffersModule } from 'features/home/types'
import { OfferTileWrapper } from 'features/offer/components/OfferTile/OfferTileWrapper'
import { analytics } from 'libs/analytics'
import { ContentTypes, DisplayParametersFields } from 'libs/contentful/types'
import { usePlaylistItemDimensionsFromLayout } from 'libs/contentful/usePlaylistItemDimensionsFromLayout'
import { useGetPacificFrancToEuroRate } from 'libs/firebase/firestore/exchangeRates/useGetPacificFrancToEuroRate'
import { useFeatureFlag } from 'libs/firebase/firestore/featureFlags/useFeatureFlag'
import { RemoteStoreFeatureFlags } from 'libs/firebase/firestore/types'
import useFunctionOnce from 'libs/hooks/useFunctionOnce'
import { useLocation } from 'libs/location/LocationWrapper'
import { formatDates } from 'libs/parsers/formatDates'
import { getDisplayPrice } from 'libs/parsers/getDisplayPrice'
import { useCategoryHomeLabelMapping, useCategoryIdMapping } from 'libs/subcategories'
import { useGetCurrencyToDisplay } from 'shared/currency/useGetCurrencyToDisplay'
import { Offer } from 'shared/offer/types'
import { PassPlaylist } from 'ui/components/PassPlaylist'
import { CustomListRenderItem } from 'ui/components/Playlist'
Expand All @@ -31,14 +26,10 @@ type RecommendationModuleProps = {
const keyExtractor = (item: Offer) => item.objectID

export const RecommendationModule = (props: RecommendationModuleProps) => {
const currency = useGetCurrencyToDisplay()
const euroToPacificFrancRate = useGetPacificFrancToEuroRate()
const isNewOfferTileDisplayed = useFeatureFlag(RemoteStoreFeatureFlags.WIP_NEW_OFFER_TILE)
const { displayParameters, index, recommendationParameters, moduleId, homeEntryId } = props
const { userLocation: position } = useLocation()
const { user: profile } = useAuthContext()
const mapping = useCategoryIdMapping()
const labelMapping = useCategoryHomeLabelMapping()

const { offers, recommendationApiParams } = useHomeRecommendedOffers(
position,
Expand Down Expand Up @@ -69,41 +60,19 @@ export const RecommendationModule = (props: RecommendationModuleProps) => {
}, [shouldModuleBeDisplayed])

const renderItem: CustomListRenderItem<Offer> = useCallback(
({ item, width, height }) => {
const timestampsInMillis = item.offer.dates?.map((timestampInSec) => timestampInSec * 1000)

return (
<HomeOfferTile
categoryLabel={labelMapping[item.offer.subcategoryId]}
categoryId={mapping[item.offer.subcategoryId]}
subcategoryId={item.offer.subcategoryId}
offerId={+item.objectID}
offerLocation={item._geoloc}
name={item.offer.name}
date={formatDates(timestampsInMillis)}
isDuo={item.offer.isDuo}
thumbUrl={item.offer.thumbUrl}
price={getDisplayPrice(item.offer.prices, currency, euroToPacificFrancRate)}
isBeneficiary={profile?.isBeneficiary}
moduleName={moduleName}
moduleId={moduleId}
width={width}
height={height}
homeEntryId={homeEntryId}
apiRecoParams={recommendationApiParams}
variant={isNewOfferTileDisplayed ? 'new' : 'default'}
/>
)
},
// eslint-disable-next-line react-hooks/exhaustive-deps
[
profile?.isBeneficiary,
labelMapping,
mapping,
recommendationApiParams,
currency,
euroToPacificFrancRate,
]
({ item, width, height }) => (
<OfferTileWrapper
item={item}
width={width}
height={height}
moduleId={moduleId}
moduleName={moduleName}
apiRecoParams={recommendationApiParams}
analyticsFrom="home"
variant={isNewOfferTileDisplayed ? 'new' : 'default'}
/>
),
[isNewOfferTileDisplayed, moduleId, moduleName, recommendationApiParams]
)

const { itemWidth, itemHeight } = usePlaylistItemDimensionsFromLayout(displayParameters.layout)
Expand Down
Loading

0 comments on commit 4a19556

Please sign in to comment.