@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.
@@ -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 { Box, Button, Tooltip, Typography } from "@mui/material";
13
- import { useContext } from "react";
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 { batchIp2Region } from "../libs/utils.js";
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 (!data && session.user) {
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(Box, { children: ipRegionMap[x.lastLoginIp] ? /* @__PURE__ */ jsxs(Fragment, { children: [
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.4",
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.4",
36
- "@arcblock/react-hooks": "^2.11.4",
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.9.79",
60
- "@arcblock/ux": "^2.9.79",
61
- "@blocklet/js-sdk": "^1.16.33",
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": "4c757dc656bb78a677c33a4db12567c5bd56ccf6"
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 { Box, Button, Tooltip, Typography } from '@mui/material';
14
- import { ReactElement, useContext } from 'react';
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 { batchIp2Region } from '../libs/utils';
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 (!data && session.user) {
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
  },