@arcblock/did-connect-react 3.1.28 → 3.1.31
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/Connect/components/login-item/connect-choose-list.js +104 -102
- package/lib/Connect/plugins/email/list-item.js +13 -11
- package/lib/OAuth/context.js +98 -89
- package/lib/OAuth/passport-switcher.js +3 -112
- package/lib/Passkey/context.js +65 -54
- package/lib/Session/handler.js +55 -0
- package/lib/Session/hooks/use-session-token.js +109 -105
- package/lib/Session/index.js +455 -425
- package/lib/Session/libs/constants.js +5 -2
- package/lib/components/PassportSwitcher.js +128 -0
- package/lib/package.json.js +1 -1
- package/package.json +5 -5
- package/src/Connect/components/login-item/connect-choose-list.jsx +6 -0
- package/src/Connect/plugins/email/list-item.jsx +2 -0
- package/src/OAuth/context.jsx +15 -6
- package/src/OAuth/passport-switcher.jsx +2 -133
- package/src/Passkey/context.jsx +13 -5
- package/src/Session/handler.jsx +98 -0
- package/src/Session/hooks/use-session-token.js +12 -0
- package/src/Session/index.jsx +103 -31
- package/src/Session/libs/constants.js +3 -0
- package/src/components/PassportSwitcher.jsx +160 -0
package/src/Session/index.jsx
CHANGED
|
@@ -28,8 +28,9 @@ import bridge from '@arcblock/bridge';
|
|
|
28
28
|
import { getFederatedEnabled } from '@arcblock/ux/lib/Util/federated';
|
|
29
29
|
import noop from 'lodash/noop';
|
|
30
30
|
import isNil from 'lodash/isNil';
|
|
31
|
+
import { ReactGA } from '@arcblock/ux/lib/withTracker';
|
|
31
32
|
|
|
32
|
-
import { useState } from 'react';
|
|
33
|
+
import { useEffect, useState } from 'react';
|
|
33
34
|
import createStorage from '../Storage';
|
|
34
35
|
import {
|
|
35
36
|
getAppId,
|
|
@@ -68,6 +69,14 @@ import { getWalletDid } from '../User/use-did';
|
|
|
68
69
|
import useDIDSpacesGuide from './did-spaces-guide';
|
|
69
70
|
import { FederatedProvider, useFederatedContext } from '../Federated';
|
|
70
71
|
import useVerify from './hooks/use-verify';
|
|
72
|
+
import {
|
|
73
|
+
gaBindWalletFailedHandler,
|
|
74
|
+
gaBindWalletSuccessHandler,
|
|
75
|
+
gaLoginFailedHandler,
|
|
76
|
+
gaLoginSuccessHandler,
|
|
77
|
+
gaSwitchPassportFailedHandler,
|
|
78
|
+
gaSwitchPassportSuccessHandler,
|
|
79
|
+
} from './handler';
|
|
71
80
|
|
|
72
81
|
export * from './libs';
|
|
73
82
|
|
|
@@ -261,6 +270,24 @@ function createSessionContext(
|
|
|
261
270
|
});
|
|
262
271
|
|
|
263
272
|
const events = useCreation(() => new EventEmitter(), []);
|
|
273
|
+
// Register event listeners with cleanup to prevent memory leaks
|
|
274
|
+
useEffect(() => {
|
|
275
|
+
events.on(EVENTS.LOGIN, gaLoginSuccessHandler);
|
|
276
|
+
events.on(EVENTS.LOGIN_FAILED, gaLoginFailedHandler);
|
|
277
|
+
events.on(EVENTS.SWITCH_PASSPORT, gaSwitchPassportSuccessHandler);
|
|
278
|
+
events.on(EVENTS.SWITCH_PASSPORT_FAILED, gaSwitchPassportFailedHandler);
|
|
279
|
+
events.on(EVENTS.BIND_WALLET, gaBindWalletSuccessHandler);
|
|
280
|
+
events.on(EVENTS.BIND_WALLET_FAILED, gaBindWalletFailedHandler);
|
|
281
|
+
return () => {
|
|
282
|
+
events.off(EVENTS.LOGIN, gaLoginSuccessHandler);
|
|
283
|
+
events.off(EVENTS.LOGIN_FAILED, gaLoginFailedHandler);
|
|
284
|
+
events.off(EVENTS.SWITCH_PASSPORT, gaSwitchPassportSuccessHandler);
|
|
285
|
+
events.off(EVENTS.SWITCH_PASSPORT_FAILED, gaSwitchPassportFailedHandler);
|
|
286
|
+
events.off(EVENTS.BIND_WALLET, gaBindWalletSuccessHandler);
|
|
287
|
+
events.off(EVENTS.BIND_WALLET_FAILED, gaBindWalletFailedHandler);
|
|
288
|
+
};
|
|
289
|
+
}, [events]);
|
|
290
|
+
|
|
264
291
|
/**
|
|
265
292
|
* 用于包装 did-connect 的 open 和 close 事件,添加一些默认的行为
|
|
266
293
|
* 1. 在 open 中添加的事件订阅,did-connect close 时需要取消订阅
|
|
@@ -554,6 +581,10 @@ function createSessionContext(
|
|
|
554
581
|
state.loading = false;
|
|
555
582
|
events.emit('logout');
|
|
556
583
|
|
|
584
|
+
ReactGA.set({
|
|
585
|
+
user_id: undefined,
|
|
586
|
+
});
|
|
587
|
+
|
|
557
588
|
if (typeof done === 'function') {
|
|
558
589
|
done();
|
|
559
590
|
}
|
|
@@ -623,12 +654,17 @@ function createSessionContext(
|
|
|
623
654
|
);
|
|
624
655
|
wrapOnceEmit('switch-did', done);
|
|
625
656
|
handleLoginResult({ ...result, encrypted: false });
|
|
626
|
-
handleRefreshUser({ showProgress: true })
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
657
|
+
handleRefreshUser({ showProgress: true })
|
|
658
|
+
.then(async () => {
|
|
659
|
+
await sleep(200);
|
|
660
|
+
events.emit(EVENTS.LOGIN, result, decrypt, session.current);
|
|
661
|
+
notifyBridge(session.current);
|
|
662
|
+
await connectToDidSpaceForFullAccess();
|
|
663
|
+
})
|
|
664
|
+
.catch((error) => {
|
|
665
|
+
events.emit(EVENTS.LOGIN_FAILED, error);
|
|
666
|
+
throw error;
|
|
667
|
+
});
|
|
632
668
|
});
|
|
633
669
|
} else {
|
|
634
670
|
if (isUndefined(extraParams?.forceConnected)) {
|
|
@@ -802,6 +838,10 @@ function createSessionContext(
|
|
|
802
838
|
}
|
|
803
839
|
});
|
|
804
840
|
|
|
841
|
+
const onSwitchPassportFailed = useMemoizedFn((error) => {
|
|
842
|
+
events.emit(EVENTS.SWITCH_PASSPORT_FAILED, error);
|
|
843
|
+
});
|
|
844
|
+
|
|
805
845
|
const onLogin = useMemoizedFn(async (result) => {
|
|
806
846
|
if (state.action === 'switch-passport') {
|
|
807
847
|
onSwitchPassport(result);
|
|
@@ -823,6 +863,10 @@ function createSessionContext(
|
|
|
823
863
|
}
|
|
824
864
|
});
|
|
825
865
|
|
|
866
|
+
const onLoginFailed = useMemoizedFn((error) => {
|
|
867
|
+
events.emit(EVENTS.LOGIN_FAILED, error);
|
|
868
|
+
});
|
|
869
|
+
|
|
826
870
|
const onBindWallet = useMemoizedFn((result) => {
|
|
827
871
|
pageState.extraParams = {};
|
|
828
872
|
pageState.options = {};
|
|
@@ -839,6 +883,10 @@ function createSessionContext(
|
|
|
839
883
|
// NOTICE: auth0 绑定 wallet 在新的版本中,不会再要求切换 login_token,这里的处理是为了兼容有无 sessionToken 的两种情况,都可以顺利完成当前绑定操作
|
|
840
884
|
});
|
|
841
885
|
|
|
886
|
+
const onBindWalletFailed = useMemoizedFn((error) => {
|
|
887
|
+
events.emit(EVENTS.BIND_WALLET_FAILED, error, session);
|
|
888
|
+
});
|
|
889
|
+
|
|
842
890
|
const onSwitchProfile = useMemoizedFn((result) => {
|
|
843
891
|
debug('onSwitchProfile', { result });
|
|
844
892
|
pageState.extraParams = {};
|
|
@@ -888,9 +936,14 @@ function createSessionContext(
|
|
|
888
936
|
args,
|
|
889
937
|
err,
|
|
890
938
|
});
|
|
939
|
+
callbacks[`${action}-failed`]?.(err, ...args);
|
|
891
940
|
}
|
|
892
941
|
});
|
|
893
942
|
|
|
943
|
+
const onError = useMemoizedFn((action, err, ...args) => {
|
|
944
|
+
callbacks[`${action}-failed`]?.(err, ...args);
|
|
945
|
+
});
|
|
946
|
+
|
|
894
947
|
const {
|
|
895
948
|
federatedMaster,
|
|
896
949
|
federatedEnabled,
|
|
@@ -1011,7 +1064,8 @@ function createSessionContext(
|
|
|
1011
1064
|
onClose(state.action, ...args);
|
|
1012
1065
|
},
|
|
1013
1066
|
onError(err) {
|
|
1014
|
-
console.error(err);
|
|
1067
|
+
console.error(state.action, err);
|
|
1068
|
+
onError(state.action, err, session.current);
|
|
1015
1069
|
},
|
|
1016
1070
|
};
|
|
1017
1071
|
state.open = true;
|
|
@@ -1148,12 +1202,17 @@ function createSessionContext(
|
|
|
1148
1202
|
true
|
|
1149
1203
|
);
|
|
1150
1204
|
handleLoginResult({ ...result, encrypted: false });
|
|
1151
|
-
handleRefreshUser({ showProgress: true })
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1205
|
+
handleRefreshUser({ showProgress: true })
|
|
1206
|
+
.then(async () => {
|
|
1207
|
+
await sleep(200);
|
|
1208
|
+
events.emit(EVENTS.LOGIN, result, decrypt, session.current);
|
|
1209
|
+
notifyBridge(session.current);
|
|
1210
|
+
await connectToDidSpaceForFullAccess();
|
|
1211
|
+
})
|
|
1212
|
+
.catch((error) => {
|
|
1213
|
+
events.emit(EVENTS.LOGIN_FAILED, error);
|
|
1214
|
+
throw error;
|
|
1215
|
+
});
|
|
1157
1216
|
});
|
|
1158
1217
|
close();
|
|
1159
1218
|
return;
|
|
@@ -1253,7 +1312,7 @@ function createSessionContext(
|
|
|
1253
1312
|
resolve();
|
|
1254
1313
|
},
|
|
1255
1314
|
onError(err) {
|
|
1256
|
-
|
|
1315
|
+
events.emit(EVENTS.LOGIN_FAILED, err);
|
|
1257
1316
|
reject(err);
|
|
1258
1317
|
},
|
|
1259
1318
|
});
|
|
@@ -1293,22 +1352,27 @@ function createSessionContext(
|
|
|
1293
1352
|
WrapDid,
|
|
1294
1353
|
getUserSessions,
|
|
1295
1354
|
async loginUserSession(userSessionItem) {
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1355
|
+
try {
|
|
1356
|
+
const loginResult = await loginUserSession(userSessionItem);
|
|
1357
|
+
updateConnectedInfo(
|
|
1358
|
+
{
|
|
1359
|
+
connected_did: userSessionItem.user.did,
|
|
1360
|
+
connected_pk: userSessionItem.user.pk,
|
|
1361
|
+
connected_wallet_os: userSessionItem.extra.walletOS,
|
|
1362
|
+
connected_app: getAppId(),
|
|
1363
|
+
},
|
|
1364
|
+
true
|
|
1365
|
+
);
|
|
1366
|
+
handleLoginResult({ ...loginResult, encrypted: false });
|
|
1367
|
+
await handleRefreshUser({ showProgress: true });
|
|
1368
|
+
await sleep(200);
|
|
1369
|
+
events.emit(EVENTS.LOGIN, loginResult, decrypt, session.current);
|
|
1370
|
+
notifyBridge(session.current);
|
|
1371
|
+
await connectToDidSpaceForFullAccess();
|
|
1372
|
+
} catch (err) {
|
|
1373
|
+
events.emit(EVENTS.LOGIN_FAILED, err);
|
|
1374
|
+
throw err;
|
|
1375
|
+
}
|
|
1312
1376
|
},
|
|
1313
1377
|
unReadCount,
|
|
1314
1378
|
setUnReadCount,
|
|
@@ -1332,7 +1396,10 @@ function createSessionContext(
|
|
|
1332
1396
|
login: onLogin,
|
|
1333
1397
|
'switch-profile': onSwitchProfile,
|
|
1334
1398
|
'switch-passport': onSwitchPassport,
|
|
1399
|
+
'switch-passport-failed': onSwitchPassportFailed,
|
|
1335
1400
|
'bind-wallet': onBindWallet,
|
|
1401
|
+
'bind-wallet-failed': onBindWalletFailed,
|
|
1402
|
+
'login-failed': onLoginFailed,
|
|
1336
1403
|
};
|
|
1337
1404
|
|
|
1338
1405
|
// user change 事件
|
|
@@ -1455,6 +1522,9 @@ function createSessionContext(
|
|
|
1455
1522
|
state.initialized = true;
|
|
1456
1523
|
return;
|
|
1457
1524
|
}
|
|
1525
|
+
} catch (err) {
|
|
1526
|
+
events.emit(EVENTS.LOGIN_FAILED, err);
|
|
1527
|
+
throw err;
|
|
1458
1528
|
} finally {
|
|
1459
1529
|
state.loading = false;
|
|
1460
1530
|
}
|
|
@@ -1540,6 +1610,7 @@ function createSessionContext(
|
|
|
1540
1610
|
});
|
|
1541
1611
|
}
|
|
1542
1612
|
} catch (err) {
|
|
1613
|
+
events.emit(EVENTS.LOGIN_FAILED, err);
|
|
1543
1614
|
logger.error('Failed to autoLogin ArcSphere', err);
|
|
1544
1615
|
}
|
|
1545
1616
|
}
|
|
@@ -1561,6 +1632,7 @@ function createSessionContext(
|
|
|
1561
1632
|
onBindOAuth: () => handleRefreshUser(),
|
|
1562
1633
|
onAddPasskey: () => handleRefreshUser(),
|
|
1563
1634
|
onRemovePasskey: () => handleRefreshUser(),
|
|
1635
|
+
session: session.current,
|
|
1564
1636
|
};
|
|
1565
1637
|
|
|
1566
1638
|
return (
|
|
@@ -2,13 +2,16 @@
|
|
|
2
2
|
|
|
3
3
|
export const EVENTS = {
|
|
4
4
|
LOGIN: Symbol('login'),
|
|
5
|
+
LOGIN_FAILED: Symbol('loginFailed'),
|
|
5
6
|
CANCEL_LOGIN: Symbol('cancelLogin'),
|
|
6
7
|
LOGOUT: Symbol('logout'),
|
|
7
8
|
SWITCH_PROFILE: Symbol('switchProfile'),
|
|
8
9
|
CANCEL_SWITCH_PROFILE: Symbol('cancelSwitchProfile'),
|
|
9
10
|
SWITCH_PASSPORT: Symbol('switchPassport'),
|
|
11
|
+
SWITCH_PASSPORT_FAILED: Symbol('switchPassportFailed'),
|
|
10
12
|
CANCEL_SWITCH_PASSPORT: Symbol('cancelSwitchPassport'),
|
|
11
13
|
BIND_WALLET: Symbol('bindWallet'),
|
|
14
|
+
BIND_WALLET_FAILED: Symbol('bindWalletFailed'),
|
|
12
15
|
CANCEL_BIND_WALLET: Symbol('cancelBindWallet'),
|
|
13
16
|
DID_SPACE_CONNECTED: Symbol('didSpaceConnected'),
|
|
14
17
|
};
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
/* eslint-disable react/prop-types */
|
|
2
|
+
import pick from 'lodash/pick';
|
|
3
|
+
import { Confirm } from '@arcblock/ux/lib/Dialog';
|
|
4
|
+
import { Box } from '@mui/material';
|
|
5
|
+
import Toast from '@arcblock/ux/lib/Toast';
|
|
6
|
+
import CardSelector from '@arcblock/ux/lib/CardSelector';
|
|
7
|
+
import { translate } from '@arcblock/ux/lib/Locale/util';
|
|
8
|
+
import { useCreation, useMemoizedFn } from 'ahooks';
|
|
9
|
+
import { createPassportSvg } from '@arcblock/ux/lib/Util/passport';
|
|
10
|
+
|
|
11
|
+
// eslint-disable-next-line import/no-unresolved
|
|
12
|
+
import { ReactGA } from '@arcblock/ux/lib/withTracker';
|
|
13
|
+
// eslint-disable-next-line import/no-unresolved
|
|
14
|
+
import Guest from '../OAuth/guest.svg?react';
|
|
15
|
+
|
|
16
|
+
export const parseResponse = (data) => {
|
|
17
|
+
if (!data) {
|
|
18
|
+
return {};
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// for backward compatibility
|
|
22
|
+
if (typeof data === 'string') {
|
|
23
|
+
return { sessionToken: data };
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
return pick(data, ['sessionToken', 'refreshToken', 'visitorId', 'destroySessionId']);
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
const translations = {
|
|
30
|
+
zh: {
|
|
31
|
+
switchPassport: '切换通行证',
|
|
32
|
+
switchPassportFailed: '切换通行证失败',
|
|
33
|
+
switchPassportSucceed: '切换通行证成功',
|
|
34
|
+
selectedPassport: '请选择一个通行证',
|
|
35
|
+
switch: '切换',
|
|
36
|
+
cancel: '取消',
|
|
37
|
+
currentRole: '当前角色:',
|
|
38
|
+
passport: '通行证',
|
|
39
|
+
getPassportFailed: '获取通行证失败',
|
|
40
|
+
},
|
|
41
|
+
en: {
|
|
42
|
+
switchPassport: 'Switch passport',
|
|
43
|
+
switchPassportFailed: 'Switch passport failed',
|
|
44
|
+
switchPassportSucceed: 'Switch passport succeed',
|
|
45
|
+
selectedPassport: 'Select a passport to switch',
|
|
46
|
+
switch: 'Switch',
|
|
47
|
+
cancel: 'Cancel',
|
|
48
|
+
currentRole: 'Current role: ',
|
|
49
|
+
passport: 'Passport',
|
|
50
|
+
getPassportFailed: 'Get passports failed',
|
|
51
|
+
},
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
export function PassportSwitcher({ api, locale, switchState, onSwitchPassport, session }) {
|
|
55
|
+
const t = useMemoizedFn((key, data = {}) => {
|
|
56
|
+
return translate(translations, key, locale, 'en', data);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
const visiblePassports = useCreation(() => {
|
|
60
|
+
return switchState.passports.filter((x) => x.role !== switchState.currentUser?.role);
|
|
61
|
+
}, [switchState.passports, switchState.currentUser?.role]);
|
|
62
|
+
|
|
63
|
+
const onSwitch = async () => {
|
|
64
|
+
const passportId = switchState.selectedPassport;
|
|
65
|
+
const fromRole = session?.user?.role;
|
|
66
|
+
const toRole = session?.user?.passports?.find((x) => x.id === passportId)?.role ?? 'guest';
|
|
67
|
+
const change = `${fromRole} -> ${toRole}`;
|
|
68
|
+
try {
|
|
69
|
+
const { data } = await api.post('/switch', { passportId });
|
|
70
|
+
const { sessionToken, refreshToken } = parseResponse(data);
|
|
71
|
+
onSwitchPassport({ sessionToken, refreshToken });
|
|
72
|
+
Toast.success(t('switchPassportSucceed'));
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* @type {import('@arcblock/ux/lib/withTracker/action/switch-passport').SwitchPassportSuccessEvent}
|
|
76
|
+
*/
|
|
77
|
+
const switchPassportSuccessEvent = {
|
|
78
|
+
action: 'switchPassportSuccess',
|
|
79
|
+
change,
|
|
80
|
+
success: true,
|
|
81
|
+
};
|
|
82
|
+
ReactGA.event(switchPassportSuccessEvent.action, switchPassportSuccessEvent);
|
|
83
|
+
} catch (err) {
|
|
84
|
+
const errorMessage = err.message || t('switchPassportFailed');
|
|
85
|
+
/**
|
|
86
|
+
* @type {import('@arcblock/ux/lib/withTracker/action/switch-passport').SwitchPassportFailedEvent}
|
|
87
|
+
*/
|
|
88
|
+
const switchPassportFailedEvent = {
|
|
89
|
+
action: 'switchPassportFailed',
|
|
90
|
+
success: false,
|
|
91
|
+
change,
|
|
92
|
+
errorMessage,
|
|
93
|
+
};
|
|
94
|
+
ReactGA.event(switchPassportFailedEvent.action, switchPassportFailedEvent);
|
|
95
|
+
Toast.error(errorMessage);
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
return (
|
|
100
|
+
<Confirm
|
|
101
|
+
open={switchState.open}
|
|
102
|
+
title={t('switchPassport')}
|
|
103
|
+
onConfirm={onSwitch}
|
|
104
|
+
onCancel={switchState.reset}
|
|
105
|
+
confirmButton={{
|
|
106
|
+
text: t('switch'),
|
|
107
|
+
props: {
|
|
108
|
+
variant: 'contained',
|
|
109
|
+
color: 'primary',
|
|
110
|
+
},
|
|
111
|
+
}}
|
|
112
|
+
cancelButton={{
|
|
113
|
+
text: t('cancel'),
|
|
114
|
+
props: {
|
|
115
|
+
color: 'inherit',
|
|
116
|
+
},
|
|
117
|
+
}}
|
|
118
|
+
PaperProps={{ style: { width: 600 } }}>
|
|
119
|
+
<Box
|
|
120
|
+
sx={{
|
|
121
|
+
mb: 2,
|
|
122
|
+
}}>
|
|
123
|
+
{t('currentRole')}
|
|
124
|
+
{switchState.currentUser?.role}
|
|
125
|
+
</Box>
|
|
126
|
+
<CardSelector
|
|
127
|
+
width={160}
|
|
128
|
+
height={240}
|
|
129
|
+
cardSpace={24}
|
|
130
|
+
list={[
|
|
131
|
+
// eslint-disable-next-line react/no-unstable-nested-components
|
|
132
|
+
() => <Guest />,
|
|
133
|
+
...visiblePassports.map((x) => {
|
|
134
|
+
if (x.display) {
|
|
135
|
+
return {
|
|
136
|
+
src: x.display,
|
|
137
|
+
name: x.title,
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// eslint-disable-next-line react/no-unstable-nested-components, react/function-component-definition
|
|
142
|
+
return () => (
|
|
143
|
+
<Box
|
|
144
|
+
key={x.id}
|
|
145
|
+
sx={{ width: '100%' }}
|
|
146
|
+
dangerouslySetInnerHTML={{
|
|
147
|
+
__html: createPassportSvg(x),
|
|
148
|
+
}}
|
|
149
|
+
/>
|
|
150
|
+
);
|
|
151
|
+
}),
|
|
152
|
+
]}
|
|
153
|
+
onSelect={(index) => {
|
|
154
|
+
switchState.selectedPassport = index > 0 ? visiblePassports[index - 1].id : undefined;
|
|
155
|
+
}}
|
|
156
|
+
defaultIndex={0}
|
|
157
|
+
/>
|
|
158
|
+
</Confirm>
|
|
159
|
+
);
|
|
160
|
+
}
|