]> git.localhorst.tv Git - alttp.git/commitdiff
option to invert event filter
authorDaniel Karbach <daniel.karbach@localhorst.tv>
Tue, 25 Jul 2023 12:47:18 +0000 (14:47 +0200)
committerDaniel Karbach <daniel.karbach@localhorst.tv>
Tue, 25 Jul 2023 12:47:18 +0000 (14:47 +0200)
resources/js/components/common/Icon.js
resources/js/components/episodes/Filter.js
resources/js/components/pages/Schedule.js
resources/js/helpers/Episode.js
resources/js/i18n/de.js
resources/js/i18n/en.js

index facd3f23f20f6f41d5768147025669110ce59c5c..ddd35fee627c139d57be6b6c7722d1fb00151625 100644 (file)
@@ -72,6 +72,7 @@ Icon.FINISHED = makePreset('FinishedIcon', 'square-check');
 Icon.FIRST_PLACE = makePreset('FirstPlaceIcon', 'trophy');
 Icon.FORBIDDEN = makePreset('ForbiddenIcon', 'square-xmark');
 Icon.FORFEIT = makePreset('ForfeitIcon', 'square-xmark');
+Icon.INVERT = makePreset('InveretIcon', 'circle-half-stroke');
 Icon.LANGUAGE = makePreset('LanguageIcon', 'language');
 Icon.LOCKED = makePreset('LockedIcon', 'lock');
 Icon.LOGOUT = makePreset('LogoutIcon', 'sign-out-alt');
index 06dcf13f65501839f08090b58754c69d35fafa1f..98d965dd3ff902751164140df20a79cc280b211d 100644 (file)
@@ -1,61 +1,20 @@
-import axios from 'axios';
-import moment from 'moment';
 import PropTypes from 'prop-types';
 import React from 'react';
 import { Button } from 'react-bootstrap';
 
