@blocklet/launcher-workflow 2.1.108 → 2.2.0

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.
Files changed (48) hide show
  1. package/es/assets/images/blocklet-server.svg +10 -0
  2. package/es/assets/images/check-reverse.svg +5 -0
  3. package/es/assets/images/check.svg +5 -0
  4. package/es/assets/images/checkbox-checked.svg +5 -0
  5. package/es/assets/images/checkbox-unchecked.svg +3 -0
  6. package/es/assets/images/next.svg +8 -0
  7. package/es/assets/images/prev.svg +8 -0
  8. package/es/assets/images/space.svg +9 -0
  9. package/es/assets/images/uncheck-reverse.svg +4 -0
  10. package/es/assets/images/uncheck.svg +4 -0
  11. package/es/checkout.js +573 -0
  12. package/es/components/agreement.js +5 -0
  13. package/es/components/checkbox.js +94 -0
  14. package/es/components/confirm.js +95 -0
  15. package/es/components/in-progress-session.js +96 -0
  16. package/es/components/launch-dedicated.js +290 -0
  17. package/es/components/launch-serverless.js +113 -0
  18. package/es/components/layout/body.js +41 -0
  19. package/es/components/layout/footer.js +21 -0
  20. package/es/components/layout/header.js +25 -0
  21. package/es/components/layout/index.js +24 -0
  22. package/es/components/plan.js +445 -0
  23. package/es/contexts/locale.js +2 -0
  24. package/es/contexts/request.js +71 -0
  25. package/es/contexts/session.js +13 -0
  26. package/es/contexts/workflow.js +64 -0
  27. package/es/hooks/query.js +4 -0
  28. package/es/launch.js +36 -0
  29. package/es/locales/en.js +135 -0
  30. package/es/locales/index.js +8 -0
  31. package/es/locales/zh.js +134 -0
  32. package/es/purchase.js +631 -0
  33. package/es/util.js +165 -0
  34. package/lib/checkout.js +5 -5
  35. package/lib/components/checkbox.js +2 -2
  36. package/lib/components/in-progress-session.js +4 -4
  37. package/lib/components/launch-dedicated.js +3 -3
  38. package/lib/components/launch-serverless.js +1 -1
  39. package/lib/components/layout/body.js +2 -2
  40. package/lib/components/layout/footer.js +2 -2
  41. package/lib/components/layout/index.js +2 -2
  42. package/lib/components/plan.js +6 -6
  43. package/lib/contexts/request.js +2 -2
  44. package/lib/locales/en.js +7 -2
  45. package/lib/locales/index.js +15 -9
  46. package/lib/locales/zh.js +7 -2
  47. package/lib/purchase.js +4 -4
  48. package/package.json +32 -7
@@ -0,0 +1,94 @@
1
+ import React from "react";
2
+ import styled from '@emotion/styled';
3
+ import Tooltip from '@mui/material/Tooltip';
4
+ import PropTypes from 'prop-types';
5
+ var CheckedIcon = function CheckedIcon(props) {
6
+ return /*#__PURE__*/_jsxs("svg", {
7
+ ...props,
8
+ children: [/*#__PURE__*/_jsx("path", {
9
+ d: "M10 20c5.523 0 10-4.477 10-10S15.523 0 10 0 0 4.477 0 10s4.477 10 10 10Z",
10
+ fill: "#1DC1C7"
11
+ }), /*#__PURE__*/_jsx("path", {
12
+ d: "m5.474 10.646 2.586 2.586 6.465-6.465",
13
+ fill: "#1DC1C7"
14
+ }), /*#__PURE__*/_jsx("path", {
15
+ fillRule: "evenodd",
16
+ clipRule: "evenodd",
17
+ d: "M15.233 6.06a1 1 0 0 1 0 1.415L8.768 13.94a1 1 0 0 1-1.415 0l-2.586-2.586a1 1 0 1 1 1.415-1.415l1.878 1.88 5.758-5.759a1 1 0 0 1 1.415 0Z",
18
+ fill: "#fff"
19
+ })]
20
+ });
21
+ };
22
+ CheckedIcon.defaultProps = {
23
+ width: "20",
24
+ height: "20",
25
+ fill: "none",
26
+ xmlns: "http://www.w3.org/2000/svg"
27
+ };
28
+ var UncheckedIcon = function UncheckedIcon(props) {
29
+ return /*#__PURE__*/_jsx("svg", {
30
+ ...props,
31
+ children: /*#__PURE__*/_jsx("path", {
32
+ d: "M19.5 10a9.5 9.5 0 0 1-9.5 9.5A9.5 9.5 0 0 1 .5 10 9.5 9.5 0 0 1 10 .5a9.5 9.5 0 0 1 9.5 9.5Z",
33
+ stroke: "#DDD"
34
+ })
35
+ });
36
+ };
37
+ UncheckedIcon.defaultProps = {
38
+ width: "20",
39
+ height: "20",
40
+ fill: "none",
41
+ xmlns: "http://www.w3.org/2000/svg"
42
+ };
43
+ import { useLocaleContext } from '../contexts/locale';
44
+ import { jsx as _jsx } from "react/jsx-runtime";
45
+ import { jsxs as _jsxs } from "react/jsx-runtime";
46
+ export default function CheckBox({
47
+ label,
48
+ checked,
49
+ showHint,
50
+ ...rest
51
+ }) {
52
+ const {
53
+ t
54
+ } = useLocaleContext();
55
+ return /*#__PURE__*/_jsxs(Container, {
56
+ ...rest,
57
+ children: [/*#__PURE__*/_jsx(Tooltip, {
58
+ open: showHint,
59
+ title: t('common.agreementHint'),
60
+ arrow: true,
61
+ placement: "top-start",
62
+ children: /*#__PURE__*/_jsx("div", {
63
+ style: {
64
+ display: 'flex',
65
+ alignItems: 'center'
66
+ },
67
+ children: checked ? /*#__PURE__*/_jsx(CheckedIcon, {}) : /*#__PURE__*/_jsx(UncheckedIcon, {})
68
+ })
69
+ }), /*#__PURE__*/_jsx("div", {
70
+ className: "label",
71
+ children: label
72
+ })]
73
+ });
74
+ }
75
+ const Container = styled.div`
76
+ display: flex;
77
+ justify-content: flex-start;
78
+ align-items: center;
79
+ cursor: pointer;
80
+
81
+ .label {
82
+ margin-left: 8px;
83
+ }
84
+ `;
85
+ CheckBox.propTypes = {
86
+ showHint: PropTypes.bool,
87
+ checked: PropTypes.bool,
88
+ label: PropTypes.any
89
+ };
90
+ CheckBox.defaultProps = {
91
+ checked: false,
92
+ showHint: false,
93
+ label: ''
94
+ };
@@ -0,0 +1,95 @@
1
+ import { useState, useContext } from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import get from 'lodash.get';
4
+ import Button from '@arcblock/ux/lib/Button';
5
+ import Dialog from '@mui/material/Dialog';
6
+ import DialogActions from '@mui/material/DialogActions';
7
+ import DialogContent from '@mui/material/DialogContent';
8
+ import DialogContentText from '@mui/material/DialogContentText';
9
+ import DialogTitle from '@mui/material/DialogTitle';
10
+ import { useTheme } from '@mui/material/styles';
11
+ import useMediaQuery from '@mui/material/useMediaQuery';
12
+ import { LocaleContext } from '../contexts/locale';
13
+ import { jsx as _jsx } from "react/jsx-runtime";
14
+ import { jsxs as _jsxs } from "react/jsx-runtime";
15
+ const translations = {
16
+ en: {
17
+ confirm: 'Confirm',
18
+ cancel: 'Cancel'
19
+ },
20
+ zh: {
21
+ confirm: '同意',
22
+ cancel: '取消'
23
+ }
24
+ };
25
+ export default function ConfirmDialog({
26
+ title,
27
+ description,
28
+ cancel,
29
+ confirm,
30
+ color,
31
+ params: initialParams,
32
+ onCancel,
33
+ onConfirm,
34
+ locale
35
+ }) {
36
+ const {
37
+ locale: innerLocale
38
+ } = useContext(LocaleContext) || {
39
+ locale
40
+ };
41
+ const [params, setParams] = useState(initialParams);
42
+ const theme = useTheme();
43
+ const isBreakpointsDownSm = useMediaQuery(theme.breakpoints.down('md'));
44
+ const onCallback = cb => {
45
+ if (typeof cb === 'function') {
46
+ cb(params);
47
+ }
48
+ };
49
+ return /*#__PURE__*/_jsxs(Dialog, {
50
+ open: true,
51
+ fullScreen: isBreakpointsDownSm,
52
+ children: [/*#__PURE__*/_jsx(DialogTitle, {
53
+ children: typeof title === 'function' ? title() : title
54
+ }), /*#__PURE__*/_jsx(DialogContent, {
55
+ children: /*#__PURE__*/_jsx(DialogContentText, {
56
+ children: typeof description === 'function' ? description(params, setParams) : description
57
+ })
58
+ }), /*#__PURE__*/_jsxs(DialogActions, {
59
+ style: {
60
+ padding: '8px 24px 24px'
61
+ },
62
+ children: [/*#__PURE__*/_jsx(Button, {
63
+ onClick: () => onCallback(onCancel),
64
+ children: cancel || get(translations, `${innerLocale}.cancel`)
65
+ }), /*#__PURE__*/_jsx(Button, {
66
+ onClick: () => onCallback(onConfirm),
67
+ color: color,
68
+ variant: "contained",
69
+ autoFocus: true,
70
+ children: confirm || get(translations, `${innerLocale}.confirm`)
71
+ })]
72
+ })]
73
+ });
74
+ }
75
+ ConfirmDialog.propTypes = {
76
+ title: PropTypes.any.isRequired,
77
+ description: PropTypes.any.isRequired,
78
+ // can be a function that renders different content based on params
79
+ cancel: PropTypes.string,
80
+ color: PropTypes.string,
81
+ confirm: PropTypes.string,
82
+ params: PropTypes.object,
83
+ // This object holds states managed in the dialog
84
+ onCancel: PropTypes.func,
85
+ onConfirm: PropTypes.func.isRequired,
86
+ locale: PropTypes.oneOf(['en', 'zh'])
87
+ };
88
+ ConfirmDialog.defaultProps = {
89
+ onCancel: () => {},
90
+ cancel: '',
91
+ confirm: '',
92
+ color: 'error',
93
+ params: {},
94
+ locale: 'en'
95
+ };
@@ -0,0 +1,96 @@
1
+ import Button from '@arcblock/ux/lib/Button';
2
+ import Dialog from '@arcblock/ux/lib/Dialog';
3
+ import { getContinueLaunchURL } from '@blocklet/launcher-util/es/util';
4
+ import Box from '@mui/material/Box';
5
+ import Link from '@mui/material/Link';
6
+ import { useState } from 'react';
7
+ import useAsync from 'react-use/lib/useAsync';
8
+ import joinURL from 'url-join';
9
+ import { useLocaleContext } from '../contexts/locale';
10
+ import useRequest from '../contexts/request';
11
+ import { useWorkflowContext } from '../contexts/workflow';
12
+
13
+ // 不考虑 localstorage 的过期时间
14
+ import { jsx as _jsx } from "react/jsx-runtime";
15
+ import { jsxs as _jsxs } from "react/jsx-runtime";
16
+ const IGNORE_LAUNCH_SESSION_KEY = 'launcher-ignore-launch-session';
17
+ export default function InProgressSession() {
18
+ const {
19
+ t
20
+ } = useLocaleContext();
21
+ const {
22
+ create
23
+ } = useRequest();
24
+ const {
25
+ routerPrefix,
26
+ baseURL
27
+ } = useWorkflowContext();
28
+ const [open, setOpen] = useState(false);
29
+ const api = create();
30
+ const state = useAsync(async () => {
31
+ const {
32
+ data
33
+ } = await api.get(joinURL(routerPrefix, '/launches/in-progress'));
34
+ if (data.launch) {
35
+ if (localStorage.getItem(IGNORE_LAUNCH_SESSION_KEY) === data.launch._id) {
36
+ return null;
37
+ }
38
+ setOpen(true);
39
+ }
40
+ return data.launch;
41
+ });
42
+ if (state.loading || !state.value) {
43
+ return '';
44
+ }
45
+ if (state.error) {
46
+ console.error('load in progress session failed', state.error);
47
+ return '';
48
+ }
49
+ const handleClose = () => {
50
+ setOpen(false);
51
+ localStorage.setItem(IGNORE_LAUNCH_SESSION_KEY, state.value._id);
52
+ };
53
+ let continueBaseURL = '';
54
+ try {
55
+ continueBaseURL = getContinueLaunchURL({
56
+ baseURL: joinURL(window.location.origin, baseURL),
57
+ launch: state.value
58
+ });
59
+ } catch (error) {
60
+ console.error('get continue url error:', error);
61
+ }
62
+ return /*#__PURE__*/_jsx(Dialog, {
63
+ open: open,
64
+ showCloseButton: true,
65
+ PaperProps: {
66
+ style: {
67
+ maxWidth: 700,
68
+ minWidth: 400,
69
+ minHeight: 200
70
+ }
71
+ },
72
+ title: t('purchase.inProgress.title'),
73
+ onClose: () => handleClose(null),
74
+ actions: /*#__PURE__*/_jsxs(Box, {
75
+ display: "flex",
76
+ gap: 2,
77
+ children: [/*#__PURE__*/_jsx(Button, {
78
+ variant: "outlined",
79
+ size: "small",
80
+ onClick: handleClose,
81
+ children: t('purchase.inProgress.notRemind')
82
+ }), /*#__PURE__*/_jsx(Button, {
83
+ component: Link,
84
+ autoFocus: true,
85
+ variant: "contained",
86
+ color: "primary",
87
+ size: "small",
88
+ href: continueBaseURL,
89
+ children: t('purchase.inProgress.continue')
90
+ })]
91
+ }),
92
+ children: t('purchase.inProgress.content', {
93
+ blockletName: state.value?.blockletMeta?.title
94
+ })
95
+ });
96
+ }
@@ -0,0 +1,290 @@
1
+ import { useContext, useEffect, useState } from 'react';
2
+ import { useNavigate, useParams } from 'react-router-dom';
3
+ import PropTypes from 'prop-types';
4
+ import get from 'lodash.get';
5
+ import useInterval from 'react-use/lib/useInterval';
6
+ import throttle from 'lodash.throttle';
7
+ import styled from '@emotion/styled';
8
+ import Button from '@arcblock/ux/lib/Button';
9
+ import Spinner from '@mui/material/CircularProgress';
10
+ import AnimationWaiter from '@arcblock/ux/lib/AnimationWaiter';
11
+ import { INSTANCE_STATUS } from '@blocklet/launcher-util/es/constant';
12
+ import Toast from '@arcblock/ux/lib/Toast';
13
+ import PageHeader from '@blocklet/launcher-layout/lib/page-header';
14
+ import LaunchResultMessage from '@blocklet/launcher-layout/lib/launch-result-message';
15
+ import ProgressMessage from '@blocklet/launcher-ux/lib/progress-message';
16
+ import { getAPIResponseError, getLaunchBlockletUrl, loadURL } from '../util';
17
+ import useQuery from '../hooks/query';
18
+ import useRequest from '../contexts/request';
19
+ import { LocaleContext } from '../contexts/locale';
20
+ import { jsx as _jsx } from "react/jsx-runtime";
21
+ import { jsxs as _jsxs } from "react/jsx-runtime";
22
+ const STATUS = {
23
+ waiting: 0,
24
+ // 未使用
25
+ launching: 1,
26
+ // 初始状态
27
+ success: 2,
28
+ error: 10,
29
+ notFoundError: 11,
30
+ launchFailedError: 12
31
+ };
32
+ export default function LaunchDedicated({
33
+ type
34
+ }) {
35
+ const query = useQuery();
36
+ const blockletMetaUrl = query.get('blocklet_meta_url');
37
+ const {
38
+ t,
39
+ locale
40
+ } = useContext(LocaleContext);
41
+ const [launchState, setLaunchState] = useState({
42
+ appUrl: '',
43
+ status: STATUS.waiting
44
+ });
45
+ const {
46
+ nftId
47
+ } = useParams();
48
+ const {
49
+ api
50
+ } = useRequest();
51
+ const [progressStepIndex, setProgressStepIndex] = useState(0);
52
+ const [isDone, setIsDone] = useState(false);
53
+ const navigate = useNavigate();
54
+ let createServerTime = sessionStorage.getItem(`launcher-create-server-${nftId}-time`);
55
+ if (!createServerTime) {
56
+ createServerTime = Date.now();
57
+ sessionStorage.setItem(`launcher-create-server-${nftId}-time`, createServerTime);
58
+ }
59
+ const handleInstance = instance => {
60
+ if (instance.status >= INSTANCE_STATUS.running) {
61
+ const url = getLaunchBlockletUrl({
62
+ serverUrl: instance.serverUrl,
63
+ blockletMetaUrl,
64
+ locale,
65
+ chainHost: window.blocklet?.CHAIN_HOST,
66
+ from: query.get('from')
67
+ });
68
+ setIsDone(true);
69
+ setTimeout(() => {
70
+ setLaunchState(pre => ({
71
+ ...pre,
72
+ status: STATUS.success,
73
+ appUrl: url
74
+ }));
75
+ loadURL(url).finally(() => {
76
+ setTimeout(() => {
77
+ window.location.href = url;
78
+ }, 1000);
79
+ });
80
+ }, 1000);
81
+ }
82
+ };
83
+ const launch = async () => {
84
+ try {
85
+ setLaunchState(pre => ({
86
+ ...pre,
87
+ status: STATUS.launching
88
+ }));
89
+ const {
90
+ data: {
91
+ instance
92
+ }
93
+ } = await api.post('/instances/launch', {
94
+ nftId
95
+ }, {
96
+ params: {
97
+ launchType: query.get('launchType')
98
+ }
99
+ });
100
+ handleInstance(instance);
101
+ } catch (err) {
102
+ const errDesc = getAPIResponseError(err);
103
+ if (errDesc === 'Unauthorized') {
104
+ // TODO: 如果没有授权,要求用户提供 NFT
105
+ Toast.error(t('launchPrepare.error.loginFirst'));
106
+ } else {
107
+ Toast.error(errDesc);
108
+ }
109
+ }
110
+ };
111
+ const getInstanceStatus = async () => {
112
+ try {
113
+ const {
114
+ data: {
115
+ instance
116
+ }
117
+ } = await api.get(`/public/instances/${nftId}/status`);
118
+ handleInstance(instance);
119
+ } catch (error) {
120
+ sessionStorage.removeItem(`launcher-create-server-${nftId}-time`);
121
+ if (get(error, 'response.status') === 404 && launchState.status !== STATUS.launching) {
122
+ setLaunchState(state => ({
123
+ ...state,
124
+ status: STATUS.notFoundError
125
+ }));
126
+ throw error;
127
+ }
128
+ setLaunchState(state => ({
129
+ ...state,
130
+ status: STATUS.error
131
+ }));
132
+ console.warn('load instance status error', error);
133
+ throw error;
134
+ }
135
+ };
136
+ const poll = throttle(getInstanceStatus, 5000);
137
+ useInterval(poll, launchState.status === STATUS.launching && nftId ? 5000 : null);
138
+ const successful = launchState.status === STATUS.success;
139
+ const progressSteps = [t('launch.waiting.starting'), t('launch.waiting.securing'), t('launch.waiting.prepare'), t('launch.waiting.waiting'), t('launch.waiting.done')];
140
+ const messageDuration = 40000;
141
+ const fixStepIndex = () => {
142
+ // 防止用户刷新后message进度重新开始,修正 message 展示的信息
143
+ const nowTime = Date.now();
144
+ const creatingTime = nowTime - createServerTime;
145
+ let step = Math.floor(creatingTime / messageDuration);
146
+ if (step > 0) {
147
+ if (step >= progressSteps.length - 1) {
148
+ step = progressSteps.length - 2;
149
+ }
150
+ }
151
+ setProgressStepIndex(step);
152
+ };
153
+ useEffect(() => {
154
+ fixStepIndex();
155
+ const timer = setInterval(() => {
156
+ fixStepIndex();
157
+ }, 1000);
158
+ launch();
159
+ return () => clearInterval(timer);
160
+ // eslint-disable-next-line react-hooks/exhaustive-deps
161
+ }, []);
162
+ return /*#__PURE__*/_jsxs(Content, {
163
+ children: [/*#__PURE__*/_jsx(PageHeader, {
164
+ title: t('launch.pageTitle'),
165
+ className: `launch-page-header ${successful ? 'header-hide' : ''}`,
166
+ disableBack: true
167
+ }), launchState.status === STATUS.waiting && /*#__PURE__*/_jsx("div", {
168
+ className: "center",
169
+ children: /*#__PURE__*/_jsx(Spinner, {})
170
+ }), launchState.status === STATUS.launching && /*#__PURE__*/_jsx(WaiterContainer, {
171
+ children: /*#__PURE__*/_jsx(AnimationWaiter, {
172
+ message: /*#__PURE__*/_jsx("div", {
173
+ style: {
174
+ textAlign: 'center'
175
+ },
176
+ children: /*#__PURE__*/_jsx(ProgressMessage, {
177
+ steps: progressSteps,
178
+ stepIndex: isDone ? 4 : progressStepIndex,
179
+ autoGrowth: isDone ? 500 : 20000
180
+ })
181
+ }),
182
+ increaseSpeed: 0.3,
183
+ messageLoop: false,
184
+ maybeDuration: 180000,
185
+ messageDuration: messageDuration
186
+ })
187
+ }), successful && /*#__PURE__*/_jsx(LaunchResultMessage, {
188
+ variant: successful ? 'success' : 'loading',
189
+ title: successful ? t('launch.launched') : '',
190
+ subTitle: !successful ? t('launch.waitingForLaunching') : '',
191
+ footer: successful ? /*#__PURE__*/_jsxs(LinkEle, {
192
+ href: launchState.appUrl,
193
+ rel: "noreferrer",
194
+ children: [/*#__PURE__*/_jsx(Spinner, {
195
+ size: 16,
196
+ style: {
197
+ marginRight: 14
198
+ }
199
+ }), " ", t('common.redirecting')]
200
+ }) : ''
201
+ }), [STATUS.error, STATUS.launchFailedError].includes(launchState.status) && /*#__PURE__*/_jsx(LaunchResultMessage, {
202
+ variant: "error",
203
+ title: t(`${type}.error.launchFailed`),
204
+ subTitle: type === 'launch' ? t('launch.error.launchFailedDescription') : '',
205
+ footer: type === 'launch' && /*#__PURE__*/_jsx(Button, {
206
+ color: "primary",
207
+ variant: "contained",
208
+ className: "button",
209
+ onClick: () => navigate(0),
210
+ children: t('common.retry')
211
+ })
212
+ }), launchState.status === STATUS.notFoundError && /*#__PURE__*/_jsx(LaunchResultMessage, {
213
+ variant: "error",
214
+ title: t(`${type}.error.notFound`),
215
+ subTitle: type === 'launch' ? t('launch.error.notFoundDescription') : '',
216
+ footer: type === 'launch' && /*#__PURE__*/_jsx(Button, {
217
+ color: "primary",
218
+ variant: "contained",
219
+ className: "button",
220
+ onClick: launch,
221
+ children: t('common.launch')
222
+ })
223
+ })]
224
+ });
225
+ }
226
+ const LinkEle = styled.a`
227
+ display: flex;
228
+ justify-content: center;
229
+ align-items: center;
230
+ height: 36px;
231
+ color: ${props => props.theme.palette.primary.main};
232
+ `;
233
+ const WaiterContainer = styled.div`
234
+ color: #1dc1c7;
235
+ `;
236
+ const Content = styled.div`
237
+ margin: auto;
238
+ height: 100%;
239
+
240
+ .link {
241
+ color: ${props => props.theme.palette.primary.main};
242
+ }
243
+
244
+ .link:hover {
245
+ text-decoration: underline !important;
246
+ }
247
+
248
+ .status {
249
+ color: ${props => props.theme.palette.primary.main};
250
+ display: flex;
251
+ flex-direction: column;
252
+ align-items: center;
253
+
254
+ .status-spinner {
255
+ color: inherit !important;
256
+ }
257
+
258
+ .status-text {
259
+ display: inline-block;
260
+ padding: 0 20px;
261
+ max-width: 420px;
262
+ margin-top: 24px;
263
+ text-align: center;
264
+ }
265
+ }
266
+
267
+ .button {
268
+ min-width: 150px;
269
+ margin-bottom: 100px;
270
+ }
271
+ .center {
272
+ display: flex;
273
+ align-items: center;
274
+ justify-content: center;
275
+ width: 100%;
276
+ height: 100%;
277
+ }
278
+ .launch-page-header {
279
+ transition: all ease 0.3s;
280
+ &.header-hide {
281
+ opacity: 0;
282
+ }
283
+ }
284
+ `;
285
+ LaunchDedicated.propTypes = {
286
+ type: PropTypes.oneOf(['launch', 'start'])
287
+ };
288
+ LaunchDedicated.defaultProps = {
289
+ type: 'launch'
290
+ };
@@ -0,0 +1,113 @@
1
+ import styled from '@emotion/styled';
2
+ import PropTypes from 'prop-types';
3
+ import React from 'react';
4
+ import { useSearchParams } from 'react-router-dom';
5
+ import useAsync from 'react-use/lib/useAsync';
6
+ import PageHeader from '@blocklet/launcher-layout/lib/page-header';
7
+ import formatError from '@blocklet/launcher-util/es/format-error';
8
+ import LaunchResultMessage from '@blocklet/launcher-layout/lib/launch-result-message';
9
+ import Button from '@blocklet/launcher-ux/lib/button';
10
+ import { useLocaleContext } from '../contexts/locale';
11
+ import useRequest from '../contexts/request';
12
+ import { getLaunchBlockletUrl } from '../util';
13
+ import { jsx as _jsx } from "react/jsx-runtime";
14
+ import { jsxs as _jsxs } from "react/jsx-runtime";
15
+ export default function LaunchServerless({
16
+ nftId
17
+ }) {
18
+ const {
19
+ t,
20
+ locale
21
+ } = useLocaleContext();
22
+ const {
23
+ api
24
+ } = useRequest();
25
+ const [searchParams] = useSearchParams();
26
+ const sessionId = searchParams.get('sessionId');
27
+ const state = useAsync(async () => {
28
+ const {
29
+ data: {
30
+ instance
31
+ }
32
+ } = await api.post(`/serverless/${nftId}/allocate?sessionId=${sessionId}`);
33
+ setTimeout(() => {
34
+ window.location = getLaunchBlockletUrl({
35
+ serverUrl: instance.serverUrl,
36
+ blockletMetaUrl: searchParams.get('blocklet_meta_url'),
37
+ locale,
38
+ launchType: 'serverless',
39
+ nftId,
40
+ sessionId,
41
+ chainHost: window.blocklet?.CHAIN_HOST,
42
+ from: searchParams.get('from')
43
+ });
44
+ }, 1000);
45
+ return instance;
46
+ });
47
+ return /*#__PURE__*/_jsxs(Container, {
48
+ children: [/*#__PURE__*/_jsx(PageHeader, {
49
+ title: t('launch.pageTitle')
50
+ }), /*#__PURE__*/_jsx("div", {
51
+ className: "page-body",
52
+ children: /*#__PURE__*/_jsxs("div", {
53
+ className: "content",
54
+ children: [state.loading && /*#__PURE__*/_jsx(LaunchResultMessage, {
55
+ variant: "loading",
56
+ title: t('prepare.serverless.allocate'),
57
+ subTitle: t('prepare.serverless.preparing')
58
+ }), state.error && /*#__PURE__*/_jsx(LaunchResultMessage, {
59
+ variant: "error",
60
+ title: t('prepare.serverless.allocate'),
61
+ subTitle: t('prepare.serverless.prepareFailed', {
62
+ error: formatError(state.error)
63
+ }),
64
+ footer: /*#__PURE__*/_jsx(Button, {
65
+ size: "small",
66
+ style: {
67
+ marginTop: '8px'
68
+ },
69
+ onClick: () => window.location.reload(),
70
+ children: t('common.retry')
71
+ })
72
+ }), state.value && /*#__PURE__*/_jsx(LaunchResultMessage, {
73
+ variant: "success",
74
+ title: t('prepare.serverless.allocated'),
75
+ subTitle: t('prepare.serverless.prepared')
76
+ })]
77
+ })
78
+ })]
79
+ });
80
+ }
81
+ const Container = styled.div`
82
+ display: flex;
83
+ flex-direction: column;
84
+ width: 100%;
85
+ height: 100%;
86
+
87
+ .page-logo {
88
+ display: flex;
89
+ justify-content: center;
90
+ margin-top: 62px;
91
+ ${props => props.theme.breakpoints.down('md')} {
92
+ margin-top: 48px;
93
+ }
94
+ }
95
+
96
+ .page-body {
97
+ margin-top: 128px;
98
+
99
+ .content {
100
+ display: flex;
101
+ align-items: center;
102
+ flex-direction: column;
103
+ text-align: center;
104
+
105
+ .loading-description {
106
+ margin-top: 8px;
107
+ }
108
+ }
109
+ }
110
+ `;
111
+ LaunchServerless.propTypes = {
112
+ nftId: PropTypes.string.isRequired
113
+ };