@selfcommunity/react-ui 0.7.9-alpha.47 → 0.7.9-alpha.48

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.
@@ -1,5 +1,4 @@
1
1
  import { SCGroupType } from '@selfcommunity/types';
2
- import { EndpointType } from '@selfcommunity/api-services';
3
2
  import { GroupProps } from '../Group';
4
3
  export interface GroupsProps {
5
4
  /**
@@ -8,24 +7,19 @@ export interface GroupsProps {
8
7
  */
9
8
  className?: string;
10
9
  /**
11
- * Endpoint to call
10
+ * Feed API Query Params
11
+ * @default [{'limit': 10, 'offset': 0}]
12
12
  */
13
- endpoint?: EndpointType;
13
+ endpointQueryParams?: Record<string, string | number>;
14
14
  /**
15
15
  * Props to spread to single group object
16
16
  * @default {variant: 'outlined', ButtonBaseProps: {disableRipple: 'true'}}
17
17
  */
18
18
  GroupComponentProps?: GroupProps;
19
- /**
20
- * Prefetch groups. Useful for SSR.
21
- * Use this to init the component with groups
22
- * @default null
23
- */
24
- prefetchedGroups?: SCGroupType[];
25
19
  /** If true, it means that the endpoint fetches all groups available
26
- * @default null
20
+ * @default true
27
21
  */
28
- general?: boolean;
22
+ general: boolean;
29
23
  /**
30
24
  * Show/Hide filters
31
25
  * @default false
@@ -14,7 +14,9 @@ const Errors_1 = require("../../constants/Errors");
14
14
  const system_1 = require("@mui/system");
15
15
  const HiddenPlaceholder_1 = tslib_1.__importDefault(require("../../shared/HiddenPlaceholder"));
16
16
  const constants_1 = require("./constants");
17
- const Group_1 = tslib_1.__importDefault(require("../Group"));
17
+ const Group_1 = tslib_1.__importStar(require("../Group"));
18
+ const Pagination_1 = require("../../constants/Pagination");
19
+ const InfiniteScroll_1 = tslib_1.__importDefault(require("../../shared/InfiniteScroll"));
18
20
  const classes = {
19
21
  root: `${constants_1.PREFIX}-root`,
20
22
  filters: `${constants_1.PREFIX}-filter`,
@@ -65,10 +67,11 @@ function Groups(inProps) {
65
67
  props: inProps,
66
68
  name: constants_1.PREFIX
67
69
  });
68
- const { endpoint, className, GroupComponentProps = { variant: 'outlined', ButtonBaseProps: { disableRipple: true, component: material_1.Box } }, prefetchedGroups = [], showFilters = false, filters, handleFilterGroups, general } = props, rest = tslib_1.__rest(props, ["endpoint", "className", "GroupComponentProps", "prefetchedGroups", "showFilters", "filters", "handleFilterGroups", "general"]);
70
+ const { endpointQueryParams = { limit: Pagination_1.DEFAULT_PAGINATION_LIMIT, offset: Pagination_1.DEFAULT_PAGINATION_OFFSET }, className, GroupComponentProps = { variant: 'outlined', ButtonBaseProps: { disableRipple: true, component: material_1.Box } }, showFilters = false, filters, handleFilterGroups, general = true } = props, rest = tslib_1.__rest(props, ["endpointQueryParams", "className", "GroupComponentProps", "showFilters", "filters", "handleFilterGroups", "general"]);
69
71
  // STATE
70
72
  const [groups, setGroups] = (0, react_1.useState)([]);
71
73
  const [loading, setLoading] = (0, react_1.useState)(true);
74
+ const [next, setNext] = (0, react_1.useState)(null);
72
75
  const [filterName, setFilterName] = (0, react_1.useState)('');
73
76
  // CONTEXT
74
77
  const scUserContext = (0, react_core_1.useSCUser)();
@@ -78,19 +81,31 @@ function Groups(inProps) {
78
81
  scPreferencesContext.preferences[react_core_1.SCPreferences.CONFIGURATIONS_CONTENT_AVAILABILITY].value, [scPreferencesContext.preferences]);
79
82
  // CONST
80
83
  const authUserId = scUserContext.user ? scUserContext.user.id : null;
81
- // REFS
82
- const isMountedRef = (0, react_core_1.useIsComponentMountedRef)();
84
+ // HANDLERS
85
+ const handleScrollUp = () => {
86
+ window.scrollTo({ left: 0, top: 0, behavior: 'smooth' });
87
+ };
83
88
  /**
84
89
  * Fetches groups list
85
90
  */
86
- const fetchGroups = (next = endpoint.url({})) => tslib_1.__awaiter(this, void 0, void 0, function* () {
87
- const response = yield api_services_1.http.request({
88
- url: next,
89
- method: endpoint.method
91
+ const fetchGroups = () => {
92
+ let groupService;
93
+ if (general) {
94
+ groupService = api_services_1.GroupService.searchGroups(endpointQueryParams);
95
+ }
96
+ else {
97
+ groupService = api_services_1.GroupService.getUserGroups(endpointQueryParams);
98
+ }
99
+ groupService
100
+ .then((res) => {
101
+ setGroups(res.results);
102
+ setNext(res.next);
103
+ setLoading(false);
104
+ })
105
+ .catch((error) => {
106
+ utils_1.Logger.error(Errors_1.SCOPE_SC_UI, error);
90
107
  });
91
- const data = response.data;
92
- return data.next ? data.results.concat(yield fetchGroups(data.next)) : data.results;
93
- });
108
+ };
94
109
  /**
95
110
  * On mount, fetches groups list
96
111
  */
@@ -98,23 +113,10 @@ function Groups(inProps) {
98
113
  if (!contentAvailability && !authUserId) {
99
114
  return;
100
115
  }
101
- else if (prefetchedGroups.length) {
102
- setGroups(prefetchedGroups);
103
- setLoading(false);
104
- }
105
116
  else {
106
- fetchGroups()
107
- .then((data) => {
108
- if (isMountedRef.current) {
109
- setGroups(data);
110
- setLoading(false);
111
- }
112
- })
113
- .catch((error) => {
114
- utils_1.Logger.error(Errors_1.SCOPE_SC_UI, error);
115
- });
117
+ fetchGroups();
116
118
  }
117
- }, [contentAvailability, authUserId, prefetchedGroups.length]);
119
+ }, [contentAvailability, authUserId]);
118
120
  const handleSubscribe = (group) => {
119
121
  if (!general) {
120
122
  const newGroups = [...groups];
@@ -122,6 +124,22 @@ function Groups(inProps) {
122
124
  setGroups(_updated);
123
125
  }
124
126
  };
127
+ const handleNext = (0, react_1.useMemo)(() => () => {
128
+ if (!next) {
129
+ return;
130
+ }
131
+ return api_services_1.http
132
+ .request({
133
+ url: next,
134
+ method: general ? api_services_1.Endpoints.SearchGroups.method : api_services_1.Endpoints.GetUserGroups.method
135
+ })
136
+ .then((res) => {
137
+ setGroups([...groups, ...res.data.results]);
138
+ setNext(res.data.next);
139
+ })
140
+ .catch((error) => console.log(error))
141
+ .then(() => setLoading(false));
142
+ }, [next]);
125
143
  /**
126
144
  * Get groups filtered
127
145
  */
@@ -148,16 +166,23 @@ function Groups(inProps) {
148
166
  const content = (react_1.default.createElement(react_1.default.Fragment, null,
149
167
  showFilters && (react_1.default.createElement(material_1.Grid, { container: true, direction: "row", justifyContent: "center", alignItems: "center", className: classes.filters }, filters ? (filters) : (react_1.default.createElement(material_1.Grid, { item: true, xs: 12, md: 6 },
150
168
  react_1.default.createElement(material_1.TextField, { fullWidth: true, value: filterName, label: react_1.default.createElement(react_intl_1.FormattedMessage, { id: "ui.groups.filterByName", defaultMessage: "ui.groups.filterByName" }), variant: "outlined", onChange: handleOnChangeFilterName, disabled: loading }))))),
151
- loading ? (react_1.default.createElement(Skeleton_1.default, null)) : (react_1.default.createElement(react_1.default.Fragment, null, !groups.length ? (react_1.default.createElement(material_1.Box, { className: classes.noResults },
169
+ react_1.default.createElement(react_1.default.Fragment, null, !groups.length ? (react_1.default.createElement(material_1.Box, { className: classes.noResults },
152
170
  react_1.default.createElement(material_1.Typography, { variant: "h4" },
153
171
  react_1.default.createElement(react_intl_1.FormattedMessage, { id: "ui.groups.noGroups.title", defaultMessage: "ui.groups.noGroups.title" })),
154
172
  react_1.default.createElement(material_1.Typography, { variant: "body1" },
155
- react_1.default.createElement(react_intl_1.FormattedMessage, { id: "ui.groups.noGroups.subtitle", defaultMessage: "ui.groups.noGroups.subtitle" })))) : (react_1.default.createElement(material_1.Grid, { container: true, spacing: { xs: 2 }, className: classes.groups }, filteredGroups.map((group) => (react_1.default.createElement(material_1.Grid, { item: true, xs: 12, sm: 8, md: 6, key: group.id, className: classes.item },
156
- react_1.default.createElement(Group_1.default, Object.assign({ group: group, groupId: group.id, groupSubscribeButtonProps: { onSubscribe: handleSubscribe } }, GroupComponentProps)))))))))));
173
+ react_1.default.createElement(react_intl_1.FormattedMessage, { id: "ui.groups.noGroups.subtitle", defaultMessage: "ui.groups.noGroups.subtitle" })))) : (react_1.default.createElement(InfiniteScroll_1.default, { dataLength: groups.length, next: handleNext, hasMoreNext: Boolean(next), loaderNext: react_1.default.createElement(Group_1.GroupSkeleton, null), endMessage: react_1.default.createElement(material_1.Typography, { component: "div", className: classes.endMessage },
174
+ react_1.default.createElement(react_intl_1.FormattedMessage, { id: "ui.groups.endMessage", defaultMessage: "ui.groups.endMessage", values: {
175
+ button: (chunk) => (react_1.default.createElement(material_1.Button, { color: "secondary", variant: "text", onClick: handleScrollUp }, chunk))
176
+ } })) },
177
+ react_1.default.createElement(material_1.Grid, { container: true, spacing: { xs: 2 }, className: classes.groups }, filteredGroups.map((group) => (react_1.default.createElement(material_1.Grid, { item: true, xs: 12, sm: 8, md: 6, key: group.id, className: classes.item },
178
+ react_1.default.createElement(Group_1.default, Object.assign({ group: group, groupId: group.id, groupSubscribeButtonProps: { onSubscribe: handleSubscribe } }, GroupComponentProps)))))))))));
157
179
  // RENDER
