@blocklet/list 0.8.64 → 0.9.0

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
- finalBlockletList,
43
+ blockletList,
44
44
  filters,
45
45
  developerName,
46
46
  handleSort,
@@ -62,52 +62,54 @@ function ListBase() {
62
62
  mdDown: true,
63
63
  children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_aside.default, {})
64
64
  }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(StyledMin, {
65
- children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_material.Box, {
66
- className: "filter-bar",
67
- display: "flex",
68
- alignItems: "center",
69
- children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(StyledSearch, {
70
- className: "search-container",
71
- placeholder: t('common.searchStore')
65
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(FilterContainer, {
66
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_material.Box, {
67
+ className: "filter-bar",
68
+ display: "flex",
69
+ alignItems: "center",
70
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(StyledSearch, {
71
+ className: "search-container",
72
+ placeholder: t('common.searchStore')
73
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_material.Box, {
74
+ mt: 0,
75
+ ml: "16px",
76
+ className: "filter-container",
77
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_material.Hidden, {
78
+ mdUp: true,
79
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_filter2.FilterIcon, {})
80
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_customSelect.default, {
81
+ value: filters.sortBy,
82
+ options: sortOptions,
83
+ title: sortLocale,
84
+ icon: /*#__PURE__*/(0, _jsxRuntime.jsx)(_Sort.default, {}),
85
+ onChange: v => {
86
+ handleSort(v);
87
+ }
88
+ })]
89
+ })]
72
90
  }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_material.Box, {
73
- mt: 0,
74
- ml: "16px",
75
- className: "filter-container",
76
- children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_material.Hidden, {
77
- mdUp: true,
78
- children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_filter2.FilterIcon, {})
79
- }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_customSelect.default, {
80
- value: filters.sortBy,
81
- options: sortOptions,
82
- title: sortLocale,
83
- icon: /*#__PURE__*/(0, _jsxRuntime.jsx)(_Sort.default, {}),
84
- onChange: v => {
85
- handleSort(v);
91
+ display: "flex",
92
+ flexWrap: "wrap",
93
+ alignItems: "center",
94
+ mb: "16px",
95
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_filter2.CustomChip, {
96
+ label: developerName,
97
+ icon: /*#__PURE__*/(0, _jsxRuntime.jsx)(_Face.default, {}),
98
+ onDelete: () => {
99
+ handleDeveloper(null);
100
+ }
101
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_filter2.CustomChip, {
102
+ label: categoryLocale,
103
+ onDelete: () => {
104
+ handleCategory(null);
105
+ }
106
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_filter2.CustomChip, {
107
+ label: priceLocale,
108
+ onDelete: () => {
109
+ handlePrice(null);
86
110
  }
87
111
  })]
88
112
  })]
89
- }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_material.Box, {
90
- display: "flex",
91
- flexWrap: "wrap",
92
- alignItems: "center",
93
- mb: "16px",
94
- children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_filter2.CustomChip, {
95
- label: developerName,
96
- icon: /*#__PURE__*/(0, _jsxRuntime.jsx)(_Face.default, {}),
97
- onDelete: () => {
98
- handleDeveloper(null);
99
- }
100
- }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_filter2.CustomChip, {
101
- label: categoryLocale,
102
- onDelete: () => {
103
- handleCategory(null);
104
- }
105
- }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_filter2.CustomChip, {
106
- label: priceLocale,
107
- onDelete: () => {
108
- handlePrice(null);
109
- }
110
- })]
111
113
  }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactErrorBoundary.ErrorBoundary, {
112
114
  FallbackComponent: _ErrorBoundary.ErrorFallback,
113
115
  children: /*#__PURE__*/(0, _jsxRuntime.jsx)("div", {
@@ -117,7 +119,7 @@ function ListBase() {
117
119
  overflow: 'auto'
118
120
  },
119
121
  children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_list.default, {
120
- blocklets: finalBlockletList
122
+ blocklets: blockletList
121
123
  })
122
124
  })
123
125
  })]
@@ -134,5 +136,11 @@ const StyledSearch = (0, _styledComponents.default)(_search.default).withConfig(
134
136
  displayName: "base__StyledSearch",
135
137
  componentId: "sc-wumzlv-1"
136
138
  })(["background-color:", ";"], props => props.theme.palette.grey[50]);
139
+
140
+ const FilterContainer = _styledComponents.default.div.withConfig({
141
+ displayName: "base__FilterContainer",
142
+ componentId: "sc-wumzlv-2"
143
+ })(["position:sticky;top:0;z-index:999;background-color:white;"]);
144
+
137
145
  var _default = ListBase;
138
146
  exports.default = _default;
@@ -50,7 +50,7 @@ function Aside() {
50
50
  const StyledAside = _styledComponents.default.aside.withConfig({
51
51
  displayName: "aside__StyledAside",
52
52
  componentId: "sc-1wkvsv6-0"
53
- })(["width:220px;margin-right:", ";height:100%;overflow-y:auto;"], props => props.theme.spacing(2));
53
+ })(["width:220px;margin-right:", ";height:100%;position:sticky;top:0;overflow-y:auto;z-index:999;"], props => props.theme.spacing(2));
54
54
 
55
55
  Aside.propTypes = {};
56
56
  Aside.defaultProps = {};
@@ -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
+ color: "primary",
46
47
  "data-cy": "filter-tag",
47
48
  icon: icon,
48
49
  label: label,
@@ -54,7 +54,6 @@ function BlockletList(_ref) {
54
54
  errors,
55
55
  loadings,
56
56
  selectedCategory,
57
- finalBlockletList,
58
57
  getCategoryLocale,
59
58
  filters,
60
59
  t,
@@ -62,9 +61,10 @@ function BlockletList(_ref) {
62
61
  loadMore,
63
62
  endpoint
64
63
  } = (0, _filter.useFilterContext)();
65
- const showFilterTip = !!selectedCategory || !!filters.price;
64
+ const showFilterTip = !!selectedCategory || !!filters.price; // eslint-disable-next-line no-unused-vars
65
+
66
66
  const [sentryRef] = (0, _reactInfiniteScrollHook.default)({
67
- loading: loadings.fetchBlockletsLoading,
67
+ loading: loadings.fetchBlockletsLoading || loadings.loadingMore,
68
68
  hasNextPage,
69
69
  onLoadMore: loadMore
70
70
  });
@@ -85,7 +85,7 @@ function BlockletList(_ref) {
85
85
  });
