Merge remote-tracking branch 'oswaldo/toast-notifications' into audio-refactor-notifications

This commit is contained in:
gcampes 2017-10-13 10:43:27 -03:00
commit 2ae020ba9a
11 changed files with 386 additions and 73 deletions

View File

@ -0,0 +1,120 @@
/* TODO: We can remove this file after we update react animation package */
@keyframes toastify-bounceInRight {
from, 60%, 75%, 90%, to {
animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1); }
from {
opacity: 0;
transform: translate3d(3000px, 0, 0); }
60% {
opacity: 1;
transform: translate3d(-25px, 0, 0); }
75% {
transform: translate3d(10px, 0, 0); }
90% {
transform: translate3d(-5px, 0, 0); }
to {
transform: none; } }
.toastify-bounceInRight, .toast-enter--top-right, .toast-enter--bottom-right {
animation-name: toastify-bounceInRight; }
@keyframes toastify-bounceOutRight {
20% {
opacity: 1;
transform: translate3d(-20px, 0, 0); }
to {
opacity: 0;
transform: translate3d(2000px, 0, 0); } }
.toastify-bounceOutRight, .toast-exit--top-right, .toast-exit--bottom-right {
animation-name: toastify-bounceOutRight; }
@keyframes toastify-bounceInLeft {
from, 60%, 75%, 90%, to {
animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1); }
0% {
opacity: 0;
transform: translate3d(-3000px, 0, 0); }
60% {
opacity: 1;
transform: translate3d(25px, 0, 0); }
75% {
transform: translate3d(-10px, 0, 0); }
90% {
transform: translate3d(5px, 0, 0); }
to {
transform: none; } }
.toastify-bounceInLeft, .toast-enter--top-left, .toast-enter--bottom-left {
animation-name: toastify-bounceInLeft; }
@keyframes toastify-bounceOutLeft {
20% {
opacity: 1;
transform: translate3d(20px, 0, 0); }
to {
opacity: 0;
transform: translate3d(-2000px, 0, 0); } }
.toastify-bounceOutLeft, .toast-exit--top-left, .toast-exit--bottom-left {
animation-name: toastify-bounceOutLeft; }
@keyframes toastify-bounceInUp {
from, 60%, 75%, 90%, to {
animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1); }
from {
opacity: 0;
transform: translate3d(0, 3000px, 0); }
60% {
opacity: 1;
transform: translate3d(0, -20px, 0); }
75% {
transform: translate3d(0, 10px, 0); }
90% {
transform: translate3d(0, -5px, 0); }
to {
transform: translate3d(0, 0, 0); } }
.toastify-bounceInUp, .toast-enter--bottom-center {
animation-name: toastify-bounceInUp; }
@keyframes toastify-bounceOutUp {
20% {
transform: translate3d(0, -10px, 0); }
40%, 45% {
opacity: 1;
transform: translate3d(0, 20px, 0); }
to {
opacity: 0;
transform: translate3d(0, -2000px, 0); } }
.toastify-bounceOutUp, .toast-exit--top-center {
animation-name: toastify-bounceOutUp; }
@keyframes toastify-bounceInDown {
from, 60%, 75%, 90%, to {
animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1); }
0% {
opacity: 0;
transform: translate3d(0, -3000px, 0); }
60% {
opacity: 1;
transform: translate3d(0, 25px, 0); }
75% {
transform: translate3d(0, -10px, 0); }
90% {
transform: translate3d(0, 5px, 0); }
to {
transform: none; } }
.toastify-bounceInDown, .toast-enter--top-center {
animation-name: toastify-bounceInDown; }
@keyframes toastify-bounceOutDown {
20% {
transform: translate3d(0, 10px, 0); }
40%, 45% {
opacity: 1;
transform: translate3d(0, -20px, 0); }
to {
opacity: 0;
transform: translate3d(0, 2000px, 0); } }
.toastify-bounceOutDown, .toast-exit--bottom-center {
animation-name: toastify-bounceOutDown; }
.toastify-animated {
animation-duration: 0.75s;
animation-fill-mode: both; }

View File

@ -4,6 +4,7 @@ import { defineMessages, injectIntl } from 'react-intl';
import Modal from 'react-modal'; import Modal from 'react-modal';
import cx from 'classnames'; import cx from 'classnames';
import ToastContainer from '../toast/container';
import ModalContainer from '../modal/container'; import ModalContainer from '../modal/container';
import NotificationsBarContainer from '../notifications-bar/container'; import NotificationsBarContainer from '../notifications-bar/container';
import AudioNotificationContainer from '../audio/audio-notification/container'; import AudioNotificationContainer from '../audio/audio-notification/container';
@ -189,6 +190,7 @@ class App extends Component {
</section> </section>
<ModalContainer /> <ModalContainer />
<AudioContainer /> <AudioContainer />
<ToastContainer />
<ChatNotificationContainer currentChatID={params.chatID} /> <ChatNotificationContainer currentChatID={params.chatID} />
</main> </main>
); );

