@selfcommunity/react-ui 0.7.9-alpha.44 → 0.7.9-alpha.46

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 (26) hide show
  1. package/lib/cjs/components/CategoryHeader/Skeleton.js +3 -2
  2. package/lib/cjs/components/Composer/Composer.js +1 -1
  3. package/lib/cjs/components/Composer/Layer/AudienceLayer/AudienceLayer.js +3 -3
  4. package/lib/cjs/components/GroupAutocomplete/GroupAutocomplete.js +1 -1
  5. package/lib/cjs/components/GroupHeader/GroupHeader.js +2 -1
  6. package/lib/cjs/components/GroupInviteButton/GroupInviteButton.js +13 -0
  7. package/lib/cjs/components/GroupInvitedWidget/GroupInvitedWidget.js +25 -1
  8. package/lib/cjs/components/PrivateMessageComponent/PrivateMessageComponent.js +1 -0
  9. package/lib/cjs/components/PrivateMessageSnippets/PrivateMessageSnippets.js +3 -3
  10. package/lib/cjs/components/PrivateMessageThreadItem/PrivateMessageThreadItem.js +6 -0
  11. package/lib/cjs/constants/PubSub.d.ts +1 -0
  12. package/lib/cjs/constants/PubSub.js +1 -0
  13. package/lib/esm/components/CategoryHeader/Skeleton.js +3 -2
  14. package/lib/esm/components/Composer/Composer.js +1 -1
  15. package/lib/esm/components/Composer/Layer/AudienceLayer/AudienceLayer.js +3 -3
  16. package/lib/esm/components/GroupAutocomplete/GroupAutocomplete.js +1 -1
  17. package/lib/esm/components/GroupHeader/GroupHeader.js +2 -1
  18. package/lib/esm/components/GroupInviteButton/GroupInviteButton.js +13 -0
  19. package/lib/esm/components/GroupInvitedWidget/GroupInvitedWidget.js +26 -2
  20. package/lib/esm/components/PrivateMessageComponent/PrivateMessageComponent.js +1 -0
  21. package/lib/esm/components/PrivateMessageSnippets/PrivateMessageSnippets.js +3 -3
  22. package/lib/esm/components/PrivateMessageThreadItem/PrivateMessageThreadItem.js +7 -1
  23. package/lib/esm/constants/PubSub.d.ts +1 -0
  24. package/lib/esm/constants/PubSub.js +1 -0
  25. package/lib/umd/react-ui.js +1 -1
  26. package/package.json +4 -4
@@ -7,7 +7,8 @@ const styles_1 = require("@mui/material/styles");
7
7
  const Skeleton_1 = tslib_1.__importDefault(require("@mui/material/Skeleton"));
8
8
  const constants_1 = require("./constants");
9
9
  const classes = {
10
- root: `${constants_1.PREFIX}-skeleton-root`
10
+ root: `${constants_1.PREFIX}-skeleton-root`,
11
+ cover: `${constants_1.PREFIX}-skeleton-cover`,
11
12
  };
