header changes with sign in/sign out

This commit is contained in:
Sean Yesmunt 2019-08-21 16:54:44 -04:00
parent b0a34cfc46
commit 3311e1af1f
20 changed files with 204 additions and 170 deletions

View file

@ -20,4 +20,7 @@ export const remote = {
require: callable,
};
export const clipboard = {};
export const ipcRenderer = {};
export const isDev = false;

View file

@ -295,4 +295,11 @@ export const icons = {
<line x1="15" y1="12" x2="3" y2="12" />
</g>
),
[ICONS.SIGN_OUT]: buildIcon(
<g>
<path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4" />
<polyline points="16 17 21 12 16 7" />
<line x1="21" y1="12" x2="9" y2="12" />
</g>
),
};

View file

@ -1,10 +1,10 @@
import * as SETTINGS from 'constants/settings';
import { connect } from 'react-redux';
import { selectBalance, SETTINGS as LBRY_REDUX_SETTINGS } from 'lbry-redux';
import { selectUserEmail } from 'lbryinc';
import { formatCredits } from 'util/format-credits';
import { doSetClientSetting } from 'redux/actions/settings';
import { makeSelectClientSetting } from 'redux/selectors/settings';
import Header from './view';
const select = state => ({
@ -14,6 +14,7 @@ const select = state => ({
currentTheme: makeSelectClientSetting(SETTINGS.THEME)(state),
automaticDarkModeEnabled: makeSelectClientSetting(SETTINGS.AUTOMATIC_DARK_MODE_ENABLED)(state),
hideBalance: makeSelectClientSetting(SETTINGS.HIDE_BALANCE)(state),
email: selectUserEmail(state),
});
const perform = dispatch => ({

View file

@ -1,7 +1,8 @@
// @flow
import * as ICONS from 'constants/icons';
import * as SETTINGS from 'constants/settings';
import * as React from 'react';
import * as PAGES from 'constants/pages';
import React, { Fragment } from 'react';
import { withRouter } from 'react-router';
import Button from 'component/button';
import LbcSymbol from 'component/common/lbc-symbol';
@ -9,6 +10,22 @@ import WunderBar from 'component/wunderbar';
import Icon from 'component/common/icon';
import { Menu, MenuList, MenuButton, MenuItem } from '@reach/menu-button';
// Move this into jessops password util
import cookie from 'cookie';
// @if TARGET='app'
import keytar from 'keytar';
// @endif;
function deleteAuthToken() {
// @if TARGET='app'
keytar.deletePassword('LBRY', 'auth_token').catch(console.error); //eslint-disable-line
// @endif;
// @if TARGET='web'
document.cookie = cookie.serialize('auth_token', '', {
expires: new Date(),
});
// @endif
}
type Props = {
autoUpdateDownloaded: boolean,
balance: string,
@ -20,10 +37,20 @@ type Props = {
automaticDarkModeEnabled: boolean,
setClientSetting: (string, boolean | string) => void,
hideBalance: boolean,
email: ?string,
};
const Header = (props: Props) => {
const { roundedBalance, history, setClientSetting, currentTheme, automaticDarkModeEnabled, hideBalance } = props;
const {
roundedBalance,
history,
setClientSetting,
currentTheme,
automaticDarkModeEnabled,
hideBalance,
email,
} = props;
const isAuthenticated = Boolean(email);
function handleThemeToggle() {
if (automaticDarkModeEnabled) {
@ -49,6 +76,12 @@ const Header = (props: Props) => {
return __('Account');
}
function signOut() {
// Replace this with actual clearUser function
window.store.dispatch({ type: 'USER_FETCH_FAILURE' });
deleteAuthToken();
}
return (
<header className="header">
<div className="header__contents">
@ -78,17 +111,18 @@ const Header = (props: Props) => {
/>
</div>
{/* @endif */}
<WunderBar />
</div>
<WunderBar />
<div className="header__navigation">
<div className="header__menu">
{isAuthenticated ? (
<Fragment>
<Menu>
<MenuButton className="header__navigation-item menu__title">
<Icon icon={ICONS.ACCOUNT} />
{getAccountTitle()}
</MenuButton>
<MenuList>
<MenuList className="menu__list--header">
<MenuItem className="menu__link" onSelect={() => history.push(`/$/account`)}>
<Icon aria-hidden icon={ICONS.OVERVIEW} />
{__('Overview')}
@ -105,6 +139,10 @@ const Header = (props: Props) => {
<Icon aria-hidden icon={ICONS.PUBLISH} />
{__('Publish')}
</MenuItem>
<MenuItem className="menu__link" onSelect={signOut}>
<Icon aria-hidden icon={ICONS.SIGN_OUT} />
{__('Sign Out')}
</MenuItem>
</MenuList>
</Menu>
@ -112,7 +150,7 @@ const Header = (props: Props) => {
<MenuButton className="header__navigation-item menu__title">
<Icon size={18} icon={ICONS.SETTINGS} />
</MenuButton>
<MenuList>
<MenuList className="menu__list--header">
<MenuItem className="menu__link" onSelect={() => history.push(`/$/settings`)}>
<Icon aria-hidden tootlip icon={ICONS.SETTINGS} />
{__('Settings')}
@ -127,6 +165,13 @@ const Header = (props: Props) => {
</MenuItem>
</MenuList>
</Menu>
</Fragment>
) : (
<Fragment>
<Button navigate={`/$/${PAGES.AUTH}/signin`} button="link" label={__('Sign In')} />
<Button navigate={`/$/${PAGES.AUTH}/signup`} button="primary" label={__('Sign Up')} />
</Fragment>
)}
</div>
</div>
</header>

View file

@ -24,6 +24,8 @@ import FollowingPage from 'page/following';
import ListBlocked from 'page/listBlocked';
import FourOhFourPage from 'page/fourOhFour';
import UserEmail from 'component/userEmail';
// Tell the browser we are handling scroll restoration
if ('scrollRestoration' in history) {
history.scrollRestoration = 'manual';
@ -35,7 +37,7 @@ type Props = {
};
function SignInPage() {
return <h1>Sign In</h1>;
return <UserEmail />;
}
function SignUpPage() {

View file

@ -2,6 +2,7 @@ import * as SETTINGS from 'constants/settings';
import { connect } from 'react-redux';
import { selectSubscriptions } from 'redux/selectors/subscriptions';
import { selectFollowedTags } from 'lbry-redux';
import { selectUserEmail } from 'lbryinc';
import SideBar from './view';
import { makeSelectClientSetting } from 'redux/selectors/settings';
@ -9,6 +10,7 @@ const select = state => ({
subscriptions: selectSubscriptions(state),
followedTags: selectFollowedTags(state),
language: makeSelectClientSetting(SETTINGS.LANGUAGE)(state), // trigger redraw on language change
email: selectUserEmail(state),
});
const perform = () => ({});

View file

@ -9,10 +9,11 @@ import StickyBox from 'react-sticky-box/dist/esnext';
type Props = {
subscriptions: Array<Subscription>,
followedTags: Array<Tag>,
email: ?string,
};
function SideBar(props: Props) {
const { subscriptions, followedTags } = props;
const { subscriptions, followedTags, email } = props;
function buildLink(path, label, icon, guide) {
return {
@ -25,15 +26,18 @@ function SideBar(props: Props) {
return (
<StickyBox offsetTop={100} offsetBottom={20}>
{email ? (
<nav className="navigation">
<ul className="navigation-links">
{[
{
...buildLink(null, __('Home'), ICONS.HOME),
},
// @if TARGET='app'
{
...buildLink(PAGES.LIBRARY, __('Library'), ICONS.LIBRARY),
},
// @endif
{
...buildLink(PAGES.PUBLISHED, __('Publishes'), ICONS.PUBLISH),
},
@ -66,6 +70,9 @@ function SideBar(props: Props) {
))}
</ul>
</nav>
) : (
<div className="navigation--placeholder" />
)}
</StickyBox>
);
}

View file

@ -1,3 +1,4 @@
/* eslint-disable */
/*
This is taken from https://github.com/reactjs/react-autocomplete
@ -208,7 +209,7 @@ export default class Autocomplete extends React.Component {
this.maybeAutoCompleteText = this.maybeAutoCompleteText.bind(this);
}
componentWillMount() {
UNSAFE_componentWillMount() {
// this.refs is frozen, so we need to assign a new object to it
this.refs = {};
this._ignoreBlur = false;
@ -222,7 +223,7 @@ export default class Autocomplete extends React.Component {
this._scrollTimer = null;
}
componentWillReceiveProps(nextProps) {
UNSAFE_componentWillReceiveProps(nextProps) {
if (this.state.highlightedIndex !== null) {
this.setState(this.ensureHighlightedIndex);
}
@ -585,3 +586,4 @@ export default class Autocomplete extends React.Component {
);
}
}
/* eslint-enable */

View file

@ -75,3 +75,4 @@ export const UNBLOCK = 'Circle';
export const VIEW = 'View';
export const EYE = 'Eye';
export const EYE_OFF = 'EyeOff';
export const SIGN_OUT = 'SignOut';

View file

@ -82,7 +82,7 @@ Lbryio.setOverride(
authToken = newAuthToken;
// @if TARGET='web'
document.cookie = cookie.serialize('auth_token', authToken, {
cookie.serialize('auth_token', authToken, {
maxAge: COOKIE_EXPIRE_TIME,
});
// @endif

View file

@ -1,14 +0,0 @@
import { connect } from 'react-redux';
import { doHideModal } from 'redux/actions/app';
import ModalAuthFailure from './view';
const select = () => ({});
const perform = dispatch => ({
close: () => dispatch(doHideModal()),
});
export default connect(
select,
perform
)(ModalAuthFailure);

View file

@ -1,32 +0,0 @@
// @flow
import React from 'react';
import { Modal } from 'modal/modal';
type Props = {
close: () => void,
};
class ModalAuthFailure extends React.PureComponent<Props> {
render() {
const { close } = this.props;
return (
<Modal
isOpen
contentLabel={__('Unable to Authenticate')}
title={__('Authentication Failure')}
type="confirm"
confirmButtonLabel={__('Reload')}
abortButtonLabel={__('Continue')}
onConfirmed={() => {
window.location.reload();
}}
onAborted={close}
>
<p>{__('If reloading does not fix this, or you see this at every start up, please email help@lbry.com.')}</p>
</Modal>
);
}
}
export default ModalAuthFailure;

View file

@ -2,7 +2,6 @@
import React from 'react';
import * as MODALS from 'constants/modal_types';
import ModalError from 'modal/modalError';
import ModalAuthFailure from 'modal/modalAuthFailure';
import ModalDownloading from 'modal/modalDownloading';
import ModalAutoGenerateThumbnail from 'modal/modalAutoGenerateThumbnail';
import ModalAutoUpdateDownloaded from 'modal/modalAutoUpdateDownloaded';
@ -67,8 +66,6 @@ function ModalRouter(props: Props) {
return <ModalWelcome {...modalProps} />;
case MODALS.FIRST_REWARD:
return <ModalFirstReward {...modalProps} />;
case MODALS.AUTHENTICATION_FAILURE:
return <ModalAuthFailure {...modalProps} />;
case MODALS.TRANSACTION_FAILED:
return <ModalTransactionFailed {...modalProps} />;
case MODALS.REWARD_APPROVAL_REQUIRED:

View file

@ -1,7 +1,6 @@
// @flow
import * as ACTIONS from 'constants/action_types';
import * as MODALS from 'constants/modal_types';
import { remote } from 'electron';
// @if TARGET='app'
@ -251,14 +250,6 @@ reducers[ACTIONS.HIDE_MODAL] = state =>
modalProps: null,
});
// This is fired from the lbryinc module
// Instead of adding callbacks in that module, we can just listen for this event
// There will be no other modals at this time as this is a blocking action
reducers[ACTIONS.AUTHENTICATION_FAILURE] = state =>
Object.assign({}, state, {
modal: MODALS.AUTHENTICATION_FAILURE,
});
reducers[ACTIONS.TOGGLE_SEARCH_EXPANDED] = state =>
Object.assign({}, state, {
searchOptionsExpanded: !state.searchOptionsExpanded,

View file

@ -67,3 +67,14 @@
width: 0;
}
}
@keyframes menu-animate-in {
0% {
transform: scaleY(0);
opacity: 0;
}
100% {
transform: scaleY(1);
opacity: 1;
}
}

View file

@ -24,14 +24,13 @@
max-width: var(--page-max-width);
height: calc(var(--header-height) - 1px);
display: flex;
justify-content: space-between;
margin: auto;
padding: 0 var(--spacing-large);
}
.header__navigation {
flex: 1;
display: flex;
justify-content: space-between;
&:last-of-type {
width: var(--side-nav-width);
@ -42,6 +41,14 @@
}
}
.header__menu {
width: var(--side-nav-width);
margin-left: auto;
display: flex;
justify-content: space-between;
align-items: center;
}
.header__navigation-arrows {
display: flex;
margin-right: var(--spacing-small);
@ -126,7 +133,6 @@
}
.header__navigation-item--lbry {
flex: 1;
font-weight: 800;
margin-right: var(--spacing-medium);

View file

@ -1,13 +1,19 @@
.navigation {
width: var(--side-nav-width);
font-size: var(--font-body);
// padding-top: 100px;
@media (max-width: 600px) {
display: none;
}
}
.navigation--placeholder {
@extend .navigation;
height: 80vh;
background-color: $lbry-blue-1;
border-radius: var(--card-radius);
}
.navigation-links {
@extend .ul--no-style;
flex-direction: column;

View file

@ -1,10 +1,9 @@
.wunderbar {
min-width: 175px;
flex: 1;
height: 100%;
cursor: text;
display: flex;
align-items: center;
flex: 1;
position: relative;
z-index: 1;
margin-right: var(--spacing-main-padding);

View file

@ -17,16 +17,13 @@
white-space: nowrap;
outline: none;
background-color: $lbry-white;
box-shadow: var(--card-box-shadow) $lbry-gray-2;
border: 1px solid $lbry-gray-1;
border-top: none;
[data-mode='dark'] & {
background-color: var(--dm-color-05);
color: $lbry-white;
box-shadow: var(--card-box-shadow) $lbry-black;
border: 1px solid $lbry-gray-5;
border-top: none;
border-color: var(--dm-color-03);
}
}
@ -69,13 +66,14 @@
}
}
.menu__title {
&:not(:last-of-type) {
margin-right: var(--spacing-medium);
}
.menu__list--header {
margin-top: -1px;
margin-left: calc(var(--spacing-medium) * -1);
box-shadow: var(--card-box-shadow--attached) $lbry-gray-1;
animation: menu-animate-in var(--animation-duration) var(--animation-style);
span {
margin-left: var(--spacing-small);
[data-mode='dark'] & {
box-shadow: var(--card-box-shadow) $lbry-black;
}
}
@ -83,6 +81,7 @@
display: flex;
align-items: center;
padding: var(--spacing-medium);
padding-right: var(--spacing-large);
}
.menu__title,

View file

@ -74,7 +74,8 @@ $large-breakpoint: 1921px;
// Card
--card-radius: 5px;
--card-max-width: 1000px;
--card-box-shadow: 0px 2px 6px 0;
--card-box-shadow: 0px 2px 6px 0px;
--card-box-shadow--attached: 0px 4px 6px 0px;
// Modal
--modal-width: 440px;