@selfcommunity/react-ui 0.7.9-alpha.9 → 0.7.10-alpha.0

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 (181) hide show
  1. package/lib/cjs/components/AccountRecover/AccountRecover.js +6 -1
  2. package/lib/cjs/components/BottomNavigation/BottomNavigation.js +4 -3
  3. package/lib/cjs/components/CategoryHeader/Skeleton.js +3 -2
  4. package/lib/cjs/components/ChangeGroupCover/ChangeGroupCover.js +6 -6
  5. package/lib/cjs/components/ChangeGroupPicture/ChangeGroupPicture.js +19 -16
  6. package/lib/cjs/components/Composer/Attributes/Attributes.js +3 -3
  7. package/lib/cjs/components/Composer/Composer.js +3 -3
  8. package/lib/cjs/components/Composer/Layer/AudienceLayer/AudienceLayer.d.ts +1 -1
  9. package/lib/cjs/components/Composer/Layer/AudienceLayer/AudienceLayer.js +9 -19
  10. package/lib/cjs/components/Editor/Editor.js +2 -0
  11. package/lib/cjs/components/Editor/nodes/ImageNode.js +6 -0
  12. package/lib/cjs/components/Editor/plugins/ImagePlugin.js +4 -0
  13. package/lib/cjs/components/Editor/plugins/ToolbarPlugin.js +17 -3
  14. package/lib/cjs/components/FeedObject/Actions/Share/Share.js +18 -16
  15. package/lib/cjs/components/FeedObject/Contributors/Contributors.js +1 -1
  16. package/lib/cjs/components/FeedObject/FeedObject.d.ts +1 -0
  17. package/lib/cjs/components/FeedObject/FeedObject.js +27 -8
  18. package/lib/cjs/components/FeedObject/Poll/Poll.js +20 -20
  19. package/lib/cjs/components/FeedUpdatesWidget/FeedUpdatesWidget.js +1 -1
  20. package/lib/cjs/components/Footer/Footer.js +1 -1
  21. package/lib/cjs/components/Group/Group.d.ts +9 -1
  22. package/lib/cjs/components/Group/Group.js +18 -6
  23. package/lib/cjs/components/GroupAutocomplete/GroupAutocomplete.d.ts +0 -1
  24. package/lib/cjs/components/GroupAutocomplete/GroupAutocomplete.js +1 -2
  25. package/lib/cjs/components/GroupForm/GroupForm.js +64 -13
  26. package/lib/cjs/components/GroupHeader/GroupHeader.d.ts +2 -3
  27. package/lib/cjs/components/GroupHeader/GroupHeader.js +38 -5
  28. package/lib/cjs/components/GroupInfoWidget/GroupInfoWidget.js +65 -9
  29. package/lib/cjs/components/GroupInviteButton/GroupInviteButton.js +29 -7
  30. package/lib/cjs/components/GroupInvitedWidget/GroupInvitedWidget.d.ts +73 -0
  31. package/lib/cjs/components/GroupInvitedWidget/GroupInvitedWidget.js +220 -0
  32. package/lib/cjs/components/GroupInvitedWidget/Skeleton.d.ts +22 -0
  33. package/lib/cjs/components/GroupInvitedWidget/Skeleton.js +38 -0
  34. package/lib/cjs/components/GroupInvitedWidget/constants.d.ts +1 -0
  35. package/lib/cjs/components/GroupInvitedWidget/constants.js +4 -0
  36. package/lib/cjs/components/GroupInvitedWidget/index.d.ts +4 -0
  37. package/lib/cjs/components/GroupInvitedWidget/index.js +8 -0
  38. package/lib/cjs/components/GroupMembersButton/GroupMembersButton.js +6 -2
  39. package/lib/cjs/components/GroupMembersWidget/GroupMembersWidget.js +21 -6
  40. package/lib/cjs/components/GroupRequestsWidget/GroupRequestsWidget.d.ts +12 -2
  41. package/lib/cjs/components/GroupRequestsWidget/GroupRequestsWidget.js +13 -12
  42. package/lib/cjs/components/GroupSettingsIconButton/GroupSettingsIconButton.d.ts +4 -12
  43. package/lib/cjs/components/GroupSettingsIconButton/GroupSettingsIconButton.js +33 -19
  44. package/lib/cjs/components/GroupSubscribeButton/GroupSubscribeButton.d.ts +3 -3
  45. package/lib/cjs/components/GroupSubscribeButton/GroupSubscribeButton.js +22 -6
  46. package/lib/cjs/components/Groups/Groups.d.ts +15 -8
  47. package/lib/cjs/components/Groups/Groups.js +86 -32
  48. package/lib/cjs/components/Groups/Skeleton.d.ts +4 -0
  49. package/lib/cjs/components/Groups/Skeleton.js +2 -2
  50. package/lib/cjs/components/InlineComposerWidget/InlineComposerWidget.js +7 -0
  51. package/lib/cjs/components/NavigationSettingsIconButton/NavigationSettingsIconButton.js +4 -4
  52. package/lib/cjs/components/NavigationToolbar/NavigationToolbar.js +10 -2
  53. package/lib/cjs/components/NavigationToolbarMobile/NavigationToolbarMobile.d.ts +1 -0
  54. package/lib/cjs/components/NavigationToolbarMobile/NavigationToolbarMobile.js +9 -1
  55. package/lib/cjs/components/Notification/Group/Group.d.ts +15 -0
  56. package/lib/cjs/components/Notification/Group/Group.js +87 -0
  57. package/lib/cjs/components/Notification/Group/index.d.ts +3 -0
  58. package/lib/cjs/components/Notification/Group/index.js +5 -0
  59. package/lib/cjs/components/Notification/Notification.js +34 -1
  60. package/lib/cjs/components/Notification/PrivateMessage/PrivateMessage.js +16 -5
  61. package/lib/cjs/components/PrivateMessageComponent/PrivateMessageComponent.d.ts +7 -1
  62. package/lib/cjs/components/PrivateMessageComponent/PrivateMessageComponent.js +16 -8
  63. package/lib/cjs/components/PrivateMessageSettingsIconButton/PrivateMessageSettingsIconButton.js +1 -1
  64. package/lib/cjs/components/PrivateMessageSnippetItem/PrivateMessageSnippetItem.js +11 -6
  65. package/lib/cjs/components/PrivateMessageSnippets/PrivateMessageSnippets.d.ts +9 -4
  66. package/lib/cjs/components/PrivateMessageSnippets/PrivateMessageSnippets.js +24 -6
  67. package/lib/cjs/components/PrivateMessageThread/PrivateMessageThread.d.ts +6 -1
  68. package/lib/cjs/components/PrivateMessageThread/PrivateMessageThread.js +46 -20
  69. package/lib/cjs/components/PrivateMessageThreadItem/PrivateMessageThreadItem.js +6 -0
  70. package/lib/cjs/components/SearchAutocomplete/SearchAutocomplete.js +22 -5
  71. package/lib/cjs/components/SnippetNotifications/SnippetNotifications.js +7 -0
  72. package/lib/cjs/components/ToastNotifications/ToastNotifications.js +7 -0
  73. package/lib/cjs/components/User/User.d.ts +6 -1
  74. package/lib/cjs/components/User/User.js +5 -4
  75. package/lib/cjs/components/UserSubscribedGroupsWidget/Skeleton.d.ts +21 -0
  76. package/lib/cjs/components/UserSubscribedGroupsWidget/Skeleton.js +46 -0
  77. package/lib/cjs/components/UserSubscribedGroupsWidget/UserSubscribedGroupsWidget.d.ts +68 -0
  78. package/lib/cjs/components/UserSubscribedGroupsWidget/UserSubscribedGroupsWidget.js +183 -0
  79. package/lib/cjs/components/UserSubscribedGroupsWidget/constants.d.ts +1 -0
  80. package/lib/cjs/components/UserSubscribedGroupsWidget/constants.js +4 -0
  81. package/lib/cjs/components/UserSubscribedGroupsWidget/index.d.ts +4 -0
  82. package/lib/cjs/components/UserSubscribedGroupsWidget/index.js +8 -0
  83. package/lib/cjs/components/VoteAudienceButton/VoteAudienceButton.js +1 -1
  84. package/lib/cjs/constants/PubSub.d.ts +28 -0
  85. package/lib/cjs/constants/PubSub.js +22 -0
  86. package/lib/cjs/index.d.ts +5 -3
  87. package/lib/cjs/index.js +11 -4
  88. package/lib/cjs/types/index.d.ts +2 -1
  89. package/lib/esm/components/AccountRecover/AccountRecover.js +6 -1
  90. package/lib/esm/components/BottomNavigation/BottomNavigation.js +5 -4
  91. package/lib/esm/components/CategoryHeader/Skeleton.js +3 -2
  92. package/lib/esm/components/ChangeGroupCover/ChangeGroupCover.js +6 -6
  93. package/lib/esm/components/ChangeGroupPicture/ChangeGroupPicture.js +19 -16
  94. package/lib/esm/components/Composer/Attributes/Attributes.js +3 -3
  95. package/lib/esm/components/Composer/Composer.js +3 -3
  96. package/lib/esm/components/Composer/Layer/AudienceLayer/AudienceLayer.d.ts +1 -1
  97. package/lib/esm/components/Composer/Layer/AudienceLayer/AudienceLayer.js +9 -19
  98. package/lib/esm/components/Editor/Editor.js +2 -0
  99. package/lib/esm/components/Editor/nodes/ImageNode.js +6 -0
  100. package/lib/esm/components/Editor/plugins/ImagePlugin.js +4 -0
  101. package/lib/esm/components/Editor/plugins/ToolbarPlugin.js +19 -5
  102. package/lib/esm/components/FeedObject/Actions/Share/Share.js +19 -17
  103. package/lib/esm/components/FeedObject/Contributors/Contributors.js +1 -1
  104. package/lib/esm/components/FeedObject/FeedObject.d.ts +1 -0
  105. package/lib/esm/components/FeedObject/FeedObject.js +28 -9
  106. package/lib/esm/components/FeedObject/Poll/Poll.js +20 -20
  107. package/lib/esm/components/FeedUpdatesWidget/FeedUpdatesWidget.js +1 -1
  108. package/lib/esm/components/Footer/Footer.js +2 -2
  109. package/lib/esm/components/Group/Group.d.ts +9 -1
  110. package/lib/esm/components/Group/Group.js +22 -10
  111. package/lib/esm/components/GroupAutocomplete/GroupAutocomplete.d.ts +0 -1
  112. package/lib/esm/components/GroupAutocomplete/GroupAutocomplete.js +1 -2
  113. package/lib/esm/components/GroupForm/GroupForm.js +64 -13
  114. package/lib/esm/components/GroupHeader/GroupHeader.d.ts +2 -3
  115. package/lib/esm/components/GroupHeader/GroupHeader.js +40 -7
  116. package/lib/esm/components/GroupInfoWidget/GroupInfoWidget.js +65 -9
  117. package/lib/esm/components/GroupInviteButton/GroupInviteButton.js +29 -7
  118. package/lib/esm/components/GroupInvitedWidget/GroupInvitedWidget.d.ts +73 -0
  119. package/lib/esm/components/GroupInvitedWidget/GroupInvitedWidget.js +217 -0
  120. package/lib/esm/components/GroupInvitedWidget/Skeleton.d.ts +22 -0
  121. package/lib/esm/components/GroupInvitedWidget/Skeleton.js +34 -0
  122. package/lib/esm/components/GroupInvitedWidget/constants.d.ts +1 -0
  123. package/lib/esm/components/GroupInvitedWidget/constants.js +1 -0
  124. package/lib/esm/components/GroupInvitedWidget/index.d.ts +4 -0
  125. package/lib/esm/components/GroupInvitedWidget/index.js +4 -0
  126. package/lib/esm/components/GroupMembersButton/GroupMembersButton.js +7 -3
  127. package/lib/esm/components/GroupMembersWidget/GroupMembersWidget.js +22 -7
  128. package/lib/esm/components/GroupRequestsWidget/GroupRequestsWidget.d.ts +12 -2
  129. package/lib/esm/components/GroupRequestsWidget/GroupRequestsWidget.js +13 -12
  130. package/lib/esm/components/GroupSettingsIconButton/GroupSettingsIconButton.d.ts +4 -12
  131. package/lib/esm/components/GroupSettingsIconButton/GroupSettingsIconButton.js +33 -19
  132. package/lib/esm/components/GroupSubscribeButton/GroupSubscribeButton.d.ts +3 -3
  133. package/lib/esm/components/GroupSubscribeButton/GroupSubscribeButton.js +22 -6
  134. package/lib/esm/components/Groups/Groups.d.ts +15 -8
  135. package/lib/esm/components/Groups/Groups.js +91 -37
  136. package/lib/esm/components/Groups/Skeleton.d.ts +4 -0
  137. package/lib/esm/components/Groups/Skeleton.js +2 -2
  138. package/lib/esm/components/InlineComposerWidget/InlineComposerWidget.js +9 -2
  139. package/lib/esm/components/NavigationSettingsIconButton/NavigationSettingsIconButton.js +4 -4
  140. package/lib/esm/components/NavigationToolbar/NavigationToolbar.js +11 -3
  141. package/lib/esm/components/NavigationToolbarMobile/NavigationToolbarMobile.d.ts +1 -0
  142. package/lib/esm/components/NavigationToolbarMobile/NavigationToolbarMobile.js +11 -3
  143. package/lib/esm/components/Notification/Group/Group.d.ts +15 -0
  144. package/lib/esm/components/Notification/Group/Group.js +84 -0
  145. package/lib/esm/components/Notification/Group/index.d.ts +3 -0
  146. package/lib/esm/components/Notification/Group/index.js +2 -0
  147. package/lib/esm/components/Notification/Notification.js +34 -1
  148. package/lib/esm/components/Notification/PrivateMessage/PrivateMessage.js +16 -5
  149. package/lib/esm/components/PrivateMessageComponent/PrivateMessageComponent.d.ts +7 -1
  150. package/lib/esm/components/PrivateMessageComponent/PrivateMessageComponent.js +17 -9
  151. package/lib/esm/components/PrivateMessageSettingsIconButton/PrivateMessageSettingsIconButton.js +1 -1
  152. package/lib/esm/components/PrivateMessageSnippetItem/PrivateMessageSnippetItem.js +11 -6
  153. package/lib/esm/components/PrivateMessageSnippets/PrivateMessageSnippets.d.ts +9 -4
  154. package/lib/esm/components/PrivateMessageSnippets/PrivateMessageSnippets.js +26 -8
  155. package/lib/esm/components/PrivateMessageThread/PrivateMessageThread.d.ts +6 -1
  156. package/lib/esm/components/PrivateMessageThread/PrivateMessageThread.js +48 -22
  157. package/lib/esm/components/PrivateMessageThreadItem/PrivateMessageThreadItem.js +7 -1
  158. package/lib/esm/components/SearchAutocomplete/SearchAutocomplete.js +22 -5
  159. package/lib/esm/components/SnippetNotifications/SnippetNotifications.js +7 -0
  160. package/lib/esm/components/ToastNotifications/ToastNotifications.js +7 -0
  161. package/lib/esm/components/User/User.d.ts +6 -1
  162. package/lib/esm/components/User/User.js +5 -4
  163. package/lib/esm/components/UserSubscribedGroupsWidget/Skeleton.d.ts +21 -0
  164. package/lib/esm/components/UserSubscribedGroupsWidget/Skeleton.js +42 -0
  165. package/lib/esm/components/UserSubscribedGroupsWidget/UserSubscribedGroupsWidget.d.ts +68 -0
  166. package/lib/esm/components/UserSubscribedGroupsWidget/UserSubscribedGroupsWidget.js +180 -0
  167. package/lib/esm/components/UserSubscribedGroupsWidget/constants.d.ts +1 -0
  168. package/lib/esm/components/UserSubscribedGroupsWidget/constants.js +1 -0
  169. package/lib/esm/components/UserSubscribedGroupsWidget/index.d.ts +4 -0
  170. package/lib/esm/components/UserSubscribedGroupsWidget/index.js +4 -0
  171. package/lib/esm/components/VoteAudienceButton/VoteAudienceButton.js +1 -1
  172. package/lib/esm/constants/PubSub.d.ts +28 -0
  173. package/lib/esm/constants/PubSub.js +19 -0
  174. package/lib/esm/index.d.ts +5 -3
  175. package/lib/esm/index.js +4 -2
  176. package/lib/esm/types/index.d.ts +2 -1
  177. package/lib/umd/311.js +2 -0
  178. package/lib/umd/react-ui.js +1 -1
  179. package/package.json +10 -10
  180. package/lib/umd/871.js +0 -2
  181. /package/lib/umd/{871.js.LICENSE.txt → 311.js.LICENSE.txt} +0 -0
