@arcblock/did-connect-react 3.4.0 → 3.4.2

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 (28) hide show
  1. package/lib/Connect/assets/locale.js +4 -2
  2. package/lib/Connect/components/login-item/connect-choose-list.js +120 -114
  3. package/lib/Connect/components/login-item/connect-provider-list.js +187 -180
  4. package/lib/Connect/components/login-item/login-method-item.js +63 -47
  5. package/lib/Connect/components/login-item/passkey-login-item.js +17 -15
  6. package/lib/Connect/components/login-item/wallet-login-options.js +42 -40
  7. package/lib/Connect/connect.js +116 -117
  8. package/lib/Connect/contexts/state.js +73 -64
  9. package/lib/Connect/hooks/provider-list.js +43 -33
  10. package/lib/Connect/hooks/token.js +121 -122
  11. package/lib/Connect/plugins/email/index.js +15 -12
  12. package/lib/Connect/plugins/email/list-item.js +19 -18
  13. package/lib/Passkey/actions.js +80 -75
  14. package/lib/package.json.js +1 -1
  15. package/package.json +5 -5
  16. package/src/Connect/assets/locale.js +2 -0
  17. package/src/Connect/components/login-item/connect-choose-list.jsx +12 -5
  18. package/src/Connect/components/login-item/connect-provider-list.jsx +11 -4
  19. package/src/Connect/components/login-item/login-method-item.jsx +24 -7
  20. package/src/Connect/components/login-item/passkey-login-item.jsx +3 -1
  21. package/src/Connect/components/login-item/wallet-login-options.jsx +3 -2
  22. package/src/Connect/connect.jsx +36 -35
  23. package/src/Connect/contexts/state.jsx +10 -0
  24. package/src/Connect/hooks/provider-list.js +43 -17
  25. package/src/Connect/hooks/token.js +13 -13
  26. package/src/Connect/plugins/email/index.jsx +3 -0
  27. package/src/Connect/plugins/email/list-item.jsx +3 -2
  28. package/src/Passkey/actions.jsx +5 -0
@@ -9,6 +9,7 @@ import { translate } from '@arcblock/ux/lib/Locale/util';
9
9
  import { getCurrentApp, getMaster } from '@arcblock/ux/lib/Util/federated';
10
10
  import { getDIDMotifInfo } from '@arcblock/did-motif';
11
11
  import { getDIDColor, isEthereumDid } from '@arcblock/ux/lib/Util';
12
+ import { GA_LAST_LOGIN_METHOD } from '@arcblock/ux/lib/withTracker/constant';
12
13
 
13
14
  import useApps from '../hooks/use-apps';
14
15
  import { SessionContext } from '../../Session/context';
@@ -68,6 +69,13 @@ function StateProvider({
68
69
  sourceAppPid: extraParams?.sourceAppPid,
69
70
  enableSwitchApp: extraParams?.forceSwitch || extraParams?.enableSwitchApp,
70
71
  });
72
+ const lastLoginMethod = useCreation(() => {
73
+ try {
74
+ return localStorage.getItem(GA_LAST_LOGIN_METHOD);
75
+ } catch (error) {
76
+ return '';
77
+ }
78
+ }, []);
71
79
 
72
80
  const rootRef = useRef(null);
73
81
  const size = useSize(rootRef);
