diff --git a/packages/console/src/assets/avatars/avatar-001.png b/packages/console/src/assets/avatars/avatar-001.png
deleted file mode 100644
index 222790c55..000000000
Binary files a/packages/console/src/assets/avatars/avatar-001.png and /dev/null differ
diff --git a/packages/console/src/assets/avatars/avatar-002.png b/packages/console/src/assets/avatars/avatar-002.png
deleted file mode 100644
index 46dea241d..000000000
Binary files a/packages/console/src/assets/avatars/avatar-002.png and /dev/null differ
diff --git a/packages/console/src/assets/avatars/avatar-003.png b/packages/console/src/assets/avatars/avatar-003.png
deleted file mode 100644
index a027d7386..000000000
Binary files a/packages/console/src/assets/avatars/avatar-003.png and /dev/null differ
diff --git a/packages/console/src/assets/avatars/avatar-004.png b/packages/console/src/assets/avatars/avatar-004.png
deleted file mode 100644
index f52af3d13..000000000
Binary files a/packages/console/src/assets/avatars/avatar-004.png and /dev/null differ
diff --git a/packages/console/src/assets/avatars/avatar-005.png b/packages/console/src/assets/avatars/avatar-005.png
deleted file mode 100644
index 9013d8cf8..000000000
Binary files a/packages/console/src/assets/avatars/avatar-005.png and /dev/null differ
diff --git a/packages/console/src/assets/avatars/avatar-006.png b/packages/console/src/assets/avatars/avatar-006.png
deleted file mode 100644
index d74b9c478..000000000
Binary files a/packages/console/src/assets/avatars/avatar-006.png and /dev/null differ
diff --git a/packages/console/src/assets/avatars/avatar-007.png b/packages/console/src/assets/avatars/avatar-007.png
deleted file mode 100644
index 4b6441fd7..000000000
Binary files a/packages/console/src/assets/avatars/avatar-007.png and /dev/null differ
diff --git a/packages/console/src/assets/avatars/avatar-008.png b/packages/console/src/assets/avatars/avatar-008.png
deleted file mode 100644
index 3cef8a0af..000000000
Binary files a/packages/console/src/assets/avatars/avatar-008.png and /dev/null differ
diff --git a/packages/console/src/assets/avatars/avatar-009.png b/packages/console/src/assets/avatars/avatar-009.png
deleted file mode 100644
index 4c137f791..000000000
Binary files a/packages/console/src/assets/avatars/avatar-009.png and /dev/null differ
diff --git a/packages/console/src/assets/images/broken-image-dark.svg b/packages/console/src/assets/images/broken-image-dark.svg
new file mode 100644
index 000000000..25dad9e92
--- /dev/null
+++ b/packages/console/src/assets/images/broken-image-dark.svg
@@ -0,0 +1,7 @@
+
diff --git a/packages/console/src/assets/images/broken-image-light.svg b/packages/console/src/assets/images/broken-image-light.svg
new file mode 100644
index 000000000..da7b37aef
--- /dev/null
+++ b/packages/console/src/assets/images/broken-image-light.svg
@@ -0,0 +1,7 @@
+
diff --git a/packages/console/src/assets/images/default-avatar-dark.svg b/packages/console/src/assets/images/default-avatar-dark.svg
new file mode 100644
index 000000000..dc9b44512
--- /dev/null
+++ b/packages/console/src/assets/images/default-avatar-dark.svg
@@ -0,0 +1,4 @@
+
diff --git a/packages/console/src/assets/images/default-avatar-light.svg b/packages/console/src/assets/images/default-avatar-light.svg
new file mode 100644
index 000000000..b81017021
--- /dev/null
+++ b/packages/console/src/assets/images/default-avatar-light.svg
@@ -0,0 +1,4 @@
+
diff --git a/packages/console/src/components/AppContent/components/UserInfo/index.module.scss b/packages/console/src/components/AppContent/components/UserInfo/index.module.scss
index 99dc774a4..ba7f6f81a 100644
--- a/packages/console/src/components/AppContent/components/UserInfo/index.module.scss
+++ b/packages/console/src/components/AppContent/components/UserInfo/index.module.scss
@@ -18,7 +18,7 @@
background-color: var(--color-focused-variant);
}
- img {
+ .avatar {
width: 36px;
height: 36px;
margin-right: _.unit(2);
diff --git a/packages/console/src/components/AppContent/components/UserInfo/index.tsx b/packages/console/src/components/AppContent/components/UserInfo/index.tsx
index b1a06903b..76702e408 100644
--- a/packages/console/src/components/AppContent/components/UserInfo/index.tsx
+++ b/packages/console/src/components/AppContent/components/UserInfo/index.tsx
@@ -7,7 +7,7 @@ import { useTranslation } from 'react-i18next';
import SignOut from '@/assets/images/sign-out.svg';
import Dropdown, { DropdownItem } from '@/components/Dropdown';
import { Ring as Spinner } from '@/components/Spinner';
-import { generateAvatarPlaceHolderById } from '@/consts/avatars';
+import UserAvatar from '@/components/UserAvatar';
import { onKeyDownHandler } from '@/utilities/a11y';
import UserInfoSkeleton from '../UserInfoSkeleton';
@@ -35,7 +35,7 @@ const UserInfo = () => {
return ;
}
- const { sub: id, username, picture } = user;
+ const { username, picture } = user;
return (
<>
@@ -51,7 +51,7 @@ const UserInfo = () => {
setShowDropdown(true);
}}
>
-
+
diff --git a/packages/console/src/components/ImageWithErrorFallback/index.tsx b/packages/console/src/components/ImageWithErrorFallback/index.tsx
new file mode 100644
index 000000000..4eb014ced
--- /dev/null
+++ b/packages/console/src/components/ImageWithErrorFallback/index.tsx
@@ -0,0 +1,25 @@
+import { AppearanceMode } from '@logto/schemas';
+import type { ImgHTMLAttributes } from 'react';
+import { useState } from 'react';
+
+import FallbackImageDark from '@/assets/images/broken-image-dark.svg';
+import FallbackImageLight from '@/assets/images/broken-image-light.svg';
+import { useTheme } from '@/hooks/use-theme';
+
+const ImageWithErrorFallback = ({ src, alt, className }: ImgHTMLAttributes) => {
+ const [hasError, setHasError] = useState(false);
+ const theme = useTheme();
+ const Fallback = theme === AppearanceMode.LightMode ? FallbackImageLight : FallbackImageDark;
+
+ const errorHandler = () => {
+ setHasError(true);
+ };
+
+ if (hasError) {
+ return ;
+ }
+
+ return ;
+};
+
+export default ImageWithErrorFallback;
diff --git a/packages/console/src/components/UserAvatar/index.tsx b/packages/console/src/components/UserAvatar/index.tsx
new file mode 100644
index 000000000..6fca40b7e
--- /dev/null
+++ b/packages/console/src/components/UserAvatar/index.tsx
@@ -0,0 +1,38 @@
+import { AppearanceMode } from '@logto/schemas';
+import type { Nullable } from '@silverhand/essentials';
+
+import DarkAvatar from '@/assets/images/default-avatar-dark.svg';
+import LightAvatar from '@/assets/images/default-avatar-light.svg';
+import { useTheme } from '@/hooks/use-theme';
+
+import ImageWithErrorFallback from '../ImageWithErrorFallback';
+
+type Props = {
+ className?: string;
+ url?: Nullable;
+};
+
+const UserAvatar = ({ className, url }: Props) => {
+ const theme = useTheme();
+ const DefaultAvatar = theme === AppearanceMode.LightMode ? LightAvatar : DarkAvatar;
+
+ if (url) {
+ return (
+
+ );
+ }
+
+ return ;
+};
+
+export default UserAvatar;
diff --git a/packages/console/src/consts/avatars.ts b/packages/console/src/consts/avatars.ts
deleted file mode 100644
index 32b79b63c..000000000
--- a/packages/console/src/consts/avatars.ts
+++ /dev/null
@@ -1,24 +0,0 @@
-import avatar001 from '@/assets/avatars/avatar-001.png';
-import avatar002 from '@/assets/avatars/avatar-002.png';
-import avatar003 from '@/assets/avatars/avatar-003.png';
-import avatar004 from '@/assets/avatars/avatar-004.png';
-import avatar005 from '@/assets/avatars/avatar-005.png';
-import avatar006 from '@/assets/avatars/avatar-006.png';
-import avatar007 from '@/assets/avatars/avatar-007.png';
-import avatar008 from '@/assets/avatars/avatar-008.png';
-import avatar009 from '@/assets/avatars/avatar-009.png';
-
-export const Avatars = [
- avatar001,
- avatar002,
- avatar003,
- avatar004,
- avatar005,
- avatar006,
- avatar007,
- avatar008,
- avatar009,
-];
-
-export const generateAvatarPlaceHolderById = (id: string) =>
- Avatars[(id.codePointAt(0) ?? 0) % Avatars.length];
diff --git a/packages/console/src/consts/index.ts b/packages/console/src/consts/index.ts
index 51290fadb..9530fc830 100644
--- a/packages/console/src/consts/index.ts
+++ b/packages/console/src/consts/index.ts
@@ -1,5 +1,4 @@
export * from './applications';
-export * from './avatars';
export * from './connectors';
export * from './logs';
diff --git a/packages/console/src/pages/UserDetails/index.tsx b/packages/console/src/pages/UserDetails/index.tsx
index caa488945..a91afff7f 100644
--- a/packages/console/src/pages/UserDetails/index.tsx
+++ b/packages/console/src/pages/UserDetails/index.tsx
@@ -18,8 +18,8 @@ import DeleteConfirmModal from '@/components/DeleteConfirmModal';
import DetailsSkeleton from '@/components/DetailsSkeleton';
import TabNav, { TabNavItem } from '@/components/TabNav';
import TextLink from '@/components/TextLink';
+import UserAvatar from '@/components/UserAvatar';
import { generatedPasswordStorageKey } from '@/consts';
-import { generateAvatarPlaceHolderById } from '@/consts/avatars';
import type { RequestError } from '@/hooks/use-api';
import useApi from '@/hooks/use-api';
import * as detailsStyles from '@/scss/details.module.scss';
@@ -86,17 +86,7 @@ const UserDetails = () => {
{userId && data && (
<>
- {/**
- * Some social connectors like Google will block the references to its image resource,
- * without specifying the referrerPolicy attribute. Reference:
- * https://stackoverflow.com/questions/40570117/http403-forbidden-error-when-trying-to-load-img-src-with-google-profile-pic
- */}
-
+
{data.name ?? '-'}
diff --git a/packages/console/src/pages/Users/index.tsx b/packages/console/src/pages/Users/index.tsx
index 047837121..e4122b384 100644
--- a/packages/console/src/pages/Users/index.tsx
+++ b/packages/console/src/pages/Users/index.tsx
@@ -18,8 +18,8 @@ import Search from '@/components/Search';
import TableEmpty from '@/components/Table/TableEmpty';
import TableError from '@/components/Table/TableError';
import TableLoading from '@/components/Table/TableLoading';
+import UserAvatar from '@/components/UserAvatar';
import { generatedPasswordStorageKey } from '@/consts';
-import { generateAvatarPlaceHolderById } from '@/consts/avatars';
import type { RequestError } from '@/hooks/use-api';
import * as modalStyles from '@/scss/modal.module.scss';
import * as resourcesStyles from '@/scss/resources.module.scss';
@@ -141,13 +141,7 @@ const Users = () => {
- }
+ icon={}
to={`/users/${id}`}
size="compact"
/>