@selfcommunity/react-ui 0.7.9-alpha.6 → 0.7.9-alpha.7
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.
- package/lib/cjs/components/ChangeGroupCover/ChangeGroupCover.js +24 -1
- package/lib/cjs/components/ChangeGroupPicture/ChangeGroupPicture.js +22 -4
- package/lib/cjs/components/GroupInviteButton/GroupInviteButton.js +3 -3
- package/lib/cjs/components/Groups/Groups.d.ts +13 -17
- package/lib/cjs/components/Groups/Groups.js +40 -93
- package/lib/esm/components/ChangeGroupCover/ChangeGroupCover.js +24 -1
- package/lib/esm/components/ChangeGroupPicture/ChangeGroupPicture.js +22 -4
- package/lib/esm/components/GroupInviteButton/GroupInviteButton.js +3 -3
- package/lib/esm/components/Groups/Groups.d.ts +13 -17
- package/lib/esm/components/Groups/Groups.js +45 -98
- package/lib/umd/react-ui.js +1 -1
- package/package.json +4 -4
|
@@ -25,6 +25,10 @@ const messages = (0, react_intl_1.defineMessages)({
|
|
|
25
25
|
errorLoadImage: {
|
|
26
26
|
id: 'ui.changeGroupCover.button.change.alertErrorImage',
|
|
27
27
|
defaultMessage: 'ui.changeGroupCover.button.change.alertErrorImage'
|
|
28
|
+
},
|
|
29
|
+
errorImageSize: {
|
|
30
|
+
id: 'ui.changeGroupCover.alert',
|
|
31
|
+
defaultMessage: 'ui.changeGroupCover.alert'
|
|
28
32
|
}
|
|
29
33
|
});
|
|
30
34
|
/**
|
|
@@ -75,7 +79,26 @@ function ChangeGroupCover(inProps) {
|
|
|
75
79
|
*/
|
|
76
80
|
const handleUpload = (event) => {
|
|
77
81
|
fileInput = event.target.files[0];
|
|
78
|
-
|
|
82
|
+
if (fileInput) {
|
|
83
|
+
const reader = new FileReader();
|
|
84
|
+
reader.onload = (e) => {
|
|
85
|
+
const img = new Image();
|
|
86
|
+
img.onload = () => {
|
|
87
|
+
if (img.width < 1920) {
|
|
88
|
+
setAlert(intl.formatMessage(messages.errorImageSize));
|
|
89
|
+
}
|
|
90
|
+
else {
|
|
91
|
+
isCreationMode ? onChange && onChange(fileInput) : handleSave();
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-ignore
|
|
95
|
+
// @ts-ignore
|
|
96
|
+
img.src = e.target.result;
|
|
97
|
+
};
|
|
98
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-ignore
|
|
99
|
+
// @ts-ignore
|
|
100
|
+
reader.readAsDataURL(fileInput);
|
|
101
|
+
}
|
|
79
102
|
};
|
|
80
103
|
/**
|
|
81
104
|
* Handles cover saving after upload action
|
|
@@ -16,8 +16,8 @@ const react_intl_1 = require("react-intl");
|
|
|
16
16
|
const lab_1 = require("@mui/lab");
|
|
17
17
|
const messages = (0, react_intl_1.defineMessages)({
|
|
18
18
|
errorLoadImage: {
|
|
19
|
-
id: 'ui.
|
|
20
|
-
defaultMessage: 'ui.
|
|
19
|
+
id: 'ui.changeGroupPicture.alert',
|
|
20
|
+
defaultMessage: 'ui.changeGroupPicture.alert'
|
|
21
21
|
}
|
|
22
22
|
});
|
|
23
23
|
const classes = {
|
|
@@ -76,9 +76,27 @@ function ChangeGroupPicture(inProps) {
|
|
|
76
76
|
* @param event
|
|
77
77
|
*/
|
|
78
78
|
function handleUpload(event) {
|
|
79
|
-
fileInput = event.target.files[0];
|
|
80
|
-
|
|
79
|
+
const fileInput = event.target.files[0];
|
|
80
|
+
if (fileInput) {
|
|
81
|
+
const reader = new FileReader();
|
|
82
|
+
reader.onload = (e) => {
|
|
83
|
+
const img = new Image();
|
|
84
|
+
img.onload = () => {
|
|
85
|
+
if (img.width < 600 && img.height < 600) {
|
|
86
|
+
setAlert(intl.formatMessage(messages.errorLoadImage));
|
|
87
|
+
}
|
|
88
|
+
else {
|
|
89
|
+
isCreationMode ? onChange && onChange(fileInput) : handleSave();
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-ignore
|
|
93
|
+
// @ts-ignore
|
|
94
|
+
img.src = e.target.result;
|
|
95
|
+
};
|
|
96
|
+
reader.readAsDataURL(fileInput);
|
|
97
|
+
}
|
|
81
98
|
}
|
|
99
|
+
// ui.changeGroupPicture.alert
|
|
82
100
|
/**
|
|
83
101
|
* Performs save avatar after upload
|
|
84
102
|
*/
|
|
@@ -226,9 +226,9 @@ function GroupInviteButton(inProps) {
|
|
|
226
226
|
setList((prev) => [...prev, option]);
|
|
227
227
|
};
|
|
228
228
|
/**
|
|
229
|
-
* If
|
|
229
|
+
* If in group edit mode and logged-in user is not also the group manager, the component is hidden.
|
|
230
230
|
// */
|
|
231
|
-
if (!canEdit) {
|
|
231
|
+
if (group && !canEdit) {
|
|
232
232
|
return null;
|
|
233
233
|
}
|
|
234
234
|
/**
|
|
@@ -242,7 +242,7 @@ function GroupInviteButton(inProps) {
|
|
|
242
242
|
react_1.default.createElement(material_1.Icon, { fontSize: "medium" }, "arrow_back")),
|
|
243
243
|
react_1.default.createElement(material_1.Typography, { className: classes.dialogTitle },
|
|
244
244
|
react_1.default.createElement(react_intl_1.FormattedMessage, { id: "ui.groupInviteButton.dialog.title", defaultMessage: "ui.groupInviteButton.dialog.title" })),
|
|
245
|
-
react_1.default.createElement(lab_1.LoadingButton, { size: "small", color: "secondary", variant: "contained", onClick: handleSendInvitations, loading: isSending },
|
|
245
|
+
react_1.default.createElement(lab_1.LoadingButton, { size: "small", color: "secondary", variant: "contained", onClick: handleSendInvitations, loading: isSending, disabled: !invited.length },
|
|
246
246
|
react_1.default.createElement(react_intl_1.FormattedMessage, { id: "ui.groupInviteButton.dialog.button.end", defaultMessage: "ui.groupInviteButton.dialog.button.end" }))) },
|
|
247
247
|
react_1.default.createElement(material_1.Box, { className: classes.dialogContent },
|
|
248
248
|
react_1.default.createElement(Autocomplete_1.default, { className: classes.autocomplete, loading: loading, size: "small", multiple: true, freeSolo: true, disableClearable: true, options: suggested, onChange: handleChange, onInputChange: handleInputChange, inputValue: value, value: invited, getOptionLabel: (option) => (option ? option.username : '...'), isOptionEqualToValue: (option, value) => (option ? value.id === option.id : false), renderTags: () => null, renderOption: (props, option) => (react_1.default.createElement(material_1.Box, Object.assign({ component: "li" }, props),
|
|
@@ -1,31 +1,27 @@
|
|
|
1
|
+
import { SCGroupType } from '@selfcommunity/types';
|
|
1
2
|
import { EndpointType } from '@selfcommunity/api-services';
|
|
2
|
-
import { CacheStrategies } from '@selfcommunity/utils';
|
|
3
3
|
import { GroupProps } from '../Group';
|
|
4
4
|
export interface GroupsProps {
|
|
5
5
|
/**
|
|
6
|
-
*
|
|
7
|
-
|
|
8
|
-
endpoint: EndpointType;
|
|
9
|
-
/**
|
|
10
|
-
* Hides this component
|
|
11
|
-
* @default false
|
|
6
|
+
* Overrides or extends the styles applied to the component.
|
|
7
|
+
* @default null
|
|
12
8
|
*/
|
|
13
|
-
|
|
9
|
+
className?: string;
|
|
14
10
|
/**
|
|
15
|
-
*
|
|
16
|
-
* @default false
|
|
11
|
+
* Endpoint to call
|
|
17
12
|
*/
|
|
18
|
-
|
|
13
|
+
endpoint: EndpointType;
|
|
19
14
|
/**
|
|
20
|
-
*
|
|
21
|
-
* @default
|
|
15
|
+
* Props to spread to single group object
|
|
16
|
+
* @default {variant: 'outlined', ButtonBaseProps: {disableRipple: 'true'}}
|
|
22
17
|
*/
|
|
23
|
-
|
|
18
|
+
GroupComponentProps?: GroupProps;
|
|
24
19
|
/**
|
|
25
|
-
*
|
|
26
|
-
*
|
|
20
|
+
* Prefetch groups. Useful for SSR.
|
|
21
|
+
* Use this to init the component with groups
|
|
22
|
+
* @default null
|
|
27
23
|
*/
|
|
28
|
-
|
|
24
|
+
prefetchedGroups?: SCGroupType[];
|
|
29
25
|
/**
|
|
30
26
|
* Other props
|
|
31
27
|
*/
|
|
@@ -7,7 +7,6 @@ const material_1 = require("@mui/material");
|
|
|
7
7
|
const api_services_1 = require("@selfcommunity/api-services");
|
|
8
8
|
const utils_1 = require("@selfcommunity/utils");
|
|
9
9
|
const react_core_1 = require("@selfcommunity/react-core");
|
|
10
|
-
const widget_1 = require("../../utils/widget");
|
|
11
10
|
const Skeleton_1 = tslib_1.__importDefault(require("./Skeleton"));
|
|
12
11
|
const react_intl_1 = require("react-intl");
|
|
13
12
|
const classnames_1 = tslib_1.__importDefault(require("classnames"));
|
|
@@ -65,117 +64,65 @@ function Groups(inProps) {
|
|
|
65
64
|
props: inProps,
|
|
66
65
|
name: constants_1.PREFIX
|
|
67
66
|
});
|
|
68
|
-
const { endpoint,
|
|
67
|
+
const { endpoint, className, GroupComponentProps = { variant: 'outlined', ButtonBaseProps: { disableRipple: true, component: material_1.Box } }, prefetchedGroups = [] } = props, rest = tslib_1.__rest(props, ["endpoint", "className", "GroupComponentProps", "prefetchedGroups"]);
|
|
69
68
|
// STATE
|
|
70
|
-
const [
|
|
71
|
-
|
|
72
|
-
next: null,
|
|
73
|
-
cacheKey: react_core_1.SCCache.getWidgetStateCacheKey(react_core_1.SCCache.GROUPS_LIST_TOOLS_STATE_CACHE_PREFIX_KEY),
|
|
74
|
-
cacheStrategy,
|
|
75
|
-
visibleItems: limit
|
|
76
|
-
}, widget_1.stateWidgetInitializer);
|
|
69
|
+
const [groups, setGroups] = (0, react_1.useState)([]);
|
|
70
|
+
const [loading, setLoading] = (0, react_1.useState)(true);
|
|
77
71
|
// CONTEXT
|
|
78
72
|
const scUserContext = (0, react_core_1.useSCUser)();
|
|
79
73
|
const scPreferencesContext = (0, react_core_1.useSCPreferences)();
|
|
80
74
|
// MEMO
|
|
81
75
|
const contentAvailability = (0, react_1.useMemo)(() => react_core_1.SCPreferences.CONFIGURATIONS_CONTENT_AVAILABILITY in scPreferencesContext.preferences &&
|
|
82
76
|
scPreferencesContext.preferences[react_core_1.SCPreferences.CONFIGURATIONS_CONTENT_AVAILABILITY].value, [scPreferencesContext.preferences]);
|
|
83
|
-
//
|
|
84
|
-
|
|
85
|
-
//
|
|
77
|
+
// CONST
|
|
78
|
+
const authUserId = scUserContext.user ? scUserContext.user.id : null;
|
|
79
|
+
// REFS
|
|
80
|
+
const isMountedRef = (0, react_core_1.useIsComponentMountedRef)();
|
|
86
81
|
/**
|
|
87
|
-
*
|
|
88
|
-
* Fetch data only if the component is not initialized, and it is not loading data
|
|
82
|
+
* Fetches groups list
|
|
89
83
|
*/
|
|
90
|
-
const
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
.then((payload) => {
|
|
99
|
-
dispatch({ type: widget_1.actionWidgetTypes.LOAD_NEXT_SUCCESS, payload: Object.assign(Object.assign({}, payload.data), { initialized: true }) });
|
|
100
|
-
})
|
|
101
|
-
.catch((error) => {
|
|
102
|
-
dispatch({ type: widget_1.actionWidgetTypes.LOAD_NEXT_FAILURE, payload: { errorLoadNext: error } });
|
|
103
|
-
utils_1.Logger.error(Errors_1.SCOPE_SC_UI, error);
|
|
104
|
-
});
|
|
105
|
-
}
|
|
106
|
-
}, [state.isLoadingNext, state.initialized, endpoint, limit, dispatch]);
|
|
107
|
-
// EFFECTS
|
|
108
|
-
(0, react_1.useEffect)(() => {
|
|
109
|
-
var _a;
|
|
110
|
-
let _t;
|
|
111
|
-
if ((contentAvailability || (!contentAvailability && ((_a = scUserContext.user) === null || _a === void 0 ? void 0 : _a.id))) && scUserContext.user !== undefined) {
|
|
112
|
-
_t = setTimeout(_initComponent);
|
|
113
|
-
return () => {
|
|
114
|
-
_t && clearTimeout(_t);
|
|
115
|
-
};
|
|
116
|
-
}
|
|
117
|
-
}, [scUserContext.user, contentAvailability]);
|
|
118
|
-
(0, react_1.useEffect)(() => {
|
|
119
|
-
if (state.next && state.results.length === limit && state.initialized) {
|
|
120
|
-
dispatch({ type: widget_1.actionWidgetTypes.LOADING_NEXT });
|
|
121
|
-
api_services_1.http
|
|
122
|
-
.request({
|
|
123
|
-
url: endpoint.url({ offset: limit, limit: 10 }),
|
|
124
|
-
method: endpoint.method
|
|
125
|
-
})
|
|
126
|
-
.then((payload) => {
|
|
127
|
-
dispatch({ type: widget_1.actionWidgetTypes.LOAD_NEXT_SUCCESS, payload: payload.data });
|
|
128
|
-
})
|
|
129
|
-
.catch((error) => {
|
|
130
|
-
dispatch({ type: widget_1.actionWidgetTypes.LOAD_NEXT_FAILURE, payload: { errorLoadNext: error } });
|
|
131
|
-
utils_1.Logger.error(Errors_1.SCOPE_SC_UI, error);
|
|
132
|
-
});
|
|
133
|
-
}
|
|
134
|
-
}, [state.next, state.results.length, state.initialized, limit]);
|
|
84
|
+
const fetchGroups = (next = endpoint.url({})) => tslib_1.__awaiter(this, void 0, void 0, function* () {
|
|
85
|
+
const response = yield api_services_1.http.request({
|
|
86
|
+
url: next,
|
|
87
|
+
method: endpoint.method
|
|
88
|
+
});
|
|
89
|
+
const data = response.data;
|
|
90
|
+
return data.next ? data.results.concat(yield fetchGroups(data.next)) : data.results;
|
|
91
|
+
});
|
|
135
92
|
/**
|
|
136
|
-
*
|
|
93
|
+
* On mount, fetches groups list
|
|
137
94
|
*/
|
|
138
95
|
(0, react_1.useEffect)(() => {
|
|
139
|
-
|
|
140
|
-
}, [state.results]);
|
|
141
|
-
(0, react_1.useEffect)(() => {
|
|
142
|
-
if (!endpoint || (!contentAvailability && !scUserContext.user)) {
|
|
96
|
+
if (!contentAvailability && !authUserId) {
|
|
143
97
|
return;
|
|
144
98
|
}
|
|
145
|
-
else if (
|
|
146
|
-
|
|
99
|
+
else if (prefetchedGroups.length) {
|
|
100
|
+
setGroups(prefetchedGroups);
|
|
101
|
+
setLoading(false);
|
|
147
102
|
}
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
103
|
+
else {
|
|
104
|
+
fetchGroups()
|
|
105
|
+
.then((data) => {
|
|
106
|
+
if (isMountedRef.current) {
|
|
107
|
+
setGroups(data);
|
|
108
|
+
setLoading(false);
|
|
109
|
+
}
|
|
110
|
+
})
|
|
111
|
+
.catch((error) => {
|
|
112
|
+
utils_1.Logger.error(Errors_1.SCOPE_SC_UI, error);
|
|
113
|
+
});
|
|
152
114
|
}
|
|
153
|
-
}, []);
|
|
154
|
-
// HANDLERS
|
|
155
|
-
const handleNext = (0, react_1.useMemo)(() => () => {
|
|
156
|
-
dispatch({ type: widget_1.actionWidgetTypes.LOADING_NEXT });
|
|
157
|
-
api_services_1.http
|
|
158
|
-
.request({
|
|
159
|
-
url: state.next,
|
|
160
|
-
method: endpoint.method
|
|
161
|
-
})
|
|
162
|
-
.then((res) => {
|
|
163
|
-
dispatch({ type: widget_1.actionWidgetTypes.LOAD_NEXT_SUCCESS, payload: res.data });
|
|
164
|
-
});
|
|
165
|
-
}, [dispatch, state.next, state.isLoadingNext, state.initialized, endpoint.method]);
|
|
115
|
+
}, [contentAvailability, authUserId, prefetchedGroups.length]);
|
|
166
116
|
// RENDER
|
|
167
|
-
if (
|
|
117
|
+
if (!contentAvailability && !scUserContext.user) {
|
|
168
118
|
return react_1.default.createElement(HiddenPlaceholder_1.default, null);
|
|
169
119
|
}
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
react_1.default.createElement(
|
|
176
|
-
react_1.default.createElement(Group_1.default, Object.assign({ group: group, groupId: group.id }, GroupProps)))))),
|
|
177
|
-
state.count > state.visibleItems && (react_1.default.createElement(material_1.Button, { className: classes.showMore, onClick: handleNext },
|
|
178
|
-
react_1.default.createElement(react_intl_1.FormattedMessage, { id: "ui.groupRequestsWidget.button.showMore", defaultMessage: "ui.groupRequestsWidget.button.showMore" })))))));
|
|
120
|
+
const content = (react_1.default.createElement(react_1.default.Fragment, null, loading ? (react_1.default.createElement(Skeleton_1.default, null)) : (react_1.default.createElement(material_1.Grid, { container: true, spacing: { xs: 3 }, className: classes.groups }, !groups.length ? (react_1.default.createElement(material_1.Box, { className: classes.noResults },
|
|
121
|
+
react_1.default.createElement(material_1.Typography, { variant: "h4" },
|
|
122
|
+
react_1.default.createElement(react_intl_1.FormattedMessage, { id: "ui.groups.noGroups.title", defaultMessage: "ui.groups.noGroups.title" })),
|
|
123
|
+
react_1.default.createElement(material_1.Typography, { variant: "body1" },
|
|
124
|
+
react_1.default.createElement(react_intl_1.FormattedMessage, { id: "ui.groups.noGroups.subtitle", defaultMessage: "ui.groups.noGroups.subtitle" })))) : (react_1.default.createElement(react_1.default.Fragment, null, groups.map((group) => (react_1.default.createElement(material_1.Grid, { item: true, xs: 12, sm: 8, md: 6, key: group.id, className: classes.item },
|
|
125
|
+
react_1.default.createElement(Group_1.default, Object.assign({ group: group, groupId: group.id }, GroupComponentProps)))))))))));
|
|
179
126
|
return (react_1.default.createElement(Root, Object.assign({ className: (0, classnames_1.default)(classes.root, className) }, rest), content));
|
|
180
127
|
}
|
|
181
128
|
exports.default = Groups;
|
|
@@ -23,6 +23,10 @@ const messages = defineMessages({
|
|
|
23
23
|
errorLoadImage: {
|
|
24
24
|
id: 'ui.changeGroupCover.button.change.alertErrorImage',
|
|
25
25
|
defaultMessage: 'ui.changeGroupCover.button.change.alertErrorImage'
|
|
26
|
+
},
|
|
27
|
+
errorImageSize: {
|
|
28
|
+
id: 'ui.changeGroupCover.alert',
|
|
29
|
+
defaultMessage: 'ui.changeGroupCover.alert'
|
|
26
30
|
}
|
|
27
31
|
});
|
|
28
32
|
/**
|
|
@@ -73,7 +77,26 @@ export default function ChangeGroupCover(inProps) {
|
|
|
73
77
|
*/
|
|
74
78
|
const handleUpload = (event) => {
|
|
75
79
|
fileInput = event.target.files[0];
|
|
76
|
-
|
|
80
|
+
if (fileInput) {
|
|
81
|
+
const reader = new FileReader();
|
|
82
|
+
reader.onload = (e) => {
|
|
83
|
+
const img = new Image();
|
|
84
|
+
img.onload = () => {
|
|
85
|
+
if (img.width < 1920) {
|
|
86
|
+
setAlert(intl.formatMessage(messages.errorImageSize));
|
|
87
|
+
}
|
|
88
|
+
else {
|
|
89
|
+
isCreationMode ? onChange && onChange(fileInput) : handleSave();
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-ignore
|
|
93
|
+
// @ts-ignore
|
|
94
|
+
img.src = e.target.result;
|
|
95
|
+
};
|
|
96
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-ignore
|
|
97
|
+
// @ts-ignore
|
|
98
|
+
reader.readAsDataURL(fileInput);
|
|
99
|
+
}
|
|
77
100
|
};
|
|
78
101
|
/**
|
|
79
102
|
* Handles cover saving after upload action
|
|
@@ -14,8 +14,8 @@ import { defineMessages, useIntl } from 'react-intl';
|
|
|
14
14
|
import { LoadingButton } from '@mui/lab';
|
|
15
15
|
const messages = defineMessages({
|
|
16
16
|
errorLoadImage: {
|
|
17
|
-
id: 'ui.
|
|
18
|
-
defaultMessage: 'ui.
|
|
17
|
+
id: 'ui.changeGroupPicture.alert',
|
|
18
|
+
defaultMessage: 'ui.changeGroupPicture.alert'
|
|
19
19
|
}
|
|
20
20
|
});
|
|
21
21
|
const classes = {
|
|
@@ -74,9 +74,27 @@ export default function ChangeGroupPicture(inProps) {
|
|
|
74
74
|
* @param event
|
|
75
75
|
*/
|
|
76
76
|
function handleUpload(event) {
|
|
77
|
-
fileInput = event.target.files[0];
|
|
78
|
-
|
|
77
|
+
const fileInput = event.target.files[0];
|
|
78
|
+
if (fileInput) {
|
|
79
|
+
const reader = new FileReader();
|
|
80
|
+
reader.onload = (e) => {
|
|
81
|
+
const img = new Image();
|
|
82
|
+
img.onload = () => {
|
|
83
|
+
if (img.width < 600 && img.height < 600) {
|
|
84
|
+
setAlert(intl.formatMessage(messages.errorLoadImage));
|
|
85
|
+
}
|
|
86
|
+
else {
|
|
87
|
+
isCreationMode ? onChange && onChange(fileInput) : handleSave();
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-ignore
|
|
91
|
+
// @ts-ignore
|
|
92
|
+
img.src = e.target.result;
|
|
93
|
+
};
|
|
94
|
+
reader.readAsDataURL(fileInput);
|
|
95
|
+
}
|
|
79
96
|
}
|
|
97
|
+
// ui.changeGroupPicture.alert
|
|
80
98
|
/**
|
|
81
99
|
* Performs save avatar after upload
|
|
82
100
|
*/
|
|
@@ -224,9 +224,9 @@ export default function GroupInviteButton(inProps) {
|
|
|
224
224
|
setList((prev) => [...prev, option]);
|
|
225
225
|
};
|
|
226
226
|
/**
|
|
227
|
-
* If
|
|
227
|
+
* If in group edit mode and logged-in user is not also the group manager, the component is hidden.
|
|
228
228
|
// */
|
|
229
|
-
if (!canEdit) {
|
|
229
|
+
if (group && !canEdit) {
|
|
230
230
|
return null;
|
|
231
231
|
}
|
|
232
232
|
/**
|
|
@@ -240,7 +240,7 @@ export default function GroupInviteButton(inProps) {
|
|
|
240
240
|
React.createElement(Icon, { fontSize: "medium" }, "arrow_back")),
|
|
241
241
|
React.createElement(Typography, { className: classes.dialogTitle },
|
|
242
242
|
React.createElement(FormattedMessage, { id: "ui.groupInviteButton.dialog.title", defaultMessage: "ui.groupInviteButton.dialog.title" })),
|
|
243
|
-
React.createElement(LoadingButton, { size: "small", color: "secondary", variant: "contained", onClick: handleSendInvitations, loading: isSending },
|
|
243
|
+
React.createElement(LoadingButton, { size: "small", color: "secondary", variant: "contained", onClick: handleSendInvitations, loading: isSending, disabled: !invited.length },
|
|
244
244
|
React.createElement(FormattedMessage, { id: "ui.groupInviteButton.dialog.button.end", defaultMessage: "ui.groupInviteButton.dialog.button.end" }))) },
|
|
245
245
|
React.createElement(Box, { className: classes.dialogContent },
|
|
246
246
|
React.createElement(Autocomplete, { className: classes.autocomplete, loading: loading, size: "small", multiple: true, freeSolo: true, disableClearable: true, options: suggested, onChange: handleChange, onInputChange: handleInputChange, inputValue: value, value: invited, getOptionLabel: (option) => (option ? option.username : '...'), isOptionEqualToValue: (option, value) => (option ? value.id === option.id : false), renderTags: () => null, renderOption: (props, option) => (React.createElement(Box, Object.assign({ component: "li" }, props),
|
|
@@ -1,31 +1,27 @@
|
|
|
1
|
+
import { SCGroupType } from '@selfcommunity/types';
|
|
1
2
|
import { EndpointType } from '@selfcommunity/api-services';
|
|
2
|
-
import { CacheStrategies } from '@selfcommunity/utils';
|
|
3
3
|
import { GroupProps } from '../Group';
|
|
4
4
|
export interface GroupsProps {
|
|
5
5
|
/**
|
|
6
|
-
*
|
|
7
|
-
|
|
8
|
-
endpoint: EndpointType;
|
|
9
|
-
/**
|
|
10
|
-
* Hides this component
|
|
11
|
-
* @default false
|
|
6
|
+
* Overrides or extends the styles applied to the component.
|
|
7
|
+
* @default null
|
|
12
8
|
*/
|
|
13
|
-
|
|
9
|
+
className?: string;
|
|
14
10
|
/**
|
|
15
|
-
*
|
|
16
|
-
* @default false
|
|
11
|
+
* Endpoint to call
|
|
17
12
|
*/
|
|
18
|
-
|
|
13
|
+
endpoint: EndpointType;
|
|
19
14
|
/**
|
|
20
|
-
*
|
|
21
|
-
* @default
|
|
15
|
+
* Props to spread to single group object
|
|
16
|
+
* @default {variant: 'outlined', ButtonBaseProps: {disableRipple: 'true'}}
|
|
22
17
|
*/
|
|
23
|
-
|
|
18
|
+
GroupComponentProps?: GroupProps;
|
|
24
19
|
/**
|
|
25
|
-
*
|
|
26
|
-
*
|
|
20
|
+
* Prefetch groups. Useful for SSR.
|
|
21
|
+
* Use this to init the component with groups
|
|
22
|
+
* @default null
|
|
27
23
|
*/
|
|
28
|
-
|
|
24
|
+
prefetchedGroups?: SCGroupType[];
|
|
29
25
|
/**
|
|
30
26
|
* Other props
|
|
31
27
|
*/
|
|
@@ -1,11 +1,10 @@
|
|
|
1
|
-
import { __rest } from "tslib";
|
|
2
|
-
import React, { useEffect, useMemo,
|
|
1
|
+
import { __awaiter, __rest } from "tslib";
|
|
2
|
+
import React, { useEffect, useMemo, useState } from 'react';
|
|
3
3
|
import { styled } from '@mui/material/styles';
|
|
4
|
-
import { Box,
|
|
4
|
+
import { Box, Grid, Typography } from '@mui/material';
|
|
5
5
|
import { http } from '@selfcommunity/api-services';
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import { actionWidgetTypes, dataWidgetReducer, stateWidgetInitializer } from '../../utils/widget';
|
|
6
|
+
import { Logger } from '@selfcommunity/utils';
|
|
7
|
+
import { SCPreferences, useIsComponentMountedRef, useSCPreferences, useSCUser } from '@selfcommunity/react-core';
|
|
9
8
|
import Skeleton from './Skeleton';
|
|
10
9
|
import { FormattedMessage } from 'react-intl';
|
|
11
10
|
import classNames from 'classnames';
|
|
@@ -63,116 +62,64 @@ export default function Groups(inProps) {
|
|
|
63
62
|
props: inProps,
|
|
64
63
|
name: PREFIX
|
|
65
64
|
});
|
|
66
|
-
const { endpoint,
|
|
65
|
+
const { endpoint, className, GroupComponentProps = { variant: 'outlined', ButtonBaseProps: { disableRipple: true, component: Box } }, prefetchedGroups = [] } = props, rest = __rest(props, ["endpoint", "className", "GroupComponentProps", "prefetchedGroups"]);
|
|
67
66
|
// STATE
|
|
68
|
-
const [
|
|
69
|
-
|
|
70
|
-
next: null,
|
|
71
|
-
cacheKey: SCCache.getWidgetStateCacheKey(SCCache.GROUPS_LIST_TOOLS_STATE_CACHE_PREFIX_KEY),
|
|
72
|
-
cacheStrategy,
|
|
73
|
-
visibleItems: limit
|
|
74
|
-
}, stateWidgetInitializer);
|
|
67
|
+
const [groups, setGroups] = useState([]);
|
|
68
|
+
const [loading, setLoading] = useState(true);
|
|
75
69
|
// CONTEXT
|
|
76
70
|
const scUserContext = useSCUser();
|
|
77
71
|
const scPreferencesContext = useSCPreferences();
|
|
78
72
|
// MEMO
|
|
79
73
|
const contentAvailability = useMemo(() => SCPreferences.CONFIGURATIONS_CONTENT_AVAILABILITY in scPreferencesContext.preferences &&
|
|
80
74
|
scPreferencesContext.preferences[SCPreferences.CONFIGURATIONS_CONTENT_AVAILABILITY].value, [scPreferencesContext.preferences]);
|
|
81
|
-
//
|
|
82
|
-
|
|
83
|
-
//
|
|
75
|
+
// CONST
|
|
76
|
+
const authUserId = scUserContext.user ? scUserContext.user.id : null;
|
|
77
|
+
// REFS
|
|
78
|
+
const isMountedRef = useIsComponentMountedRef();
|
|
84
79
|
/**
|
|
85
|
-
*
|
|
86
|
-
* Fetch data only if the component is not initialized, and it is not loading data
|
|
80
|
+
* Fetches groups list
|
|
87
81
|
*/
|
|
88
|
-
const
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
.then((payload) => {
|
|
97
|
-
dispatch({ type: actionWidgetTypes.LOAD_NEXT_SUCCESS, payload: Object.assign(Object.assign({}, payload.data), { initialized: true }) });
|
|
98
|
-
})
|
|
99
|
-
.catch((error) => {
|
|
100
|
-
dispatch({ type: actionWidgetTypes.LOAD_NEXT_FAILURE, payload: { errorLoadNext: error } });
|
|
101
|
-
Logger.error(SCOPE_SC_UI, error);
|
|
102
|
-
});
|
|
103
|
-
}
|
|
104
|
-
}, [state.isLoadingNext, state.initialized, endpoint, limit, dispatch]);
|
|
105
|
-
// EFFECTS
|
|
106
|
-
useEffect(() => {
|
|
107
|
-
var _a;
|
|
108
|
-
let _t;
|
|
109
|
-
if ((contentAvailability || (!contentAvailability && ((_a = scUserContext.user) === null || _a === void 0 ? void 0 : _a.id))) && scUserContext.user !== undefined) {
|
|
110
|
-
_t = setTimeout(_initComponent);
|
|
111
|
-
return () => {
|
|
112
|
-
_t && clearTimeout(_t);
|
|
113
|
-
};
|
|
114
|
-
}
|
|
115
|
-
}, [scUserContext.user, contentAvailability]);
|
|
116
|
-
useEffect(() => {
|
|
117
|
-
if (state.next && state.results.length === limit && state.initialized) {
|
|
118
|
-
dispatch({ type: actionWidgetTypes.LOADING_NEXT });
|
|
119
|
-
http
|
|
120
|
-
.request({
|
|
121
|
-
url: endpoint.url({ offset: limit, limit: 10 }),
|
|
122
|
-
method: endpoint.method
|
|
123
|
-
})
|
|
124
|
-
.then((payload) => {
|
|
125
|
-
dispatch({ type: actionWidgetTypes.LOAD_NEXT_SUCCESS, payload: payload.data });
|
|
126
|
-
})
|
|
127
|
-
.catch((error) => {
|
|
128
|
-
dispatch({ type: actionWidgetTypes.LOAD_NEXT_FAILURE, payload: { errorLoadNext: error } });
|
|
129
|
-
Logger.error(SCOPE_SC_UI, error);
|
|
130
|
-
});
|
|
131
|
-
}
|
|
132
|
-
}, [state.next, state.results.length, state.initialized, limit]);
|
|
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
|
|
86
|
+
});
|
|
87
|
+
const data = response.data;
|
|
88
|
+
return data.next ? data.results.concat(yield fetchGroups(data.next)) : data.results;
|
|
89
|
+
});
|
|
133
90
|
/**
|
|
134
|
-
*
|
|
91
|
+
* On mount, fetches groups list
|
|
135
92
|
*/
|
|
136
93
|
useEffect(() => {
|
|
137
|
-
|
|
138
|
-
}, [state.results]);
|
|
139
|
-
useEffect(() => {
|
|
140
|
-
if (!endpoint || (!contentAvailability && !scUserContext.user)) {
|
|
94
|
+
if (!contentAvailability && !authUserId) {
|
|
141
95
|
return;
|
|
142
96
|
}
|
|
143
|
-
else if (
|
|
144
|
-
|
|
97
|
+
else if (prefetchedGroups.length) {
|
|
98
|
+
setGroups(prefetchedGroups);
|
|
99
|
+
setLoading(false);
|
|
145
100
|
}
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
101
|
+
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
|
+
});
|
|
150
112
|
}
|
|
151
|
-
}, []);
|
|
152
|
-
// HANDLERS
|
|
153
|
-
const handleNext = useMemo(() => () => {
|
|
154
|
-
dispatch({ type: actionWidgetTypes.LOADING_NEXT });
|
|
155
|
-
http
|
|
156
|
-
.request({
|
|
157
|
-
url: state.next,
|
|
158
|
-
method: endpoint.method
|
|
159
|
-
})
|
|
160
|
-
.then((res) => {
|
|
161
|
-
dispatch({ type: actionWidgetTypes.LOAD_NEXT_SUCCESS, payload: res.data });
|
|
162
|
-
});
|
|
163
|
-
}, [dispatch, state.next, state.isLoadingNext, state.initialized, endpoint.method]);
|
|
113
|
+
}, [contentAvailability, authUserId, prefetchedGroups.length]);
|
|
164
114
|
// RENDER
|
|
165
|
-
if (
|
|
115
|
+
if (!contentAvailability && !scUserContext.user) {
|
|
166
116
|
return React.createElement(HiddenPlaceholder, null);
|
|
167
117
|
}
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
React.createElement(
|
|
174
|
-
React.createElement(Group, Object.assign({ group: group, groupId: group.id }, GroupProps)))))),
|
|
175
|
-
state.count > state.visibleItems && (React.createElement(Button, { className: classes.showMore, onClick: handleNext },
|
|
176
|
-
React.createElement(FormattedMessage, { id: "ui.groupRequestsWidget.button.showMore", defaultMessage: "ui.groupRequestsWidget.button.showMore" })))))));
|
|
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)))))))))));
|
|
177
124
|
return (React.createElement(Root, Object.assign({ className: classNames(classes.root, className) }, rest), content));
|
|
178
125
|
}
|