@selfcommunity/react-ui 0.11.0-alpha.94 → 0.11.0-alpha.96

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.
@@ -12,6 +12,7 @@ import NavigationSettingsIconButton from '../NavigationSettingsIconButton';
12
12
  import NavigationMenuIconButton from '../NavigationMenuIconButton';
13
13
  import { PREFIX } from './constants';
14
14
  import { SCFeatureName } from '@selfcommunity/types';
15
+ import { scroll } from 'seamless-scroll-polyfill';
15
16
  import ComposerIconButton from '../ComposerIconButton';
16
17
  const classes = {
17
18
  root: `${PREFIX}-root`,
@@ -99,13 +100,24 @@ export default function NavigationToolbarMobile(inProps) {
99
100
  const handleCloseSearch = useCallback(() => {
100
101
  setSearchOpen(false);
101
102
  }, [setSearchOpen]);
103
+ const handleClickHome = useCallback(() => {
104
+ if (onClickHome) {
105
+ onClickHome();
106
+ }
107
+ else {
108
+ const pathName = window.location.pathname;
109
+ if (pathName && (pathName === '/' || pathName === scRoutingContext.url(SCRoutes.HOME_ROUTE_NAME, {}))) {
110
+ scroll(window, { top: 0, behavior: 'smooth' });
111
+ }
112
+ }
113
+ }, [onClickHome]);
102
114
  // RENDER
103
115
  if (scUserContext.loading) {
104
116
  return _jsx(NavigationToolbarMobileSkeleton, {});
105
117
  }
106
118
  const _children = children || (_jsxs(_Fragment, { children: [_jsx(NavigationMenuIconButtonComponent, {}), _jsx(Link, Object.assign({ to: scRoutingContext.url(SCRoutes.HOME_ROUTE_NAME, {}), className: classNames(className, classes.logo, {
107
119
  [classes.logoFlex]: preferences[SCPreferences.CONFIGURATIONS_CUSTOM_NAVBAR_ITEM_URL].value
108
- }) }, (onClickHome && { onClick: onClickHome }), { children: !preserveDesktopLogo ? (_jsx("img", { src: preferences[SCPreferences.LOGO_NAVBAR_LOGO_MOBILE].value, alt: "logo" })) : (_jsx("img", { src: preferences[SCPreferences.LOGO_NAVBAR_LOGO].value, alt: "logo" })) })), preferences[SCPreferences.CONFIGURATIONS_CUSTOM_NAVBAR_ITEM_ENABLED].value && (_jsx(Link, Object.assign({ target: "blank", to: preferences[SCPreferences.CONFIGURATIONS_CUSTOM_NAVBAR_ITEM_URL].value, className: classes.customItem }, { children: _jsx("img", { src: preferences[SCPreferences.CONFIGURATIONS_CUSTOM_NAVBAR_ITEM_IMAGE].value, alt: "custom_item" }) })))] }));
120
+ }), onClick: handleClickHome }, { children: !preserveDesktopLogo ? (_jsx("img", { src: preferences[SCPreferences.LOGO_NAVBAR_LOGO_MOBILE].value, alt: "logo" })) : (_jsx("img", { src: preferences[SCPreferences.LOGO_NAVBAR_LOGO].value, alt: "logo" })) })), preferences[SCPreferences.CONFIGURATIONS_CUSTOM_NAVBAR_ITEM_ENABLED].value && (_jsx(Link, Object.assign({ target: "blank", to: preferences[SCPreferences.CONFIGURATIONS_CUSTOM_NAVBAR_ITEM_URL].value, className: classes.customItem }, { children: _jsx("img", { src: preferences[SCPreferences.CONFIGURATIONS_CUSTOM_NAVBAR_ITEM_IMAGE].value, alt: "custom_item" }) })))] }));
109
121
  return (_jsxs(Root, Object.assign({ className: classNames(className, classes.root) }, rest, { children: [_children, startActions, (contentAvailable || scUserContext.user) && !disableSearch && (_jsxs(_Fragment, { children: [_jsx(IconButton, Object.assign({ className: classes.search, onClick: handleOpenSearch }, { children: _jsx(Icon, { children: "search" }) })), _jsx(SearchDialog, { className: classes.searchDialog, fullScreen: true, open: searchOpen, SearchAutocompleteComponentProps: Object.assign(Object.assign({}, SearchAutocompleteComponentProps), { onClear: handleCloseSearch }) })] })), endActions, (!postOnlyStaffEnabled || UserUtils.isStaff(scUserContext.user) || UserUtils.isPublisher(scUserContext.user)) &&
110
122
  (scUserContext.user || contentAvailable) &&
111
123
  exploreStreamEnabled && _jsx(ComposerIconButton, Object.assign({}, ComposerIconButtonProps)), scUserContext.user && (groupsEnabled || eventsEnabled) && (_jsx(IconButton, Object.assign({ className: classes.notifications, component: Link, to: scRoutingContext.url(SCRoutes.USER_NOTIFICATIONS_ROUTE_NAME, {}) }, { children: _jsx(Badge, Object.assign({ badgeContent: scUserContext.user.unseen_notification_banners_counter + scUserContext.user.unseen_interactions_counter, color: "secondary" }, { children: _jsx(Icon, { children: "notifications_active" }) })) }))), scUserContext.user ? (_jsx(NavigationSettingsIconButtonComponent, { className: classes.settings })) : (_jsx(Button, Object.assign({ className: classes.login, color: "inherit", component: Link, to: scRoutingContext.url(SCRoutes.SIGNIN_ROUTE_NAME, {}) }, { children: _jsx(FormattedMessage, { id: "ui.appBar.navigation.login", defaultMessage: "ui.appBar.navigation.login" }) })))] })));
