@arcblock/did-connect-react 3.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (183) hide show
  1. package/LICENSE +13 -0
  2. package/README.md +134 -0
  3. package/lib/Address/index.js +4 -0
  4. package/lib/Avatar/index.js +4 -0
  5. package/lib/Button/index.js +17 -0
  6. package/lib/Connect/assets/locale.js +143 -0
  7. package/lib/Connect/assets/login-bg.png +0 -0
  8. package/lib/Connect/assets/login-slogan.js +9 -0
  9. package/lib/Connect/components/action-button.js +26 -0
  10. package/lib/Connect/components/app-tips.js +132 -0
  11. package/lib/Connect/components/auto-height.js +31 -0
  12. package/lib/Connect/components/back-button.js +24 -0
  13. package/lib/Connect/components/connect-status.js +263 -0
  14. package/lib/Connect/components/did-connect-title.js +126 -0
  15. package/lib/Connect/components/download-tips.js +52 -0
  16. package/lib/Connect/components/loading.js +26 -0
  17. package/lib/Connect/components/login-item/connect-choose-list.js +249 -0
  18. package/lib/Connect/components/login-item/login-method-item.js +129 -0
  19. package/lib/Connect/components/login-item/mobile-login-item.js +114 -0
  20. package/lib/Connect/components/login-item/passkey-login-item.js +44 -0
  21. package/lib/Connect/components/login-item/web-login-item.js +97 -0
  22. package/lib/Connect/components/mask-overlay.js +34 -0
  23. package/lib/Connect/components/refresh-overlay.js +57 -0
  24. package/lib/Connect/components/switch-app.js +70 -0
  25. package/lib/Connect/contexts/state.js +142 -0
  26. package/lib/Connect/fullpage.js +5 -0
  27. package/lib/Connect/hooks/auth-url.js +23 -0
  28. package/lib/Connect/hooks/method-list.js +46 -0
  29. package/lib/Connect/hooks/page-show.js +17 -0
  30. package/lib/Connect/hooks/security.js +27 -0
  31. package/lib/Connect/hooks/token.js +305 -0
  32. package/lib/Connect/hooks/use-apps.js +19 -0
  33. package/lib/Connect/hooks/use-quick-connect.js +97 -0
  34. package/lib/Connect/index.js +498 -0
  35. package/lib/Connect/landing-page.js +5 -0
  36. package/lib/Connect/plugins/email/index.js +62 -0
  37. package/lib/Connect/plugins/email/list-item.js +28 -0
  38. package/lib/Connect/plugins/email/placeholder.js +283 -0
  39. package/lib/Connect/plugins/index.js +4 -0
  40. package/lib/Connect/use-connect.js +164 -0
  41. package/lib/Connect/with-blocklet.js +15 -0
  42. package/lib/Connect/with-bridge-call.js +108 -0
  43. package/lib/Federated/context.js +61 -0
  44. package/lib/Federated/index.js +7 -0
  45. package/lib/Logo/index.js +4 -0
  46. package/lib/OAuth/context.js +234 -0
  47. package/lib/OAuth/guest.svg.js +5 -0
  48. package/lib/OAuth/index.js +7 -0
  49. package/lib/OAuth/passport-switcher.js +114 -0
  50. package/lib/Passkey/actions.js +165 -0
  51. package/lib/Passkey/constants.js +4 -0
  52. package/lib/Passkey/context.js +266 -0
  53. package/lib/Passkey/dialog.js +277 -0
  54. package/lib/Passkey/icon.js +13 -0
  55. package/lib/Passkey/index.js +9 -0
  56. package/lib/Service/index.js +62 -0
  57. package/lib/Session/assets/did-spaces-guide-cover.svg.js +135 -0
  58. package/lib/Session/assets/did-spaces-guide-icon.svg.js +9 -0
  59. package/lib/Session/context.js +5 -0
  60. package/lib/Session/did-spaces-guide.js +136 -0
  61. package/lib/Session/hooks/use-federated.js +64 -0
  62. package/lib/Session/hooks/use-mobile.js +8 -0
  63. package/lib/Session/hooks/use-protected-routes.js +11 -0
  64. package/lib/Session/hooks/use-session-token.js +169 -0
  65. package/lib/Session/hooks/use-verify.js +45 -0
  66. package/lib/Session/index.js +896 -0
  67. package/lib/Session/libs/constants.js +15 -0
  68. package/lib/Session/libs/did-spaces.js +10 -0
  69. package/lib/Session/libs/federated.js +42 -0
  70. package/lib/Session/libs/index.js +15 -0
  71. package/lib/Session/libs/locales.js +161 -0
  72. package/lib/Session/libs/login-mobile.js +55 -0
  73. package/lib/Session/window-focus-aware.js +17 -0
  74. package/lib/SessionManager/index.js +4 -0
  75. package/lib/Storage/engine/cookie.js +21 -0
  76. package/lib/Storage/engine/local-storage.js +36 -0
  77. package/lib/Storage/index.js +23 -0
  78. package/lib/User/index.js +6 -0
  79. package/lib/User/use-did.js +59 -0
  80. package/lib/User/wrap-did.js +13 -0
  81. package/lib/WebWalletSWKeeper/index.js +5 -0
  82. package/lib/constant.js +22 -0
  83. package/lib/error.js +8 -0
  84. package/lib/hooks/use-locale.js +7 -0
  85. package/lib/index.js +33 -0
  86. package/lib/locales/en.js +17 -0
  87. package/lib/locales/index.js +10 -0
  88. package/lib/locales/zh.js +17 -0
  89. package/lib/package.json.js +7 -0
  90. package/lib/types.d.ts +355 -0
  91. package/lib/utils.js +214 -0
  92. package/package.json +84 -0
  93. package/src/Address/index.jsx +2 -0
  94. package/src/Avatar/index.jsx +2 -0
  95. package/src/Button/Button.stories.jsx +7 -0
  96. package/src/Button/index.jsx +21 -0
  97. package/src/Connect/Connect.stories.jsx +34 -0
  98. package/src/Connect/assets/locale.js +145 -0
  99. package/src/Connect/assets/login-bg.png +0 -0
  100. package/src/Connect/assets/login-slogan.js +7 -0
  101. package/src/Connect/components/action-button.jsx +22 -0
  102. package/src/Connect/components/app-tips.jsx +156 -0
  103. package/src/Connect/components/auto-height.jsx +38 -0
  104. package/src/Connect/components/back-button.jsx +23 -0
  105. package/src/Connect/components/connect-status.jsx +259 -0
  106. package/src/Connect/components/did-connect-title.jsx +106 -0
  107. package/src/Connect/components/download-tips.jsx +55 -0
  108. package/src/Connect/components/loading.jsx +25 -0
  109. package/src/Connect/components/login-item/connect-choose-list.jsx +304 -0
  110. package/src/Connect/components/login-item/login-method-item.jsx +118 -0
  111. package/src/Connect/components/login-item/mobile-login-item.jsx +179 -0
  112. package/src/Connect/components/login-item/passkey-login-item.jsx +52 -0
  113. package/src/Connect/components/login-item/web-login-item.jsx +149 -0
  114. package/src/Connect/components/mask-overlay.jsx +32 -0
  115. package/src/Connect/components/refresh-overlay.jsx +52 -0
  116. package/src/Connect/components/switch-app.jsx +69 -0
  117. package/src/Connect/contexts/state.jsx +219 -0
  118. package/src/Connect/fullpage.jsx +3 -0
  119. package/src/Connect/hooks/auth-url.js +31 -0
  120. package/src/Connect/hooks/method-list.js +121 -0
  121. package/src/Connect/hooks/page-show.js +24 -0
  122. package/src/Connect/hooks/security.js +40 -0
  123. package/src/Connect/hooks/token.js +639 -0
  124. package/src/Connect/hooks/use-apps.js +69 -0
  125. package/src/Connect/hooks/use-quick-connect.js +130 -0
  126. package/src/Connect/index.jsx +600 -0
  127. package/src/Connect/landing-page.jsx +3 -0
  128. package/src/Connect/plugins/email/index.jsx +82 -0
  129. package/src/Connect/plugins/email/list-item.jsx +31 -0
  130. package/src/Connect/plugins/email/placeholder.jsx +365 -0
  131. package/src/Connect/plugins/index.js +2 -0
  132. package/src/Connect/use-connect.jsx +321 -0
  133. package/src/Connect/with-blocklet.jsx +26 -0
  134. package/src/Connect/with-bridge-call.jsx +138 -0
  135. package/src/Federated/context.jsx +93 -0
  136. package/src/Federated/index.jsx +1 -0
  137. package/src/Logo/index.jsx +2 -0
  138. package/src/OAuth/context.jsx +346 -0
  139. package/src/OAuth/guest.svg +20 -0
  140. package/src/OAuth/index.jsx +1 -0
  141. package/src/OAuth/passport-switcher.jsx +133 -0
  142. package/src/Passkey/actions.jsx +212 -0
  143. package/src/Passkey/constants.js +2 -0
  144. package/src/Passkey/context.jsx +381 -0
  145. package/src/Passkey/dialog.jsx +391 -0
  146. package/src/Passkey/icon.jsx +10 -0
  147. package/src/Passkey/index.jsx +2 -0
  148. package/src/Service/index.jsx +96 -0
  149. package/src/Session/assets/did-spaces-guide-cover.svg +128 -0
  150. package/src/Session/assets/did-spaces-guide-icon.svg +7 -0
  151. package/src/Session/context.jsx +7 -0
  152. package/src/Session/did-spaces-guide.jsx +173 -0
  153. package/src/Session/hooks/use-federated.js +88 -0
  154. package/src/Session/hooks/use-mobile.jsx +6 -0
  155. package/src/Session/hooks/use-protected-routes.js +16 -0
  156. package/src/Session/hooks/use-session-token.js +365 -0
  157. package/src/Session/hooks/use-verify.jsx +76 -0
  158. package/src/Session/index.jsx +1687 -0
  159. package/src/Session/libs/constants.js +14 -0
  160. package/src/Session/libs/did-spaces.js +38 -0
  161. package/src/Session/libs/federated.js +79 -0
  162. package/src/Session/libs/index.js +5 -0
  163. package/src/Session/libs/locales.js +160 -0
  164. package/src/Session/libs/login-mobile.js +80 -0
  165. package/src/Session/window-focus-aware.jsx +28 -0
  166. package/src/SessionManager/index.jsx +2 -0
  167. package/src/Storage/engine/cookie.js +23 -0
  168. package/src/Storage/engine/local-storage.js +55 -0
  169. package/src/Storage/index.js +25 -0
  170. package/src/User/index.js +4 -0
  171. package/src/User/use-did.js +80 -0
  172. package/src/User/wrap-did.jsx +18 -0
  173. package/src/WebWalletSWKeeper/index.jsx +3 -0
  174. package/src/constant.js +26 -0
  175. package/src/error.js +6 -0
  176. package/src/hooks/use-locale.jsx +6 -0
  177. package/src/index.js +43 -0
  178. package/src/locales/en.jsx +15 -0
  179. package/src/locales/index.jsx +13 -0
  180. package/src/locales/zh.jsx +15 -0
  181. package/src/types.d.ts +355 -0
  182. package/src/utils.js +395 -0
  183. package/vite.config.mjs +29 -0