12
13
  const Root = (0, styles_1.styled)(material_1.Box, {
13
14
  name: constants_1.PREFIX,
@@ -35,7 +36,7 @@ const Root = (0, styles_1.styled)(material_1.Box, {
35
36
  */
36
37
  function CategoryHeaderSkeleton() {
37
38
  return (react_1.default.createElement(Root, { className: classes.root },
38
- react_1.default.createElement(Skeleton_1.default, { sx: { height: 230 }, animation: "wave", variant: "rectangular" }),
39
+ react_1.default.createElement(Skeleton_1.default, { sx: { height: 230 }, animation: "wave", variant: "rectangular", className: classes.cover }),
39
40
  react_1.default.createElement(material_1.Box, null,
40
41
  react_1.default.createElement(Skeleton_1.default, { animation: "wave", sx: { height: 20, maxWidth: 300, width: '100%', margin: '0 auto' } })),
41
42
  react_1.default.createElement(material_1.Box, null,
@@ -307,7 +307,7 @@ function Composer(inProps) {
307
307
  }
308
308
  }), [handleAddLayer, handleRemoveLayer, handleChangeCategories, categories]);
309
309
  const handleChangeAudience = (0, react_1.useCallback)((value) => {
310
- if (group || typeof value === 'object') {
310
+ if (group || Object.prototype.hasOwnProperty.call(value, 'managed_by')) {
311
311
  dispatch({ type: 'group', value });
312
312
  }
313
313
  else {
@@ -46,7 +46,7 @@ const AudienceLayer = react_1.default.forwardRef((props, ref) => {
46
46
  // @ts-ignore
47
47
  defaultValue === null || defaultValue.length === 0
48
48
  ? AudienceTypes.AUDIENCE_ALL
49
- : defaultValue && typeof defaultValue === 'object'
49
+ : defaultValue && Object.prototype.hasOwnProperty.call(defaultValue, 'managed_by')
50
50
  ? AudienceTypes.AUDIENCE_GROUP
51
51
  : AudienceTypes.AUDIENCE_TAG);
52
52
  // eslint-disable-next-line @typescript-eslint/ban-ts-comment
@@ -81,14 +81,14 @@ const AudienceLayer = react_1.default.forwardRef((props, ref) => {
81
81
  , {
82
82
  // eslint-disable-next-line @typescript-eslint/ban-ts-comment
83
83
  // @ts-ignore
84
- disabled: defaultValue && typeof defaultValue !== 'object', value: AudienceTypes.AUDIENCE_GROUP, icon: react_1.default.createElement(Icon_1.default, null, "groups"), label: react_1.default.createElement(react_intl_1.FormattedMessage, { id: "ui.composer.layer.audience.group", defaultMessage: "ui.composer.layer.audience.group" }) }),
84
+ disabled: defaultValue && !Object.prototype.hasOwnProperty.call(value, 'managed_by'), value: AudienceTypes.AUDIENCE_GROUP, icon: react_1.default.createElement(Icon_1.default, null, "groups"), label: react_1.default.createElement(react_intl_1.FormattedMessage, { id: "ui.composer.layer.audience.group", defaultMessage: "ui.composer.layer.audience.group" }) }),
85
85
  react_1.default.createElement(material_1.Tab
86
86
  // eslint-disable-next-line @typescript-eslint/ban-ts-comment
87
87
  // @ts-ignore
88
88
  , {
89
89
  // eslint-disable-next-line @typescript-eslint/ban-ts-comment
90
90
  // @ts-ignore
91
- disabled: value && typeof defaultValue === 'object', value: AudienceTypes.AUDIENCE_TAG, icon: react_1.default.createElement(Icon_1.default, null, "label"), label: react_1.default.createElement(react_intl_1.FormattedMessage, { id: "ui.composer.layer.audience.tag", defaultMessage: "ui.composer.layer.audience.tag" }) })),
91
+ disabled: value && Object.prototype.hasOwnProperty.call(value, 'managed_by'), value: AudienceTypes.AUDIENCE_TAG, icon: react_1.default.createElement(Icon_1.default, null, "label"), label: react_1.default.createElement(react_intl_1.FormattedMessage, { id: "ui.composer.layer.audience.tag", defaultMessage: "ui.composer.layer.audience.tag" }) })),
92
92
  react_1.default.createElement(material_1.Typography, { className: classes.message },
93
93
  audience === AudienceTypes.AUDIENCE_ALL && (react_1.default.createElement(react_intl_1.FormattedMessage, { id: "ui.composer.layer.audience.all.message", defaultMessage: "ui.composer.audience.layer.all.message" })),
94
94
  audience === AudienceTypes.AUDIENCE_GROUP && (react_1.default.createElement(react_intl_1.FormattedMessage, { id: "ui.composer.layer.audience.group.message", defaultMessage: "ui.composer.audience.layer.group.message" })),
@@ -80,7 +80,7 @@ const GroupAutocomplete = (inProps) => {
80
80
  setValue(value);
81
81
  };
82
82
  // Render
83
- return (react_1.default.createElement(Root, Object.assign({ freeSolo: true, className: classes.root, open: open, onOpen: handleOpen, onClose: handleClose, options: groups || [], getOptionLabel: (option) => option.name || '', value: value, selectOnFocus: true, clearOnBlur: true, blurOnSelect: true, handleHomeEndKeys: true, clearIcon: null, disabled: disabled || isLoading, noOptionsText: react_1.default.createElement(react_intl_1.FormattedMessage, { id: "ui.groupAutocomplete.empty", defaultMessage: "ui.groupAutocomplete.empty" }), onChange: handleChange, isOptionEqualToValue: (option, value) => value.id === option.id,
83
+ return (react_1.default.createElement(Root, Object.assign({ className: classes.root, open: open, onOpen: handleOpen, onClose: handleClose, options: groups || [], getOptionLabel: (option) => option.name || '', value: value, selectOnFocus: true, clearOnBlur: true, blurOnSelect: true, handleHomeEndKeys: true, clearIcon: null, disabled: disabled || isLoading, noOptionsText: react_1.default.createElement(react_intl_1.FormattedMessage, { id: "ui.groupAutocomplete.empty", defaultMessage: "ui.groupAutocomplete.empty" }), onChange: handleChange, isOptionEqualToValue: (option, value) => value.id === option.id,
84
84
  // renderTags={(value, getTagProps) => {
85
85
  // return value.map((option: any, index) => (
86
86
  // <Chip key={option.id} id={option.id} label={option.name} color={option.color} {...getTagProps({index})} />
@@ -115,7 +115,8 @@ function GroupHeader(inProps) {
115
115
  * Subscriber for pubsub callback
116
116
  */
117
117
  const onChangeGroupMembersHandler = (0, react_1.useCallback)((msg, data) => {
118
- if (data && data.group.id === scGroup.id) {
118
+ var _a;
119
+ if (data && ((_a = data === null || data === void 0 ? void 0 : data.group) === null || _a === void 0 ? void 0 : _a.id) === (scGroup === null || scGroup === void 0 ? void 0 : scGroup.id)) {
119
120
  let _group = Object.assign({}, scGroup);
120
121
  if (msg === `${PubSub_1.SCTopicType.GROUP}.${PubSub_1.SCEventType.ADD_MEMBER}`) {
121
122
  _group.subscribers_counter = _group.subscribers_counter + 1;
@@ -15,6 +15,8 @@ const Autocomplete_1 = tslib_1.__importDefault(require("@mui/material/Autocomple
15
15
  const User_1 = tslib_1.__importDefault(require("../User"));
16
16
  const Errors_1 = require("../../constants/Errors");
17
17
  const utils_1 = require("@selfcommunity/utils");
18
+ const PubSub_1 = require("../../constants/PubSub");
19
+ const pubsub_js_1 = tslib_1.__importDefault(require("pubsub-js"));
18
20
  const messages = (0, react_intl_1.defineMessages)({
19
21
  placeholder: {
20
22
  id: 'ui.groupInviteButton.searchBar.placeholder',
@@ -90,6 +92,16 @@ function GroupInviteButton(inProps) {
90
92
  const [list, setList] = (0, react_1.useState)([]);
91
93
  const [loading, setLoading] = (0, react_1.useState)(false);
92
94
  const [invited, setInvited] = (0, react_1.useState)([]);
95
+ /**
96
+ * Notify UI when a member is invited to a group
97
+ * @param group
98
+ * @param usersInvited
99
+ */
100
+ function notifyChanges(group, usersInvited) {
101
+ if (group && usersInvited) {
102
+ pubsub_js_1.default.publish(`${PubSub_1.SCTopicType.GROUP}.${PubSub_1.SCEventType.INVITE_MEMBER}`, usersInvited);
103
+ }
104
+ }
93
105
  function convertToInvitedUsersObject(data) {
94
106
  const invite_users = {};
95
107
  data.forEach((user, index) => {
@@ -182,6 +194,7 @@ function GroupInviteButton(inProps) {
182
194
  setIsSending(false);
183
195
  setOpen(false);
184
196
  setInvited([]);
197
+ notifyChanges(scGroup, invited);
185
198
  })
186
199
  .catch((error) => {
187
200
  setOpen(false);
@@ -20,6 +20,8 @@ const system_1 = require("@mui/system");
20
20
  const HiddenPlaceholder_1 = tslib_1.__importDefault(require("../../shared/HiddenPlaceholder"));
21
21
  const constants_1 = require("./constants");
22
22
  const User_1 = tslib_1.__importStar(require("../User"));
23
+ const pubsub_js_1 = tslib_1.__importDefault(require("pubsub-js"));
24
+ const PubSub_1 = require("../../constants/PubSub");
23
25
  const classes = {
24
26
  root: `${constants_1.PREFIX}-root`,
25
27
  title: `${constants_1.PREFIX}-title`,
@@ -95,6 +97,8 @@ function GroupInvitedWidget(inProps) {
95
97
  const theme = (0, material_1.useTheme)();
96
98
  const isMobile = (0, material_1.useMediaQuery)(theme.breakpoints.down('md'));
97
99
  const isGroupAdmin = (0, react_1.useMemo)(() => { var _a; return scUserContext.user && ((_a = scGroup === null || scGroup === void 0 ? void 0 : scGroup.managed_by) === null || _a === void 0 ? void 0 : _a.id) === scUserContext.user.id; }, [scUserContext.user, (_a = scGroup === null || scGroup === void 0 ? void 0 : scGroup.managed_by) === null || _a === void 0 ? void 0 : _a.id]);
100
+ // REFS
101
+ const updatesSubscription = (0, react_1.useRef)(null);
98
102
  /**
99
103
  * Initialize component
100
104
  * Fetch data only if the component is not initialized and it is not loading data
@@ -155,13 +159,33 @@ function GroupInvitedWidget(inProps) {
155
159
  return;
156
160
  }
157
161
  }, []);
162
+ /**
163
+ * Subscriber for pubsub callback
164
+ */
165
+ const onChangeGroupHandler = (0, react_1.useCallback)((_msg, newInvited) => {
166
+ dispatch({
167
+ type: widget_1.actionWidgetTypes.SET_RESULTS,
168
+ payload: { results: [...state.results, ...newInvited] }
169
+ });
170
+ }, [scGroup, dispatch, state.results]);
171
+ /**
172
+ * On mount, subscribe to receive groups updates (only edit)
173
+ */
174
+ (0, react_1.useEffect)(() => {
175
+ if (scGroup && state.results) {
176
+ updatesSubscription.current = pubsub_js_1.default.subscribe(`${PubSub_1.SCTopicType.GROUP}.${PubSub_1.SCEventType.INVITE_MEMBER}`, onChangeGroupHandler);
177
+ }
178
+ return () => {
179
+ updatesSubscription.current && pubsub_js_1.default.unsubscribe(updatesSubscription.current);
180
+ };
181
+ }, [scGroup, state.results]);
158
182
  // HANDLERS
159
183
  const handleNext = (0, react_1.useMemo)(() => () => {
160
184
  dispatch({ type: widget_1.actionWidgetTypes.LOADING_NEXT });
161
185
  api_services_1.http
162
186
  .request({
163
187
  url: state.next,
164
- method: api_services_1.Endpoints.GetGroupWaitingApprovalSubscribers.method
188
+ method: api_services_1.Endpoints.getGroupInvitedUsers.method
165
189
  })
166
190
  .then((res) => {
167
191
  dispatch({ type: widget_1.actionWidgetTypes.LOAD_NEXT_SUCCESS, payload: res.data });
@@ -127,6 +127,7 @@ function PrivateMessageComponent(inProps) {
127
127
  id && setLayout('mobile');
128
128
  setOpenNewMessage(false);
129
129
  setObj(null);
130
+ setType(null);
130
131
  onThreadBack && onThreadBack();
131
132
  };
132
133
  /**
@@ -109,13 +109,13 @@ function PrivateMessageSnippets(inProps) {
109
109
  const isSelected = (0, react_1.useMemo)(() => {
110
110
  return (message) => {
111
111
  var _a, _b;
112
- if (type === types_1.SCPrivateMessageType.GROUP) {
112
+ if (threadObj && type === types_1.SCPrivateMessageType.GROUP) {
113
113
  return ((_a = message === null || message === void 0 ? void 0 : message.group) === null || _a === void 0 ? void 0 : _a.id) === (isObj ? (_b = threadObj === null || threadObj === void 0 ? void 0 : threadObj.group) === null || _b === void 0 ? void 0 : _b.id : threadObj);
114
114
  }
115
- else if (type === types_1.SCPrivateMessageType.USER) {
115
+ else if (threadObj && type === types_1.SCPrivateMessageType.USER) {
116
116
  return messageReceiver(message, authUserId) === (isObj ? messageReceiver(threadObj, authUserId) : threadObj);
117
117
  }
118
- return false;
118
+ return null;
119
119
  };
120
120
  }, [threadObj, authUserId, type]);
121
121
  //HANDLERS
@@ -10,6 +10,7 @@ const types_1 = require("@selfcommunity/types");
10
10
  const Icon_1 = tslib_1.__importDefault(require("@mui/material/Icon"));
11
11
  const classnames_1 = tslib_1.__importDefault(require("classnames"));
12
12
  const system_1 = require("@mui/system");
13
+ const react_core_1 = require("@selfcommunity/react-core");
13
14
  const useMediaQuery_1 = tslib_1.__importDefault(require("@mui/material/useMediaQuery"));
14
15
  const PrivateMessageSettingsIconButton_1 = tslib_1.__importDefault(require("../PrivateMessageSettingsIconButton"));
15
16
  const sizeCoverter_1 = require("../../utils/sizeCoverter");
@@ -21,6 +22,7 @@ const constants_1 = require("./constants");
21
22
  const thumbnailCoverter_1 = require("../../utils/thumbnailCoverter");
22
23
  const classes = {
23
24
  root: `${constants_1.PREFIX}-root`,
25
+ username: `${constants_1.PREFIX}-username`,
24
26
  text: `${constants_1.PREFIX}-text`,
25
27
  img: `${constants_1.PREFIX}-img`,
26
28
  document: `${constants_1.PREFIX}-document`,
@@ -72,6 +74,7 @@ const Root = (0, styles_1.styled)(material_1.ListItem, {
72
74
  * @param inProps
73
75
  */
74
76
  function PrivateMessageThreadItem(inProps) {
77
+ var _a;
75
78
  // PROPS
76
79
  const props = (0, system_1.useThemeProps)({
77
80
  props: inProps,
@@ -80,6 +83,8 @@ function PrivateMessageThreadItem(inProps) {
80
83
  const { message = null, className = null, mouseEvents = {}, isHovering = null, showMenuIcon = false, onMenuIconClick = null } = props, rest = tslib_1.__rest(props, ["message", "className", "mouseEvents", "isHovering", "showMenuIcon", "onMenuIconClick"]);
81
84
  // INTL
82
85
  const intl = (0, react_intl_1.useIntl)();
86
+ // CONTEXT
87
+ const scUserContext = (0, react_1.useContext)(react_core_1.SCUserContext);
83
88
  // STATE
84
89
  const theme = (0, material_1.useTheme)();
85
90
  const isMobile = (0, useMediaQuery_1.default)(theme.breakpoints.down('md'));
@@ -167,6 +172,7 @@ function PrivateMessageThreadItem(inProps) {
167
172
  return (react_1.default.createElement(Root, Object.assign({ className: (0, classnames_1.default)(classes.root, className) }, getMouseEvents(mouseEvents.onMouseEnter, mouseEvents.onMouseLeave), rest, { secondaryAction: (isHovering || isMobile) &&
168
173
  showMenuIcon &&
169
174
  message.status !== types_1.SCPrivateMessageStatusType.HIDDEN && react_1.default.createElement(PrivateMessageSettingsIconButton_1.default, { onMenuItemDeleteClick: handleMenuItemClick }) }),
175
+ message.group && ((_a = scUserContext === null || scUserContext === void 0 ? void 0 : scUserContext.user) === null || _a === void 0 ? void 0 : _a.username) !== message.sender.username && (react_1.default.createElement(material_1.Typography, { color: "secondary", variant: "h4", className: classes.username }, message.sender.username)),
170
176
  react_1.default.createElement(react_1.default.Fragment, null,
171
177
  hasFile && message.status !== types_1.SCPrivateMessageStatusType.HIDDEN ? (renderMessageFile(message)) : (react_1.default.createElement(material_1.Box, { className: classes.text },
172
178
  react_1.default.createElement(material_1.Typography, { component: "span", dangerouslySetInnerHTML: { __html: message.message } }))),
@@ -13,6 +13,7 @@ export declare enum SCEventType {
13
13
  EDIT = "edit",
14
14
  MEMBERS = "members",
15
15
  ADD_MEMBER = "members.add_member",
16
+ INVITE_MEMBER = "members.invite_member",
16
17
  REMOVE_MEMBER = "members.remove_member"
17
18
  }
18
19
  /**
@@ -17,5 +17,6 @@ var SCEventType;
17
17
  SCEventType["EDIT"] = "edit";
18
18
  SCEventType["MEMBERS"] = "members";
19
19
  SCEventType["ADD_MEMBER"] = "members.add_member";
20
+ SCEventType["INVITE_MEMBER"] = "members.invite_member";
20
21
  SCEventType["REMOVE_MEMBER"] = "members.remove_member";
21
22
  })(SCEventType = exports.SCEventType || (exports.SCEventType = {}));
@@ -4,7 +4,8 @@ import { styled } from '@mui/material/styles';
4
4
  import Skeleton from '@mui/material/Skeleton';
5
5
  import { PREFIX } from './constants';
6
6
  const classes = {
7
- root: `${PREFIX}-skeleton-root`
7
+ root: `${PREFIX}-skeleton-root`,
8
+ cover: `${PREFIX}-skeleton-cover`,
8
9
  };
9
10
  const Root = styled(Box, {
10
11
  name: PREFIX,
@@ -32,7 +33,7 @@ const Root = styled(Box, {
32
33
  */
33
34
  export default function CategoryHeaderSkeleton() {
34
35
  return (React.createElement(Root, { className: classes.root },
35
- React.createElement(Skeleton, { sx: { height: 230 }, animation: "wave", variant: "rectangular" }),
36
+ React.createElement(Skeleton, { sx: { height: 230 }, animation: "wave", variant: "rectangular", className: classes.cover }),
36
37
  React.createElement(Box, null,
37
38
  React.createElement(Skeleton, { animation: "wave", sx: { height: 20, maxWidth: 300, width: '100%', margin: '0 auto' } })),
38
39
  React.createElement(Box, null,
@@ -305,7 +305,7 @@ export default function Composer(inProps) {
305
305
  }
306
306
  }), [handleAddLayer, handleRemoveLayer, handleChangeCategories, categories]);
307
307
  const handleChangeAudience = useCallback((value) => {
308
- if (group || typeof value === 'object') {
308
+ if (group || Object.prototype.hasOwnProperty.call(value, 'managed_by')) {
309
309
  dispatch({ type: 'group', value });
310
310
  }
311
311
  else {
@@ -43,7 +43,7 @@ const AudienceLayer = React.forwardRef((props, ref) => {
43
43
  // @ts-ignore
44
44
  defaultValue === null || defaultValue.length === 0
45
45
  ? AudienceTypes.AUDIENCE_ALL
46
- : defaultValue && typeof defaultValue === 'object'
46
+ : defaultValue && Object.prototype.hasOwnProperty.call(defaultValue, 'managed_by')
47
47
  ? AudienceTypes.AUDIENCE_GROUP
48
48
  : AudienceTypes.AUDIENCE_TAG);
49
49
  // eslint-disable-next-line @typescript-eslint/ban-ts-comment
@@ -78,14 +78,14 @@ const AudienceLayer = React.forwardRef((props, ref) => {
78
78
  , {
79
79
  // eslint-disable-next-line @typescript-eslint/ban-ts-comment
80
80
  // @ts-ignore
81
- disabled: defaultValue && typeof defaultValue !== 'object', value: AudienceTypes.AUDIENCE_GROUP, icon: React.createElement(Icon, null, "groups"), label: React.createElement(FormattedMessage, { id: "ui.composer.layer.audience.group", defaultMessage: "ui.composer.layer.audience.group" }) }),
81
+ disabled: defaultValue && !Object.prototype.hasOwnProperty.call(value, 'managed_by'), value: AudienceTypes.AUDIENCE_GROUP, icon: React.createElement(Icon, null, "groups"), label: React.createElement(FormattedMessage, { id: "ui.composer.layer.audience.group", defaultMessage: "ui.composer.layer.audience.group" }) }),
82
82
  React.createElement(Tab
83
83
  // eslint-disable-next-line @typescript-eslint/ban-ts-comment
84
84
  // @ts-ignore
85
85
  , {
86
86
  // eslint-disable-next-line @typescript-eslint/ban-ts-comment
87
87
  // @ts-ignore
88
- disabled: value && typeof defaultValue === 'object', value: AudienceTypes.AUDIENCE_TAG, icon: React.createElement(Icon, null, "label"), label: React.createElement(FormattedMessage, { id: "ui.composer.layer.audience.tag", defaultMessage: "ui.composer.layer.audience.tag" }) })),
88
+ disabled: value && Object.prototype.hasOwnProperty.call(value, 'managed_by'), value: AudienceTypes.AUDIENCE_TAG, icon: React.createElement(Icon, null, "label"), label: React.createElement(FormattedMessage, { id: "ui.composer.layer.audience.tag", defaultMessage: "ui.composer.layer.audience.tag" }) })),
89
89
  React.createElement(Typography, { className: classes.message },
90
90
  audience === AudienceTypes.AUDIENCE_ALL && (React.createElement(FormattedMessage, { id: "ui.composer.layer.audience.all.message", defaultMessage: "ui.composer.audience.layer.all.message" })),
91
91
  audience === AudienceTypes.AUDIENCE_GROUP && (React.createElement(FormattedMessage, { id: "ui.composer.layer.audience.group.message", defaultMessage: "ui.composer.audience.layer.group.message" })),
@@ -78,7 +78,7 @@ const GroupAutocomplete = (inProps) => {
78
78
  setValue(value);
79
79
  };
80
80
  // Render
81
- return (React.createElement(Root, Object.assign({ freeSolo: true, className: classes.root, open: open, onOpen: handleOpen, onClose: handleClose, options: groups || [], getOptionLabel: (option) => option.name || '', value: value, selectOnFocus: true, clearOnBlur: true, blurOnSelect: true, handleHomeEndKeys: true, clearIcon: null, disabled: disabled || isLoading, noOptionsText: React.createElement(FormattedMessage, { id: "ui.groupAutocomplete.empty", defaultMessage: "ui.groupAutocomplete.empty" }), onChange: handleChange, isOptionEqualToValue: (option, value) => value.id === option.id,
81
+ return (React.createElement(Root, Object.assign({ className: classes.root, open: open, onOpen: handleOpen, onClose: handleClose, options: groups || [], getOptionLabel: (option) => option.name || '', value: value, selectOnFocus: true, clearOnBlur: true, blurOnSelect: true, handleHomeEndKeys: true, clearIcon: null, disabled: disabled || isLoading, noOptionsText: React.createElement(FormattedMessage, { id: "ui.groupAutocomplete.empty", defaultMessage: "ui.groupAutocomplete.empty" }), onChange: handleChange, isOptionEqualToValue: (option, value) => value.id === option.id,
82
82
  // renderTags={(value, getTagProps) => {
83
83
  // return value.map((option: any, index) => (
84
84
  // <Chip key={option.id} id={option.id} label={option.name} color={option.color} {...getTagProps({index})} />
@@ -113,7 +113,8 @@ export default function GroupHeader(inProps) {
113
113
  * Subscriber for pubsub callback
114
114
  */
115
115
  const onChangeGroupMembersHandler = useCallback((msg, data) => {
116
- if (data && data.group.id === scGroup.id) {
116
+ var _a;
117
+ if (data && ((_a = data === null || data === void 0 ? void 0 : data.group) === null || _a === void 0 ? void 0 : _a.id) === (scGroup === null || scGroup === void 0 ? void 0 : scGroup.id)) {
117
118
  let _group = Object.assign({}, scGroup);
118
119
  if (msg === `${SCTopicType.GROUP}.${SCEventType.ADD_MEMBER}`) {
119
120
  _group.subscribers_counter = _group.subscribers_counter + 1;
@@ -13,6 +13,8 @@ import Autocomplete from '@mui/material/Autocomplete';
13
13
  import User from '../User';
14
14
  import { SCOPE_SC_UI } from '../../constants/Errors';
15
15
  import { Logger } from '@selfcommunity/utils';
16
+ import { SCEventType, SCTopicType } from '../../constants/PubSub';
17
+ import PubSub from 'pubsub-js';
16
18
  const messages = defineMessages({
17
19
  placeholder: {
18
20
  id: 'ui.groupInviteButton.searchBar.placeholder',
@@ -88,6 +90,16 @@ export default function GroupInviteButton(inProps) {
88
90
  const [list, setList] = useState([]);
89
91
  const [loading, setLoading] = useState(false);
90
92
  const [invited, setInvited] = useState([]);
93
+ /**
94
+ * Notify UI when a member is invited to a group
95
+ * @param group
96
+ * @param usersInvited
97
+ */
98
+ function notifyChanges(group, usersInvited) {
99
+ if (group && usersInvited) {
100
+ PubSub.publish(`${SCTopicType.GROUP}.${SCEventType.INVITE_MEMBER}`, usersInvited);
101
+ }
102
+ }
91
103
  function convertToInvitedUsersObject(data) {
92
104
  const invite_users = {};
93
105
  data.forEach((user, index) => {
@@ -180,6 +192,7 @@ export default function GroupInviteButton(inProps) {
180
192
  setIsSending(false);
181
193
  setOpen(false);
182
194
  setInvited([]);
195
+ notifyChanges(scGroup, invited);
183
196
  })
184
197
  .catch((error) => {
185
198
  setOpen(false);
@@ -1,5 +1,5 @@
1
1
  import { __rest } from "tslib";
2
- import React, { useEffect, useMemo, useReducer, useState } from 'react';
2
+ import React, { useCallback, useEffect, useMemo, useReducer, useRef, useState } from 'react';
3
3
  import { styled } from '@mui/material/styles';
4
4
  import List from '@mui/material/List';
5
5
  import { Button, CardContent, ListItem, Typography, useMediaQuery, useTheme } from '@mui/material';
@@ -18,6 +18,8 @@ import { useThemeProps } from '@mui/system';
18
18
  import HiddenPlaceholder from '../../shared/HiddenPlaceholder';
19
19
  import { PREFIX } from './constants';
20
20
  import User, { UserSkeleton } from '../User';
21
+ import PubSub from 'pubsub-js';
22
+ import { SCEventType, SCTopicType } from '../../constants/PubSub';
21
23
  const classes = {
22
24
  root: `${PREFIX}-root`,
23
25
  title: `${PREFIX}-title`,
@@ -93,6 +95,8 @@ export default function GroupInvitedWidget(inProps) {
93
95
  const theme = useTheme();
94
96
  const isMobile = useMediaQuery(theme.breakpoints.down('md'));
95
97
  const isGroupAdmin = useMemo(() => { var _a; return scUserContext.user && ((_a = scGroup === null || scGroup === void 0 ? void 0 : scGroup.managed_by) === null || _a === void 0 ? void 0 : _a.id) === scUserContext.user.id; }, [scUserContext.user, (_a = scGroup === null || scGroup === void 0 ? void 0 : scGroup.managed_by) === null || _a === void 0 ? void 0 : _a.id]);
98
+ // REFS
99
+ const updatesSubscription = useRef(null);
96
100
  /**
97
101
  * Initialize component
98
102
  * Fetch data only if the component is not initialized and it is not loading data
@@ -153,13 +157,33 @@ export default function GroupInvitedWidget(inProps) {
153
157
  return;
154
158
  }
155
159
  }, []);
160
+ /**
161
+ * Subscriber for pubsub callback
162
+ */
163
+ const onChangeGroupHandler = useCallback((_msg, newInvited) => {
164
+ dispatch({
165
+ type: actionWidgetTypes.SET_RESULTS,
166
+ payload: { results: [...state.results, ...newInvited] }
167
+ });
168
+ }, [scGroup, dispatch, state.results]);
169
+ /**
170
+ * On mount, subscribe to receive groups updates (only edit)
171
+ */
172
+ useEffect(() => {
173
+ if (scGroup && state.results) {
174
+ updatesSubscription.current = PubSub.subscribe(`${SCTopicType.GROUP}.${SCEventType.INVITE_MEMBER}`, onChangeGroupHandler);
175
+ }
176
+ return () => {
177
+ updatesSubscription.current && PubSub.unsubscribe(updatesSubscription.current);
178
+ };
179
+ }, [scGroup, state.results]);
156
180
  // HANDLERS
157
181
  const handleNext = useMemo(() => () => {
158
182
  dispatch({ type: actionWidgetTypes.LOADING_NEXT });
159
183
  http
160
184
  .request({
161
185
  url: state.next,
162
- method: Endpoints.GetGroupWaitingApprovalSubscribers.method
186
+ method: Endpoints.getGroupInvitedUsers.method
163
187
  })
164
188
  .then((res) => {
165
189
  dispatch({ type: actionWidgetTypes.LOAD_NEXT_SUCCESS, payload: res.data });
@@ -125,6 +125,7 @@ export default function PrivateMessageComponent(inProps) {
125
125
  id && setLayout('mobile');
126
126
  setOpenNewMessage(false);
127
127
  setObj(null);
128
+ setType(null);
128
129
  onThreadBack && onThreadBack();
129
130
  };
130
131
  /**
@@ -107,13 +107,13 @@ export default function PrivateMessageSnippets(inProps) {
107
107
  const isSelected = useMemo(() => {
108
108
  return (message) => {
109
109
  var _a, _b;
110
- if (type === SCPrivateMessageType.GROUP) {
110
+ if (threadObj && type === SCPrivateMessageType.GROUP) {
111
111
  return ((_a = message === null || message === void 0 ? void 0 : message.group) === null || _a === void 0 ? void 0 : _a.id) === (isObj ? (_b = threadObj === null || threadObj === void 0 ? void 0 : threadObj.group) === null || _b === void 0 ? void 0 : _b.id : threadObj);
112
112
  }
113
- else if (type === SCPrivateMessageType.USER) {
113
+ else if (threadObj && type === SCPrivateMessageType.USER) {
114
114
  return messageReceiver(message, authUserId) === (isObj ? messageReceiver(threadObj, authUserId) : threadObj);
115
115
  }
116
- return false;
116
+ return null;
117
117
  };
118
118
  }, [threadObj, authUserId, type]);
119
119
  //HANDLERS
@@ -1,5 +1,5 @@
1
1
  import { __awaiter, __rest } from "tslib";
2
- import React, { useState } from 'react';
2
+ import React, { useContext, useState } from 'react';
3
3
  import { styled } from '@mui/material/styles';
4
4
  import { ListItem, Typography, IconButton, Box, useTheme, Button } from '@mui/material';
5
5
  import PrivateMessageThreadItemSkeleton from './Skeleton';
@@ -8,6 +8,7 @@ import { SCMessageFileType, SCPrivateMessageStatusType } from '@selfcommunity/ty
8
8
  import Icon from '@mui/material/Icon';
9
9
  import classNames from 'classnames';
10
10
  import { useThemeProps } from '@mui/system';
11
+ import { SCUserContext } from '@selfcommunity/react-core';
11
12
  import useMediaQuery from '@mui/material/useMediaQuery';
12
13
  import PrivateMessageSettingsIconButton from '../PrivateMessageSettingsIconButton';
13
14
  import { bytesToSize } from '../../utils/sizeCoverter';
@@ -19,6 +20,7 @@ import { PREFIX } from './constants';
19
20
  import { isSupportedVideoFormat } from '../../utils/thumbnailCoverter';
20
21
  const classes = {
21
22
  root: `${PREFIX}-root`,
23
+ username: `${PREFIX}-username`,
22
24
  text: `${PREFIX}-text`,
23
25
  img: `${PREFIX}-img`,
24
26
  document: `${PREFIX}-document`,
@@ -70,6 +72,7 @@ const Root = styled(ListItem, {
70
72
  * @param inProps
71
73
  */
72
74
  export default function PrivateMessageThreadItem(inProps) {
75
+ var _a;
73
76
  // PROPS
74
77
  const props = useThemeProps({
75
78
  props: inProps,
@@ -78,6 +81,8 @@ export default function PrivateMessageThreadItem(inProps) {
78
81
  const { message = null, className = null, mouseEvents = {}, isHovering = null, showMenuIcon = false, onMenuIconClick = null } = props, rest = __rest(props, ["message", "className", "mouseEvents", "isHovering", "showMenuIcon", "onMenuIconClick"]);
79
82
  // INTL
80
83
  const intl = useIntl();
84
+ // CONTEXT
85
+ const scUserContext = useContext(SCUserContext);
81
86
  // STATE
82
87
  const theme = useTheme();
83
88
  const isMobile = useMediaQuery(theme.breakpoints.down('md'));
@@ -165,6 +170,7 @@ export default function PrivateMessageThreadItem(inProps) {
165
170
  return (React.createElement(Root, Object.assign({ className: classNames(classes.root, className) }, getMouseEvents(mouseEvents.onMouseEnter, mouseEvents.onMouseLeave), rest, { secondaryAction: (isHovering || isMobile) &&
166
171
  showMenuIcon &&
167
172
  message.status !== SCPrivateMessageStatusType.HIDDEN && React.createElement(PrivateMessageSettingsIconButton, { onMenuItemDeleteClick: handleMenuItemClick }) }),
173
+ message.group && ((_a = scUserContext === null || scUserContext === void 0 ? void 0 : scUserContext.user) === null || _a === void 0 ? void 0 : _a.username) !== message.sender.username && (React.createElement(Typography, { color: "secondary", variant: "h4", className: classes.username }, message.sender.username)),
168
174
  React.createElement(React.Fragment, null,
169
175
  hasFile && message.status !== SCPrivateMessageStatusType.HIDDEN ? (renderMessageFile(message)) : (React.createElement(Box, { className: classes.text },
170
176
  React.createElement(Typography, { component: "span", dangerouslySetInnerHTML: { __html: message.message } }))),
@@ -13,6 +13,7 @@ export declare enum SCEventType {
13
13
  EDIT = "edit",
14
14
  MEMBERS = "members",
15
15
  ADD_MEMBER = "members.add_member",
16
+ INVITE_MEMBER = "members.invite_member",
16
17
  REMOVE_MEMBER = "members.remove_member"
17
18
  }
18
19
  /**
@@ -14,5 +14,6 @@ export var SCEventType;
14
14
  SCEventType["EDIT"] = "edit";
15
15
  SCEventType["MEMBERS"] = "members";
16
16
  SCEventType["ADD_MEMBER"] = "members.add_member";
17
+ SCEventType["INVITE_MEMBER"] = "members.invite_member";
17
18
  SCEventType["REMOVE_MEMBER"] = "members.remove_member";
18
19
  })(SCEventType || (SCEventType = {}));