X-Git-Url: https://git.localhorst.tv/?a=blobdiff_plain;f=resources%2Fjs%2Fcomponents%2Fpages%2FSchedule.js;h=3d0a14082b8d5262c8350d207b6186c6a04e6116;hb=5a575dc29f3af10f1d8e142ff9e1c6ccdfa3b075;hp=14d0891707e23a59da9b444c6c9cb5b115bd293d;hpb=dec43db11e9433f5bfcfaa091518082559cb3169;p=alttp.git diff --git a/resources/js/components/pages/Schedule.js b/resources/js/components/pages/Schedule.js index 14d0891..3d0a140 100644 --- a/resources/js/components/pages/Schedule.js +++ b/resources/js/components/pages/Schedule.js @@ -1,28 +1,54 @@ import axios from 'axios'; import moment from 'moment'; +import PropTypes from 'prop-types'; import React from 'react'; -import { Container } from 'react-bootstrap'; +import { Alert, Container } from 'react-bootstrap'; import { Helmet } from 'react-helmet'; import { useTranslation } from 'react-i18next'; +import toastr from 'toastr'; import CanonicalLinks from '../common/CanonicalLinks'; import ErrorBoundary from '../common/ErrorBoundary'; +import ApplyDialog from '../episodes/ApplyDialog'; +import Filter from '../episodes/Filter'; import List from '../episodes/List'; +import RestreamDialog from '../episodes/RestreamDialog'; +import { withUser } from '../../helpers/UserContext'; -const Schedule = () => { - const [ahead, setAhead] = React.useState(6); - const [behind, setBehind] = React.useState(0); +const Schedule = ({ user }) => { + const [ahead] = React.useState(14); + const [applyAs, setApplyAs] = React.useState('commentary'); + const [behind] = React.useState(0); const [episodes, setEpisodes] = React.useState([]); + const [filter, setFilter] = React.useState({}); + const [restreamChannel, setRestreamChannel] = React.useState(null); + const [restreamEpisode, setRestreamEpisode] = React.useState(null); + const [showApplyDialog, setShowApplyDialog] = React.useState(false); + const [showRestreamDialog, setShowRestreamDialog] = React.useState(false); const { t } = useTranslation(); React.useEffect(() => { - const controller = new AbortController(); + const savedFilter = localStorage.getItem('episodes.filter.schedule'); + if (savedFilter) { + setFilter(JSON.parse(savedFilter)); + } else { + setFilter(filter => filter ? {} : filter); + } + }, []); + + const updateFilter = React.useCallback(newFilter => { + localStorage.setItem('episodes.filter.schedule', JSON.stringify(newFilter)); + setFilter(newFilter); + }, []); + + const fetchEpisodes = React.useCallback((controller, ahead, behind, filter) => { axios.get(`/api/episodes`, { signal: controller.signal, params: { after: moment().startOf('day').subtract(behind, 'days').toISOString(), before: moment().startOf('day').add(ahead + 1, 'days').toISOString(), + ...filter, }, }).then(response => { setEpisodes(response.data || []); @@ -31,10 +57,159 @@ const Schedule = () => { console.error(e); } }); + }, []); + + const onAddRestream = React.useCallback(episode => { + setRestreamEpisode(episode); + setShowRestreamDialog(true); + }, []); + + const onAddRestreamSubmit = React.useCallback(async values => { + try { + const response = await axios.post( + `/api/episodes/${values.episode_id}/add-restream`, values); + const newEpisode = response.data; + setEpisodes(episodes => episodes.map(episode => + episode.id === newEpisode.id ? { + ...episode, + ...newEpisode, + } : episode + )); + toastr.success(t('episodes.restreamDialog.addSuccess')); + } catch (e) { + toastr.error(t('episodes.restreamDialog.addError')); + throw e; + } + setRestreamEpisode(null); + setShowRestreamDialog(false); + }, []); + + const onRemoveRestream = React.useCallback(async (episode, channel) => { + try { + const response = await axios.post( + `/api/episodes/${episode.id}/remove-restream`, { channel_id: channel.id }); + const newEpisode = response.data; + setEpisodes(episodes => episodes.map(episode => + episode.id === newEpisode.id ? { + ...episode, + ...newEpisode, + } : episode + )); + toastr.success(t('episodes.restreamDialog.removeSuccess')); + setRestreamChannel(null); + setRestreamEpisode(null); + setShowRestreamDialog(false); + } catch (e) { + toastr.error(t('episodes.restreamDialog.removeError')); + } + }, []); + + const onEditRestream = React.useCallback((episode, channel) => { + setRestreamChannel(channel); + setRestreamEpisode(episode); + setShowRestreamDialog(true); + }, []); + + const editRestream = React.useCallback(async values => { + try { + const response = await axios.post( + `/api/episodes/${values.episode_id}/edit-restream`, values); + const newEpisode = response.data; + setEpisodes(episodes => episodes.map(episode => + episode.id === newEpisode.id ? { + ...episode, + ...newEpisode, + } : episode + )); + setRestreamEpisode(episode => ({ + ...episode, + ...newEpisode, + })); + const newChannel = newEpisode.channels.find(c => c.id === values.channel_id); + setRestreamChannel(channel => ({ + ...channel, + ...newChannel, + })); + toastr.success(t('episodes.restreamDialog.editSuccess')); + } catch (e) { + toastr.error(t('episodes.restreamDialog.editError')); + } + }, []); + + const manageCrew = React.useCallback(async values => { + try { + const response = await axios.post( + `/api/episodes/${values.episode_id}/crew-manage`, values); + const newEpisode = response.data; + setEpisodes(episodes => episodes.map(episode => + episode.id === newEpisode.id ? { + ...episode, + ...newEpisode, + } : episode + )); + setRestreamEpisode(episode => ({ + ...episode, + ...newEpisode, + })); + const newChannel = newEpisode.channels.find(c => c.id === values.channel_id); + setRestreamChannel(channel => ({ + ...channel, + ...newChannel, + })); + toastr.success(t('episodes.restreamDialog.crewSuccess')); + } catch (e) { + toastr.error(t('episodes.restreamDialog.crewError')); + } + }, []); + + const onHideRestreamDialog = React.useCallback(() => { + setShowRestreamDialog(false); + setRestreamChannel(null); + setRestreamEpisode(null); + }, []); + + const onApply = React.useCallback((episode, as) => { + setShowApplyDialog(true); + setRestreamEpisode(episode); + setApplyAs(as); + }, []); + + const onSubmitApplyDialog = React.useCallback(async values => { + try { + const response = await axios.post( + `/api/episodes/${values.episode_id}/crew-signup`, values); + const newEpisode = response.data; + setEpisodes(episodes => episodes.map(episode => + episode.id === newEpisode.id ? { + ...episode, + ...newEpisode, + } : episode + )); + toastr.success(t('episodes.applyDialog.applySuccess')); + } catch (e) { + toastr.error(t('episodes.applyDialog.applyError')); + throw e; + } + setRestreamEpisode(null); + setShowApplyDialog(false); + }, []); + + const onHideApplyDialog = React.useCallback(() => { + setShowApplyDialog(false); + setRestreamEpisode(null); + }, []); + + React.useEffect(() => { + const controller = new AbortController(); + fetchEpisodes(controller, ahead, behind, filter); + const timer = setInterval(() => { + fetchEpisodes(controller, ahead, behind, filter); + }, 1.5 * 60 * 1000); return () => { controller.abort(); + clearInterval(timer); }; - }, [ahead, behind]); + }, [ahead, behind, fetchEpisodes, filter]); return @@ -42,11 +217,51 @@ const Schedule = () => { -

{t('schedule.heading')}

+
+

{t('schedule.heading')}

+
+ +
+
- + {episodes.length ? + + : + + {t('episodes.empty')} + + } + {user ? <> + + + : null}
; }; -export default Schedule; +Schedule.propTypes = { + user: PropTypes.shape({ + }), +}; + +export default withUser(Schedule);