@abtnode/ux 1.16.19-beta-7d3ef675 → 1.16.19-beta-ba354e01

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 (49) hide show
  1. package/es/blocklet/component/add-component-core.js +35 -27
  2. package/es/blocklet/component/button.js +12 -0
  3. package/es/blocklet/component/did-space/connect-to.js +355 -0
  4. package/es/blocklet/component/did-space/connected.js +42 -0
  5. package/es/blocklet/component/did-space/delete.js +104 -0
  6. package/es/blocklet/component/did-space/disconnect.js +230 -0
  7. package/es/blocklet/component/did-space/index.js +73 -0
  8. package/es/blocklet/component/did-space/item.js +233 -0
  9. package/es/blocklet/component/index.js +3 -3
  10. package/es/blocklet/component/step-actions.js +99 -0
  11. package/es/blocklet/publish/create-release.js +14 -13
  12. package/es/blocklet/publish/project-list.js +1 -0
  13. package/es/blocklet/publish/release-list.js +1 -0
  14. package/es/blocklet/publish/tree.js +4 -0
  15. package/es/blocklet/resource-type.js +34 -0
  16. package/es/blocklet/storage/connect-to.js +11 -15
  17. package/es/contexts/config-space.js +160 -0
  18. package/es/hooks/use-mobile.js +17 -0
  19. package/es/hooks/use-storage-endpoint.js +23 -0
  20. package/es/images.js +48 -0
  21. package/es/locales/en.js +2 -0
  22. package/es/locales/zh.js +2 -0
  23. package/es/util/index.js +5 -1
  24. package/es/util/spaces.js +66 -5
  25. package/lib/blocklet/component/add-component-core.js +44 -36
  26. package/lib/blocklet/component/button.js +23 -0
  27. package/lib/blocklet/component/did-space/connect-to.js +362 -0
  28. package/lib/blocklet/component/did-space/connected.js +49 -0
  29. package/lib/blocklet/component/did-space/delete.js +111 -0
  30. package/lib/blocklet/component/did-space/disconnect.js +241 -0
  31. package/lib/blocklet/component/did-space/index.js +82 -0
  32. package/lib/blocklet/component/did-space/item.js +197 -0
  33. package/lib/blocklet/component/index.js +3 -3
  34. package/lib/blocklet/component/step-actions.js +63 -0
  35. package/lib/blocklet/publish/create-release.js +14 -13
  36. package/lib/blocklet/publish/project-list.js +1 -0
  37. package/lib/blocklet/publish/release-list.js +1 -0
  38. package/lib/blocklet/publish/tree.js +45 -38
  39. package/lib/blocklet/resource-type.js +51 -0
  40. package/lib/blocklet/storage/connect-to.js +11 -15
  41. package/lib/contexts/config-space.js +169 -0
  42. package/lib/hooks/use-mobile.js +24 -0
  43. package/lib/hooks/use-storage-endpoint.js +28 -0
  44. package/lib/images.js +51 -0
  45. package/lib/locales/en.js +2 -0
  46. package/lib/locales/zh.js +2 -0
  47. package/lib/util/index.js +8 -2
  48. package/lib/util/spaces.js +69 -3
  49. package/package.json +11 -7
