Dismiss notifications from component
This commit is contained in:
parent
67d9177a91
commit
fcb47a2cc5
@ -3,8 +3,6 @@ import { Dispatch } from 'redux'
|
|||||||
import * as constants from '../constants'
|
import * as constants from '../constants'
|
||||||
import { ThunkResult } from '../store'
|
import { ThunkResult } from '../store'
|
||||||
|
|
||||||
const TIMEOUT = 5000
|
|
||||||
|
|
||||||
function format (string: string, args: string[]) {
|
function format (string: string, args: string[]) {
|
||||||
string = args
|
string = args
|
||||||
.reduce((string, arg, i) => string.replace('{' + i + '}', arg), string)
|
.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 id = uniqueId('notification')
|
||||||
const payload: Notification = { id, type, message }
|
const payload: Notification = { id, type, message }
|
||||||
|
|
||||||
setTimeout(() => {
|
|
||||||
dispatch(dismissNotification(id))
|
|
||||||
}, TIMEOUT)
|
|
||||||
|
|
||||||
return addNotification(payload)
|
return addNotification(payload)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -45,7 +39,7 @@ function addNotification(payload: Notification): NotificationAddAction {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function dismissNotification(id: string): NotificationDismissAction {
|
export function dismissNotification(id: string): NotificationDismissAction {
|
||||||
return {
|
return {
|
||||||
type: constants.NOTIFY_DISMISS,
|
type: constants.NOTIFY_DISMISS,
|
||||||
payload: { id },
|
payload: { id },
|
||||||
|
|||||||
@ -2,7 +2,7 @@ import map from 'lodash/map'
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
import Peer from 'simple-peer'
|
import Peer from 'simple-peer'
|
||||||
import { Message } from '../actions/ChatActions'
|
import { Message } from '../actions/ChatActions'
|
||||||
import { Alert, Notification } from '../actions/NotifyActions'
|
import { Alert, Notification, dismissNotification } from '../actions/NotifyActions'
|
||||||
import { TextMessage } from '../actions/PeerActions'
|
import { TextMessage } from '../actions/PeerActions'
|
||||||
import { AddStreamPayload } from '../actions/StreamActions'
|
import { AddStreamPayload } from '../actions/StreamActions'
|
||||||
import * as constants from '../constants'
|
import * as constants from '../constants'
|
||||||
@ -17,6 +17,7 @@ export interface AppProps {
|
|||||||
active: string | null
|
active: string | null
|
||||||
alerts: Alert[]
|
alerts: Alert[]
|
||||||
dismissAlert: (alert: Alert) => void
|
dismissAlert: (alert: Alert) => void
|
||||||
|
dismissNotification: typeof dismissNotification
|
||||||
init: () => void
|
init: () => void
|
||||||
notifications: Record<string, Notification>
|
notifications: Record<string, Notification>
|
||||||
messages: Message[]
|
messages: Message[]
|
||||||
@ -63,6 +64,7 @@ export default class App extends React.PureComponent<AppProps, AppState> {
|
|||||||
active,
|
active,
|
||||||
alerts,
|
alerts,
|
||||||
dismissAlert,
|
dismissAlert,
|
||||||
|
dismissNotification,
|
||||||
notifications,
|
notifications,
|
||||||
messages,
|
messages,
|
||||||
messagesCount,
|
messagesCount,
|
||||||
@ -86,7 +88,10 @@ export default class App extends React.PureComponent<AppProps, AppState> {
|
|||||||
stream={streams[constants.ME]}
|
stream={streams[constants.ME]}
|
||||||
/>
|
/>
|
||||||
<Alerts alerts={alerts} dismiss={dismissAlert} />
|
<Alerts alerts={alerts} dismiss={dismissAlert} />
|
||||||
<Notifications notifications={notifications} />
|
<Notifications
|
||||||
|
dismiss={dismissNotification}
|
||||||
|
notifications={notifications}
|
||||||
|
/>
|
||||||
<Media />
|
<Media />
|
||||||
<Chat
|
<Chat
|
||||||
messages={messages}
|
messages={messages}
|
||||||
|
|||||||
45
src/client/components/Notifications.test.tsx
Normal file
45
src/client/components/Notifications.test.tsx
Normal 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' ]])
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
||||||
@ -2,10 +2,11 @@ import CSSTransition from 'react-transition-group/CSSTransition'
|
|||||||
import TransitionGroup from 'react-transition-group/TransitionGroup'
|
import TransitionGroup from 'react-transition-group/TransitionGroup'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import classnames from 'classnames'
|
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>
|
notifications: Record<string, Notification>
|
||||||
|
dismiss: typeof dismissNotification
|
||||||
max: number
|
max: number
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -14,13 +15,37 @@ const transitionTimeout = {
|
|||||||
exit: 100,
|
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
|
export default class Notifications
|
||||||
extends React.PureComponent<NotificationProps> {
|
extends React.PureComponent<NotificationsProps> {
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
max: 10,
|
max: 10,
|
||||||
}
|
}
|
||||||
render () {
|
render () {
|
||||||
const { notifications, max } = this.props
|
const { dismiss, notifications, max } = this.props
|
||||||
return (
|
return (
|
||||||
<div className="notifications">
|
<div className="notifications">
|
||||||
<TransitionGroup>
|
<TransitionGroup>
|
||||||
@ -30,11 +55,11 @@ extends React.PureComponent<NotificationProps> {
|
|||||||
classNames='fade'
|
classNames='fade'
|
||||||
timeout={transitionTimeout}
|
timeout={transitionTimeout}
|
||||||
>
|
>
|
||||||
<div
|
<Notification
|
||||||
className={classnames(notifications[id].type, 'notification')}
|
notification={notifications[id]}
|
||||||
>
|
dismiss={dismiss}
|
||||||
{notifications[id].message}
|
timeout={10000}
|
||||||
</div>
|
/>
|
||||||
</CSSTransition>
|
</CSSTransition>
|
||||||
))}
|
))}
|
||||||
</TransitionGroup>
|
</TransitionGroup>
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import { connect } from 'react-redux'
|
import { connect } from 'react-redux'
|
||||||
import { init } from '../actions/CallActions'
|
import { init } from '../actions/CallActions'
|
||||||
import { play } from '../actions/MediaActions'
|
import { play } from '../actions/MediaActions'
|
||||||
import { dismissAlert } from '../actions/NotifyActions'
|
import { dismissAlert, dismissNotification } from '../actions/NotifyActions'
|
||||||
import { sendFile, sendMessage } from '../actions/PeerActions'
|
import { sendFile, sendMessage } from '../actions/PeerActions'
|
||||||
import { toggleActive } from '../actions/StreamActions'
|
import { toggleActive } from '../actions/StreamActions'
|
||||||
import App from '../components/App'
|
import App from '../components/App'
|
||||||
@ -23,6 +23,7 @@ const mapDispatchToProps = {
|
|||||||
toggleActive,
|
toggleActive,
|
||||||
sendMessage,
|
sendMessage,
|
||||||
dismissAlert: dismissAlert,
|
dismissAlert: dismissAlert,
|
||||||
|
dismissNotification,
|
||||||
init,
|
init,
|
||||||
onSendFile: sendFile,
|
onSendFile: sendFile,
|
||||||
play,
|
play,
|
||||||
|
|||||||
@ -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', () => {
|
it('does not fail when no arguments', () => {
|
||||||
notifyActions[type]()
|
notifyActions[type]()
|
||||||
})
|
})
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user