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