Outstanding routing cleanup #2405
26 changed files with 252 additions and 301 deletions
3
flow-typed/web.js
vendored
Normal file
3
flow-typed/web.js
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
// @flow
|
||||||
|
|
||||||
|
declare var IS_WEB: boolean;
|
|
@ -70,14 +70,14 @@ class App extends React.PureComponent<Props> {
|
||||||
return (
|
return (
|
||||||
<div id="window" onContextMenu={e => openContextMenu(e)}>
|
<div id="window" onContextMenu={e => openContextMenu(e)}>
|
||||||
<Header />
|
<Header />
|
||||||
<main className="page">
|
<section className="page">
|
||||||
{enhancedLayout && <Yrbl className="yrbl--enhanced" />}
|
{enhancedLayout && <Yrbl className="yrbl--enhanced" />}
|
||||||
|
|||||||
<SideBar />
|
<SideBar />
|
||||||
<div className="content" id="content">
|
<div className="content" id="content">
|
||||||
<Router />
|
<Router />
|
||||||
<ModalRouter />
|
<ModalRouter />
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
// @flow
|
// @flow
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
const LbcSymbol = () => <span> LBC</span>; // ℄
|
const LbcSymbol = () => <span>LBC</span>; // ℄
|
||||||
|
|
||||||
export default LbcSymbol;
|
export default LbcSymbol;
|
||||||
|
|
|
@ -202,8 +202,8 @@ class MediaPlayer extends React.PureComponent<Props, State> {
|
||||||
}
|
}
|
||||||
|
|
||||||
// @if TARGET='app'
|
// @if TARGET='app'
|
||||||
sleep(ms) {
|
sleep(ms: number) {
|
||||||
return new Promise(resolve => setTimeout(resolve, ms));
|
return new Promise<void>(resolve => setTimeout(resolve, ms));
|
||||||
}
|
}
|
||||||
|
|
||||||
refreshMetadata() {
|
refreshMetadata() {
|
||||||
|
|
|
@ -47,6 +47,7 @@ type Props = {
|
||||||
nextFileToPlay: ?string,
|
nextFileToPlay: ?string,
|
||||||
navigate: (string, {}) => void,
|
navigate: (string, {}) => void,
|
||||||
costInfo: ?{ cost: number },
|
costInfo: ?{ cost: number },
|
||||||
|
insufficientCredits: boolean,
|
||||||
};
|
};
|
||||||
|
|
||||||
class FileViewer extends React.PureComponent<Props> {
|
class FileViewer extends React.PureComponent<Props> {
|
||||||
|
@ -150,7 +151,11 @@ class FileViewer extends React.PureComponent<Props> {
|
||||||
}
|
}
|
||||||
|
|
||||||
playContent() {
|
playContent() {
|
||||||
const { play, uri, fileInfo, isDownloading, isLoading } = this.props;
|
const { play, uri, fileInfo, isDownloading, isLoading, insufficientCredits } = this.props;
|
||||||
|
|
||||||
|
if (insufficientCredits) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// @if TARGET='app'
|
// @if TARGET='app'
|
||||||
if (fileInfo || isDownloading || isLoading) {
|
if (fileInfo || isDownloading || isLoading) {
|
||||||
|
@ -220,6 +225,7 @@ class FileViewer extends React.PureComponent<Props> {
|
||||||
className,
|
className,
|
||||||
obscureNsfw,
|
obscureNsfw,
|
||||||
mediaType,
|
mediaType,
|
||||||
|
insufficientCredits,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
const isPlaying = playingUri === uri;
|
const isPlaying = playingUri === uri;
|
||||||
|
@ -246,7 +252,10 @@ class FileViewer extends React.PureComponent<Props> {
|
||||||
}
|
}
|
||||||
|
|
||||||
const poster = metadata && metadata.thumbnail;
|
const poster = metadata && metadata.thumbnail;
|
||||||
const layoverClass = classnames('content__cover', { 'card__media--nsfw': shouldObscureNsfw });
|
const layoverClass = classnames('content__cover', {
|
||||||
|
'card__media--nsfw': shouldObscureNsfw,
|
||||||
|
'card__media--disabled': insufficientCredits,
|
||||||
|
});
|
||||||
|
|
||||||
const layoverStyle =
|
const layoverStyle =
|
||||||
!shouldObscureNsfw && poster ? { backgroundImage: `url("${poster}")` } : {};
|
!shouldObscureNsfw && poster ? { backgroundImage: `url("${poster}")` } : {};
|
||||||
|
|
|
@ -85,8 +85,7 @@ const Header = (props: Props) => {
|
||||||
title={`Your balance is ${balance} LBRY Credits`}
|
title={`Your balance is ${balance} LBRY Credits`}
|
||||||
label={
|
label={
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
<span>{roundedBalance}</span>
|
{roundedBalance} <LbcSymbol />
|
||||||
<LbcSymbol />
|
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
}
|
}
|
||||||
navigate="/$/wallet"
|
navigate="/$/wallet"
|
||||||
|
@ -94,6 +93,7 @@ const Header = (props: Props) => {
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
className="header__navigation-item header__navigation-item--right-action"
|
className="header__navigation-item header__navigation-item--right-action"
|
||||||
|
activeClass="header__navigation-item--active"
|
||||||
description={__('Publish content')}
|
description={__('Publish content')}
|
||||||
icon={ICONS.UPLOAD}
|
icon={ICONS.UPLOAD}
|
||||||
iconSize={24}
|
iconSize={24}
|
||||||
|
|
|
@ -332,6 +332,7 @@ class PublishForm extends React.PureComponent<Props> {
|
||||||
resetThumbnailStatus,
|
resetThumbnailStatus,
|
||||||
isStillEditing,
|
isStillEditing,
|
||||||
amountNeededForTakeover,
|
amountNeededForTakeover,
|
||||||
|
balance,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
const formDisabled = (!filePath && !editingURI) || publishing;
|
const formDisabled = (!filePath && !editingURI) || publishing;
|
||||||
|
@ -352,7 +353,7 @@ class PublishForm extends React.PureComponent<Props> {
|
||||||
<Form onSubmit={this.handlePublish}>
|
<Form onSubmit={this.handlePublish}>
|
||||||
<section
|
<section
|
||||||
className={classnames('card card--section', {
|
className={classnames('card card--section', {
|
||||||
'card--disabled': IS_WEB || publishing,
|
'card--disabled': IS_WEB || publishing || balance === 0,
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
<header className="card__header">
|
<header className="card__header">
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import * as PAGES from 'constants/pages';
|
import * as PAGES from 'constants/pages';
|
||||||
import React from 'react';
|
import React, { useEffect } from 'react';
|
||||||
import { Router } from '@reach/router';
|
import { Router } from '@reach/router';
|
||||||
import SettingsPage from 'page/settings';
|
import SettingsPage from 'page/settings';
|
||||||
import HelpPage from 'page/help';
|
import HelpPage from 'page/help';
|
||||||
|
@ -21,12 +21,27 @@ import UserHistoryPage from 'page/userHistory';
|
||||||
import SendCreditsPage from 'page/sendCredits';
|
import SendCreditsPage from 'page/sendCredits';
|
||||||
import NavigationHistory from 'page/navigationHistory';
|
import NavigationHistory from 'page/navigationHistory';
|
||||||
|
|
||||||
export default function AppRouter(props) {
|
const ScrollHandler = props => {
|
||||||
|
const { key } = props.location;
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
// This shouldn't scroll to top when you click "back"
|
||||||
|
// Might take some more work but fixes scroll position being stuck on navigation for now
|
||||||
|
const main = document.querySelector('main');
|
||||||
|
main.scrollIntoView();
|
||||||
|
}, [key]);
|
||||||
|
|
||||||
|
return props.children;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function AppRouter() {
|
||||||
return (
|
return (
|
||||||
<Router>
|
<Router>
|
||||||
|
<ScrollHandler path="/">
|
||||||
<DiscoverPage path="/" />
|
<DiscoverPage path="/" />
|
||||||
<ShowPage path="/:claimName/:claimId" />
|
<ShowPage path="/:claimName/:claimId" />
|
||||||
<ShowPage path="/:claimName" />
|
<ShowPage path="/:claimName" />
|
||||||
|
{/* <ShowPage path="/" uri="five" /> */}
|
||||||
|
|
||||||
<AuthPage path={`$/${PAGES.AUTH}`} />
|
<AuthPage path={`$/${PAGES.AUTH}`} />
|
||||||
<BackupPage path={`$/${PAGES.BACKUP}`} />
|
<BackupPage path={`$/${PAGES.BACKUP}`} />
|
||||||
|
@ -46,6 +61,7 @@ export default function AppRouter(props) {
|
||||||
<SendCreditsPage path={`$/${PAGES.SEND}`} />
|
<SendCreditsPage path={`$/${PAGES.SEND}`} />
|
||||||
<UserHistoryPage path={`$/${PAGES.HISTORY}`} />
|
<UserHistoryPage path={`$/${PAGES.HISTORY}`} />
|
||||||
<NavigationHistory path={`$/${PAGES.HISTORY}/all`} />
|
<NavigationHistory path={`$/${PAGES.HISTORY}/all`} />
|
||||||
|
</ScrollHandler>
|
||||||
</Router>
|
</Router>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,12 +27,12 @@ export default class extends React.PureComponent<Props> {
|
||||||
const image = yrblTypes[type];
|
const image = yrblTypes[type];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="yrbl-wrap">
|
<div className="yrbl__wrap">
|
||||||
<img alt="Friendly gerbil" className={classnames('yrbl', className)} src={image} />
|
<img alt="Friendly gerbil" className={classnames('yrbl', className)} src={`/${image}`} />
|
||||||
{title && subtitle && (
|
{title && subtitle && (
|
||||||
<div className="card__content">
|
<div className="yrbl__content">
|
||||||
<h2 className="card__title">{title}</h2>
|
<h2 className="card__title">{title}</h2>
|
||||||
<div className="card__subtitle">{subtitle}</div>
|
<div className="card__content">{subtitle}</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -6,7 +6,6 @@ export const DOWNLOADING = 'downloading';
|
||||||
export const AUTO_UPDATE_DOWNLOADED = 'auto_update_downloaded';
|
export const AUTO_UPDATE_DOWNLOADED = 'auto_update_downloaded';
|
||||||
export const AUTO_UPDATE_CONFIRM = 'auto_update_confirm';
|
export const AUTO_UPDATE_CONFIRM = 'auto_update_confirm';
|
||||||
export const ERROR = 'error';
|
export const ERROR = 'error';
|
||||||
export const INSUFFICIENT_CREDITS = 'insufficient_credits';
|
|
||||||
export const UPGRADE = 'upgrade';
|
export const UPGRADE = 'upgrade';
|
||||||
export const WELCOME = 'welcome';
|
export const WELCOME = 'welcome';
|
||||||
export const EMAIL_COLLECTION = 'email_collection';
|
export const EMAIL_COLLECTION = 'email_collection';
|
||||||
|
|
|
@ -1,31 +0,0 @@
|
||||||
import { connect } from 'react-redux';
|
|
||||||
import { doSetClientSetting } from 'redux/actions/settings';
|
|
||||||
import { selectUserIsRewardApproved, selectUnclaimedRewardValue } from 'lbryinc';
|
|
||||||
import { selectBalance } from 'lbry-redux';
|
|
||||||
import { doHideModal } from 'redux/actions/app';
|
|
||||||
import * as settings from 'constants/settings';
|
|
||||||
import ModalCreditIntro from './view';
|
|
||||||
import { navigate } from '@reach/router';
|
|
||||||
|
|
||||||
const select = state => ({
|
|
||||||
currentBalance: selectBalance(state),
|
|
||||||
isRewardApproved: selectUserIsRewardApproved(state),
|
|
||||||
totalRewardValue: selectUnclaimedRewardValue(state),
|
|
||||||
});
|
|
||||||
|
|
||||||
const perform = dispatch => () => ({
|
|
||||||
addBalance: () => {
|
|
||||||
navigate('/$/getcredits');
|
|
||||||
dispatch(doSetClientSetting(settings.CREDIT_REQUIRED_ACKNOWLEDGED, true));
|
|
||||||
dispatch(doHideModal());
|
|
||||||
},
|
|
||||||
closeModal: () => {
|
|
||||||
dispatch(doSetClientSetting(settings.CREDIT_REQUIRED_ACKNOWLEDGED, true));
|
|
||||||
dispatch(doHideModal());
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
export default connect(
|
|
||||||
select,
|
|
||||||
perform
|
|
||||||
)(ModalCreditIntro);
|
|
|
@ -1,56 +0,0 @@
|
||||||
// @flow
|
|
||||||
import React from 'react';
|
|
||||||
import { Modal } from 'modal/modal';
|
|
||||||
import CurrencySymbol from 'component/common/lbc-symbol';
|
|
||||||
import CreditAmount from 'component/common/credit-amount';
|
|
||||||
import Button from 'component/button';
|
|
||||||
|
|
||||||
type Props = {
|
|
||||||
totalRewardValue: number,
|
|
||||||
currentBalance: number,
|
|
||||||
closeModal: () => void,
|
|
||||||
addBalance: () => void,
|
|
||||||
};
|
|
||||||
|
|
||||||
const ModalCreditIntro = (props: Props) => {
|
|
||||||
const { closeModal, totalRewardValue, currentBalance, addBalance } = props;
|
|
||||||
const totalRewardRounded = Math.floor(totalRewardValue / 10) * 10;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Modal type="custom" isOpen contentLabel="Welcome to LBRY" title={__('LBRY Credits Needed')}>
|
|
||||||
<section className="card__content">
|
|
||||||
<p>
|
|
||||||
Some actions require LBRY credits (
|
|
||||||
<em>
|
|
||||||
<CurrencySymbol />
|
|
||||||
</em>
|
|
||||||
), the blockchain token that powers the LBRY network.
|
|
||||||
</p>
|
|
||||||
{currentBalance <= 0 && (
|
|
||||||
<p>
|
|
||||||
You currently have <CreditAmount inheritStyle amount={currentBalance} />, so the actions
|
|
||||||
you can take are limited.
|
|
||||||
</p>
|
|
||||||
)}
|
|
||||||
{Boolean(totalRewardValue) && (
|
|
||||||
<p>
|
|
||||||
{__(' There are a variety of ways to get credits, including more than')}{' '}
|
|
||||||
<CreditAmount inheritStyle amount={totalRewardRounded} />{' '}
|
|
||||||
{__('in free rewards for participating in the LBRY beta.')}
|
|
||||||
</p>
|
|
||||||
)}
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<div className="card__actions">
|
|
||||||
<Button button="primary" onClick={addBalance} label={__('Get Credits')} />
|
|
||||||
<Button
|
|
||||||
button="link"
|
|
||||||
onClick={closeModal}
|
|
||||||
label={currentBalance <= 0 ? __('Use Without LBC') : __('Meh, Not Now')}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</Modal>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default ModalCreditIntro;
|
|
|
@ -1,28 +1,15 @@
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import * as settings from 'constants/settings';
|
|
||||||
import { selectBalance, makeSelectCostInfoForUri, selectError, doToast } from 'lbry-redux';
|
|
||||||
import { makeSelectClientSetting } from 'redux/selectors/settings';
|
|
||||||
import { selectUser, selectUserIsVerificationCandidate } from 'lbryinc';
|
|
||||||
import { selectModal } from 'redux/selectors/app';
|
import { selectModal } from 'redux/selectors/app';
|
||||||
import { doOpenModal } from 'redux/actions/app';
|
import { doOpenModal } from 'redux/actions/app';
|
||||||
|
import { selectError } from 'lbry-redux';
|
||||||
import ModalRouter from './view';
|
import ModalRouter from './view';
|
||||||
|
|
||||||
const select = (state, props) => ({
|
const select = (state, props) => ({
|
||||||
balance: selectBalance(state),
|
|
||||||
showPageCost: makeSelectCostInfoForUri(props.uri)(state),
|
|
||||||
isVerificationCandidate: selectUserIsVerificationCandidate(state),
|
|
||||||
isCreditIntroAcknowledged: makeSelectClientSetting(settings.CREDIT_REQUIRED_ACKNOWLEDGED)(state),
|
|
||||||
isEmailCollectionAcknowledged: makeSelectClientSetting(settings.EMAIL_COLLECTION_ACKNOWLEDGED)(
|
|
||||||
state
|
|
||||||
),
|
|
||||||
isWelcomeAcknowledged: makeSelectClientSetting(settings.NEW_USER_ACKNOWLEDGED)(state),
|
|
||||||
user: selectUser(state),
|
|
||||||
modal: selectModal(state),
|
modal: selectModal(state),
|
||||||
error: selectError(state),
|
error: selectError(state),
|
||||||
});
|
});
|
||||||
|
|
||||||
const perform = dispatch => ({
|
const perform = dispatch => ({
|
||||||
showToast: props => dispatch(doToast(props)),
|
|
||||||
openModal: props => dispatch(doOpenModal(props)),
|
openModal: props => dispatch(doOpenModal(props)),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,6 @@ import ModalUpgrade from 'modal/modalUpgrade';
|
||||||
import ModalWelcome from 'modal/modalWelcome';
|
import ModalWelcome from 'modal/modalWelcome';
|
||||||
import ModalFirstReward from 'modal/modalFirstReward';
|
import ModalFirstReward from 'modal/modalFirstReward';
|
||||||
import ModalRewardApprovalRequired from 'modal/modalRewardApprovalRequired';
|
import ModalRewardApprovalRequired from 'modal/modalRewardApprovalRequired';
|
||||||
import ModalCreditIntro from 'modal/modalCreditIntro';
|
|
||||||
import ModalRemoveFile from 'modal/modalRemoveFile';
|
import ModalRemoveFile from 'modal/modalRemoveFile';
|
||||||
import ModalTransactionFailed from 'modal/modalTransactionFailed';
|
import ModalTransactionFailed from 'modal/modalTransactionFailed';
|
||||||
import ModalFileTimeout from 'modal/modalFileTimeout';
|
import ModalFileTimeout from 'modal/modalFileTimeout';
|
||||||
|
@ -32,93 +31,10 @@ import ModalRewardCode from 'modal/modalRewardCode';
|
||||||
type Props = {
|
type Props = {
|
||||||
modal: { id: string, modalProps: {} },
|
modal: { id: string, modalProps: {} },
|
||||||
error: { message: string },
|
error: { message: string },
|
||||||
openModal: string => void,
|
|
||||||
page: string,
|
|
||||||
isWelcomeAcknowledged: boolean,
|
|
||||||
isEmailCollectionAcknowledged: boolean,
|
|
||||||
isVerificationCandidate: boolean,
|
|
||||||
isCreditIntroAcknowledged: boolean,
|
|
||||||
balance: number,
|
|
||||||
showPageCost: number,
|
|
||||||
user: {
|
|
||||||
is_reward_approved: boolean,
|
|
||||||
is_identity_verified: boolean,
|
|
||||||
has_verified_email: boolean,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
type State = {
|
function ModalRouter(props: Props) {
|
||||||
lastTransitionModal: ?string,
|
const { modal, error } = props;
|
||||||
lastTransitionPage: ?string,
|
|
||||||
};
|
|
||||||
|
|
||||||
class ModalRouter extends React.PureComponent<Props, State> {
|
|
||||||
constructor(props: Props) {
|
|
||||||
super(props);
|
|
||||||
|
|
||||||
this.state = {
|
|
||||||
lastTransitionModal: null,
|
|
||||||
lastTransitionPage: null,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
componentWillMount() {
|
|
||||||
this.showTransitionModals(this.props);
|
|
||||||
}
|
|
||||||
|
|
||||||
componentWillReceiveProps(nextProps: Props) {
|
|
||||||
this.showTransitionModals(nextProps);
|
|
||||||
}
|
|
||||||
|
|
||||||
showTransitionModals(props: Props) {
|
|
||||||
const { modal, openModal, page } = props;
|
|
||||||
|
|
||||||
if (modal) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const transitionModal = [this.checkShowCreditIntro].reduce(
|
|
||||||
(acc, func) => (!acc ? func.bind(this)(props) : acc),
|
|
||||||
false
|
|
||||||
);
|
|
||||||
|
|
||||||
if (
|
|
||||||
transitionModal &&
|
|
||||||
(transitionModal !== this.state.lastTransitionModal || page !== this.state.lastTransitionPage)
|
|
||||||
) {
|
|
||||||
openModal(transitionModal);
|
|
||||||
this.setState({
|
|
||||||
lastTransitionModal: transitionModal,
|
|
||||||
lastTransitionPage: page,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
checkShowCreditIntro(props: Props) {
|
|
||||||
// @if TARGET='app'
|
|
||||||
// This doesn't make sense to show until the web has wallet support
|
|
||||||
const { balance, page, isCreditIntroAcknowledged } = props;
|
|
||||||
|
|
||||||
if (
|
|
||||||
balance === 0 &&
|
|
||||||
!isCreditIntroAcknowledged &&
|
|
||||||
(['send', 'publish'].includes(page) || this.isPaidShowPage(props))
|
|
||||||
) {
|
|
||||||
return MODALS.INSUFFICIENT_CREDITS;
|
|
||||||
}
|
|
||||||
// @endif
|
|
||||||
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
isPaidShowPage(props: Props) {
|
|
||||||
const { page, showPageCost } = props;
|
|
||||||
// Fix me
|
|
||||||
return page === 'show' && showPageCost > 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const { modal, error } = this.props;
|
|
||||||
|
|
||||||
if (error) {
|
if (error) {
|
||||||
return <ModalError {...error} />;
|
return <ModalError {...error} />;
|
||||||
|
@ -143,8 +59,6 @@ class ModalRouter extends React.PureComponent<Props, State> {
|
||||||
return <ModalError {...modalProps} />;
|
return <ModalError {...modalProps} />;
|
||||||
case MODALS.FILE_TIMEOUT:
|
case MODALS.FILE_TIMEOUT:
|
||||||
return <ModalFileTimeout {...modalProps} />;
|
return <ModalFileTimeout {...modalProps} />;
|
||||||
case MODALS.INSUFFICIENT_CREDITS:
|
|
||||||
return <ModalCreditIntro {...modalProps} />;
|
|
||||||
case MODALS.WELCOME:
|
case MODALS.WELCOME:
|
||||||
return <ModalWelcome {...modalProps} />;
|
return <ModalWelcome {...modalProps} />;
|
||||||
case MODALS.FIRST_REWARD:
|
case MODALS.FIRST_REWARD:
|
||||||
|
@ -188,7 +102,6 @@ class ModalRouter extends React.PureComponent<Props, State> {
|
||||||
default:
|
default:
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default ModalRouter;
|
export default ModalRouter;
|
||||||
|
|
|
@ -12,6 +12,7 @@ import {
|
||||||
makeSelectContentTypeForUri,
|
makeSelectContentTypeForUri,
|
||||||
makeSelectMetadataForUri,
|
makeSelectMetadataForUri,
|
||||||
makeSelectChannelForClaimUri,
|
makeSelectChannelForClaimUri,
|
||||||
|
selectBalance,
|
||||||
} from 'lbry-redux';
|
} from 'lbry-redux';
|
||||||
import {
|
import {
|
||||||
doFetchViewCount,
|
doFetchViewCount,
|
||||||
|
@ -39,6 +40,7 @@ const select = (state, props) => ({
|
||||||
isSubscribed: makeSelectIsSubscribed(props.uri)(state),
|
isSubscribed: makeSelectIsSubscribed(props.uri)(state),
|
||||||
channelUri: makeSelectChannelForClaimUri(props.uri, true)(state),
|
channelUri: makeSelectChannelForClaimUri(props.uri, true)(state),
|
||||||
viewCount: makeSelectViewCountForUri(props.uri)(state),
|
viewCount: makeSelectViewCountForUri(props.uri)(state),
|
||||||
|
balance: selectBalance(state),
|
||||||
});
|
});
|
||||||
|
|
||||||
const perform = dispatch => ({
|
const perform = dispatch => ({
|
||||||
|
|
|
@ -42,6 +42,7 @@ type Props = {
|
||||||
openModal: (id: string, { uri: string }) => void,
|
openModal: (id: string, { uri: string }) => void,
|
||||||
markSubscriptionRead: (string, string) => void,
|
markSubscriptionRead: (string, string) => void,
|
||||||
fetchViewCount: string => void,
|
fetchViewCount: string => void,
|
||||||
|
balance: number,
|
||||||
};
|
};
|
||||||
|
|
||||||
class FilePage extends React.Component<Props> {
|
class FilePage extends React.Component<Props> {
|
||||||
|
@ -135,6 +136,7 @@ class FilePage extends React.Component<Props> {
|
||||||
fileInfo,
|
fileInfo,
|
||||||
channelUri,
|
channelUri,
|
||||||
viewCount,
|
viewCount,
|
||||||
|
balance,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
// File info
|
// File info
|
||||||
|
@ -147,6 +149,7 @@ class FilePage extends React.Component<Props> {
|
||||||
const mediaType = getMediaType(contentType, fileName);
|
const mediaType = getMediaType(contentType, fileName);
|
||||||
const showFile =
|
const showFile =
|
||||||
PLAYABLE_MEDIA_TYPES.includes(mediaType) || PREVIEW_MEDIA_TYPES.includes(mediaType);
|
PLAYABLE_MEDIA_TYPES.includes(mediaType) || PREVIEW_MEDIA_TYPES.includes(mediaType);
|
||||||
|
|
||||||
const speechShareable =
|
const speechShareable =
|
||||||
costInfo &&
|
costInfo &&
|
||||||
costInfo.cost === 0 &&
|
costInfo.cost === 0 &&
|
||||||
|
@ -168,11 +171,32 @@ class FilePage extends React.Component<Props> {
|
||||||
editUri = buildURI(uriObject);
|
editUri = buildURI(uriObject);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const insufficientCredits = costInfo && costInfo.cost > balance;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Page notContained className="main--file-page">
|
<Page notContained className="main--file-page">
|
||||||
<div className="grid-area--content">
|
<div className="grid-area--content">
|
||||||
<h1 className="media__uri">{uri}</h1>
|
<h1 className="media__uri">
|
||||||
{showFile && <FileViewer className="content__embedded" uri={uri} mediaType={mediaType} />}
|
<Button navigate={uri} label={uri} />
|
||||||
|
</h1>
|
||||||
|
{insufficientCredits && (
|
||||||
|
<div className="media__insufficient-credits help--warning">
|
||||||
|
{__(
|
||||||
|
'The publisher has chosen to charge LBC to view this content. Your balance is currently to low to view it.'
|
||||||
|
)}{' '}
|
||||||
|
{__('Checkout')}{' '}
|
||||||
|
<Button button="link" navigate="/$/rewards" label={__('the rewards page')} />{' '}
|
||||||
|
{__('or send more LBC to your wallet.')}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{showFile && (
|
||||||
|
<FileViewer
|
||||||
|
insufficientCredits={insufficientCredits}
|
||||||
|
className="content__embedded"
|
||||||
|
uri={uri}
|
||||||
|
mediaType={mediaType}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
{!showFile &&
|
{!showFile &&
|
||||||
(thumbnail ? (
|
(thumbnail ? (
|
||||||
<Thumbnail shouldObscure={shouldObscureThumbnail} src={thumbnail} />
|
<Thumbnail shouldObscure={shouldObscureThumbnail} src={thumbnail} />
|
||||||
|
|
|
@ -14,6 +14,7 @@ import {
|
||||||
doPublish,
|
doPublish,
|
||||||
doPrepareEdit,
|
doPrepareEdit,
|
||||||
} from 'redux/actions/publish';
|
} from 'redux/actions/publish';
|
||||||
|
import { selectUnclaimedRewardValue } from 'lbryinc';
|
||||||
import PublishPage from './view';
|
import PublishPage from './view';
|
||||||
|
|
||||||
const select = state => ({
|
const select = state => ({
|
||||||
|
@ -27,6 +28,7 @@ const select = state => ({
|
||||||
isStillEditing: selectIsStillEditing(state),
|
isStillEditing: selectIsStillEditing(state),
|
||||||
balance: selectBalance(state),
|
balance: selectBalance(state),
|
||||||
isResolvingUri: selectIsResolvingPublishUris(state),
|
isResolvingUri: selectIsResolvingPublishUris(state),
|
||||||
|
totalRewardValue: selectUnclaimedRewardValue(state),
|
||||||
});
|
});
|
||||||
|
|
||||||
const perform = dispatch => ({
|
const perform = dispatch => ({
|
||||||
|
|
|
@ -1,19 +1,65 @@
|
||||||
import React from 'react';
|
import React, { Fragment } from 'react';
|
||||||
import PublishForm from 'component/publishForm';
|
import PublishForm from 'component/publishForm';
|
||||||
import Page from 'component/page';
|
import Page from 'component/page';
|
||||||
|
import Yrbl from 'component/yrbl';
|
||||||
|
import LbcSymbol from 'component/common/lbc-symbol';
|
||||||
|
import CreditAmount from 'component/common/credit-amount';
|
||||||
|
import Button from 'component/button';
|
||||||
|
|
||||||
class PublishPage extends React.PureComponent {
|
class PublishPage extends React.PureComponent {
|
||||||
scrollToTop = () => {
|
scrollToTop = () => {
|
||||||
// #content wraps every <Page>
|
const mainContent = document.querySelector('main');
|
||||||
const mainContent = document.getElementById('content');
|
|
||||||
if (mainContent) {
|
if (mainContent) {
|
||||||
mainContent.scrollTop = 0; // It would be nice to animate this
|
mainContent.scrollTop = 0; // It would be nice to animate this
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
const { balance, totalRewardValue } = this.props;
|
||||||
|
const totalRewardRounded = Math.floor(totalRewardValue / 10) * 10;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Page>
|
<Page>
|
||||||
|
{balance === 0 && (
|
||||||
|
<Fragment>
|
||||||
|
<Yrbl
|
||||||
|
title={__("You can't publish things quite yet")}
|
||||||
|
subtitle={
|
||||||
|
<Fragment>
|
||||||
|
<p>
|
||||||
|
{__(
|
||||||
|
'LBRY uses a blockchain, which is a fancy way of saying that users (you) are in control of your data.'
|
||||||
|
)}
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
{__('Because of the blockchain, some actions require LBRY credits')} (
|
||||||
|
<LbcSymbol />
|
||||||
|
).
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<LbcSymbol />{' '}
|
||||||
|
{__(
|
||||||
|
'allows you to do some neat things, like paying your favorite creators for their content. And no company can stop you.'
|
||||||
|
)}
|
||||||
|
</p>
|
||||||
|
</Fragment>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<section className="card card--section">
|
||||||
|
<header className="card__header">
|
||||||
|
<h1 className="card__title">{__('LBRY Credits Required')}</h1>
|
||||||
|
</header>
|
||||||
|
<p className="card__subtitle">
|
||||||
|
{__(' There are a variety of ways to get credits, including more than')}{' '}
|
||||||
|
<CreditAmount inheritStyle amount={totalRewardRounded} />{' '}
|
||||||
|
{__('in free rewards for participating in the LBRY beta.')}
|
||||||
|
</p>
|
||||||
|
<div className="card__actions">
|
||||||
|
<Button button="link" navigate="/$/rewards" label={__('Checkout the rewards')} />
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</Fragment>
|
||||||
|
)}
|
||||||
<PublishForm {...this.props} scrollToTop={this.scrollToTop} />
|
<PublishForm {...this.props} scrollToTop={this.scrollToTop} />
|
||||||
</Page>
|
</Page>
|
||||||
);
|
);
|
||||||
|
|
|
@ -272,7 +272,6 @@ export function doPurchaseUri(
|
||||||
|
|
||||||
if (cost > balance) {
|
if (cost > balance) {
|
||||||
dispatch(doSetPlayingUri(null));
|
dispatch(doSetPlayingUri(null));
|
||||||
dispatch(doOpenModal(MODALS.INSUFFICIENT_CREDITS));
|
|
||||||
Promise.resolve();
|
Promise.resolve();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
.card {
|
.card {
|
||||||
background-color: $lbry-white;
|
background-color: $lbry-white;
|
||||||
border: 1px solid $lbry-gray-1;
|
border: 1px solid $lbry-gray-1;
|
||||||
margin-bottom: var(--spacing-vertical-large);
|
margin-bottom: var(--spacing-vertical-xlarge);
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
html[data-mode='dark'] & {
|
html[data-mode='dark'] & {
|
||||||
|
@ -84,6 +84,11 @@
|
||||||
p:not(:last-child) {
|
p:not(:last-child) {
|
||||||
margin-bottom: var(--spacing-vertical-medium);
|
margin-bottom: var(--spacing-vertical-medium);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.badge {
|
||||||
|
bottom: -0.15rem;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// C A R D
|
// C A R D
|
||||||
|
@ -92,10 +97,6 @@
|
||||||
.card__header {
|
.card__header {
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
+ .card__content {
|
|
||||||
margin-top: var(--spacing-vertical-medium);
|
|
||||||
}
|
|
||||||
|
|
||||||
&:not(.card__header--flat) {
|
&:not(.card__header--flat) {
|
||||||
margin-bottom: var(--spacing-vertical-small);
|
margin-bottom: var(--spacing-vertical-small);
|
||||||
}
|
}
|
||||||
|
@ -195,6 +196,10 @@
|
||||||
.button {
|
.button {
|
||||||
font-size: 1.2rem;
|
font-size: 1.2rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
+ .card__content {
|
||||||
|
margin-top: var(--spacing-vertical-medium);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.card__title--flex {
|
.card__title--flex {
|
||||||
|
|
|
@ -67,6 +67,11 @@
|
||||||
background-color: $lbry-grape-3;
|
background-color: $lbry-grape-3;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.card__media--disabled {
|
||||||
|
opacity: 0.5;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
.content__loading {
|
.content__loading {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
|
|
@ -81,6 +81,14 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.header__navigation-item--active {
|
||||||
|
background-image: linear-gradient(
|
||||||
|
to bottom,
|
||||||
|
transparent 0%,
|
||||||
|
mix(transparent, $lbry-teal-3, 70%) 90%
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
.header__navigation-item--back,
|
.header__navigation-item--back,
|
||||||
.header__navigation-item--forward,
|
.header__navigation-item--forward,
|
||||||
.header__navigation-item--home,
|
.header__navigation-item--home,
|
||||||
|
|
|
@ -10,6 +10,16 @@
|
||||||
.media__properties {
|
.media__properties {
|
||||||
height: 1em;
|
height: 1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
box-shadow: 0 0 50px rgba($lbry-black, 0.08);
|
||||||
|
background-color: rgba($lbry-black, 0.04);
|
||||||
|
|
||||||
|
html[data-mode='dark'] & {
|
||||||
|
box-shadow: 0 0 50px rgba($lbry-white, 0.07);
|
||||||
|
background-color: rgba($lbry-white, 0.03);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// M E D I A
|
// M E D I A
|
||||||
|
@ -145,6 +155,10 @@
|
||||||
opacity: 0.6;
|
opacity: 0.6;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.media__insufficient-credits {
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
// M E D I A
|
// M E D I A
|
||||||
// A C T I O N S
|
// A C T I O N S
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
.yrbl-wrap {
|
.yrbl__wrap {
|
||||||
align-items: center;
|
align-items: center;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
margin-bottom: var(--spacing-vertical-large);
|
margin-bottom: var(--spacing-vertical-xlarge);
|
||||||
}
|
}
|
||||||
|
|
||||||
.yrbl {
|
.yrbl {
|
||||||
|
@ -12,6 +12,10 @@
|
||||||
margin-right: var(--spacing-vertical-large);
|
margin-right: var(--spacing-vertical-large);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.yrbl__content {
|
||||||
|
max-width: 500px;
|
||||||
|
}
|
||||||
|
|
||||||
.yrbl--first-run {
|
.yrbl--first-run {
|
||||||
align-self: center;
|
align-self: center;
|
||||||
height: 250px;
|
height: 250px;
|
||||||
|
|
|
@ -170,7 +170,7 @@ code {
|
||||||
}
|
}
|
||||||
|
|
||||||
html[data-mode='dark'] & {
|
html[data-mode='dark'] & {
|
||||||
background-color: $lbry-yellow-4;
|
background-color: $lbry-yellow-3;
|
||||||
color: $lbry-black;
|
color: $lbry-black;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,7 @@ $large-breakpoint: 1921px;
|
||||||
--spacing-vertical-small: calc(2rem / 3);
|
--spacing-vertical-small: calc(2rem / 3);
|
||||||
--spacing-vertical-medium: calc(2rem / 2);
|
--spacing-vertical-medium: calc(2rem / 2);
|
||||||
--spacing-vertical-large: 2rem;
|
--spacing-vertical-large: 2rem;
|
||||||
|
--spacing-vertical-xlarge: 3rem;
|
||||||
|
|
||||||
--file-page-max-width: 1787px;
|
--file-page-max-width: 1787px;
|
||||||
--file-max-height: 788px;
|
--file-max-height: 788px;
|
||||||
|
|
Loading…
Reference in a new issue
The
<Page>
component contained<main>
too. It makes more sense to keep there.