Skip to content

Commit

Permalink
Merge pull request #1259 from sharetribe/login-as-user-banner
Browse files Browse the repository at this point in the history
Show a banner when user is logged in with limited access
  • Loading branch information
lyyder authored Feb 14, 2020
2 parents 87a7993 + 650dfcf commit 606cec3
Show file tree
Hide file tree
Showing 8 changed files with 135 additions and 6 deletions.
5 changes: 4 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,10 @@ way to update this template, but currently, we follow a pattern:

## Upcoming version 2020-XX-XX

- [add] Support for logging in as a user from Console. [#1254](https://github.com/sharetribe/ftw-daily/pull/1254)
- [add] Show a banner when a user is logged in with limited access.
[#1259](https://github.com/sharetribe/ftw-daily/pull/1259)
- [add] Support for logging in as a user from Console.
[#1254](https://github.com/sharetribe/ftw-daily/pull/1254)
- [change] Add `handlebars` 4.5.3 and `serialize-javascript` 2.1.1 to resolutions in `package.json`.
[#1251](https://github.com/sharetribe/ftw-daily/pull/1251)

Expand Down
31 changes: 31 additions & 0 deletions src/components/LimitedAccessBanner/LimitedAccessBanner.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
@import '../../marketplace.css';

.root {
background-color: #df492a;
text-align: center;
padding: 10px 20px 9px;
}

.text {
margin: 0;
display: inline-block;
color: #fff;
font-size: 16px;
margin-bottom: 16px;
line-height: 20px;
}

.button {
background: #2a3d4b;
margin: 0 16px;
padding: 8px 16px;
border-radius: 4px;
font-size: 14px;
color: #fff;
border: 0;

&:hover {
text-decoration: none;
background: #364f61;
}
}
68 changes: 68 additions & 0 deletions src/components/LimitedAccessBanner/LimitedAccessBanner.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import React from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
import { FormattedMessage } from '../../util/reactIntl';
import { propTypes } from '../../util/types';
import { Button } from '../../components';
import { ensureCurrentUser } from '../../util/data';

import css from './LimitedAccessBanner.css';

// Due to the layout structure, do not render the banner on the following pages
const disabledPages = ['SearchPage'];

const LimitedAccessBanner = props => {
const {
rootClassName,
className,
isAuthenticated,
authScopes,
currentUser,
onLogout,
currentPage,
} = props;
const classes = classNames(rootClassName || css.root, className);
const user = ensureCurrentUser(currentUser);

const showBanner =
user.id &&
isAuthenticated &&
authScopes.length === 1 &&
authScopes[0] === 'user:limited' &&
!disabledPages.includes(currentPage);

const { firstName, lastName } = user.attributes.profile;

return showBanner ? (
<div className={classes}>
<p className={css.text}>
<FormattedMessage id="LimitedAccessBanner.message" values={{ firstName, lastName }} />
</p>
<Button rootClassName={css.button} onClick={onLogout}>
<FormattedMessage id="LimitedAccessBanner.logout" />
</Button>
</div>
) : null;
};

LimitedAccessBanner.defaultProps = {
rootClassName: null,
className: null,
currentUser: null,
authScopes: [],
currentPage: null,
};

const { array, bool, func, string } = PropTypes;

LimitedAccessBanner.propTypes = {
rootClassName: string,
className: string,
isAuthenticated: bool.isRequired,
authScopes: array,
currentUser: propTypes.currentUser,
onLogout: func.isRequired,
currentPage: string,
};

export default LimitedAccessBanner;
13 changes: 12 additions & 1 deletion src/components/Topbar/Topbar.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { createResourceLocatorString, pathByRouteName } from '../../util/routes'
import { propTypes } from '../../util/types';
import {
Button,
LimitedAccessBanner,
Logo,
Modal,
ModalMissingInformation,
Expand Down Expand Up @@ -134,6 +135,7 @@ class TopbarComponent extends Component {
mobileRootClassName,
mobileClassName,
isAuthenticated,
authScopes,
authInProgress,
currentUser,
currentUserHasListings,
Expand Down Expand Up @@ -189,6 +191,13 @@ class TopbarComponent extends Component {

return (
<div className={classes}>
<LimitedAccessBanner
isAuthenticated={isAuthenticated}
authScopes={authScopes}
currentUser={currentUser}
onLogout={this.handleLogout}
currentPage={currentPage}
/>
<div className={classNames(mobileRootClassName || css.container, mobileClassName)}>
<Button
rootClassName={css.menu}
Expand Down Expand Up @@ -283,9 +292,10 @@ TopbarComponent.defaultProps = {
currentUserHasOrders: null,
currentPage: null,
sendVerificationEmailError: null,
authScopes: [],
};

const { func, number, shape, string } = PropTypes;
const { array, func, number, shape, string } = PropTypes;

TopbarComponent.propTypes = {
className: string,
Expand All @@ -294,6 +304,7 @@ TopbarComponent.propTypes = {
mobileRootClassName: string,
mobileClassName: string,
isAuthenticated: bool.isRequired,
authScopes: array,
authInProgress: bool.isRequired,
currentUser: propTypes.currentUser,
currentUserHasListings: bool.isRequired,
Expand Down
1 change: 1 addition & 0 deletions src/components/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ export { default as IconSuccess } from './IconSuccess/IconSuccess';
export { default as ExternalLink } from './ExternalLink/ExternalLink';
export { default as ExpandingTextarea } from './ExpandingTextarea/ExpandingTextarea';
export { default as Form } from './Form/Form';
export { default as LimitedAccessBanner } from './LimitedAccessBanner/LimitedAccessBanner';
export { default as Logo } from './Logo/Logo';
export { default as NamedLink } from './NamedLink/NamedLink';
export { default as NamedRedirect } from './NamedRedirect/NamedRedirect';
Expand Down
9 changes: 7 additions & 2 deletions src/containers/TopbarContainer/TopbarContainer.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React from 'react';
import { bool, func, number, object, shape, string } from 'prop-types';
import { array, bool, func, number, object, shape, string } from 'prop-types';
import { compose } from 'redux';
import { connect } from 'react-redux';
import { withRouter } from 'react-router-dom';
Expand All @@ -19,6 +19,7 @@ export const TopbarContainerComponent = props => {
currentUserHasOrders,
history,
isAuthenticated,
authScopes,
hasGenericError,
location,
notificationCount,
Expand All @@ -40,6 +41,7 @@ export const TopbarContainerComponent = props => {
currentUserHasOrders={currentUserHasOrders}
history={history}
isAuthenticated={isAuthenticated}
authScopes={authScopes}
location={location}
notificationCount={notificationCount}
onLogout={onLogout}
Expand All @@ -60,6 +62,7 @@ TopbarContainerComponent.defaultProps = {
currentUserHasOrders: null,
notificationCount: 0,
sendVerificationEmailError: null,
authScopes: null,
};

TopbarContainerComponent.propTypes = {
Expand All @@ -70,6 +73,7 @@ TopbarContainerComponent.propTypes = {
currentUserHasListings: bool.isRequired,
currentUserHasOrders: bool,
isAuthenticated: bool.isRequired,
authScopes: array,
notificationCount: number,
onLogout: func.isRequired,
onManageDisableScrolling: func.isRequired,
Expand All @@ -87,7 +91,7 @@ TopbarContainerComponent.propTypes = {

const mapStateToProps = state => {
// Topbar needs isAuthenticated
const { isAuthenticated, logoutError } = state.Auth;
const { isAuthenticated, logoutError, authScopes } = state.Auth;
// Topbar needs user info.
const {
currentUser,
Expand All @@ -105,6 +109,7 @@ const mapStateToProps = state => {
currentUserHasOrders,
notificationCount,
isAuthenticated,
authScopes,
sendVerificationEmailInProgress,
sendVerificationEmailError,
hasGenericError,
Expand Down
12 changes: 10 additions & 2 deletions src/ducks/Auth.duck.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ export const USER_LOGOUT = 'app/USER_LOGOUT';
const initialState = {
isAuthenticated: false,

// scopes associated with current token
authScopes: [],

// auth info
authInfoLoaded: false,

Expand All @@ -53,7 +56,12 @@ export default function reducer(state = initialState, action = {}) {
case AUTH_INFO_REQUEST:
return state;
case AUTH_INFO_SUCCESS:
return { ...state, authInfoLoaded: true, isAuthenticated: authenticated(payload) };
return {
...state,
authInfoLoaded: true,
isAuthenticated: authenticated(payload),
authScopes: payload.scopes,
};

case LOGIN_REQUEST:
return {
Expand All @@ -71,7 +79,7 @@ export default function reducer(state = initialState, action = {}) {
case LOGOUT_REQUEST:
return { ...state, logoutInProgress: true, loginError: null, logoutError: null };
case LOGOUT_SUCCESS:
return { ...state, logoutInProgress: false, isAuthenticated: false };
return { ...state, logoutInProgress: false, isAuthenticated: false, authScopes: [] };
case LOGOUT_ERROR:
return { ...state, logoutInProgress: false, logoutError: payload };

Expand Down
2 changes: 2 additions & 0 deletions src/translations/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -322,6 +322,8 @@
"LayoutWrapperAccountSettingsSideNav.passwordTabTitle": "Password",
"LayoutWrapperAccountSettingsSideNav.paymentsTabTitle": "Payout details",
"LayoutWrapperAccountSettingsSideNav.paymentMethodsTabTitle": "Payment methods",
"LimitedAccessBanner.message": "Your are logged in as {firstName} {lastName}. You have limited rights for actions.",
"LimitedAccessBanner.logout": "Log out",
"ListingCard.hostedBy": "Hosted by {authorName}.",
"ListingCard.perDay": "per day",
"ListingCard.perNight": "per night",
Expand Down

0 comments on commit 606cec3

Please sign in to comment.