Adding mopidy-local back; Consistency with updated events; Notification progress indicator

This commit is contained in:
James Barnsley
2020-01-24 13:43:03 +13:00
parent a70818b88d
commit 87aa88a908
15 changed files with 27240 additions and 26940 deletions

View File

@ -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>
);
});

View File

@ -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 : {}),

View File

@ -138,7 +138,6 @@ export function dragEnd() {
}
export function set(data) {
console.log('set',data);
return {
type: 'UI_SET',
data,

View File

@ -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>

View File

@ -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;
}

View File

@ -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;