@blocklet/list 0.8.34 → 0.8.37

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/base.js CHANGED
@@ -40,7 +40,7 @@ function ListBase() {
40
40
 
41
41
  const {
42
42
  handleDeveloper,
43
- blockletList,
43
+ finalBlockletList,
44
44
  filters,
45
45
  developerName,
46
46
  handleSort,
@@ -111,7 +111,7 @@ function ListBase() {
111
111
  }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactErrorBoundary.ErrorBoundary, {
112
112
  FallbackComponent: _ErrorBoundary.ErrorFallback,
113
113
  children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_list.default, {
114
- blocklets: blockletList
114
+ blocklets: finalBlockletList
115
115
  })
116
116
  })]
117
117
  })]
@@ -43,6 +43,7 @@ function FilterChip(_ref) {
43
43
  if (!label) return null;
44
44
  return /*#__PURE__*/(0, _jsxRuntime.jsx)(StyleDiv, _objectSpread(_objectSpread({}, containerProps), {}, {
45
45
  children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.Chip, {
46
+ "data-cy": "filter-tag",
46
47
  icon: icon,
47
48
  label: label,
48
49
  onDelete: onDelete
@@ -5,6 +5,8 @@ Object.defineProperty(exports, "__esModule", {
5
5
  });
6
6
  exports.default = BlockletList;
7
7
 
8
+ var _react = require("react");
9
+
8
10
  var _propTypes = _interopRequireDefault(require("prop-types"));
9
11
 
10
12
  var _styledComponents = _interopRequireDefault(require("styled-components"));
@@ -17,6 +19,8 @@ var _Grid = _interopRequireDefault(require("@mui/material/Grid"));
17
19
 
18
20
  var _CircularProgress = _interopRequireDefault(require("@mui/material/CircularProgress"));
19
21
 
22
+ var _reactInfiniteScrollHook = _interopRequireDefault(require("react-infinite-scroll-hook"));
23
+
20
24
  var _ErrorBoundary = require("@arcblock/ux/lib/ErrorBoundary");
21
25
 
22
26
  var _empty = require("./empty");
@@ -50,13 +54,21 @@ function BlockletList(_ref) {
50
54
  errors,
51
55
  loadings,
52
56
  selectedCategory,
53
- blockletList,
57
+ finalBlockletList,
54
58
  getCategoryLocale,
55
59
  filters,
56
60
  t,
61
+ hasNextPage,
62
+ loadMore,
57
63
  endpoint
58
64
  } = (0, _filter.useFilterContext)();
59
65
  const showFilterTip = !!selectedCategory || !!filters.price;
66
+ const [sentryRef] = (0, _reactInfiniteScrollHook.default)({
67
+ loading: loadings.fetchBlockletsLoading,
68
+ hasNextPage,
69
+ onLoadMore: loadMore,
70
+ rootMargin: '0px 0px 400px 0px'
71
+ });
60
72
 
61
73
  if (errors.fetchBlockletsError) {
62
74
  return /*#__PURE__*/(0, _jsxRuntime.jsx)(_ErrorBoundary.ErrorFallback, {
@@ -73,7 +85,7 @@ function BlockletList(_ref) {
73
85
  });
74
86
  }
75
87
 
76
- if (filters.keyword && showFilterTip && blockletList.length === 0) {
88
+ if (filters.keyword && showFilterTip && finalBlockletList.length === 0) {
77
89
  return /*#__PURE__*/(0, _jsxRuntime.jsxs)(CustomEmpty, {
78
90
  children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_empty.EmptyTitle, {
79
91
  primaryStart: t('blocklet.noBlockletPart1'),
@@ -86,7 +98,7 @@ function BlockletList(_ref) {
86
98
  });
87
99
  }
88
100
 
89
- if (filters.keyword && blockletList.length === 0) {
101
+ if (filters.keyword && finalBlockletList.length === 0) {
90
102
  return /*#__PURE__*/(0, _jsxRuntime.jsxs)(CustomEmpty, {
91
103
  children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_empty.EmptyTitle, {
92
104
  primaryStart: t('blocklet.noBlockletPart1'),
@@ -98,7 +110,7 @@ function BlockletList(_ref) {
98
110
  });
99
111
  }
100
112
 
101
- if (showFilterTip && blockletList.length === 0) {
113
+ if (showFilterTip && finalBlockletList.length === 0) {
102
114
  const categoryLocale = getCategoryLocale(selectedCategory);
103
115
  return /*#__PURE__*/(0, _jsxRuntime.jsxs)(CustomEmpty, {
104
116
  children: [categoryLocale ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_empty.EmptyTitle, {
@@ -111,16 +123,16 @@ function BlockletList(_ref) {
111
123
  });
112
124
  }
113
125
 
114
- if (blockletList.length === 0) {
126
+ if (finalBlockletList.length === 0) {
115
127
  return /*#__PURE__*/(0, _jsxRuntime.jsx)(CustomEmpty, {
116
128
  children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_empty.NoResults, {})
117
129
  });
118
130
  }
119
131
 
120
- return /*#__PURE__*/(0, _jsxRuntime.jsx)(StyledGrid, _objectSpread(_objectSpread({
132
+ return /*#__PURE__*/(0, _jsxRuntime.jsxs)(StyledGrid, _objectSpread(_objectSpread({
121
133
  container: true
122
134
  }, rest), {}, {
123
- children: blocklets.map(blocklet => /*#__PURE__*/(0, _jsxRuntime.jsx)(StyledGridItem, {
135
+ children: [blocklets.map(blocklet => /*#__PURE__*/(0, _jsxRuntime.jsx)(StyledGridItem, {
124
136
  item: true,
125
137
  lg: 4,
126
138
  md: 6,
@@ -129,9 +141,20 @@ function BlockletList(_ref) {
129
141
  "data-blocklet-did": blocklet.did,
130
142
  children: blockletRender({
131
143
  blocklet,
132
- blocklets: blockletList
144
+ blocklets: finalBlockletList
145
+ })
146
+ }, blocklet.did)), hasNextPage && /*#__PURE__*/(0, _jsxRuntime.jsx)(StyledGridItem, {
147
+ item: true,
148
+ md: 12,
149
+ sm: 12,
150
+ xs: 12,
151
+ ref: sentryRef,
152
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_Box.default, {
153
+ display: "flex",
154
+ justifyContent: "center",
155
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_CircularProgress.default, {})
133
156
  })
134
- }, blocklet.did))
157
+ })]
135
158
  }));
136
159
  }
137
160
 
@@ -143,10 +166,10 @@ const StyledGrid = (0, _styledComponents.default)(_Grid.default).withConfig({
143
166
  displayName: "list__StyledGrid",
144
167
  componentId: "sc-1guvpon-0"
145
168
  })(["&.MuiGrid-root{width:auto;margin:0 -16px;}"]);
146
- const StyledGridItem = (0, _styledComponents.default)(_Grid.default).withConfig({
169
+ const StyledGridItem = /*#__PURE__*/(0, _react.memo)((0, _styledComponents.default)(_Grid.default).withConfig({
147
170
  displayName: "list__StyledGridItem",
148
171
  componentId: "sc-1guvpon-1"
149
- })(["@media (max-width:", "px){&.MuiGrid-item{padding-bottom:0px;}}@media (min-width:", "px){&.MuiGrid-item{margin-bottom:", ";}}"], props => props.theme.breakpoints.values.sm, props => props.theme.breakpoints.values.sm, props => props.theme.spacing(2));
172
+ })(["@media (max-width:", "px){&.MuiGrid-item{padding-bottom:0px;}}@media (min-width:", "px){&.MuiGrid-item{margin-bottom:", ";}}"], props => props.theme.breakpoints.values.sm, props => props.theme.breakpoints.values.sm, props => props.theme.spacing(2)));
150
173
  const CustomEmpty = (0, _styledComponents.default)(_Empty.default).withConfig({
151
174
  displayName: "list__CustomEmpty",
152
175
  componentId: "sc-1guvpon-2"
@@ -95,6 +95,11 @@ function FilterProvider(_ref) {
95
95
  initialData: [],
96
96
  manual: true
97
97
  });
98
+ const paginateState = (0, _ahooks.useReactive)({
99
+ currentPage: 1,
100
+ pageSize: (0, _utils.isMobileScreen)() ? 10 : 18,
101
+ defaultCurrentPage: 1
102
+ });
98
103
 
99
104
  const finalFilters = _objectSpread({
100
105
  sortBy: 'popularity',
@@ -104,10 +109,11 @@ function FilterProvider(_ref) {
104
109
  const selectedCategory = finalFilters.category;
105
110
  const hasDeveloperFilter = !!finalFilters.developer;
106
111
  const categoryState = (0, _react.useMemo)(() => {
112
+ // 当按作者过滤时,需要从所有blocklets中找出属于作者的分类
107
113
  return !hasDeveloperFilter ? {
108
114
  data: allCategories
109
- } : (0, _utils.getCategories)(allBlocklets, finalFilters.developer);
110
- }, [hasDeveloperFilter, allCategories]);
115
+ } : (0, _utils.getCategoriesByAuthor)(allBlocklets, finalFilters.developer);
116
+ }, [hasDeveloperFilter, allCategories, allBlocklets]);
111
117
  const blockletList = (0, _react.useMemo)(() => {
112
118
  var _finalFilters$keyword;
113
119
 
@@ -153,6 +159,10 @@ function FilterProvider(_ref) {
153
159
 
154
160
  return (0, _orderBy.default)(blocklets, [sortMap[finalFilters.sortBy]], [finalFilters.sortDirection]);
155
161
  }, [allBlocklets, finalFilters]);
162
+ const finalBlockletList = (0, _react.useMemo)(() => {
163
+ // 前端分页 currentPage 当前页数 pageSize 每页条数
164
+ return blockletList.slice((paginateState.defaultCurrentPage - 1) * paginateState.pageSize, paginateState.currentPage * paginateState.pageSize);
165
+ }, [blockletList, paginateState]);
156
166
  const categoryList = (0, _react.useMemo)(() => {
157
167
  const list = categoryState.data || []; // 分类按照名称排序
158
168
 
@@ -180,7 +190,7 @@ function FilterProvider(_ref) {
180
190
  fetchCategoriesLoading
181
191
  },
182
192
  endpoint,
183
- blockletList,
193
+ finalBlockletList,
184
194
  t: translate,
185
195
  filters: finalFilters,
186
196
  selectedCategory,
@@ -189,6 +199,7 @@ function FilterProvider(_ref) {
189
199
  locale,
190
200
  categoryOptions,
191
201
  priceOptions,
202
+ hasNextPage: blockletList.length >= paginateState.pageSize * paginateState.currentPage,
192
203
  handleSort: sort => {
193
204
  const changeData = _objectSpread(_objectSpread({}, finalFilters), {}, {
194
205
  sortBy: sort,
@@ -231,6 +242,18 @@ function FilterProvider(_ref) {
231
242
 
232
243
  onFilterChange(changeData);
233
244
  },
245
+ handlePage: page => {
246
+ const changeData = _objectSpread(_objectSpread({}, finalFilters), {}, {
247
+ currentPage: page
248
+ });
249
+
250
+ onFilterChange(changeData);
251
+ },
252
+ loadMore: () => {
253
+ setTimeout(() => {
254
+ paginateState.currentPage += 1;
255
+ }, 1000);
256
+ },
234
257
  getCategoryLocale: category => {
235
258
  if (!category) return null;
236
259
  let result = null;
@@ -243,6 +266,10 @@ function FilterProvider(_ref) {
243
266
  return result;
244
267
  },
245
268
 
269
+ get allBlocklets() {
270
+ return allBlocklets || [];
271
+ },
272
+
246
273
  get developerName() {
247
274
  var _blocklets$find, _blocklets$find$owner;
248
275
 
package/lib/libs/utils.js CHANGED
@@ -3,7 +3,7 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.urlStringify = exports.replaceTranslate = exports.removeUndefined = exports.getStoreDetail = exports.getSortOptions = exports.getPrices = exports.getCategoryOptions = exports.getCategories = exports.formatLogoPath = exports.formatError = exports.filterBlockletByPrice = void 0;
6
+ exports.urlStringify = exports.replaceTranslate = exports.removeUndefined = exports.isMobileScreen = exports.getStoreDetail = exports.getSortOptions = exports.getPrices = exports.getCategoryOptions = exports.getCategoriesByAuthor = exports.formatLogoPath = exports.formatError = exports.filterBlockletByPrice = void 0;
7
7
 
8
8
  var _urlJoin = _interopRequireDefault(require("url-join"));
9
9
 
@@ -59,7 +59,7 @@ const getCategoryOptions = function getCategoryOptions() {
59
59
  }));
60
60
  };
61
61
  /**
62
- * 从开发者所属 blocklets 中的得到 Categories
62
+ * blocklets 中的得到属于作者的 Categories
63
63
  * @param {*} list
64
64
  * @param {*} developerDid
65
65
  * @returns
@@ -68,7 +68,7 @@ const getCategoryOptions = function getCategoryOptions() {
68
68
 
69
69
  exports.getCategoryOptions = getCategoryOptions;
70
70
 
71
- const getCategories = function getCategories() {
71
+ const getCategoriesByAuthor = function getCategoriesByAuthor() {
72
72
  let list = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
73
73
  let developerDid = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;
74
74
  const filterList = list.filter(item => developerDid ? item.owner.did === developerDid : true);
@@ -87,7 +87,7 @@ const getCategories = function getCategories() {
87
87
  */
88
88
 
89
89
 
90
- exports.getCategories = getCategories;
90
+ exports.getCategoriesByAuthor = getCategoriesByAuthor;
91
91
 
92
92
  const filterBlockletByPrice = function filterBlockletByPrice() {
93
93
  let list = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
@@ -159,4 +159,10 @@ const urlStringify = obj => {
159
159
  return new URLSearchParams(removeUndefined(obj)).toString();
160
160
  };
161
161
 
162
- exports.urlStringify = urlStringify;
162
+ exports.urlStringify = urlStringify;
163
+
164
+ const isMobileScreen = () => {
165
+ return window.innerWidth <= 600;
166
+ };
167
+
168
+ exports.isMobileScreen = isMobileScreen;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blocklet/list",
3
- "version": "0.8.34",
3
+ "version": "0.8.37",
4
4
  "description": "Common ux components of blocklet",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -38,7 +38,7 @@
38
38
  "react": ">=18.1.0"
39
39
  },
40
40
  "dependencies": {
41
- "@arcblock/ux": "^2.1.19",
41
+ "@arcblock/ux": "^2.1.27",
42
42
  "@emotion/react": "^11.9.0",
43
43
  "@emotion/styled": "^11.8.1",
44
44
  "@mui/icons-material": "^5.6.2",
@@ -48,6 +48,7 @@
48
48
  "lodash": "^4.17.21",
49
49
  "prop-types": "^15.7.2",
50
50
  "react-error-boundary": "^3.1.4",
51
+ "react-infinite-scroll-hook": "^4.0.3",
51
52
  "styled-components": "5.3.5",
52
53
  "url-join": "^4.0.1"
53
54
  },
@@ -63,5 +64,5 @@
63
64
  "eslint": "^8.16.0",
64
65
  "prettier": "^2.6.2"
65
66
  },
66
- "gitHead": "fed8918e15b5bd8cb3f4bac84b32434b58dda21b"
67
+ "gitHead": "522321b1eb13c7c2ec7312c22eba8c9b2397972f"
67
68
  }
package/src/base.js CHANGED
@@ -16,7 +16,7 @@ import Search from './components/search';
16
16
  function ListBase() {
17
17
  const {
18
18
  handleDeveloper,
19
- blockletList,
19
+ finalBlockletList,
20
20
  filters,
21
21
  developerName,
22
22
  handleSort,
@@ -79,7 +79,7 @@ function ListBase() {
79
79
  />
80
80
  </Box>
81
81
  <ErrorBoundary FallbackComponent={ErrorFallback}>
82
- <BlockletList blocklets={blockletList} />
82
+ <BlockletList blocklets={finalBlockletList} />
83
83
  </ErrorBoundary>
84
84
  </StyledMin>
85
85
  </Box>
@@ -15,7 +15,7 @@ function FilterChip({ label, icon, onDelete, ...containerProps }) {
15
15
  if (!label) return null;
16
16
  return (
17
17
  <StyleDiv {...containerProps}>
18
- <Chip icon={icon} label={label} onDelete={onDelete} />
18
+ <Chip data-cy="filter-tag" icon={icon} label={label} onDelete={onDelete} />
19
19
  </StyleDiv>
20
20
  );
21
21
  }
@@ -1,20 +1,40 @@
1
+ import { memo } from 'react';
1
2
  import PropTypes from 'prop-types';
2
3
  import styled from 'styled-components';
3
4
  import Empty from '@arcblock/ux/lib/Empty';
4
5
  import Box from '@mui/material/Box';
5
6
  import Grid from '@mui/material/Grid';
6
7
  import CircularProgress from '@mui/material/CircularProgress';
8
+ import useInfiniteScroll from 'react-infinite-scroll-hook';
7
9
  import { ErrorFallback } from '@arcblock/ux/lib/ErrorBoundary';
8
10
 
9
11
  import { NoResults, EmptyTitle, NoResultsTips } from './empty';
10
12
  import { useFilterContext } from '../../contexts/filter';
11
13
 
12
14
  export default function BlockletList({ blocklets, ...rest }) {
13
- const { blockletRender, errors, loadings, selectedCategory, blockletList, getCategoryLocale, filters, t, endpoint } =
14
- useFilterContext();
15
+ const {
16
+ blockletRender,
17
+ errors,
18
+ loadings,
19
+ selectedCategory,
20
+ finalBlockletList,
21
+ getCategoryLocale,
22
+ filters,
23
+ t,
24
+ hasNextPage,
25
+ loadMore,
26
+ endpoint,
27
+ } = useFilterContext();
15
28
 
16
29
  const showFilterTip = !!selectedCategory || !!filters.price;
17
30
 
31
+ const [sentryRef] = useInfiniteScroll({
32
+ loading: loadings.fetchBlockletsLoading,
33
+ hasNextPage,
34
+ onLoadMore: loadMore,
35
+ rootMargin: '0px 0px 400px 0px',
36
+ });
37
+
18
38
  if (errors.fetchBlockletsError) {
19
39
  return (
20
40
  <ErrorFallback
@@ -29,7 +49,7 @@ export default function BlockletList({ blocklets, ...rest }) {
29
49
  </Box>
30
50
  );
31
51
  }
32
- if (filters.keyword && showFilterTip && blockletList.length === 0) {
52
+ if (filters.keyword && showFilterTip && finalBlockletList.length === 0) {
33
53
  return (
34
54
  <CustomEmpty>
35
55
  <EmptyTitle
@@ -41,7 +61,7 @@ export default function BlockletList({ blocklets, ...rest }) {
41
61
  </CustomEmpty>
42
62
  );
43
63
  }
44
- if (filters.keyword && blockletList.length === 0) {
64
+ if (filters.keyword && finalBlockletList.length === 0) {
45
65
  return (
46
66
  <CustomEmpty>
47
67
  <EmptyTitle
@@ -53,7 +73,7 @@ export default function BlockletList({ blocklets, ...rest }) {
53
73
  </CustomEmpty>
54
74
  );
55
75
  }
56
- if (showFilterTip && blockletList.length === 0) {
76
+ if (showFilterTip && finalBlockletList.length === 0) {
57
77
  const categoryLocale = getCategoryLocale(selectedCategory);
58
78
  return (
59
79
  <CustomEmpty>
@@ -70,7 +90,7 @@ export default function BlockletList({ blocklets, ...rest }) {
70
90
  </CustomEmpty>
71
91
  );
72
92
  }
73
- if (blockletList.length === 0) {
93
+ if (finalBlockletList.length === 0) {
74
94
  return (
75
95
  <CustomEmpty>
76
96
  <NoResults />
@@ -82,9 +102,16 @@ export default function BlockletList({ blocklets, ...rest }) {
82
102
  <StyledGrid container {...rest}>
83
103
  {blocklets.map((blocklet) => (
84
104
  <StyledGridItem item lg={4} md={6} sm={6} xs={12} key={blocklet.did} data-blocklet-did={blocklet.did}>
85
- {blockletRender({ blocklet, blocklets: blockletList })}
105
+ {blockletRender({ blocklet, blocklets: finalBlockletList })}
86
106
  </StyledGridItem>
87
107
  ))}
108
+ {hasNextPage && (
109
+ <StyledGridItem item md={12} sm={12} xs={12} ref={sentryRef}>
110
+ <Box display="flex" justifyContent="center">
111
+ <CircularProgress />
112
+ </Box>
113
+ </StyledGridItem>
114
+ )}
88
115
  </StyledGrid>
89
116
  );
90
117
  }
@@ -102,7 +129,7 @@ const StyledGrid = styled(Grid)`
102
129
  }
103
130
  `;
104
131
 
105
- const StyledGridItem = styled(Grid)`
132
+ const StyledGridItem = memo(styled(Grid)`
106
133
  @media (max-width: ${(props) => props.theme.breakpoints.values.sm}px) {
107
134
  &.MuiGrid-item {
108
135
  padding-bottom: 0px;
@@ -113,7 +140,7 @@ const StyledGridItem = styled(Grid)`
113
140
  margin-bottom: ${(props) => props.theme.spacing(2)};
114
141
  }
115
142
  }
116
- `;
143
+ `);
117
144
  const CustomEmpty = styled(Empty)`
118
145
  text-align: center;
119
146
  .primary {
@@ -1,11 +1,17 @@
1
1
  import { useContext, createContext, useMemo, useEffect } from 'react';
2
2
  import PropTypes from 'prop-types';
3
- import { useRequest } from 'ahooks';
3
+ import { useRequest, useReactive } from 'ahooks';
4
4
  import orderBy from 'lodash/orderBy';
5
5
  import axios from 'axios';
6
6
  import isArray from 'lodash/isArray';
7
-
8
- import { getCategories, filterBlockletByPrice, replaceTranslate, getPrices, getCategoryOptions } from '../libs/utils';
7
+ import {
8
+ getCategoriesByAuthor,
9
+ filterBlockletByPrice,
10
+ replaceTranslate,
11
+ getPrices,
12
+ getCategoryOptions,
13
+ isMobileScreen,
14
+ } from '../libs/utils';
9
15
  import translations from '../assets/locale';
10
16
  import { propTypes, defaultProps } from '../libs/prop-types';
11
17
 
@@ -48,12 +54,14 @@ function FilterProvider({ filters, children, endpoint, locale, blockletRender, o
48
54
  { initialData: [], manual: true }
49
55
  );
50
56
 
57
+ const paginateState = useReactive({ currentPage: 1, pageSize: isMobileScreen() ? 10 : 18, defaultCurrentPage: 1 });
51
58
  const finalFilters = { sortBy: 'popularity', sortDirection: 'desc', ...filters };
52
59
  const selectedCategory = finalFilters.category;
53
60
  const hasDeveloperFilter = !!finalFilters.developer;
54
61
  const categoryState = useMemo(() => {
55
- return !hasDeveloperFilter ? { data: allCategories } : getCategories(allBlocklets, finalFilters.developer);
56
- }, [hasDeveloperFilter, allCategories]);
62
+ // 当按作者过滤时,需要从所有blocklets中找出属于作者的分类
63
+ return !hasDeveloperFilter ? { data: allCategories } : getCategoriesByAuthor(allBlocklets, finalFilters.developer);
64
+ }, [hasDeveloperFilter, allCategories, allBlocklets]);
57
65
 
58
66
  const blockletList = useMemo(() => {
59
67
  const sortByName = (x) => x?.title?.toLocaleLowerCase() || x?.name?.toLocaleLowerCase(); // 按名称排序
@@ -89,6 +97,14 @@ function FilterProvider({ filters, children, endpoint, locale, blockletRender, o
89
97
  return orderBy(blocklets, [sortMap[finalFilters.sortBy]], [finalFilters.sortDirection]);
90
98
  }, [allBlocklets, finalFilters]);
91
99
 
100
+ const finalBlockletList = useMemo(() => {
101
+ // 前端分页 currentPage 当前页数 pageSize 每页条数
102
+ return blockletList.slice(
103
+ (paginateState.defaultCurrentPage - 1) * paginateState.pageSize,
104
+ paginateState.currentPage * paginateState.pageSize
105
+ );
106
+ }, [blockletList, paginateState]);
107
+
92
108
  const categoryList = useMemo(() => {
93
109
  const list = categoryState.data || [];
94
110
  // 分类按照名称排序
@@ -109,7 +125,7 @@ function FilterProvider({ filters, children, endpoint, locale, blockletRender, o
109
125
  errors: { fetchBlockletsError, fetchCategoriesError },
110
126
  loadings: { fetchBlockletsLoading, fetchCategoriesLoading },
111
127
  endpoint,
112
- blockletList,
128
+ finalBlockletList,
113
129
  t: translate,
114
130
  filters: finalFilters,
115
131
  selectedCategory,
@@ -118,6 +134,7 @@ function FilterProvider({ filters, children, endpoint, locale, blockletRender, o
118
134
  locale,
119
135
  categoryOptions,
120
136
  priceOptions,
137
+ hasNextPage: blockletList.length >= paginateState.pageSize * paginateState.currentPage,
121
138
  handleSort: (sort) => {
122
139
  const changeData = {
123
140
  ...finalFilters,
@@ -149,6 +166,15 @@ function FilterProvider({ filters, children, endpoint, locale, blockletRender, o
149
166
  const changeData = { ...finalFilters, developer: developer || undefined };
150
167
  onFilterChange(changeData);
151
168
  },
169
+ handlePage: (page) => {
170
+ const changeData = { ...finalFilters, currentPage: page };
171
+ onFilterChange(changeData);
172
+ },
173
+ loadMore: () => {
174
+ setTimeout(() => {
175
+ paginateState.currentPage += 1;
176
+ }, 1000);
177
+ },
152
178
  getCategoryLocale: (category) => {
153
179
  if (!category) return null;
154
180
  let result = null;
@@ -158,6 +184,9 @@ function FilterProvider({ filters, children, endpoint, locale, blockletRender, o
158
184
  }
159
185
  return result;
160
186
  },
187
+ get allBlocklets() {
188
+ return allBlocklets || [];
189
+ },
161
190
  get developerName() {
162
191
  const blocklets = allBlocklets || [];
163
192
  return blocklets.find((blocklet) => blocklet.owner.did === finalFilters.developer)?.owner?.name || '';
package/src/libs/utils.js CHANGED
@@ -5,10 +5,10 @@ const isFreeBlocklet = (meta) => {
5
5
  if (!meta.payment) {
6
6
  return true;
7
7
  }
8
-
9
8
  const priceList = (meta.payment.price || []).map((x) => x.value || 0);
10
9
  return priceList.every((x) => x === 0);
11
10
  };
11
+
12
12
  const getSortOptions = (t) => {
13
13
  return [
14
14
  {
@@ -39,12 +39,12 @@ const getCategoryOptions = (list = [], locale = 'en') => {
39
39
  return list.map((item) => ({ name: item.locales[locale], value: item.name }));
40
40
  };
41
41
  /**
42
- * 从开发者所属 blocklets 中的得到 Categories
42
+ * blocklets 中的得到属于作者的 Categories
43
43
  * @param {*} list
44
44
  * @param {*} developerDid
45
45
  * @returns
46
46
  */
47
- const getCategories = (list = [], developerDid = null) => {
47
+ const getCategoriesByAuthor = (list = [], developerDid = null) => {
48
48
  const filterList = list.filter((item) => (developerDid ? item.owner.did === developerDid : true));
49
49
  const Categories = filterList.map((item) => item.category);
50
50
  const res = new Map();
@@ -106,10 +106,14 @@ const urlStringify = (obj) => {
106
106
  return new URLSearchParams(removeUndefined(obj)).toString();
107
107
  };
108
108
 
109
+ const isMobileScreen = () => {
110
+ return window.innerWidth <= 600;
111
+ };
112
+
109
113
  export {
110
114
  getSortOptions,
111
115
  getPrices,
112
- getCategories,
116
+ getCategoriesByAuthor,
113
117
  filterBlockletByPrice,
114
118
  getStoreDetail,
115
119
  formatLogoPath,
@@ -118,4 +122,5 @@ export {
118
122
  removeUndefined,
119
123
  urlStringify,
120
124
  getCategoryOptions,
125
+ isMobileScreen,
121
126
  };