@blocklet/list 0.8.10 → 0.8.13

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
@@ -58,7 +58,7 @@ const ListBase = () => {
58
58
  alignItems: "center"
59
59
  }, /*#__PURE__*/_react.default.createElement(_material.Hidden, {
60
60
  mdDown: true
61
- }, !!queryParams.developer && /*#__PURE__*/_react.default.createElement(_filterAuthor.default, {
61
+ }, !!queryParams.get('developer') && /*#__PURE__*/_react.default.createElement(_filterAuthor.default, {
62
62
  user: developerName,
63
63
  deleteUserTag: () => {
64
64
  history.push('/');
@@ -93,8 +93,8 @@ const ListBase = () => {
93
93
  display: "flex",
94
94
  flexWrap: "wrap",
95
95
  alignItems: "center",
96
- justifyContent: queryParams.developer ? 'space-between' : 'flex-end'
97
- }, !!queryParams.developer && /*#__PURE__*/_react.default.createElement(_filterAuthor.default, {
96
+ justifyContent: queryParams.get('developer') ? 'space-between' : 'flex-end'
97
+ }, !!queryParams.get('developer') && /*#__PURE__*/_react.default.createElement(_filterAuthor.default, {
98
98
  user: developerName,
99
99
  deleteUserTag: () => {
100
100
  history.push('/');
@@ -103,10 +103,10 @@ const ListBase = () => {
103
103
  marginBottom: '16px'
104
104
  }
105
105
  }), /*#__PURE__*/_react.default.createElement(_customSelect.default, {
106
- value: queryParams.price,
106
+ value: queryParams.get('price'),
107
107
  icon: /*#__PURE__*/_react.default.createElement(_FilterList.default, null),
108
108
  options: (0, _utils.getPrices)(t),
109
- title: ((_getPrices$find = (0, _utils.getPrices)(t).find(f => f.value === queryParams.price)) === null || _getPrices$find === void 0 ? void 0 : _getPrices$find.name) || t('common.price'),
109
+ title: ((_getPrices$find = (0, _utils.getPrices)(t).find(f => f.value === queryParams.get('price'))) === null || _getPrices$find === void 0 ? void 0 : _getPrices$find.name) || t('common.price'),
110
110
  onChange: v => {
111
111
  searchStore.handlePriceFilter(v);
112
112
  },
@@ -40,7 +40,7 @@ const Aside = () => {
40
40
  handlePriceFilter(item.value);
41
41
  },
42
42
  size: "small",
43
- checked: item.value === queryParams.price
43
+ checked: item.value === queryParams.get('price')
44
44
  }),
45
45
  label: item.name
46
46
  }))), /*#__PURE__*/_react.default.createElement(_material.List, {
@@ -11,14 +11,14 @@ var _reactRouterDom = require("react-router-dom");
11
11
 
12
12
  var _material = require("@mui/material");
13
13
 
14
- var _context = require("@arcblock/ux/lib/Locale/context");
15
-
16
- var _qs = _interopRequireDefault(require("qs"));
14
+ var _urlJoin = _interopRequireDefault(require("url-join"));
17
15
 
18
16
  var _store = require("../contexts/store");
19
17
 
20
18
  var _customSelect = _interopRequireDefault(require("./custom-select"));
21
19
 
20
+ var _utils = require("../tools/utils");
21
+
22
22
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
23
23
 
24
24
  function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; }
@@ -72,10 +72,6 @@ const CategorySelect = () => {
72
72
  exports.CategorySelect = CategorySelect;
73
73
 
74
74
  const CategoryLinkList = () => {
75
- const {
76
- t,
77
- locale
78
- } = (0, _context.useLocaleContext)();
79
75
  const searchStore = (0, _store.useSearchContext)();
80
76
  const {
81
77
  queryParams,
@@ -84,7 +80,9 @@ const CategoryLinkList = () => {
84
80
  selectedCategory,
85
81
  isPageMode,
86
82
  handleCategorySelect,
87
- baseUrl
83
+ baseUrl,
84
+ locale,
85
+ t
88
86
  } = searchStore;
89
87
  let content = null;
90
88
 
@@ -97,7 +95,7 @@ const CategoryLinkList = () => {
97
95
  selected: !selectedCategory,
98
96
  button: true,
99
97
  component: _reactRouterDom.Link,
100
- to: !isSearchPage ? baseUrl : "".concat(baseUrl, "search?").concat(_qs.default.stringify(_objectSpread(_objectSpread({}, queryParams), {}, {
98
+ to: !isSearchPage ? baseUrl : "".concat((0, _urlJoin.default)(baseUrl, '/search'), "?").concat((0, _utils.urlStringify)(_objectSpread(_objectSpread({}, Object.fromEntries(queryParams.entries())), {}, {
101
99
  category: undefined
102
100
  })))
103
101
  }, /*#__PURE__*/_react.default.createElement(_material.ListItemText, {
@@ -108,7 +106,7 @@ const CategoryLinkList = () => {
108
106
  button: true,
109
107
  selected: item.name === selectedCategory,
110
108
  component: _reactRouterDom.Link,
111
- to: !isSearchPage ? "".concat(baseUrl, "category/").concat(item.name) : "".concat(baseUrl, "search?").concat(_qs.default.stringify(_objectSpread(_objectSpread({}, queryParams), {}, {
109
+ to: !isSearchPage ? "".concat((0, _urlJoin.default)(baseUrl, '/category', item.name)) : "".concat((0, _urlJoin.default)(baseUrl, '/search'), "?").concat((0, _utils.urlStringify)(_objectSpread(_objectSpread({}, Object.fromEntries(queryParams.entries())), {}, {
112
110
  category: item.name
113
111
  })))
114
112
  }, /*#__PURE__*/_react.default.createElement(_material.ListItemText, {
@@ -51,7 +51,7 @@ function BlockletList(_ref) {
51
51
  queryParams,
52
52
  t
53
53
  } = (0, _store.useSearchContext)();
54
- const showFilterTip = !!selectedCategory || !!queryParams.price;
54
+ const showFilterTip = !!selectedCategory || !!queryParams.get('price');
55
55
 
56
56
  if (errors.fetchBlockletsError) {
57
57
  return /*#__PURE__*/_react.default.createElement(_Alert.default, {
@@ -68,22 +68,22 @@ function BlockletList(_ref) {
68
68
  }, /*#__PURE__*/_react.default.createElement(_CircularProgress.default, null));
69
69
  }
70
70
 
71
- if (queryParams.search && showFilterTip && blockletList.length === 0) {
71
+ if (queryParams.get('search') && showFilterTip && blockletList.length === 0) {
72
72
  return /*#__PURE__*/_react.default.createElement(CustomEmpty, null, /*#__PURE__*/_react.default.createElement(_empty.EmptyTitle, {
73
73
  primaryStart: t('blocklet.noBlockletPart1'),
74
74
  primaryEnd: t('blocklet.noBlockletPart2'),
75
- search: queryParams.search
75
+ search: queryParams.get('search')
76
76
  }), /*#__PURE__*/_react.default.createElement(_empty.NoResultsTips, {
77
77
  keywordTip: true,
78
78
  filterTip: true
79
79
  }));
80
80
  }
81
81
 
82
- if (queryParams.search && blockletList.length === 0) {
82
+ if (queryParams.get('search') && blockletList.length === 0) {
83
83
  return /*#__PURE__*/_react.default.createElement(CustomEmpty, null, /*#__PURE__*/_react.default.createElement(_empty.EmptyTitle, {
84
84
  primaryStart: t('blocklet.noBlockletPart1'),
85
85
  primaryEnd: t('blocklet.noBlockletPart2'),
86
- search: queryParams.search
86
+ search: queryParams.get('search')
87
87
  }), /*#__PURE__*/_react.default.createElement(_empty.NoResultsTips, {
88
88
  keywordTip: true
89
89
  }));
@@ -115,7 +115,10 @@ function BlockletList(_ref) {
115
115
  xs: 12,
116
116
  key: blocklet.did,
117
117
  "data-blocklet-did": blocklet.did
118
- }, blockletRender(blocklet))));
118
+ }, blockletRender({
119
+ blocklet,
120
+ blocklets: blockletList
121
+ }))));
119
122
  }
120
123
 
121
124
  BlockletList.propTypes = {
@@ -42,7 +42,7 @@ const Search = _ref => {
42
42
  queryParams,
43
43
  handleSearchKeyword
44
44
  } = searchStore;
45
- const [searchStr, setSearchStr] = (0, _react.useState)(queryParams.search || '');
45
+ const [searchStr, setSearchStr] = (0, _react.useState)(queryParams.get('search') || '');
46
46
  const {
47
47
  run: handleSearch
48
48
  } = (0, _ahooks.useDebounceFn)(handleSearchKeyword, {
@@ -63,6 +63,9 @@ const Search = _ref => {
63
63
  };
64
64
 
65
65
  return /*#__PURE__*/_react.default.createElement(StyledSearch, Object.assign({
66
+ inputProps: {
67
+ 'data-cy': 'search-blocklet'
68
+ },
66
69
  startAdornment: /*#__PURE__*/_react.default.createElement(_material.InputAdornment, {
67
70
  position: "start"
68
71
  }, /*#__PURE__*/_react.default.createElement(StyledMagnify, null)),
@@ -70,10 +73,13 @@ const Search = _ref => {
70
73
  placeholder: placeholder,
71
74
  value: searchStr,
72
75
  title: placeholder,
76
+ "data-cy": "search",
73
77
  endAdornment: searchStr && /*#__PURE__*/_react.default.createElement(_material.InputAdornment, {
74
- onClick: handleClose,
75
78
  position: "end"
76
- }, /*#__PURE__*/_react.default.createElement(StyledClose, null))
79
+ }, /*#__PURE__*/_react.default.createElement(StyledClose, {
80
+ "data-cy": "search-delete",
81
+ onClick: handleClose
82
+ }))
77
83
  }, rest));
78
84
  };
79
85
 
@@ -11,8 +11,6 @@ var _react = _interopRequireWildcard(require("react"));
11
11
 
12
12
  var _propTypes = _interopRequireDefault(require("prop-types"));
13
13
 
14
- var _qs = _interopRequireDefault(require("qs"));
15
-
16
14
  var _reactRouterDom = require("react-router-dom");
17
15
 
18
16
  var _ahooks = require("ahooks");
@@ -59,7 +57,9 @@ function SearchProvider(_ref) {
59
57
  locale,
60
58
  blockletRender
61
59
  } = _ref;
62
- const location = (0, _reactRouterDom.useLocation)();
60
+ const {
61
+ location
62
+ } = window;
63
63
  const history = (0, _reactRouterDom.useHistory)();
64
64
  const pathParams = (0, _reactRouterDom.useParams)();
65
65
  const isPageMode = type === 'page';
@@ -71,14 +71,16 @@ function SearchProvider(_ref) {
71
71
  const {
72
72
  data: allBlocklets,
73
73
  error: fetchBlockletsError,
74
- loading: fetchBlockletsLoading
74
+ loading: fetchBlockletsLoading,
75
+ run: fetchBlocklets
75
76
  } = (0, _ahooks.useRequest)(async () => {
76
77
  const {
77
78
  data: list
78
79
  } = await axiosInstance.get((0, _urlJoin.default)(endpoint, '/api/blocklets.json'));
79
80
  return list;
80
81
  }, {
81
- initialData: []
82
+ initialData: [],
83
+ manual: true
82
84
  });
83
85
  const {
84
86
  data: allCategories,
@@ -99,9 +101,7 @@ function SearchProvider(_ref) {
99
101
  sortDirection: 'desc'
100
102
  });
101
103
  const queryParams = (0, _react.useMemo)(() => {
102
- return isPageMode ? _qs.default.parse(location.search, {
103
- ignoreQueryPrefix: true
104
- }) : memoryParams;
104
+ return isPageMode ? new URLSearchParams(location.search) : new Map(Object.entries(memoryParams));
105
105
  }, [memoryParams, location.search]);
106
106
  let sortParams; // 当作页面使用时 sort 数据比较特殊, 默认取 localStorge 中的值,如果 url query 中有sort值则优先使用
107
107
 
@@ -111,8 +111,8 @@ function SearchProvider(_ref) {
111
111
  direction: 'desc'
112
112
  }, baseUrl);
113
113
  const urlSortParams = {
114
- sort: queryParams.sortBy,
115
- direction: queryParams.sortDirection
114
+ sort: queryParams.get('sortBy'),
115
+ direction: queryParams.get('sortDirection')
116
116
  };
117
117
  sortParams = urlSortParams.sort && urlSortParams.direction ? urlSortParams : localSortParams;
118
118
  } else {
@@ -124,22 +124,22 @@ function SearchProvider(_ref) {
124
124
  }, [memoryParams]);
125
125
  }
126
126
 
127
- const isSearchPage = location.pathname === '/search';
127
+ const isSearchPage = location.pathname === (0, _urlJoin.default)(baseUrl, '/search');
128
128
  const selectedCategory = (0, _react.useMemo)(() => {
129
129
  let result = null;
130
130
 
131
131
  if (isPageMode) {
132
- result = !isSearchPage ? pathParams.category : queryParams.category;
132
+ result = !isSearchPage ? pathParams.category : queryParams.get('category');
133
133
  } else {
134
- result = queryParams.category;
134
+ result = queryParams.get('category');
135
135
  }
136
136
 
137
137
  return result;
138
138
  }, [isPageMode, pathParams, queryParams]);
139
- const hasDeveloperFilter = !!queryParams.developer;
139
+ const hasDeveloperFilter = !!queryParams.get('developer');
140
140
  const categoryState = !hasDeveloperFilter ? {
141
141
  data: allCategories
142
- } : (0, _utils.getCategories)(allBlocklets, queryParams.developer);
142
+ } : (0, _utils.getCategories)(allBlocklets, queryParams.get('developer'));
143
143
  const blockletList = (0, _react.useMemo)(() => {
144
144
  var _queryParams$search;
145
145
 
@@ -164,7 +164,7 @@ function SearchProvider(_ref) {
164
164
  };
165
165
  let result = allBlocklets || []; // 按照付费/免费筛选
166
166
 
167
- result = (0, _utils.filterBlockletByPrice)(result, queryParams.price); // 按照分类筛选
167
+ result = (0, _utils.filterBlockletByPrice)(result, queryParams.get('price')); // 按照分类筛选
168
168
 
169
169
  result = result.filter(item => {
170
170
  var _item$category;
@@ -172,7 +172,7 @@ function SearchProvider(_ref) {
172
172
  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;
173
173
  }); // 按照作者筛选
174
174
 
175
- result = result.filter(item => queryParams !== null && queryParams !== void 0 && queryParams.developer ? item.owner.did === queryParams.developer : true);
175
+ result = result.filter(item => queryParams !== null && queryParams !== void 0 && queryParams.developer ? item.owner.did === queryParams.get('developer') : true);
176
176
  const lowerSearch = (queryParams === null || queryParams === void 0 ? void 0 : (_queryParams$search = queryParams.search) === null || _queryParams$search === void 0 ? void 0 : _queryParams$search.toLocaleLowerCase()) || ''; // 按照搜索筛选
177
177
 
178
178
  result = result.filter(item => {
@@ -195,7 +195,7 @@ function SearchProvider(_ref) {
195
195
  return key;
196
196
  }
197
197
 
198
- return (0, _utils.replace)(_locale.default[locale][key], data);
198
+ return (0, _utils.replaceTranslate)(_locale.default[locale][key], data);
199
199
  };
200
200
 
201
201
  const searchStore = {
@@ -221,7 +221,7 @@ function SearchProvider(_ref) {
221
221
  blockletRender,
222
222
  locale,
223
223
  handleSort: value => {
224
- const changData = _objectSpread(_objectSpread({}, queryParams), {}, {
224
+ const changData = _objectSpread(_objectSpread({}, Object.fromEntries(queryParams.entries())), {}, {
225
225
  sortBy: value,
226
226
  sortDirection: value === 'nameAsc' ? 'asc' : 'desc'
227
227
  });
@@ -229,29 +229,29 @@ function SearchProvider(_ref) {
229
229
  if (isPageMode) {
230
230
  sortParams.sort = changData.sortBy;
231
231
  sortParams.direction = changData.sortDirection;
232
- history.push("".concat(baseUrl, "search?").concat(_qs.default.stringify(changData)));
232
+ history.push("".concat((0, _urlJoin.default)(baseUrl, 'search'), "?").concat((0, _utils.urlStringify)(changData)));
233
233
  } else {
234
234
  setMemoryParams(changData);
235
235
  }
236
236
  },
237
237
  handleSearchKeyword: value => {
238
- const changData = _objectSpread(_objectSpread({}, queryParams), {}, {
238
+ const changData = _objectSpread(_objectSpread({}, Object.fromEntries(queryParams.entries())), {}, {
239
239
  search: value || undefined
240
240
  });
241
241
 
242
242
  if (isPageMode) {
243
- history.push("".concat(baseUrl, "search?").concat(_qs.default.stringify(changData)));
243
+ history.push("".concat((0, _urlJoin.default)(baseUrl, 'search'), "?").concat((0, _utils.urlStringify)(changData)));
244
244
  } else {
245
245
  setMemoryParams(changData);
246
246
  }
247
247
  },
248
248
  handlePriceFilter: value => {
249
- const changData = _objectSpread(_objectSpread({}, queryParams), {}, {
250
- price: value === queryParams.price ? undefined : value
249
+ const changData = _objectSpread(_objectSpread({}, Object.fromEntries(queryParams.entries())), {}, {
250
+ price: value === queryParams.get('price') ? undefined : value
251
251
  });
252
252
 
253
253
  if (isPageMode) {
254
- history.push("".concat(baseUrl, "search?").concat(_qs.default.stringify(changData)));
254
+ history.push("".concat((0, _urlJoin.default)(baseUrl, 'search'), "?").concat((0, _utils.urlStringify)(changData)));
255
255
  } else {
256
256
  setMemoryParams(changData);
257
257
  }
@@ -269,22 +269,22 @@ function SearchProvider(_ref) {
269
269
  },
270
270
  handleCategorySelect: value => {
271
271
  if (value === 'all') {
272
- const changData = _objectSpread(_objectSpread({}, queryParams), {}, {
272
+ const changData = _objectSpread(_objectSpread({}, Object.fromEntries(queryParams.entries())), {}, {
273
273
  category: undefined
274
274
  });
275
275
 
276
276
  if (isPageMode) {
277
- history.push(!isSearchPage ? baseUrl : "".concat(baseUrl, "search?").concat(_qs.default.stringify(changData)));
277
+ history.push(!isSearchPage ? baseUrl : "".concat((0, _urlJoin.default)(baseUrl, 'search'), "?").concat((0, _utils.urlStringify)(changData)));
278
278
  } else {
279
279
  setMemoryParams(changData);
280
280
  }
281
281
  } else {
282
- const changData = _objectSpread(_objectSpread({}, queryParams), {}, {
282
+ const changData = _objectSpread(_objectSpread({}, Object.fromEntries(queryParams.entries())), {}, {
283
283
  category: value
284
284
  });
285
285
 
286
286
  if (isPageMode) {
287
- history.push(!isSearchPage ? "".concat(baseUrl, "category/").concat(value) : "".concat(baseUrl, "search?").concat(_qs.default.stringify(changData)));
287
+ history.push(!isSearchPage ? "".concat((0, _urlJoin.default)(baseUrl, 'category', value)) : "".concat((0, _urlJoin.default)(baseUrl, 'search'), "?").concat((0, _utils.urlStringify)(changData)));
288
288
  } else {
289
289
  setMemoryParams(changData);
290
290
  }
@@ -294,7 +294,7 @@ function SearchProvider(_ref) {
294
294
  get developerName() {
295
295
  var _allBlocklets$find, _allBlocklets$find$ow;
296
296
 
297
- return ((_allBlocklets$find = allBlocklets.find(i => i.owner.did === queryParams.developer)) === null || _allBlocklets$find === void 0 ? void 0 : (_allBlocklets$find$ow = _allBlocklets$find.owner) === null || _allBlocklets$find$ow === void 0 ? void 0 : _allBlocklets$find$ow.name) || '';
297
+ return ((_allBlocklets$find = allBlocklets.find(i => i.owner.did === queryParams.get('developer'))) === null || _allBlocklets$find === void 0 ? void 0 : (_allBlocklets$find$ow = _allBlocklets$find.owner) === null || _allBlocklets$find$ow === void 0 ? void 0 : _allBlocklets$find$ow.name) || '';
298
298
  }
299
299
 
300
300
  };
@@ -303,6 +303,9 @@ function SearchProvider(_ref) {
303
303
  fetchCategories();
304
304
  }
305
305
  }, [!hasDeveloperFilter]);
306
+ (0, _react.useEffect)(() => {
307
+ fetchBlocklets();
308
+ }, [endpoint]);
306
309
  return /*#__PURE__*/_react.default.createElement(Provider, {
307
310
  value: searchStore
308
311
  }, children);
@@ -3,10 +3,12 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.replace = exports.getStoreDetail = exports.getSortOptions = exports.getPrices = exports.getCategories = exports.formatLogoPath = exports.formatError = exports.filterBlockletByPrice = void 0;
6
+ exports.urlStringify = exports.replaceTranslate = exports.removeUndefined = exports.getStoreDetail = exports.getSortOptions = exports.getPrices = exports.getCategories = exports.formatLogoPath = exports.formatError = exports.filterBlockletByPrice = void 0;
7
7
 
8
8
  var _urlJoin = _interopRequireDefault(require("url-join"));
9
9
 
10
+ var _cloneDeep = _interopRequireDefault(require("lodash-es/cloneDeep"));
11
+
10
12
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
11
13
 
12
14
  const isFreeBlocklet = meta => {
@@ -119,7 +121,29 @@ const formatLogoPath = function formatLogoPath(did, asset) {
119
121
 
120
122
  exports.formatLogoPath = formatLogoPath;
121
123
 
122
- const replace = (template, data) => // eslint-disable-next-line no-prototype-builtins
124
+ const replaceTranslate = (template, data) => // eslint-disable-next-line no-prototype-builtins
123
125
  template.replace(/{(\w*)}/g, (m, key) => data.hasOwnProperty(key) ? data[key] : '');
124
126
 
125
- exports.replace = replace;
127
+ exports.replaceTranslate = replaceTranslate;
128
+
129
+ const removeUndefined = obj => {
130
+ const innerObj = (0, _cloneDeep.default)(obj);
131
+ Object.keys(innerObj).forEach(key => {
132
+ if (innerObj[key] === undefined) {
133
+ delete innerObj[key];
134
+ }
135
+ });
136
+ return innerObj;
137
+ };
138
+
139
+ exports.removeUndefined = removeUndefined;
140
+
141
+ const urlStringify = obj => {
142
+ if (!obj) {
143
+ throw new Error('obj is required in urlStringify ');
144
+ }
145
+
146
+ return new URLSearchParams(removeUndefined(obj)).toString();
147
+ };
148
+
149
+ exports.urlStringify = urlStringify;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blocklet/list",
3
- "version": "0.8.10",
3
+ "version": "0.8.13",
4
4
  "description": "Common ux components of blocklet",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -35,7 +35,7 @@
35
35
  "react": ">=16.12.0"
36
36
  },
37
37
  "dependencies": {
38
- "@arcblock/ux": "^2.1.2",
38
+ "@arcblock/ux": "^2.1.3",
39
39
  "@emotion/react": "^11.9.0",
40
40
  "@emotion/styled": "^11.8.1",
41
41
  "@mui/icons-material": "^5.6.2",
@@ -62,5 +62,5 @@
62
62
  "babel-plugin-inline-react-svg": "^2.0.1",
63
63
  "babel-plugin-styled-components": "^1.10.7"
64
64
  },
65
- "gitHead": "6808074d05a774af33190102bdf18d61194e2f04"
65
+ "gitHead": "966ec885a8c7a68eb84be25bd138be696f3b09f4"
66
66
  }
package/src/base.js CHANGED
@@ -24,7 +24,7 @@ const ListBase = () => {
24
24
  <StyledMin>
25
25
  <Box className="marketplace-header" display="flex" alignItems="center">
26
26
  <Hidden mdDown>
27
- {!!queryParams.developer && (
27
+ {!!queryParams.get('developer') && (
28
28
  <FilterAuthor
29
29
  user={developerName}
30
30
  deleteUserTag={() => {
@@ -58,8 +58,8 @@ const ListBase = () => {
58
58
  display="flex"
59
59
  flexWrap="wrap"
60
60
  alignItems="center"
61
- justifyContent={queryParams.developer ? 'space-between' : 'flex-end'}>
62
- {!!queryParams.developer && (
61
+ justifyContent={queryParams.get('developer') ? 'space-between' : 'flex-end'}>
62
+ {!!queryParams.get('developer') && (
63
63
  <FilterAuthor
64
64
  user={developerName}
65
65
  deleteUserTag={() => {
@@ -70,10 +70,10 @@ const ListBase = () => {
70
70
  )}
71
71
  {/* 筛选付费/免费 */}
72
72
  <CustomSelect
73
- value={queryParams.price}
73
+ value={queryParams.get('price')}
74
74
  icon={<FilterListIcon />}
75
75
  options={getPrices(t)}
76
- title={getPrices(t).find((f) => f.value === queryParams.price)?.name || t('common.price')}
76
+ title={getPrices(t).find((f) => f.value === queryParams.get('price'))?.name || t('common.price')}
77
77
  onChange={(v) => {
78
78
  searchStore.handlePriceFilter(v);
79
79
  }}
@@ -24,7 +24,7 @@ const Aside = () => {
24
24
  handlePriceFilter(item.value);
25
25
  }}
26
26
  size="small"
27
- checked={item.value === queryParams.price}
27
+ checked={item.value === queryParams.get('price')}
28
28
  />
29
29
  }
30
30
  label={item.name}
@@ -1,11 +1,11 @@
1
1
  import React from 'react';
2
2
  import { Link } from 'react-router-dom';
3
3
  import { MenuItem, ListItem, ListItemText } from '@mui/material';
4
- import { useLocaleContext } from '@arcblock/ux/lib/Locale/context';
5
- import qs from 'qs';
4
+ import joinUrl from 'url-join';
6
5
 
7
6
  import { useSearchContext } from '../contexts/store';
8
7
  import CustomSelect from './custom-select';
8
+ import { urlStringify } from '../tools/utils';
9
9
 
10
10
  /**
11
11
  * 小屏幕下的分类选择器
@@ -47,10 +47,18 @@ const CategorySelect = () => {
47
47
  * @returns
48
48
  */
49
49
  const CategoryLinkList = () => {
50
- const { t, locale } = useLocaleContext();
51
50
  const searchStore = useSearchContext();
52
- const { queryParams, categoryList, isSearchPage, selectedCategory, isPageMode, handleCategorySelect, baseUrl } =
53
- searchStore;
51
+ const {
52
+ queryParams,
53
+ categoryList,
54
+ isSearchPage,
55
+ selectedCategory,
56
+ isPageMode,
57
+ handleCategorySelect,
58
+ baseUrl,
59
+ locale,
60
+ t,
61
+ } = searchStore;
54
62
 
55
63
  let content = null;
56
64
  if (isPageMode) {
@@ -60,7 +68,14 @@ const CategoryLinkList = () => {
60
68
  selected={!selectedCategory}
61
69
  button
62
70
  component={Link}
63
- to={!isSearchPage ? baseUrl : `${baseUrl}search?${qs.stringify({ ...queryParams, category: undefined })}`}>
71
+ to={
72
+ !isSearchPage
73
+ ? baseUrl
74
+ : `${joinUrl(baseUrl, '/search')}?${urlStringify({
75
+ ...Object.fromEntries(queryParams.entries()),
76
+ category: undefined,
77
+ })}`
78
+ }>
64
79
  <ListItemText primary={t('category.all')} />
65
80
  </ListItem>
66
81
  {categoryList.map((item) => (
@@ -72,8 +87,11 @@ const CategoryLinkList = () => {
72
87
  component={Link}
73
88
  to={
74
89
  !isSearchPage
75
- ? `${baseUrl}category/${item.name}`
76
- : `${baseUrl}search?${qs.stringify({ ...queryParams, category: item.name })}`
90
+ ? `${joinUrl(baseUrl, '/category', item.name)}`
91
+ : `${joinUrl(baseUrl, '/search')}?${urlStringify({
92
+ ...Object.fromEntries(queryParams.entries()),
93
+ category: item.name,
94
+ })}`
77
95
  }>
78
96
  <ListItemText primary={item.locales[locale]} />
79
97
  </ListItem>
@@ -15,7 +15,7 @@ export default function BlockletList({ blocklets, ...rest }) {
15
15
  const { blockletRender, errors, loadings, selectedCategory, blockletList, getCategoryLocale, queryParams, t } =
16
16
  useSearchContext();
17
17
 
18
- const showFilterTip = !!selectedCategory || !!queryParams.price;
18
+ const showFilterTip = !!selectedCategory || !!queryParams.get('price');
19
19
 
20
20
  if (errors.fetchBlockletsError) {
21
21
  return (
@@ -31,25 +31,25 @@ export default function BlockletList({ blocklets, ...rest }) {
31
31
  </Box>
32
32
  );
33
33
  }
34
- if (queryParams.search && showFilterTip && blockletList.length === 0) {
34
+ if (queryParams.get('search') && showFilterTip && blockletList.length === 0) {
35
35
  return (
36
36
  <CustomEmpty>
37
37
  <EmptyTitle
38
38
  primaryStart={t('blocklet.noBlockletPart1')}
39
39
  primaryEnd={t('blocklet.noBlockletPart2')}
40
- search={queryParams.search}
40
+ search={queryParams.get('search')}
41
41
  />
42
42
  <NoResultsTips keywordTip filterTip />
43
43
  </CustomEmpty>
44
44
  );
45
45
  }
46
- if (queryParams.search && blockletList.length === 0) {
46
+ if (queryParams.get('search') && blockletList.length === 0) {
47
47
  return (
48
48
  <CustomEmpty>
49
49
  <EmptyTitle
50
50
  primaryStart={t('blocklet.noBlockletPart1')}
51
51
  primaryEnd={t('blocklet.noBlockletPart2')}
52
- search={queryParams.search}
52
+ search={queryParams.get('search')}
53
53
  />
54
54
  <NoResultsTips keywordTip />
55
55
  </CustomEmpty>
@@ -84,7 +84,7 @@ export default function BlockletList({ blocklets, ...rest }) {
84
84
  <Grid container spacing={4} {...rest}>
85
85
  {blocklets.map((blocklet) => (
86
86
  <StyledGrid item lg={4} md={6} sm={6} xs={12} key={blocklet.did} data-blocklet-did={blocklet.did}>
87
- {blockletRender(blocklet)}
87
+ {blockletRender({ blocklet, blocklets: blockletList })}
88
88
  </StyledGrid>
89
89
  ))}
90
90
  </Grid>
@@ -10,7 +10,7 @@ import { useSearchContext } from '../contexts/store';
10
10
  const Search = ({ placeholder, ...rest }) => {
11
11
  const searchStore = useSearchContext();
12
12
  const { queryParams, handleSearchKeyword } = searchStore;
13
- const [searchStr, setSearchStr] = useState(queryParams.search || '');
13
+ const [searchStr, setSearchStr] = useState(queryParams.get('search') || '');
14
14
 
15
15
  const { run: handleSearch } = useDebounceFn(handleSearchKeyword, { wait: 300 });
16
16
  const handleChange = (event) => {
@@ -24,6 +24,9 @@ const Search = ({ placeholder, ...rest }) => {
24
24
  };
25
25
  return (
26
26
  <StyledSearch
27
+ inputProps={{
28
+ 'data-cy': 'search-blocklet',
29
+ }}
27
30
  startAdornment={
28
31
  <InputAdornment position="start">
29
32
  <StyledMagnify />
@@ -33,10 +36,11 @@ const Search = ({ placeholder, ...rest }) => {
33
36
  placeholder={placeholder}
34
37
  value={searchStr}
35
38
  title={placeholder}
39
+ data-cy="search"
36
40
  endAdornment={
37
41
  searchStr && (
38
- <InputAdornment onClick={handleClose} position="end">
39
- <StyledClose />
42
+ <InputAdornment position="end">
43
+ <StyledClose data-cy="search-delete" onClick={handleClose} />
40
44
  </InputAdornment>
41
45
  )
42
46
  }
@@ -1,14 +1,13 @@
1
1
  import React, { useContext, createContext, useMemo, useEffect, useState } from 'react';
2
2
  import PropTypes from 'prop-types';
3
- import qs from 'qs';
4
- import { useParams, useLocation, useHistory } from 'react-router-dom';
3
+ import { useParams, useHistory } from 'react-router-dom';
5
4
  import { useRequest } from 'ahooks';
6
5
  import orderBy from 'lodash-es/orderBy';
7
6
  import axios from 'axios';
8
7
  import joinUrl from 'url-join';
9
8
 
10
9
  import usePageState from '../hooks/page-state';
11
- import { getCategories, filterBlockletByPrice, replace } from '../tools/utils';
10
+ import { getCategories, filterBlockletByPrice, replaceTranslate, urlStringify } from '../tools/utils';
12
11
  import translations from '../assets/locale';
13
12
 
14
13
  const axiosInstance = axios.create();
@@ -16,7 +15,7 @@ const Search = createContext({});
16
15
  const { Provider, Consumer } = Search;
17
16
 
18
17
  function SearchProvider({ children, baseUrl, type, endpoint, locale, blockletRender }) {
19
- const location = useLocation();
18
+ const { location } = window;
20
19
  const history = useHistory();
21
20
  const pathParams = useParams();
22
21
  const isPageMode = type === 'page';
@@ -27,12 +26,13 @@ function SearchProvider({ children, baseUrl, type, endpoint, locale, blockletRen
27
26
  data: allBlocklets,
28
27
  error: fetchBlockletsError,
29
28
  loading: fetchBlockletsLoading,
29
+ run: fetchBlocklets,
30
30
  } = useRequest(
31
31
  async () => {
32
32
  const { data: list } = await axiosInstance.get(joinUrl(endpoint, '/api/blocklets.json'));
33
33
  return list;
34
34
  },
35
- { initialData: [] }
35
+ { initialData: [], manual: true }
36
36
  );
37
37
 
38
38
  const {
@@ -54,7 +54,7 @@ function SearchProvider({ children, baseUrl, type, endpoint, locale, blockletRen
54
54
  });
55
55
 
56
56
  const queryParams = useMemo(() => {
57
- return isPageMode ? qs.parse(location.search, { ignoreQueryPrefix: true }) : memoryParams;
57
+ return isPageMode ? new URLSearchParams(location.search) : new Map(Object.entries(memoryParams));
58
58
  }, [memoryParams, location.search]);
59
59
 
60
60
  let sortParams;
@@ -68,8 +68,8 @@ function SearchProvider({ children, baseUrl, type, endpoint, locale, blockletRen
68
68
  baseUrl
69
69
  );
70
70
  const urlSortParams = {
71
- sort: queryParams.sortBy,
72
- direction: queryParams.sortDirection,
71
+ sort: queryParams.get('sortBy'),
72
+ direction: queryParams.get('sortDirection'),
73
73
  };
74
74
  sortParams = urlSortParams.sort && urlSortParams.direction ? urlSortParams : localSortParams;
75
75
  } else {
@@ -81,21 +81,21 @@ function SearchProvider({ children, baseUrl, type, endpoint, locale, blockletRen
81
81
  }, [memoryParams]);
82
82
  }
83
83
 
84
- const isSearchPage = location.pathname === '/search';
84
+ const isSearchPage = location.pathname === joinUrl(baseUrl, '/search');
85
85
  const selectedCategory = useMemo(() => {
86
86
  let result = null;
87
87
  if (isPageMode) {
88
- result = !isSearchPage ? pathParams.category : queryParams.category;
88
+ result = !isSearchPage ? pathParams.category : queryParams.get('category');
89
89
  } else {
90
- result = queryParams.category;
90
+ result = queryParams.get('category');
91
91
  }
92
92
  return result;
93
93
  }, [isPageMode, pathParams, queryParams]);
94
94
 
95
- const hasDeveloperFilter = !!queryParams.developer;
95
+ const hasDeveloperFilter = !!queryParams.get('developer');
96
96
  const categoryState = !hasDeveloperFilter
97
97
  ? { data: allCategories }
98
- : getCategories(allBlocklets, queryParams.developer);
98
+ : getCategories(allBlocklets, queryParams.get('developer'));
99
99
 
100
100
  const blockletList = useMemo(() => {
101
101
  const sortByName = (x) => x?.title?.toLocaleLowerCase() || x?.name?.toLocaleLowerCase(); // 按名称排序
@@ -110,11 +110,11 @@ function SearchProvider({ children, baseUrl, type, endpoint, locale, blockletRen
110
110
 
111
111
  let result = allBlocklets || [];
112
112
  // 按照付费/免费筛选
113
- result = filterBlockletByPrice(result, queryParams.price);
113
+ result = filterBlockletByPrice(result, queryParams.get('price'));
114
114
  // 按照分类筛选
115
115
  result = result.filter((item) => (selectedCategory ? item?.category?.name === selectedCategory : true));
116
116
  // 按照作者筛选
117
- result = result.filter((item) => (queryParams?.developer ? item.owner.did === queryParams.developer : true));
117
+ result = result.filter((item) => (queryParams?.developer ? item.owner.did === queryParams.get('developer') : true));
118
118
  const lowerSearch = queryParams?.search?.toLocaleLowerCase() || '';
119
119
  // 按照搜索筛选
120
120
  result = result.filter((item) => {
@@ -140,7 +140,7 @@ function SearchProvider({ children, baseUrl, type, endpoint, locale, blockletRen
140
140
  return key;
141
141
  }
142
142
 
143
- return replace(translations[locale][key], data);
143
+ return replaceTranslate(translations[locale][key], data);
144
144
  };
145
145
 
146
146
  const searchStore = {
@@ -160,27 +160,34 @@ function SearchProvider({ children, baseUrl, type, endpoint, locale, blockletRen
160
160
  blockletRender,
161
161
  locale,
162
162
  handleSort: (value) => {
163
- const changData = { ...queryParams, sortBy: value, sortDirection: value === 'nameAsc' ? 'asc' : 'desc' };
163
+ const changData = {
164
+ ...Object.fromEntries(queryParams.entries()),
165
+ sortBy: value,
166
+ sortDirection: value === 'nameAsc' ? 'asc' : 'desc',
167
+ };
164
168
  if (isPageMode) {
165
169
  sortParams.sort = changData.sortBy;
166
170
  sortParams.direction = changData.sortDirection;
167
- history.push(`${baseUrl}search?${qs.stringify(changData)}`);
171
+ history.push(`${joinUrl(baseUrl, 'search')}?${urlStringify(changData)}`);
168
172
  } else {
169
173
  setMemoryParams(changData);
170
174
  }
171
175
  },
172
176
  handleSearchKeyword: (value) => {
173
- const changData = { ...queryParams, search: value || undefined };
177
+ const changData = { ...Object.fromEntries(queryParams.entries()), search: value || undefined };
174
178
  if (isPageMode) {
175
- history.push(`${baseUrl}search?${qs.stringify(changData)}`);
179
+ history.push(`${joinUrl(baseUrl, 'search')}?${urlStringify(changData)}`);
176
180
  } else {
177
181
  setMemoryParams(changData);
178
182
  }
179
183
  },
180
184
  handlePriceFilter: (value) => {
181
- const changData = { ...queryParams, price: value === queryParams.price ? undefined : value };
185
+ const changData = {
186
+ ...Object.fromEntries(queryParams.entries()),
187
+ price: value === queryParams.get('price') ? undefined : value,
188
+ };
182
189
  if (isPageMode) {
183
- history.push(`${baseUrl}search?${qs.stringify(changData)}`);
190
+ history.push(`${joinUrl(baseUrl, 'search')}?${urlStringify(changData)}`);
184
191
  } else {
185
192
  setMemoryParams(changData);
186
193
  }
@@ -196,23 +203,27 @@ function SearchProvider({ children, baseUrl, type, endpoint, locale, blockletRen
196
203
  },
197
204
  handleCategorySelect: (value) => {
198
205
  if (value === 'all') {
199
- const changData = { ...queryParams, category: undefined };
206
+ const changData = { ...Object.fromEntries(queryParams.entries()), category: undefined };
200
207
  if (isPageMode) {
201
- history.push(!isSearchPage ? baseUrl : `${baseUrl}search?${qs.stringify(changData)}`);
208
+ history.push(!isSearchPage ? baseUrl : `${joinUrl(baseUrl, 'search')}?${urlStringify(changData)}`);
202
209
  } else {
203
210
  setMemoryParams(changData);
204
211
  }
205
212
  } else {
206
- const changData = { ...queryParams, category: value };
213
+ const changData = { ...Object.fromEntries(queryParams.entries()), category: value };
207
214
  if (isPageMode) {
208
- history.push(!isSearchPage ? `${baseUrl}category/${value}` : `${baseUrl}search?${qs.stringify(changData)}`);
215
+ history.push(
216
+ !isSearchPage
217
+ ? `${joinUrl(baseUrl, 'category', value)}`
218
+ : `${joinUrl(baseUrl, 'search')}?${urlStringify(changData)}`
219
+ );
209
220
  } else {
210
221
  setMemoryParams(changData);
211
222
  }
212
223
  }
213
224
  },
214
225
  get developerName() {
215
- return allBlocklets.find((i) => i.owner.did === queryParams.developer)?.owner?.name || '';
226
+ return allBlocklets.find((i) => i.owner.did === queryParams.get('developer'))?.owner?.name || '';
216
227
  },
217
228
  };
218
229
 
@@ -221,6 +232,11 @@ function SearchProvider({ children, baseUrl, type, endpoint, locale, blockletRen
221
232
  fetchCategories();
222
233
  }
223
234
  }, [!hasDeveloperFilter]);
235
+
236
+ useEffect(() => {
237
+ fetchBlocklets();
238
+ }, [endpoint]);
239
+
224
240
  return <Provider value={searchStore}>{children}</Provider>;
225
241
  }
226
242
 
@@ -1,4 +1,5 @@
1
1
  import joinURL from 'url-join';
2
+ import cloneDeep from 'lodash-es/cloneDeep';
2
3
 
3
4
  const isFreeBlocklet = (meta) => {
4
5
  if (!meta.payment) {
@@ -81,10 +82,27 @@ const formatLogoPath = (did, asset, target = 'assets') => {
81
82
  }
82
83
  return `${target}/${did}/${asset}`;
83
84
  };
84
- const replace = (template, data) =>
85
+ const replaceTranslate = (template, data) =>
85
86
  // eslint-disable-next-line no-prototype-builtins
86
87
  template.replace(/{(\w*)}/g, (m, key) => (data.hasOwnProperty(key) ? data[key] : ''));
87
88
 
89
+ const removeUndefined = (obj) => {
90
+ const innerObj = cloneDeep(obj);
91
+ Object.keys(innerObj).forEach((key) => {
92
+ if (innerObj[key] === undefined) {
93
+ delete innerObj[key];
94
+ }
95
+ });
96
+ return innerObj;
97
+ };
98
+
99
+ const urlStringify = (obj) => {
100
+ if (!obj) {
101
+ throw new Error('obj is required in urlStringify ');
102
+ }
103
+ return new URLSearchParams(removeUndefined(obj)).toString();
104
+ };
105
+
88
106
  export {
89
107
  getSortOptions,
90
108
  getPrices,
@@ -92,6 +110,8 @@ export {
92
110
  filterBlockletByPrice,
93
111
  getStoreDetail,
94
112
  formatLogoPath,
95
- replace,
113
+ replaceTranslate,
96
114
  formatError,
115
+ removeUndefined,
116
+ urlStringify,
97
117
  };