Adding mopidy-local back; Consistency with updated events; Notification progress indicator
This commit is contained in:
@ -1,44 +1,93 @@
|
||||
|
||||
import React, { memo } from 'react';
|
||||
import Icon from './Icon';
|
||||
|
||||
const ProgressRing = ({ radius, stroke, progress }) => {
|
||||
const normalizedRadius = radius - stroke * 2;
|
||||
const circumference = normalizedRadius * 2 * Math.PI;
|
||||
const strokeDashoffset = circumference - progress * circumference;
|
||||
|
||||
return (
|
||||
<svg
|
||||
className="loader__spinner__progress"
|
||||
height={radius * 2}
|
||||
width={radius * 2}
|
||||
style={{ transform: `rotate(-90deg) translate(${stroke}px, -${stroke}px)` }}
|
||||
>
|
||||
<circle
|
||||
stroke="white"
|
||||
fill="transparent"
|
||||
strokeWidth={stroke}
|
||||
strokeDasharray={`${circumference} ${circumference}`}
|
||||
strokeDashoffset={strokeDashoffset}
|
||||
r={normalizedRadius}
|
||||
cx={radius}
|
||||
cy={radius}
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
|
||||
export default memo((props) => {
|
||||
const { body, loading, mini, lazy, white, className = '', children = null } = props;
|
||||
const {
|
||||
body,
|
||||
loading,
|
||||
mini,
|
||||
lazy,
|
||||
white,
|
||||
className = '',
|
||||
progress = null,
|
||||
} = props;
|
||||
|
||||
if (!loading) {
|
||||
return null;
|
||||
}
|
||||
if (!loading) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let classNameString = 'loader';
|
||||
if (className) {
|
||||
classNameString += ` ${className}`;
|
||||
}
|
||||
if (mini) {
|
||||
classNameString += ` loader--mini`;
|
||||
}
|
||||
if (body) {
|
||||
classNameString += ` loader--body`;
|
||||
}
|
||||
if (lazy) {
|
||||
classNameString += ` loader--lazy`;
|
||||
}
|
||||
if (white) {
|
||||
classNameString += ` loader--white`;
|
||||
}
|
||||
let classNameString = 'loader';
|
||||
if (className) {
|
||||
classNameString += ` ${className}`;
|
||||
}
|
||||
if (mini) {
|
||||
classNameString += ' loader--mini';
|
||||
}
|
||||
if (body) {
|
||||
classNameString += ' loader--body';
|
||||
}
|
||||
if (lazy) {
|
||||
classNameString += ' loader--lazy';
|
||||
}
|
||||
if (white) {
|
||||
classNameString += ' loader--white';
|
||||
}
|
||||
|
||||
if (!navigator.onLine) {
|
||||
return (
|
||||
<div className={classNameString}>
|
||||
{
|
||||
navigator.onLine ? (
|
||||
<div className="loader__spinner"></div>
|
||||
) : (
|
||||
<div className="loader__offline">
|
||||
<Icon name="wifi_off" />
|
||||
<p>You need to be online load this resource</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
{children}
|
||||
<div className={classNameString}>
|
||||
<div className="loader__offline">
|
||||
<Icon name="wifi_off" />
|
||||
<p> You need to be online load this resource </p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (progress) {
|
||||
classNameString += ' loader--progress';
|
||||
return (
|
||||
<div className={classNameString}>
|
||||
<div className="loader__spinner">
|
||||
<ProgressRing
|
||||
stroke="2"
|
||||
radius={mini ? '12' : '60'}
|
||||
progress={progress}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={classNameString}>
|
||||
<div className="loader__spinner" />
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
@ -112,48 +112,53 @@ class Notifications extends React.Component {
|
||||
}
|
||||
|
||||
renderProcess(process) {
|
||||
const {
|
||||
data: { total, remaining },
|
||||
message,
|
||||
status,
|
||||
closing,
|
||||
key,
|
||||
} = process;
|
||||
const { uiActions } = this.props;
|
||||
let progress = 0;
|
||||
if (process.data.total && process.data.remaining) {
|
||||
progress = ((process.data.total - process.data.remaining) / process.data.total * 100).toFixed();
|
||||
if (total && remaining) {
|
||||
progress = ((total - remaining) / total).toFixed(4);
|
||||
}
|
||||
|
||||
switch (process.status) {
|
||||
switch (status) {
|
||||
case 'running':
|
||||
return (
|
||||
<div className={`notification notification--process${process.closing ? ' closing' : ''}`} key={process.key}>
|
||||
<Loader loading mini white>
|
||||
<div className="progress">
|
||||
<div className="fill" style={{ width: `${progress}%` }} />
|
||||
</div>
|
||||
</Loader>
|
||||
{process.message}
|
||||
<Icon name="close" className="notification__close-button" onClick={(e) => { this.props.uiActions.cancelProcess(process.key); }} />
|
||||
<div className={`notification notification--process${closing ? ' closing' : ''}`} key={key}>
|
||||
<Loader
|
||||
progress={progress}
|
||||
loading
|
||||
mini
|
||||
white
|
||||
/>
|
||||
{message}
|
||||
<Icon name="close" className="notification__close-button" onClick={() => { uiActions.cancelProcess(key); }} />
|
||||
</div>
|
||||
);
|
||||
|
||||
case 'cancelling':
|
||||
return (
|
||||
<div className={`notification notification--process cancelling${process.closing ? ' closing' : ''}`} key={process.key}>
|
||||
<div className="loader" />
|
||||
<div className={`notification notification--process cancelling${closing ? ' closing' : ''}`} key={key}>
|
||||
<Loader />
|
||||
Cancelling
|
||||
</div>
|
||||
);
|
||||
|
||||
case 'cancelled':
|
||||
case 'finished':
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
renderProcesses() {
|
||||
if (!this.props.processes || this.props.processes.length <= 0) return null;
|
||||
|
||||
const processes = [];
|
||||
for (const key in this.props.processes) {
|
||||
if (this.props.processes.hasOwnProperty(key)) {
|
||||
processes.push(this.props.processes[key]);
|
||||
}
|
||||
}
|
||||
const { processes: processesObj = {} } = this.props;
|
||||
const processes = Object.keys(processesObj).map((key) => processesObj[key]);
|
||||
if (!processes.length) return null;
|
||||
|
||||
return (
|
||||
<span>
|
||||
@ -162,41 +167,9 @@ class Notifications extends React.Component {
|
||||
);
|
||||
}
|
||||
|
||||
// do we want the loading of everything to be displayed?
|
||||
// not likely...
|
||||
renderLoader() {
|
||||
if (!this.props.load_queue) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const { load_queue } = this.props;
|
||||
let load_count = 0;
|
||||
for (const key in load_queue) {
|
||||
if (load_queue.hasOwnProperty(key)) {
|
||||
load_count++;
|
||||
}
|
||||
}
|
||||
|
||||
if (load_count > 0) {
|
||||
let className = 'loading ';
|
||||
if (load_count > 20) {
|
||||
className += 'high';
|
||||
} else if (load_count > 5) {
|
||||
className += 'medium';
|
||||
} else {
|
||||
className += 'low';
|
||||
}
|
||||
return (
|
||||
<div className={className} />
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className="notifications">
|
||||
{this.renderLoader()}
|
||||
{this.renderNotifications()}
|
||||
{this.renderProcesses()}
|
||||
</div>
|
||||
@ -204,7 +177,7 @@ class Notifications extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
const mapStateToProps = (state, ownProps) => ({
|
||||
const mapStateToProps = (state) => ({
|
||||
broadcasts: (state.ui.broadcasts ? state.ui.broadcasts : []),
|
||||
notifications: (state.ui.notifications ? state.ui.notifications : []),
|
||||
processes: (state.ui.processes ? state.ui.processes : {}),
|
||||
|
||||
@ -138,7 +138,6 @@ export function dragEnd() {
|
||||
}
|
||||
|
||||
export function set(data) {
|
||||
console.log('set',data);
|
||||
return {
|
||||
type: 'UI_SET',
|
||||
data,
|
||||
|
||||
@ -139,7 +139,7 @@ Back
|
||||
<div className="name" />
|
||||
<div className="input">
|
||||
<a className="button button--default" onClick={(e) => this.props.uiActions.createNotification({ content: 'Test notification' })}>Create notification</a>
|
||||
<a className="button button--default" onClick={(e) => this.props.uiActions.startProcess('test_process', "Test process", {remaining: 50, total: 100})}>Create process notification</a>
|
||||
<a className="button button--default" onClick={(e) => this.props.uiActions.startProcess('test_process', "Test process", {remaining: 32, total: 100})}>Create process notification</a>
|
||||
<a className="button button--default" onClick={(e) => this.props.pusherActions.request('test')}>Run test process</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
|
||||
.loader {
|
||||
|
||||
&__offline {
|
||||
@ -35,13 +34,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
&:before {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
border-color: rgba(128,128,128,0.15);
|
||||
}
|
||||
|
||||
&:after {
|
||||
@include spin();
|
||||
border-top-color: transparent;
|
||||
@ -49,14 +41,25 @@
|
||||
border-bottom-color: transparent;
|
||||
border-right-color: colour(blue);
|
||||
}
|
||||
|
||||
&:before {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
border-color: rgba(128,128,128,0.2);
|
||||
}
|
||||
}
|
||||
|
||||
&--progress {
|
||||
.loader__spinner {
|
||||
&:after {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&--white {
|
||||
.loader__spinner {
|
||||
&:before {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
&:after {
|
||||
border-right-color: colour(white);
|
||||
}
|
||||
@ -66,7 +69,7 @@
|
||||
&--body {
|
||||
padding: 35vh 0;
|
||||
}
|
||||
|
||||
|
||||
&--mini {
|
||||
padding: 0;
|
||||
|
||||
@ -74,7 +77,7 @@
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
}
|
||||
|
||||
|
||||
.loader__offline {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@ -15,17 +15,17 @@
|
||||
margin: 0.5rem;
|
||||
background: colour(soft_grey);
|
||||
color: colour(white);
|
||||
pointer-events: all;
|
||||
pointer-events: all;
|
||||
border-radius: 3px;
|
||||
font-size: 0.9rem;
|
||||
|
||||
|
||||
&__title {
|
||||
padding-top: 0;
|
||||
}
|
||||
|
||||
&__actions {
|
||||
padding-top: 5px;
|
||||
|
||||
|
||||
&__item {
|
||||
margin-bottom: 0;
|
||||
margin-top: 5px;
|
||||
@ -111,25 +111,6 @@
|
||||
position: absolute;
|
||||
top: 13px;
|
||||
left: 13px;
|
||||
|
||||
.progress {
|
||||
position: absolute;
|
||||
top: 4px;
|
||||
left: 4px;
|
||||
bottom: 4px;
|
||||
right: 4px;
|
||||
border-radius: 50%;
|
||||
overflow: hidden;
|
||||
|
||||
.fill {
|
||||
background: colour(white);
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
opacity: 0.35;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -139,29 +120,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
.loading {
|
||||
@include pulse(0.1s);
|
||||
position: fixed;
|
||||
bottom: 8px;
|
||||
right: 8px;
|
||||
width: 5px;
|
||||
height: 5px;
|
||||
border-radius: 50%;
|
||||
background: colour(mid_grey);
|
||||
|
||||
&.high {
|
||||
background: colour(red);
|
||||
}
|
||||
|
||||
&.medium {
|
||||
background: colour(orange);
|
||||
}
|
||||
|
||||
&.low {
|
||||
background: colour(green);
|
||||
}
|
||||
}
|
||||
|
||||
@include responsive($bp_medium){
|
||||
bottom: 60px;
|
||||
left: 10px;
|
||||
|
||||
Reference in New Issue
Block a user