@blocklet/launcher-workflow 2.5.0 → 2.5.2
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/es/components/launch-serverless/allocate.js +6 -5
- package/es/components/launch-serverless/shared/common-components.js +4 -298
- package/es/components/launch-serverless/shared/success-display.js +429 -0
- package/es/components/launch-serverless/start-app.js +25 -102
- package/es/locales/en.js +7 -1
- package/es/locales/zh.js +7 -1
- package/es/util.js +1 -1
- package/lib/components/launch-serverless/allocate.js +6 -5
- package/lib/components/launch-serverless/shared/common-components.js +2 -299
- package/lib/components/launch-serverless/shared/success-display.js +440 -0
- package/lib/components/launch-serverless/start-app.js +24 -98
- package/lib/locales/en.js +7 -1
- package/lib/locales/zh.js +7 -1
- package/lib/util.js +1 -1
- package/package.json +4 -4
|
@@ -0,0 +1,429 @@
|
|
|
1
|
+
import Img from '@arcblock/ux/lib/Img';
|
|
2
|
+
import { getBaseURL, getSubscriptionLink } from '@blocklet/launcher-util/es/util';
|
|
3
|
+
import ContentCopyIcon from '@mui/icons-material/ContentCopy';
|
|
4
|
+
import DashboardIcon from '@mui/icons-material/Dashboard';
|
|
5
|
+
import OpenInNewIcon from '@mui/icons-material/OpenInNew';
|
|
6
|
+
import TaskAltIcon from '@mui/icons-material/TaskAlt';
|
|
7
|
+
import { alpha, Box, Button, CircularProgress, Typography, useTheme } from '@mui/material';
|
|
8
|
+
import Chip from '@mui/material/Chip';
|
|
9
|
+
import IconButton from '@mui/material/IconButton';
|
|
10
|
+
import { useSetState } from 'ahooks';
|
|
11
|
+
import PropTypes from 'prop-types';
|
|
12
|
+
import React, { useEffect, useMemo } from 'react';
|
|
13
|
+
import { joinURL } from 'ufo';
|
|
14
|
+
import { useLocaleContext } from '../../../contexts/locale';
|
|
15
|
+
import useRequest from '../../../contexts/request';
|
|
16
|
+
import useSerialPolling from '../../../hooks/use-serial-polling';
|
|
17
|
+
import { checkBlockletAccessible, checkUrlAccessible } from '../../../util';
|
|
18
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
19
|
+
export default function AppSuccessDisplay({
|
|
20
|
+
accessibleUrl,
|
|
21
|
+
sessionId,
|
|
22
|
+
blockletInfo,
|
|
23
|
+
urls,
|
|
24
|
+
launchSession
|
|
25
|
+
}) {
|
|
26
|
+
const theme = useTheme();
|
|
27
|
+
const {
|
|
28
|
+
t
|
|
29
|
+
} = useLocaleContext();
|
|
30
|
+
const {
|
|
31
|
+
api
|
|
32
|
+
} = useRequest();
|
|
33
|
+
const [state, setState] = useSetState({
|
|
34
|
+
accessibleUrls: [accessibleUrl],
|
|
35
|
+
checkingBlockletStatus: true,
|
|
36
|
+
blockletInfo,
|
|
37
|
+
accessibleUrl,
|
|
38
|
+
hasTryStart: false,
|
|
39
|
+
error: null
|
|
40
|
+
});
|
|
41
|
+
const showUrl = useMemo(() => {
|
|
42
|
+
return localStorage.getItem('show-url') === 'true';
|
|
43
|
+
}, []);
|
|
44
|
+
const checkLaunchSession = async () => {
|
|
45
|
+
try {
|
|
46
|
+
const newBlockletInfo = await checkBlockletAccessible(state.accessibleUrl);
|
|
47
|
+
if (!newBlockletInfo) {
|
|
48
|
+
console.warn('the blocklet is not ready or accessible url is not correct, retry getting accessible url');
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
setState({
|
|
52
|
+
blockletInfo: newBlockletInfo
|
|
53
|
+
});
|
|
54
|
+
if (newBlockletInfo.status === 'running') {
|
|
55
|
+
setState({
|
|
56
|
+
blockletInfo: newBlockletInfo,
|
|
57
|
+
checkingBlockletStatus: false
|
|
58
|
+
});
|
|
59
|
+
} else if (newBlockletInfo.status === 'starting') {
|
|
60
|
+
if (!state.hasTryStart) {
|
|
61
|
+
setState(prev => {
|
|
62
|
+
if (Date.now() - prev.startTime > 30 * 1000) {
|
|
63
|
+
api.post(`/launches/${sessionId}/start`);
|
|
64
|
+
return {
|
|
65
|
+
...prev,
|
|
66
|
+
hasTryStart: true
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
return prev;
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
} else if (newBlockletInfo.status === 'stopped') {
|
|
73
|
+
api.post(`/launches/${sessionId}/start`);
|
|
74
|
+
setState({
|
|
75
|
+
hasTryStart: true
|
|
76
|
+
});
|
|
77
|
+
} else {
|
|
78
|
+
console.warn('the blocklet is not installed!', newBlockletInfo.status, newBlockletInfo);
|
|
79
|
+
setState({
|
|
80
|
+
error: 'installFailed',
|
|
81
|
+
checkingBlockletStatus: false
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
setState({
|
|
85
|
+
retryRequestCount: 0
|
|
86
|
+
});
|
|
87
|
+
} catch (error) {
|
|
88
|
+
setState(prev => {
|
|
89
|
+
if (prev.retryRequestCount < 5) {
|
|
90
|
+
console.warn('check launch session occurred error, retry', prev.retryRequestCount, error);
|
|
91
|
+
return {
|
|
92
|
+
...prev,
|
|
93
|
+
retryRequestCount: prev.retryRequestCount + 1
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
return {
|
|
97
|
+
...prev,
|
|
98
|
+
error: 'installFailed',
|
|
99
|
+
retryRequestCount: 0
|
|
100
|
+
};
|
|
101
|
+
});
|
|
102
|
+
console.error(error);
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
// 串行检查,确保前一个请求完成后等待指定时间再发起下一个
|
|
107
|
+
useSerialPolling({
|
|
108
|
+
isEnabled: state.checkingBlockletStatus,
|
|
109
|
+
interval: 3000,
|
|
110
|
+
onPoll: checkLaunchSession
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
// 获取应用基本信息
|
|
114
|
+
const appName = blockletInfo?.appName || t('startApp.unknownApp');
|
|
115
|
+
const appVersion = blockletInfo?.version || launchSession.appInfo?.version || launchSession.blockletMeta?.version || '';
|
|
116
|
+
const appLogo = blockletInfo?.appLogo || launchSession.appInfo?.appLogo || '';
|
|
117
|
+
const description = blockletInfo?.appDescription || launchSession.metadata?.description;
|
|
118
|
+
useEffect(() => {
|
|
119
|
+
const stepActive = document.querySelector('.step-active');
|
|
120
|
+
if (stepActive) {
|
|
121
|
+
stepActive.classList.add('step-checked');
|
|
122
|
+
}
|
|
123
|
+
}, []);
|
|
124
|
+
useEffect(() => {
|
|
125
|
+
const checkUrls = urls.filter(url => !state.accessibleUrls.includes(url));
|
|
126
|
+
checkUrls.forEach(url => {
|
|
127
|
+
checkUrlAccessible(url, 10 * 60 * 1000).then(res => {
|
|
128
|
+
if (res) {
|
|
129
|
+
setState(prev => {
|
|
130
|
+
const accessible = [...prev.accessibleUrls, url];
|
|
131
|
+
return {
|
|
132
|
+
...prev,
|
|
133
|
+
accessibleUrls: accessible
|
|
134
|
+
};
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
});
|
|
138
|
+
});
|
|
139
|
+
}, [state.accessibleUrls, urls, setState]);
|
|
140
|
+
const appUrl = state.accessibleUrls[0] || urls[0];
|
|
141
|
+
const started = state.blockletInfo?.status === 'running';
|
|
142
|
+
return /*#__PURE__*/_jsxs(Box, {
|
|
143
|
+
sx: {
|
|
144
|
+
width: '100%',
|
|
145
|
+
maxWidth: 800,
|
|
146
|
+
margin: '0 auto',
|
|
147
|
+
p: {
|
|
148
|
+
xs: 0,
|
|
149
|
+
sm: 3
|
|
150
|
+
}
|
|
151
|
+
},
|
|
152
|
+
children: [/*#__PURE__*/_jsxs(Box, {
|
|
153
|
+
textAlign: "center",
|
|
154
|
+
mb: 4,
|
|
155
|
+
children: [/*#__PURE__*/_jsx(Box, {
|
|
156
|
+
sx: {
|
|
157
|
+
width: 80,
|
|
158
|
+
height: 80,
|
|
159
|
+
borderRadius: '50%',
|
|
160
|
+
backgroundColor: started ? alpha(theme.palette.success.main, 0.2) : alpha(theme.palette.warning.main, 0.1),
|
|
161
|
+
display: 'flex',
|
|
162
|
+
alignItems: 'center',
|
|
163
|
+
justifyContent: 'center',
|
|
164
|
+
margin: '0 auto 16px',
|
|
165
|
+
transition: 'all 0.3s ease'
|
|
166
|
+
},
|
|
167
|
+
children: started ? /*#__PURE__*/_jsx(TaskAltIcon, {
|
|
168
|
+
sx: {
|
|
169
|
+
fontSize: 40,
|
|
170
|
+
color: 'success.main'
|
|
171
|
+
}
|
|
172
|
+
}) : /*#__PURE__*/_jsx(CircularProgress, {
|
|
173
|
+
sx: {
|
|
174
|
+
color: alpha(theme.palette.warning.main, 0.8)
|
|
175
|
+
},
|
|
176
|
+
size: 40
|
|
177
|
+
})
|
|
178
|
+
}), /*#__PURE__*/_jsx(Typography, {
|
|
179
|
+
variant: "h3",
|
|
180
|
+
component: "h1",
|
|
181
|
+
fontWeight: "bold",
|
|
182
|
+
gutterBottom: true,
|
|
183
|
+
children: started ? `🎉 ${t('startApp.started')}` : `🚀 ${t('startApp.starting')}`
|
|
184
|
+
}), /*#__PURE__*/_jsx(Typography, {
|
|
185
|
+
variant: "body1",
|
|
186
|
+
color: "text.secondary",
|
|
187
|
+
children: started ? t('startApp.startedDescription') : t('startApp.startingDescription')
|
|
188
|
+
})]
|
|
189
|
+
}), /*#__PURE__*/_jsxs(Box, {
|
|
190
|
+
sx: {
|
|
191
|
+
p: 3,
|
|
192
|
+
mb: 3,
|
|
193
|
+
borderRadius: 2,
|
|
194
|
+
border: '1px solid',
|
|
195
|
+
borderColor: 'divider',
|
|
196
|
+
backgroundColor: 'background.paper'
|
|
197
|
+
},
|
|
198
|
+
children: [/*#__PURE__*/_jsxs(Box, {
|
|
199
|
+
display: "flex",
|
|
200
|
+
alignItems: "center",
|
|
201
|
+
gap: 2,
|
|
202
|
+
mb: 2,
|
|
203
|
+
flexWrap: "wrap",
|
|
204
|
+
children: [/*#__PURE__*/_jsxs(Box, {
|
|
205
|
+
flex: 1,
|
|
206
|
+
display: "flex",
|
|
207
|
+
alignItems: "center",
|
|
208
|
+
gap: 2,
|
|
209
|
+
children: [appLogo ? /*#__PURE__*/_jsx(Img, {
|
|
210
|
+
src: joinURL(appUrl, appLogo),
|
|
211
|
+
alt: appName,
|
|
212
|
+
width: 48,
|
|
213
|
+
height: 48,
|
|
214
|
+
style: {
|
|
215
|
+
borderRadius: 10,
|
|
216
|
+
overflow: 'hidden'
|
|
217
|
+
}
|
|
218
|
+
}, appUrl) : null, /*#__PURE__*/_jsxs(Box, {
|
|
219
|
+
flex: 1,
|
|
220
|
+
textAlign: "left",
|
|
221
|
+
children: [/*#__PURE__*/_jsx(Typography, {
|
|
222
|
+
variant: "h6",
|
|
223
|
+
color: "text.hint",
|
|
224
|
+
mb: 1,
|
|
225
|
+
children: t('startApp.projectName')
|
|
226
|
+
}), /*#__PURE__*/_jsx(Typography, {
|
|
227
|
+
variant: "body1",
|
|
228
|
+
fontWeight: "medium",
|
|
229
|
+
children: appName
|
|
230
|
+
})]
|
|
231
|
+
})]
|
|
232
|
+
}), /*#__PURE__*/_jsxs(Box, {
|
|
233
|
+
flex: 1,
|
|
234
|
+
display: "flex",
|
|
235
|
+
alignItems: "center",
|
|
236
|
+
gap: 2,
|
|
237
|
+
minWidth: {
|
|
238
|
+
xs: '100%',
|
|
239
|
+
sm: 'auto'
|
|
240
|
+
},
|
|
241
|
+
children: [appVersion ? /*#__PURE__*/_jsxs(Box, {
|
|
242
|
+
flex: 1,
|
|
243
|
+
textAlign: "left",
|
|
244
|
+
children: [/*#__PURE__*/_jsx(Typography, {
|
|
245
|
+
variant: "h6",
|
|
246
|
+
color: "text.hint",
|
|
247
|
+
mb: 1,
|
|
248
|
+
children: t('startApp.version')
|
|
249
|
+
}), /*#__PURE__*/_jsx(Chip, {
|
|
250
|
+
label: `v${appVersion}`,
|
|
251
|
+
size: "small",
|
|
252
|
+
color: "primary",
|
|
253
|
+
variant: "outlined"
|
|
254
|
+
})]
|
|
255
|
+
}) : null, /*#__PURE__*/_jsxs(Box, {
|
|
256
|
+
flex: 1,
|
|
257
|
+
textAlign: "left",
|
|
258
|
+
children: [/*#__PURE__*/_jsx(Typography, {
|
|
259
|
+
variant: "h6",
|
|
260
|
+
color: "text.hint",
|
|
261
|
+
mb: 1,
|
|
262
|
+
children: t('startApp.status')
|
|
263
|
+
}), started ? /*#__PURE__*/_jsx(Chip, {
|
|
264
|
+
label: t('startApp.running'),
|
|
265
|
+
size: "small",
|
|
266
|
+
color: "success"
|
|
267
|
+
}) : /*#__PURE__*/_jsx(Chip, {
|
|
268
|
+
label: t('startApp.startingText'),
|
|
269
|
+
size: "small",
|
|
270
|
+
color: "warning",
|
|
271
|
+
sx: {
|
|
272
|
+
animation: 'pulse 2s infinite',
|
|
273
|
+
'@keyframes pulse': {
|
|
274
|
+
'0%': {
|
|
275
|
+
opacity: 0.85
|
|
276
|
+
},
|
|
277
|
+
'50%': {
|
|
278
|
+
opacity: 0.7
|
|
279
|
+
},
|
|
280
|
+
'100%': {
|
|
281
|
+
opacity: 0.85
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
})]
|
|
286
|
+
})]
|
|
287
|
+
})]
|
|
288
|
+
}), description && /*#__PURE__*/_jsx(Typography, {
|
|
289
|
+
variant: "body2",
|
|
290
|
+
color: "text.secondary",
|
|
291
|
+
textAlign: "left",
|
|
292
|
+
children: description
|
|
293
|
+
}), showUrl && /*#__PURE__*/_jsxs(Box, {
|
|
294
|
+
children: [/*#__PURE__*/_jsx(Typography, {
|
|
295
|
+
variant: "h6",
|
|
296
|
+
color: "text.hint",
|
|
297
|
+
textAlign: "left",
|
|
298
|
+
children: t('startApp.visitUrl')
|
|
299
|
+
}), urls.map(url => /*#__PURE__*/_jsxs(Box, {
|
|
300
|
+
display: "flex",
|
|
301
|
+
alignItems: "center",
|
|
302
|
+
mt: 1,
|
|
303
|
+
gap: 1,
|
|
304
|
+
sx: {
|
|
305
|
+
backgroundColor: 'divider',
|
|
306
|
+
borderRadius: 1,
|
|
307
|
+
py: 0.5,
|
|
308
|
+
px: 1.5,
|
|
309
|
+
flex: 1
|
|
310
|
+
},
|
|
311
|
+
children: [/*#__PURE__*/_jsx(Box, {
|
|
312
|
+
sx: {
|
|
313
|
+
width: 8,
|
|
314
|
+
height: 8,
|
|
315
|
+
borderRadius: '50%',
|
|
316
|
+
backgroundColor: state.accessibleUrls.includes(url) ? 'success.main' : 'error.main'
|
|
317
|
+
}
|
|
318
|
+
}), /*#__PURE__*/_jsx(Typography, {
|
|
319
|
+
variant: "body2",
|
|
320
|
+
textAlign: "left",
|
|
321
|
+
sx: {
|
|
322
|
+
flex: 1,
|
|
323
|
+
fontSize: '0.875rem',
|
|
324
|
+
wordBreak: 'break-all'
|
|
325
|
+
},
|
|
326
|
+
children: url
|
|
327
|
+
}), /*#__PURE__*/_jsx(IconButton, {
|
|
328
|
+
size: "small",
|
|
329
|
+
onClick: () => navigator.clipboard.writeText(url),
|
|
330
|
+
children: /*#__PURE__*/_jsx(ContentCopyIcon, {
|
|
331
|
+
sx: {
|
|
332
|
+
fontSize: 16
|
|
333
|
+
}
|
|
334
|
+
})
|
|
335
|
+
})]
|
|
336
|
+
}, url))]
|
|
337
|
+
})]
|
|
338
|
+
}), /*#__PURE__*/_jsxs(Box, {
|
|
339
|
+
display: "flex",
|
|
340
|
+
flexDirection: "column",
|
|
341
|
+
gap: 2,
|
|
342
|
+
mt: 4,
|
|
343
|
+
children: [/*#__PURE__*/_jsx(Box, {
|
|
344
|
+
display: "flex",
|
|
345
|
+
flexDirection: "column",
|
|
346
|
+
gap: 2,
|
|
347
|
+
alignItems: "center",
|
|
348
|
+
children: /*#__PURE__*/_jsxs(Box, {
|
|
349
|
+
display: "flex",
|
|
350
|
+
gap: 2,
|
|
351
|
+
margin: "0 auto",
|
|
352
|
+
flexDirection: {
|
|
353
|
+
xs: 'column-reverse',
|
|
354
|
+
md: 'row'
|
|
355
|
+
},
|
|
356
|
+
children: [/*#__PURE__*/_jsx(Button, {
|
|
357
|
+
variant: "outlined",
|
|
358
|
+
sx: {
|
|
359
|
+
minWidth: 220
|
|
360
|
+
},
|
|
361
|
+
color: "primary",
|
|
362
|
+
onClick: () => window.open(joinURL(getBaseURL(), `/api/launches/${sessionId}/redirect/dashboard?appUrl=${appUrl}&appDid=${blockletInfo.appPid}&ownerDid=${blockletInfo.ownerDid}`), '_blank'),
|
|
363
|
+
startIcon: /*#__PURE__*/_jsx(DashboardIcon, {
|
|
364
|
+
sx: {
|
|
365
|
+
fontSize: 24,
|
|
366
|
+
color: 'primary.main'
|
|
367
|
+
}
|
|
368
|
+
}),
|
|
369
|
+
children: t('startApp.dashboard')
|
|
370
|
+
}), /*#__PURE__*/_jsx(Button, {
|
|
371
|
+
variant: "contained",
|
|
372
|
+
color: started ? 'primary' : 'inherit',
|
|
373
|
+
disabled: !started,
|
|
374
|
+
sx: {
|
|
375
|
+
minWidth: 220,
|
|
376
|
+
opacity: started ? 1 : 0.7,
|
|
377
|
+
transform: started ? 'scale(1)' : 'scale(0.98)',
|
|
378
|
+
transition: 'all 0.4s cubic-bezier(0.4, 0, 0.2, 1)',
|
|
379
|
+
background: started ? undefined : `linear-gradient(45deg, ${alpha(theme.palette.primary.main, 0.1)}, ${alpha(theme.palette.primary.main, 0.2)})`,
|
|
380
|
+
'&:disabled': {
|
|
381
|
+
color: theme.palette.text.secondary
|
|
382
|
+
}
|
|
383
|
+
},
|
|
384
|
+
onClick: () => started && window.open(joinURL(getBaseURL(), `/api/launches/${sessionId}/redirect/app?appUrl=${appUrl}&appDid=${blockletInfo.appPid}&ownerDid=${blockletInfo.ownerDid}`), '_blank'),
|
|
385
|
+
startIcon: started ? /*#__PURE__*/_jsx(OpenInNewIcon, {
|
|
386
|
+
sx: {
|
|
387
|
+
fontSize: 24,
|
|
388
|
+
color: 'inherit'
|
|
389
|
+
}
|
|
390
|
+
}) : /*#__PURE__*/_jsx(OpenInNewIcon, {
|
|
391
|
+
sx: {
|
|
392
|
+
fontSize: 24,
|
|
393
|
+
color: 'inherit',
|
|
394
|
+
opacity: 0.5
|
|
395
|
+
}
|
|
396
|
+
}),
|
|
397
|
+
children: started ? t('startApp.visitApp') : t('startApp.preparing')
|
|
398
|
+
})]
|
|
399
|
+
})
|
|
400
|
+
}), /*#__PURE__*/_jsxs(Typography, {
|
|
401
|
+
variant: "body2",
|
|
402
|
+
color: "text.secondary",
|
|
403
|
+
sx: {
|
|
404
|
+
margin: '0 auto',
|
|
405
|
+
fontWeight: 400,
|
|
406
|
+
cursor: 'pointer',
|
|
407
|
+
display: 'flex',
|
|
408
|
+
alignItems: 'center',
|
|
409
|
+
gap: 0.5,
|
|
410
|
+
opacity: 0.8
|
|
411
|
+
},
|
|
412
|
+
onClick: () => window.open(getSubscriptionLink(launchSession.subscriptionId), '_blank'),
|
|
413
|
+
children: [/*#__PURE__*/_jsx(OpenInNewIcon, {
|
|
414
|
+
sx: {
|
|
415
|
+
fontSize: 14,
|
|
416
|
+
color: 'inherit'
|
|
417
|
+
}
|
|
418
|
+
}), t('startApp.subscription')]
|
|
419
|
+
})]
|
|
420
|
+
})]
|
|
421
|
+
});
|
|
422
|
+
}
|
|
423
|
+
AppSuccessDisplay.propTypes = {
|
|
424
|
+
launchSession: PropTypes.object.isRequired,
|
|
425
|
+
urls: PropTypes.arrayOf(PropTypes.string).isRequired,
|
|
426
|
+
accessibleUrl: PropTypes.string.isRequired,
|
|
427
|
+
sessionId: PropTypes.string.isRequired,
|
|
428
|
+
blockletInfo: PropTypes.object.isRequired
|
|
429
|
+
};
|
|
@@ -6,9 +6,10 @@ import { LAUNCH_STATUS } from '@blocklet/launcher-util/es/constant';
|
|
|
6
6
|
import { useLocaleContext } from '../../contexts/locale';
|
|
7
7
|
import useRequest from '../../contexts/request';
|
|
8
8
|
import useSerialPolling from '../../hooks/use-serial-polling';
|
|
9
|
-
import {
|
|
9
|
+
import { getBlockletUrls, sortUrls, waitingForRaceAccessible } from '../../util';
|
|
10
10
|
import BaseServerlessLayout from './shared/base-serverless-layout';
|
|
11
|
-
import {
|
|
11
|
+
import { calculateEstimatedTime, useFormatTime } from './shared/common-components';
|
|
12
|
+
import AppSuccessDisplay from './shared/success-display';
|
|
12
13
|
import LoadingDisplayLayout from './shared/loading-display-layout';
|
|
13
14
|
import RetryErrorMessage from './shared/retry-error-message';
|
|
14
15
|
import useWorkflowProgress from './shared/use-workflow-progress';
|
|
@@ -44,26 +45,20 @@ export default function StartApp({
|
|
|
44
45
|
const actions = useMemo(() => {
|
|
45
46
|
// 基础步骤时间
|
|
46
47
|
const steps = [{
|
|
47
|
-
message: t('startApp.waiting.starting'),
|
|
48
|
-
time: 5
|
|
49
|
-
}, {
|
|
50
|
-
message: t('startApp.waiting.parsing'),
|
|
51
|
-
time: 1
|
|
52
|
-
}, {
|
|
53
48
|
message: t('startApp.waiting.initializing'),
|
|
54
|
-
time:
|
|
49
|
+
time: 1
|
|
55
50
|
}, {
|
|
56
51
|
message: t('startApp.waiting.initializingOwner'),
|
|
57
|
-
time:
|
|
52
|
+
time: 1
|
|
58
53
|
}, {
|
|
59
54
|
message: t('startApp.waiting.creatingSecurityRules'),
|
|
60
|
-
time:
|
|
55
|
+
time: 1
|
|
61
56
|
}, {
|
|
62
57
|
message: t('startApp.waiting.assigningDomain'),
|
|
63
|
-
time:
|
|
58
|
+
time: 1
|
|
64
59
|
}, {
|
|
65
60
|
message: t('startApp.waiting.waitingForDomain'),
|
|
66
|
-
time:
|
|
61
|
+
time: 1
|
|
67
62
|
}];
|
|
68
63
|
|
|
69
64
|
// 计算总时间
|
|
@@ -84,7 +79,7 @@ export default function StartApp({
|
|
|
84
79
|
const estimatedTime = useMemo(() => {
|
|
85
80
|
const components = window.blockletMeta?.components || [];
|
|
86
81
|
const additionalTime = calculateEstimatedTime(components);
|
|
87
|
-
return actions.reduce((acc, action) => acc + action.time, 0) + additionalTime;
|
|
82
|
+
return actions.reduce((acc, action) => acc + action.time, 0) + Math.ceil(additionalTime * 0.4);
|
|
88
83
|
}, [actions]);
|
|
89
84
|
const {
|
|
90
85
|
time,
|
|
@@ -107,8 +102,11 @@ export default function StartApp({
|
|
|
107
102
|
};
|
|
108
103
|
const checkOneAccessibleUrl = async urls => {
|
|
109
104
|
try {
|
|
110
|
-
const [accessibleUrl] = await waitingForRaceAccessible(urls);
|
|
111
|
-
return
|
|
105
|
+
const [accessibleUrl, blockletInfo] = await waitingForRaceAccessible(urls);
|
|
106
|
+
return {
|
|
107
|
+
accessibleUrl,
|
|
108
|
+
blockletInfo
|
|
109
|
+
};
|
|
112
110
|
} catch (error) {
|
|
113
111
|
console.error(error);
|
|
114
112
|
return null;
|
|
@@ -136,88 +134,6 @@ export default function StartApp({
|
|
|
136
134
|
}
|
|
137
135
|
return urls;
|
|
138
136
|
}, [state, setState]);
|
|
139
|
-
const checkLaunchSession = async () => {
|
|
140
|
-
try {
|
|
141
|
-
const {
|
|
142
|
-
accessibleUrl
|
|
143
|
-
} = state;
|
|
144
|
-
if (!accessibleUrl) {
|
|
145
|
-
console.warn('the accessible url is not ready, retry getting accessible url');
|
|
146
|
-
setState({
|
|
147
|
-
checkingAccessible: true,
|
|
148
|
-
checkingBlockletStatus: false
|
|
149
|
-
});
|
|
150
|
-
return;
|
|
151
|
-
}
|
|
152
|
-
const blockletInfo = await checkBlockletAccessible(accessibleUrl);
|
|
153
|
-
if (!blockletInfo) {
|
|
154
|
-
console.warn('the blocklet is not ready or accessible url is not correct, retry getting accessible url');
|
|
155
|
-
return;
|
|
156
|
-
}
|
|
157
|
-
setState({
|
|
158
|
-
blockletInfo
|
|
159
|
-
});
|
|
160
|
-
if (blockletInfo.status === 'running') {
|
|
161
|
-
setState({
|
|
162
|
-
blockletInfo,
|
|
163
|
-
starting: false,
|
|
164
|
-
started: true,
|
|
165
|
-
checkingBlockletStatus: false,
|
|
166
|
-
checkingAccessible: false,
|
|
167
|
-
isLoading: false
|
|
168
|
-
});
|
|
169
|
-
} else if (blockletInfo.status === 'starting') {
|
|
170
|
-
if (!state.hasTryStart) {
|
|
171
|
-
setState(prev => {
|
|
172
|
-
if (Date.now() - prev.startTime > estimatedTime * 1000) {
|
|
173
|
-
api.post(`/launches/${sessionId}/start`);
|
|
174
|
-
return {
|
|
175
|
-
...prev,
|
|
176
|
-
hasTryStart: true
|
|
177
|
-
};
|
|
178
|
-
}
|
|
179
|
-
return prev;
|
|
180
|
-
});
|
|
181
|
-
}
|
|
182
|
-
} else if (blockletInfo.status === 'stopped') {
|
|
183
|
-
api.post(`/launches/${sessionId}/start`);
|
|
184
|
-
} else {
|
|
185
|
-
console.warn('the blocklet is not installed!', blockletInfo.status, blockletInfo);
|
|
186
|
-
setState({
|
|
187
|
-
error: 'installFailed',
|
|
188
|
-
starting: false,
|
|
189
|
-
checkingBlockletStatus: false
|
|
190
|
-
});
|
|
191
|
-
}
|
|
192
|
-
setState({
|
|
193
|
-
retryRequestCount: 0
|
|
194
|
-
});
|
|
195
|
-
} catch (error) {
|
|
196
|
-
setState(prev => {
|
|
197
|
-
if (prev.retryRequestCount < 5) {
|
|
198
|
-
console.warn('check launch session occurred error, retry', prev.retryRequestCount, error);
|
|
199
|
-
return {
|
|
200
|
-
...prev,
|
|
201
|
-
retryRequestCount: prev.retryRequestCount + 1
|
|
202
|
-
};
|
|
203
|
-
}
|
|
204
|
-
return {
|
|
205
|
-
...prev,
|
|
206
|
-
error: 'installFailed',
|
|
207
|
-
starting: false,
|
|
208
|
-
retryRequestCount: 0
|
|
209
|
-
};
|
|
210
|
-
});
|
|
211
|
-
console.error(error);
|
|
212
|
-
}
|
|
213
|
-
};
|
|
214
|
-
|
|
215
|
-
// 串行检查,确保前一个请求完成后等待指定时间再发起下一个
|
|
216
|
-
useSerialPolling({
|
|
217
|
-
isEnabled: state.checkingBlockletStatus,
|
|
218
|
-
interval: CHECK_INTERVAL,
|
|
219
|
-
onPoll: checkLaunchSession
|
|
220
|
-
});
|
|
221
137
|
|
|
222
138
|
// 检查可用性 url
|
|
223
139
|
const checkAccessibleUrls = async () => {
|
|
@@ -233,13 +149,20 @@ export default function StartApp({
|
|
|
233
149
|
});
|
|
234
150
|
return;
|
|
235
151
|
}
|
|
236
|
-
const
|
|
152
|
+
const {
|
|
153
|
+
accessibleUrl,
|
|
154
|
+
blockletInfo
|
|
155
|
+
} = await checkOneAccessibleUrl(urls);
|
|
237
156
|
setState(prev => {
|
|
238
157
|
const obj = accessibleUrl ? {
|
|
239
158
|
...prev,
|
|
159
|
+
starting: false,
|
|
160
|
+
started: true,
|
|
161
|
+
isLoading: false,
|
|
240
162
|
checkingAccessible: false,
|
|
241
|
-
checkingBlockletStatus:
|
|
242
|
-
accessibleUrl
|
|
163
|
+
checkingBlockletStatus: false,
|
|
164
|
+
accessibleUrl,
|
|
165
|
+
blockletInfo
|
|
243
166
|
} : prev;
|
|
244
167
|
if (Date.now() - prev.startTime > estimatedTime * 1000 && !prev.hasTryStart) {
|
|
245
168
|
api.post(`/launches/${sessionId}/start`);
|
|
@@ -256,7 +179,7 @@ export default function StartApp({
|
|
|
256
179
|
};
|
|
257
180
|
useSerialPolling({
|
|
258
181
|
isEnabled: state.checkingAccessible,
|
|
259
|
-
interval:
|
|
182
|
+
interval: CHECK_INTERVAL,
|
|
260
183
|
onPoll: checkAccessibleUrls
|
|
261
184
|
});
|
|
262
185
|
useEffect(() => {
|
package/es/locales/en.js
CHANGED
|
@@ -91,13 +91,17 @@ export default {
|
|
|
91
91
|
},
|
|
92
92
|
startApp: {
|
|
93
93
|
pageTitle: 'Start Blocklet',
|
|
94
|
-
starting: 'Starting Blocklet...',
|
|
95
94
|
started: 'Blocklet is ready to use!',
|
|
96
95
|
startedDescription: 'Your blocklet has been successfully started and is now ready to use.',
|
|
96
|
+
starting: 'Blocklet is starting up',
|
|
97
|
+
startingDescription: 'Your blocklet is starting up and will be ready shortly. Please wait a moment.',
|
|
98
|
+
startingTitle: 'Almost there!',
|
|
99
|
+
startingWaitMessage: 'Your blocklet is starting and will be accessible in a moment. You can access the management panel to monitor the progress.',
|
|
97
100
|
startFailed: 'Start Blocklet Failed',
|
|
98
101
|
installFailed: 'Install Blocklet Failed',
|
|
99
102
|
visit: 'Visit Blocklet',
|
|
100
103
|
dashboard: 'View Blocklet Dashboard',
|
|
104
|
+
managementPanel: 'Management Panel',
|
|
101
105
|
subscription: 'Manage Subscription',
|
|
102
106
|
unknownApp: 'Unknown Blocklet',
|
|
103
107
|
appInfo: 'Blocklet Information',
|
|
@@ -106,9 +110,11 @@ export default {
|
|
|
106
110
|
status: 'Status',
|
|
107
111
|
version: 'Version',
|
|
108
112
|
running: 'Running',
|
|
113
|
+
startingText: 'Starting',
|
|
109
114
|
projectName: 'Project Name',
|
|
110
115
|
visitUrl: 'Visit URL',
|
|
111
116
|
visitApp: 'Visit Blocklet',
|
|
117
|
+
preparing: 'Preparing...',
|
|
112
118
|
waiting: {
|
|
113
119
|
starting: 'Starting blocklet...',
|
|
114
120
|
parsing: 'Parsing blocklet metadata...',
|
package/es/locales/zh.js
CHANGED
|
@@ -90,13 +90,17 @@ export default {
|
|
|
90
90
|
},
|
|
91
91
|
startApp: {
|
|
92
92
|
pageTitle: '启动 Blocklet',
|
|
93
|
-
starting: '正在启动 Blocklet...',
|
|
94
93
|
started: 'Blocklet 启动成功',
|
|
95
94
|
startedDescription: '您的 Blocklet 已成功启动并准备就绪,现在可以开始使用了。',
|
|
95
|
+
starting: 'Blocklet 正在启动',
|
|
96
|
+
startingDescription: '您的 Blocklet 正在启动中,很快就可以使用了,请稍等片刻。',
|
|
97
|
+
startingTitle: '即将完成!',
|
|
98
|
+
startingWaitMessage: '您的 Blocklet 正在启动中,稍后即可访问。您可以先进入管理面板查看启动进度。',
|
|
96
99
|
startFailed: '启动 Blocklet 失败',
|
|
97
100
|
installFailed: '安装 Blocklet 失败',
|
|
98
101
|
visit: '访问 Blocklet',
|
|
99
102
|
dashboard: '查看 Blocklet 仪表盘',
|
|
103
|
+
managementPanel: '管理面板',
|
|
100
104
|
subscription: '管理订阅',
|
|
101
105
|
unknownApp: '未知 Blocklet',
|
|
102
106
|
appInfo: 'Blocklet 信息',
|
|
@@ -105,9 +109,11 @@ export default {
|
|
|
105
109
|
status: '状态',
|
|
106
110
|
version: '版本',
|
|
107
111
|
running: '运行中',
|
|
112
|
+
startingText: '启动中',
|
|
108
113
|
projectName: '项目名称',
|
|
109
114
|
visitUrl: '访问地址',
|
|
110
115
|
visitApp: '访问 Blocklet',
|
|
116
|
+
preparing: '准备中...',
|
|
111
117
|
waiting: {
|
|
112
118
|
starting: '正在启动 Blocklet...',
|
|
113
119
|
parsing: '正在解析 Blocklet 元数据...',
|
package/es/util.js
CHANGED
|
@@ -178,7 +178,7 @@ export const checkBlockletAccessible = async (url, timeout = 5000, controller =
|
|
|
178
178
|
try {
|
|
179
179
|
const urlObj = new URL(url);
|
|
180
180
|
urlObj.protocol = 'https';
|
|
181
|
-
const res = await axios.get(joinURL(urlObj.toString(), '/api/__blocklet__.js?type=json'), {
|
|
181
|
+
const res = await axios.get(joinURL(urlObj.toString(), '/api/__blocklet__.js?type=json&owner=1&force=1&nocache=1'), {
|
|
182
182
|
timeout,
|
|
183
183
|
signal: controller?.signal,
|
|
184
184
|
validateStatus: status => {
|