View File

@ -0,0 +1,38 @@
import React from 'react';
import PropTypes from 'prop-types';
import { toast } from 'react-toastify';
import Icon from '../icon/component';
import styles from './styles';
const propTypes = {
icon: PropTypes.string,
message: PropTypes.node.isRequired,
type: PropTypes.oneOf(Object.values(toast.TYPE)).isRequired,
};
const defaultProps = {
icon: null,
};
const defaultIcons = {
[toast.TYPE.INFO]: 'help',
[toast.TYPE.SUCCESS]: 'checkmark',
[toast.TYPE.WARNING]: 'warning',
[toast.TYPE.ERROR]: 'close',
[toast.TYPE.DEFAULT]: 'about',
};
const Toast = ({ icon, type, message }) => (
<div className={styles[type]}>
<div className={styles.icon}><Icon iconName={icon || defaultIcons[type]} /></div>
<div className={styles.message}>
<span>{message}</span>
</div>
</div>
);
export default Toast;
Toast.propTypes = propTypes;
Toast.defaultProps = defaultProps;

View File

@ -0,0 +1,21 @@
import React from 'react';
import { ToastContainer } from 'react-toastify';
import Icon from '../icon/component';
import styles from './styles';
export default () => (
<ToastContainer
closeButton={<Icon className={styles.close} iconName="close" />}
autoClose={5000}
className={styles.container}
toastClassName={styles.toast}
bodyClassName={styles.body}
progressClassName={styles.progress}
newestOnTop={false}
hideProgressBar={false}
closeOnClick
pauseOnHover
/>
);

View File

@ -0,0 +1,21 @@
import React from 'react';
import { toast } from 'react-toastify';
import Toast from './component';
let lastToastId = null;
const notify = (message, type = 'default', icon, options) => {
const settings = {
type,
...options,
};
if (!toast.isActive(lastToastId)) {
lastToastId = toast(<Toast {...{ type, icon, message }} />, settings);
}
};
export default notify;
export const withToast = ComponentToWrap =>
props => (<ComponentToWrap {...props} toastNotify={notify} />);

View File

@ -0,0 +1,178 @@
@import "../../stylesheets/variables/_all";
$toast-default-color: $color-white;
$toast-default-bg: $color-gray;
$toast-info-color: $color-white;
$toast-info-bg: $color-primary;
$toast-success-color: $color-white;
$toast-success-bg: $color-success;
$toast-error-color: $color-white;
$toast-error-bg: $color-danger;
$toast-warning-color: $color-white;
$toast-warning-bg: $color-warning;
$background: $color-white;
$background-active: darken($color-white, 5%);
@mixin notification-variant($icon-color, $icon-bg) {
display: flex;
> .icon {
color: $icon-color;
background-color: $icon-bg;
}
}
.icon {
align-self: flex-start;
margin-bottom: auto;
margin-right: $sm-padding-x;
width: 2rem;
border-radius: 50%;
position: relative;
height: 2rem;
flex-shrink: 0;
> i {
line-height: 0;
color: inherit;
position: absolute;
top: 50%;
width: 100%;
}
}
.message {
margin-top: auto;
margin-bottom: auto;
font-size: $font-size-small;
max-height: 15vh;
overflow: auto;
}
.default {
@include notification-variant($toast-default-color, $toast-default-bg);
}
.info {
@include notification-variant($toast-info-color, $toast-info-bg);
}
.success {
@include notification-variant($toast-success-color, $toast-success-bg);
}
.error {
@include notification-variant($toast-error-color, $toast-error-bg);
}
.warning {
@include notification-variant($toast-warning-color, $toast-warning-bg);
}
.container {
z-index: 9999;
position: fixed;
padding: $sm-padding-y;
width: 15vw;
min-width: 320px;
box-sizing: border-box;
top: $md-padding-y;
right: $md-padding-y;
max-height: 75vh;
overflow: hidden;
@include mq($small-only) {
left: $sm-padding-y;
width: auto;
}
}
.toast {
position: relative;
margin-bottom: $sm-padding-x;
padding: $md-padding-x;
border-radius: $border-radius;
box-shadow: 0 1px 10px 0 rgba(0, 0, 0, 0.1), 0 2px 15px 0 rgba(0, 0, 0, 0.05);
display: flex;
justify-content: space-between;
cursor: pointer;
color: $color-text;
background-color: $background;
animation-duration: 0.75s;
animation-fill-mode: both;
&:hover,
&:focus {
background-color: $background-active;
}
}
.body {
margin: auto 0;
flex: 1;
}
.close {
background: transparent;
outline: none;
border: none;
cursor: pointer;
opacity: .3;
transition: .3s ease;
font-size: .35rem;
color: $color-gray-dark;
border: 1px solid;
border-radius: 50%;
padding: .4rem;
line-height: 0;
position: absolute;
top: $md-padding-y;
right: $md-padding-y;
&:before {
margin-left: -.2rem;
}
&:hover,
&:focus {
opacity: 1;
}
@include mq($small-only) {
position: relative;
top: auto;
right: auto;
align-self: flex-start;
margin-top: auto;
margin-bottom: auto;
margin-left: $sm-padding-x;
font-size: 1rem;
line-height: 1;
border: none;
}
}
@keyframes track-progress {
0% {
width: 100%;
}
100% {
width: 0;
}
}
.progress {
position: absolute;
bottom: 0;
left: 0;
width: 0;
height: 5px;
z-index: 9999;
animation: track-progress linear 1;
background-color: $color-gray-lighter;
}

