@blocklet/ui-react 2.11.4 → 2.11.6
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/UserCenter/components/third-party-login/third-party-item.js +3 -0
- package/lib/UserSessions/components/user-sessions.js +37 -27
- package/package.json +8 -7
- package/src/UserCenter/components/third-party-login/third-party-item.tsx +3 -0
- package/src/UserSessions/components/user-sessions.tsx +56 -40
|
@@ -55,6 +55,9 @@ export default function ThirdPartyItem({
|
|
|
55
55
|
title: t("thirdPartyLogin.confirmUnbind", { name: title }),
|
|
56
56
|
content: t("thirdPartyLogin.confirmUnbindDescription", { name: title }),
|
|
57
57
|
confirmButtonText: t("common.confirm"),
|
|
58
|
+
confirmButtonProps: {
|
|
59
|
+
color: "error"
|
|
60
|
+
},
|
|
58
61
|
cancelButtonText: t("common.cancel"),
|
|
59
62
|
onConfirm(close) {
|
|
60
63
|
unbindOAuth({
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
2
2
|
import Datatable from "@arcblock/ux/lib/Datatable";
|
|
3
|
-
import { useCreation, useMemoizedFn, useRequest } from "ahooks";
|
|
3
|
+
import { useCreation, useMemoizedFn, useReactive, useRequest } from "ahooks";
|
|
4
4
|
import { translate } from "@arcblock/ux/lib/Locale/util";
|
|
5
5
|
import { useLocaleContext } from "@arcblock/ux/lib/Locale/context";
|
|
6
6
|
import RelativeTime from "@arcblock/ux/lib/RelativeTime";
|
|
@@ -9,13 +9,14 @@ import UAParser from "ua-parser-js";
|
|
|
9
9
|
import { getVisitorId } from "@arcblock/ux/lib/Util";
|
|
10
10
|
import { useConfirm } from "@arcblock/ux/lib/Dialog";
|
|
11
11
|
import pAll from "p-all";
|
|
12
|
-
import
|
|
13
|
-
import {
|
|
12
|
+
import PQueue from "p-queue";
|
|
13
|
+
import { Box, Button, CircularProgress, Tooltip, Typography } from "@mui/material";
|
|
14
|
+
import { memo, useContext, useEffect } from "react";
|
|
14
15
|
import { SessionContext } from "@arcblock/did-connect/lib/Session";
|
|
15
16
|
import UserSessionInfo from "./user-session-info.js";
|
|
16
17
|
import { client } from "../../libs/client.js";
|
|
17
18
|
import { translations } from "../libs/locales.js";
|
|
18
|
-
import {
|
|
19
|
+
import { ip2Region } from "../libs/utils.js";
|
|
19
20
|
const parseUa = (ua) => {
|
|
20
21
|
const parser = new UAParser(ua, {
|
|
21
22
|
// eslint-disable-next-line no-useless-escape
|
|
@@ -24,6 +25,30 @@ const parseUa = (ua) => {
|
|
|
24
25
|
const result = parser.getResult();
|
|
25
26
|
return result;
|
|
26
27
|
};
|
|
28
|
+
const queue = new PQueue({ concurrency: 1 });
|
|
29
|
+
const UserSessionIp = memo(({ userSession }) => {
|
|
30
|
+
const currentState = useReactive({
|
|
31
|
+
loading: true,
|
|
32
|
+
ipRegion: ""
|
|
33
|
+
});
|
|
34
|
+
const { t } = useLocaleContext();
|
|
35
|
+
useEffect(() => {
|
|
36
|
+
queue.add(async () => {
|
|
37
|
+
try {
|
|
38
|
+
currentState.ipRegion = await ip2Region(userSession.lastLoginIp);
|
|
39
|
+
} finally {
|
|
40
|
+
currentState.loading = false;
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
}, [currentState, userSession.lastLoginIp]);
|
|
44
|
+
return /* @__PURE__ */ jsx(Box, { children: currentState.ipRegion ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
45
|
+
/* @__PURE__ */ jsx(Typography, { variant: "body2", children: currentState.ipRegion }),
|
|
46
|
+
/* @__PURE__ */ jsx(Typography, { variant: "body2", color: "grey", children: userSession.lastLoginIp || t("unknown") })
|
|
47
|
+
] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
48
|
+
/* @__PURE__ */ jsx(Typography, { children: userSession.lastLoginIp || t("unknown") }),
|
|
49
|
+
currentState.loading ? /* @__PURE__ */ jsx(Typography, { variant: "body2", color: "grey", sx: { textAlign: "center" }, children: /* @__PURE__ */ jsx(CircularProgress, { size: 12, color: "inherit" }) }) : null
|
|
50
|
+
] }) });
|
|
51
|
+
});
|
|
27
52
|
export default function UserSessions({
|
|
28
53
|
user,
|
|
29
54
|
showAction = true,
|
|
@@ -39,18 +64,9 @@ export default function UserSessions({
|
|
|
39
64
|
const getData = useMemoizedFn(async () => {
|
|
40
65
|
let data = user?.userSessions || [];
|
|
41
66
|
try {
|
|
42
|
-
if (!
|
|
67
|
+
if (!user?.userSessions && session.user) {
|
|
43
68
|
data = await client.userSession.getMyLoginSessions();
|
|
44
69
|
}
|
|
45
|
-
const ipIndexList = data?.map((x, index) => [index, x.lastLoginIp]).filter((x) => !!x[1]);
|
|
46
|
-
const ipList = ipIndexList?.map((x) => x[1]);
|
|
47
|
-
const result = await batchIp2Region(ipList);
|
|
48
|
-
for (let index = 0; index < result.length; index++) {
|
|
49
|
-
const x = result[index];
|
|
50
|
-
const ipIndexItem = ipIndexList[index];
|
|
51
|
-
const dataItem = data[ipIndexItem[0]];
|
|
52
|
-
dataItem.ipRegion = x;
|
|
53
|
-
}
|
|
54
70
|
} catch (e) {
|
|
55
71
|
console.warn("Failed to convert ip to region");
|
|
56
72
|
console.error(e);
|
|
@@ -67,20 +83,14 @@ export default function UserSessions({
|
|
|
67
83
|
const safeData = useCreation(() => {
|
|
68
84
|
return pageState.data || [];
|
|
69
85
|
}, [pageState.data]);
|
|
70
|
-
const ipRegionMap = useCreation(() => {
|
|
71
|
-
return safeData.reduce(
|
|
72
|
-
(acc, x) => {
|
|
73
|
-
acc[x.lastLoginIp] = x.ipRegion;
|
|
74
|
-
return acc;
|
|
75
|
-
},
|
|
76
|
-
{}
|
|
77
|
-
);
|
|
78
|
-
}, [safeData]);
|
|
79
86
|
const logout = useMemoizedFn(({ visitorId }) => {
|
|
80
87
|
confirmApi.open({
|
|
81
88
|
title: t("logoutThisSession"),
|
|
82
89
|
content: t("logoutThisSessionConfirm"),
|
|
83
90
|
confirmButtonText: t("confirm"),
|
|
91
|
+
confirmButtonProps: {
|
|
92
|
+
color: "error"
|
|
93
|
+
},
|
|
84
94
|
cancelButtonText: t("cancel"),
|
|
85
95
|
onConfirm: async () => {
|
|
86
96
|
await client.user.logout({ visitorId });
|
|
@@ -98,6 +108,9 @@ export default function UserSessions({
|
|
|
98
108
|
title: t("logoutAllSession"),
|
|
99
109
|
content: t("logoutAllSessionConfirm"),
|
|
100
110
|
confirmButtonText: t("confirm"),
|
|
111
|
+
confirmButtonProps: {
|
|
112
|
+
color: "error"
|
|
113
|
+
},
|
|
101
114
|
cancelButtonText: t("cancel"),
|
|
102
115
|
onConfirm: async () => {
|
|
103
116
|
const list = otherUserSessions.map((x) => {
|
|
@@ -210,10 +223,7 @@ export default function UserSessions({
|
|
|
210
223
|
options: {
|
|
211
224
|
customBodyRenderLite: (rawIndex) => {
|
|
212
225
|
const x = safeData[rawIndex];
|
|
213
|
-
return /* @__PURE__ */ jsx(
|
|
214
|
-
/* @__PURE__ */ jsx(Typography, { variant: "body2", children: ipRegionMap[x.lastLoginIp] }),
|
|
215
|
-
/* @__PURE__ */ jsx(Typography, { variant: "body2", color: "grey", children: x.lastLoginIp || t("unknown") })
|
|
216
|
-
] }) : /* @__PURE__ */ jsx(Typography, { children: x.lastLoginIp || t("unknown") }) });
|
|
226
|
+
return /* @__PURE__ */ jsx(UserSessionIp, { userSession: x });
|
|
217
227
|
}
|
|
218
228
|
}
|
|
219
229
|
},
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@blocklet/ui-react",
|
|
3
|
-
"version": "2.11.
|
|
3
|
+
"version": "2.11.6",
|
|
4
4
|
"description": "Some useful front-end web components that can be used in Blocklets.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"react",
|
|
@@ -32,8 +32,8 @@
|
|
|
32
32
|
"url": "https://github.com/ArcBlock/ux/issues"
|
|
33
33
|
},
|
|
34
34
|
"dependencies": {
|
|
35
|
-
"@arcblock/bridge": "^2.11.
|
|
36
|
-
"@arcblock/react-hooks": "^2.11.
|
|
35
|
+
"@arcblock/bridge": "^2.11.6",
|
|
36
|
+
"@arcblock/react-hooks": "^2.11.6",
|
|
37
37
|
"@blocklet/did-space-react": "^0.5.83",
|
|
38
38
|
"@iconify-icons/logos": "^1.2.36",
|
|
39
39
|
"@iconify-icons/material-symbols": "^1.2.58",
|
|
@@ -47,6 +47,7 @@
|
|
|
47
47
|
"is-url": "^1.2.4",
|
|
48
48
|
"lodash": "^4.17.21",
|
|
49
49
|
"p-all": "^5.0.0",
|
|
50
|
+
"p-queue": "^6.6.2",
|
|
50
51
|
"p-wait-for": "^5.0.2",
|
|
51
52
|
"prop-types": "^15.8.1",
|
|
52
53
|
"react-error-boundary": "^3.1.4",
|
|
@@ -56,9 +57,9 @@
|
|
|
56
57
|
"ufo": "^1.5.3"
|
|
57
58
|
},
|
|
58
59
|
"peerDependencies": {
|
|
59
|
-
"@arcblock/did-connect": "^2.
|
|
60
|
-
"@arcblock/ux": "^2.
|
|
61
|
-
"@blocklet/js-sdk": "^1.16.
|
|
60
|
+
"@arcblock/did-connect": "^2.11.3",
|
|
61
|
+
"@arcblock/ux": "^2.11.3",
|
|
62
|
+
"@blocklet/js-sdk": "^1.16.36",
|
|
62
63
|
"@emotion/react": "^11.10.4",
|
|
63
64
|
"@emotion/styled": "^11.10.4",
|
|
64
65
|
"@mui/icons-material": "^5.15.0",
|
|
@@ -80,5 +81,5 @@
|
|
|
80
81
|
"jest": "^29.7.0",
|
|
81
82
|
"unbuild": "^2.0.0"
|
|
82
83
|
},
|
|
83
|
-
"gitHead": "
|
|
84
|
+
"gitHead": "ad7d69b1e27a68f7a238a77503afb4581eeb71a8"
|
|
84
85
|
}
|
|
@@ -71,6 +71,9 @@ export default function ThirdPartyItem({
|
|
|
71
71
|
title: t('thirdPartyLogin.confirmUnbind', { name: title }),
|
|
72
72
|
content: t('thirdPartyLogin.confirmUnbindDescription', { name: title }),
|
|
73
73
|
confirmButtonText: t('common.confirm'),
|
|
74
|
+
confirmButtonProps: {
|
|
75
|
+
color: 'error',
|
|
76
|
+
},
|
|
74
77
|
cancelButtonText: t('common.cancel'),
|
|
75
78
|
onConfirm(close: () => void) {
|
|
76
79
|
unbindOAuth({
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/* eslint-disable no-nested-ternary */
|
|
2
2
|
/* eslint-disable react/no-unstable-nested-components */
|
|
3
3
|
import Datatable from '@arcblock/ux/lib/Datatable';
|
|
4
|
-
import { useCreation, useMemoizedFn, useRequest } from 'ahooks';
|
|
4
|
+
import { useCreation, useMemoizedFn, useReactive, useRequest } from 'ahooks';
|
|
5
5
|
import { translate } from '@arcblock/ux/lib/Locale/util';
|
|
6
6
|
import { useLocaleContext } from '@arcblock/ux/lib/Locale/context';
|
|
7
7
|
import RelativeTime from '@arcblock/ux/lib/RelativeTime';
|
|
@@ -10,8 +10,9 @@ import UAParser from 'ua-parser-js';
|
|
|
10
10
|
import { getVisitorId } from '@arcblock/ux/lib/Util';
|
|
11
11
|
import { useConfirm } from '@arcblock/ux/lib/Dialog';
|
|
12
12
|
import pAll from 'p-all';
|
|
13
|
-
import
|
|
14
|
-
import {
|
|
13
|
+
import PQueue from 'p-queue';
|
|
14
|
+
import { Box, Button, CircularProgress, Tooltip, Typography } from '@mui/material';
|
|
15
|
+
import { memo, ReactElement, useContext, useEffect } from 'react';
|
|
15
16
|
import { UserSession } from '@blocklet/js-sdk';
|
|
16
17
|
import { SessionContext } from '@arcblock/did-connect/lib/Session';
|
|
17
18
|
|
|
@@ -19,7 +20,7 @@ import UserSessionInfo from './user-session-info';
|
|
|
19
20
|
import { User, SessionContext as TSessionContext } from '../../@types';
|
|
20
21
|
import { client } from '../../libs/client';
|
|
21
22
|
import { translations } from '../libs/locales';
|
|
22
|
-
import {
|
|
23
|
+
import { ip2Region } from '../libs/utils';
|
|
23
24
|
|
|
24
25
|
const parseUa = (ua: string) => {
|
|
25
26
|
const parser = new UAParser(ua, {
|
|
@@ -30,6 +31,49 @@ const parseUa = (ua: string) => {
|
|
|
30
31
|
return result;
|
|
31
32
|
};
|
|
32
33
|
|
|
34
|
+
const queue = new PQueue({ concurrency: 1 });
|
|
35
|
+
|
|
36
|
+
const UserSessionIp = memo(({ userSession }: { userSession: any }) => {
|
|
37
|
+
const currentState = useReactive({
|
|
38
|
+
loading: true,
|
|
39
|
+
ipRegion: '',
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
const { t } = useLocaleContext();
|
|
43
|
+
|
|
44
|
+
useEffect(() => {
|
|
45
|
+
queue.add(async () => {
|
|
46
|
+
try {
|
|
47
|
+
currentState.ipRegion = await ip2Region(userSession.lastLoginIp);
|
|
48
|
+
} finally {
|
|
49
|
+
currentState.loading = false;
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
}, [currentState, userSession.lastLoginIp]);
|
|
53
|
+
|
|
54
|
+
return (
|
|
55
|
+
<Box>
|
|
56
|
+
{currentState.ipRegion ? (
|
|
57
|
+
<>
|
|
58
|
+
<Typography variant="body2">{currentState.ipRegion}</Typography>
|
|
59
|
+
<Typography variant="body2" color="grey">
|
|
60
|
+
{userSession.lastLoginIp || t('unknown')}
|
|
61
|
+
</Typography>
|
|
62
|
+
</>
|
|
63
|
+
) : (
|
|
64
|
+
<>
|
|
65
|
+
<Typography>{userSession.lastLoginIp || t('unknown')}</Typography>
|
|
66
|
+
{currentState.loading ? (
|
|
67
|
+
<Typography variant="body2" color="grey" sx={{ textAlign: 'center' }}>
|
|
68
|
+
<CircularProgress size={12} color="inherit" />
|
|
69
|
+
</Typography>
|
|
70
|
+
) : null}
|
|
71
|
+
</>
|
|
72
|
+
)}
|
|
73
|
+
</Box>
|
|
74
|
+
);
|
|
75
|
+
});
|
|
76
|
+
|
|
33
77
|
export default function UserSessions({
|
|
34
78
|
user,
|
|
35
79
|
showAction = true,
|
|
@@ -51,20 +95,9 @@ export default function UserSessions({
|
|
|
51
95
|
const getData: () => Promise<(UserSession & { ipRegion?: string })[]> = useMemoizedFn(async () => {
|
|
52
96
|
let data = (user?.userSessions || []) as (UserSession & { ipRegion?: string })[];
|
|
53
97
|
try {
|
|
54
|
-
if (!
|
|
55
|
-
// 依赖新版 js-sdk
|
|
98
|
+
if (!user?.userSessions && session.user) {
|
|
56
99
|
data = await client.userSession.getMyLoginSessions();
|
|
57
100
|
}
|
|
58
|
-
// FIXME: @zhanghan 优化获取 IP 信息的处理逻辑
|
|
59
|
-
const ipIndexList = data?.map((x, index) => [index, x.lastLoginIp] as [number, string]).filter((x) => !!x[1]);
|
|
60
|
-
const ipList = ipIndexList?.map((x) => x[1]);
|
|
61
|
-
const result = await batchIp2Region(ipList);
|
|
62
|
-
for (let index = 0; index < result.length; index++) {
|
|
63
|
-
const x = result[index];
|
|
64
|
-
const ipIndexItem = ipIndexList[index];
|
|
65
|
-
const dataItem = data[ipIndexItem[0]];
|
|
66
|
-
dataItem.ipRegion = x;
|
|
67
|
-
}
|
|
68
101
|
} catch (e) {
|
|
69
102
|
console.warn('Failed to convert ip to region');
|
|
70
103
|
console.error(e);
|
|
@@ -85,21 +118,14 @@ export default function UserSessions({
|
|
|
85
118
|
return pageState.data || [];
|
|
86
119
|
}, [pageState.data]);
|
|
87
120
|
|
|
88
|
-
const ipRegionMap = useCreation(() => {
|
|
89
|
-
return safeData.reduce(
|
|
90
|
-
(acc, x) => {
|
|
91
|
-
acc[x.lastLoginIp] = x.ipRegion;
|
|
92
|
-
return acc;
|
|
93
|
-
},
|
|
94
|
-
{} as { [key: string]: string | undefined }
|
|
95
|
-
);
|
|
96
|
-
}, [safeData]);
|
|
97
|
-
|
|
98
121
|
const logout = useMemoizedFn(({ visitorId }) => {
|
|
99
122
|
confirmApi.open({
|
|
100
123
|
title: t('logoutThisSession'),
|
|
101
124
|
content: t('logoutThisSessionConfirm'),
|
|
102
125
|
confirmButtonText: t('confirm'),
|
|
126
|
+
confirmButtonProps: {
|
|
127
|
+
color: 'error',
|
|
128
|
+
},
|
|
103
129
|
cancelButtonText: t('cancel'),
|
|
104
130
|
onConfirm: async () => {
|
|
105
131
|
await client.user.logout({ visitorId });
|
|
@@ -117,6 +143,9 @@ export default function UserSessions({
|
|
|
117
143
|
title: t('logoutAllSession'),
|
|
118
144
|
content: t('logoutAllSessionConfirm'),
|
|
119
145
|
confirmButtonText: t('confirm'),
|
|
146
|
+
confirmButtonProps: {
|
|
147
|
+
color: 'error',
|
|
148
|
+
},
|
|
120
149
|
cancelButtonText: t('cancel'),
|
|
121
150
|
onConfirm: async () => {
|
|
122
151
|
const list = otherUserSessions.map((x) => {
|
|
@@ -240,20 +269,7 @@ export default function UserSessions({
|
|
|
240
269
|
options: {
|
|
241
270
|
customBodyRenderLite: (rawIndex: number) => {
|
|
242
271
|
const x = safeData[rawIndex];
|
|
243
|
-
return
|
|
244
|
-
<Box>
|
|
245
|
-
{ipRegionMap[x.lastLoginIp] ? (
|
|
246
|
-
<>
|
|
247
|
-
<Typography variant="body2">{ipRegionMap[x.lastLoginIp]}</Typography>
|
|
248
|
-
<Typography variant="body2" color="grey">
|
|
249
|
-
{x.lastLoginIp || t('unknown')}
|
|
250
|
-
</Typography>
|
|
251
|
-
</>
|
|
252
|
-
) : (
|
|
253
|
-
<Typography>{x.lastLoginIp || t('unknown')}</Typography>
|
|
254
|
-
)}
|
|
255
|
-
</Box>
|
|
256
|
-
);
|
|
272
|
+
return <UserSessionIp userSession={x} />;
|
|
257
273
|
},
|
|
258
274
|
},
|
|
259
275
|
},
|