ui feedback

This commit is contained in:
Sean Yesmunt 2019-10-28 10:04:37 -04:00
parent 7372f5dff9
commit c953c9d0a8
14 changed files with 96 additions and 59 deletions
package.json
src/ui
component
header
page
syncPassword
transactionList
userEmailNew
userEmailVerify
page/signIn
scss/component
static
yarn.lock

View file

@ -67,8 +67,7 @@
"@babel/register": "^7.0.0", "@babel/register": "^7.0.0",
"@exponent/electron-cookies": "^2.0.0", "@exponent/electron-cookies": "^2.0.0",
"@hot-loader/react-dom": "^16.8", "@hot-loader/react-dom": "^16.8",
"@lbry/color": "^1.0.2", "@lbry/components": "^2.8.0",
"@lbry/components": "^2.7.4",
"@reach/menu-button": "^0.1.18", "@reach/menu-button": "^0.1.18",
"@reach/rect": "^0.2.1", "@reach/rect": "^0.2.1",
"@reach/tabs": "^0.1.5", "@reach/tabs": "^0.1.5",

View file

@ -24,7 +24,7 @@ type Props = {
setClientSetting: (string, boolean | string) => void, setClientSetting: (string, boolean | string) => void,
hideBalance: boolean, hideBalance: boolean,
email: ?string, email: ?string,
minimal: boolean, authHeader: boolean,
signOut: () => void, signOut: () => void,
}; };
@ -37,10 +37,12 @@ const Header = (props: Props) => {
automaticDarkModeEnabled, automaticDarkModeEnabled,
hideBalance, hideBalance,
email, email,
minimal, authHeader,
signOut, signOut,
} = props; } = props;
const authenticated = Boolean(email); const authenticated = Boolean(email);
const homeButtonNavigationProps = authHeader ? { onClick: signOut } : { navigate: '/' };
const closeButtonNavigationProps = authHeader ? { onClick: signOut } : { onClick: () => history.goBack() };
function handleThemeToggle() { function handleThemeToggle() {
if (automaticDarkModeEnabled) { if (automaticDarkModeEnabled) {
@ -67,7 +69,7 @@ const Header = (props: Props) => {
return ( return (
<header <header
className={classnames('header', { className={classnames('header', {
'header--minimal': minimal, 'header--minimal': authHeader,
// @if TARGET='app' // @if TARGET='app'
'header--mac': IS_MAC, 'header--mac': IS_MAC,
// @endif // @endif
@ -79,11 +81,11 @@ const Header = (props: Props) => {
className="header__navigation-item header__navigation-item--lbry" className="header__navigation-item header__navigation-item--lbry"
label={__('LBRY')} label={__('LBRY')}
icon={ICONS.LBRY} icon={ICONS.LBRY}
navigate="/" {...homeButtonNavigationProps}
/> />
{/* @if TARGET='app' */} {/* @if TARGET='app' */}
{!minimal && ( {!authHeader && (
<div className="header__navigation-arrows"> <div className="header__navigation-arrows">
<Button <Button
className="header__navigation-item header__navigation-item--back" className="header__navigation-item header__navigation-item--back"
@ -104,10 +106,10 @@ const Header = (props: Props) => {
)} )}
{/* @endif */} {/* @endif */}
{!minimal && <WunderBar />} {!authHeader && <WunderBar />}
</div> </div>
{!minimal ? ( {!authHeader ? (
<div className={classnames('header__menu', { 'header__menu--small': IS_WEB && !authenticated })}> <div className={classnames('header__menu', { 'header__menu--small': IS_WEB && !authenticated })}>
{!IS_WEB || authenticated ? ( {!IS_WEB || authenticated ? (
<Fragment> <Fragment>
@ -178,8 +180,11 @@ const Header = (props: Props) => {
</div> </div>
) : ( ) : (
<div className="header__menu"> <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 />
<Tooltip label={__('Go Back')}> <Tooltip label={__('Go Back')}>
<Button icon={ICONS.REMOVE} onClick={() => history.goBack()} /> <Button icon={ICONS.REMOVE} {...closeButtonNavigationProps} />
</Tooltip> </Tooltip>
</div> </div>
)} )}

View file

@ -10,20 +10,20 @@ type Props = {
className: ?string, className: ?string,
autoUpdateDownloaded: boolean, autoUpdateDownloaded: boolean,
isUpgradeAvailable: boolean, isUpgradeAvailable: boolean,
fullscreen: boolean, authPage: boolean,
authenticated: boolean, authenticated: boolean,
}; };
function Page(props: Props) { function Page(props: Props) {
const { children, className, fullscreen = false, authenticated } = props; const { children, className, authPage = false, authenticated } = props;
const obscureSideBar = IS_WEB ? !authenticated : false; const obscureSideBar = IS_WEB ? !authenticated : false;
return ( return (
<Fragment> <Fragment>
<Header minimal={fullscreen} /> <Header authHeader={authPage} />
<div className={classnames('main-wrapper__inner')}> <div className={classnames('main-wrapper__inner')}>
<main className={classnames('main', className, { 'main--full-width': fullscreen })}>{children}</main> <main className={classnames('main', className, { 'main--full-width': authPage })}>{children}</main>
{!fullscreen && <SideBar obscureSideBar={obscureSideBar} />} {!authPage && <SideBar obscureSideBar={obscureSideBar} />}
</div> </div>
</Fragment> </Fragment>
); );

View file

@ -1,6 +1,7 @@
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { doGetSync, selectGetSyncIsPending, selectUserEmail } from 'lbryinc'; import { doGetSync, selectGetSyncIsPending, selectUserEmail } from 'lbryinc';
import { doSetClientSetting } from 'redux/actions/settings'; import { doSetClientSetting } from 'redux/actions/settings';
import { doSignOut } from 'redux/actions/app';
import SyncPassword from './view'; import SyncPassword from './view';
const select = state => ({ const select = state => ({
@ -11,6 +12,7 @@ const select = state => ({
const perform = dispatch => ({ const perform = dispatch => ({
getSync: password => dispatch(doGetSync(password)), getSync: password => dispatch(doGetSync(password)),
setClientSetting: (key, value) => dispatch(doSetClientSetting(key, value)), setClientSetting: (key, value) => dispatch(doSetClientSetting(key, value)),
signOut: () => dispatch(doSignOut()),
}); });
export default connect( export default connect(

View file

@ -5,15 +5,17 @@ import Button from 'component/button';
import Card from 'component/common/card'; import Card from 'component/common/card';
import { setSavedPassword } from 'util/saved-passwords'; import { setSavedPassword } from 'util/saved-passwords';
import usePersistedState from 'effects/use-persisted-state'; import usePersistedState from 'effects/use-persisted-state';
import I18nMessage from 'component/i18nMessage';
type Props = { type Props = {
getSync: (?string) => void, getSync: (?string) => void,
getSyncIsPending: boolean, getSyncIsPending: boolean,
email: string, email: string,
signOut: () => void,
}; };
function SyncPassword(props: Props) { function SyncPassword(props: Props) {
const { getSync, getSyncIsPending, email } = props; const { getSync, getSyncIsPending, email, signOut } = props;
const [password, setPassword] = React.useState(''); const [password, setPassword] = React.useState('');
const [rememberPassword, setRememberPassword] = usePersistedState(true); const [rememberPassword, setRememberPassword] = usePersistedState(true);
@ -25,7 +27,7 @@ function SyncPassword(props: Props) {
return ( return (
<Form onSubmit={handleSubmit}> <Form onSubmit={handleSubmit}>
<Card <Card
title={__('Enter Your LBRY Password')} title={__('Enter Your Wallet Password')}
subtitle={__( subtitle={__(
'You set your wallet password when you previously installed LBRY. This may have been on different device.' 'You set your wallet password when you previously installed LBRY. This may have been on different device.'
)} )}
@ -46,7 +48,18 @@ function SyncPassword(props: Props) {
/> />
<div className="card__actions"> <div className="card__actions">
<Button type="submit" button="primary" label={__('Continue')} disabled={getSyncIsPending} /> <Button type="submit" button="primary" label={__('Continue')} disabled={getSyncIsPending} />
<Button button="link" label={__('Cancel')} onClick={signOut} />
</div> </div>
<p className="help">
<I18nMessage
tokens={{
help: <Button button="link" label={__('Help Guide')} />,
email: <Button button="link" label={'help@lbry.com'} href="mailto:help@lbry.com" />,
}}
>
If you are having issues, checkout our %help% or email us at %email%.
</I18nMessage>
</p>
</div> </div>
} }
/> />

View file

@ -45,6 +45,7 @@ function TransactionList(props: Props) {
<h2 className="card__title--between"> <h2 className="card__title--between">
{title} {title}
<div className="card__actions--inline"> <div className="card__actions--inline">
<RefreshTransactionButton />
{/* @if TARGET='app' */} {/* @if TARGET='app' */}
{!slim && ( {!slim && (
<FileExporter <FileExporter
@ -57,7 +58,6 @@ function TransactionList(props: Props) {
/> />
)} )}
{/* @endif */} {/* @endif */}
<RefreshTransactionButton />
{!slim && ( {!slim && (
<FormField <FormField
type="select" type="select"

View file

@ -2,7 +2,6 @@
import React, { useState } from 'react'; import React, { useState } from 'react';
import { FormField, Form } from 'component/common/form'; import { FormField, Form } from 'component/common/form';
import Button from 'component/button'; import Button from 'component/button';
import { Lbryio } from 'lbryinc';
import analytics from 'analytics'; import analytics from 'analytics';
import { EMAIL_REGEX } from 'constants/email'; import { EMAIL_REGEX } from 'constants/email';
import I18nMessage from 'component/i18nMessage'; import I18nMessage from 'component/i18nMessage';
@ -26,20 +25,15 @@ function UserEmailNew(props: Props) {
setSync(formSyncEnabled); setSync(formSyncEnabled);
addUserEmail(newEmail); addUserEmail(newEmail);
analytics.emailProvidedEvent(); analytics.emailProvidedEvent();
// @if TARGET='web'
Lbryio.call('user_tag', 'edit', { add: 'lbrytv' });
// @endif
} }
return ( return (
<React.Fragment> <React.Fragment>
<h1 className="section__title--large">{__('Welcome To LBRY')}</h1> <h1 className="section__title--large">{__('Sign In To LBRY')}</h1>
<p className="section__subtitle">{__('Create a new account or sign in.')}</p> <p className="section__subtitle">{__('Create a new account or sign in.')}</p>
<Form onSubmit={handleSubmit} className="section__body"> <Form onSubmit={handleSubmit} className="section__body">
<FormField <FormField
autoFocus autoFocus
className="form-field--short"
placeholder={__('hotstuff_96@hotmail.com')} placeholder={__('hotstuff_96@hotmail.com')}
type="email" type="email"
name="sign_up_email" name="sign_up_email"
@ -59,19 +53,6 @@ function UserEmailNew(props: Props) {
<Button button="link" href="https://lbry.com/faq/account-sync" label={__('Learn More')} /> <Button button="link" href="https://lbry.com/faq/account-sync" label={__('Learn More')} />
</React.Fragment> </React.Fragment>
} }
helper={
<React.Fragment>
<I18nMessage
tokens={{
terms: (
<Button button="link" href="https://www.lbry.com/termsofservice" label={__('Terms of Service')} />
),
}}
>
By continuing, I agree to the %terms% and confirm I am over the age of 13.
</I18nMessage>
</React.Fragment>
}
checked={formSyncEnabled} checked={formSyncEnabled}
onChange={() => setFormSyncEnabled(!formSyncEnabled)} onChange={() => setFormSyncEnabled(!formSyncEnabled)}
/> />
@ -79,6 +60,19 @@ function UserEmailNew(props: Props) {
<div className="card__actions"> <div className="card__actions">
<Button button="primary" type="submit" label={__('Continue')} disabled={!newEmail || !valid || isPending} /> <Button button="primary" type="submit" label={__('Continue')} disabled={!newEmail || !valid || isPending} />
</div> </div>
<p className="help">
<React.Fragment>
<I18nMessage
tokens={{
terms: (
<Button button="link" href="https://www.lbry.com/termsofservice" label={__('Terms of Service')} />
),
}}
>
By continuing, I agree to the %terms% and confirm I am over the age of 13.
</I18nMessage>
</React.Fragment>
</p>
</Form> </Form>
</React.Fragment> </React.Fragment>
); );

View file

@ -1,15 +1,26 @@
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { selectEmailToVerify, doUserResendVerificationEmail, doUserCheckEmailVerified, selectUser } from 'lbryinc'; import {
selectEmailAlreadyExists,
selectEmailToVerify,
doUserResendVerificationEmail,
doUserCheckEmailVerified,
selectUser,
selectResendingVerificationEmail,
} from 'lbryinc';
import { doToast } from 'lbry-redux';
import UserEmailVerify from './view'; import UserEmailVerify from './view';
const select = state => ({ const select = state => ({
email: selectEmailToVerify(state), email: selectEmailToVerify(state),
isReturningUser: selectEmailAlreadyExists(state),
user: selectUser(state), user: selectUser(state),
resendingEmail: selectResendingVerificationEmail(state),
}); });
const perform = dispatch => ({ const perform = dispatch => ({
resendVerificationEmail: email => dispatch(doUserResendVerificationEmail(email)), resendVerificationEmail: email => dispatch(doUserResendVerificationEmail(email)),
checkEmailVerified: () => dispatch(doUserCheckEmailVerified()), checkEmailVerified: () => dispatch(doUserCheckEmailVerified()),
toast: message => dispatch(doToast({ message })),
}); });
export default connect( export default connect(

View file

@ -5,8 +5,11 @@ import UserSignOutButton from 'component/userSignOutButton';
type Props = { type Props = {
email: string, email: string,
isReturningUser: boolean,
resendVerificationEmail: string => void, resendVerificationEmail: string => void,
resendingEmail: boolean,
checkEmailVerified: () => void, checkEmailVerified: () => void,
toast: string => void,
user: { user: {
has_verified_email: boolean, has_verified_email: boolean,
}, },
@ -37,7 +40,9 @@ class UserEmailVerify extends React.PureComponent<Props> {
} }
handleResendVerificationEmail() { handleResendVerificationEmail() {
this.props.resendVerificationEmail(this.props.email); const { email, resendVerificationEmail, toast } = this.props;
resendVerificationEmail(email);
toast(__('New email sent.'));
} }
checkIfVerified() { checkIfVerified() {
@ -48,21 +53,25 @@ class UserEmailVerify extends React.PureComponent<Props> {
emailVerifyCheckInterval: ?IntervalID; emailVerifyCheckInterval: ?IntervalID;
render() { render() {
const { email } = this.props; const { email, isReturningUser, resendingEmail } = this.props;
return ( return (
<React.Fragment> <React.Fragment>
<h1 className="section__title--large">{__('Confirm Your Email')}</h1> <h1 className="section__title--large">{isReturningUser ? __('Check Your Email') : __('Confirm Your Email')}</h1>
<p className="section__subtitle"> <p className="section__subtitle">
{__('An email was sent to')} {email}. {__('Follow the link to sign in. This will update automatically.')} {__('An email was sent to %email%. Follow the link to %verify_text%. This will update automatically.', {
email,
verify_text: isReturningUser ? __('sign in') : __('verify your email'),
})}
</p> </p>
<div className="section__body section__actions"> <div className="section__body section__actions">
<Button <Button
button="primary" button="primary"
label={__('Resend Verification Email')} label={__('Resend Email')}
onClick={this.handleResendVerificationEmail} onClick={this.handleResendVerificationEmail}
disabled={resendingEmail}
/> />
<UserSignOutButton label={__('Start Over')} /> <UserSignOutButton label={__('Start Over')} />
</div> </div>

View file

@ -5,7 +5,7 @@ import Page from 'component/page';
export default function SignInPage() { export default function SignInPage() {
return ( return (
<Page fullscreen className="main--auth-page"> <Page authPage className="main--auth-page">
<UserSignIn /> <UserSignIn />
</Page> </Page>
); );

View file

@ -21,6 +21,10 @@
background-color: $lbry-teal-4; background-color: $lbry-teal-4;
} }
&:active {
background-color: $lbry-teal-3;
}
&:disabled { &:disabled {
opacity: 0.5; opacity: 0.5;
} }

View file

@ -65,10 +65,6 @@ checkbox-element input[type='checkbox'] + label {
fieldset-section { fieldset-section {
margin: 0; margin: 0;
&:not(:first-of-type) {
margin-top: var(--spacing-small);
}
input, input,
select, select,
textarea { textarea {
@ -92,6 +88,13 @@ fieldset-section {
color: $lbry-gray-3; color: $lbry-gray-3;
} }
} }
&:not(:first-of-type) {
checkbox-element,
radio-element {
margin-top: var(--spacing-small);
}
}
} }
checkbox-element { checkbox-element {
@ -141,6 +144,8 @@ checkbox-toggle {
} }
fieldset-group { fieldset-group {
margin-top: var(--spacing-small);
&.fieldset-group--smushed { &.fieldset-group--smushed {
justify-content: flex-start; justify-content: flex-start;

View file

@ -740,7 +740,7 @@
"Follow New Tags": "Follow New Tags", "Follow New Tags": "Follow New Tags",
"Sign In": "Sign In", "Sign In": "Sign In",
"Go Back": "Go Back", "Go Back": "Go Back",
"Welcome To LBRY": "Welcome To LBRY", "Sign In To LBRY": "Sign In To LBRY",
"Create a new account or sign in.": "Create a new account or sign in.", "Create a new account or sign in.": "Create a new account or sign in.",
"hotstuff_96@hotmail.com": "hotstuff_96@hotmail.com", "hotstuff_96@hotmail.com": "hotstuff_96@hotmail.com",
"Terms of Service": "Terms of Service", "Terms of Service": "Terms of Service",

View file

@ -860,15 +860,10 @@
prop-types "^15.6.2" prop-types "^15.6.2"
scheduler "^0.15.0" scheduler "^0.15.0"
"@lbry/color@^1.0.2": "@lbry/components@^2.8.0":
version "1.1.1" version "2.8.0"
resolved "https://registry.yarnpkg.com/@lbry/color/-/color-1.1.1.tgz#b80ec25fb515d436129332b20c767c5a7014ac09" resolved "https://registry.yarnpkg.com/@lbry/components/-/components-2.8.0.tgz#960b013d89992ce32c20cd916267e3962a765487"
integrity sha512-BdxqWmm84WYOd1ZsPruJiGr7WBEophVfJKg7oywFOAMb0h6ERe4Idx1ceweMSvCOyPlR5GAhil9Gvk70SBFdxQ== integrity sha512-WPapGe+8KFdc8A1URvxQ95IdsqmaUECSEREfbFT7ZKT117PUEJfS+ze+B+Hj7/iExibtCF5YIbsO3hrFc7jWZQ==
"@lbry/components@^2.7.4":
version "2.7.4"
resolved "https://registry.yarnpkg.com/@lbry/components/-/components-2.7.4.tgz#3a633725137ffcb1f081f71dbf5397d66bf444cd"
integrity sha512-X0QBpdlsPntmuC/COolx/rOmUxa5XeHDjdcpl79Db3Y3upkEYwwtvl+S/OkkPbdxouoJfZTB4QqQuGkNi1xGdw==
"@mapbox/hast-util-table-cell-style@^0.1.3": "@mapbox/hast-util-table-cell-style@^0.1.3":
version "0.1.3" version "0.1.3"