-
Notifications
You must be signed in to change notification settings - Fork 2.9k
/
Avatar.tsx
134 lines (113 loc) · 5.09 KB
/
Avatar.tsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
import React, {useEffect, useState} from 'react';
import type {ImageStyle, StyleProp, ViewStyle} from 'react-native';
import {View} from 'react-native';
import useNetwork from '@hooks/useNetwork';
import useStyleUtils from '@hooks/useStyleUtils';
import useTheme from '@hooks/useTheme';
import useThemeStyles from '@hooks/useThemeStyles';
import * as ReportUtils from '@libs/ReportUtils';
import type {AvatarSource} from '@libs/UserUtils';
import * as UserUtils from '@libs/UserUtils';
import type {AvatarSizeName} from '@styles/utils';
import CONST from '@src/CONST';
import type {AvatarType} from '@src/types/onyx/OnyxCommon';
import Icon from './Icon';
import * as Expensicons from './Icon/Expensicons';
import Image from './Image';
type AvatarProps = {
/** Source for the avatar. Can be a URL or an icon. */
source?: AvatarSource;
/** Extra styles to pass to Image */
imageStyles?: StyleProp<ViewStyle & ImageStyle>;
/** Additional styles to pass to Icon */
iconAdditionalStyles?: StyleProp<ViewStyle>;
/** Extra styles to pass to View wrapper */
containerStyles?: StyleProp<ViewStyle>;
/** Set the size of Avatar */
size?: AvatarSizeName;
/**
* The fill color for the icon. Can be hex, rgb, rgba, or valid react-native named color such as 'red' or 'blue'
* If the avatar is type === workspace, this fill color will be ignored and decided based on the name prop.
*/
fill?: string;
/** A fallback avatar icon to display when there is an error on loading avatar from remote URL.
* If the avatar is type === workspace, this fallback icon will be ignored and decided based on the name prop.
*/
fallbackIcon?: AvatarSource;
/** Used to locate fallback icon in end-to-end tests. */
fallbackIconTestID?: string;
/** Owner of the avatar. If user, displayName. If workspace, policy name */
name?: string;
/** Denotes whether it is an avatar or a workspace avatar */
type: AvatarType;
/** Optional account id if it's user avatar or policy id if it's workspace avatar */
avatarID?: number | string;
};
function Avatar({
source: originalSource,
imageStyles,
iconAdditionalStyles,
containerStyles,
size = CONST.AVATAR_SIZE.DEFAULT,
fill,
fallbackIcon = Expensicons.FallbackAvatar,
fallbackIconTestID = '',
type,
name = '',
avatarID,
}: AvatarProps) {
const theme = useTheme();
const styles = useThemeStyles();
const StyleUtils = useStyleUtils();
const [imageError, setImageError] = useState(false);
useNetwork({onReconnect: () => setImageError(false)});
useEffect(() => {
setImageError(false);
}, [originalSource]);
const isWorkspace = type === CONST.ICON_TYPE_WORKSPACE;
const userAccountID = isWorkspace ? undefined : (avatarID as number);
const source = isWorkspace ? originalSource : UserUtils.getAvatar(originalSource, userAccountID);
const useFallBackAvatar = imageError || !source || source === Expensicons.FallbackAvatar;
const fallbackAvatar = isWorkspace ? ReportUtils.getDefaultWorkspaceAvatar(name) : fallbackIcon || Expensicons.FallbackAvatar;
const fallbackAvatarTestID = isWorkspace ? ReportUtils.getDefaultWorkspaceAvatarTestID(name) : fallbackIconTestID || 'SvgFallbackAvatar Icon';
const avatarSource = useFallBackAvatar ? fallbackAvatar : source;
// We pass the color styles down to the SVG for the workspace and fallback avatar.
const iconSize = StyleUtils.getAvatarSize(size);
const imageStyle: StyleProp<ImageStyle> = [StyleUtils.getAvatarStyle(size), imageStyles, styles.noBorderRadius];
const iconStyle = imageStyles ? [StyleUtils.getAvatarStyle(size), styles.bgTransparent, imageStyles] : undefined;
let iconColors;
if (isWorkspace) {
iconColors = StyleUtils.getDefaultWorkspaceAvatarColor(avatarID?.toString() ?? '');
} else if (useFallBackAvatar) {
iconColors = StyleUtils.getBackgroundColorAndFill(theme.buttonHoveredBG, theme.icon);
} else {
iconColors = null;
}
return (
<View style={[containerStyles, styles.pointerEventsNone]}>
{typeof avatarSource === 'string' ? (
<View style={[iconStyle, StyleUtils.getAvatarBorderStyle(size, type), iconAdditionalStyles]}>
<Image
source={{uri: avatarSource}}
style={imageStyle}
onError={() => setImageError(true)}
/>
</View>
) : (
<View style={iconStyle}>
<Icon
testID={fallbackAvatarTestID}
src={avatarSource}
height={iconSize}
width={iconSize}
fill={imageError ? iconColors?.fill ?? theme.offline : iconColors?.fill ?? fill}
additionalStyles={[StyleUtils.getAvatarBorderStyle(size, type), iconColors, iconAdditionalStyles]}
/>
</View>
)}
</View>
);
}
Avatar.displayName = 'Avatar';
export default Avatar;
export {type AvatarProps};