@arcblock/did-connect-react 3.4.14 → 3.5.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 (208) hide show
  1. package/dist/standalone/did-connect-react.css +1 -0
  2. package/dist/standalone/index.js +133700 -0
  3. package/lib/Connect/components/login-item/connect-choose-list.js +111 -111
  4. package/lib/Connect/components/login-item/connect-provider-list.js +180 -180
  5. package/lib/Connect/components/login-item/mobile-login-item.js +56 -56
  6. package/lib/Connect/components/login-item/passkey-login-item.js +27 -29
  7. package/lib/Connect/components/login-item/web-login-item.js +31 -29
  8. package/lib/Connect/connect.js +202 -197
  9. package/lib/Connect/contexts/state.js +19 -17
  10. package/lib/Connect/hooks/provider-list.js +33 -33
  11. package/lib/Connect/plugins/email/list-item.js +14 -14
  12. package/lib/Connect/plugins/email/placeholder.js +77 -76
  13. package/lib/package.json.js +1 -1
  14. package/package.json +14 -9
  15. package/.aigne/doc-smith/config.yaml +0 -85
  16. package/.aigne/doc-smith/history.yaml +0 -6
  17. package/.aigne/doc-smith/output/structure-plan.json +0 -204
  18. package/.aigne/doc-smith/translation-cache.yaml +0 -11
  19. package/.aigne/doc-smith/upload-cache.yaml +0 -213
  20. package/docs/_sidebar.md +0 -18
  21. package/docs/advanced-authentication-methods.ja.md +0 -261
  22. package/docs/advanced-authentication-methods.md +0 -261
  23. package/docs/advanced-authentication-methods.zh-TW.md +0 -261
  24. package/docs/advanced-authentication-methods.zh.md +0 -261
  25. package/docs/advanced-utilities.ja.md +0 -132
  26. package/docs/advanced-utilities.md +0 -132
  27. package/docs/advanced-utilities.zh-TW.md +0 -132
  28. package/docs/advanced-utilities.zh.md +0 -132
  29. package/docs/advanced.ja.md +0 -95
  30. package/docs/advanced.md +0 -95
  31. package/docs/advanced.zh-TW.md +0 -95
  32. package/docs/advanced.zh.md +0 -95
  33. package/docs/api-reference.ja.md +0 -178
  34. package/docs/api-reference.md +0 -178
  35. package/docs/api-reference.zh-TW.md +0 -178
  36. package/docs/api-reference.zh.md +0 -178
  37. package/docs/assets/diagram/core-components-session-provider-01.ja.jpg +0 -0
  38. package/docs/assets/diagram/core-components-session-provider-01.jpg +0 -0
  39. package/docs/assets/diagram/core-components-session-provider-01.zh-TW.jpg +0 -0
  40. package/docs/assets/diagram/core-components-session-provider-01.zh.jpg +0 -0
  41. package/docs/assets/diagram/did-connect-diagram-0.ja.jpg +0 -0
  42. package/docs/assets/diagram/did-connect-diagram-0.jpg +0 -0
  43. package/docs/assets/diagram/did-connect-diagram-0.zh-TW.jpg +0 -0
  44. package/docs/assets/diagram/did-connect-diagram-0.zh.jpg +0 -0
  45. package/docs/assets/diagram/overview-01.ja.jpg +0 -0
  46. package/docs/assets/diagram/overview-01.jpg +0 -0
  47. package/docs/assets/diagram/overview-01.zh-TW.jpg +0 -0
  48. package/docs/assets/diagram/overview-01.zh.jpg +0 -0
  49. package/docs/assets/diagram/use-connect-diagram-0.ja.jpg +0 -0
  50. package/docs/assets/diagram/use-connect-diagram-0.jpg +0 -0
  51. package/docs/assets/diagram/use-connect-diagram-0.zh-TW.jpg +0 -0
  52. package/docs/assets/diagram/use-connect-diagram-0.zh.jpg +0 -0
  53. package/docs/core-components-did-connect.ja.md +0 -166
  54. package/docs/core-components-did-connect.md +0 -166
  55. package/docs/core-components-did-connect.zh-TW.md +0 -166
  56. package/docs/core-components-did-connect.zh.md +0 -166
  57. package/docs/core-components-session-provider.ja.md +0 -197
  58. package/docs/core-components-session-provider.md +0 -197
  59. package/docs/core-components-session-provider.zh-TW.md +0 -197
  60. package/docs/core-components-session-provider.zh.md +0 -197
  61. package/docs/core-components.ja.md +0 -16
  62. package/docs/core-components.md +0 -16
  63. package/docs/core-components.zh-TW.md +0 -16
  64. package/docs/core-components.zh.md +0 -16
  65. package/docs/getting-started.ja.md +0 -138
  66. package/docs/getting-started.md +0 -138
  67. package/docs/getting-started.zh-TW.md +0 -138
  68. package/docs/getting-started.zh.md +0 -138
  69. package/docs/hooks-use-connect.ja.md +0 -178
  70. package/docs/hooks-use-connect.md +0 -178
  71. package/docs/hooks-use-connect.zh-TW.md +0 -178
  72. package/docs/hooks-use-connect.zh.md +0 -178
  73. package/docs/hooks-use-did.ja.md +0 -107
  74. package/docs/hooks-use-did.md +0 -107
  75. package/docs/hooks-use-did.zh-TW.md +0 -107
  76. package/docs/hooks-use-did.zh.md +0 -107
  77. package/docs/hooks-use-oauth-passkey.ja.md +0 -188
  78. package/docs/hooks-use-oauth-passkey.md +0 -188
  79. package/docs/hooks-use-oauth-passkey.zh-TW.md +0 -188
  80. package/docs/hooks-use-oauth-passkey.zh.md +0 -188
  81. package/docs/hooks.ja.md +0 -23
  82. package/docs/hooks.md +0 -23
  83. package/docs/hooks.zh-TW.md +0 -23
  84. package/docs/hooks.zh.md +0 -23
  85. package/docs/overview.ja.md +0 -119
  86. package/docs/overview.md +0 -119
  87. package/docs/overview.zh-TW.md +0 -119
  88. package/docs/overview.zh.md +0 -119
  89. package/docs/ui-components-address.ja.md +0 -121
  90. package/docs/ui-components-address.md +0 -121
  91. package/docs/ui-components-address.zh-TW.md +0 -121
  92. package/docs/ui-components-address.zh.md +0 -121
  93. package/docs/ui-components-avatar.ja.md +0 -65
  94. package/docs/ui-components-avatar.md +0 -65
  95. package/docs/ui-components-avatar.zh-TW.md +0 -65
  96. package/docs/ui-components-avatar.zh.md +0 -65
  97. package/docs/ui-components-button.ja.md +0 -99
  98. package/docs/ui-components-button.md +0 -99
  99. package/docs/ui-components-button.zh-TW.md +0 -99
  100. package/docs/ui-components-button.zh.md +0 -99
  101. package/docs/ui-components-logo.ja.md +0 -52
  102. package/docs/ui-components-logo.md +0 -52
  103. package/docs/ui-components-logo.zh-TW.md +0 -52
  104. package/docs/ui-components-logo.zh.md +0 -52
  105. package/docs/ui-components.ja.md +0 -57
  106. package/docs/ui-components.md +0 -57
  107. package/docs/ui-components.zh-TW.md +0 -57
  108. package/docs/ui-components.zh.md +0 -57
  109. package/glossary.md +0 -1
  110. package/src/Address/index.jsx +0 -2
  111. package/src/Avatar/index.jsx +0 -2
  112. package/src/Button/Button.stories.jsx +0 -7
  113. package/src/Button/index.jsx +0 -21
  114. package/src/Connect/Connect.stories.jsx +0 -34
  115. package/src/Connect/assets/locale.js +0 -149
  116. package/src/Connect/assets/login-bg.png +0 -0
  117. package/src/Connect/assets/login-slogan.js +0 -7
  118. package/src/Connect/components/action-button.jsx +0 -22
  119. package/src/Connect/components/app-tips.jsx +0 -156
  120. package/src/Connect/components/auto-height.jsx +0 -38
  121. package/src/Connect/components/back-button.jsx +0 -24
  122. package/src/Connect/components/connect-status.jsx +0 -259
  123. package/src/Connect/components/did-connect-title.jsx +0 -107
  124. package/src/Connect/components/download-tips.jsx +0 -55
  125. package/src/Connect/components/loading.jsx +0 -25
  126. package/src/Connect/components/login-item/connect-choose-list.jsx +0 -317
  127. package/src/Connect/components/login-item/connect-provider-list.jsx +0 -462
  128. package/src/Connect/components/login-item/login-method-item.jsx +0 -139
  129. package/src/Connect/components/login-item/mobile-login-item.jsx +0 -181
  130. package/src/Connect/components/login-item/passkey-login-item.jsx +0 -54
  131. package/src/Connect/components/login-item/wallet-login-options.jsx +0 -129
  132. package/src/Connect/components/login-item/web-login-item.jsx +0 -157
  133. package/src/Connect/components/mask-overlay.jsx +0 -32
  134. package/src/Connect/components/refresh-overlay.jsx +0 -52
  135. package/src/Connect/components/switch-app.jsx +0 -69
  136. package/src/Connect/connect.jsx +0 -617
  137. package/src/Connect/contexts/state.jsx +0 -234
  138. package/src/Connect/fallback-connect.jsx +0 -47
  139. package/src/Connect/fullpage.jsx +0 -3
  140. package/src/Connect/hooks/auth-url.js +0 -31
  141. package/src/Connect/hooks/method-list.js +0 -121
  142. package/src/Connect/hooks/page-show.js +0 -24
  143. package/src/Connect/hooks/provider-list.js +0 -165
  144. package/src/Connect/hooks/security.js +0 -40
  145. package/src/Connect/hooks/token.js +0 -627
  146. package/src/Connect/hooks/use-apps.js +0 -69
  147. package/src/Connect/hooks/use-quick-connect.js +0 -119
  148. package/src/Connect/index.jsx +0 -21
  149. package/src/Connect/landing-page.jsx +0 -3
  150. package/src/Connect/plugins/email/index.jsx +0 -85
  151. package/src/Connect/plugins/email/list-item.jsx +0 -34
  152. package/src/Connect/plugins/email/placeholder.jsx +0 -365
  153. package/src/Connect/plugins/index.js +0 -2
  154. package/src/Connect/use-connect.jsx +0 -321
  155. package/src/Connect/with-blocklet.jsx +0 -26
  156. package/src/Connect/with-bridge-call.jsx +0 -138
  157. package/src/Federated/context.jsx +0 -93
  158. package/src/Federated/index.jsx +0 -1
  159. package/src/Logo/index.jsx +0 -2
  160. package/src/OAuth/bind-conflict-alert.jsx +0 -37
  161. package/src/OAuth/context.jsx +0 -407
  162. package/src/OAuth/guest.svg +0 -20
  163. package/src/OAuth/index.jsx +0 -1
  164. package/src/OAuth/passport-switcher.jsx +0 -2
  165. package/src/Passkey/actions.jsx +0 -217
  166. package/src/Passkey/constants.js +0 -2
  167. package/src/Passkey/context.jsx +0 -395
  168. package/src/Passkey/dialog.jsx +0 -401
  169. package/src/Passkey/icon.jsx +0 -10
  170. package/src/Passkey/index.jsx +0 -2
  171. package/src/Service/index.jsx +0 -96
  172. package/src/Session/assets/did-spaces-guide-cover.svg +0 -1
  173. package/src/Session/assets/did-spaces-guide-icon.svg +0 -7
  174. package/src/Session/context.jsx +0 -7
  175. package/src/Session/did-spaces-guide.jsx +0 -173
  176. package/src/Session/handler.jsx +0 -98
  177. package/src/Session/hooks/use-federated.js +0 -91
  178. package/src/Session/hooks/use-mobile.jsx +0 -6
  179. package/src/Session/hooks/use-protected-routes.js +0 -16
  180. package/src/Session/hooks/use-session-token.js +0 -400
  181. package/src/Session/hooks/use-verify.jsx +0 -76
  182. package/src/Session/index.jsx +0 -1789
  183. package/src/Session/libs/constants.js +0 -17
  184. package/src/Session/libs/did-spaces.js +0 -38
  185. package/src/Session/libs/federated.js +0 -82
  186. package/src/Session/libs/index.js +0 -5
  187. package/src/Session/libs/locales.js +0 -160
  188. package/src/Session/libs/login-mobile.js +0 -80
  189. package/src/Session/window-focus-aware.jsx +0 -28
  190. package/src/SessionManager/index.jsx +0 -2
  191. package/src/Storage/engine/cookie.js +0 -25
  192. package/src/Storage/engine/local-storage.js +0 -57
  193. package/src/Storage/index.js +0 -25
  194. package/src/User/index.js +0 -4
  195. package/src/User/use-did.js +0 -80
  196. package/src/User/wrap-did.jsx +0 -18
  197. package/src/WebWalletSWKeeper/index.jsx +0 -3
  198. package/src/components/PassportSwitcher.jsx +0 -160
  199. package/src/constant.js +0 -27
  200. package/src/error.js +0 -6
  201. package/src/hooks/use-locale.jsx +0 -6
  202. package/src/index.js +0 -32
  203. package/src/locales/en.jsx +0 -15
  204. package/src/locales/index.jsx +0 -13
  205. package/src/locales/zh.jsx +0 -15
  206. package/src/types.d.ts +0 -355
  207. package/src/utils.js +0 -413
  208. package/vite.config.mjs +0 -29
