Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[TS migration] Migrate 'AvatarWithIndicator.js' component to TypeScript #30328

Merged
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import PropTypes from 'prop-types';
import React from 'react';
import {View} from 'react-native';
import * as UserUtils from '@libs/UserUtils';
Expand All @@ -9,38 +8,33 @@ import * as Expensicons from './Icon/Expensicons';
import Indicator from './Indicator';
import Tooltip from './Tooltip';

const propTypes = {
type AvatarWithIndicatorProps = {
/** URL for the avatar */
source: PropTypes.oneOfType([PropTypes.string, PropTypes.func]).isRequired,
source: UserUtils.AvatarSource;

/** To show a tooltip on hover */
tooltipText: PropTypes.string,
tooltipText?: string;

/** A fallback avatar icon to display when there is an error on loading avatar from remote URL. */
fallbackIcon: PropTypes.oneOfType([PropTypes.func, PropTypes.string]),
fallbackIcon?: UserUtils.AvatarSource;

/** Indicates whether the avatar is loaded or not */
isLoading: PropTypes.bool,
isLoading?: boolean;
};

const defaultProps = {
tooltipText: '',
fallbackIcon: Expensicons.FallbackAvatar,
isLoading: true,
};

function AvatarWithIndicator(props) {
function AvatarWithIndicator({source, tooltipText = '', fallbackIcon = Expensicons.FallbackAvatar, isLoading = true}: AvatarWithIndicatorProps) {
const styles = useThemeStyles();

return (
<Tooltip text={props.tooltipText}>
<Tooltip text={tooltipText}>
<View style={[styles.sidebarAvatar]}>
{props.isLoading ? (
{isLoading ? (
<AvatarSkeleton />
) : (
<>
<Avatar
source={UserUtils.getSmallSizeAvatar(props.source)}
fallbackIcon={props.fallbackIcon}
source={UserUtils.getSmallSizeAvatar(source)}
fallbackIcon={fallbackIcon}
/>
<Indicator />
</>
Expand All @@ -50,8 +44,6 @@ function AvatarWithIndicator(props) {
);
}

AvatarWithIndicator.defaultProps = defaultProps;
AvatarWithIndicator.propTypes = propTypes;
AvatarWithIndicator.displayName = 'AvatarWithIndicator';

export default AvatarWithIndicator;
32 changes: 16 additions & 16 deletions src/libs/UserUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,37 +105,37 @@ function getDefaultAvatarURL(accountID: string | number = '', isNewDot = false):

/**
* Given a user's avatar path, returns true if user doesn't have an avatar or if URL points to a default avatar
* @param [avatarURL] - the avatar source from user's personalDetails
* @param avatarSource - the avatar source from user's personalDetails
*/
function isDefaultAvatar(avatarURL?: string | React.FC<SvgProps>): boolean {
if (typeof avatarURL === 'string') {
if (avatarURL.includes('images/avatars/avatar_') || avatarURL.includes('images/avatars/default-avatar_') || avatarURL.includes('images/avatars/user/default')) {
function isDefaultAvatar(avatarSource?: AvatarSource): boolean {
if (typeof avatarSource === 'string') {
if (avatarSource.includes('images/avatars/avatar_') || avatarSource.includes('images/avatars/default-avatar_') || avatarSource.includes('images/avatars/user/default')) {
return true;
}

// We use a hardcoded "default" Concierge avatar
if (avatarURL === CONST.CONCIERGE_ICON_URL_2021 || avatarURL === CONST.CONCIERGE_ICON_URL) {
if (avatarSource === CONST.CONCIERGE_ICON_URL_2021 || avatarSource === CONST.CONCIERGE_ICON_URL) {
return true;
}
}

if (!avatarURL) {
// If null URL, we should also use a default avatar
if (!avatarSource) {
// If null source, we should also use a default avatar
return true;
}

return false;
}

/**
* Provided a source URL, if source is a default avatar, return the associated SVG.
* Otherwise, return the URL pointing to a user-uploaded avatar.
* Provided an avatar source, if source is a default avatar, return the associated SVG.
* Otherwise, return the URL or SVG pointing to the user-uploaded avatar.
*
* @param avatarURL - the avatar source from user's personalDetails
* @param avatarSource - the avatar source from user's personalDetails
* @param accountID - the accountID of the user
*/
function getAvatar(avatarURL: AvatarSource, accountID: number): AvatarSource {
return isDefaultAvatar(avatarURL) ? getDefaultAvatar(accountID) : avatarURL;
function getAvatar(avatarSource: AvatarSource, accountID?: number): AvatarSource {
return isDefaultAvatar(avatarSource) ? getDefaultAvatar(accountID) : avatarSource;
}

/**
Expand All @@ -153,8 +153,8 @@ function getAvatarUrl(avatarURL: string, accountID: number): string {
* Avatars uploaded by users will have a _128 appended so that the asset server returns a small version.
* This removes that part of the URL so the full version of the image can load.
*/
function getFullSizeAvatar(avatarURL: string, accountID: number): AvatarSource {
const source = getAvatar(avatarURL, accountID);
function getFullSizeAvatar(avatarSource: AvatarSource, accountID: number): AvatarSource {
const source = getAvatar(avatarSource, accountID);
if (typeof source !== 'string') {
return source;
}
Expand All @@ -165,8 +165,8 @@ function getFullSizeAvatar(avatarURL: string, accountID: number): AvatarSource {
* Small sized avatars end with _128.<file-type>. This adds the _128 at the end of the
* source URL (before the file type) if it doesn't exist there already.
*/
function getSmallSizeAvatar(avatarURL: string, accountID: number): AvatarSource {
const source = getAvatar(avatarURL, accountID);
function getSmallSizeAvatar(avatarSource: AvatarSource, accountID?: number): AvatarSource {
const source = getAvatar(avatarSource, accountID);
if (typeof source !== 'string') {
return source;
}
Expand Down
6 changes: 3 additions & 3 deletions src/types/onyx/ReportAction.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {SvgProps} from 'react-native-svg';
import {ValueOf} from 'type-fest';
import {AvatarSource} from '@libs/UserUtils';
import CONST from '@src/CONST';
import * as OnyxCommon from './OnyxCommon';
import OriginalMessage, {Decision, Reaction} from './OriginalMessage';
Expand Down Expand Up @@ -85,7 +85,7 @@ type ReportActionBase = {
/** accountIDs of the people to which the whisper was sent to (if any). Returns empty array if it is not a whisper */
whisperedToAccountIDs?: number[];

avatar?: string | React.FC<SvgProps>;
avatar?: AvatarSource;

automatic?: boolean;

Expand Down Expand Up @@ -145,4 +145,4 @@ type ReportAction = ReportActionBase & OriginalMessage;
type ReportActions = Record<string, ReportAction>;

export default ReportAction;
export type {ReportActions, Message};
export type {Message, ReportActions};