@selfcommunity/react-ui 0.8.0-alpha.0 → 0.8.0-alpha.10

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.
Files changed (60) hide show
  1. package/lib/cjs/components/BottomNavigation/BottomNavigation.js +11 -2
  2. package/lib/cjs/components/Composer/Composer.js +8 -2
  3. package/lib/cjs/components/Composer/Layer/AudienceLayer/AudienceLayer.js +12 -3
  4. package/lib/cjs/components/CreateEventButton/CreateEventButton.js +5 -1
  5. package/lib/cjs/components/CreateEventWidget/CreateEventWidget.js +5 -1
  6. package/lib/cjs/components/EventForm/EventAddress.d.ts +3 -8
  7. package/lib/cjs/components/EventForm/EventAddress.js +20 -20
  8. package/lib/cjs/components/EventForm/EventForm.d.ts +1 -1
  9. package/lib/cjs/components/EventForm/EventForm.js +90 -76
  10. package/lib/cjs/components/EventForm/constants.d.ts +4 -0
  11. package/lib/cjs/components/EventForm/constants.js +5 -1
  12. package/lib/cjs/components/EventForm/types.d.ts +27 -0
  13. package/lib/cjs/components/EventForm/types.js +2 -0
  14. package/lib/cjs/components/EventForm/utils.d.ts +4 -0
  15. package/lib/cjs/components/EventForm/utils.js +28 -0
  16. package/lib/cjs/components/EventHeader/EventHeader.js +1 -1
  17. package/lib/cjs/components/EventInfoWidget/EventInfoWidget.js +24 -1
  18. package/lib/cjs/components/Events/Events.js +30 -4
  19. package/lib/cjs/components/MyEventsWidget/MyEventsWidget.js +34 -2
  20. package/lib/cjs/components/NavigationToolbar/NavigationToolbar.js +12 -3
  21. package/lib/cjs/components/NavigationToolbarMobile/NavigationToolbarMobile.js +11 -2
  22. package/lib/cjs/components/OnBoardingWidget/OnBoardingWidget.js +23 -15
  23. package/lib/cjs/components/SuggestedEventsWidget/SuggestedEventsWidget.js +26 -0
  24. package/lib/cjs/components/UserSubscribedGroupsWidget/UserSubscribedGroupsWidget.js +7 -2
  25. package/lib/cjs/constants/PubSub.d.ts +1 -0
  26. package/lib/cjs/constants/PubSub.js +1 -0
  27. package/lib/cjs/shared/EventActionsMenu/index.d.ts +4 -0
  28. package/lib/cjs/shared/EventActionsMenu/index.js +16 -3
  29. package/lib/cjs/shared/Media/Link/DisplayComponent.js +21 -5
  30. package/lib/esm/components/BottomNavigation/BottomNavigation.js +11 -2
  31. package/lib/esm/components/Composer/Composer.js +8 -2
  32. package/lib/esm/components/Composer/Layer/AudienceLayer/AudienceLayer.js +13 -4
  33. package/lib/esm/components/CreateEventButton/CreateEventButton.js +5 -1
  34. package/lib/esm/components/CreateEventWidget/CreateEventWidget.js +5 -1
  35. package/lib/esm/components/EventForm/EventAddress.d.ts +3 -8
  36. package/lib/esm/components/EventForm/EventAddress.js +20 -20
  37. package/lib/esm/components/EventForm/EventForm.d.ts +1 -1
  38. package/lib/esm/components/EventForm/EventForm.js +92 -78
  39. package/lib/esm/components/EventForm/constants.d.ts +4 -0
  40. package/lib/esm/components/EventForm/constants.js +4 -0
  41. package/lib/esm/components/EventForm/types.d.ts +27 -0
  42. package/lib/esm/components/EventForm/types.js +1 -0
  43. package/lib/esm/components/EventForm/utils.d.ts +4 -0
  44. package/lib/esm/components/EventForm/utils.js +21 -0
  45. package/lib/esm/components/EventHeader/EventHeader.js +1 -1
  46. package/lib/esm/components/EventInfoWidget/EventInfoWidget.js +25 -2
  47. package/lib/esm/components/Events/Events.js +31 -5
  48. package/lib/esm/components/MyEventsWidget/MyEventsWidget.js +36 -4
  49. package/lib/esm/components/NavigationToolbar/NavigationToolbar.js +12 -3
  50. package/lib/esm/components/NavigationToolbarMobile/NavigationToolbarMobile.js +11 -2
  51. package/lib/esm/components/OnBoardingWidget/OnBoardingWidget.js +24 -16
  52. package/lib/esm/components/SuggestedEventsWidget/SuggestedEventsWidget.js +27 -1
  53. package/lib/esm/components/UserSubscribedGroupsWidget/UserSubscribedGroupsWidget.js +8 -3
  54. package/lib/esm/constants/PubSub.d.ts +1 -0
  55. package/lib/esm/constants/PubSub.js +1 -0
  56. package/lib/esm/shared/EventActionsMenu/index.d.ts +4 -0
  57. package/lib/esm/shared/EventActionsMenu/index.js +16 -3
  58. package/lib/esm/shared/Media/Link/DisplayComponent.js +21 -5
  59. package/lib/umd/react-ui.js +1 -1
  60. package/package.json +6 -6
@@ -0,0 +1,21 @@
1
+ import { addDays, addHours } from 'date-fns';
2
+ export function getNewDate(date) {
3
+ if (date) {
4
+ return new Date(date);
5
+ }
6
+ return new Date();
7
+ }
8
+ export function getLaterHoursDate(h) {
9
+ return addHours(getNewDate(), h);
10
+ }
11
+ export function getLaterDaysDate(d) {
12
+ return addDays(getNewDate(), d);
13
+ }
14
+ export const combineDateAndTime = (date, time) => {
15
+ const combined = date;
16
+ combined.setHours(time.getHours());
17
+ combined.setMinutes(time.getMinutes());
18
+ combined.setSeconds(time.getSeconds());
19
+ combined.setMilliseconds(time.getMilliseconds());
20
+ return combined.toISOString();
21
+ };
@@ -174,5 +174,5 @@ export default function EventHeader(inProps) {
174
174
  month: 'long'
175
175
  }),
176
176
  hour: intl.formatDate(scEvent.start_date, { hour: 'numeric', minute: 'numeric' })