@@ -9,6 +9,8 @@ import { FormattedMessage } from 'react-intl';
9
9
  import classNames from 'classnames';
10
10
  import { useThemeProps } from '@mui/system';
11
11
  import { SCOPE_SC_UI } from '../../constants/Errors';
12
+ import { SCEventType, SCTopicType } from '../../constants/PubSub';
13
+ import PubSub from 'pubsub-js';
12
14
  const PREFIX = 'SCGroupSubscribeButton';
13
15
  const classes = {
14
16
  root: `${PREFIX}-root`
@@ -47,7 +49,7 @@ export default function GroupSubscribeButton(inProps) {
47
49
  props: inProps,
48
50
  name: PREFIX
49
51
  });
50
- const { className, groupId, group, userId, onSubscribe } = props, rest = __rest(props, ["className", "groupId", "group", "userId", "onSubscribe"]);
52
+ const { className, groupId, group, user, onSubscribe } = props, rest = __rest(props, ["className", "groupId", "group", "user", "onSubscribe"]);
51
53
  // STATE
52
54
  const [status, setStatus] = useState(null);
53
55
  // CONTEXT
@@ -71,11 +73,25 @@ export default function GroupSubscribeButton(inProps) {
71
73
  setStatus(scGroupsManager.subscriptionStatus(scGroup));
72
74
  }
