confirmation cleanup

This commit is contained in:
Sean Yesmunt 2020-06-15 10:04:44 -04:00
parent 01e0a2a5db
commit deb4107dd0
8 changed files with 146 additions and 167 deletions

View file

@ -135,7 +135,7 @@
"imagesloaded": "^4.1.4", "imagesloaded": "^4.1.4",
"json-loader": "^0.5.4", "json-loader": "^0.5.4",
"lbry-format": "https://github.com/lbryio/lbry-format.git", "lbry-format": "https://github.com/lbryio/lbry-format.git",
"lbry-redux": "lbryio/lbry-redux#70c2ffc0bd8b69e44fc867b98c4763f387494c4d", "lbry-redux": "lbryio/lbry-redux#f8ac5359d9d05fba2c3a536003a9d4c64b86c9f0",
"lbryinc": "lbryio/lbryinc#3ceb09549cb5ec22927ce3bea44ae8dbe2e4a006", "lbryinc": "lbryio/lbryinc#3ceb09549cb5ec22927ce3bea44ae8dbe2e4a006",
"lint-staged": "^7.0.2", "lint-staged": "^7.0.2",
"localforage": "^1.7.1", "localforage": "^1.7.1",

View file

@ -8,6 +8,7 @@ import {
SETTINGS, SETTINGS,
selectMyChannelClaims, selectMyChannelClaims,
makeSelectClaimIsMine, makeSelectClaimIsMine,
selectFetchingMyChannels,
} from 'lbry-redux'; } from 'lbry-redux';
import WalletSendTip from './view'; import WalletSendTip from './view';
import { doOpenModal, doHideModal } from 'redux/actions/app'; import { doOpenModal, doHideModal } from 'redux/actions/app';
@ -23,12 +24,13 @@ const select = (state, props) => ({
instantTipMax: makeSelectClientSetting(SETTINGS.INSTANT_PURCHASE_MAX)(state), instantTipMax: makeSelectClientSetting(SETTINGS.INSTANT_PURCHASE_MAX)(state),
channels: selectMyChannelClaims(state), channels: selectMyChannelClaims(state),
claimIsMine: makeSelectClaimIsMine(props.uri)(state), claimIsMine: makeSelectClaimIsMine(props.uri)(state),
fetchingChannels: selectFetchingMyChannels(state),
}); });
const perform = dispatch => ({ const perform = dispatch => ({
openModal: (modal, props) => dispatch(doOpenModal(modal, props)), openModal: (modal, props) => dispatch(doOpenModal(modal, props)),
closeModal: () => dispatch(doHideModal()), closeModal: () => dispatch(doHideModal()),
sendSupport: (amount, claimId, isSupport) => dispatch(doSendTip(amount, claimId, isSupport)), sendSupport: (params, isSupport) => dispatch(doSendTip(params, isSupport)),
}); });
export default withRouter(connect(select, perform)(WalletSendTip)); export default withRouter(connect(select, perform)(WalletSendTip));

View file

