You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
177 lines
4.7 KiB
177 lines
4.7 KiB
import * as classNames from "classnames"; |
|
import { observer } from "mobx-react"; |
|
import * as React from "react"; |
|
import { Button, Icon, Progress, Segment } from "semantic-ui-react"; |
|
|
|
import { Duration } from "@common/Duration"; |
|
import log from "@common/logger"; |
|
import { Section, SectionRun, SectionRunner } from "@common/sprinklersRpc"; |
|
|
|
import "@client/styles/SectionRunnerView"; |
|
|
|
interface PausedStateProps { |
|
paused: boolean; |
|
togglePaused: () => void; |
|
} |
|
|
|
function PausedState({ paused, togglePaused }: PausedStateProps) { |
|
const classes = classNames({ |
|
"sectionRunner--pausedState": true, |
|
"sectionRunner--pausedState-paused": paused, |
|
"sectionRunner--pausedState-unpaused": !paused |
|
}); |
|
return ( |
|
<Button className={classes} size="medium" onClick={togglePaused}> |
|
<Icon name={paused ? "pause" : "play"} /> |
|
{paused ? "Paused" : "Processing"} |
|
</Button> |
|
); |
|
} |
|
|
|
class SectionRunView extends React.Component< |
|
{ |
|
run: SectionRun; |
|
sections: Section[]; |
|
}, |
|
{ |
|
now: number; |
|
} |
|
> { |
|
animationFrameHandle: number | null = null; |
|
startTime: number; |
|
|
|
constructor(p: any) { |
|
super(p); |
|
const now = performance.now(); |
|
this.state = { now }; |
|
this.startTime = Date.now() - now; |
|
} |
|
|
|
componentDidMount() { |
|
this.requestAnimationFrame(); |
|
} |
|
|
|
componentDidUpdate() { |
|
this.requestAnimationFrame(); |
|
} |
|
|
|
componentWillUnmount() { |
|
this.cancelAnimationFrame(); |
|
} |
|
|
|
cancelAnimationFrame = () => { |
|
if (this.animationFrameHandle != null) { |
|
cancelAnimationFrame(this.animationFrameHandle); |
|
this.animationFrameHandle = null; |
|
} |
|
}; |
|
|
|
requestAnimationFrame = () => { |
|
const startTime = this.props.run.startTime; |
|
if (startTime != null) { |
|
if (this.animationFrameHandle == null) { |
|
this.animationFrameHandle = requestAnimationFrame(this.updateNow); |
|
} |
|
} else { |
|
this.cancelAnimationFrame(); |
|
} |
|
}; |
|
|
|
updateNow = (now: number) => { |
|
this.animationFrameHandle = null; |
|
this.setState({ |
|
now: this.startTime + now |
|
}); |
|
this.requestAnimationFrame(); |
|
}; |
|
|
|
render() { |
|
const { run, sections } = this.props; |
|
const startTime = run.unpauseTime ? run.unpauseTime : run.startTime; |
|
const now = this.state.now; |
|
const section = sections[run.section]; |
|
const duration = Duration.fromSeconds(run.duration); |
|
const cancel = run.cancel; |
|
let running: boolean = false; // tslint:disable-line:no-unused-variable |
|
let paused: boolean = false; |
|
let progressBar: React.ReactNode | undefined; |
|
if (startTime != null) { |
|
let elapsed = run.totalDuration - run.duration; |
|
if (run.pauseTime) { |
|
paused = true; |
|
} else { |
|
running = true; |
|
elapsed += (now - startTime.valueOf()) / 1000; |
|
} |
|
const percentage = elapsed / run.totalDuration; |
|
progressBar = ( |
|
<Progress |
|
color={paused ? "yellow" : "blue"} |
|
size="tiny" |
|
percent={percentage * 100} |
|
/> |
|
); |
|
} |
|
const description = |
|
`'${section.name}' for ${duration.toString()}` + |
|
(paused ? " (paused)" : "") + |
|
(running ? " (running)" : ""); |
|
return ( |
|
<Segment className="sectionRun"> |
|
<div className="flex-horizontal-space-between"> |
|
{description} |
|
<Button negative onClick={cancel} icon size="mini"> |
|
<Icon name="remove" /> |
|
</Button> |
|
</div> |
|
{progressBar} |
|
</Segment> |
|
); |
|
} |
|
} |
|
|
|
@observer |
|
export default class SectionRunnerView extends React.Component< |
|
{ |
|
sectionRunner: SectionRunner; |
|
sections: Section[]; |
|
}, |
|
{} |
|
> { |
|
render() { |
|
const { current, queue, paused } = this.props.sectionRunner; |
|
const { sections } = this.props; |
|
const queueView = queue.map(run => ( |
|
<SectionRunView key={run.id} run={run} sections={sections} /> |
|
)); |
|
if (current) { |
|
queueView.unshift( |
|
<SectionRunView key={-1} run={current} sections={sections} /> |
|
); |
|
} |
|
if (queueView.length === 0) { |
|
queueView.push(<Segment key={0}>No items in queue</Segment>); |
|
} |
|
return ( |
|
<Segment className="sectionRunner"> |
|
<div style={{ display: "flex", alignContent: "baseline" }}> |
|
<h3 style={{ marginBottom: 0 }}>Section Runner Queue</h3> |
|
<div className="flex-spacer" /> |
|
<PausedState paused={paused} togglePaused={this.togglePaused} /> |
|
</div> |
|
<Segment.Group className="queue">{queueView}</Segment.Group> |
|
</Segment> |
|
); |
|
} |
|
|
|
togglePaused = () => { |
|
const { sectionRunner } = this.props; |
|
const paused = !sectionRunner.paused; |
|
sectionRunner |
|
.setPaused(paused) |
|
.then(res => log.info(res, "set section runner paused to " + paused)) |
|
.catch(err => |
|
log.info({ err }, "error setting section runner paused status") |
|
); |
|
}; |
|
}
|
|
|