Move timer into separate class

This commit is contained in:
Daniel Schädler 2025-03-13 15:51:24 +01:00
parent bcba2a0727
commit 42b2003336
2 changed files with 54 additions and 44 deletions

33
js/components/Timer.js Normal file
View File

@ -0,0 +1,33 @@
import React from 'react';
export function Timer({timerEnd}) {
const [timeLeft, setTimeLeft] = React.useState(null);
React.useEffect(() => {
if (timerEnd) {
const intervalId = setInterval(() => {
const now = new Date();
const timeLeft = timerEnd - now;
setTimeLeft(timeLeft > 0 ? timeLeft : 0);
}, 1000);
return () => clearInterval(intervalId);
}
}, [timerEnd]);
const formatTimeLeft = timeLeft => {
if (timeLeft === null) return '';
const hours = Math.floor(timeLeft / (1000 * 60 * 60));
const minutes = Math.floor((timeLeft % (1000 * 60 * 60)) / (1000 * 60));
const seconds = Math.floor((timeLeft % (1000 * 60)) / 1000);
const formattedSeconds = seconds < 10 ? '0' + seconds : seconds;
let formattedTimeLeft = '';
if (hours > 0) {
formattedTimeLeft += hours + 'h ';
}
formattedTimeLeft += minutes + ':' + formattedSeconds;
return formattedTimeLeft;
};
return <span>{formatTimeLeft(timeLeft)}</span>;
}

View File

@ -8,11 +8,13 @@ import {
} from 'reactstrap'; } from 'reactstrap';
import {Match} from '../js/components/Match'; import {Match} from '../js/components/Match';
import {sortMatchesByPositionAscending} from '../js/utils/sorting'; import {sortMatchesByPositionAscending} from '../js/utils/sorting';
import {Timer} from '../js/components/Timer';
function FullscreenPage(props) { function FullscreenPage(props) {
return (<div> return (<div>
<FullscreenPageHeader title={props.tournamentMeta.name} code={props.tournamentMeta.code} filter={props.filter} timerEnd={props.timerEnd}/> <FullscreenPageHeader title={props.tournamentMeta.name} code={props.tournamentMeta.code} filter={props.filter}
timerEnd={props.timerEnd}/>
<Matches matches={props.matches}/> <Matches matches={props.matches}/>
</div>); </div>);
} }
@ -20,20 +22,20 @@ function FullscreenPage(props) {
function Matches(props) { function Matches(props) {
let matches; let matches;
if (props.matches == null) { if (props.matches == null) {
matches = (<div className='text-center text-secondary'> matches = (<div className="text-center text-secondary">
<Spinner animation='border' role='status' size='sm'/> <Spinner animation="border" role="status" size="sm"/>
<span className='ml-3'>lade Matches</span> <span className="ml-3">lade Matches</span>
</div>); </div>);
} else if (props.matches.length === 0) { } else if (props.matches.length === 0) {
matches = (<div className='text-center text-secondary font-italic'>keine Matches</div>); matches = (<div className="text-center text-secondary font-italic">keine Matches</div>);
} else { } else {
matches = (<Row> matches = (<Row>
{props.matches.sort(sortMatchesByPositionAscending()).map( {props.matches.sort(sortMatchesByPositionAscending()).map(
match => <Col md='auto'><Match key={match.id} match={match}/></Col> match => <Col md="auto"><Match key={match.id} match={match}/></Col>
)} )}
</Row>); </Row>);
} }
return (<div className='mx-4 h5'> return (<div className="mx-4 h5">
{matches} {matches}
</div>); </div>);
} }
@ -41,7 +43,7 @@ function Matches(props) {
function FilterDropdown(props) { function FilterDropdown(props) {
return (<UncontrolledDropdown> return (<UncontrolledDropdown>
<i>Match-Filter: </i> <i>Match-Filter: </i>
<DropdownToggle color='light' caret> <DropdownToggle color="light" caret>
{props.selected.label} {props.selected.label}
</DropdownToggle> </DropdownToggle>
<DropdownMenu> <DropdownMenu>
@ -55,39 +57,14 @@ function FilterDropdown(props) {
function FullscreenPageHeader(props) { function FullscreenPageHeader(props) {
const [timeLeft, setTimeLeft] = React.useState(null); return (
<Navbar color="light" className="mb-4 border-bottom py-0">
React.useEffect(() => { <FilterDropdown {...props.filter} />
if (props.timerEnd) {
const intervalId = setInterval(() => {
const now = new Date();
const timeLeft = props.timerEnd - now;
setTimeLeft(timeLeft > 0 ? timeLeft : 0);
}, 1000);
return () => clearInterval(intervalId);
}
}, [props.timerEnd]);
const formatTimeLeft = timeLeft => {
if (timeLeft === null) return '';
const hours = Math.floor(timeLeft / (1000 * 60 * 60));
const minutes = Math.floor((timeLeft % (1000 * 60 * 60)) / (1000 * 60));
const seconds = Math.floor((timeLeft % (1000 * 60)) / 1000);
const formattedSeconds = seconds < 10 ? '0' + seconds : seconds;
let formattedTimeLeft = '';
if (hours > 0) {
formattedTimeLeft += hours + 'h ';
}
formattedTimeLeft += minutes + ':' + formattedSeconds;
return formattedTimeLeft;
};
return (<Navbar color='light' className='mb-4 border-bottom py-0'>
<FilterDropdown {...props.filter}/>
<NavbarBrand>{props.title}</NavbarBrand> <NavbarBrand>{props.title}</NavbarBrand>
{props.timerEnd && <div className='ml-auto'>Spielzeit: <NavbarBrand>{formatTimeLeft(timeLeft)}</NavbarBrand></div>} {props.timerEnd &&
</Navbar>); <div className="ml-auto">Spielzeit: <NavbarBrand><Timer timerEnd={props.timerEnd}/></NavbarBrand></div>}
</Navbar>
);
} }
const matchFilters = { const matchFilters = {
@ -195,9 +172,9 @@ class Main extends React.Component {
<Head> <Head>
<title>Vollbild-Ansicht: turnie.re</title> <title>Vollbild-Ansicht: turnie.re</title>
</Head> </Head>
<Container className='p-5 text-center text-secondary'> <Container className="p-5 text-center text-secondary">
<Spinner size='sm'/> <Spinner size="sm"/>
<span className='ml-3'>lade Vollbild-Ansicht</span> <span className="ml-3">lade Vollbild-Ansicht</span>
</Container> </Container>
</div>); </div>);
} }