mirror of
https://github.com/kingomarnajjar/flex-template-web.git
synced 2026-07-27 19:42:11 +10:00
Use displayName/abbreviatedName of the user
The API changed from having firstName/lastName in the user object profile. The displayName/abbreviatedName should be used instead.
This commit is contained in:
parent
2f6bb884af
commit
852d8250f2
14 changed files with 78 additions and 83 deletions
|
|
@ -1,18 +1,16 @@
|
|||
import React, { PropTypes } from 'react';
|
||||
import classNames from 'classnames';
|
||||
import * as propTypes from '../../util/propTypes';
|
||||
|
||||
import css from './Avatar.css';
|
||||
|
||||
const Avatar = props => {
|
||||
const { className, firstName, lastName, rootClassName } = props;
|
||||
const { rootClassName, className, user } = props;
|
||||
const classes = classNames(rootClassName || css.root, className);
|
||||
|
||||
const authorName = lastName ? `${firstName} ${lastName}` : firstName;
|
||||
const initials = lastName ? firstName.charAt(0) + lastName.charAt(0) : firstName.charAt(0);
|
||||
|
||||
const { displayName, abbreviatedName } = user.profile;
|
||||
const placeHolderAvatar = (
|
||||
<div className={classes} title={authorName}>
|
||||
<span className={css.initials}>{initials}</span>
|
||||
<div className={classes} title={displayName}>
|
||||
<span className={css.initials}>{abbreviatedName}</span>
|
||||
</div>
|
||||
);
|
||||
|
||||
|
|
@ -23,14 +21,12 @@ const { string } = PropTypes;
|
|||
|
||||
Avatar.defaultProps = {
|
||||
className: null,
|
||||
lastName: null,
|
||||
rootClassName: null,
|
||||
};
|
||||
|
||||
Avatar.propTypes = {
|
||||
className: string,
|
||||
firstName: string.isRequired,
|
||||
lastName: string,
|
||||
user: propTypes.user.isRequired,
|
||||
rootClassName: string,
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ export const ListingCardComponent = props => {
|
|||
const { title = '', price } = currentListing.attributes;
|
||||
const slug = createSlug(title);
|
||||
const author = ensureUser(listing.author);
|
||||
const authorName = `${author.attributes.profile.firstName} ${author.attributes.profile.lastName}`;
|
||||
const authorName = author.attributes.profile.displayName;
|
||||
const firstImage = currentListing.images && currentListing.images.length > 0
|
||||
? currentListing.images[0]
|
||||
: null;
|
||||
|
|
|
|||
|
|
@ -132,9 +132,8 @@ const OrderDetailsPanel = props => {
|
|||
const currentCustomer = ensureUser(currentTransaction.customer);
|
||||
|
||||
const providerProfile = currentProvider.attributes.profile;
|
||||
const authorFirstName = providerProfile.firstName;
|
||||
const authorLastName = providerProfile.lastName;
|
||||
const customerName = currentCustomer.attributes.profile.firstName;
|
||||
const authorDisplayName = providerProfile.displayName;
|
||||
const customerDisplayName = currentCustomer.attributes.profile.displayName;
|
||||
const transactionState = currentTransaction.attributes.state;
|
||||
const lastTransitionedAt = currentTransaction.attributes.lastTransitionedAt;
|
||||
const lastTransition = currentTransaction.attributes.lastTransitione;
|
||||
|
|
@ -154,11 +153,11 @@ const OrderDetailsPanel = props => {
|
|||
const listingTitle = currentListing.attributes.title;
|
||||
|
||||
const bookingInfo = breakdown(currentTransaction);
|
||||
const title = orderTitle(transactionState, listingLink, customerName, lastTransition);
|
||||
const title = orderTitle(transactionState, listingLink, customerDisplayName, lastTransition);
|
||||
const message = orderMessage(
|
||||
transactionState,
|
||||
listingLink,
|
||||
authorFirstName,
|
||||
authorDisplayName,
|
||||
lastTransitionedAt,
|
||||
lastTransition
|
||||
);
|
||||
|
|
@ -185,7 +184,7 @@ const OrderDetailsPanel = props => {
|
|||
/>
|
||||
</div>
|
||||
<div className={classNames(css.avatarWrapper, css.avatarMobile)}>
|
||||
<AvatarMedium firstName={authorFirstName} lastName={authorLastName} />
|
||||
<AvatarMedium user={currentProvider} />
|
||||
</div>
|
||||
<div className={css.orderInfo}>
|
||||
<h1 className={css.title}>{title}</h1>
|
||||
|
|
@ -214,14 +213,14 @@ const OrderDetailsPanel = props => {
|
|||
/>
|
||||
</div>
|
||||
<div className={css.avatarWrapper}>
|
||||
<AvatarMedium firstName={authorFirstName} lastName={authorLastName} />
|
||||
<AvatarMedium user={currentProvider} />
|
||||
</div>
|
||||
<div className={css.breakdownHeadings}>
|
||||
<h2 className={css.breakdownTitle}>{listingTitle}</h2>
|
||||
<p className={css.breakdownSubtitle}>
|
||||
<FormattedMessage
|
||||
id="OrderDetailsPanel.hostedBy"
|
||||
values={{ name: authorFirstName }}
|
||||
values={{ name: authorDisplayName }}
|
||||
/>
|
||||
</p>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -125,8 +125,7 @@ const SaleDetailsPanel = props => {
|
|||
const currentListing = ensureListing(currentTransaction.listing);
|
||||
const currentCustomer = ensureUser(currentTransaction.customer);
|
||||
|
||||
const customerFirstName = currentCustomer.attributes.profile.firstName;
|
||||
const customerLastName = currentCustomer.attributes.profile.lastName;
|
||||
const customerDisplayName = currentCustomer.attributes.profile.displayName;
|
||||
const transactionState = currentTransaction.attributes.state;
|
||||
const lastTransitionedAt = currentTransaction.attributes.lastTransitionedAt;
|
||||
const lastTransition = currentTransaction.attributes.lastTransition;
|
||||
|
|
@ -150,10 +149,10 @@ const SaleDetailsPanel = props => {
|
|||
|
||||
const bookingInfo = breakdown(currentTransaction, totalMessage);
|
||||
|
||||
const title = saleTitle(transactionState, listingLink, customerFirstName, lastTransition);
|
||||
const title = saleTitle(transactionState, listingLink, customerDisplayName, lastTransition);
|
||||
const message = saleMessage(
|
||||
transactionState,
|
||||
customerFirstName,
|
||||
customerDisplayName,
|
||||
lastTransitionedAt,
|
||||
lastTransition
|
||||
);
|
||||
|
|
@ -202,11 +201,11 @@ const SaleDetailsPanel = props => {
|
|||
/>
|
||||
</div>
|
||||
<div className={css.avatarWrapperMobile}>
|
||||
<AvatarMedium firstName={customerFirstName} lastName={customerLastName} />
|
||||
<AvatarMedium user={currentCustomer} />
|
||||
</div>
|
||||
<div className={css.info}>
|
||||
<div className={css.avatarWrapperDesktop}>
|
||||
<AvatarMedium firstName={customerFirstName} lastName={customerLastName} />
|
||||
<AvatarMedium user={currentCustomer} />
|
||||
</div>
|
||||
<h1 className={css.title}>{title}</h1>
|
||||
<p className={css.message}>{message}</p>
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import { Button, Modal, NamedLink, TopbarDesktop, TopbarMobileMenu } from '../..
|
|||
import { TopbarSearchForm } from '../../containers';
|
||||
import { withFlattenedRoutes, withViewport } from '../../util/contextHelpers';
|
||||
import { parse, stringify } from '../../util/urlHelpers';
|
||||
import { ensureUser } from '../../util/data';
|
||||
import { ensureCurrentUser } from '../../util/data';
|
||||
import { createResourceLocatorString, pathByRouteName } from '../../util/routes';
|
||||
import * as propTypes from '../../util/propTypes';
|
||||
|
||||
|
|
@ -96,8 +96,7 @@ class TopbarComponent extends Component {
|
|||
location,
|
||||
onManageDisableScrolling,
|
||||
} = this.props;
|
||||
const me = ensureUser(currentUser);
|
||||
const profile = me.attributes.profile;
|
||||
const me = ensureCurrentUser(currentUser);
|
||||
|
||||
const { mobilemenu, mobilesearch, address, origin, bounds, country } = parse(location.search, {
|
||||
latlng: ['origin'],
|
||||
|
|
@ -114,8 +113,7 @@ class TopbarComponent extends Component {
|
|||
<TopbarMobileMenu
|
||||
isAuthenticated={isAuthenticated}
|
||||
currentUserHasListings={currentUserHasListings}
|
||||
firstName={profile.firstName}
|
||||
lastName={profile.lastName}
|
||||
user={me}
|
||||
onLogout={this.handleLogout}
|
||||
notificationCount={notificationCount}
|
||||
/>
|
||||
|
|
@ -157,8 +155,7 @@ class TopbarComponent extends Component {
|
|||
<div className={css.desktop}>
|
||||
<TopbarDesktop
|
||||
currentUserHasListings={currentUserHasListings}
|
||||
firstName={profile.firstName}
|
||||
lastName={profile.lastName}
|
||||
user={me}
|
||||
initialSearchFormValues={initialSearchFormValues}
|
||||
intl={intl}
|
||||
isAuthenticated={isAuthenticated}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import React, { PropTypes } from 'react';
|
||||
import { FormattedMessage, intlShape } from 'react-intl';
|
||||
import classNames from 'classnames';
|
||||
import * as propTypes from '../../util/propTypes';
|
||||
import {
|
||||
Avatar,
|
||||
InlineTextButton,
|
||||
|
|
@ -18,8 +19,7 @@ import css from './TopbarDesktop.css';
|
|||
const TopbarDesktop = props => {
|
||||
const {
|
||||
className,
|
||||
firstName,
|
||||
lastName,
|
||||
user,
|
||||
rootClassName,
|
||||
currentUserHasListings,
|
||||
notificationCount,
|
||||
|
|
@ -61,7 +61,7 @@ const TopbarDesktop = props => {
|
|||
const profileMenu = isAuthenticated
|
||||
? <Menu>
|
||||
<MenuLabel className={css.profileMenuLabel} isOpenClassName={css.profileMenuIsOpen}>
|
||||
<Avatar className={css.avatar} firstName={firstName} lastName={lastName} />
|
||||
<Avatar className={css.avatar} user={user} />
|
||||
</MenuLabel>
|
||||
<MenuContent className={css.profileMenuContent}>
|
||||
<MenuItem key="logout">
|
||||
|
|
@ -111,8 +111,6 @@ const TopbarDesktop = props => {
|
|||
const { bool, func, object, number, string } = PropTypes;
|
||||
|
||||
TopbarDesktop.defaultProps = {
|
||||
firstName: '',
|
||||
lastName: '',
|
||||
notificationCount: 0,
|
||||
className: null,
|
||||
rootClassName: null,
|
||||
|
|
@ -124,8 +122,7 @@ TopbarDesktop.propTypes = {
|
|||
currentUserHasListings: bool.isRequired,
|
||||
isAuthenticated: bool.isRequired,
|
||||
onLogout: func.isRequired,
|
||||
firstName: string,
|
||||
lastName: string,
|
||||
user: propTypes.currentUser.isRequired,
|
||||
notificationCount: number,
|
||||
rootClassName: string,
|
||||
onSearchSubmit: func.isRequired,
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
*/
|
||||
import React, { PropTypes } from 'react';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
import * as propTypes from '../../util/propTypes';
|
||||
import { AvatarLarge, InlineTextButton, NamedLink, NotificationBadge } from '../../components';
|
||||
|
||||
import css from './TopbarMobileMenu.css';
|
||||
|
|
@ -12,8 +13,7 @@ const TopbarMobileMenu = props => {
|
|||
const {
|
||||
isAuthenticated,
|
||||
currentUserHasListings,
|
||||
firstName,
|
||||
lastName,
|
||||
user,
|
||||
notificationCount,
|
||||
onLogout,
|
||||
} = props;
|
||||
|
|
@ -69,12 +69,14 @@ const TopbarMobileMenu = props => {
|
|||
</NamedLink>
|
||||
);
|
||||
|
||||
const displayName = user.attributes.profile.displayName;
|
||||
|
||||
return (
|
||||
<div className={css.root}>
|
||||
<AvatarLarge className={css.avatar} firstName={firstName} lastName={lastName} />
|
||||
<AvatarLarge className={css.avatar} user={user} />
|
||||
<div className={css.content}>
|
||||
<span className={css.greeting}>
|
||||
<FormattedMessage id="TopbarMobileMenu.greeting" values={{ firstName }} />
|
||||
<FormattedMessage id="TopbarMobileMenu.greeting" values={{ displayName }} />
|
||||
</span>
|
||||
<InlineTextButton className={css.logoutButton} onClick={onLogout}>
|
||||
<FormattedMessage id="TopbarMobileMenu.logoutLink" />
|
||||
|
|
@ -90,15 +92,14 @@ const TopbarMobileMenu = props => {
|
|||
);
|
||||
};
|
||||
|
||||
TopbarMobileMenu.defaultProps = { firstName: '', lastName: '', notificationCount: 0 };
|
||||
TopbarMobileMenu.defaultProps = { notificationCount: 0 };
|
||||
|
||||
const { bool, func, number, string } = PropTypes;
|
||||
const { bool, func, number } = PropTypes;
|
||||
|
||||
TopbarMobileMenu.propTypes = {
|
||||
isAuthenticated: bool.isRequired,
|
||||
currentUserHasListings: bool.isRequired,
|
||||
firstName: string,
|
||||
lastName: string,
|
||||
user: propTypes.currentUser.isRequired,
|
||||
notificationCount: number,
|
||||
onLogout: func.isRequired,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -124,9 +124,7 @@ export class CheckoutPageComponent extends Component {
|
|||
const { bookingStart, bookingEnd } = pageData.bookingDates || {};
|
||||
const currentListing = ensureListing(pageData.listing);
|
||||
const currentAuthor = ensureUser(currentListing.author);
|
||||
const authorProfile = currentAuthor.attributes.profile;
|
||||
const authorFirstName = authorProfile.firstName;
|
||||
const authorLastName = authorProfile.lastName;
|
||||
const authorDisplayName = currentAuthor.attributes.profile.displayName;
|
||||
|
||||
const isOwnListing = currentListing.id &&
|
||||
currentUser &&
|
||||
|
|
@ -207,14 +205,17 @@ export class CheckoutPageComponent extends Component {
|
|||
/>
|
||||
</div>
|
||||
<div className={classNames(css.avatarWrapper, css.avatarMobile)}>
|
||||
<AvatarMedium firstName={authorFirstName} lastName={authorLastName} />
|
||||
<AvatarMedium user={currentAuthor} />
|
||||
</div>
|
||||
<div className={css.bookListingContainer}>
|
||||
<div className={css.heading}>
|
||||
<h1 className={css.title}>{title}</h1>
|
||||
<div className={css.author}>
|
||||
<span className={css.authorName}>
|
||||
<FormattedMessage id="ListingPage.hostedBy" values={{ name: authorFirstName }} />
|
||||
<FormattedMessage
|
||||
id="ListingPage.hostedBy"
|
||||
values={{ name: authorDisplayName }}
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -257,12 +258,12 @@ export class CheckoutPageComponent extends Component {
|
|||
/>
|
||||
</div>
|
||||
<div className={css.avatarWrapper}>
|
||||
<AvatarMedium firstName={authorFirstName} lastName={authorLastName} />
|
||||
<AvatarMedium user={currentAuthor} />
|
||||
</div>
|
||||
<div className={css.detailsHeadings}>
|
||||
<h2 className={css.detailsTitle}>{listingTitle}</h2>
|
||||
<p className={css.detailsSubtitle}>
|
||||
<FormattedMessage id="CheckoutPage.hostedBy" values={{ name: authorFirstName }} />
|
||||
<FormattedMessage id="CheckoutPage.hostedBy" values={{ name: authorDisplayName }} />
|
||||
</p>
|
||||
</div>
|
||||
<h3 className={css.bookingBreakdownTitle}>
|
||||
|
|
|
|||
|
|
@ -26,17 +26,6 @@ import css from './InboxPage.css';
|
|||
|
||||
const { arrayOf, bool, func, instanceOf, number, object, oneOf, shape, string } = PropTypes;
|
||||
|
||||
const formatUser = user => {
|
||||
const profile = user && user.attributes && user.attributes.profile
|
||||
? user.attributes.profile
|
||||
: null;
|
||||
return {
|
||||
name: `${profile.firstName} ${profile.lastName}`,
|
||||
firstName: profile.firstName,
|
||||
lastName: profile.lastName,
|
||||
};
|
||||
};
|
||||
|
||||
const formatDate = (intl, date) => {
|
||||
return {
|
||||
short: intl.formatDate(date, {
|
||||
|
|
@ -103,7 +92,8 @@ export const InboxItem = props => {
|
|||
const { customer, provider, booking } = tx;
|
||||
const isOrder = type === 'order';
|
||||
|
||||
const otherUser = formatUser(isOrder ? provider : customer);
|
||||
const otherUser = isOrder ? provider : customer;
|
||||
const otherUserDisplayName = otherUser.attributes.profile.displayName;
|
||||
|
||||
const stateData = txState(intl, tx, isOrder);
|
||||
const isSaleNotification = !isOrder && tx.attributes.state === propTypes.TX_STATE_PREAUTHORIZED;
|
||||
|
|
@ -121,14 +111,14 @@ export const InboxItem = props => {
|
|||
params={{ id: tx.id.uuid }}
|
||||
>
|
||||
<div className={css.itemAvatar}>
|
||||
<Avatar firstName={otherUser.firstName} lastName={otherUser.lastName} />
|
||||
<Avatar user={otherUser} />
|
||||
</div>
|
||||
<div className={css.rowNotificationDot}>
|
||||
{rowNotificationDot}
|
||||
</div>
|
||||
<div className={css.itemInfo}>
|
||||
<div className={classNames(css.itemUsername, stateData.nameClassName)}>
|
||||
{otherUser.name}
|
||||
{otherUserDisplayName}
|
||||
</div>
|
||||
<div className={classNames(css.bookingInfo, stateData.bookingClassName)}>
|
||||
{bookingStart.short} - {bookingEnd.short}
|
||||
|
|
|
|||
|
|
@ -132,9 +132,8 @@ export class ListingPageComponent extends Component {
|
|||
currentListing.author.id.uuid === currentUser.id.uuid;
|
||||
const showBookButton = !isOwnListing;
|
||||
|
||||
const { firstName: authorFirstName, lastName: authorLastName } = authorAvailable
|
||||
? ensureUser(currentListing.author).attributes.profile
|
||||
: { firstName: '', lastName: '' };
|
||||
const currentAuthor = ensureUser(authorAvailable ? currentListing.author : {});
|
||||
const currentAuthorDisplayName = currentAuthor.attributes.profile.displayName;
|
||||
|
||||
// TODO location address is currently serialized inside address field (API will change later)
|
||||
// Content is something like { locationAddress: 'Street, Province, Country', building: 'A 42' };
|
||||
|
|
@ -219,11 +218,7 @@ export class ListingPageComponent extends Component {
|
|||
|
||||
<div className={css.contentContainer}>
|
||||
<div className={css.avatarWrapper}>
|
||||
<Avatar
|
||||
rootClassName={css.avatar}
|
||||
firstName={authorFirstName}
|
||||
lastName={authorLastName}
|
||||
/>
|
||||
<Avatar rootClassName={css.avatar} user={currentAuthor} />
|
||||
</div>
|
||||
|
||||
<div className={css.mainContent}>
|
||||
|
|
@ -242,7 +237,7 @@ export class ListingPageComponent extends Component {
|
|||
<span className={css.authorName}>
|
||||
<FormattedMessage
|
||||
id="ListingPage.hostedBy"
|
||||
values={{ name: authorFirstName }}
|
||||
values={{ name: currentAuthorDisplayName }}
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
|
|
@ -276,7 +271,7 @@ export class ListingPageComponent extends Component {
|
|||
<span className={css.authorName}>
|
||||
<FormattedMessage
|
||||
id="ListingPage.hostedBy"
|
||||
values={{ name: authorFirstName }}
|
||||
values={{ name: currentAuthorDisplayName }}
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -317,7 +317,7 @@
|
|||
"TopbarDesktop.logo": "Logo",
|
||||
"TopbarDesktop.logout": "Log out",
|
||||
"TopbarDesktop.signup": "Sign up",
|
||||
"TopbarMobileMenu.greeting": "Hello {firstName}",
|
||||
"TopbarMobileMenu.greeting": "Hello {displayName}",
|
||||
"TopbarMobileMenu.inboxLink": "Inbox",
|
||||
"TopbarMobileMenu.loginLink": "Log in",
|
||||
"TopbarMobileMenu.logoutLink": "Log out",
|
||||
|
|
|
|||
|
|
@ -153,3 +153,13 @@ export const ensureUser = user => {
|
|||
const empty = { id: null, type: 'user', attributes: { profile: {} } };
|
||||
return { ...empty, ...user };
|
||||
};
|
||||
|
||||
/**
|
||||
* Create shell objects to ensure that attributes etc. exists.
|
||||
*
|
||||
* @param {Object} current user entity object, which is to be ensured agains null values
|
||||
*/
|
||||
export const ensureCurrentUser = user => {
|
||||
const empty = { id: null, type: 'current-user', attributes: { profile: {} } };
|
||||
return { ...empty, ...user };
|
||||
};
|
||||
|
|
|
|||
|
|
@ -70,6 +70,8 @@ export const currentUser = shape({
|
|||
profile: shape({
|
||||
firstName: string.isRequired,
|
||||
lastName: string.isRequired,
|
||||
displayName: string.isRequired,
|
||||
abbreviatedName: string.isRequired,
|
||||
}).isRequired,
|
||||
stripeConnected: bool.isRequired,
|
||||
}),
|
||||
|
|
@ -81,8 +83,8 @@ export const user = shape({
|
|||
type: value('user').isRequired,
|
||||
attributes: shape({
|
||||
profile: shape({
|
||||
firstName: string.isRequired,
|
||||
lastName: string.isRequired,
|
||||
displayName: string.isRequired,
|
||||
abbreviatedName: string.isRequired,
|
||||
}).isRequired,
|
||||
}),
|
||||
});
|
||||
|
|
|
|||
|
|
@ -19,7 +19,10 @@ export const createUser = id => ({
|
|||
id: new UUID(id),
|
||||
type: 'user',
|
||||
attributes: {
|
||||
profile: { firstName: `${id} first name`, lastName: `${id} last name` },
|
||||
profile: {
|
||||
displayName: `${id} display name`,
|
||||
abbreviatedName: `${id} abbreviated name`,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
|
|
@ -29,7 +32,12 @@ export const createCurrentUser = id => ({
|
|||
type: 'current-user',
|
||||
attributes: {
|
||||
email: `${id}@example.com`,
|
||||
profile: { firstName: `${id} first name`, lastName: `${id} last name` },
|
||||
profile: {
|
||||
firstName: `${id} first name`,
|
||||
lastName: `${id} last name`,
|
||||
displayName: `${id} display name`,
|
||||
abbreviatedName: `${id} abbreviated name`,
|
||||
},
|
||||
stripeConnected: true,
|
||||
},
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue