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