@ -1,11 +1,10 @@
// @flow // @flow
import * as MODALS from 'constants/modal_types';
import * as ICONS from 'constants/icons'; import * as ICONS from 'constants/icons';
import * as PAGES from 'constants/pages'; import * as PAGES from 'constants/pages';
import React from 'react'; import React from 'react';
import Button from 'component/button'; import Button from 'component/button';
import { FormField, Form } from 'component/common/form'; import { FormField, Form } from 'component/common/form';
import { MINIMUM_PUBLISH_BID } from 'constants/claim'; import { MINIMUM_PUBLISH_BID, CHANNEL_ANONYMOUS } from 'constants/claim';
import useIsMobile from 'effects/use-is-mobile'; import useIsMobile from 'effects/use-is-mobile';
import CreditAmount from 'component/common/credit-amount'; import CreditAmount from 'component/common/credit-amount';
import I18nMessage from 'component/i18nMessage'; import I18nMessage from 'component/i18nMessage';
@ -18,17 +17,19 @@ import usePersistedState from 'effects/use-persisted-state';
const DEFAULT_TIP_AMOUNTS = [5, 25, 100, 1000]; const DEFAULT_TIP_AMOUNTS = [5, 25, 100, 1000];
type SupportParams = { amount: number, claim_id: string, channel_id?: string };
type Props = { type Props = {
uri: string, uri: string,
claimIsMine: boolean, claimIsMine: boolean,
title: string, title: string,
claim: StreamClaim, claim: StreamClaim,
isPending: boolean, isPending: boolean,
sendSupport: (number, string, boolean) => void, sendSupport: (SupportParams, boolean) => void,
closeModal: () => void, closeModal: () => void,
balance: number, balance: number,
isSupport: boolean, isSupport: boolean,
openModal: (id: string, { tipAmount: number, claimId: string, isSupport: boolean }) => void, fetchingChannels: boolean,
instantTipEnabled: boolean, instantTipEnabled: boolean,
instantTipMax: { amount: number, currency: string }, instantTipMax: { amount: number, currency: string },
channels: ?Array<ChannelClaim>, channels: ?Array<ChannelClaim>,
@ -44,19 +45,21 @@ function WalletSendTip(props: Props) {
claim = {}, claim = {},
instantTipEnabled, instantTipEnabled,
instantTipMax, instantTipMax,
openModal,
sendSupport, sendSupport,
closeModal, closeModal,
channels, channels,
fetchingChannels,
} = props; } = props;
const [tipAmount, setTipAmount] = React.useState(DEFAULT_TIP_AMOUNTS[0]); const [tipAmount, setTipAmount] = React.useState(DEFAULT_TIP_AMOUNTS[0]);
const [tipError, setTipError] = React.useState(); const [tipError, setTipError] = React.useState();
const [isSupport, setIsSupport] = React.useState(claimIsMine); const [isSupport, setIsSupport] = React.useState(claimIsMine);
const [showMore, setShowMore] = React.useState(false); const [showMore, setShowMore] = React.useState(false);
const [isConfirming, setIsConfirming] = React.useState(false);
const isMobile = useIsMobile(); const isMobile = useIsMobile();
const [selectedChannel, setSelectedChannel] = usePersistedState('comment-support:channel'); const [selectedChannel, setSelectedChannel] = usePersistedState('comment-support:channel');
const { claim_id: claimId } = claim; const { claim_id: claimId } = claim;
const { channelName } = parseURI(uri);
const channelStrings = channels && channels.map(channel => channel.permanent_url).join(','); const channelStrings = channels && channels.map(channel => channel.permanent_url).join(',');
React.useEffect(() => { React.useEffect(() => {
if (!selectedChannel && channelStrings) { if (!selectedChannel && channelStrings) {
@ -89,11 +92,27 @@ function WalletSendTip(props: Props) {
}, [tipAmount, balance, setTipError]); }, [tipAmount, balance, setTipError]);
function sendSupportOrConfirm(instantTipMaxAmount = null) { function sendSupportOrConfirm(instantTipMaxAmount = null) {
if (!isSupport && (!instantTipMaxAmount || !instantTipEnabled || tipAmount > instantTipMaxAmount)) { let selectedChannelId;
const modalProps = { uri, tipAmount, claimId, title, isSupport }; if (selectedChannel !== CHANNEL_ANONYMOUS) {
openModal(MODALS.CONFIRM_SEND_TIP, modalProps); const selectedChannelClaim = channels && channels.find(channelClaim => channelClaim.name === selectedChannel);
if (selectedChannelClaim) {
selectedChannelId = selectedChannelClaim.claim_id;
}
}
if (
!isSupport &&
!isConfirming &&
(!instantTipMaxAmount || !instantTipEnabled || tipAmount > instantTipMaxAmount)
) {
setIsConfirming(true);
} else { } else {
sendSupport(tipAmount, claimId, isSupport); const supportParams: SupportParams = { amount: tipAmount, claim_id: claimId };
if (selectedChannelId) {
supportParams.channel_id = selectedChannelId;
}
sendSupport(supportParams, isSupport);
closeModal(); closeModal();
} }
} }
@ -147,94 +166,114 @@ function WalletSendTip(props: Props) {
</React.Fragment> </React.Fragment>
} }
actions={ actions={
<> isConfirming ? (
<div className="section"> <>
<SelectChannel <div className="section section--padded card--inline confirm__wrapper">
label="Channel to show support as" <div className="section">
channel={selectedChannel} <div className="confirm__label">{__('To')}</div>
onChannelChange={newChannel => setSelectedChannel(newChannel)} <div className="confirm__value">{channelName || title}</div>
/> <div className="confirm__label">{__('From')}</div>
</div> <div className="confirm__value">{selectedChannel}</div>
<div className="confirm__label">{__(isSupport ? 'Supporting' : 'Tipping')}</div>
<div className="confirm__value">{tipAmount} LBC</div>
</div>
</div>
<div className="section__actions">
<Button autoFocus button="primary" disabled={isPending} label={__('Confirm')} />
<Button button="link" label={__('Cancel')} onClick={() => setIsConfirming(false)} />
</div>
</>
) : (
<>
<div className="section">
<SelectChannel
label="Channel to show support as"
channel={selectedChannel}
onChannelChange={newChannel => setSelectedChannel(newChannel)}
/>
</div>
<div className="section"> <div className="section">
{DEFAULT_TIP_AMOUNTS.map(amount => ( {DEFAULT_TIP_AMOUNTS.map(amount => (
<Button
key={amount}
disabled={amount > balance}
button="alt"
className={classnames('button-toggle', {
'button-toggle--active': tipAmount === amount,
'button-toggle--disabled': amount > balance,
})}
label={`${amount} LBC`}
onClick={() => setTipAmount(amount)}
/>
))}
<Button <Button
key={amount}
disabled={amount > balance}
button="alt" button="alt"
className={classnames('button-toggle', { className={classnames('button-toggle', {
'button-toggle--active': tipAmount === amount, 'button-toggle--active': !DEFAULT_TIP_AMOUNTS.includes(tipAmount),
'button-toggle--disabled': amount > balance,
})} })}
label={`${amount} LBC`} label={__('Custom')}
onClick={() => setTipAmount(amount)} onClick={() => setShowMore(true)}
/>
))}
<Button
button="alt"
className={classnames('button-toggle', {
'button-toggle--active': !DEFAULT_TIP_AMOUNTS.includes(tipAmount),
})}
label={__('Custom')}
onClick={() => setShowMore(true)}
/>
</div>
{showMore && (
<div className="section">
<FormField
autoFocus
name="tip-input"
label={
<React.Fragment>
{'Custom support amount'}{' '}
{isMobile && (
<I18nMessage tokens={{ lbc_balance: <CreditAmount badge={false} amount={balance} /> }}>
(%lbc_balance% available)
</I18nMessage>
)}
</React.Fragment>
}
className="form-field--price-amount"
error={tipError}
min="0"
step="any"
type="number"
placeholder="1.23"
onChange={event => handleSupportPriceChange(event)}
/> />
</div> </div>
)}
<div className="section__actions"> {showMore && (
<Button <div className="section">
autoFocus <FormField
icon={isSupport ? undefined : ICONS.SUPPORT} autoFocus
button="primary" name="tip-input"
type="submit" label={
disabled={isPending || tipError || !tipAmount} <React.Fragment>
label={ {'Custom support amount'}{' '}
isSupport {isMobile && (
? __('Send Revokable Support') <I18nMessage tokens={{ lbc_balance: <CreditAmount badge={false} amount={balance} /> }}>
: __('Send a %amount% Tip', { amount: tipAmount ? `${tipAmount} LBC` : '' }) (%lbc_balance% available)
} </I18nMessage>
/> )}
{!claimIsMine && ( </React.Fragment>
<FormField }
name="toggle-is-support" className="form-field--price-amount"
type="checkbox" error={tipError}
label={__('Make this support permanent')} min="0"
checked={!isSupport} step="any"
onChange={() => setIsSupport(!isSupport)} type="number"
/> placeholder="1.23"
onChange={event => handleSupportPriceChange(event)}
/>
</div>
)} )}
</div>
{DEFAULT_TIP_AMOUNTS.some(val => val > balance) && ( <div className="section__actions">
<div className="section"> <Button
<Button button="link" label={__('Buy More LBC')} /> autoFocus
icon={isSupport ? undefined : ICONS.SUPPORT}
button="primary"
type="submit"
disabled={fetchingChannels || isPending || tipError || !tipAmount}
label={
isSupport
? __('Send Revokable Support')
: __('Send a %amount% Tip', { amount: tipAmount ? `${tipAmount} LBC` : '' })
}
/>
{fetchingChannels && <span className="help">{__('Loading your channels...')}</span>}
{!claimIsMine && !fetchingChannels && (
<FormField
name="toggle-is-support"
type="checkbox"
label={__('Make this support permanent')}
checked={!isSupport}
onChange={() => setIsSupport(!isSupport)}
/>
)}
</div> </div>
)} {DEFAULT_TIP_AMOUNTS.some(val => val > balance) && (
</> <div className="section">
<Button button="link" label={__('Buy More LBC')} />
</div>
)}
</>
)
} }
/> />
)} )}

