This repository has been archived by the owner on Jul 21, 2020. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Cooldown.tsx
123 lines (111 loc) · 3.02 KB
/
Cooldown.tsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
import * as Mixer from '@mixer/cdk-std';
import { Component, h } from 'preact';
import { classes } from '../../alchemy/Style';
function prettyTime(secs: number): string {
const seconds: number = Math.floor(secs) % 60;
const minutes: number = Math.floor(secs / 60) % 60;
const hours: number = Math.floor(secs / 3600) % 24;
const days: number = Math.floor(secs / 86400);
let sTime: string = `${seconds}s`;
if (days) {
sTime = `${days}d ${hours}h`;
} else if (hours) {
sTime = `${hours}h ${minutes}m`;
} else if (minutes) {
sTime = `${minutes}m ${sTime}`;
}
return sTime;
}
/**
* When the cooldown is active, Cooldown shows the
* cooldown timer and text on the button.
*/
export class CoolDown extends Component<
{
cooldown: number;
onCooldownEnd: Function;
progress?: number;
hideTime?: boolean;
},
{ ttl: number }
> {
public componentDidMount() {
this.handleCooldown(this.props.cooldown);
}
public componentWillReceiveProps(nextProps: { cooldown: number }) {
this.handleCooldown(nextProps.cooldown);
}
public componentWillUnmount() {
this.cancel();
}
public render() {
return (
<div
key={`cooldown=${this.props.cooldown}`}
class={classes({
mixerCooldown: true,
cActive: this.state.ttl >= 0,
progress: !!this.props.progress,
})}
role="status"
aria-label={this.state.ttl > 0 ? 'Cooling Down' : ''}
>
<div
class={classes({
hidden: this.props.hideTime,
})}
>
<div class="time" aria-hidden="true">
{prettyTime(this.state.ttl + 1)}
</div>
</div>
</div>
);
}
private cancel: () => void = () => undefined;
private setCountdown(delta: number) {
// Make sure to set the timeout/interval on a leading edge of the
// second. This keeps the timeout from "flickering" and make sure it
// counts perfectly down to 1. (Intervals will fire later, but never
// earlier, than the specified time.)
let remaining = Math.floor(delta / 1000);
const timeout = setTimeout(() => {
const interval = setInterval(() => {
if (remaining < 0) {
clearInterval(interval);
}
this.updateTtl(remaining--);
}, 1000);
this.updateTtl(remaining--);
this.cancel = () => clearInterval(interval);
}, delta % 1000);
this.updateTtl(remaining--);
this.cancel = () => clearTimeout(timeout);
}
private handleCooldown(cooldown: number) {
this.cancel();
Mixer.clock.remoteToLocal(cooldown).then(date => {
const delta = date - Date.now();
if (delta < 0) {
this.updateTtl(-1);
return;
}
this.setCountdown(delta);
});
}
private updateTtl(ttl: number) {
if (ttl !== this.state.ttl) {
this.setState(
{
...this.state,
ttl,
},
() => {
if (this.state.ttl === -1) {
this.props.onCooldownEnd();
}
},
);
}
}
}