send tip modal
This commit is contained in:
parent
2d80c35ead
commit
b07c332c0f
9 changed files with 138 additions and 40 deletions
|
@ -26,11 +26,12 @@ type FormFieldProps = {
|
||||||
prefix?: string,
|
prefix?: string,
|
||||||
postfix?: string,
|
postfix?: string,
|
||||||
error?: string | boolean,
|
error?: string | boolean,
|
||||||
|
helper?: string | React.Node,
|
||||||
};
|
};
|
||||||
|
|
||||||
export class FormField extends React.PureComponent<FormFieldProps> {
|
export class FormField extends React.PureComponent<FormFieldProps> {
|
||||||
render() {
|
render() {
|
||||||
const { render, label, prefix, postfix, error } = this.props;
|
const { render, label, prefix, postfix, error, helper } = this.props;
|
||||||
/* eslint-disable jsx-a11y/label-has-for */
|
/* eslint-disable jsx-a11y/label-has-for */
|
||||||
// Will come back to this on the settings page
|
// Will come back to this on the settings page
|
||||||
// Need htmlFor on the label
|
// Need htmlFor on the label
|
||||||
|
@ -47,6 +48,7 @@ export class FormField extends React.PureComponent<FormFieldProps> {
|
||||||
{typeof error === 'string' ? error : __('There was an error')}
|
{typeof error === 'string' ? error : __('There was an error')}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
{helper && <div className="form-field__help">{helper}</div>}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
/* eslint-enable jsx-a11y/label-has-for */
|
/* eslint-enable jsx-a11y/label-has-for */
|
||||||
|
|
|
@ -1,66 +1,92 @@
|
||||||
// I'll come back to this
|
// @flow
|
||||||
/* eslint-disable */
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import Link from 'component/link';
|
import Button from 'component/link';
|
||||||
import { FormRow } from 'component/common/form';
|
import { FormField } from 'component/common/form';
|
||||||
import UriIndicator from 'component/uriIndicator';
|
import UriIndicator from 'component/uriIndicator';
|
||||||
|
|
||||||
class WalletSendTip extends React.PureComponent {
|
type Props = {
|
||||||
constructor(props) {
|
claim_id: string,
|
||||||
|
uri: string,
|
||||||
|
title: string,
|
||||||
|
errorMessage: string,
|
||||||
|
isPending: boolean,
|
||||||
|
sendSupport: (number, string, string) => void,
|
||||||
|
onCancel: () => void,
|
||||||
|
sendTipCallback?: () => void,
|
||||||
|
};
|
||||||
|
|
||||||
|
type State = {
|
||||||
|
amount: number,
|
||||||
|
};
|
||||||
|
|
||||||
|
class WalletSendTip extends React.PureComponent<Props, State> {
|
||||||
|
constructor(props: Props) {
|
||||||
super(props);
|
super(props);
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
amount: 0.0,
|
amount: 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
(this: any).handleSendButtonClicked = this.handleSendButtonClicked.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleSendButtonClicked() {
|
handleSendButtonClicked() {
|
||||||
const { claim_id, uri } = this.props;
|
const { claim_id: claimId, uri, sendSupport, sendTipCallback } = this.props;
|
||||||
const amount = this.state.amount;
|
const { amount } = this.state;
|
||||||
this.props.sendSupport(amount, claim_id, uri);
|
|
||||||
|
sendSupport(amount, claimId, uri);
|
||||||
|
|
||||||
|
// ex: close modal
|
||||||
|
if (sendTipCallback) {
|
||||||
|
sendTipCallback();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handleSupportPriceChange(event) {
|
handleSupportPriceChange(event: SyntheticInputEvent<*>) {
|
||||||
this.setState({
|
this.setState({
|
||||||
amount: Number(event.target.value),
|
amount: Number(event.target.value),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { errorMessage, isPending, title, uri } = this.props;
|
const { errorMessage, isPending, title, uri, onCancel } = this.props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<div className="card__title-primary">
|
<div className="card__title-primary">
|
||||||
<h1>
|
<h1>
|
||||||
{__('Send a tip')} <UriIndicator uri={uri} />
|
{__('Send a tip to')} <UriIndicator uri={uri} />
|
||||||
</h1>
|
</h1>
|
||||||
</div>
|
</div>
|
||||||
<div className="card__content">
|
<div className="card__content">
|
||||||
<FormRow
|
<FormField
|
||||||
label={__('Amount')}
|
label={__('Amount')}
|
||||||
postfix={__('LBC')}
|
postfix={__('LBC')}
|
||||||
|
error={errorMessage}
|
||||||
|
helper={
|
||||||
|
<span>
|
||||||
|
{__(`This will appear as a tip for ${title} located at ${uri}.`)}
|
||||||
|
{" "}
|
||||||
|
<Button label={__('Learn more')} fakeLink href="https://lbry.io/faq/tipping" />
|
||||||
|
</span>
|
||||||
|
}
|
||||||
|
render={() => (
|
||||||
|
<input
|
||||||
min="0"
|
min="0"
|
||||||
step="any"
|
step="any"
|
||||||
type="number"
|
type="number"
|
||||||
errorMessage={errorMessage}
|
|
||||||
helper={
|
|
||||||
<span>
|
|
||||||
{`${__('This will appear as a tip for "%s" located at %s.', title, uri)} `}
|
|
||||||
<Link label={__('Learn more')} href="https://lbry.io/faq/tipping" />
|
|
||||||
</span>
|
|
||||||
}
|
|
||||||
placeholder="1.00"
|
placeholder="1.00"
|
||||||
onChange={event => this.handleSupportPriceChange(event)}
|
onChange={event => this.handleSupportPriceChange(event)}
|
||||||
/>
|
/>
|
||||||
<div className="form-row-submit">
|
)}
|
||||||
<Link
|
|
||||||
label={__('Send')}
|
|
||||||
button="primary"
|
|
||||||
disabled={isPending}
|
|
||||||
onClick={this.handleSendButtonClicked.bind(this)}
|
|
||||||
/>
|
/>
|
||||||
<Link label={__('Cancel')} button="alt" navigate="/show" navigateParams={{ uri }} />
|
<div className="card__actions">
|
||||||
|
<Button
|
||||||
|
label={__('Send')}
|
||||||
|
disabled={isPending}
|
||||||
|
onClick={this.handleSendButtonClicked}
|
||||||
|
/>
|
||||||
|
<Button alt label={__('Cancel')} onClick={onCancel} navigateParams={{ uri }} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -69,4 +95,3 @@ class WalletSendTip extends React.PureComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
export default WalletSendTip;
|
export default WalletSendTip;
|
||||||
/* eslint-enable */
|
|
||||||
|
|
|
@ -14,3 +14,4 @@ export const TRANSACTION_FAILED = 'transaction_failed';
|
||||||
export const REWARD_APPROVAL_REQUIRED = 'reward_approval_required';
|
export const REWARD_APPROVAL_REQUIRED = 'reward_approval_required';
|
||||||
export const AFFIRM_PURCHASE = 'affirm_purchase';
|
export const AFFIRM_PURCHASE = 'affirm_purchase';
|
||||||
export const CONFIRM_CLAIM_REVOKE = 'confirmClaimRevoke';
|
export const CONFIRM_CLAIM_REVOKE = 'confirmClaimRevoke';
|
||||||
|
export const SEND_TIP = 'sendTip';
|
||||||
|
|
|
@ -14,6 +14,7 @@ import ModalAffirmPurchase from 'modal/modalAffirmPurchase';
|
||||||
import ModalRevokeClaim from 'modal/modalRevokeClaim';
|
import ModalRevokeClaim from 'modal/modalRevokeClaim';
|
||||||
import ModalEmailCollection from '../modalEmailCollection';
|
import ModalEmailCollection from '../modalEmailCollection';
|
||||||
import ModalPhoneCollection from '../modalPhoneCollection';
|
import ModalPhoneCollection from '../modalPhoneCollection';
|
||||||
|
import ModalSendTip from '../modalSendTip';
|
||||||
import * as modals from 'constants/modal_types';
|
import * as modals from 'constants/modal_types';
|
||||||
|
|
||||||
class ModalRouter extends React.PureComponent {
|
class ModalRouter extends React.PureComponent {
|
||||||
|
@ -129,6 +130,8 @@ class ModalRouter extends React.PureComponent {
|
||||||
return <ModalPhoneCollection {...modalProps} />;
|
return <ModalPhoneCollection {...modalProps} />;
|
||||||
case modals.EMAIL_COLLECTION:
|
case modals.EMAIL_COLLECTION:
|
||||||
return <ModalEmailCollection {...modalProps} />;
|
return <ModalEmailCollection {...modalProps} />;
|
||||||
|
case modals.SEND_TIP:
|
||||||
|
return <ModalSendTip {...modalProps} />;
|
||||||
default:
|
default:
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
9
src/renderer/modal/modalSendTip/index.js
Normal file
9
src/renderer/modal/modalSendTip/index.js
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
import { doCloseModal } from 'redux/actions/app';
|
||||||
|
import ModalSendTip from './view';
|
||||||
|
|
||||||
|
const perform = dispatch => ({
|
||||||
|
closeModal: () => dispatch(doCloseModal()),
|
||||||
|
});
|
||||||
|
|
||||||
|
export default connect(null, perform)(ModalSendTip);
|
23
src/renderer/modal/modalSendTip/view.jsx
Normal file
23
src/renderer/modal/modalSendTip/view.jsx
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
// @flow
|
||||||
|
import React from 'react';
|
||||||
|
import { Modal } from 'modal/modal';
|
||||||
|
import SendTip from 'component/walletSendTip';
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
closeModal: () => void,
|
||||||
|
uri: string,
|
||||||
|
};
|
||||||
|
|
||||||
|
class ModalSendTip extends React.PureComponent<Props> {
|
||||||
|
render() {
|
||||||
|
const { closeModal, uri } = this.props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal isOpen type="custom">
|
||||||
|
<SendTip uri={uri} onCancel={closeModal} sendTipCallback={closeModal} />
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ModalSendTip;
|
|
@ -12,6 +12,7 @@ import {
|
||||||
import { makeSelectCostInfoForUri } from 'redux/selectors/cost_info';
|
import { makeSelectCostInfoForUri } from 'redux/selectors/cost_info';
|
||||||
import { selectShowNsfw } from 'redux/selectors/settings';
|
import { selectShowNsfw } from 'redux/selectors/settings';
|
||||||
import { selectMediaPaused } from 'redux/selectors/media';
|
import { selectMediaPaused } from 'redux/selectors/media';
|
||||||
|
import { doOpenModal } from 'redux/actions/app';
|
||||||
import FilePage from './view';
|
import FilePage from './view';
|
||||||
|
|
||||||
const select = (state, props) => ({
|
const select = (state, props) => ({
|
||||||
|
@ -30,6 +31,7 @@ const perform = dispatch => ({
|
||||||
navigate: (path, params) => dispatch(doNavigate(path, params)),
|
navigate: (path, params) => dispatch(doNavigate(path, params)),
|
||||||
fetchFileInfo: uri => dispatch(doFetchFileInfo(uri)),
|
fetchFileInfo: uri => dispatch(doFetchFileInfo(uri)),
|
||||||
fetchCostInfo: uri => dispatch(doFetchCostInfoForUri(uri)),
|
fetchCostInfo: uri => dispatch(doFetchCostInfoForUri(uri)),
|
||||||
|
openModal: (modal, props) => dispatch(doOpenModal(modal, props)),
|
||||||
});
|
});
|
||||||
|
|
||||||
export default connect(select, perform)(FilePage);
|
export default connect(select, perform)(FilePage);
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
/* eslint-disable */
|
// @flow
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import lbry from 'lbry';
|
import lbry from 'lbry';
|
||||||
import { buildURI, normalizeURI } from 'lbryURI';
|
import { buildURI, normalizeURI } from 'lbryURI';
|
||||||
|
@ -12,29 +12,58 @@ import Icon from 'component/common/icon';
|
||||||
import WalletSendTip from 'component/walletSendTip';
|
import WalletSendTip from 'component/walletSendTip';
|
||||||
import DateTime from 'component/dateTime';
|
import DateTime from 'component/dateTime';
|
||||||
import * as icons from 'constants/icons';
|
import * as icons from 'constants/icons';
|
||||||
import Link from 'component/link';
|
import Button from 'component/link';
|
||||||
import SubscribeButton from 'component/subscribeButton';
|
import SubscribeButton from 'component/subscribeButton';
|
||||||
import Page from 'component/page';
|
import Page from 'component/page';
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
import player from 'render-media';
|
import player from 'render-media';
|
||||||
|
import * as modals from 'constants/modal_types';
|
||||||
|
|
||||||
class FilePage extends React.PureComponent {
|
type Props = {
|
||||||
|
claim: {
|
||||||
|
claim_id: string,
|
||||||
|
height: number,
|
||||||
|
channel_name: string,
|
||||||
|
value: {
|
||||||
|
publisherSignature: ?{
|
||||||
|
certificateId: ?string
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
fileInfo: {},
|
||||||
|
metadata: {
|
||||||
|
title: string,
|
||||||
|
thumbnail: string,
|
||||||
|
nsfw: boolean
|
||||||
|
},
|
||||||
|
contentType: string,
|
||||||
|
uri: string,
|
||||||
|
rewardedContentClaimIds: Array<string>,
|
||||||
|
obscureNsfw: boolean,
|
||||||
|
playingUri: ?string,
|
||||||
|
isPaused: boolean,
|
||||||
|
openModal: (string, any) => void,
|
||||||
|
fetchFileInfo: (string) => void,
|
||||||
|
fetchCostInfo: (string) => void,
|
||||||
|
}
|
||||||
|
|
||||||
|
class FilePage extends React.Component<Props> {
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
this.fetchFileInfo(this.props);
|
this.fetchFileInfo(this.props);
|
||||||
this.fetchCostInfo(this.props);
|
this.fetchCostInfo(this.props);
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillReceiveProps(nextProps) {
|
componentWillReceiveProps(nextProps: Props) {
|
||||||
this.fetchFileInfo(nextProps);
|
this.fetchFileInfo(nextProps);
|
||||||
}
|
}
|
||||||
|
|
||||||
fetchFileInfo(props) {
|
fetchFileInfo(props: Props) {
|
||||||
if (props.fileInfo === undefined) {
|
if (props.fileInfo === undefined) {
|
||||||
props.fetchFileInfo(props.uri);
|
props.fetchFileInfo(props.uri);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fetchCostInfo(props) {
|
fetchCostInfo(props: Props) {
|
||||||
if (props.costInfo === undefined) {
|
if (props.costInfo === undefined) {
|
||||||
props.fetchCostInfo(props.uri);
|
props.fetchCostInfo(props.uri);
|
||||||
}
|
}
|
||||||
|
@ -51,6 +80,7 @@ class FilePage extends React.PureComponent {
|
||||||
obscureNsfw,
|
obscureNsfw,
|
||||||
playingUri,
|
playingUri,
|
||||||
isPaused,
|
isPaused,
|
||||||
|
openModal,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
// This should be included below in the page
|
// This should be included below in the page
|
||||||
|
@ -65,9 +95,9 @@ class FilePage extends React.PureComponent {
|
||||||
const shouldObscureThumbnail = obscureNsfw && metadata.nsfw;
|
const shouldObscureThumbnail = obscureNsfw && metadata.nsfw;
|
||||||
const thumbnail = metadata.thumbnail;
|
const thumbnail = metadata.thumbnail;
|
||||||
const { height, channel_name: channelName, value } = claim;
|
const { height, channel_name: channelName, value } = claim;
|
||||||
|
const mediaType = lbry.getMediaType(contentType);
|
||||||
const isPlayable =
|
const isPlayable =
|
||||||
Object.values(player.mime).indexOf(contentType) !== -1 || mediaType === 'audio';
|
Object.values(player.mime).indexOf(contentType) !== -1 || mediaType === 'audio';
|
||||||
const mediaType = lbry.getMediaType(contentType);
|
|
||||||
const channelClaimId =
|
const channelClaimId =
|
||||||
value && value.publisherSignature && value.publisherSignature.certificateId;
|
value && value.publisherSignature && value.publisherSignature.certificateId;
|
||||||
let subscriptionUri;
|
let subscriptionUri;
|
||||||
|
@ -76,7 +106,6 @@ class FilePage extends React.PureComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
const isPlaying = playingUri === uri && !isPaused;
|
const isPlaying = playingUri === uri && !isPaused;
|
||||||
console.log('isPlaying?', isPlaying);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Page>
|
<Page>
|
||||||
|
@ -112,7 +141,12 @@ class FilePage extends React.PureComponent {
|
||||||
<div className="card__channel-info">
|
<div className="card__channel-info">
|
||||||
<UriIndicator uri={uri} link />
|
<UriIndicator uri={uri} link />
|
||||||
<div className="card__actions card__actions--no-margin">
|
<div className="card__actions card__actions--no-margin">
|
||||||
<Link alt iconRight="Send" label={__('Enjoy this? Send a tip')} />
|
<Button
|
||||||
|
alt
|
||||||
|
iconRight="Send"
|
||||||
|
label={__('Enjoy this? Send a tip')}
|
||||||
|
onClick={() => openModal(modals.SEND_TIP, { uri })}
|
||||||
|
/>
|
||||||
<SubscribeButton uri={subscriptionUri} channelName={channelName} />
|
<SubscribeButton uri={subscriptionUri} channelName={channelName} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -128,4 +162,3 @@ class FilePage extends React.PureComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
export default FilePage;
|
export default FilePage;
|
||||||
/* eslint-enable */
|
|
||||||
|
|
|
@ -121,7 +121,7 @@
|
||||||
color: var(--color-grey-dark);
|
color: var(--color-grey-dark);
|
||||||
font-size: calc(var(--font-size-subtext-multiple) * 1em);
|
font-size: calc(var(--font-size-subtext-multiple) * 1em);
|
||||||
padding-top: $spacing-vertical * 1/3;
|
padding-top: $spacing-vertical * 1/3;
|
||||||
word-break: break-all;
|
word-break: break-word;
|
||||||
font-weight: 300;
|
font-weight: 300;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue