@arcblock/ux 2.13.28 → 2.13.30

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.
@@ -1,119 +1,113 @@
1
1
  import { Box } from '@mui/material';
2
2
  import type { SxProps } from '@mui/material';
3
- import React, { memo, useEffect, useId, useRef } from 'react';
3
+ import { forwardRef, memo, useEffect, useId, useImperativeHandle, useRef } from 'react';
4
4
  import { withQuery } from 'ufo';
5
5
  import { useMemoizedFn, useReactive } from 'ahooks';
6
+ import noop from 'lodash/noop';
6
7
 
7
8
  import { mergeSx } from '../Util/style';
8
9
  import { callIframe, getCallbackAction } from '../Util/iframe';
9
10
  import { Locale } from '../type';
10
- import NeedStorageAccessApiDialog from './need-storage-access-api-dialog';
11
- import { DIDConnectContainer } from '../DIDConnect';
12
11
 
13
- const SharedBridge = memo(function SharedBridge({
14
- src,
15
- onClick,
16
- onLoad,
17
- sx,
18
- iframeRef,
19
- locale = 'en',
20
- ...rest
21
- }: {
22
- src: string;
23
- onClick: (data: { action: string; value: boolean; visitorId?: string }) => void;
24
- onLoad: () => void;
25
- sx?: SxProps;
26
- iframeRef?: React.RefObject<HTMLIFrameElement>;
27
- locale?: Locale;
28
- }) {
29
- const _iframeRef = useRef<HTMLIFrameElement>(null);
30
- const refId = useId();
31
- const dataId = `shared-bridge_${refId}`;
32
- const currentState = useReactive<{
33
- hasInited?: boolean;
34
- open: boolean;
35
- hasStorageAccess: boolean;
36
- origin: string;
37
- host: string;
38
- }>({
39
- hasInited: undefined,
40
- open: false,
41
- hasStorageAccess: false,
42
- get origin() {
43
- try {
44
- return new URL(src).origin;
45
- } catch (error) {
46
- return src;
47
- }
48
- },
49
- get host() {
50
- try {
51
- return new URL(src).host;
52
- } catch (error) {
53
- return src;
54
- }
12
+ const SharedBridge = forwardRef(
13
+ (
14
+ {
15
+ src,
16
+ onClick,
17
+ onLoad = noop,
18
+ sx,
19
+ locale = 'en',
20
+ ...rest
21
+ }: {
22
+ src: string;
23
+ onClick: (data: { action: string; value: boolean; visitorId?: string; error?: Error }) => void;
24
+ onLoad?: () => void;
25
+ sx?: SxProps;
26
+ locale?: Locale;
55
27
  },
56
- });
28
+ ref
29
+ ) => {
30
+ const targetIframeRef = useRef<HTMLIFrameElement>(null);
31
+ const refId = useId();
32
+ const dataId = `shared-bridge_${refId}`;
33
+ const currentState = useReactive<{
34
+ hasInited?: boolean;
35
+ open: boolean;
36
+ hasStorageAccess: boolean;
37
+ origin: string;
38
+ host: string;
39
+ containerEl: HTMLDivElement | null;
40
+ }>({
41
+ hasInited: undefined,
42
+ open: false,
43
+ hasStorageAccess: false,
44
+ containerEl: null,
45
+ get origin() {
46
+ try {
47
+ return new URL(src).origin;
48
+ } catch (error) {
49
+ return src;
50
+ }
51
+ },
52
+ get host() {
53
+ try {
54
+ return new URL(src).host;
55
+ } catch (error) {
56
+ return src;
57
+ }
58
+ },
59
+ });
57
60
 
58
- const targetIframeRef = iframeRef ?? _iframeRef;
61
+ useEffect(() => {
62
+ async function handleMessage(event: MessageEvent) {
63
+ const { data } = event;
64
+ if (data.action === getCallbackAction(dataId, 'requestStorageAccess')) {
65
+ currentState.open = false;
59
66
 
60
- useEffect(() => {
61
- async function handleMessage(event: MessageEvent) {
62
- const { data } = event;
63
- if (data.action === getCallbackAction(dataId, 'requestStorageAccess')) {
64
- currentState.open = false;
67
+ if (!data.value) {
68
+ onClick(data);
69
+ return;
70
+ }
65
71
 
66
- if (!data.value) {
67
- onClick(data);
68
- return;
72
+ const { value: visitorId } = await callIframe(targetIframeRef.current as HTMLIFrameElement, 'getVisitorId');
73
+ onClick({ ...data, visitorId });
74
+ } else if (data.action === getCallbackAction(dataId, 'preRequestStorageAccess')) {
75
+ currentState.open = true;
69
76
  }
70
-
71
- const { value: visitorId } = await callIframe(targetIframeRef.current as HTMLIFrameElement, 'getVisitorId');
72
- onClick({ ...data, visitorId });
73
- } else if (data.action === getCallbackAction(dataId, 'preRequestStorageAccess')) {
74
- currentState.open = true;
75
77
  }
76
- }
77
78
 
78
- window.addEventListener('message', handleMessage);
79
- return () => {
80
- window.removeEventListener('message', handleMessage);
81
- };
82
- // eslint-disable-next-line react-hooks/exhaustive-deps
83
- }, [onClick, dataId, targetIframeRef?.current]);
79
+ window.addEventListener('message', handleMessage);
80
+ return () => {
81
+ window.removeEventListener('message', handleMessage);
82
+ };
83
+ // eslint-disable-next-line react-hooks/exhaustive-deps
84
+ }, [onClick, dataId, targetIframeRef?.current]);
84
85
 
85
- const handleLoad = useMemoizedFn(() => {
86
- callIframe(targetIframeRef.current as HTMLIFrameElement, 'hasStorageAccess').then(({ value }) => {
87
- currentState.hasStorageAccess = value;
88
- currentState.hasInited = true;
86
+ const handleLoad = useMemoizedFn(() => {
87
+ callIframe(targetIframeRef.current as HTMLIFrameElement, 'hasStorageAccess').then(({ value }) => {
88
+ currentState.hasStorageAccess = value;
89
+ currentState.hasInited = true;
90
+ });
91
+ // HACK: 如果目标 bridge 1s 内没有初始化,则认为目标 bridge 不兼容,不进行后续内容的加载
92
+ setTimeout(() => {
93
+ if (currentState.hasInited === undefined) {
94
+ currentState.hasInited = false;
95
+ }
96
+ }, 1000);
97
+ onLoad();
89
98
  });
90
- setTimeout(() => {
91
- if (currentState.hasInited === undefined) {
92
- currentState.hasInited = false;
93
- }
94
- }, 1000);
95
- onLoad();
96
- });
97
-
98
- if (currentState.hasInited === false) {
99
- return null;
100
- }
101
99
 
102
- if (currentState.hasStorageAccess) {
103
- return null;
104
- }
105
-
106
- return (
107
- <>
108
- <DIDConnectContainer popup hideCloseButton open={currentState.open}>
109
- <NeedStorageAccessApiDialog locale={locale} origin={currentState.origin} host={currentState.host} />
110
- </DIDConnectContainer>
100
+ useImperativeHandle(ref, () => ({
101
+ callIframe(action: string) {
102
+ return callIframe(targetIframeRef.current as HTMLIFrameElement, action);
103
+ },
104
+ }));
105
+ return (
111
106
  <Box
112
107
  {...rest}
113
108
  component="iframe"
114
109
  ref={targetIframeRef}
115
110
  onLoad={handleLoad}
116
- title="shared-bridge"
117
111
  data-id={dataId}
118
112
  src={withQuery(src, { id: dataId })}
119
113
  sx={mergeSx(
@@ -125,13 +119,13 @@ const SharedBridge = memo(function SharedBridge({
125
119
  width: '100%',
126
120
  height: '100%',
127
121
  cursor: 'pointer',
128
- opacity: 0,
122
+ backgroundColor: 'transparent',
129
123
  },
130
124
  sx
131
125
  )}
132
126
  />
133
- </>
134
- );
135
- });
127
+ );
128
+ }
129
+ );
136
130
 
137
- export default SharedBridge;
131
+ export default memo(SharedBridge);
@@ -1,12 +0,0 @@
1
- import { Locale } from '../type';
2
- type LoginButtonProps = {
3
- onClick: (options?: {
4
- openMode?: 'popup' | 'window';
5
- }) => void;
6
- render: (options: {
7
- onClick: () => void;
8
- }) => React.ReactNode;
9
- locale?: Locale;
10
- };
11
- export default function LoginButton({ onClick, render, locale }: LoginButtonProps): string | number | boolean | Iterable<import("react").ReactNode> | import("react/jsx-runtime").JSX.Element | null | undefined;
12
- export {};
@@ -1,74 +0,0 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { Box } from '@mui/material';
3
- import { joinURL } from 'ufo';
4
- import { useRef, useState } from 'react';
5
- import { useMemoizedFn } from 'ahooks';
6
- import { useBrowser } from '@arcblock/react-hooks';
7
- import SharedBridge from '../SharedBridge';
8
- import { setVisitorId } from '../Util';
9
- import { getFederatedEnabled, getMaster } from '../Util/federated';
10
- import { callIframe } from '../Util/iframe';
11
- export default function LoginButton({
12
- onClick,
13
- render,
14
- locale
15
- }) {
16
- const blocklet = window?.blocklet;
17
- const federatedEnabled = getFederatedEnabled(blocklet);
18
- const masterSite = getMaster(blocklet);
19
- const sharedBridgeRef = useRef(null);
20
- const [hasStorageAccess, setHasStorageAccess] = useState(false);
21
- const browser = useBrowser();
22
- const handleClick = useMemoizedFn(() => {
23
- if (hasStorageAccess) {
24
- onClick({
25
- openMode: 'popup'
26
- });
27
- } else {
28
- onClick();
29
- }
30
- });
31
- const handleLoad = useMemoizedFn(async () => {
32
- const {
33
- value: visitorId
34
- } = await callIframe(sharedBridgeRef.current, 'getVisitorId');
35
- if (visitorId) {
36
- setHasStorageAccess(true);
37
- setVisitorId(visitorId);
38
- }
39
- });
40
- const handleClickBridge = useMemoizedFn(({
41
- value,
42
- visitorId
43
- }) => {
44
- if (visitorId) {
45
- setVisitorId(visitorId);
46
- }
47
- if (value) {
48
- onClick({
49
- openMode: 'popup'
50
- });
51
- } else {
52
- onClick();
53
- }
54
- });
55
- if (browser.arcSphere || browser.wallet) {
56
- return render({
57
- onClick
58
- });
59
- }
60
- return /*#__PURE__*/_jsxs(Box, {
61
- sx: {
62
- position: 'relative'
63
- },
64
- children: [render({
65
- onClick: handleClick
66
- }), masterSite?.appUrl && federatedEnabled ? /*#__PURE__*/_jsx(SharedBridge, {
67
- locale: locale,
68
- iframeRef: sharedBridgeRef,
69
- onLoad: handleLoad,
70
- onClick: handleClickBridge,
71
- src: joinURL(masterSite.appUrl, '/.well-known/service/share/shared-bridge.html')
72
- }) : null]
73
- });
74
- }
@@ -1,6 +0,0 @@
1
- import { Locale } from '../type';
2
- export default function NeedStorageAccessApiDialog({ locale, origin, host, }: {
3
- locale?: Locale;
4
- origin: string;
5
- host: string;
6
- }): import("react/jsx-runtime").JSX.Element;
@@ -1,191 +0,0 @@
1
- import { Fragment as _Fragment, jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
2
- import { Box, Typography, Chip, List, ListItem } from '@mui/material';
3
- import { Icon } from '@iconify/react';
4
- import externalLinkIcon from '@iconify-icons/tabler/external-link';
5
- import lockOutlineIcon from '@iconify-icons/material-symbols/lock-outline';
6
- import checkCircleIcon from '@iconify-icons/material-symbols/check-circle';
7
- import rocketLaunchRoundedIcon from '@iconify-icons/material-symbols/rocket-launch-rounded';
8
- import { useMemoizedFn } from 'ahooks';
9
- import { translate } from '../Locale/util';
10
- const translations = {
11
- en: {
12
- allow: 'Allow',
13
- dataUsage: 'Your data is only used for identity authentication, and will not be collected or used for any other purpose.',
14
- title: 'Cross-site authorization request',
15
- clickAllow: ({
16
- allowButton
17
- }) => {
18
- return /*#__PURE__*/_jsxs(_Fragment, {
19
- children: ["You only need to click the ", allowButton, " button above, and you will not see this request again."]
20
- });
21
- },
22
- reason: ({
23
- site
24
- }) => {
25
- return /*#__PURE__*/_jsxs(_Fragment, {
26
- children: ["For a better login experience, we need to apply for the storage permission of the ", site, " site."]
27
- });
28
- },
29
- afterAllow: {
30
- title: 'After authorization, you will enjoy:',
31
- list1: 'More convenient login experience',
32
- list2: 'Faster access speed'
33
- }
34
- },
35
- zh: {
36
- allow: '允许',
37
- dataUsage: '您的数据仅用于身份认证,不会被收集或用于其他用途。',
38
- title: '跨站授权请求',
39
- clickAllow: ({
40
- allowButton
41
- }) => {
42
- return /*#__PURE__*/_jsxs(_Fragment, {
43
- children: ["\u60A8\u53EA\u9700\u8981\u70B9\u51FB\u5C4F\u5E55\u4E0A\u65B9\u7684 ", allowButton, " \u6309\u94AE\uFF0C\u540E\u7EED\u5C06\u4E0D\u4F1A\u518D\u770B\u5230\u8FD9\u4E2A\u8BF7\u6C42\u3002"]
44
- });
45
- },
46
- reason: ({
47
- site
48
- }) => {
49
- return /*#__PURE__*/_jsxs(_Fragment, {
50
- children: ["\u4E3A\u4E86\u8BA9\u60A8\u83B7\u5F97\u66F4\u597D\u7684\u767B\u5F55\u4F53\u9A8C\uFF0C\u6211\u4EEC\u9700\u8981\u7533\u8BF7 ", site, " \u7AD9\u70B9\u5B58\u50A8\u6743\u9650\u3002"]
51
- });
52
- },
53
- afterAllow: {
54
- title: '授权后,您将享受:',
55
- list1: '更便捷的登录体验',
56
- list2: '更快的访问速度'
57
- }
58
- }
59
- };
60
- export default function NeedStorageAccessApiDialog({
61
- locale = 'en',
62
- origin,
63
- host
64
- }) {
65
- const t = useMemoizedFn((key, data = {}) => {
66
- return translate(translations, key, locale, 'en', data);
67
- });
68
- return /*#__PURE__*/_jsxs(Box, {
69
- sx: {
70
- backgroundColor: 'background.default',
71
- display: 'flex',
72
- flexDirection: 'column',
73
- height: '100%',
74
- position: 'relative',
75
- maxWidth: '100%',
76
- transition: 'width 0.2s ease-in-out',
77
- margin: 'auto',
78
- p: 3,
79
- gap: 2
80
- },
81
- children: [/*#__PURE__*/_jsxs(Typography, {
82
- component: "h1",
83
- variant: "h4",
84
- sx: {
85
- fontWeight: 700,
86
- fontFamily: 'Lexend',
87
- display: 'flex',
88
- alignItems: 'center',
89
- gap: 1
90
- },
91
- children: [/*#__PURE__*/_jsx(Box, {
92
- component: Icon,
93
- icon: lockOutlineIcon,
94
- fontSize: 28,
95
- sx: {
96
- color: 'warning.main'
97
- }
98
- }), t('title')]
99
- }), /*#__PURE__*/_jsxs(Box, {
100
- sx: {
101
- display: 'flex',
102
- flexDirection: 'column',
103
- gap: 1
104
- },
105
- children: [/*#__PURE__*/_jsx(Typography, {
106
- children: t('reason', {
107
- site: /*#__PURE__*/_jsx(Chip, {
108
- clickable: true,
109
- component: "a",
110
- href: origin,
111
- label: host,
112
- size: "small",
113
- deleteIcon: /*#__PURE__*/_jsx(Icon, {
114
- icon: externalLinkIcon
115
- }),
116
- onDelete: () => {},
117
- target: "_blank"
118
- })
119
- })
120
- }), /*#__PURE__*/_jsx(Typography, {
121
- children: t('clickAllow', {
122
- allowButton: /*#__PURE__*/_jsx(Chip, {
123
- label: t('allow'),
124
- size: "small",
125
- color: "success"
126
- })
127
- })
128
- }), /*#__PURE__*/_jsxs(Box, {
129
- sx: {
130
- mt: 2
131
- },
132
- children: [/*#__PURE__*/_jsxs(Typography, {
133
- sx: {
134
- display: 'flex',
135
- alignItems: 'center',
136
- gap: 1,
137
- mb: 0.5
138
- },
139
- children: [/*#__PURE__*/_jsx(Box, {
140
- component: Icon,
141
- icon: checkCircleIcon,
142
- fontSize: 24,
143
- sx: {
144
- color: 'success.main'
145
- }
146
- }), t('afterAllow.title')]
147
- }), /*#__PURE__*/_jsxs(List, {
148
- dense: true,
149
- sx: {
150
- py: 0,
151
- pl: 2
152
- },
153
- children: [/*#__PURE__*/_jsxs(ListItem, {
154
- sx: {
155
- display: 'flex',
156
- alignItems: 'center',
157
- gap: 0.8
158
- },
159
- children: [/*#__PURE__*/_jsx(Box, {
160
- component: Icon,
161
- icon: rocketLaunchRoundedIcon,
162
- fontSize: 20,
163
- sx: {
164
- color: 'success.main'
165
- }
166
- }), t('afterAllow.list1')]
167
- }), /*#__PURE__*/_jsxs(ListItem, {
168
- sx: {
169
- display: 'flex',
170
- alignItems: 'center',
171
- gap: 0.8
172
- },
173
- children: [/*#__PURE__*/_jsx(Box, {
174
- component: Icon,
175
- icon: rocketLaunchRoundedIcon,
176
- fontSize: 20,
177
- sx: {
178
- color: 'success.main'
179
- }
180
- }), t('afterAllow.list2')]
181
- })]
182
- })]
183
- }), /*#__PURE__*/_jsx(Typography, {
184
- component: "div",
185
- variant: "body2",
186
- color: "grey.700",
187
- children: t('dataUsage')
188
- })]
189
- })]
190
- });
191
- }
@@ -1,73 +0,0 @@
1
- import { Box } from '@mui/material';
2
- import { joinURL } from 'ufo';
3
- import { useRef, useState } from 'react';
4
- import { useMemoizedFn } from 'ahooks';
5
- import { useBrowser } from '@arcblock/react-hooks';
6
-
7
- import SharedBridge from '../SharedBridge';
8
- import { setVisitorId } from '../Util';
9
- import { getFederatedEnabled, getMaster } from '../Util/federated';
10
- import { callIframe } from '../Util/iframe';
11
- import { Locale } from '../type';
12
-
13
- type LoginButtonProps = {
14
- onClick: (options?: { openMode?: 'popup' | 'window' }) => void;
15
- render: (options: { onClick: () => void }) => React.ReactNode;
16
- locale?: Locale;
17
- };
18
-
19
- export default function LoginButton({ onClick, render, locale }: LoginButtonProps) {
20
- const blocklet = window?.blocklet;
21
- const federatedEnabled = getFederatedEnabled(blocklet);
22
- const masterSite = getMaster(blocklet);
23
- const sharedBridgeRef = useRef<HTMLIFrameElement>(null);
24
- const [hasStorageAccess, setHasStorageAccess] = useState(false);
25
- const browser = useBrowser();
26
-
27
- const handleClick = useMemoizedFn(() => {
28
- if (hasStorageAccess) {
29
- onClick({ openMode: 'popup' });
30
- } else {
31
- onClick();
32
- }
33
- });
34
- const handleLoad = useMemoizedFn(async () => {
35
- const { value: visitorId } = await callIframe(sharedBridgeRef.current as HTMLIFrameElement, 'getVisitorId');
36
- if (visitorId) {
37
- setHasStorageAccess(true);
38
- setVisitorId(visitorId);
39
- }
40
- });
41
- const handleClickBridge = useMemoizedFn(({ value, visitorId }) => {
42
- if (visitorId) {
43
- setVisitorId(visitorId);
44
- }
45
- if (value) {
46
- onClick({ openMode: 'popup' });
47
- } else {
48
- onClick();
49
- }
50
- });
51
-
52
- if (browser.arcSphere || browser.wallet) {
53
- return render({ onClick });
54
- }
55
-
56
- return (
57
- <Box
58
- sx={{
59
- position: 'relative',
60
- }}>
61
- {render({ onClick: handleClick })}
62
- {masterSite?.appUrl && federatedEnabled ? (
63
- <SharedBridge
64
- locale={locale}
65
- iframeRef={sharedBridgeRef}
66
- onLoad={handleLoad}
67
- onClick={handleClickBridge}
68
- src={joinURL(masterSite.appUrl, '/.well-known/service/share/shared-bridge.html')}
69
- />
70
- ) : null}
71
- </Box>
72
- );
73
- }