@@ -0,0 +1,99 @@
1
+ import styled from '@emotion/styled';
2
+ import PropTypes from 'prop-types';
3
+ import Box from '@mui/material/Box';
4
+ import { useLocaleContext } from '@arcblock/ux/lib/Locale/context';
5
+ import Button from './button';
6
+ import useMobile from '../../hooks/use-mobile';
7
+ import { hasRequiredSteps } from '../../util';
8
+ import { jsx as _jsx } from "react/jsx-runtime";
9
+ import { jsxs as _jsxs } from "react/jsx-runtime";
10
+ export default function StepActions({
11
+ blocklet,
12
+ onStartNow,
13
+ children,
14
+ disabled
15
+ }) {
16
+ const {
17
+ t
18
+ } = useLocaleContext();
19
+ const isMobile = useMobile();
20
+ if (!blocklet || hasRequiredSteps(blocklet.meta)) {
21
+ return /*#__PURE__*/_jsx(Center, {
22
+ children: children
23
+ });
24
+ }
25
+ return /*#__PURE__*/_jsxs(Between, {
26
+ isMobile: isMobile,
27
+ children: [/*#__PURE__*/_jsx(Button, {
28
+ className: "start-now",
29
+ variant: "contained",
30
+ color: "primary",
31
+ disabled: disabled,
32
+ onClick: () => onStartNow(),
33
+ children: t('setup.startNow')
34
+ }), /*#__PURE__*/_jsx(Box, {
35
+ display: "flex",
36
+ alignItems: "center",
37
+ children: children
38
+ })]
39
+ });
40
+ }
41
+ StepActions.propTypes = {
42
+ blocklet: PropTypes.object,
43
+ onStartNow: PropTypes.func,
44
+ children: PropTypes.any,
45
+ disabled: PropTypes.bool
46
+ };
47
+ StepActions.defaultProps = {
48
+ blocklet: null,
49
+ onStartNow: () => {},
50
+ children: null,
51
+ disabled: false
52
+ };
53
+ const Center = styled(Box)`
54
+ width: 100%;
55
+ margin-top: auto;
56
+ padding-top: 24px;
57
+ display: flex;
58
+ align-items: center;
59
+ justify-content: center;
60
+ .bottom-button {
61
+ min-width: 200px;
62
+ }
63
+ ${props => props.theme.breakpoints.down('md')} {
64
+ .bottom-button {
65
+ width: 100%;
66
+ }
67
+ }
68
+ `;
69
+ const Between = styled(Box)`
70
+ padding-top: 24px;
71
+ margin-top: auto;
72
+ width: 100%;
73
+ display: flex;
74
+ align-items: center;
75
+ justify-content: space-between;
76
+ .start-now {
77
+ min-width: 140px;
78
+ }
79
+ .bottom-button {
80
+ min-width: 120px;
81
+ }
82
+ ${props => props.theme.breakpoints.down('lg')} {
83
+ width: 100%;
84
+ }
85
+
86
+ ${props => props.theme.breakpoints.down('md')} {
87
+ .start-now {
88
+ min-width: inherit;
89
+ }
90
+ .bottom-button {
91
+ min-width: 100px;
92
+ }
93
+ }
94
+
95
+ ${props => props.isMobile} {
96
+ justify-content: space-between;
97
+ gap: '32px',
98
+ }}
99
+ `;
@@ -1,4 +1,4 @@
1
- import { useState, useEffect, lazy, useRef } from 'react';
1
+ import { useState, useEffect, lazy, useRef, useCallback } from 'react';
2
2
  import { Link, useNavigate, useParams } from 'react-router-dom';
3
3
  import styled from '@emotion/styled';
4
4
  import useSetState from 'react-use/lib/useSetState';
@@ -179,7 +179,7 @@ export default function CreateRelease({
179
179
  Toast.error(formatError(err));
180
180
  });
181
181
  };
