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