mirror of
https://github.com/hedgedoc/hedgedoc.git
synced 2025-05-22 19:25:18 -04:00
feature: add identicon generation to users without photo
Signed-off-by: Erik Michelson <github@erik.michelson.eu>
This commit is contained in:
parent
55398e2428
commit
a8b3b117dc
17 changed files with 210 additions and 23 deletions
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
@ -7,5 +7,5 @@
|
|||
export interface UserInfo {
|
||||
username: string
|
||||
displayName: string
|
||||
photo: string
|
||||
photoUrl: string
|
||||
}
|
||||
|
|
|
@ -104,3 +104,47 @@ exports[`UserAvatar renders the user avatar in size sm 1`] = `
|
|||
</span>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`UserAvatar uses identicon when empty photoUrl is given 1`] = `
|
||||
<div>
|
||||
<span
|
||||
class="d-inline-flex align-items-center "
|
||||
>
|
||||
<img
|
||||
alt="common.avatarOf"
|
||||
class="rounded user-image"
|
||||
height="20"
|
||||
src="data:image/x-other,identicon-mock"
|
||||
title="common.avatarOf"
|
||||
width="20"
|
||||
/>
|
||||
<span
|
||||
class="ms-2 me-1 user-line-name"
|
||||
>
|
||||
test
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`UserAvatar uses identicon when no photoUrl is given 1`] = `
|
||||
<div>
|
||||
<span
|
||||
class="d-inline-flex align-items-center "
|
||||
>
|
||||
<img
|
||||
alt="common.avatarOf"
|
||||
class="rounded user-image"
|
||||
height="20"
|
||||
src="data:image/x-other,identicon-mock"
|
||||
title="common.avatarOf"
|
||||
width="20"
|
||||
/>
|
||||
<span
|
||||
class="ms-2 me-1 user-line-name"
|
||||
>
|
||||
test
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
`;
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 14 KiB |
|
@ -1,3 +0,0 @@
|
|||
SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
|
||||
|
||||
SPDX-License-Identifier: CC0-1.0
|
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import { useMemo } from 'react'
|
||||
import { createAvatar } from '@dicebear/core'
|
||||
import identicon from '@dicebear/identicon'
|
||||
|
||||
/**
|
||||
* Returns the correct avatar url for a user.
|
||||
* When an empty or no photoUrl is given, a random avatar is generated from the displayName.
|
||||
*
|
||||
* @param photoUrl The photo url of the user to use. Maybe empty or not set.
|
||||
* @param displayName The display name of the user to use as input to the random avatar.
|
||||
* @return The correct avatar url for the user.
|
||||
*/
|
||||
export const useAvatarUrl = (photoUrl: string | undefined, displayName: string): string => {
|
||||
return useMemo(() => {
|
||||
if (photoUrl && photoUrl.trim() !== '') {
|
||||
return photoUrl
|
||||
}
|
||||
const avatar = createAvatar(identicon, {
|
||||
seed: displayName
|
||||
})
|
||||
return avatar.toDataUriSync()
|
||||
}, [photoUrl, displayName])
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
@ -19,5 +19,5 @@ export interface UserAvatarForUserProps extends Omit<UserAvatarProps, 'photoUrl'
|
|||
* @param props remaining avatar props
|
||||
*/
|
||||
export const UserAvatarForUser: React.FC<UserAvatarForUserProps> = ({ user, ...props }) => {
|
||||
return <UserAvatar displayName={user.displayName} photoUrl={user.photo} {...props} />
|
||||
return <UserAvatar displayName={user.displayName} photoUrl={user.photoUrl} {...props} />
|
||||
}
|
||||
|
|
|
@ -7,12 +7,20 @@ import type { UserInfo } from '../../../api/users/types'
|
|||
import { mockI18n } from '../../../test-utils/mock-i18n'
|
||||
import { UserAvatarForUser } from './user-avatar-for-user'
|
||||
import { render } from '@testing-library/react'
|
||||
import { UserAvatar } from './user-avatar'
|
||||
|
||||
jest.mock('@dicebear/identicon', () => null)
|
||||
jest.mock('@dicebear/core', () => ({
|
||||
createAvatar: jest.fn(() => ({
|
||||
toDataUriSync: jest.fn(() => 'data:image/x-other,identicon-mock')
|
||||
}))
|
||||
}))
|
||||
|
||||
describe('UserAvatar', () => {
|
||||
const user: UserInfo = {
|
||||
username: 'boatface',
|
||||
displayName: 'Boaty McBoatFace',
|
||||
photo: 'https://example.com/test.png'
|
||||
photoUrl: 'https://example.com/test.png'
|
||||
}
|
||||
|
||||
beforeEach(async () => {
|
||||
|
@ -41,4 +49,14 @@ describe('UserAvatar', () => {
|
|||
const view = render(<UserAvatarForUser user={user} showName={false} />)
|
||||
expect(view.container).toMatchSnapshot()
|
||||
})
|
||||
|
||||
it('uses identicon when no photoUrl is given', () => {
|
||||
const view = render(<UserAvatar displayName={'test'} />)
|
||||
expect(view.container).toMatchSnapshot()
|
||||
})
|
||||
|
||||
it('uses identicon when empty photoUrl is given', () => {
|
||||
const view = render(<UserAvatar displayName={'test'} photoUrl={''} />)
|
||||
expect(view.container).toMatchSnapshot()
|
||||
})
|
||||
})
|
||||
|
|
|
@ -1,15 +1,15 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import { useTranslatedText } from '../../../hooks/common/use-translated-text'
|
||||
import { ShowIf } from '../show-if/show-if'
|
||||
import defaultAvatar from './default-avatar.png'
|
||||
import styles from './user-avatar.module.scss'
|
||||
import React, { useCallback, useMemo } from 'react'
|
||||
import { OverlayTrigger, Tooltip } from 'react-bootstrap'
|
||||
import type { OverlayInjectedProps } from 'react-bootstrap/Overlay'
|
||||
import { useAvatarUrl } from './hooks/use-avatar-url'
|
||||
|
||||
export interface UserAvatarProps {
|
||||
size?: 'sm' | 'lg'
|
||||
|
@ -45,9 +45,7 @@ export const UserAvatar: React.FC<UserAvatarProps> = ({
|
|||
}
|
||||
}, [size])
|
||||
|
||||
const avatarUrl = useMemo(() => {
|
||||
return photoUrl || defaultAvatar.src
|
||||
}, [photoUrl])
|
||||
const avatarUrl = useAvatarUrl(photoUrl, displayName)
|
||||
|
||||
const imageTranslateOptions = useMemo(
|
||||
() => ({
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
@ -15,7 +15,7 @@ export const fetchAndSetUser: () => Promise<void> = async () => {
|
|||
setUser({
|
||||
username: me.username,
|
||||
displayName: me.displayName,
|
||||
photo: me.photo,
|
||||
photoUrl: me.photoUrl,
|
||||
authProvider: me.authProvider,
|
||||
email: me.email
|
||||
})
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
@ -15,7 +15,7 @@ const handler = (req: NextApiRequest, res: NextApiResponse): void => {
|
|||
}
|
||||
respondToMatchingRequest<LoginUserInfo>(HttpMethod.GET, req, res, {
|
||||
username: 'mock',
|
||||
photo: '/public/img/avatar.png',
|
||||
photoUrl: '/public/img/avatar.png',
|
||||
displayName: 'Mock User',
|
||||
authProvider: 'local',
|
||||
email: 'mock@hedgedoc.test'
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
@ -11,7 +11,7 @@ const handler = (req: NextApiRequest, res: NextApiResponse): void => {
|
|||
respondToMatchingRequest<UserInfo>(HttpMethod.GET, req, res, {
|
||||
username: 'erik',
|
||||
displayName: 'Erik',
|
||||
photo: '/public/img/avatar.png'
|
||||
photoUrl: '/public/img/avatar.png'
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
@ -11,7 +11,7 @@ const handler = (req: NextApiRequest, res: NextApiResponse): void => {
|
|||
respondToMatchingRequest<UserInfo>(HttpMethod.GET, req, res, {
|
||||
username: 'molly',
|
||||
displayName: 'Molly',
|
||||
photo: '/public/img/avatar.png'
|
||||
photoUrl: '/public/img/avatar.png'
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
@ -11,7 +11,7 @@ const handler = (req: NextApiRequest, res: NextApiResponse): void => {
|
|||
respondToMatchingRequest<UserInfo>(HttpMethod.GET, req, res, {
|
||||
username: 'tilman',
|
||||
displayName: 'Tilman',
|
||||
photo: '/public/img/avatar.png'
|
||||
photoUrl: '/public/img/avatar.png'
|
||||
})
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue