add createChannel to first run flow
This commit is contained in:
parent
3311e1af1f
commit
30d396fce9
28 changed files with 589 additions and 254 deletions
24
flow-typed/user.js
vendored
Normal file
24
flow-typed/user.js
vendored
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
// @flow
|
||||||
|
|
||||||
|
// Move this to lbryinc
|
||||||
|
declare type User = {
|
||||||
|
created_at: string,
|
||||||
|
family_name: ?string,
|
||||||
|
given_name: ?string,
|
||||||
|
groups: Array<string>,
|
||||||
|
has_verified_email: boolean,
|
||||||
|
id: number,
|
||||||
|
invite_reward_claimed: boolean,
|
||||||
|
invited_at: ?number,
|
||||||
|
invited_by_id: number,
|
||||||
|
invites_remaining: number,
|
||||||
|
is_email_enabled: boolean,
|
||||||
|
is_identity_verified: boolean,
|
||||||
|
is_reward_approved: boolean,
|
||||||
|
language: string,
|
||||||
|
manual_approval_user_id: ?number,
|
||||||
|
primary_email: string,
|
||||||
|
reward_status_change_trigger: string,
|
||||||
|
updated_at: string,
|
||||||
|
youtube_channels: ?Array<string>,
|
||||||
|
};
|
|
@ -6,9 +6,12 @@ import { Lbry, buildURI, parseURI } from 'lbry-redux';
|
||||||
import Router from 'component/router/index';
|
import Router from 'component/router/index';
|
||||||
import ModalRouter from 'modal/modalRouter';
|
import ModalRouter from 'modal/modalRouter';
|
||||||
import ReactModal from 'react-modal';
|
import ReactModal from 'react-modal';
|
||||||
|
<<<<<<< HEAD
|
||||||
import SideBar from 'component/sideBar';
|
import SideBar from 'component/sideBar';
|
||||||
import Header from 'component/header';
|
import Header from 'component/header';
|
||||||
import Button from 'component/button';
|
import Button from 'component/button';
|
||||||
|
=======
|
||||||
|
>>>>>>> add createChannel to first run flow
|
||||||
import { openContextMenu } from 'util/context-menu';
|
import { openContextMenu } from 'util/context-menu';
|
||||||
import useKonamiListener from 'util/enhanced-layout';
|
import useKonamiListener from 'util/enhanced-layout';
|
||||||
import Yrbl from 'component/yrbl';
|
import Yrbl from 'component/yrbl';
|
||||||
|
@ -111,13 +114,7 @@ function App(props: Props) {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={MAIN_WRAPPER_CLASS} ref={appRef} onContextMenu={e => openContextMenu(e)}>
|
<div className={MAIN_WRAPPER_CLASS} ref={appRef} onContextMenu={e => openContextMenu(e)}>
|
||||||
<Header />
|
<Router />
|
||||||
|
|
||||||
<div className="main-wrapper__inner">
|
|
||||||
<Router />
|
|
||||||
<SideBar />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<ModalRouter />
|
<ModalRouter />
|
||||||
<FileViewer pageUri={uri} />
|
<FileViewer pageUri={uri} />
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
// @flow
|
// @flow
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
import { formatCredits, formatFullPrice } from 'util/format-credits';
|
import { formatCredits, formatFullPrice } from 'lbry-redux';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
amount: number,
|
amount: number,
|
||||||
|
@ -39,7 +39,7 @@ class CreditAmount extends React.PureComponent<Props> {
|
||||||
formattedAmount =
|
formattedAmount =
|
||||||
amount > 0 && amount < minimumRenderableAmount
|
amount > 0 && amount < minimumRenderableAmount
|
||||||
? `<${minimumRenderableAmount}`
|
? `<${minimumRenderableAmount}`
|
||||||
: formatCredits(amount, precision);
|
: formatCredits(amount, precision, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
let amountText;
|
let amountText;
|
||||||
|
|
|
@ -1,16 +1,15 @@
|
||||||
import * as SETTINGS from 'constants/settings';
|
import * as SETTINGS from 'constants/settings';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { selectBalance, SETTINGS as LBRY_REDUX_SETTINGS } from 'lbry-redux';
|
import { selectBalance, formatCredits } from 'lbry-redux';
|
||||||
import { selectUserEmail } from 'lbryinc';
|
import { selectUserEmail } from 'lbryinc';
|
||||||
import { formatCredits } from 'util/format-credits';
|
|
||||||
import { doSetClientSetting } from 'redux/actions/settings';
|
import { doSetClientSetting } from 'redux/actions/settings';
|
||||||
import { makeSelectClientSetting } from 'redux/selectors/settings';
|
import { makeSelectClientSetting } from 'redux/selectors/settings';
|
||||||
import Header from './view';
|
import Header from './view';
|
||||||
|
|
||||||
const select = state => ({
|
const select = state => ({
|
||||||
balance: selectBalance(state),
|
balance: selectBalance(state),
|
||||||
language: makeSelectClientSetting(LBRY_REDUX_SETTINGS.LANGUAGE)(state), // trigger redraw on language change
|
language: makeSelectClientSetting(SETTINGS.LANGUAGE)(state), // trigger redraw on language change
|
||||||
roundedBalance: formatCredits(selectBalance(state) || 0, 2),
|
roundedBalance: formatCredits(selectBalance(state) || 0, 2, true),
|
||||||
currentTheme: makeSelectClientSetting(SETTINGS.THEME)(state),
|
currentTheme: makeSelectClientSetting(SETTINGS.THEME)(state),
|
||||||
automaticDarkModeEnabled: makeSelectClientSetting(SETTINGS.AUTOMATIC_DARK_MODE_ENABLED)(state),
|
automaticDarkModeEnabled: makeSelectClientSetting(SETTINGS.AUTOMATIC_DARK_MODE_ENABLED)(state),
|
||||||
hideBalance: makeSelectClientSetting(SETTINGS.HIDE_BALANCE)(state),
|
hideBalance: makeSelectClientSetting(SETTINGS.HIDE_BALANCE)(state),
|
||||||
|
|
|
@ -4,11 +4,13 @@ import * as SETTINGS from 'constants/settings';
|
||||||
import * as PAGES from 'constants/pages';
|
import * as PAGES from 'constants/pages';
|
||||||
import React, { Fragment } from 'react';
|
import React, { Fragment } from 'react';
|
||||||
import { withRouter } from 'react-router';
|
import { withRouter } from 'react-router';
|
||||||
|
import classnames from 'classnames';
|
||||||
import Button from 'component/button';
|
import Button from 'component/button';
|
||||||
import LbcSymbol from 'component/common/lbc-symbol';
|
import LbcSymbol from 'component/common/lbc-symbol';
|
||||||
import WunderBar from 'component/wunderbar';
|
import WunderBar from 'component/wunderbar';
|
||||||
import Icon from 'component/common/icon';
|
import Icon from 'component/common/icon';
|
||||||
import { Menu, MenuList, MenuButton, MenuItem } from '@reach/menu-button';
|
import { Menu, MenuList, MenuButton, MenuItem } from '@reach/menu-button';
|
||||||
|
import Tooltip from 'component/common/tooltip';
|
||||||
|
|
||||||
// Move this into jessops password util
|
// Move this into jessops password util
|
||||||
import cookie from 'cookie';
|
import cookie from 'cookie';
|
||||||
|
@ -38,6 +40,7 @@ type Props = {
|
||||||
setClientSetting: (string, boolean | string) => void,
|
setClientSetting: (string, boolean | string) => void,
|
||||||
hideBalance: boolean,
|
hideBalance: boolean,
|
||||||
email: ?string,
|
email: ?string,
|
||||||
|
minimal: boolean,
|
||||||
};
|
};
|
||||||
|
|
||||||
const Header = (props: Props) => {
|
const Header = (props: Props) => {
|
||||||
|
@ -49,8 +52,12 @@ const Header = (props: Props) => {
|
||||||
automaticDarkModeEnabled,
|
automaticDarkModeEnabled,
|
||||||
hideBalance,
|
hideBalance,
|
||||||
email,
|
email,
|
||||||
|
minimal,
|
||||||
} = props;
|
} = props;
|
||||||
|
const showUpgradeButton = autoUpdateDownloaded || (process.platform === 'linux' && isUpgradeAvailable);
|
||||||
const isAuthenticated = Boolean(email);
|
const isAuthenticated = Boolean(email);
|
||||||
|
// Auth is optional in the desktop app
|
||||||
|
const showFullHeader = IS_WEB ? isAuthenticated : true;
|
||||||
|
|
||||||
function handleThemeToggle() {
|
function handleThemeToggle() {
|
||||||
if (automaticDarkModeEnabled) {
|
if (automaticDarkModeEnabled) {
|
||||||
|
@ -64,115 +71,161 @@ const Header = (props: Props) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function getAccountTitle() {
|
|
||||||
if (roundedBalance > 0 && !hideBalance) {
|
|
||||||
return (
|
|
||||||
<React.Fragment>
|
|
||||||
{roundedBalance} <LbcSymbol />
|
|
||||||
</React.Fragment>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return __('Account');
|
|
||||||
}
|
|
||||||
|
|
||||||
function signOut() {
|
function signOut() {
|
||||||
// Replace this with actual clearUser function
|
// Replace this with actual clearUser function
|
||||||
window.store.dispatch({ type: 'USER_FETCH_FAILURE' });
|
window.store.dispatch({ type: 'USER_FETCH_FAILURE' });
|
||||||
deleteAuthToken();
|
deleteAuthToken();
|
||||||
|
location.reload();
|
||||||
|
}
|
||||||
|
|
||||||
|
const accountMenuButtons = [
|
||||||
|
{
|
||||||
|
path: `/$/account`,
|
||||||
|
icon: ICONS.OVERVIEW,
|
||||||
|
label: __('Overview'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: `/$/rewards`,
|
||||||
|
icon: ICONS.FEATURED,
|
||||||
|
label: __('Rewards'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: `/$/wallet`,
|
||||||
|
icon: ICONS.WALLET,
|
||||||
|
label: __('Wallet'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: `/$/publish`,
|
||||||
|
icon: ICONS.PUBLISH,
|
||||||
|
label: __('Publish'),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
if (!isAuthenticated) {
|
||||||
|
accountMenuButtons.push({
|
||||||
|
onClick: signOut,
|
||||||
|
icon: ICONS.PUBLISH,
|
||||||
|
label: __('Publish'),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<header className="header">
|
<header className={classnames('header', { 'header--minimal': minimal })}>
|
||||||
<div className="header__contents">
|
<div className="header__contents">
|
||||||
<div className="header__navigation">
|
<div className="header__navigation">
|
||||||
|
{/* @if TARGET='app' */}
|
||||||
|
{!minimal && (
|
||||||
|
<div className="header__navigation-arrows">
|
||||||
|
<Button
|
||||||
|
className="header__navigation-item header__navigation-item--back"
|
||||||
|
description={__('Navigate back')}
|
||||||
|
onClick={() => history.goBack()}
|
||||||
|
icon={ICONS.ARROW_LEFT}
|
||||||
|
iconSize={18}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
className="header__navigation-item header__navigation-item--forward"
|
||||||
|
description={__('Navigate forward')}
|
||||||
|
onClick={() => history.goForward()}
|
||||||
|
icon={ICONS.ARROW_RIGHT}
|
||||||
|
iconSize={18}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{/* @endif */}
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
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="/"
|
navigate="/"
|
||||||
/>
|
/>
|
||||||
{/* @if TARGET='app' */}
|
|
||||||
<div className="header__navigation-arrows">
|
|
||||||
<Button
|
|
||||||
className="header__navigation-item header__navigation-item--back"
|
|
||||||
description={__('Navigate back')}
|
|
||||||
onClick={() => history.goBack()}
|
|
||||||
icon={ICONS.ARROW_LEFT}
|
|
||||||
iconSize={18}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<Button
|
{!minimal && <WunderBar />}
|
||||||
className="header__navigation-item header__navigation-item--forward"
|
</div>
|
||||||
description={__('Navigate forward')}
|
|
||||||
onClick={() => history.goForward()}
|
{!minimal ? (
|
||||||
icon={ICONS.ARROW_RIGHT}
|
<div className="header__menu">
|
||||||
iconSize={18}
|
{showFullHeader ? (
|
||||||
/>
|
<Fragment>
|
||||||
|
<Menu>
|
||||||
|
<MenuButton className="header__navigation-item menu__title">
|
||||||
|
{roundedBalance} <LbcSymbol />
|
||||||
|
</MenuButton>
|
||||||
|
<MenuList className="menu__list--header">
|
||||||
|
<MenuItem className="menu__link" onSelect={() => history.push(`/$/wallet`)}>
|
||||||
|
<Icon aria-hidden icon={ICONS.WALLET} />
|
||||||
|
{__('Wallet')}
|
||||||
|
</MenuItem>
|
||||||
|
<MenuItem className="menu__link" onSelect={() => history.push(`/$/rewards`)}>
|
||||||
|
<Icon aria-hidden icon={ICONS.FEATURED} />
|
||||||
|
{__('Rewards')}
|
||||||
|
</MenuItem>
|
||||||
|
</MenuList>
|
||||||
|
</Menu>
|
||||||
|
<Menu>
|
||||||
|
<MenuButton className="header__navigation-item menu__title">
|
||||||
|
<Icon size={18} icon={ICONS.ACCOUNT} />
|
||||||
|
</MenuButton>
|
||||||
|
<MenuList className="menu__list--header">
|
||||||
|
<MenuItem
|
||||||
|
className="menu__link"
|
||||||
|
onSelect={() => history.push(isAuthenticated ? `/$/account` : `/$/auth/signup`)}
|
||||||
|
>
|
||||||
|
<Icon aria-hidden icon={ICONS.OVERVIEW} />
|
||||||
|
{__('Overview')}
|
||||||
|
</MenuItem>
|
||||||
|
|
||||||
|
<MenuItem className="menu__link" onSelect={() => history.push(`/$/publish`)}>
|
||||||
|
<Icon aria-hidden icon={ICONS.PUBLISH} />
|
||||||
|
{__('Publish')}
|
||||||
|
</MenuItem>
|
||||||
|
{isAuthenticated ? (
|
||||||
|
<MenuItem className="menu__link" onSelect={signOut}>
|
||||||
|
<Icon aria-hidden icon={ICONS.SIGN_OUT} />
|
||||||
|
{__('Sign Out')}
|
||||||
|
</MenuItem>
|
||||||
|
) : (
|
||||||
|
<MenuItem onSelect={() => {}} />
|
||||||
|
)}
|
||||||
|
</MenuList>
|
||||||
|
</Menu>
|
||||||
|
|
||||||
|
<Menu>
|
||||||
|
<MenuButton className="header__navigation-item menu__title">
|
||||||
|
<Icon size={18} icon={ICONS.SETTINGS} />
|
||||||
|
</MenuButton>
|
||||||
|
<MenuList className="menu__list--header">
|
||||||
|
<MenuItem className="menu__link" onSelect={() => history.push(`/$/settings`)}>
|
||||||
|
<Icon aria-hidden tootlip icon={ICONS.SETTINGS} />
|
||||||
|
{__('Settings')}
|
||||||
|
</MenuItem>
|
||||||
|
<MenuItem className="menu__link" onSelect={() => history.push(`/$/help`)}>
|
||||||
|
<Icon aria-hidden icon={ICONS.HELP} />
|
||||||
|
{__('Help')}
|
||||||
|
</MenuItem>
|
||||||
|
<MenuItem className="menu__link" onSelect={handleThemeToggle}>
|
||||||
|
<Icon icon={currentTheme === 'light' ? ICONS.DARK : ICONS.LIGHT} />
|
||||||
|
{currentTheme === 'light' ? 'Dark' : 'Light'}
|
||||||
|
</MenuItem>
|
||||||
|
</MenuList>
|
||||||
|
</Menu>
|
||||||
|
</Fragment>
|
||||||
|
) : (
|
||||||
|
<Fragment>
|
||||||
|
<span />
|
||||||
|
<Button navigate={`/$/${PAGES.AUTH}/signin`} button="primary" label={__('Sign In')} />
|
||||||
|
</Fragment>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
{/* @endif */}
|
) : (
|
||||||
<WunderBar />
|
<div className="header__menu">
|
||||||
</div>
|
<Tooltip label={__('Go Back')}>
|
||||||
|
<Button icon={ICONS.REMOVE} onClick={() => history.goBack()} />
|
||||||
<div className="header__menu">
|
</Tooltip>
|
||||||
{isAuthenticated ? (
|
</div>
|
||||||
<Fragment>
|
)}
|
||||||
<Menu>
|
|
||||||
<MenuButton className="header__navigation-item menu__title">
|
|
||||||
<Icon icon={ICONS.ACCOUNT} />
|
|
||||||
{getAccountTitle()}
|
|
||||||
</MenuButton>
|
|
||||||
<MenuList className="menu__list--header">
|
|
||||||
<MenuItem className="menu__link" onSelect={() => history.push(`/$/account`)}>
|
|
||||||
<Icon aria-hidden icon={ICONS.OVERVIEW} />
|
|
||||||
{__('Overview')}
|
|
||||||
</MenuItem>
|
|
||||||
<MenuItem className="menu__link" onSelect={() => history.push(`/$/rewards`)}>
|
|
||||||
<Icon aria-hidden icon={ICONS.FEATURED} />
|
|
||||||
{__('Rewards')}
|
|
||||||
</MenuItem>
|
|
||||||
<MenuItem className="menu__link" onSelect={() => history.push(`/$/wallet`)}>
|
|
||||||
<Icon aria-hidden icon={ICONS.WALLET} />
|
|
||||||
{__('Wallet')}
|
|
||||||
</MenuItem>
|
|
||||||
<MenuItem className="menu__link" onSelect={() => history.push(`/$/publish`)}>
|
|
||||||
<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>
|
|
||||||
|
|
||||||
<Menu>
|
|
||||||
<MenuButton className="header__navigation-item menu__title">
|
|
||||||
<Icon size={18} icon={ICONS.SETTINGS} />
|
|
||||||
</MenuButton>
|
|
||||||
<MenuList className="menu__list--header">
|
|
||||||
<MenuItem className="menu__link" onSelect={() => history.push(`/$/settings`)}>
|
|
||||||
<Icon aria-hidden tootlip icon={ICONS.SETTINGS} />
|
|
||||||
{__('Settings')}
|
|
||||||
</MenuItem>
|
|
||||||
<MenuItem className="menu__link" onSelect={() => history.push(`/$/help`)}>
|
|
||||||
<Icon aria-hidden icon={ICONS.HELP} />
|
|
||||||
{__('Help')}
|
|
||||||
</MenuItem>
|
|
||||||
<MenuItem className="menu__link" onSelect={handleThemeToggle}>
|
|
||||||
<Icon icon={currentTheme === 'light' ? ICONS.DARK : ICONS.LIGHT} />
|
|
||||||
{currentTheme === 'light' ? 'Dark' : 'Light'}
|
|
||||||
</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>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,16 +1,50 @@
|
||||||
// @flow
|
// @flow
|
||||||
import * as React from 'react';
|
import type { Node } from 'react';
|
||||||
|
import * as ICONS from 'constants/icons';
|
||||||
|
import React, { Fragment } from 'react';
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
|
import SideBar from 'component/sideBar';
|
||||||
|
import Header from 'component/header';
|
||||||
|
import Button from 'component/button';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
children: React.Node | Array<React.Node>,
|
children: Node | Array<Node>,
|
||||||
className: ?string,
|
className: ?string,
|
||||||
|
autoUpdateDownloaded: boolean,
|
||||||
|
isUpgradeAvailable: boolean,
|
||||||
|
fullscreen: boolean,
|
||||||
|
doDownloadUpgradeRequested: () => void,
|
||||||
};
|
};
|
||||||
|
|
||||||
function Page(props: Props) {
|
function Page(props: Props) {
|
||||||
const { children, className } = props;
|
const {
|
||||||
|
children,
|
||||||
|
className,
|
||||||
|
fullscreen = false,
|
||||||
|
autoUpdateDownloaded,
|
||||||
|
isUpgradeAvailable,
|
||||||
|
doDownloadUpgradeRequested,
|
||||||
|
} = props;
|
||||||
|
const showUpgradeButton = autoUpdateDownloaded || (process.platform === 'linux' && isUpgradeAvailable);
|
||||||
|
|
||||||
return <main className={classnames('main', className)}>{children}</main>;
|
return (
|
||||||
|
<Fragment>
|
||||||
|
<Header minimal={fullscreen} />
|
||||||
|
<div className={classnames('main-wrapper__inner')}>
|
||||||
|
{/* @if TARGET='app' */}
|
||||||
|
{showUpgradeButton && (
|
||||||
|
<div className="main__status">
|
||||||
|
{__('Upgrade is ready')}
|
||||||
|
<Button button="alt" icon={ICONS.DOWNLOAD} label={__('Install now')} onClick={doDownloadUpgradeRequested} />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{/* @endif */}
|
||||||
|
|
||||||
|
<main className={classnames('main', className)}>{children}</main>
|
||||||
|
{!fullscreen && <SideBar />}
|
||||||
|
</div>
|
||||||
|
</Fragment>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Page;
|
export default Page;
|
||||||
|
|
|
@ -21,10 +21,9 @@ import WalletPage from 'page/wallet';
|
||||||
import NavigationHistory from 'page/navigationHistory';
|
import NavigationHistory from 'page/navigationHistory';
|
||||||
import TagsPage from 'page/tags';
|
import TagsPage from 'page/tags';
|
||||||
import FollowingPage from 'page/following';
|
import FollowingPage from 'page/following';
|
||||||
import ListBlocked from 'page/listBlocked';
|
import ListBlockedPage from 'page/listBlocked';
|
||||||
import FourOhFourPage from 'page/fourOhFour';
|
import FourOhFourPage from 'page/fourOhFour';
|
||||||
|
import SignInPage from 'page/signIn';
|
||||||
import UserEmail from 'component/userEmail';
|
|
||||||
|
|
||||||
// Tell the browser we are handling scroll restoration
|
// Tell the browser we are handling scroll restoration
|
||||||
if ('scrollRestoration' in history) {
|
if ('scrollRestoration' in history) {
|
||||||
|
@ -36,14 +35,6 @@ type Props = {
|
||||||
location: { pathname: string, search: string },
|
location: { pathname: string, search: string },
|
||||||
};
|
};
|
||||||
|
|
||||||
function SignInPage() {
|
|
||||||
return <UserEmail />;
|
|
||||||
}
|
|
||||||
|
|
||||||
function SignUpPage() {
|
|
||||||
return <h1>Sign In</h1>;
|
|
||||||
}
|
|
||||||
|
|
||||||
function AppRouter(props: Props) {
|
function AppRouter(props: Props) {
|
||||||
const { currentScroll } = props;
|
const { currentScroll } = props;
|
||||||
|
|
||||||
|
@ -57,7 +48,6 @@ function AppRouter(props: Props) {
|
||||||
<Route path={`/$/${PAGES.DISCOVER}`} exact component={DiscoverPage} />
|
<Route path={`/$/${PAGES.DISCOVER}`} exact component={DiscoverPage} />
|
||||||
<Route path={`/$/${PAGES.AUTH}`} exact component={AuthPage} />
|
<Route path={`/$/${PAGES.AUTH}`} exact component={AuthPage} />
|
||||||
<Route path={`/$/${PAGES.AUTH}/signin`} exact component={SignInPage} />
|
<Route path={`/$/${PAGES.AUTH}/signin`} exact component={SignInPage} />
|
||||||
<Route path={`/$/${PAGES.AUTH}/signup`} exact component={SignUpPage} />
|
|
||||||
<Route path={`/$/${PAGES.INVITE}`} exact component={InvitePage} />
|
<Route path={`/$/${PAGES.INVITE}`} exact component={InvitePage} />
|
||||||
<Route path={`/$/${PAGES.DOWNLOADED}`} exact component={FileListDownloaded} />
|
<Route path={`/$/${PAGES.DOWNLOADED}`} exact component={FileListDownloaded} />
|
||||||
<Route path={`/$/${PAGES.PUBLISHED}`} exact component={FileListPublished} />
|
<Route path={`/$/${PAGES.PUBLISHED}`} exact component={FileListPublished} />
|
||||||
|
@ -74,7 +64,7 @@ function AppRouter(props: Props) {
|
||||||
<Route path={`/$/${PAGES.TAGS}`} exact component={TagsPage} />
|
<Route path={`/$/${PAGES.TAGS}`} exact component={TagsPage} />
|
||||||
<Route path={`/$/${PAGES.FOLLOWING}`} exact component={FollowingPage} />
|
<Route path={`/$/${PAGES.FOLLOWING}`} exact component={FollowingPage} />
|
||||||
<Route path={`/$/${PAGES.WALLET}`} exact component={WalletPage} />
|
<Route path={`/$/${PAGES.WALLET}`} exact component={WalletPage} />
|
||||||
<Route path={`/$/${PAGES.BLOCKED}`} exact component={ListBlocked} />
|
<Route path={`/$/${PAGES.BLOCKED}`} exact component={ListBlockedPage} />
|
||||||
{/* Below need to go at the end to make sure we don't match any of our pages first */}
|
{/* Below need to go at the end to make sure we don't match any of our pages first */}
|
||||||
<Route path="/:claimName" exact component={ShowPage} />
|
<Route path="/:claimName" exact component={ShowPage} />
|
||||||
<Route path="/:claimName/:streamName" exact component={ShowPage} />
|
<Route path="/:claimName/:streamName" exact component={ShowPage} />
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
// @flow
|
// @flow
|
||||||
import * as PAGES from 'constants/pages';
|
import * as PAGES from 'constants/pages';
|
||||||
import * as ICONS from 'constants/icons';
|
import * as ICONS from 'constants/icons';
|
||||||
import React from 'react';
|
import React, { Fragment } from 'react';
|
||||||
import Button from 'component/button';
|
import Button from 'component/button';
|
||||||
import Tag from 'component/tag';
|
import Tag from 'component/tag';
|
||||||
import StickyBox from 'react-sticky-box/dist/esnext';
|
import StickyBox from 'react-sticky-box/dist/esnext';
|
||||||
|
@ -14,6 +14,7 @@ type Props = {
|
||||||
|
|
||||||
function SideBar(props: Props) {
|
function SideBar(props: Props) {
|
||||||
const { subscriptions, followedTags, email } = props;
|
const { subscriptions, followedTags, email } = props;
|
||||||
|
const showSideBar = IS_WEB ? Boolean(email) : true;
|
||||||
|
|
||||||
function buildLink(path, label, icon, guide) {
|
function buildLink(path, label, icon, guide) {
|
||||||
return {
|
return {
|
||||||
|
@ -26,7 +27,7 @@ function SideBar(props: Props) {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<StickyBox offsetTop={100} offsetBottom={20}>
|
<StickyBox offsetTop={100} offsetBottom={20}>
|
||||||
{email ? (
|
{showSideBar ? (
|
||||||
<nav className="navigation">
|
<nav className="navigation">
|
||||||
<ul className="navigation-links">
|
<ul className="navigation-links">
|
||||||
{[
|
{[
|
||||||
|
@ -41,34 +42,47 @@ function SideBar(props: Props) {
|
||||||
{
|
{
|
||||||
...buildLink(PAGES.PUBLISHED, __('Publishes'), ICONS.PUBLISH),
|
...buildLink(PAGES.PUBLISHED, __('Publishes'), ICONS.PUBLISH),
|
||||||
},
|
},
|
||||||
{
|
|
||||||
...buildLink(PAGES.FOLLOWING, __('Customize'), ICONS.EDIT),
|
|
||||||
},
|
|
||||||
].map(linkProps => (
|
].map(linkProps => (
|
||||||
<li key={linkProps.label}>
|
<li key={linkProps.label}>
|
||||||
<Button {...linkProps} className="navigation-link" activeClass="navigation-link--active" />
|
<Button {...linkProps} className="navigation-link" activeClass="navigation-link--active" />
|
||||||
</li>
|
</li>
|
||||||
))}
|
))}
|
||||||
</ul>
|
</ul>
|
||||||
<ul className="navigation-links tags--vertical">
|
|
||||||
{followedTags.map(({ name }, key) => (
|
{email ? (
|
||||||
<li className="navigation-link__wrapper" key={name}>
|
<Fragment>
|
||||||
<Tag navigate={`/$/tags?t${name}`} name={name} />
|
<Button
|
||||||
</li>
|
navigate={`/$/${PAGES.FOLLOWING}`}
|
||||||
))}
|
label={__('Customize')}
|
||||||
</ul>
|
icon={ICONS.EDIT}
|
||||||
<ul className="navigation-links--small">
|
className="navigation-link"
|
||||||
{subscriptions.map(({ uri, channelName }, index) => (
|
activeClass="navigation-link--active"
|
||||||
<li key={uri} className="navigation-link__wrapper">
|
/>
|
||||||
<Button
|
<ul className="navigation-links tags--vertical">
|
||||||
navigate={uri}
|
{followedTags.map(({ name }, key) => (
|
||||||
label={channelName}
|
<li className="navigation-link__wrapper" key={name}>
|
||||||
className="navigation-link"
|
<Tag navigate={`/$/tags?t${name}`} name={name} />
|
||||||
activeClass="navigation-link--active"
|
</li>
|
||||||
/>
|
))}
|
||||||
</li>
|
</ul>
|
||||||
))}
|
<ul className="navigation-links--small">
|
||||||
</ul>
|
{subscriptions.map(({ uri, channelName }, index) => (
|
||||||
|
<li key={uri} className="navigation-link__wrapper">
|
||||||
|
<Button
|
||||||
|
navigate={uri}
|
||||||
|
label={channelName}
|
||||||
|
className="navigation-link"
|
||||||
|
activeClass="navigation-link--active"
|
||||||
|
/>
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
</Fragment>
|
||||||
|
) : (
|
||||||
|
<div className="navigation--placeholder" style={{ height: '20vh', marginTop: '1rem', padding: '1rem' }}>
|
||||||
|
Something about logging up to customize here
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</nav>
|
</nav>
|
||||||
) : (
|
) : (
|
||||||
<div className="navigation--placeholder" />
|
<div className="navigation--placeholder" />
|
||||||
|
|
|
@ -1,43 +1,23 @@
|
||||||
// @flow
|
// @flow
|
||||||
import * as React 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 { Lbryio } from 'lbryinc';
|
||||||
import analytics from 'analytics';
|
import analytics from 'analytics';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
cancelButton: React.Node,
|
|
||||||
errorMessage: ?string,
|
errorMessage: ?string,
|
||||||
isPending: boolean,
|
isPending: boolean,
|
||||||
addUserEmail: string => void,
|
addUserEmail: string => void,
|
||||||
};
|
};
|
||||||
|
|
||||||
type State = {
|
function UserEmailNew(props: Props) {
|
||||||
email: string,
|
const { errorMessage, isPending, addUserEmail } = props;
|
||||||
};
|
const [newEmail, setEmail] = useState('');
|
||||||
|
const [sync, setSync] = useState(false);
|
||||||
|
|
||||||
class UserEmailNew extends React.PureComponent<Props, State> {
|
function handleSubmit() {
|
||||||
constructor() {
|
addUserEmail(newEmail);
|
||||||
super();
|
|
||||||
|
|
||||||
this.state = {
|
|
||||||
email: '',
|
|
||||||
};
|
|
||||||
|
|
||||||
(this: any).handleSubmit = this.handleSubmit.bind(this);
|
|
||||||
(this: any).handleEmailChanged = this.handleEmailChanged.bind(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
handleEmailChanged(event: SyntheticInputEvent<*>) {
|
|
||||||
this.setState({
|
|
||||||
email: event.target.value,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
handleSubmit() {
|
|
||||||
const { email } = this.state;
|
|
||||||
const { addUserEmail } = this.props;
|
|
||||||
addUserEmail(email);
|
|
||||||
analytics.emailProvidedEvent();
|
analytics.emailProvidedEvent();
|
||||||
|
|
||||||
// @if TARGET='web'
|
// @if TARGET='web'
|
||||||
|
@ -45,40 +25,28 @@ class UserEmailNew extends React.PureComponent<Props, State> {
|
||||||
// @endif
|
// @endif
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
return (
|
||||||
const { cancelButton, errorMessage, isPending } = this.props;
|
<Form onSubmit={handleSubmit}>
|
||||||
|
<FormField
|
||||||
|
type="email"
|
||||||
|
id="sign_up_email"
|
||||||
|
label={__('Email')}
|
||||||
|
value={newEmail}
|
||||||
|
error={errorMessage}
|
||||||
|
onChange={e => setEmail(e.target.value)}
|
||||||
|
/>
|
||||||
|
<FormField
|
||||||
|
type="checkbox"
|
||||||
|
id="sign_up_sync"
|
||||||
|
label={__('Sync my bidnezz on this device')}
|
||||||
|
helper={__('Maybe some additional text with this field')}
|
||||||
|
checked={sync}
|
||||||
|
onChange={() => setSync(!sync)}
|
||||||
|
/>
|
||||||
|
|
||||||
return (
|
<Button button="primary" type="submit" label={__('Continue')} disabled={isPending} />
|
||||||
<React.Fragment>
|
</Form>
|
||||||
<h2 className="card__title">{__('Verify Your Email')}</h2>
|
);
|
||||||
<p className="card__subtitle">
|
|
||||||
{/* @if TARGET='app' */}
|
|
||||||
{__("We'll let you know about LBRY updates, security issues, and great new content.")}
|
|
||||||
{/* @endif */}
|
|
||||||
{/* @if TARGET='web' */}
|
|
||||||
{__('Stay up to date with lbry.tv and be the first to know about the progress we make.')}
|
|
||||||
{/* @endif */}
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<Form onSubmit={this.handleSubmit}>
|
|
||||||
<FormField
|
|
||||||
type="email"
|
|
||||||
label="Email"
|
|
||||||
placeholder="youremail@example.org"
|
|
||||||
name="email"
|
|
||||||
value={this.state.email}
|
|
||||||
error={errorMessage}
|
|
||||||
onChange={this.handleEmailChanged}
|
|
||||||
inputButton={
|
|
||||||
<Button type="submit" button="inverse" label="Submit" disabled={isPending || !this.state.email} />
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</Form>
|
|
||||||
<div className="card__actions">{cancelButton}</div>
|
|
||||||
<p className="help">{__('Your email address will never be sold and you can unsubscribe at any time.')}</p>
|
|
||||||
</React.Fragment>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default UserEmailNew;
|
export default UserEmailNew;
|
||||||
|
|
|
@ -18,7 +18,7 @@ function UserEmailResetButton(props: Props) {
|
||||||
}
|
}
|
||||||
: { href: 'https://lbry.com/faq/how-to-change-email' };
|
: { href: 'https://lbry.com/faq/how-to-change-email' };
|
||||||
|
|
||||||
return <Button button={button} label={__('Change')} {...buttonsProps} />;
|
return <Button button={button} label={__('Change... fix me sean')} {...buttonsProps} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default UserEmailResetButton;
|
export default UserEmailResetButton;
|
||||||
|
|
|
@ -24,7 +24,8 @@ class UserEmailVerify extends React.PureComponent<Props> {
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidUpdate() {
|
componentDidUpdate() {
|
||||||
if (this.emailVerifyCheckInterval && this.props.user.has_verified_email) {
|
const { user } = this.props;
|
||||||
|
if (this.emailVerifyCheckInterval && user && user.has_verified_email) {
|
||||||
clearInterval(this.emailVerifyCheckInterval);
|
clearInterval(this.emailVerifyCheckInterval);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -51,8 +52,6 @@ class UserEmailVerify extends React.PureComponent<Props> {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
<h2 className="card__title">{__('Waiting For Verification')}</h2>
|
|
||||||
|
|
||||||
<p className="card__subtitle">
|
<p className="card__subtitle">
|
||||||
{__('An email was sent to')} {email}.{' '}
|
{__('An email was sent to')} {email}.{' '}
|
||||||
{__('Follow the link and you will be good to go. This will update automatically.')}
|
{__('Follow the link and you will be good to go. This will update automatically.')}
|
||||||
|
@ -61,7 +60,7 @@ class UserEmailVerify extends React.PureComponent<Props> {
|
||||||
<div className="card__actions">
|
<div className="card__actions">
|
||||||
<Button
|
<Button
|
||||||
button="primary"
|
button="primary"
|
||||||
label={__('Resend verification email')}
|
label={__('Resend Verification Email')}
|
||||||
onClick={this.handleResendVerificationEmail}
|
onClick={this.handleResendVerificationEmail}
|
||||||
/>
|
/>
|
||||||
<UserEmailResetButton />
|
<UserEmailResetButton />
|
||||||
|
|
28
src/ui/component/userFirstChannel/index.js
Normal file
28
src/ui/component/userFirstChannel/index.js
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
import { selectUser, selectEmailToVerify, rewards as REWARD_TYPES, doClaimRewardType } from 'lbryinc';
|
||||||
|
import { doCreateChannel, selectCreatingChannel, selectMyChannelClaims } from 'lbry-redux';
|
||||||
|
import UserSignUp from './view';
|
||||||
|
|
||||||
|
const select = state => ({
|
||||||
|
email: selectEmailToVerify(state),
|
||||||
|
user: selectUser(state),
|
||||||
|
creatingChannel: selectCreatingChannel(state),
|
||||||
|
channels: selectMyChannelClaims(state),
|
||||||
|
});
|
||||||
|
|
||||||
|
const perform = dispatch => ({
|
||||||
|
createChannel: (name, amount) => dispatch(doCreateChannel(name, amount)),
|
||||||
|
claimReward: cb =>
|
||||||
|
dispatch(
|
||||||
|
doClaimRewardType(REWARD_TYPES.TYPE_REWARD_CODE, {
|
||||||
|
notifyError: true,
|
||||||
|
successCallback: cb,
|
||||||
|
params: { code: 'sean-test' },
|
||||||
|
})
|
||||||
|
),
|
||||||
|
});
|
||||||
|
|
||||||
|
export default connect(
|
||||||
|
select,
|
||||||
|
perform
|
||||||
|
)(UserSignUp);
|
65
src/ui/component/userFirstChannel/view.jsx
Normal file
65
src/ui/component/userFirstChannel/view.jsx
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
// @flow
|
||||||
|
import React, { useState } from 'react';
|
||||||
|
import { isNameValid } from 'lbry-redux';
|
||||||
|
import Button from 'component/button';
|
||||||
|
import { Form, FormField } from 'component/common/form';
|
||||||
|
|
||||||
|
const DEFAULT_BID_FOR_FIRST_CHANNEL = 0.1;
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
createChannel: (string, number) => void,
|
||||||
|
claimReward: (() => void) => void,
|
||||||
|
creatingChannel: boolean,
|
||||||
|
};
|
||||||
|
|
||||||
|
function UserFirstChannel(props: Props) {
|
||||||
|
const { createChannel, creatingChannel, claimReward } = props;
|
||||||
|
const [channel, setChannel] = useState('');
|
||||||
|
const [nameError, setNameError] = useState();
|
||||||
|
|
||||||
|
function handleCreateChannel() {
|
||||||
|
claimReward(() => {
|
||||||
|
createChannel(`@${channel}`, DEFAULT_BID_FOR_FIRST_CHANNEL);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleChannelChange(e) {
|
||||||
|
const { value } = e.target;
|
||||||
|
setChannel(value);
|
||||||
|
if (!isNameValid(value, false)) {
|
||||||
|
setNameError(__('LBRY names cannot contain spaces or reserved symbols ($#@;/"<>%{}|^~[]`)'));
|
||||||
|
} else {
|
||||||
|
setNameError();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Form onSubmit={handleCreateChannel}>
|
||||||
|
<h1 className="card__title--large">{__('Choose Your Channel Name')}</h1>
|
||||||
|
<p className="card__subtitle">
|
||||||
|
{__("Normally this would cost LBRY credits, but we're hooking you up for your first one.")}
|
||||||
|
</p>
|
||||||
|
<fieldset-group class="fieldset-group--smushed fieldset-group--disabled-prefix">
|
||||||
|
<fieldset-section>
|
||||||
|
<label htmlFor="auth_first_channel">
|
||||||
|
{nameError ? <span className="error-text">{nameError}</span> : __('Your Channel')}
|
||||||
|
</label>
|
||||||
|
<div className="form-field__prefix">@</div>
|
||||||
|
</fieldset-section>
|
||||||
|
|
||||||
|
<FormField type="text" name="auth_first_channel" value={channel} onChange={handleChannelChange} />
|
||||||
|
</fieldset-group>
|
||||||
|
<div className="card__actions">
|
||||||
|
<Button
|
||||||
|
button="primary"
|
||||||
|
type="submit"
|
||||||
|
disabled={nameError || !channel || creatingChannel}
|
||||||
|
label={__('Create')}
|
||||||
|
onClick={handleCreateChannel}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</Form>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default UserFirstChannel;
|
27
src/ui/component/userSignIn/index.js
Normal file
27
src/ui/component/userSignIn/index.js
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
import {
|
||||||
|
selectEmailToVerify,
|
||||||
|
doUserResendVerificationEmail,
|
||||||
|
doUserCheckEmailVerified,
|
||||||
|
selectUser,
|
||||||
|
doFetchAccessToken,
|
||||||
|
selectAccessToken,
|
||||||
|
} from 'lbryinc';
|
||||||
|
import UserSignUp from './view';
|
||||||
|
|
||||||
|
const select = state => ({
|
||||||
|
email: selectEmailToVerify(state),
|
||||||
|
user: selectUser(state),
|
||||||
|
accessToken: selectAccessToken(state),
|
||||||
|
});
|
||||||
|
|
||||||
|
const perform = dispatch => ({
|
||||||
|
resendVerificationEmail: email => dispatch(doUserResendVerificationEmail(email)),
|
||||||
|
checkEmailVerified: () => dispatch(doUserCheckEmailVerified()),
|
||||||
|
fetchAccessToken: () => dispatch(doFetchAccessToken()),
|
||||||
|
});
|
||||||
|
|
||||||
|
export default connect(
|
||||||
|
select,
|
||||||
|
perform
|
||||||
|
)(UserSignUp);
|
32
src/ui/component/userSignIn/view.jsx
Normal file
32
src/ui/component/userSignIn/view.jsx
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
// @flow
|
||||||
|
import React from 'react';
|
||||||
|
import UserEmailNew from 'component/userEmailNew';
|
||||||
|
import UserEmailVerify from 'component/userEmailVerify';
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
user: ?User,
|
||||||
|
email: ?string,
|
||||||
|
};
|
||||||
|
|
||||||
|
function UserSignUp(props: Props) {
|
||||||
|
const { email, user } = props;
|
||||||
|
const verifiedEmail = user && email && user.has_verified_email;
|
||||||
|
|
||||||
|
function getTitle() {
|
||||||
|
if (!email) {
|
||||||
|
return __('Get Rockin');
|
||||||
|
} else if (email && !verifiedEmail) {
|
||||||
|
return __('We Sent You An Email');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<section>
|
||||||
|
<h1 className="card__title--large">{getTitle()}</h1>
|
||||||
|
{!email && <UserEmailNew />}
|
||||||
|
{email && !verifiedEmail && <UserEmailVerify />}
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default UserSignUp;
|
|
@ -50,11 +50,10 @@ class UserVerify extends React.PureComponent<Props> {
|
||||||
button="inverse"
|
button="inverse"
|
||||||
label={__('Submit Phone Number')}
|
label={__('Submit Phone Number')}
|
||||||
/>
|
/>
|
||||||
|
</div>
|
||||||
<div className="help">
|
<div className="help">
|
||||||
{__('Standard messaging rates apply. LBRY will not text or call you otherwise. Having trouble?')}{' '}
|
{__('Standard messaging rates apply. LBRY will not text or call you otherwise. Having trouble?')}{' '}
|
||||||
<Button button="link" href="https://lbry.com/faq/phone" label={__('Read more.')} />
|
<Button button="link" href="https://lbry.com/faq/phone" label={__('Read more.')} />
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@ import * as ACTIONS from 'constants/action_types';
|
||||||
import { ipcRenderer, remote, shell } from 'electron';
|
import { ipcRenderer, remote, shell } from 'electron';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import * as MODALS from 'constants/modal_types';
|
import * as MODALS from 'constants/modal_types';
|
||||||
import React, { useState, useEffect } from 'react';
|
import React, { Fragment, useState, useEffect } from 'react';
|
||||||
import ReactDOM from 'react-dom';
|
import ReactDOM from 'react-dom';
|
||||||
import { Provider } from 'react-redux';
|
import { Provider } from 'react-redux';
|
||||||
import { doConditionalAuthNavigate, doDaemonReady, doAutoUpdate, doOpenModal, doHideModal } from 'redux/actions/app';
|
import { doConditionalAuthNavigate, doDaemonReady, doAutoUpdate, doOpenModal, doHideModal } from 'redux/actions/app';
|
||||||
|
@ -237,7 +237,7 @@ function AppWrapper() {
|
||||||
return (
|
return (
|
||||||
<Provider store={store}>
|
<Provider store={store}>
|
||||||
<PersistGate persistor={persistor} loading={<div className="main--launching" />}>
|
<PersistGate persistor={persistor} loading={<div className="main--launching" />}>
|
||||||
<div>
|
<Fragment>
|
||||||
{readyToLaunch ? (
|
{readyToLaunch ? (
|
||||||
<ConnectedRouter history={history}>
|
<ConnectedRouter history={history}>
|
||||||
<ErrorBoundary>
|
<ErrorBoundary>
|
||||||
|
@ -251,7 +251,7 @@ function AppWrapper() {
|
||||||
onReadyToLaunch={() => setReadyToLaunch(true)}
|
onReadyToLaunch={() => setReadyToLaunch(true)}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</Fragment>
|
||||||
</PersistGate>
|
</PersistGate>
|
||||||
</Provider>
|
</Provider>
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
// @flow
|
// @flow
|
||||||
|
import * as PAGES from 'constants/pages';
|
||||||
import React, { PureComponent, Fragment } from 'react';
|
import React, { PureComponent, Fragment } from 'react';
|
||||||
import BusyIndicator from 'component/common/busy-indicator';
|
import BusyIndicator from 'component/common/busy-indicator';
|
||||||
import RewardListClaimed from 'component/rewardListClaimed';
|
import RewardListClaimed from 'component/rewardListClaimed';
|
||||||
|
@ -39,15 +40,18 @@ class RewardsPage extends PureComponent<Props> {
|
||||||
return (
|
return (
|
||||||
!IS_WEB && (
|
!IS_WEB && (
|
||||||
<section className="card card--section">
|
<section className="card card--section">
|
||||||
<h2 className="card__title">{__('Rewards Approval to Earn Credits (LBC)')}</h2>
|
<h2 className="card__title">{__('Sign In To Unlock Rewards')}</h2>
|
||||||
<p className="card__subtitle">
|
<p className="card__subtitle">
|
||||||
{__(
|
{__(
|
||||||
'This step is optional. You can continue to use this app without rewards, but LBC may be needed for some tasks.'
|
'This is optional. You can continue to use this app without rewards, but LBC may be needed for some tasks.'
|
||||||
)}{' '}
|
)}
|
||||||
<Button button="link" label={__('Learn more')} href="https://lbry.com/faq/rewards" />.
|
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<Button navigate="/$/auth?redirect=rewards" button="primary" label="Prove Humanity" />
|
<Button
|
||||||
|
navigate={`/$/${PAGES.AUTH}/signin?redirect=rewards`}
|
||||||
|
button="primary"
|
||||||
|
label={__('Unlock Rewards')}
|
||||||
|
/>
|
||||||
</section>
|
</section>
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
15
src/ui/page/signIn/index.js
Normal file
15
src/ui/page/signIn/index.js
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
import { selectEmailToVerify, selectUser } from 'lbryinc';
|
||||||
|
import { selectMyChannelClaims } from 'lbry-redux';
|
||||||
|
import SignUpPage from './view';
|
||||||
|
|
||||||
|
const select = state => ({
|
||||||
|
email: selectEmailToVerify(state),
|
||||||
|
user: selectUser(state),
|
||||||
|
channels: selectMyChannelClaims(state),
|
||||||
|
});
|
||||||
|
|
||||||
|
export default connect(
|
||||||
|
select,
|
||||||
|
null
|
||||||
|
)(SignUpPage);
|
42
src/ui/page/signIn/view.jsx
Normal file
42
src/ui/page/signIn/view.jsx
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
// @flow
|
||||||
|
import React from 'react';
|
||||||
|
import UserSignIn from 'component/userSignIn';
|
||||||
|
import UserFirstChannel from 'component/userFirstChannel';
|
||||||
|
import UserVerify from 'component/userVerify';
|
||||||
|
import Page from 'component/page';
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
user: ?User,
|
||||||
|
channels: ?Array<ChannelClaim>,
|
||||||
|
location: { search: string },
|
||||||
|
history: { replace: string => void },
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function SignInPage(props: Props) {
|
||||||
|
const { user, channels, location, history } = props;
|
||||||
|
const { search } = location;
|
||||||
|
const urlParams = new URLSearchParams(search);
|
||||||
|
const redirect = urlParams.get('redirect');
|
||||||
|
const hasVerifiedEmail = user && user.has_verified_email;
|
||||||
|
const rewardsApproved = user && user.is_reward_approved;
|
||||||
|
const channelCount = channels ? channels.length : 0;
|
||||||
|
const showWelcome = !hasVerifiedEmail || channelCount === 0;
|
||||||
|
|
||||||
|
if (rewardsApproved && channelCount > 0) {
|
||||||
|
history.replace(redirect ? `/$/${redirect}` : '/');
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Page fullscreen className="main--auth-page">
|
||||||
|
{showWelcome && (
|
||||||
|
<div className="columns">
|
||||||
|
{!hasVerifiedEmail && <UserSignIn />}
|
||||||
|
{hasVerifiedEmail && channelCount === 0 && <UserFirstChannel />}
|
||||||
|
<div style={{ width: '100%', height: '20rem', borderRadius: 20, backgroundColor: '#ffc7e6' }} />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{hasVerifiedEmail && channelCount > 0 && <UserVerify />}
|
||||||
|
</Page>
|
||||||
|
);
|
||||||
|
}
|
|
@ -49,6 +49,12 @@
|
||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.card--fill {
|
||||||
|
min-width: 25rem;
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
}
|
||||||
|
|
||||||
// C A R D
|
// C A R D
|
||||||
// A C T I O N S
|
// A C T I O N S
|
||||||
|
|
||||||
|
@ -189,6 +195,12 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.card__title--large {
|
||||||
|
@extend .card__title;
|
||||||
|
font-weight: 700;
|
||||||
|
font-size: var(--font-heading);
|
||||||
|
}
|
||||||
|
|
||||||
.card__title--between {
|
.card__title--between {
|
||||||
@extend .card__title;
|
@extend .card__title;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
|
|
|
@ -85,6 +85,10 @@ fieldset-section:last-child {
|
||||||
|
|
||||||
checkbox-element,
|
checkbox-element,
|
||||||
radio-element {
|
radio-element {
|
||||||
|
&:hover {
|
||||||
|
cursor: pointer !important;
|
||||||
|
}
|
||||||
|
|
||||||
label {
|
label {
|
||||||
color: lighten($lbry-black, 20%);
|
color: lighten($lbry-black, 20%);
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
|
@ -102,6 +106,9 @@ radio-element {
|
||||||
}
|
}
|
||||||
|
|
||||||
checkbox-toggle {
|
checkbox-toggle {
|
||||||
|
border-width: 1px;
|
||||||
|
border-radius: var(--input-border-radius);
|
||||||
|
|
||||||
&:before {
|
&:before {
|
||||||
background-position: center;
|
background-position: center;
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
|
@ -155,6 +162,10 @@ fieldset-group {
|
||||||
|
|
||||||
label {
|
label {
|
||||||
min-height: 18px;
|
min-height: 18px;
|
||||||
|
white-space: nowrap;
|
||||||
|
// Set width 0 and overflow visible so the label can act as if it's the input label and not a random text node in a side by side div
|
||||||
|
overflow: visible;
|
||||||
|
width: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
fieldset-section:first-child .form-field__prefix,
|
fieldset-section:first-child .form-field__prefix,
|
||||||
|
|
|
@ -20,6 +20,12 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.header--minimal {
|
||||||
|
box-shadow: none;
|
||||||
|
background-color: transparent;
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
|
||||||
.header__contents {
|
.header__contents {
|
||||||
max-width: var(--page-max-width);
|
max-width: var(--page-max-width);
|
||||||
height: calc(var(--header-height) - 1px);
|
height: calc(var(--header-height) - 1px);
|
||||||
|
@ -47,6 +53,10 @@
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
||||||
|
.button {
|
||||||
|
margin-left: auto;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.header__navigation-arrows {
|
.header__navigation-arrows {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
.main-wrapper {
|
.main-wrapper {
|
||||||
position: relative;
|
position: relative;
|
||||||
min-height: 100vh;
|
min-height: 80vh;
|
||||||
|
|
||||||
[data-mode='dark'] & {
|
[data-mode='dark'] & {
|
||||||
background-color: var(--dm-color-08);
|
background-color: var(--dm-color-08);
|
||||||
|
@ -41,6 +41,13 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.main--auth-page {
|
||||||
|
max-width: 60rem;
|
||||||
|
margin-top: calc(var(--spacing-main-padding) * 2);
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
}
|
||||||
|
|
||||||
.main--empty {
|
.main--empty {
|
||||||
align-items: center;
|
align-items: center;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
@ -56,6 +63,24 @@
|
||||||
background-color: var(--color-background);
|
background-color: var(--color-background);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.main--fullscreen {
|
||||||
|
width: 100vw;
|
||||||
|
height: 100vh;
|
||||||
|
z-index: 9999;
|
||||||
|
background-color: $lbry-white;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
margin: 0;
|
||||||
|
margin-top: 0;
|
||||||
|
|
||||||
|
* {
|
||||||
|
z-index: 10000;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.main__status {
|
.main__status {
|
||||||
@extend .card__subtitle;
|
@extend .card__subtitle;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
|
@ -89,7 +89,6 @@
|
||||||
color: lighten($lbry-black, 20%);
|
color: lighten($lbry-black, 20%);
|
||||||
|
|
||||||
.icon {
|
.icon {
|
||||||
margin-right: var(--spacing-small);
|
|
||||||
stroke: $lbry-gray-5;
|
stroke: $lbry-gray-5;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -101,3 +100,16 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.menu__link {
|
||||||
|
.icon {
|
||||||
|
margin-right: var(--spacing-small);
|
||||||
|
stroke: $lbry-gray-5;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu__title {
|
||||||
|
span {
|
||||||
|
margin-left: var(--spacing-miniscule);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -24,7 +24,6 @@ body {
|
||||||
cursor: default;
|
cursor: default;
|
||||||
font-size: 1rem;
|
font-size: 1rem;
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
height: 100%;
|
|
||||||
line-height: 1.5;
|
line-height: 1.5;
|
||||||
background-color: mix($lbry-white, $lbry-gray-1, 70%);
|
background-color: mix($lbry-white, $lbry-gray-1, 70%);
|
||||||
|
|
||||||
|
@ -107,10 +106,13 @@ a {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: flex-start;
|
align-items: flex-start;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
|
||||||
> * {
|
> * {
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
flex-basis: 0;
|
flex-basis: 0;
|
||||||
|
min-width: 15rem;
|
||||||
|
margin-bottom: var(--spacing-large);
|
||||||
|
|
||||||
&:first-child {
|
&:first-child {
|
||||||
margin-right: 1.5rem;
|
margin-right: 1.5rem;
|
||||||
|
|
|
@ -1,26 +0,0 @@
|
||||||
export function formatCredits(amount, precision = 1) {
|
|
||||||
return parseFloat(amount)
|
|
||||||
.toFixed(precision || 1)
|
|
||||||
.replace(/\.?0+$/, '');
|
|
||||||
}
|
|
||||||
|
|
||||||
export function formatFullPrice(amount, precision = 1) {
|
|
||||||
if (!amount) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
let formated = '';
|
|
||||||
const quantity = amount.toString().split('.');
|
|
||||||
const fraction = quantity[1];
|
|
||||||
|
|
||||||
if (fraction) {
|
|
||||||
const decimals = fraction.split('');
|
|
||||||
const first = decimals.filter(number => number !== '0')[0];
|
|
||||||
const index = decimals.indexOf(first);
|
|
||||||
|
|
||||||
// Set format fraction
|
|
||||||
formated = `.${fraction.substring(0, index + precision)}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
return parseFloat(quantity[0] + formated);
|
|
||||||
}
|
|
|
@ -638,7 +638,6 @@
|
||||||
"Refreshed!": "Refreshed!",
|
"Refreshed!": "Refreshed!",
|
||||||
"Starting...": "Starting...",
|
"Starting...": "Starting...",
|
||||||
"Spin Spin Sugar": "Spin Spin Sugar",
|
"Spin Spin Sugar": "Spin Spin Sugar",
|
||||||
"URI does not include name.": "URI does not include name.",
|
|
||||||
"You're not following any channels.": "You're not following any channels.",
|
"You're not following any channels.": "You're not following any channels.",
|
||||||
"Look what's trending for everyone": "Look what's trending for everyone",
|
"Look what's trending for everyone": "Look what's trending for everyone",
|
||||||
"or": "or",
|
"or": "or",
|
||||||
|
|
Loading…
Reference in a new issue