158
180
  if (!contentAvailability && !scUserContext.user) {
159
181
  return react_1.default.createElement(HiddenPlaceholder_1.default, null);
160
182
  }
183
+ if (loading) {
184
+ return react_1.default.createElement(Skeleton_1.default, null);
185
+ }
161
186
  return (react_1.default.createElement(Root, Object.assign({ className: (0, classnames_1.default)(classes.root, className) }, rest), content));
162
187
  }
163
188
  exports.default = Groups;
@@ -1,5 +1,4 @@
1
1
  import { SCGroupType } from '@selfcommunity/types';
2
- import { EndpointType } from '@selfcommunity/api-services';
3
2
  import { GroupProps } from '../Group';
4
3
  export interface GroupsProps {
5
4
  /**
@@ -8,24 +7,19 @@ export interface GroupsProps {
8
7
  */
9
8
  className?: string;
10
9
  /**
11
- * Endpoint to call
10
+ * Feed API Query Params
11
+ * @default [{'limit': 10, 'offset': 0}]
12
12
  */
13
- endpoint?: EndpointType;
13
+ endpointQueryParams?: Record<string, string | number>;
14
14
  /**
15
15
  * Props to spread to single group object
16
16
  * @default {variant: 'outlined', ButtonBaseProps: {disableRipple: 'true'}}
17
17
  */
18
18
  GroupComponentProps?: GroupProps;
19
- /**
20
- * Prefetch groups. Useful for SSR.
21
- * Use this to init the component with groups
22
- * @default null
23
- */
24
- prefetchedGroups?: SCGroupType[];
25
19
  /** If true, it means that the endpoint fetches all groups available
26
- * @default null
20
+ * @default true
27
21
  */
28
- general?: boolean;
22
+ general: boolean;
29
23
  /**
30
24
  * Show/Hide filters
31
25
  * @default false
@@ -1,10 +1,10 @@
1
- import { __awaiter, __rest } from "tslib";
1
+ import { __rest } from "tslib";
2
2
  import React, { useEffect, useMemo, useState } from 'react';
3
3
  import { styled } from '@mui/material/styles';
4
- import { Box, Grid, TextField, Typography } from '@mui/material';
5
- import { http } from '@selfcommunity/api-services';
4
+ import { Box, Button, Grid, TextField, Typography } from '@mui/material';
5
+ import { Endpoints, GroupService, http } from '@selfcommunity/api-services';
6
6
  import { Logger, sortByAttr } from '@selfcommunity/utils';
7
- import { SCPreferences, useIsComponentMountedRef, useSCPreferences, useSCUser } from '@selfcommunity/react-core';
7
+ import { SCPreferences, useSCPreferences, useSCUser } from '@selfcommunity/react-core';
8
8
  import Skeleton from './Skeleton';
9
9
  import { FormattedMessage } from 'react-intl';
10
10
  import classNames from 'classnames';
@@ -12,7 +12,9 @@ import { SCOPE_SC_UI } from '../../constants/Errors';
12
12
  import { useThemeProps } from '@mui/system';
13
13
  import HiddenPlaceholder from '../../shared/HiddenPlaceholder';
14
14
  import { PREFIX } from './constants';
15
- import Group from '../Group';
15
+ import Group, { GroupSkeleton } from '../Group';
16
+ import { DEFAULT_PAGINATION_LIMIT, DEFAULT_PAGINATION_OFFSET } from '../../constants/Pagination';
17
+ import InfiniteScroll from '../../shared/InfiniteScroll';
16
18
  const classes = {
17
19
  root: `${PREFIX}-root`,
18
20
  filters: `${PREFIX}-filter`,
@@ -63,10 +65,11 @@ export default function Groups(inProps) {
63
65
  props: inProps,
64
66
  name: PREFIX
65
67
  });
66
- const { endpoint, className, GroupComponentProps = { variant: 'outlined', ButtonBaseProps: { disableRipple: true, component: Box } }, prefetchedGroups = [], showFilters = false, filters, handleFilterGroups, general } = props, rest = __rest(props, ["endpoint", "className", "GroupComponentProps", "prefetchedGroups", "showFilters", "filters", "handleFilterGroups", "general"]);
68
+ const { endpointQueryParams = { limit: DEFAULT_PAGINATION_LIMIT, offset: DEFAULT_PAGINATION_OFFSET }, className, GroupComponentProps = { variant: 'outlined', ButtonBaseProps: { disableRipple: true, component: Box } }, showFilters = false, filters, handleFilterGroups, general = true } = props, rest = __rest(props, ["endpointQueryParams", "className", "GroupComponentProps", "showFilters", "filters", "handleFilterGroups", "general"]);
67
69
  // STATE
68
70
  const [groups, setGroups] = useState([]);
69
71
  const [loading, setLoading] = useState(true);
72
+ const [next, setNext] = useState(null);
70
73
  const [filterName, setFilterName] = useState('');
71
74
  // CONTEXT
72
75
  const scUserContext = useSCUser();
@@ -76,19 +79,31 @@ export default function Groups(inProps) {
76
79
  scPreferencesContext.preferences[SCPreferences.CONFIGURATIONS_CONTENT_AVAILABILITY].value, [scPreferencesContext.preferences]);
77
80
  // CONST
78
81
  const authUserId = scUserContext.user ? scUserContext.user.id : null;
79
- // REFS
80
- const isMountedRef = useIsComponentMountedRef();
82
+ // HANDLERS
83
+ const handleScrollUp = () => {
84
+ window.scrollTo({ left: 0, top: 0, behavior: 'smooth' });
85
+ };
81
86
  /**
82
87
  * Fetches groups list
83
88
  */
84
- const fetchGroups = (next = endpoint.url({})) => __awaiter(this, void 0, void 0, function* () {
85
- const response = yield http.request({
86
- url: next,
87
- method: endpoint.method
89
+ const fetchGroups = () => {
90
+ let groupService;
91
+ if (general) {
92
+ groupService = GroupService.searchGroups(endpointQueryParams);
93
+ }
94
+ else {
95
+ groupService = GroupService.getUserGroups(endpointQueryParams);
96
+ }
97
+ groupService
98
+ .then((res) => {
99
+ setGroups(res.results);
100
+ setNext(res.next);
101
+ setLoading(false);
102
+ })
103
+ .catch((error) => {
104
+ Logger.error(SCOPE_SC_UI, error);
88
105
  });
89
- const data = response.data;
90
- return data.next ? data.results.concat(yield fetchGroups(data.next)) : data.results;
91
- });
106
+ };
92
107
  /**
93
108
  * On mount, fetches groups list
94
109
  */
@@ -96,23 +111,10 @@ export default function Groups(inProps) {
96
111
  if (!contentAvailability && !authUserId) {
97
112
  return;
98
113
  }
99
- else if (prefetchedGroups.length) {
100
- setGroups(prefetchedGroups);
101
- setLoading(false);
102
- }
103
114
  else {
104
- fetchGroups()
105
- .then((data) => {
106
- if (isMountedRef.current) {
107
- setGroups(data);
108
- setLoading(false);
109
- }
110
- })
111
- .catch((error) => {
112
- Logger.error(SCOPE_SC_UI, error);
113
- });
115
+ fetchGroups();
114
116
  }
115
- }, [contentAvailability, authUserId, prefetchedGroups.length]);
117
+ }, [contentAvailability, authUserId]);
116
118
  const handleSubscribe = (group) => {
117
119
  if (!general) {
118
120
  const newGroups = [...groups];
@@ -120,6 +122,22 @@ export default function Groups(inProps) {
120
122
  setGroups(_updated);
121
123
  }
122
124
  };
125
+ const handleNext = useMemo(() => () => {
126
+ if (!next) {
127
+ return;
128
+ }
129
+ return http
130
+ .request({
131
+ url: next,
132
+ method: general ? Endpoints.SearchGroups.method : Endpoints.GetUserGroups.method
133
+ })
134
+ .then((res) => {
135
+ setGroups([...groups, ...res.data.results]);
136
+ setNext(res.data.next);
137
+ })
138
+ .catch((error) => console.log(error))
139
+ .then(() => setLoading(false));
140
+ }, [next]);
123
141
  /**
124
142
  * Get groups filtered
125
143
  */
@@ -146,15 +164,22 @@ export default function Groups(inProps) {
146
164
  const content = (React.createElement(React.Fragment, null,
147
165
  showFilters && (React.createElement(Grid, { container: true, direction: "row", justifyContent: "center", alignItems: "center", className: classes.filters }, filters ? (filters) : (React.createElement(Grid, { item: true, xs: 12, md: 6 },
148
166
  React.createElement(TextField, { fullWidth: true, value: filterName, label: React.createElement(FormattedMessage, { id: "ui.groups.filterByName", defaultMessage: "ui.groups.filterByName" }), variant: "outlined", onChange: handleOnChangeFilterName, disabled: loading }))))),
149
- loading ? (React.createElement(Skeleton, null)) : (React.createElement(React.Fragment, null, !groups.length ? (React.createElement(Box, { className: classes.noResults },
167
+ React.createElement(React.Fragment, null, !groups.length ? (React.createElement(Box, { className: classes.noResults },
150
168
  React.createElement(Typography, { variant: "h4" },
151
169
  React.createElement(FormattedMessage, { id: "ui.groups.noGroups.title", defaultMessage: "ui.groups.noGroups.title" })),
152
170
  React.createElement(Typography, { variant: "body1" },
153
- React.createElement(FormattedMessage, { id: "ui.groups.noGroups.subtitle", defaultMessage: "ui.groups.noGroups.subtitle" })))) : (React.createElement(Grid, { container: true, spacing: { xs: 2 }, className: classes.groups }, filteredGroups.map((group) => (React.createElement(Grid, { item: true, xs: 12, sm: 8, md: 6, key: group.id, className: classes.item },
154
- React.createElement(Group, Object.assign({ group: group, groupId: group.id, groupSubscribeButtonProps: { onSubscribe: handleSubscribe } }, GroupComponentProps)))))))))));
171
+ React.createElement(FormattedMessage, { id: "ui.groups.noGroups.subtitle", defaultMessage: "ui.groups.noGroups.subtitle" })))) : (React.createElement(InfiniteScroll, { dataLength: groups.length, next: handleNext, hasMoreNext: Boolean(next), loaderNext: React.createElement(GroupSkeleton, null), endMessage: React.createElement(Typography, { component: "div", className: classes.endMessage },
172
+ React.createElement(FormattedMessage, { id: "ui.groups.endMessage", defaultMessage: "ui.groups.endMessage", values: {
173
+ button: (chunk) => (React.createElement(Button, { color: "secondary", variant: "text", onClick: handleScrollUp }, chunk))
174
+ } })) },
175
+ React.createElement(Grid, { container: true, spacing: { xs: 2 }, className: classes.groups }, filteredGroups.map((group) => (React.createElement(Grid, { item: true, xs: 12, sm: 8, md: 6, key: group.id, className: classes.item },
176
+ React.createElement(Group, Object.assign({ group: group, groupId: group.id, groupSubscribeButtonProps: { onSubscribe: handleSubscribe } }, GroupComponentProps)))))))))));
155
177
  // RENDER
156
178
  if (!contentAvailability && !scUserContext.user) {
157
179
  return React.createElement(HiddenPlaceholder, null);
158
180
  }
181
+ if (loading) {
182
+ return React.createElement(Skeleton, null);
183
+ }
159
184
  return (React.createElement(Root, Object.assign({ className: classNames(classes.root, className) }, rest), content));
160
185
  }