@blocklet/ui-react 2.12.64 → 2.12.71

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.
@@ -1,13 +1,12 @@
1
1
  import { jsx } from "react/jsx-runtime";
2
2
  import { useMemo } from "react";
3
3
  import PropTypes from "prop-types";
4
- import { styled } from "@arcblock/ux/lib/Theme";
4
+ import { styled, useTheme, deepmerge, ThemeProvider } from "@arcblock/ux/lib/Theme";
5
5
  import { withErrorBoundary } from "react-error-boundary";
6
6
  import { useLocaleContext } from "@arcblock/ux/lib/Locale/context";
7
7
  import { ErrorFallback } from "@arcblock/ux/lib/ErrorBoundary";
8
8
  import { temp as colors } from "@arcblock/ux/lib/Colors";
9
9
  import omit from "lodash/omit";
10
- import OverridableThemeProvider from "../common/overridable-theme-provider.js";
11
10
  import InternalFooter from "./internal-footer.js";
12
11
  import { mapRecursive } from "../utils.js";
13
12
  import { formatBlockletInfo, getLocalizedNavigation } from "../blocklets.js";
@@ -15,6 +14,7 @@ import { BlockletMetaProps } from "../types.js";
15
14
  import withHideWhenEmbed from "../libs/with-hide-when-embed.js";
