Skip to content

Commit

Permalink
Merge pull request #39637 from nkdengineer/fix/39478
Browse files Browse the repository at this point in the history
Generate Workspace Avatar Color based on PolicyID instead of Workspace Name
  • Loading branch information
grgia authored Apr 30, 2024
2 parents e1e77ba + 0629655 commit dac5dfd
Show file tree
Hide file tree
Showing 18 changed files with 43 additions and 33 deletions.
14 changes: 10 additions & 4 deletions src/components/Avatar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ 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';
Expand Down Expand Up @@ -49,10 +50,13 @@ type AvatarProps = {

/** Owner of the avatar. If user, displayName. If workspace, policy name */
name?: string;

/** Optional account id if it's user avatar or policy id if it's workspace avatar */
accountID?: number | string;
};

function Avatar({
source,
source: originalSource,
imageStyles,
iconAdditionalStyles,
containerStyles,
Expand All @@ -62,6 +66,7 @@ function Avatar({
fallbackIconTestID = '',
type = CONST.ICON_TYPE_AVATAR,
name = '',
accountID,
}: AvatarProps) {
const theme = useTheme();
const styles = useThemeStyles();
Expand All @@ -72,7 +77,7 @@ function Avatar({

useEffect(() => {
setImageError(false);
}, [source]);
}, [originalSource]);

const isWorkspace = type === CONST.ICON_TYPE_WORKSPACE;
const iconSize = StyleUtils.getAvatarSize(size);
Expand All @@ -81,14 +86,15 @@ function Avatar({
const iconStyle = imageStyles ? [StyleUtils.getAvatarStyle(size), styles.bgTransparent, imageStyles] : undefined;

// We pass the color styles down to the SVG for the workspace and fallback avatar.
const useFallBackAvatar = imageError || source === Expensicons.FallbackAvatar || !source;
const source = isWorkspace ? originalSource : UserUtils.getAvatar(originalSource, Number(accountID));
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;

let iconColors;
if (isWorkspace) {
iconColors = StyleUtils.getDefaultWorkspaceAvatarColor(name);
iconColors = StyleUtils.getDefaultWorkspaceAvatarColor(accountID?.toString() ?? '');
} else if (useFallBackAvatar) {
iconColors = StyleUtils.getBackgroundColorAndFill(theme.buttonHoveredBG, theme.icon);
} else {
Expand Down
1 change: 1 addition & 0 deletions src/components/HeaderWithBackButton/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,7 @@ function HeaderWithBackButton({
containerStyles={[StyleUtils.getWidthAndHeightStyle(StyleUtils.getAvatarSize(CONST.AVATAR_SIZE.DEFAULT)), styles.mr3]}
source={policyAvatar?.source}
name={policyAvatar?.name}
accountID={policyAvatar?.id}
type={policyAvatar?.type}
/>
)}
Expand Down
1 change: 1 addition & 0 deletions src/components/MentionSuggestions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ function MentionSuggestions({prefix, mentions, highlightedMentionIndex = 0, onSe
source={item.icons[0].source}
size={isIcon ? CONST.AVATAR_SIZE.MENTION_ICON : CONST.AVATAR_SIZE.SMALLER}
name={item.icons[0].name}
accountID={item.icons[0].id}
type={item.icons[0].type}
fill={isIcon ? theme.success : undefined}
fallbackIcon={item.icons[0].fallbackIcon}
Expand Down
4 changes: 4 additions & 0 deletions src/components/MultipleAvatars.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,7 @@ function MultipleAvatars({
size={size}
fill={icons[0].fill}
name={icons[0].name}
accountID={icons[0].id}
type={icons[0].type}
fallbackIcon={icons[0].fallbackIcon}
/>
Expand Down Expand Up @@ -205,6 +206,7 @@ function MultipleAvatars({
source={icon.source ?? fallbackIcon}
size={size}
name={icon.name}
accountID={icon.id}
type={icon.type}
fallbackIcon={icon.fallbackIcon}
/>
Expand Down Expand Up @@ -263,6 +265,7 @@ function MultipleAvatars({
imageStyles={[singleAvatarStyle]}
name={icons[0].name}
type={icons[0].type}
accountID={icons[0].id}
fallbackIcon={icons[0].fallbackIcon}
/>
</View>
Expand All @@ -282,6 +285,7 @@ function MultipleAvatars({
size={avatarSize}
imageStyles={[singleAvatarStyle]}
name={icons[1].name}
accountID={icons[1].id}
type={icons[1].type}
fallbackIcon={icons[1].fallbackIcon}
/>
Expand Down
2 changes: 2 additions & 0 deletions src/components/RoomHeaderAvatars.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ function RoomHeaderAvatars({icons, reportID}: RoomHeaderAvatarsProps) {
imageStyles={styles.avatarLarge}
size={CONST.AVATAR_SIZE.LARGE}
name={icons[0].name}
accountID={icons[0].id}
type={icons[0].type}
fallbackIcon={icons[0].fallbackIcon}
/>
Expand Down Expand Up @@ -82,6 +83,7 @@ function RoomHeaderAvatars({icons, reportID}: RoomHeaderAvatarsProps) {
size={CONST.AVATAR_SIZE.LARGE}
containerStyles={[...iconStyle, StyleUtils.getAvatarBorderRadius(CONST.AVATAR_SIZE.LARGE_BORDERED, icon.type)]}
name={icon.name}
accountID={icon.id}
type={icon.type}
fallbackIcon={icon.fallbackIcon}
/>
Expand Down
2 changes: 2 additions & 0 deletions src/components/SubscriptAvatar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ function SubscriptAvatar({
source={mainAvatar?.source}
size={size}
name={mainAvatar?.name}
accountID={mainAvatar?.id}
type={mainAvatar?.type}
fallbackIcon={mainAvatar?.fallbackIcon}
/>
Expand All @@ -108,6 +109,7 @@ function SubscriptAvatar({
size={isSmall ? CONST.AVATAR_SIZE.SMALL_SUBSCRIPT : CONST.AVATAR_SIZE.SUBSCRIPT}
fill={secondaryAvatar.fill}
name={secondaryAvatar.name}
accountID={secondaryAvatar.id}
type={secondaryAvatar.type}
fallbackIcon={secondaryAvatar.fallbackIcon}
/>
Expand Down
20 changes: 2 additions & 18 deletions src/components/UserDetailsTooltip/types.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type {AvatarSource} from '@libs/UserUtils';
import type {AvatarType} from '@src/types/onyx/OnyxCommon';
import type {AvatarType, Icon as IconType} from '@src/types/onyx/OnyxCommon';
import type ChildrenProps from '@src/types/utils/ChildrenProps';

type FallbackUserDetails = {
Expand All @@ -16,22 +16,6 @@ type FallbackUserDetails = {
type?: AvatarType;
};

type Icon = {
/** Source for the avatar. Can be a URL or an icon. */
source?: AvatarSource;

/** 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;
};

type UserDetailsTooltipProps = ChildrenProps & {
/** User's Account ID */
accountID: number;
Expand All @@ -40,7 +24,7 @@ type UserDetailsTooltipProps = ChildrenProps & {
fallbackUserDetails?: FallbackUserDetails;

/** Optionally, pass in the icon instead of calculating it. If defined, will take precedence. */
icon?: Icon;
icon?: IconType;

/** The accountID of the copilot who took this action on behalf of the user */
delegateAccountID?: number;
Expand Down
5 changes: 3 additions & 2 deletions src/components/WorkspaceSwitcherButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ function WorkspaceSwitcherButton({policy}: WorkspaceSwitcherButtonProps) {

const pressableRef = useRef<HTMLDivElement | View | null>(null);

const {source, name, type} = useMemo(() => {
const {source, name, type, id} = useMemo(() => {
if (!policy) {
return {source: Expensicons.ExpensifyAppIcon, name: CONST.WORKSPACE_SWITCHER.NAME, type: CONST.ICON_TYPE_AVATAR};
}
Expand All @@ -36,6 +36,7 @@ function WorkspaceSwitcherButton({policy}: WorkspaceSwitcherButtonProps) {
source: avatar,
name: policy?.name ?? '',
type: CONST.ICON_TYPE_WORKSPACE,
id: policy?.id ?? '',
};
}, [policy]);

Expand All @@ -55,7 +56,7 @@ function WorkspaceSwitcherButton({policy}: WorkspaceSwitcherButtonProps) {
>
{({hovered}) => (
<SubscriptAvatar
mainAvatar={{source, name, type}}
mainAvatar={{source, name, type, id}}
subscriptIcon={{
source: Expensicons.DownArrow,
width: CONST.WORKSPACE_SWITCHER.SUBSCRIPT_ICON_SIZE,
Expand Down
4 changes: 2 additions & 2 deletions src/libs/ReportUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1721,7 +1721,7 @@ function getWorkspaceIcon(report: OnyxEntry<Report>, policy: OnyxEntry<Policy> =
source: policyExpenseChatAvatarSource ?? '',
type: CONST.ICON_TYPE_WORKSPACE,
name: workspaceName,
id: -1,
id: report?.policyID,
};
return workspaceIcon;
}
Expand Down Expand Up @@ -1924,7 +1924,7 @@ function getIcons(
source: policyExpenseChatAvatarSource,
type: CONST.ICON_TYPE_WORKSPACE,
name: domainName ?? '',
id: -1,
id: report?.policyID,
};
return [domainIcon];
}
Expand Down
1 change: 1 addition & 0 deletions src/pages/WorkspaceSwitcherPage/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ function WorkspaceSwitcherPage() {
fallbackIcon: Expensicons.FallbackWorkspaceAvatar,
name: policy?.name,
type: CONST.ICON_TYPE_WORKSPACE,
id: policy?.id,
},
],
isBold: hasUnreadData(policy?.id),
Expand Down
3 changes: 1 addition & 2 deletions src/pages/home/report/ReportActionItemFragment.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import React, {memo} from 'react';
import type {StyleProp, TextStyle} from 'react-native';
import type {AvatarProps} from '@components/Avatar';
import RenderHTML from '@components/RenderHTML';
import Text from '@components/Text';
import UserDetailsTooltip from '@components/UserDetailsTooltip';
Expand Down Expand Up @@ -39,7 +38,7 @@ type ReportActionItemFragmentProps = {
delegateAccountID?: number;

/** icon */
actorIcon?: AvatarProps;
actorIcon?: OnyxCommon.Icon;

/** Whether the comment is a thread parent message/the first message in a thread */
isThreadParentMessage?: boolean;
Expand Down
1 change: 1 addition & 0 deletions src/pages/home/report/ReportActionItemSingle.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,7 @@ function ReportActionItemSingle({
source={icon.source}
type={icon.type}
name={icon.name}
accountID={icon.id}
fallbackIcon={fallbackIcon}
/>
</View>
Expand Down
1 change: 1 addition & 0 deletions src/pages/workspace/WorkspaceInitialPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -315,6 +315,7 @@ function WorkspaceInitialPage({policyDraft, policy: policyProp, reimbursementAcc
source: avatar,
name: policy?.name ?? '',
type: CONST.ICON_TYPE_WORKSPACE,
id: policy.id ?? '',
};
}, [policy]);

Expand Down
3 changes: 2 additions & 1 deletion src/pages/workspace/WorkspaceProfilePage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -83,10 +83,11 @@ function WorkspaceProfilePage({policy, currencyList = {}, route}: WorkSpaceProfi
fallbackIcon={Expensicons.FallbackWorkspaceAvatar}
size={CONST.AVATAR_SIZE.XLARGE}
name={policyName}
accountID={policy?.id ?? ''}
type={CONST.ICON_TYPE_WORKSPACE}
/>
),
[policy?.avatar, policyName, styles.alignSelfCenter, styles.avatarXLarge],
[policy?.avatar, policy?.id, policyName, styles.alignSelfCenter, styles.avatarXLarge],
);

const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false);
Expand Down
1 change: 1 addition & 0 deletions src/pages/workspace/WorkspacesListPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,7 @@ function WorkspacesListPage({policies, reimbursementAccount, reports, session}:
{({hovered}) => (
<WorkspacesListRow
title={item.title}
policyID={item.policyID}
menuItems={threeDotsMenuItems}
workspaceIcon={item.icon}
ownerAccountID={item.ownerAccountID}
Expand Down
5 changes: 5 additions & 0 deletions src/pages/workspace/WorkspacesListRow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,9 @@ type WorkspacesListRowProps = WithCurrentUserPersonalDetailsProps & {

/** Determines if pending column should be shown or not */
isJoinRequestPending?: boolean;

/** ID of the policy */
policyID?: string;
};

type BrickRoadIndicatorIconProps = {
Expand Down Expand Up @@ -104,6 +107,7 @@ function WorkspacesListRow({
brickRoadIndicator,
shouldDisableThreeDotsMenu,
isJoinRequestPending,
policyID,
}: WorkspacesListRowProps) {
const styles = useThemeStyles();
const {translate} = useLocalize();
Expand Down Expand Up @@ -192,6 +196,7 @@ function WorkspacesListRow({
size={CONST.AVATAR_SIZE.DEFAULT}
source={workspaceIcon}
fallbackIcon={fallbackWorkspaceIcon}
accountID={policyID}
name={title}
type={CONST.ICON_TYPE_WORKSPACE}
/>
Expand Down
4 changes: 2 additions & 2 deletions src/styles/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -270,8 +270,8 @@ function getAvatarBorderStyle(size: AvatarSizeName, type: string): ViewStyle {
/**
* Helper method to return workspace avatar color styles
*/
function getDefaultWorkspaceAvatarColor(workspaceName: string): ViewStyle {
const colorHash = UserUtils.hashText(workspaceName.trim(), workspaceColorOptions.length);
function getDefaultWorkspaceAvatarColor(text: string): ViewStyle {
const colorHash = UserUtils.hashText(text.trim(), workspaceColorOptions.length);

return workspaceColorOptions[colorHash];
}
Expand Down
4 changes: 2 additions & 2 deletions src/types/onyx/OnyxCommon.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,10 @@ type AvatarType = typeof CONST.ICON_TYPE_AVATAR | typeof CONST.ICON_TYPE_WORKSPA

type Icon = {
/** Avatar source to display */
source: AvatarSource;
source?: AvatarSource;

/** Denotes whether it is an avatar or a workspace avatar */
type: AvatarType;
type?: AvatarType;

/** Owner of the avatar. If user, displayName. If workspace, policy name */
name?: string;
Expand Down

0 comments on commit dac5dfd

Please sign in to comment.