@arcblock/ux 2.10.13 → 2.10.15
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/NFTDisplay/index.js
CHANGED
|
@@ -7,7 +7,6 @@ import get from 'lodash/get';
|
|
|
7
7
|
import pako from 'pako';
|
|
8
8
|
import base64 from 'base64-url';
|
|
9
9
|
import isSvg from 'is-svg';
|
|
10
|
-
import { ReactSVG } from 'react-svg';
|
|
11
10
|
import noop from 'lodash/noop';
|
|
12
11
|
import AspectRatioContainer from './aspect-ratio-container';
|
|
13
12
|
import ImgSvgEmbedder from './svg-embedder/img';
|
|
@@ -65,6 +64,7 @@ function NFTDisplay({
|
|
|
65
64
|
checkSvg,
|
|
66
65
|
minimumLoadingTime,
|
|
67
66
|
onCompleted,
|
|
67
|
+
imageFilter,
|
|
68
68
|
...rest
|
|
69
69
|
}) {
|
|
70
70
|
const wrapRoot = children => /*#__PURE__*/_jsx(Root, {
|
|
@@ -96,16 +96,72 @@ function NFTDisplay({
|
|
|
96
96
|
// 首次加载, 对于 url type 的情况, loading 为 true
|
|
97
97
|
const [state, setState] = useState({
|
|
98
98
|
loading: isUrlType,
|
|
99
|
-
error: false
|
|
99
|
+
error: false,
|
|
100
|
+
loadingUrlType: true,
|
|
101
|
+
urlType: null
|
|
100
102
|
});
|
|
101
103
|
const [minimumLoadingReady, setMinimumLoadingReady] = useState(minimumLoadingTime <= 0);
|
|
102
104
|
// console.log('[debug] render', {type, minimumLoadingTime}, JSON.stringify(state))
|
|
103
105
|
|
|
106
|
+
// assemble the complete url
|
|
107
|
+
const getFullContentUrl = ({
|
|
108
|
+
useImageFilter = false,
|
|
109
|
+
t
|
|
110
|
+
} = {}) => {
|
|
111
|
+
const urlObj = new URL(content);
|
|
112
|
+
if (!urlObj.searchParams.has('assetId')) {
|
|
113
|
+
urlObj.searchParams.append('assetId', address);
|
|
114
|
+
}
|
|
115
|
+
if (!urlObj.searchParams.has('vcId') && vcId) {
|
|
116
|
+
urlObj.searchParams.append('vcId', vcId);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// image filter
|
|
120
|
+
if (useImageFilter && imageFilter) {
|
|
121
|
+
Object.entries(imageFilter).forEach(([key, value]) => {
|
|
122
|
+
urlObj.searchParams.append(key, value);
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// other params
|
|
127
|
+
if (t) {
|
|
128
|
+
urlObj.searchParams.append('t', t);
|
|
129
|
+
}
|
|
130
|
+
return urlObj.href;
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
// get url content type
|
|
134
|
+
const getUrlContentType = async () => {
|
|
135
|
+
try {
|
|
136
|
+
const response = await fetch(getFullContentUrl({
|
|
137
|
+
useImageFilter: false,
|
|
138
|
+
t: 'nftdisplay'
|
|
139
|
+
}), {
|
|
140
|
+
method: 'HEAD'
|
|
141
|
+
});
|
|
142
|
+
const contentType = response.headers.get('Content-Type');
|
|
143
|
+
setState({
|
|
144
|
+
...state,
|
|
145
|
+
loadingUrlType: false,
|
|
146
|
+
urlType: contentType
|
|
147
|
+
});
|
|
148
|
+
} catch (error) {
|
|
149
|
+
console.error('Failed to fetch url content type', error);
|
|
150
|
+
setState({
|
|
151
|
+
...state,
|
|
152
|
+
loadingUrlType: false,
|
|
153
|
+
urlType: null
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
};
|
|
104
157
|
useEffect(() => {
|
|
105
158
|
let timer;
|
|
106
159
|
if (minimumLoadingTime > 0) {
|
|
107
160
|
timer = setTimeout(() => setMinimumLoadingReady(true), minimumLoadingTime);
|
|
108
161
|
}
|
|
162
|
+
if (type === 'url') {
|
|
163
|
+
getUrlContentType();
|
|
164
|
+
}
|
|
109
165
|
return () => clearTimeout(timer);
|
|
110
166
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
111
167
|
}, []);
|
|
@@ -120,69 +176,73 @@ function NFTDisplay({
|
|
|
120
176
|
if (state.error) {
|
|
121
177
|
throw new Error('Failed to render NFT Display.');
|
|
122
178
|
}
|
|
179
|
+
const onLoad = () => {
|
|
180
|
+
if (state.loading) {
|
|
181
|
+
setState({
|
|
182
|
+
...state,
|
|
183
|
+
loading: false
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
};
|
|
187
|
+
const onError = () => {
|
|
188
|
+
setState({
|
|
189
|
+
...state,
|
|
190
|
+
error: true,
|
|
191
|
+
loading: false
|
|
192
|
+
});
|
|
193
|
+
};
|
|
194
|
+
|
|
195
|
+
// render image
|
|
196
|
+
const renderImg = () => {
|
|
197
|
+
const url = getFullContentUrl({
|
|
198
|
+
useImageFilter: true
|
|
199
|
+
});
|
|
200
|
+
return /*#__PURE__*/_jsx("img", {
|
|
201
|
+
src: url,
|
|
202
|
+
onError: onError,
|
|
203
|
+
onLoad: onLoad,
|
|
204
|
+
alt: "NFT Display",
|
|
205
|
+
style: {
|
|
206
|
+
width: 'auto',
|
|
207
|
+
height: 'auto'
|
|
208
|
+
}
|
|
209
|
+
});
|
|
210
|
+
};
|
|
211
|
+
|
|
212
|
+
// render object
|
|
213
|
+
const renderObject = () => {
|
|
214
|
+
const objectType = state.urlType || 'image/svg+xml';
|
|
215
|
+
const url = getFullContentUrl();
|
|
216
|
+
return (
|
|
217
|
+
/*#__PURE__*/
|
|
218
|
+
// eslint-disable-next-line jsx-a11y/alt-text
|
|
219
|
+
_jsx("object", {
|
|
220
|
+
type: objectType,
|
|
221
|
+
data: url,
|
|
222
|
+
onErrorCapture: onError,
|
|
223
|
+
onLoad: onLoad,
|
|
224
|
+
style: {
|
|
225
|
+
width: 'auto',
|
|
226
|
+
height: 'auto',
|
|
227
|
+
pointerEvents: 'none'
|
|
228
|
+
}
|
|
229
|
+
}, url)
|
|
230
|
+
);
|
|
231
|
+
};
|
|
123
232
|
const renderNFT = () => {
|
|
124
233
|
if (content) {
|
|
125
234
|
switch (type) {
|
|
126
235
|
case 'url':
|
|
127
236
|
{
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
urlObj.searchParams.append('assetId', address);
|
|
237
|
+
if (state.loadingUrlType) {
|
|
238
|
+
return null;
|
|
131
239
|
}
|
|
132
|
-
|
|
133
|
-
|
|
240
|
+
|
|
241
|
+
// render image
|
|
242
|
+
if (state.urlType?.startsWith('image') && !state.urlType.startsWith('image/svg')) {
|
|
243
|
+
return renderImg();
|
|
134
244
|
}
|
|
135
|
-
|
|
136
|
-
const safeOnLoad = () => {
|
|
137
|
-
if (state.loading) {
|
|
138
|
-
setState({
|
|
139
|
-
...state,
|
|
140
|
-
loading: false
|
|
141
|
-
});
|
|
142
|
-
}
|
|
143
|
-
};
|
|
144
|
-
return state.fallback ? state.fallback?.() : /*#__PURE__*/_jsx(ReactSVG, {
|
|
145
|
-
src: url,
|
|
146
|
-
beforeInjection: svg => {
|
|
147
|
-
svg.setAttribute('style', 'pointer-events: none; width: 100%; height: 100%;');
|
|
148
|
-
},
|
|
149
|
-
afterInjection: safeOnLoad,
|
|
150
|
-
onError: error => {
|
|
151
|
-
let objectType = null;
|
|
152
|
-
if (error?.message?.indexOf('Invalid content type: ') > -1) {
|
|
153
|
-
objectType = error.message.split('Invalid content type: ')?.[1];
|
|
154
|
-
} else if (error?.message?.indexOf('Unable to load SVG file: ') > -1) {
|
|
155
|
-
objectType = 'image/svg+xml';
|
|
156
|
-
}
|
|
157
|
-
setState({
|
|
158
|
-
...state,
|
|
159
|
-
// fallback to object, and use objectType to render
|
|
160
|
-
fallback: () =>
|
|
161
|
-
/*#__PURE__*/
|
|
162
|
-
// eslint-disable-next-line jsx-a11y/alt-text
|
|
163
|
-
_jsx("object", {
|
|
164
|
-
type: objectType,
|
|
165
|
-
data: url,
|
|
166
|
-
onErrorCapture: () => {
|
|
167
|
-
setState({
|
|
168
|
-
...state,
|
|
169
|
-
error: true,
|
|
170
|
-
loading: false
|
|
171
|
-
});
|
|
172
|
-
},
|
|
173
|
-
onLoad: safeOnLoad,
|
|
174
|
-
style: {
|
|
175
|
-
width: 'auto',
|
|
176
|
-
height: 'auto',
|
|
177
|
-
pointerEvents: 'none'
|
|
178
|
-
}
|
|
179
|
-
}, url)
|
|
180
|
-
});
|
|
181
|
-
}
|
|
182
|
-
// evalScripts="always"
|
|
183
|
-
,
|
|
184
|
-
wrapper: "div"
|
|
185
|
-
});
|
|
245
|
+
return renderObject();
|
|
186
246
|
}
|
|
187
247
|
case 'uri':
|
|
188
248
|
{
|
|
@@ -259,7 +319,10 @@ NFTDisplay.propTypes = {
|
|
|
259
319
|
// loading 最小显示时间 (避免闪烁)
|
|
260
320
|
minimumLoadingTime: PropTypes.number,
|
|
261
321
|
// 完成回调, 无论加载成功|失败
|
|
262
|
-
onCompleted: PropTypes.func
|
|
322
|
+
onCompleted: PropTypes.func,
|
|
323
|
+
// 图片处理,参考:https://team.arcblock.io/comment/docs/c158aee4-accd-42f4-9ced-6a23f28c00e0/en/blocklet-image-service-guide
|
|
324
|
+
// 配置参数会全部转发给 Image Service
|
|
325
|
+
imageFilter: PropTypes.object
|
|
263
326
|
};
|
|
264
327
|
NFTDisplay.defaultProps = {
|
|
265
328
|
component: 'span',
|
|
@@ -271,7 +334,8 @@ NFTDisplay.defaultProps = {
|
|
|
271
334
|
preferredSvgEmbedder: 'img',
|
|
272
335
|
checkSvg: false,
|
|
273
336
|
minimumLoadingTime: 0,
|
|
274
|
-
onCompleted: noop
|
|
337
|
+
onCompleted: noop,
|
|
338
|
+
imageFilter: null
|
|
275
339
|
};
|
|
276
340
|
const Root = styled('div')`
|
|
277
341
|
display: flex;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@arcblock/ux",
|
|
3
|
-
"version": "2.10.
|
|
3
|
+
"version": "2.10.15",
|
|
4
4
|
"description": "Common used react components for arcblock products",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"react",
|
|
@@ -54,12 +54,12 @@
|
|
|
54
54
|
"react": ">=18.2.0",
|
|
55
55
|
"react-router-dom": ">=6.22.3"
|
|
56
56
|
},
|
|
57
|
-
"gitHead": "
|
|
57
|
+
"gitHead": "b1c72a6e908b2e4621a64ac5cf3b565d6712c71b",
|
|
58
58
|
"dependencies": {
|
|
59
59
|
"@arcblock/did-motif": "^1.1.13",
|
|
60
|
-
"@arcblock/icons": "^2.10.
|
|
61
|
-
"@arcblock/nft-display": "^2.10.
|
|
62
|
-
"@arcblock/react-hooks": "^2.10.
|
|
60
|
+
"@arcblock/icons": "^2.10.15",
|
|
61
|
+
"@arcblock/nft-display": "^2.10.15",
|
|
62
|
+
"@arcblock/react-hooks": "^2.10.15",
|
|
63
63
|
"@babel/plugin-syntax-dynamic-import": "^7.8.3",
|
|
64
64
|
"@fontsource/inter": "^5.0.16",
|
|
65
65
|
"@fontsource/ubuntu-mono": "^5.0.18",
|
package/src/NFTDisplay/index.js
CHANGED
|
@@ -7,7 +7,6 @@ import get from 'lodash/get';
|
|
|
7
7
|
import pako from 'pako';
|
|
8
8
|
import base64 from 'base64-url';
|
|
9
9
|
import isSvg from 'is-svg';
|
|
10
|
-
import { ReactSVG } from 'react-svg';
|
|
11
10
|
import noop from 'lodash/noop';
|
|
12
11
|
|
|
13
12
|
import AspectRatioContainer from './aspect-ratio-container';
|
|
@@ -64,6 +63,7 @@ function NFTDisplay({
|
|
|
64
63
|
checkSvg,
|
|
65
64
|
minimumLoadingTime,
|
|
66
65
|
onCompleted,
|
|
66
|
+
imageFilter,
|
|
67
67
|
...rest
|
|
68
68
|
}) {
|
|
69
69
|
const wrapRoot = (children) => (
|
|
@@ -90,15 +90,65 @@ function NFTDisplay({
|
|
|
90
90
|
const isUrlType = type === 'url';
|
|
91
91
|
|
|
92
92
|
// 首次加载, 对于 url type 的情况, loading 为 true
|
|
93
|
-
const [state, setState] = useState({
|
|
93
|
+
const [state, setState] = useState({
|
|
94
|
+
loading: isUrlType,
|
|
95
|
+
error: false,
|
|
96
|
+
loadingUrlType: true,
|
|
97
|
+
urlType: null,
|
|
98
|
+
});
|
|
94
99
|
const [minimumLoadingReady, setMinimumLoadingReady] = useState(minimumLoadingTime <= 0);
|
|
95
100
|
// console.log('[debug] render', {type, minimumLoadingTime}, JSON.stringify(state))
|
|
96
101
|
|
|
102
|
+
// assemble the complete url
|
|
103
|
+
const getFullContentUrl = ({ useImageFilter = false, t } = {}) => {
|
|
104
|
+
const urlObj = new URL(content);
|
|
105
|
+
if (!urlObj.searchParams.has('assetId')) {
|
|
106
|
+
urlObj.searchParams.append('assetId', address);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
if (!urlObj.searchParams.has('vcId') && vcId) {
|
|
110
|
+
urlObj.searchParams.append('vcId', vcId);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// image filter
|
|
114
|
+
if (useImageFilter && imageFilter) {
|
|
115
|
+
Object.entries(imageFilter).forEach(([key, value]) => {
|
|
116
|
+
urlObj.searchParams.append(key, value);
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// other params
|
|
121
|
+
if (t) {
|
|
122
|
+
urlObj.searchParams.append('t', t);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
return urlObj.href;
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
// get url content type
|
|
129
|
+
const getUrlContentType = async () => {
|
|
130
|
+
try {
|
|
131
|
+
const response = await fetch(getFullContentUrl({ useImageFilter: false, t: 'nftdisplay' }), {
|
|
132
|
+
method: 'HEAD',
|
|
133
|
+
});
|
|
134
|
+
const contentType = response.headers.get('Content-Type');
|
|
135
|
+
setState({ ...state, loadingUrlType: false, urlType: contentType });
|
|
136
|
+
} catch (error) {
|
|
137
|
+
console.error('Failed to fetch url content type', error);
|
|
138
|
+
setState({ ...state, loadingUrlType: false, urlType: null });
|
|
139
|
+
}
|
|
140
|
+
};
|
|
141
|
+
|
|
97
142
|
useEffect(() => {
|
|
98
143
|
let timer;
|
|
99
144
|
if (minimumLoadingTime > 0) {
|
|
100
145
|
timer = setTimeout(() => setMinimumLoadingReady(true), minimumLoadingTime);
|
|
101
146
|
}
|
|
147
|
+
|
|
148
|
+
if (type === 'url') {
|
|
149
|
+
getUrlContentType();
|
|
150
|
+
}
|
|
151
|
+
|
|
102
152
|
return () => clearTimeout(timer);
|
|
103
153
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
104
154
|
}, []);
|
|
@@ -115,66 +165,55 @@ function NFTDisplay({
|
|
|
115
165
|
throw new Error('Failed to render NFT Display.');
|
|
116
166
|
}
|
|
117
167
|
|
|
168
|
+
const onLoad = () => {
|
|
169
|
+
if (state.loading) {
|
|
170
|
+
setState({ ...state, loading: false });
|
|
171
|
+
}
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
const onError = () => {
|
|
175
|
+
setState({ ...state, error: true, loading: false });
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
// render image
|
|
179
|
+
const renderImg = () => {
|
|
180
|
+
const url = getFullContentUrl({ useImageFilter: true });
|
|
181
|
+
return (
|
|
182
|
+
<img src={url} onError={onError} onLoad={onLoad} alt="NFT Display" style={{ width: 'auto', height: 'auto' }} />
|
|
183
|
+
);
|
|
184
|
+
};
|
|
185
|
+
|
|
186
|
+
// render object
|
|
187
|
+
const renderObject = () => {
|
|
188
|
+
const objectType = state.urlType || 'image/svg+xml';
|
|
189
|
+
const url = getFullContentUrl();
|
|
190
|
+
return (
|
|
191
|
+
// eslint-disable-next-line jsx-a11y/alt-text
|
|
192
|
+
<object
|
|
193
|
+
key={url}
|
|
194
|
+
type={objectType}
|
|
195
|
+
data={url}
|
|
196
|
+
onErrorCapture={onError}
|
|
197
|
+
onLoad={onLoad}
|
|
198
|
+
style={{ width: 'auto', height: 'auto', pointerEvents: 'none' }}
|
|
199
|
+
/>
|
|
200
|
+
);
|
|
201
|
+
};
|
|
202
|
+
|
|
118
203
|
const renderNFT = () => {
|
|
119
204
|
if (content) {
|
|
120
205
|
switch (type) {
|
|
121
206
|
case 'url': {
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
urlObj.searchParams.append('assetId', address);
|
|
207
|
+
if (state.loadingUrlType) {
|
|
208
|
+
return null;
|
|
125
209
|
}
|
|
126
210
|
|
|
127
|
-
|
|
128
|
-
|
|
211
|
+
// render image
|
|
212
|
+
if (state.urlType?.startsWith('image') && !state.urlType.startsWith('image/svg')) {
|
|
213
|
+
return renderImg();
|
|
129
214
|
}
|
|
130
215
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
const safeOnLoad = () => {
|
|
134
|
-
if (state.loading) {
|
|
135
|
-
setState({ ...state, loading: false });
|
|
136
|
-
}
|
|
137
|
-
};
|
|
138
|
-
|
|
139
|
-
return state.fallback ? (
|
|
140
|
-
state.fallback?.()
|
|
141
|
-
) : (
|
|
142
|
-
<ReactSVG
|
|
143
|
-
src={url}
|
|
144
|
-
beforeInjection={(svg) => {
|
|
145
|
-
svg.setAttribute('style', 'pointer-events: none; width: 100%; height: 100%;');
|
|
146
|
-
}}
|
|
147
|
-
afterInjection={safeOnLoad}
|
|
148
|
-
onError={(error) => {
|
|
149
|
-
let objectType = null;
|
|
150
|
-
if (error?.message?.indexOf('Invalid content type: ') > -1) {
|
|
151
|
-
objectType = error.message.split('Invalid content type: ')?.[1];
|
|
152
|
-
} else if (error?.message?.indexOf('Unable to load SVG file: ') > -1) {
|
|
153
|
-
objectType = 'image/svg+xml';
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
setState({
|
|
157
|
-
...state,
|
|
158
|
-
// fallback to object, and use objectType to render
|
|
159
|
-
fallback: () => (
|
|
160
|
-
// eslint-disable-next-line jsx-a11y/alt-text
|
|
161
|
-
<object
|
|
162
|
-
key={url}
|
|
163
|
-
type={objectType}
|
|
164
|
-
data={url}
|
|
165
|
-
onErrorCapture={() => {
|
|
166
|
-
setState({ ...state, error: true, loading: false });
|
|
167
|
-
}}
|
|
168
|
-
onLoad={safeOnLoad}
|
|
169
|
-
style={{ width: 'auto', height: 'auto', pointerEvents: 'none' }}
|
|
170
|
-
/>
|
|
171
|
-
),
|
|
172
|
-
});
|
|
173
|
-
}}
|
|
174
|
-
// evalScripts="always"
|
|
175
|
-
wrapper="div"
|
|
176
|
-
/>
|
|
177
|
-
);
|
|
216
|
+
return renderObject();
|
|
178
217
|
}
|
|
179
218
|
case 'uri': {
|
|
180
219
|
return (
|
|
@@ -245,6 +284,9 @@ NFTDisplay.propTypes = {
|
|
|
245
284
|
minimumLoadingTime: PropTypes.number,
|
|
246
285
|
// 完成回调, 无论加载成功|失败
|
|
247
286
|
onCompleted: PropTypes.func,
|
|
287
|
+
// 图片处理,参考:https://team.arcblock.io/comment/docs/c158aee4-accd-42f4-9ced-6a23f28c00e0/en/blocklet-image-service-guide
|
|
288
|
+
// 配置参数会全部转发给 Image Service
|
|
289
|
+
imageFilter: PropTypes.object,
|
|
248
290
|
};
|
|
249
291
|
|
|
250
292
|
NFTDisplay.defaultProps = {
|
|
@@ -258,6 +300,7 @@ NFTDisplay.defaultProps = {
|
|
|
258
300
|
checkSvg: false,
|
|
259
301
|
minimumLoadingTime: 0,
|
|
260
302
|
onCompleted: noop,
|
|
303
|
+
imageFilter: null,
|
|
261
304
|
};
|
|
262
305
|
|
|
263
306
|
const Root = styled('div')`
|