177
- } })) })), _jsx(Typography, Object.assign({ variant: "h5", className: classes.name }, { children: scEvent.name })), _jsxs(Box, Object.assign({ className: classes.visibility }, { children: [_jsx(_Fragment, { children: scEvent.privacy === SCEventPrivacyType.PUBLIC ? (_jsxs(Typography, Object.assign({ className: classes.visibilityItem }, { children: [_jsx(Icon, { children: "public" }), _jsx(FormattedMessage, { id: "ui.eventHeader.visibility.public", defaultMessage: "ui.eventHeader.visibility.public" })] }))) : (_jsxs(Typography, Object.assign({ className: classes.visibilityItem }, { children: [_jsx(Icon, { children: "private" }), _jsx(FormattedMessage, { id: "ui.eventHeader.visibility.private", defaultMessage: "ui.eventHeader.visibility.private" })] }))) }), _jsx(Bullet, {}), _jsx(Typography, Object.assign({ className: classes.visibilityItem }, { children: scEvent.location === SCEventLocationType.PERSON ? (_jsx(FormattedMessage, { id: "ui.eventHeader.location.live", defaultMessage: "ui.eventHeader.location.live" })) : (_jsx(FormattedMessage, { id: "ui.eventHeader.location.online", defaultMessage: "ui.eventHeader.location.online" })) }))] })), _jsx(User, { className: classes.planner, userId: scEvent.managed_by.id, secondary: _jsx(FormattedMessage, { id: "ui.eventHeader.user.manager", defaultMessage: "ui.eventHeader.user.manager" }), elevation: 0, actions: _jsx(_Fragment, { children: isEventAdmin ? (_jsxs(Box, Object.assign({ className: classes.multiActions }, { children: [_jsx(EventInviteButton, { size: isMobile ? 'small' : 'medium', event: scEvent, eventId: scEvent.id, disabled: isEventFinished }), _jsxs(Box, { children: [_jsx(EditEventButton, { size: isMobile ? 'small' : 'medium', event: scEvent, eventId: scEvent.id, onEditSuccess: (data) => setSCEvent(data), disabled: isEventFinished }), _jsx(EventActionsMenu, Object.assign({ event: scEvent }, EventActionsProps))] })] }))) : (_jsxs(_Fragment, { children: [_jsx(EventSubscribeButton, Object.assign({ event: scEvent, onSubscribe: handleSubscribe }, EventSubscribeButtonProps, { disabled: isEventFinished })), _jsx(EventActionsMenu, Object.assign({ event: scEvent }, EventActionsProps))] })) }) })] }))] })));
177
+ } })) })), _jsx(Typography, Object.assign({ variant: "h5", className: classes.name }, { children: scEvent.name })), _jsxs(Box, Object.assign({ className: classes.visibility }, { children: [_jsx(_Fragment, { children: scEvent.privacy === SCEventPrivacyType.PUBLIC ? (_jsxs(Typography, Object.assign({ className: classes.visibilityItem }, { children: [_jsx(Icon, { children: "public" }), _jsx(FormattedMessage, { id: "ui.eventHeader.visibility.public", defaultMessage: "ui.eventHeader.visibility.public" })] }))) : (_jsxs(Typography, Object.assign({ className: classes.visibilityItem }, { children: [_jsx(Icon, { children: "private" }), _jsx(FormattedMessage, { id: "ui.eventHeader.visibility.private", defaultMessage: "ui.eventHeader.visibility.private" })] }))) }), _jsx(Bullet, {}), _jsx(Typography, Object.assign({ className: classes.visibilityItem }, { children: scEvent.location === SCEventLocationType.PERSON ? (_jsx(FormattedMessage, { id: "ui.eventHeader.location.live", defaultMessage: "ui.eventHeader.location.live" })) : (_jsx(FormattedMessage, { id: "ui.eventHeader.location.online", defaultMessage: "ui.eventHeader.location.online" })) }))] })), _jsx(User, { className: classes.planner, userId: scEvent.managed_by.id, secondary: _jsx(FormattedMessage, { id: "ui.eventHeader.user.manager", defaultMessage: "ui.eventHeader.user.manager" }), elevation: 0, actions: _jsx(_Fragment, { children: isEventAdmin ? (_jsxs(Box, Object.assign({ className: classes.multiActions }, { children: [_jsx(EventInviteButton, { size: isMobile ? 'small' : 'medium', event: scEvent, eventId: scEvent.id, disabled: isEventFinished }), _jsxs(Box, { children: [!isMobile && (_jsx(EditEventButton, { size: isMobile ? 'small' : 'medium', event: scEvent, eventId: scEvent.id, onEditSuccess: (data) => setSCEvent(data), disabled: isEventFinished })), _jsx(EventActionsMenu, Object.assign({ event: scEvent, onEditSuccess: (data) => setSCEvent(data) }, EventActionsProps))] })] }))) : (_jsxs(_Fragment, { children: [_jsx(EventSubscribeButton, Object.assign({ event: scEvent, onSubscribe: handleSubscribe }, EventSubscribeButtonProps, { disabled: isEventFinished })), _jsx(EventActionsMenu, Object.assign({ event: scEvent, onEditSuccess: (data) => setSCEvent(data) }, EventActionsProps))] })) }) })] }))] })));
178
178
  }
@@ -3,13 +3,15 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { Box, Button, CardContent, Icon, Stack, styled, Typography, useThemeProps } from '@mui/material';
4
4
  import { useSCFetchEvent } from '@selfcommunity/react-core';
5
5
  import { SCEventPrivacyType, SCEventSubscriptionStatusType } from '@selfcommunity/types';
6
- import { useCallback, useEffect, useMemo, useState } from 'react';
6
+ import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
7
7
  import { FormattedMessage } from 'react-intl';
8
8
  import EventInfoDetails from '../../shared/EventInfoDetails';
9
9
  import HiddenPlaceholder from '../../shared/HiddenPlaceholder';
10
10
  import Widget from '../Widget';
11
11
  import { PREFIX } from './constants';
12
12
  import Skeleton from './Skeleton';