View file

@ -1,11 +0,0 @@
import { connect } from 'react-redux';
import { doSendTip } from 'lbry-redux';
import { doHideModal } from 'redux/actions/app';
import ModalConfirmSendTip from './view';
const perform = dispatch => ({
closeModal: () => dispatch(doHideModal()),
sendSupport: (tipAmount, claimId, isSupport) => dispatch(doSendTip(tipAmount, claimId, isSupport)),
});
export default connect(null, perform)(ModalConfirmSendTip);

View file

@ -1,61 +0,0 @@
// @flow
import React from 'react';
import { parseURI } from 'lbry-redux';
import Button from 'component/button';
import { Form } from 'component/common/form';
import { Modal } from 'modal/modal';
import Card from 'component/common/card';
type Props = {
uri: string,
claimId: string,
title: string,
tipAmount: number,
isSupport: boolean,
closeModal: () => void,
sendSupport: (number, string, boolean) => void,
};
class ModalConfirmSendTip extends React.PureComponent<Props> {
onConfirmed() {
const { closeModal, sendSupport, tipAmount, claimId, isSupport } = this.props;
sendSupport(tipAmount, claimId, isSupport);
closeModal();
}
render() {
const { tipAmount, title, isSupport, closeModal, uri } = this.props;
const cardTitle = __(isSupport ? 'Confirm Support' : 'Confirm Tip');
const { channelName } = parseURI(uri);
return (
<Modal isOpen type="card" onAborted={closeModal} contentLabel={cardTitle}>
<Form onSubmit={() => this.onConfirmed()}>
<Card
title={cardTitle}
body={
<>
<div className="section">
<label>{__(isSupport ? 'Supporting: ' : 'Tipping: ')}</label>
<blockquote>{tipAmount} LBC</blockquote>
</div>
<div className="section">
<label>{__('To: ')}</label>
<blockquote>{title || channelName}</blockquote>
</div>
</>
}
actions={
<div className="section__actions">
<Button autoFocus button="primary" type="submit" label={__('Send')} />
<Button button="link" label={__('Cancel')} onClick={closeModal} />
</div>
}
/>
</Form>
</Modal>
);
}
}
export default ModalConfirmSendTip;