182
- const getData = () => {
182
+ const getData = useCallback(() => {
183
183
  if (mode === 'edit') {
184
184
  if (!releaseId) {
185
185
  Toast.error('Empty release id');
@@ -190,11 +190,11 @@ export default function CreateRelease({
190
190
  if (mode === 'create') {
191
191
  getProject();
192
192
  }
193
- };
193
+ // eslint-disable-next-line react-hooks/exhaustive-deps
194
+ }, [mode, releaseId]);
194
195
  useEffect(() => {
195
196
  getData();
196
- }, []); // eslint-disable-line react-hooks/exhaustive-deps
197
-
197
+ }, [getData]);
198
198
  const onRelease = status => {
199
199
  const errors = validateParams(params, status, t);
200
200
  if (errors) {
@@ -234,15 +234,16 @@ export default function CreateRelease({
234
234
  setCreateLoading(false);
235
235
  setLoading(false);
236
236
  if (status === 'draft') {
237
- Toast.success('Save draft success');
238
- if (mode === 'create') {
239
- const id = res?.release?.id;
240
- navigate(`../${projectId}/${id}`, {
241
- replace: true
242
- });
243
- }
237
+ Toast.success(t('blocklet.publish.saveDraftSuccess'));
238
+ } else {
239
+ Toast.success(t('blocklet.publish.createReleaseSuccess'));
240
+ }
241
+ if (mode === 'create') {
242
+ const id = res?.release?.id;
243
+ navigate(`../${projectId}/${id}`, {
244
+ replace: true
245
+ });
244
246
  } else {
245
- Toast.success('Create Release success');
246
247
  getRelease();
247
248
  }
248
249
  }).catch(err => {
@@ -66,6 +66,7 @@ export default function ProjectList() {
66
66
  setLoading(false);
67
67
  getProjects();
68
68
  Toast.success(t('common.removeSuccess'));
69
+ setDeleteConfirm(null);
69
70
  }).catch(err => {
70
71
  setLoading(false);
71
72
  Toast.error(err.message);
@@ -98,6 +98,7 @@ export default function ReleaseList() {
98
98
  setLoading(false);
99
99
  getReleases();
100
100
  Toast.success(t('common.removeSuccess'));
101
+ setDeleteConfirm(null);
101
102
  }).catch(err => {
102
103
  setLoading(false);
103
104
  Toast.error(err.message);
@@ -11,6 +11,7 @@ import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
11
11
  import ChevronRightIcon from '@mui/icons-material/ChevronRight';
12
12
  import OpenInNewIcon from '@mui/icons-material/OpenInNew';
13
13
  import { Icon } from '@iconify/react';
14
+ import Images from '../../images';
14
15
 
15
16
  // BFS algorithm to find node by his ID
16
17
  import { jsx as _jsx } from "react/jsx-runtime";
@@ -147,6 +148,9 @@ export default function Tree({
147
148
  }), !!node.description && /*#__PURE__*/_jsx(Box, {
148
149
  className: "description",
149
150
  children: node.description
151
+ }), !!node.images?.length && /*#__PURE__*/_jsx(Images, {
152
+ className: "images",
153
+ data: node.images
150
154
  })]
151
155
  })
152
156
  }),
@@ -0,0 +1,34 @@
1
+ import { useMemo } from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import uniq from 'lodash/uniq';
4
+ import upperFirst from 'lodash/upperFirst';
5
+ import Box from '@mui/material/Box';
6
+ import Tag from '@arcblock/ux/lib/Tag';
7
+ import { jsx as _jsx } from "react/jsx-runtime";
8
+ export default function ResourceType({
9
+ blocklet,
10
+ ...rest
11
+ }) {
12
+ const types = useMemo(() => {
13
+ return uniq((blocklet?.meta?.resources || []).map(x => upperFirst(x.split('.').slice(-1)[0])).filter(Boolean));
14
+ }, [blocklet]);
15
+ const style = {
16
+ color: '#FFF',
17
+ backgroundColor: '#5B9BD5',
18
+ marginRight: '4px'
19
+ };
20
+ return /*#__PURE__*/_jsx(Box, {
21
+ display: "flex",
22
+ mr: "-4px",
23
+ ...rest,
24
+ children: types.map(x => /*#__PURE__*/_jsx(Tag, {
25
+ type: "custom",
26
+ style: style,
27
+ children: x
28
+ }, x))
29
+ });
30
+ }
31
+ ResourceType.propTypes = {
32
+ blocklet: PropTypes.object.isRequired
33
+ };
34
+ ResourceType.defaultProps = {};
@@ -14,7 +14,7 @@ import { axios } from '../../util/api';
14
14
  import { formatError, getWebWalletUrl } from '../../util';
15
15
  import { useNodeContext } from '../../contexts/node';
16
16
  import useBlockletInfoForConnectDIDSpaces from '../../hooks/use-blocklet-info-for-connect-did-spaces';
17
- import { extraDIDSpacesCoreUrl, getDIDSpaceDidFromEndpoint, isValidSpaceGatewayUrl } from '../../util/spaces';
17
+ import { extraDIDSpacesCoreUrl, getDIDSpaceDidFromEndpoint, getSpaceGatewayUrl, isValidSpaceGatewayUrl } from '../../util/spaces';
18
18
  import { useBlockletStorageContext } from '../../contexts/blocklet-storage';
19
19
  /**
20
20
  * @description
@@ -148,12 +148,8 @@ function ConnectTo({
148
148
  name,
149
149
  did
150
150
  }) => {
151
- let normalizeUrl = endpoint?.replace(/\/api\/.+/, '')?.trim()?.replace(/\/$/, '');
152
- if (!normalizeUrl.startsWith('http')) {
153
- // eslint-disable-next-line no-param-reassign
154
- normalizeUrl = `https://${normalizeUrl}`;
155
- }
156
- if (!(await isValidSpaceGatewayUrl(normalizeUrl))) {
151
+ const spaceGatewayUrl = await getSpaceGatewayUrl(endpoint);
152
+ if (!(await isValidSpaceGatewayUrl(spaceGatewayUrl))) {
157
153
  toast.error(t('storage.spaces.gateway.add.invalidUrl'));
158
154
  return;
159
155
  }
@@ -167,7 +163,7 @@ function ConnectTo({
167
163
  const spaceGateway = {
168
164
  name,
169
165
  did,
170
- url: normalizeUrl,
166
+ url: spaceGatewayUrl,
171
167
  endpoint
172
168
  };
173
169
  await addSpaceGateway(spaceGateway, endpoint);
@@ -193,19 +189,19 @@ function ConnectTo({
193
189
  const handleUseSpaceGatewayConnect = async () => {
194
190
  try {
195
191
  setLoading(true);
196
- const tempUrl = url.startsWith('http') ? url : `https://${url}`;
197
- const fullUrl = extraDIDSpacesCoreUrl(tempUrl);
198
- const spaceDid = new URL(tempUrl).searchParams.get('spaceDid');
199
- if (!(await isValidSpaceGatewayUrl(fullUrl))) {
192
+ const spaceGatewayUrl = await getSpaceGatewayUrl(url);
193
+ const didSpacesCoreUrl = extraDIDSpacesCoreUrl(spaceGatewayUrl);
194
+ const spaceDid = new URL(spaceGatewayUrl).searchParams.get('spaceDid');
195
+ if (!(await isValidSpaceGatewayUrl(didSpacesCoreUrl))) {
200
196
  throw new Error(t('storage.spaces.gateway.add.invalidUrl'));
201
197
  }
202
198
  setAuthorizeConnect(preValue => ({
203
199
  ...preValue,
204
200
  open: true,
205
- prefix: joinUrl(fullUrl, 'space/api/did'),
206
- baseUrl: new URL(fullUrl).origin,
201
+ prefix: joinUrl(didSpacesCoreUrl, 'space/api/did'),
202
+ baseUrl: new URL(didSpacesCoreUrl).origin,
207
203
  checkFn: axios.create({
208
- baseURL: joinUrl(fullUrl, 'space')
204
+ baseURL: joinUrl(didSpacesCoreUrl, 'space')
209
205
  }).get,
210
206
  extraParams: {
211
207
  appDid: blockletInfo.appDid,
@@ -0,0 +1,160 @@
1
+ import { createContext, useContext, useEffect, useState } from 'react';
2
+ import { node } from 'prop-types';
3
+ import { isEmpty, merge } from 'lodash';
4
+ import { BLOCKLET_CONFIGURABLE_KEY } from '@blocklet/constant';
5
+ import { useAsyncEffect } from 'ahooks';
6
+ import Toast from '@arcblock/ux/lib/Toast';
7
+ import { useNodeContext } from './node';
8
+ import { getSpaceGateway, getSpaceNameByEndpoint } from '../util/spaces';
9
+ import { useBlockletContext } from './blocklet';
10
+ import useStorageEndpoint from '../hooks/use-storage-endpoint';
11
+
12
+ /**
13
+ * @typedef {{
14
+ * did: string,
15
+ * url: string,
16
+ * name: string,
17
+ * endpoint: string,
18
+ * }} SpaceGateway
19
+ */
20
+
21
+ /**
22
+ * @typedef {{
23
+ * loading: boolean,
24
+ * spaceGateway: SpaceGateway,
25
+ * deleteSpaceGateway: (spaceGateway: SpaceGateway) => void | Promise<void>,
26
+ * updateSpaceGateway: (updateSpaceGateway: SpaceGateway) => void | Promise<void>,
27
+ * storageEndpoint: string,
28
+ * settingStorageEndpoint: (endpoint: string) => void | Promise<void>,
29
+ * hasStorageEndpoint: boolean,
30
+ * }} ConfigSpaceContextType
31
+ */
32
+
33
+ /** @type {import('react').Context<ConfigSpaceContextType>} */
34
+ import { jsx as _jsx } from "react/jsx-runtime";
35
+ const ConfigSpaceContext = /*#__PURE__*/createContext({});
36
+ const {
37
+ Provider,
38
+ Consumer
39
+ } = ConfigSpaceContext;
40
+
41
+ /**
42
+ * @description
43
+ * @param {{
44
+ * children: React.ReactNode,
45
+ * }} { children }
46
+ * @return {any}
47
+ */
48
+ function ConfigSpaceProvider({
49
+ children
50
+ }) {
51
+ const [loading, setLoading] = useState(false);
52
+ const [spaceGateway, setSpaceGateway] = useState(null);
53
+ /**
54
+ * @type {{
55
+ * blocklet: import('@abtnode/client').BlockletState,
56
+ * actions: { refreshBlocklet: Function }
57
+ * }}
58
+ */
59
+ const {
60
+ blocklet,
61
+ actions: {
62
+ refreshBlocklet
63
+ }
64
+ } = useBlockletContext();
65
+ const {
66
+ storageEndpoint
67
+ } = useStorageEndpoint();
68
+ const {
69
+ api: nodeApi
70
+ } = useNodeContext();
71
+ useAsyncEffect(async () => {
72
+ try {
73
+ setLoading(true);
74
+ if (isEmpty(storageEndpoint)) {
75
+ setSpaceGateway(null);
76
+ return;
77
+ }
78
+ const $spaceGateway = await getSpaceGateway(storageEndpoint);
79
+ setSpaceGateway($spaceGateway);
80
+ } catch (error) {
81
+ console.error(error);
82
+ Toast.error(error.message);
83
+ } finally {
84
+ setLoading(false);
85
+ }
86
+ }, [storageEndpoint]);
87
+
88
+ /**
89
+ * @description
90
+ * @param {SpaceGateway} spaceGateway
91
+ */
92
+ const deleteSpaceGateway = () => {
93
+ setSpaceGateway(null);
94
+ refreshBlocklet();
95
+ };
96
+
97
+ /**
98
+ * @description
99
+ * @param {SpaceGateway} x
100
+ */
101
+ const updateSpaceGateway = async x => {
102
+ setSpaceGateway(x);
103
+ // eslint-disable-next-line no-use-before-define
104
+ await settingStorageEndpoint(x.endpoint);
105
+ refreshBlocklet();
106
+ };
107
+
108
+ /**
109
+ * @description
110
+ * @param {string} endpoint
111
+ */
112
+ const settingStorageEndpoint = async endpoint => {
113
+ await nodeApi.configBlocklet({
114
+ input: {
115
+ did: [blocklet.meta.did],
116
+ configs: [{
117
+ key: BLOCKLET_CONFIGURABLE_KEY.BLOCKLET_APP_SPACE_ENDPOINT,
118
+ value: endpoint
119
+ }]
120
+ }
121
+ });
122
+ refreshBlocklet();
123
+ };
124
+ useEffect(() => {
125
+ async function syncSpaceGateway() {
126
+ if (!spaceGateway) {
127
+ return;
128
+ }
129
+ const name = await getSpaceNameByEndpoint(spaceGateway.endpoint, spaceGateway.name);
130
+ const latestSpaceGateway = merge(spaceGateway, {
131
+ name
132
+ });
133
+ setSpaceGateway(latestSpaceGateway);
134
+ }
135
+ syncSpaceGateway();
136
+ // eslint-disable-next-line react-hooks/exhaustive-deps
137
+ }, []);
138
+ const hasStorageEndpoint = Boolean(storageEndpoint && spaceGateway);
139
+ return /*#__PURE__*/_jsx(Provider, {
140
+ value: {
141
+ loading,
142
+ spaceGateway,
143
+ deleteSpaceGateway,
144
+ updateSpaceGateway,
145
+ storageEndpoint,
146
+ settingStorageEndpoint,
147
+ hasStorageEndpoint
148
+ },
149
+ children: children
150
+ });
151
+ }
152
+ ConfigSpaceProvider.propTypes = {
153
+ children: node.isRequired
154
+ };
155
+ ConfigSpaceProvider.defaultProps = {};
156
+ function useConfigSpaceContext() {
157
+ const res = useContext(ConfigSpaceContext);
158
+ return res;
159
+ }
160
+ export { ConfigSpaceContext, ConfigSpaceProvider, Consumer as ConfigSpaceConsumer, useConfigSpaceContext };
@@ -0,0 +1,17 @@
1
+ import { useTheme } from '@mui/material/styles';
2
+ import useMediaQuery from '@mui/material/useMediaQuery';
3
+
4
+ /**
5
+ * @description
6
+ * @export
7
+ * @param {{ key: string | import('@mui/material').Breakpoint }} [{ key }={ key: 'md' }]
8
+ * @return {*}
9
+ */
10
+ export default function useMobile({
11
+ key
12
+ } = {
13
+ key: 'sm'
14
+ }) {
15
+ const theme = useTheme();
16
+ return useMediaQuery(theme.breakpoints.down(key));
17
+ }
@@ -0,0 +1,23 @@
1
+ import { useEffect, useState } from 'react';
2
+ import { useBlockletContext } from '../contexts/blocklet';
3
+ import { getDIDSpaceEndpoint } from '../util/spaces';
4
+
5
+ /**
6
+ * @description
7
+ * @export
8
+ * @return {{
9
+ * storageEndpoint: string
10
+ * }}
11
+ */
12
+ export default function useStorageEndpoint() {
13
+ const [storageEndpoint, setStorageEndpoint] = useState(null);
14
+ const {
15
+ blocklet
16
+ } = useBlockletContext();
17
+ useEffect(() => {
18
+ setStorageEndpoint(getDIDSpaceEndpoint(blocklet));
19
+ }, [blocklet]);
20
+ return {
21
+ storageEndpoint
22
+ };
23
+ }
package/es/images.js ADDED
@@ -0,0 +1,48 @@
1
+ import PropTypes from 'prop-types';
2
+ import styled from '@emotion/styled';
3
+ import Box from '@mui/material/Box';
4
+ import { jsx as _jsx } from "react/jsx-runtime";
5
+ export default function Images({
6
+ data,
7
+ ...props
8
+ }) {
9
+ if (!data || !data.length) {
10
+ return null;
11
+ }
12
+ return /*#__PURE__*/_jsx(StyledBox, {
13
+ ...props,
14
+ children: data.map(item => /*#__PURE__*/_jsx("a", {
15
+ href: item.url,
16
+ target: "_blank",
17
+ rel: "noreferrer",
18
+ children: /*#__PURE__*/_jsx("img", {
19
+ src: item.url,
20
+ alt: item.alt || ''
21
+ })
22
+ }, item.url))
23
+ });
24
+ }
25
+ Images.propTypes = {
26
+ data: PropTypes.arrayOf(PropTypes.shape({
27
+ url: PropTypes.string,
28
+ alt: PropTypes.string
29
+ }))
30
+ };
31
+ Images.defaultProps = {
32
+ data: []
33
+ };
34
+ const StyledBox = styled(Box)`
35
+ display: flex;
36
+ justify-content: flex-start;
37
+ flex-wrap: wrap;
38
+ margin-bottom: -12px;
39
+ a {
40
+ display: flex;
41
+ }
42
+ img {
43
+ height: 128px;
44
+ cursor: pointer;
45
+ margin-right: 12px;
46
+ margin-bottom: 12px;
47
+ }
48
+ `;
package/es/locales/en.js CHANGED
@@ -638,6 +638,8 @@ module.exports = {
638
638
  createRelease: 'Create a new release',
639
639
  deleteReleaseTip: 'The release cannot be recovered after deletion',
640
640
  createBlockletDid: 'Create from DID Wallet',
641
+ createReleaseSuccess: 'Release created successfully',
642
+ saveDraftSuccess: 'Draft saved successfully',
641
643
  connect: {
642
644
  title: 'Create Blocklet DID',
643
645
  scan: 'Scan the QR code below to generate Blocklet DID',
package/es/locales/zh.js CHANGED
@@ -645,6 +645,8 @@ module.exports = {
645
645
  createRelease: '创建新的发布',
646
646
  deleteReleaseTip: '此发布删除后不可恢复,是否继续?',
647
647
  createBlockletDid: '通过 DID 钱包创建',
648
+ createReleaseSuccess: '发布成功',
649
+ saveDraftSuccess: '保存成功',
648
650
  connect: {
649
651
  title: '创建 Blocklet DID',
650
652
  scan: '扫描下面的二维码来生成 Blocklet DID',
package/es/util/index.js CHANGED
@@ -615,4 +615,8 @@ export const getSubscriptionURL = async ({
615
615
  };
616
616
  export const nanoid = (length = 16) => [...Array(length)].map(() => Math.random().toString(36)[2]).join('');
617
617
  export const getSystemDomains = domains => domains.filter(x => x.isProtected);
618
- export const getLogoHash = logo => encodeURIComponent((logo || '').split('/').slice(-1)[0].slice(0, 7));
618
+ export const getLogoHash = logo => encodeURIComponent((logo || '').split('/').slice(-1)[0].slice(0, 7));
619
+ const hasRequiredEnvironments = meta => (meta.environments || []).some(x => x.required);
620
+ export const hasRequiredSteps = meta => {
621
+ return !!meta.requirements?.fuels?.length || hasRequiredEnvironments(meta);
622
+ };
package/es/util/spaces.js CHANGED
@@ -1,11 +1,15 @@
1
1
  import isUrl from 'is-url';
2
2
  import { BLOCKLET_CONFIGURABLE_KEY } from '@blocklet/constant';
3
- import { isObject } from 'lodash';
3
+ import { isObject, isString } from 'lodash';
4
4
  import { joinURL } from 'ufo';
5
5
  import { api } from './api';
6
6
 
7
7
  /* eslint-disable import/prefer-default-export */
8
8
 
9
+ /**
10
+ * @typedef {import('../contexts/config-space').SpaceGateway} SpaceGateway
11
+ */
12
+
9
13
  /**
10
14
  * @FIXME 希望之后放在 sdk 中 @jianchao
11
15
  * @description
@@ -82,7 +86,7 @@ export function getSpaceBackupEndpoint(environments) {
82
86
  * @description
83
87
  * @export
84
88
  * @param {string} endpoint
85
- * @return {boolean}
89
+ * @return {string}
86
90
  */
87
91
  export function getSpaceGatewayFromEndpoint(endpoint) {
88
92
  return endpoint?.replace(/\/api\/space\/.+/, '');
@@ -134,7 +138,11 @@ export async function isValidSpaceGatewayUrl(spaceGatewayUrl) {
134
138
  * @param {string} endpoint
135
139
  * @return {Promise<string >}
136
140
  */
137
- export async function getSpaceNameByEndpoint(endpoint) {
141
+ export async function getSpaceNameByEndpoint(endpoint, {
142
+ timeout
143
+ } = {
144
+ timeout: 3000
145
+ }) {
138
146
  try {
139
147
  if (!isUrl(endpoint)) {
140
148
  return '';
@@ -142,7 +150,7 @@ export async function getSpaceNameByEndpoint(endpoint) {
142
150
  const {
143
151
  headers
144
152
  } = await api.head(endpoint, {
145
- timeout: 3000
153
+ timeout
146
154
  });
147
155
  const spaceName = headers['x-space-name'];
148
156
  return spaceName ? decodeURIComponent(spaceName) : '';
@@ -171,4 +179,57 @@ export function getSpaceNftDisplayUrlFromEndpoint(endpoint) {
171
179
  */
172
180
  export const getDIDSpaceEndpoint = blocklet => {
173
181
  return blocklet?.configs?.find(item => item.key === BLOCKLET_CONFIGURABLE_KEY.BLOCKLET_APP_SPACE_ENDPOINT)?.value || null;
174
- };
182
+ };
183
+
184
+ /**
185
+ * @description
186
+ * @export
187
+ * @param {string} endpoint
188
+ * @returns {Promise<SpaceGateway>}
189
+ */
190
+ export async function getSpaceGateway(endpoint) {
191
+ /** @type {SpaceGateway} */
192
+ const spaceGateway = {};
193
+ spaceGateway.did = getDIDSpaceDidFromEndpoint(endpoint);
194
+ spaceGateway.name = await getSpaceNameByEndpoint(endpoint);
195
+ spaceGateway.url = getSpaceGatewayFromEndpoint(endpoint);
196
+ spaceGateway.endpoint = endpoint;
197
+ return spaceGateway;
198
+ }
199
+
200
+ /**
201
+ * @description
202
+ * @export
203
+ * @param {string} anyUrl
204
+ * @param {{
205
+ * timeout: number,
206
+ * withSearchParams: true | false,
207
+ * }} options
208
+ * @returns {Promise<string>}
209
+ */
210
+ export async function getSpaceGatewayUrl(anyUrl, options = {
211
+ timeout: 30000,
212
+ withSearchParams: true
213
+ }) {
214
+ if (!isString(anyUrl)) {
215
+ throw new Error(`url(${anyUrl}) must be a string`);
216
+ }
217
+ const url = anyUrl.startsWith('http') ? anyUrl : `https://${anyUrl}`;
218
+ const u = new URL(url);
219
+
220
+ /** @type {{ data: import('@blocklet/sdk').WindowBlocklet }} */
221
+ const {
222
+ data: blockletMeta
223
+ } = await api.get(joinURL(u.origin, '/__blocklet__.js?type=json'), {
224
+ timeout: options.timeout
225
+ });
226
+
227
+ /** @type {string} */
228
+ const mountPoint = blockletMeta.componentMountPoints.findLast(x => x.did === 'z8iZnaYxnkMD5AKRjTKiCb8pQr1ut8UantAcf')?.mountPoint;
229
+ if (!mountPoint) {
230
+ throw new Error(`MountPoint(${mountPoint}) not found`);
231
+ }
232
+ const didSpacesCoreUrl = joinURL(u.origin, mountPoint);
233
+ const spaceGatewayUrl = options.withSearchParams && u.searchParams.get('spaceDid') ? joinURL(didSpacesCoreUrl, `?spaceDid=${u.searchParams.get('spaceDid')}`) : didSpacesCoreUrl;
234
+ return spaceGatewayUrl;
235
+ }