@selfcommunity/react-ui 0.11.0-alpha.102 → 0.11.0-alpha.103

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.
@@ -102,18 +102,31 @@ const AudienceLayer = react_1.default.forwardRef((props, ref) => {
102
102
  const handleChangeAudience = (0, react_1.useCallback)((_event, data) => setAudience(data), []);
103
103
  const handleAutocompleteOpen = (0, react_1.useCallback)(() => setAutocompleteOpen(true), []);
104
104
  const handleAutocompleteClose = (0, react_1.useCallback)(() => setAutocompleteOpen(false), []);
105
- return ((0, jsx_runtime_1.jsxs)(Root, Object.assign({ ref: ref, className: (0, classnames_1.default)(className, classes.root) }, rest, { children: [(0, jsx_runtime_1.jsxs)(material_1.DialogTitle, Object.assign({ className: classes.title }, { children: [(0, jsx_runtime_1.jsx)(material_1.IconButton, Object.assign({ onClick: onClose }, { children: (0, jsx_runtime_1.jsx)(material_1.Icon, { children: "arrow_back" }) })), (0, jsx_runtime_1.jsx)(material_1.Typography, { children: (0, jsx_runtime_1.jsx)(react_intl_1.FormattedMessage, { id: "ui.composer.layer.audience.title", defaultMessage: "ui.composer.layer.audience.title" }) }), (0, jsx_runtime_1.jsx)(material_1.Button, Object.assign({ size: "small", color: "secondary", variant: "contained", onClick: handleSave }, { children: (0, jsx_runtime_1.jsx)(react_intl_1.FormattedMessage, { id: "ui.composer.layer.save", defaultMessage: "ui.composer.layer.save" }) }))] })), (0, jsx_runtime_1.jsxs)(material_1.DialogContent, Object.assign({ className: classes.content }, { children: [(0, jsx_runtime_1.jsxs)(material_1.Tabs, Object.assign({ value: audience, onChange: handleChangeAudience, "aria-label": "audience type" }, { children: [!taggingRequiredEnabled && ((0, jsx_runtime_1.jsx)(material_1.Tab, { value: AudienceTypes.AUDIENCE_ALL, icon: (0, jsx_runtime_1.jsx)(material_1.Icon, { children: "public" }), label: (0, jsx_runtime_1.jsx)(react_intl_1.FormattedMessage, { id: "ui.composer.layer.audience.all", defaultMessage: "ui.composer.layer.audience.all" }) })), eventsEnabled && ((0, jsx_runtime_1.jsx)(material_1.Tab, { disabled: (Boolean(value === null || value === void 0 ? void 0 : value.length) && !Object.prototype.hasOwnProperty.call(value, 'recurring')) ||
106
- (value !== undefined && Boolean(!(value === null || value === void 0 ? void 0 : value.length)) && audience !== AudienceTypes.AUDIENCE_ALL) ||
107
- (Boolean((value === null || value === void 0 ? void 0 : value.length) === 0) && audience === AudienceTypes.AUDIENCE_ALL && Boolean((defaultValue === null || defaultValue === void 0 ? void 0 : defaultValue.length) !== 0)), value: AudienceTypes.AUDIENCE_EVENT, icon: (0, jsx_runtime_1.jsx)(material_1.Icon, { children: "CalendarIcon" }), label: (0, jsx_runtime_1.jsx)(react_intl_1.FormattedMessage, { id: "ui.composer.layer.audience.event", defaultMessage: "ui.composer.layer.audience.event" }) })), groupsEnabled && ((0, jsx_runtime_1.jsx)(material_1.Tab, { disabled: (Boolean(value === null || value === void 0 ? void 0 : value.length) && !Object.prototype.hasOwnProperty.call(value, 'managed_by')) ||
108
- (value !== undefined && Boolean(!(value === null || value === void 0 ? void 0 : value.length)) && audience !== AudienceTypes.AUDIENCE_ALL) ||
109
- (Boolean((value === null || value === void 0 ? void 0 : value.length) === 0) && audience === AudienceTypes.AUDIENCE_ALL && Boolean((defaultValue === null || defaultValue === void 0 ? void 0 : defaultValue.length) !== 0)), value: AudienceTypes.AUDIENCE_GROUP, icon: (0, jsx_runtime_1.jsx)(material_1.Icon, { children: "groups" }), label: (0, jsx_runtime_1.jsx)(react_intl_1.FormattedMessage, { id: "ui.composer.layer.audience.group", defaultMessage: "ui.composer.layer.audience.group" }) })), (0, jsx_runtime_1.jsx)(material_1.Tab, { disabled: value &&
110
- ((Array.isArray(value) && (value.some((v) => v === null || v === void 0 ? void 0 : v.username) || value.some((v) => typeof v === 'string'))) ||
111
- (!Array.isArray(value) && Object.prototype.hasOwnProperty.call(value, 'managed_by'))) // group object
112
- , value: AudienceTypes.AUDIENCE_TAG, icon: (0, jsx_runtime_1.jsx)(material_1.Icon, { children: "label" }), label: (0, jsx_runtime_1.jsx)(react_intl_1.FormattedMessage, { id: "ui.composer.layer.audience.tag", defaultMessage: "ui.composer.layer.audience.tag" }) }), usersTaggingEnabled && ((0, jsx_runtime_1.jsx)(material_1.Tab, { disabled: value &&
113
- ((Array.isArray(value) && value.length > 0 && !value.some((v) => v === null || v === void 0 ? void 0 : v.username) && !value.every((v) => typeof v === 'string')) ||
114
- (!Array.isArray(value) &&
115
- Object.keys(value).length > 0 &&
116
- (Object.prototype.hasOwnProperty.call(value, 'managed_by') || Object.prototype.hasOwnProperty.call(value, 'recurring')))), value: AudienceTypes.AUDIENCE_USERS, icon: (0, jsx_runtime_1.jsx)(material_1.Icon, { children: "people_alt" }), label: (0, jsx_runtime_1.jsx)(react_intl_1.FormattedMessage, { id: "ui.composer.layer.audience.users", defaultMessage: "ui.composer.layer.audience.users" }) }))] })), (0, jsx_runtime_1.jsxs)(material_1.Typography, Object.assign({ className: classes.message }, { children: [audience === AudienceTypes.AUDIENCE_ALL && !taggingRequiredEnabled && ((0, jsx_runtime_1.jsx)(react_intl_1.FormattedMessage, { id: "ui.composer.layer.audience.all.message", defaultMessage: "ui.composer.layer.audience.all.message" })), audience === AudienceTypes.AUDIENCE_EVENT && ((0, jsx_runtime_1.jsx)(react_intl_1.FormattedMessage, { id: "ui.composer.layer.audience.event.message", defaultMessage: "ui.composer.layer.audience.event.message" })), audience === AudienceTypes.AUDIENCE_GROUP && ((0, jsx_runtime_1.jsx)(react_intl_1.FormattedMessage, { id: "ui.composer.layer.audience.group.message", defaultMessage: "ui.composer.layer.audience.group.message" })), audience === AudienceTypes.AUDIENCE_TAG && ((0, jsx_runtime_1.jsx)(react_intl_1.FormattedMessage, { id: "ui.composer.layer.audience.tag.message", defaultMessage: "ui.composer.layer.audience.tag.message" })), audience === AudienceTypes.AUDIENCE_USERS && ((0, jsx_runtime_1.jsx)(react_intl_1.FormattedMessage, { id: "ui.composer.layer.audience.users.message", defaultMessage: "ui.composer.layer.audience.users.message" }))] })), audience === AudienceTypes.AUDIENCE_TAG && ((0, jsx_runtime_1.jsx)(material_1.Autocomplete, { className: classes.autocomplete, open: autocompleteOpen, onOpen: handleAutocompleteOpen, onClose: handleAutocompleteClose, multiple: true, options: scAddressingTags || [], getOptionLabel: (option) => option.name || '', value: value, selectOnFocus: true, clearOnBlur: true, handleHomeEndKeys: true, clearIcon: null, noOptionsText: (0, jsx_runtime_1.jsx)(react_intl_1.FormattedMessage, { id: "ui.composer.layer.audience.tags.empty", defaultMessage: "ui.composer.layer.audience.tags.empty" }), onChange: handleChange, isOptionEqualToValue: (option, value) => value.id === option.id, renderTags: (value, getTagProps) => {
105
+ const isAudienceTypeSelected = (type) => {
106
+ // "ALL" tab is never disabled
107
+ if (type === AudienceTypes.AUDIENCE_ALL)
108
+ return false;
109
+ // Empty value nothing is disabled
110
+ if (!value || (Array.isArray(value) && value.length === 0))
111
+ return false;
112
+ const valueType = (() => {
113
+ if (Array.isArray(value)) {
114
+ if (value.some((v) => 'color' in v))
115
+ return AudienceTypes.AUDIENCE_TAG;
116
+ return AudienceTypes.AUDIENCE_USERS;
117
+ }
118
+ else {
119
+ if ('recurring' in value)
120
+ return AudienceTypes.AUDIENCE_EVENT;
121
+ if ('managed_by' in value)
122
+ return AudienceTypes.AUDIENCE_GROUP;
123
+ return AudienceTypes.AUDIENCE_USERS;
124
+ }
125
+ })();
126
+ // Disable all tabs except the currently selected type
127
+ return type !== valueType;
128
+ };
129
+ return ((0, jsx_runtime_1.jsxs)(Root, Object.assign({ ref: ref, className: (0, classnames_1.default)(className, classes.root) }, rest, { children: [(0, jsx_runtime_1.jsxs)(material_1.DialogTitle, Object.assign({ className: classes.title }, { children: [(0, jsx_runtime_1.jsx)(material_1.IconButton, Object.assign({ onClick: onClose }, { children: (0, jsx_runtime_1.jsx)(material_1.Icon, { children: "arrow_back" }) })), (0, jsx_runtime_1.jsx)(material_1.Typography, { children: (0, jsx_runtime_1.jsx)(react_intl_1.FormattedMessage, { id: "ui.composer.layer.audience.title", defaultMessage: "ui.composer.layer.audience.title" }) }), (0, jsx_runtime_1.jsx)(material_1.Button, Object.assign({ size: "small", color: "secondary", variant: "contained", onClick: handleSave }, { children: (0, jsx_runtime_1.jsx)(react_intl_1.FormattedMessage, { id: "ui.composer.layer.save", defaultMessage: "ui.composer.layer.save" }) }))] })), (0, jsx_runtime_1.jsxs)(material_1.DialogContent, Object.assign({ className: classes.content }, { children: [(0, jsx_runtime_1.jsxs)(material_1.Tabs, Object.assign({ value: audience, onChange: handleChangeAudience, "aria-label": "audience type" }, { children: [!taggingRequiredEnabled && ((0, jsx_runtime_1.jsx)(material_1.Tab, { value: AudienceTypes.AUDIENCE_ALL, icon: (0, jsx_runtime_1.jsx)(material_1.Icon, { children: "public" }), label: (0, jsx_runtime_1.jsx)(react_intl_1.FormattedMessage, { id: "ui.composer.layer.audience.all", defaultMessage: "ui.composer.layer.audience.all" }) })), eventsEnabled && ((0, jsx_runtime_1.jsx)(material_1.Tab, { disabled: isAudienceTypeSelected(AudienceTypes.AUDIENCE_EVENT), value: AudienceTypes.AUDIENCE_EVENT, icon: (0, jsx_runtime_1.jsx)(material_1.Icon, { children: "CalendarIcon" }), label: (0, jsx_runtime_1.jsx)(react_intl_1.FormattedMessage, { id: "ui.composer.layer.audience.event", defaultMessage: "ui.composer.layer.audience.event" }) })), groupsEnabled && ((0, jsx_runtime_1.jsx)(material_1.Tab, { disabled: isAudienceTypeSelected(AudienceTypes.AUDIENCE_GROUP), value: AudienceTypes.AUDIENCE_GROUP, icon: (0, jsx_runtime_1.jsx)(material_1.Icon, { children: "groups" }), label: (0, jsx_runtime_1.jsx)(react_intl_1.FormattedMessage, { id: "ui.composer.layer.audience.group", defaultMessage: "ui.composer.layer.audience.group" }) })), (0, jsx_runtime_1.jsx)(material_1.Tab, { disabled: isAudienceTypeSelected(AudienceTypes.AUDIENCE_TAG), value: AudienceTypes.AUDIENCE_TAG, icon: (0, jsx_runtime_1.jsx)(material_1.Icon, { children: "label" }), label: (0, jsx_runtime_1.jsx)(react_intl_1.FormattedMessage, { id: "ui.composer.layer.audience.tag", defaultMessage: "ui.composer.layer.audience.tag" }) }), usersTaggingEnabled && ((0, jsx_runtime_1.jsx)(material_1.Tab, { disabled: isAudienceTypeSelected(AudienceTypes.AUDIENCE_USERS), value: AudienceTypes.AUDIENCE_USERS, icon: (0, jsx_runtime_1.jsx)(material_1.Icon, { children: "people_alt" }), label: (0, jsx_runtime_1.jsx)(react_intl_1.FormattedMessage, { id: "ui.composer.layer.audience.users", defaultMessage: "ui.composer.layer.audience.users" }) }))] })), (0, jsx_runtime_1.jsxs)(material_1.Typography, Object.assign({ className: classes.message }, { children: [audience === AudienceTypes.AUDIENCE_ALL && !taggingRequiredEnabled && ((0, jsx_runtime_1.jsx)(react_intl_1.FormattedMessage, { id: "ui.composer.layer.audience.all.message", defaultMessage: "ui.composer.layer.audience.all.message" })), audience === AudienceTypes.AUDIENCE_EVENT && ((0, jsx_runtime_1.jsx)(react_intl_1.FormattedMessage, { id: "ui.composer.layer.audience.event.message", defaultMessage: "ui.composer.layer.audience.event.message" })), audience === AudienceTypes.AUDIENCE_GROUP && ((0, jsx_runtime_1.jsx)(react_intl_1.FormattedMessage, { id: "ui.composer.layer.audience.group.message", defaultMessage: "ui.composer.layer.audience.group.message" })), audience === AudienceTypes.AUDIENCE_TAG && ((0, jsx_runtime_1.jsx)(react_intl_1.FormattedMessage, { id: "ui.composer.layer.audience.tag.message", defaultMessage: "ui.composer.layer.audience.tag.message" })), audience === AudienceTypes.AUDIENCE_USERS && ((0, jsx_runtime_1.jsx)(react_intl_1.FormattedMessage, { id: "ui.composer.layer.audience.users.message", defaultMessage: "ui.composer.layer.audience.users.message" }))] })), audience === AudienceTypes.AUDIENCE_TAG && ((0, jsx_runtime_1.jsx)(material_1.Autocomplete, { className: classes.autocomplete, open: autocompleteOpen, onOpen: handleAutocompleteOpen, onClose: handleAutocompleteClose, multiple: true, options: scAddressingTags || [], getOptionLabel: (option) => option.name || '', value: value, selectOnFocus: true, clearOnBlur: true, handleHomeEndKeys: true, clearIcon: null, noOptionsText: (0, jsx_runtime_1.jsx)(react_intl_1.FormattedMessage, { id: "ui.composer.layer.audience.tags.empty", defaultMessage: "ui.composer.layer.audience.tags.empty" }), onChange: handleChange, isOptionEqualToValue: (option, value) => value.id === option.id, renderTags: (value, getTagProps) => {
117
130
  return value.map((option, index) => (0, jsx_runtime_1.jsx)(TagChip_1.default, Object.assign({ tag: option }, getTagProps({ index })), option.id));
118
131
  }, renderOption: (props, option, { selected, inputValue }) => {
119
132
  const matches = (0, match_1.default)(option.name, inputValue);
@@ -46,16 +46,18 @@ const UserAutocomplete = (inProps) => {
46
46
  .filter(Boolean)
47
47
  .join(','), [value]);
48
48
  // Fetch users excluding selected ones
49
- const { users, isLoading } = (0, react_core_1.useSCFetchUsers)({ search: inputValue, exclude: excludeIds });
49
+ const { users, isLoading } = (0, react_core_1.useSCFetchUsers)({ search: (inputValue === null || inputValue === void 0 ? void 0 : inputValue.length) >= 3 ? inputValue : '', exclude: excludeIds });
50
50
  const filteredUsers = users.filter((u) => !excludeIds.includes(u.id));
51
51
  (0, react_1.useEffect)(() => {
52
52
  onChange === null || onChange === void 0 ? void 0 : onChange(value);
53
53
  setTextAreaValue(value.map((u) => (typeof u === 'object' ? u.username : u)).join('\n'));
54
54
  }, [value]);
55
55
  // Handlers
56
- const handleOpen = () => {
57
- filteredUsers.length > 0 && setOpen(true);
58
- };
56
+ const handleOpen = (0, react_1.useCallback)(() => {
57
+ if (filteredUsers.length > 0) {
58
+ setOpen(true);
59
+ }
60
+ }, [filteredUsers]);
59
61
  const handleClose = () => {
60
62
  setOpen(false);
61
63
  };
@@ -67,24 +69,37 @@ const UserAutocomplete = (inProps) => {
67
69
  .split(/\s|,|\n/)
68
70
  .map((s) => s.trim())
69
71
  .filter(Boolean)));
70
- let resolvedUsers = [];
71
72
  try {
72
73
  if (names.length > 0) {
73
74
  const resp = yield api_services_1.UserService.matchUsernames(names);
74
- const matchedMap = new Map(resp.map((u) => [u.username, u]));
75
- resolvedUsers = names.map((name) => matchedMap.get(name) || name);
75
+ const resolvedUsernames = new Set(resp.map((u) => u.username));
76
+ const resolvedUsers = resp.filter((u) => resolvedUsernames.has(u.username));
77
+ setValue(resolvedUsers);
78
+ setTextAreaValue(resolvedUsers.map((u) => u.username).join('\n'));
79
+ }
80
+ else {
81
+ setValue([]);
82
+ setTextAreaValue('');
76
83
  }
77
84
  }
78
85
  catch (err) {
79
- console.error(`Failed fetching users`, err);
80
- resolvedUsers = names;
86
+ console.error(`Failed fetching matching usernames`, err);
87
+ setValue([]);
88
+ setTextAreaValue('');
81
89
  }
82
- setValue(resolvedUsers);
83
- setTextAreaValue(e.target.value);
84
90
  });
85
- return ((0, jsx_runtime_1.jsxs)(Root, Object.assign({ className: classes.root }, { children: [(0, jsx_runtime_1.jsx)(AutocompleteRoot, Object.assign({ multiple: true, className: classes.autocompleteRoot, open: open, onOpen: handleOpen, onClose: handleClose, options: filteredUsers || [], getOptionLabel: (option) => option.username || '', value: value, inputValue: inputValue, onInputChange: (_event, newInputValue) => {
91
+ const filterOptions = (0, react_1.useCallback)((options, state) => {
92
+ const search = state.inputValue.toLowerCase();
93
+ return options.filter((option) => {
94
+ var _a, _b;
95
+ const usernameMatch = (_a = option.username) === null || _a === void 0 ? void 0 : _a.toLowerCase().includes(search);
96
+ const nameMatch = (_b = option.real_name) === null || _b === void 0 ? void 0 : _b.toLowerCase().includes(search);
97
+ return usernameMatch || nameMatch;
98
+ });
99
+ }, []);
100
+ return ((0, jsx_runtime_1.jsxs)(Root, Object.assign({ className: classes.root }, { children: [(0, jsx_runtime_1.jsx)(AutocompleteRoot, Object.assign({ multiple: true, className: classes.autocompleteRoot, open: open, onOpen: handleOpen, onClose: handleClose, options: filteredUsers || [], filterOptions: filterOptions, getOptionLabel: (option) => option.username || '', value: value, inputValue: inputValue, onInputChange: (_event, newInputValue) => {
86
101
  setInputValue(newInputValue);
87
- }, selectOnFocus: true, clearOnBlur: true, blurOnSelect: true, handleHomeEndKeys: true, clearIcon: null, disabled: disabled || isLoading, noOptionsText: (0, jsx_runtime_1.jsx)(react_intl_1.FormattedMessage, { id: "ui.userAutocomplete.empty", defaultMessage: "ui.userAutocomplete.empty" }), onChange: handleChange, isOptionEqualToValue: (option, val) => option.id === val.id, renderTags: (value, getTagProps) => value.map((option, index) => {
102
+ }, selectOnFocus: true, clearOnBlur: true, blurOnSelect: true, handleHomeEndKeys: true, clearIcon: null, disabled: disabled || isLoading, noOptionsText: (0, jsx_runtime_1.jsx)(react_intl_1.FormattedMessage, { id: "ui.userAutocomplete.empty", defaultMessage: "ui.userAutocomplete.empty" }), onChange: handleChange, isOptionEqualToValue: (option, val) => (option ? val.id === option.id : false), renderTags: (value, getTagProps) => value.map((option, index) => {
88
103
  const username = typeof option === 'string' ? option : option.username;
89
104
  const avatar = typeof option === 'string' ? '' : option.avatar;
90
105
  const id = typeof option === 'string' ? `fallback-${option}` : option.id;
@@ -92,8 +107,8 @@ const UserAutocomplete = (inProps) => {
92
107
  }), renderOption: (props, option, { inputValue }) => {
93
108
  const matches = (0, match_1.default)(option.username, inputValue);
94
109
  const parts = (0, parse_1.default)(option.username, matches);
95
- return ((0, jsx_runtime_1.jsxs)(material_1.Box, Object.assign({ component: "li" }, props, { children: [(0, jsx_runtime_1.jsx)(material_1.Avatar, { alt: option.username, src: option.avatar, sx: { marginRight: 1 } }), (0, jsx_runtime_1.jsx)(react_1.default.Fragment, { children: parts.map((part, index) => ((0, jsx_runtime_1.jsx)(material_1.Typography, Object.assign({ sx: { fontWeight: part.highlight ? 700 : 400 } }, { children: part.text }), index))) })] })));
96
- }, renderInput: (params) => ((0, jsx_runtime_1.jsx)(material_1.TextField, Object.assign({}, params, TextFieldProps, { margin: "dense", InputProps: Object.assign(Object.assign({}, params.InputProps), { autoComplete: 'off', endAdornment: ((0, jsx_runtime_1.jsxs)(react_1.Fragment, { children: [isLoading ? (0, jsx_runtime_1.jsx)(material_1.CircularProgress, { color: "inherit", size: 20 }) : null, params.InputProps.endAdornment] })) }) }))) }, rest)), (0, jsx_runtime_1.jsx)(material_1.Typography, Object.assign({ variant: "body2", color: "primary", className: classes.info }, { children: (0, jsx_runtime_1.jsx)(react_intl_1.FormattedMessage, { id: "ui.userAutocomplete.textarea.info", defaultMessage: "ui.userAutocomplete.textarea.info", values: {
110
+ return ((0, jsx_runtime_1.jsxs)(material_1.Box, Object.assign({ component: "li" }, props, { children: [(0, jsx_runtime_1.jsx)(material_1.Avatar, { alt: option.username, src: option.avatar, sx: { marginRight: 1 } }), (0, jsx_runtime_1.jsx)(react_1.default.Fragment, { children: parts.map((part, index) => ((0, jsx_runtime_1.jsx)(material_1.Typography, { children: part.text }, index))) })] })));
111
+ }, renderInput: (params) => ((0, jsx_runtime_1.jsx)(material_1.TextField, Object.assign({}, params, TextFieldProps, { margin: "dense", InputProps: Object.assign(Object.assign({}, params.InputProps), { autoComplete: 'off', endAdornment: filteredUsers.length > 0 ? ((0, jsx_runtime_1.jsxs)(react_1.Fragment, { children: [isLoading && (0, jsx_runtime_1.jsx)(material_1.CircularProgress, { color: "inherit", size: 20 }), params.InputProps.endAdornment] })) : null }) }))) }, rest)), (0, jsx_runtime_1.jsx)(material_1.Typography, Object.assign({ variant: "body2", color: "primary", className: classes.info }, { children: (0, jsx_runtime_1.jsx)(react_intl_1.FormattedMessage, { id: "ui.userAutocomplete.textarea.info", defaultMessage: "ui.userAutocomplete.textarea.info", values: {
97
112
  // eslint-disable-next-line @typescript-eslint/ban-ts-ignore
98
113
  // @ts-ignore
99
114
  icon: (...chunks) => (0, jsx_runtime_1.jsx)(material_1.Icon, { children: chunks })
@@ -99,18 +99,31 @@ const AudienceLayer = React.forwardRef((props, ref) => {
99
99
  const handleChangeAudience = useCallback((_event, data) => setAudience(data), []);
100
100
  const handleAutocompleteOpen = useCallback(() => setAutocompleteOpen(true), []);
101
101
  const handleAutocompleteClose = useCallback(() => setAutocompleteOpen(false), []);
102
- return (_jsxs(Root, Object.assign({ ref: ref, className: classNames(className, classes.root) }, rest, { children: [_jsxs(DialogTitle, Object.assign({ className: classes.title }, { children: [_jsx(IconButton, Object.assign({ onClick: onClose }, { children: _jsx(Icon, { children: "arrow_back" }) })), _jsx(Typography, { children: _jsx(FormattedMessage, { id: "ui.composer.layer.audience.title", defaultMessage: "ui.composer.layer.audience.title" }) }), _jsx(Button, Object.assign({ size: "small", color: "secondary", variant: "contained", onClick: handleSave }, { children: _jsx(FormattedMessage, { id: "ui.composer.layer.save", defaultMessage: "ui.composer.layer.save" }) }))] })), _jsxs(DialogContent, Object.assign({ className: classes.content }, { children: [_jsxs(Tabs, Object.assign({ value: audience, onChange: handleChangeAudience, "aria-label": "audience type" }, { children: [!taggingRequiredEnabled && (_jsx(Tab, { value: AudienceTypes.AUDIENCE_ALL, icon: _jsx(Icon, { children: "public" }), label: _jsx(FormattedMessage, { id: "ui.composer.layer.audience.all", defaultMessage: "ui.composer.layer.audience.all" }) })), eventsEnabled && (_jsx(Tab, { disabled: (Boolean(value === null || value === void 0 ? void 0 : value.length) && !Object.prototype.hasOwnProperty.call(value, 'recurring')) ||
103
- (value !== undefined && Boolean(!(value === null || value === void 0 ? void 0 : value.length)) && audience !== AudienceTypes.AUDIENCE_ALL) ||
104
- (Boolean((value === null || value === void 0 ? void 0 : value.length) === 0) && audience === AudienceTypes.AUDIENCE_ALL && Boolean((defaultValue === null || defaultValue === void 0 ? void 0 : defaultValue.length) !== 0)), value: AudienceTypes.AUDIENCE_EVENT, icon: _jsx(Icon, { children: "CalendarIcon" }), label: _jsx(FormattedMessage, { id: "ui.composer.layer.audience.event", defaultMessage: "ui.composer.layer.audience.event" }) })), groupsEnabled && (_jsx(Tab, { disabled: (Boolean(value === null || value === void 0 ? void 0 : value.length) && !Object.prototype.hasOwnProperty.call(value, 'managed_by')) ||
105
- (value !== undefined && Boolean(!(value === null || value === void 0 ? void 0 : value.length)) && audience !== AudienceTypes.AUDIENCE_ALL) ||
106
- (Boolean((value === null || value === void 0 ? void 0 : value.length) === 0) && audience === AudienceTypes.AUDIENCE_ALL && Boolean((defaultValue === null || defaultValue === void 0 ? void 0 : defaultValue.length) !== 0)), value: AudienceTypes.AUDIENCE_GROUP, icon: _jsx(Icon, { children: "groups" }), label: _jsx(FormattedMessage, { id: "ui.composer.layer.audience.group", defaultMessage: "ui.composer.layer.audience.group" }) })), _jsx(Tab, { disabled: value &&
107
- ((Array.isArray(value) && (value.some((v) => v === null || v === void 0 ? void 0 : v.username) || value.some((v) => typeof v === 'string'))) ||
108
- (!Array.isArray(value) && Object.prototype.hasOwnProperty.call(value, 'managed_by'))) // group object
109
- , value: AudienceTypes.AUDIENCE_TAG, icon: _jsx(Icon, { children: "label" }), label: _jsx(FormattedMessage, { id: "ui.composer.layer.audience.tag", defaultMessage: "ui.composer.layer.audience.tag" }) }), usersTaggingEnabled && (_jsx(Tab, { disabled: value &&
110
- ((Array.isArray(value) && value.length > 0 && !value.some((v) => v === null || v === void 0 ? void 0 : v.username) && !value.every((v) => typeof v === 'string')) ||
111
- (!Array.isArray(value) &&
112
- Object.keys(value).length > 0 &&
113
- (Object.prototype.hasOwnProperty.call(value, 'managed_by') || Object.prototype.hasOwnProperty.call(value, 'recurring')))), value: AudienceTypes.AUDIENCE_USERS, icon: _jsx(Icon, { children: "people_alt" }), label: _jsx(FormattedMessage, { id: "ui.composer.layer.audience.users", defaultMessage: "ui.composer.layer.audience.users" }) }))] })), _jsxs(Typography, Object.assign({ className: classes.message }, { children: [audience === AudienceTypes.AUDIENCE_ALL && !taggingRequiredEnabled && (_jsx(FormattedMessage, { id: "ui.composer.layer.audience.all.message", defaultMessage: "ui.composer.layer.audience.all.message" })), audience === AudienceTypes.AUDIENCE_EVENT && (_jsx(FormattedMessage, { id: "ui.composer.layer.audience.event.message", defaultMessage: "ui.composer.layer.audience.event.message" })), audience === AudienceTypes.AUDIENCE_GROUP && (_jsx(FormattedMessage, { id: "ui.composer.layer.audience.group.message", defaultMessage: "ui.composer.layer.audience.group.message" })), audience === AudienceTypes.AUDIENCE_TAG && (_jsx(FormattedMessage, { id: "ui.composer.layer.audience.tag.message", defaultMessage: "ui.composer.layer.audience.tag.message" })), audience === AudienceTypes.AUDIENCE_USERS && (_jsx(FormattedMessage, { id: "ui.composer.layer.audience.users.message", defaultMessage: "ui.composer.layer.audience.users.message" }))] })), audience === AudienceTypes.AUDIENCE_TAG && (_jsx(Autocomplete, { className: classes.autocomplete, open: autocompleteOpen, onOpen: handleAutocompleteOpen, onClose: handleAutocompleteClose, multiple: true, options: scAddressingTags || [], getOptionLabel: (option) => option.name || '', value: value, selectOnFocus: true, clearOnBlur: true, handleHomeEndKeys: true, clearIcon: null, noOptionsText: _jsx(FormattedMessage, { id: "ui.composer.layer.audience.tags.empty", defaultMessage: "ui.composer.layer.audience.tags.empty" }), onChange: handleChange, isOptionEqualToValue: (option, value) => value.id === option.id, renderTags: (value, getTagProps) => {
102
+ const isAudienceTypeSelected = (type) => {
103
+ // "ALL" tab is never disabled
104
+ if (type === AudienceTypes.AUDIENCE_ALL)
105
+ return false;
106
+ // Empty value nothing is disabled
107
+ if (!value || (Array.isArray(value) && value.length === 0))
108
+ return false;
109
+ const valueType = (() => {
110
+ if (Array.isArray(value)) {
111
+ if (value.some((v) => 'color' in v))
112
+ return AudienceTypes.AUDIENCE_TAG;
113
+ return AudienceTypes.AUDIENCE_USERS;
114
+ }
115
+ else {
116
+ if ('recurring' in value)
117
+ return AudienceTypes.AUDIENCE_EVENT;
118
+ if ('managed_by' in value)
119
+ return AudienceTypes.AUDIENCE_GROUP;
120
+ return AudienceTypes.AUDIENCE_USERS;
121
+ }
122
+ })();
123
+ // Disable all tabs except the currently selected type
124
+ return type !== valueType;
125
+ };
126
+ return (_jsxs(Root, Object.assign({ ref: ref, className: classNames(className, classes.root) }, rest, { children: [_jsxs(DialogTitle, Object.assign({ className: classes.title }, { children: [_jsx(IconButton, Object.assign({ onClick: onClose }, { children: _jsx(Icon, { children: "arrow_back" }) })), _jsx(Typography, { children: _jsx(FormattedMessage, { id: "ui.composer.layer.audience.title", defaultMessage: "ui.composer.layer.audience.title" }) }), _jsx(Button, Object.assign({ size: "small", color: "secondary", variant: "contained", onClick: handleSave }, { children: _jsx(FormattedMessage, { id: "ui.composer.layer.save", defaultMessage: "ui.composer.layer.save" }) }))] })), _jsxs(DialogContent, Object.assign({ className: classes.content }, { children: [_jsxs(Tabs, Object.assign({ value: audience, onChange: handleChangeAudience, "aria-label": "audience type" }, { children: [!taggingRequiredEnabled && (_jsx(Tab, { value: AudienceTypes.AUDIENCE_ALL, icon: _jsx(Icon, { children: "public" }), label: _jsx(FormattedMessage, { id: "ui.composer.layer.audience.all", defaultMessage: "ui.composer.layer.audience.all" }) })), eventsEnabled && (_jsx(Tab, { disabled: isAudienceTypeSelected(AudienceTypes.AUDIENCE_EVENT), value: AudienceTypes.AUDIENCE_EVENT, icon: _jsx(Icon, { children: "CalendarIcon" }), label: _jsx(FormattedMessage, { id: "ui.composer.layer.audience.event", defaultMessage: "ui.composer.layer.audience.event" }) })), groupsEnabled && (_jsx(Tab, { disabled: isAudienceTypeSelected(AudienceTypes.AUDIENCE_GROUP), value: AudienceTypes.AUDIENCE_GROUP, icon: _jsx(Icon, { children: "groups" }), label: _jsx(FormattedMessage, { id: "ui.composer.layer.audience.group", defaultMessage: "ui.composer.layer.audience.group" }) })), _jsx(Tab, { disabled: isAudienceTypeSelected(AudienceTypes.AUDIENCE_TAG), value: AudienceTypes.AUDIENCE_TAG, icon: _jsx(Icon, { children: "label" }), label: _jsx(FormattedMessage, { id: "ui.composer.layer.audience.tag", defaultMessage: "ui.composer.layer.audience.tag" }) }), usersTaggingEnabled && (_jsx(Tab, { disabled: isAudienceTypeSelected(AudienceTypes.AUDIENCE_USERS), value: AudienceTypes.AUDIENCE_USERS, icon: _jsx(Icon, { children: "people_alt" }), label: _jsx(FormattedMessage, { id: "ui.composer.layer.audience.users", defaultMessage: "ui.composer.layer.audience.users" }) }))] })), _jsxs(Typography, Object.assign({ className: classes.message }, { children: [audience === AudienceTypes.AUDIENCE_ALL && !taggingRequiredEnabled && (_jsx(FormattedMessage, { id: "ui.composer.layer.audience.all.message", defaultMessage: "ui.composer.layer.audience.all.message" })), audience === AudienceTypes.AUDIENCE_EVENT && (_jsx(FormattedMessage, { id: "ui.composer.layer.audience.event.message", defaultMessage: "ui.composer.layer.audience.event.message" })), audience === AudienceTypes.AUDIENCE_GROUP && (_jsx(FormattedMessage, { id: "ui.composer.layer.audience.group.message", defaultMessage: "ui.composer.layer.audience.group.message" })), audience === AudienceTypes.AUDIENCE_TAG && (_jsx(FormattedMessage, { id: "ui.composer.layer.audience.tag.message", defaultMessage: "ui.composer.layer.audience.tag.message" })), audience === AudienceTypes.AUDIENCE_USERS && (_jsx(FormattedMessage, { id: "ui.composer.layer.audience.users.message", defaultMessage: "ui.composer.layer.audience.users.message" }))] })), audience === AudienceTypes.AUDIENCE_TAG && (_jsx(Autocomplete, { className: classes.autocomplete, open: autocompleteOpen, onOpen: handleAutocompleteOpen, onClose: handleAutocompleteClose, multiple: true, options: scAddressingTags || [], getOptionLabel: (option) => option.name || '', value: value, selectOnFocus: true, clearOnBlur: true, handleHomeEndKeys: true, clearIcon: null, noOptionsText: _jsx(FormattedMessage, { id: "ui.composer.layer.audience.tags.empty", defaultMessage: "ui.composer.layer.audience.tags.empty" }), onChange: handleChange, isOptionEqualToValue: (option, value) => value.id === option.id, renderTags: (value, getTagProps) => {
114
127
  return value.map((option, index) => _jsx(TagChip, Object.assign({ tag: option }, getTagProps({ index })), option.id));
115
128
  }, renderOption: (props, option, { selected, inputValue }) => {
116
129
  const matches = match(option.name, inputValue);
@@ -1,6 +1,6 @@
1
1
  import { __awaiter, __rest } from "tslib";
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
- import React, { Fragment, useEffect, useMemo, useState } from 'react';
3
+ import React, { Fragment, useCallback, useEffect, useMemo, useState } from 'react';
4
4
  import { FormattedMessage } from 'react-intl';
5
5
  import parse from 'autosuggest-highlight/parse';
6
6
  import match from 'autosuggest-highlight/match';
@@ -44,16 +44,18 @@ const UserAutocomplete = (inProps) => {
44
44
  .filter(Boolean)
45
45
  .join(','), [value]);
46
46
  // Fetch users excluding selected ones
47
- const { users, isLoading } = useSCFetchUsers({ search: inputValue, exclude: excludeIds });
47
+ const { users, isLoading } = useSCFetchUsers({ search: (inputValue === null || inputValue === void 0 ? void 0 : inputValue.length) >= 3 ? inputValue : '', exclude: excludeIds });
48
48
  const filteredUsers = users.filter((u) => !excludeIds.includes(u.id));
49
49
  useEffect(() => {
50
50
  onChange === null || onChange === void 0 ? void 0 : onChange(value);
51
51
  setTextAreaValue(value.map((u) => (typeof u === 'object' ? u.username : u)).join('\n'));
52
52
  }, [value]);
53
53
  // Handlers
54
- const handleOpen = () => {
55
- filteredUsers.length > 0 && setOpen(true);
56
- };
54
+ const handleOpen = useCallback(() => {
55
+ if (filteredUsers.length > 0) {
56
+ setOpen(true);
57
+ }
58
+ }, [filteredUsers]);
57
59
  const handleClose = () => {
58
60
  setOpen(false);
59
61
  };
@@ -65,24 +67,37 @@ const UserAutocomplete = (inProps) => {
65
67
  .split(/\s|,|\n/)
66
68
  .map((s) => s.trim())
67
69
  .filter(Boolean)));
68
- let resolvedUsers = [];
69
70
  try {
70
71
  if (names.length > 0) {
71
72
  const resp = yield UserService.matchUsernames(names);
72
- const matchedMap = new Map(resp.map((u) => [u.username, u]));
73
- resolvedUsers = names.map((name) => matchedMap.get(name) || name);
73
+ const resolvedUsernames = new Set(resp.map((u) => u.username));
74
+ const resolvedUsers = resp.filter((u) => resolvedUsernames.has(u.username));
75
+ setValue(resolvedUsers);
76
+ setTextAreaValue(resolvedUsers.map((u) => u.username).join('\n'));
77
+ }
78
+ else {
79
+ setValue([]);
80
+ setTextAreaValue('');
74
81
  }
75
82
  }
76
83
  catch (err) {
77
- console.error(`Failed fetching users`, err);
78
- resolvedUsers = names;
84
+ console.error(`Failed fetching matching usernames`, err);
85
+ setValue([]);
86
+ setTextAreaValue('');
79
87
  }
80
- setValue(resolvedUsers);
81
- setTextAreaValue(e.target.value);
82
88
  });
83
- return (_jsxs(Root, Object.assign({ className: classes.root }, { children: [_jsx(AutocompleteRoot, Object.assign({ multiple: true, className: classes.autocompleteRoot, open: open, onOpen: handleOpen, onClose: handleClose, options: filteredUsers || [], getOptionLabel: (option) => option.username || '', value: value, inputValue: inputValue, onInputChange: (_event, newInputValue) => {
89
+ const filterOptions = useCallback((options, state) => {
90
+ const search = state.inputValue.toLowerCase();
91
+ return options.filter((option) => {
92
+ var _a, _b;
93
+ const usernameMatch = (_a = option.username) === null || _a === void 0 ? void 0 : _a.toLowerCase().includes(search);
94
+ const nameMatch = (_b = option.real_name) === null || _b === void 0 ? void 0 : _b.toLowerCase().includes(search);
95
+ return usernameMatch || nameMatch;
96
+ });
97
+ }, []);
98
+ return (_jsxs(Root, Object.assign({ className: classes.root }, { children: [_jsx(AutocompleteRoot, Object.assign({ multiple: true, className: classes.autocompleteRoot, open: open, onOpen: handleOpen, onClose: handleClose, options: filteredUsers || [], filterOptions: filterOptions, getOptionLabel: (option) => option.username || '', value: value, inputValue: inputValue, onInputChange: (_event, newInputValue) => {
84
99
  setInputValue(newInputValue);
85
- }, selectOnFocus: true, clearOnBlur: true, blurOnSelect: true, handleHomeEndKeys: true, clearIcon: null, disabled: disabled || isLoading, noOptionsText: _jsx(FormattedMessage, { id: "ui.userAutocomplete.empty", defaultMessage: "ui.userAutocomplete.empty" }), onChange: handleChange, isOptionEqualToValue: (option, val) => option.id === val.id, renderTags: (value, getTagProps) => value.map((option, index) => {
100
+ }, selectOnFocus: true, clearOnBlur: true, blurOnSelect: true, handleHomeEndKeys: true, clearIcon: null, disabled: disabled || isLoading, noOptionsText: _jsx(FormattedMessage, { id: "ui.userAutocomplete.empty", defaultMessage: "ui.userAutocomplete.empty" }), onChange: handleChange, isOptionEqualToValue: (option, val) => (option ? val.id === option.id : false), renderTags: (value, getTagProps) => value.map((option, index) => {
86
101
  const username = typeof option === 'string' ? option : option.username;
87
102
  const avatar = typeof option === 'string' ? '' : option.avatar;
88
103
  const id = typeof option === 'string' ? `fallback-${option}` : option.id;
@@ -90,8 +105,8 @@ const UserAutocomplete = (inProps) => {
90
105
  }), renderOption: (props, option, { inputValue }) => {
91
106
  const matches = match(option.username, inputValue);
92
107
  const parts = parse(option.username, matches);
93
- return (_jsxs(Box, Object.assign({ component: "li" }, props, { children: [_jsx(Avatar, { alt: option.username, src: option.avatar, sx: { marginRight: 1 } }), _jsx(React.Fragment, { children: parts.map((part, index) => (_jsx(Typography, Object.assign({ sx: { fontWeight: part.highlight ? 700 : 400 } }, { children: part.text }), index))) })] })));
94
- }, renderInput: (params) => (_jsx(TextField, Object.assign({}, params, TextFieldProps, { margin: "dense", InputProps: Object.assign(Object.assign({}, params.InputProps), { autoComplete: 'off', endAdornment: (_jsxs(Fragment, { children: [isLoading ? _jsx(CircularProgress, { color: "inherit", size: 20 }) : null, params.InputProps.endAdornment] })) }) }))) }, rest)), _jsx(Typography, Object.assign({ variant: "body2", color: "primary", className: classes.info }, { children: _jsx(FormattedMessage, { id: "ui.userAutocomplete.textarea.info", defaultMessage: "ui.userAutocomplete.textarea.info", values: {
108
+ return (_jsxs(Box, Object.assign({ component: "li" }, props, { children: [_jsx(Avatar, { alt: option.username, src: option.avatar, sx: { marginRight: 1 } }), _jsx(React.Fragment, { children: parts.map((part, index) => (_jsx(Typography, { children: part.text }, index))) })] })));
109
+ }, renderInput: (params) => (_jsx(TextField, Object.assign({}, params, TextFieldProps, { margin: "dense", InputProps: Object.assign(Object.assign({}, params.InputProps), { autoComplete: 'off', endAdornment: filteredUsers.length > 0 ? (_jsxs(Fragment, { children: [isLoading && _jsx(CircularProgress, { color: "inherit", size: 20 }), params.InputProps.endAdornment] })) : null }) }))) }, rest)), _jsx(Typography, Object.assign({ variant: "body2", color: "primary", className: classes.info }, { children: _jsx(FormattedMessage, { id: "ui.userAutocomplete.textarea.info", defaultMessage: "ui.userAutocomplete.textarea.info", values: {
95
110
  // eslint-disable-next-line @typescript-eslint/ban-ts-ignore
96
111
  // @ts-ignore
97
112
  icon: (...chunks) => _jsx(Icon, { children: chunks })