import PropTypes from 'prop-types';
import React from 'react';
-import { Button } from 'react-bootstrap';
+import { Button, Col, Form, Row } from 'react-bootstrap';
import { useTranslation } from 'react-i18next';
import Icon from '../common/Icon';
import {
+ getFilterLookahead,
+ getFilterLookbehind,
+ getFilterReverse,
invertEventFilter,
invertGameFilter,
isEventSelected,
isGameSelected,
+ resetEventFilter,
+ resetGameFilter,
+ resetTimeFilter,
+ setFilterLookahead,
+ setFilterLookbehind,
+ setFilterReverse,
toggleEventFilter,
toggleGameFilter,
} from '../../helpers/Episode';
setFilter(invertEventFilter(filter));
}, [filter, setFilter]);
+ const resetEvents = React.useCallback(() => {
+ setFilter(resetEventFilter(filter));
+ }, [filter, setFilter]);
+
const toggleEvent = React.useCallback(event => {
setFilter(toggleEventFilter(events, filter, event));
}, [events, filter, setFilter]);
setFilter(invertGameFilter(filter));
}, [filter, setFilter]);
+ const resetGames = React.useCallback(() => {
+ setFilter(resetGameFilter(filter));
+ }, [filter, setFilter]);
+
const toggleGame = React.useCallback(game => {
setFilter(toggleGameFilter(games, filter, game));
}, [games, filter, setFilter]);
+ const resetTime = React.useCallback(() => {
+ setFilter(resetTimeFilter(filter));
+ }, [filter, setFilter]);
+
+ const setLookahead = React.useCallback((value) => {
+ setFilter(setFilterLookahead(filter, parseInt(value, 10) || 0));
+ }, [filter, setFilter]);
+
+ const setLookbehind = React.useCallback((value) => {
+ setFilter(setFilterLookbehind(filter, parseInt(value, 10) || 0));
+ }, [filter, setFilter]);
+
+ const setReverse = React.useCallback((value) => {
+ setFilter(setFilterReverse(filter, parseInt(value, 10) || 0));
+ }, [filter, setFilter]);
+
return <div className="episode-filter my-3">
<h2>{t('schedule.filter')}</h2>
{events?.length ? <>
<div className="d-flex align-items-start justify-content-start gap-3 mt-3">
<h3>{t('schedule.filters.event')}</h3>
- <Button
- onClick={invertEvents}
- size="sm"
- title={t('button.invert')}
- variant="outline-secondary"
- >
- <Icon.INVERT title="" />
- </Button>
+ <div className="button-bar">
+ <Button
+ onClick={invertEvents}
+ size="sm"
+ title={t('button.invert')}
+ variant="outline-secondary"
+ >
+ <Icon.INVERT title="" />
+ </Button>
+ <Button
+ onClick={resetEvents}
+ size="sm"
+ title={t('button.reset')}
+ variant="outline-secondary"
+ >
+ <Icon.RESET title="" />
+ </Button>
+ </div>
</div>
<div className="button-bar my-2">
{events.map(event =>
{games?.length ? <>
<div className="d-flex align-items-start justify-content-start gap-3 mt-3">
<h3>{t('schedule.filters.game')}</h3>
- <Button
- onClick={invertGames}
- size="sm"
- title={t('button.invert')}
- variant="outline-secondary"
- >
- <Icon.INVERT title="" />
- </Button>
+ <div className="button-bar">
+ <Button
+ onClick={invertGames}
+ size="sm"
+ title={t('button.invert')}
+ variant="outline-secondary"
+ >
+ <Icon.INVERT title="" />
+ </Button>
+ <Button
+ onClick={resetGames}
+ size="sm"
+ title={t('button.reset')}
+ variant="outline-secondary"
+ >
+ <Icon.RESET title="" />
+ </Button>
+ </div>
</div>
<div className="button-bar my-2">
{games.map(game =>
)}
</div>
</> : null}
+ <div className="d-flex align-items-start justify-content-start gap-3 mt-3">
+ <h3>{t('schedule.filters.time')}</h3>
+ <Button
+ onClick={resetTime}
+ size="sm"
+ title={t('button.reset')}
+ variant="outline-secondary"
+ >
+ <Icon.RESET title="" />
+ </Button>
+ </div>
+ <Row>
+ <Col sm={4} md={3} lg={2}>
+ <Form.Group>
+ <Form.Label>{t('schedule.lookahead')}</Form.Label>
+ <Form.Control
+ onChange={(e) => setLookahead(e.target.value)}
+ type="number"
+ value={getFilterLookahead(filter)}
+ />
+ </Form.Group>
+ </Col>
+ <Col sm={4} md={3} lg={2}>
+ <Form.Group>
+ <Form.Label>{t('schedule.lookbehind')}</Form.Label>
+ <Form.Control
+ onChange={(e) => setLookbehind(e.target.value)}
+ type="number"
+ value={getFilterLookbehind(filter)}
+ />
+ </Form.Group>
+ </Col>
+ <Col sm={4} md={3} lg={2}>
+ <Form.Group>
+ <Form.Label>{t('schedule.direction')}</Form.Label>
+ <Form.Select
+ onChange={(e) => setReverse(e.target.value)}
+ value={getFilterReverse(filter)}
+ >
+ <option value="0">{t('schedule.forward')}</option>
+ <option value="1">{t('schedule.reverse')}</option>
+ </Form.Select>
+ </Form.Group>
+ </Col>
+ </Row>
</div>;
};
eventInvert: filter.eventInvert ? 0 : 1,
});
+export const resetEventFilter = (filter) => ({
+ ...filter,
+ event: [],
+ eventInvert: 0,
+});
+
export const toggleEventFilter = (events, filter, event) => {
const eventFilter = filter.event || [];
if (eventFilter.includes(event.id)) {
gameInvert: filter.gameInvert ? 0 : 1,
});
+export const resetGameFilter = (filter) => ({
+ ...filter,
+ game: [],
+ gameInvert: 0,
+});
+
export const toggleGameFilter = (games, filter, game) => {
const gameFilter = filter.game || [];
if (gameFilter.includes(game)) {
game: [...gameFilter, game],
};
};
+
+export const getFilterLookahead = (filter, fallback = 14) => {
+ if (!filter) return fallback;
+ if ('lookahead' in filter) {
+ return filter.lookahead;
+ }
+ return fallback;
+};
+
+export const getFilterLookbehind = (filter, fallback = 0) => {
+ if (!filter) return fallback;
+ if ('lookbehind' in filter) {
+ return filter.lookbehind;
+ }
+ return fallback;
+};
+
+export const getFilterReverse = (filter, fallback = 0) => {
+ if (!filter) return fallback;
+ if ('reverse' in filter) {
+ return filter.reverse;
+ }
+ return fallback;
+};
+
+export const setFilterLookahead = (filter, value) => ({
+ ...filter,
+ lookahead: value,
+});
+
+export const setFilterLookbehind = (filter, value) => ({
+ ...filter,
+ lookbehind: value,
+});
+
+export const setFilterReverse = (filter, reverse) => ({
+ ...filter,
+ reverse: reverse ? 1 : 0,
+});
+
+export const resetTimeFilter = (filter) => ({
+ ...filter,
+ lookahead: 14,
+ lookbehind: 0,
+ reverse: 0,
+});
+
+export const getFilterParams = (filter) => {
+ return {
+ after: moment().subtract(2, 'hours').subtract(getFilterLookbehind(filter, 0), 'days').toISOString(),
+ before: moment().add(16, 'hours').add(getFilterLookahead(filter, 14), 'days').toISOString(),
+ ...filter,
+ };
+};
},
schedule: {
description: 'Anstehende Spiele und andere Termine.',
+ direction: 'Richtung',
filter: 'Filter',
filters: {
event: 'Nach Veranstaltung',
game: 'Nach Spiel',
+ time: 'Zeitraum',
},
+ forward: 'Vorwärts',
games: {
alttp: 'ALttP',
alttpr: 'ALttPR',
smz3: 'SMZ3',
},
heading: 'Terminplan',
+ lookahead: 'Voraus (Tage)',
+ lookbehind: 'Zurück (Tage)',
+ reverse: 'Rückwärts',
startTime: '{{ date, LT }}',
},
search: {
},
schedule: {
description: 'Upcoming matches and other events.',
+ direction: 'Direction',
filter: 'Filter',
filters: {
event: 'By event',
game: 'By game',
+ time: 'Time frame',
},
+ forward: 'Foreward',
games: {
alttp: 'ALttP',
alttpr: 'ALttPR',
smz3: 'SMZ3',
},
heading: 'Schedule',
+ lookahead: 'Ahead (days)',
+ lookbehind: 'Behind (days)',
+ reverse: 'Reverse',
startTime: '{{ date, LT }}',
},
search: {
import Filter from '../components/episodes/Filter';
import List from '../components/episodes/List';
import RestreamDialog from '../components/episodes/RestreamDialog';
-import { isFilterActive } from '../helpers/Episode';
+import { getFilterParams, isFilterActive } from '../helpers/Episode';
import { useUser } from '../hooks/user';
const GAMES = [
];
export const Component = () => {
- const [ahead] = React.useState(14);
const [applyAs, setApplyAs] = React.useState('commentary');
- const [behind] = React.useState(0);
const [episodes, setEpisodes] = React.useState([]);
const [events, setEvents] = React.useState([]);
const [filter, setFilter] = React.useState({});
setFilter(newFilter);
}, []);
- const fetchEpisodes = React.useCallback((controller, ahead, behind, filter) => {
+ const fetchEpisodes = React.useCallback((controller, filter) => {
axios.get(`/api/episodes`, {
signal: controller.signal,
- params: {
- after: moment().subtract(2, 'hours').subtract(behind, 'days').toISOString(),
- before: moment().add(16, 'hours').add(ahead, 'days').toISOString(),
- ...filter,
- },
+ params: getFilterParams(filter),
}).then(response => {
setEpisodes(response.data || []);
}).catch(e => {
React.useEffect(() => {
const controller = new AbortController();
- fetchEpisodes(controller, ahead, behind, filter);
+ fetchEpisodes(controller, filter);
const timer = setInterval(() => {
- fetchEpisodes(controller, ahead, behind, filter);
+ fetchEpisodes(controller, filter);
}, 1.5 * 60 * 1000);
return () => {
controller.abort();
clearInterval(timer);
};
- }, [ahead, behind, fetchEpisodes, filter]);
+ }, [fetchEpisodes, filter]);
const toggleFilter = React.useCallback(() => {
setShowFilter(show => !show);