Skip to content

Commit

Permalink
Add gravatar link popup to the user avatar component
Browse files Browse the repository at this point in the history
  • Loading branch information
brionmario committed Dec 5, 2019
1 parent bb25b8b commit 5de24b8
Show file tree
Hide file tree
Showing 13 changed files with 175 additions and 37 deletions.
31 changes: 28 additions & 3 deletions apps/user-portal/src/components/profile/profile.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,11 @@ import { Field, Forms, Validation } from "@wso2is/forms";
import { FormValidation } from "@wso2is/validation";
import { isEmpty } from "lodash";
import React, { FunctionComponent, useEffect, useState } from "react";
import { useTranslation } from "react-i18next";
import { Trans, useTranslation } from "react-i18next";
import { useDispatch, useSelector } from "react-redux";
import { Form, Grid, Icon, List, Popup, Responsive } from "semantic-ui-react";
import { updateProfileInfo } from "../../api";
import * as UIConstants from "../../constants/ui-constants";
import { AuthStateInterface, createEmptyProfile, Notification } from "../../models";
import { AppState } from "../../store";
import { getProfileInformation } from "../../store/actions";
Expand Down Expand Up @@ -642,8 +643,32 @@ export const Profile: FunctionComponent<ProfileProps> = (props: ProfileProps): J
<SettingsSection
description={ t("views:sections.profile.description") }
header={ t("views:sections.profile.heading") }
icon={ <UserAvatar authState={ profileDetails } size="tiny" /> }
iconMini={ <UserAvatar authState={ profileDetails } size="tiny" /> }
icon={ (
<UserAvatar
authState={ profileDetails }
size="tiny"
showGravatarLabel
gravatarInfoPopoverText={ (
<Trans i18nKey="views:components.userAvatar.infoPopover">
This image has been retrieved from
<a href={ UIConstants.GRAVATAR_URL } target="_blank" rel="noopener">Gravatar</a> service.
</Trans>
) }
/>
) }
iconMini={ (
<UserAvatar
authState={ profileDetails }
size="tiny"
showGravatarLabel
gravatarInfoPopoverText={ (
<Trans i18nKey="views:components.userAvatar.infoPopover">
This image has been retrieved from
<a href={ UIConstants.GRAVATAR_URL } target="_blank" rel="noopener">Gravatar</a> service.
</Trans>
) }
/>
) }
>
<List divided={ true } verticalAlign="middle" className="main-content-inner">
<List.Item className="inner-list-item">
Expand Down
61 changes: 42 additions & 19 deletions apps/user-portal/src/components/shared/avatar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@

import classNames from "classnames";
import * as React from "react";
import { Image, SemanticSIZES } from "semantic-ui-react";
import { Image, Label, SemanticSIZES } from "semantic-ui-react";
import { DefaultAppIcon } from "../../configs";
import { UserImageDummy } from "./ui";

Expand All @@ -35,9 +35,11 @@ export interface AvatarProps {
inline?: boolean;
name?: string;
relaxed?: boolean | "very";
showTopLabel?: boolean;
size?: AvatarSizes;
spaced?: "left" | "right";
style?: object;
topLabel?: string;
transparent?: boolean;
}

Expand Down Expand Up @@ -66,20 +68,22 @@ export const Avatar: React.FunctionComponent<AvatarProps> = (props): JSX.Element
size,
spaced,
style,
topLabel,
transparent,
...rest
} = props;
const relaxLevel = (relaxed && relaxed === true) ? "" : relaxed;

const classes = classNames({
bordered,
[`floated-${floated}`]: floated,
[ `floated-${ floated }` ]: floated,
inline,
relaxed,
[`${size}`]: size, // Size is used as a class to support the custom size "little"
[`spaced-${spaced}`]: spaced,
[ `${ size }` ]: size, // Size is used as a class to support the custom size "little"
[ `spaced-${ spaced }` ]: spaced,
transparent,
[`${avatarType === "user" ? "user-avatar" : "app-avatar"}`]: avatar,
[`${relaxLevel}`]: relaxLevel,
[ `${ avatarType === "user" ? "user-avatar" : "app-avatar" }` ]: avatar,
[ `${ relaxLevel }` ]: relaxLevel,
}, className);

