]> git.localhorst.tv Git - alttp.git/blob - resources/js/pages/Event.js
show translated event title if available
[alttp.git] / resources / js / pages / Event.js
1 import axios from 'axios';
2 import moment from 'moment';
3 import React from 'react';
4 import { Container } from 'react-bootstrap';
5 import { Helmet } from 'react-helmet';
6 import { withTranslation } from 'react-i18next';
7 import { useParams } from 'react-router-dom';
8 import toastr from 'toastr';
9
10 import NotFound from './NotFound';
11 import CanonicalLinks from '../components/common/CanonicalLinks';
12 import ErrorBoundary from '../components/common/ErrorBoundary';
13 import ErrorMessage from '../components/common/ErrorMessage';
14 import Loading from '../components/common/Loading';
15 import EpisodeList from '../components/episodes/List';
16 import Detail from '../components/events/Detail';
17 import Dialog from '../components/techniques/Dialog';
18 import {
19         mayEditContent,
20 } from '../helpers/permissions';
21 import { getTranslation } from '../helpers/Technique';
22 import { useUser } from '../helpers/UserContext';
23 import i18n from '../i18n';
24
25 const Event = () => {
26         const params = useParams();
27         const { name } = params;
28         const user = useUser();
29
30         const [error, setError] = React.useState(null);
31         const [loading, setLoading] = React.useState(true);
32         const [event, setEvent] = React.useState(null);
33
34         const [editContent, setEditContent] = React.useState(null);
35         const [episodes, setEpisodes] = React.useState([]);
36         const [showContentDialog, setShowContentDialog] = React.useState(false);
37
38         const actions = React.useMemo(() => ({
39                 editContent: mayEditContent(user) ? content => {
40                         setEditContent(content);
41                         setShowContentDialog(true);
42                 } : null,
43         }), [user]);
44
45         const fetchEpisodes = React.useCallback((controller, event) => {
46                 if (!event) {
47                         setEpisodes([]);
48                         return;
49                 }
50                 axios.get(`/api/episodes`, {
51                         signal: controller.signal,
52                         params: {
53                                 after: moment().subtract(3, 'hours').toISOString(),
54                                 before: moment().add(14, 'days').toISOString(),
55                                 event: [event.id],
56                         },
57                 }).then(response => {
58                         setEpisodes(response.data || []);
59                 }).catch(e => {
60                         if (!axios.isCancel(e)) {
61                                 console.error(e);
62                         }
63                 });
64         }, []);
65
66         const saveContent = React.useCallback(async values => {
67                 try {
68                         const response = await axios.put(`/api/content/${values.id}`, {
69                                 parent_id: event.description_id,
70                                 ...values,
71                         });
72                         toastr.success(i18n.t('content.saveSuccess'));
73                         setEvent(event => ({
74                                 ...event,
75                                 description: response.data,
76                         }));
77                         setShowContentDialog(false);
78                 } catch (e) {
79                         toastr.error(i18n.t('content.saveError'));
80                 }
81         }, [event && event.description_id]);
82
83         React.useEffect(() => {
84                 const ctrl = new AbortController();
85                 setLoading(true);
86                 axios
87                         .get(`/api/events/${name}`, { signal: ctrl.signal })
88                         .then(response => {
89                                 setError(null);
90                                 setLoading(false);
91                                 setEvent(response.data);
92                         })
93                         .catch(error => {
94                                 setError(error);
95                                 setLoading(false);
96                                 setEvent(null);
97                         });
98                 return () => {
99                         ctrl.abort();
100                 };
101         }, [name]);
102
103         React.useEffect(() => {
104                 const controller = new AbortController();
105                 fetchEpisodes(controller, event);
106                 const timer = setInterval(() => {
107                         fetchEpisodes(controller, event);
108                 }, 1.5 * 60 * 1000);
109                 return () => {
110                         controller.abort();
111                         clearInterval(timer);
112                 };
113         }, [event, fetchEpisodes]);
114
115         if (loading) {
116                 return <Loading />;
117         }
118
119         if (error) {
120                 return <ErrorMessage error={error} />;
121         }
122
123         if (!event) {
124                 return <NotFound />;
125         }
126
127         return <ErrorBoundary>
128                 <Helmet>
129                         <title>
130                                 {(event.description && getTranslation(event.description, 'title', i18n.language))
131                                         || event.title}
132                         </title>
133                 </Helmet>
134                 {event.description ? <Helmet>
135                         <meta
136                                 name="description"
137                                 content={getTranslation(event.description, 'short', i18n.language)}
138                         />
139                 </Helmet> : null}
140                 <CanonicalLinks base={`/events/${event.name}`} />
141                 <Container>
142                         <Detail actions={actions} event={event} />
143                         {episodes.length ? <>
144                                 <h2>{i18n.t('events.upcomingEpisodes')}</h2>
145                                 <EpisodeList episodes={episodes} />
146                         </> : null}
147                 </Container>
148                 <Dialog
149                         content={editContent}
150                         language={i18n.language}
151                         onHide={() => { setShowContentDialog(false); }}
152                         onSubmit={saveContent}
153                         show={showContentDialog}
154                 />
155         </ErrorBoundary>;
156 };
157
158 export default withTranslation()(Event);