2.6 KB107 lines
Blame
1/**
2 * Copyright (c) Meta Platforms, Inc. and affiliates.
3 *
4 * This source code is licensed under the MIT license found in the
5 * LICENSE file in the root directory of this source tree.
6 */
7
8import type {DetailedHTMLProps} from 'react';
9
10import * as stylex from '@stylexjs/stylex';
11import {useAtomValue} from 'jotai';
12import {colors, radius} from '../../components/theme/tokens.stylex';
13import serverAPI from './ClientToServerAPI';
14import {t} from './i18n';
15import {atomFamilyWeak, lazyAtom} from './jotaiUtils';
16
17const avatarUrl = atomFamilyWeak((author: string) => {
18 // Rate limitor for the same author is by lazyAtom and atomFamilyWeak caching.
19 return lazyAtom(async () => {
20 serverAPI.postMessage({
21 type: 'fetchAvatars',
22 authors: [author],
23 });
24 const result = await serverAPI.nextMessageMatching('fetchedAvatars', ({authors}) =>
25 authors.includes(author),
26 );
27 return result.avatars.get(author);
28 }, undefined);
29});
30
31export function AvatarImg({
32 url,
33 username,
34 xstyle,
35 ...rest
36}: {url?: string; username: string; xstyle?: stylex.StyleXStyles} & DetailedHTMLProps<
37 React.ImgHTMLAttributes<HTMLImageElement>,
38 HTMLImageElement
39>) {
40 return url == null ? null : (
41 <img
42 {...stylex.props(styles.circle, xstyle)}
43 src={url}
44 width={14}
45 height={14}
46 alt={t("$user's avatar photo", {replace: {$user: username}})}
47 {...rest}
48 />
49 );
50}
51
52const styles = stylex.create({
53 circle: {
54 width: 14,
55 height: 14,
56 border: '2px solid',
57 borderRadius: radius.full,
58 borderColor: colors.fg,
59 },
60 empty: {
61 content: '',
62 backgroundColor: 'var(--foreground)',
63 },
64});
65
66export function BlankAvatar() {
67 return <div {...stylex.props(styles.circle, styles.empty)} />;
68}
69
70export function Avatar({
71 username,
72 ...rest
73}: {username: string} & DetailedHTMLProps<
74 React.ImgHTMLAttributes<HTMLImageElement>,
75 HTMLImageElement
76>) {
77 const url = useAtomValue(avatarUrl(username));
78 return url == null ? <BlankAvatar /> : <AvatarImg url={url} username={username} {...rest} />;
79}
80
81/** Render as a SVG pattern */
82export function AvatarPattern({
83 username,
84 size,
85 id,
86 fallbackFill,
87}: {
88 username: string;
89 size: number;
90 id: string;
91 fallbackFill: string;
92}) {
93 const img = useAtomValue(avatarUrl(username));
94 return (
95 <pattern
96 id={id}
97 patternUnits="userSpaceOnUse"
98 width={size}
99 height={size}
100 x={-size / 2}
101 y={-size / 2}>
102 <rect width={size} height={size} fill={fallbackFill} strokeWidth={0} />
103 <image href={img} width={size} height={size} />
104 </pattern>
105 );
106}
107