/**
Expand All @@ -101,37 +105,54 @@ export const Avatar: React.FunctionComponent<AvatarProps> = (props): JSX.Element
}

if (nameParts.length >= 2) {
return (nameParts[0].charAt(0) + nameParts[1].charAt(0)).toUpperCase();
return (nameParts[ 0 ].charAt(0) + nameParts[ 1 ].charAt(0)).toUpperCase();
}
return name.charAt(0).toUpperCase();
};

if (image) {
return (
<Image
className={ `${avatarType === "user" ? "user-image" : "app-image"} ${classes}` }
bordered={ bordered }
floated={ floated }
circular={ avatarType === "user" }
rounded={ avatarType === "app" }
style={ style }
>
<img alt="avatar" src={ image as string } />
</Image>
<>
{
topLabel
? (
<Label circular floating size="mini" className="custom-label">
<Image
avatar
circular
src={ topLabel }
/>
</Label>
)
: null
}
<Image
className={ `${ avatarType === "user" ? "user-image" : "app-image" } ${ classes }` }
bordered={ bordered }
floated={ floated }
circular={ avatarType === "user" }
rounded={ avatarType === "app" }
style={ style }
{ ...rest }
>
<img alt="avatar" src={ image as string }/>
</Image>
</>
);
}

if (avatar && name) {
return (
<Image
className={ `${avatarType === "user" ? "user-image" : "app-image"} ${classes}` }
className={ `${ avatarType === "user" ? "user-image" : "app-image" } ${ classes }` }
bordered={ bordered }
floated={ floated }
verticalAlign="middle"
circular={ avatarType === "user" }
rounded={ avatarType === "app" }
centered
style={ style }
{ ...rest }
>
<span className="initials">{ generateInitials() }</span>
</Image>
Expand All @@ -140,7 +161,7 @@ export const Avatar: React.FunctionComponent<AvatarProps> = (props): JSX.Element

return (
<Image
className={ `${avatarType === "user" ? "user-image" : "app-image"} ${classes}` }
className={ `${ avatarType === "user" ? "user-image" : "app-image" } ${ classes }` }
src={ avatarType === "user" ? UserImageDummy : DefaultAppIcon.default }
bordered={ bordered }
floated={ floated }
Expand All @@ -149,6 +170,7 @@ export const Avatar: React.FunctionComponent<AvatarProps> = (props): JSX.Element
rounded={ avatarType === "app" }
centered
style={ style }
{ ...rest }
/>
);
};
Expand All @@ -166,5 +188,6 @@ Avatar.defaultProps = {
size: "mini",
spaced: null,
style: {},
topLabel: null,
transparent: false,
};
84 changes: 69 additions & 15 deletions apps/user-portal/src/components/shared/user-avatar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@
*/

import React, { FunctionComponent, useState } from "react";
import { Popup } from "semantic-ui-react";
import { ThirdPartyLogos } from "../../configs";
import * as UIConstants from "../../constants/ui-constants";
import { resolveUserDisplayName } from "../../helpers";
import { AuthStateInterface } from "../../models";
import { Avatar, AvatarProps } from "./avatar";
Expand All @@ -26,6 +29,8 @@ import { Avatar, AvatarProps } from "./avatar";
*/
interface UserAvatarProps extends AvatarProps {
authState?: AuthStateInterface;
gravatarInfoPopoverText?: React.ReactNode;
showGravatarLabel?: boolean;
}

