use useInterval hook in ui-notification-toast

Signed-off-by: Tilman Vatteroth <git@tilmanvatteroth.de>
This commit is contained in:
Tilman Vatteroth 2021-11-21 18:22:22 +01:00
parent a96b06c95b
commit 1adb1bd52f

View file

@ -4,16 +4,17 @@
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
import React, { Fragment, useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react' import React, { Fragment, useCallback, useEffect, useMemo, useState } from 'react'
import { Button, ProgressBar, Toast } from 'react-bootstrap' import { Button, ProgressBar, Toast } from 'react-bootstrap'
import type { UiNotification } from '../../redux/ui-notifications/types' import type { UiNotification } from '../../redux/ui-notifications/types'
import { ForkAwesomeIcon } from '../common/fork-awesome/fork-awesome-icon' import { ForkAwesomeIcon } from '../common/fork-awesome/fork-awesome-icon'
import { ShowIf } from '../common/show-if/show-if' import { ShowIf } from '../common/show-if/show-if'
import type { IconName } from '../common/fork-awesome/types' import type { IconName } from '../common/fork-awesome/types'
import { dismissUiNotification } from '../../redux/ui-notifications/methods'
import { Trans, useTranslation } from 'react-i18next' import { Trans, useTranslation } from 'react-i18next'
import { Logger } from '../../utils/logger' import { Logger } from '../../utils/logger'
import { cypressId } from '../../utils/cypress-attribute' import { cypressId } from '../../utils/cypress-attribute'
import { useEffectOnce, useInterval } from 'react-use'
import { dismissUiNotification } from '../../redux/ui-notifications/methods'
const STEPS_PER_SECOND = 10 const STEPS_PER_SECOND = 10
const log = new Logger('UiNotificationToast') const log = new Logger('UiNotificationToast')
@ -35,56 +36,35 @@ export const UiNotificationToast: React.FC<UiNotificationProps> = ({
buttons buttons
}) => { }) => {
const { t } = useTranslation() const { t } = useTranslation()
const [eta, setEta] = useState<number>() const [remainingSteps, setRemainingSteps] = useState<number>(() => durationInSecond * STEPS_PER_SECOND)
const interval = useRef<NodeJS.Timeout | undefined>(undefined)
const deleteInterval = useCallback(() => { const dismissNow = useCallback(() => {
if (interval.current) { log.debug(`Dismiss notification ${notificationId} immediately`)
clearInterval(interval.current) setRemainingSteps(0)
}
}, [])
const dismissThisNotification = useCallback(() => {
log.debug(`Dismissed notification ${notificationId}`)
dismissUiNotification(notificationId)
}, [notificationId]) }, [notificationId])
useLayoutEffect(() => { useEffectOnce(() => {
if (dismissed || !!interval.current) {
return
}
log.debug(`Show notification ${notificationId}`) log.debug(`Show notification ${notificationId}`)
setEta(durationInSecond * STEPS_PER_SECOND) })
interval.current = setInterval(
() => useInterval(
setEta((lastETA) => { () => setRemainingSteps((lastRemainingSteps) => lastRemainingSteps - 1),
if (lastETA === undefined) { useMemo(() => (dismissed || remainingSteps <= 0 ? null : 1000 / STEPS_PER_SECOND), [dismissed, remainingSteps])
return )
} else if (lastETA <= 0) {
return 0
} else {
return lastETA - 1
}
}),
1000 / STEPS_PER_SECOND
)
return () => {
deleteInterval()
}
}, [deleteInterval, dismissThisNotification, dismissed, durationInSecond, notificationId])
useEffect(() => { useEffect(() => {
if (eta === 0) { if (remainingSteps <= 0 && !dismissed) {
dismissThisNotification() log.debug(`Dismiss notification ${notificationId}`)
dismissUiNotification(notificationId)
} }
}, [dismissThisNotification, eta]) }, [dismissed, remainingSteps, notificationId])
const buttonsDom = useMemo( const buttonsDom = useMemo(
() => () =>
buttons?.map((button, buttonIndex) => { buttons?.map((button, buttonIndex) => {
const buttonClick = () => { const buttonClick = () => {
button.onClick() button.onClick()
dismissThisNotification() dismissNow()
} }
return ( return (
<Button key={buttonIndex} size={'sm'} onClick={buttonClick} variant={'link'}> <Button key={buttonIndex} size={'sm'} onClick={buttonClick} variant={'link'}>
@ -92,7 +72,7 @@ export const UiNotificationToast: React.FC<UiNotificationProps> = ({
</Button> </Button>
) )
}), }),
[buttons, dismissThisNotification] [buttons, dismissNow]
) )
const contentDom = useMemo(() => { const contentDom = useMemo(() => {
@ -109,10 +89,7 @@ export const UiNotificationToast: React.FC<UiNotificationProps> = ({
}, [contentI18nKey, contentI18nOptions, t]) }, [contentI18nKey, contentI18nOptions, t])
return ( return (
<Toast <Toast show={!dismissed} onClose={dismissNow} {...cypressId('notification-toast')}>
show={!dismissed && eta !== undefined}
onClose={dismissThisNotification}
{...cypressId('notification-toast')}>
<Toast.Header> <Toast.Header>
<strong className='mr-auto'> <strong className='mr-auto'>
<ShowIf condition={!!icon}> <ShowIf condition={!!icon}>
@ -123,7 +100,12 @@ export const UiNotificationToast: React.FC<UiNotificationProps> = ({
<small>{date.toRelative({ style: 'short' })}</small> <small>{date.toRelative({ style: 'short' })}</small>
</Toast.Header> </Toast.Header>
<Toast.Body>{contentDom}</Toast.Body> <Toast.Body>{contentDom}</Toast.Body>
<ProgressBar variant={'info'} now={eta} max={durationInSecond * STEPS_PER_SECOND} min={0} /> <ProgressBar
variant={'info'}
now={remainingSteps}
max={durationInSecond * STEPS_PER_SECOND}
min={STEPS_PER_SECOND}
/>
<div>{buttonsDom}</div> <div>{buttonsDom}</div>
</Toast> </Toast>
) )