diff --git a/src/components/Avatar.js b/src/components/Avatar.js
deleted file mode 100644
index 0e173f856593..000000000000
--- a/src/components/Avatar.js
+++ /dev/null
@@ -1,126 +0,0 @@
-import PropTypes from 'prop-types';
-import React, {useEffect, useState} from 'react';
-import {View} from 'react-native';
-import _ from 'underscore';
-import useNetwork from '@hooks/useNetwork';
-import * as ReportUtils from '@libs/ReportUtils';
-import stylePropTypes from '@styles/stylePropTypes';
-import * as StyleUtils from '@styles/StyleUtils';
-import useTheme from '@styles/themes/useTheme';
-import useThemeStyles from '@styles/useThemeStyles';
-import CONST from '@src/CONST';
-import Icon from './Icon';
-import * as Expensicons from './Icon/Expensicons';
-import Image from './Image';
-
-const propTypes = {
- /** Source for the avatar. Can be a URL or an icon. */
- source: PropTypes.oneOfType([PropTypes.string, PropTypes.func]),
-
- /** Extra styles to pass to Image */
- // eslint-disable-next-line react/forbid-prop-types
- imageStyles: PropTypes.arrayOf(PropTypes.object),
-
- /** Additional styles to pass to Icon */
- // eslint-disable-next-line react/forbid-prop-types
- iconAdditionalStyles: PropTypes.arrayOf(PropTypes.object),
-
- /** Extra styles to pass to View wrapper */
- containerStyles: stylePropTypes,
-
- /** Set the size of Avatar */
- size: PropTypes.oneOf(_.values(CONST.AVATAR_SIZE)),
-
- /**
- * 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: PropTypes.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: PropTypes.oneOfType([PropTypes.func, PropTypes.string]),
-
- /** Denotes whether it is an avatar or a workspace avatar */
- type: PropTypes.oneOf([CONST.ICON_TYPE_AVATAR, CONST.ICON_TYPE_WORKSPACE]),
-
- /** Owner of the avatar. If user, displayName. If workspace, policy name */
- name: PropTypes.string,
-};
-
-const defaultProps = {
- source: null,
- imageStyles: [],
- iconAdditionalStyles: [],
- containerStyles: [],
- size: CONST.AVATAR_SIZE.DEFAULT,
- fill: undefined,
- fallbackIcon: Expensicons.FallbackAvatar,
- type: CONST.ICON_TYPE_AVATAR,
- name: '',
-};
-
-function Avatar(props) {
- const theme = useTheme();
- const styles = useThemeStyles();
- const [imageError, setImageError] = useState(false);
-
- useNetwork({onReconnect: () => setImageError(false)});
-
- useEffect(() => {
- setImageError(false);
- }, [props.source]);
-
- if (!props.source) {
- return null;
- }
-
- const isWorkspace = props.type === CONST.ICON_TYPE_WORKSPACE;
- const iconSize = StyleUtils.getAvatarSize(props.size);
-
- const imageStyle =
- props.imageStyles && props.imageStyles.length
- ? [StyleUtils.getAvatarStyle(theme, props.size), ...props.imageStyles, styles.noBorderRadius]
- : [StyleUtils.getAvatarStyle(theme, props.size), styles.noBorderRadius];
-
- const iconStyle = props.imageStyles && props.imageStyles.length ? [StyleUtils.getAvatarStyle(theme, props.size), styles.bgTransparent, ...props.imageStyles] : undefined;
-
- const iconFillColor = isWorkspace ? StyleUtils.getDefaultWorkspaceAvatarColor(props.name).fill : props.fill || theme.icon;
- const fallbackAvatar = isWorkspace ? ReportUtils.getDefaultWorkspaceAvatar(props.name) : props.fallbackIcon || Expensicons.FallbackAvatar;
-
- return (
-
- {_.isFunction(props.source) || (imageError && _.isFunction(fallbackAvatar)) ? (
-
-
-
- ) : (
-
- setImageError(true)}
- />
-
- )}
-
- );
-}
-
-Avatar.defaultProps = defaultProps;
-Avatar.propTypes = propTypes;
-Avatar.displayName = 'Avatar';
-
-export default Avatar;
diff --git a/src/components/Avatar.tsx b/src/components/Avatar.tsx
new file mode 100644
index 000000000000..d394a84bd207
--- /dev/null
+++ b/src/components/Avatar.tsx
@@ -0,0 +1,118 @@
+import React, {useEffect, useState} from 'react';
+import {StyleProp, View, ViewStyle} from 'react-native';
+import useNetwork from '@hooks/useNetwork';
+import * as ReportUtils from '@libs/ReportUtils';
+import {AvatarSource} from '@libs/UserUtils';
+import * as StyleUtils from '@styles/StyleUtils';
+import type {AvatarSizeName} from '@styles/StyleUtils';
+import useTheme from '@styles/themes/useTheme';
+import useThemeStyles from '@styles/useThemeStyles';
+import CONST from '@src/CONST';
+import {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;
+
+ /** Additional styles to pass to Icon */
+ iconAdditionalStyles?: StyleProp;
+
+ /** Extra styles to pass to View wrapper */
+ containerStyles?: StyleProp;
+
+ /** 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;
+
+ /** Denotes whether it is an avatar or a workspace avatar */
+ type?: AvatarType;
+
+ /** Owner of the avatar. If user, displayName. If workspace, policy name */
+ name?: string;
+};
+
+function Avatar({
+ source,
+ imageStyles,
+ iconAdditionalStyles,
+ containerStyles,
+ size = CONST.AVATAR_SIZE.DEFAULT,
+ fill,
+ fallbackIcon = Expensicons.FallbackAvatar,
+ type = CONST.ICON_TYPE_AVATAR,
+ name = '',
+}: AvatarProps) {
+ const theme = useTheme();
+ const styles = useThemeStyles();
+ const [imageError, setImageError] = useState(false);
+
+ useNetwork({onReconnect: () => setImageError(false)});
+
+ useEffect(() => {
+ setImageError(false);
+ }, [source]);
+
+ if (!source) {
+ return null;
+ }
+
+ const isWorkspace = type === CONST.ICON_TYPE_WORKSPACE;
+ const iconSize = StyleUtils.getAvatarSize(size);
+
+ const imageStyle = [StyleUtils.getAvatarStyle(theme, size), imageStyles, styles.noBorderRadius];
+ const iconStyle = imageStyles ? [StyleUtils.getAvatarStyle(theme, size), styles.bgTransparent, imageStyles] : undefined;
+
+ const iconFillColor = isWorkspace ? StyleUtils.getDefaultWorkspaceAvatarColor(name).fill : fill ?? theme.icon;
+ const fallbackAvatar = isWorkspace ? ReportUtils.getDefaultWorkspaceAvatar(name) : fallbackIcon || Expensicons.FallbackAvatar;
+
+ const avatarSource = imageError ? fallbackAvatar : source;
+
+ return (
+
+ {typeof avatarSource === 'function' ? (
+
+
+
+ ) : (
+
+ setImageError(true)}
+ />
+
+ )}
+
+ );
+}
+
+Avatar.displayName = 'Avatar';
+
+export default Avatar;
diff --git a/src/components/SubscriptAvatar.tsx b/src/components/SubscriptAvatar.tsx
index 0a244d4392e4..52ecf6633d9b 100644
--- a/src/components/SubscriptAvatar.tsx
+++ b/src/components/SubscriptAvatar.tsx
@@ -6,6 +6,7 @@ import * as StyleUtils from '@styles/StyleUtils';
import useTheme from '@styles/themes/useTheme';
import useThemeStyles from '@styles/useThemeStyles';
import CONST from '@src/CONST';
+import {AvatarType} from '@src/types/onyx/OnyxCommon';
import Avatar from './Avatar';
import UserDetailsTooltip from './UserDetailsTooltip';
@@ -14,7 +15,7 @@ type SubAvatar = {
source?: AvatarSource;
/** Denotes whether it is an avatar or a workspace avatar */
- type?: typeof CONST.ICON_TYPE_AVATAR | typeof CONST.ICON_TYPE_WORKSPACE;
+ type?: AvatarType;
/** Owner of the avatar. If user, displayName. If workspace, policy name */
name?: string;
diff --git a/src/styles/StyleUtils.ts b/src/styles/StyleUtils.ts
index 9a131dad48cd..4cb62b930632 100644
--- a/src/styles/StyleUtils.ts
+++ b/src/styles/StyleUtils.ts
@@ -1377,6 +1377,8 @@ function getDotIndicatorTextStyles(styles: ThemeStyles, isErrorText = true): Tex
return isErrorText ? {...styles.offlineFeedback.text, color: styles.formError.color} : {...styles.offlineFeedback.text};
}
+export type {AvatarSizeName};
+
export {
combineStyles,
displayIfTrue,
diff --git a/src/types/onyx/OnyxCommon.ts b/src/types/onyx/OnyxCommon.ts
index 49d3428be532..956e9ff36b24 100644
--- a/src/types/onyx/OnyxCommon.ts
+++ b/src/types/onyx/OnyxCommon.ts
@@ -10,12 +10,14 @@ type ErrorFields = Record;
+type AvatarType = typeof CONST.ICON_TYPE_AVATAR | typeof CONST.ICON_TYPE_WORKSPACE;
+
type Icon = {
/** Avatar source to display */
source: AvatarSource;
/** Denotes whether it is an avatar or a workspace avatar */
- type: typeof CONST.ICON_TYPE_AVATAR | typeof CONST.ICON_TYPE_WORKSPACE;
+ type: AvatarType;
/** Owner of the avatar. If user, displayName. If workspace, policy name */
name: string;
@@ -27,4 +29,4 @@ type Icon = {
fallbackIcon?: AvatarSource;
};
-export type {Icon, PendingAction, PendingFields, ErrorFields, Errors};
+export type {Icon, PendingAction, PendingFields, ErrorFields, Errors, AvatarType};