16
15
  function Footer({ meta, theme: themeOverrides, ...rest }) {
17
16
  const { locale } = useLocaleContext() || {};
17
+ const parentTheme = useTheme();
18
18
  const formattedBlocklet = useMemo(() => {
19
19
  const blocklet = Object.assign({}, window.blocklet, meta);
20
20
  try {
@@ -24,10 +24,12 @@ function Footer({ meta, theme: themeOverrides, ...rest }) {
24
24
  return blocklet;
25
25
  }
26
26
  }, [meta]);
27
+ const mergeTheme = useMemo(() => deepmerge(parentTheme, themeOverrides), [parentTheme, themeOverrides]);
27
28
  if (!formattedBlocklet.appName) {
28
29
  return null;
29
30
  }
30
- const { appLogo, appLogoRect, appName, appDescription, description, theme, copyright } = formattedBlocklet;
31
+ const { appLogo, appLogoRect, appName, appDescription, description, copyright } = formattedBlocklet;
32
+ const $bgColor = mergeTheme.palette.background.default;
31
33
  const localized = {
32
34
  footerNav: getLocalizedNavigation(formattedBlocklet?.navigation?.footer, locale) || [],
33
35
  socialMedia: getLocalizedNavigation(formattedBlocklet?.navigation?.social, locale) || [],
@@ -52,15 +54,7 @@ function Footer({ meta, theme: themeOverrides, ...rest }) {
52
54
  socialMedia: localized.socialMedia,
53
55
  links: localized.links.map((item) => ({ ...item, label: item.title }))
54
56
  };
55
- return /* @__PURE__ */ jsx(OverridableThemeProvider, { theme: themeOverrides, children: /* @__PURE__ */ jsx(
56
- StyledInternalFooter,
57
- {
58
- ...props,
59
- ...omit(rest, ["bordered"]),
60
- $bordered: rest?.bordered,
61
- $bgcolor: theme?.background?.footer
62
- }
63
- ) });
57
+ return /* @__PURE__ */ jsx(ThemeProvider, { theme: mergeTheme, children: /* @__PURE__ */ jsx(StyledInternalFooter, { ...props, ...omit(rest, ["bordered"]), $bordered: rest?.bordered, $bgcolor: $bgColor }) });
64
58
  }
65
59
  Footer.propTypes = {
66
60
  meta: BlockletMetaProps,
@@ -75,8 +69,6 @@ const StyledInternalFooter = styled(InternalFooter)`
75
69
  ${({ $bordered }) => `border-top: 1px solid ${$bordered ? colors.strokeSep : "#eee"};`}
76
70
  color: ${(props) => props.theme.palette.grey[600]};
77
71
  ${({ $bgcolor }) => $bgcolor && `background-color: ${$bgcolor};`}
78
- font-family: Inter, Avenir, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif,
79
- 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol';
80
72
  `;
81
73
  export default withErrorBoundary(withHideWhenEmbed(Footer), {
82
74
  FallbackComponent: ErrorFallback
@@ -128,7 +128,7 @@ Links.defaultProps = {
128
128
  };
129
129
  const Root = styled("div")`
130
130
  overflow: hidden;
131
- color: #9397a1;
131
+ color: ${({ theme }) => theme.palette.grey[500]};
132
132
  .footer-links-inner {
133
133
  display: flex;
134
134
  justify-content: space-between;
@@ -159,8 +159,8 @@ const Root = styled("div")`
159
159
  font-size: 14px;
160
160
  &--new::after {
161
161
  content: 'New';
162
- color: #4672ea;
163
- background-color: #e1e8fb;
162
+ color: ${({ theme }) => theme.palette.info.main};
163
+ background-color: ${({ theme }) => theme.palette.info.light};
164
164
  padding: 1px 8px;
165
165
  border-radius: 10px/50%;
166
166
  margin-left: 8px;
@@ -170,7 +170,7 @@ const Root = styled("div")`
170
170
  .footer-links-group {
171
171
  > .footer-links-item {
172
172
  font-weight: 600;
173
- color: #25292f;
173
+ color: ${({ theme }) => theme.palette.text.primary};
174
174
  }
175
175
  .footer-links-sub {
176
176
  margin-top: 8px;
@@ -184,7 +184,7 @@ const Root = styled("div")`
184
184
  text-decoration: none;
185
185
  transition: color 0.2s ease-in-out;
186
186
  &:hover {
187
- color: #25292f;
187
+ color: ${({ theme }) => theme.palette.text.primary};
188
188
  }
189
189
  }
190
190
  /* columns 布局 */
@@ -48,11 +48,11 @@ const Root = styled("div")`
48
48
  justify-content: center;
49
49
  gap: 20px;
50
50
  a {
51
- color: ${(props) => props.theme.palette.grey[400]};
51
+ color: ${(props) => props.theme.palette.grey[500]};
52
52
  text-decoration: none;
53
53
  transition: color 0.2s ease-in-out;
54
54
  &:hover {
55
- color: #25292f;
55
+ color: ${({ theme }) => theme.palette.text.primary};
56
56
  }
57
57
  }
58
58
  ${(props) => props.theme.breakpoints.down("md")} {
@@ -3,7 +3,7 @@ import { useMemo } from "react";
3
3
  import { useMemoizedFn } from "ahooks";
4
4
  import { withErrorBoundary } from "react-error-boundary";
5
5
  import { ErrorFallback } from "@arcblock/ux/lib/ErrorBoundary";
6
- import { styled } from "@arcblock/ux/lib/Theme";
6
+ import { styled, useTheme, deepmerge, ThemeProvider } from "@arcblock/ux/lib/Theme";
7
7
  import { ResponsiveHeader } from "@arcblock/ux/lib/Header";
8
8
  import NavMenu, { Products } from "@arcblock/ux/lib/NavMenu";
9
9
  import { useLocaleContext } from "@arcblock/ux/lib/Locale/context";
@@ -13,7 +13,6 @@ import omit from "lodash/omit";
13
13
  import isFinite from "lodash/isFinite";
14
14
  import clsx from "clsx";
15
15
  import Icon from "../Icon/index.js";
16
- import OverridableThemeProvider from "../common/overridable-theme-provider.js";
17
16
  import { mapRecursive, flatRecursive, matchPaths } from "../utils.js";
18
17
  import { publicPath, formatBlockletInfo, getLocalizedNavigation } from "../blocklets.js";
19
18
  import HeaderAddons from "../common/header-addons.js";
@@ -76,6 +75,7 @@ function Header({
76
75
  ...rest
77
76
  }) {
78
77
  useWalletHiddenTopbar();
78
+ const parentTheme = useTheme();
79
79
  const { locale } = useLocaleContext() || {};
80
80
  const t = useMemoizedFn((key, data = {}) => {
81
81
  return translate(translations, key, locale, "en", data);
@@ -90,10 +90,11 @@ function Header({
90
90
  }
91
91
  }, [meta]);
92
92
  const isMobileDevice = useMobile();
93
+ const mergeTheme = useMemo(() => deepmerge(parentTheme, themeOverrides), [parentTheme, themeOverrides]);
93
94
  if (!formattedBlocklet.appName) {
94
95
  return null;
95
96
  }
96
- const { appLogo, appLogoRect, theme } = formattedBlocklet;
97
+ const { appLogo, appLogoRect } = formattedBlocklet;
97
98
  const navigation = getLocalizedNavigation(formattedBlocklet?.navigation?.header, locale);
98
99
  const parsedNavigation = parseNavigation(navigation);
99
100
  const { navItems, activeId } = parsedNavigation;
@@ -118,7 +119,7 @@ function Header({
118
119
  }
119
120
  )
120
121
  );
121
- return /* @__PURE__ */ jsx(OverridableThemeProvider, { theme: themeOverrides, children: /* @__PURE__ */ jsx(
122
+ return /* @__PURE__ */ jsx(ThemeProvider, { theme: mergeTheme, children: /* @__PURE__ */ jsx(
122
123
  StyledUxHeader,
123
124
  {
124
125
  homeLink,
@@ -126,7 +127,7 @@ function Header({
126
127
  addons: headerAddons,
127
128
  ...omit(rest, ["bordered"]),
128
129
  $bordered: rest?.bordered,
129
- $bgcolor: theme?.background?.header,
130
+ $bgcolor: mergeTheme.palette.background.default,
130
131
  className: clsx("blocklet__header", rest.className),
131
132
  children: hideNavMenu || !navItems?.length ? null : ({ isMobile }) => (
132
133
  // @ts-ignore
@@ -138,7 +139,8 @@ function Header({
138
139
  items: navItems,
139
140
  className: "header-nav",
140
141
  bgColor: "transparent",
141
- textColor: "#777"
142
+ textColor: mergeTheme.palette.grey[500],
143
+ activeTextColor: mergeTheme.palette.text.primary
142
144
  }
143
145
  )
144
146
  )
@@ -147,8 +149,6 @@ function Header({
147
149
  }
148
150
  const StyledUxHeader = styled(ResponsiveHeader)`
149
151
  ${({ $bgcolor }) => `background-color: ${$bgcolor || "#fff"};`}
150
- font-family: Inter, Avenir, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif,
151
- 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol';
152
152
  .header-logo {
153
153
  min-width: 44px;
154
154
  }
@@ -19,6 +19,7 @@ interface EditableFieldProps {
19
19
  verified?: boolean;
20
20
  errorMsg?: string;
21
21
  canEdit?: boolean;
22
+ hidePreview?: boolean;
22
23
  renderValue?: (value: string) => React.ReactNode;
23
24
  }
24
25
  export declare const commonInputStyle: {
@@ -47,5 +48,5 @@ export declare const inputFieldStyle: {
47
48
  borderColor: string;
48
49
  };
49
50
  };
50
- declare function EditableField({ value, onChange, onValueValidate, errorMsg, editable, component, placeholder, rows, maxLength, icon, label, children, tooltip, inline, style, verified, canEdit, renderValue, disabled, }: EditableFieldProps): JSX.Element | null;
51
+ declare function EditableField({ value, onChange, onValueValidate, errorMsg, editable, component, placeholder, rows, maxLength, icon, label, children, tooltip, inline, style, verified, canEdit, renderValue, disabled, hidePreview, }: EditableFieldProps): JSX.Element | null;
51
52
  export default EditableField;
@@ -55,7 +55,8 @@ function EditableField({
55
55
  verified = false,
56
56
  canEdit = true,
57
57
  renderValue,
58
- disabled = false
58
+ disabled = false,
59
+ hidePreview = false
59
60
  }) {
60
61
  const { locale } = useLocaleContext();
61
62
  const t = useMemoizedFn((key, data = {}) => {
@@ -145,7 +146,7 @@ function EditableField({
145
146
  return null;
146
147
  }
147
148
  if (!editable) {
148
- return value ? /* @__PURE__ */ jsx(
149
+ return value && !hidePreview ? /* @__PURE__ */ jsx(
149
150
  Tooltip,
150
151
  {
151
152
  open: Boolean(mousePosition),
@@ -13,6 +13,7 @@ import { UserSessions } from "../../UserSessions/index.js";
13
13
  import ThirdPartyLogin from "./third-party-login/index.js";
14
14
  import ConfigProfile from "./config-profile.js";
15
15
  import DangerZone from "./danger-zone.js";
16
+ import { client } from "../../libs/client.js";
16
17
  export default function Settings({
17
18
  user,
18
19
  settings,
@@ -59,7 +60,16 @@ export default function Settings({
59
60
  {
60
61
  label: t("sessionManagement"),
61
62
  value: "session",
62
- content: /* @__PURE__ */ jsx(UserSessions, { user, showUser: false })
63
+ content: /* @__PURE__ */ jsx(
64
+ UserSessions,
65
+ {
66
+ user,
67
+ showUser: false,
68
+ getUserSessions: (params) => {
69
+ return client.userSession.getMyLoginSessions({}, params);
70
+ }
71
+ }
72
+ )
63
73
  },
64
74
  {
65
75
  label: t("dangerZone.title"),
@@ -22,12 +22,12 @@ import isEmail from "validator/lib/isEmail";
22
22
  import isPostalCode from "validator/lib/isPostalCode";
23
23
  import { useLocaleContext } from "@arcblock/ux/lib/Locale/context";
24
24
  import { useBrowser } from "@arcblock/react-hooks";
25
+ import Clock from "@arcblock/ux/lib/UserCard/Content/clock";
25
26
  import { translations } from "../../libs/locales.js";
26
27
  import EditableField, { commonInputStyle, inputFieldStyle } from "../editable-field.js";
27
28
  import { LinkPreviewInput } from "./link-preview-input.js";
28
29
  import { currentTimezone, defaultButtonStyle, primaryButtonStyle } from "./utils.js";
29
30
  import { TimezoneSelect } from "./timezone-select.js";
30
- import Clock from "./clock.js";
31
31
  import AddressEditor from "./address.js";
32
32
  const LocationIcon = lazy(() => import("@arcblock/icons/lib/Location"));
33
33
  const TimezoneIcon = lazy(() => import("@arcblock/icons/lib/Timezone"));
@@ -363,6 +363,7 @@ export default function UserMetadataComponent({
363
363
  {
364
364
  value: metadata.email ?? user?.email ?? "",
365
365
  editable: editing,
366
+ hidePreview: !isMyself,
366
367
  disabled: user?.sourceProvider === LOGIN_PROVIDER.EMAIL,
367
368
  canEdit: !emailVerified,
368
369
  verified: emailVerified,
@@ -384,7 +385,7 @@ export default function UserMetadataComponent({
384
385
  ] }),
385
386
  onChange: (value) => onChange(value, "email"),
386
387
  errorMsg: validateMsg.email,
387
- renderValue: (value) => /* @__PURE__ */ jsx(
388
+ renderValue: (value) => isMyself ? /* @__PURE__ */ jsx(
388
389
  "a",
389
390
  {
390
391
  href: `mailto:${value}`,
@@ -394,7 +395,7 @@ export default function UserMetadataComponent({
394
395
  },
395
396
  children: value
396
397
  }
397
- ),
398
+ ) : null,
398
399
  onValueValidate: (value) => {
399
400
  let msg = "";
400
401
  if (!!value && !isEmail(value)) {
@@ -409,6 +410,7 @@ export default function UserMetadataComponent({
409
410
  {
410
411
  value: phoneValue.phone,
411
412
  editable: editing,
413
+ hidePreview: !isMyself,
412
414
  canEdit: !phoneVerified,
413
415
  verified: phoneVerified,
414
416
  placeholder: "Phone",
@@ -416,7 +418,7 @@ export default function UserMetadataComponent({
416
418
  onChange: (value) => onChange(value, "phone"),
417
419
  label: t("profile.phone"),
418
420
  renderValue: () => {
419
- return /* @__PURE__ */ jsx(PhoneInput, { value: phoneValue, preview: true });
421
+ return isMyself ? /* @__PURE__ */ jsx(PhoneInput, { value: phoneValue, preview: true }) : null;
420
422
  },
421
423
  children: /* @__PURE__ */ jsx(
422
424
  PhoneInput,
@@ -1,8 +1,19 @@
1
+ import { UserSession } from '@blocklet/js-sdk';
1
2
  import { User } from '../../@types';
2
- export default function UserSessions({ user, showAction, showUser, }: {
3
+ export default function UserSessions({ user, showAction, showUser, getUserSessions, }: {
3
4
  readonly user: User & {
4
5
  userSessions?: any[];
5
6
  };
6
7
  readonly showAction?: boolean;
7
8
  readonly showUser?: boolean;
9
+ readonly getUserSessions: (params: {
10
+ page: number;
11
+ pageSize: number;
12
+ status: string;
13
+ }) => Promise<{
14
+ paging: {
15
+ total: number;
16
+ };
17
+ list: UserSession[];
18
+ }>;
8
19
  }): import("react").JSX.Element;
@@ -4,16 +4,23 @@ 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";
7
- import sortBy from "lodash/sortBy";
8
7
  import UAParser from "ua-parser-js";
9
8
  import { getVisitorId } from "@arcblock/ux/lib/Util";
10
9
  import { useConfirm } from "@arcblock/ux/lib/Dialog";
11
10
  import { temp as colors } from "@arcblock/ux/lib/Colors";
12
- import pAll from "p-all";
13
11
  import PQueue from "p-queue";
14
- import { Box, Button, CircularProgress, Tooltip, Typography, useMediaQuery } from "@mui/material";
15
- import { memo, useContext, useEffect } from "react";
16
- import { SessionContext } from "@arcblock/did-connect/lib/Session";
12
+ import {
13
+ Box,
14
+ Button,
15
+ CircularProgress,
16
+ FormControlLabel,
17
+ Radio,
18
+ RadioGroup,
19
+ Tooltip,
20
+ Typography,
21
+ useMediaQuery
22
+ } from "@mui/material";
23
+ import { memo, useEffect } from "react";
17
24
  import useMobile from "../../hooks/use-mobile.js";
18
25
  import UserSessionInfo from "./user-session-info.js";
19
26
  import { client } from "../../libs/client.js";
@@ -54,9 +61,18 @@ const UserSessionIp = memo(({ userSession, isMobile = false }) => {
54
61
  export default function UserSessions({
55
62
  user,
56
63
  showAction = true,
57
- showUser = true
64
+ showUser = true,
65
+ getUserSessions
58
66
  }) {
59
- const { session } = useContext(SessionContext);
67
+ const filterParams = useReactive({
68
+ status: "online",
69
+ page: 1,
70
+ pageSize: 10
71
+ });
72
+ const userSessionsCountMap = useReactive({
73
+ online: 0,
74
+ expired: 0
75
+ });
60
76
  const currentVisitorId = getVisitorId();
61
77
  const { locale } = useLocaleContext();
62
78
  const isMobile = useMobile({ key: "md" });
@@ -66,27 +82,40 @@ export default function UserSessions({
66
82
  return translate(translations, key, locale, "en", data);
67
83
  });
68
84
  const getData = useMemoizedFn(async () => {
69
- let data = user?.userSessions || [];
70
- try {
71
- if (!user?.userSessions && session.user) {
72
- data = await client.userSession.getMyLoginSessions();
73
- }
74
- } catch (e) {
75
- console.warn("Failed to convert ip to region");
76
- console.error(e);
77
- }
78
- const now = (/* @__PURE__ */ new Date()).getTime();
79
- return sortBy(data, (x) => {
80
- if (x.visitorId === currentVisitorId) {
81
- return -1;
82
- }
83
- return now - new Date(x.updatedAt).getTime();
85
+ const result = await getUserSessions({
86
+ page: filterParams.page,
87
+ pageSize: filterParams.pageSize,
88
+ status: filterParams.status
84
89
  });
90
+ const total = result?.paging?.total || 0;
91
+ userSessionsCountMap[filterParams.status] = total;
92
+ return {
93
+ total,
94
+ list: result?.list || []
95
+ };
96
+ });
97
+ useRequest(async () => {
98
+ const result = await getUserSessions({
99
+ page: 1,
100
+ pageSize: 1,
101
+ status: "expired"
102
+ });
103
+ userSessionsCountMap.expired = result?.paging?.total || 0;
104
+ return result?.paging?.total || 0;
105
+ });
106
+ const pageState = useRequest(getData, {
107
+ refreshDeps: [filterParams.status, filterParams.page, filterParams.pageSize]
85
108
  });
86
- const pageState = useRequest(getData);
87
109
  const safeData = useCreation(() => {
88
- return pageState.data || [];
89
- }, [pageState.data]);
110
+ return pageState.data?.list || [];
111
+ }, [pageState.data?.list]);
112
+ const disableLogout = useCreation(() => {
113
+ const status = filterParams.status;
114
+ if (status === "online") {
115
+ return userSessionsCountMap[status] <= 1;
116
+ }
117
+ return userSessionsCountMap[status] === 0;
118
+ }, [safeData, currentVisitorId]);
90
119
  const logout = useMemoizedFn(({ visitorId }) => {
91
120
  confirmApi.open({
92
121
  title: t("logoutThisSession"),
@@ -103,26 +132,24 @@ export default function UserSessions({
103
132
  }
104
133
  });
105
134
  });
106
- const otherUserSessions = useCreation(() => {
107
- const list = safeData.filter((x) => x.visitorId !== currentVisitorId);
108
- return list;
109
- }, [safeData]);
110
135
  const logoutAll = useMemoizedFn(() => {
111
136
  confirmApi.open({
112
- title: t("logoutAllSession"),
113
- content: t("logoutAllSessionConfirm"),
137
+ title: t("logoutAllSession", {
138
+ type: t(filterParams.status)
139
+ }),
140
+ content: t("logoutAllSessionConfirm", {
141
+ type: t(filterParams.status)
142
+ }),
114
143
  confirmButtonText: t("confirm"),
115
144
  confirmButtonProps: {
116
145
  color: "error"
117
146
  },
118
147
  cancelButtonText: t("cancel"),
119
148
  onConfirm: async () => {
120
- const list = otherUserSessions.map((x) => {
121
- return () => client.user.logout({ visitorId: x.visitorId });
122
- });
123
- await pAll(list, {
124
- concurrency: 3,
125
- stopOnError: false
149
+ await client.user.logout({
150
+ // @ts-expect-error js-sdk 发了新版后,会有这个类型定义
151
+ status: filterParams.status,
152
+ visitorId: currentVisitorId
126
153
  });
127
154
  pageState.refresh();
128
155
  confirmApi.close();
@@ -132,18 +159,29 @@ export default function UserSessions({
132
159
  const customButtons = [];
133
160
  if (showAction) {
134
161
  customButtons.push(
135
- /* @__PURE__ */ jsx(Tooltip, { title: t("logoutAllTips"), children: /* @__PURE__ */ jsx(
136
- Button,
162
+ /* @__PURE__ */ jsx(
163
+ Tooltip,
137
164
  {
138
- sx: { ml: 0.5 },
139
- size: "small",
140
- variant: "contained",
141
- color: "error",
142
- onClick: logoutAll,
143
- disabled: otherUserSessions.length === 0,
144
- children: t("logoutAll")
145
- }
146
- ) }, "logoutAll")
165
+ title: t("logoutAllTips", {
166
+ type: t(filterParams.status)
167
+ }),
168
+ children: /* @__PURE__ */ jsx(
169
+ Button,
170
+ {
171
+ sx: { ml: 0.5 },
172
+ size: "small",
173
+ variant: "contained",
174
+ color: "error",
175
+ onClick: logoutAll,
176
+ disabled: disableLogout,
177
+ children: t("logoutAll", {
178
+ type: t(filterParams.status)
179
+ })
180
+ }
181
+ )
182
+ },
183
+ "logoutAll"
184
+ )
147
185
  );
148
186
  }
149
187
  const tableOptions = useCreation(() => {
@@ -155,9 +193,12 @@ export default function UserSessions({
155
193
  filter: false,
156
194
  print: false,
157
195
  expandableRowsOnClick: false,
158
- searchDebounceTime: 600
196
+ searchDebounceTime: 600,
197
+ page: filterParams.page - 1,
198
+ rowsPerPage: filterParams.pageSize,
199
+ count: pageState.data?.total || 0
159
200
  };
160
- }, []);
201
+ }, [pageState.data?.total, filterParams.page, filterParams.pageSize]);
161
202
  const columns = [
162
203
  {
163
204
  label: t("platform"),
@@ -307,13 +348,60 @@ export default function UserSessions({
307
348
  /* @__PURE__ */ jsx(
308
349
  Datatable,
309
350
  {
351
+ count: pageState.data?.total || 0,
310
352
  locale,
311
353
  data: safeData,
312
354
  columns,
313
355
  customButtons,
314
356
  options: tableOptions,
315
357
  loading: pageState.loading,
316
- className: "pc-user-sessions-table"
358
+ className: "pc-user-sessions-table",
359
+ title: /* @__PURE__ */ jsxs(
360
+ RadioGroup,
361
+ {
362
+ row: true,
363
+ sx: { lineHeight: 1, ml: 1 },
364
+ onChange: (e) => {
365
+ filterParams.status = e.target.value;
366
+ },
367
+ children: [
368
+ /* @__PURE__ */ jsx(
369
+ FormControlLabel,
370
+ {
371
+ value: "online",
372
+ control: /* @__PURE__ */ jsx(Radio, { size: "small", sx: { lineHeight: 1, fontSize: 0 }, checked: filterParams.status === "online" }),
373
+ label: /* @__PURE__ */ jsxs(Typography, { sx: { display: "flex", alignItems: "center" }, children: [
374
+ t("online"),
375
+ /* @__PURE__ */ jsxs(Typography, { component: "span", sx: { ml: 0.5, color: "text.secondary" }, children: [
376
+ "(",
377
+ userSessionsCountMap.online,
378
+ ")"
379
+ ] })
380
+ ] })
381
+ }
382
+ ),
383
+ /* @__PURE__ */ jsx(
384
+ FormControlLabel,
385
+ {
386
+ value: "expired",
387
+ control: /* @__PURE__ */ jsx(Radio, { size: "small", sx: { lineHeight: 1, fontSize: 0 }, checked: filterParams.status === "expired" }),
388
+ label: /* @__PURE__ */ jsxs(Typography, { sx: { display: "flex", alignItems: "center" }, children: [
389
+ t("expired"),
390
+ /* @__PURE__ */ jsxs(Typography, { component: "span", sx: { ml: 0.5, color: "text.secondary" }, children: [
391
+ "(",
392
+ userSessionsCountMap.expired,
393
+ ")"
394
+ ] })
395
+ ] })
396
+ }
397
+ )
398
+ ]
399
+ }
400
+ ),
401
+ onChange: (state) => {
402
+ filterParams.page = state.page + 1;
403
+ filterParams.pageSize = state.rowsPerPage;
404
+ }
317
405
  }
318
406
  )
319
407
  ]
@@ -26,6 +26,7 @@ export declare const translations: {
26
26
  logoutThisSessionConfirm: string;
27
27
  logoutAllSession: string;
28
28
  logoutAllSessionConfirm: string;
29
+ online: string;
29
30
  };
30
31
  en: {
31
32
  confirm: string;
@@ -54,5 +55,6 @@ export declare const translations: {
54
55
  logoutThisSessionConfirm: string;
55
56
  logoutAllSession: string;
56
57
  logoutAllSessionConfirm: string;
58
+ online: string;
57
59
  };
58
60
  };
@@ -15,7 +15,7 @@ export const translations = {
15
15
  updatedAt: "\u6700\u8FD1\u6D3B\u52A8\u65F6\u95F4",
16
16
  lastLoginIp: "\u6700\u8FD1\u6D3B\u52A8\u5730\u5740",
17
17
  actions: "\u64CD\u4F5C",
18
- logoutAll: "\u6CE8\u9500\u6240\u6709\u4F1A\u8BDD",
18
+ logoutAll: "\u6CE8\u9500\u6240\u6709{type}\u4F1A\u8BDD",
19
19
  logoutAllTips: "\u4E0D\u4F1A\u6CE8\u9500\u5F53\u524D\u4F1A\u8BDD",
20
20
  logout: "\u6CE8\u9500",
21
21
  currentSession: "\u5F53\u524D\u4F1A\u8BDD",
@@ -24,8 +24,9 @@ export const translations = {
24
24
  remove: "\u5220\u9664",
25
25
  logoutThisSession: "\u6CE8\u9500\u6307\u5B9A\u4F1A\u8BDD",
26
26
  logoutThisSessionConfirm: "\u786E\u5B9A\u8981\u6CE8\u9500\u6B64\u4F1A\u8BDD\u5417?",
27
- logoutAllSession: "\u6CE8\u9500\u6240\u6709\u4F1A\u8BDD",
28
- logoutAllSessionConfirm: "\u786E\u5B9A\u8981\u6CE8\u9500\u6240\u6709\u4F1A\u8BDD\u5417?"
27
+ logoutAllSession: "\u6CE8\u9500\u6240\u6709{type}\u4F1A\u8BDD",
28
+ logoutAllSessionConfirm: "\u786E\u5B9A\u8981\u6CE8\u9500\u6240\u6709{type}\u4F1A\u8BDD\u5417?",
29
+ online: "\u6D3B\u8DC3"
29
30
  },
30
31
  en: {
31
32
  confirm: "Confirm",
@@ -43,7 +44,7 @@ export const translations = {
43
44
  updatedAt: "Last Active Time",
44
45
  actions: "Actions",
45
46
  lastLoginIp: "Last Login IP",
46
- logoutAll: "Logout all",
47
+ logoutAll: "Logout all {type} sessions",
47
48
  logoutAllTips: "Will not logout current session",
48
49
  logout: "Logout",
49
50
  currentSession: "Current Session",
@@ -52,7 +53,8 @@ export const translations = {
52
53
  remove: "Remove",
53
54
  logoutThisSession: "Logout this session",
54
55
  logoutThisSessionConfirm: "Are you sure to logout this session?",
55
- logoutAllSession: "Logout all sessions",
56
- logoutAllSessionConfirm: "Are you sure to logout all sessions?"
56
+ logoutAllSession: "Logout all {type} sessions",
57
+ logoutAllSessionConfirm: "Are you sure to logout all {type} sessions?",
58
+ online: "Active"
57
59
  }
58
60
  };
@@ -2,7 +2,6 @@ import { jsx } from "react/jsx-runtime";
2
2
  import Badge from "@mui/material/Badge";
3
3
  import PropTypes from "prop-types";
4
4
  import { useCallback, useEffect } from "react";
5
- import { temp as colors } from "@arcblock/ux/lib/Colors";
6
5
  import { IconButton } from "@mui/material";
7
6
  import { useSnackbar } from "notistack";
8
7
  import NotificationsOutlinedIcon from "@arcblock/icons/lib/Notification";
@@ -89,7 +88,6 @@ export default function NotificationAddon({ session = {} }) {
89
88
  variant: "outlined",
90
89
  href: viewAllUrl,
91
90
  sx: {
92
- borderColor: colors.lineBorderStrong,
93
91
  "&:hover": {
94
92
  borderRadius: "50%"
95
93
  }