@@ -1,627 +0,0 @@
1
- import { useEffect, useRef } from 'react';
2
- import get from 'lodash/get';
3
- import Cookie from 'js-cookie';
4
- import omitBy from 'lodash/omitBy';
5
- import isNil from 'lodash/isNil';
6
- import omit from 'lodash/omit';
7
- import cloneDeep from 'lodash/cloneDeep';
8
- import { useCreation, useMemoizedFn, useReactive } from 'ahooks';
9
- import useInterval from '@arcblock/react-hooks/lib/useInterval';
10
- import useBrowser from '@arcblock/react-hooks/lib/useBrowser';
11
- import { getVisitorId, isUrl, stringifyQuery } from '@arcblock/ux/lib/Util';
12
- import { WsClient } from '@arcblock/ws';
13
-
14
- import {
15
- decodeConnectUrl,
16
- parseTokenFromConnectUrl,
17
- updateConnectedInfo,
18
- parseNextWorkflow,
19
- createAxios,
20
- sleep,
21
- getConnectedInfo,
22
- getExtraHeaders,
23
- } from '../../utils';
24
-
25
- import translations from '../assets/locale';
26
- import useSecurity from './security';
27
- import usePageShow from './page-show';
28
- import { RELAY_SOCKET_PREFIX } from '../../constant';
29
-
30
- function getSocketHost(baseUrl) {
31
- const targetUrl = baseUrl || window.location.href;
32
- return new URL(targetUrl).host;
33
- }
34
-
35
- const fns = {};
36
- function createTokenFn({ action, prefix, checkFn, extraParams, baseUrl }) {
37
- const key = `${prefix}/token?${stringifyQuery(extraParams)}`;
38
- if (!fns[key]) {
39
- fns[key] = async function createToken() {
40
- const startTime = +new Date();
41
- const res = await checkFn(key, { headers: getExtraHeaders(baseUrl) });
42
- const endTime = +new Date();
43
- if (endTime - startTime < 500) {
44
- await sleep(500 - (endTime - startTime));
45
- }
46
- if (res?.data?.token) {
47
- return res.data;
48
- }
49
-
50
- if (res?.data?.error) {
51
- throw new Error(res.data.error);
52
- }
53
-
54
- const errorUrl = isUrl(prefix) ? prefix : `${baseUrl}${prefix}`;
55
- throw new Error(`Error generating ${action} QR Code from: ${errorUrl}`);
56
- };
57
- }
58
- return fns[key];
59
- }
60
-
61
- // 从 url params 中获取已存在的 session (token & connect url)
62
- const parseExistingSession = () => {
63
- try {
64
- const url = new URL(window.location.href);
65
- const connectUrlParam = url.searchParams.get('__connect_url__');
66
- if (!connectUrlParam) {
67
- return null;
68
- }
69
- const connectUrl = decodeConnectUrl(connectUrlParam);
70
- const token = parseTokenFromConnectUrl(connectUrl);
71
- return {
72
- token,
73
- url: connectUrl,
74
- };
75
- } catch (e) {
76
- return {
77
- error: e,
78
- };
79
- }
80
- };
81
-
82
- // FIXME: @zhanghan 当 connect 组件使用不同的 baseUrl 时,不应该直接从 window.blocklet 去取值
83
- const getAppId = () =>
84
- get(globalThis, 'blocklet.appPid') || get(globalThis, 'blocklet.appId') || get(globalThis, 'env.appId') || '';
85
- const getAppPrefix = () =>
86
- (get(globalThis, 'env.apiPrefix') || '/').replace(/\/$/, '').replace(RELAY_SOCKET_PREFIX, '');
87
- const getRelayChannel = (token) => `relay:${getAppId()}:${token}`;
88
- const getRelayProtocol = () => (window.location.protocol === 'https:' ? 'wss:' : 'ws:');
89
- const isSameOrigin = (baseUrl) => !baseUrl || new URL(baseUrl).origin === window.location.origin;
90
- const isSessionEnded = (status) => ['succeed', 'error', 'timeout', 'busy'].includes(status);
91
-
92
- export default function useToken({
93
- action,
94
- checkFn,
95
- checkInterval,
96
- checkTimeout,
97
- extraParams,
98
- locale,
99
- prefix,
100
- tokenKey,
101
- encKey,
102
- onError,
103
- onSuccess,
104
- baseUrl,
105
- autoConnect = true,
106
- forceConnected = true,
107
- saveConnect = true, // FIXME: @zhanghan 将来需要设置为默认 false,仅在需要的时候进行保存 login, switch/role/profile
108
- useSocket = true,
109
- provider = 'wallet',
110
- }) {
111
- /**
112
- * @type {import('../../types').TokenState}
113
- */
114
- const state = useReactive({
115
- checking: false,
116
- loading: false,
117
- token: '',
118
- url: '',
119
- store: null,
120
- status: 'created',
121
- error: '',
122
- mfaCode: 0,
123
- appInfo: null,
124
- memberAppInfo: null,
125
- connectedDid: '',
126
- saveConnect: false,
127
- inExistingSession: false,
128
- nextWorkflow: '',
129
- baseUrl,
130
- prefix: `${prefix}/${action}`,
131
- extraParams: omitBy(extraParams, isNil),
132
- checkFn,
133
- results: {},
134
- action,
135
- provider,
136
- reset() {
137
- this.error = '';
138
- this.status = 'created';
139
- },
140
- });
141
-
142
- // 使用 ref 存储 checkCount,避免计数时触发不必要的渲染
143
- const checkCountRef = useRef(0);
144
- const statusExtraParams = useRef(null);
145
-
146
- const translation = translations[locale] || translations.en;
147
- const existingSession = useCreation(() => parseExistingSession(), []);
148
- const maxCheckCount = Math.ceil(checkTimeout / checkInterval);
149
-
150
- const currentState = useReactive({
151
- onSuccessCalled: false,
152
- cancelWhenScannedCounter: 0,
153
- isSocketAvailable: false,
154
- });
155
-
156
- const { encryptKey, decrypt } = useSecurity();
157
-
158
- // scanned 状态下 cancel (Back 按钮) 操作的计数器, 每次 cancel 操作时 +1
159
- // - 外部可以传入 autoConnect 表示是否启用自动连接
160
- // - 计数器默认为 0, 表示还未进行过 cancel 操作
161
- // - 如果计数器 > 0, 说明用户进行过 cancel 操作
162
- // (临时禁用, 重新打开 did-connect 窗口时恢复)
163
- const browser = useBrowser();
164
- const visitorId = getVisitorId();
165
- const socket = useRef(null);
166
- const subscription = useRef(null);
167
-
168
- const params = {
169
- ...state.extraParams,
170
- locale,
171
- [tokenKey]: state.token || existingSession?.token,
172
- provider: state.provider,
173
- };
174
-
175
- const unsubscribe = async () => {
176
- // HACK: 如果 url 中已经存在 session,则不允许注销当前的 session(需要利用当前 session 作为 sourceToken 去创建一个新的 session)
177
- if (existingSession?.token && existingSession?.token === state.token) {
178
- return;
179
- }
180
- try {
181
- if (params[tokenKey] && state.status === 'created') {
182
- await state.checkFn(`${state.prefix}/timeout?${stringifyQuery(params)}`, {
183
- headers: getExtraHeaders(state.baseUrl),
184
- });
185
- }
186
- } catch (err) {
187
- // Do nothing
188
- }
189
-
190
- try {
191
- if (state.token && subscription.current) {
192
- await socket.current.unsubscribe(getRelayChannel(state.token));
193
- subscription.current = null;
194
- }
195
- } catch (err) {
196
- // Do nothing
197
- }
198
- };
199
-
200
- // HACK: 为了保证 createToken 函数永远都是最新的,需要使用 useMemoizedFn 来包裹
201
- const createToken = useMemoizedFn((...args) => {
202
- const innerParams = {
203
- [encKey]: encryptKey,
204
- ...state.extraParams,
205
- locale,
206
- forceConnected,
207
- // - autoConnect 请求参数用于控制服务端是否给钱包应用发送自动连接通知
208
- // - 使用 connect 组件时明确传入了 autoConnect = false, 则 autoConnect 请求参数 为 false
209
- // - 如果 cancelWhenScannedCounter > 0, 说明用户进行过 cancel 操作, 则临时禁用自动连接, autoConnect 请求参数 为 false
210
- // (避免 "无限自动连接问题")
211
- // - 如果上次连接设备 (connected_wallet_os) 不是 ios/android, 则禁用自动连接
212
- autoConnect:
213
- autoConnect &&
214
- // 如果是 wallet webview 环境, 不发送通知, 避免 wallet 连续弹出 auth 窗口 2 次 (#341)
215
- !(browser.wallet || browser.arcSphere) &&
216
- !currentState.cancelWhenScannedCounter &&
217
- ['ios', 'android'].includes(Cookie.get('connected_wallet_os')),
218
- provider: state.provider,
219
- };
220
-
221
- if (visitorId) {
222
- // NOTICE: 这个 visitorId 必须保留,才能在三方的 did connect 流程中正确传递
223
- innerParams.visitorId = visitorId;
224
- }
225
-
226
- const mergeInData = omit(statusExtraParams.current || {}, ['sourceAppPid', 'verbose']);
227
- Object.keys(mergeInData).forEach((x) => {
228
- if (!innerParams[x] || (x === encKey && mergeInData[x])) {
229
- innerParams[x] = mergeInData[x];
230
- }
231
- });
232
- if (existingSession?.token && !innerParams.sourceToken) {
233
- innerParams.sourceToken = existingSession?.token;
234
- }
235
- const fn = createTokenFn({
236
- action,
237
- prefix: state.prefix,
238
- baseUrl: state.baseUrl,
239
- checkFn: state.checkFn,
240
- extraParams: innerParams,
241
- });
242
- return fn(...args);
243
- });
244
-
245
- // 切换账户或者 token 过期之后需要重新生成
246
- const generate = useMemoizedFn(async (cleanup = true) => {
247
- if (cleanup) {
248
- unsubscribe();
249
- }
250
-
251
- try {
252
- state.loading = true;
253
- state.token = '';
254
- state.url = '';
255
- state.store = null;
256
- state.extraParams = omitBy(extraParams, isNil);
257
- // HACK: 更改了 createToken 中依赖的参数,需要强制等待 createToken 函数更新后,再调用 createToken 函数
258
- await sleep();
259
-
260
- const data = await createToken();
261
- const extra = data.extra || {};
262
-
263
- state.loading = false;
264
- state.token = data.token;
265
- state.url = data.url;
266
- state.status = 'created';
267
- state.error = '';
268
- checkCountRef.current = 0;
269
- state.appInfo = data.appInfo;
270
- state.memberAppInfo = data.memberAppInfo;
271
- state.connectedDid = extra.connectedDid;
272
- state.saveConnect = extra.saveConnect;
273
- } catch (err) {
274
- state.loading = false;
275
- state.status = 'error';
276
- state.error = `${translation.generateError}: ${err.message}`;
277
- }
278
- });
279
-
280
- // 每次 cancel 操作时计数器 +1 => 重新生成 token
281
- const cancelWhenScanned = useMemoizedFn(() => {
282
- currentState.cancelWhenScannedCounter++;
283
- });
284
- useEffect(() => {
285
- // FIXME: @zhanghan 在有 nw 的情况下,需要考虑如何进行 regenrate
286
- // 计数器 > 0, 说明人为触发了 cancel, 重新生成 token,但是只在没有 nextWorkflow 的情况下重新生成
287
- if (currentState.cancelWhenScannedCounter > 0) {
288
- if (extraParams.nw) {
289
- onError(new Error(translation.retryForbidden));
290
- } else {
291
- generate();
292
- }
293
- }
294
- // eslint-disable-next-line react-hooks/exhaustive-deps
295
- }, [currentState.cancelWhenScannedCounter]);
296
-
297
- useEffect(() => {
298
- const isSafari = /^((?!chrome|android).)*safari/i.test(globalThis.navigator.userAgent);
299
- const disableSocket = isSafari || Object.hasOwn(globalThis?.blocklet || {}, 'DID_CONNECT_DISABLE_SOCKET');
300
- // FIXME: @zhanghan websocket 默认支持跨域,这里为何要检查域名是同源
301
- if (!disableSocket && useSocket && isSameOrigin(state.baseUrl) && getAppId()) {
302
- const needReconnect =
303
- !socket.current || socket.current.isConnected() === false || socket.current.baseUrl !== state.baseUrl;
304
- if (needReconnect) {
305
- socket.current = new WsClient(
306
- `${getRelayProtocol()}//${getSocketHost(state.baseUrl)}${getAppPrefix()}${RELAY_SOCKET_PREFIX}/relay`,
307
- {
308
- longpollerTimeout: 5000, // connection timeout
309
- heartbeatIntervalMs: 30 * 1000,
310
- }
311
- );
312
- socket.current.baseUrl = state.baseUrl;
313
- socket.current.onOpen(() => {
314
- currentState.isSocketAvailable = true;
315
- });
316
- socket.current.connect();
317
- }
318
- } else if (currentState.isSocketAvailable) {
319
- currentState.isSocketAvailable = false;
320
- }
321
-
322
- return () => {
323
- if (socket.current) {
324
- socket.current.disconnect();
325
- socket.current = null;
326
- }
327
- };
328
- }, [state.baseUrl]); // eslint-disable-line react-hooks/exhaustive-deps
329
-
330
- const getCheckInterval = () => {
331
- if (!state.token) {
332
- return null;
333
- }
334
- if (state.loading) {
335
- return null;
336
- }
337
- if (isSessionEnded(state.status)) {
338
- return null;
339
- }
340
-
341
- return checkInterval;
342
- };
343
-
344
- // 任何导致 Connect 组件 unmounted 的操作 (比如 dialog 关闭) => 调用 timeout api 清除 token
345
- // 关闭时如果 token 状态处于 created,那么应该通知后端给删掉
346
- // 说明: 此处需要借助 useRef 在 useEffect return function 中访问 state
347
- const closeSessionRef = useRef(null);
348
- useEffect(() => {
349
- closeSessionRef.current = { state, params };
350
- });
351
- useEffect(() => {
352
- return () => {
353
- // eslint-disable-next-line no-shadow
354
- const { state, params } = closeSessionRef.current;
355
- if (state.status === 'created') {
356
- if (params[tokenKey]) {
357
- state.checkFn(`${state.prefix}/timeout?${stringifyQuery(params)}`, {
358
- headers: getExtraHeaders(state.baseUrl),
359
- });
360
- }
361
- }
362
- };
363
- // eslint-disable-next-line react-hooks/exhaustive-deps
364
- }, []);
365
-
366
- // Check auth token status
367
- const checkStatus = useMemoizedFn(async (force = false) => {
368
- if ((state.checking || document.hidden) && !force) {
369
- return null;
370
- }
371
-
372
- // NOTICE: 仅当状态为 created 时才去计算次数统计
373
- if (state.status === 'created') {
374
- checkCountRef.current++;
375
-
376
- // 超时判断:超过最大检查次数则标记为超时
377
- if (state.status !== 'timeout' && checkCountRef.current > maxCheckCount) {
378
- state.status = 'timeout';
379
- unsubscribe();
380
- return null;
381
- }
382
- }
383
-
384
- if (currentState.isSocketAvailable && !force) {
385
- return null;
386
- }
387
-
388
- try {
389
- state.checking = true;
390
- const res = await state.checkFn(`${state.prefix}/status?${stringifyQuery(params)}`, {
391
- headers: getExtraHeaders(state.baseUrl),
392
- });
393
- const { status, error: newError, mfaCode = 0 } = res.data;
394
-
395
- state.store = res.data;
396
- state.mfaCode = mfaCode;
397
- state.checking = false;
398
- if (state.status === 'scanned' && status === 'created') {
399
- // HACK: 不把 scanned 状态切换回 created
400
- } else {
401
- state.status = status;
402
- }
403
- if (status === 'error' && newError) {
404
- const err = new Error(newError);
405
- err.response = res;
406
- throw err;
407
- }
408
- return res.data;
409
- } catch (err) {
410
- const { response } = err;
411
- if (response?.status) {
412
- if (params[tokenKey]) {
413
- state.checkFn(`${state.prefix}/timeout?${stringifyQuery(params)}`, {
414
- headers: getExtraHeaders(state.baseUrl),
415
- });
416
- }
417
- const _msg = response?.data?.error ? response.data.error : err.message;
418
- const _err = new Error(_msg);
419
- _err.code = response.status;
420
- state.status = 'error';
421
- state.checking = false;
422
- state.error = _msg;
423
- onError(_err, state?.store, decrypt);
424
- } else {
425
- state.status = 'error';
426
- state.checking = false;
427
- state.error = translation.generateError;
428
- }
429
- }
430
- return null;
431
- });
432
-
433
- const applyExistingToken = async () => {
434
- try {
435
- if (existingSession.error) {
436
- throw existingSession.error;
437
- }
438
- state.loading = true;
439
- state.token = existingSession.token;
440
- state.url = existingSession.url;
441
- state.inExistingSession = true;
442
- let data = await checkStatus();
443
-
444
- // 1. 如果 existingSession 当前状态已经是 scanned,则刷新页面后,应该根据 existingSession.token 生成一个新的 session,继续完成后续的操作
445
- // 2. HACK: 如果当前 existingSession 已经产生过新的 session,则目前已经无法通过 existingSession 本身来监听变化了,需要再创建一个新的 session 才能完成监听状态
446
- if (['scanned'].includes(data?.status) || data?.sourceToken === existingSession?.token) {
447
- await generate(false);
448
- data = await checkStatus();
449
- }
450
- statusExtraParams.current = data?.extraParams || null;
451
- // existingSession.token 合法的初始状态必须是 created
452
- if (data?.status && !['created'].includes(data?.status)) {
453
- throw new Error(`${translation.invalidSessionStatus} [${data.status}]`);
454
- }
455
- } catch (e) {
456
- state.status = 'error';
457
- state.error = e.message;
458
- onError(e, state?.store, decrypt);
459
- } finally {
460
- state.loading = false;
461
- }
462
- };
463
-
464
- // eslint-disable-next-line react-hooks/exhaustive-deps
465
- useEffect(() => {
466
- if (!state.token && !state.store && !state.loading && !state.error) {
467
- // connect to existing session if any
468
- if (existingSession) {
469
- applyExistingToken();
470
- // Create our first token if we do not have one
471
- } else {
472
- generate(false);
473
- return;
474
- }
475
- }
476
-
477
- // Trigger on success if we completed the process
478
- if (state.status === 'succeed' && !currentState.onSuccessCalled) {
479
- // save connected_did to cookie if only on same origin
480
- const connectedInfo = getConnectedInfo({
481
- ...state.store,
482
- appInfo: state.appInfo,
483
- memberAppInfo: state.memberAppInfo,
484
- });
485
- if (isSameOrigin(state.baseUrl) && saveConnect && state.saveConnect && state.store.did) {
486
- updateConnectedInfo(connectedInfo, true);
487
- }
488
-
489
- if (state.store) {
490
- const { nextWorkflow } = state.store;
491
- // Switch to nextWorkflow if we have
492
- if (nextWorkflow) {
493
- try {
494
- const parsed = parseNextWorkflow(nextWorkflow, tokenKey);
495
- state.nextWorkflow = parsed?.nextWorkflow || nextWorkflow;
496
- Object.assign(state, parsed);
497
- state.store = null;
498
- state.error = '';
499
- state.url = '';
500
- state.appInfo = null;
501
- state.memberAppInfo = null;
502
- checkCountRef.current = 0;
503
- state.status = 'scanned';
504
- state.results = {
505
- ...state.results,
506
- [state.token]: state.store,
507
- };
508
- state.checkFn = createAxios({ baseURL: parsed.baseUrl, timeout: 8000 }).get;
509
- checkStatus();
510
- } catch (err) {
511
- console.error(`Invalid nextWorkflow: ${nextWorkflow}`, err);
512
- state.status = 'error';
513
- state.error = `Invalid nextWorkflow: ${nextWorkflow}: ${err.message}`;
514
- }
515
- } else {
516
- if (state.nextWorkflow) {
517
- state.nextWorkflow = '';
518
- state.url = '';
519
- }
520
-
521
- // trigger success callback when last workflow complete
522
- if (typeof onSuccess === 'function') {
523
- const results = Object.values({ ...state.results, [state.token]: state.store });
524
- currentState.onSuccessCalled = true;
525
- const result = results.length > 1 ? results : results[0];
526
- const clonedResult = cloneDeep(result);
527
- onSuccess(clonedResult, decrypt, {
528
- sourceAppPid: extraParams?.sourceAppPid,
529
- ...connectedInfo,
530
- });
531
- }
532
- }
533
- }
534
- return;
535
- }
536
-
537
- // FIXME: @zhanghan 待调查最近 ws 通道不会将 status 设置为 status,而直接进行到下一 nextWorkflow status 为 scanned 的情况
538
- // 此处仅用于处理上述情况
539
- if (state.store?.nextWorkflow) {
540
- const { nextWorkflow } = state.store;
541
- try {
542
- const parsed = parseNextWorkflow(nextWorkflow, tokenKey);
543
- state.nextWorkflow = parsed?.nextWorkflow || nextWorkflow;
544
- Object.assign(state, parsed);
545
- state.store = null;
546
- state.error = '';
547
- state.url = '';
548
- state.appInfo = null;
549
- state.memberAppInfo = null;
550
- checkCountRef.current = 0;
551
- state.status = 'scanned';
552
- state.results = {
553
- ...state.results,
554
- [state.token]: state.store,
555
- };
556
- state.checkFn = createAxios({ baseURL: parsed.baseUrl, timeout: 8000 }).get;
557
- checkStatus();
558
- } catch (err) {
559
- console.error(`Invalid nextWorkflow: ${nextWorkflow}`, err);
560
- state.status = 'error';
561
- state.error = `Invalid nextWorkflow: ${nextWorkflow}: ${err.message}`;
562
- }
563
- }
564
- });
565
- const subscriptionUpdatedAt = useRef(0);
566
-
567
- // Try to use websocket
568
- useEffect(() => {
569
- if (state.token && currentState.isSocketAvailable && socket.current) {
570
- let needSubscription = false;
571
- if (subscription.current) {
572
- if (subscription.current.token !== state.token) {
573
- socket.current.unsubscribe(getRelayChannel(subscription.current.token));
574
- needSubscription = true;
575
- }
576
- } else {
577
- needSubscription = true;
578
- }
579
-
580
- if (needSubscription) {
581
- subscription.current = socket.current.subscribe(getRelayChannel(state.token));
582
- // 每次重新监听时,需要重置 subscriptionUpdatedAt
583
- subscriptionUpdatedAt.current = 0;
584
- subscription.current.token = state.token;
585
- subscription.current.on('updated', ({ response }) => {
586
- const updatedAt = +new Date(response.updatedAt);
587
- if (updatedAt <= subscriptionUpdatedAt.current) {
588
- console.warn('Ignore outdated message', response);
589
- return;
590
- }
591
-
592
- subscriptionUpdatedAt.current = updatedAt;
593
- let { status, error } = response;
594
-
595
- if (status === 'forbidden') {
596
- error = translation.forbidden;
597
- status = 'error';
598
- }
599
- state.status = status;
600
- state.mfaCode = response.mfaCode || 0;
601
- state.store = response;
602
- state.error = error;
603
- // ws 通道发现的错误,也需要 onError 回调
604
- if (error) {
605
- onError(new Error(error), state?.store, decrypt);
606
- }
607
- });
608
- }
609
- }
610
- }, [state.token, currentState.isSocketAvailable, socket.current]); // eslint-disable-line react-hooks/exhaustive-deps
611
-
612
- useInterval(checkStatus, getCheckInterval());
613
-
614
- // Restore session status when page lost focus and get focus again
615
- usePageShow(() => {
616
- // HACK: 由于 safari 的特性,在页面不可见时,无法正常发起请求,所以需要在页面可见时强制发送一次请求来更新状态
617
- if (state.token && isSessionEnded(state.status) === false) {
618
- state.checking = false;
619
- // HACK: 不能在页面可见时立刻发起请求,仍然可能会被 safari 拦截,需要等一段时间
620
- setTimeout(() => {
621
- checkStatus(true);
622
- }, 100);
623
- }
624
- });
625
-
626
- return { state, generate, cancelWhenScanned };
627
- }
@@ -1,69 +0,0 @@
1
- import { useCreation } from 'ahooks';
2
- import { getApps, getFederatedEnabled, getMaster } from '@arcblock/ux/lib/Util/federated';
3
- import isUndefined from 'lodash/isUndefined';
4
- import { use } from 'react';
5
- import { SessionContext } from '../../Session/context';
6
-
7
- /**
8
- *
9
- * @param {object} param
10
- * @param {object} param.blocklet
11
- * @param {string} param.action
12
- * @param {string} [param.sourceAppPid]
13
- * @param {object} param.connectState
14
- * @param {boolean} [param.enableSwitchApp]
15
- * @returns
16
- */
17
- export default function useApps({ blocklet, action, sourceAppPid, connectState, enableSwitchApp }) {
18
- const sessionContext = use(SessionContext);
19
-
20
- const canSwitchApp = useCreation(() => {
21
- if (enableSwitchApp === true) {
22
- return true;
23
- }
24
- if (enableSwitchApp === false) {
25
- return false;
26
- }
27
-
28
- if (sessionContext?.session?.user) {
29
- if (!['login', 'invite', 'connect-to-did-space', 'connect-to-did-domain'].includes(action)) {
30
- return false;
31
- }
32
- }
33
- return true;
34
- }, [action, sessionContext?.session?.user]);
35
-
36
- const autoGenerateSourceAppPid = useCreation(() => {
37
- if (isUndefined(sourceAppPid)) {
38
- const federatedEnabled = getFederatedEnabled(blocklet);
39
- const master = getMaster(blocklet);
40
- if (federatedEnabled && master?.appPid) {
41
- return master.appPid;
42
- }
43
- }
44
- return sourceAppPid;
45
- }, [sourceAppPid, blocklet]);
46
-
47
- const appInfoList = useCreation(() => {
48
- const appList = getApps(blocklet);
49
-
50
- return appList.filter((x) => {
51
- if (canSwitchApp) {
52
- return true;
53
- }
54
-
55
- if (!isUndefined(sourceAppPid)) {
56
- return x.sourceAppPid === sourceAppPid;
57
- }
58
- if (!isUndefined(connectState.sourceAppPid)) {
59
- return x.sourceAppPid === connectState.sourceAppPid;
60
- }
61
- if (sessionContext?.session?.user) {
62
- return x.sourceAppPid === sessionContext.session.user.sourceAppPid;
63
- }
64
- return x.sourceAppPid === autoGenerateSourceAppPid;
65
- });
66
- }, [canSwitchApp, sourceAppPid, connectState.sourceAppPid, autoGenerateSourceAppPid, sessionContext?.session?.user]);
67
-
68
- return { appInfoList, autoGenerateSourceAppPid, canSwitchApp };
69
- }