-const Filter = ({ filter, setFilter }) => {
-       const [events, setEvents] = React.useState([]);
-
-       React.useEffect(() => {
-               const controller = new AbortController();
-               axios.get(`/api/events`, {
-                       signal: controller.signal,
-                       params: {
-                               after: moment().startOf('day').subtract(7, 'days').toISOString(),
-                               before: moment().startOf('day').add(8, 'days').toISOString(),
-                       },
-               }).then(response => {
-                       const newEvents = (response.data || []).sort(
-                               (a, b) => (a.short || a.title).localeCompare(b.short || b.title)
-                       );
-                       setEvents(newEvents);
-               }).catch(e => {
-                       if (!axios.isCancel(e)) {
-                               console.error(e);
-                       }
-               });
-               return () => {
-                       controller.abort();
-               };
-       }, []);
+import { isEventSelected, toggleEventFilter } from '../../helpers/Episode';
 
+const Filter = ({ events, filter, setFilter }) => {
        const toggleEvent = React.useCallback(event => {
-               const eventFilter = filter.event || [];
-               if (eventFilter.includes(event.id)) {
-                       setFilter({
-                               ...filter,
-                               event: eventFilter.filter(id => id !== event.id && events.find(e => e.id === id)),
-                       });
-               } else {
-                       setFilter({
-                               ...filter,
-                               event: [...eventFilter, event.id],
-                       });
-               }
+               setFilter(toggleEventFilter(events, filter, event));
        }, [events, filter, setFilter]);
 
-       const isEventSelected = React.useCallback(event => {
-               const eventFilter = filter.event || [];
-               return eventFilter.includes(event.id);
-       }, [filter]);
-
        if (!events || !events.length) return null;
 
        return <div className="episode-filter button-bar text-end">
                {events.map(event =>
                        <Button
-                               active={isEventSelected(event)}
+                               active={isEventSelected(filter, event)}
                                key={event.id}
                                onClick={() => toggleEvent(event)}
                                title={event.short ? event.title : null}
@@ -68,6 +27,8 @@ const Filter = ({ filter, setFilter }) => {
 };
 
 Filter.propTypes = {
+       events: PropTypes.arrayOf(PropTypes.shape({
+       })),
        filter: PropTypes.shape(),
        setFilter: PropTypes.func,
 };
index dfac328b57dc484d5605570aee6024fb1e21f514..cac23e7e66d19e5cc39534db61a528ef6a50349f 100644 (file)
@@ -14,6 +14,7 @@ import ApplyDialog from '../episodes/ApplyDialog';
 import Filter from '../episodes/Filter';
 import List from '../episodes/List';
 import RestreamDialog from '../episodes/RestreamDialog';
+import { toggleEventFilter } from '../../helpers/Episode';
 import { withUser } from '../../helpers/UserContext';
 
 const Schedule = ({ user }) => {
@@ -21,6 +22,7 @@ const Schedule = ({ user }) => {
        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({});
        const [restreamChannel, setRestreamChannel] = React.useState(null);
        const [restreamEpisode, setRestreamEpisode] = React.useState(null);
@@ -39,11 +41,40 @@ const Schedule = ({ user }) => {
                }
        }, []);
 
+       React.useEffect(() => {
+               const controller = new AbortController();
+               axios.get(`/api/events`, {
+                       signal: controller.signal,
+                       params: {
+                               after: moment().startOf('day').subtract(7, 'days').toISOString(),
+                               before: moment().startOf('day').add(8, 'days').toISOString(),
+                       },
+               }).then(response => {
+                       const newEvents = (response.data || []).sort(
+                               (a, b) => (a.short || a.title).localeCompare(b.short || b.title)
+                       );
+                       setEvents(newEvents);
+               }).catch(e => {
+                       if (!axios.isCancel(e)) {
+                               console.error(e);
+                       }
+               });
+               return () => {
+                       controller.abort();
+               };
+       }, []);
+
        const updateFilter = React.useCallback(newFilter => {
                localStorage.setItem('episodes.filter.schedule', JSON.stringify(newFilter));
                setFilter(newFilter);
        }, []);
 
+       const invertFilter = React.useCallback(() => {
+               updateFilter(events.reduce((newFilter, event) => {
+                       return toggleEventFilter(events, newFilter, event);
+               }, filter));
+       }, [events, filter]);
+
        const fetchEpisodes = React.useCallback((controller, ahead, behind, filter) => {
                axios.get(`/api/episodes`, {
                        signal: controller.signal,
@@ -231,17 +262,28 @@ const Schedule = ({ user }) => {
                <CanonicalLinks base="/schedule" />
                <div className="d-flex align-items-end justify-content-between">
                        <h1 className="mb-0">{t('schedule.heading')}</h1>
-                       <Button
-                               onClick={toggleFilter}
-                               title={t('button.filter')}
-                               variant={filterButtonVariant}
-                       >
-                               <Icon.FILTER title="" />
-                       </Button>
+                       <div className="button-bar">
+                               {showFilter ?
+                                       <Button
+                                               onClick={invertFilter}
+                                               title={t('button.invert')}
+                                               variant="outline-secondary"
+                                       >
+                                               <Icon.INVERT title="" />
+                                       </Button>
+                               : null}
+                               <Button
+                                       onClick={toggleFilter}
+                                       title={t('button.filter')}
+                                       variant={filterButtonVariant}
+                               >
+                                       <Icon.FILTER title="" />
+                               </Button>
+                       </div>
                </div>
                {showFilter ?
                        <div className="my-2">
-                               <Filter filter={filter} setFilter={updateFilter} />
+                               <Filter events={events} filter={filter} setFilter={updateFilter} />
                        </div>
                : null}
                <ErrorBoundary>
index 675fb365c1ba5ad8b25b1f007ea8191d741b5862..60c1a7b9f77976e6c640aefbebeec644a5538c26 100644 (file)
@@ -13,3 +13,21 @@ export const acceptsCrew = episode => {
        return !!episode.channels.find(c =>
                c.pivot && (c.pivot.accept_comms || c.pivot.accept_tracker));
 };
+
+export const isEventSelected = (filter, event) => {
+       return (filter.event || []).includes(event.id);
+};
+
+export const toggleEventFilter = (events, filter, event) => {
+       const eventFilter = filter.event || [];
+       if (eventFilter.includes(event.id)) {
+               return {
+                       ...filter,
+                       event: eventFilter.filter(id => id !== event.id && events.find(e => e.id === id)),
+               };
+       }
+       return {
+               ...filter,
+               event: [...eventFilter, event.id],
+       };
+};
index 77023f077c62e92a8d98cc7afd28966a926a5bc4..58732c5f60adbd8a2cf5ae1567e35a2134e4aee6 100644 (file)
@@ -255,6 +255,7 @@ export default {
                        filter: 'Filter',
                        generate: 'Generieren',
                        help: 'Hilfe',
+                       invert: 'Umkehren',
                        login: 'Login',
                        logout: 'Logout',
                        new: 'Neu',
index edd8e0a498d780ac0bbdaf0fce61b6b193957979..8976017684f96f71bb57bf8a9a8d2c371d34b39d 100644 (file)
@@ -255,6 +255,7 @@ export default {
                        filter: 'Filter',
                        generate: 'Generate',
                        help: 'Help',
+                       invert: 'Invert',
                        login: 'Login',
                        logout: 'Logout',
                        new: 'New',