8
bigbluebutton-html5/imports/ui/services/api/index.js Normal file → Executable file
View File

@ -1,6 +1,6 @@
import Auth from '/imports/ui/services/auth'; import Auth from '/imports/ui/services/auth';
import { check } from 'meteor/check'; import { check } from 'meteor/check';
import NotificationService from '/imports/ui/services/notification/notificationService'; import notify from '/imports/ui/components/toast/service';
/** /**
* Send the request to the server via Meteor.call and don't treat errors. * Send the request to the server via Meteor.call and don't treat errors.
@ -36,7 +36,7 @@ function makeCall(name, ...args) {
*/ */
function call(name, ...args) { function call(name, ...args) {
return makeCall(name, ...args).catch((e) => { return makeCall(name, ...args).catch((e) => {
NotificationService.add({ notification: `Error while executing ${name}` }); notify(`Ops! Error while executing ${name}`, 'error');
throw e; throw e;
}); });
} }
@ -47,9 +47,9 @@ function call(name, ...args) {
* @example * @example
* @code{ logClient({error:"Error caused by blabla"}) } * @code{ logClient({error:"Error caused by blabla"}) }
*/ */
function logClient() { function logClient(...arggs) {
const credentials = Auth.credentials; const credentials = Auth.credentials;
const args = Array.prototype.slice.call(arguments, 0); const args = Array.prototype.slice.call(arggs, 0);
const userInfo = window.navigator; const userInfo = window.navigator;
args.push({ args.push({

View File

@ -1,28 +0,0 @@
import { check } from 'meteor/check';
const collection = new Mongo.Collection(null);
function findById(notificationId) {
check(notificationId, String);
collection.find({ notificationId });
}
function add(notification) {
check(notification.notification, String);
collection.insert(notification);
}
function remove(notificationId) {
check(notificationId, String);
collection.remove({ notificationId });
}
const NotificationCollection = { findById, add, remove };
export default NotificationCollection;
export {
findById,
add,
remove,
};

View File

@ -1,41 +0,0 @@
import db from '/imports/ui/services/notification/index.js';
class NotificationService {
/**
* Database to be transacted
* @param {Object} database
*/
constructor(database) {
this.database = database;
}
/**
* @param {string} notificationID
*/
get(notificationID) {
this.database.findById(notificationID);
}
/**
* @param {Object} notification
*/
add(notification) {
this.database.add(notification);
}
/**
* @param {string} notificationID
*/
remove(notificationID) {
this.database.remove(notificationID);
}
}
const NotificationServiceSingleton = new NotificationService(db);
export {
NotificationService,
};
export default NotificationServiceSingleton;

View File

@ -8,6 +8,7 @@ $color-gray-lighter: lighten($color-gray-light, 25%) !default;
$color-primary: #0F70D7 !default; $color-primary: #0F70D7 !default;
$color-success: #008081 !default; $color-success: #008081 !default;
$color-danger: #DF2721 !default; $color-danger: #DF2721 !default;
$color-warning: purple !default;
$color-background: $color-gray-dark !default; $color-background: $color-gray-dark !default;

View File

@ -39,6 +39,7 @@
"react-modal": "~1.7.7", "react-modal": "~1.7.7",
"react-router": "~3.0.2", "react-router": "~3.0.2",
"react-tabs": "~1.0.0", "react-tabs": "~1.0.0",
"react-toastify": "^2.1.0",
"react-toggle": "~4.0.1", "react-toggle": "~4.0.1",
"react-transition-group": "~1.1.3", "react-transition-group": "~1.1.3",
"redis": "^2.6.2", "redis": "^2.6.2",