86
86
  }
87
87
 
88
- if (filters.keyword && showFilterTip && finalBlockletList.length === 0) {
88
+ if (filters.keyword && showFilterTip && blocklets.length === 0) {
89
89
  return /*#__PURE__*/(0, _jsxRuntime.jsxs)(CustomEmpty, {
90
90
  children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_empty.EmptyTitle, {
91
91
  primaryStart: t('blocklet.noBlockletPart1'),
@@ -98,7 +98,7 @@ function BlockletList(_ref) {
98
98
  });
99
99
  }
100
100
 
101
- if (filters.keyword && finalBlockletList.length === 0) {
101
+ if (filters.keyword && blocklets.length === 0) {
102
102
  return /*#__PURE__*/(0, _jsxRuntime.jsxs)(CustomEmpty, {
103
103
  children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_empty.EmptyTitle, {
104
104
  primaryStart: t('blocklet.noBlockletPart1'),
@@ -110,7 +110,7 @@ function BlockletList(_ref) {
110
110
  });
111
111
  }
112
112
 
113
- if (showFilterTip && finalBlockletList.length === 0) {
113
+ if (showFilterTip && blocklets.length === 0) {
114
114
  const categoryLocale = getCategoryLocale(selectedCategory);
115
115
  return /*#__PURE__*/(0, _jsxRuntime.jsxs)(CustomEmpty, {
116
116
  children: [categoryLocale ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_empty.EmptyTitle, {
@@ -123,7 +123,7 @@ function BlockletList(_ref) {
123
123
  });
124
124
  }
125
125
 
126
- if (finalBlockletList.length === 0) {
126
+ if (blocklets.length === 0) {
127
127
  return /*#__PURE__*/(0, _jsxRuntime.jsx)(CustomEmpty, {
128
128
  children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_empty.NoResults, {})
129
129
  });
@@ -141,7 +141,7 @@ function BlockletList(_ref) {
141
141
  "data-blocklet-did": blocklet.did,
142
142
  children: blockletRender({
143
143
  blocklet,
144
- blocklets: finalBlockletList
144
+ blocklets
145
145
  })
146
146
  }, blocklet.did)), hasNextPage && /*#__PURE__*/(0, _jsxRuntime.jsx)(StyledGridItem, {
147
147
  item: true,
@@ -17,14 +17,14 @@ var _orderBy = _interopRequireDefault(require("lodash/orderBy"));
17
17
 
18
18
  var _axios = _interopRequireDefault(require("axios"));
19
19
 
20
- var _isArray = _interopRequireDefault(require("lodash/isArray"));
21
-
22
20
  var _utils = require("../libs/utils");
23
21
 
24
22
  var _locale = _interopRequireDefault(require("../assets/locale"));
25
23
 
26
24
  var _propTypes2 = require("../libs/prop-types");
27
25
 
26
+ var _constant = _interopRequireDefault(require("../libs/constant"));
27
+
28
28
  var _jsxRuntime = require("react/jsx-runtime");
29
29
 
30
30
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
@@ -57,118 +57,72 @@ function FilterProvider(_ref) {
57
57
  baseURL: endpoint
58
58
  });
59
59
 
60
+ const finalFilters = (0, _react.useMemo)(() => {
61
+ return _objectSpread({
62
+ sortBy: 'popularity',
63
+ sortDirection: 'desc'
64
+ }, filters);
65
+ }, [filters]);
66
+ const selectedCategory = finalFilters.category;
67
+ const paginateState = (0, _ahooks.useReactive)({
68
+ currentPage: _constant.default.currentPage,
69
+ pageSize: (0, _utils.isMobileScreen)() ? _constant.default.mobilePageSize : _constant.default.pageSize,
70
+ defaultCurrentPage: _constant.default.defaultCurrentPage
71
+ });
60
72
  const {
61
- data: allBlocklets,
73
+ data: blockletsState,
62
74
  error: fetchBlockletsError,
63
75
  loading: fetchBlockletsLoading,
64
- run: fetchBlocklets
65
- } = (0, _ahooks.useRequest)(async () => {
66
- const {
67
- data
68
- } = await storeApi.get('/api/blocklets.json');
76
+ loadMore: _loadMore,
77
+ loadingMore
78
+ } = (0, _ahooks.useRequest)(async d => {
79
+ var _d$list;
69
80
 
70
- if (!(0, _isArray.default)(data)) {
71
- throw new Error('/api/blocklets.json response is not array');
72
- }
81
+ const params = _objectSpread(_objectSpread({}, finalFilters), {}, {
82
+ sortBy: _constant.default[finalFilters.sortBy],
83
+ page: (0, _utils.getCurrentPage)((d === null || d === void 0 ? void 0 : (_d$list = d.list) === null || _d$list === void 0 ? void 0 : _d$list.length) || 0, paginateState.pageSize),
84
+ pageSize: paginateState.pageSize
85
+ });
73
86
 
87
+ const {
88
+ data = {}
89
+ } = await storeApi.get('/api/v2/blocklets.json', {
90
+ params
91
+ });
74
92
  return data;
75
93
  }, {
76
- initialData: [],
77
- manual: true
94
+ initialData: {
95
+ list: [],
96
+ total: 0
97
+ },
98
+ loadMore: true,
99
+ isNoMore: d => d ? d.list.length >= d.total : false,
100
+ // eslint-disable-next-line no-console
101
+ formatResult: response => {
102
+ return {
103
+ list: response === null || response === void 0 ? void 0 : response.dataList,
104
+ total: response.total
105
+ };
106
+ },
107
+ refreshDeps: [endpoint, filters]
78
108
  });
79
109
  const {
80
110
  data: allCategories,
81
111
  error: fetchCategoriesError,
82
- loading: fetchCategoriesLoading,
83
- run: fetchCategories
112
+ loading: fetchCategoriesLoading
84
113
  } = (0, _ahooks.useRequest)(async () => {
85
114
  const {
86
115
  data
87
116
  } = await storeApi.get('/api/blocklets/categories');
88
-
89
- if (!(0, _isArray.default)(data)) {
90
- throw new Error('/api/blocklets/categories response is not array');
91
- }
92
-
93
117
  return data;
94
118
  }, {
95
- initialData: [],
96
- manual: true
119
+ initialData: []
97
120
  });
98
- const paginateState = (0, _ahooks.useReactive)({
99
- currentPage: 1,
100
- pageSize: (0, _utils.isMobileScreen)() ? 10 : 18,
101
- defaultCurrentPage: 1
102
- }); // TODO: 这里如果用 useMemo 包裹,滚动分页会异常
103
- // eslint-disable-next-line react-hooks/exhaustive-deps
104
-
105
- const finalFilters = _objectSpread({
106
- sortBy: 'popularity',
107
- sortDirection: 'desc'
108
- }, filters);
109
-
110
- const selectedCategory = finalFilters.category;
111
- const hasDeveloperFilter = !!finalFilters.developer;
112
- const categoryState = (0, _react.useMemo)(() => {
113
- // 当按作者过滤时,需要从所有blocklets中找出属于作者的分类
114
- return !hasDeveloperFilter ? {
115
- data: allCategories
116
- } : (0, _utils.getCategoriesByAuthor)(allBlocklets, finalFilters.developer);
117
- }, [hasDeveloperFilter, allCategories, allBlocklets, finalFilters.developer]);
118
- const blockletList = (0, _react.useMemo)(() => {
119
- var _finalFilters$keyword;
120
-
121
- const sortByName = x => {
122
- var _x$title, _x$name;
123
-
124
- return (x === null || x === void 0 ? void 0 : (_x$title = x.title) === null || _x$title === void 0 ? void 0 : _x$title.toLocaleLowerCase()) || (x === null || x === void 0 ? void 0 : (_x$name = x.name) === null || _x$name === void 0 ? void 0 : _x$name.toLocaleLowerCase());
125
- }; // 按名称排序
126
-
127
-
128
- const sortByPopularity = x => x.stats.downloads; // 按下载量排序
129
-
130
-
131
- const sortByPublish = x => x.lastPublishedAt; // 按发布时间
132
-
133
-
134
- const sortMap = {
135
- nameAsc: sortByName,
136
- nameDesc: sortByName,
137
- popularity: sortByPopularity,
138
- publishAt: sortByPublish
139
- };
140
- let blocklets = allBlocklets || []; // 按照付费/免费筛选
141
-
142
- blocklets = (0, _utils.filterBlockletByPrice)(blocklets, finalFilters.price); // 按照分类筛选
143
-
144
- blocklets = blocklets.filter(item => {
145
- var _item$category;
146
-
147
- return selectedCategory ? (item === null || item === void 0 ? void 0 : (_item$category = item.category) === null || _item$category === void 0 ? void 0 : _item$category.name) === selectedCategory : true;
148
- }); // 按照作者筛选
149
-
150
- blocklets = blocklets.filter(item => finalFilters !== null && finalFilters !== void 0 && finalFilters.developer ? item.owner.did === finalFilters.developer : true);
151
- const lowerSearch = (finalFilters === null || finalFilters === void 0 ? void 0 : (_finalFilters$keyword = finalFilters.keyword) === null || _finalFilters$keyword === void 0 ? void 0 : _finalFilters$keyword.toLocaleLowerCase()) || ''; // 按照搜索筛选
152
-
153
- blocklets = blocklets.filter(item => {
154
- var _ref2, _item$description, _item$version;
155
-
156
- return ((_ref2 = (item === null || item === void 0 ? void 0 : item.title) || (item === null || item === void 0 ? void 0 : item.name)) === null || _ref2 === void 0 ? void 0 : _ref2.toLocaleLowerCase().includes(lowerSearch)) || ((_item$description = item.description) === null || _item$description === void 0 ? void 0 : _item$description.toLocaleLowerCase().includes(lowerSearch)) || (item === null || item === void 0 ? void 0 : (_item$version = item.version) === null || _item$version === void 0 ? void 0 : _item$version.toLocaleLowerCase().includes(lowerSearch));
157
- }); // 用户传入的过滤函数
158
-
159
- blocklets = extraFilter(blocklets); // 排序
160
-
161
- return (0, _orderBy.default)(blocklets, [sortMap[finalFilters.sortBy]], [finalFilters.sortDirection]);
162
- }, [allBlocklets, finalFilters, selectedCategory, extraFilter]);
163
- const finalBlockletList = (0, _react.useMemo)(() => {
164
- // 前端分页 currentPage 当前页数 pageSize 每页条数
165
- return blockletList.slice((paginateState.defaultCurrentPage - 1) * paginateState.pageSize, paginateState.currentPage * paginateState.pageSize);
166
- }, [blockletList, paginateState]);
167
121
  const categoryList = (0, _react.useMemo)(() => {
168
- const list = categoryState.data || []; // 分类按照名称排序
122
+ const list = allCategories || []; // 分类按照名称排序
169
123
 
170
124
  return (0, _orderBy.default)(list, [i => i.name], ['asc']);
171
- }, [categoryState.data]);
125
+ }, [allCategories]);
172
126
 
173
127
  const translate = (key, data) => {
174
128
  if (!_locale.default[locale] || !_locale.default[locale][key]) {
@@ -179,6 +133,12 @@ function FilterProvider(_ref) {
179
133
  return (0, _utils.replaceTranslate)(_locale.default[locale][key], data);
180
134
  };
181
135
 
136
+ const blockletList = (0, _react.useMemo)(() => {
137
+ let blocklets = blockletsState.list || []; // 用户传入的过滤函数
138
+
139
+ blocklets = extraFilter(blocklets);
140
+ return blocklets; // eslint-disable-next-line react-hooks/exhaustive-deps
141
+ }, [blockletsState, extraFilter]);
182
142
  const categoryOptions = (0, _react.useMemo)(() => (0, _utils.getCategoryOptions)(categoryList, locale), [categoryList, locale]);
183
143
  const priceOptions = (0, _utils.getPrices)(translate);
184
144
  const filterStore = {
@@ -188,10 +148,11 @@ function FilterProvider(_ref) {
188
148
  },
189
149
  loadings: {
190
150
  fetchBlockletsLoading,
191
- fetchCategoriesLoading
151
+ fetchCategoriesLoading,
152
+ loadingMore
192
153
  },
193
154
  endpoint,
194
- finalBlockletList,
155
+ blockletList,
195
156
  t: translate,
196
157
  filters: finalFilters,
197
158
  selectedCategory,
@@ -200,7 +161,7 @@ function FilterProvider(_ref) {
200
161
  locale,
201
162
  categoryOptions,
202
163
  priceOptions,
203
- hasNextPage: blockletList.length >= paginateState.pageSize * paginateState.currentPage,
164
+ hasNextPage: blockletsState.list.length < blockletsState.total,
204
165
  handleSort: sort => {
205
166
  const changeData = _objectSpread(_objectSpread({}, finalFilters), {}, {
206
167
  sortBy: sort,
@@ -236,9 +197,9 @@ function FilterProvider(_ref) {
236
197
 
237
198
  onFilterChange(changeData);
238
199
  },
239
- handleDeveloper: developer => {
200
+ handleDeveloper: owner => {
240
201
  const changeData = _objectSpread(_objectSpread({}, finalFilters), {}, {
241
- developer: developer || undefined
202
+ owner: owner || undefined
242
203
  });
243
204
 
244
205
  onFilterChange(changeData);
@@ -252,13 +213,13 @@ function FilterProvider(_ref) {
252
213
  },
253
214
  loadMore: () => {
254
215
  setTimeout(() => {
255
- paginateState.currentPage += 1;
216
+ _loadMore();
256
217
  }, 1000);
257
218
  },
258
219
  getCategoryLocale: category => {
259
220
  if (!category) return null;
260
221
  let result = null;
261
- const find = categoryState.data.find(item => item.name === category);
222
+ const find = allCategories.find(item => item._id === category);
262
223
 
263
224
  if (find) {
264
225
  result = find.locales[locale];
@@ -267,28 +228,18 @@ function FilterProvider(_ref) {
267
228
  return result;
268
229
  },
269
230
 
270
- get allBlocklets() {
271
- return allBlocklets || [];
272
- },
273
-
274
231
  get developerName() {
275
232
  var _blocklets$find, _blocklets$find$owner;
276
233
 
277
- const blocklets = allBlocklets || [];
278
- return ((_blocklets$find = blocklets.find(blocklet => blocklet.owner.did === finalFilters.developer)) === null || _blocklets$find === void 0 ? void 0 : (_blocklets$find$owner = _blocklets$find.owner) === null || _blocklets$find$owner === void 0 ? void 0 : _blocklets$find$owner.name) || '';
234
+ const blocklets = blockletList;
235
+ return ((_blocklets$find = blocklets.find(blocklet => {
236
+ var _blocklet$owner;
237
+
238
+ return (blocklet === null || blocklet === void 0 ? void 0 : (_blocklet$owner = blocklet.owner) === null || _blocklet$owner === void 0 ? void 0 : _blocklet$owner.did) === finalFilters.owner;
239
+ })) === null || _blocklets$find === void 0 ? void 0 : (_blocklets$find$owner = _blocklets$find.owner) === null || _blocklets$find$owner === void 0 ? void 0 : _blocklets$find$owner.name) || '';
279
240
  }
280
241
 
281
242
  };
282
- (0, _react.useEffect)(() => {
283
- if (!hasDeveloperFilter) {
284
- fetchCategories();
285
- } // eslint-disable-next-line react-hooks/exhaustive-deps
286
-
287
- }, [hasDeveloperFilter]);
288
- (0, _react.useEffect)(() => {
289
- fetchBlocklets();
290
- fetchCategories(); // eslint-disable-next-line react-hooks/exhaustive-deps
291
- }, [endpoint]);
292
243
  return /*#__PURE__*/(0, _jsxRuntime.jsx)(Provider, {
293
244
  value: filterStore,
294
245
  children: children
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.default = void 0;
7
+ const constant = {
8
+ nameAsc: 'title',
9
+ nameDesc: 'title',
10
+ popularity: 'stats.downloads',
11
+ publishAt: 'lastPublishedAt',
12
+ mobilePageSize: 10,
13
+ pageSize: 18,
14
+ defaultCurrentPage: 1
15
+ };
16
+ var _default = constant;
17
+ exports.default = _default;
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.isMobileScreen = exports.getStoreDetail = exports.getSortOptions = exports.getPrices = exports.getCategoryOptions = exports.getCategoriesByAuthor = exports.formatLogoPath = exports.formatError = exports.filterBlockletByPrice = void 0;
6
+ exports.urlStringify = exports.replaceTranslate = exports.removeUndefined = exports.isMobileScreen = exports.getStoreDetail = exports.getSortOptions = exports.getPrices = exports.getCurrentPage = exports.getCategoryOptions = exports.formatLogoPath = exports.formatError = exports.filterBlockletByPrice = void 0;
7
7
 
8
8
  var _urlJoin = _interopRequireDefault(require("url-join"));
9
9
 
@@ -11,12 +11,12 @@ var _cloneDeep = _interopRequireDefault(require("lodash/cloneDeep"));
11
11
 
12
12
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
13
13
 
14
- const isFreeBlocklet = meta => {
15
- if (!meta.payment) {
14
+ const isFreeBlocklet = blocklet => {
15
+ if (!blocklet.payment) {
16
16
  return true;
17
17
  }
18
18
 
19
- const priceList = (meta.payment.price || []).map(x => x.value || 0);
19
+ const priceList = (blocklet.payment.price || []).map(x => x.value || 0);
20
20
  return priceList.every(x => x === 0);
21
21
  };
22
22
 
@@ -55,30 +55,9 @@ const getCategoryOptions = function getCategoryOptions() {
55
55
  let locale = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'en';
56
56
  return list.map(item => ({
57
57
  name: item.locales[locale],
58
- value: item.name
58
+ value: item._id
59
59
  }));
60
60
  };
61
- /**
62
- * 从 blocklets 中的得到属于作者的 Categories
63
- * @param {*} list
64
- * @param {*} developerDid
65
- * @returns
66
- */
67
-
68
-
69
- exports.getCategoryOptions = getCategoryOptions;
70
-
71
- const getCategoriesByAuthor = function getCategoriesByAuthor() {
72
- let list = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
73
- let developerDid = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;
74
- const filterList = list.filter(item => developerDid ? item.owner.did === developerDid : true);
75
- const Categories = filterList.map(item => item.category);
76
- const res = new Map();
77
- const result = Categories.filter(i => !!i).filter(a => !res.has(a._id) && res.set(a._id, 1));
78
- return {
79
- data: result
80
- };
81
- };
82
61
  /**
83
62
  * 根据 是否付费 过滤 blocklet list
84
63
  * @param {*} list
@@ -87,7 +66,7 @@ const getCategoriesByAuthor = function getCategoriesByAuthor() {
87
66
  */
88
67
 
89
68
 
90
- exports.getCategoriesByAuthor = getCategoriesByAuthor;
69
+ exports.getCategoryOptions = getCategoryOptions;
91
70
 
92
71
  const filterBlockletByPrice = function filterBlockletByPrice() {
93
72
  let list = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
@@ -165,4 +144,12 @@ const isMobileScreen = () => {
165
144
  return window.innerWidth <= 600;
166
145
  };
167
146
 
168
- exports.isMobileScreen = isMobileScreen;
147
+ exports.isMobileScreen = isMobileScreen;
148
+
149
+ const getCurrentPage = (length, pageSize) => {
150
+ const page = (length + pageSize) / pageSize;
151
+ if (page > 1) return page.toFixed();
152
+ return 1;
153
+ };
154
+
155
+ exports.getCurrentPage = getCurrentPage;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blocklet/list",
3
- "version": "0.8.64",
3
+ "version": "0.9.0",
4
4
  "description": "Common ux components of blocklet",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -38,15 +38,15 @@
38
38
  "react": ">=18.1.0"
39
39
  },
40
40
  "dependencies": {
41
- "@arcblock/ux": "^2.1.63",
42
- "@emotion/react": "^11.9.0",
43
- "@emotion/styled": "^11.8.1",
44
- "@mui/icons-material": "^5.6.2",
45
- "ahooks": "^2.10.12",
41
+ "@arcblock/ux": "^2.2.0",
42
+ "@emotion/react": "^11.10.0",
43
+ "@emotion/styled": "^11.10.0",
44
+ "@mui/icons-material": "^5.8.4",
45
+ "ahooks": "^2.10.14",
46
46
  "axios": "^0.27.2",
47
47
  "flat": "^5.0.2",
48
48
  "lodash": "^4.17.21",
49
- "prop-types": "^15.7.2",
49
+ "prop-types": "^15.8.1",
50
50
  "react-error-boundary": "^3.1.4",
51
51
  "react-infinite-scroll-hook": "^4.0.3",
52
52
  "styled-components": "5.3.5",
@@ -54,15 +54,15 @@
54
54
  },
55
55
  "devDependencies": {
56
56
  "@arcblock/eslint-config": "0.2.2",
57
- "@babel/cli": "^7.8.4",
58
- "@babel/core": "^7.8.4",
59
- "@babel/preset-env": "^7.8.4",
60
- "@babel/preset-react": "^7.8.3",
61
- "@emotion/babel-plugin": "^11.9.2",
57
+ "@babel/cli": "^7.18.10",
58
+ "@babel/core": "^7.18.10",
59
+ "@babel/preset-env": "^7.18.10",
60
+ "@babel/preset-react": "^7.18.6",
61
+ "@emotion/babel-plugin": "^11.10.0",
62
62
  "babel-plugin-inline-react-svg": "^2.0.1",
63
- "babel-plugin-styled-components": "^1.10.7",
64
- "eslint": "^8.16.0",
65
- "prettier": "^2.6.2"
63
+ "babel-plugin-styled-components": "^1.13.3",
64
+ "eslint": "^8.21.0",
65
+ "prettier": "^2.7.1"
66
66
  },
67
- "gitHead": "1f20a221cf88da47a2c8eb93dfd58c08357cdc97"
67
+ "gitHead": "3292764e5d1ab3a7197491a5dcc17a99192fae5b"
68
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
- finalBlockletList,
19
+ blockletList,
20
20
  filters,
21
21
  developerName,
22
22
  handleSort,
@@ -37,49 +37,51 @@ function ListBase() {
37
37
  <Aside />
38
38
  </Hidden>
39
39
  <StyledMin>
40
- <Box className="filter-bar" display="flex" alignItems="center">
41
- <StyledSearch className="search-container" placeholder={t('common.searchStore')} />
42
- <Box mt={0} ml="16px" className="filter-container">
43
- <Hidden mdUp>
44
- {/* 小屏幕下类别 */}
45
- <FilterIcon />
46
- </Hidden>
47
- {/* 排序选择器 */}
48
- <CustomSelect
49
- value={filters.sortBy}
50
- options={sortOptions}
51
- title={sortLocale}
52
- icon={<SortIcon />}
53
- onChange={(v) => {
54
- handleSort(v);
40
+ <FilterContainer>
41
+ <Box className="filter-bar" display="flex" alignItems="center">
42
+ <StyledSearch className="search-container" placeholder={t('common.searchStore')} />
43
+ <Box mt={0} ml="16px" className="filter-container">
44
+ <Hidden mdUp>
45
+ {/* 小屏幕下类别 */}
46
+ <FilterIcon />
47
+ </Hidden>
48
+ {/* 排序选择器 */}
49
+ <CustomSelect
50
+ value={filters.sortBy}
51
+ options={sortOptions}
52
+ title={sortLocale}
53
+ icon={<SortIcon />}
54
+ onChange={(v) => {
55
+ handleSort(v);
56
+ }}
57
+ />
58
+ </Box>
59
+ </Box>
60
+ <Box display="flex" flexWrap="wrap" alignItems="center" mb="16px">
61
+ <CustomChip
62
+ label={developerName}
63
+ icon={<FaceIcon />}
64
+ onDelete={() => {
65
+ handleDeveloper(null);
66
+ }}
67
+ />
68
+ <CustomChip
69
+ label={categoryLocale}
70
+ onDelete={() => {
71
+ handleCategory(null);
72
+ }}
73
+ />
74
+ <CustomChip
75
+ label={priceLocale}
76
+ onDelete={() => {
77
+ handlePrice(null);
55
78
  }}
56
79
  />
57
80
  </Box>
58
- </Box>
59
- <Box display="flex" flexWrap="wrap" alignItems="center" mb="16px">
60
- <CustomChip
61
- label={developerName}
62
- icon={<FaceIcon />}
63
- onDelete={() => {
64
- handleDeveloper(null);
65
- }}
66
- />
67
- <CustomChip
68
- label={categoryLocale}
69
- onDelete={() => {
70
- handleCategory(null);
71
- }}
72
- />
73
- <CustomChip
74
- label={priceLocale}
75
- onDelete={() => {
76
- handlePrice(null);
77
- }}
78
- />
79
- </Box>
81
+ </FilterContainer>
80
82
  <ErrorBoundary FallbackComponent={ErrorFallback}>
81
83
  <div style={{ position: 'relative', flex: 1, overflow: 'auto' }}>
82
- <BlockletList blocklets={finalBlockletList} />
84
+ <BlockletList blocklets={blockletList} />
83
85
  </div>
84
86
  </ErrorBoundary>
85
87
  </StyledMin>
@@ -137,4 +139,11 @@ const StyledSearch = styled(Search)`
137
139
  background-color: ${(props) => props.theme.palette.grey[50]};
138
140
  `;
139
141
 
142
+ const FilterContainer = styled.div`
143
+ position: sticky;
144
+ top: 0;
145
+ z-index: 999;
146
+ background-color: white;
147
+ `;
148
+
140
149
  export default ListBase;
@@ -30,7 +30,10 @@ const StyledAside = styled.aside`
30
30
  width: 220px;
31
31
  margin-right: ${(props) => props.theme.spacing(2)};
32
32
  height: 100%;
33
+ position: sticky;
34
+ top: 0;
33
35
  overflow-y: auto;
36
+ z-index: 999;
34
37
  `;
35
38
 
36
39
  Aside.propTypes = {};
@@ -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 data-cy="filter-tag" icon={icon} label={label} onDelete={onDelete} />
18
+ <Chip color="primary" data-cy="filter-tag" icon={icon} label={label} onDelete={onDelete} />
19
19
  </StyleDiv>
20
20
  );
21
21
  }
@@ -17,7 +17,6 @@ export default function BlockletList({ blocklets, ...rest }) {
17
17
  errors,
18
18
  loadings,
19
19
  selectedCategory,
20
- finalBlockletList,
21
20
  getCategoryLocale,
22
21
  filters,
23
22
  t,
@@ -28,8 +27,9 @@ export default function BlockletList({ blocklets, ...rest }) {
28
27
 
29
28
  const showFilterTip = !!selectedCategory || !!filters.price;
30
29
 
30
+ // eslint-disable-next-line no-unused-vars
31
31
  const [sentryRef] = useInfiniteScroll({
32
- loading: loadings.fetchBlockletsLoading,
32
+ loading: loadings.fetchBlockletsLoading || loadings.loadingMore,
33
33
  hasNextPage,
34
34
  onLoadMore: loadMore,
35
35
  });
@@ -48,7 +48,7 @@ export default function BlockletList({ blocklets, ...rest }) {
48
48
  </Box>
49
49
  );
50
50
  }
51
- if (filters.keyword && showFilterTip && finalBlockletList.length === 0) {
51
+ if (filters.keyword && showFilterTip && blocklets.length === 0) {
52
52
  return (
53
53
  <CustomEmpty>
54
54
  <EmptyTitle
@@ -60,7 +60,7 @@ export default function BlockletList({ blocklets, ...rest }) {
60
60
  </CustomEmpty>
61
61
  );
62
62
  }
63
- if (filters.keyword && finalBlockletList.length === 0) {
63
+ if (filters.keyword && blocklets.length === 0) {
64
64
  return (
65
65
  <CustomEmpty>
66
66
  <EmptyTitle
@@ -72,7 +72,7 @@ export default function BlockletList({ blocklets, ...rest }) {
72
72
  </CustomEmpty>
73
73
  );
74
74
  }
75
- if (showFilterTip && finalBlockletList.length === 0) {
75
+ if (showFilterTip && blocklets.length === 0) {
76
76
  const categoryLocale = getCategoryLocale(selectedCategory);
77
77
  return (
78
78
  <CustomEmpty>
@@ -89,7 +89,7 @@ export default function BlockletList({ blocklets, ...rest }) {
89
89
  </CustomEmpty>
90
90
  );
91
91
  }
92
- if (finalBlockletList.length === 0) {
92
+ if (blocklets.length === 0) {
93
93
  return (
94
94
  <CustomEmpty>
95
95
  <NoResults />
@@ -101,7 +101,7 @@ export default function BlockletList({ blocklets, ...rest }) {
101
101
  <StyledGrid container {...rest}>
102
102
  {blocklets.map((blocklet) => (
103
103
  <StyledGridItem item lg={4} md={6} sm={6} xs={12} key={blocklet.did} data-blocklet-did={blocklet.did}>
104
- {blockletRender({ blocklet, blocklets: finalBlockletList })}
104
+ {blockletRender({ blocklet, blocklets })}
105
105
  </StyledGridItem>
106
106
  ))}
107
107
  {hasNextPage && (
@@ -1,19 +1,12 @@
1
- import { useContext, createContext, useMemo, useEffect } from 'react';
1
+ import { useContext, createContext, useMemo } from 'react';
2
2
  import PropTypes from 'prop-types';
3
3
  import { useRequest, useReactive } from 'ahooks';
4
4
  import orderBy from 'lodash/orderBy';
5
5
  import axios from 'axios';
6
- import isArray from 'lodash/isArray';
7
- import {
8
- getCategoriesByAuthor,
9
- filterBlockletByPrice,
10
- replaceTranslate,
11
- getPrices,
12
- getCategoryOptions,
13
- isMobileScreen,
14
- } from '../libs/utils';
6
+ import { replaceTranslate, getPrices, getCategoryOptions, isMobileScreen, getCurrentPage } from '../libs/utils';
15
7
  import translations from '../assets/locale';
16
8
  import { propTypes, defaultProps } from '../libs/prop-types';
9
+ import constant from '../libs/constant';
17
10
 
18
11
  const Filter = createContext({});
19
12
  const { Provider, Consumer } = Filter;
@@ -22,97 +15,65 @@ function FilterProvider({ filters, children, endpoint, locale, blockletRender, o
22
15
  const storeApi = axios.create({
23
16
  baseURL: endpoint,
24
17
  });
18
+
19
+ const finalFilters = useMemo(() => {
20
+ return { sortBy: 'popularity', sortDirection: 'desc', ...filters };
21
+ }, [filters]);
22
+
23
+ const selectedCategory = finalFilters.category;
24
+
25
+ const paginateState = useReactive({
26
+ currentPage: constant.currentPage,
27
+ pageSize: isMobileScreen() ? constant.mobilePageSize : constant.pageSize,
28
+ defaultCurrentPage: constant.defaultCurrentPage,
29
+ });
30
+
25
31
  const {
26
- data: allBlocklets,
32
+ data: blockletsState,
27
33
  error: fetchBlockletsError,
28
34
  loading: fetchBlockletsLoading,
29
- run: fetchBlocklets,
35
+ loadMore,
36
+ loadingMore,
30
37
  } = useRequest(
31
- async () => {
32
- const { data } = await storeApi.get('/api/blocklets.json');
33
- if (!isArray(data)) {
34
- throw new Error('/api/blocklets.json response is not array');
35
- }
38
+ async (d) => {
39
+ const params = {
40
+ ...finalFilters,
41
+ sortBy: constant[finalFilters.sortBy],
42
+ page: getCurrentPage(d?.list?.length || 0, paginateState.pageSize),
43
+ pageSize: paginateState.pageSize,
44
+ };
45
+ const { data = {} } = await storeApi.get('/api/v2/blocklets.json', { params });
36
46
  return data;
37
47
  },
38
- { initialData: [], manual: true }
48
+ {
49
+ initialData: { list: [], total: 0 },
50
+ loadMore: true,
51
+ isNoMore: (d) => (d ? d.list.length >= d.total : false),
52
+ // eslint-disable-next-line no-console
53
+ formatResult: (response) => {
54
+ return { list: response?.dataList, total: response.total };
55
+ },
56
+ refreshDeps: [endpoint, filters],
57
+ }
39
58
  );
40
59
 
41
60
  const {
42
61
  data: allCategories,
43
62
  error: fetchCategoriesError,
44
63
  loading: fetchCategoriesLoading,
45
- run: fetchCategories,
46
64
  } = useRequest(
47
65
  async () => {
48
66
  const { data } = await storeApi.get('/api/blocklets/categories');
49
- if (!isArray(data)) {
50
- throw new Error('/api/blocklets/categories response is not array');
51
- }
52
67
  return data;
53
68
  },
54
- { initialData: [], manual: true }
69
+ { initialData: [] }
55
70
  );
56
71
 
57
- const paginateState = useReactive({ currentPage: 1, pageSize: isMobileScreen() ? 10 : 18, defaultCurrentPage: 1 });
58
- // TODO: 这里如果用 useMemo 包裹,滚动分页会异常
59
- // eslint-disable-next-line react-hooks/exhaustive-deps
60
- const finalFilters = { sortBy: 'popularity', sortDirection: 'desc', ...filters };
61
-
62
- const selectedCategory = finalFilters.category;
63
- const hasDeveloperFilter = !!finalFilters.developer;
64
- const categoryState = useMemo(() => {
65
- // 当按作者过滤时,需要从所有blocklets中找出属于作者的分类
66
- return !hasDeveloperFilter ? { data: allCategories } : getCategoriesByAuthor(allBlocklets, finalFilters.developer);
67
- }, [hasDeveloperFilter, allCategories, allBlocklets, finalFilters.developer]);
68
-
69
- const blockletList = useMemo(() => {
70
- const sortByName = (x) => x?.title?.toLocaleLowerCase() || x?.name?.toLocaleLowerCase(); // 按名称排序
71
- const sortByPopularity = (x) => x.stats.downloads; // 按下载量排序
72
- const sortByPublish = (x) => x.lastPublishedAt; // 按发布时间
73
- const sortMap = {
74
- nameAsc: sortByName,
75
- nameDesc: sortByName,
76
- popularity: sortByPopularity,
77
- publishAt: sortByPublish,
78
- };
79
- let blocklets = allBlocklets || [];
80
- // 按照付费/免费筛选
81
- blocklets = filterBlockletByPrice(blocklets, finalFilters.price);
82
- // 按照分类筛选
83
- blocklets = blocklets.filter((item) => (selectedCategory ? item?.category?.name === selectedCategory : true));
84
- // 按照作者筛选
85
- blocklets = blocklets.filter((item) =>
86
- finalFilters?.developer ? item.owner.did === finalFilters.developer : true
87
- );
88
- const lowerSearch = finalFilters?.keyword?.toLocaleLowerCase() || '';
89
- // 按照搜索筛选
90
- blocklets = blocklets.filter((item) => {
91
- return (
92
- (item?.title || item?.name)?.toLocaleLowerCase().includes(lowerSearch) ||
93
- item.description?.toLocaleLowerCase().includes(lowerSearch) ||
94
- item?.version?.toLocaleLowerCase().includes(lowerSearch)
95
- );
96
- });
97
- // 用户传入的过滤函数
98
- blocklets = extraFilter(blocklets);
99
- // 排序
100
- return orderBy(blocklets, [sortMap[finalFilters.sortBy]], [finalFilters.sortDirection]);
101
- }, [allBlocklets, finalFilters, selectedCategory, extraFilter]);
102
-
103
- const finalBlockletList = useMemo(() => {
104
- // 前端分页 currentPage 当前页数 pageSize 每页条数
105
- return blockletList.slice(
106
- (paginateState.defaultCurrentPage - 1) * paginateState.pageSize,
107
- paginateState.currentPage * paginateState.pageSize
108
- );
109
- }, [blockletList, paginateState]);
110
-
111
72
  const categoryList = useMemo(() => {
112
- const list = categoryState.data || [];
73
+ const list = allCategories || [];
113
74
  // 分类按照名称排序
114
75
  return orderBy(list, [(i) => i.name], ['asc']);
115
- }, [categoryState.data]);
76
+ }, [allCategories]);
116
77
 
117
78
  const translate = (key, data) => {
118
79
  if (!translations[locale] || !translations[locale][key]) {
@@ -122,13 +83,21 @@ function FilterProvider({ filters, children, endpoint, locale, blockletRender, o
122
83
 
123
84
  return replaceTranslate(translations[locale][key], data);
124
85
  };
86
+ const blockletList = useMemo(() => {
87
+ let blocklets = blockletsState.list || [];
88
+ // 用户传入的过滤函数
89
+ blocklets = extraFilter(blocklets);
90
+ return blocklets;
91
+ // eslint-disable-next-line react-hooks/exhaustive-deps
92
+ }, [blockletsState, extraFilter]);
93
+
125
94
  const categoryOptions = useMemo(() => getCategoryOptions(categoryList, locale), [categoryList, locale]);
126
95
  const priceOptions = getPrices(translate);
127
96
  const filterStore = {
128
97
  errors: { fetchBlockletsError, fetchCategoriesError },
129
- loadings: { fetchBlockletsLoading, fetchCategoriesLoading },
98
+ loadings: { fetchBlockletsLoading, fetchCategoriesLoading, loadingMore },
130
99
  endpoint,
131
- finalBlockletList,
100
+ blockletList,
132
101
  t: translate,
133
102
  filters: finalFilters,
134
103
  selectedCategory,
@@ -137,7 +106,7 @@ function FilterProvider({ filters, children, endpoint, locale, blockletRender, o
137
106
  locale,
138
107
  categoryOptions,
139
108
  priceOptions,
140
- hasNextPage: blockletList.length >= paginateState.pageSize * paginateState.currentPage,
109
+ hasNextPage: blockletsState.list.length < blockletsState.total,
141
110
  handleSort: (sort) => {
142
111
  const changeData = {
143
112
  ...finalFilters,
@@ -165,8 +134,8 @@ function FilterProvider({ filters, children, endpoint, locale, blockletRender, o
165
134
  const changeData = { ...finalFilters, category: finalCategory };
166
135
  onFilterChange(changeData);
167
136
  },
168
- handleDeveloper: (developer) => {
169
- const changeData = { ...finalFilters, developer: developer || undefined };
137
+ handleDeveloper: (owner) => {
138
+ const changeData = { ...finalFilters, owner: owner || undefined };
170
139
  onFilterChange(changeData);
171
140
  },
172
141
  handlePage: (page) => {
@@ -175,40 +144,24 @@ function FilterProvider({ filters, children, endpoint, locale, blockletRender, o
175
144
  },
176
145
  loadMore: () => {
177
146
  setTimeout(() => {
178
- paginateState.currentPage += 1;
147
+ loadMore();
179
148
  }, 1000);
180
149
  },
181
150
  getCategoryLocale: (category) => {
182
151
  if (!category) return null;
183
152
  let result = null;
184
- const find = categoryState.data.find((item) => item.name === category);
153
+ const find = allCategories.find((item) => item._id === category);
185
154
  if (find) {
186
155
  result = find.locales[locale];
187
156
  }
188
157
  return result;
189
158
  },
190
- get allBlocklets() {
191
- return allBlocklets || [];
192
- },
193
159
  get developerName() {
194
- const blocklets = allBlocklets || [];
195
- return blocklets.find((blocklet) => blocklet.owner.did === finalFilters.developer)?.owner?.name || '';
160
+ const blocklets = blockletList;
161
+ return blocklets.find((blocklet) => blocklet?.owner?.did === finalFilters.owner)?.owner?.name || '';
196
162
  },
197
163
  };
198
164
 
199
- useEffect(() => {
200
- if (!hasDeveloperFilter) {
201
- fetchCategories();
202
- }
203
- // eslint-disable-next-line react-hooks/exhaustive-deps
204
- }, [hasDeveloperFilter]);
205
-
206
- useEffect(() => {
207
- fetchBlocklets();
208
- fetchCategories();
209
- // eslint-disable-next-line react-hooks/exhaustive-deps
210
- }, [endpoint]);
211
-
212
165
  return <Provider value={filterStore}>{children}</Provider>;
213
166
  }
214
167
 
@@ -0,0 +1,11 @@
1
+ const constant = {
2
+ nameAsc: 'title',
3
+ nameDesc: 'title',
4
+ popularity: 'stats.downloads',
5
+ publishAt: 'lastPublishedAt',
6
+ mobilePageSize: 10,
7
+ pageSize: 18,
8
+ defaultCurrentPage: 1,
9
+ };
10
+
11
+ export default constant;
package/src/libs/utils.js CHANGED
@@ -1,11 +1,11 @@
1
1
  import joinURL from 'url-join';
2
2
  import cloneDeep from 'lodash/cloneDeep';
3
3
 
4
- const isFreeBlocklet = (meta) => {
5
- if (!meta.payment) {
4
+ const isFreeBlocklet = (blocklet) => {
5
+ if (!blocklet.payment) {
6
6
  return true;
7
7
  }
8
- const priceList = (meta.payment.price || []).map((x) => x.value || 0);
8
+ const priceList = (blocklet.payment.price || []).map((x) => x.value || 0);
9
9
  return priceList.every((x) => x === 0);
10
10
  };
11
11
 
@@ -36,21 +36,9 @@ const getPrices = (t) => {
36
36
  ];
37
37
  };
38
38
  const getCategoryOptions = (list = [], locale = 'en') => {
39
- return list.map((item) => ({ name: item.locales[locale], value: item.name }));
40
- };
41
- /**
42
- * 从 blocklets 中的得到属于作者的 Categories
43
- * @param {*} list
44
- * @param {*} developerDid
45
- * @returns
46
- */
47
- const getCategoriesByAuthor = (list = [], developerDid = null) => {
48
- const filterList = list.filter((item) => (developerDid ? item.owner.did === developerDid : true));
49
- const Categories = filterList.map((item) => item.category);
50
- const res = new Map();
51
- const result = Categories.filter((i) => !!i).filter((a) => !res.has(a._id) && res.set(a._id, 1));
52
- return { data: result };
39
+ return list.map((item) => ({ name: item.locales[locale], value: item._id }));
53
40
  };
41
+
54
42
  /**
55
43
  * 根据 是否付费 过滤 blocklet list
56
44
  * @param {*} list
@@ -110,10 +98,15 @@ const isMobileScreen = () => {
110
98
  return window.innerWidth <= 600;
111
99
  };
112
100
 
101
+ const getCurrentPage = (length, pageSize) => {
102
+ const page = (length + pageSize) / pageSize;
103
+ if (page > 1) return page.toFixed();
104
+ return 1;
105
+ };
106
+
113
107
  export {
114
108
  getSortOptions,
115
109
  getPrices,
116
- getCategoriesByAuthor,
117
110
  filterBlockletByPrice,
118
111
  getStoreDetail,
119
112
  formatLogoPath,
@@ -123,4 +116,5 @@ export {
123
116
  urlStringify,
124
117
  getCategoryOptions,
125
118
  isMobileScreen,
119
+ getCurrentPage,
126
120
  };