replace insufficient credits modal with inline ui elements
This commit is contained in:
parent
497e499c62
commit
dcafe9ab9e
25 changed files with 202 additions and 276 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;
|
|
@ -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">
|
||||||
|
|
|
@ -41,6 +41,7 @@ export default function AppRouter() {
|
||||||
<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}`} />
|
||||||
|
|
|
@ -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,162 +31,76 @@ 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> {
|
if (error) {
|
||||||
constructor(props: Props) {
|
return <ModalError {...error} />;
|
||||||
super(props);
|
|
||||||
|
|
||||||
this.state = {
|
|
||||||
lastTransitionModal: null,
|
|
||||||
lastTransitionPage: null,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillMount() {
|
if (!modal) {
|
||||||
this.showTransitionModals(this.props);
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillReceiveProps(nextProps: Props) {
|
const { id, modalProps } = modal;
|
||||||
this.showTransitionModals(nextProps);
|
|
||||||
}
|
|
||||||
|
|
||||||
showTransitionModals(props: Props) {
|
switch (id) {
|
||||||
const { modal, openModal, page } = props;
|
case MODALS.UPGRADE:
|
||||||
|
return <ModalUpgrade {...modalProps} />;
|
||||||
if (modal) {
|
case MODALS.DOWNLOADING:
|
||||||
return;
|
return <ModalDownloading {...modalProps} />;
|
||||||
}
|
case MODALS.AUTO_UPDATE_DOWNLOADED:
|
||||||
|
return <ModalAutoUpdateDownloaded {...modalProps} />;
|
||||||
const transitionModal = [this.checkShowCreditIntro].reduce(
|
case MODALS.AUTO_UPDATE_CONFIRM:
|
||||||
(acc, func) => (!acc ? func.bind(this)(props) : acc),
|
return <ModalAutoUpdateConfirm {...modalProps} />;
|
||||||
false
|
case MODALS.ERROR:
|
||||||
);
|
return <ModalError {...modalProps} />;
|
||||||
|
case MODALS.FILE_TIMEOUT:
|
||||||
if (
|
return <ModalFileTimeout {...modalProps} />;
|
||||||
transitionModal &&
|
case MODALS.WELCOME:
|
||||||
(transitionModal !== this.state.lastTransitionModal || page !== this.state.lastTransitionPage)
|
return <ModalWelcome {...modalProps} />;
|
||||||
) {
|
case MODALS.FIRST_REWARD:
|
||||||
openModal(transitionModal);
|
return <ModalFirstReward {...modalProps} />;
|
||||||
this.setState({
|
case MODALS.AUTHENTICATION_FAILURE:
|
||||||
lastTransitionModal: transitionModal,
|
return <ModalAuthFailure {...modalProps} />;
|
||||||
lastTransitionPage: page,
|
case MODALS.TRANSACTION_FAILED:
|
||||||
});
|
return <ModalTransactionFailed {...modalProps} />;
|
||||||
}
|
case MODALS.REWARD_APPROVAL_REQUIRED:
|
||||||
}
|
return <ModalRewardApprovalRequired {...modalProps} />;
|
||||||
|
case MODALS.CONFIRM_FILE_REMOVE:
|
||||||
checkShowCreditIntro(props: Props) {
|
return <ModalRemoveFile {...modalProps} />;
|
||||||
// @if TARGET='app'
|
case MODALS.AFFIRM_PURCHASE:
|
||||||
// This doesn't make sense to show until the web has wallet support
|
return <ModalAffirmPurchase {...modalProps} />;
|
||||||
const { balance, page, isCreditIntroAcknowledged } = props;
|
case MODALS.CONFIRM_CLAIM_REVOKE:
|
||||||
|
return <ModalRevokeClaim {...modalProps} />;
|
||||||
if (
|
case MODALS.PHONE_COLLECTION:
|
||||||
balance === 0 &&
|
return <ModalPhoneCollection {...modalProps} />;
|
||||||
!isCreditIntroAcknowledged &&
|
case MODALS.FIRST_SUBSCRIPTION:
|
||||||
(['send', 'publish'].includes(page) || this.isPaidShowPage(props))
|
return <ModalFirstSubscription {...modalProps} />;
|
||||||
) {
|
case MODALS.SEND_TIP:
|
||||||
return MODALS.INSUFFICIENT_CREDITS;
|
return <ModalSendTip {...modalProps} />;
|
||||||
}
|
case MODALS.SOCIAL_SHARE:
|
||||||
// @endif
|
return <ModalSocialShare {...modalProps} />;
|
||||||
|
case MODALS.PUBLISH:
|
||||||
return undefined;
|
return <ModalPublish {...modalProps} />;
|
||||||
}
|
case MODALS.CONFIRM_EXTERNAL_LINK:
|
||||||
|
return <ModalOpenExternalLink {...modalProps} />;
|
||||||
isPaidShowPage(props: Props) {
|
case MODALS.CONFIRM_TRANSACTION:
|
||||||
const { page, showPageCost } = props;
|
return <ModalConfirmTransaction {...modalProps} />;
|
||||||
// Fix me
|
case MODALS.CONFIRM_THUMBNAIL_UPLOAD:
|
||||||
return page === 'show' && showPageCost > 0;
|
return <ModalConfirmThumbnailUpload {...modalProps} />;
|
||||||
}
|
case MODALS.WALLET_ENCRYPT:
|
||||||
|
return <ModalWalletEncrypt {...modalProps} />;
|
||||||
render() {
|
case MODALS.WALLET_DECRYPT:
|
||||||
const { modal, error } = this.props;
|
return <ModalWalletDecrypt {...modalProps} />;
|
||||||
|
case MODALS.WALLET_UNLOCK:
|
||||||
if (error) {
|
return <ModalWalletUnlock {...modalProps} />;
|
||||||
return <ModalError {...error} />;
|
case MODALS.REWARD_GENERATED_CODE:
|
||||||
}
|
return <ModalRewardCode {...modalProps} />;
|
||||||
|
default:
|
||||||
if (!modal) {
|
|
||||||
return null;
|
return null;
|
||||||
}
|
|
||||||
|
|
||||||
const { id, modalProps } = modal;
|
|
||||||
|
|
||||||
switch (id) {
|
|
||||||
case MODALS.UPGRADE:
|
|
||||||
return <ModalUpgrade {...modalProps} />;
|
|
||||||
case MODALS.DOWNLOADING:
|
|
||||||
return <ModalDownloading {...modalProps} />;
|
|
||||||
case MODALS.AUTO_UPDATE_DOWNLOADED:
|
|
||||||
return <ModalAutoUpdateDownloaded {...modalProps} />;
|
|
||||||
case MODALS.AUTO_UPDATE_CONFIRM:
|
|
||||||
return <ModalAutoUpdateConfirm {...modalProps} />;
|
|
||||||
case MODALS.ERROR:
|
|
||||||
return <ModalError {...modalProps} />;
|
|
||||||
case MODALS.FILE_TIMEOUT:
|
|
||||||
return <ModalFileTimeout {...modalProps} />;
|
|
||||||
case MODALS.INSUFFICIENT_CREDITS:
|
|
||||||
return <ModalCreditIntro {...modalProps} />;
|
|
||||||
case MODALS.WELCOME:
|
|
||||||
return <ModalWelcome {...modalProps} />;
|
|
||||||
case MODALS.FIRST_REWARD:
|
|
||||||
return <ModalFirstReward {...modalProps} />;
|
|
||||||
case MODALS.AUTHENTICATION_FAILURE:
|
|
||||||
return <ModalAuthFailure {...modalProps} />;
|
|
||||||
case MODALS.TRANSACTION_FAILED:
|
|
||||||
return <ModalTransactionFailed {...modalProps} />;
|
|
||||||
case MODALS.REWARD_APPROVAL_REQUIRED:
|
|
||||||
return <ModalRewardApprovalRequired {...modalProps} />;
|
|
||||||
case MODALS.CONFIRM_FILE_REMOVE:
|
|
||||||
return <ModalRemoveFile {...modalProps} />;
|
|
||||||
case MODALS.AFFIRM_PURCHASE:
|
|
||||||
return <ModalAffirmPurchase {...modalProps} />;
|
|
||||||
case MODALS.CONFIRM_CLAIM_REVOKE:
|
|
||||||
return <ModalRevokeClaim {...modalProps} />;
|
|
||||||
case MODALS.PHONE_COLLECTION:
|
|
||||||
return <ModalPhoneCollection {...modalProps} />;
|
|
||||||
case MODALS.FIRST_SUBSCRIPTION:
|
|
||||||
return <ModalFirstSubscription {...modalProps} />;
|
|
||||||
case MODALS.SEND_TIP:
|
|
||||||
return <ModalSendTip {...modalProps} />;
|
|
||||||
case MODALS.SOCIAL_SHARE:
|
|
||||||
return <ModalSocialShare {...modalProps} />;
|
|
||||||
case MODALS.PUBLISH:
|
|
||||||
return <ModalPublish {...modalProps} />;
|
|
||||||
case MODALS.CONFIRM_EXTERNAL_LINK:
|
|
||||||
return <ModalOpenExternalLink {...modalProps} />;
|
|
||||||
case MODALS.CONFIRM_TRANSACTION:
|
|
||||||
return <ModalConfirmTransaction {...modalProps} />;
|
|
||||||
case MODALS.CONFIRM_THUMBNAIL_UPLOAD:
|
|
||||||
return <ModalConfirmThumbnailUpload {...modalProps} />;
|
|
||||||
case MODALS.WALLET_ENCRYPT:
|
|
||||||
return <ModalWalletEncrypt {...modalProps} />;
|
|
||||||
case MODALS.WALLET_DECRYPT:
|
|
||||||
return <ModalWalletDecrypt {...modalProps} />;
|
|
||||||
case MODALS.WALLET_UNLOCK:
|
|
||||||
return <ModalWalletUnlock {...modalProps} />;
|
|
||||||
case MODALS.REWARD_GENERATED_CODE:
|
|
||||||
return <ModalRewardCode {...modalProps} />;
|
|
||||||
default:
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -155,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