the browser-facing portion of osu!
at master 71 lines 1.9 kB view raw
1// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the GNU Affero General Public License v3.0. 2// See the LICENCE file in the repository root for full licence text. 3 4import { computed, makeObservable } from 'mobx'; 5import { observer } from 'mobx-react'; 6import * as moment from 'moment'; 7import * as React from 'react'; 8import { trans } from 'utils/lang'; 9 10const bn = 'countdown-timer'; 11const secondsPerDay = 60 * 60 * 24; 12const secondsPerHour = 60 * 60; 13 14interface Props { 15 deadline: string; 16} 17 18@observer 19export default class CountdownTimer extends React.Component<Props> { 20 private timer?: number; 21 22 @computed 23 private get deadline() { 24 return moment(this.props.deadline).valueOf(); 25 } 26 27 private get diff() { 28 return Math.max(this.deadline - (new Date()).valueOf(), 0) / 1000; 29 } 30 31 constructor(props: Props) { 32 super(props); 33 34 makeObservable(this); 35 } 36 37 componentWillUnmount() { 38 window.clearTimeout(this.timer); 39 } 40 41 render() { 42 const diff = this.diff; 43 const fields = { 44 days: Math.floor(diff / (secondsPerDay)), 45 hours: Math.floor((diff / (secondsPerHour)) % 24), 46 minutes: Math.floor((diff / 60) % 60), 47 seconds: Math.floor(diff % 60), 48 }; 49 if (diff !== 0) { 50 this.setTimeout(); 51 } 52 53 return ( 54 <div className={bn}> 55 <div className={`${bn}__header`}>{`${trans('common.time.remaining')}:`}</div> 56 {Object.entries(fields).map(([field, value]) => ( 57 <div key={field} className={`${bn}__field`}> 58 <div className={`${bn}__digit`}> 59 {value < 10 ? `0${value}` : value} 60 </div> 61 <div className={`${bn}__label`}>{trans(`common.countdown.${field}`)}</div> 62 </div> 63 ))} 64 </div> 65 ); 66 } 67 68 private setTimeout() { 69 this.timer = window.setTimeout(() => this.forceUpdate(), 1000); 70 } 71}