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(points): Add error banner on failure fetching subsequent points history pages #5362

Merged
merged 14 commits into from
May 1, 2024
5 changes: 5 additions & 0 deletions locales/base/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -2278,6 +2278,11 @@
"title": "No points activity yet",
"subtitle": "It looks like you haven't earned any points yet. Use {{appName}} to start earning points!",
"gotIt": "Got it"
},
"pageError": {
"title": "Unable to load",
"subtitle": "Oops, something went wrong when trying to finish loading. Please try again.",
"refresh": "Refresh"
}
},
"loading": {
Expand Down
4 changes: 3 additions & 1 deletion src/analytics/Properties.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1561,7 +1561,9 @@ interface PointsEventsProperties {
activityId: PointsActivityId
}
[PointsEvents.points_screen_activity_press]: undefined
[PointsEvents.points_screen_activity_try_again_press]: undefined
[PointsEvents.points_screen_activity_try_again_press]: {
getNextPage: boolean
}
[PointsEvents.points_screen_activity_fetch_more]: undefined
[PointsEvents.points_screen_activity_learn_more_press]: undefined
}
Expand Down
28 changes: 27 additions & 1 deletion src/points/PointsHistoryBottomSheet.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,10 @@ describe(PointsHistoryBottomSheet, () => {
fireEvent.press(getByText('points.history.error.tryAgain'))
await waitFor(() =>
expect(ValoraAnalytics.track).toHaveBeenCalledWith(
PointsEvents.points_screen_activity_try_again_press
PointsEvents.points_screen_activity_try_again_press,
{
getNextPage: false,
}
)
)
expect(dispatch).toHaveBeenCalledWith(getHistoryStarted({ getNextPage: false }))
Expand All @@ -145,4 +148,27 @@ describe(PointsHistoryBottomSheet, () => {
)
)
})

it('shows inline error if failure while fetching subsequent page', async () => {
const tree = renderScreen({
points: { getHistoryStatus: 'error', pointsHistory: MOCK_RESPONSE_NO_NEXT_PAGE.data },
})
expect(tree.getByTestId('PointsHistoryBottomSheet/ErrorBanner')).toBeTruthy()
})

it('refreshes if error banner CTA is pressed', async () => {
const { dispatch, getByText } = renderScreen({
points: { getHistoryStatus: 'error', pointsHistory: MOCK_RESPONSE_NO_NEXT_PAGE.data },
})
fireEvent.press(getByText('points.history.pageError.refresh'))
await waitFor(() =>
expect(ValoraAnalytics.track).toHaveBeenCalledWith(
PointsEvents.points_screen_activity_try_again_press,
{
getNextPage: true,
}
)
)
expect(dispatch).toHaveBeenCalledWith(getHistoryStarted({ getNextPage: true }))
})
})
47 changes: 39 additions & 8 deletions src/points/PointsHistoryBottomSheet.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useMemo } from 'react'
import React, { useMemo, useState, useEffect } from 'react'
import { ActivityIndicator, StyleSheet, Text, View, ListRenderItem } from 'react-native'
import SectionHead from 'src/components/SectionHead'
import GorhomBottomSheet from '@gorhom/bottom-sheet'
Expand All @@ -21,6 +21,8 @@ import { BottomSheetSectionList } from '@gorhom/bottom-sheet'
import { useGetHistoryDefinition } from 'src/points/cardDefinitions'
import { HistoryCardMetadata } from 'src/points/cardDefinitions'
import { useSafeAreaInsets } from 'react-native-safe-area-context'
import InLineNotification, { NotificationVariant } from 'src/components/InLineNotification'
import AttentionIcon from 'src/icons/Attention'

interface Props {
forwardedRef: React.RefObject<GorhomBottomSheet>
Expand Down Expand Up @@ -55,6 +57,8 @@ function PointsHistoryBottomSheet({ forwardedRef }: Props) {
const pointsHistoryStatus = useSelector(pointsHistoryStatusSelector)
const pointsHistory = useSelector(pointsHistorySelector)

const [showError, setShowError] = useState(false)

const getHistoryDefinition = useGetHistoryDefinition()

const insets = useSafeAreaInsets()
Expand All @@ -78,11 +82,13 @@ function PointsHistoryBottomSheet({ forwardedRef }: Props) {
)
}

const onPressTryAgain = () => {
ValoraAnalytics.track(PointsEvents.points_screen_activity_try_again_press)
const onPressTryAgain = (getNextPage: boolean) => {
ValoraAnalytics.track(PointsEvents.points_screen_activity_try_again_press, {
getNextPage,
})
dispatch(
getHistoryStarted({
getNextPage: false,
getNextPage,
})
)
}
Expand Down Expand Up @@ -112,7 +118,7 @@ function PointsHistoryBottomSheet({ forwardedRef }: Props) {
</View>
<Button
testID={'PointsHistoryBottomSheet/TryAgain'}
onPress={onPressTryAgain}
onPress={() => onPressTryAgain(false)}
text={t('points.history.error.tryAgain')}
type={BtnTypes.GRAY_WITH_BORDER}
size={BtnSizes.FULL}
Expand All @@ -138,12 +144,16 @@ function PointsHistoryBottomSheet({ forwardedRef }: Props) {

const isEmpty = pointsHistoryStatus !== 'loading' && !pointsHistory.length

// TODO: Figure out what to render when error occurs on subsequent page fetch

const sections = useMemo(() => {
return groupFeedItemsInSections([], pointsHistory)
}, [pointsHistory, pointsHistoryStatus])

useEffect(() => {
if (pointsHistory.length && pointsHistoryStatus === 'error') {
setShowError(true)
}
}, [pointsHistory, pointsHistoryStatus])

return (
<BottomSheetBase snapPoints={['80%']} forwardedRef={forwardedRef}>
{!isEmpty && <Text style={styles.contentHeader}>{t('points.history.title')}</Text>}
Expand All @@ -163,13 +173,34 @@ function PointsHistoryBottomSheet({ forwardedRef }: Props) {
onEndReached={onFetchMoreHistory}
ListFooterComponent={Loading}
ListEmptyComponent={isEmpty ? EmptyOrError : null}
onEndReachedThreshold={0.5}
onEndReachedThreshold={0.2}
stickySectionHeadersEnabled={false}
/>
{showError && (
<InLineNotification
variant={NotificationVariant.Error}
title={t('points.history.pageError.title')}
description={t('points.history.pageError.subtitle')}
ctaLabel={t('points.history.pageError.refresh')}
onPressCta={() => onPressTryAgain(true)}
withBorder={true}
style={{
...styles.errorNotification,
marginBottom: Math.max(insets.bottom, Spacing.Thick24),
}}
customIcon={<AttentionIcon color={colors.errorDark} size={20} />}
testID={'PointsHistoryBottomSheet/ErrorBanner'}
/>
)}
</BottomSheetBase>
)
}

const styles = StyleSheet.create({
errorNotification: {
positioning: 'absolute',
marginHorizontal: Spacing.Regular16,
},
emptyContainer: {
flex: 1,
padding: Spacing.Thick24,
Expand Down
Loading