Skip to content

Instantly share code, notes, and snippets.

@rosskevin
Created June 30, 2017 22:04
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rosskevin/6585499c7f6d97a232a80b3e3f982c92 to your computer and use it in GitHub Desktop.
Save rosskevin/6585499c7f6d97a232a80b3e3f982c92 to your computer and use it in GitHub Desktop.
material-ui NotificationCenter
// @flow
import Queue from 'es-collections/Queue'
import Snackbar from '@alienfast/material-ui/Snackbar'
import PropTypes from 'prop-types'
import React, {Component} from 'react'
import Logger from './util/Logger'
import Performance from './util/Performance'
import Button from './Button'
// if test, shorten duration for multiple messages - saves ~9 seconds on reset_password.feature
const DefaultDuration = __TEST__ ? 750 : 3000
type Props = {}
type State = {
queueSize: number,
status: 'queueSizeChanged' | 'exited'
}
type Notification = {
message: string,
duration?: number,
delay?: number,
action?: string,
onAction?: Function
}
const EmptyNotification = {
message: ''
}
class NotificationCenter extends Component<void, Props, State> {
static contextTypes = {
eventBus: PropTypes.object.isRequired
}
props: Props
state: State = {
queueSize: 0,
status: 'exited'
}
subscriptions: Array<EmitterSubscription>
queue: Queue
notification: ?Notification = EmptyNotification
componentWillMount () {
log.debug('componentWillMount')
this.queue = new Queue()
const { eventBus } = this.context
// eventBus.registerEvent('NotificationCenter', 'notification')
this.subscriptions = eventBus.subscribe('notification', this.handleNotification)
}
componentWillUnmount () {
log.debug('componentWillUnmount')
const { eventBus } = this.context
eventBus.unsubscribe(this.subscriptions)
}
shouldComponentUpdate (nextProps: Props, nextState: State, nextContext: any) {
//log.debug('shouldComponentUpdate', this.queue.size)
// check to see if we are still showing the current notification
const peek = this.queue.peek()
//log.debug('compare notifications', this.notification === peek, this.notification, peek)
if (this.notification === peek) {
return false
}
return Performance.shouldComponentUpdate(this, nextProps, nextState, nextContext, true)
}
updateQueueSize () {
this.setState({
queueSize: this.queue.size,
status: 'queueSizeChanged'
})
}
handleNotification = (notification: Notification) => {
const { delay } = notification
const enqueue = () => {
this.queue.enqueue(notification)
log.debug('handleNotification', this.queue.size, notification)
this.updateQueueSize()
}
if (delay) {
setTimeout(enqueue, delay)
} else {
enqueue()
}
}
handleRequestClose = (event: ?SyntheticUIEvent, reason: string, onAction?: Function) => {
log.debug('handleRequestClose', reason, onAction, this.notification)
const { duration } = (this.notification ? this.notification : {}) // wacky flow maybe type
if (reason === 'clickaway' && !duration === 0) { // allow clickaway on duration 0
log.debug(reason)
} else {
if (onAction) {
onAction()
}
this.queue.dequeue()
this.updateQueueSize()
}
}
handleExited = () => {
log.debug('handleExited', this.notification)
this.setState({ status: 'exited' })
}
render () {
const { status } = this.state
const size = this.queue.size
let open
let shownNotification: ?Notification
if (size === 0) {
// We must render the snackbar with an empty notification and close it so
// that we still achieve the ease out effect.
shownNotification = this.notification
this.notification = EmptyNotification
open = false
} else if (status === 'queueSizeChanged' && this.notification !== EmptyNotification) {
// allow this message to exit
shownNotification = this.notification
open = false
} else {
// ready to display a new message from the queue
shownNotification = this.notification = this.queue.peek()
open = true
}
const { message, action: actionString, duration, onAction } = (shownNotification || {})
// from the action string, create a button to trigger the action and dismiss the snackbar
let action
if (actionString) {
action = [
<Button
key={actionString}
color='accent'
dense
onClick={() => onAction ? onAction() : null}
>
{actionString}
</Button>
]
}
const snackbarProps = {
open,
autoHideDuration: (duration === undefined ? DefaultDuration : duration)
}
log.debug('render Queue size:', size, ' status: ', status, message, snackbarProps)
return (
<Snackbar
key={message}
{...snackbarProps}
action={action}
onRequestClose={this.handleRequestClose}
onExited={this.handleExited}
SnackbarContentProps={{
'aria-describedby': 'message-id'
}}
message={<span id='message-id'>{message}</span>}
/>
)
}
}
const log = Logger.get('NotificationCenter')
log.debug('DefaultDuration', DefaultDuration, __TEST__)
export default NotificationCenter
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment