@abtnode/ux 1.16.18-beta-adeeb0b3 → 1.16.18-beta-aa01bd8e

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.
@@ -0,0 +1,1317 @@
1
+ /* eslint-disable react/no-unstable-nested-components */
2
+ import { useState, useRef, useImperativeHandle, forwardRef, useEffect } from 'react';
3
+ import PropTypes from 'prop-types';
4
+ import { useParams } from 'react-router-dom';
5
+ import uniqBy from 'lodash/uniqBy';
6
+ import flatten from 'lodash/flatten';
7
+ import isEmpty from 'lodash/isEmpty';
8
+ import isNil from 'lodash/isNil';
9
+ import PageHeader from '@blocklet/launcher-layout/lib/page-header';
10
+ import Spinner from '@mui/material/CircularProgress';
11
+ import Typography from '@mui/material/Typography';
12
+ import TextField from '@mui/material/TextField';
13
+ import AddIcon from '@mui/icons-material/Link';
14
+ import CheckIcon from '@mui/icons-material/Check';
15
+ import useMediaQuery from '@mui/material/useMediaQuery';
16
+ import styled from '@emotion/styled';
17
+ import { useLocaleContext } from '@arcblock/ux/lib/Locale/context';
18
+ import { BLOCKLET_CONFIGURABLE_KEY, BlockletStatus, BlockletEvents } from '@blocklet/constant';
19
+ import Layout from '@blocklet/launcher-layout';
20
+ import { StepProvider, useStepContext } from '@blocklet/launcher-layout/lib/context/step';
21
+ import Box from '@mui/material/Box';
22
+ import { NODE_MODES } from '@abtnode/constant';
23
+ import Button from '@arcblock/ux/lib/Button';
24
+ import Alert from '@arcblock/ux/lib/Alert';
25
+ import AnimationWaiter from '@arcblock/ux/lib/AnimationWaiter';
26
+ import ResultMessage from '@blocklet/launcher-layout/lib/launch-result-message';
27
+ import { getDisplayName, isFreeBlocklet, getSharedConfigObj, hasStartEngine } from '@blocklet/meta/lib/util';
28
+ import urlPathFriendly from '@blocklet/meta/lib/url-path-friendly';
29
+ import Toast from '@arcblock/ux/lib/Toast';
30
+ import { useNodeContext } from '../../contexts/node';
31
+ import { useBlockletContext } from '../../contexts/blocklet';
32
+ import { getBlockletLogoUrl, getBlockletMetaUrl, isNewStoreUrl, getStoreList, formatError, formatMountPoint } from '../../util';
33
+ import SchemaForm from '../../schema-form';
34
+ import Required from '../../form/required';
35
+ import { ComponentPurchaseSelect } from '../purchase';
36
+ import { validatePathPrefix } from '../router/util';
37
+ import Agreement from '../agreement';
38
+ import InstallFromUrl from '../install-from-url';
39
+ import SelectStore from './select-store';
40
+ import BlockletList from './store-blocklet-list';
41
+ import SpaceConnector from './space-connector';
42
+ import { jsx as _jsx } from "react/jsx-runtime";
43
+ import { jsxs as _jsxs } from "react/jsx-runtime";
44
+ import { Fragment as _Fragment } from "react/jsx-runtime";
45
+ const requirePurchase = meta => meta.inStore && isFreeBlocklet(meta) === false;
46
+ const getDIDSpaceCapability = meta => {
47
+ return meta?.capabilities?.didSpace;
48
+ };
49
+ /**
50
+ * @description
51
+ * @param {import('@abtnode/client').BlockletState} blocklet
52
+ * @return {string}
53
+ */
54
+ const getDIDSpaceEndpoint = blocklet => {
55
+ return blocklet?.configs?.find(item => item.key === BLOCKLET_CONFIGURABLE_KEY.BLOCKLET_APP_SPACE_ENDPOINT)?.value || null;
56
+ };
57
+ const hasRequiredEnvironments = meta => (meta.environments || []).some(x => x.required);
58
+ const hasMissRequiredConfigs = (configsList, sharedValueMap) => {
59
+ return configsList?.some(item => {
60
+ return item.required && !sharedValueMap[item.key];
61
+ });
62
+ };
63
+ const getConfirmText = ({
64
+ params,
65
+ blocklet,
66
+ t
67
+ }) => {
68
+ const name = params.title || params.name;
69
+ if (!name) {
70
+ return t('common.next');
71
+ }
72
+ const exist = (blocklet?.children || []).find(x => x.meta.bundleDid === params.bundleDid);
73
+ if (!exist) {
74
+ return t('blocklet.component.addNext', {
75
+ name
76
+ });
77
+ }
78
+ return t('blocklet.component.upgradeNext', {
79
+ name
80
+ });
81
+ };
82
+ const StepContent = /*#__PURE__*/forwardRef(({
83
+ meta,
84
+ isMobile,
85
+ onStepChange
86
+ }, ref) => {
87
+ const {
88
+ steps,
89
+ activeStep,
90
+ setActiveStepByKey,
91
+ setActiveStepByIndex
92
+ } = useStepContext();
93
+ const {
94
+ t,
95
+ locale
96
+ } = useLocaleContext();
97
+ const step = steps[activeStep];
98
+ useEffect(() => {
99
+ onStepChange(activeStep);
100
+ }, [onStepChange, activeStep]);
101
+
102
+ // expose the func and activeStep
103
+ useImperativeHandle(ref, () => ({
104
+ setActiveStepByKey,
105
+ setActiveStepByIndex
106
+ }));
107
+ return /*#__PURE__*/_jsx(ContentWrapper, {
108
+ component: "div",
109
+ className: isMobile ? 'mobileStyle' : '',
110
+ sx: {
111
+ height: '100%'
112
+ },
113
+ children: /*#__PURE__*/_jsx(Layout, {
114
+ locale: locale,
115
+ blockletMeta: {
116
+ title: ' ',
117
+ ...meta
118
+ },
119
+ pcWidth: "100%",
120
+ pcHeight: "100%",
121
+ logoUrl: meta && meta.logo && meta.registryUrl ? getBlockletLogoUrl({
122
+ did: meta.did,
123
+ baseUrl: meta.registryUrl,
124
+ logoPath: meta.logo
125
+ }) : null,
126
+ stepTip: t('blocklet.component.add'),
127
+ children: /*#__PURE__*/_jsx(RightContent, {
128
+ children: step?.body && typeof step.body === 'function' ? step.body() : step.body
129
+ })
130
+ })
131
+ });
132
+ });
133
+ StepContent.propTypes = {
134
+ meta: PropTypes.any,
135
+ onStepChange: PropTypes.func.isRequired,
136
+ isMobile: PropTypes.bool.isRequired
137
+ };
138
+ StepContent.defaultProps = {
139
+ meta: null
140
+ };
141
+ const postInstalledMessage = componentDid => {
142
+ // send event to parent window
143
+ window.parent.postMessage({
144
+ event: 'component.installed',
145
+ componentDid
146
+ }, '*');
147
+ };
148
+ export default function AddComponentCore({
149
+ onClose,
150
+ mode,
151
+ stores,
152
+ resourceType,
153
+ storageKey
154
+ }) {
155
+ const {
156
+ t,
157
+ locale
158
+ } = useLocaleContext();
159
+ const [loading, setLoading] = useState(false);
160
+ const [editingItem, setEditingItem] = useState(null);
161
+ const [error, setError] = useState('');
162
+ const [mountPointHelperText, setMountPointHelperText] = useState('');
163
+ const {
164
+ api,
165
+ ws: {
166
+ useSubscription
167
+ },
168
+ inService,
169
+ info: nodeInfo
170
+ } = useNodeContext();
171
+ const {
172
+ blocklet,
173
+ actions: {
174
+ refreshBlocklet: refreshApp
175
+ }
176
+ } = useBlockletContext();
177
+ const [purchaseData, setPurchaseData] = useState(null);
178
+ const [isWaitingPurchase, setIsWaitingPurchase] = useState(false);
179
+ const initParams = {
180
+ bundleDid: '',
181
+ componentDid: '',
182
+ componentName: '',
183
+ pathPrefix: '',
184
+ name: '',
185
+ title: '',
186
+ configsList: [],
187
+ configsValue: {},
188
+ hasMissRequiredConfigs: false,
189
+ hasEnvironmentsStep: false,
190
+ hasRequiredEnvironments: false,
191
+ requirePurchase: false,
192
+ didSpaceCapability: null,
193
+ installResultProps: {},
194
+ purchaseResultProps: {},
195
+ showFromUrlDialog: false
196
+ };
197
+ const [params, setParams] = useState(initParams);
198
+ const [activeStep, setActiveStep] = useState(0);
199
+ const component = useRef({});
200
+ const stepRef = useRef({});
201
+ const purchaseRef = useRef({});
202
+ const isMobile = useMediaQuery(theme => theme.breakpoints.down('md'));
203
+ const routerParams = useParams();
204
+ const {
205
+ meta
206
+ } = component.current;
207
+ // eslint-disable-next-line no-unused-vars
208
+ const {
209
+ setActiveStepByIndex
210
+ } = stepRef.current || {};
211
+ const isServerlessMode = nodeInfo.mode === NODE_MODES.SERVERLESS;
212
+ const updateParams = obj => setParams(x => ({
213
+ ...x,
214
+ ...obj
215
+ }));
216
+ useEffect(() => {
217
+ if (params.pathPrefix) {
218
+ const {
219
+ errMsg
220
+ } = validateInput({
221
+ ...params,
222
+ pathPrefix: params.pathPrefix,
223
+ locale
224
+ });
225
+ setError(errMsg);
226
+ }
227
+ }, [params.pathPrefix]); // eslint-disable-line
228
+
229
+ useEffect(() => {
230
+ if (activeStep === 0) {
231
+ // reset params, when step change to select component
232
+ setParams(initParams);
233
+ }
234
+ }, [activeStep]); // eslint-disable-line
235
+
236
+ const getComponentName = () => {
237
+ if (component?.current?.meta) {
238
+ return getDisplayName(component.current);
239
+ }
240
+ return '';
241
+ };
242
+ const setInstallErrorResult = errorMessage => {
243
+ const update = _loading => {
244
+ updateParams({
245
+ installResultProps: {
246
+ variant: 'error',
247
+ title: getComponentName(),
248
+ subTitle: errorMessage,
249
+ footer: /*#__PURE__*/_jsx(Button, {
250
+ className: "bottom-button",
251
+ disabled: _loading,
252
+ "data-cy": "retry-install-component",
253
+ onClick: async () => {
254
+ // set button disabled and show spinner
255
+ update(true);
256
+ await onInstall(component.current.installInput);
257
+ },
258
+ children: /*#__PURE__*/_jsxs(Box, {
259
+ display: "flex",
260
+ alignItems: "center",
261
+ children: [_loading && /*#__PURE__*/_jsx(Spinner, {
262
+ size: 16,
263
+ style: {
264
+ marginRight: 8
265
+ }
266
+ }), t('common.retry')]
267
+ })
268
+ })
269
+ }
270
+ });
271
+ };
272
+ update();
273
+ };
274
+ const createBlockletEventHandler = handler => {
275
+ return b => {
276
+ if ((b?.meta?.did || b?.meta) === blocklet?.meta?.did) {
277
+ handler(b);
278
+ }
279
+ };
280
+ };
281
+ useSubscription(BlockletEvents.statusChange, createBlockletEventHandler(e => {
282
+ switch (e.status) {
283
+ case BlockletStatus.upgrading:
284
+ updateParams({
285
+ installResultProps: {
286
+ variant: 'success',
287
+ title: getComponentName(),
288
+ subTitle: t('blocklet.component.installSuccessTip')
289
+ }
290
+ });
291
+ postInstalledMessage(routerParams.componentDid);
292
+ break;
293
+ default:
294
+ }
295
+ }), [blocklet?.meta?.did]);
296
+ useSubscription(BlockletEvents.downloadFailed, createBlockletEventHandler(() => {
297
+ setInstallErrorResult(t('blocklet.component.installComponentError'));
298
+ }), [blocklet?.meta?.did]);
299
+ useSubscription(BlockletEvents.installFailed, createBlockletEventHandler(() => {
300
+ setInstallErrorResult(t('blocklet.component.installComponentError'));
301
+ }), [blocklet?.meta?.did]);
302
+ const onOver = () => {
303
+ setLoading(false);
304
+ setParams({});
305
+ component.current = {};
306
+ onClose();
307
+ };
308
+ const onInstall = async payload => {
309
+ const {
310
+ pathPrefix: mountPoint,
311
+ bundleDid,
312
+ title,
313
+ componentName = '',
314
+ componentDid = ''
315
+ } = params;
316
+ // eslint-disable-next-line no-shadow
317
+ const {
318
+ configs = [],
319
+ registryUrl,
320
+ inStore,
321
+ inputUrl
322
+ } = component.current?.meta || {};
323
+ const downloadTokenList = component.current?.downloadTokenList;
324
+ const url = inStore ? getBlockletMetaUrl(registryUrl, bundleDid) : inputUrl;
325
+ setLoading(true);
326
+ const installInput = {
327
+ rootDid: blocklet.meta.did,
328
+ url,
329
+ mountPoint: urlPathFriendly(mountPoint),
330
+ title,
331
+ name: componentName,
332
+ did: componentDid,
333
+ // if blocklet has config, provide the configs
334
+ ...(params.hasEnvironmentsStep && configs.length > 0 ? {
335
+ configs: configs.map(item => {
336
+ return {
337
+ ...item,
338
+ value: params.configsValue[item.key] || ''
339
+ };
340
+ })
341
+ } : {}),
342
+ ...(downloadTokenList ? {
343
+ downloadTokenList
344
+ } : {}),
345
+ ...payload
346
+ };
347
+ try {
348
+ await api.installComponent({
349
+ input: installInput
350
+ });
351
+ if (registryUrl) {
352
+ const {
353
+ teamDid,
354
+ storeList
355
+ } = getStoreList({
356
+ fromBlocklet: inService,
357
+ nodeInfo,
358
+ blocklet
359
+ });
360
+ const {
361
+ isNew,
362
+ decoded
363
+ } = isNewStoreUrl(registryUrl, storeList);
364
+ if (isNew) {
365
+ await api.addBlockletStore({
366
+ input: {
367
+ teamDid,
368
+ url: decoded
369
+ }
370
+ });
371
+ }
372
+ }
373
+ updateParams({
374
+ installResultProps: {}
375
+ });
376
+
377
+ // let app state refresh fast before receive subscribe event
378
+ refreshApp({
379
+ showError: false,
380
+ attachRuntimeInfo: false
381
+ });
382
+ } catch (err) {
383
+ const errMsg = formatError(err);
384
+ Toast.error(errMsg);
385
+ console.error('installComponent error: ', err);
386
+ setInstallErrorResult(errMsg);
387
+ } finally {
388
+ setLoading(false);
389
+ component.current.installInput = installInput;
390
+ }
391
+ };
392
+ const validateInput = input => {
393
+ // FIXME: validatePathPrefix should be updated
394
+ const errMsg = validatePathPrefix({
395
+ params: {
396
+ ...input,
397
+ did: input.bundleDid
398
+ },
399
+ blocklets: [blocklet],
400
+ // blocklets: component.current?.blocklets?.map(x => ({ meta: x })) || [],
401
+ blocklet,
402
+ locale
403
+ });
404
+ return {
405
+ errMsg
406
+ };
407
+ };
408
+ const onNext = async payload => {
409
+ const {
410
+ errMsg
411
+ } = validateInput({
412
+ ...params,
413
+ locale
414
+ });
415
+ setError(errMsg);
416
+
417
+ // the step can complete, install component
418
+ if (activeStep === steps.length - 2) {
419
+ await onInstall(payload);
420
+ }
421
+ setActiveStepByIndex(x => x + 1);
422
+ };
423
+ const onCancel = () => {
424
+ setActiveStepByIndex(x => x - 1);
425
+ };
426
+ const onGeneratePurchaseData = () => {
427
+ const {
428
+ pathPrefix: mountPoint,
429
+ bundleDid,
430
+ title,
431
+ componentName = '',
432
+ componentDid = ''
433
+ } = params;
434
+
435
+ // eslint-disable-next-line no-shadow
436
+ const {
437
+ meta = {}
438
+ } = component.current || {};
439
+ const {
440
+ registryUrl,
441
+ inStore,
442
+ inputUrl
443
+ } = meta;
444
+ const url = inStore ? getBlockletMetaUrl(registryUrl, bundleDid) : inputUrl;
445
+ setPurchaseData({
446
+ meta,
447
+ installOpts: {
448
+ type: 'component',
449
+ rootDid: blocklet.meta.did,
450
+ mountPoint,
451
+ url,
452
+ title,
453
+ name: componentName,
454
+ did: componentDid
455
+ }
456
+ });
457
+ };
458
+ const onCancelPurchase = errorMessage => {
459
+ setIsWaitingPurchase(false);
460
+ updateParams({
461
+ purchaseResultProps: {
462
+ variant: 'error',
463
+ title: getComponentName(),
464
+ subTitle: errorMessage,
465
+ style: {
466
+ paddingTop: 120
467
+ },
468
+ footer: /*#__PURE__*/_jsx(Button, {
469
+ className: "bottom-button",
470
+ "data-cy": "retry-purchase-component",
471
+ onClick: () => {
472
+ updateParams({
473
+ purchaseResultProps: {}
474
+ });
475
+ },
476
+ children: t('common.retry')
477
+ })
478
+ }
479
+ });
480
+ };
481
+ const onSuccessPurchase = ({
482
+ downloadTokenList
483
+ }) => {
484
+ component.current.downloadTokenList = downloadTokenList;
485
+ setIsWaitingPurchase(false);
486
+ onNext();
487
+ };
488
+ const setConfigValue = ({
489
+ chooseParams = component.current,
490
+ componentDid: componentDidValue,
491
+ isInit = false
492
+ }) => {
493
+ // eslint-disable-next-line no-shadow
494
+ const {
495
+ meta,
496
+ registryUrl,
497
+ inStore,
498
+ inputUrl
499
+ } = chooseParams;
500
+ const {
501
+ did: bundleDid,
502
+ title,
503
+ name
504
+ } = meta;
505
+
506
+ // deleted history list
507
+ const list = (blocklet.settings?.children || []).filter(x => x.status === 'deleted' && x.meta.bundleDid === bundleDid);
508
+ const componentDid = componentDidValue || '';
509
+ const newConfigs = meta.environments?.map(item => {
510
+ const {
511
+ name: key,
512
+ validation,
513
+ ...rest
514
+ } = item;
515
+ const formatItem = {
516
+ // if the blocklet has validation, provide the validation (fix not inStore)
517
+ validation: isNil(validation) ? '' : validation
518
+ };
519
+ return {
520
+ ...rest,
521
+ key,
522
+ ...formatItem
523
+ };
524
+ });
525
+ component.current = {
526
+ ...chooseParams,
527
+ meta: {
528
+ ...meta,
529
+ // init configs from environments
530
+ configs: newConfigs,
531
+ registryUrl,
532
+ inStore,
533
+ inputUrl
534
+ }
535
+ };
536
+ let doc = {};
537
+
538
+ // if is init, should update configs
539
+ if (isInit) {
540
+ const ancestors = [blocklet];
541
+
542
+ // component config
543
+ const componentSelfConfigs = newConfigs || [];
544
+
545
+ // TODO: meta not include children environments/configs before install, waiting for next sprint to design
546
+ const componentChildrenConfigs = [];
547
+
548
+ // eslint-disable-next-line
549
+ // chooseBlocklet?.children?.map(childBlocklet => {
550
+ // // eslint-disable-next-line no-shadow
551
+ // forEachChildSync(childBlocklet, (b, { ancestors }) => {
552
+ // const ancestorDids = ancestors.slice(1).map(x => x.meta.did);
553
+
554
+ // componentChildrenConfigs.push(
555
+ // (b.configs || []).map(x => ({ ...x, childDid: ancestorDids.concat(b.meta.did) }))
556
+ // );
557
+ // });
558
+
559
+ // return false;
560
+ // });
561
+
562
+ const componentAllConfigs = uniqBy(flatten([...componentSelfConfigs, ...componentChildrenConfigs]), 'key').filter(x => !!x.key).sort((a, b) => {
563
+ if (a.required && !b.required) {
564
+ return -1;
565
+ }
566
+ if (b.required && !a.required) {
567
+ return 1;
568
+ }
569
+ return 0;
570
+ });
571
+ const sharedConfigObj = getSharedConfigObj(ancestors[0], {
572
+ configs: componentAllConfigs
573
+ });
574
+ const configsValue = {};
575
+ const configsList = componentAllConfigs.map(item => {
576
+ const {
577
+ default: defaultValue,
578
+ key,
579
+ ...rest
580
+ } = item;
581
+ configsValue[key] = sharedConfigObj[key] || defaultValue;
582
+ return {
583
+ ...rest,
584
+ key,
585
+ hidden: !!BLOCKLET_CONFIGURABLE_KEY[key]
586
+ };
587
+ });
588
+ doc = {
589
+ ...doc,
590
+ configsList,
591
+ configsValue,
592
+ hasMissRequiredConfigs: hasMissRequiredConfigs(configsList, configsValue)
593
+ };
594
+ }
595
+ const hasRootPath = blocklet.children.some(x => x.mountPoint === '/');
596
+
597
+ // use deleted history
598
+ const config = list.find(x => x.meta.bundleDid === bundleDid);
599
+ if (config) {
600
+ doc = {
601
+ ...doc,
602
+ pathPrefix: config.mountPoint || '',
603
+ title: title || '',
604
+ componentName: config.meta.name,
605
+ // use history's environments
606
+ hasEnvironmentsStep: false
607
+ };
608
+ } else {
609
+ doc = {
610
+ ...doc,
611
+ pathPrefix: hasRootPath ? `/${urlPathFriendly(title) || urlPathFriendly(name)}` : '/',
612
+ title: title || '',
613
+ componentName: '',
614
+ hasEnvironmentsStep: component.current.meta?.environments?.length > 0
615
+ };
616
+ }
617
+
618
+ // eslint-disable-next-line no-shadow
619
+ const componentRequirePurchase = requirePurchase(component.current.meta);
620
+ const componentDIDSpaceCapability = getDIDSpaceCapability(component.current.meta);
621
+ const didSpaceEndpoint = getDIDSpaceEndpoint(blocklet);
622
+ updateParams({
623
+ bundleDid,
624
+ componentDid,
625
+ ...doc,
626
+ hasRequiredEnvironments: hasRequiredEnvironments(component.current.meta),
627
+ requirePurchase: componentRequirePurchase,
628
+ didSpaceCapability: componentDIDSpaceCapability,
629
+ didSpaceEndpoint
630
+ });
631
+ if (isInit && componentRequirePurchase) {
632
+ onGeneratePurchaseData();
633
+ }
634
+ };
635
+ const steps = [{
636
+ key: 'selectComponent',
637
+ name: t('blocklet.component.selectComponent'),
638
+ body: /*#__PURE__*/_jsxs(TypographyWrapper, {
639
+ component: "div",
640
+ children: [params.showFromUrlDialog && /*#__PURE__*/_jsx(InstallFromUrl
641
+ // if not set defaultUrl, the dialog will always show step 1 when the dialog is opened
642
+ // defaultUrl={meta?.inputUrl}
643
+ , {
644
+ mode: "component",
645
+ onCancel: () => {
646
+ updateParams({
647
+ showFromUrlDialog: false
648
+ });
649
+ }
650
+ // eslint-disable-next-line no-shadow
651
+ ,
652
+ onSuccess: ({
653
+ meta,
654
+ inputUrl,
655
+ inStore,
656
+ registryUrl
657
+ }) => {
658
+ setConfigValue({
659
+ chooseParams: {
660
+ meta,
661
+ inputUrl,
662
+ inStore,
663
+ registryUrl
664
+ },
665
+ isInit: true
666
+ });
667
+ updateParams({
668
+ showFromUrlDialog: false
669
+ });
670
+ onNext();
671
+ },
672
+ handleText: {
673
+ title: t('blocklet.component.addComponentTip.fromUrl'),
674
+ confirm: t('blocklet.component.choose')
675
+ }
676
+ }), /*#__PURE__*/_jsx(SelectStore, {
677
+ stores: stores,
678
+ extra: !isServerlessMode ? /*#__PURE__*/_jsxs(Button, {
679
+ variant: "text",
680
+ color: "primary",
681
+ "data-cy": "add-component-from-url",
682
+ onClick: () => {
683
+ updateParams({
684
+ showFromUrlDialog: true
685
+ });
686
+ },
687
+ style: {
688
+ padding: '0 2px'
689
+ },
690
+ children: [/*#__PURE__*/_jsx(AddIcon, {
691
+ style: {
692
+ fontSize: 20,
693
+ marginRight: 4
694
+ }
695
+ }), " ", t('blocklet.component.addComponentTip.fromUrl'), ' ']
696
+ }) : null,
697
+ loading: loading,
698
+ onChange: () => {
699
+ setParams({});
700
+ component.current = {};
701
+ },
702
+ storageKey: storageKey,
703
+ children: ({
704
+ currentRegistry
705
+ }) => {
706
+ return /*#__PURE__*/_jsx(BlockletList, {
707
+ serverVersion: nodeInfo?.version,
708
+ style: isMobile ? {
709
+ // should remove other dom height/margin
710
+ height: 'calc(100vh - 56px - 80px - 32px - 40px )'
711
+ } : {
712
+ height: 'calc(100% - 40px)'
713
+ },
714
+ storeUrl: currentRegistry.url,
715
+ resourceType: resourceType,
716
+ handleButtonClick: chooseParams => {
717
+ setConfigValue({
718
+ chooseParams: {
719
+ ...chooseParams,
720
+ inStore: true // choose from store, must be inStore
721
+ },
722
+
723
+ isInit: true
724
+ });
725
+ },
726
+ handleBlockletRender: ({
727
+ blocklet: blockletItem,
728
+ defaultRender
729
+ }) => {
730
+ const isChosen = params?.bundleDid && params?.bundleDid === blockletItem?.did;
731
+ return /*#__PURE__*/_jsxs(StoreBlockletItemWrapper, {
732
+ children: [isChosen && /*#__PURE__*/_jsx("div", {
733
+ className: "check-container",
734
+ children: /*#__PURE__*/_jsx(CheckIcon, {
735
+ className: "check-icon"
736
+ })
737
+ }), /*#__PURE__*/_jsx("div", {
738
+ className: isChosen ? 'choose-blocklet' : '',
739
+ style: {
740
+ marginLeft: 16
741
+ },
742
+ children: defaultRender
743
+ })]
744
+ });
745
+ }
746
+ });
747
+ }
748
+ })]
749
+ }),
750
+ cancel: mode === 'embed' ? undefined : t('common.cancel'),
751
+ confirm: getConfirmText({
752
+ params,
753
+ blocklet,
754
+ t
755
+ }),
756
+ onCancel: onOver,
757
+ onConfirm: () => {
758
+ onNext();
759
+ }
760
+ }, {
761
+ key: 'agreement',
762
+ name: t('launchBlocklet.introduction'),
763
+ body: /*#__PURE__*/_jsxs(TypographyWrapper, {
764
+ component: "div",
765
+ className: "agreement-wrapper",
766
+ children: [/*#__PURE__*/_jsx(PageHeader, {
767
+ title: t('launchBlocklet.introduction'),
768
+ subTitle: t('blocklet.component.addComponentTip.introduction')
769
+ }), /*#__PURE__*/_jsx(Agreement, {
770
+ meta: meta,
771
+ onClickNext: onNext,
772
+ handleDescEle: /*#__PURE__*/_jsxs(_Fragment, {
773
+ children: [/*#__PURE__*/_jsx("p", {
774
+ children: /*#__PURE__*/_jsx("div", {
775
+ // eslint-disable-next-line
776
+ dangerouslySetInnerHTML: {
777
+ __html: t(`blocklet.component.addComponentTip.${params.requirePurchase ? 'isPurchase' : 'isFree'}`, {
778
+ name: getComponentName()
779
+ })
780
+ }
781
+ })
782
+ }), /*#__PURE__*/_jsx("p", {
783
+ children: t('blocklet.component.addComponentTip.belowInformation')
784
+ })]
785
+ })
786
+ })]
787
+ }),
788
+ cancel: t('common.pre'),
789
+ confirm: t('launchBlocklet.next'),
790
+ onCancel,
791
+ onConfirm: () => {
792
+ setIsWaitingPurchase(false);
793
+ onNext();
794
+ }
795
+ }, meta && params.requirePurchase && {
796
+ key: 'purchase',
797
+ name: /*#__PURE__*/_jsxs(_Fragment, {
798
+ children: [t('common.verifyNFT'), /*#__PURE__*/_jsx(Required, {})]
799
+ }),
800
+ body: /*#__PURE__*/_jsxs(TypographyWrapper, {
801
+ component: "div",
802
+ className: "flex-align-center",
803
+ style: {
804
+ flexDirection: 'column'
805
+ },
806
+ children: [!(purchaseRef.current?.getCurrentStep?.() === 2 || isWaitingPurchase) && /*#__PURE__*/_jsx(PageHeaderWrapper, {
807
+ title: t('common.verifyNFT'),
808
+ subTitle: t('blocklet.component.addComponentTip.verifyNFT')
809
+ }), !isEmpty(params.purchaseResultProps) ? /*#__PURE__*/_jsx("div", {
810
+ className: "flex-align-center flex-justify-center",
811
+ children: /*#__PURE__*/_jsx(ResultMessage, {
812
+ ...params.purchaseResultProps
813
+ })
814
+ }) : purchaseData && /*#__PURE__*/_jsx(ComponentPurchaseSelect, {
815
+ ref: purchaseRef,
816
+ meta: purchaseData.meta
817
+ // if select deleted history, use verify mode
818
+ ,
819
+ mode: params.componentDid ? 'verify' : 'both',
820
+ onCancel: onCancelPurchase,
821
+ installOpts: purchaseData.installOpts,
822
+ handlePaySuccess: onSuccessPurchase
823
+ })]
824
+ }),
825
+ disabled: purchaseRef.current?.getCurrentStep?.() === 2 || isWaitingPurchase,
826
+ cancel: t('common.pre'),
827
+ confirm: t('common.next'),
828
+ onConfirm: () => {
829
+ if (typeof purchaseRef.current.onNext === 'function') {
830
+ purchaseRef.current.onNext();
831
+ setIsWaitingPurchase(true);
832
+ }
833
+ },
834
+ onCancel: () => {
835
+ onCancel();
836
+ }
837
+ }, hasStartEngine(meta) && {
838
+ key: 'config',
839
+ name: t('common.config'),
840
+ error,
841
+ body: /*#__PURE__*/_jsxs(TypographyWrapper, {
842
+ component: "div",
843
+ children: [/*#__PURE__*/_jsx(PageHeaderWrapper, {
844
+ title: t('common.config'),
845
+ subTitle: t('blocklet.component.addComponentTip.config')
846
+ }), /*#__PURE__*/_jsx(TextField, {
847
+ label: t('blocklet.component.mountPoint'),
848
+ autoComplete: "off",
849
+ variant: "outlined",
850
+ name: "pathPrefix",
851
+ inputProps: {
852
+ 'data-cy': 'mount-point-input'
853
+ },
854
+ fullWidth: true,
855
+ helperText: mountPointHelperText || t('blocklet.component.mountPointTip'),
856
+ style: {
857
+ marginBottom: 32
858
+ },
859
+ margin: "normal",
860
+ value: params.pathPrefix,
861
+ onChange: e => {
862
+ const pathPrefix = e.target.value;
863
+ updateParams({
864
+ pathPrefix
865
+ });
866
+ setMountPointHelperText(t('common.slugifyHint', {
867
+ value: formatMountPoint(pathPrefix)
868
+ }));
869
+ }
870
+ }), !!error && /*#__PURE__*/_jsx(Alert, {
871
+ type: "error",
872
+ style: {
873
+ width: '100%',
874
+ marginTop: 8
875
+ },
876
+ children: error
877
+ })]
878
+ }),
879
+ cancel: t('common.pre'),
880
+ confirm: t('common.next'),
881
+ onCancel,
882
+ onConfirm: () => {
883
+ onNext();
884
+ }
885
+ }, meta && params?.hasEnvironmentsStep && {
886
+ key: 'environment',
887
+ name: /*#__PURE__*/_jsxs(_Fragment, {
888
+ children: [t('common.environment'), " ", params.hasRequiredEnvironments && /*#__PURE__*/_jsx(Required, {})]
889
+ }),
890
+ disabled: !!editingItem || params?.hasMissRequiredConfigs,
891
+ body: () => {
892
+ return /*#__PURE__*/_jsxs(TypographyWrapper, {
893
+ component: "div",
894
+ children: [/*#__PURE__*/_jsx(PageHeaderWrapper, {
895
+ title: t('common.environment'),
896
+ subTitle: t('blocklet.component.addComponentTip.environment')
897
+ }), /*#__PURE__*/_jsx(SchemaForm, {
898
+ style: {
899
+ marginTop: -12,
900
+ width: '100%'
901
+ },
902
+ schema: params.configsList,
903
+ defaultValue: params.configsValue
904
+ // eslint-disable-next-line
905
+ ,
906
+ onChange: (changeValue, {
907
+ action,
908
+ currentItem,
909
+ allValues
910
+ }) => {
911
+ if (action === 'confirm') {
912
+ updateParams({
913
+ hasMissRequiredConfigs: hasMissRequiredConfigs(params.configsList, allValues),
914
+ configsValue: allValues
915
+ });
916
+ }
917
+ if (action === 'edit') {
918
+ setEditingItem(currentItem);
919
+ } else if (['cancel', 'confirm'].includes(action)) {
920
+ setEditingItem(null);
921
+ }
922
+ }
923
+ })]
924
+ });
925
+ },
926
+ cancel: t('common.pre'),
927
+ confirm: t('common.next'),
928
+ onCancel,
929
+ onConfirm: onNext
930
+ }, meta && params?.didSpaceCapability && {
931
+ key: 'didSpace',
932
+ name: /*#__PURE__*/_jsxs(_Fragment, {
933
+ children: [t('blocklet.component.didSpaceConfig'), " ", params.didSpaceCapability === 'required' && /*#__PURE__*/_jsx(Required, {})]
934
+ }),
935
+ disabled: params.didSpaceCapability === 'required' && !params.didSpaceEndpoint,
936
+ body: () => {
937
+ return /*#__PURE__*/_jsxs(TypographyWrapper, {
938
+ component: "div",
939
+ children: [/*#__PURE__*/_jsx(PageHeaderWrapper, {
940
+ title: t('blocklet.component.didSpaceConfig'),
941
+ subTitle: t('blocklet.component.didSpaceConfigTip')
942
+ }), /*#__PURE__*/_jsx(Box, {
943
+ sx: {
944
+ mt: '36px',
945
+ '& > div': {
946
+ maxWidth: 'unset !important',
947
+ '& .MuiAutocomplete-root': {
948
+ maxWidth: 'unset !important'
949
+ }
950
+ }
951
+ },
952
+ children: /*#__PURE__*/_jsx(SpaceConnector, {
953
+ sx: {
954
+ '& .MuiFormHelperText-root': {
955
+ wordBreak: 'break-all'
956
+ }
957
+ },
958
+ helperText: params.didSpaceEndpoint && t('blocklet.component.didSpaceConnected', {
959
+ endpoint: params.didSpaceEndpoint
960
+ }),
961
+ onConnect: () => {
962
+ onNext();
963
+ }
964
+ })
965
+ })]
966
+ });
967
+ },
968
+ cancel: t('common.pre'),
969
+ confirm: t('common.next'),
970
+ onCancel,
971
+ onConfirm: onNext
972
+ }, {
973
+ key: 'install',
974
+ name: t('common.install'),
975
+ disabled: false,
976
+ loading: false,
977
+ body: /*#__PURE__*/_jsx(TypographyWrapper, {
978
+ component: "div",
979
+ className: "flex-justify-center flex-align-center",
980
+ style: {
981
+ flexDirection: 'column'
982
+ },
983
+ children: isEmpty(params.installResultProps) ? /*#__PURE__*/_jsx(AnimationWaiter, {
984
+ message: /*#__PURE__*/_jsx(MessageDiv, {
985
+ children: t('blocklet.component.installingCanCloseWindowTip')
986
+ }),
987
+ increaseSpeed: 0.3,
988
+ messageLoop: false
989
+ }) : /*#__PURE__*/_jsx("div", {
990
+ style: {
991
+ marginTop: 120
992
+ },
993
+ children: /*#__PURE__*/_jsx(ResultMessage, {
994
+ ...params.installResultProps
995
+ })
996
+ })
997
+ }),
998
+ confirm: t('common.complete'),
999
+ onConfirm: () => {
1000
+ postInstalledMessage(routerParams.componentDid);
1001
+ onOver();
1002
+ // scroll to bottom
1003
+ setTimeout(() => {
1004
+ const scrollDom = document.getElementsByClassName('dashboard-main')[0];
1005
+ if (scrollDom) {
1006
+ scrollDom.scrollTop = scrollDom.scrollHeight;
1007
+ }
1008
+ }, 200);
1009
+ }
1010
+ }].filter(item => item);
1011
+ const step = steps[activeStep] || {};
1012
+ const isDisabled = () => {
1013
+ if (loading || step.error || !params.bundleDid) {
1014
+ return true;
1015
+ }
1016
+
1017
+ // after select component and confirm license
1018
+ if (activeStep > 1) {
1019
+ return !params.pathPrefix;
1020
+ }
1021
+ return false;
1022
+ };
1023
+ if (!blocklet) {
1024
+ return null;
1025
+ }
1026
+ return /*#__PURE__*/_jsxs(Wrapper, {
1027
+ sx: isMobile ? {
1028
+ width: '100%',
1029
+ height: window.innerHeight
1030
+ } : {
1031
+ width: '100%',
1032
+ height: mode === 'embed' ? '100vh' : '72vh'
1033
+ },
1034
+ children: [/*#__PURE__*/_jsx(StepProvider, {
1035
+ steps: steps,
1036
+ mode: "memory",
1037
+ children: /*#__PURE__*/_jsx(StepContent, {
1038
+ ref: stepRef,
1039
+ meta: meta,
1040
+ isMobile: isMobile,
1041
+ onStepChange: newStep => {
1042
+ setActiveStep(newStep);
1043
+ }
1044
+ })
1045
+ }), /*#__PURE__*/_jsxs(Box, {
1046
+ className: "action-bar",
1047
+ children: [step.cancel && /*#__PURE__*/_jsx(Button, {
1048
+ onClick: e => {
1049
+ e.stopPropagation();
1050
+ step.onCancel();
1051
+ },
1052
+ disabled: activeStep === steps.length - 2 && loading,
1053
+ color: "inherit",
1054
+ children: step.cancel || t('common.cancel')
1055
+ }), step.confirm && /*#__PURE__*/_jsx(Button, {
1056
+ onClick: e => {
1057
+ e.stopPropagation();
1058
+ step.onConfirm();
1059
+ },
1060
+ color: "primary",
1061
+ "data-cy": "submit-confirm-next",
1062
+ disabled: loading || (typeof step.disabled === 'boolean' ? step.disabled : isDisabled()),
1063
+ variant: "contained",
1064
+ autoFocus: true,
1065
+ title: step.confirm,
1066
+ style: {
1067
+ marginLeft: 8,
1068
+ overflow: 'hidden',
1069
+ textOverflow: 'ellipsis',
1070
+ whiteSpace: 'nowrap',
1071
+ minWidth: '140px',
1072
+ maxWidth: '280px',
1073
+ textAlign: 'center'
1074
+ },
1075
+ children: /*#__PURE__*/_jsxs(Box, {
1076
+ display: "flex",
1077
+ alignItems: "center",
1078
+ children: [(typeof step.loading === 'boolean' ? step.loading : loading) && /*#__PURE__*/_jsx(Spinner, {
1079
+ style: {
1080
+ marginRight: 8
1081
+ },
1082
+ size: 16
1083
+ }), step.confirm]
1084
+ })
1085
+ })]
1086
+ })]
1087
+ });
1088
+ }
1089
+ AddComponentCore.propTypes = {
1090
+ onClose: PropTypes.func,
1091
+ mode: PropTypes.oneOf(['normal', 'embed']),
1092
+ stores: PropTypes.arrayOf(PropTypes.string),
1093
+ resourceType: PropTypes.string,
1094
+ storageKey: PropTypes.string
1095
+ };
1096
+ AddComponentCore.defaultProps = {
1097
+ onClose: () => {},
1098
+ mode: 'normal',
1099
+ stores: [],
1100
+ resourceType: '',
1101
+ storageKey: ''
1102
+ };
1103
+ const Wrapper = styled(Box)`
1104
+ .action-bar {
1105
+ display: flex;
1106
+ justify-content: flex-end;
1107
+ position: sticky;
1108
+ bottom: 0;
1109
+ padding-top: 16px;
1110
+ z-index: 100;
1111
+ width: 100%;
1112
+ background: #fff;
1113
+ border-top: 1px solid #eee;
1114
+ }
1115
+ `;
1116
+ const TypographyWrapper = styled(Typography)`
1117
+ width: 100%;
1118
+ height: 100%;
1119
+ `;
1120
+ const RightContent = styled.div`
1121
+ width: 100%;
1122
+ height: 100%;
1123
+ display: flex;
1124
+ flex-direction: column;
1125
+ justify-content: flex-start;
1126
+ align-items: flex-start;
1127
+
1128
+ .bottom-button {
1129
+ min-width: 200px;
1130
+ }
1131
+
1132
+ // blocklet-select-side
1133
+ aside {
1134
+ width: 140px;
1135
+ }
1136
+ `;
1137
+ const isMobileContent = props => {
1138
+ if (props.isMobile) {
1139
+ return `
1140
+ height: calc(100vh - 56px - 64px - 48px);
1141
+ `;
1142
+ }
1143
+ return '';
1144
+ };
1145
+ const ContentWrapper = styled(Box)`
1146
+ ${({
1147
+ className
1148
+ }) => {
1149
+ // mobile extra style
1150
+ if (className === 'mobileStyle') {
1151
+ return `
1152
+ & > div{
1153
+ height: 100%;
1154
+ }
1155
+ .root-header {
1156
+ .app-name-content {
1157
+ display: none;
1158
+ }
1159
+ }
1160
+
1161
+
1162
+
1163
+ // content-panel
1164
+ .root-header + div{
1165
+ padding-top: 0;
1166
+ position: relative;
1167
+ & > div {
1168
+ padding-top: 0;
1169
+ }
1170
+ }
1171
+
1172
+ header + div {
1173
+ padding-top: 0;
1174
+ }
1175
+
1176
+ header {
1177
+ .header-title {
1178
+ margin-left: 0;
1179
+ }
1180
+
1181
+ .header-title-name {
1182
+ max-width: 100% !important;
1183
+ }
1184
+ }
1185
+ `;
1186
+ }
1187
+ return `
1188
+ position: relative;
1189
+ .root-header {
1190
+ z-index: auto !important;
1191
+ position: fixed !important;
1192
+ }
1193
+ & > div {
1194
+ height: 100%;
1195
+ width: 100%;
1196
+ max-width: 100%;
1197
+ & > div {
1198
+ max-height: unset !important;
1199
+ max-width: 100%;
1200
+ & > div {
1201
+ // left-panel
1202
+ &:first-child {
1203
+ padding: 0;
1204
+ width: 25%;
1205
+ min-width: 240px;
1206
+ & > div:first-child {
1207
+ display: none;
1208
+ }
1209
+ // step
1210
+ & > div:last-child {
1211
+ // margin-top: 32px;
1212
+ margin-top: 0;
1213
+ }
1214
+ }
1215
+ // right-panel
1216
+ &:last-child {
1217
+ padding-top: 0;
1218
+ padding-right: 0px;
1219
+ overflow-y: auto;
1220
+ .app-content {
1221
+ padding: 0 0 24px 0;
1222
+ }
1223
+ .button-container {
1224
+ padding-right: 0;
1225
+ }
1226
+ }
1227
+ }
1228
+ }
1229
+ }
1230
+
1231
+ `;
1232
+ }}
1233
+
1234
+ & .flex-justify-center {
1235
+ display: flex;
1236
+ justify-content: center;
1237
+ }
1238
+
1239
+ & .flex-align-center {
1240
+ display: flex;
1241
+ height: 100%;
1242
+ align-items: center;
1243
+ ${props => isMobileContent(props)}
1244
+ }
1245
+
1246
+ & .agreement-wrapper {
1247
+ ${props => isMobileContent(props)}
1248
+ max-height: 60vh;
1249
+ .eula-trigger {
1250
+ padding-right: 0;
1251
+ }
1252
+ .next-button {
1253
+ display: none;
1254
+ }
1255
+ }
1256
+
1257
+ & .connect {
1258
+ background: white;
1259
+ }
1260
+ `;
1261
+ const PageHeaderWrapper = styled(PageHeader)`
1262
+ margin-bottom: 24px;
1263
+ `;
1264
+ const StoreBlockletItemWrapper = styled.div`
1265
+ position: relative;
1266
+
1267
+ .choose-blocklet {
1268
+ background-color: rgb(236, 251, 253);
1269
+ border-color: rgb(236, 251, 253);
1270
+ border-radius: 8px;
1271
+ }
1272
+
1273
+ .check-container {
1274
+ position: absolute;
1275
+ right: 0;
1276
+ bottom: 0;
1277
+ display: flex;
1278
+ justify-content: flex-end;
1279
+ align-items: flex-end;
1280
+ width: 30px;
1281
+ height: 30px;
1282
+ border-radius: 0 0 8px 0;
1283
+ color: ${props => props.theme.palette.common.white};
1284
+ overflow: hidden;
1285
+ transition: all ease 0.3s;
1286
+ &:after {
1287
+ position: absolute;
1288
+ z-index: 0;
1289
+ display: block;
1290
+ width: 0;
1291
+ height: 0;
1292
+ border-top: transparent solid 15px;
1293
+ border-left: transparent solid 15px;
1294
+ border-bottom: ${props => props.theme.palette.primary.main} solid 15px;
1295
+ border-right: ${props => props.theme.palette.primary.main} solid 15px;
1296
+ transition: all ease 0.1s;
1297
+ content: '';
1298
+ }
1299
+
1300
+ .check-icon {
1301
+ position: relative;
1302
+ z-index: 2;
1303
+ margin: 0 1px 1px 0;
1304
+ font-size: 16px;
1305
+ transition: all ease 0.2s;
1306
+ }
1307
+ }
1308
+ `;
1309
+ const MessageDiv = styled.div`
1310
+ color: ${props => props.theme.palette.primary.main};
1311
+ .msg-before {
1312
+ display: inline-block;
1313
+ color: #aaa;
1314
+ font-size: 14px;
1315
+ margin-right: 6px;
1316
+ }
1317
+ `;