/**
Expand All @@ -35,7 +40,7 @@ interface UserAvatarProps extends AvatarProps {
* @return {JSX.Element}
*/
export const UserAvatar: FunctionComponent<UserAvatarProps> = (props: UserAvatarProps): JSX.Element => {
const { authState, name, image } = props;
const { authState, gravatarInfoPopoverText, name, image, showGravatarLabel } = props;
const [ userImage, setUserImage ] = useState(null);

// Check if the image is a promise, and resolve.
Expand All @@ -44,30 +49,77 @@ export const UserAvatar: FunctionComponent<UserAvatarProps> = (props: UserAvatar
.then((response) => {
setUserImage(response);
})
.catch((error) => {
.catch(() => {
setUserImage(null);
});
}

/**
* Resolves the top label image.
*
* @return {string}
*/
const resolveTopLabel = (): string => {
if (isGravatarURL()) {
return ThirdPartyLogos.gravatar;
}

return null;
};

/**
* Checks if the image is from `Gravatar`.
*
* @return {boolean}
*/
const isGravatarURL = (): boolean => {
return (userImage && userImage.includes(UIConstants.GRAVATAR_URL))
|| (authState && authState.profileInfo && authState.profileInfo.userimage
&& authState.profileInfo.userimage.includes(UIConstants.GRAVATAR_URL));
};

// Avatar for the authenticated user.
if (authState && authState.profileInfo && authState.profileInfo.userimage) {
return (
<Avatar
{ ...props }
avatarType="user"
bordered={ false }
image={ authState.profileInfo && authState.profileInfo.userimage }
<Popup
content={ gravatarInfoPopoverText }
position="top center"
siz="mini"
disabled={ !(showGravatarLabel && isGravatarURL()) }
inverted
hoverable
trigger={ (
<Avatar
{ ...props }
avatarType="user"
bordered={ false }
image={ authState.profileInfo && authState.profileInfo.userimage }
topLabel={ showGravatarLabel ? resolveTopLabel() : null }
/>
) }
/>
);
}

return (
<Avatar
{ ...props }
image={ userImage }
avatarType="user"
bordered={ false }
avatar
name={ authState ? resolveUserDisplayName(authState) : name }
<Popup
content={ gravatarInfoPopoverText }
position="top center"
siz="mini"
disabled={ !(showGravatarLabel && isGravatarURL()) }
inverted
hoverable
trigger={ (
<Avatar
{ ...props }
image={ userImage }
avatarType="user"
bordered={ false }
avatar
name={ authState ? resolveUserDisplayName(authState) : name }
topLabel={ showGravatarLabel ? resolveTopLabel() : null }
/>
) }
/>
);
};
Expand All @@ -77,5 +129,7 @@ export const UserAvatar: FunctionComponent<UserAvatarProps> = (props: UserAvatar
*/
UserAvatar.defaultProps = {
authState: null,
name: null
gravatarInfoPopoverText: null,
name: null,
showGravatarLabel: false
};
5 changes: 5 additions & 0 deletions apps/user-portal/src/configs/ui.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import {
DummyUser,
EmptySearchResultsIllustration,
ForbiddenIcon,
GravatarLogo,
HomeTileIcons,
Logo,
MFAIconSet,
Expand Down Expand Up @@ -97,3 +98,7 @@ export const EmptyPlaceholderIllustrations = {
export const AppIconBackgrounds = {
orange: OrangeAppIconBackground
};

export const ThirdPartyLogos = {
gravatar: GravatarLogo
};
8 changes: 8 additions & 0 deletions apps/user-portal/src/constants/ui-constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,3 +55,11 @@ export const RECENT_APPLICATIONS_LIST_LIMIT: number = 3;
* @default
*/
export const ADD_LOCAL_LINKED_ACCOUNT_FORM_IDENTIFIER: string = "addLocalLinkedAccountForm";

/**
* `Gravatar` website URL.
* @constant
* @type {string}
* @default
*/
export const GRAVATAR_URL: string = "https://www.gravatar.com";
3 changes: 3 additions & 0 deletions apps/user-portal/src/locales/en/views.ts
Original file line number Diff line number Diff line change
Expand Up @@ -911,6 +911,9 @@ export const views: Views = {
}
}
},
userAvatar: {
infoPopover: "This image has been retrieved from <1>Gravatar</1> service."
},
userSessions: {
browserAndOS: "{{browser}} on {{os}} {{version}}",
lastAccessed: "Last accessed {{date}}",
Expand Down
3 changes: 3 additions & 0 deletions apps/user-portal/src/locales/pt/views.ts
Original file line number Diff line number Diff line change
Expand Up @@ -925,6 +925,9 @@ export const views: Views = {
}
}
},
userAvatar: {
infoPopover: "Esta imagem foi recuperada do serviço <1>Gravatar</1>."
},
userSessions: {
browserAndOS: "{{browser}} no {{os}} {{version}}",
lastAccessed: "Último acesso {{date}}",
Expand Down
3 changes: 3 additions & 0 deletions apps/user-portal/src/locales/si/views.ts
Original file line number Diff line number Diff line change
Expand Up @@ -914,6 +914,9 @@ export const views: Views = {
}
}
},
userAvatar: {
infoPopover: "මෙම පින්තූරය <1>Gravatar</1> සේවාවෙන් ලබාගෙන ඇත."
},
userSessions: {
browserAndOS: "{{os}} {{version}} මත {{browser}}",
lastAccessed: "අවසන් ප්‍රවේශය {{date}}",
Expand Down
3 changes: 3 additions & 0 deletions apps/user-portal/src/locales/ta/views.ts
Original file line number Diff line number Diff line change
Expand Up @@ -951,6 +951,9 @@ export const views: Views = {
}
}
},
userAvatar: {
infoPopover: "இந்த படம் <1>Gravatar</1> சேவையிலிருந்து மீட்டெடுக்கப்பட்டது."
},
userSessions: {
browserAndOS: "{{os}} {{version}} இல் {{browser}}",
lastAccessed: "இறுதியாக அணுகியது {{date}}",
Expand Down
3 changes: 3 additions & 0 deletions apps/user-portal/src/models/locales.ts
Original file line number Diff line number Diff line change
Expand Up @@ -633,6 +633,9 @@ export interface Views {
downloadProfileInfo: Notification;
}
},
userAvatar: {
infoPopover: string;
},
userSessions: {
browserAndOS: string;
lastAccessed: string;
Expand Down
Loading

0 comments on commit 5de24b8

Please sign in to comment.