13
+ import PubSub from 'pubsub-js';
14
+ import { SCGroupEventType, SCTopicType } from '../../constants/PubSub';
13
15
  const classes = {
14
16
  root: `${PREFIX}-root`,
15
17
  content: `${PREFIX}-content`,
@@ -43,7 +45,9 @@ export default function EventInfoWidget(inProps) {
43
45
  const [showButton, setShowButton] = useState(!summaryExpanded);
44
46
  const [loading, setLoading] = useState(true);
45
47
  // HOOKS
46
- const { scEvent } = useSCFetchEvent({ id: eventId, event });
48
+ const { scEvent, setSCEvent } = useSCFetchEvent({ id: eventId, event });
49
+ // REFS
50
+ const updatesSubscription = useRef(null);
47
51
  useEffect(() => {
48
52
  setLoading(false);
49
53
  }, []);
@@ -65,6 +69,25 @@ export default function EventInfoWidget(inProps) {
65
69
  const showInfo = useMemo(() => (scEvent === null || scEvent === void 0 ? void 0 : scEvent.privacy) === SCEventPrivacyType.PUBLIC ||
66
70
  [SCEventSubscriptionStatusType.SUBSCRIBED, SCEventSubscriptionStatusType.GOING, SCEventSubscriptionStatusType.NOT_GOING].indexOf(scEvent === null || scEvent === void 0 ? void 0 : scEvent.subscription_status) > -1, [scEvent]);
67
71
  const description = useMemo(() => (expanded ? scEvent === null || scEvent === void 0 ? void 0 : scEvent.description : getTruncatedText(scEvent === null || scEvent === void 0 ? void 0 : scEvent.description, 220)), [expanded, scEvent]);
72
+ /**
73
+ * Subscriber for pubsub callback
74
+ */
75
+ const onChangeGroupHandler = useCallback((_msg, data) => {
76
+ if (data && scEvent.id === data.id) {
77
+ setSCEvent(data);
78
+ }
79
+ }, [scEvent, setSCEvent]);
80
+ /**
81
+ * On mount, subscribe to receive groups updates (only edit)
82
+ */
83
+ useEffect(() => {
84
+ if (scEvent) {
85
+ updatesSubscription.current = PubSub.subscribe(`${SCTopicType.EVENT}.${SCGroupEventType.EDIT}`, onChangeGroupHandler);
86
+ }
87
+ return () => {
88
+ updatesSubscription.current && PubSub.unsubscribe(updatesSubscription.current);
89
+ };
90
+ }, [scEvent]);
68
91
  // RENDER
69
92
  if (!scEvent && loading) {
70
93
  return _jsx(Skeleton, {});
@@ -8,7 +8,7 @@ import { SCPreferences, SCPreferencesContext, SCUserContext, UserUtils } from '@
8
8
  import { SCEventDateFilterType, SCEventSubscriptionStatusType } from '@selfcommunity/types';
9
9
  import { Logger } from '@selfcommunity/utils';
10
10
  import classNames from 'classnames';
11
- import { useContext, useEffect, useMemo, useState } from 'react';
11
+ import { useCallback, useContext, useEffect, useMemo, useRef, useState } from 'react';
12
12
  import { FormattedMessage } from 'react-intl';
13
13
  import { SCOPE_SC_UI } from '../../constants/Errors';
14
14
  import { DEFAULT_PAGINATION_OFFSET } from '../../constants/Pagination';
@@ -17,6 +17,8 @@ import Event, { EventSkeleton } from '../Event';
17
17
  import Skeleton from '../Events/Skeleton';
18
18
  import { PREFIX } from './constants';
19
19
  import PastEventsFilter from './PastEventsFilter';
20
+ import PubSub from 'pubsub-js';
21
+ import { SCGroupEventType, SCTopicType } from '../../constants/PubSub';
20
22
  const classes = {
21
23
  root: `${PREFIX}-root`,
22
24
  filters: `${PREFIX}-filters`,
@@ -98,6 +100,8 @@ export default function Events(inProps) {
98
100
  const authUserId = scUserContext.user ? scUserContext.user.id : null;
99
101
  const theme = useTheme();
100
102
  const isMobile = useMediaQuery(theme.breakpoints.down('md'));
103
+ // REFS
104
+ const updatesSubscription = useRef(null);
101
105
  // HANDLERS
102
106
  const handleChipClick = () => {
103
107
  setShowFollowed(!showFollowed);
@@ -143,6 +147,28 @@ export default function Events(inProps) {
143
147
  query === '' && fetchEvents();
144
148
  }
145
149
  }, [contentAvailability, dateSearch, showFollowed, showPastEvents, showMyEvents, query]);
150
+ /**
151
+ * Subscriber for pubsub callback
152
+ */
153
+ const onDeleteEventHandler = useCallback((_msg, deleted) => {
154
+ setEvents((prev) => {
155
+ if (prev.some((e) => e.id === deleted)) {
156
+ return prev.filter((e) => e.id !== deleted);
157
+ }
158
+ return prev;
159
+ });
160
+ }, [events]);
161
+ /**
162
+ * On mount, subscribe to receive event updates (only delete)
163
+ */
164
+ useEffect(() => {
165
+ if (events) {
166
+ updatesSubscription.current = PubSub.subscribe(`${SCTopicType.EVENT}.${SCGroupEventType.DELETE}`, onDeleteEventHandler);
167
+ }
168
+ return () => {
169
+ updatesSubscription.current && PubSub.unsubscribe(updatesSubscription.current);
170
+ };
171
+ }, [events]);
146
172
  const handleNext = useMemo(() => () => {
147
173
  if (!next) {
148
174
  return;
@@ -188,14 +214,14 @@ export default function Events(inProps) {
188
214
  variant: showMyEvents ? 'filled' : 'outlined', label: _jsx(FormattedMessage, { id: "ui.events.filterByCreatedByMe", defaultMessage: "ui.events.filterByCreatedByMe" }), onClick: () => setShowMyEvents(!showMyEvents),
189
215
  // eslint-disable-next-line @typescript-eslint/ban-ts-ignore
190
216
  // @ts-ignore
191
- showFollowed: showMyEvents, deleteIcon: showMyEvents ? _jsx(Icon, { children: "close" }) : null, onDelete: showMyEvents ? handleDeleteClick : null, disabled: loading }) })))] })) : (_jsxs(_Fragment, { children: [_jsx(Grid, Object.assign({ item: true, xs: 12, md: 4 }, { children: _jsx(TextField, { className: classes.search, size: 'small', fullWidth: true, value: query, label: _jsx(FormattedMessage, { id: "ui.events.filterByName", defaultMessage: "ui.events.filterByName" }), variant: "outlined", onChange: handleOnChangeFilterName, disabled: loading, onKeyUp: (e) => {
217
+ showFollowed: showMyEvents, deleteIcon: showMyEvents ? _jsx(Icon, { children: "close" }) : null, onDelete: showMyEvents ? handleDeleteClick : null, disabled: loading }) })))] })) : (_jsxs(_Fragment, { children: [_jsx(Grid, Object.assign({ item: true, xs: 12, md: 4 }, { children: _jsx(TextField, { className: classes.search, size: 'small', fullWidth: true, value: query, label: _jsx(FormattedMessage, { id: "ui.events.filterByName", defaultMessage: "ui.events.filterByName" }), variant: "outlined", onChange: handleOnChangeFilterName, disabled: loading || (!events.length && !query), onKeyUp: (e) => {
192
218
  e.preventDefault();
193
219
  if (e.key === 'Enter') {
194
220
  fetchEvents(true);
195
221
  }
196
222
  }, InputProps: {
197
- endAdornment: (_jsx(InputAdornment, Object.assign({ position: "end" }, { children: isMobile ? (_jsx(IconButton, Object.assign({ onClick: () => fetchEvents(true), disabled: loading }, { children: _jsx(Icon, { children: "search" }) }))) : (_jsx(Button, { size: "small", variant: "contained", color: "secondary", onClick: () => fetchEvents(true), endIcon: _jsx(Icon, { children: "search" }), disabled: loading })) })))
198
- } }) })), _jsx(Grid, Object.assign({ item: true, xs: 12, md: 2 }, { children: _jsxs(FormControl, Object.assign({ fullWidth: true }, { children: [_jsx(InputLabel, { children: _jsx(FormattedMessage, { id: "ui.events.filterByDate", defaultMessage: "ui.events.filterByDate" }) }), _jsx(Select, Object.assign({ disabled: showPastEvents || loading, size: 'small', label: _jsx(FormattedMessage, { id: "ui.events.filterByDate", defaultMessage: "ui.events.filterByDate" }), value: dateSearch, onChange: handleOnChangeTimeFrame, renderValue: (selected) => options.find((option) => option.value === selected).label }, { children: options.map((option) => (_jsxs(MenuItem, Object.assign({ value: option.value }, { children: [_jsx(Radio, { checked: dateSearch === option.value, value: option.value, name: "radio-button-select", inputProps: { 'aria-label': option.label } }), option.label] }), option.value))) }))] })) })), authUserId && (_jsx(Grid, Object.assign({ item: true }, { children: _jsx(EventsChipRoot
223
+ endAdornment: (_jsx(InputAdornment, Object.assign({ position: "end" }, { children: isMobile ? (_jsx(IconButton, Object.assign({ onClick: () => fetchEvents(true), disabled: loading || (!events.length && !query) }, { children: _jsx(Icon, { children: "search" }) }))) : (_jsx(Button, { size: "small", variant: "contained", color: "secondary", onClick: () => fetchEvents(true), endIcon: _jsx(Icon, { children: "search" }), disabled: loading || (!events.length && !query) })) })))
224
+ } }) })), _jsx(Grid, Object.assign({ item: true, xs: 12, md: 2 }, { children: _jsxs(FormControl, Object.assign({ fullWidth: true }, { children: [_jsx(InputLabel, { children: _jsx(FormattedMessage, { id: "ui.events.filterByDate", defaultMessage: "ui.events.filterByDate" }) }), _jsx(Select, Object.assign({ disabled: showPastEvents || loading || (!events.length && dateSearch === SCEventDateFilterType.ANY), size: 'small', label: _jsx(FormattedMessage, { id: "ui.events.filterByDate", defaultMessage: "ui.events.filterByDate" }), value: dateSearch, onChange: handleOnChangeTimeFrame, renderValue: (selected) => options.find((option) => option.value === selected).label }, { children: options.map((option) => (_jsxs(MenuItem, Object.assign({ value: option.value }, { children: [_jsx(Radio, { checked: dateSearch === option.value, value: option.value, name: "radio-button-select", inputProps: { 'aria-label': option.label } }), option.label] }), option.value))) }))] })) })), authUserId && (_jsx(Grid, Object.assign({ item: true }, { children: _jsx(EventsChipRoot
199
225
  // eslint-disable-next-line @typescript-eslint/ban-ts-ignore
200
226
  // @ts-ignore
201
227
  , {
@@ -207,7 +233,7 @@ export default function Events(inProps) {
207
233
  variant: showFollowed ? 'filled' : 'outlined', label: _jsx(FormattedMessage, { id: "ui.events.filterByFollowedInterest", defaultMessage: "ui.events.filterByFollowedInterest" }), onClick: handleChipClick,
208
234
  // eslint-disable-next-line @typescript-eslint/ban-ts-ignore
209
235
  // @ts-ignore
210
- showFollowed: showFollowed, deleteIcon: showFollowed ? _jsx(Icon, { children: "close" }) : null, onDelete: showFollowed ? handleDeleteClick : null, disabled: loading }) }))), _jsx(Grid, Object.assign({ item: true }, { children: _jsx(PastEventsFilter, { showPastEvents: showPastEvents, handleClick: handleChipPastClick, handleDeleteClick: handleDeletePastClick, disabled: dateSearch !== SCEventDateFilterType.ANY || loading }) }))] })) }))), _jsx(_Fragment, { children: loading ? (_jsx(Skeleton, Object.assign({}, EventsSkeletonComponentProps, { EventSkeletonProps: EventSkeletonComponentProps }))) : (_jsx(_Fragment, { children: !events.length ? (_jsx(Box, Object.assign({ className: classes.noResults }, { children: (onlyStaffEnabled && !UserUtils.isStaff(scUserContext.user)) ||
236
+ showFollowed: showFollowed, deleteIcon: showFollowed ? _jsx(Icon, { children: "close" }) : null, onDelete: showFollowed ? handleDeleteClick : null, disabled: loading || (!events.length && !showFollowed) }) }))), _jsx(Grid, Object.assign({ item: true }, { children: _jsx(PastEventsFilter, { showPastEvents: showPastEvents, handleClick: handleChipPastClick, handleDeleteClick: handleDeletePastClick, disabled: dateSearch !== SCEventDateFilterType.ANY || loading || (!events.length && !showPastEvents) }) }))] })) }))), _jsx(_Fragment, { children: loading ? (_jsx(Skeleton, Object.assign({}, EventsSkeletonComponentProps, { EventSkeletonProps: EventSkeletonComponentProps }))) : (_jsx(_Fragment, { children: !events.length ? (_jsx(Box, Object.assign({ className: classes.noResults }, { children: (onlyStaffEnabled && !UserUtils.isStaff(scUserContext.user)) ||
211
237
  (onlyStaffEnabled && UserUtils.isStaff(scUserContext.user) && general) ? (_jsxs(_Fragment, { children: [_jsx(EventSkeleton, Object.assign({}, EventSkeletonComponentProps)), _jsx(Typography, Object.assign({ variant: "body1" }, { children: _jsx(FormattedMessage, { id: "ui.events.noEvents.title", defaultMessage: "ui.events.noEvents.title" }) }))] })) : (_jsxs(_Fragment, { children: [_jsx(EventSkeleton, Object.assign({}, EventSkeletonComponentProps, { skeletonsAnimation: false, actions: _jsx(CreateEventButton, {}) })), _jsx(Typography, Object.assign({ variant: "body1" }, { children: _jsx(FormattedMessage, { id: "ui.events.noEvents.title.onlyStaff", defaultMessage: "ui.events.noEvents.title.onlyStaff" }) }))] })) }))) : (_jsxs(_Fragment, { children: [_jsx(Grid, Object.assign({ container: true, spacing: { xs: 2 }, className: classes.events }, GridContainerComponentProps, { children: _jsxs(_Fragment, { children: [events.map((event) => (_jsx(Grid, Object.assign({ item: true, xs: 12, sm: 12, md: 6, className: classes.item }, GridItemComponentProps, { children: _jsx(Event, Object.assign({ event: event, eventId: event.id }, EventComponentProps)) }), event.id))), authUserId && events.length % 2 !== 0 && (_jsx(Grid, Object.assign({ item: true, xs: 12, sm: 12, md: 6, className: classes.itemSkeleton }, GridItemComponentProps, { children: _jsx(EventSkeleton, Object.assign({}, EventSkeletonComponentProps, { skeletonsAnimation: false, actions: _jsx(CreateEventButton, Object.assign({ variant: "outlined", color: "primary", size: "small" }, { children: _jsx(FormattedMessage, { id: "ui.events.skeleton.action.add", defaultMessage: "ui.events.skeleton.action.add" }) })) })) }), 'skeleton-item'))] }) })), Boolean(next) && (_jsx(Button, Object.assign({ color: "secondary", variant: "text", onClick: handleNext, className: classes.showMore }, { children: _jsx(FormattedMessage, { id: "ui.events.button.seeMore", defaultMessage: "ui.events.button.seeMore" }) })))] })) })) })] }));
