diff --git a/src/platforms/web/stubs.js b/src/platforms/web/stubs.js
index 74e8d3c52..0ef460058 100644
--- a/src/platforms/web/stubs.js
+++ b/src/platforms/web/stubs.js
@@ -20,4 +20,7 @@ export const remote = {
require: callable,
};
+export const clipboard = {};
+export const ipcRenderer = {};
+
export const isDev = false;
diff --git a/src/ui/component/common/icon-custom.jsx b/src/ui/component/common/icon-custom.jsx
index d0123f8d3..e98bce146 100644
--- a/src/ui/component/common/icon-custom.jsx
+++ b/src/ui/component/common/icon-custom.jsx
@@ -295,4 +295,11 @@ export const icons = {
),
+ [ICONS.SIGN_OUT]: buildIcon(
+
+
+
+
+
+ ),
};
diff --git a/src/ui/component/header/index.js b/src/ui/component/header/index.js
index 12c91ad43..4cb56f16e 100644
--- a/src/ui/component/header/index.js
+++ b/src/ui/component/header/index.js
@@ -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 => ({
diff --git a/src/ui/component/header/view.jsx b/src/ui/component/header/view.jsx
index 4dbb85374..e494fc0e1 100644
--- a/src/ui/component/header/view.jsx
+++ b/src/ui/component/header/view.jsx
@@ -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 (
diff --git a/src/ui/component/router/view.jsx b/src/ui/component/router/view.jsx
index 6656a9f23..5d99450de 100644
--- a/src/ui/component/router/view.jsx
+++ b/src/ui/component/router/view.jsx
@@ -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
Sign In
;
+ return ;
}
function SignUpPage() {
diff --git a/src/ui/component/sideBar/index.js b/src/ui/component/sideBar/index.js
index 2e2519897..32bbf837e 100644
--- a/src/ui/component/sideBar/index.js
+++ b/src/ui/component/sideBar/index.js
@@ -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 = () => ({});
diff --git a/src/ui/component/sideBar/view.jsx b/src/ui/component/sideBar/view.jsx
index 552a2a00d..38d6a271b 100644
--- a/src/ui/component/sideBar/view.jsx
+++ b/src/ui/component/sideBar/view.jsx
@@ -9,10 +9,11 @@ import StickyBox from 'react-sticky-box/dist/esnext';
type Props = {
subscriptions: Array,
followedTags: Array,
+ email: ?string,
};
function SideBar(props: Props) {
- const { subscriptions, followedTags } = props;
+ const { subscriptions, followedTags, email } = props;
function buildLink(path, label, icon, guide) {
return {
@@ -25,47 +26,53 @@ function SideBar(props: Props) {
return (
-
+ {email ? (
+
+ ) : (
+
+ )}
);
}
diff --git a/src/ui/component/wunderbar/internal/autocomplete.jsx b/src/ui/component/wunderbar/internal/autocomplete.jsx
index c24c556c5..7c1f0d378 100644
--- a/src/ui/component/wunderbar/internal/autocomplete.jsx
+++ b/src/ui/component/wunderbar/internal/autocomplete.jsx
@@ -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 */
diff --git a/src/ui/constants/icons.js b/src/ui/constants/icons.js
index b2a88790f..31191ef78 100644
--- a/src/ui/constants/icons.js
+++ b/src/ui/constants/icons.js
@@ -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';
diff --git a/src/ui/index.jsx b/src/ui/index.jsx
index 9a30e57a6..3a96dc8bf 100644
--- a/src/ui/index.jsx
+++ b/src/ui/index.jsx
@@ -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
diff --git a/src/ui/modal/modalAuthFailure/index.js b/src/ui/modal/modalAuthFailure/index.js
deleted file mode 100644
index ed984b8b1..000000000
--- a/src/ui/modal/modalAuthFailure/index.js
+++ /dev/null
@@ -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);
diff --git a/src/ui/modal/modalAuthFailure/view.jsx b/src/ui/modal/modalAuthFailure/view.jsx
deleted file mode 100644
index 90848ce9d..000000000
--- a/src/ui/modal/modalAuthFailure/view.jsx
+++ /dev/null
@@ -1,32 +0,0 @@
-// @flow
-import React from 'react';
-import { Modal } from 'modal/modal';
-
-type Props = {
- close: () => void,
-};
-
-class ModalAuthFailure extends React.PureComponent {
- render() {
- const { close } = this.props;
-
- return (
- {
- window.location.reload();
- }}
- onAborted={close}
- >
- {__('If reloading does not fix this, or you see this at every start up, please email help@lbry.com.')}
-
- );
- }
-}
-
-export default ModalAuthFailure;
diff --git a/src/ui/modal/modalRouter/view.jsx b/src/ui/modal/modalRouter/view.jsx
index 25a81cc04..013da8be5 100644
--- a/src/ui/modal/modalRouter/view.jsx
+++ b/src/ui/modal/modalRouter/view.jsx
@@ -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 ;
case MODALS.FIRST_REWARD:
return ;
- case MODALS.AUTHENTICATION_FAILURE:
- return ;
case MODALS.TRANSACTION_FAILED:
return ;
case MODALS.REWARD_APPROVAL_REQUIRED:
diff --git a/src/ui/redux/reducers/app.js b/src/ui/redux/reducers/app.js
index 2517cc442..5746fddd3 100644
--- a/src/ui/redux/reducers/app.js
+++ b/src/ui/redux/reducers/app.js
@@ -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,
diff --git a/src/ui/scss/component/_animation.scss b/src/ui/scss/component/_animation.scss
index fe2878ac5..18e767679 100644
--- a/src/ui/scss/component/_animation.scss
+++ b/src/ui/scss/component/_animation.scss
@@ -67,3 +67,14 @@
width: 0;
}
}
+
+@keyframes menu-animate-in {
+ 0% {
+ transform: scaleY(0);
+ opacity: 0;
+ }
+ 100% {
+ transform: scaleY(1);
+ opacity: 1;
+ }
+}
diff --git a/src/ui/scss/component/_header.scss b/src/ui/scss/component/_header.scss
index 5def79c3d..9ae8fff98 100644
--- a/src/ui/scss/component/_header.scss
+++ b/src/ui/scss/component/_header.scss
@@ -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);
diff --git a/src/ui/scss/component/_navigation.scss b/src/ui/scss/component/_navigation.scss
index 92a60e992..af2877767 100644
--- a/src/ui/scss/component/_navigation.scss
+++ b/src/ui/scss/component/_navigation.scss
@@ -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;
diff --git a/src/ui/scss/component/_wunderbar.scss b/src/ui/scss/component/_wunderbar.scss
index bbda27414..14c5ab443 100644
--- a/src/ui/scss/component/_wunderbar.scss
+++ b/src/ui/scss/component/_wunderbar.scss
@@ -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);
diff --git a/src/ui/scss/component/menu-button.scss b/src/ui/scss/component/menu-button.scss
index 6e40544a6..c53ed8bfd 100644
--- a/src/ui/scss/component/menu-button.scss
+++ b/src/ui/scss/component/menu-button.scss
@@ -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,
diff --git a/src/ui/scss/init/_vars.scss b/src/ui/scss/init/_vars.scss
index edd45360c..1f2a187b9 100644
--- a/src/ui/scss/init/_vars.scss
+++ b/src/ui/scss/init/_vars.scss
@@ -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;