Remove alerts from state
This commit is contained in:
parent
fcb47a2cc5
commit
58039eb086
@ -70,57 +70,6 @@ export const clear = (): NotificationClearAction => ({
|
||||
type: constants.NOTIFY_CLEAR,
|
||||
})
|
||||
|
||||
export interface Alert {
|
||||
action?: string
|
||||
dismissable: boolean
|
||||
message: string
|
||||
type: NotifyType
|
||||
}
|
||||
|
||||
export interface AlertAddAction {
|
||||
type: 'ALERT'
|
||||
payload: Alert
|
||||
}
|
||||
|
||||
export function alert (message: string, dismissable = false): AlertAddAction {
|
||||
return {
|
||||
type: constants.ALERT,
|
||||
payload: {
|
||||
action: dismissable ? 'Dismiss' : undefined,
|
||||
dismissable: !!dismissable,
|
||||
message,
|
||||
type: 'warning',
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
export interface AlertDismissAction {
|
||||
type: 'ALERT_DISMISS'
|
||||
payload: Alert
|
||||
}
|
||||
|
||||
export const dismissAlert = (alert: Alert): AlertDismissAction => {
|
||||
return {
|
||||
type: constants.ALERT_DISMISS,
|
||||
payload: alert,
|
||||
}
|
||||
}
|
||||
|
||||
export interface AlertClearAction {
|
||||
type: 'ALERT_CLEAR'
|
||||
}
|
||||
|
||||
export const clearAlerts = (): AlertClearAction => {
|
||||
return {
|
||||
type: constants.ALERT_CLEAR,
|
||||
}
|
||||
}
|
||||
|
||||
export type AlertActionType =
|
||||
AlertAddAction |
|
||||
AlertDismissAction |
|
||||
AlertClearAction
|
||||
|
||||
export type NotificationActionType =
|
||||
NotificationAddAction |
|
||||
NotificationDismissAction |
|
||||
|
||||
@ -1,48 +1,29 @@
|
||||
import React from 'react'
|
||||
import classnames from 'classnames'
|
||||
import { Alert as AlertType } from '../actions/NotifyActions'
|
||||
|
||||
export interface AlertProps {
|
||||
alert: AlertType
|
||||
dismiss: (alert: AlertType) => void
|
||||
children: React.ReactNode
|
||||
}
|
||||
|
||||
export class Alert extends React.PureComponent<AlertProps> {
|
||||
dismiss = () => {
|
||||
const { alert, dismiss } = this.props
|
||||
dismiss(alert)
|
||||
}
|
||||
render () {
|
||||
const { alert } = this.props
|
||||
|
||||
export const Alert = React.memo(
|
||||
function Alert(props: AlertProps) {
|
||||
return (
|
||||
<div className={classnames('alert', alert.type)}>
|
||||
<span>{alert.message}</span>
|
||||
{alert.dismissable && (
|
||||
<button
|
||||
className="action-alert-dismiss"
|
||||
onClick={this.dismiss}
|
||||
>{alert.action}</button>
|
||||
)}
|
||||
<div className='alert'>
|
||||
{props.children}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
export interface AlertsProps {
|
||||
alerts: AlertType[]
|
||||
dismiss: (alert: AlertType) => void
|
||||
children: React.ReactNode
|
||||
}
|
||||
|
||||
export default class Alerts extends React.PureComponent<AlertsProps> {
|
||||
render () {
|
||||
const { alerts, dismiss } = this.props
|
||||
export const Alerts = React.memo(
|
||||
function Alerts(props: AlertsProps) {
|
||||
return (
|
||||
<div className="alerts">
|
||||
{alerts.map((alert, i) => (
|
||||
<Alert alert={alert} dismiss={dismiss} key={i} />
|
||||
))}
|
||||
{props.children}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
@ -2,11 +2,10 @@ import map from 'lodash/map'
|
||||
import React from 'react'
|
||||
import Peer from 'simple-peer'
|
||||
import { Message } from '../actions/ChatActions'
|
||||
import { Alert, Notification, dismissNotification } from '../actions/NotifyActions'
|
||||
import { Notification, dismissNotification } from '../actions/NotifyActions'
|
||||
import { TextMessage } from '../actions/PeerActions'
|
||||
import { AddStreamPayload } from '../actions/StreamActions'
|
||||
import * as constants from '../constants'
|
||||
import Alerts from './Alerts'
|
||||
import Chat from './Chat'
|
||||
import Notifications from './Notifications'
|
||||
import Toolbar from './Toolbar'
|
||||
@ -15,8 +14,6 @@ import { Media } from './Media'
|
||||
|
||||
export interface AppProps {
|
||||
active: string | null
|
||||
alerts: Alert[]
|
||||
dismissAlert: (alert: Alert) => void
|
||||
dismissNotification: typeof dismissNotification
|
||||
init: () => void
|
||||
notifications: Record<string, Notification>
|
||||
@ -62,8 +59,6 @@ export default class App extends React.PureComponent<AppProps, AppState> {
|
||||
render () {
|
||||
const {
|
||||
active,
|
||||
alerts,
|
||||
dismissAlert,
|
||||
dismissNotification,
|
||||
notifications,
|
||||
messages,
|
||||
@ -87,7 +82,6 @@ export default class App extends React.PureComponent<AppProps, AppState> {
|
||||
onSendFile={onSendFile}
|
||||
stream={streams[constants.ME]}
|
||||
/>
|
||||
<Alerts alerts={alerts} dismiss={dismissAlert} />
|
||||
<Notifications
|
||||
dismiss={dismissNotification}
|
||||
notifications={notifications}
|
||||
|
||||
@ -3,6 +3,7 @@ import { connect } from 'react-redux'
|
||||
import { AudioConstraint, MediaDevice, setAudioConstraint, setVideoConstraint, VideoConstraint, getMediaStream, enumerateDevices, play } from '../actions/MediaActions'
|
||||
import { MediaState } from '../reducers/media'
|
||||
import { State } from '../store'
|
||||
import { Alerts, Alert } from './Alerts'
|
||||
|
||||
export type MediaProps = MediaState & {
|
||||
enumerateDevices: typeof enumerateDevices
|
||||
@ -99,14 +100,14 @@ export interface AutoplayProps {
|
||||
export const AutoplayMessage = React.memo(
|
||||
function Autoplay(props: AutoplayProps) {
|
||||
return (
|
||||
<div className='autoplay'>
|
||||
<React.Fragment>
|
||||
The browser has blocked video autoplay on this page.
|
||||
To continue with your call, please press the play button:
|
||||
|
||||
<button className='button' onClick={props.play}>
|
||||
Play
|
||||
</button>
|
||||
</div>
|
||||
</React.Fragment>
|
||||
)
|
||||
},
|
||||
)
|
||||
@ -114,6 +115,14 @@ export const AutoplayMessage = React.memo(
|
||||
export const Media = c(React.memo(function Media(props: MediaProps) {
|
||||
return (
|
||||
<div className='media-container'>
|
||||
<Alerts>
|
||||
{props.autoplayError && (
|
||||
<Alert>
|
||||
<AutoplayMessage play={props.play} />
|
||||
</Alert>
|
||||
)}
|
||||
</Alerts>
|
||||
|
||||
{props.autoplayError && <AutoplayMessage play={props.play} />}
|
||||
<MediaForm {...props} />
|
||||
</div>
|
||||
|
||||
@ -8,7 +8,6 @@ import TestUtils from 'react-dom/test-utils'
|
||||
import { Provider } from 'react-redux'
|
||||
import { AnyAction, applyMiddleware, createStore } from 'redux'
|
||||
import { init } from '../actions/CallActions'
|
||||
import { Alert } from '../actions/NotifyActions'
|
||||
import * as constants from '../constants'
|
||||
import reducers from '../reducers'
|
||||
import { middlewares, State, Store } from '../store'
|
||||
@ -66,7 +65,6 @@ describe('App', () => {
|
||||
})
|
||||
|
||||
describe('state', () => {
|
||||
let alert: Alert
|
||||
beforeEach(async () => {
|
||||
state.streams = {
|
||||
test: {
|
||||
@ -85,28 +83,9 @@ describe('App', () => {
|
||||
type: 'warning',
|
||||
},
|
||||
}
|
||||
alert = {
|
||||
dismissable: true,
|
||||
action: 'Dismiss',
|
||||
message: 'test alert',
|
||||
type: 'info',
|
||||
}
|
||||
state.alerts = [alert]
|
||||
await render()
|
||||
})
|
||||
|
||||
describe('alerts', () => {
|
||||
it('can be dismissed', () => {
|
||||
const dismiss = node.querySelector('.action-alert-dismiss')!
|
||||
dispatchSpy.mockReset()
|
||||
TestUtils.Simulate.click(dismiss)
|
||||
expect(dispatchSpy.mock.calls).toEqual([[{
|
||||
type: constants.ALERT_DISMISS,
|
||||
payload: alert,
|
||||
}]])
|
||||
})
|
||||
})
|
||||
|
||||
describe('video', () => {
|
||||
it('can be activated', () => {
|
||||
dispatchSpy.mockReset()
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { connect } from 'react-redux'
|
||||
import { init } from '../actions/CallActions'
|
||||
import { play } from '../actions/MediaActions'
|
||||
import { dismissAlert, dismissNotification } from '../actions/NotifyActions'
|
||||
import { dismissNotification } from '../actions/NotifyActions'
|
||||
import { sendFile, sendMessage } from '../actions/PeerActions'
|
||||
import { toggleActive } from '../actions/StreamActions'
|
||||
import App from '../components/App'
|
||||
@ -11,7 +11,6 @@ function mapStateToProps (state: State) {
|
||||
return {
|
||||
streams: state.streams,
|
||||
peers: state.peers,
|
||||
alerts: state.alerts,
|
||||
notifications: state.notifications,
|
||||
messages: state.messages.list,
|
||||
messagesCount: state.messages.count,
|
||||
@ -22,7 +21,6 @@ function mapStateToProps (state: State) {
|
||||
const mapDispatchToProps = {
|
||||
toggleActive,
|
||||
sendMessage,
|
||||
dismissAlert: dismissAlert,
|
||||
dismissNotification,
|
||||
init,
|
||||
onSendFile: sendFile,
|
||||
|
||||
@ -1,106 +0,0 @@
|
||||
import * as NotifyActions from '../actions/NotifyActions'
|
||||
import { applyMiddleware, createStore, Store, bindActionCreators } from 'redux'
|
||||
import { create } from '../middlewares'
|
||||
import reducers from './index'
|
||||
import values from 'lodash/values'
|
||||
|
||||
jest.useFakeTimers()
|
||||
|
||||
describe('reducers/alerts', () => {
|
||||
|
||||
let store: Store
|
||||
let notifyActions: typeof NotifyActions
|
||||
beforeEach(() => {
|
||||
store = createStore(
|
||||
reducers,
|
||||
applyMiddleware(...create()),
|
||||
)
|
||||
notifyActions = bindActionCreators(NotifyActions, store.dispatch)
|
||||
})
|
||||
|
||||
|
||||
describe('clearAlert', () => {
|
||||
|
||||
[true, false].forEach(dismissable => {
|
||||
beforeEach(() => {
|
||||
notifyActions.clearAlerts()
|
||||
})
|
||||
it('adds alert to store', () => {
|
||||
notifyActions.alert('test', dismissable)
|
||||
expect(store.getState().alerts).toEqual([{
|
||||
action: dismissable ? 'Dismiss' : undefined,
|
||||
dismissable,
|
||||
message: 'test',
|
||||
type: 'warning',
|
||||
}])
|
||||
})
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
describe('dismissAlert', () => {
|
||||
|
||||
it('removes an alert', () => {
|
||||
store.dispatch(NotifyActions.alert('test', true))
|
||||
expect(store.getState().alerts.length).toBe(1)
|
||||
store.dispatch(NotifyActions.dismissAlert(store.getState().alerts[0]))
|
||||
expect(store.getState().alerts.length).toBe(0)
|
||||
})
|
||||
|
||||
it('does not remove an alert when not found', () => {
|
||||
store.dispatch(NotifyActions.alert('test', true))
|
||||
expect(store.getState().alerts.length).toBe(1)
|
||||
store.dispatch(NotifyActions.dismissAlert({
|
||||
action: undefined,
|
||||
dismissable: false,
|
||||
message: 'bla',
|
||||
type: 'error',
|
||||
}))
|
||||
expect(store.getState().alerts.length).toBe(1)
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
const methods: Array<'info' | 'warning' | 'error'> = [
|
||||
'info',
|
||||
'warning',
|
||||
'error',
|
||||
]
|
||||
|
||||
methods.forEach(type => {
|
||||
|
||||
describe(type, () => {
|
||||
|
||||
beforeEach(() => {
|
||||
notifyActions[type]('Hi {0}!', 'John')
|
||||
})
|
||||
|
||||
it('adds a notification', () => {
|
||||
expect(values(store.getState().notifications)).toEqual([{
|
||||
id: jasmine.any(String),
|
||||
message: 'Hi John!',
|
||||
type,
|
||||
}])
|
||||
})
|
||||
|
||||
it('does not fail when no arguments', () => {
|
||||
notifyActions[type]()
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
describe('clear', () => {
|
||||
|
||||
it('clears all alerts', () => {
|
||||
notifyActions.info('Hi {0}!', 'John')
|
||||
notifyActions.warning('Hi {0}!', 'John')
|
||||
notifyActions.error('Hi {0}!', 'John')
|
||||
store.dispatch(NotifyActions.clear())
|
||||
expect(store.getState().notifications).toEqual({})
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
})
|
||||
@ -1,19 +0,0 @@
|
||||
import * as constants from '../constants'
|
||||
import { AlertActionType, Alert } from '../actions/NotifyActions'
|
||||
|
||||
export type AlertState = Alert[]
|
||||
|
||||
const defaultState: AlertState = []
|
||||
|
||||
export default function alerts (state = defaultState, action: AlertActionType) {
|
||||
switch (action.type) {
|
||||
case constants.ALERT:
|
||||
return [...state, action.payload]
|
||||
case constants.ALERT_DISMISS:
|
||||
return state.filter(a => a !== action.payload)
|
||||
case constants.ALERT_CLEAR:
|
||||
return defaultState
|
||||
default:
|
||||
return state
|
||||
}
|
||||
}
|
||||
@ -1,5 +1,4 @@
|
||||
import active from './active'
|
||||
import alerts from './alerts'
|
||||
import notifications from './notifications'
|
||||
import messages from './messages'
|
||||
import peers from './peers'
|
||||
@ -9,7 +8,6 @@ import { combineReducers } from 'redux'
|
||||
|
||||
export default combineReducers({
|
||||
active,
|
||||
alerts,
|
||||
notifications,
|
||||
messages,
|
||||
media,
|
||||
|
||||
@ -1,20 +1,3 @@
|
||||
.media-container .autoplay {
|
||||
background-color: rgba(0, 0, 0, 0.3);
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 0;
|
||||
z-index: 1000;
|
||||
position: absolute;
|
||||
color: $color-warning;
|
||||
text-align: center;
|
||||
padding: 0.5rem;
|
||||
|
||||
button {
|
||||
padding: 0.5rem;
|
||||
@include button($color-primary, $color-warning);
|
||||
}
|
||||
}
|
||||
|
||||
.media-container form.media {
|
||||
max-width: 440px;
|
||||
text-align: center;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user