@@ -0,0 +1,149 @@
1
+ import PropTypes from 'prop-types';
2
+ import { CircularProgress } from '@mui/material';
3
+ import { useCreation, useMemoizedFn, useReactive } from 'ahooks';
4
+ import { openWebWallet, detectWalletExtension } from '@arcblock/ux/lib/Util';
5
+ import { mergeSx } from '@arcblock/ux/lib/Util/style';
6
+ import noop from 'lodash/noop';
7
+ import omit from 'lodash/omit';
8
+ import { useImperativeHandle } from 'react';
9
+ import ProviderIcon from '@arcblock/ux/lib/DIDConnect/provider-icon';
10
+ import { LOGIN_PROVIDER } from '@arcblock/ux/lib/Util/constant';
11
+
12
+ import LoginMethodItem from './login-method-item';
13
+ import { useStateContext } from '../../contexts/state';
14
+ import { EXT_DOWNLOAD_URL } from '../../../constant';
15
+ import useAuthUrl from '../../hooks/auth-url';
16
+
17
+ export default function WebLoginItem({
18
+ ref = null,
19
+ tokenState,
20
+ locale = 'en',
21
+ webWalletUrl,
22
+ onClick = noop,
23
+ disableSwitchApp = false,
24
+ ...rest
25
+ }) {
26
+ const { browserBrand } = useStateContext();
27
+ const extension = detectWalletExtension();
28
+ const currentState = useReactive({
29
+ loading: false,
30
+ });
31
+
32
+ const urlWithParams = useAuthUrl({ disableSwitchApp, tokenState });
33
+
34
+ const handleConnectFn = useMemoizedFn(() => {
35
+ currentState.loading = true;
36
+ const openResult = openWebWallet({
37
+ webWalletUrl,
38
+ url: urlWithParams,
39
+ locale,
40
+ appInfo: tokenState.appInfo,
41
+ memberAppInfo: tokenState.memberAppInfo,
42
+ });
43
+ if (openResult?.type === 'web') {
44
+ currentState.loading = false;
45
+ } else {
46
+ // 后续需要通过插件回传消息,才能准确的关闭 loading 状态
47
+ setTimeout(() => {
48
+ currentState.loading = false;
49
+ }, 3000);
50
+ }
51
+ });
52
+ const handleConnect = useMemoizedFn((e) => {
53
+ if (!tokenState.url) {
54
+ return;
55
+ }
56
+
57
+ onClick(e);
58
+ e?.preventDefault();
59
+ handleConnectFn();
60
+ });
61
+
62
+ useImperativeHandle(ref, () => ({
63
+ connect: handleConnectFn,
64
+ }));
65
+
66
+ const extDownloadUrl = useCreation(() => {
67
+ if (browserBrand === 'edge') {
68
+ return EXT_DOWNLOAD_URL.edge;
69
+ }
70
+ if (browserBrand === 'chrome') {
71
+ return EXT_DOWNLOAD_URL.chrome;
72
+ }
73
+ return null;
74
+ }, [browserBrand]);
75
+
76
+ const buttonProps = useCreation(() => {
77
+ const result = {};
78
+ if (extension) {
79
+ result.onClick = handleConnect;
80
+ } else if (extDownloadUrl) {
81
+ result.component = 'a';
82
+ result.href = extDownloadUrl;
83
+ result.target = '_blank';
84
+ result.rel = 'noopener';
85
+ } else {
86
+ result.onClick = handleConnect;
87
+ }
88
+
89
+ if (!tokenState.url) {
90
+ result.sx = {
91
+ cursor: 'not-allowed',
92
+ };
93
+ }
94
+ return result;
95
+ }, [handleConnect, tokenState.url]);
96
+
97
+ const title = useCreation(() => {
98
+ if (extension || extDownloadUrl) {
99
+ return 'DID Wallet (Extension)';
100
+ }
101
+ return 'DID Wallet (Web)';
102
+ }, [extension, extDownloadUrl]);
103
+
104
+ return (
105
+ <LoginMethodItem
106
+ {...rest}
107
+ sx={mergeSx(
108
+ {
109
+ textDecoration: 'none',
110
+ },
111
+ rest?.sx,
112
+ buttonProps.sx
113
+ )}
114
+ title={title}
115
+ icon={
116
+ currentState.loading || !tokenState.url ? (
117
+ <CircularProgress
118
+ color="primary"
119
+ size={24}
120
+ sx={{
121
+ '& svg': {
122
+ transform: 'scale(0.75)',
123
+ },
124
+ }}
125
+ />
126
+ ) : (
127
+ <ProviderIcon
128
+ width="24px"
129
+ height="24px"
130
+ provider={LOGIN_PROVIDER.DID_WALLET}
131
+ style={{
132
+ transform: 'scale(0.9)',
133
+ }}
134
+ />
135
+ )
136
+ }
137
+ {...omit(buttonProps, 'sx')}
138
+ />
139
+ );
140
+ }
141
+
142
+ WebLoginItem.propTypes = {
143
+ tokenState: PropTypes.object.isRequired,
144
+ locale: PropTypes.string,
145
+ webWalletUrl: PropTypes.string.isRequired,
146
+ onClick: PropTypes.func,
147
+ disableSwitchApp: PropTypes.bool,
148
+ ref: PropTypes.any,
149
+ };
@@ -0,0 +1,32 @@
1
+ import PropTypes from 'prop-types';
2
+ import { alpha, Box } from '@mui/material';
3
+ import { mergeSx } from '@arcblock/ux/lib/Util/style';
4
+
5
+ // 需要父元素设置 relative position
6
+ export default function MaskOverlay({ children, ...rest }) {
7
+ return (
8
+ <Box
9
+ {...rest}
10
+ sx={mergeSx(
11
+ {
12
+ position: 'absolute',
13
+ top: 0,
14
+ bottom: 0,
15
+ left: 0,
16
+ right: 0,
17
+ display: 'flex',
18
+ justifyContent: 'center',
19
+ alignItems: 'center',
20
+ backgroundColor: ({ palette }) => alpha(palette.background.default, 0.75),
21
+ backdropFilter: 'blur(3px)',
22
+ },
23
+ rest?.sx
24
+ )}>
25
+ {children}
26
+ </Box>
27
+ );
28
+ }
29
+
30
+ MaskOverlay.propTypes = {
31
+ children: PropTypes.any.isRequired,
32
+ };
@@ -0,0 +1,52 @@
1
+ import PropTypes from 'prop-types';
2
+ import { Box, useTheme } from '@mui/material';
3
+ import { Icon } from '@iconify/react';
4
+ import RefreshRoundedIcon from '@iconify-icons/material-symbols/refresh-rounded';
5
+
6
+ // 需要父元素设置 relative position
7
+ export default function RefreshOverlay({ onRefresh, ...rest }) {
8
+ const { palette } = useTheme();
9
+ const handleOnRefresh = (e) => {
10
+ e.stopPropagation();
11
+ onRefresh();
12
+ };
13
+
14
+ return (
15
+ <Box
16
+ {...rest}
17
+ onClick={handleOnRefresh}
18
+ sx={{
19
+ position: 'absolute',
20
+ top: 0,
21
+ bottom: 0,
22
+ left: 0,
23
+ right: 0,
24
+ display: 'flex',
25
+ justifyContent: 'center',
26
+ alignItems: 'center',
27
+ backgroundColor: 'rgba(255, 255, 255, 0.75)',
28
+ cursor: 'pointer',
29
+ ...rest.sx,
30
+ }}>
31
+ <Box
32
+ sx={{
33
+ backgroundColor: 'secondary.main',
34
+ borderRadius: '100%',
35
+ fontSize: 0,
36
+ }}>
37
+ <Icon
38
+ icon={RefreshRoundedIcon}
39
+ fontSize={52}
40
+ style={{
41
+ transform: 'scale(0.7)',
42
+ color: palette.secondary.contrastText,
43
+ }}
44
+ />
45
+ </Box>
46
+ </Box>
47
+ );
48
+ }
49
+
50
+ RefreshOverlay.propTypes = {
51
+ onRefresh: PropTypes.func.isRequired,
52
+ };
@@ -0,0 +1,69 @@
1
+ import { useRef } from 'react';
2
+ import { Box, Menu, MenuItem } from '@mui/material';
3
+ import { useReactive } from 'ahooks';
4
+ import { mergeSx } from '@arcblock/ux/lib/Util/style';
5
+ import PropTypes from 'prop-types';
6
+ import { AppInfoItem } from '@arcblock/ux/lib/DIDConnect';
7
+
8
+ import { useStateContext } from '../contexts/state';
9
+
10
+ export default function SwitchApp({ sx = {}, children }) {
11
+ const { appInfoList, extraParams, connectState } = useStateContext();
12
+ const switchAppRef = useRef(null);
13
+ const currentState = useReactive({
14
+ open: false,
15
+ });
16
+ if (appInfoList.length <= 1) return null;
17
+
18
+ return (
19
+ <>
20
+ <Box
21
+ component="span"
22
+ ref={switchAppRef}
23
+ sx={mergeSx(
24
+ {
25
+ color: 'text.secondary',
26
+ cursor: 'pointer',
27
+ },
28
+ sx
29
+ )}
30
+ onClick={() => {
31
+ currentState.open = true;
32
+ }}>
33
+ {children}
34
+ </Box>
35
+ <Menu
36
+ anchorEl={switchAppRef.current}
37
+ open={currentState.open}
38
+ onClose={() => {
39
+ currentState.open = false;
40
+ }}
41
+ slotProps={{
42
+ paper: {
43
+ variant: 'outlined',
44
+ sx: {
45
+ border: 0,
46
+ boxShadow: ({ palette }) => `0px 4px 8px 0px ${palette.grey[100]}, 0px 0px 0px 1px ${palette.grey[100]}`,
47
+ borderRadius: 1,
48
+ },
49
+ },
50
+ }}>
51
+ {appInfoList.map((item) => (
52
+ <MenuItem
53
+ key={item.appPid}
54
+ onClick={() => {
55
+ currentState.open = false;
56
+ connectState.sourceAppPid = item.sourceAppPid;
57
+ }}>
58
+ <AppInfoItem appInfo={item} active={item.sourceAppPid === extraParams?.sourceAppPid} />
59
+ </MenuItem>
60
+ ))}
61
+ </Menu>
62
+ </>
63
+ );
64
+ }
65
+
66
+ SwitchApp.propTypes = {
67
+ sx: PropTypes.object,
68
+ children: PropTypes.any.isRequired,
69
+ };
@@ -0,0 +1,219 @@
1
+ import PropTypes from 'prop-types';
2
+ import { createContext, use, useEffect, useRef, useState } from 'react';
3
+ import useBrowser from '@arcblock/react-hooks/lib/useBrowser';
4
+ import { useCreation, useLatest, useMemoizedFn, useReactive, useSize, useUpdate } from 'ahooks';
5
+ import isUndefined from 'lodash/isUndefined';
6
+ import noop from 'lodash/noop';
7
+ import { Box } from '@mui/material';
8
+ import { translate } from '@arcblock/ux/lib/Locale/util';
9
+ import { getCurrentApp, getMaster } from '@arcblock/ux/lib/Util/federated';
10
+ import { getDIDMotifInfo } from '@arcblock/did-motif';
11
+ import { getDIDColor, isEthereumDid } from '@arcblock/ux/lib/Util';
12
+
13
+ import useApps from '../hooks/use-apps';
14
+ import { SessionContext } from '../../Session/context';
15
+ import translations from '../assets/locale';
16
+
17
+ const StateContext = createContext({
18
+ isWalletWebview: false,
19
+ isMobile: false,
20
+ matchSmallScreen: false,
21
+ blocklet: null,
22
+ });
23
+
24
+ const { Provider, Consumer } = StateContext;
25
+
26
+ function StateProvider({
27
+ children,
28
+ blocklet,
29
+ masterBlocklet = undefined,
30
+ action,
31
+ extraParams = {},
32
+ locale = 'en',
33
+ testOnlyBorderColor = undefined,
34
+ ...rest
35
+ }) {
36
+ const forceUpdate = useUpdate();
37
+ const [plugins, setPlugins] = useState([]);
38
+ const [selectedPlugin, setSelectedPlugin] = useState('');
39
+ const pluginsMap = useCreation(() => {
40
+ return new Map(plugins.map((plugin) => [plugin.name, plugin]));
41
+ }, [plugins]);
42
+ const getPlugin = useMemoizedFn((name) => {
43
+ return pluginsMap.get(name);
44
+ });
45
+ const latestActivePlugin = useLatest(selectedPlugin);
46
+ const t = useMemoizedFn((key, data = {}) => {
47
+ return translate(translations, key, locale, 'en', data);
48
+ });
49
+ const sessionContext = use(SessionContext);
50
+ const reactiveState = useReactive({
51
+ sourceAppPid: undefined,
52
+ status: 'created',
53
+ autoActiveWebview: true,
54
+ deeplink: undefined,
55
+ chooseMethod: '',
56
+ retryConnect: noop,
57
+ });
58
+ const staticState = useRef({
59
+ cancelCount: 0,
60
+ });
61
+
62
+ const browser = useBrowser();
63
+ const { appInfoList, autoGenerateSourceAppPid, canSwitchApp } = useApps({
64
+ blocklet,
65
+ connectState: reactiveState,
66
+ action,
67
+ sourceAppPid: extraParams?.sourceAppPid,
68
+ enableSwitchApp: extraParams?.forceSwitch || extraParams?.enableSwitchApp,
69
+ });
70
+
71
+ const rootRef = useRef(null);
72
+ const size = useSize(rootRef);
73
+
74
+ const matchSmallScreen = useCreation(() => {
75
+ const width = size?.width || 0;
76
+ return width < 500;
77
+ }, [size?.width]);
78
+
79
+ const mergeExtraParams = useCreation(() => {
80
+ if (canSwitchApp) {
81
+ return {
82
+ ...extraParams,
83
+ sourceAppPid: isUndefined(reactiveState.sourceAppPid) ? autoGenerateSourceAppPid : reactiveState.sourceAppPid,
84
+ };
85
+ }
86
+
87
+ const masterAppPid = getMaster(blocklet)?.appPid;
88
+ const currentPageMasterAppPid = getMaster(globalThis.blocklet)?.appPid;
89
+ if (sessionContext?.session?.user && (masterAppPid === currentPageMasterAppPid || !masterAppPid)) {
90
+ if (!isUndefined(extraParams?.sourceAppPid)) {
91
+ return extraParams;
92
+ }
93
+ return {
94
+ ...extraParams,
95
+ sourceAppPid: sessionContext.session.user?.sourceAppPid,
96
+ };
97
+ }
98
+
99
+ return {
100
+ ...extraParams,
101
+ sourceAppPid: autoGenerateSourceAppPid,
102
+ };
103
+ }, [
104
+ canSwitchApp,
105
+ extraParams,
106
+ reactiveState.sourceAppPid,
107
+ action,
108
+ autoGenerateSourceAppPid,
109
+ sessionContext?.session?.user,
110
+ ]);
111
+
112
+ const browserBrand = useCreation(() => {
113
+ const userAgent = window?.navigator?.userAgent;
114
+ if (userAgent.indexOf('Edge') > -1 || userAgent.indexOf('Edg') > -1) {
115
+ return 'edge';
116
+ }
117
+ if (userAgent.indexOf('Chrome') > -1) {
118
+ return 'chrome';
119
+ }
120
+ return 'unknown';
121
+ }, []);
122
+
123
+ useEffect(() => {
124
+ reactiveState.sourceAppPid = mergeExtraParams.sourceAppPid;
125
+ // eslint-disable-next-line react-hooks/exhaustive-deps
126
+ }, [mergeExtraParams.sourceAppPid]);
127
+
128
+ const changeStatus = useMemoizedFn((value) => {
129
+ reactiveState.status = value || 'created';
130
+ });
131
+
132
+ // currentAppInfo 固定不变,就是当前网站的应用信息
133
+ const currentAppInfo = globalThis.blocklet ? getCurrentApp(blocklet) : globalThis.env;
134
+
135
+ const currentAppColor = useCreation(() => {
136
+ if (testOnlyBorderColor) {
137
+ return testOnlyBorderColor;
138
+ }
139
+
140
+ const did = currentAppInfo.appPid;
141
+ const isEthDid = isEthereumDid(did);
142
+ const didMotifInfo = isEthDid ? undefined : getDIDMotifInfo(did);
143
+ if (isEthDid) {
144
+ return getDIDColor(did);
145
+ }
146
+
147
+ return didMotifInfo.color;
148
+ }, [currentAppInfo.appId, testOnlyBorderColor]);
149
+
150
+ // NOTICE: size 的变化频率很高,会触发子组件多次渲染,需要在传递 context 时,排除 size 对它的影响
151
+ const value = useCreation(() => {
152
+ return {
153
+ isWalletWebview: browser.wallet || browser.arcSphere,
154
+ isMobile: browser.mobile.any,
155
+ matchSmallScreen,
156
+ connectState: reactiveState,
157
+ staticState,
158
+ reactiveState,
159
+ appInfoList,
160
+ extraParams: mergeExtraParams,
161
+ blocklet,
162
+ masterBlocklet,
163
+ browserBrand,
164
+ currentAppInfo,
165
+ currentAppColor,
166
+
167
+ t,
168
+ locale,
169
+ action,
170
+ changeStatus,
171
+ getPlugin,
172
+ latestActivePlugin,
173
+ plugins,
174
+ setPlugins,
175
+ selectedPlugin,
176
+ setSelectedPlugin,
177
+ forceUpdate,
178
+ };
179
+ }, [
180
+ browser.wallet,
181
+ browser.arcSphere,
182
+ browser.mobile.any,
183
+ matchSmallScreen,
184
+ JSON.stringify(reactiveState),
185
+ JSON.stringify(appInfoList),
186
+ JSON.stringify(mergeExtraParams),
187
+ blocklet,
188
+ masterBlocklet,
189
+ locale,
190
+ selectedPlugin,
191
+ forceUpdate,
192
+ currentAppInfo,
193
+ currentAppColor,
194
+ ]);
195
+
196
+ return (
197
+ <Provider value={value}>
198
+ <Box {...rest} ref={rootRef}>
199
+ {children}
200
+ </Box>
201
+ </Provider>
202
+ );
203
+ }
204
+
205
+ StateProvider.propTypes = {
206
+ children: PropTypes.any.isRequired,
207
+ blocklet: PropTypes.object.isRequired,
208
+ masterBlocklet: PropTypes.object,
209
+ action: PropTypes.string.isRequired,
210
+ extraParams: PropTypes.object,
211
+ locale: PropTypes.string,
212
+ testOnlyBorderColor: PropTypes.string,
213
+ };
214
+
215
+ function useStateContext() {
216
+ return use(StateContext);
217
+ }
218
+
219
+ export { StateContext, StateProvider, Consumer as StateConsumer, useStateContext };
@@ -0,0 +1,3 @@
1
+ import LandingPage from '@arcblock/ux/lib/DIDConnect/landing-page';
2
+
3
+ export default LandingPage;
@@ -0,0 +1,31 @@
1
+ import { useCreation } from 'ahooks';
2
+ import { useStateContext } from '../contexts/state';
3
+ import useSecurity from './security';
4
+ import { getVisitorId } from '../../utils';
5
+
6
+ export default function useAuthUrl({ disableSwitchApp, tokenState }) {
7
+ const { appInfoList, connectState } = useStateContext();
8
+ const { encryptKey } = useSecurity();
9
+ const urlWithParams = useCreation(() => {
10
+ if (!tokenState.url) {
11
+ return '';
12
+ }
13
+ const canSwitch = !disableSwitchApp && appInfoList.length > 1;
14
+ const resultUrl = new URL(tokenState.url || 'https://www.didwallet.io');
15
+ const actionRawUrl = resultUrl.searchParams.get('url');
16
+ if (actionRawUrl && canSwitch) {
17
+ const actionUrl = new URL(decodeURIComponent(actionRawUrl));
18
+ actionUrl.searchParams.set('_ek_', encryptKey);
19
+ const visitorId = getVisitorId();
20
+ if (visitorId) {
21
+ actionUrl.searchParams.set('vid', visitorId);
22
+ }
23
+ if (connectState?.sourceAppPid) {
24
+ actionUrl.searchParams.set('spid', connectState?.sourceAppPid);
25
+ }
26
+ resultUrl.searchParams.set('url', actionUrl.toString());
27
+ }
28
+ return resultUrl.toString();
29
+ }, [tokenState.url, appInfoList.length, connectState?.sourceAppPid, encryptKey]);
30
+ return urlWithParams;
31
+ }
@@ -0,0 +1,121 @@
1
+ import { useBrowser } from '@arcblock/react-hooks';
2
+ import { detectWalletExtension } from '@arcblock/ux/lib/Util';
3
+ import { useCreation, useRequest } from 'ahooks';
4
+ import intersection from 'lodash/intersection';
5
+ import { OAUTH_PROVIDER } from '@arcblock/ux/lib/Util/constant';
6
+
7
+ import { useOAuth } from '../../OAuth';
8
+ import { checkSameProtocol, getWebWalletUrl } from '../../utils';
9
+
10
+ export default function useMethodList({
11
+ enabledConnectTypes = [],
12
+ allowWallet = true,
13
+ passkeyBehavior = 'none',
14
+ webWalletUrl = getWebWalletUrl(),
15
+ action,
16
+ sourceAppPid,
17
+ mode = 'dialog',
18
+ blocklet = globalThis.blocklet,
19
+ isSmallView = false,
20
+ }) {
21
+ const actionList = ['login', 'invite', 'connect-to-did-space', 'connect-to-did-domain', 'destroy-self'];
22
+ const { getOAuthConfigList } = useOAuth();
23
+ const browser = useBrowser();
24
+ const extension = detectWalletExtension();
25
+ const { data: oauthConfigList = [] } = useRequest(
26
+ async () => {
27
+ const data = await getOAuthConfigList({ sourceAppPid });
28
+ return data;
29
+ },
30
+ { refreshDeps: [sourceAppPid] }
31
+ );
32
+
33
+ const oauthProviderList = useCreation(() => {
34
+ // return [];
35
+ const showProviders = intersection(Object.keys(OAUTH_PROVIDER), enabledConnectTypes);
36
+ const result = oauthConfigList.filter((item) => showProviders.includes(item.provider));
37
+ return result;
38
+ }, [oauthConfigList, enabledConnectTypes]);
39
+
40
+ const showOAuthLogin = useCreation(() => {
41
+ if (!actionList.includes(action)) {
42
+ return false;
43
+ }
44
+ return oauthProviderList.length > 0;
45
+ }, [oauthProviderList, action]);
46
+
47
+ const showMobileLogin = useCreation(() => {
48
+ let _allowWallet = allowWallet;
49
+ if (!actionList.includes(action)) {
50
+ _allowWallet = true;
51
+ }
52
+ return _allowWallet && enabledConnectTypes.includes('mobile');
53
+ }, [allowWallet, enabledConnectTypes, action]);
54
+
55
+ const isSameProtocol = checkSameProtocol(webWalletUrl);
56
+ const showWebLogin = useCreation(() => {
57
+ let _allowWallet = allowWallet;
58
+ if (!actionList.includes(action)) {
59
+ _allowWallet = true;
60
+ }
61
+ return (
62
+ _allowWallet &&
63
+ enabledConnectTypes.includes('web') &&
64
+ (isSameProtocol || extension) &&
65
+ (!browser.mobile.any || extension)
66
+ );
67
+ }, [allowWallet, enabledConnectTypes, browser?.mobile?.any, action]);
68
+
69
+ const showPasskeyLogin = useCreation(() => {
70
+ // NOTICE: 允许所有 action 都展示 passkey
71
+ // if (!actionList.includes(action)) {
72
+ // return false;
73
+ // }
74
+ return passkeyBehavior !== 'none' && !browser.wallet && !browser.arcSphere;
75
+ }, [action]);
76
+
77
+ const showEmailLogin = useCreation(() => {
78
+ if (!actionList.includes(action)) {
79
+ return false;
80
+ }
81
+ // FIXME: @zhanghan 这里是临时用于控制是否展示邮箱登录,待确认没问题后,需要移除
82
+ if (!['true', undefined, null].includes(blocklet?.DID_CONNECT_ALLOW_EMAIL)) {
83
+ return false;
84
+ }
85
+ return blocklet?.settings?.notification?.email?.enabled ?? false;
86
+ }, [action, blocklet]);
87
+
88
+ const hideChooseList = useCreation(() => {
89
+ return false;
90
+ // NOTICE: 先保留这部分逻辑,后续根据需求调整
91
+ // if (showOAuthLogin || showPasskeyLogin) {
92
+ // return false;
93
+ // }
94
+ // return true;
95
+ });
96
+
97
+ const hideQRCode = useCreation(() => {
98
+ if (mode === 'drawer' || (isSmallView && browser.mobile.any)) {
99
+ return true;
100
+ }
101
+ if (actionList.includes(action) && !showMobileLogin) {
102
+ return true;
103
+ }
104
+ return false;
105
+ }, [mode, isSmallView]);
106
+
107
+ const result = {
108
+ hideQRCode,
109
+ hideChooseList,
110
+
111
+ showOAuthLogin,
112
+ showMobileLogin,
113
+ showWebLogin,
114
+ showPasskeyLogin,
115
+ showEmailLogin,
116
+
117
+ oauthProviderList,
118
+ };
119
+
120
+ return result;
121
+ }
@@ -0,0 +1,24 @@
1
+ import { useEffect, useRef } from 'react';
2
+
3
+ function usePageShow(handler) {
4
+ const handlerRef = useRef(handler);
5
+
6
+ useEffect(() => {
7
+ handlerRef.current = handler;
8
+ }, [handler]);
9
+
10
+ useEffect(() => {
11
+ const onChange = (event) => {
12
+ if (!document.hidden && handlerRef.current) {
13
+ handlerRef.current(event);
14
+ }
15
+ };
16
+
17
+ document.addEventListener('visibilitychange', onChange);
18
+ return () => {
19
+ document.removeEventListener('visibilitychange', onChange);
20
+ };
21
+ }, []);
22
+ }
23
+
24
+ export default usePageShow;