Skip to content

Commit

Permalink
(PC-31778) feat(Bookings): post reaction on tab change or on change s…
Browse files Browse the repository at this point in the history
…creen (#6909)

* (PC-31778) feat(api): update api file

* (PC-31778) feat(TabLayout): adapt tabLayout on change

* (PC-31778) feat(EndedBookings): adapt types with new post reaction

* (PC-31778) feat(Bookings): post reaction on tab change or on change screen

* (PC-31778) fix(EndedBookings): fix test

* (PC-31778) fix types

* (PC-31778) refacto wording after review
  • Loading branch information
yleclercq-pass authored Sep 23, 2024
1 parent b04ceba commit 29ace50
Show file tree
Hide file tree
Showing 17 changed files with 160 additions and 53 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,7 @@ exports[`EndedBookings should render correctly 1`] = `
},
"token": "352UW5",
"totalAmount": 1900,
"userReaction": null,
},
{
"confirmationDate": "2021-02-15T23:01:37.925926",
Expand Down Expand Up @@ -297,6 +298,7 @@ exports[`EndedBookings should render correctly 1`] = `
},
"token": "352UW5",
"totalAmount": 1900,
"userReaction": null,
},
]
}
Expand Down Expand Up @@ -787,6 +789,7 @@ exports[`EndedBookings should render correctly 1`] = `
},
"token": "352UW5",
"totalAmount": 1900,
"userReaction": null,
}
}
style={
Expand Down
19 changes: 15 additions & 4 deletions src/api/gen/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2710,20 +2710,31 @@ export interface PostFeedbackBody {
}
/**
* @export
* @interface PostReactionRequest
* @interface PostOneReactionRequest
*/
export interface PostReactionRequest {
export interface PostOneReactionRequest {
/**
* @type {number}
* @memberof PostReactionRequest
* @memberof PostOneReactionRequest
*/
offerId: number
/**
* @type {ReactionTypeEnum}
* @memberof PostReactionRequest
* @memberof PostOneReactionRequest
*/
reactionType: ReactionTypeEnum
}
/**
* @export
* @interface PostReactionRequest
*/
export interface PostReactionRequest {
/**
* @type {Array<PostOneReactionRequest>}
* @memberof PostReactionRequest
*/
reactions: Array<PostOneReactionRequest>
}
/**
* @export
* @interface ProfileUpdateRequest
Expand Down
5 changes: 2 additions & 3 deletions src/features/bookings/components/EndedBookingItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import React, { ComponentProps, FunctionComponent, useCallback, useMemo, useStat
import { View } from 'react-native'
import styled from 'styled-components/native'

import { BookingCancellationReasons, PostReactionRequest, ReactionTypeEnum } from 'api/gen'
import { BookingCancellationReasons, PostOneReactionRequest, ReactionTypeEnum } from 'api/gen'
import { BookingItemTitle } from 'features/bookings/components/BookingItemTitle'
import { isEligibleBookingsForArchive } from 'features/bookings/helpers/expirationDateUtils'
import { BookingItemProps } from 'features/bookings/types'
Expand Down Expand Up @@ -50,7 +50,6 @@ export const EndedBookingItem = ({ booking, onSaveReaction }: BookingItemProps)
const { cancellationDate, cancellationReason, dateUsed, stock } = booking
const subcategoriesMapping = useSubcategoriesMapping()
const subCategory = subcategoriesMapping[stock.offer.subcategoryId]

const prePopulateOffer = usePrePopulateOffer()
const netInfo = useNetInfoContext()
const { showErrorSnackBar } = useSnackBarContext()
Expand Down Expand Up @@ -150,7 +149,7 @@ export const EndedBookingItem = ({ booking, onSaveReaction }: BookingItemProps)
utmMedium: 'ended_booking',
})

const handleSaveReaction = async ({ offerId, reactionType }: PostReactionRequest) => {
const handleSaveReaction = async ({ offerId, reactionType }: PostOneReactionRequest) => {
await onSaveReaction?.({ offerId, reactionType })
setUserReaction(reactionType)
hideReactionModal()
Expand Down
2 changes: 2 additions & 0 deletions src/features/bookings/fixtures/bookingsSnap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ export const bookingsSnap = toMutable({
totalAmount: 1900,
token: '352UW5',
quantity: 10,
userReaction: null,
stock: {
id: 150230,
price: 400,
Expand Down Expand Up @@ -106,6 +107,7 @@ export const bookingsSnap = toMutable({
totalAmount: 1900,
token: '352UW5',
quantity: 10,
userReaction: null,
stock: {
id: 150230,
price: 400,
Expand Down
42 changes: 38 additions & 4 deletions src/features/bookings/pages/Bookings/Bookings.native.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import React from 'react'
import { QueryObserverResult } from 'react-query'

import { navigate } from '__mocks__/@react-navigation/native'
import { BookingsResponse, SubcategoriesResponseModelv2 } from 'api/gen'
import { BookingsResponse, ReactionTypeEnum, SubcategoriesResponseModelv2 } from 'api/gen'
import * as bookingsAPI from 'features/bookings/api/useBookings'
import { bookingsSnap, emptyBookingsSnap } from 'features/bookings/fixtures/bookingsSnap'
import * as useFeatureFlagAPI from 'libs/firebase/firestore/featureFlags/useFeatureFlag'
Expand Down Expand Up @@ -49,6 +49,11 @@ jest.mock('react-native/Libraries/Animated/createAnimatedComponent', () => {
}
})

const mockMutate = jest.fn()
jest.mock('features/reactions/api/useReactionMutation', () => ({
useReactionMutation: () => ({ mutate: mockMutate }),
}))

describe('Bookings', () => {
beforeEach(() => {
mockServer.getApi<SubcategoriesResponseModelv2>('/v1/subcategories/v2', subcategoriesDataTest)
Expand All @@ -66,8 +71,8 @@ describe('Bookings', () => {
renderBookings()
await act(async () => {})

//Due to multiple renders useBookings is called twice
expect(useBookingsSpy).toHaveBeenCalledTimes(2)
//Due to multiple renders useBookings is called three times
expect(useBookingsSpy).toHaveBeenCalledTimes(3)
})

it('should display the right number of ongoing bookings', async () => {
Expand All @@ -82,10 +87,11 @@ describe('Bookings', () => {
data: emptyBookingsSnap,
isFetching: false,
} as unknown as QueryObserverResult<BookingsResponse, unknown>
// Due to multiple renders we need to mock useBookings twice
// Due to multiple renders we need to mock useBookings three times
useBookingsSpy
.mockReturnValueOnce(useBookingsResultMock)
.mockReturnValueOnce(useBookingsResultMock)
.mockReturnValueOnce(useBookingsResultMock)
renderBookings()

await act(async () => {})
Expand All @@ -112,6 +118,9 @@ describe('Bookings', () => {

describe('when feature flag is activated', () => {
beforeEach(() => {
// Due to multiple renders we need to mock useBookings three times
useFeatureFlagSpy.mockReturnValueOnce(true)
useFeatureFlagSpy.mockReturnValueOnce(true)
useFeatureFlagSpy.mockReturnValueOnce(true)
})

Expand All @@ -135,6 +144,31 @@ describe('Bookings', () => {

expect(await screen.findAllByText('Avez-vous déjà vu\u00a0?')).toHaveLength(2)
})

it('should call updateReactions when switching from COMPLETED tab', async () => {
renderBookings()

fireEvent.press(await screen.findByText('Terminées'))

fireEvent.press(await screen.findByText('En cours'))

expect(mockMutate).toHaveBeenCalledTimes(1)
})

it('should update reactions for ended bookings without user reaction', async () => {
renderBookings()

fireEvent.press(await screen.findByText('Terminées'))

fireEvent.press(await screen.findByText('En cours'))

expect(mockMutate).toHaveBeenCalledWith({
reactions: [
{ offerId: 147874, reactionType: ReactionTypeEnum.NO_REACTION },
{ offerId: 147874, reactionType: ReactionTypeEnum.NO_REACTION },
],
})
})
})
})

Expand Down
45 changes: 43 additions & 2 deletions src/features/bookings/pages/Bookings/Bookings.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
import React from 'react'
import { useFocusEffect } from '@react-navigation/native'
import React, { useCallback, useState } from 'react'
import styled from 'styled-components/native'

import { ReactionTypeEnum } from 'api/gen'
import { useBookings } from 'features/bookings/api'
import { OnGoingBookingsList } from 'features/bookings/components/OnGoingBookingsList'
import { EndedBookings } from 'features/bookings/pages/EndedBookings/EndedBookings'
import { useReactionMutation } from 'features/reactions/api/useReactionMutation'
import { TabLayout } from 'features/venue/components/TabLayout/TabLayout'
import { useFeatureFlag } from 'libs/firebase/firestore/featureFlags/useFeatureFlag'
import { RemoteStoreFeatureFlags } from 'libs/firebase/firestore/types'
Expand All @@ -16,10 +20,44 @@ enum Tab {

export function Bookings() {
const enableBookingImprove = useFeatureFlag(RemoteStoreFeatureFlags.WIP_BOOKING_IMPROVE)
const [activeTab, setActiveTab] = useState<Tab>(Tab.CURRENT)
const [previousTab, setPreviousTab] = useState(activeTab)
const { data: bookings } = useBookings()
const { mutate: addReaction } = useReactionMutation()

const updateReactions = useCallback(() => {
const bookingsToUpdate =
bookings?.ended_bookings
.filter((ended_booking) => ended_booking.userReaction === null)
.map((booking) => booking.stock.offer.id) ?? []

const mutationPayload = bookingsToUpdate.map((bookingId) => ({
offerId: bookingId,
reactionType: ReactionTypeEnum.NO_REACTION,
}))
if (mutationPayload.length > 0) {
addReaction({ reactions: mutationPayload })
}
}, [addReaction, bookings?.ended_bookings])

useFocusEffect(
useCallback(() => {
return () => {
if (previousTab === Tab.COMPLETED) {
updateReactions()
}
setPreviousTab(activeTab)
}
}, [activeTab, previousTab, updateReactions])
)

const tabPanels = {
[Tab.CURRENT]: <OnGoingBookingsList enableBookingImprove={enableBookingImprove} />,
[Tab.COMPLETED]: <EndedBookings enableBookingImprove={enableBookingImprove} />,
[Tab.COMPLETED]: (
<EndedBookings enableBookingImprove={enableBookingImprove} bookings={bookings} />
),
}

return (
<React.Fragment>
{enableBookingImprove ? (
Expand All @@ -29,6 +67,9 @@ export function Bookings() {
tabPanels={tabPanels}
defaultTab={Tab.CURRENT}
tabs={[{ key: Tab.CURRENT }, { key: Tab.COMPLETED }]}
onTabChange={(key) => {
setActiveTab(key)
}}
/>
</ContainerTab>
) : (
Expand Down
2 changes: 1 addition & 1 deletion src/features/bookings/pages/Bookings/Bookings.web.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ describe('Bookings', () => {
renderBookings(bookingsSnap)

await waitFor(() => {
expect(useBookings).toHaveBeenCalledTimes(1)
expect(useBookings).toHaveBeenCalledTimes(2)
})
})

Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import React, { Fragment, FunctionComponent } from 'react'
import { QueryObserverResult } from 'react-query'

import { BookingsResponse } from 'api/gen'
import * as bookingsAPI from 'features/bookings/api/useBookings'
import { bookingsSnap } from 'features/bookings/fixtures/bookingsSnap'
import * as useGoBack from 'features/navigation/useGoBack'
import * as useFeatureFlagAPI from 'libs/firebase/firestore/featureFlags/useFeatureFlag'
Expand Down Expand Up @@ -62,13 +60,6 @@ describe('EndedBookings', () => {
expect(screen).toMatchSnapshot()
})

it('should always execute the query (in cache or in network)', () => {
const useBookings = jest.spyOn(bookingsAPI, 'useBookings')
renderEndedBookings(bookingsSnap)

expect(useBookings).toHaveBeenCalledTimes(1)
})

it('should display the right number of ended bookings', () => {
renderEndedBookings(bookingsSnap)

Expand Down Expand Up @@ -107,11 +98,9 @@ const renderEndedBookings = (
bookings: BookingsResponse,
Wrapper: FunctionComponent<{ children: JSX.Element }> = Fragment
) => {
jest
.spyOn(bookingsAPI, 'useBookings')
.mockReturnValue({ data: bookings } as QueryObserverResult<BookingsResponse, unknown>)

return render(
<Wrapper>{reactQueryProviderHOC(<EndedBookings enableBookingImprove={false} />)}</Wrapper>
<Wrapper>
{reactQueryProviderHOC(<EndedBookings enableBookingImprove={false} bookings={bookings} />)}
</Wrapper>
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ describe('<EndedBookings />', () => {
await measurePerformance(
reactQueryProviderHOC(
<AuthWrapper>
<EndedBookings enableBookingImprove={false} />
<EndedBookings enableBookingImprove={false} bookings={bookingsSnap} />
</AuthWrapper>
),
{
Expand Down
14 changes: 8 additions & 6 deletions src/features/bookings/pages/EndedBookings/EndedBookings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@ import React, { FunctionComponent, useCallback } from 'react'
import { FlatList, ListRenderItem } from 'react-native'
import styled from 'styled-components/native'

import { PostReactionRequest } from 'api/gen'
import { useBookings } from 'features/bookings/api'
import { BookingsResponse, PostOneReactionRequest, PostReactionRequest } from 'api/gen'
import { EndedBookingItem } from 'features/bookings/components/EndedBookingItem'
import { NoBookingsView } from 'features/bookings/components/NoBookingsView'
import { Booking } from 'features/bookings/types'
Expand All @@ -25,10 +24,10 @@ const keyExtractor: (item: Booking) => string = (item) => item.id.toString()

type Props = {
enableBookingImprove: boolean
bookings: BookingsResponse | undefined
}

export const EndedBookings: FunctionComponent<Props> = ({ enableBookingImprove }) => {
const { data: bookings } = useBookings()
export const EndedBookings: FunctionComponent<Props> = ({ enableBookingImprove, bookings }) => {
const { goBack } = useGoBack(...getTabNavConfig('Bookings'))
const headerHeight = useGetHeaderHeight()
const { mutate: addReaction } = useReactionMutation()
Expand All @@ -40,8 +39,11 @@ export const EndedBookings: FunctionComponent<Props> = ({ enableBookingImprove }
})

const handleSaveReaction = useCallback(
({ offerId, reactionType }: PostReactionRequest) => {
addReaction({ offerId, reactionType })
({ offerId, reactionType }: PostOneReactionRequest) => {
const reactionRequest: PostReactionRequest = {
reactions: [{ offerId, reactionType }],
}
addReaction(reactionRequest)
return Promise.resolve(true)
},
[addReaction]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,5 +45,7 @@ const renderEndedBookings = (bookings: BookingsResponse) => {
.spyOn(bookingsAPI, 'useBookings')
.mockReturnValue({ data: bookings } as QueryObserverResult<BookingsResponse, unknown>)

return render(reactQueryProviderHOC(<EndedBookings enableBookingImprove={false} />))
return render(
reactQueryProviderHOC(<EndedBookings enableBookingImprove={false} bookings={bookingsSnap} />)
)
}
4 changes: 2 additions & 2 deletions src/features/bookings/types.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { BookingReponse, PostReactionRequest } from 'api/gen'
import { BookingReponse, PostOneReactionRequest } from 'api/gen'

export type BookingProperties = {
isDuo?: boolean
Expand All @@ -14,5 +14,5 @@ export type Booking = BookingReponse
export interface BookingItemProps {
booking: Booking
eligibleBookingsForArchive?: Booking[]
onSaveReaction?: (reactionParams: PostReactionRequest) => Promise<boolean>
onSaveReaction?: (reactionParams: PostOneReactionRequest) => Promise<boolean>
}
Loading

0 comments on commit 29ace50

Please sign in to comment.