Skip to content
Open
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
5 changes: 4 additions & 1 deletion src/components/ui/containers/WebView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,10 @@ export const WebView = ({
ref={ref}
renderLoading={() => (
<Column grow={1}>
<PleaseWait testID={testID} />
<PleaseWait
showFeedback
testID={testID}
/>
</Column>
)}
source={{uri: urlWithParams}}
Expand Down
14 changes: 14 additions & 0 deletions src/components/ui/feedback/PleaseWait.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,17 @@ export const Default: StoryObj<typeof PleaseWait> = {
grow: true,
},
}

export const After5Seconds: StoryObj<typeof PleaseWait> = {
args: {
grow: true,
startedTimeStamp: Date.now() - 5000,
},
}

export const After15Seconds: StoryObj<typeof PleaseWait> = {
args: {
grow: true,
startedTimeStamp: Date.now() - 15000,
},
}
108 changes: 108 additions & 0 deletions src/components/ui/feedback/PleaseWait.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
import {act, render} from '@testing-library/react-native'
import {type ComponentProps} from 'react'
import {PleaseWait} from '@/components/ui/feedback/PleaseWait'
import {StoreProvider} from '@/providers/store.provider'

describe('PleaseWait', () => {
beforeEach(() => {
jest.useFakeTimers()
jest.setSystemTime(new Date('2026-01-01T12:00:00.000Z'))
})

afterEach(() => {
jest.useRealTimers()
})

const renderPleaseWait = (
props: ComponentProps<typeof PleaseWait> | Record<string, unknown>,
) =>
render(
<StoreProvider>
<PleaseWait {...(props as ComponentProps<typeof PleaseWait>)} />
</StoreProvider>,
)

it('renders the spinner with a valid testID', () => {
const {getByTestId, queryByTestId} = renderPleaseWait({
testID: 'PleaseWait',
})

expect(getByTestId('PleaseWait')).toBeTruthy()
expect(queryByTestId('PleaseWaitFeedbackPhrase')).toBeNull()
})

it('does not render feedback when showFeedback is undefined or null', () => {
const undefinedPropsRender = renderPleaseWait({showFeedback: undefined})

act(() => {
jest.advanceTimersByTime(16000)
})

expect(
undefinedPropsRender.queryByTestId('PleaseWaitFeedbackPhrase'),
).toBeNull()

undefinedPropsRender.unmount()

const nullPropsRender = renderPleaseWait({showFeedback: null})

act(() => {
jest.advanceTimersByTime(16000)
})

expect(nullPropsRender.queryByTestId('PleaseWaitFeedbackPhrase')).toBeNull()
})

it('shows the first feedback message after five seconds when showFeedback is true, and still at 14.9 seconds', () => {
const {queryByText} = renderPleaseWait({showFeedback: true})

act(() => {
jest.advanceTimersByTime(5000)
})

expect(queryByText('Gegevens worden geladen')).toBeTruthy()

act(() => {
jest.advanceTimersByTime(9999)
})

expect(queryByText('Gegevens worden geladen')).toBeTruthy()

act(() => {
jest.advanceTimersByTime(1)
})

expect(queryByText('Gegevens worden geladen')).not.toBeTruthy()
expect(
queryByText('Dit duurt langer dan normaal. \n We zijn nog bezig.'),
).toBeTruthy()
})

it('shows the second feedback message after fifteen seconds when startedTimeStamp is valid, and infinitely beyond that', () => {
const {queryByText} = renderPleaseWait({startedTimeStamp: Date.now()})

act(() => {
jest.advanceTimersByTime(5000)
})

expect(
queryByText('Dit duurt langer dan normaal. \n We zijn nog bezig.'),
).not.toBeTruthy()

act(() => {
jest.advanceTimersByTime(10000)
})

expect(
queryByText('Dit duurt langer dan normaal. \n We zijn nog bezig.'),
).toBeTruthy()

act(() => {
jest.advanceTimersByTime(60000)
})

expect(
queryByText('Dit duurt langer dan normaal. \n We zijn nog bezig.'),
).toBeTruthy()
})
})
101 changes: 87 additions & 14 deletions src/components/ui/feedback/PleaseWait.tsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,94 @@
import {useEffect, useMemo, useRef, useState} from 'react'
import {Box} from '@/components/ui/containers/Box'
import {Center} from '@/components/ui/layout/Center'
import {Column} from '@/components/ui/layout/Column'
import {Icon} from '@/components/ui/media/Icon'
import {Phrase} from '@/components/ui/text/Phrase'
import {type TestProps} from '@/components/ui/types'
import {dayjs} from '@/utils/datetime/dayjs'

type Props = {
grow?: boolean
} & TestProps

export const PleaseWait = ({grow, testID}: Props) => (
<Center grow={grow}>
<Box>
<Icon
color="link"
name="spinner"
size="lg"
testID={testID}
/>
</Box>
</Center>
)
} & TestProps &
Or<
{
/**
* Add a timestamp to start a timer which shows textual loading timeout feedback.
*/
startedTimeStamp?: number
},
{
/**
* Starts a timer from time of mount (as ref) and show textual loading timeout feedback.
*/
showFeedback?: true
}
>

