Skip to content

Commit 4878d69

Browse files
committed
♻️(frontend) add user avatar to thread comments
We extracted the UserAvatar component from the doc-share feature and integrated it into the users feature. It will be used in the thread comments feature as well.
1 parent 2a61008 commit 4878d69

File tree

6 files changed

+118
-77
lines changed

6 files changed

+118
-77
lines changed
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import React from 'react';
2+
3+
interface AvatarSvgProps {
4+
initials: string;
5+
background: string;
6+
}
7+
8+
export const AvatarSvg: React.FC<AvatarSvgProps> = ({
9+
initials,
10+
background,
11+
}) => (
12+
<svg
13+
xmlns="http://www.w3.org/2000/svg"
14+
width="24"
15+
height="24"
16+
viewBox="0 0 24 24"
17+
>
18+
<rect
19+
x="0.5"
20+
y="0.5"
21+
width="23"
22+
height="23"
23+
rx="11.5"
24+
ry="11.5"
25+
fill={background}
26+
stroke="rgba(255,255,255,0.5)"
27+
strokeWidth="1"
28+
/>
29+
<text
30+
x="50%"
31+
y="50%"
32+
dy="0.35em"
33+
textAnchor="middle"
34+
fontFamily="Arial, Helvetica, sans-serif"
35+
fontSize="10"
36+
fontWeight="600"
37+
fill="rgba(255,255,255,0.9)"
38+
>
39+
{initials}
40+
</text>
41+
</svg>
42+
);
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
import { renderToStaticMarkup } from 'react-dom/server';
2+
3+
import { Box } from '@/components';
4+
import { tokens } from '@/cunningham';
5+
6+
import { AvatarSvg } from './AvatarSvg';
7+
8+
const colors = tokens.themes.default.theme.colors;
9+
10+
const avatarsColors = [
11+
colors['blue-500'],
12+
colors['brown-500'],
13+
colors['cyan-500'],
14+
colors['gold-500'],
15+
colors['green-500'],
16+
colors['olive-500'],
17+
colors['orange-500'],
18+
colors['pink-500'],
19+
colors['purple-500'],
20+
colors['yellow-500'],
21+
];
22+
23+
const getColorFromName = (name: string) => {
24+
let hash = 0;
25+
for (let i = 0; i < name.length; i++) {
26+
hash = name.charCodeAt(i) + ((hash << 5) - hash);
27+
}
28+
return avatarsColors[Math.abs(hash) % avatarsColors.length];
29+
};
30+
31+
const getInitialFromName = (name: string) => {
32+
const splitName = name?.split(' ');
33+
return (splitName[0]?.charAt(0) || '?') + (splitName?.[1]?.charAt(0) || '');
34+
};
35+
36+
type UserAvatarProps = {
37+
fullName?: string;
38+
background?: string;
39+
};
40+
41+
export const UserAvatar = ({ fullName, background }: UserAvatarProps) => {
42+
const name = fullName?.trim() || '?';
43+
44+
return (
45+
<Box
46+
$width="24px"
47+
$height="24px"
48+
$direction="row"
49+
$align="center"
50+
$justify="center"
51+
className="--docs--user-avatar"
52+
>
53+
<AvatarSvg
54+
initials={getInitialFromName(name).toUpperCase()}
55+
background={background || getColorFromName(name)}
56+
/>
57+
</Box>
58+
);
59+
};
60+
61+
export const avatarUrlFromName = (fullName?: string): string => {
62+
const name = fullName?.trim() || '?';
63+
const initials = getInitialFromName(name).toUpperCase();
64+
const background = getColorFromName(name);
65+
66+
const svgMarkup = renderToStaticMarkup(
67+
<AvatarSvg initials={initials} background={background} />,
68+
);
69+
70+
return `data:image/svg+xml;charset=UTF-8,${encodeURIComponent(svgMarkup)}`;
71+
};
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
export * from './Auth';
22
export * from './ButtonLogin';
3+
export * from './UserAvatar';

src/frontend/apps/impress/src/features/docs/doc-editor/components/BlockNoteEditor.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import * as Y from 'yjs';
1818

1919
import { Box, TextErrors } from '@/components';
2020
import { Doc, useIsCollaborativeEditable } from '@/docs/doc-management';
21-
import { useAuth } from '@/features/auth';
21+
import { avatarUrlFromName, useAuth } from '@/features/auth';
2222

2323
import {
2424
useHeadings,
@@ -162,7 +162,7 @@ export const BlockNoteEditor = ({ doc, provider }: BlockNoteEditorProps) => {
162162
return {
163163
id: encodedURIUserId,
164164
username: fullName,
165-
avatarUrl: 'https://i.pravatar.cc/300',
165+
avatarUrl: avatarUrlFromName(fullName),
166166
};
167167
}),
168168
);

src/frontend/apps/impress/src/features/docs/doc-share/components/SearchUserRow.tsx

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,7 @@ import {
44
QuickSearchItemContentProps,
55
} from '@/components/quick-search';
66
import { useCunninghamTheme } from '@/cunningham';
7-
import { User } from '@/features/auth';
8-
9-
import { UserAvatar } from './UserAvatar';
7+
import { User, UserAvatar } from '@/features/auth';
108

119
type Props = {
1210
user: User;
@@ -36,7 +34,7 @@ export const SearchUserRow = ({
3634
className="--docs--search-user-row"
3735
>
3836
<UserAvatar
39-
user={user}
37+
fullName={user.full_name || user.email}
4038
background={
4139
isInvitation ? colorsTokens['greyscale-400'] : undefined
4240
}

src/frontend/apps/impress/src/features/docs/doc-share/components/UserAvatar.tsx

Lines changed: 0 additions & 71 deletions
This file was deleted.

0 commit comments

Comments
 (0)