frontend/containers/EventBar/index.tsx (view raw)
1import {useEffect, useState, useReducer} from 'react';
2import {useRouter} from 'next/router';
3import Link from 'next/link';
4import {makeStyles} from '@material-ui/core/styles';
5import AppBar from '@material-ui/core/AppBar';
6import Toolbar from '@material-ui/core/Toolbar';
7import Typography from '@material-ui/core/Typography';
8import IconButton from '@material-ui/core/IconButton';
9import Tooltip from '@material-ui/core/Tooltip';
10import Avatar from '@material-ui/core/Avatar';
11import Icon from '@material-ui/core/Icon';
12import clsx from 'clsx';
13import {useTranslation} from 'react-i18next';
14import useAuthStore from '../../stores/useAuthStore';
15import useEventStore from '../../stores/useEventStore';
16import useTourStore from '../../stores/useTourStore';
17import useProfile from '../../hooks/useProfile';
18import useSettings from '../../hooks/useSettings';
19import GenericMenu from '../GenericMenu';
20import EventDetails from '../EventDetails';
21import useBannerStore from '../../stores/useBannerStore';
22import Banner from '../../components/Banner';
23
24let persistedLastAnnouncementSeen = '';
25if (typeof localStorage !== 'undefined') {
26 persistedLastAnnouncementSeen = localStorage.getItem('lastAnnouncementSeen');
27}
28
29const EventBar = ({event, onAdd, onSave, onShare}) => {
30 const {t} = useTranslation();
31 const router = useRouter();
32 const [detailsOpen, toggleDetails] = useReducer(i => !i, false);
33 const [anchorEl, setAnchorEl] = useState(null);
34 const isEditing = useEventStore(s => s.isEditing);
35 const setIsEditing = useEventStore(s => s.setIsEditing);
36 const token = useAuthStore(s => s.token);
37 const {user} = useProfile();
38 const settings = useSettings();
39 const setTour = useTourStore(s => s.setTour);
40 const tourStep = useTourStore(s => s.step);
41 const bannerOffset = useBannerStore(s => s.offset);
42 const bannerHeight = useBannerStore(s => s.height);
43 const classes = useStyles({detailsOpen, bannerOffset, bannerHeight});
44 const announcement = settings?.announcement || '';
45 const [lastAnnouncementSeen, setLastAnnouncementSeen] = useState(
46 persistedLastAnnouncementSeen
47 );
48 const showAnnouncement =
49 announcement !== '' && announcement !== lastAnnouncementSeen;
50
51 const onBannerClear = () => {
52 if (typeof announcement != 'undefined') {
53 localStorage.setItem('lastAnnouncementSeen', String(announcement));
54 }
55 setLastAnnouncementSeen(announcement);
56 };
57
58 useEffect(() => {
59 onTourChange(toggleDetails);
60 }, [tourStep]);
61
62 useEffect(() => {
63 if (!detailsOpen) setIsEditing(false);
64 }, [detailsOpen]); // eslint-disable-line react-hooks/exhaustive-deps
65
66 const signUp = () =>
67 router.push({
68 pathname: '/auth/register',
69 state: {event: event?.id},
70 });
71 const signIn = () => router.push('/auth/login');
72 const goToDashboard = () => router.push('/dashboard');
73 const goProfile = () => router.push('/profile');
74
75 const onTourRestart = () => setTour({showWelcome: true});
76
77 const noUserMenuActions = [
78 {
79 label: t('event.actions.add_to_my_events'),
80 onClick: () => {
81 onAdd(true);
82 },
83 id: 'AddToMyEventsTab',
84 },
85 {divider: true},
86 {
87 label: t('menu.login'),
88 onClick: signIn,
89 id: 'SignInTab',
90 },
91 {
92 label: t('menu.register'),
93 onClick: signUp,
94 id: 'SignUpTab',
95 },
96 {divider: true},
97 {
98 label: t('menu.tour'),
99 onClick: () => {
100 setAnchorEl(null);
101 onTourRestart();
102 },
103 id: 'TourTab',
104 },
105 ];
106
107 const loggedMenuActions = [
108 {
109 label: t('menu.dashboard'),
110 onClick: goToDashboard,
111 id: 'GoToDashboardTab',
112 },
113 {
114 label: t('menu.profile'),
115 onClick: goProfile,
116 id: 'ProfileTab',
117 },
118 {divider: true},
119 {
120 label: t('menu.tour'),
121 onClick: () => {
122 setAnchorEl(null);
123 onTourRestart();
124 },
125 id: 'TourTab',
126 },
127 ];
128
129 const menuActions = token ? loggedMenuActions : noUserMenuActions;
130 const userInfos = user
131 ? [{label: user.username, id: 'Email'}, {divider: true}]
132 : [];
133
134 const appLink = user ? '/dashboard' : settings?.['about_link'] || '';
135
136 const UserIcon = user ? (
137 <Avatar className={classes.avatar}>
138 {`${user.username[0]}`.toUpperCase()}
139 </Avatar>
140 ) : (
141 <Icon>more_vert</Icon>
142 );
143
144 return (
145 <AppBar
146 className={classes.appbar}
147 position="fixed"
148 color="primary"
149 id={(isEditing && 'EditEvent') || (detailsOpen && 'Details') || 'Menu'}
150 >
151 <Banner
152 message={announcement}
153 open={showAnnouncement}
154 onClear={onBannerClear}
155 />
156 <Toolbar>
157 <div className={classes.name}>
158 <Link href={appLink}>
159 <img
160 className={classes.logo}
161 src="/assets/Logo_in_beta.svg"
162 alt="Logo"
163 />
164 </Link>
165 <Tooltip title={event.name}>
166 <Typography
167 variant="h6"
168 noWrap
169 id="MenuHeaderTitle"
170 className={classes.title}
171 >
172 {event.name}
173 </Typography>
174 </Tooltip>
175
176 {detailsOpen && (
177 <IconButton
178 className="tour_event_edit"
179 color="inherit"
180 edge="end"
181 id="HeaderAction"
182 onClick={isEditing ? onSave : () => setIsEditing(true)}
183 >
184 <Icon>{isEditing ? 'done' : 'edit'}</Icon>
185 </IconButton>
186 )}
187 </div>
188 {detailsOpen ? (
189 <IconButton
190 color="inherit"
191 edge="end"
192 id="CloseDetailsBtn"
193 onClick={() => {
194 setIsEditing(false);
195 toggleDetails();
196 }}
197 >
198 <Icon>close</Icon>
199 </IconButton>
200 ) : (
201 <>
202 <IconButton
203 className={classes.shareIcon}
204 color="inherit"
205 edge="end"
206 id="ShareBtn"
207 onClick={toggleDetails}
208 >
209 <Icon>share</Icon>
210 </IconButton>
211 <IconButton
212 className={clsx(classes.iconButtons, 'tour_event_infos')}
213 color="inherit"
214 edge="end"
215 id="ShareBtn"
216 onClick={toggleDetails}
217 >
218 <Icon>information_outline</Icon>
219 </IconButton>
220 <IconButton
221 color="inherit"
222 edge="end"
223 id="MenuMoreInfo"
224 onClick={e => setAnchorEl(e.currentTarget)}
225 >
226 {UserIcon}
227 </IconButton>
228 </>
229 )}
230 {!detailsOpen && (
231 <GenericMenu
232 anchorEl={anchorEl}
233 setAnchorEl={setAnchorEl}
234 actions={[
235 ...userInfos,
236 ...[
237 {
238 label: detailsOpen
239 ? t('event.actions.hide_details')
240 : t('event.actions.show_details'),
241 onClick: e => {
242 setAnchorEl(null);
243 toggleDetails();
244 },
245 id: 'DetailsTab',
246 },
247 ],
248 ...menuActions,
249 ]}
250 />
251 )}
252 </Toolbar>
253 {detailsOpen && (
254 <EventDetails toggleDetails={toggleDetails} onShare={onShare} />
255 )}
256 </AppBar>
257 );
258};
259
260const onTourChange = (toggleDetails: Function) => {
261 const {prev, step, isCreator} = useTourStore.getState();
262 const fromTo = (step1: number, step2: number) =>
263 prev === step1 && step === step2;
264
265 if (isCreator) {
266 if (fromTo(3, 2) || fromTo(2, 3) || fromTo(4, 5)) toggleDetails();
267 } else if (fromTo(2, 3) || fromTo(3, 2) || fromTo(3, 4)) toggleDetails();
268};
269
270const useStyles = makeStyles(theme => ({
271 appbar: ({detailsOpen, bannerOffset, bannerHeight}) => ({
272 overflow: 'hidden',
273 minHeight: detailsOpen ? '100vh' : theme.mixins.toolbar.minHeight,
274 overflowY: detailsOpen ? 'scroll' : 'hidden',
275 transition: 'height 0.3s ease',
276 zIndex: theme.zIndex.appBar,
277 marginTop: bannerOffset - bannerHeight ,
278 }),
279 logo: {
280 marginRight: theme.spacing(2),
281 width: 64,
282 height: 32,
283 cursor: 'pointer',
284 },
285 name: {
286 flexGrow: 1,
287 display: 'flex',
288 alignItems: 'center',
289 },
290 title: {
291 maxWidth: `calc(100vw - ${theme.spacing(30)}px)`,
292 },
293 iconButtons: {
294 margin: theme.spacing(0),
295 },
296 avatar: {
297 width: theme.spacing(3),
298 height: theme.spacing(3),
299 fontSize: 16,
300 },
301 shareIcon: {
302 marginRight: 0,
303 },
304}));
305
306export default EventBar;