1
0
mirror of https://github.com/ascribe/onion.git synced 2024-12-23 01:39:36 +01:00

Implement pause and resume functionality to global notifications

Also part of this change:
* Handle the notification cooldown periods using status changes rather
than emptying the queue
* Refactor how notifications are rendered
This commit is contained in:
Brett Sun 2015-11-21 19:23:20 +01:00
parent 60bf4b12f5
commit 6649b7d7f1
3 changed files with 83 additions and 74 deletions

View File

@ -2,13 +2,15 @@
import { alt } from '../alt'; import { alt } from '../alt';
class GlobalNotificationActions { class GlobalNotificationActions {
constructor() { constructor() {
this.generateActions( this.generateActions(
'appendGlobalNotification', 'appendGlobalNotification',
'showNextGlobalNotification',
'shiftGlobalNotification', 'shiftGlobalNotification',
'emulateEmptyStore' 'cooldownGlobalNotifications',
'pauseGlobalNotifications',
'resumeGlobalNotifications'
); );
} }
} }

View File

@ -1,7 +1,9 @@
'use strict'; 'use strict';
import React from 'react'; import React from 'react';
import classNames from 'classnames';
import GlobalNotificationActions from '../actions/global_notification_actions';
import GlobalNotificationStore from '../stores/global_notification_store'; import GlobalNotificationStore from '../stores/global_notification_store';
import Row from 'react-bootstrap/lib/Row'; import Row from 'react-bootstrap/lib/Row';
@ -9,14 +11,18 @@ import Col from 'react-bootstrap/lib/Col';
import { mergeOptions } from '../utils/general_utils'; import { mergeOptions } from '../utils/general_utils';
const MAX_NOTIFICATION_BUBBLE_CONTAINER_WIDTH = 768;
let GlobalNotification = React.createClass({ let GlobalNotification = React.createClass({
getInitialState() { getInitialState() {
const notificationStore = GlobalNotificationStore.getState();
return mergeOptions( return mergeOptions(
{ {
containerWidth: 0 containerWidth: 0
}, },
this.extractFirstElem(GlobalNotificationStore.getState().notificationQue) notificationStore
); );
}, },
@ -36,35 +42,8 @@ let GlobalNotification = React.createClass({
window.removeEventListener('resize', this.handleContainerResize); window.removeEventListener('resize', this.handleContainerResize);
}, },
extractFirstElem(l) {
if(l.length > 0) {
return {
show: true,
message: l[0]
};
} else {
return {
show: false,
message: ''
};
}
},
onChange(state) { onChange(state) {
let notification = this.extractFirstElem(state.notificationQue); this.setState(state);
// error handling for notifications
if(notification.message && notification.type === 'danger') {
console.logGlobal(new Error(notification.message.message));
}
if(notification.show) {
this.setState(notification);
} else {
this.setState({
show: false
});
}
}, },
handleContainerResize() { handleContainerResize() {
@ -73,32 +52,28 @@ let GlobalNotification = React.createClass({
}); });
}, },
render() { renderNotification() {
let notificationClass = 'ascribe-global-notification'; const {
let textClass; notificationQueue: [notification],
notificationStatus,
notificationsPaused,
containerWidth } = this.state;
if(this.state.containerWidth > 768) { if (notification && !notificationsPaused) {
notificationClass = 'ascribe-global-notification-bubble'; const notificationClasses = [];
let textClass;
if(this.state.show) { if (this.state.containerWidth > 768) {
notificationClass += ' ascribe-global-notification-bubble-on'; notificationClasses.push('ascribe-global-notification-bubble');
notificationClasses.push(notificationStatus === 'show' ? 'ascribe-global-notification-bubble-on'
: 'ascribe-global-notification-bubble-off');
} else { } else {
notificationClass += ' ascribe-global-notification-bubble-off'; notificationClasses.push('ascribe-global-notification');
notificationClasses.push(notificationStatus === 'show' ? 'ascribe-global-notification-on'
: 'ascribe-global-notification-off');
} }
} else { switch(notification.type) {
notificationClass = 'ascribe-global-notification';
if(this.state.show) {
notificationClass += ' ascribe-global-notification-on';
} else {
notificationClass += ' ascribe-global-notification-off';
}
}
if(this.state.message) {
switch(this.state.message.type) {
case 'success': case 'success':
textClass = 'ascribe-global-notification-success'; textClass = 'ascribe-global-notification-success';
break; break;
@ -106,18 +81,23 @@ let GlobalNotification = React.createClass({
textClass = 'ascribe-global-notification-danger'; textClass = 'ascribe-global-notification-danger';
break; break;
default: default:
console.warn('Could not find a matching type in global_notification.js'); console.warn('Could not find a matching notification type in global_notification.js');
} }
return (
<div className={classNames(...notificationClasses)}>
<div className={textClass}>{notification.message}</div>
</div>
);
} }
},
render() {
return ( return (
<div ref="notificationWrapper"> <div ref="notificationWrapper">
<Row> <Row>
<Col> <Col>
<div className={notificationClass}> {this.renderNotification()}
<div className={textClass}>{this.state.message.message}</div>
</div>
</Col> </Col>
</Row> </Row>
</div> </div>

View File

@ -4,36 +4,63 @@ import { alt } from '../alt';
import GlobalNotificationActions from '../actions/global_notification_actions'; import GlobalNotificationActions from '../actions/global_notification_actions';
const GLOBAL_NOTIFICATION_COOLDOWN = 400;
class GlobalNotificationStore { class GlobalNotificationStore {
constructor() { constructor() {
this.notificationQue = []; this.notificationQueue = [];
this.notificationStatus = 'ready';
this.notificationsPaused = false;
this.bindActions(GlobalNotificationActions); this.bindActions(GlobalNotificationActions);
} }
onAppendGlobalNotification(newNotification) { onAppendGlobalNotification(newNotification) {
let notificationDelay = 0; this.notificationQueue.push(newNotification);
for(let i = 0; i < this.notificationQue.length; i++) {
notificationDelay += this.notificationQue[i].dismissAfter;
}
this.notificationQue.push(newNotification); if (!this.notificationsPaused && this.notificationStatus === 'ready') {
setTimeout(GlobalNotificationActions.emulateEmptyStore, notificationDelay + newNotification.dismissAfter); this.showNextNotification();
}
} }
onEmulateEmptyStore() { showNextNotification() {
let actualNotificitionQue = this.notificationQue.slice(); this.notificationStatus = 'show';
this.notificationQue = []; setTimeout(GlobalNotificationActions.cooldownGlobalNotifications, this.notificationQueue[0].dismissAfter);
}
setTimeout(() => { onCooldownGlobalNotifications() {
this.notificationQue = actualNotificitionQue.slice(); // When still paused on cooldown, don't shift the queue so we can repeat the current notification.
GlobalNotificationActions.shiftGlobalNotification(); if (!this.notificationsPaused) {
}, 400); this.notificationStatus = 'cooldown';
// Leave some time between consecutive notifications
setTimeout(GlobalNotificationActions.shiftGlobalNotification, GLOBAL_NOTIFICATION_COOLDOWN);
} else {
this.notificationStatus = 'ready';
}
} }
onShiftGlobalNotification() { onShiftGlobalNotification() {
this.notificationQue.shift(); this.notificationQueue.shift();
if (!this.notificationsPaused && this.notificationQueue.length > 0) {
this.showNextNotification();
} else {
this.notificationStatus = 'ready';
}
}
onPauseGlobalNotifications() {
this.notificationsPaused = true;
}
onResumeGlobalNotifications() {
this.notificationsPaused = false;
if (this.notificationStatus === 'ready' && this.notificationQueue.length > 0) {
this.showNextNotification();
}
} }
} }