@blocklet/did-space-react 1.0.9 → 1.0.10

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/README.md CHANGED
@@ -143,3 +143,46 @@ export default function Demo() {
143
143
  );
144
144
  }
145
145
  ```
146
+
147
+ ### Custom Footer
148
+
149
+ You can customize the footer content of DIDSpaceConnection through the footer attribute. By default, when footer is set to true, it displays the latest audit log information. You can also provide your own custom footer content.
150
+
151
+ Example 1: Display the latest audit log by default
152
+
153
+ ```tsx
154
+ import { DIDSpaceConnection } from '@blocklet/did-space-react';
155
+
156
+ export default function Demo() {
157
+ const endpoint = 'https://${spaceDomain}/app/api/space/${spaceDid}/app/${appDid}/object/';
158
+
159
+ return <DIDSpaceConnection endpoint={x.endpoint} selected footer />;
160
+ }
161
+ ```
162
+
163
+ Example 2: Custom Footer Content
164
+
165
+ ```tsx
166
+ import { DIDSpaceConnection } from '@blocklet/did-space-react';
167
+
168
+ export default function Demo() {
169
+ const endpoint = 'https://${spaceDomain}/app/api/space/${spaceDid}/app/${appDid}/object/';
170
+
171
+ return (
172
+ <DIDSpaceConnection
173
+ endpoint={x.endpoint}
174
+ selected
175
+ footer={({ spaceGateway, selected, refresh, originalFooter }) => (
176
+ <>
177
+ {/* display the latest audit log */}
178
+ {originalFooter}
179
+ {/* display DID Space version number */}
180
+ <Box display="flex" alignItems="center">
181
+ <Typography>{spaceGateway.version}</Typography>
182
+ </Box>
183
+ </>
184
+ )}
185
+ />
186
+ );
187
+ }
188
+ ```
@@ -0,0 +1,4 @@
1
+ export declare function AuditLogBar({ endpoint, deps }: {
2
+ endpoint: string;
3
+ deps?: any[];
4
+ }): import("react/jsx-runtime").JSX.Element | null;
@@ -0,0 +1,44 @@
1
+ 'use strict';
2
+
3
+ const jsxRuntime = require('react/jsx-runtime');
4
+ const material = require('@mui/material');
5
+ const OpenInNewIcon = require('@mui/icons-material/OpenInNew');
6
+ const ufo = require('ufo');
7
+ const util = require('../../libs/util.js');
8
+ const useLocale = require('../../hooks/use-locale.js');
9
+ const useAuditLog = require('../../hooks/use-audit-log.js');
10
+
11
+ function AuditLogBar({ endpoint, deps = [] }) {
12
+ const { t } = useLocale();
13
+ const { loading, data: auditLogs } = useAuditLog({
14
+ endpoint,
15
+ deps
16
+ });
17
+ const { baseUrl, spaceDid, appDid } = util.getSpaceEndpointContext(endpoint);
18
+ const latestLog = auditLogs?.[0];
19
+ if (loading) {
20
+ return /* @__PURE__ */ jsxRuntime.jsx(material.Skeleton, { variant: "text", sx: { width: "100%", fontSize: "1rem" } });
21
+ }
22
+ if (!latestLog) {
23
+ return null;
24
+ }
25
+ return /* @__PURE__ */ jsxRuntime.jsxs(material.Box, { sx: { ml: "4px", fontSize: "14px" }, children: [
26
+ latestLog.description,
27
+ /* @__PURE__ */ jsxRuntime.jsxs(
28
+ material.Link,
29
+ {
30
+ href: ufo.joinURL(baseUrl, `space/${spaceDid}/apps/${appDid}/auditLogs`),
31
+ target: "_blank",
32
+ underline: "hover",
33
+ sx: { color: "primary.main", ml: 1, display: "inline-flex", alignItems: "center" },
34
+ children: [
35
+ t("common.viewMore"),
36
+ " ",
37
+ /* @__PURE__ */ jsxRuntime.jsx(OpenInNewIcon, { sx: { fontSize: "14px", marginLeft: "4px", verticalAlign: "-2px" } })
38
+ ]
39
+ }
40
+ )
41
+ ] });
42
+ }
43
+
44
+ exports.AuditLogBar = AuditLogBar;
@@ -1,21 +1,26 @@
1
1
  import { BoxProps } from '@mui/material';
2
2
  import { DIDSpaceGateway, DIDSpaceStatus } from '../../types';
3
- export type DIDSpaceConnectionAction = React.ReactNode | ((props: {
3
+ export interface DIDSpaceConnectionContext {
4
4
  spaceGateway: DIDSpaceGateway;
5
5
  spaceStatus: DIDSpaceStatus;
6
6
  errorCode: number;
7
7
  selected: boolean;
8
8
  compat: boolean;
9
9
  refresh: () => void;
10
+ }
11
+ export type DIDSpaceConnectionAction = React.ReactNode | ((props: DIDSpaceConnectionContext) => React.ReactNode);
12
+ export type DIDSpaceConnectionFooter = false | true | React.ReactNode | ((props: DIDSpaceConnectionContext & {
13
+ originalFooter: React.ReactNode;
10
14
  }) => React.ReactNode);
11
15
  export interface DIDSpaceConnectionProps extends BoxProps {
12
16
  endpoint: string;
13
17
  selected?: boolean;
14
18
  compat?: boolean;
15
19
  action?: DIDSpaceConnectionAction;
20
+ footer?: DIDSpaceConnectionFooter;
16
21
  deps?: any[];
17
22
  }
18
- export declare function DIDSpaceConnection({ endpoint, selected, compat, action, className, deps, ...rest }: DIDSpaceConnectionProps): import("react/jsx-runtime").JSX.Element;
23
+ export declare function DIDSpaceConnection({ endpoint, selected, compat, action, footer, className, deps, ...rest }: DIDSpaceConnectionProps): import("react/jsx-runtime").JSX.Element;
19
24
  export type SpaceCardProps = DIDSpaceConnectionProps;
20
25
  export declare const SpaceCard: typeof DIDSpaceConnection;
21
26
  export type Action = DIDSpaceConnectionAction;
@@ -11,8 +11,9 @@ const React = require('react');
11
11
  const axios = require('axios');
12
12
  const ahooks = require('ahooks');
13
13
  const semver = require('semver');
14
+ const constants = require('@did-space/core/constants');
14
15
  const _package = require('../../package.json.js');
15
- const constants = require('../../libs/constants.js');
16
+ const constants$1 = require('../../libs/constants.js');
16
17
  const useMobile = require('../../hooks/use-mobile.js');
17
18
  const useSpaceInfo = require('../../hooks/use-space-info.js');
18
19
  const useLocale = require('../../hooks/use-locale.js');
@@ -20,6 +21,7 @@ const util = require('../../libs/util.js');
20
21
  const index = require('../../types/index.js');
21
22
  require('../../icons/index.js');
22
23
  const index$1 = require('../preview-space-nft/index.js');
24
+ const auditLogBar = require('./audit-log-bar.js');
23
25
  const spaceConnected = require('../../icons/space-connected.svg.js');
24
26
  const spaceDisconnect = require('../../icons/space-disconnect.svg.js');
25
27
  const spaceConnectError = require('../../icons/space-connect-error.svg.js');
@@ -88,7 +90,7 @@ function Status({
88
90
  // CORS 拦截
89
91
  [constants.SPACE_CONNECT_ERROR_CODE.CORS_BLOCKED]: {
90
92
  icon: /* @__PURE__ */ jsxRuntime.jsx(spaceConnectError, { style: iconStyle }),
91
- text: /* @__PURE__ */ jsxRuntime.jsx(ErrorLink, { title: t("storage.spaces.error.corsBlocked"), url: ufo.withQuery(constants.fixCorsErrorPageUrl, { locale }) })
93
+ text: /* @__PURE__ */ jsxRuntime.jsx(ErrorLink, { title: t("storage.spaces.error.corsBlocked"), url: ufo.withQuery(constants$1.fixCorsErrorPageUrl, { locale }) })
92
94
  },
93
95
  // Space 版本不兼容
94
96
  [constants.SPACE_CONNECT_ERROR_CODE.INCOMPATIBLE]: {
@@ -157,8 +159,9 @@ function DIDSpaceConnection({
157
159
  selected = false,
158
160
  compat,
159
161
  action,
162
+ footer = false,
160
163
  className,
161
- deps,
164
+ deps = [],
162
165
  ...rest
163
166
  }) {
164
167
  const isMobile = useMobile();
@@ -171,6 +174,7 @@ function DIDSpaceConnection({
171
174
  const spaceUrl = util.getSpaceUrlFromEndpoint(endpoint);
172
175
  const gatewayUrl = util.getSpaceGatewayUrlFromEndpoint(endpoint);
173
176
  const [refreshSpaceInfo, setRefreshSpaceInfo] = React.useState(false);
177
+ const refreshDeps = React.useMemo(() => [refreshSpaceInfo].concat(deps), [refreshSpaceInfo, deps]);
174
178
  const refresh = () => setRefreshSpaceInfo((p) => !p);
175
179
  const {
176
180
  data: spaceInfo,
@@ -178,7 +182,7 @@ function DIDSpaceConnection({
178
182
  loading
179
183
  } = useSpaceInfo({
180
184
  endpoint,
181
- deps: [refreshSpaceInfo].concat(deps ?? [])
185
+ deps: refreshDeps
182
186
  });
183
187
  let spaceName = "";
184
188
  const isAvailable = spaceInfo?.isAvailable ?? false;
@@ -212,27 +216,41 @@ function DIDSpaceConnection({
212
216
  spaceStatus.current = index.DIDSpaceStatus.CONNECTED;
213
217
  }
214
218
  }
219
+ const context = {
220
+ spaceGateway: {
221
+ did: spaceDid,
222
+ name: spaceInfo?.name ?? "",
223
+ url: gatewayUrl,
224
+ endpoint,
225
+ ownerDid: spaceInfo?.ownerDid ?? ""
226
+ },
227
+ spaceStatus: spaceStatus.current,
228
+ errorCode,
229
+ selected,
230
+ compat: isCompact,
231
+ refresh
232
+ };
215
233
  const renderAction = () => {
216
234
  if (loading)
217
235
  return null;
218
236
  if (typeof action === "function") {
219
- return action({
220
- spaceGateway: {
221
- did: spaceDid,
222
- name: spaceInfo?.name ?? "",
223
- url: gatewayUrl,
224
- endpoint,
225
- ownerDid: spaceInfo?.ownerDid ?? ""
226
- },
227
- spaceStatus: spaceStatus.current,
228
- errorCode,
229
- selected,
230
- compat: isCompact,
231
- refresh
232
- });
237
+ return action(context);
233
238
  }
234
239
  return action;
235
240
  };
241
+ const originalFooter = /* @__PURE__ */ jsxRuntime.jsx(auditLogBar.AuditLogBar, { endpoint, deps: refreshDeps });
242
+ const renderFooter = () => {
243
+ if (typeof footer === "function") {
244
+ return footer({
245
+ ...context,
246
+ originalFooter
247
+ });
248
+ }
249
+ if (footer === true) {
250
+ return originalFooter;
251
+ }
252
+ return null;
253
+ };
236
254
  return /* @__PURE__ */ jsxRuntime.jsxs(
237
255
  BoxContainer,
238
256
  {
@@ -242,6 +260,22 @@ function DIDSpaceConnection({
242
260
  }),
243
261
  ...rest,
244
262
  children: [
263
+ isCompact && /* @__PURE__ */ jsxRuntime.jsxs(material.Box, { display: "flex", alignItems: "center", marginBottom: 1, children: [
264
+ selected && /* @__PURE__ */ jsxRuntime.jsx(
265
+ Status,
266
+ {
267
+ spaceUrl,
268
+ status: spaceStatus.current,
269
+ errorCode,
270
+ refresh,
271
+ spaceInfo,
272
+ flexShrink: 0,
273
+ sx: { ml: "4px" }
274
+ }
275
+ ),
276
+ /* @__PURE__ */ jsxRuntime.jsx(material.Box, { flex: 1 }),
277
+ renderAction()
278
+ ] }),
245
279
  /* @__PURE__ */ jsxRuntime.jsxs(material.Box, { display: "flex", alignItems: "center", children: [
246
280
  /* @__PURE__ */ jsxRuntime.jsx(index$1.DIDSpaceNFTPreview, { src: util.getSpaceNftDisplayUrlFromEndpoint(endpoint), width: "72px", height: "72px" }),
247
281
  /* @__PURE__ */ jsxRuntime.jsxs(material.Stack, { ml: 2, flex: 1, spacing: 1, minWidth: 0, children: [
@@ -277,21 +311,7 @@ function DIDSpaceConnection({
277
311
  ] }),
278
312
  !isCompact && renderAction()
279
313
  ] }),
280
- isCompact && /* @__PURE__ */ jsxRuntime.jsxs(material.Box, { display: "flex", alignItems: "center", marginTop: 0.5, children: [
281
- selected && /* @__PURE__ */ jsxRuntime.jsx(
282
- Status,
283
- {
284
- spaceUrl,
285
- status: spaceStatus.current,
286
- errorCode,
287
- refresh,
288
- spaceInfo,
289
- flexShrink: 0
290
- }
291
- ),
292
- /* @__PURE__ */ jsxRuntime.jsx(material.Box, { flex: 1 }),
293
- renderAction()
294
- ] })
314
+ renderFooter()
295
315
  ]
296
316
  }
297
317
  );
@@ -0,0 +1,16 @@
1
+ import type { AuditLogModel } from '@did-space/core';
2
+ import { AuditLogAction, AuditLogStatus } from '@did-space/core/model';
3
+ export interface AuditLogModelExtra extends AuditLogModel {
4
+ description: React.ReactNode;
5
+ }
6
+ export default function useAuditLog({ endpoint, size, status, actionType, deps, }: {
7
+ endpoint: string;
8
+ size?: number;
9
+ status?: AuditLogStatus | AuditLogStatus[];
10
+ actionType?: AuditLogAction | AuditLogAction[];
11
+ deps?: any[];
12
+ }): {
13
+ loading: boolean;
14
+ data: AuditLogModelExtra[] | undefined;
15
+ error: Error | undefined;
16
+ };
@@ -0,0 +1,86 @@
1
+ 'use strict';
2
+
3
+ const isUrl = require('is-url');
4
+ const ahooks = require('ahooks');
5
+ const model = require('@did-space/core/model');
6
+ const ufo = require('ufo');
7
+ const xbytes = require('xbytes');
8
+ const api = require('../libs/api.js');
9
+ const useLocale = require('./use-locale.js');
10
+ const util = require('../libs/util.js');
11
+
12
+ const TranslateKeyMap = {
13
+ [model.AuditLogAction.APP_BACKUP]: {
14
+ description: "auditLog.backup.description"
15
+ },
16
+ [model.AuditLogAction.APP_RESTORE]: {
17
+ description: "auditLog.restore.description"
18
+ },
19
+ [model.AuditLogAction.APP_CONNECTED]: {
20
+ description: "auditLog.connected.description"
21
+ },
22
+ [model.AuditLogAction.APP_DISCONNECTED]: {
23
+ description: "auditLog.disconnected.description"
24
+ },
25
+ [model.AuditLogAction.APP_OBJECTS_SYNC]: {
26
+ description: "auditLog.objectsSync.description"
27
+ }
28
+ };
29
+ function useAuditLog({
30
+ endpoint,
31
+ size = 1,
32
+ status = model.AuditLogStatus.SUCCEEDED,
33
+ actionType = [model.AuditLogAction.APP_BACKUP, model.AuditLogAction.APP_RESTORE, model.AuditLogAction.APP_OBJECTS_SYNC],
34
+ deps = []
35
+ }) {
36
+ const { t, locale } = useLocale();
37
+ const { loading, data, error } = ahooks.useRequest(
38
+ async () => {
39
+ if (!isUrl(endpoint)) {
40
+ return void 0;
41
+ }
42
+ const { data: res } = await api.get(
43
+ ufo.joinURL(window.location.origin, "/.well-known/service/api/did-space/audit-log/list"),
44
+ {
45
+ timeout: 1e3 * 30,
46
+ params: {
47
+ endpoint,
48
+ size,
49
+ status,
50
+ actionType
51
+ }
52
+ }
53
+ );
54
+ return res.data.rows;
55
+ },
56
+ {
57
+ refreshDeps: [endpoint, size].concat(deps),
58
+ onError(err) {
59
+ console.error(err);
60
+ }
61
+ }
62
+ );
63
+ const rows = ahooks.useCreation(() => {
64
+ return data?.map((v) => {
65
+ const extraData = v.data;
66
+ if (v.actionType === model.AuditLogAction.APP_OBJECTS_SYNC) {
67
+ extraData.size = xbytes(extraData.size, { iec: true });
68
+ }
69
+ const result = {
70
+ ...v,
71
+ description: t(TranslateKeyMap[v.actionType].description, {
72
+ ...extraData,
73
+ relativeTime: util.formatRelativeTime(v.createdAt, locale)
74
+ })
75
+ };
76
+ return result;
77
+ });
78
+ }, [data, locale]);
79
+ return {
80
+ loading,
81
+ data: rows,
82
+ error
83
+ };
84
+ }
85
+
86
+ module.exports = useAuditLog;
@@ -2,7 +2,7 @@
2
2
 
3
3
  const isUrl = require('is-url');
4
4
  const ahooks = require('ahooks');
5
- const constants = require('../libs/constants.js');
5
+ const constants = require('@did-space/core/constants');
6
6
  const api = require('../libs/api.js');
7
7
 
8
8
  function useSpaceInfo({ endpoint, deps = [] }) {
package/dist/cjs/index.js CHANGED
@@ -37,15 +37,16 @@ exports.ReConnect = index$2.ReConnect;
37
37
  exports.SessionConnectTo = index$2.SessionConnectTo;
38
38
  exports.BaseConnectTo = index$3.BaseConnectTo;
39
39
  exports.AUTHORIZE = constants.AUTHORIZE;
40
- exports.SPACE_CONNECT_ERROR_CODE = constants.SPACE_CONNECT_ERROR_CODE;
41
40
  exports.copyGatewayPageUrl = constants.copyGatewayPageUrl;
42
41
  exports.fixCorsErrorPageUrl = constants.fixCorsErrorPageUrl;
43
42
  exports.isDebugMode = constants.isDebugMode;
44
43
  exports.classNames = util.classNames;
45
44
  exports.decryptSpaceGateway = util.decryptSpaceGateway;
46
45
  exports.extraDIDSpacesCoreUrl = util.extraDIDSpacesCoreUrl;
46
+ exports.formatRelativeTime = util.formatRelativeTime;
47
47
  exports.getSpaceDidFromEndpoint = util.getSpaceDidFromEndpoint;
48
48
  exports.getSpaceDidFromSpaceUrl = util.getSpaceDidFromSpaceUrl;
49
+ exports.getSpaceEndpointContext = util.getSpaceEndpointContext;
49
50
  exports.getSpaceGatewayUrlFromEndpoint = util.getSpaceGatewayUrlFromEndpoint;
50
51
  exports.getSpaceNftDisplayUrlFromEndpoint = util.getSpaceNftDisplayUrlFromEndpoint;
51
52
  exports.getSpaceUrlFromEndpoint = util.getSpaceUrlFromEndpoint;
@@ -1,21 +1,3 @@
1
- export declare const SPACE_CONNECT_ERROR_CODE: {
2
- /** 网络错误 */
3
- NETWORK_ERROR: number;
4
- /** 未授权 */
5
- UNAUTHORIZED: number;
6
- /** 订阅过期 */
7
- EXPIRED: number;
8
- /** 订阅逾期 */
9
- PAST_DUE: number;
10
- /** 订阅状态异常 */
11
- INVALID: number;
12
- /** 用量不足 */
13
- UNIT_LIMIT: number;
14
- /** 跨域限制 */
15
- CORS_BLOCKED: number;
16
- /** Space 版本不兼容 */
17
- INCOMPATIBLE: number;
18
- };
19
1
  export declare const AUTHORIZE: {
20
2
  DEFAULT_SCOPE: string;
21
3
  };
@@ -1,23 +1,5 @@
1
1
  'use strict';
2
2
 
3
- const SPACE_CONNECT_ERROR_CODE = {
4
- /** 网络错误 */
5
- NETWORK_ERROR: 1001,
6
- /** 未授权 */
7
- UNAUTHORIZED: 1002,
8
- /** 订阅过期 */
9
- EXPIRED: 1003,
10
- /** 订阅逾期 */
11
- PAST_DUE: 1004,
12
- /** 订阅状态异常 */
13
- INVALID: 1005,
14
- /** 用量不足 */
15
- UNIT_LIMIT: 1006,
16
- /** 跨域限制 */
17
- CORS_BLOCKED: 1007,
18
- /** Space 版本不兼容 */
19
- INCOMPATIBLE: 1008
20
- };
21
3
  const AUTHORIZE = {
22
4
  DEFAULT_SCOPE: "list:object read:object write:object"
23
5
  };
@@ -26,7 +8,6 @@ const fixCorsErrorPageUrl = "https://www.arcblock.io/docs/did-spaces/fixing-cors
26
8
  const isDebugMode = window.blocklet?.DEBUG_MODE === "true" || window.localStorage.getItem("debug") === "true";
27
9
 
28
10
  exports.AUTHORIZE = AUTHORIZE;
29
- exports.SPACE_CONNECT_ERROR_CODE = SPACE_CONNECT_ERROR_CODE;
30
11
  exports.copyGatewayPageUrl = copyGatewayPageUrl;
31
12
  exports.fixCorsErrorPageUrl = fixCorsErrorPageUrl;
32
13
  exports.isDebugMode = isDebugMode;
@@ -1,4 +1,5 @@
1
1
  import { type AxiosError } from 'axios';
2
+ import { type TDate } from 'timeago.js';
2
3
  type ClassStr = string | undefined | null;
3
4
  /** classObj -> className
4
5
  * examples:
@@ -11,7 +12,12 @@ type ClassStr = string | undefined | null;
11
12
  * classNames(['foo', 'bar']); // => 'foo bar'
12
13
  */
13
14
  export declare function classNames(...classes: (ClassStr | Record<string, boolean> | Array<ClassStr | Record<string, boolean>>)[]): string;
14
- export declare function getSpaceDidFromEndpoint(endpoint: string): string | undefined;
15
+ export declare function getSpaceEndpointContext(endpoint: string): {
16
+ baseUrl: string;
17
+ spaceDid: string;
18
+ appDid: string;
19
+ };
20
+ export declare function getSpaceDidFromEndpoint(endpoint: string): string;
15
21
  export declare function getSpaceUrlFromEndpoint(endpoint: string): string;
16
22
  export declare function getSpaceNftDisplayUrlFromEndpoint(endpoint: string): string;
17
23
  export declare function getSpaceGatewayUrlFromEndpoint(endpoint: string): string;
@@ -21,4 +27,5 @@ export declare function decryptSpaceGateway(response: Record<string, string>, de
21
27
  export declare function isCorsBlockedError(error: AxiosError): boolean;
22
28
  export declare function t(key: string, locale?: string, data?: Record<string, any>): string;
23
29
  export declare function t(key: string, data?: Record<string, any>): string;
30
+ export declare function formatRelativeTime(date: TDate, locale?: string): string;
24
31
  export {};
@@ -3,6 +3,7 @@
3
3
  const ufo = require('ufo');
4
4
  const isEmpty = require('lodash/isEmpty');
5
5
  const util = require('@arcblock/ux/lib/Locale/util');
6
+ const timeago_js = require('timeago.js');
6
7
  const index = require('../locales/index.js');
7
8
 
8
9
  function classNames(...classes) {
@@ -24,22 +25,28 @@ function classNames(...classes) {
24
25
  });
25
26
  return result.join(" ");
26
27
  }
27
- function getSpaceDidFromEndpoint(endpoint) {
28
+ function getSpaceEndpointContext(endpoint) {
29
+ const baseUrl = endpoint.replace(/\/api\/space\/.+/, "");
28
30
  const strArray = endpoint.replace(/\/$/, "").split("/");
29
31
  const spaceDid = strArray.at(-4);
32
+ const appDid = strArray.at(-2);
33
+ return {
34
+ baseUrl,
35
+ spaceDid,
36
+ appDid
37
+ };
38
+ }
39
+ function getSpaceDidFromEndpoint(endpoint) {
40
+ const { spaceDid } = getSpaceEndpointContext(endpoint);
30
41
  return spaceDid;
31
42
  }
32
43
  function getSpaceUrlFromEndpoint(endpoint) {
33
- const prefix = endpoint.replace(/\/api\/space\/.+/, "");
34
- const strArray = endpoint.replace(/\/$/, "").split("/");
35
- const spaceDid = strArray.at(-4);
36
- return ufo.joinURL(prefix, "space", spaceDid);
44
+ const { baseUrl, spaceDid } = getSpaceEndpointContext(endpoint);
45
+ return ufo.joinURL(baseUrl, "space", spaceDid);
37
46
  }
38
47
  function getSpaceNftDisplayUrlFromEndpoint(endpoint) {
39
- const prefix = endpoint.replace(/\/api\/space\/.+/, "");
40
- const strArray = endpoint.replace(/\/$/, "").split("/");
41
- const spaceDid = strArray.at(-4);
42
- return ufo.joinURL(prefix, `/api/space/nft/display?spaceDid=${spaceDid}`);
48
+ const { baseUrl, spaceDid } = getSpaceEndpointContext(endpoint);
49
+ return ufo.joinURL(baseUrl, `/api/space/nft/display?spaceDid=${spaceDid}`);
43
50
  }
44
51
  function getSpaceGatewayUrlFromEndpoint(endpoint) {
45
52
  const spaceUrl = getSpaceUrlFromEndpoint(endpoint);
@@ -81,12 +88,22 @@ function t(key, localeOrData, data = {}) {
81
88
  const finalData = typeof localeOrData === "object" ? localeOrData : data;
82
89
  return util.translate(index.translations, key, locale, "en", finalData);
83
90
  }
91
+ function formatRelativeTime(date, locale = "en") {
92
+ let _locale = locale;
93
+ if (locale === "zh")
94
+ _locale = "zh_CN";
95
+ if (locale === "en")
96
+ _locale = "en_US";
97
+ return timeago_js.format(date, _locale);
98
+ }
84
99
 
85
100
  exports.classNames = classNames;
86
101
  exports.decryptSpaceGateway = decryptSpaceGateway;
87
102
  exports.extraDIDSpacesCoreUrl = extraDIDSpacesCoreUrl;
103
+ exports.formatRelativeTime = formatRelativeTime;
88
104
  exports.getSpaceDidFromEndpoint = getSpaceDidFromEndpoint;
89
105
  exports.getSpaceDidFromSpaceUrl = getSpaceDidFromSpaceUrl;
106
+ exports.getSpaceEndpointContext = getSpaceEndpointContext;
90
107
  exports.getSpaceGatewayUrlFromEndpoint = getSpaceGatewayUrlFromEndpoint;
91
108
  exports.getSpaceNftDisplayUrlFromEndpoint = getSpaceNftDisplayUrlFromEndpoint;
92
109
  exports.getSpaceUrlFromEndpoint = getSpaceUrlFromEndpoint;
@@ -11,7 +11,8 @@ const en = flat.flatten({
11
11
  open: "Open",
12
12
  unknown: "Unknown",
13
13
  invalidUrl: "Invalid url: {url}",
14
- upgrade: "Upgrade"
14
+ upgrade: "Upgrade",
15
+ viewMore: "View More"
15
16
  },
16
17
  storage: {
17
18
  spaces: {
@@ -60,6 +61,23 @@ const en = flat.flatten({
60
61
  fixCorsError: "Fixing CORS Errors When Connecting to DID Spaces"
61
62
  }
62
63
  }
64
+ },
65
+ auditLog: {
66
+ backup: {
67
+ description: "A backup operation was completed {relativeTime}"
68
+ },
69
+ restore: {
70
+ description: "A restore operation was completed {relativeTime}"
71
+ },
72
+ connected: {
73
+ description: "A connection was established {relativeTime}"
74
+ },
75
+ disconnected: {
76
+ description: "The connection was disconnected {relativeTime}"
77
+ },
78
+ objectsSync: {
79
+ description: "{totalCount} objects ({size}) were synchronized {relativeTime}"
80
+ }
63
81
  }
64
82
  });
65
83
 
@@ -11,7 +11,8 @@ const zh = flat.flatten({
11
11
  open: "\u6253\u5F00",
12
12
  unknown: "\u672A\u77E5",
13
13
  invalidUrl: "\u65E0\u6548\u7684 url: {url}",
14
- upgrade: "\u5347\u7EA7"
14
+ upgrade: "\u5347\u7EA7",
15
+ viewMore: "\u67E5\u770B\u66F4\u591A"
15
16
  },
16
17
  storage: {
17
18
  spaces: {
@@ -60,6 +61,23 @@ const zh = flat.flatten({
60
61
  fixCorsError: "\u4FEE\u590D\u8FDE\u63A5 DID Spaces \u65F6\u7684 CORS \u9519\u8BEF"
61
62
  }
62
63
  }
64
+ },
65
+ auditLog: {
66
+ backup: {
67
+ description: "\u5728 {relativeTime} \u5B8C\u6210\u4E86\u5907\u4EFD\u64CD\u4F5C"
68
+ },
69
+ restore: {
70
+ description: "\u5728 {relativeTime} \u5B8C\u6210\u4E86\u8FD8\u539F\u64CD\u4F5C"
71
+ },
72
+ connected: {
73
+ description: "\u5728 {relativeTime} \u5B8C\u6210\u4E86\u8FDE\u63A5\u64CD\u4F5C"
74
+ },
75
+ disconnected: {
76
+ description: "\u5728 {relativeTime} \u65AD\u5F00\u4E86\u8FDE\u63A5"
77
+ },
78
+ objectsSync: {
79
+ description: "{totalCount} \u4E2A\u5BF9\u8C61\uFF08{size}\uFF09\u5728 {relativeTime} \u5B8C\u6210\u4E86\u540C\u6B65"
80
+ }
63
81
  }
64
82
  });
65
83
 
@@ -1,5 +1,5 @@
1
1
  'use strict';
2
2
 
3
- const version = "1.0.9";
3
+ const version = "1.0.10";
4
4
 
5
5
  exports.version = version;
@@ -0,0 +1,4 @@
1
+ export declare function AuditLogBar({ endpoint, deps }: {
2
+ endpoint: string;
3
+ deps?: any[];
4
+ }): import("react/jsx-runtime").JSX.Element | null;
@@ -0,0 +1,42 @@
1
+ import { jsx, jsxs } from 'react/jsx-runtime';
2
+ import { Skeleton, Box, Link } from '@mui/material';
3
+ import OpenInNewIcon from '@mui/icons-material/OpenInNew';
4
+ import { joinURL } from 'ufo';
5
+ import { getSpaceEndpointContext } from '../../libs/util.js';
6
+ import useLocale from '../../hooks/use-locale.js';
7
+ import useAuditLog from '../../hooks/use-audit-log.js';
8
+
9
+ function AuditLogBar({ endpoint, deps = [] }) {
10
+ const { t } = useLocale();
11
+ const { loading, data: auditLogs } = useAuditLog({
12
+ endpoint,
13
+ deps
14
+ });
15
+ const { baseUrl, spaceDid, appDid } = getSpaceEndpointContext(endpoint);
16
+ const latestLog = auditLogs?.[0];
17
+ if (loading) {
18
+ return /* @__PURE__ */ jsx(Skeleton, { variant: "text", sx: { width: "100%", fontSize: "1rem" } });
19
+ }
20
+ if (!latestLog) {
21
+ return null;
22
+ }
23
+ return /* @__PURE__ */ jsxs(Box, { sx: { ml: "4px", fontSize: "14px" }, children: [
24
+ latestLog.description,
25
+ /* @__PURE__ */ jsxs(
26
+ Link,
27
+ {
28
+ href: joinURL(baseUrl, `space/${spaceDid}/apps/${appDid}/auditLogs`),
29
+ target: "_blank",
30
+ underline: "hover",
31
+ sx: { color: "primary.main", ml: 1, display: "inline-flex", alignItems: "center" },
32
+ children: [
33
+ t("common.viewMore"),
34
+ " ",
35
+ /* @__PURE__ */ jsx(OpenInNewIcon, { sx: { fontSize: "14px", marginLeft: "4px", verticalAlign: "-2px" } })
36
+ ]
37
+ }
38
+ )
39
+ ] });
40
+ }
41
+
42
+ export { AuditLogBar };
@@ -1,21 +1,26 @@
1
1
  import { BoxProps } from '@mui/material';
2
2
  import { DIDSpaceGateway, DIDSpaceStatus } from '../../types';
3
- export type DIDSpaceConnectionAction = React.ReactNode | ((props: {
3
+ export interface DIDSpaceConnectionContext {
4
4
  spaceGateway: DIDSpaceGateway;
5
5
  spaceStatus: DIDSpaceStatus;
6
6
  errorCode: number;
7
7
  selected: boolean;
8
8
  compat: boolean;
9
9
  refresh: () => void;
10
+ }
11
+ export type DIDSpaceConnectionAction = React.ReactNode | ((props: DIDSpaceConnectionContext) => React.ReactNode);
12
+ export type DIDSpaceConnectionFooter = false | true | React.ReactNode | ((props: DIDSpaceConnectionContext & {
13
+ originalFooter: React.ReactNode;
10
14
  }) => React.ReactNode);
11
15
  export interface DIDSpaceConnectionProps extends BoxProps {
12
16
  endpoint: string;
13
17
  selected?: boolean;
14
18
  compat?: boolean;
15
19
  action?: DIDSpaceConnectionAction;
20
+ footer?: DIDSpaceConnectionFooter;
16
21
  deps?: any[];
17
22
  }
18
- export declare function DIDSpaceConnection({ endpoint, selected, compat, action, className, deps, ...rest }: DIDSpaceConnectionProps): import("react/jsx-runtime").JSX.Element;
23
+ export declare function DIDSpaceConnection({ endpoint, selected, compat, action, footer, className, deps, ...rest }: DIDSpaceConnectionProps): import("react/jsx-runtime").JSX.Element;
19
24
  export type SpaceCardProps = DIDSpaceConnectionProps;
20
25
  export declare const SpaceCard: typeof DIDSpaceConnection;
21
26
  export type Action = DIDSpaceConnectionAction;
@@ -5,12 +5,13 @@ import { Box, Stack, Tooltip, Link, Skeleton } from '@mui/material';
5
5
  import OpenInNewIcon from '@mui/icons-material/OpenInNew';
6
6
  import DidAddress from '@arcblock/ux/lib/DID';
7
7
  import { styled } from '@arcblock/ux/lib/Theme';
8
- import { useState, useRef, useMemo, useEffect } from 'react';
8
+ import { useState, useMemo, useRef, useEffect } from 'react';
9
9
  import { AxiosError } from 'axios';
10
10
  import { useCreation } from 'ahooks';
11
11
  import semver from 'semver';
12
+ import { SPACE_CONNECT_ERROR_CODE } from '@did-space/core/constants';
12
13
  import { version } from '../../package.json.js';
13
- import { SPACE_CONNECT_ERROR_CODE, fixCorsErrorPageUrl } from '../../libs/constants.js';
14
+ import { fixCorsErrorPageUrl } from '../../libs/constants.js';
14
15
  import useMobile from '../../hooks/use-mobile.js';
15
16
  import useSpaceInfo from '../../hooks/use-space-info.js';
16
17
  import useLocale from '../../hooks/use-locale.js';
@@ -18,6 +19,7 @@ import { getSpaceDidFromEndpoint, getSpaceUrlFromEndpoint, getSpaceGatewayUrlFro
18
19
  import { DIDSpaceStatus } from '../../types/index.js';
19
20
  import '../../icons/index.js';
20
21
  import { DIDSpaceNFTPreview } from '../preview-space-nft/index.js';
22
+ import { AuditLogBar } from './audit-log-bar.js';
21
23
  import SvgSpaceConnected from '../../icons/space-connected.svg.js';
22
24
  import SvgSpaceDisconnect from '../../icons/space-disconnect.svg.js';
23
25
  import SvgSpaceConnectError from '../../icons/space-connect-error.svg.js';
@@ -155,8 +157,9 @@ function DIDSpaceConnection({
155
157
  selected = false,
156
158
  compat,
157
159
  action,
160
+ footer = false,
158
161
  className,
159
- deps,
162
+ deps = [],
160
163
  ...rest
161
164
  }) {
162
165
  const isMobile = useMobile();
@@ -169,6 +172,7 @@ function DIDSpaceConnection({
169
172
  const spaceUrl = getSpaceUrlFromEndpoint(endpoint);
170
173
  const gatewayUrl = getSpaceGatewayUrlFromEndpoint(endpoint);
171
174
  const [refreshSpaceInfo, setRefreshSpaceInfo] = useState(false);
175
+ const refreshDeps = useMemo(() => [refreshSpaceInfo].concat(deps), [refreshSpaceInfo, deps]);
172
176
  const refresh = () => setRefreshSpaceInfo((p) => !p);
173
177
  const {
174
178
  data: spaceInfo,
@@ -176,7 +180,7 @@ function DIDSpaceConnection({
176
180
  loading
177
181
  } = useSpaceInfo({
178
182
  endpoint,
179
- deps: [refreshSpaceInfo].concat(deps ?? [])
183
+ deps: refreshDeps
180
184
  });
181
185
  let spaceName = "";
182
186
  const isAvailable = spaceInfo?.isAvailable ?? false;
@@ -210,27 +214,41 @@ function DIDSpaceConnection({
210
214
  spaceStatus.current = DIDSpaceStatus.CONNECTED;
211
215
  }
212
216
  }
217
+ const context = {
218
+ spaceGateway: {
219
+ did: spaceDid,
220
+ name: spaceInfo?.name ?? "",
221
+ url: gatewayUrl,
222
+ endpoint,
223
+ ownerDid: spaceInfo?.ownerDid ?? ""
224
+ },
225
+ spaceStatus: spaceStatus.current,
226
+ errorCode,
227
+ selected,
228
+ compat: isCompact,
229
+ refresh
230
+ };
213
231
  const renderAction = () => {
214
232
  if (loading)
215
233
  return null;
216
234
  if (typeof action === "function") {
217
- return action({
218
- spaceGateway: {
219
- did: spaceDid,
220
- name: spaceInfo?.name ?? "",
221
- url: gatewayUrl,
222
- endpoint,
223
- ownerDid: spaceInfo?.ownerDid ?? ""
224
- },
225
- spaceStatus: spaceStatus.current,
226
- errorCode,
227
- selected,
228
- compat: isCompact,
229
- refresh
230
- });
235
+ return action(context);
231
236
  }
232
237
  return action;
233
238
  };
239
+ const originalFooter = /* @__PURE__ */ jsx(AuditLogBar, { endpoint, deps: refreshDeps });
240
+ const renderFooter = () => {
241
+ if (typeof footer === "function") {
242
+ return footer({
243
+ ...context,
244
+ originalFooter
245
+ });
246
+ }
247
+ if (footer === true) {
248
+ return originalFooter;
249
+ }
250
+ return null;
251
+ };
234
252
  return /* @__PURE__ */ jsxs(
235
253
  BoxContainer,
236
254
  {
@@ -240,6 +258,22 @@ function DIDSpaceConnection({
240
258
  }),
241
259
  ...rest,
242
260
  children: [
261
+ isCompact && /* @__PURE__ */ jsxs(Box, { display: "flex", alignItems: "center", marginBottom: 1, children: [
262
+ selected && /* @__PURE__ */ jsx(
263
+ Status,
264
+ {
265
+ spaceUrl,
266
+ status: spaceStatus.current,
267
+ errorCode,
268
+ refresh,
269
+ spaceInfo,
270
+ flexShrink: 0,
271
+ sx: { ml: "4px" }
272
+ }
273
+ ),
274
+ /* @__PURE__ */ jsx(Box, { flex: 1 }),
275
+ renderAction()
276
+ ] }),
243
277
  /* @__PURE__ */ jsxs(Box, { display: "flex", alignItems: "center", children: [
244
278
  /* @__PURE__ */ jsx(DIDSpaceNFTPreview, { src: getSpaceNftDisplayUrlFromEndpoint(endpoint), width: "72px", height: "72px" }),
245
279
  /* @__PURE__ */ jsxs(Stack, { ml: 2, flex: 1, spacing: 1, minWidth: 0, children: [
@@ -275,21 +309,7 @@ function DIDSpaceConnection({
275
309
  ] }),
276
310
  !isCompact && renderAction()
277
311
  ] }),
278
- isCompact && /* @__PURE__ */ jsxs(Box, { display: "flex", alignItems: "center", marginTop: 0.5, children: [
279
- selected && /* @__PURE__ */ jsx(
280
- Status,
281
- {
282
- spaceUrl,
283
- status: spaceStatus.current,
284
- errorCode,
285
- refresh,
286
- spaceInfo,
287
- flexShrink: 0
288
- }
289
- ),
290
- /* @__PURE__ */ jsx(Box, { flex: 1 }),
291
- renderAction()
292
- ] })
312
+ renderFooter()
293
313
  ]
294
314
  }
295
315
  );
@@ -0,0 +1,16 @@
1
+ import type { AuditLogModel } from '@did-space/core';
2
+ import { AuditLogAction, AuditLogStatus } from '@did-space/core/model';
3
+ export interface AuditLogModelExtra extends AuditLogModel {
4
+ description: React.ReactNode;
5
+ }
6
+ export default function useAuditLog({ endpoint, size, status, actionType, deps, }: {
7
+ endpoint: string;
8
+ size?: number;
9
+ status?: AuditLogStatus | AuditLogStatus[];
10
+ actionType?: AuditLogAction | AuditLogAction[];
11
+ deps?: any[];
12
+ }): {
13
+ loading: boolean;
14
+ data: AuditLogModelExtra[] | undefined;
15
+ error: Error | undefined;
16
+ };
@@ -0,0 +1,84 @@
1
+ import isUrl from 'is-url';
2
+ import { useRequest, useCreation } from 'ahooks';
3
+ import { AuditLogAction, AuditLogStatus } from '@did-space/core/model';
4
+ import { joinURL } from 'ufo';
5
+ import xbytes from 'xbytes';
6
+ import api from '../libs/api.js';
7
+ import useLocale from './use-locale.js';
8
+ import { formatRelativeTime } from '../libs/util.js';
9
+
10
+ const TranslateKeyMap = {
11
+ [AuditLogAction.APP_BACKUP]: {
12
+ description: "auditLog.backup.description"
13
+ },
14
+ [AuditLogAction.APP_RESTORE]: {
15
+ description: "auditLog.restore.description"
16
+ },
17
+ [AuditLogAction.APP_CONNECTED]: {
18
+ description: "auditLog.connected.description"
19
+ },
20
+ [AuditLogAction.APP_DISCONNECTED]: {
21
+ description: "auditLog.disconnected.description"
22
+ },
23
+ [AuditLogAction.APP_OBJECTS_SYNC]: {
24
+ description: "auditLog.objectsSync.description"
25
+ }
26
+ };
27
+ function useAuditLog({
28
+ endpoint,
29
+ size = 1,
30
+ status = AuditLogStatus.SUCCEEDED,
31
+ actionType = [AuditLogAction.APP_BACKUP, AuditLogAction.APP_RESTORE, AuditLogAction.APP_OBJECTS_SYNC],
32
+ deps = []
33
+ }) {
34
+ const { t, locale } = useLocale();
35
+ const { loading, data, error } = useRequest(
36
+ async () => {
37
+ if (!isUrl(endpoint)) {
38
+ return void 0;
39
+ }
40
+ const { data: res } = await api.get(
41
+ joinURL(window.location.origin, "/.well-known/service/api/did-space/audit-log/list"),
42
+ {
43
+ timeout: 1e3 * 30,
44
+ params: {
45
+ endpoint,
46
+ size,
47
+ status,
48
+ actionType
49
+ }
50
+ }
51
+ );
52
+ return res.data.rows;
53
+ },
54
+ {
55
+ refreshDeps: [endpoint, size].concat(deps),
56
+ onError(err) {
57
+ console.error(err);
58
+ }
59
+ }
60
+ );
61
+ const rows = useCreation(() => {
62
+ return data?.map((v) => {
63
+ const extraData = v.data;
64
+ if (v.actionType === AuditLogAction.APP_OBJECTS_SYNC) {
65
+ extraData.size = xbytes(extraData.size, { iec: true });
66
+ }
67
+ const result = {
68
+ ...v,
69
+ description: t(TranslateKeyMap[v.actionType].description, {
70
+ ...extraData,
71
+ relativeTime: formatRelativeTime(v.createdAt, locale)
72
+ })
73
+ };
74
+ return result;
75
+ });
76
+ }, [data, locale]);
77
+ return {
78
+ loading,
79
+ data: rows,
80
+ error
81
+ };
82
+ }
83
+
84
+ export { useAuditLog as default };
@@ -1,6 +1,6 @@
1
1
  import isUrl from 'is-url';
2
2
  import { useRequest } from 'ahooks';
3
- import { SPACE_CONNECT_ERROR_CODE } from '../libs/constants.js';
3
+ import { SPACE_CONNECT_ERROR_CODE } from '@did-space/core/constants';
4
4
  import api from '../libs/api.js';
5
5
 
6
6
  function useSpaceInfo({ endpoint, deps = [] }) {
package/dist/es/index.js CHANGED
@@ -6,8 +6,8 @@ export { DIDSpaceConnection, SpaceCard } from './components/space-card/index.js'
6
6
  export { DIDSpaceNFTPreview, PreviewSpaceNft } from './components/preview-space-nft/index.js';
7
7
  export { AuthConnectTo, DIDSpaceConnect, ReConnect, SessionConnectTo } from './components/auth-connect-to/index.js';
8
8
  export { BaseConnectTo } from './components/base-connect-to/index.js';
9
- export { AUTHORIZE, SPACE_CONNECT_ERROR_CODE, copyGatewayPageUrl, fixCorsErrorPageUrl, isDebugMode } from './libs/constants.js';
10
- export { classNames, decryptSpaceGateway, extraDIDSpacesCoreUrl, getSpaceDidFromEndpoint, getSpaceDidFromSpaceUrl, getSpaceGatewayUrlFromEndpoint, getSpaceNftDisplayUrlFromEndpoint, getSpaceUrlFromEndpoint, isCorsBlockedError, t } from './libs/util.js';
9
+ export { AUTHORIZE, copyGatewayPageUrl, fixCorsErrorPageUrl, isDebugMode } from './libs/constants.js';
10
+ export { classNames, decryptSpaceGateway, extraDIDSpacesCoreUrl, formatRelativeTime, getSpaceDidFromEndpoint, getSpaceDidFromSpaceUrl, getSpaceEndpointContext, getSpaceGatewayUrlFromEndpoint, getSpaceNftDisplayUrlFromEndpoint, getSpaceUrlFromEndpoint, isCorsBlockedError, t } from './libs/util.js';
11
11
  export { getSpaceGatewayUrl, verifySpaceUrl } from './libs/gateway.js';
12
12
  export { translations } from './locales/index.js';
13
13
  export { DIDSpaceStatus, SpaceStatus } from './types/index.js';
@@ -1,21 +1,3 @@
1
- export declare const SPACE_CONNECT_ERROR_CODE: {
2
- /** 网络错误 */
3
- NETWORK_ERROR: number;
4
- /** 未授权 */
5
- UNAUTHORIZED: number;
6
- /** 订阅过期 */
7
- EXPIRED: number;
8
- /** 订阅逾期 */
9
- PAST_DUE: number;
10
- /** 订阅状态异常 */
11
- INVALID: number;
12
- /** 用量不足 */
13
- UNIT_LIMIT: number;
14
- /** 跨域限制 */
15
- CORS_BLOCKED: number;
16
- /** Space 版本不兼容 */
17
- INCOMPATIBLE: number;
18
- };
19
1
  export declare const AUTHORIZE: {
20
2
  DEFAULT_SCOPE: string;
21
3
  };
@@ -1,21 +1,3 @@
1
- const SPACE_CONNECT_ERROR_CODE = {
2
- /** 网络错误 */
3
- NETWORK_ERROR: 1001,
4
- /** 未授权 */
5
- UNAUTHORIZED: 1002,
6
- /** 订阅过期 */
7
- EXPIRED: 1003,
8
- /** 订阅逾期 */
9
- PAST_DUE: 1004,
10
- /** 订阅状态异常 */
11
- INVALID: 1005,
12
- /** 用量不足 */
13
- UNIT_LIMIT: 1006,
14
- /** 跨域限制 */
15
- CORS_BLOCKED: 1007,
16
- /** Space 版本不兼容 */
17
- INCOMPATIBLE: 1008
18
- };
19
1
  const AUTHORIZE = {
20
2
  DEFAULT_SCOPE: "list:object read:object write:object"
21
3
  };
@@ -23,4 +5,4 @@ const copyGatewayPageUrl = "https://www.arcblock.io/content/docs/did-spaces/how-
23
5
  const fixCorsErrorPageUrl = "https://www.arcblock.io/docs/did-spaces/fixing-cors-errors-when-connecting-to-space";
24
6
  const isDebugMode = window.blocklet?.DEBUG_MODE === "true" || window.localStorage.getItem("debug") === "true";
25
7
 
26
- export { AUTHORIZE, SPACE_CONNECT_ERROR_CODE, copyGatewayPageUrl, fixCorsErrorPageUrl, isDebugMode };
8
+ export { AUTHORIZE, copyGatewayPageUrl, fixCorsErrorPageUrl, isDebugMode };
@@ -1,4 +1,5 @@
1
1
  import { type AxiosError } from 'axios';
2
+ import { type TDate } from 'timeago.js';
2
3
  type ClassStr = string | undefined | null;
3
4
  /** classObj -> className
4
5
  * examples:
@@ -11,7 +12,12 @@ type ClassStr = string | undefined | null;
11
12
  * classNames(['foo', 'bar']); // => 'foo bar'
12
13
  */
13
14
  export declare function classNames(...classes: (ClassStr | Record<string, boolean> | Array<ClassStr | Record<string, boolean>>)[]): string;
14
- export declare function getSpaceDidFromEndpoint(endpoint: string): string | undefined;
15
+ export declare function getSpaceEndpointContext(endpoint: string): {
16
+ baseUrl: string;
17
+ spaceDid: string;
18
+ appDid: string;
19
+ };
20
+ export declare function getSpaceDidFromEndpoint(endpoint: string): string;
15
21
  export declare function getSpaceUrlFromEndpoint(endpoint: string): string;
16
22
  export declare function getSpaceNftDisplayUrlFromEndpoint(endpoint: string): string;
17
23
  export declare function getSpaceGatewayUrlFromEndpoint(endpoint: string): string;
@@ -21,4 +27,5 @@ export declare function decryptSpaceGateway(response: Record<string, string>, de
21
27
  export declare function isCorsBlockedError(error: AxiosError): boolean;
22
28
  export declare function t(key: string, locale?: string, data?: Record<string, any>): string;
23
29
  export declare function t(key: string, data?: Record<string, any>): string;
30
+ export declare function formatRelativeTime(date: TDate, locale?: string): string;
24
31
  export {};
@@ -1,6 +1,7 @@
1
1
  import { joinURL } from 'ufo';
2
2
  import isEmpty from 'lodash/isEmpty';
3
3
  import { translate } from '@arcblock/ux/lib/Locale/util';
4
+ import { format } from 'timeago.js';
4
5
  import { translations } from '../locales/index.js';
5
6
 
6
7
  function classNames(...classes) {
@@ -22,22 +23,28 @@ function classNames(...classes) {
22
23
  });
23
24
  return result.join(" ");
24
25
  }
25
- function getSpaceDidFromEndpoint(endpoint) {
26
+ function getSpaceEndpointContext(endpoint) {
27
+ const baseUrl = endpoint.replace(/\/api\/space\/.+/, "");
26
28
  const strArray = endpoint.replace(/\/$/, "").split("/");
27
29
  const spaceDid = strArray.at(-4);
30
+ const appDid = strArray.at(-2);
31
+ return {
32
+ baseUrl,
33
+ spaceDid,
34
+ appDid
35
+ };
36
+ }
37
+ function getSpaceDidFromEndpoint(endpoint) {
38
+ const { spaceDid } = getSpaceEndpointContext(endpoint);
28
39
  return spaceDid;
29
40
  }
30
41
  function getSpaceUrlFromEndpoint(endpoint) {
31
- const prefix = endpoint.replace(/\/api\/space\/.+/, "");
32
- const strArray = endpoint.replace(/\/$/, "").split("/");
33
- const spaceDid = strArray.at(-4);
34
- return joinURL(prefix, "space", spaceDid);
42
+ const { baseUrl, spaceDid } = getSpaceEndpointContext(endpoint);
43
+ return joinURL(baseUrl, "space", spaceDid);
35
44
  }
36
45
  function getSpaceNftDisplayUrlFromEndpoint(endpoint) {
37
- const prefix = endpoint.replace(/\/api\/space\/.+/, "");
38
- const strArray = endpoint.replace(/\/$/, "").split("/");
39
- const spaceDid = strArray.at(-4);
40
- return joinURL(prefix, `/api/space/nft/display?spaceDid=${spaceDid}`);
46
+ const { baseUrl, spaceDid } = getSpaceEndpointContext(endpoint);
47
+ return joinURL(baseUrl, `/api/space/nft/display?spaceDid=${spaceDid}`);
41
48
  }
42
49
  function getSpaceGatewayUrlFromEndpoint(endpoint) {
43
50
  const spaceUrl = getSpaceUrlFromEndpoint(endpoint);
@@ -79,5 +86,13 @@ function t(key, localeOrData, data = {}) {
79
86
  const finalData = typeof localeOrData === "object" ? localeOrData : data;
80
87
  return translate(translations, key, locale, "en", finalData);
81
88
  }
89
+ function formatRelativeTime(date, locale = "en") {
90
+ let _locale = locale;
91
+ if (locale === "zh")
92
+ _locale = "zh_CN";
93
+ if (locale === "en")
94
+ _locale = "en_US";
95
+ return format(date, _locale);
96
+ }
82
97
 
83
- export { classNames, decryptSpaceGateway, extraDIDSpacesCoreUrl, getSpaceDidFromEndpoint, getSpaceDidFromSpaceUrl, getSpaceGatewayUrlFromEndpoint, getSpaceNftDisplayUrlFromEndpoint, getSpaceUrlFromEndpoint, isCorsBlockedError, t };
98
+ export { classNames, decryptSpaceGateway, extraDIDSpacesCoreUrl, formatRelativeTime, getSpaceDidFromEndpoint, getSpaceDidFromSpaceUrl, getSpaceEndpointContext, getSpaceGatewayUrlFromEndpoint, getSpaceNftDisplayUrlFromEndpoint, getSpaceUrlFromEndpoint, isCorsBlockedError, t };
@@ -9,7 +9,8 @@ const en = flatten({
9
9
  open: "Open",
10
10
  unknown: "Unknown",
11
11
  invalidUrl: "Invalid url: {url}",
12
- upgrade: "Upgrade"
12
+ upgrade: "Upgrade",
13
+ viewMore: "View More"
13
14
  },
14
15
  storage: {
15
16
  spaces: {
@@ -58,6 +59,23 @@ const en = flatten({
58
59
  fixCorsError: "Fixing CORS Errors When Connecting to DID Spaces"
59
60
  }
60
61
  }
62
+ },
63
+ auditLog: {
64
+ backup: {
65
+ description: "A backup operation was completed {relativeTime}"
66
+ },
67
+ restore: {
68
+ description: "A restore operation was completed {relativeTime}"
69
+ },
70
+ connected: {
71
+ description: "A connection was established {relativeTime}"
72
+ },
73
+ disconnected: {
74
+ description: "The connection was disconnected {relativeTime}"
75
+ },
76
+ objectsSync: {
77
+ description: "{totalCount} objects ({size}) were synchronized {relativeTime}"
78
+ }
61
79
  }
62
80
  });
63
81
 
@@ -9,7 +9,8 @@ const zh = flatten({
9
9
  open: "\u6253\u5F00",
10
10
  unknown: "\u672A\u77E5",
11
11
  invalidUrl: "\u65E0\u6548\u7684 url: {url}",
12
- upgrade: "\u5347\u7EA7"
12
+ upgrade: "\u5347\u7EA7",
13
+ viewMore: "\u67E5\u770B\u66F4\u591A"
13
14
  },
14
15
  storage: {
15
16
  spaces: {
@@ -58,6 +59,23 @@ const zh = flatten({
58
59
  fixCorsError: "\u4FEE\u590D\u8FDE\u63A5 DID Spaces \u65F6\u7684 CORS \u9519\u8BEF"
59
60
  }
60
61
  }
62
+ },
63
+ auditLog: {
64
+ backup: {
65
+ description: "\u5728 {relativeTime} \u5B8C\u6210\u4E86\u5907\u4EFD\u64CD\u4F5C"
66
+ },
67
+ restore: {
68
+ description: "\u5728 {relativeTime} \u5B8C\u6210\u4E86\u8FD8\u539F\u64CD\u4F5C"
69
+ },
70
+ connected: {
71
+ description: "\u5728 {relativeTime} \u5B8C\u6210\u4E86\u8FDE\u63A5\u64CD\u4F5C"
72
+ },
73
+ disconnected: {
74
+ description: "\u5728 {relativeTime} \u65AD\u5F00\u4E86\u8FDE\u63A5"
75
+ },
76
+ objectsSync: {
77
+ description: "{totalCount} \u4E2A\u5BF9\u8C61\uFF08{size}\uFF09\u5728 {relativeTime} \u5B8C\u6210\u4E86\u540C\u6B65"
78
+ }
61
79
  }
62
80
  });
63
81
 
@@ -1,3 +1,3 @@
1
- const version = "1.0.9";
1
+ const version = "1.0.10";
2
2
 
3
3
  export { version };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blocklet/did-space-react",
3
- "version": "1.0.9",
3
+ "version": "1.0.10",
4
4
  "description": "Reusable react components for did space",
5
5
  "keywords": [
6
6
  "react",
@@ -81,6 +81,7 @@
81
81
  },
82
82
  "dependencies": {
83
83
  "@arcblock/did": "^1.19.6",
84
+ "@did-space/core": "^1.0.10",
84
85
  "@mui/icons-material": "^5.16.14",
85
86
  "@mui/lab": "^5.0.0-alpha.175",
86
87
  "@mui/material": "^5.16.14",
@@ -93,7 +94,9 @@
93
94
  "p-wait-for": "3",
94
95
  "react-error-boundary": "^4.1.2",
95
96
  "semver": "^7.6.3",
96
- "ufo": "^1.5.4"
97
+ "timeago.js": "^4.0.2",
98
+ "ufo": "^1.5.4",
99
+ "xbytes": "^1.9.1"
97
100
  },
98
101
  "peerDependencies": {
99
102
  "@arcblock/did-connect": "^2.11.29",
@@ -147,5 +150,5 @@
147
150
  "vite-plugin-node-polyfills": "^0.22.0",
148
151
  "vitest": "^3.0.2"
149
152
  },
150
- "gitHead": "f9d7a27fdaa977cf9666b6288ed6cf890ad03e73"
153
+ "gitHead": "bf1e53904f28b786d728648fe0a7d828478551de"
151
154
  }