212
238
  /**
213
239
  * Renders root object (if content availability community option is false and user is anonymous, component is hidden)
@@ -4,10 +4,10 @@ import { Button, CardActions, Icon, IconButton, Typography } from '@mui/material
4
4
  import { styled } from '@mui/material/styles';
5
5
  import { Box, useThemeProps } from '@mui/system';
6
6
  import { Endpoints, EventService, http } from '@selfcommunity/api-services';
7
- import { SCCache, SCRoutes, useSCPreferences, useSCRouting, useSCUser } from '@selfcommunity/react-core';
7
+ import { SCCache, SCPreferences, SCRoutes, useSCPreferences, useSCRouting, useSCUser } from '@selfcommunity/react-core';
8
8
  import { SCEventSubscriptionStatusType, SCFeatureName } from '@selfcommunity/types';
9
9
  import { Logger } from '@selfcommunity/utils';
10
- import { useCallback, useEffect, useMemo, useReducer, useState } from 'react';
10
+ import { useCallback, useEffect, useMemo, useReducer, useRef, useState } from 'react';
11
11
  import { FormattedMessage } from 'react-intl';
12
12
  import { SCOPE_SC_UI } from '../../constants/Errors';
13
13
  import { DEFAULT_PAGINATION_LIMIT, DEFAULT_PAGINATION_OFFSET } from '../../constants/Pagination';
@@ -18,6 +18,8 @@ import Event from '../Event';
18
18
  import Widget from '../Widget';
19
19
  import { PREFIX } from './constants';
20
20
  import Skeleton from './Skeleton';
21
+ import PubSub from 'pubsub-js';
22
+ import { SCGroupEventType, SCTopicType } from '../../constants/PubSub';
21
23
  const classes = {
22
24
  root: `${PREFIX}-root`,
23
25
  titleWrapper: `${PREFIX}-title-wrapper`,
@@ -62,8 +64,14 @@ export default function MyEventsWidget(inProps) {
62
64
  // CONTEXT
63
65
  const scUserContext = useSCUser();
64
66
  const scRoutingContext = useSCRouting();
65
- const { features } = useSCPreferences();
66
- const eventsEnabled = useMemo(() => features && features.includes(SCFeatureName.EVENT) && features.includes(SCFeatureName.TAGGING), [features]);
67
+ const { preferences, features } = useSCPreferences();
68
+ const eventsEnabled = useMemo(() => preferences &&
69
+ features &&
70
+ features.includes(SCFeatureName.TAGGING) &&
71
+ SCPreferences.CONFIGURATIONS_EVENTS_ENABLED in preferences &&
72
+ preferences[SCPreferences.CONFIGURATIONS_EVENTS_ENABLED].value, [preferences, features]);
73
+ // REFS
74
+ const updatesSubscription = useRef(null);
67
75
  /**
68
76
  * Initialize component
69
77
  * Fetch data only if the component is not initialized and it is not loading data
@@ -118,6 +126,30 @@ export default function MyEventsWidget(inProps) {
118
126
  _fetchNext();
119
127
  }
120
128
  }, [eventIndex, state.results]);
129
+ /**
130
+ * Subscriber for pubsub callback
131
+ */
132
+ const onDeleteEventHandler = useCallback((_msg, deleted) => {
133
+ const _events = [...state.results];
134
+ if (_events.some((e) => e.id === deleted)) {
135
+ const updatedEvents = _events.filter((e) => e.id !== deleted);
136
+ dispatch({
137
+ type: actionWidgetTypes.SET_RESULTS,
138
+ payload: { results: updatedEvents }
139
+ });
140
+ }
141
+ }, [state.results]);
142
+ /**
143
+ * On mount, subscribe to receive event updates (only delete)
144
+ */
145
+ useEffect(() => {
146
+ if (state.results) {
147
+ updatesSubscription.current = PubSub.subscribe(`${SCTopicType.EVENT}.${SCGroupEventType.DELETE}`, onDeleteEventHandler);
148
+ }
149
+ return () => {
150
+ updatesSubscription.current && PubSub.unsubscribe(updatesSubscription.current);
151
+ };
152
+ }, [state.results]);
121
153
  // RENDER