73
75
  }, [authUserId, scGroupsManager.subscriptionStatus]);
74
- const subscribe = (userId) => {
76
+ /**
77
+ * Notify UI when a member is added to a group
78
+ * @param group
79
+ * @param user
80
+ */
81
+ function notifyChanges(group, user) {
82
+ if (group && user) {
83
+ PubSub.publish(`${SCTopicType.GROUP}.${SCEventType.ADD_MEMBER}`, { group, user });
84
+ }
85
+ }
86
+ const subscribe = (user) => {
75
87
  scGroupsManager
76
- .subscribe(scGroup, userId)
88
+ .subscribe(scGroup, user === null || user === void 0 ? void 0 : user.id)
77
89
  .then(() => {
78
- onSubscribe && onSubscribe(scGroup, SCGroupSubscriptionStatusType.SUBSCRIBED);
90
+ const _status = scGroup.privacy === SCGroupPrivacyType.PRIVATE && scGroup.subscription_status !== SCGroupSubscriptionStatusType.INVITED
91
+ ? SCGroupSubscriptionStatusType.REQUESTED
92
+ : SCGroupSubscriptionStatusType.SUBSCRIBED;
93
+ notifyChanges(scGroup, user);
94
+ onSubscribe && onSubscribe(scGroup, _status);
79
95
  })
80
96
  .catch((e) => {
81
97
  Logger.error(SCOPE_SC_UI, e);
@@ -96,7 +112,7 @@ export default function GroupSubscribeButton(inProps) {
96
112
  scContext.settings.handleAnonymousAction();
97
113
  }
98
114
  else {
99
- status === SCGroupSubscriptionStatusType.SUBSCRIBED && !userId ? unsubscribe() : userId ? subscribe(userId) : subscribe();
115
+ status === SCGroupSubscriptionStatusType.SUBSCRIBED && !(user === null || user === void 0 ? void 0 : user.id) ? unsubscribe() : (user === null || user === void 0 ? void 0 : user.id) ? subscribe(user) : subscribe();
100
116
  }
101
117
  };
102
118
  /**
@@ -122,7 +138,7 @@ export default function GroupSubscribeButton(inProps) {
122
138
  }
123
139
  return _status;
124
140
  };
125
- if (!scGroup || (isGroupAdmin && userId === scUserContext.user.id)) {
141
+ if (!scGroup || (isGroupAdmin && (user === null || user === void 0 ? void 0 : user.id) === scUserContext.user.id) || (isGroupAdmin && !(user === null || user === void 0 ? void 0 : user.id))) {
126
142
  return null;
127
143
  }
128
144
  return (React.createElement(Root, Object.assign({ size: "small", variant: "outlined", onClick: handleSubscribeAction, loading: scUserContext.user ? scGroupsManager.isLoading(scGroup) : null, disabled: status === SCGroupSubscriptionStatusType.REQUESTED, className: classNames(classes.root, className) }, rest), isGroupAdmin ? React.createElement(FormattedMessage, { defaultMessage: "ui.groupSubscribeButton.accept", id: "ui.groupSubscribeButton.accept" }) : getStatus()));
@@ -1,5 +1,3 @@
1
- import { SCGroupType } from '@selfcommunity/types';
2
- import { EndpointType } from '@selfcommunity/api-services';
3
1
  import { GroupProps } from '../Group';
4
2
  export interface GroupsProps {
5
3
  /**
@@ -8,20 +6,29 @@ export interface GroupsProps {
8
6
  */
9
7
  className?: string;
10
8
  /**
11
- * Endpoint to call
9
+ * Feed API Query Params
10
+ * @default [{'limit': 20, 'offset': 0}]
12
11
  */
13
- endpoint: EndpointType;
12
+ endpointQueryParams?: Record<string, string | number>;
14
13
  /**
15
14
  * Props to spread to single group object
16
15
  * @default {variant: 'outlined', ButtonBaseProps: {disableRipple: 'true'}}
17
16
  */
18
17
  GroupComponentProps?: GroupProps;
18
+ /** If true, it means that the endpoint fetches all groups available
19
+ * @default true
20
+ */
21
+ general?: boolean;
19
22
  /**
20
- * Prefetch groups. Useful for SSR.
21
- * Use this to init the component with groups
22
- * @default null
23
+ * Show/Hide filters
24
+ * @default true
25
+ */
26
+ showFilters?: boolean;
27
+ /**
28
+ * Filters component
29
+ * @param props
23
30
  */
24
- prefetchedGroups?: SCGroupType[];
31
+ filters?: JSX.Element;
25
32
  /**
26
33
  * Other props
27
34
  */
@@ -1,10 +1,10 @@
1
- import { __awaiter, __rest } from "tslib";
1
+ import { __rest } from "tslib";
2
2
  import React, { useEffect, useMemo, useState } from 'react';
3
3
  import { styled } from '@mui/material/styles';
4
- import { Box, Grid, Typography } from '@mui/material';
5
- import { http } from '@selfcommunity/api-services';
6
- import { Logger } from '@selfcommunity/utils';
7
- import { SCPreferences, useIsComponentMountedRef, useSCPreferences, useSCUser } from '@selfcommunity/react-core';
4
+ import { Box, Button, Grid, TextField, Typography, useMediaQuery, useTheme } from '@mui/material';
5
+ import { Endpoints, GroupService, http } from '@selfcommunity/api-services';
6
+ import { Logger, sortByAttr } from '@selfcommunity/utils';
7
+ import { SCPreferences, useSCPreferences, useSCUser } from '@selfcommunity/react-core';
8
8
  import Skeleton from './Skeleton';
9
9
  import { FormattedMessage } from 'react-intl';
10
10
  import classNames from 'classnames';
@@ -12,9 +12,12 @@ import { SCOPE_SC_UI } from '../../constants/Errors';
12
12
  import { useThemeProps } from '@mui/system';
13
13
  import HiddenPlaceholder from '../../shared/HiddenPlaceholder';
14
14
  import { PREFIX } from './constants';
15
- import Group from '../Group';
15
+ import Group, { GroupSkeleton } from '../Group';
16
+ import { DEFAULT_PAGINATION_OFFSET } from '../../constants/Pagination';
17
+ import InfiniteScroll from '../../shared/InfiniteScroll';
16
18
  const classes = {
17
19
  root: `${PREFIX}-root`,
20
+ filters: `${PREFIX}-filter`,
18
21
  groups: `${PREFIX}-groups`,
19
22
  item: `${PREFIX}-item`,
20
23
  noResults: `${PREFIX}-no-results`,
@@ -62,10 +65,14 @@ export default function Groups(inProps) {
62
65
  props: inProps,
63
66
  name: PREFIX
64
67
  });
65
- const { endpoint, className, GroupComponentProps = { variant: 'outlined', ButtonBaseProps: { disableRipple: true, component: Box } }, prefetchedGroups = [] } = props, rest = __rest(props, ["endpoint", "className", "GroupComponentProps", "prefetchedGroups"]);
68
+ const { endpointQueryParams = { limit: 20, offset: DEFAULT_PAGINATION_OFFSET }, className, GroupComponentProps = { variant: 'outlined', ButtonBaseProps: { disableRipple: true, component: Box } }, showFilters = true, filters, general = true } = props, rest = __rest(props, ["endpointQueryParams", "className", "GroupComponentProps", "showFilters", "filters", "general"]);
66
69
  // STATE
67
70
  const [groups, setGroups] = useState([]);
68
71
  const [loading, setLoading] = useState(true);
72
+ const [next, setNext] = useState(null);
73
+ const [search, setSearch] = useState('');
74
+ const theme = useTheme();
75
+ const isMobile = useMediaQuery(theme.breakpoints.down('md'));
69
76
  // CONTEXT
70
77
  const scUserContext = useSCUser();
71
78
  const scPreferencesContext = useSCPreferences();
@@ -74,19 +81,31 @@ export default function Groups(inProps) {
74
81
  scPreferencesContext.preferences[SCPreferences.CONFIGURATIONS_CONTENT_AVAILABILITY].value, [scPreferencesContext.preferences]);
75
82
  // CONST
76
83
  const authUserId = scUserContext.user ? scUserContext.user.id : null;
77
- // REFS
78
- const isMountedRef = useIsComponentMountedRef();
84
+ // HANDLERS
85
+ const handleScrollUp = () => {
86
+ window.scrollTo({ left: 0, top: 0, behavior: 'smooth' });
87
+ };
79
88
  /**
80
89
  * Fetches groups list
81
90
  */
82
- const fetchGroups = (next = endpoint.url({})) => __awaiter(this, void 0, void 0, function* () {
83
- const response = yield http.request({
84
- url: next,
85
- method: endpoint.method
91
+ const fetchGroups = () => {
92
+ let groupService;
93
+ if (general) {
94
+ groupService = GroupService.searchGroups(Object.assign(Object.assign({}, endpointQueryParams), (search !== '' && { search: search })));
95
+ }
96
+ else {
97
+ groupService = GroupService.getUserGroups(Object.assign(Object.assign({}, endpointQueryParams), (search !== '' && { search: search })));
98
+ }
99
+ groupService
100
+ .then((res) => {
101
+ setGroups(res.results);
102
+ setNext(res.next);
103
+ setLoading(false);
104
+ })
105
+ .catch((error) => {
106
+ Logger.error(SCOPE_SC_UI, error);
86
107
  });
87
- const data = response.data;
88
- return data.next ? data.results.concat(yield fetchGroups(data.next)) : data.results;
89
- });
108
+ };
90
109
  /**
91
110
  * On mount, fetches groups list
92
111
  */
@@ -94,32 +113,67 @@ export default function Groups(inProps) {
94
113
  if (!contentAvailability && !authUserId) {
95
114
  return;
96
115
  }
97
- else if (prefetchedGroups.length) {
98
- setGroups(prefetchedGroups);
99
- setLoading(false);
100
- }
101
116
  else {
102
- fetchGroups()
103
- .then((data) => {
104
- if (isMountedRef.current) {
105
- setGroups(data);
106
- setLoading(false);
107
- }
108
- })
109
- .catch((error) => {
110
- Logger.error(SCOPE_SC_UI, error);
111
- });
117
+ fetchGroups();
118
+ }
119
+ }, [contentAvailability, authUserId, search]);
120
+ const handleNext = useMemo(() => () => {
121
+ if (!next) {
122
+ return;
112
123
  }
113
- }, [contentAvailability, authUserId, prefetchedGroups.length]);
124
+ return http
125
+ .request({
126
+ url: next,
127
+ method: general ? Endpoints.SearchGroups.method : Endpoints.GetUserGroups.method
128
+ })
129
+ .then((res) => {
130
+ setGroups([...groups, ...res.data.results]);
131
+ setNext(res.data.next);
132
+ })
133
+ .catch((error) => console.log(error))
134
+ .then(() => setLoading(false));
135
+ }, [next]);
136
+ /**
137
+ * Get groups filtered
138
+ */
139
+ const getFilteredGroups = () => {
140
+ if (search) {
141
+ return groups.filter((g) => g.name.toLowerCase().includes(search.toLowerCase()));
142
+ }
143
+ return groups;
144
+ };
145
+ /**
146
+ * Handle change filter name
147
+ * @param event
148
+ */
149
+ const handleOnChangeFilterName = (event) => {
150
+ setSearch(event.target.value);
151
+ };
152
+ /**
153
+ * Renders groups list
154
+ */
155
+ const filteredGroups = sortByAttr(getFilteredGroups(), 'order');
156
+ const content = (React.createElement(React.Fragment, null,
157
+ showFilters && groups.length !== 0 && (React.createElement(Grid, { container: true, direction: "row", justifyContent: "center", alignItems: "center", className: classes.filters }, filters ? (filters) : (React.createElement(Grid, { item: true, xs: 12, md: 6 },
158
+ React.createElement(TextField, { fullWidth: true, value: search, label: React.createElement(FormattedMessage, { id: "ui.groups.filterByName", defaultMessage: "ui.groups.filterByName" }), variant: "outlined", onChange: handleOnChangeFilterName, disabled: loading }))))),
159
+ React.createElement(React.Fragment, null, !groups.length ? (React.createElement(Box, { className: classes.noResults },
160
+ React.createElement(Typography, { variant: "h4" },
161
+ React.createElement(FormattedMessage, { id: "ui.groups.noGroups.title", defaultMessage: "ui.groups.noGroups.title" })),
162
+ React.createElement(Typography, { variant: "body1" },
163
+ React.createElement(FormattedMessage, { id: "ui.groups.noGroups.subtitle", defaultMessage: "ui.groups.noGroups.subtitle" })))) : (React.createElement(InfiniteScroll, { dataLength: groups.length, next: handleNext, hasMoreNext: Boolean(next), loaderNext: isMobile ? React.createElement(GroupSkeleton, null) : React.createElement(Skeleton, { groupsNumber: 2 }), endMessage: React.createElement(Typography, { component: "div", className: classes.endMessage },
164
+ React.createElement(FormattedMessage, { id: "ui.groups.endMessage", defaultMessage: "ui.groups.endMessage", values: {
165
+ // eslint-disable-next-line @typescript-eslint/ban-ts-ignore
166
+ // @ts-ignore
167
+ button: (chunk) => (React.createElement(Button, { color: "secondary", variant: "text", onClick: handleScrollUp }, chunk))
168
+ } })) },
169
+ React.createElement(Grid, { container: true, spacing: { xs: 2 }, className: classes.groups }, filteredGroups.map((group) => (React.createElement(Grid, { item: true, xs: 12, sm: 8, md: 6, key: group.id, className: classes.item },
170
+ React.createElement(Group, Object.assign({ group: group, groupId: group.id, actionRedirect: true }, GroupComponentProps)))))))))));
114
171
  // RENDER
115
172
  if (!contentAvailability && !scUserContext.user) {
116
173
  return React.createElement(HiddenPlaceholder, null);
117
174
  }
118
- const content = (React.createElement(React.Fragment, null, loading ? (React.createElement(Skeleton, null)) : (React.createElement(Grid, { container: true, spacing: { xs: 3 }, className: classes.groups }, !groups.length ? (React.createElement(Box, { className: classes.noResults },
119
- React.createElement(Typography, { variant: "h4" },
120
- React.createElement(FormattedMessage, { id: "ui.groups.noGroups.title", defaultMessage: "ui.groups.noGroups.title" })),
121
- React.createElement(Typography, { variant: "body1" },
122
- React.createElement(FormattedMessage, { id: "ui.groups.noGroups.subtitle", defaultMessage: "ui.groups.noGroups.subtitle" })))) : (React.createElement(React.Fragment, null, groups.map((group) => (React.createElement(Grid, { item: true, xs: 12, sm: 8, md: 6, key: group.id, className: classes.item },
123
- React.createElement(Group, Object.assign({ group: group, groupId: group.id }, GroupComponentProps)))))))))));
175
+ if (loading) {
176
+ return React.createElement(Skeleton, null);
177
+ }
124
178
  return (React.createElement(Root, Object.assign({ className: classNames(classes.root, className) }, rest), content));
125
179
  }
@@ -9,6 +9,10 @@ export interface GroupsSkeletonProps {
9
9
  * @default null
10
10
  */
11
11
  GroupSkeletonProps?: any;
12
+ /**
13
+ * @default 20
14
+ */
15
+ groupsNumber?: number;
12
16
  }
13
17
  /**
14
18
  * > API documentation for the Community-JS Groups Skeleton component. Learn about the available props and the CSS API.
@@ -35,8 +35,8 @@ const Root = styled(Box, {
35
35
  *
36
36
  */
37
37
  export default function GroupsSkeleton(inProps) {
38
- const { className, GroupSkeletonProps = {} } = inProps, rest = __rest(inProps, ["className", "GroupSkeletonProps"]);
38
+ const { className, GroupSkeletonProps = {}, groupsNumber = 20 } = inProps, rest = __rest(inProps, ["className", "GroupSkeletonProps", "groupsNumber"]);
39
39
  return (React.createElement(Root, Object.assign({ className: classNames(classes.root, className) }, rest),
40
- React.createElement(Grid, { container: true, spacing: { xs: 3 }, className: classes.groups }, [...Array(15)].map((category, index) => (React.createElement(Grid, { item: true, xs: 12, sm: 8, md: 6, key: index },
40
+ React.createElement(Grid, { container: true, spacing: { xs: 3 }, className: classes.groups }, [...Array(groupsNumber)].map((category, index) => (React.createElement(Grid, { item: true, xs: 12, sm: 8, md: 6, key: index },
41
41
  React.createElement(GroupSkeleton, Object.assign({ elevation: 0, variant: 'outlined' }, GroupSkeletonProps))))))));
42
42
  }
@@ -1,6 +1,6 @@
1
1
  import { __rest } from "tslib";
2
- import React, { useCallback, useState } from 'react';
3
- import { Link, SCRoutes, UserUtils, useSCContext, useSCRouting, useSCUser } from '@selfcommunity/react-core';
2
+ import React, { useCallback, useMemo, useState } from 'react';
3
+ import { Link, SCPreferences, SCRoutes, UserUtils, useSCContext, useSCPreferences, useSCRouting, useSCUser } from '@selfcommunity/react-core';
4
4
  import { Avatar, Box, Button, CardContent } from '@mui/material';
5
5
  import { styled } from '@mui/material/styles';
6
6
  import { FormattedMessage } from 'react-intl';
@@ -10,6 +10,7 @@ import { useThemeProps } from '@mui/system';
10
10
  import Composer from '../Composer';
11
11
  import { File, Link as MediaLink } from '../../shared/Media';
12
12
  import { PREFIX } from './constants';
13
+ import HiddenPlaceholder from '../../shared/HiddenPlaceholder';
13
14
  const classes = {
14
15
  root: `${PREFIX}-root`,
15
16
  content: `${PREFIX}-content`,
@@ -58,6 +59,9 @@ export default function InlineComposerWidget(inProps) {
58
59
  const scUserContext = useSCUser();
59
60
  const scRoutingContext = useSCRouting();
60
61
  const { enqueueSnackbar } = useSnackbar();
62
+ // PREFERENCES
63
+ const preferences = useSCPreferences();
64
+ const onlyStaffEnabled = useMemo(() => preferences.preferences[SCPreferences.CONFIGURATIONS_POST_ONLY_STAFF_ENABLED].value, [preferences.preferences]);
61
65
  // State variables
62
66
  const [open, setOpen] = useState(false);
63
67
  // Handlers
@@ -92,6 +96,9 @@ export default function InlineComposerWidget(inProps) {
92
96
  }
93
97
  setOpen(false);
94
98
  };
99
+ if (!UserUtils.isStaff(scUserContext.user) && onlyStaffEnabled) {
100
+ return React.createElement(HiddenPlaceholder, null);
101
+ }
95
102
  return (React.createElement(React.Fragment, null,
96
103
  React.createElement(Root, Object.assign({ className: classes.root }, rest),
97
104
  React.createElement(CardContent, { className: classes.content },
@@ -2,7 +2,6 @@ import { __rest } from "tslib";
2
2
  import React, { useMemo, useState } from 'react';
3
3
  import { styled } from '@mui/material/styles';
4
4
  import { Divider, Icon, IconButton, List, ListItem, ListItemButton, Menu, MenuItem, SwipeableDrawer, useMediaQuery, useTheme } from '@mui/material';
5
- import { SCFeatureName } from '@selfcommunity/types';
6
5
  import { Link, SCPreferences, SCRoutes, UserUtils, useSCPreferences, useSCRouting, useSCUser } from '@selfcommunity/react-core';
7
6
  import classNames from 'classnames';
8
7
  import { useThemeProps } from '@mui/system';
@@ -34,7 +33,8 @@ const MenuRoot = styled(Menu, {
34
33
  const PREFERENCES = [
35
34
  SCPreferences.CONFIGURATIONS_FOLLOW_ENABLED,
36
35
  SCPreferences.CONFIGURATIONS_POST_TYPE_ENABLED,
37
- SCPreferences.CONFIGURATIONS_DISCUSSION_TYPE_ENABLED
36
+ SCPreferences.CONFIGURATIONS_DISCUSSION_TYPE_ENABLED,
37
+ SCPreferences.ADDONS_LOYALTY_POINTS_COLLECTION
38
38
  ];
39
39
  /**
40
40
  * > API documentation for the Community-JS Navigation Settings Icon Button component. Learn about the available props and the CSS API.
@@ -118,7 +118,7 @@ export default function NavigationSettingsIconButton(inProps) {
118
118
  !preferences[SCPreferences.CONFIGURATIONS_FOLLOW_ENABLED] && (React.createElement(ListItem, { className: classes.item, key: "connections" },
119
119
  React.createElement(ListItemButton, { component: Link, to: scRoutingContext.url(SCRoutes.USER_PROFILE_CONNECTIONS_ROUTE_NAME, scUserContext.user) },
120
120
  React.createElement(FormattedMessage, { id: "ui.navigationSettingsIconButton.connections", defaultMessage: "ui.navigationSettingsIconButton.connections" })))),
121
- scPreferences.features.includes(SCFeatureName.LOYALTY) && (React.createElement(ListItem, { className: classes.item, key: "loyaltyProgram" },
121
+ preferences[SCPreferences.ADDONS_LOYALTY_POINTS_COLLECTION] && (React.createElement(ListItem, { className: classes.item, key: "loyaltyProgram" },
122
122
  React.createElement(ListItemButton, { component: Link, to: scRoutingContext.url(SCRoutes.LOYALTY_ROUTE_NAME, {}) },
123
123
  React.createElement(FormattedMessage, { id: "ui.navigationSettingsIconButton.loyalty", defaultMessage: "ui.navigationSettingsIconButton.loyalty" })))),
124
124
  preferences[SCPreferences.CONFIGURATIONS_POST_TYPE_ENABLED] && (React.createElement(ListItem, { className: classes.item, key: "followedPosts" },
@@ -167,7 +167,7 @@ export default function NavigationSettingsIconButton(inProps) {
167
167
  React.createElement(FormattedMessage, { id: "ui.navigationSettingsIconButton.followers", defaultMessage: "ui.navigationSettingsIconButton.followers" }))),
168
168
  !preferences[SCPreferences.CONFIGURATIONS_FOLLOW_ENABLED] && (React.createElement(MenuItem, { className: classes.item, key: "connections", component: Link, to: scRoutingContext.url(SCRoutes.USER_PROFILE_CONNECTIONS_ROUTE_NAME, scUserContext.user) },
169
169
  React.createElement(FormattedMessage, { id: "ui.navigationSettingsIconButton.connections", defaultMessage: "ui.navigationSettingsIconButton.connections" }))),
170
- scPreferences.features.includes(SCFeatureName.LOYALTY) && (React.createElement(MenuItem, { className: classes.item, key: "loyaltyProgram", component: Link, to: scRoutingContext.url(SCRoutes.LOYALTY_ROUTE_NAME, {}) },
170
+ preferences[SCPreferences.ADDONS_LOYALTY_POINTS_COLLECTION] && (React.createElement(MenuItem, { className: classes.item, key: "loyaltyProgram", component: Link, to: scRoutingContext.url(SCRoutes.LOYALTY_ROUTE_NAME, {}) },
171
171
  React.createElement(FormattedMessage, { id: "ui.navigationSettingsIconButton.loyalty", defaultMessage: "ui.navigationSettingsIconButton.loyalty" }))),
172
172
  preferences[SCPreferences.CONFIGURATIONS_POST_TYPE_ENABLED] && (React.createElement(MenuItem, { className: classes.item, key: "followedPosts", component: Link, to: scRoutingContext.url(SCRoutes.USER_PROFILE_FOLLOWED_POSTS_ROUTE_NAME, scUserContext.user) },
173
173
  React.createElement(FormattedMessage, { id: "ui.navigationSettingsIconButton.postsFollowed", defaultMessage: "ui.navigationSettingsIconButton.postsFollowed" }))),
@@ -11,7 +11,7 @@ import SearchAutocomplete from '../SearchAutocomplete';
11
11
  import NavigationSettingsIconButton from '../NavigationSettingsIconButton';
12
12
  import ComposerIconButton from '../ComposerIconButton';
13
13
  import { SCFeatureName } from '@selfcommunity/types';
14
- import { Link, SCPreferences, SCRoutes, useSCPreferences, useSCRouting, useSCUser } from '@selfcommunity/react-core';
14
+ import { Link, SCPreferences, SCRoutes, UserUtils, useSCPreferences, useSCRouting, useSCUser } from '@selfcommunity/react-core';
15
15
  import NavigationMenuIconButton from '../NavigationMenuIconButton';
16
16
  import { PREFIX } from './constants';
17
17
  const classes = {
@@ -100,6 +100,11 @@ export default function NavigationToolbar(inProps) {
100
100
  }, [scPreferences.preferences]);
101
101
  const privateMessagingEnabled = useMemo(() => scPreferences.features.includes(SCFeatureName.PRIVATE_MESSAGING), [scPreferences.features]);
102
102
  const groupsEnabled = useMemo(() => scPreferences.features.includes(SCFeatureName.GROUPING), [scPreferences.features]);
103
+ const showComposer = useMemo(() => {
104
+ return (scPreferences.preferences[SCPreferences.CONFIGURATIONS_POST_ONLY_STAFF_ENABLED].value &&
105
+ !disableComposer &&
106
+ (!scPreferences.preferences[SCPreferences.CONFIGURATIONS_POST_ONLY_STAFF_ENABLED].value || UserUtils.isStaff(scUserContext.user)));
107
+ }, [scPreferences, disableComposer, scUserContext.user]);
103
108
  // STATE
104
109
  const [anchorNotification, setAnchorNotification] = React.useState(null);
105
110
  // HANDLERS
@@ -121,7 +126,10 @@ export default function NavigationToolbar(inProps) {
121
126
  preferences[SCPreferences.CONFIGURATIONS_EXPLORE_STREAM_ENABLED] &&
122
127
  (preferences[SCPreferences.CONFIGURATIONS_CONTENT_AVAILABILITY] || scUserContext.user) && (React.createElement(IconButton, { className: classNames(classes.explore, { [classes.active]: value.startsWith(scRoutingContext.url(SCRoutes.EXPLORE_ROUTE_NAME, {})) }), "aria-label": "Explore", to: scRoutingContext.url(SCRoutes.EXPLORE_ROUTE_NAME, {}), component: Link },
123
128
  React.createElement(Icon, null, "explore"))),
124
- groupsEnabled && scUserContext.user && (React.createElement(IconButton, { className: classNames(classes.groups, { [classes.active]: value.startsWith(scRoutingContext.url(SCRoutes.GROUPS_ROUTE_NAME, {})) }), "aria-label": "Groups", to: scRoutingContext.url(SCRoutes.GROUPS_ROUTE_NAME, {}), component: Link },
129
+ groupsEnabled && scUserContext.user && (React.createElement(IconButton, { className: classNames(classes.groups, {
130
+ [classes.active]: value.startsWith(scRoutingContext.url(SCRoutes.GROUPS_SUBSCRIBED_ROUTE_NAME, {})) ||
131
+ value.startsWith(scRoutingContext.url(SCRoutes.GROUPS_ROUTE_NAME, {}))
132
+ }), "aria-label": "Groups", to: scRoutingContext.url(SCRoutes.GROUPS_SUBSCRIBED_ROUTE_NAME, {}), component: Link },
125
133
  React.createElement(Icon, null, "groups")))));
126
134
  return (React.createElement(Root, Object.assign({ className: classNames(className, classes.root) }, rest),
127
135
  React.createElement(NavigationMenuIconButtonComponent, null),
@@ -133,7 +141,7 @@ export default function NavigationToolbar(inProps) {
133
141
  (preferences[SCPreferences.CONFIGURATIONS_CONTENT_AVAILABILITY] || scUserContext.user) && !disableSearch ? (React.createElement(SearchAutocomplete, Object.assign({ className: classes.search, blurOnSelect: true }, SearchAutocompleteProps))) : (React.createElement(Box, { className: classes.search })),
134
142
  startActions,
135
143
  scUserContext.user ? (React.createElement(React.Fragment, null,
136
- !disableComposer && React.createElement(ComposerIconButton, Object.assign({ className: classes.composer }, ComposerIconButtonProps)),
144
+ showComposer && React.createElement(ComposerIconButton, Object.assign({ className: classes.composer }, ComposerIconButtonProps)),
137
145
  React.createElement(Tooltip, { title: scUserContext.user.username },
138
146
  React.createElement(IconButton, { component: Link, to: scRoutingContext.url(SCRoutes.USER_PROFILE_ROUTE_NAME, scUserContext.user), "aria-label": "Profile", className: classes.profile },
139
147
  React.createElement(Avatar, { alt: scUserContext.user.username, src: scUserContext.user.avatar }))),
@@ -59,6 +59,7 @@ export interface NavigationToolbarMobileProps extends ToolbarProps {
59
59
  |logo|.SCNavigationToolbarMobile-logo|Styles applied to the logo element.|
60
60
  |search|.SCNavigationToolbarMobile-search|Styles applied to the search button element|
61
61
  |searchDialog|.SCNavigationToolbarMobile-search-dialog|Styles applied to the search dialog element|
62
+ |notifications|.SCNavigationToolbarMobile-notifications|Styles applied to the notifications button element|
62
63
  |settings|.SCNavigationToolbarMobile-settings|Styles applied to the settings button element|
63
64
  |settingsDialog|.SCNavigationToolbarMobile-settingsDialog|Styles applied to the settings dialog elements|
64
65
  |login|.SCNavigationToolbarMobile-login|Styles applied to the login element.|
@@ -1,6 +1,6 @@
1
1
  import { __rest } from "tslib";
2
- import { Button, IconButton, styled, Toolbar } from '@mui/material';
3
- import React, { useCallback, useState } from 'react';
2
+ import { Badge, Button, IconButton, styled, Toolbar } from '@mui/material';
3
+ import React, { useCallback, useMemo, useState } from 'react';
4
4
  import { Link, SCPreferences, SCRoutes, useSCPreferences, useSCRouting, useSCUser } from '@selfcommunity/react-core';
5
5
  import Icon from '@mui/material/Icon';
6
6
  import { useThemeProps } from '@mui/system';
@@ -11,11 +11,13 @@ import SearchDialog from '../SearchDialog';
11
11
  import NavigationSettingsIconButton from '../NavigationSettingsIconButton';
12
12
  import NavigationMenuIconButton from '../NavigationMenuIconButton';
13
13
  import { PREFIX } from './constants';
14
+ import { SCFeatureName } from '@selfcommunity/types';
14
15
  const classes = {
15
16
  root: `${PREFIX}-root`,
16
17
  logo: `${PREFIX}-logo`,
17
18
  search: `${PREFIX}-search`,
18
19
  searchDialog: `${PREFIX}-search-dialog`,
20
+ notifications: `${PREFIX}-notifications`,
19
21
  settings: `${PREFIX}-settings`,
20
22
  settingsDialog: `${PREFIX}-settings-dialog`,
21
23
  login: `${PREFIX}-login`
@@ -50,6 +52,7 @@ const Root = styled(Toolbar, {
50
52
  |logo|.SCNavigationToolbarMobile-logo|Styles applied to the logo element.|
51
53
  |search|.SCNavigationToolbarMobile-search|Styles applied to the search button element|
52
54
  |searchDialog|.SCNavigationToolbarMobile-search-dialog|Styles applied to the search dialog element|
55
+ |notifications|.SCNavigationToolbarMobile-notifications|Styles applied to the notifications button element|
53
56
  |settings|.SCNavigationToolbarMobile-settings|Styles applied to the settings button element|
54
57
  |settingsDialog|.SCNavigationToolbarMobile-settingsDialog|Styles applied to the settings dialog elements|
55
58
  |login|.SCNavigationToolbarMobile-login|Styles applied to the login element.|
@@ -67,9 +70,11 @@ export default function NavigationToolbarMobile(inProps) {
67
70
  const scUserContext = useSCUser();
68
71
  const scRoutingContext = useSCRouting();
69
72
  // PREFERENCES
70
- const { preferences } = useSCPreferences();
73
+ const { preferences, features } = useSCPreferences();
71
74
  // STATE
72
75
  const [searchOpen, setSearchOpen] = useState(false);
76
+ // MEMO
77
+ const groupsEnabled = useMemo(() => features.includes(SCFeatureName.GROUPING), [features]);
73
78
  // HANDLERS
74
79
  const handleOpenSearch = useCallback(() => {
75
80
  setSearchOpen(true);
@@ -92,6 +97,9 @@ export default function NavigationToolbarMobile(inProps) {
92
97
  React.createElement(Icon, null, "search")),
93
98
  React.createElement(SearchDialog, { className: classes.searchDialog, fullScreen: true, open: searchOpen, SearchAutocompleteProps: Object.assign(Object.assign({}, SearchAutocompleteProps), { onClear: handleCloseSearch }) }))),
94
99
  endActions,
100
+ scUserContext.user && groupsEnabled && (React.createElement(IconButton, { className: classes.notifications, component: Link, to: scRoutingContext.url(SCRoutes.USER_NOTIFICATIONS_ROUTE_NAME, {}) },
101
+ React.createElement(Badge, { badgeContent: scUserContext.user.unseen_notification_banners_counter + scUserContext.user.unseen_interactions_counter, color: "secondary" },
102
+ React.createElement(Icon, null, "notifications_active")))),
95
103
  scUserContext.user ? (React.createElement(NavigationSettingsIconButtonComponent, { className: classes.settings })) : (React.createElement(Button, { className: classes.login, color: "inherit", component: Link, to: scRoutingContext.url(SCRoutes.SIGNIN_ROUTE_NAME, {}) },
96
104
  React.createElement(FormattedMessage, { id: "ui.appBar.navigation.login", defaultMessage: "ui.appBar.navigation.login" })))));
97
105
  }
@@ -0,0 +1,15 @@
1
+ import { SCNotificationGroupActivityType } from '@selfcommunity/types';
2
+ import { NotificationItemProps } from '../../../shared/NotificationItem';
3
+ export interface NotificationGroupProps extends Pick<NotificationItemProps, Exclude<keyof NotificationItemProps, 'image' | 'disableTypography' | 'primary' | 'primaryTypographyProps' | 'secondary' | 'secondaryTypographyProps' | 'actions' | 'footer' | 'isNew'>> {
4
+ /**
5
+ * Notification obj
6
+ * @default null
7
+ */
8
+ notificationObject: SCNotificationGroupActivityType;
9
+ }
10
+ /**
11
+ * This component render the content of the notification of type group
12
+ * @constructor
13
+ * @param props
14
+ */
15
+ export default function GroupNotification(props: NotificationGroupProps): JSX.Element;
@@ -0,0 +1,84 @@
1
+ import { __rest } from "tslib";
2
+ import React, { useContext, useEffect, useState } from 'react';
3
+ import { styled } from '@mui/material/styles';
4
+ import { Avatar, Box, Stack, Typography } from '@mui/material';
5
+ import { Link, SCRoutes, SCUserContext, useSCRouting } from '@selfcommunity/react-core';
6
+ import { SCGroupSubscriptionStatusType, SCNotificationTypologyType } from '@selfcommunity/types';
7
+ import { FormattedMessage } from 'react-intl';
8
+ import DateTimeAgo from '../../../shared/DateTimeAgo';
9
+ import classNames from 'classnames';
10
+ import { SCNotificationObjectTemplateType } from '../../../types';
11
+ import NotificationItem from '../../../shared/NotificationItem';
12
+ import { LoadingButton } from '@mui/lab';
13
+ import UserDeletedSnackBar from '../../../shared/UserDeletedSnackBar';
14
+ import UserAvatar from '../../../shared/UserAvatar';
15
+ import { PREFIX } from '../constants';
16
+ const classes = {
17
+ root: `${PREFIX}-group-root`,
18
+ avatar: `${PREFIX}-avatar`,
19
+ actions: `${PREFIX}-actions`,
20
+ acceptButton: `${PREFIX}-reply-button`,
21
+ activeAt: `${PREFIX}-active-at`,
22
+ username: `${PREFIX}-username`
23
+ };
24
+ const Root = styled(NotificationItem, {
25
+ name: PREFIX,
26
+ slot: 'GroupRoot'
27
+ })(() => ({}));
28
+ /**
29
+ * This component render the content of the notification of type group
30
+ * @constructor
31
+ * @param props
32
+ */
33
+ export default function GroupNotification(props) {
34
+ // PROPS
35
+ const { notificationObject, id = `n_${props.notificationObject['sid']}`, className, template = SCNotificationObjectTemplateType.DETAIL } = props, rest = __rest(props, ["notificationObject", "id", "className", "template"]);
36
+ // CONTEXT
37
+ const scRoutingContext = useSCRouting();
38
+ const scUserContext = useContext(SCUserContext);
39
+ const manager = scUserContext.managers.groups;
40
+ // STATE
41
+ const [status, setStatus] = useState(null);
42
+ const [openAlert, setOpenAlert] = useState(false);
43
+ // CONST
44
+ const isSnippetTemplate = template === SCNotificationObjectTemplateType.SNIPPET;
45
+ const isToastTemplate = template === SCNotificationObjectTemplateType.TOAST;
46
+ useEffect(() => {
47
+ setStatus(manager.subscriptionStatus(notificationObject.group));
48
+ }, [manager.subscriptionStatus, notificationObject.group]);
49
+ // RENDER
50
+ if (isSnippetTemplate || isToastTemplate) {
51
+ return (React.createElement(Root, Object.assign({ id: id, className: classNames(classes.root, className, `${PREFIX}-${template}`), template: template, isNew: notificationObject.is_new, disableTypography: true, image: React.createElement(Link, Object.assign({}, (!notificationObject.user.deleted && {
52
+ to: scRoutingContext.url(SCRoutes.USER_PROFILE_ROUTE_NAME, notificationObject.user)
53
+ }), { onClick: notificationObject.user.deleted ? () => setOpenAlert(true) : null }),
54
+ React.createElement(UserAvatar, { hide: !notificationObject.user.community_badge, smaller: true },
55
+ React.createElement(Avatar, { alt: notificationObject.user.username, variant: "circular", src: notificationObject.user.avatar, classes: { root: classes.avatar } }))), primary: React.createElement(Box, null,
56
+ React.createElement(Link, Object.assign({}, (!notificationObject.user.deleted && {
57
+ to: scRoutingContext.url(SCRoutes.USER_PROFILE_ROUTE_NAME, notificationObject.user)
58
+ }), { onClick: notificationObject.user.deleted ? () => setOpenAlert(true) : null, className: classes.username }), notificationObject.user.username),
59
+ ' ',
60
+ React.createElement(FormattedMessage, { id: `ui.notification.${notificationObject.type}`, defaultMessage: `ui.notification.${notificationObject.type}`, values: {
61
+ group: notificationObject.group.name,
62
+ link: (...chunks) => (React.createElement(Link, { to: notificationObject.type === SCNotificationTypologyType.USER_REQUESTED_TO_JOIN_GROUP ||
63
+ notificationObject.type === SCNotificationTypologyType.USER_ACCEPTED_TO_JOIN_GROUP
64
+ ? scRoutingContext.url(SCRoutes.GROUP_MEMBERS_ROUTE_NAME, notificationObject.group)
65
+ : scRoutingContext.url(SCRoutes.GROUP_ROUTE_NAME, notificationObject.group) }, chunks))
66
+ } })), footer: isToastTemplate && (React.createElement(Stack, { direction: "row", justifyContent: "space-between", alignItems: "center", spacing: 2 },
67
+ React.createElement(DateTimeAgo, { date: notificationObject.active_at }),
68
+ status && status !== SCGroupSubscriptionStatusType.SUBSCRIBED && (React.createElement(Typography, { color: "primary" },
69
+ React.createElement(Link, { to: notificationObject.type === SCNotificationTypologyType.USER_REQUESTED_TO_JOIN_GROUP ||
70
+ notificationObject.type === SCNotificationTypologyType.USER_ACCEPTED_TO_JOIN_GROUP
71
+ ? scRoutingContext.url(SCRoutes.GROUP_MEMBERS_ROUTE_NAME, notificationObject.group)
72
+ : scRoutingContext.url(SCRoutes.GROUP_ROUTE_NAME, notificationObject.group) },
73
+ React.createElement(FormattedMessage, { id: "ui.notification.group.button.see", defaultMessage: "ui.notification.group.button.see" })))))) }, rest)));
74
+ }
75
+ return (React.createElement(React.Fragment, null,
76
+ React.createElement(Root, Object.assign({ id: id, className: classNames(classes.root, className, `${PREFIX}-${template}`), template: template, isNew: notificationObject.is_new, disableTypography: true, actions: React.createElement(Stack, { direction: "row", justifyContent: "space-between", alignItems: "center", spacing: 2 },
77
+ React.createElement(DateTimeAgo, { date: notificationObject.active_at, className: classes.activeAt }),
78
+ status && status !== SCGroupSubscriptionStatusType.SUBSCRIBED && (React.createElement(LoadingButton, { color: 'primary', variant: "outlined", size: "small", classes: { root: classes.acceptButton }, component: Link, loading: scUserContext.user ? status === null || manager.isLoading(notificationObject.group) : null, to: notificationObject.type === SCNotificationTypologyType.USER_REQUESTED_TO_JOIN_GROUP ||
79
+ notificationObject.type === SCNotificationTypologyType.USER_ACCEPTED_TO_JOIN_GROUP
80
+ ? scRoutingContext.url(SCRoutes.GROUP_MEMBERS_ROUTE_NAME, notificationObject.group)
81
+ : scRoutingContext.url(SCRoutes.GROUP_ROUTE_NAME, notificationObject.group) },
82
+ React.createElement(FormattedMessage, { id: "ui.notification.group.button.see", defaultMessage: "ui.notification.group.button.see" })))) }, rest)),
83
+ openAlert && React.createElement(UserDeletedSnackBar, { open: openAlert, handleClose: () => setOpenAlert(false) })));
84
+ }
@@ -0,0 +1,3 @@
1
+ import GroupNotification, { NotificationGroupProps } from './Group';
2
+ export default GroupNotification;
3
+ export { NotificationGroupProps };
@@ -0,0 +1,2 @@
1
+ import GroupNotification from './Group';
2
+ export default GroupNotification;