@@ -180,6 +188,8 @@ function StateProvider({
180
188
  // 控制钱包登录的显示
181
189
  showWalletOptions,
182
190
  setShowWalletOptions,
191
+ // 用于记录上一次登录方式
192
+ lastLoginMethod,
183
193
  };
184
194
  }, [
185
195
  browser.wallet,
@@ -56,6 +56,7 @@ export default function useProviderList({
56
56
  mode = 'dialog',
57
57
  blocklet = globalThis.blocklet,
58
58
  isSmallView = false,
59
+ lastLoginMethod = '',
59
60
  }) {
60
61
  const [loadingProviderList, setLoadingProviderList] = useState(false);
61
62
  const actionList = ['login', 'invite', 'connect-to-did-space', 'connect-to-did-domain', 'destroy-self'];
@@ -72,26 +73,51 @@ export default function useProviderList({
72
73
  );
73
74
 
74
75
  const showedLoginProviderList = useCreation(() => {
75
- const result = loginProviderList.filter((item) => {
76
- if (item.name === LOGIN_PROVIDER.WALLET) {
77
- return allowWallet;
78
- }
79
- if (item.name === LOGIN_PROVIDER.PASSKEY) {
80
- return passkeyBehavior !== 'none' && !browser.wallet && !browser.arcSphere;
81
- }
82
- if (item.name === LOGIN_PROVIDER.EMAIL) {
83
- return blocklet?.settings?.notification?.email?.enabled ?? false;
84
- }
76
+ const result = loginProviderList
77
+ .filter((item) => {
78
+ if (item.name === LOGIN_PROVIDER.WALLET) {
79
+ return allowWallet;
80
+ }
81
+ if (item.name === LOGIN_PROVIDER.PASSKEY) {
82
+ return passkeyBehavior !== 'none' && !browser.wallet && !browser.arcSphere;
83
+ }
84
+ if (item.name === LOGIN_PROVIDER.EMAIL) {
85
+ return blocklet?.settings?.notification?.email?.enabled ?? false;
86
+ }
85
87
 
86
- if (item.type === 'oauth') {
87
- if (!actionList.includes(action)) {
88
- return false;
88
+ if (item.type === 'oauth') {
89
+ if (!actionList.includes(action)) {
90
+ return false;
91
+ }
89
92
  }
90
- }
91
- return true;
92
- });
93
+ return true;
94
+ })
95
+ .sort((a, b) => {
96
+ // 优先处理 lastLoginMethod:匹配的项目排在最前面
97
+ if (lastLoginMethod) {
98
+ const aIsLastMethod = a.provider === lastLoginMethod;
99
+ const bIsLastMethod = b.provider === lastLoginMethod;
100
+ if (aIsLastMethod && !bIsLastMethod) {
101
+ return -1;
102
+ }
103
+ if (!aIsLastMethod && bIsLastMethod) {
104
+ return 1;
105
+ }
106
+ }
107
+ // 然后按照 order 属性排序
108
+ if (!isNil(a?.order) && !isNil(b?.order)) {
109
+ return a.order - b.order;
110
+ }
111
+ if (!isNil(a?.order)) {
112
+ return -1;
113
+ }
114
+ if (!isNil(b?.order)) {
115
+ return 1;
116
+ }
117
+ return 0;
118
+ });
93
119
  return result;
94
- }, [loginProviderList]);
120
+ }, [loginProviderList, lastLoginMethod]);
95
121
 
96
122
  const hideQRCode = useCreation(() => {
97
123
  const isMobileView = mode === 'drawer' || (isSmallView && browser.mobile.any);
@@ -119,7 +119,6 @@ export default function useToken({
119
119
  store: null,
120
120
  status: 'created',
121
121
  error: '',
122
- checkCount: 0,
123
122
  mfaCode: 0,
124
123
  appInfo: null,
125
124
  memberAppInfo: null,
@@ -140,6 +139,8 @@ export default function useToken({
140
139
  },
141
140
  });
142
141
 
142
+ // 使用 ref 存储 checkCount,避免计数时触发不必要的渲染
143
+ const checkCountRef = useRef(0);
143
144
  const statusExtraParams = useRef(null);
144
145
 
145
146
  const translation = translations[locale] || translations.en;
@@ -264,7 +265,7 @@ export default function useToken({
264
265
  state.url = data.url;
265
266
  state.status = 'created';
266
267
  state.error = '';
267
- state.checkCount = 0;
268
+ checkCountRef.current = 0;
268
269
  state.appInfo = data.appInfo;
269
270
  state.memberAppInfo = data.memberAppInfo;
270
271
  state.connectedDid = extra.connectedDid;
@@ -363,7 +364,6 @@ export default function useToken({
363
364
  }, []);
364
365
 
365
366
  // Check auth token status
366
- // FIXME: @zhanghan 这个函数会造成 connect 组件每秒重渲染一次,需要从 setState 入手,优化这个问题。优化的原则是,页面数据没有变更时,不应该触发重渲染
367
367
  const checkStatus = useMemoizedFn(async (force = false) => {
368
368
  if ((state.checking || document.hidden) && !force) {
369
369
  return null;
@@ -371,7 +371,14 @@ export default function useToken({
371
371
 
372
372
  // NOTICE: 仅当状态为 created 时才去计算次数统计
373
373
  if (state.status === 'created') {
374
- state.checkCount++;
374
+ checkCountRef.current++;
375
+
376
+ // 超时判断:超过最大检查次数则标记为超时
377
+ if (state.status !== 'timeout' && checkCountRef.current > maxCheckCount) {
378
+ state.status = 'timeout';
379
+ unsubscribe();
380
+ return null;
381
+ }
375
382
  }
376
383
 
377
384
  if (currentState.isSocketAvailable && !force) {
@@ -467,13 +474,6 @@ export default function useToken({
467
474
  }
468
475
  }
469
476
 
470
- // Mark token as expired if exceed max retry count
471
- if (state.status !== 'timeout' && state.checkCount > maxCheckCount) {
472
- state.status = 'timeout';
473
- unsubscribe();
474
- return;
475
- }
476
-
477
477
  // Trigger on success if we completed the process
478
478
  if (state.status === 'succeed' && !currentState.onSuccessCalled) {
479
479
  // save connected_did to cookie if only on same origin
@@ -499,7 +499,7 @@ export default function useToken({
499
499
  state.url = '';
500
500
  state.appInfo = null;
501
501
  state.memberAppInfo = null;
502
- state.checkCount = 0;
502
+ checkCountRef.current = 0;
503
503
  state.status = 'scanned';
504
504
  state.results = {
505
505
  ...state.results,
@@ -547,7 +547,7 @@ export default function useToken({
547
547
  state.url = '';
548
548
  state.appInfo = null;
549
549
  state.memberAppInfo = null;
550
- state.checkCount = 0;
550
+ checkCountRef.current = 0;
551
551
  state.status = 'scanned';
552
552
  state.results = {
553
553
  ...state.results,
@@ -5,6 +5,7 @@ import { translate } from '@arcblock/ux/lib/Locale/util';
5
5
 
6
6
  import EmailListItem from './list-item';
7
7
  import EmailPlaceholder from './placeholder';
8
+ import defaultTranslations from '../../assets/locale';
8
9
 
9
10
  export default function useEmailPlugin({ baseUrl }) {
10
11
  const state = useReactive({
@@ -38,6 +39,7 @@ export default function useEmailPlugin({ baseUrl }) {
38
39
  const icon = mailOutlineRoundedIcon;
39
40
  const translations = {
40
41
  zh: {
42
+ ...defaultTranslations.zh,
41
43
  email: '邮箱地址',
42
44
  emailPlaceholder: '请输入邮箱地址',
43
45
  sendCode: '发送验证邮件',
@@ -50,6 +52,7 @@ export default function useEmailPlugin({ baseUrl }) {
50
52
  emailRequired: '邮箱地址不能为空',
51
53
  },
52
54
  en: {
55
+ ...defaultTranslations.en,
53
56
  email: 'Email',
54
57
  emailPlaceholder: 'Enter your email address',
55
58
  sendCode: 'Send Verification Email',
@@ -8,9 +8,9 @@ import LoginMethodItem from '../../components/login-item/login-method-item';
8
8
  import { useStateContext } from '../../contexts/state';
9
9
 
10
10
  export default function EmailListItem({ ...rest }) {
11
- const { setSelectedPlugin, getPlugin, connectState } = useStateContext();
11
+ const { setSelectedPlugin, getPlugin, connectState, lastLoginMethod } = useStateContext();
12
12
  const handleConnect = useMemoizedFn(() => {
13
- localStorage.setItem(GA_LAST_LOGIN_METHOD, 'email');
13
+ localStorage.setItem(GA_LAST_LOGIN_METHOD, LOGIN_PROVIDER.EMAIL);
14
14
  const plugin = getPlugin(LOGIN_PROVIDER.EMAIL);
15
15
  plugin.state.reset();
16
16
  plugin.state.status = 'creating';
@@ -21,6 +21,7 @@ export default function EmailListItem({ ...rest }) {
21
21
  return (
22
22
  <LoginMethodItem
23
23
  {...rest}
24
+ isLatest={lastLoginMethod && lastLoginMethod === LOGIN_PROVIDER.EMAIL}
24
25
  title={LOGIN_PROVIDER_NAME[LOGIN_PROVIDER.EMAIL]}
25
26
  icon={mailFilledIcon}
26
27
  onClick={handleConnect}
@@ -67,6 +67,7 @@ export default function PasskeyActions({
67
67
  sx = {},
68
68
  mode = 'normal',
69
69
  onClick = noop,
70
+ isLatest = false,
70
71
  }) {
71
72
  const currentAction = useRef('');
72
73
  const passkeyDialogRef = useRef(null);
@@ -149,6 +150,8 @@ export default function PasskeyActions({
149
150
  <>
150
151
  {['both', 'only-existing'].includes(behavior) ? (
151
152
  <PasskeyAction
153
+ isLatest={isLatest}
154
+ t={t}
152
155
  action="verifying"
153
156
  state={passkeyState}
154
157
  title={t('usePasskey')}
@@ -167,6 +170,7 @@ export default function PasskeyActions({
167
170
  {['both', 'only-new'].includes(behavior) ? (
168
171
  <>
169
172
  <PasskeyAction
173
+ t={t}
170
174
  action="creating"
171
175
  state={passkeyState}
172
176
  title={createButtonText || t('createPasskey')}
@@ -209,4 +213,5 @@ PasskeyActions.propTypes = {
209
213
  mode: PropTypes.oneOf(['simple', 'normal']),
210
214
  onClick: PropTypes.func,
211
215
  ref: PropTypes.any,
216
+ isLatest: PropTypes.bool,
212
217
  };