Files
Iris/src/js/components/Notifications.js

295 lines
12 KiB
JavaScript
Executable File

import React, { Fragment } from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import * as uiActions from '../services/ui/actions';
import * as spotifyActions from '../services/spotify/actions';
import * as lastfmActions from '../services/lastfm/actions';
import * as geniusActions from '../services/genius/actions';
import * as snapcastActions from '../services/snapcast/actions';
import Icon from './Icon';
import Loader from './Loader';
import { indexToArray } from '../util/arrays';
import { i18n, I18n } from '../locale';
import ErrorBoundary from './ErrorBoundary';
import Button from './Button';
class Notifications extends React.Component {
importConfiguration(notification_key, configuration) {
if (configuration.ui) {
this.props.uiActions.set(configuration.ui);
}
if (configuration.spotify) {
this.props.spotifyActions.importAuthorization(configuration.spotify.authorization, configuration.spotify.me);
}
if (configuration.snapcast) {
this.props.snapcastActions.set(configuration.snapcast);
setTimeout(() => this.props.snapcastActions.connect(), 100);
}
if (configuration.lastfm) {
this.props.lastfmActions.importAuthorization(configuration.lastfm.authorization, configuration.lastfm.me);
}
if (configuration.genius) {
this.props.geniusActions.importAuthorization(configuration.genius.authorization, configuration.genius.me);
}
this.props.uiActions.removeNotification(notification_key, true);
this.props.uiActions.createNotification({
content: i18n('modal.share_configuration.import.successful'),
});
}
renderNotifications() {
if (!this.props.notifications || this.props.notifications.length <= 0) return null;
const notifications = indexToArray(this.props.notifications);
return (
<ErrorBoundary>
{
notifications.map((notification) => {
switch (notification.type) {
case 'shortcut':
return (
<div className="notification__wrapper" key={notification.key}>
<div className={`notification notification--shortcut${notification.closing ? ' closing' : ''}`} data-duration={notification.duration}>
<Icon name={notification.content} />
{notification.title && (
<h4 className="notification__title">{notification.title}</h4>
)}
</div>
</div>
);
case 'share-configuration-received':
return (
<div className="notification__wrapper" key={notification.key}>
<div className="notification notification--info" key={notification.key} data-duration={notification.duration}>
<Icon
name="close"
className="notification__close-button"
onClick={(e) => this.props.uiActions.removeNotification(notification.key, true)}
/>
<h4 className="notification__title">
<I18n path="modal.share_configuration.import.title" />
</h4>
<div className="notification__content">
<p>
<I18n path="modal.share_configuration.import.subtitle" />
</p>
<ul>
{notification.configuration.ui && (
<li><I18n path="modal.share_configuration.interface" /></li>
)}
{notification.configuration.spotify && (
<li><I18n path="services.spotify.title" /></li>
)}
{notification.configuration.lastfm && (
<li><I18n path="services.lastfm.title" /></li>
)}
{notification.configuration.genius && (
<li><I18n path="services.genius.title" /></li>
)}
{notification.configuration.snapcast && (
<li><I18n path="services.snapcast.title" /></li>
)}
</ul>
<p>
<I18n path="modal.share_configuration.import.do_you_want_to_import" />
</p>
</div>
<div className="notification__actions">
<Button
className="notification__actions__item"
onClick={() => this.importConfiguration(notification.key, notification.configuration)}
tracking={{ category: 'ShareConfiguration', action: 'Import' }}
>
<I18n path="modal.share_configuration.import.import_now" />
</Button>
</div>
</div>
</div>
);
default:
return (
<div className="notification__wrapper" key={notification.key}>
<div
className={`notification notification--${notification.level}${notification.closing ? ' closing' : ''}`}
data-key={notification.key}
data-duration={notification.duration}
>
<Icon
name="close"
className="notification__close-button"
onClick={() => this.props.uiActions.removeNotification(notification.key, true)}
/>
{notification.title && (
<h4 className="notification__title">
{notification.title}
</h4>
)}
{notification.content && (
<div className="notification__content">
{notification.content}
</div>
)}
{notification.description && (
<div className="notification__description">
{notification.description}
</div>
)}
{notification.links && (
<div className="notification__actions">
{
notification.links.map((link, i) => (
<Button
type="secondary"
className="notification__actions__item"
href={link.url}
target={link.new_window ? '_blank' : 'self'}
key={i}
tracking={{ category: 'NotificationLink', action: 'Click', label: link.text }}
>
{link.text}
</Button>
))
}
</div>
)}
</div>
</div>
);
}
})
}
</ErrorBoundary>
);
}
renderProcess(process) {
const {
total,
remaining,
level = 'info',
content,
description,
status,
closing,
key,
} = process;
const { uiActions } = this.props;
let progress = 0;
if (total && remaining) {
progress = ((total - remaining) / total).toFixed(4);
}
switch (status) {
case 'running':
return (
<div className="notification__wrapper" key={key}>
<div
className={
`notification notification--${level} notification--process${closing ? ' closing' : ''}`
}
>
<Loader
progress={progress}
loading
mini
white
/>
{content && content !== '' && <div className="notification__content">{content}</div>}
{description && description !== '' && <div className="notification__description">{description}</div>}
<Icon name="close" className="notification__close-button" onClick={() => { uiActions.cancelProcess(key); }} />
</div>
</div>
);
case 'finished':
return (
<div className="notification__wrapper" key={key}>
<div
className={
`notification notification--${level} notification--process${closing ? ' closing' : ''}`
}
>
<Icon className="notification__icon" name={level === 'error' ? 'close' : 'check'} />
{content && content !== '' && <div className="notification__content">{content}</div>}
{description && description !== '' && <div className="notification__description">{description}</div>}
<Icon name="close" className="notification__close-button" onClick={() => { uiActions.closeProcess(key); }} />
</div>
</div>
);
case 'cancelling':
return (
<div className="notification__wrapper" key={key}>
<div
className={
`notification notification--${level} notification--process cancelling${closing ? ' closing' : ''}`
}
>
<Loader
progress={progress}
loading
mini
white
/>
{content && content !== '' && <div className="notification__content">{content}</div>}
{description && description !== '' && <div className="notification__description">{description}</div>}
<Icon name="close" className="notification__close-button" />
</div>
</div>
);
case 'cancelled':
case 'completed':
default:
return null;
}
}
renderProcesses = () => {
const { processes: processesObj } = this.props;
const processes = indexToArray(processesObj).filter((process) => process.notification);
if (!processes) return null;
return (
<>
{processes.map((process) => this.renderProcess(process))}
</>
);
}
render() {
return (
<div className="notifications">
{this.renderNotifications()}
{this.renderProcesses()}
</div>
);
}
}
const mapStateToProps = (state) => ({
broadcasts: (state.ui.broadcasts ? state.ui.broadcasts : []),
notifications: (state.ui.notifications ? state.ui.notifications : []),
processes: (state.ui.processes ? state.ui.processes : {}),
});
const mapDispatchToProps = (dispatch) => ({
uiActions: bindActionCreators(uiActions, dispatch),
spotifyActions: bindActionCreators(spotifyActions, dispatch),
geniusActions: bindActionCreators(geniusActions, dispatch),
lastfmActions: bindActionCreators(lastfmActions, dispatch),
snapcastActions: bindActionCreators(snapcastActions, dispatch),
});
export default connect(mapStateToProps, mapDispatchToProps)(Notifications);