@arcblock/ux 2.13.18 → 2.13.19

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.
@@ -29,7 +29,7 @@ function MinimalContent(props) {
29
29
  display: "flex",
30
30
  justifyContent: "flex-start",
31
31
  alignItems: "center",
32
- gap: 2,
32
+ gap: 1,
33
33
  flex: 1,
34
34
  minWidth: 0,
35
35
  children: [/*#__PURE__*/_jsx(TooltipAvatar, {
@@ -1,6 +1,7 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import React from 'react';
3
- import { Box } from '@mui/material';
3
+ import Box from '@mui/material/Box';
4
+ import MaterialAvatar from '@mui/material/Avatar';
4
5
  import Avatar from '../Avatar';
5
6
  import { createNameOnlyAvatar } from './utils';
6
7
 
@@ -12,16 +13,25 @@ export const renderAvatar = (user, avatarSize = 48, avatarProps = undefined, onA
12
13
  // 如果用户没有头像,则显示名称首字母头像
13
14
  if (!user.avatar) {
14
15
  const avatarContent = createNameOnlyAvatar(user);
15
- return /*#__PURE__*/_jsx(Avatar, {
16
- size: avatarSize,
17
- did: user.did,
18
- variant: "circle",
16
+ // 从 avatarProps 中提取 MUI 的 Avatar 支持的基本 props
17
+ // className, style 等基本 React 属性
18
+ const {
19
+ className,
20
+ style,
21
+ alt
22
+ } = avatarProps || {};
23
+ return /*#__PURE__*/_jsx(MaterialAvatar, {
19
24
  onClick: onClick,
25
+ className: className,
26
+ style: style,
27
+ alt: alt,
20
28
  sx: {
29
+ width: avatarSize,
30
+ height: avatarSize,
21
31
  fontSize: avatarSize * 0.4,
22
32
  cursor: shouldShowHoverCard || onAvatarClick ? 'pointer' : 'default'
23
33
  },
24
- ...(avatarProps || {}),
34
+ variant: "circular",
25
35
  children: avatarContent
26
36
  });
27
37
  }
@@ -46,7 +46,11 @@ function UserCard(props) {
46
46
  } else if (props.did && isUserDid(props.did) && !props.user) {
47
47
  getUserByDid(props.did).then(_user => {
48
48
  if (isSubscribed) {
49
- setUser(_user);
49
+ setUser(_user || {
50
+ fullName: 'Anonymous',
51
+ did: props.did,
52
+ avatar: ''
53
+ });
50
54
  }
51
55
  });
52
56
  }
@@ -76,7 +80,8 @@ function UserCard(props) {
76
80
  avatarProps: props.popupAvatarProps,
77
81
  shortenLabelProps: props.popupShortenLabelProps,
78
82
  renderFields: props.popupRenderFields,
79
- renderName: props.popupRenderName
83
+ renderName: props.popupRenderName,
84
+ infoType: props.popupInfoType || props.infoType
80
85
  })
81
86
  });
82
87
  };
@@ -7,7 +7,6 @@ type UserPublicInfo = {
7
7
  avatar: string;
8
8
  did: string;
9
9
  fullName: string;
10
- sourceAppPid: string | null;
11
10
  };
12
11
  export type UserMetadataLink = {
13
12
  url: string;
@@ -95,6 +94,7 @@ export interface UserCardProps {
95
94
  did?: string;
96
95
  cardType?: CardType;
97
96
  infoType?: InfoType;
97
+ popupInfoType?: InfoType;
98
98
  avatarSize?: number;
99
99
  showHoverCard?: boolean;
100
100
  showDid?: boolean;
@@ -2,3 +2,4 @@ import { User } from './types';
2
2
  export declare function createNameOnlyAvatar(user: User): string | null;
3
3
  export declare function isUserDid(did: string): boolean;
4
4
  export declare function getUserByDid(did: string): Promise<User | null>;
5
+ export declare function clearUserCache(did?: string): void;
@@ -9,6 +9,53 @@ try {
9
9
  client = null;
10
10
  }
11
11
 
12
+ // 用户信息缓存键前缀
13
+ const USER_CACHE_PREFIX = 'ux_user_';
14
+ // 用户缓存的有效期(毫秒),默认60分钟
15
+ const USER_CACHE_EXPIRATION = 60 * 60 * 1000;
16
+
17
+ // 从sessionStorage获取用户信息
18
+ const getUserFromStorage = did => {
19
+ if (typeof sessionStorage === 'undefined') return null;
20
+ try {
21
+ const cacheKey = `${USER_CACHE_PREFIX}${did}`;
22
+ const cachedData = sessionStorage.getItem(cacheKey);
23
+ if (!cachedData) return null;
24
+ const parsedData = JSON.parse(cachedData);
25
+ const timestamp = parsedData.timestamp || 0;
26
+ const now = Date.now();
27
+
28
+ // 检查缓存是否过期
29
+ if (now - timestamp > USER_CACHE_EXPIRATION) {
30
+ // 缓存已过期,删除并返回null
31
+ sessionStorage.removeItem(cacheKey);
32
+ return null;
33
+ }
34
+
35
+ // 返回用户数据
36
+ return parsedData.user;
37
+ } catch (error) {
38
+ console.error(`Failed to load user cache for did ${did}:`, error);
39
+ return null;
40
+ }
41
+ };
42
+
43
+ // 将用户信息保存到sessionStorage
44
+ const saveUserToStorage = (did, user) => {
45
+ if (typeof sessionStorage === 'undefined') return;
46
+ try {
47
+ const cacheKey = `${USER_CACHE_PREFIX}${did}`;
48
+ // 创建包含用户数据和时间戳的缓存对象
49
+ const cacheData = {
50
+ user,
51
+ timestamp: Date.now()
52
+ };
53
+ sessionStorage.setItem(cacheKey, JSON.stringify(cacheData));
54
+ } catch (error) {
55
+ console.error(`Failed to save user cache for did ${did}:`, error);
56
+ }
57
+ };
58
+
12
59
  // 创建仅显示名称首字母的头像
13
60
  // eslint-disable-next-line import/prefer-default-export
14
61
  export function createNameOnlyAvatar(user) {
@@ -39,14 +86,54 @@ export function isUserDid(did) {
39
86
  }
40
87
  }
41
88
  export async function getUserByDid(did) {
89
+ if (!did) return null;
90
+
91
+ // 先检查sessionStorage中是否有缓存
92
+ const storedUser = getUserFromStorage(did);
93
+ if (storedUser) {
94
+ return storedUser;
95
+ }
42
96
  if (!client) return null;
43
97
  try {
44
98
  const user = await client.user.getUserPublicInfo({
45
99
  did
46
100
  });
101
+ // 将获取到的用户信息存入缓存
102
+ if (user) {
103
+ const userData = user;
104
+ saveUserToStorage(did, userData);
105
+ }
47
106
  return user;
48
107
  } catch (error) {
49
108
  console.error('Failed to get user by did:', error);
50
109
  return null;
51
110
  }
111
+ }
112
+
113
+ // 清除缓存中特定用户的信息
114
+ export function clearUserCache(did) {
115
+ if (typeof sessionStorage === 'undefined') return;
116
+ if (did) {
117
+ // 清除指定用户缓存
118
+ try {
119
+ sessionStorage.removeItem(`${USER_CACHE_PREFIX}${did}`);
120
+ } catch (error) {
121
+ console.error(`Failed to remove cache for did ${did}:`, error);
122
+ }
123
+ } else {
124
+ // 清除所有用户缓存
125
+ try {
126
+ // 只清除以USER_CACHE_PREFIX开头的项
127
+ const keysToRemove = [];
128
+ for (let i = 0; i < sessionStorage.length; i++) {
129
+ const key = sessionStorage.key(i);
130
+ if (key && key.startsWith(USER_CACHE_PREFIX)) {
131
+ keysToRemove.push(key);
132
+ }
133
+ }
134
+ keysToRemove.forEach(key => sessionStorage.removeItem(key));
135
+ } catch (error) {
136
+ console.error('Failed to clear all user caches:', error);
137
+ }
138
+ }
52
139
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@arcblock/ux",
3
- "version": "2.13.18",
3
+ "version": "2.13.19",
4
4
  "description": "Common used react components for arcblock products",
5
5
  "keywords": [
6
6
  "react",
@@ -71,14 +71,14 @@
71
71
  "react": ">=18.2.0",
72
72
  "react-router-dom": ">=6.22.3"
73
73
  },
74
- "gitHead": "7bb88b45cd68de7eb079b590d0d73fd41d303917",
74
+ "gitHead": "64c12fb7646d2323f1be0bef7a3901261687b165",
75
75
  "dependencies": {
76
76
  "@arcblock/did-motif": "^1.1.13",
77
- "@arcblock/icons": "^2.13.18",
78
- "@arcblock/nft-display": "^2.13.18",
79
- "@arcblock/react-hooks": "^2.13.18",
77
+ "@arcblock/icons": "^2.13.19",
78
+ "@arcblock/nft-display": "^2.13.19",
79
+ "@arcblock/react-hooks": "^2.13.19",
80
80
  "@babel/plugin-syntax-dynamic-import": "^7.8.3",
81
- "@blocklet/theme": "^2.13.18",
81
+ "@blocklet/theme": "^2.13.19",
82
82
  "@fontsource/roboto": "~5.1.1",
83
83
  "@fontsource/ubuntu-mono": "^5.0.18",
84
84
  "@iconify-icons/logos": "^1.2.36",
@@ -33,7 +33,7 @@ function MinimalContent(props: MinimalContentProps) {
33
33
 
34
34
  return (
35
35
  <Box display="flex" justifyContent="space-between" alignItems="center" className="user-card__avatar-content">
36
- <Box display="flex" justifyContent="flex-start" alignItems="center" gap={2} flex={1} minWidth={0}>
36
+ <Box display="flex" justifyContent="flex-start" alignItems="center" gap={1} flex={1} minWidth={0}>
37
37
  <TooltipAvatar
38
38
  user={user}
39
39
  avatarSize={avatarSize}
@@ -1,5 +1,6 @@
1
1
  import React from 'react';
2
- import { Box } from '@mui/material';
2
+ import Box from '@mui/material/Box';
3
+ import MaterialAvatar from '@mui/material/Avatar';
3
4
  import Avatar from '../Avatar';
4
5
  import { User, UserCardProps } from './types';
5
6
  import { createNameOnlyAvatar } from './utils';
@@ -18,19 +19,25 @@ export const renderAvatar = (
18
19
  // 如果用户没有头像,则显示名称首字母头像
19
20
  if (!user.avatar) {
20
21
  const avatarContent = createNameOnlyAvatar(user);
22
+ // 从 avatarProps 中提取 MUI 的 Avatar 支持的基本 props
23
+ // className, style 等基本 React 属性
24
+ const { className, style, alt } = avatarProps || {};
25
+
21
26
  return (
22
- <Avatar
23
- size={avatarSize}
24
- did={user.did}
25
- variant="circle"
27
+ <MaterialAvatar
26
28
  onClick={onClick}
29
+ className={className}
30
+ style={style}
31
+ alt={alt}
27
32
  sx={{
33
+ width: avatarSize,
34
+ height: avatarSize,
28
35
  fontSize: avatarSize * 0.4,
29
36
  cursor: shouldShowHoverCard || onAvatarClick ? 'pointer' : 'default',
30
37
  }}
31
- {...(avatarProps || {})}>
38
+ variant="circular">
32
39
  {avatarContent}
33
- </Avatar>
40
+ </MaterialAvatar>
34
41
  );
35
42
  }
36
43
 
@@ -47,7 +47,7 @@ function UserCard(props: UserCardProps) {
47
47
  } else if (props.did && isUserDid(props.did) && !props.user) {
48
48
  getUserByDid(props.did).then((_user) => {
49
49
  if (isSubscribed) {
50
- setUser(_user);
50
+ setUser(_user || { fullName: 'Anonymous', did: props.did as string, avatar: '' });
51
51
  }
52
52
  });
53
53
  }
@@ -74,6 +74,7 @@ function UserCard(props: UserCardProps) {
74
74
  shortenLabelProps={props.popupShortenLabelProps}
75
75
  renderFields={props.popupRenderFields}
76
76
  renderName={props.popupRenderName}
77
+ infoType={props.popupInfoType || props.infoType}
77
78
  />
78
79
  </DialogContainer>
79
80
  );
@@ -8,7 +8,6 @@ type UserPublicInfo = {
8
8
  avatar: string;
9
9
  did: string;
10
10
  fullName: string;
11
- sourceAppPid: string | null;
12
11
  };
13
12
  export type UserMetadataLink = {
14
13
  url: string;
@@ -112,6 +111,7 @@ export interface UserCardProps {
112
111
  did?: string;
113
112
  cardType?: CardType;
114
113
  infoType?: InfoType;
114
+ popupInfoType?: InfoType;
115
115
  avatarSize?: number;
116
116
  showHoverCard?: boolean;
117
117
  showDid?: boolean;
@@ -11,6 +11,57 @@ try {
11
11
  client = null;
12
12
  }
13
13
 
14
+ // 用户信息缓存键前缀
15
+ const USER_CACHE_PREFIX = 'ux_user_';
16
+ // 用户缓存的有效期(毫秒),默认60分钟
17
+ const USER_CACHE_EXPIRATION = 60 * 60 * 1000;
18
+
19
+ // 从sessionStorage获取用户信息
20
+ const getUserFromStorage = (did: string): User | null => {
21
+ if (typeof sessionStorage === 'undefined') return null;
22
+
23
+ try {
24
+ const cacheKey = `${USER_CACHE_PREFIX}${did}`;
25
+ const cachedData = sessionStorage.getItem(cacheKey);
26
+
27
+ if (!cachedData) return null;
28
+
29
+ const parsedData = JSON.parse(cachedData);
30
+ const timestamp = parsedData.timestamp || 0;
31
+ const now = Date.now();
32
+
33
+ // 检查缓存是否过期
34
+ if (now - timestamp > USER_CACHE_EXPIRATION) {
35
+ // 缓存已过期,删除并返回null
36
+ sessionStorage.removeItem(cacheKey);
37
+ return null;
38
+ }
39
+
40
+ // 返回用户数据
41
+ return parsedData.user;
42
+ } catch (error) {
43
+ console.error(`Failed to load user cache for did ${did}:`, error);
44
+ return null;
45
+ }
46
+ };
47
+
48
+ // 将用户信息保存到sessionStorage
49
+ const saveUserToStorage = (did: string, user: User): void => {
50
+ if (typeof sessionStorage === 'undefined') return;
51
+
52
+ try {
53
+ const cacheKey = `${USER_CACHE_PREFIX}${did}`;
54
+ // 创建包含用户数据和时间戳的缓存对象
55
+ const cacheData = {
56
+ user,
57
+ timestamp: Date.now(),
58
+ };
59
+ sessionStorage.setItem(cacheKey, JSON.stringify(cacheData));
60
+ } catch (error) {
61
+ console.error(`Failed to save user cache for did ${did}:`, error);
62
+ }
63
+ };
64
+
14
65
  // 创建仅显示名称首字母的头像
15
66
  // eslint-disable-next-line import/prefer-default-export
16
67
  export function createNameOnlyAvatar(user: User) {
@@ -44,12 +95,55 @@ export function isUserDid(did: string) {
44
95
  }
45
96
 
46
97
  export async function getUserByDid(did: string): Promise<User | null> {
98
+ if (!did) return null;
99
+
100
+ // 先检查sessionStorage中是否有缓存
101
+ const storedUser = getUserFromStorage(did);
102
+ if (storedUser) {
103
+ return storedUser;
104
+ }
105
+
47
106
  if (!client) return null;
107
+
48
108
  try {
49
109
  const user = await client.user.getUserPublicInfo({ did });
110
+ // 将获取到的用户信息存入缓存
111
+ if (user) {
112
+ const userData = user as User;
113
+ saveUserToStorage(did, userData);
114
+ }
50
115
  return user as User;
51
116
  } catch (error) {
52
117
  console.error('Failed to get user by did:', error);
53
118
  return null;
54
119
  }
55
120
  }
121
+
122
+ // 清除缓存中特定用户的信息
123
+ export function clearUserCache(did?: string): void {
124
+ if (typeof sessionStorage === 'undefined') return;
125
+
126
+ if (did) {
127
+ // 清除指定用户缓存
128
+ try {
129
+ sessionStorage.removeItem(`${USER_CACHE_PREFIX}${did}`);
130
+ } catch (error) {
131
+ console.error(`Failed to remove cache for did ${did}:`, error);
132
+ }
133
+ } else {
134
+ // 清除所有用户缓存
135
+ try {
136
+ // 只清除以USER_CACHE_PREFIX开头的项
137
+ const keysToRemove: string[] = [];
138
+ for (let i = 0; i < sessionStorage.length; i++) {
139
+ const key = sessionStorage.key(i);
140
+ if (key && key.startsWith(USER_CACHE_PREFIX)) {
141
+ keysToRemove.push(key);
142
+ }
143
+ }
144
+ keysToRemove.forEach((key) => sessionStorage.removeItem(key));
145
+ } catch (error) {
146
+ console.error('Failed to clear all user caches:', error);
147
+ }
148
+ }
149
+ }