Dismiss notifications from component

This commit is contained in:
Jerko Steiner 2019-11-17 14:10:33 -03:00
parent 67d9177a91
commit fcb47a2cc5
6 changed files with 89 additions and 24 deletions

View File

@ -3,8 +3,6 @@ import { Dispatch } from 'redux'
import * as constants from '../constants'
import { ThunkResult } from '../store'
const TIMEOUT = 5000
function format (string: string, args: string[]) {
string = args
.reduce((string, arg, i) => string.replace('{' + i + '}', arg), string)
@ -19,10 +17,6 @@ function notify(dispatch: Dispatch, type: NotifyType, args: string[]) {
const id = uniqueId('notification')
const payload: Notification = { id, type, message }
setTimeout(() => {
dispatch(dismissNotification(id))
}, TIMEOUT)
return addNotification(payload)
}
@ -45,7 +39,7 @@ function addNotification(payload: Notification): NotificationAddAction {
}
}
function dismissNotification(id: string): NotificationDismissAction {
export function dismissNotification(id: string): NotificationDismissAction {
return {
type: constants.NOTIFY_DISMISS,
payload: { id },

View File

@ -2,7 +2,7 @@ import map from 'lodash/map'
import React from 'react'
import Peer from 'simple-peer'
import { Message } from '../actions/ChatActions'
import { Alert, Notification } from '../actions/NotifyActions'
import { Alert, Notification, dismissNotification } from '../actions/NotifyActions'
import { TextMessage } from '../actions/PeerActions'
import { AddStreamPayload } from '../actions/StreamActions'
import * as constants from '../constants'
@ -17,6 +17,7 @@ export interface AppProps {
active: string | null
alerts: Alert[]
dismissAlert: (alert: Alert) => void
dismissNotification: typeof dismissNotification
init: () => void
notifications: Record<string, Notification>
messages: Message[]
@ -63,6 +64,7 @@ export default class App extends React.PureComponent<AppProps, AppState> {
active,
alerts,
dismissAlert,
dismissNotification,
notifications,
messages,
messagesCount,
@ -86,7 +88,10 @@ export default class App extends React.PureComponent<AppProps, AppState> {
stream={streams[constants.ME]}
/>
<Alerts alerts={alerts} dismiss={dismissAlert} />
<Notifications notifications={notifications} />
<Notifications
dismiss={dismissNotification}
notifications={notifications}
/>
<Media />
<Chat
messages={messages}

View File

@ -0,0 +1,45 @@
import Notifications from './Notifications'
import React from 'react'
import ReactDOM from 'react-dom'
import { Notification, NotificationDismissAction } from '../actions/NotifyActions'
describe('Notifications', () => {
let notifications: Record<string, Notification>
let dismiss: jest.Mock<NotificationDismissAction, [string]>
beforeEach(() => {
notifications = {
one: {
id: 'one',
message: 'test',
type: 'error',
},
}
dismiss = jest.fn()
})
let div: HTMLDivElement
async function render() {
div = document.createElement('div')
return new Promise<Notifications>(resolve => {
ReactDOM.render(
<Notifications
ref={n => resolve(n!)}
notifications={notifications}
dismiss={dismiss}
/>,
div,
)
})
}
describe('render', () => {
it('renders and sets a timeout for notification', async () => {
await render()
expect(dismiss.mock.calls).toEqual([])
ReactDOM.unmountComponentAtNode(div)
expect(dismiss.mock.calls).toEqual([[ 'one' ]])
})
})
})

View File

@ -2,10 +2,11 @@ import CSSTransition from 'react-transition-group/CSSTransition'
import TransitionGroup from 'react-transition-group/TransitionGroup'
import React from 'react'
import classnames from 'classnames'
import { Notification } from '../actions/NotifyActions'
import { dismissNotification, Notification } from '../actions/NotifyActions'
export interface NotificationProps {
export interface NotificationsProps {
notifications: Record<string, Notification>
dismiss: typeof dismissNotification
max: number
}
@ -14,13 +15,37 @@ const transitionTimeout = {
exit: 100,
}
export interface NotificationProps {
notification: Notification
dismiss: typeof dismissNotification
timeout: number
}
const Notification = React.memo(
function Notification(props: NotificationProps) {
const { dismiss, notification } = props
React.useEffect(() => {
const timeout = setTimeout(dismiss, props.timeout, notification.id)
return () => {
clearTimeout(timeout)
dismiss(notification.id)
}
}, [])
return (
<div className={classnames(notification.type, 'notification')}>
{notification.message}
</div>
)
},
)
export default class Notifications
extends React.PureComponent<NotificationProps> {
extends React.PureComponent<NotificationsProps> {
static defaultProps = {
max: 10,
}
render () {
const { notifications, max } = this.props
const { dismiss, notifications, max } = this.props
return (
<div className="notifications">
<TransitionGroup>
@ -30,11 +55,11 @@ extends React.PureComponent<NotificationProps> {
classNames='fade'
timeout={transitionTimeout}
>
<div
className={classnames(notifications[id].type, 'notification')}
>
{notifications[id].message}
</div>
<Notification
notification={notifications[id]}
dismiss={dismiss}
timeout={10000}
/>
</CSSTransition>
))}
</TransitionGroup>

View File

@ -1,7 +1,7 @@
import { connect } from 'react-redux'
import { init } from '../actions/CallActions'
import { play } from '../actions/MediaActions'
import { dismissAlert } from '../actions/NotifyActions'
import { dismissAlert, dismissNotification } from '../actions/NotifyActions'
import { sendFile, sendMessage } from '../actions/PeerActions'
import { toggleActive } from '../actions/StreamActions'
import App from '../components/App'
@ -23,6 +23,7 @@ const mapDispatchToProps = {
toggleActive,
sendMessage,
dismissAlert: dismissAlert,
dismissNotification,
init,
onSendFile: sendFile,
play,

View File

@ -83,11 +83,6 @@ describe('reducers/alerts', () => {
}])
})
it('dismisses notification after a timeout', () => {
jest.runAllTimers()
expect(store.getState().notifications).toEqual({})
})
it('does not fail when no arguments', () => {
notifyActions[type]()
})