122
154
  if (!eventsEnabled || (state.initialized && state.count === 0)) {
123
155
  return _jsx(HiddenPlaceholder, {});
@@ -108,12 +108,21 @@ export default function NavigationToolbar(inProps) {
108
108
  return _preferences;
109
109
  }, [scPreferences.preferences]);
110
110
  const privateMessagingEnabled = useMemo(() => scPreferences.features.includes(SCFeatureName.PRIVATE_MESSAGING), [scPreferences.features]);
111
- const groupsEnabled = useMemo(() => scPreferences.features && scPreferences.features.includes(SCFeatureName.GROUPING) && scPreferences.features.includes(SCFeatureName.TAGGING), [scPreferences.features]);
112
- const eventsEnabled = useMemo(() => scPreferences.features && scPreferences.features.includes(SCFeatureName.EVENT) && scPreferences.features.includes(SCFeatureName.TAGGING), [scPreferences.features]);
111
+ const groupsEnabled = useMemo(() => scPreferences.preferences &&
112
+ scPreferences.features &&
113
+ scPreferences.features.includes(SCFeatureName.TAGGING) &&
114
+ scPreferences.features.includes(SCFeatureName.GROUPING) &&
115
+ SCPreferences.CONFIGURATIONS_GROUPS_ENABLED in scPreferences.preferences &&
116
+ scPreferences.preferences[SCPreferences.CONFIGURATIONS_GROUPS_ENABLED].value, [scPreferences.preferences, scPreferences.features]);
117
+ const eventsEnabled = useMemo(() => scPreferences.preferences &&
118
+ scPreferences.features &&
119
+ scPreferences.features.includes(SCFeatureName.TAGGING) &&
120
+ SCPreferences.CONFIGURATIONS_EVENTS_ENABLED in preferences &&
121
+ scPreferences.preferences[SCPreferences.CONFIGURATIONS_EVENTS_ENABLED].value, [scPreferences.preferences, scPreferences.features]);
113
122
  const showComposer = useMemo(() => {
114
123
  return (!disableComposer &&
115
124
  (!scPreferences.preferences[SCPreferences.CONFIGURATIONS_POST_ONLY_STAFF_ENABLED].value || UserUtils.isStaff(scUserContext.user)));
116
- }, [scPreferences, disableComposer, scUserContext.user]);
125
+ }, [preferences, disableComposer, scUserContext.user]);
117
126
  // STATE
118
127
  const [anchorNotification, setAnchorNotification] = React.useState(null);
119
128
  // HANDLERS