View file

@ -19,7 +19,6 @@ import ModalFirstSubscription from 'modal/modalFirstSubscription';
import ModalConfirmTransaction from 'modal/modalConfirmTransaction'; import ModalConfirmTransaction from 'modal/modalConfirmTransaction';
import ModalSocialShare from 'modal/modalSocialShare'; import ModalSocialShare from 'modal/modalSocialShare';
import ModalSendTip from 'modal/modalSendTip'; import ModalSendTip from 'modal/modalSendTip';
import ModalConfirmSendTip from 'modal/modalConfirmSendTip';
import ModalPublish from 'modal/modalPublish'; import ModalPublish from 'modal/modalPublish';
import ModalOpenExternalResource from 'modal/modalOpenExternalResource'; import ModalOpenExternalResource from 'modal/modalOpenExternalResource';
import ModalConfirmThumbnailUpload from 'modal/modalConfirmThumbnailUpload'; import ModalConfirmThumbnailUpload from 'modal/modalConfirmThumbnailUpload';
@ -97,8 +96,6 @@ function ModalRouter(props: Props) {
return <ModalFirstSubscription {...modalProps} />; return <ModalFirstSubscription {...modalProps} />;
case MODALS.SEND_TIP: case MODALS.SEND_TIP:
return <ModalSendTip {...modalProps} />; return <ModalSendTip {...modalProps} />;
case MODALS.CONFIRM_SEND_TIP:
return <ModalConfirmSendTip {...modalProps} />;
case MODALS.SOCIAL_SHARE: case MODALS.SOCIAL_SHARE:
return <ModalSocialShare {...modalProps} />; return <ModalSocialShare {...modalProps} />;
case MODALS.PUBLISH: case MODALS.PUBLISH:

View file

@ -288,3 +288,16 @@ textarea {
.privacy-img { .privacy-img {
height: 10rem; height: 10rem;
} }
.confirm__label {
@extend label;
}
.confirm__value {
margin-bottom: var(--spacing-m);
font-size: var(--font-large);
&:last-child {
margin-bottom: 0;
}
}

View file

@ -6207,9 +6207,9 @@ lazy-val@^1.0.4:
yargs "^13.2.2" yargs "^13.2.2"
zstd-codec "^0.1.1" zstd-codec "^0.1.1"
lbry-redux@lbryio/lbry-redux#70c2ffc0bd8b69e44fc867b98c4763f387494c4d: lbry-redux@lbryio/lbry-redux#f8ac5359d9d05fba2c3a536003a9d4c64b86c9f0:
version "0.0.1" version "0.0.1"
resolved "https://codeload.github.com/lbryio/lbry-redux/tar.gz/70c2ffc0bd8b69e44fc867b98c4763f387494c4d" resolved "https://codeload.github.com/lbryio/lbry-redux/tar.gz/f8ac5359d9d05fba2c3a536003a9d4c64b86c9f0"
dependencies: dependencies:
proxy-polyfill "0.1.6" proxy-polyfill "0.1.6"
reselect "^3.0.0" reselect "^3.0.0"