@openeventkit/event-site 2.0.128 → 2.0.130

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@openeventkit/event-site",
3
3
  "description": "Event Site",
4
- "version": "2.0.128",
4
+ "version": "2.0.130",
5
5
  "author": "Tipit LLC",
6
6
  "dependencies": {
7
7
  "@fortawesome/fontawesome-svg-core": "^6.5.2",
@@ -33,9 +33,18 @@ export const setEventLastUpdate = (lastUpdate) => (dispatch) => {
33
33
  */
34
34
  export const getEventById = (
35
35
  eventId
36
- ) => async (dispatch) => {
36
+ ) => async (dispatch, getState) => {
37
37
 
38
38
  dispatch(startLoading());
39
+ // if we have it on the reducer , provide that first
40
+ let {allSchedulesState: {allEvents}} = getState();
41
+ const event = allEvents.find(ev => ev.id === parseInt(eventId));
42
+
43
+ if (event) {
44
+ dispatch(stopLoading());
45
+ dispatch(createAction(GET_EVENT_DATA)({event}));
46
+ }
47
+ // then refresh from api
39
48
 
40
49
  let accessToken;
41
50
  try {
@@ -2,40 +2,13 @@ import {
2
2
  booleanField,
3
3
  stringField,
4
4
  selectField,
5
- selectOption,
6
5
  listField
7
6
  } from "../../../fields";
8
7
 
9
- import {
10
- NAVBAR_FILE_PATH
11
- } from "@utils/filePath";
12
-
13
- const PAGE_RESTRICTIONS = {
14
- any: "ANY",
15
- activity: "ACTIVITY",
16
- marketing: "MARKETING",
17
- lobby: "LOBBY",
18
- show: "SHOW",
19
- customPage: "CUSTOM_PAGE"
20
- };
8
+ import { NAVBAR_FILE_PATH } from "@utils/filePath";
9
+ import { USER_REQUIREMENTS, PAGE_RESTRICTIONS } from "@utils/pageAccessConstants";
21
10
 
22
- const getPageRestrictionsOptions = () =>
23
- Object.entries(PAGE_RESTRICTIONS).map(([key, value]) => selectOption({ label: value, value: value }));
24
-
25
- /*
26
- - file: "src/content/navbar.json"
27
- label: "Navbar"
28
- name: "navbar"
29
- fields:
30
- - {label: "Navbar", name: "items", widget: list, fields: [
31
- {label: "Title", name: "title", widget: string},
32
- {label: "Link", name: "link", widget: string},
33
- {label: "Display?", name: "display", widget: boolean, required: false},
34
- {label: "Requires Auth?", name: "requiresAuth", widget: boolean, required: false, default: false},
35
- {label: "Show only at Show Time?", name: "showOnlyAtShowTime", widget: boolean, required: false, default: false},
36
- {label: "Show only on page", name: "pageRestriction", widget: select, multiple: true, default: ["ANY"], options: ["ANY", "MARKETING", "LOBBY", "ACTIVITY", "SHOW", "CUSTOM_PAGE"]},
37
- ]}
38
- */
11
+ import { mapObjectToSelectOptions } from "../../../utils";
39
12
 
40
13
  const navbar = {
41
14
  label: "Navbar",
@@ -59,32 +32,29 @@ const navbar = {
59
32
  name: "display",
60
33
  required: false
61
34
  }),
62
- booleanField({
63
- label: "Requires Auth?",
64
- name: "requiresAuth",
65
- required: false,
66
- default: false
35
+ selectField({
36
+ label: "User Requirement",
37
+ name: "userRequirement",
38
+ multiple: false,
39
+ default: USER_REQUIREMENTS.none,
40
+ options: mapObjectToSelectOptions(USER_REQUIREMENTS)
41
+ }),
42
+ selectField({
43
+ label: "Show only on Page",
44
+ name: "pageRestriction",
45
+ multiple: true,
46
+ default: [PAGE_RESTRICTIONS.any],
47
+ options: mapObjectToSelectOptions(PAGE_RESTRICTIONS)
67
48
  }),
68
49
  booleanField({
69
50
  label: "Show only at Show Time?",
70
51
  name: "showOnlyAtShowTime",
71
52
  required: false,
72
53
  default: false
73
- }),
74
- selectField({
75
- label: "Show only on page",
76
- name: "pageRestriction",
77
- multiple: true,
78
- default: [PAGE_RESTRICTIONS.any],
79
- options: getPageRestrictionsOptions()
80
54
  })
81
55
  ]
82
56
  })
83
57
  ]
84
58
  };
85
59
 
86
- export {
87
- PAGE_RESTRICTIONS
88
- };
89
-
90
60
  export default navbar;
@@ -4,8 +4,9 @@ module.exports = `
4
4
  title: String
5
5
  link: String
6
6
  display: Boolean
7
- requiresAuth: Boolean
7
+ userRequirement: String
8
8
  pageRestriction: [String]
9
+ showOnlyAtShowTime: Boolean
9
10
  }
10
11
 
11
12
  type NavbarJson implements Node {
@@ -21,6 +21,8 @@ import {
21
21
  IDP_BASE_URL
22
22
  } from "@utils/envVariables";
23
23
 
24
+ import { mapObjectToSelectOptions } from "../../../utils";
25
+
24
26
  const FONT_FORMATS = {
25
27
  truetype: "ttf",
26
28
  opentype: "otf",
@@ -29,9 +31,6 @@ const FONT_FORMATS = {
29
31
  eot: "eot"
30
32
  };
31
33
 
32
- const getFontFormatOptions = () =>
33
- Object.entries(FONT_FORMATS).map(([key, value]) => selectOption({ label: value, value: value }));
34
-
35
34
  const siteSettings = {
36
35
  label: "Site Settings",
37
36
  name: "site-settings",
@@ -151,7 +150,7 @@ const siteSettings = {
151
150
  name: "fontFormat",
152
151
  multiple: false,
153
152
  required: false,
154
- options: getFontFormatOptions()
153
+ options: mapObjectToSelectOptions(FONT_FORMATS)
155
154
  })
156
155
  ],
157
156
  }),
@@ -170,7 +169,7 @@ const siteSettings = {
170
169
  name: "fontFormat",
171
170
  multiple: false,
172
171
  required: false,
173
- options: getFontFormatOptions()
172
+ options: mapObjectToSelectOptions(FONT_FORMATS)
174
173
  })
175
174
  ]
176
175
  })
@@ -1,27 +1,16 @@
1
- import {
2
- collectionDefaults
3
- } from "../../patterns";
1
+ import { collectionDefaults } from "../../patterns";
4
2
 
5
3
  import {
6
4
  hiddenField,
7
5
  stringField,
8
6
  selectField,
9
- selectOption,
10
7
  markdownField
11
8
  } from "../../fields";
12
9
 
13
- import {
14
- CONTENT_PAGES_DIR_PATH
15
- } from "@utils/filePath";
16
-
17
- const USER_REQUIREMENTS = {
18
- none: "NONE",
19
- loggedIn: "LOGGED_IN",
20
- hasTicket: "HAS_TICKET"
21
- };
10
+ import { CONTENT_PAGES_DIR_PATH } from "@utils/filePath";
11
+ import { USER_REQUIREMENTS } from "@utils/pageAccessConstants";
22
12
 
23
- const getUserRequirementsOptions = () =>
24
- Object.entries(USER_REQUIREMENTS).map(([key, value]) => selectOption({ label: value, value: value }));
13
+ import { mapObjectToSelectOptions } from "../../utils";
25
14
 
26
15
  const contentPagesCollection = {
27
16
  ...collectionDefaults({
@@ -49,7 +38,7 @@ const contentPagesCollection = {
49
38
  name: "userRequirement",
50
39
  multiple: false,
51
40
  default: USER_REQUIREMENTS.none,
52
- options: getUserRequirementsOptions()
41
+ options: mapObjectToSelectOptions(USER_REQUIREMENTS)
53
42
  }),
54
43
  markdownField({
55
44
  label: "Body",
@@ -58,8 +47,4 @@ const contentPagesCollection = {
58
47
  ]
59
48
  };
60
49
 
61
- export {
62
- USER_REQUIREMENTS
63
- };
64
-
65
50
  export default contentPagesCollection;
@@ -0,0 +1,4 @@
1
+ import { selectOption } from "./fields";
2
+
3
+ export const mapObjectToSelectOptions = (object) =>
4
+ Object.entries(object).map(([key, value]) => selectOption({ label: value, value: value }));
@@ -17,7 +17,7 @@ import IconButton from "./IconButton";
17
17
  import Link from "./Link";
18
18
 
19
19
  import { getDefaultLocation, validateIdentityProviderButtons } from "@utils/loginUtils";
20
- import { userHasAccessLevel, VirtualAccessLevel } from "@utils/authorizedGroups";
20
+ import { userHasAccessLevel, VIRTUAL_ACCESS_LEVEL } from "@utils/authorizedGroups";
21
21
  import useSiteSettings from "@utils/useSiteSettings";
22
22
  import { PHASES } from "@utils/phasesUtils";
23
23
 
@@ -49,7 +49,7 @@ const AuthComponent = ({
49
49
  const [otpError, setOtpError] = useState(false);
50
50
 
51
51
  const hasVirtualBadge = useMemo(() =>
52
- userProfile ? userHasAccessLevel(userProfile.summit_tickets, VirtualAccessLevel) : false
52
+ userProfile ? userHasAccessLevel(userProfile.summit_tickets, VIRTUAL_ACCESS_LEVEL) : false
53
53
  , [userProfile]);
54
54
  const defaultPath = getDefaultLocation(eventRedirect, hasVirtualBadge);
55
55
 
@@ -1,5 +1,6 @@
1
1
  import React, { useState, useRef, useEffect } from "react";
2
2
  import AvatarEditor from "react-avatar-editor";
3
+ import { AjaxLoader } from "openstack-uicore-foundation/lib/components";
3
4
  import { Box, Typography, Slider, Modal } from "@mui/material";
4
5
  import { Button, IconButton } from "../ui";
5
6
  import { styled } from "@mui/system";
@@ -15,30 +16,50 @@ const CustomSlider = styled(Slider)(({ theme }) => ({
15
16
  color: "var(--color-primary)"
16
17
  }));
17
18
 
18
- const AvatarEditorModal = ({
19
- userProfile,
20
- open,
21
- changePicture,
19
+ const getUserProfilePic = async (imageUrl) => {
20
+ try {
21
+ const response = await fetch(imageUrl, {
22
+ method: "GET",
23
+ mode: "cors"
24
+ });
25
+
26
+ if (!response.ok) {
27
+ throw new Error(`Failed to fetch image: ${response.statusText}`);
28
+ }
29
+
30
+ const blob = await response.blob();
31
+ const imageObjectURL = URL.createObjectURL(blob);
32
+ return imageObjectURL;
33
+ } catch (error) {
34
+ console.error("Error fetching image:", error);
35
+ throw error;
36
+ }
37
+ };
38
+
39
+ const AvatarUploadButton = ({
40
+ onUpload,
41
+ ...rest
42
+ }) => (
43
+ <IconButton
44
+ onClick={onUpload}
45
+ {...rest}
46
+ >
47
+ <CameraAltIcon fontSize="large" />
48
+ </IconButton>
49
+ );
50
+
51
+ const AvatarEditorContent = ({
52
+ editorRef,
53
+ image,
54
+ onUpload,
55
+ handleSave,
22
56
  handleClose
23
57
  }) => {
24
- const editorRef = useRef(null);
25
- const fileInputRef = useRef(null);
26
-
27
- const [image, setImage] = useState(userProfile.picture || null);
28
58
  const [position, setPosition] = useState({ x: 0.5, y: 0.5 });
29
59
  const [scale, setScale] = useState(1);
30
60
  const [rotate, setRotate] = useState(0);
31
61
  const [newImage, setNewImage] = useState(false);
32
62
 
33
- useEffect(() => {
34
- setImage(userProfile.picture);
35
- }, [userProfile.picture]);
36
-
37
- const handleNewImage = (e) => {
38
- setImage(e.target.files[0]);
39
- setNewImage(true);
40
- };
41
-
42
63
  const handleScale = (e, newValue) => {
43
64
  setScale(newValue);
44
65
  setNewImage(true);
@@ -59,13 +80,161 @@ const AvatarEditorModal = ({
59
80
  setNewImage(true);
60
81
  };
61
82
 
83
+ return (
84
+ <>
85
+ <Box
86
+ sx={{
87
+ padding: "20px",
88
+ textAlign: "center",
89
+ position: "relative"
90
+ }}
91
+ >
92
+ <AvatarEditor
93
+ ref={editorRef}
94
+ image={image}
95
+ width={200}
96
+ height={200}
97
+ border={50}
98
+ color={[0, 0, 0, 0.8]}
99
+ position={position}
100
+ onPositionChange={handlePositionChange}
101
+ scale={scale}
102
+ rotate={rotate}
103
+ />
104
+ <AvatarUploadButton
105
+ onUpload={onUpload}
106
+ sx={{
107
+ position: "absolute",
108
+ top: "79.5%",
109
+ right: "45%",
110
+ color: "#fff"
111
+ }}
112
+ />
113
+ </Box>
114
+ <Box
115
+ sx={{
116
+ px: "20px",
117
+ display: "flex",
118
+ flexDirection: "column",
119
+ gap: "20px"
120
+ }}
121
+ >
122
+ <Box
123
+ sx={{
124
+ display: "flex",
125
+ alignItems: "center",
126
+ gap: "20px",
127
+ justifyContent: "center"
128
+ }}
129
+ >
130
+ <Typography
131
+ sx={{
132
+ fontSize: "1.5rem",
133
+ fontFamily: "var(--font_family)"
134
+ }}
135
+ >
136
+ Zoom
137
+ </Typography>
138
+ <CustomSlider
139
+ value={scale}
140
+ min={0}
141
+ max={2}
142
+ step={0.01}
143
+ onChange={handleScale}
144
+ sx={{ flex: 1, mx: 2 }}
145
+ />
146
+ </Box>
147
+ <Box
148
+ sx={{
149
+ display: "flex",
150
+ alignItems: "center"
151
+ }}
152
+ >
153
+ <Typography
154
+ sx={{
155
+ fontSize: "1.5rem",
156
+ fontFamily: "var(--font_family)"
157
+ }}
158
+ >
159
+ Rotate
160
+ </Typography>
161
+ <Box
162
+ sx={{
163
+ display: "flex",
164
+ flex: 1,
165
+ justifyContent: "center",
166
+ gap: "20px"
167
+ }}
168
+ >
169
+ <IconButton onClick={rotateLeft}>
170
+ <RotateLeftIcon fontSize="inherit" />
171
+ </IconButton>
172
+ <IconButton onClick={rotateRight}>
173
+ <RotateRightIcon fontSize="inherit" />
174
+ </IconButton>
175
+ </Box>
176
+ </Box>
177
+ </Box>
178
+ <Box
179
+ sx={{
180
+ backgroundColor: "var(--color_background_light)",
181
+ display: "flex",
182
+ justifyContent: "space-between",
183
+ padding: "20px",
184
+ gap: "20px"
185
+ }}
186
+ >
187
+ <Button onClick={handleClose}>Discard</Button>
188
+ <Button onClick={handleSave} disabled={!newImage}>
189
+ Update
190
+ </Button>
191
+ </Box>
192
+ </>
193
+ );
194
+ };
195
+
196
+ const AvatarEditorModal = ({
197
+ userProfile,
198
+ open,
199
+ changePicture,
200
+ handleClose,
201
+ }) => {
202
+ const editorRef = useRef(null);
203
+ const fileInputRef = useRef(null);
204
+
205
+ const [image, setImage] = useState(null);
206
+ const [loadingPicture, setLoadingPicture] = useState(false);
207
+ const [fetchError, setFetchError] = useState(false);
208
+
209
+ useEffect(() => {
210
+ setLoadingPicture(true);
211
+ getUserProfilePic(userProfile.picture)
212
+ .then((imageObjectURL) => {
213
+ setImage(imageObjectURL);
214
+ setFetchError(false);
215
+ })
216
+ .catch(() => {
217
+ setFetchError(true);
218
+ })
219
+ .finally(() => {
220
+ setLoadingPicture(false);
221
+ });
222
+ }, [userProfile.picture]);
223
+
224
+ const handleNewImage = (e) => {
225
+ setImage(e.target.files[0]);
226
+ setFetchError(false);
227
+ };
228
+
62
229
  const handleSave = () => {
63
- if (editorRef.current && newImage) {
230
+ if (editorRef.current) {
64
231
  const canvas = editorRef.current.getImage().toDataURL();
65
232
  fetch(canvas)
66
233
  .then((res) => res.blob())
67
234
  .then((blob) => {
68
- const file = new File([blob], `${create_UUID()}.png`, { type: blob.type });
235
+ const file = new File([blob], `${create_UUID()}.png`, {
236
+ type: blob.type
237
+ });
69
238
  changePicture(file);
70
239
  });
71
240
  }
@@ -124,36 +293,13 @@ const AvatarEditorModal = ({
124
293
  <i className="fa fa-times" />
125
294
  </IconButton>
126
295
  </Box>
127
- <Box
128
- sx={{
129
- padding: "20px",
130
- textAlign: "center",
131
- position: "relative"
132
- }}
133
- >
134
- <AvatarEditor
135
- ref={editorRef}
136
- image={image}
137
- width={200}
138
- height={200}
139
- border={50}
140
- color={[0, 0, 0, 0.8]}
141
- position={position}
142
- onPositionChange={handlePositionChange}
143
- scale={scale}
144
- rotate={rotate}
296
+ <Box>
297
+ <AjaxLoader
298
+ relative={true}
299
+ color={"var(--color_background_light)"}
300
+ show={loadingPicture}
301
+ size={120}
145
302
  />
146
- <IconButton
147
- onClick={() => fileInputRef.current.click()}
148
- sx={{
149
- position: "absolute",
150
- top: "79.5%",
151
- right: "44%",
152
- color: "#fff"
153
- }}
154
- >
155
- <CameraAltIcon fontSize="inherit" />
156
- </IconButton>
157
303
  <input
158
304
  ref={fileInputRef}
159
305
  type="file"
@@ -161,86 +307,44 @@ const AvatarEditorModal = ({
161
307
  style={{ display: "none" }}
162
308
  onChange={handleNewImage}
163
309
  />
164
- </Box>
165
- <Box
166
- sx={{
167
- px: "20px",
168
- display: "flex",
169
- flexDirection: "column",
170
- gap: "20px"
171
- }}
172
- >
173
- <Box
174
- sx={{
175
- display: "flex",
176
- alignItems: "center",
177
- gap: "20px",
178
- justifyContent: "center"
179
- }}
180
- >
181
- <Typography
182
- sx={{
183
- fontSize: "1.5rem",
184
- fontFamily: "var(--font_family)"
185
- }
186
- }>
187
- Zoom
188
- </Typography>
189
- <CustomSlider
190
- value={scale}
191
- min={0}
192
- max={2}
193
- step={0.01}
194
- onChange={handleScale}
195
- sx={{ flex: 1, mx: 2 }}
196
- />
197
- </Box>
198
- <Box
199
- sx={{
200
- display: "flex",
201
- alignItems: "center"
202
- }}
203
- >
204
- <Typography
205
- sx={{
206
- fontSize: "1.5rem",
207
- fontFamily: "var(--font_family)"
208
- }}
209
- >
210
- Rotate
211
- </Typography>
310
+ {fetchError ? (
212
311
  <Box
213
- sx={{
214
- display: "flex",
215
- flex: 1,
216
- justifyContent: "center",
217
- gap: "20px"
218
- }}
219
- >
220
- <IconButton onClick={rotateLeft}>
221
- <RotateLeftIcon fontSize="inherit" />
222
- </IconButton>
223
- <IconButton onClick={rotateRight}>
224
- <RotateRightIcon fontSize="inherit" />
225
- </IconButton>
312
+ sx={{
313
+ padding: "20px",
314
+ display: "flex",
315
+ flexDirection: "column",
316
+ alignItems: "center",
317
+ gap: "20px"
318
+ }}
319
+ >
320
+ <Typography
321
+ sx={{
322
+ fontSize: "1.5rem",
323
+ fontFamily: "var(--font_family)",
324
+ color: "var(--color_text_dark)"
325
+ }}
326
+ >
327
+ There was an error retrieving your profile picture.
328
+ Please upload a new one.
329
+ </Typography>
330
+ <AvatarUploadButton
331
+ onUpload={() => fileInputRef.current.click()}
332
+ sx={{
333
+ color: "var(--color-primary)",
334
+ border: "1px solid var(--color-primary)",
335
+ padding: "10px"
336
+ }}
337
+ />
226
338
  </Box>
227
- </Box>
228
- </Box>
229
- <Box
230
- sx={{
231
- backgroundColor: "var(--color_background_light)",
232
- display: "flex",
233
- justifyContent: "space-between",
234
- padding: "20px",
235
- gap: "20px"
236
- }}
237
- >
238
- <Button onClick={handleClose}>
239
- Discard
240
- </Button>
241
- <Button onClick={handleSave} disabled={!newImage}>
242
- Update
243
- </Button>
339
+ ) : (
340
+ <AvatarEditorContent
341
+ editorRef={editorRef}
342
+ image={image}
343
+ onUpload={() => fileInputRef.current.click()}
344
+ handleSave={handleSave}
345
+ handleClose={handleClose}
346
+ />
347
+ )}
244
348
  </Box>
245
349
  </Box>
246
350
  </Modal>
@@ -4,11 +4,11 @@ import { connect } from "react-redux";
4
4
  import { navigate } from "gatsby";
5
5
  import NavbarTemplate from "./template";
6
6
 
7
- import { userHasAccessLevel, VirtualAccessLevel } from "@utils/authorizedGroups";
7
+ import { userHasAccessLevel, VIRTUAL_ACCESS_LEVEL } from "@utils/authorizedGroups";
8
8
  import { getDefaultLocation } from "@utils/loginUtils";
9
9
 
10
10
  import { PHASES } from "@utils/phasesUtils";
11
- import { PAGE_RESTRICTIONS } from "../../cms/config/collections/configurationsCollection/navbar";
11
+ import { USER_REQUIREMENTS, PAGE_RESTRICTIONS } from "@utils/pageAccessConstants";
12
12
 
13
13
  import navbarContent from "content/navbar/index.json";
14
14
 
@@ -17,6 +17,8 @@ const Navbar = ({
17
17
  summitPhase,
18
18
  summit,
19
19
  isLoggedUser,
20
+ isAuthorized,
21
+ hasTicket,
20
22
  idpProfile,
21
23
  userProfile,
22
24
  eventRedirect
@@ -24,64 +26,57 @@ const Navbar = ({
24
26
 
25
27
  // we store this calculation to use it later
26
28
  const hasVirtualBadge = useMemo(() =>
27
- userProfile ? userHasAccessLevel(userProfile.summit_tickets, VirtualAccessLevel) : false
29
+ userProfile ? userHasAccessLevel(userProfile.summit_tickets, VIRTUAL_ACCESS_LEVEL) : false
28
30
  , [userProfile]);
29
31
 
30
32
  const defaultPath = getDefaultLocation(eventRedirect, hasVirtualBadge);
31
33
 
34
+ const meetsUserRequirement = (userRequirement) => {
35
+ switch (userRequirement) {
36
+ case USER_REQUIREMENTS.none:
37
+ return true;
38
+ case USER_REQUIREMENTS.loggedIn:
39
+ return isLoggedUser || isAuthorized;
40
+ case USER_REQUIREMENTS.hasTicket:
41
+ return hasTicket || isAuthorized;
42
+ default:
43
+ return false;
44
+ }
45
+ };
46
+
32
47
  const isCustomPage = (path) => {
33
48
  return !isMarketingPage(path) &&
34
49
  !isShowPage(path) &&
35
50
  !isProfilePage(path) &&
36
51
  !isMySchedulePage(path) &&
37
52
  !isExtraQuestionsPage(path);
38
- }
53
+ };
39
54
 
40
- const isMySchedulePage = (path) => {
41
- return path.startsWith("/a/my-schedule");
42
- }
55
+ const isMySchedulePage = (path) => path.startsWith("/a/my-schedule");
43
56
 
44
- const isProfilePage = (path) => {
45
- return path.startsWith("/a/profile");
46
- }
57
+ const isProfilePage = (path) => path.startsWith("/a/profile");
47
58
 
48
- const isExtraQuestionsPage = (path) => {
49
- return path.startsWith("/a/extra-questions");
50
- }
59
+ const isExtraQuestionsPage = (path) => path.startsWith("/a/extra-questions");
51
60
 
52
- const isMarketingPage = (path) => {
53
- return path === '/';
54
- }
61
+ const isMarketingPage = (path) => path === "/";
55
62
 
56
- const isLobbyPage = (path) => {
57
- return path === '/a' || path === '/a/';
58
- }
63
+ const isLobbyPage = (path) => path === "/a" || path === "/a/";
59
64
 
60
- const isActivityPage = (path) => {
61
- return path.startsWith("/a/event");
62
- }
65
+ const isActivityPage = (path) => path.startsWith("/a/event");
63
66
 
64
- const isSponsorPage = (path) => {
65
- return path.startsWith("/a/sponsor");
66
- }
67
+ const isSponsorPage = (path) => path.startsWith("/a/sponsor");
67
68
 
68
- const isSchedulePage = (path) => {
69
- return path.startsWith("/a/schedule");
70
- }
69
+ const isSchedulePage = (path) => path.startsWith("/a/schedule");
71
70
 
72
- const isShowPage = (path) => {
73
- return isLobbyPage(path) || // lobby
74
- isActivityPage(path) || // activity
75
- isSponsorPage(path) || // expo hall or sponsor page
76
- isSchedulePage(path);// schedule
77
- }
71
+ const isShowPage = (path) => isLobbyPage(path) || isActivityPage(path) || isSponsorPage(path) || isSchedulePage(path);
78
72
 
79
73
  // we assume that all pages under /a/* requires auth except /a/schedule
80
- // item.requiresAuth allows to mark specific pages that are not under /a/* pattern.
74
+ // item.userRequirement allows to mark specific pages that are not under /a/* pattern.
81
75
  const showItem = (item) => {
82
76
  // check if we have location defined, if so use the path name , else if window is defined use the window.location
83
77
  // as a fallback
84
- const currentPath = location ? location.pathname: (typeof window !== "undefined" ? window.location.pathname: "");
78
+ const currentPath = location ? location.pathname : (typeof window !== "undefined" ? window.location.pathname : "");
79
+
85
80
  const passPageRestriction = !item.pageRestriction ||
86
81
  item.link === currentPath || // if we are on the same page then show it
87
82
  item.pageRestriction.includes(PAGE_RESTRICTIONS.any) ||
@@ -89,11 +84,10 @@ const Navbar = ({
89
84
  (item.pageRestriction.includes(PAGE_RESTRICTIONS.marketing) && isMarketingPage(currentPath)) ||
90
85
  (item.pageRestriction.includes(PAGE_RESTRICTIONS.lobby) && isLobbyPage(currentPath)) ||
91
86
  (item.pageRestriction.includes(PAGE_RESTRICTIONS.show) && isShowPage(currentPath)) ||
92
- (item.pageRestriction.includes(PAGE_RESTRICTIONS.customPage) && isCustomPage(currentPath))
93
- ;
87
+ (item.pageRestriction.includes(PAGE_RESTRICTIONS.customPage) && isCustomPage(currentPath));
94
88
 
95
89
  return item.display &&
96
- (!item.requiresAuth || isLoggedUser) &&
90
+ meetsUserRequirement(item.userRequirement) &&
97
91
  (!item.showOnlyAtShowTime || summitPhase >= PHASES.DURING) &&
98
92
  passPageRestriction;
99
93
  };
@@ -115,7 +109,6 @@ const Navbar = ({
115
109
  );
116
110
  };
117
111
 
118
-
119
112
  const mapStateToProps = ({
120
113
  clockState,
121
114
  settingState,
@@ -126,6 +119,8 @@ const mapStateToProps = ({
126
119
  summit: summitState.summit,
127
120
  summitPhase: clockState.summit_phase,
128
121
  isLoggedUser: loggedUserState.isLoggedUser,
122
+ isAuthorized: userState.isAuthorized,
123
+ hasTicket: userState.hasTicket,
129
124
  idpProfile: userState.idpProfile,
130
125
  userProfile: userState.userProfile,
131
126
  // TODO: move to site settings i/o marketing page settings
@@ -24,7 +24,7 @@ import "summit-registration-lite/dist/index.css";
24
24
  import useSiteSettings from "@utils/useSiteSettings";
25
25
  import useMarketingSettings, { MARKETING_SETTINGS_KEYS } from "@utils/useMarketingSettings";
26
26
  import { getEnvVariable, SUMMIT_API_BASE_URL, OAUTH2_CLIENT_ID, REGISTRATION_BASE_URL, SUPPORT_EMAIL } from "@utils/envVariables";
27
- import { userHasAccessLevel, VirtualAccessLevel } from "@utils/authorizedGroups";
27
+ import { userHasAccessLevel, VIRTUAL_ACCESS_LEVEL } from "@utils/authorizedGroups";
28
28
  import { validateIdentityProviderButtons } from "@utils/loginUtils";
29
29
  import { triggerTagManagerTrackEvent } from "@utils/eventTriggers";
30
30
 
@@ -136,7 +136,7 @@ const RegistrationLiteComponent = ({
136
136
  loading: loadingProfile || loadingIDP,
137
137
  // only show info if its not a recent purchase
138
138
  ticketOwned: userProfile?.summit_tickets?.length > 0,
139
- hasVirtualAccessLevel: userHasAccessLevel(userProfile?.summit_tickets, VirtualAccessLevel),
139
+ hasVIRTUAL_ACCESS_LEVEL: userHasAccessLevel(userProfile?.summit_tickets, VIRTUAL_ACCESS_LEVEL),
140
140
  ownedTickets: attendee?.ticket_types || [],
141
141
  authUser: (provider) => onClickLogin(provider),
142
142
  getPasswordlessCode: getPasswordlessCode,
@@ -1,15 +1,16 @@
1
- import React from 'react'
2
- import { Link } from 'gatsby'
3
- import { connect } from 'react-redux'
1
+ import React from "react";
2
+ import { Link } from "gatsby";
3
+ import { connect } from "react-redux";
4
4
 
5
- import styles from '../styles/navbar.module.scss';
6
- import LogoutButton from './LogoutButton';
7
- import ProfilePopupComponent from './ProfilePopupComponent';
5
+ import LogoutButton from "./LogoutButton";
6
+ import ProfilePopupComponent from "./ProfilePopupComponent";
8
7
 
9
- import { updateProfilePicture, updateProfile } from '../actions/user-actions'
8
+ import { updateProfilePicture, updateProfile } from "../actions/user-actions"
10
9
 
11
- import { getDefaultLocation } from '../utils/loginUtils';
12
- import {userHasAccessLevel, VirtualAccessLevel} from "../utils/authorizedGroups";
10
+ import { getDefaultLocation } from ".@utils/loginUtils";
11
+ import { userHasAccessLevel, VIRTUAL_ACCESS_LEVEL } from "../utils/authorizedGroups";
12
+
13
+ import styles from "../styles/navbar.module.scss";
13
14
 
14
15
  const UserNavbar = class extends React.Component {
15
16
 
@@ -18,7 +19,7 @@ const UserNavbar = class extends React.Component {
18
19
  this.state = {
19
20
  active: false,
20
21
  showProfile: false,
21
- navBarActiveClass: '',
22
+ navBarActiveClass: "",
22
23
  }
23
24
  }
24
25
 
@@ -32,9 +33,9 @@ const UserNavbar = class extends React.Component {
32
33
 
33
34
  handleTogglePopup = (profile) => {
34
35
  if (profile) {
35
- document.body.classList.add('is-clipped');
36
+ document.body.classList.add("is-clipped");
36
37
  } else {
37
- document.body.classList.remove('is-clipped');
38
+ document.body.classList.remove("is-clipped");
38
39
  }
39
40
  this.setState({showProfile: profile})
40
41
  }
@@ -53,7 +54,7 @@ const UserNavbar = class extends React.Component {
53
54
  navBarActiveClass: `${styles.isActive}`,
54
55
  })
55
56
  : this.setState({
56
- navBarActiveClass: '',
57
+ navBarActiveClass: "",
57
58
  })
58
59
  }
59
60
  )
@@ -66,7 +67,7 @@ const UserNavbar = class extends React.Component {
66
67
 
67
68
  // we store this calculation to use it later
68
69
  const hasVirtualBadge =
69
- userProfile ? userHasAccessLevel(userProfile.summit_tickets, VirtualAccessLevel) : false;
70
+ userProfile ? userHasAccessLevel(userProfile.summit_tickets, VIRTUAL_ACCESS_LEVEL) : false;
70
71
 
71
72
  let defaultPath = getDefaultLocation(eventRedirect, hasVirtualBadge);
72
73
 
@@ -143,4 +144,4 @@ const mapStateToProps = ({ settingState, userState }) => ({
143
144
  userProfile: userState.userProfile
144
145
  });
145
146
 
146
- export default connect(mapStateToProps, { updateProfilePicture, updateProfile })(UserNavbar)
147
+ export default connect(mapStateToProps, { updateProfilePicture, updateProfile })(UserNavbar);
@@ -4,7 +4,7 @@
4
4
  "title": "Lobby",
5
5
  "link": "/a/",
6
6
  "display": true,
7
- "requiresAuth": true,
7
+ "userRequirement": "LOGGED_IN",
8
8
  "pageRestriction": [
9
9
  "ANY"
10
10
  ]
@@ -13,7 +13,7 @@
13
13
  "title": "My Tickets",
14
14
  "link": "/a/my-tickets",
15
15
  "display": true,
16
- "requiresAuth": true,
16
+ "userRequirement": "LOGGED_IN",
17
17
  "pageRestriction": [
18
18
  "ANY"
19
19
  ]
@@ -22,7 +22,7 @@
22
22
  "title": "Schedule",
23
23
  "link": "/a/schedule",
24
24
  "display": true,
25
- "requiresAuth": false,
25
+ "userRequirement": "NONE",
26
26
  "pageRestriction": [
27
27
  "ANY"
28
28
  ]
@@ -31,13 +31,13 @@
31
31
  "title": "My Schedule",
32
32
  "link": "/a/my-schedule",
33
33
  "display": true,
34
- "requiresAuth": false
34
+ "userRequirement": "NONE"
35
35
  },
36
36
  {
37
37
  "title": "My Profile",
38
38
  "link": "/a/profile",
39
39
  "display": true,
40
- "requiresAuth": true,
40
+ "userRequirement": "LOGGED_IN",
41
41
  "pageRestriction": [
42
42
  "ANY"
43
43
  ]
@@ -1,10 +1,10 @@
1
- import React, {useEffect, useState, useMemo} from "react";
2
- import {connect} from "react-redux";
3
- import {navigate} from "gatsby";
1
+ import React, { useEffect, useState, useMemo } from "react";
2
+ import { connect } from "react-redux";
3
+ import { navigate } from "gatsby";
4
4
  import { pick } from "@gatsbyjs/reach-router";
5
5
  import { getUserProfile, requireExtraQuestions } from "../actions/user-actions";
6
6
  import HeroComponent from "../components/HeroComponent";
7
- import { userHasAccessLevel, VirtualAccessLevel } from "../utils/authorizedGroups";
7
+ import { userHasAccessLevel, VIRTUAL_ACCESS_LEVEL } from "@utils/authorizedGroups";
8
8
 
9
9
  const pathsRequiringVirtualBadge = [
10
10
  { path: "/a/" },
@@ -12,7 +12,7 @@ const pathsRequiringVirtualBadge = [
12
12
  { path: "/a/posters" },
13
13
  { path: "/a/sponsors" },
14
14
  { path: "/a/posters/:trackGroupId" },
15
- { path: "/a/poster/:presentationId/" },
15
+ { path: "/a/poster/:presentationId/" }
16
16
  ];
17
17
 
18
18
  /**
@@ -44,7 +44,7 @@ const WithAuthzRoute = ({
44
44
 
45
45
  // we store this calculation to use it later
46
46
  const hasVirtualBadge = useMemo(() =>
47
- userProfile ? userHasAccessLevel(userProfile.summit_tickets, VirtualAccessLevel) : false,
47
+ userProfile ? userHasAccessLevel(userProfile.summit_tickets, VIRTUAL_ACCESS_LEVEL) : false,
48
48
  [userProfile.summit_tickets]);
49
49
 
50
50
  const userIsReady = () => {
@@ -98,7 +98,7 @@ const WithAuthzRoute = ({
98
98
  return children;
99
99
  };
100
100
 
101
- const mapStateToProps = ({userState}) => ({
101
+ const mapStateToProps = ({ userState }) => ({
102
102
  userProfile: userState.userProfile,
103
103
  isAuthorized: userState.isAuthorized,
104
104
  hasTicket: userState.hasTicket,
@@ -11,19 +11,20 @@
11
11
  * limitations under the License.
12
12
  **/
13
13
 
14
- import React from 'react'
15
- import URI from "urijs"
16
- import { navigate } from "gatsby"
14
+ import React from "react";
15
+ import URI from "urijs";
16
+ import { navigate } from "gatsby";
17
17
  import { Redirect } from "@gatsbyjs/reach-router";
18
- import { connect } from 'react-redux';
18
+ import { connect } from "react-redux";
19
19
  import AbstractAuthorizationCallbackRoute from "openstack-uicore-foundation/lib/security/abstract-auth-callback-route";
20
- import { getUserProfile, addToSchedule, removeFromSchedule } from '../actions/user-actions'
20
+ import { getUserProfile, addToSchedule, removeFromSchedule } from "../actions/user-actions";
21
21
  import HeroComponent from "../components/HeroComponent";
22
- import { getEnvVariable, IDP_BASE_URL, OAUTH2_CLIENT_ID } from '../utils/envVariables'
23
- import { getPendingAction } from '../utils/schedule';
22
+ import { getEnvVariable, IDP_BASE_URL, OAUTH2_CLIENT_ID } from "@utils/envVariables";
23
+ import { getPendingAction } from "@utils/schedule";
24
24
 
25
- import '../styles/bulma.scss';
26
- import {userHasAccessLevel, VirtualAccessLevel} from "../utils/authorizedGroups";
25
+ import { userHasAccessLevel, VIRTUAL_ACCESS_LEVEL } from "@utils/authorizedGroups";
26
+
27
+ import "../styles/bulma.scss";
27
28
 
28
29
  class AuthorizationCallbackRoute extends AbstractAuthorizationCallbackRoute {
29
30
 
@@ -36,16 +37,16 @@ class AuthorizationCallbackRoute extends AbstractAuthorizationCallbackRoute {
36
37
  const pendingAction = getPendingAction();
37
38
  if (pendingAction) {
38
39
  const { action, event } = pendingAction;
39
- action === 'ADD_EVENT' ? this.props.addToSchedule(event) : this.props.removeFromSchedule(event);
40
+ action === "ADD_EVENT" ? this.props.addToSchedule(event) : this.props.removeFromSchedule(event);
40
41
  }
41
42
  backUrl = URI.decode(backUrl);
42
43
  // fallback
43
- if(!backUrl || backUrl == '')
44
- backUrl = '/';
44
+ if(!backUrl || backUrl == "")
45
+ backUrl = "/";
45
46
  let { userProfile } = this.props;
46
47
  // if redirect to lobby first time if we have virtual access
47
- if(backUrl == '/' && userProfile && userHasAccessLevel(userProfile.summit_tickets, VirtualAccessLevel)){
48
- backUrl = '/a/';
48
+ if(backUrl == "/" && userProfile && userHasAccessLevel(userProfile.summit_tickets, VIRTUAL_ACCESS_LEVEL)){
49
+ backUrl = "/a/";
49
50
  }
50
51
  navigate(backUrl);
51
52
  });
@@ -54,9 +55,9 @@ class AuthorizationCallbackRoute extends AbstractAuthorizationCallbackRoute {
54
55
  _redirect2Error(error) {
55
56
  console.log(`AuthorizationCallbackRoute error ${error}`);
56
57
  if (
57
- error.includes('access_denied') ||
58
- error.includes('consent_required')
59
- ) return <Redirect to={'/'} noThrow />;
58
+ error.includes("access_denied") ||
59
+ error.includes("consent_required")
60
+ ) return <Redirect to={"/"} noThrow />;
60
61
  return <Redirect to={`/error?error=${error}`} noThrow/>;
61
62
  }
62
63
 
@@ -87,4 +88,4 @@ export default connect(mapStateToProps, {
87
88
  getUserProfile,
88
89
  addToSchedule,
89
90
  removeFromSchedule,
90
- })(AuthorizationCallbackRoute)
91
+ })(AuthorizationCallbackRoute);
@@ -202,7 +202,7 @@ h2 {
202
202
  }
203
203
 
204
204
  .talk {
205
- color: var(--color_text_med);
205
+ color: var(--color_text_dark);
206
206
 
207
207
  &__break {
208
208
  background-color: var(--color_secondary);
@@ -7,9 +7,9 @@ import { MDXProvider } from "@mdx-js/react";
7
7
  import ContentPageTemplate from "./template";
8
8
  import Layout from "../../components/Layout";
9
9
  import Seo from "../../components/Seo";
10
- import { titleFromPathname } from "../../utils/urlFormating";
10
+ import { titleFromPathname } from "@utils/urlFormating";
11
11
 
12
- import { USER_REQUIREMENTS } from "../../cms/config/collections/contentPagesCollection";
12
+ import { USER_REQUIREMENTS } from "@utils/pageAccessConstants";
13
13
 
14
14
  const ContentPage = ({
15
15
  data,
@@ -1,19 +1,19 @@
1
- import React, {useMemo} from 'react'
2
- import { connect } from 'react-redux'
3
- import { navigate } from "gatsby"
1
+ import React, {useMemo} from "react";
2
+ import { connect } from "react-redux";
3
+ import { navigate } from "gatsby";
4
4
 
5
- import Layout from '../components/Layout'
6
- import AuthComponent from '../components/AuthComponent'
5
+ import Layout from "../components/Layout";
6
+ import AuthComponent from "../components/AuthComponent";
7
7
 
8
- import { getDefaultLocation } from '../utils/loginUtils'
9
- import {userHasAccessLevel, VirtualAccessLevel} from "../utils/authorizedGroups";
8
+ import { getDefaultLocation } from "@utils/loginUtils";
9
+ import {userHasAccessLevel, VIRTUAL_ACCESS_LEVEL} from "@utils/authorizedGroups";
10
10
 
11
11
 
12
12
  export const LoginPageTemplate = ({ loggedUserState, eventRedirect, location, userProfile }) => {
13
13
 
14
14
  // we store this calculation to use it later
15
15
  const hasVirtualBadge = useMemo(() =>
16
- userProfile ? userHasAccessLevel(userProfile.summit_tickets, VirtualAccessLevel) : false,
16
+ userProfile ? userHasAccessLevel(userProfile.summit_tickets, VIRTUAL_ACCESS_LEVEL) : false,
17
17
  [userProfile]);
18
18
 
19
19
  if (loggedUserState.isLoggedUser) {
@@ -1,15 +1,15 @@
1
- import React from 'react'
2
- import PropTypes from 'prop-types'
3
- import { connect } from 'react-redux'
1
+ import React from "react";
2
+ import PropTypes from "prop-types";
3
+ import { connect } from "react-redux";
4
4
 
5
5
  import URI from "urijs";
6
- import { handleResetReducers } from '../actions/event-actions'
7
- import { doLogin } from 'openstack-uicore-foundation/lib/security/methods'
6
+ import { handleResetReducers } from "../actions/event-actions";
7
+ import { doLogin } from "openstack-uicore-foundation/lib/security/methods";
8
8
 
9
- import { getDefaultLocation } from '../utils/loginUtils';
9
+ import { getDefaultLocation } from "@utils/loginUtils";
10
10
 
11
- import HeroComponent from '../components/HeroComponent'
12
- import { userHasAccessLevel, VirtualAccessLevel } from "../utils/authorizedGroups";
11
+ import HeroComponent from "../components/HeroComponent";
12
+ import { userHasAccessLevel, VIRTUAL_ACCESS_LEVEL } from "../utils/authorizedGroups";
13
13
 
14
14
  export const TokenExpirePageTemplate = class extends React.Component {
15
15
 
@@ -22,10 +22,10 @@ export const TokenExpirePageTemplate = class extends React.Component {
22
22
 
23
23
  // we store this calculation to use it later
24
24
  const hasVirtualBadge =
25
- userProfile ? userHasAccessLevel(userProfile.summit_tickets, VirtualAccessLevel) : false;
25
+ userProfile ? userHasAccessLevel(userProfile.summit_tickets, VIRTUAL_ACCESS_LEVEL) : false;
26
26
 
27
27
  let defaultPath = getDefaultLocation(eventRedirect, hasVirtualBadge);
28
- let previousLocation = location.state?.backUrl && location.state.backUrl !== '/auth/expired' ? location.state.backUrl : defaultPath;
28
+ let previousLocation = location.state?.backUrl && location.state.backUrl !== "/auth/expired" ? location.state.backUrl : defaultPath;
29
29
  let backUrl = URI.encode(previousLocation);
30
30
 
31
31
  setTimeout(() => {
@@ -3,7 +3,7 @@ import {
3
3
  AUTHZ_USER_GROUPS,
4
4
  } from "./envVariables";
5
5
 
6
- export const VirtualAccessLevel = 'VIRTUAL';
6
+ export const VIRTUAL_ACCESS_LEVEL = "VIRTUAL";
7
7
 
8
8
  export const isAuthorizedUser = (groups) => {
9
9
  let authorizedGroups = getEnvVariable(AUTHZ_USER_GROUPS);
@@ -0,0 +1,16 @@
1
+ const USER_REQUIREMENTS = {
2
+ none: "NONE",
3
+ loggedIn: "LOGGED_IN",
4
+ hasTicket: "HAS_TICKET"
5
+ };
6
+
7
+ const PAGE_RESTRICTIONS = {
8
+ any: "ANY",
9
+ activity: "ACTIVITY",
10
+ marketing: "MARKETING",
11
+ lobby: "LOBBY",
12
+ show: "SHOW",
13
+ customPage: "CUSTOM_PAGE"
14
+ };
15
+
16
+ export { USER_REQUIREMENTS, PAGE_RESTRICTIONS };