add sign in flow to lbry.tv

This commit is contained in:
Jeremy Kauffman 2019-11-13 19:33:36 -05:00 committed by Sean Yesmunt
parent 3c08913d1c
commit a1f0e485b5
7 changed files with 127 additions and 17 deletions

View file

@ -150,6 +150,7 @@
"react-dom": "^16.8.2",
"react-draggable": "^3.3.0",
"react-ga": "^2.5.7",
"react-google-recaptcha": "^2.0.1",
"react-hot-loader": "^4.11.1",
"react-modal": "^3.1.7",
"react-paginate": "^5.2.1",

View file

@ -44,9 +44,14 @@ const Header = (props: Props) => {
} = props;
const authenticated = Boolean(email);
//on the verify page don't let anyone escape other than by closing the tab to keep session data consistent
const isVerifyPage = history.location.pathname.includes(PAGES.AUTH_VERIFY);
// Sign out if they click the "x" when they are on the password prompt
const authHeaderAction = syncError ? { onClick: signOut } : { navigate: '/' };
const homeButtonNavigationProps = authHeader ? authHeaderAction : { navigate: '/' };
const homeButtonNavigationProps = isVerifyPage ?
{ } :
authHeader ? authHeaderAction : { navigate: '/' };
const closeButtonNavigationProps = authHeader ? authHeaderAction : { onClick: () => history.goBack() };
function handleThemeToggle() {
@ -111,7 +116,7 @@ const Header = (props: Props) => {
)}
{/* @endif */}
{!authHeader && <WunderBar />}
{!authHeader && <WunderBar/>}
</div>
{!authHeader ? (
@ -122,37 +127,37 @@ const Header = (props: Props) => {
<MenuButton className="header__navigation-item menu__title">{getWalletTitle()}</MenuButton>
<MenuList className="menu__list--header">
<MenuItem className="menu__link" onSelect={() => history.push(`/$/${PAGES.WALLET}`)}>
<Icon aria-hidden icon={ICONS.WALLET} />
<Icon aria-hidden icon={ICONS.WALLET}/>
{__('Wallet')}
</MenuItem>
<MenuItem className="menu__link" onSelect={() => history.push(`/$/${PAGES.REWARDS}`)}>
<Icon aria-hidden icon={ICONS.FEATURED} />
<Icon aria-hidden icon={ICONS.FEATURED}/>
{__('Rewards')}
</MenuItem>
</MenuList>
</Menu>
<Menu>
<MenuButton className="header__navigation-item menu__title">
<Icon size={18} icon={ICONS.ACCOUNT} />
<Icon size={18} icon={ICONS.ACCOUNT}/>
</MenuButton>
<MenuList className="menu__list--header">
<MenuItem className="menu__link" onSelect={() => history.push(`/$/${PAGES.ACCOUNT}`)}>
<Icon aria-hidden icon={ICONS.OVERVIEW} />
<Icon aria-hidden icon={ICONS.OVERVIEW}/>
{__('Overview')}
</MenuItem>
<MenuItem className="menu__link" onSelect={() => history.push(`/$/${PAGES.PUBLISH}`)}>
<Icon aria-hidden icon={ICONS.PUBLISH} />
<Icon aria-hidden icon={ICONS.PUBLISH}/>
{__('Publish')}
</MenuItem>
{authenticated ? (
<MenuItem className="menu__link" onSelect={signOut}>
<Icon aria-hidden icon={ICONS.SIGN_OUT} />
<Icon aria-hidden icon={ICONS.SIGN_OUT}/>
{__('Sign Out')}
</MenuItem>
) : (
<MenuItem className="menu__link" onSelect={() => history.push(`/$/${PAGES.AUTH}`)}>
<Icon aria-hidden icon={ICONS.SIGN_IN} />
<Icon aria-hidden icon={ICONS.SIGN_IN}/>
{__('Sign In')}
</MenuItem>
)}
@ -161,33 +166,33 @@ const Header = (props: Props) => {
<Menu>
<MenuButton className="header__navigation-item menu__title">
<Icon size={18} icon={ICONS.SETTINGS} />
<Icon size={18} icon={ICONS.SETTINGS}/>
</MenuButton>
<MenuList className="menu__list--header">
<MenuItem className="menu__link" onSelect={() => history.push(`/$/${PAGES.SETTINGS}`)}>
<Icon aria-hidden tootlip icon={ICONS.SETTINGS} />
<Icon aria-hidden tootlip icon={ICONS.SETTINGS}/>
{__('Settings')}
</MenuItem>
<MenuItem className="menu__link" onSelect={() => history.push(`/$/${PAGES.HELP}`)}>
<Icon aria-hidden icon={ICONS.HELP} />
<Icon aria-hidden icon={ICONS.HELP}/>
{__('Help')}
</MenuItem>
<MenuItem className="menu__link" onSelect={handleThemeToggle}>
<Icon icon={currentTheme === 'light' ? ICONS.DARK : ICONS.LIGHT} />
<Icon icon={currentTheme === 'light' ? ICONS.DARK : ICONS.LIGHT}/>
{currentTheme === 'light' ? __('Dark') : __('Light')}
</MenuItem>
</MenuList>
</Menu>
</Fragment>
) : (
<Button navigate={`/$/${PAGES.AUTH}`} button="primary" label={__('Sign In')} />
<Button navigate={`/$/${PAGES.AUTH}`} button="primary" label={__('Sign In')}/>
)}
</div>
) : (
) : (!isVerifyPage &&
<div className="header__menu">
{/* Add an empty span here so we can use the same style as above */}
{/* This pushes the close button to the right side */}
<span />
<span/>
<Tooltip label={__('Go Back')}>
<Button icon={ICONS.REMOVE} {...closeButtonNavigationProps} />
</Tooltip>

View file

@ -24,6 +24,7 @@ import FollowingPage from 'page/following';
import ListBlockedPage from 'page/listBlocked';
import FourOhFourPage from 'page/fourOhFour';
import SignInPage from 'page/signIn';
import SignInVerifyPage from 'page/signInVerify';
import ChannelsPage from 'page/channels';
// Tell the browser we are handling scroll restoration
@ -77,6 +78,7 @@ function AppRouter(props: Props) {
<Route path={`/$/${PAGES.AUTH}`} exact component={SignInPage} />
<Route path={`/$/${PAGES.TAGS}`} exact component={TagsPage} />
<Route path={`/$/${PAGES.HELP}`} exact component={HelpPage} />
<Route path={`/$/${PAGES.AUTH_VERIFY}`} exact component={SignInVerifyPage} />
<Route path={`/$/${PAGES.SEARCH}`} exact component={SearchPage} />
<PrivateRoute {...props} path={`/$/${PAGES.INVITE}`} component={InvitePage} />

View file

@ -1,4 +1,5 @@
export const AUTH = 'signin';
export const AUTH_VERIFY = 'verify';
export const BACKUP = 'backup';
export const CHANNEL = 'channel';
export const DISCOVER = 'discover';

View file

@ -0,0 +1,13 @@
import { connect } from 'react-redux';
import { doToast } from 'lbry-redux';
import SignInVerifyPage from './view';
const select = () => ({});
const perform = {
doToast,
};
export default connect(
select,
perform
)(SignInVerifyPage);

View file

@ -0,0 +1,72 @@
// @flow
import React, { useState } from 'react';
import { withRouter } from 'react-router';
import Page from 'component/page';
import ReCAPTCHA from "react-google-recaptcha";
import Button from 'component/button';
import { Lbryio } from 'lbryinc';
import * as PAGES from 'constants/pages';
type Props = {
history: { push: string => void },
doToast: ({}) => void
};
function SignInVerifyPage(props: Props) {
const { history: { push }, doToast } = props;
const urlParams = new URLSearchParams(location.search);
const authToken = urlParams.get('auth_token');
const userSubmittedEmail = urlParams.get('email');
const verificationToken = urlParams.get('verification_token');
const [isAuthenticationSuccess, setIsAuthenticationSuccess] = useState(false);
function onAuthError(message) {
doToast({
message: message || __('Authentication failure.'),
isError: true,
});
//push(`/$/${PAGES.AUTH}`);
}
React.useEffect(() => {
if (!authToken || !userSubmittedEmail || !verificationToken) {
onAuthError(__('Invalid or expired sign-in link.'));
}
}, [authToken, userSubmittedEmail, verificationToken, doToast, push]);
function onCaptchaChange(value) {
Lbryio.call('user_email', 'confirm', {
auth_token: authToken,
email: userSubmittedEmail,
verification_token: verificationToken,
recaptcha: value,
})
.then(() => {
setIsAuthenticationSuccess(true);
})
.catch(() => {
onAuthError(__('Invalid captcha response or other authentication error.'));
});
}
return (
<Page authPage className="main--auth-page">
<section className="main--contained">
<h1 className="section__title--large">{isAuthenticationSuccess ? __('Sign In Success!') : __('Sign In to lbry.tv') }</h1>
<p className="section__subtitle">{ isAuthenticationSuccess ? __('You can now close this tab.') : __('Click below to sign in to lbry.tv') }</p>
{ !isAuthenticationSuccess &&
<div className="section__actions">
<ReCAPTCHA
sitekey="6LePsJgUAAAAAFTuWOKRLnyoNKhm0HA4C3elrFMG"
onChange={onCaptchaChange}
onExpired={onAuthError}
onErrored={onAuthError}
/>
</div>}
</section>
</Page>
);
};
export default withRouter(SignInVerifyPage);

View file

@ -9635,7 +9635,7 @@ promise@^7.1.1:
dependencies:
asap "~2.0.3"
prop-types@^15.5.10, prop-types@^15.5.8, prop-types@^15.6.0, prop-types@^15.6.1, prop-types@^15.6.2, prop-types@^15.7.2:
prop-types@^15.5.0, prop-types@^15.5.10, prop-types@^15.5.8, prop-types@^15.6.0, prop-types@^15.6.1, prop-types@^15.6.2, prop-types@^15.7.2:
version "15.7.2"
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5"
integrity sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==
@ -9870,6 +9870,14 @@ rc@^1.0.1, rc@^1.1.6, rc@^1.2.1, rc@^1.2.7, rc@^1.2.8:
minimist "^1.2.0"
strip-json-comments "~2.0.1"
react-async-script@^1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/react-async-script/-/react-async-script-1.1.1.tgz#f481c6c5f094bf4b94a9d52da0d0dda2e1a74bdf"
integrity sha512-pmgS3O7JcX4YtH/Xy//NXylpD5CNb5T4/zqlVUV3HvcuyOanatvuveYoxl3X30ZSq/+q/+mSXcNS8xDVQJpSeA==
dependencies:
hoist-non-react-statics "^3.3.0"
prop-types "^15.5.0"
react-compound-slider@^1.2.2:
version "1.2.2"
resolved "https://registry.yarnpkg.com/react-compound-slider/-/react-compound-slider-1.2.2.tgz#1cc6809bb2d0282ad90a06040e2b10635edfbea3"
@ -9910,6 +9918,14 @@ react-ga@^2.5.7:
resolved "https://registry.yarnpkg.com/react-ga/-/react-ga-2.5.7.tgz#1c80a289004bf84f84c26d46f3a6a6513081bf2e"
integrity sha512-UmATFaZpEQDO96KFjB5FRLcT6hFcwaxOmAJZnjrSiFN/msTqylq9G+z5Z8TYzN/dbamDTiWf92m6MnXXJkAivQ==
react-google-recaptcha@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/react-google-recaptcha/-/react-google-recaptcha-2.0.1.tgz#3276b29659493f7ca2a5b7739f6c239293cdf1d8"
integrity sha512-4Y8awVnarn7+gdVpu8uvSmRJzzlMMoXqdhLoyToTOfVK6oM+NaChNI8NShnu75Q2YGHLvR1IA1FWZesuYHwn5w==
dependencies:
prop-types "^15.5.0"
react-async-script "^1.1.1"
react-hot-loader@^4.11.1:
version "4.11.1"
resolved "https://registry.yarnpkg.com/react-hot-loader/-/react-hot-loader-4.11.1.tgz#2cabbd0f1c8a44c28837b86d6ce28521e6d9a8ac"