@@ -78,8 +78,17 @@ export default function NavigationToolbarMobile(inProps) {
78
78
  // STATE
79
79
  const [searchOpen, setSearchOpen] = useState(false);
80
80
  // MEMO
81
- const groupsEnabled = useMemo(() => features && features.includes(SCFeatureName.GROUPING) && features.includes(SCFeatureName.TAGGING), [features]);
82
- const eventsEnabled = useMemo(() => features && features.includes(SCFeatureName.EVENT) && features.includes(SCFeatureName.TAGGING), [features]);
81
+ const groupsEnabled = useMemo(() => preferences &&
82
+ features &&
83
+ features.includes(SCFeatureName.TAGGING) &&
84
+ features.includes(SCFeatureName.GROUPING) &&
85
+ SCPreferences.CONFIGURATIONS_GROUPS_ENABLED in preferences &&
86
+ preferences[SCPreferences.CONFIGURATIONS_GROUPS_ENABLED].value, [preferences, features]);
87
+ const eventsEnabled = useMemo(() => preferences &&
88
+ features &&
89
+ features.includes(SCFeatureName.TAGGING) &&
90
+ SCPreferences.CONFIGURATIONS_EVENTS_ENABLED in preferences &&
91
+ preferences[SCPreferences.CONFIGURATIONS_EVENTS_ENABLED].value, [preferences, features]);
83
92
  const exploreStreamEnabled = preferences[SCPreferences.CONFIGURATIONS_EXPLORE_STREAM_ENABLED].value;
84
93
  const postOnlyStaffEnabled = preferences[SCPreferences.CONFIGURATIONS_POST_ONLY_STAFF_ENABLED].value;
85
94
  const contentAvailable = preferences[SCPreferences.CONFIGURATIONS_CONTENT_AVAILABILITY].value;
@@ -8,7 +8,7 @@ import classNames from 'classnames';
8
8
  import { useThemeProps } from '@mui/system';
9
9
  import Category from './Steps/Category';
10
10
  import { PREFIX } from './constants';
11
- import { getTheme, usePreviousValue, UserUtils, useSCContext, useSCPreferences, useSCTheme, useSCUser } from '@selfcommunity/react-core';
11
+ import { getTheme, usePreviousValue, UserUtils, useSCContext, useSCFetchCategories, useSCPreferences, useSCTheme, useSCUser } from '@selfcommunity/react-core';
12
12
  import Appearance from './Steps/Appearance';
13
13
  import Profile from './Steps/Profile';
14
14
  import Invite from './Steps/Invite';
@@ -24,6 +24,7 @@ import OnBoardingWidgetSkeleton from './Skeleton';
24
24
  import { closeSnackbar, useSnackbar } from 'notistack';
25
25
  import { CONSOLE_PROD, CONSOLE_STAGE } from '../PlatformWidget/constants';
26
26
  import HeaderPlaceholder from '../../assets/onBoarding/header';
27
+ import BaseDialog from '../../shared/BaseDialog';
27
28
  const classes = {
28
29
  root: `${PREFIX}-root`,
29
30
  content: `${PREFIX}-content`,
@@ -52,7 +53,7 @@ const OnBoardingWidget = (inProps) => {
52
53
  });
53
54
  const { className, GenerateContentsParams = {}, onGeneratedContent = null, onHeightChange } = props, rest = __rest(props, ["className", "GenerateContentsParams", "onGeneratedContent", "onHeightChange"]);
54
55
  // STATE
55
- const [isLoading, setIsLoading] = useState(true);
56
+ const [loading, setLoading] = useState(true);
56
57
  const [initialized, setInitialized] = useState(false);
57
58
  const [steps, setSteps] = useState([]);
58
59
  const nextStep = useMemo(() => {
@@ -68,6 +69,7 @@ const OnBoardingWidget = (inProps) => {
68
69
  const prevContentsStep = usePreviousValue(currentContentsStep);
69
70
  const currentCategoriesStep = steps === null || steps === void 0 ? void 0 : steps.find((s) => s.step === SCOnBoardingStepType.CATEGORIES);
70
71
  const prevCategoriesStep = usePreviousValue(currentCategoriesStep);
72
+ const [showCategoriesModal, setShowCategoriesModal] = useState(false);
71
73
  // CONTEXT
72
74
  const scUserContext = useSCUser();
73
75
  const isAdmin = useMemo(() => UserUtils.isCommunityCreator(scUserContext.user), [scUserContext.user]);
@@ -80,6 +82,7 @@ const OnBoardingWidget = (inProps) => {
80
82
  // HOOKS
81
83
  const theme = useTheme();
82
84
  const isMobile = useMediaQuery(theme.breakpoints.down('md'));
85
+ const { categories, isLoading } = useSCFetchCategories();
83
86
  // HANDLERS
84
87
  const completeStep = (s) => __awaiter(void 0, void 0, void 0, function* () {
85
88
  if (s.status !== SCOnBoardingStepStatusType.COMPLETED) {
@@ -112,14 +115,14 @@ const OnBoardingWidget = (inProps) => {
112
115
  const contentStep = res.results.find((step) => step.step === SCOnBoardingStepType.CONTENTS);
113
116
  setIsGenerating(res.results.some((step) => step.status === 'in_progress'));
114
117
  setSteps(res.results);
115
- setIsLoading(false);
118
+ setLoading(false);
116
119
  if (contentStep.status === SCOnBoardingStepStatusType.IN_PROGRESS && contentStep.results.length !== 0 && onGeneratedContent) {
117
120
  onGeneratedContent(contentStep.results);
118
121
  }
119
122
  })
120
123
  .catch((error) => {
121
124
  Logger.error(SCOPE_SC_UI, error);
122
- setIsLoading(false);
125
+ setLoading(false);
123
126
  });
124
127
  });
125
128
  const handleChange = (newStep) => {
@@ -130,17 +133,22 @@ const OnBoardingWidget = (inProps) => {
130
133
  onHeightChange && onHeightChange();
131
134
  };
132
135
  const generateContent = (stepId) => __awaiter(void 0, void 0, void 0, function* () {
133
- yield OnBoardingService.startAStep(stepId, GenerateContentsParams)
134
- .then(() => {
135
- setIsGenerating(true);
136
- })
137
- .catch((error) => {
138
- Logger.error(SCOPE_SC_UI, error);
139
- enqueueSnackbar(_jsx(FormattedMessage, { id: "ui.common.error.action", defaultMessage: "ui.common.error.action" }), {
140
- variant: 'error',
141
- autoHideDuration: 3000
136
+ if (!isLoading && !categories.length) {
137
+ setShowCategoriesModal(true);
138
+ }
139
+ else {
140
+ yield OnBoardingService.startAStep(stepId, GenerateContentsParams)
141
+ .then(() => {
142
+ setIsGenerating(true);
143
+ })
144
+ .catch((error) => {
145
+ Logger.error(SCOPE_SC_UI, error);
146
+ enqueueSnackbar(_jsx(FormattedMessage, { id: "ui.common.error.action", defaultMessage: "ui.common.error.action" }), {
147
+ variant: 'error',
148
+ autoHideDuration: 3000
149
+ });
142
150
  });
143
- });
151
+ }
144
152
  });
145
153
  const handlePreferencesUpdate = () => {
146
154
  PreferenceService.getAllPreferences().then((preferences) => {
@@ -152,7 +160,7 @@ const OnBoardingWidget = (inProps) => {
152
160
  // EFFECTS
153
161
  useEffect(() => {
154
162
  if (prevContentsStep &&
155
- prevCategoriesStep.status === SCOnBoardingStepStatusType.IN_PROGRESS &&
163
+ prevContentsStep.status === SCOnBoardingStepStatusType.IN_PROGRESS &&
156
164
  (currentContentsStep === null || currentContentsStep === void 0 ? void 0 : currentContentsStep.status) === SCOnBoardingStepStatusType.COMPLETED) {
157
165
  showSuccessAlert(currentContentsStep);
158
166
  }
@@ -233,6 +241,6 @@ const OnBoardingWidget = (inProps) => {
233
241
  // eslint-disable-next-line @typescript-eslint/ban-ts-ignore
234
242
  // @ts-ignore
235
243
  icon: (...chunks) => (_jsx(Icon, Object.assign({ color: "secondary", fontSize: "medium" }, { children: chunks })))
236
- } }) }))) }) })), _jsx(AccordionDetails, { children: _jsx(Widget, Object.assign({ className: classes.content, elevation: 0 }, { children: isLoading ? (_jsx(OnBoardingWidgetSkeleton, {})) : (_jsxs(CardContent, { children: [_jsx(List, Object.assign({ className: isMobile ? classes.stepsMobile : classes.steps }, { children: steps === null || steps === void 0 ? void 0 : steps.map((step) => (_jsx(ListItem, { children: isMobile ? (_jsx(Chip, { size: "small", label: _jsxs(_Fragment, { children: [_jsx(FormattedMessage, { id: `ui.onBoardingWidget.${step.step}`, defaultMessage: `ui.onBoardingWidget.${step.step}` }), ' ', step.status === SCOnBoardingStepStatusType.COMPLETED && (_jsx(Icon, Object.assign({ color: (step === null || step === void 0 ? void 0 : step.status) === SCOnBoardingStepStatusType.COMPLETED && (step === null || step === void 0 ? void 0 : step.step) !== (_step === null || _step === void 0 ? void 0 : _step.step) ? 'success' : 'inherit' }, { children: "check" })))] }), onClick: () => handleChange(step), variant: step.step === (_step === null || _step === void 0 ? void 0 : _step.step) ? 'filled' : 'outlined', color: step.status === SCOnBoardingStepStatusType.COMPLETED ? 'success' : 'default' })) : (_jsxs(ListItemButton, Object.assign({ onClick: () => handleChange(step), selected: (step === null || step === void 0 ? void 0 : step.step) === (_step === null || _step === void 0 ? void 0 : _step.step) }, { children: [_jsx(ListItemIcon, { children: _jsx(Checkbox, { edge: "start", checked: step.status === SCOnBoardingStepStatusType.COMPLETED, tabIndex: -1, disableRipple: true, inputProps: { 'aria-labelledby': step.step }, size: 'small' }) }), _jsx(ListItemText, { primary: _jsx(FormattedMessage, { id: `ui.onBoardingWidget.${step.step}`, defaultMessage: `ui.onBoardingWidget.${step.step}` }) })] }))) }, step.id))) })), _jsx(Box, Object.assign({ className: classes.stepContent }, { children: _jsx(Fade, Object.assign({ in: true, timeout: 2400 }, { children: _jsx(Box, { children: getStepContent() }) })) }))] })) })) })] })) })));
244
+ } }) }))) }) })), _jsx(AccordionDetails, { children: _jsx(Widget, Object.assign({ className: classes.content, elevation: 0 }, { children: loading ? (_jsx(OnBoardingWidgetSkeleton, {})) : (_jsxs(CardContent, { children: [_jsx(List, Object.assign({ className: isMobile ? classes.stepsMobile : classes.steps }, { children: steps === null || steps === void 0 ? void 0 : steps.map((step) => (_jsx(ListItem, { children: isMobile ? (_jsx(Chip, { size: "small", label: _jsxs(_Fragment, { children: [_jsx(FormattedMessage, { id: `ui.onBoardingWidget.${step.step}`, defaultMessage: `ui.onBoardingWidget.${step.step}` }), ' ', step.status === SCOnBoardingStepStatusType.COMPLETED && (_jsx(Icon, Object.assign({ color: (step === null || step === void 0 ? void 0 : step.status) === SCOnBoardingStepStatusType.COMPLETED && (step === null || step === void 0 ? void 0 : step.step) !== (_step === null || _step === void 0 ? void 0 : _step.step) ? 'success' : 'inherit' }, { children: "check" })))] }), onClick: () => handleChange(step), variant: step.step === (_step === null || _step === void 0 ? void 0 : _step.step) ? 'filled' : 'outlined', color: step.status === SCOnBoardingStepStatusType.COMPLETED ? 'success' : 'default' })) : (_jsxs(ListItemButton, Object.assign({ onClick: () => handleChange(step), selected: (step === null || step === void 0 ? void 0 : step.step) === (_step === null || _step === void 0 ? void 0 : _step.step) }, { children: [_jsx(ListItemIcon, { children: _jsx(Checkbox, { edge: "start", checked: step.status === SCOnBoardingStepStatusType.COMPLETED, tabIndex: -1, disableRipple: true, inputProps: { 'aria-labelledby': step.step }, size: 'small' }) }), _jsx(ListItemText, { primary: _jsx(FormattedMessage, { id: `ui.onBoardingWidget.${step.step}`, defaultMessage: `ui.onBoardingWidget.${step.step}` }) })] }))) }, step.id))) })), _jsxs(Box, Object.assign({ className: classes.stepContent }, { children: [_jsx(Fade, Object.assign({ in: true, timeout: 2400 }, { children: _jsx(Box, { children: getStepContent() }) })), showCategoriesModal && (_jsx(BaseDialog, Object.assign({ title: _jsx(FormattedMessage, { id: "ui.onBoardingWidget.ai.no.categories", defaultMessage: "ui.onBoardingWidget.ai.no.categories" }), DialogContentProps: { dividers: false }, open: showCategoriesModal, onClose: () => setShowCategoriesModal(false), actions: _jsx(Button, Object.assign({ color: "secondary", onClick: () => setShowCategoriesModal(false) }, { children: _jsx(FormattedMessage, { id: "ui.onBoardingWidget.ai.no.categories.cancel", defaultMessage: "ui.onBoardingWidget.ai.no.categories.cancel" }) })) }, { children: _jsx(Button, Object.assign({ color: "primary", href: isStage ? CONSOLE_STAGE : CONSOLE_PROD, onClick: () => setShowCategoriesModal(false), target: "_blank", startIcon: _jsx(Icon, Object.assign({ fontSize: "small" }, { children: "edit" })) }, { children: _jsx(FormattedMessage, { id: "ui.onBoardingWidget.ai.no.categories.link", defaultMessage: "ui.onBoardingWidget.ai.no.categories.link" }) })) })))] }))] })) })) })] })) })));
237
245
  };
