mirror of
https://github.com/hedgedoc/hedgedoc.git
synced 2025-05-15 07:34:42 -04:00
92 lines
3 KiB
TypeScript
92 lines
3 KiB
TypeScript
/*
|
|
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
|
|
*
|
|
* SPDX-License-Identifier: AGPL-3.0-only
|
|
*/
|
|
|
|
import React, { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react'
|
|
import { Button, ProgressBar, Toast } from 'react-bootstrap'
|
|
import { UiNotification } from '../../redux/ui-notifications/types'
|
|
import { ForkAwesomeIcon } from '../common/fork-awesome/fork-awesome-icon'
|
|
import { ShowIf } from '../common/show-if/show-if'
|
|
import { IconName } from '../common/fork-awesome/types'
|
|
import { dismissUiNotification } from '../../redux/ui-notifications/methods'
|
|
|
|
const STEPS_PER_SECOND = 10
|
|
|
|
export interface UiNotificationProps extends UiNotification {
|
|
notificationId: number
|
|
}
|
|
|
|
export const UiNotificationToast: React.FC<UiNotificationProps> = ({ title, content, date, icon, dismissed, notificationId, durationInSecond, buttons }) => {
|
|
const [eta, setEta] = useState<number>()
|
|
const interval = useRef<NodeJS.Timeout | undefined>(undefined)
|
|
|
|
const deleteInterval = useCallback(() => {
|
|
if (interval.current) {
|
|
clearInterval(interval.current)
|
|
}
|
|
}, [])
|
|
|
|
const dismissThisNotification = useCallback(() => {
|
|
console.debug(`[Notifications] Dismissed notification ${ notificationId }`)
|
|
dismissUiNotification(notificationId)
|
|
}, [notificationId])
|
|
|
|
useLayoutEffect(() => {
|
|
if (dismissed || !!interval.current) {
|
|
return
|
|
}
|
|
console.debug(`[Notifications] Show notification ${ notificationId }`)
|
|
setEta(durationInSecond * STEPS_PER_SECOND)
|
|
interval.current = setInterval(() => setEta((lastETA) => {
|
|
if (lastETA === undefined) {
|
|
return
|
|
} else if (lastETA <= 0) {
|
|
return 0
|
|
} else {
|
|
return lastETA - 1
|
|
}
|
|
}), 1000 / STEPS_PER_SECOND)
|
|
return () => {
|
|
deleteInterval()
|
|
}
|
|
}, [deleteInterval, dismissThisNotification, dismissed, durationInSecond, notificationId])
|
|
|
|
useEffect(() => {
|
|
if (eta === 0) {
|
|
dismissThisNotification()
|
|
}
|
|
}, [dismissThisNotification, eta])
|
|
|
|
const buttonsDom = useMemo(() => buttons?.map(button => {
|
|
const buttonClick = () => {
|
|
button.onClick()
|
|
dismissThisNotification()
|
|
}
|
|
return <Button key={ button.label } size={ 'sm' } onClick={ buttonClick }
|
|
variant={ 'link' }>{ button.label }</Button>
|
|
}
|
|
), [buttons, dismissThisNotification])
|
|
|
|
return (
|
|
<Toast show={ !dismissed && eta !== undefined } onClose={ dismissThisNotification }>
|
|
<Toast.Header>
|
|
<strong className="mr-auto">
|
|
<ShowIf condition={ !!icon }>
|
|
<ForkAwesomeIcon icon={ icon as IconName } fixedWidth={ true } className={ 'mr-1' }/>
|
|
</ShowIf>
|
|
{ title }
|
|
</strong>
|
|
<small>{ date.toRelative({ style: 'short' }) }</small>
|
|
</Toast.Header>
|
|
<Toast.Body>{ content }</Toast.Body>
|
|
<ProgressBar variant={ 'info' } now={ eta } max={ durationInSecond * STEPS_PER_SECOND } min={ 0 }/>
|
|
<div>
|
|
{
|
|
buttonsDom
|
|
}
|
|
</div>
|
|
</Toast>
|
|
)
|
|
}
|