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); }} > - avatar +
{username}
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 {alt}; +}; + +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 - */} - avatar +
{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" />