238
246
  export default OnBoardingWidget;
@@ -6,7 +6,7 @@ import { Endpoints, http, SuggestionService } from '@selfcommunity/api-services'
6
6
  import { Link, SCRoutes, useSCRouting } from '@selfcommunity/react-core';
7
7
  import { Logger } from '@selfcommunity/utils';
8
8
  import classNames from 'classnames';
9
- import { useCallback, useEffect, useState } from 'react';
9
+ import { useCallback, useEffect, useRef, useState } from 'react';
10
10
  import { FormattedMessage } from 'react-intl';
11
11
  import { Swiper, SwiperSlide } from 'swiper/react';
12
12
  import { SCOPE_SC_UI } from '../../constants/Errors';
@@ -18,6 +18,8 @@ import Widget from '../Widget';
18
18
  import Arrow from './Arrow';
19
19
  import { PREFIX } from './constants';
20
20
  import Skeleton from './Skeleton';
21
+ import PubSub from 'pubsub-js';
22
+ import { SCGroupEventType, SCTopicType } from '../../constants/PubSub';
21
23
  const classes = {
22
24
  root: `${PREFIX}-root`,
23
25
  content: `${PREFIX}-content`,
@@ -56,6 +58,8 @@ export default function SuggestedEventsWidget(inProps) {
56
58
  const scRoutingContext = useSCRouting();
57
59
  //HOOKS
58
60
  const theme = useTheme();
61
+ // REFS
62
+ const updatesSubscription = useRef(null);
59
63
  useEffect(() => {
60
64
  SuggestionService.getEventSuggestion(Object.assign({}, endpointQueryParams))
61
65
  .then((payload) => {
@@ -104,6 +108,28 @@ export default function SuggestedEventsWidget(inProps) {
104
108
  }
105
109
  }
106
110
  }, [count, hideMarginLeft, hideMarginRight]);
111
+ /**
112
+ * Subscriber for pubsub callback
113
+ */
114
+ const onDeleteEventHandler = useCallback((_msg, deleted) => {
115
+ setEvents((prev) => {
116
+ if (prev.some((e) => e.id === deleted)) {
117
+ return prev.filter((e) => e.id !== deleted);
118
+ }
119
+ return prev;
120
+ });
121
+ }, [events]);
122
+ /**
123
+ * On mount, subscribe to receive event updates (only delete)
124
+ */
125
+ useEffect(() => {
126
+ if (events) {
127
+ updatesSubscription.current = PubSub.subscribe(`${SCTopicType.EVENT}.${SCGroupEventType.DELETE}`, onDeleteEventHandler);
128
+ }
129
+ return () => {
130
+ updatesSubscription.current && PubSub.unsubscribe(updatesSubscription.current);
131
+ };
132
+ }, [events]);
107
133
  // RENDER
108
134
  if (!events && loading) {
109
135
  return _jsx(Skeleton, {});
@@ -6,7 +6,7 @@ import List from '@mui/material/List';
6
6
  import { Button, CardContent, ListItem, Typography } from '@mui/material';
7
7
  import { GroupService } from '@selfcommunity/api-services';
8
8
  import { CacheStrategies, isInteger, Logger } from '@selfcommunity/utils';
9
- import { SCCache, useSCPreferences, useSCUser } from '@selfcommunity/react-core';
9
+ import { SCCache, SCPreferences, useSCPreferences, useSCUser } from '@selfcommunity/react-core';
10
10
  import { actionWidgetTypes, dataWidgetReducer, stateWidgetInitializer } from '../../utils/widget';
11
11
  import { SCFeatureName, SCGroupPrivacyType, SCGroupSubscriptionStatusType } from '@selfcommunity/types';
12
12
  import { SCOPE_SC_UI } from '../../constants/Errors';
@@ -71,8 +71,13 @@ export default function UserSubscribedGroupsWidget(inProps) {
71
71
  // CONTEXT
72
72
  const scUserContext = useSCUser();
73
73
  const isMe = useMemo(() => scUserContext.user && userId === scUserContext.user.id, [scUserContext.user, userId]);
74
- const { features } = useSCPreferences();
75
- const groupsEnabled = useMemo(() => features.includes(SCFeatureName.GROUPING) && features.includes(SCFeatureName.TAGGING), [features]);
74
+ const { preferences, features } = useSCPreferences();
75
+ const groupsEnabled = useMemo(() => preferences &&
76
+ features &&
77
+ features.includes(SCFeatureName.TAGGING) &&
78
+ features.includes(SCFeatureName.GROUPING) &&
79
+ SCPreferences.CONFIGURATIONS_GROUPS_ENABLED in preferences &&
80
+ preferences[SCPreferences.CONFIGURATIONS_GROUPS_ENABLED].value, [preferences, features]);
76
81
  // STATE
77
82
  const [state, dispatch] = useReducer(dataWidgetReducer, {
78
83
  isLoadingNext: false,
@@ -12,6 +12,7 @@ export declare enum SCTopicType {
12
12
  export declare enum SCGroupEventType {
13
13
  CREATE = "create",
14
14
  EDIT = "edit",
15
+ DELETE = "delete",
15
16
  MEMBERS = "members",
16
17
  ADD_MEMBER = "members.add_member",
17
18
  INVITE_MEMBER = "members.invite_member",
@@ -13,6 +13,7 @@ export var SCGroupEventType;
13
13
  (function (SCGroupEventType) {
14
14
  SCGroupEventType["CREATE"] = "create";
15
15
  SCGroupEventType["EDIT"] = "edit";
16
+ SCGroupEventType["DELETE"] = "delete";
16
17
  SCGroupEventType["MEMBERS"] = "members";
17
18
  SCGroupEventType["ADD_MEMBER"] = "members.add_member";
18
19
  SCGroupEventType["INVITE_MEMBER"] = "members.invite_member";
@@ -18,6 +18,10 @@ export interface EventActionsMenuProps extends IconButtonProps {
18
18
  * Handles callback on delete confirm
19
19
  */
20
20
  onDeleteConfirm?: () => void;
21
+ /**
22
+ * Handles on edit success
23
+ */
24
+ onEditSuccess?: (data: SCEventType) => any;
21
25
  /**
22
26
  * Any other properties
23
27
  */
@@ -13,6 +13,9 @@ import { EventService } from '@selfcommunity/api-services';
13
13
  import { ADD_EVENT_TO_CALENDAR, CANCEL_EVENT, GET_EVENT_LINK } from '../../constants/EventActionsMenu';
14
14
  import { copyTextToClipboard } from '@selfcommunity/utils';
15
15
  import { enqueueSnackbar } from 'notistack';
16
+ import PubSub from 'pubsub-js';
17
+ import { SCGroupEventType, SCTopicType } from '../../constants/PubSub';
18
+ import EventForm from '../../components/EventForm';
16
19
  const PREFIX = 'SCEventActionsMenu';
17
20
  const classes = {
18
21
  root: `${PREFIX}-root`,
@@ -67,16 +70,17 @@ export default function EventActionsMenu(inProps) {
67
70
  props: inProps,
68
71
  name: PREFIX
69
72
  });
70
- const { className, event, eventId, onDeleteConfirm } = props, rest = __rest(props, ["className", "event", "eventId", "onDeleteConfirm"]);
73
+ const { className, event, eventId, onDeleteConfirm, onEditSuccess } = props, rest = __rest(props, ["className", "event", "eventId", "onDeleteConfirm", "onEditSuccess"]);
71
74
  // STATE
72
75
  const [anchorEl, setAnchorEl] = useState(null);
73
76
  const [openConfirmDialog, setOpenConfirmDialog] = useState(false);
77
+ const [openEdit, setOpenEdit] = useState(false);
74
78
  // HOOKS
75
79
  const theme = useTheme();
76
80
  const isMobile = useMediaQuery(theme.breakpoints.down('md'));
77
81
  const scRoutingContext = useSCRouting();
78
82
  const scUserContext = useSCUser();
79
- const { scEvent } = useSCFetchEvent({ id: eventId, event });
83
+ const { scEvent, setSCEvent } = useSCFetchEvent({ id: eventId, event });
80
84
  const isEventAdmin = useMemo(() => { var _a; return scUserContext.user && ((_a = scEvent === null || scEvent === void 0 ? void 0 : scEvent.managed_by) === null || _a === void 0 ? void 0 : _a.id) === scUserContext.user.id; }, [scUserContext.user, (_a = scEvent === null || scEvent === void 0 ? void 0 : scEvent.managed_by) === null || _a === void 0 ? void 0 : _a.id]);
81
85
  const isEventFinished = useMemo(() => {
82
86
  if (scEvent && !scEvent.running) {
@@ -91,10 +95,17 @@ export default function EventActionsMenu(inProps) {
91
95
  const handleClose = () => {
92
96
  setAnchorEl(null);
93
97
  };
98
+ const handleEditClick = () => {
99
+ setOpenEdit((o) => !o);
100
+ };
94
101
  const handleCloseDialog = () => {
95
102
  setOpenConfirmDialog(false);
96
103
  setAnchorEl(null);
97
104
  };
105
+ const handleEditSuccess = (data) => {
106
+ setSCEvent(data);
107
+ onEditSuccess && onEditSuccess(data);
108
+ };
98
109
  /**
99
110
  * Handles thread deletion
100
111
  */
@@ -103,6 +114,7 @@ export default function EventActionsMenu(inProps) {
103
114
  .then(() => {
104
115
  onDeleteConfirm();
105
116
  handleCloseDialog();
117
+ PubSub.publish(`${SCTopicType.EVENT}.${SCGroupEventType.DELETE}`, scEvent.id);
106
118
  })
107
119
  .catch((error) => {
108
120
  setOpenConfirmDialog(false);
@@ -145,6 +157,7 @@ export default function EventActionsMenu(inProps) {
145
157
  isEventAdmin &&
146
158
  !isEventFinished && [
147
159
  _jsx(Divider, {}, "divider"),
160
+ isMobile && (_jsxs(MenuItem, Object.assign({ className: classes.item, onClick: handleEditClick }, { children: [_jsx(ListItemIcon, { children: _jsx(Icon, { children: "edit" }) }), _jsx(FormattedMessage, { id: "ui.shared.eventActionsMenu.item.edit", defaultMessage: "ui.shared.eventActionsMenu.item.edit" })] }), "edit")),
148
161
  _jsxs(MenuItem, Object.assign({ className: classes.item, onClick: () => handleAction(CANCEL_EVENT) }, { children: [_jsx(ListItemIcon, { children: _jsx(Icon, { children: "close" }) }), _jsx(FormattedMessage, { id: "ui.shared.eventActionsMenu.item.cancel", defaultMessage: "ui.shared.eventActionsMenu.item.cancel" })] }), "cancel")
149
162
  ]
150
163
  ];
@@ -152,5 +165,5 @@ export default function EventActionsMenu(inProps) {
152
165
  if (!scEvent) {
153
166
  return null;
154
167
  }
155
- return (_jsxs(_Fragment, { children: [_jsx(Root, Object.assign({ className: classNames(classes.root, className) }, rest, { onClick: handleOpen }, { children: _jsx(Icon, { children: "more_vert" }) })), isMobile ? (_jsx(SwipeableDrawerRoot, Object.assign({ className: classes.drawerRoot, anchor: "bottom", open: Boolean(anchorEl), onClose: handleClose, onOpen: handleOpen, PaperProps: { className: classes.paper }, disableSwipeToOpen: true }, { children: _jsx(List, { children: renderList() }) }))) : (_jsx(MenuRoot, Object.assign({ className: classes.menuRoot, anchorEl: anchorEl, open: Boolean(anchorEl), onClose: handleClose, PaperProps: { className: classes.paper } }, { children: renderList() }))), openConfirmDialog && (_jsx(ConfirmDialog, { open: openConfirmDialog, title: _jsx(FormattedMessage, { id: "ui.shared.eventActionsMenu.dialog.msg", defaultMessage: "ui.shared.eventActionsMenu.dialog.msg" }), btnConfirm: _jsx(FormattedMessage, { id: "ui.shared.eventActionsMenu.dialog.confirm", defaultMessage: "ui.shared.eventActionsMenu.dialog.confirm" }), onConfirm: handleDeleteThread, onClose: handleCloseDialog }))] }));
168
+ return (_jsxs(_Fragment, { children: [_jsx(Root, Object.assign({ className: classNames(classes.root, className) }, rest, { onClick: handleOpen }, { children: _jsx(Icon, { children: "more_vert" }) })), isMobile ? (_jsx(SwipeableDrawerRoot, Object.assign({ className: classes.drawerRoot, anchor: "bottom", open: Boolean(anchorEl), onClose: handleClose, onOpen: handleOpen, PaperProps: { className: classes.paper }, disableSwipeToOpen: true }, { children: _jsx(List, { children: renderList() }) }))) : (_jsx(MenuRoot, Object.assign({ className: classes.menuRoot, anchorEl: anchorEl, open: Boolean(anchorEl), onClose: handleClose, PaperProps: { className: classes.paper } }, { children: renderList() }))), openConfirmDialog && (_jsx(ConfirmDialog, { open: openConfirmDialog, title: _jsx(FormattedMessage, { id: "ui.shared.eventActionsMenu.dialog.msg", defaultMessage: "ui.shared.eventActionsMenu.dialog.msg" }), btnConfirm: _jsx(FormattedMessage, { id: "ui.shared.eventActionsMenu.dialog.confirm", defaultMessage: "ui.shared.eventActionsMenu.dialog.confirm" }), onConfirm: handleDeleteThread, onClose: handleCloseDialog })), openEdit && _jsx(EventForm, { onClose: handleEditClick, event: scEvent, onSuccess: handleEditSuccess })] }));
156
169
  }