const FIRST_TIMEOUT_VALUE = 5
const SECOND_TIMEOUT_VALUE = 15

const getElapsedTimeFeedback = (elapsedTime: number) => {
if (
elapsedTime >= FIRST_TIMEOUT_VALUE &&
elapsedTime < SECOND_TIMEOUT_VALUE
) {
return 'Gegevens worden geladen'
} else if (elapsedTime >= SECOND_TIMEOUT_VALUE) {
return 'Dit duurt langer dan normaal. \n We zijn nog bezig.'

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

de tweede regel begint nu met een spatie

Suggested change
return 'Dit duurt langer dan normaal. \n We zijn nog bezig.'
return 'Dit duurt langer dan normaal. \nWe zijn nog bezig.'

}
}

export const PleaseWait = ({
grow,
startedTimeStamp,
showFeedback,
testID,
}: Props) => {
const [elapsedTime, setElapsedTime] = useState(0)
const startTimeRef = useRef<number | null>(showFeedback ? Date.now() : null)
useEffect(() => {

Check warning on line 50 in src/components/ui/feedback/PleaseWait.tsx

View workflow job for this annotation

GitHub Actions / typing-and-linting

Expected blank line before this statement
const countFrom = startedTimeStamp || startTimeRef.current

if (!countFrom) {
return
}

const interval = setInterval(() => {
setElapsedTime(Math.abs(dayjs(countFrom).diff()))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Als je als unit second meegeeft, dan hoef je niet door 1000 te delen later:

Suggested change
setElapsedTime(Math.abs(dayjs(countFrom).diff()))
setElapsedTime(Math.abs(dayjs(countFrom).diff(dayjs(), 'second')))

docs: https://day.js.org/docs/en/display/difference

}, 1000)

return () => {
clearInterval(interval)
}
Comment thread
Copilot marked this conversation as resolved.
}, [startedTimeStamp, startTimeRef])

const elapsedSeconds = Math.floor(elapsedTime / 1000)

const feedback = useMemo(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

ik denk dat het berekenen van de cache check (van useMemo om te kijken of de oude waarde gebruikt moet worden) meer rekenkracht kost hier dan getElapsedTimeFeedback zelf uitvoeren

() => getElapsedTimeFeedback(elapsedSeconds),
[elapsedSeconds],
)

return (
<Center grow={grow}>
<Box>
<Column gutter="md">
<Icon
color="link"
name="spinner"
size="lg"
testID={testID}
/>
{!!feedback && (
<Phrase
testID="PleaseWaitFeedbackPhrase"
textAlign="center">
{feedback}
</Phrase>
)}
</Column>
</Box>
</Center>
)
}
7 changes: 6 additions & 1 deletion src/modules/boat-charging/components/BoatChargingDetails.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,12 @@ export const BoatChargingDetails = ({id}: {id: BoatChargingLocation['id']}) => {
const form = useNewSessionFormContext()

if (isLoadingLocation || isLoadingSessions) {
return <PleaseWait testID="BoatChargingDetailsPleaseWait" />
return (
<PleaseWait
showFeedback
testID="BoatChargingDetailsPleaseWait"
/>
)
}

if (isErrorLocation || isErrorSessions || !location) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,12 @@ export const BoatChargingHistorySessionDetails = () => {
const {toggle} = useBottomSheet()

if (isLoading) {
return <PleaseWait testID="BoatChargingHistorySessionDetailsPleaseWait" />
return (
<PleaseWait
showFeedback
testID="BoatChargingHistorySessionDetailsPleaseWait"
/>
)
}

if (!session) {
Expand Down
14 changes: 12 additions & 2 deletions src/modules/boat-charging/components/BoatChargingList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -60,11 +60,21 @@ export const BoatChargingList = ({
}, [filteredFeatures, address])

if (isLoading) {
return <PleaseWait testID="BoatChargingListPleaseWait" />
return (
<PleaseWait
showFeedback
testID="BoatChargingListPleaseWait"
/>
)
}

if (isError) {
return <SomethingWentWrong testID="BoatChargingListSomethingWentWrong" />
return (
<SomethingWentWrong
inset="md"
testID="BoatChargingListSomethingWentWrong"
/>
)
}

return (
Expand Down
14 changes: 12 additions & 2 deletions src/modules/boat-charging/components/BoatChargingMap.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -55,11 +55,21 @@ export const BoatChargingMap = ({
})

if (isLoading) {
return <PleaseWait testID="BoatChargingMapPleaseWait" />
return (
<PleaseWait
showFeedback
testID="BoatChargingMapPleaseWait"
/>
)
}

if (isError) {
return <SomethingWentWrong testID="BoatChargingMapSomethingWentWrong" />
return (
<SomethingWentWrong
inset="md"
testID="BoatChargingMapSomethingWentWrong"
/>
)
}

return (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ export const BoatChargingPointDetails = () => {
data: location,
isLoading,
isError,
startedTimeStamp,
} = useBoatChargingLocationDetailsQuery(id ?? skipToken)

const autoFocus = useAccessibilityFocus()
Expand Down Expand Up @@ -68,14 +69,20 @@ export const BoatChargingPointDetails = () => {
const {reset} = useNewSessionFormContext()

if (isLoading) {
return <PleaseWait testID="BoatChargingPointDetailsPleaseWait" />
return (
<PleaseWait
startedTimeStamp={startedTimeStamp}
testID="BoatChargingPointDetailsPleaseWait"
/>
)
}

if (isError || !location) {
return (
<Box>
<SomethingWentWrong testID="BoatChargingPointDetailsSomethingWentWrong" />
</Box>
<SomethingWentWrong
inset="md"
testID="BoatChargingPointDetailsSomethingWentWrong"
/>
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,12 @@ export const BoatChargingHistory = () => {
}

if (result.isLoading && result.data.length === 0) {
return <PleaseWait testID="BoatChargingHistoryPleaseWait" />
return (
<PleaseWait
showFeedback
testID="BoatChargingHistoryPleaseWait"
/>
)
}

if (result.isError) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,14 +39,20 @@ export const BoatChargingSession = () => {
}, [session?.status, navigation, session?.id])

if (isLoading) {
return <PleaseWait testID="BoatChargingSessionPleaseWait" />
return (
<PleaseWait
showFeedback
testID="BoatChargingSessionPleaseWait"
/>
)
}

if (isError || !session) {
return (
<Box>
<SomethingWentWrong testID="BoatChargingSessionSomethingWentWrong" />
</Box>
<SomethingWentWrong
inset="md"
testID="BoatChargingSessionSomethingWentWrong"
/>
)
}

Expand Down
Loading
Loading