@blocklet/list 0.8.33 → 0.8.36
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 +10 -3
- package/lib/components/aside.js +1 -1
- package/lib/components/filter/custom-chip.js +1 -0
- package/lib/components/filter/icon.js +1 -1
- package/lib/components/list/index.js +40 -22
- package/lib/contexts/filter.js +49 -11
- package/lib/libs/prop-types.js +0 -2
- package/lib/libs/utils.js +13 -5
- package/package.json +5 -3
- package/src/base.js +6 -2
- package/src/components/aside.js +10 -8
- package/src/components/filter/custom-chip.js +1 -1
- package/src/components/filter/icon.js +12 -10
- package/src/components/list/index.js +40 -14
- package/src/contexts/filter.js +49 -17
- package/src/libs/prop-types.js +0 -2
- package/src/libs/utils.js +9 -4
package/lib/base.js
CHANGED
|
@@ -13,6 +13,10 @@ var _material = require("@mui/material");
|
|
|
13
13
|
|
|
14
14
|
var _Face = _interopRequireDefault(require("@mui/icons-material/Face"));
|
|
15
15
|
|
|
16
|
+
var _reactErrorBoundary = require("react-error-boundary");
|
|
17
|
+
|
|
18
|
+
var _ErrorBoundary = require("@arcblock/ux/lib/ErrorBoundary");
|
|
19
|
+
|
|
16
20
|
var _filter = require("./contexts/filter");
|
|
17
21
|
|
|
18
22
|
var _customSelect = _interopRequireDefault(require("./components/custom-select"));
|
|
@@ -36,7 +40,7 @@ function ListBase() {
|
|
|
36
40
|
|
|
37
41
|
const {
|
|
38
42
|
handleDeveloper,
|
|
39
|
-
|
|
43
|
+
finalBlockletList,
|
|
40
44
|
filters,
|
|
41
45
|
developerName,
|
|
42
46
|
handleSort,
|
|
@@ -104,8 +108,11 @@ function ListBase() {
|
|
|
104
108
|
handlePrice(null);
|
|
105
109
|
}
|
|
106
110
|
})]
|
|
107
|
-
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(
|
|
108
|
-
|
|
111
|
+
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactErrorBoundary.ErrorBoundary, {
|
|
112
|
+
FallbackComponent: _ErrorBoundary.ErrorFallback,
|
|
113
|
+
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_list.default, {
|
|
114
|
+
blocklets: finalBlockletList
|
|
115
|
+
})
|
|
109
116
|
})]
|
|
110
117
|
})]
|
|
111
118
|
});
|
package/lib/components/aside.js
CHANGED
|
@@ -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,26 +5,28 @@ 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"));
|
|
11
13
|
|
|
12
14
|
var _Empty = _interopRequireDefault(require("@arcblock/ux/lib/Empty"));
|
|
13
15
|
|
|
14
|
-
var _Alert = _interopRequireDefault(require("@arcblock/ux/lib/Alert"));
|
|
15
|
-
|
|
16
16
|
var _Box = _interopRequireDefault(require("@mui/material/Box"));
|
|
17
17
|
|
|
18
18
|
var _Grid = _interopRequireDefault(require("@mui/material/Grid"));
|
|
19
19
|
|
|
20
20
|
var _CircularProgress = _interopRequireDefault(require("@mui/material/CircularProgress"));
|
|
21
21
|
|
|
22
|
+
var _reactInfiniteScrollHook = _interopRequireDefault(require("react-infinite-scroll-hook"));
|
|
23
|
+
|
|
24
|
+
var _ErrorBoundary = require("@arcblock/ux/lib/ErrorBoundary");
|
|
25
|
+
|
|
22
26
|
var _empty = require("./empty");
|
|
23
27
|
|
|
24
28
|
var _filter = require("../../contexts/filter");
|
|
25
29
|
|
|
26
|
-
var _utils = require("../../libs/utils");
|
|
27
|
-
|
|
28
30
|
var _jsxRuntime = require("react/jsx-runtime");
|
|
29
31
|
|
|
30
32
|
const _excluded = ["blocklets"];
|
|
@@ -52,20 +54,25 @@ function BlockletList(_ref) {
|
|
|
52
54
|
errors,
|
|
53
55
|
loadings,
|
|
54
56
|
selectedCategory,
|
|
55
|
-
|
|
57
|
+
finalBlockletList,
|
|
56
58
|
getCategoryLocale,
|
|
57
59
|
filters,
|
|
58
|
-
t
|
|
60
|
+
t,
|
|
61
|
+
hasNextPage,
|
|
62
|
+
loadMore,
|
|
63
|
+
endpoint
|
|
59
64
|
} = (0, _filter.useFilterContext)();
|
|
60
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
|
+
});
|
|
61
72
|
|
|
62
73
|
if (errors.fetchBlockletsError) {
|
|
63
|
-
return /*#__PURE__*/(0, _jsxRuntime.jsx)(
|
|
64
|
-
|
|
65
|
-
variant: "icon",
|
|
66
|
-
children: /*#__PURE__*/(0, _jsxRuntime.jsx)("div", {
|
|
67
|
-
children: (0, _utils.formatError)(errors.fetchBlockletsError)
|
|
68
|
-
})
|
|
74
|
+
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_ErrorBoundary.ErrorFallback, {
|
|
75
|
+
error: new Error("Failed to fetch blocklets from ".concat(endpoint, ": ").concat(errors.fetchBlockletsError.message))
|
|
69
76
|
});
|
|
70
77
|
}
|
|
71
78
|
|
|
@@ -78,7 +85,7 @@ function BlockletList(_ref) {
|
|
|
78
85
|
});
|
|
79
86
|
}
|
|
80
87
|
|
|
81
|
-
if (filters.keyword && showFilterTip &&
|
|
88
|
+
if (filters.keyword && showFilterTip && finalBlockletList.length === 0) {
|
|
82
89
|
return /*#__PURE__*/(0, _jsxRuntime.jsxs)(CustomEmpty, {
|
|
83
90
|
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_empty.EmptyTitle, {
|
|
84
91
|
primaryStart: t('blocklet.noBlockletPart1'),
|
|
@@ -91,7 +98,7 @@ function BlockletList(_ref) {
|
|
|
91
98
|
});
|
|
92
99
|
}
|
|
93
100
|
|
|
94
|
-
if (filters.keyword &&
|
|
101
|
+
if (filters.keyword && finalBlockletList.length === 0) {
|
|
95
102
|
return /*#__PURE__*/(0, _jsxRuntime.jsxs)(CustomEmpty, {
|
|
96
103
|
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_empty.EmptyTitle, {
|
|
97
104
|
primaryStart: t('blocklet.noBlockletPart1'),
|
|
@@ -103,7 +110,7 @@ function BlockletList(_ref) {
|
|
|
103
110
|
});
|
|
104
111
|
}
|
|
105
112
|
|
|
106
|
-
if (showFilterTip &&
|
|
113
|
+
if (showFilterTip && finalBlockletList.length === 0) {
|
|
107
114
|
const categoryLocale = getCategoryLocale(selectedCategory);
|
|
108
115
|
return /*#__PURE__*/(0, _jsxRuntime.jsxs)(CustomEmpty, {
|
|
109
116
|
children: [categoryLocale ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_empty.EmptyTitle, {
|
|
@@ -116,16 +123,16 @@ function BlockletList(_ref) {
|
|
|
116
123
|
});
|
|
117
124
|
}
|
|
118
125
|
|
|
119
|
-
if (
|
|
126
|
+
if (finalBlockletList.length === 0) {
|
|
120
127
|
return /*#__PURE__*/(0, _jsxRuntime.jsx)(CustomEmpty, {
|
|
121
128
|
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_empty.NoResults, {})
|
|
122
129
|
});
|
|
123
130
|
}
|
|
124
131
|
|
|
125
|
-
return /*#__PURE__*/(0, _jsxRuntime.
|
|
132
|
+
return /*#__PURE__*/(0, _jsxRuntime.jsxs)(StyledGrid, _objectSpread(_objectSpread({
|
|
126
133
|
container: true
|
|
127
134
|
}, rest), {}, {
|
|
128
|
-
children: blocklets.map(blocklet => /*#__PURE__*/(0, _jsxRuntime.jsx)(StyledGridItem, {
|
|
135
|
+
children: [blocklets.map(blocklet => /*#__PURE__*/(0, _jsxRuntime.jsx)(StyledGridItem, {
|
|
129
136
|
item: true,
|
|
130
137
|
lg: 4,
|
|
131
138
|
md: 6,
|
|
@@ -134,9 +141,20 @@ function BlockletList(_ref) {
|
|
|
134
141
|
"data-blocklet-did": blocklet.did,
|
|
135
142
|
children: blockletRender({
|
|
136
143
|
blocklet,
|
|
137
|
-
blocklets:
|
|
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, {})
|
|
138
156
|
})
|
|
139
|
-
}
|
|
157
|
+
})]
|
|
140
158
|
}));
|
|
141
159
|
}
|
|
142
160
|
|
|
@@ -148,10 +166,10 @@ const StyledGrid = (0, _styledComponents.default)(_Grid.default).withConfig({
|
|
|
148
166
|
displayName: "list__StyledGrid",
|
|
149
167
|
componentId: "sc-1guvpon-0"
|
|
150
168
|
})(["&.MuiGrid-root{width:auto;margin:0 -16px;}"]);
|
|
151
|
-
const StyledGridItem = (0, _styledComponents.default)(_Grid.default).withConfig({
|
|
169
|
+
const StyledGridItem = /*#__PURE__*/(0, _react.memo)((0, _styledComponents.default)(_Grid.default).withConfig({
|
|
152
170
|
displayName: "list__StyledGridItem",
|
|
153
171
|
componentId: "sc-1guvpon-1"
|
|
154
|
-
})(["@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)));
|
|
155
173
|
const CustomEmpty = (0, _styledComponents.default)(_Empty.default).withConfig({
|
|
156
174
|
displayName: "list__CustomEmpty",
|
|
157
175
|
componentId: "sc-1guvpon-2"
|
package/lib/contexts/filter.js
CHANGED
|
@@ -17,6 +17,8 @@ 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
|
+
|
|
20
22
|
var _utils = require("../libs/utils");
|
|
21
23
|
|
|
22
24
|
var _locale = _interopRequireDefault(require("../assets/locale"));
|
|
@@ -44,7 +46,6 @@ function FilterProvider(_ref) {
|
|
|
44
46
|
let {
|
|
45
47
|
filters,
|
|
46
48
|
children,
|
|
47
|
-
baseUrl,
|
|
48
49
|
endpoint,
|
|
49
50
|
locale,
|
|
50
51
|
blockletRender,
|
|
@@ -63,9 +64,14 @@ function FilterProvider(_ref) {
|
|
|
63
64
|
run: fetchBlocklets
|
|
64
65
|
} = (0, _ahooks.useRequest)(async () => {
|
|
65
66
|
const {
|
|
66
|
-
data
|
|
67
|
+
data
|
|
67
68
|
} = await storeApi.get('/api/blocklets.json');
|
|
68
|
-
|
|
69
|
+
|
|
70
|
+
if (!(0, _isArray.default)(data)) {
|
|
71
|
+
throw new Error('/api/blocklets.json response is not array');
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return data;
|
|
69
75
|
}, {
|
|
70
76
|
initialData: [],
|
|
71
77
|
manual: true
|
|
@@ -77,13 +83,23 @@ function FilterProvider(_ref) {
|
|
|
77
83
|
run: fetchCategories
|
|
78
84
|
} = (0, _ahooks.useRequest)(async () => {
|
|
79
85
|
const {
|
|
80
|
-
data
|
|
86
|
+
data
|
|
81
87
|
} = await storeApi.get('/api/blocklets/categories');
|
|
82
|
-
|
|
88
|
+
|
|
89
|
+
if (!(0, _isArray.default)(data)) {
|
|
90
|
+
throw new Error('/api/blocklets/categories response is not array');
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return data;
|
|
83
94
|
}, {
|
|
84
95
|
initialData: [],
|
|
85
96
|
manual: true
|
|
86
97
|
});
|
|
98
|
+
const paginateState = (0, _ahooks.useReactive)({
|
|
99
|
+
currentPage: 1,
|
|
100
|
+
pageSize: (0, _utils.isMobileScreen)() ? 10 : 18,
|
|
101
|
+
defaultCurrentPage: 1
|
|
102
|
+
});
|
|
87
103
|
|
|
88
104
|
const finalFilters = _objectSpread({
|
|
89
105
|
sortBy: 'popularity',
|
|
@@ -93,10 +109,11 @@ function FilterProvider(_ref) {
|
|
|
93
109
|
const selectedCategory = finalFilters.category;
|
|
94
110
|
const hasDeveloperFilter = !!finalFilters.developer;
|
|
95
111
|
const categoryState = (0, _react.useMemo)(() => {
|
|
112
|
+
// 当按作者过滤时,需要从所有blocklets中找出属于作者的分类
|
|
96
113
|
return !hasDeveloperFilter ? {
|
|
97
114
|
data: allCategories
|
|
98
|
-
} : (0, _utils.
|
|
99
|
-
}, [hasDeveloperFilter, allCategories]);
|
|
115
|
+
} : (0, _utils.getCategoriesByAuthor)(allBlocklets, finalFilters.developer);
|
|
116
|
+
}, [hasDeveloperFilter, allCategories, allBlocklets]);
|
|
100
117
|
const blockletList = (0, _react.useMemo)(() => {
|
|
101
118
|
var _finalFilters$keyword;
|
|
102
119
|
|
|
@@ -142,6 +159,10 @@ function FilterProvider(_ref) {
|
|
|
142
159
|
|
|
143
160
|
return (0, _orderBy.default)(blocklets, [sortMap[finalFilters.sortBy]], [finalFilters.sortDirection]);
|
|
144
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]);
|
|
145
166
|
const categoryList = (0, _react.useMemo)(() => {
|
|
146
167
|
const list = categoryState.data || []; // 分类按照名称排序
|
|
147
168
|
|
|
@@ -169,16 +190,16 @@ function FilterProvider(_ref) {
|
|
|
169
190
|
fetchCategoriesLoading
|
|
170
191
|
},
|
|
171
192
|
endpoint,
|
|
172
|
-
|
|
193
|
+
finalBlockletList,
|
|
173
194
|
t: translate,
|
|
174
195
|
filters: finalFilters,
|
|
175
196
|
selectedCategory,
|
|
176
197
|
categoryList,
|
|
177
|
-
baseUrl,
|
|
178
198
|
blockletRender,
|
|
179
199
|
locale,
|
|
180
200
|
categoryOptions,
|
|
181
201
|
priceOptions,
|
|
202
|
+
hasNextPage: blockletList.length >= paginateState.pageSize * paginateState.currentPage,
|
|
182
203
|
handleSort: sort => {
|
|
183
204
|
const changeData = _objectSpread(_objectSpread({}, finalFilters), {}, {
|
|
184
205
|
sortBy: sort,
|
|
@@ -221,6 +242,18 @@ function FilterProvider(_ref) {
|
|
|
221
242
|
|
|
222
243
|
onFilterChange(changeData);
|
|
223
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
|
+
},
|
|
224
257
|
getCategoryLocale: category => {
|
|
225
258
|
if (!category) return null;
|
|
226
259
|
let result = null;
|
|
@@ -233,10 +266,15 @@ function FilterProvider(_ref) {
|
|
|
233
266
|
return result;
|
|
234
267
|
},
|
|
235
268
|
|
|
269
|
+
get allBlocklets() {
|
|
270
|
+
return allBlocklets || [];
|
|
271
|
+
},
|
|
272
|
+
|
|
236
273
|
get developerName() {
|
|
237
|
-
var
|
|
274
|
+
var _blocklets$find, _blocklets$find$owner;
|
|
238
275
|
|
|
239
|
-
|
|
276
|
+
const blocklets = allBlocklets || [];
|
|
277
|
+
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) || '';
|
|
240
278
|
}
|
|
241
279
|
|
|
242
280
|
};
|
package/lib/libs/prop-types.js
CHANGED
|
@@ -22,12 +22,10 @@ const propTypes = {
|
|
|
22
22
|
endpoint: _propTypes.default.string.isRequired,
|
|
23
23
|
blockletRender: _propTypes.default.func.isRequired,
|
|
24
24
|
onFilterChange: _propTypes.default.func,
|
|
25
|
-
baseUrl: _propTypes.default.string,
|
|
26
25
|
locale: _propTypes.default.oneOf(['zh', 'en'])
|
|
27
26
|
};
|
|
28
27
|
exports.propTypes = propTypes;
|
|
29
28
|
const defaultProps = {
|
|
30
|
-
baseUrl: null,
|
|
31
29
|
locale: 'zh',
|
|
32
30
|
filters: {},
|
|
33
31
|
onFilterChange: () => {},
|
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.
|
|
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
|
-
*
|
|
62
|
+
* 从 blocklets 中的得到属于作者的 Categories
|
|
63
63
|
* @param {*} list
|
|
64
64
|
* @param {*} developerDid
|
|
65
65
|
* @returns
|
|
@@ -68,7 +68,9 @@ const getCategoryOptions = function getCategoryOptions() {
|
|
|
68
68
|
|
|
69
69
|
exports.getCategoryOptions = getCategoryOptions;
|
|
70
70
|
|
|
71
|
-
const
|
|
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;
|
|
72
74
|
const filterList = list.filter(item => developerDid ? item.owner.did === developerDid : true);
|
|
73
75
|
const Categories = filterList.map(item => item.category);
|
|
74
76
|
const res = new Map();
|
|
@@ -85,7 +87,7 @@ const getCategories = (list, developerDid) => {
|
|
|
85
87
|
*/
|
|
86
88
|
|
|
87
89
|
|
|
88
|
-
exports.
|
|
90
|
+
exports.getCategoriesByAuthor = getCategoriesByAuthor;
|
|
89
91
|
|
|
90
92
|
const filterBlockletByPrice = function filterBlockletByPrice() {
|
|
91
93
|
let list = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
|
|
@@ -157,4 +159,10 @@ const urlStringify = obj => {
|
|
|
157
159
|
return new URLSearchParams(removeUndefined(obj)).toString();
|
|
158
160
|
};
|
|
159
161
|
|
|
160
|
-
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.
|
|
3
|
+
"version": "0.8.36",
|
|
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.
|
|
41
|
+
"@arcblock/ux": "^2.1.23",
|
|
42
42
|
"@emotion/react": "^11.9.0",
|
|
43
43
|
"@emotion/styled": "^11.8.1",
|
|
44
44
|
"@mui/icons-material": "^5.6.2",
|
|
@@ -47,6 +47,8 @@
|
|
|
47
47
|
"flat": "^5.0.2",
|
|
48
48
|
"lodash": "^4.17.21",
|
|
49
49
|
"prop-types": "^15.7.2",
|
|
50
|
+
"react-error-boundary": "^3.1.4",
|
|
51
|
+
"react-infinite-scroll-hook": "^4.0.3",
|
|
50
52
|
"styled-components": "5.3.5",
|
|
51
53
|
"url-join": "^4.0.1"
|
|
52
54
|
},
|
|
@@ -62,5 +64,5 @@
|
|
|
62
64
|
"eslint": "^8.16.0",
|
|
63
65
|
"prettier": "^2.6.2"
|
|
64
66
|
},
|
|
65
|
-
"gitHead": "
|
|
67
|
+
"gitHead": "d7102af63503db57cb89f856a50fd83e03d1dbdb"
|
|
66
68
|
}
|
package/src/base.js
CHANGED
|
@@ -2,6 +2,8 @@ import styled from 'styled-components';
|
|
|
2
2
|
import SortIcon from '@mui/icons-material/Sort';
|
|
3
3
|
import { Box, Hidden } from '@mui/material';
|
|
4
4
|
import FaceIcon from '@mui/icons-material/Face';
|
|
5
|
+
import { ErrorBoundary } from 'react-error-boundary';
|
|
6
|
+
import { ErrorFallback } from '@arcblock/ux/lib/ErrorBoundary';
|
|
5
7
|
|
|
6
8
|
import { useFilterContext } from './contexts/filter';
|
|
7
9
|
import CustomSelect from './components/custom-select';
|
|
@@ -14,7 +16,7 @@ import Search from './components/search';
|
|
|
14
16
|
function ListBase() {
|
|
15
17
|
const {
|
|
16
18
|
handleDeveloper,
|
|
17
|
-
|
|
19
|
+
finalBlockletList,
|
|
18
20
|
filters,
|
|
19
21
|
developerName,
|
|
20
22
|
handleSort,
|
|
@@ -76,7 +78,9 @@ function ListBase() {
|
|
|
76
78
|
}}
|
|
77
79
|
/>
|
|
78
80
|
</Box>
|
|
79
|
-
<
|
|
81
|
+
<ErrorBoundary FallbackComponent={ErrorFallback}>
|
|
82
|
+
<BlockletList blocklets={finalBlockletList} />
|
|
83
|
+
</ErrorBoundary>
|
|
80
84
|
</StyledMin>
|
|
81
85
|
</Box>
|
|
82
86
|
);
|
package/src/components/aside.js
CHANGED
|
@@ -12,14 +12,16 @@ function Aside() {
|
|
|
12
12
|
<div>
|
|
13
13
|
<FilterGroup title={t('common.price')} options={priceOptions} value={filters.price} onChange={handlePrice} />
|
|
14
14
|
</div>
|
|
15
|
-
|
|
16
|
-
<
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
15
|
+
{categoryOptions.length > 0 && (
|
|
16
|
+
<div style={{ marginTop: '16px' }}>
|
|
17
|
+
<FilterGroup
|
|
18
|
+
title={t('common.category')}
|
|
19
|
+
options={categoryOptions}
|
|
20
|
+
value={selectedCategory}
|
|
21
|
+
onChange={handleCategory}
|
|
22
|
+
/>
|
|
23
|
+
</div>
|
|
24
|
+
)}
|
|
23
25
|
</StyledAside>
|
|
24
26
|
);
|
|
25
27
|
}
|
|
@@ -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
|
}
|
|
@@ -36,16 +36,18 @@ function FilterIcon() {
|
|
|
36
36
|
handelChange('price', v);
|
|
37
37
|
}}
|
|
38
38
|
/>
|
|
39
|
-
|
|
40
|
-
<
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
39
|
+
{categoryOptions.length > 0 && (
|
|
40
|
+
<div style={{ marginTop: '16px' }}>
|
|
41
|
+
<FilterGroup
|
|
42
|
+
title={t('common.category')}
|
|
43
|
+
options={categoryOptions}
|
|
44
|
+
value={selectedCategory}
|
|
45
|
+
onChange={(v) => {
|
|
46
|
+
handelChange('category', v);
|
|
47
|
+
}}
|
|
48
|
+
/>
|
|
49
|
+
</div>
|
|
50
|
+
)}
|
|
49
51
|
</Dialog>
|
|
50
52
|
</StyledDiv>
|
|
51
53
|
);
|
|
@@ -1,26 +1,45 @@
|
|
|
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
|
-
import Alert from '@arcblock/ux/lib/Alert';
|
|
5
5
|
import Box from '@mui/material/Box';
|
|
6
6
|
import Grid from '@mui/material/Grid';
|
|
7
7
|
import CircularProgress from '@mui/material/CircularProgress';
|
|
8
|
+
import useInfiniteScroll from 'react-infinite-scroll-hook';
|
|
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
|
-
import { formatError } from '../../libs/utils';
|
|
12
13
|
|
|
13
14
|
export default function BlockletList({ blocklets, ...rest }) {
|
|
14
|
-
const {
|
|
15
|
-
|
|
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();
|
|
16
28
|
|
|
17
29
|
const showFilterTip = !!selectedCategory || !!filters.price;
|
|
18
30
|
|
|
31
|
+
const [sentryRef] = useInfiniteScroll({
|
|
32
|
+
loading: loadings.fetchBlockletsLoading,
|
|
33
|
+
hasNextPage,
|
|
34
|
+
onLoadMore: loadMore,
|
|
35
|
+
rootMargin: '0px 0px 400px 0px',
|
|
36
|
+
});
|
|
37
|
+
|
|
19
38
|
if (errors.fetchBlockletsError) {
|
|
20
39
|
return (
|
|
21
|
-
<
|
|
22
|
-
|
|
23
|
-
|
|
40
|
+
<ErrorFallback
|
|
41
|
+
error={new Error(`Failed to fetch blocklets from ${endpoint}: ${errors.fetchBlockletsError.message}`)}
|
|
42
|
+
/>
|
|
24
43
|
);
|
|
25
44
|
}
|
|
26
45
|
if (loadings.fetchBlockletsLoading) {
|
|
@@ -30,7 +49,7 @@ export default function BlockletList({ blocklets, ...rest }) {
|
|
|
30
49
|
</Box>
|
|
31
50
|
);
|
|
32
51
|
}
|
|
33
|
-
if (filters.keyword && showFilterTip &&
|
|
52
|
+
if (filters.keyword && showFilterTip && finalBlockletList.length === 0) {
|
|
34
53
|
return (
|
|
35
54
|
<CustomEmpty>
|
|
36
55
|
<EmptyTitle
|
|
@@ -42,7 +61,7 @@ export default function BlockletList({ blocklets, ...rest }) {
|
|
|
42
61
|
</CustomEmpty>
|
|
43
62
|
);
|
|
44
63
|
}
|
|
45
|
-
if (filters.keyword &&
|
|
64
|
+
if (filters.keyword && finalBlockletList.length === 0) {
|
|
46
65
|
return (
|
|
47
66
|
<CustomEmpty>
|
|
48
67
|
<EmptyTitle
|
|
@@ -54,7 +73,7 @@ export default function BlockletList({ blocklets, ...rest }) {
|
|
|
54
73
|
</CustomEmpty>
|
|
55
74
|
);
|
|
56
75
|
}
|
|
57
|
-
if (showFilterTip &&
|
|
76
|
+
if (showFilterTip && finalBlockletList.length === 0) {
|
|
58
77
|
const categoryLocale = getCategoryLocale(selectedCategory);
|
|
59
78
|
return (
|
|
60
79
|
<CustomEmpty>
|
|
@@ -71,7 +90,7 @@ export default function BlockletList({ blocklets, ...rest }) {
|
|
|
71
90
|
</CustomEmpty>
|
|
72
91
|
);
|
|
73
92
|
}
|
|
74
|
-
if (
|
|
93
|
+
if (finalBlockletList.length === 0) {
|
|
75
94
|
return (
|
|
76
95
|
<CustomEmpty>
|
|
77
96
|
<NoResults />
|
|
@@ -83,9 +102,16 @@ export default function BlockletList({ blocklets, ...rest }) {
|
|
|
83
102
|
<StyledGrid container {...rest}>
|
|
84
103
|
{blocklets.map((blocklet) => (
|
|
85
104
|
<StyledGridItem item lg={4} md={6} sm={6} xs={12} key={blocklet.did} data-blocklet-did={blocklet.did}>
|
|
86
|
-
{blockletRender({ blocklet, blocklets:
|
|
105
|
+
{blockletRender({ blocklet, blocklets: finalBlockletList })}
|
|
87
106
|
</StyledGridItem>
|
|
88
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
|
+
)}
|
|
89
115
|
</StyledGrid>
|
|
90
116
|
);
|
|
91
117
|
}
|
|
@@ -103,7 +129,7 @@ const StyledGrid = styled(Grid)`
|
|
|
103
129
|
}
|
|
104
130
|
`;
|
|
105
131
|
|
|
106
|
-
const StyledGridItem = styled(Grid)`
|
|
132
|
+
const StyledGridItem = memo(styled(Grid)`
|
|
107
133
|
@media (max-width: ${(props) => props.theme.breakpoints.values.sm}px) {
|
|
108
134
|
&.MuiGrid-item {
|
|
109
135
|
padding-bottom: 0px;
|
|
@@ -114,7 +140,7 @@ const StyledGridItem = styled(Grid)`
|
|
|
114
140
|
margin-bottom: ${(props) => props.theme.spacing(2)};
|
|
115
141
|
}
|
|
116
142
|
}
|
|
117
|
-
|
|
143
|
+
`);
|
|
118
144
|
const CustomEmpty = styled(Empty)`
|
|
119
145
|
text-align: center;
|
|
120
146
|
.primary {
|
package/src/contexts/filter.js
CHANGED
|
@@ -1,18 +1,24 @@
|
|
|
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
|
-
|
|
7
|
-
|
|
8
|
-
|
|
6
|
+
import isArray from 'lodash/isArray';
|
|
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
|
|
|
12
18
|
const Filter = createContext({});
|
|
13
19
|
const { Provider, Consumer } = Filter;
|
|
14
20
|
|
|
15
|
-
function FilterProvider({ filters, children,
|
|
21
|
+
function FilterProvider({ filters, children, endpoint, locale, blockletRender, onFilterChange, extraFilter }) {
|
|
16
22
|
const storeApi = axios.create({
|
|
17
23
|
baseURL: endpoint,
|
|
18
24
|
});
|
|
@@ -23,8 +29,11 @@ function FilterProvider({ filters, children, baseUrl, endpoint, locale, blocklet
|
|
|
23
29
|
run: fetchBlocklets,
|
|
24
30
|
} = useRequest(
|
|
25
31
|
async () => {
|
|
26
|
-
const { data
|
|
27
|
-
|
|
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
|
+
}
|
|
36
|
+
return data;
|
|
28
37
|
},
|
|
29
38
|
{ initialData: [], manual: true }
|
|
30
39
|
);
|
|
@@ -36,18 +45,23 @@ function FilterProvider({ filters, children, baseUrl, endpoint, locale, blocklet
|
|
|
36
45
|
run: fetchCategories,
|
|
37
46
|
} = useRequest(
|
|
38
47
|
async () => {
|
|
39
|
-
const { data
|
|
40
|
-
|
|
48
|
+
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
|
+
return data;
|
|
41
53
|
},
|
|
42
54
|
{ initialData: [], manual: true }
|
|
43
55
|
);
|
|
44
56
|
|
|
57
|
+
const paginateState = useReactive({ currentPage: 1, pageSize: isMobileScreen() ? 10 : 18, defaultCurrentPage: 1 });
|
|
45
58
|
const finalFilters = { sortBy: 'popularity', sortDirection: 'desc', ...filters };
|
|
46
59
|
const selectedCategory = finalFilters.category;
|
|
47
60
|
const hasDeveloperFilter = !!finalFilters.developer;
|
|
48
61
|
const categoryState = useMemo(() => {
|
|
49
|
-
|
|
50
|
-
|
|
62
|
+
// 当按作者过滤时,需要从所有blocklets中找出属于作者的分类
|
|
63
|
+
return !hasDeveloperFilter ? { data: allCategories } : getCategoriesByAuthor(allBlocklets, finalFilters.developer);
|
|
64
|
+
}, [hasDeveloperFilter, allCategories, allBlocklets]);
|
|
51
65
|
|
|
52
66
|
const blockletList = useMemo(() => {
|
|
53
67
|
const sortByName = (x) => x?.title?.toLocaleLowerCase() || x?.name?.toLocaleLowerCase(); // 按名称排序
|
|
@@ -59,7 +73,6 @@ function FilterProvider({ filters, children, baseUrl, endpoint, locale, blocklet
|
|
|
59
73
|
popularity: sortByPopularity,
|
|
60
74
|
publishAt: sortByPublish,
|
|
61
75
|
};
|
|
62
|
-
|
|
63
76
|
let blocklets = allBlocklets || [];
|
|
64
77
|
// 按照付费/免费筛选
|
|
65
78
|
blocklets = filterBlockletByPrice(blocklets, finalFilters.price);
|
|
@@ -84,6 +97,14 @@ function FilterProvider({ filters, children, baseUrl, endpoint, locale, blocklet
|
|
|
84
97
|
return orderBy(blocklets, [sortMap[finalFilters.sortBy]], [finalFilters.sortDirection]);
|
|
85
98
|
}, [allBlocklets, finalFilters]);
|
|
86
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
|
+
|
|
87
108
|
const categoryList = useMemo(() => {
|
|
88
109
|
const list = categoryState.data || [];
|
|
89
110
|
// 分类按照名称排序
|
|
@@ -98,24 +119,22 @@ function FilterProvider({ filters, children, baseUrl, endpoint, locale, blocklet
|
|
|
98
119
|
|
|
99
120
|
return replaceTranslate(translations[locale][key], data);
|
|
100
121
|
};
|
|
101
|
-
|
|
102
122
|
const categoryOptions = useMemo(() => getCategoryOptions(categoryList, locale), [categoryList, locale]);
|
|
103
123
|
const priceOptions = getPrices(translate);
|
|
104
|
-
|
|
105
124
|
const filterStore = {
|
|
106
125
|
errors: { fetchBlockletsError, fetchCategoriesError },
|
|
107
126
|
loadings: { fetchBlockletsLoading, fetchCategoriesLoading },
|
|
108
127
|
endpoint,
|
|
109
|
-
|
|
128
|
+
finalBlockletList,
|
|
110
129
|
t: translate,
|
|
111
130
|
filters: finalFilters,
|
|
112
131
|
selectedCategory,
|
|
113
132
|
categoryList,
|
|
114
|
-
baseUrl,
|
|
115
133
|
blockletRender,
|
|
116
134
|
locale,
|
|
117
135
|
categoryOptions,
|
|
118
136
|
priceOptions,
|
|
137
|
+
hasNextPage: blockletList.length >= paginateState.pageSize * paginateState.currentPage,
|
|
119
138
|
handleSort: (sort) => {
|
|
120
139
|
const changeData = {
|
|
121
140
|
...finalFilters,
|
|
@@ -147,6 +166,15 @@ function FilterProvider({ filters, children, baseUrl, endpoint, locale, blocklet
|
|
|
147
166
|
const changeData = { ...finalFilters, developer: developer || undefined };
|
|
148
167
|
onFilterChange(changeData);
|
|
149
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
|
+
},
|
|
150
178
|
getCategoryLocale: (category) => {
|
|
151
179
|
if (!category) return null;
|
|
152
180
|
let result = null;
|
|
@@ -156,8 +184,12 @@ function FilterProvider({ filters, children, baseUrl, endpoint, locale, blocklet
|
|
|
156
184
|
}
|
|
157
185
|
return result;
|
|
158
186
|
},
|
|
187
|
+
get allBlocklets() {
|
|
188
|
+
return allBlocklets || [];
|
|
189
|
+
},
|
|
159
190
|
get developerName() {
|
|
160
|
-
|
|
191
|
+
const blocklets = allBlocklets || [];
|
|
192
|
+
return blocklets.find((blocklet) => blocklet.owner.did === finalFilters.developer)?.owner?.name || '';
|
|
161
193
|
},
|
|
162
194
|
};
|
|
163
195
|
|
package/src/libs/prop-types.js
CHANGED
|
@@ -13,12 +13,10 @@ const propTypes = {
|
|
|
13
13
|
endpoint: PropTypes.string.isRequired,
|
|
14
14
|
blockletRender: PropTypes.func.isRequired,
|
|
15
15
|
onFilterChange: PropTypes.func,
|
|
16
|
-
baseUrl: PropTypes.string,
|
|
17
16
|
locale: PropTypes.oneOf(['zh', 'en']),
|
|
18
17
|
};
|
|
19
18
|
|
|
20
19
|
const defaultProps = {
|
|
21
|
-
baseUrl: null,
|
|
22
20
|
locale: 'zh',
|
|
23
21
|
filters: {},
|
|
24
22
|
onFilterChange: () => {},
|
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
|
-
*
|
|
42
|
+
* 从 blocklets 中的得到属于作者的 Categories
|
|
43
43
|
* @param {*} list
|
|
44
44
|
* @param {*} developerDid
|
|
45
45
|
* @returns
|
|
46
46
|
*/
|
|
47
|
-
const
|
|
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
|
-
|
|
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
|
};
|