@@ -16,7 +16,6 @@ export interface UserAutocompleteProps extends Pick<AutocompleteProps<SCUserAuto
16
16
  * @default 0
17
17
  */
18
18
  limitCountGroups?: number;
19
- endpointQueryParams?: Record<string, string | number | boolean>;
20
19
  }
21
20
  declare const UserAutocomplete: (inProps: UserAutocompleteProps) => JSX.Element;
22
21
  export default UserAutocomplete;
@@ -1,12 +1,13 @@
1
- import { __rest } from "tslib";
1
+ import { __awaiter, __rest } from "tslib";
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
- import React, { Fragment, useEffect, useState } from 'react';
3
+ import React, { Fragment, 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';
7
7
  import { Autocomplete, Chip, TextField, CircularProgress, styled, Avatar, Box, Typography, Icon } from '@mui/material';
8
8
  import { useSCFetchUsers } from '@selfcommunity/react-core';
9
9
  import { useThemeProps } from '@mui/system';
10
+ import { UserService } from '@selfcommunity/api-services';
10
11
  const PREFIX = 'SCUserAutocomplete';
11
12
  const classes = {
12
13
  root: `${PREFIX}-root`,
@@ -37,15 +38,21 @@ const UserAutocomplete = (inProps) => {
37
38
  const [inputValue, setInputValue] = useState('');
38
39
  const [value, setValue] = useState(Array.isArray(defaultValue) ? defaultValue : defaultValue ? [defaultValue] : []);
39
40
  const [textAreaValue, setTextAreaValue] = useState('');
40
- // Fetch users
41
- const { users, isLoading } = useSCFetchUsers({ search: inputValue });
41
+ // Exclude selected users by ID
42
+ const excludeIds = useMemo(() => value
43
+ .map((u) => (typeof u === 'object' ? u.id : null))
44
+ .filter(Boolean)
45
+ .join(','), [value]);
46
+ // Fetch users excluding selected ones
47
+ const { users, isLoading } = useSCFetchUsers({ search: inputValue, exclude: excludeIds });
48
+ const filteredUsers = users.filter((u) => !excludeIds.includes(u.id));
42
49
  useEffect(() => {
43
50
  onChange === null || onChange === void 0 ? void 0 : onChange(value);
44
51
  setTextAreaValue(value.map((u) => (typeof u === 'object' ? u.username : u)).join('\n'));
45
52
  }, [value]);
46
53
  // Handlers
47
54
  const handleOpen = () => {
48
- users.length > 0 && setOpen(true);
55
+ filteredUsers.length > 0 && setOpen(true);
49
56
  };
50
57
  const handleClose = () => {
51
58
  setOpen(false);
@@ -53,26 +60,29 @@ const UserAutocomplete = (inProps) => {
53
60
  const handleChange = (_event, newValue) => {
54
61
  setValue(newValue);
55
62
  };
56
- const handleTextAreaChange = (e) => {
57
- const names = e.target.value
63
+ const handleTextAreaChange = (e) => __awaiter(void 0, void 0, void 0, function* () {
64
+ const names = Array.from(new Set(e.target.value
58
65
  .split(/\s|,|\n/)
59
66
  .map((s) => s.trim())
60
- .filter(Boolean);
61
- const matched = names.map((name) => {
62
- const user = users.find((u) => u.username === name);
63
- return user || name;
64
- });
65
- setValue(matched);
67
+ .filter(Boolean)));
68
+ let resolvedUsers = [];
69
+ try {
70
+ if (names.length > 0) {
71
+ const resp = yield UserService.matchUsernames(names.join(','));
72
+ const matchedMap = new Map(resp.map((u) => [u.username, u]));
73
+ resolvedUsers = names.map((name) => matchedMap.get(name) || name);
74
+ }
75
+ }
76
+ catch (err) {
77
+ console.error(`Failed fetching users`, err);
78
+ resolvedUsers = names;
79
+ }
80
+ setValue(resolvedUsers);
66
81
  setTextAreaValue(e.target.value);
67
- };
68
- 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: users || [], getOptionLabel: (option) => option.username || '', value: value, inputValue: inputValue, onInputChange: (_event, newInputValue) => {
82
+ });
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) => {
69
84
  setInputValue(newInputValue);
70
- }, 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) => {
71
- if (typeof val === 'string') {
72
- return option.username === val;
73
- }
74
- return option.id === val.id;
75
- }, renderTags: (value, getTagProps) => value.map((option, index) => {
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) => {
76
86
  const username = typeof option === 'string' ? option : option.username;
77
87
  const avatar = typeof option === 'string' ? '' : option.avatar;
78
88
  const id = typeof option === 'string' ? `fallback-${option}` : option.id;