Stripe integration fix #6850

Merged
jessopb merged 25 commits from stripe-integration-fix into master 2021-08-11 22:58:56 +02:00
7 changed files with 414 additions and 296 deletions

View file

@ -131,6 +131,7 @@ export function CommentCreate(props: Props) {
return; return;
} }
// if comment post didn't work, but tip was already made, try again to create comment
if (commentFailure && tipAmount === successTip.tipAmount) { if (commentFailure && tipAmount === successTip.tipAmount) {
handleCreateComment(successTip.txid); handleCreateComment(successTip.txid);
return; return;
@ -147,27 +148,6 @@ export function CommentCreate(props: Props) {
const activeChannelName = activeChannelClaim && activeChannelClaim.name; const activeChannelName = activeChannelClaim && activeChannelClaim.name;
const activeChannelId = activeChannelClaim && activeChannelClaim.claim_id; const activeChannelId = activeChannelClaim && activeChannelClaim.claim_id;
setIsSubmitting(true);
if (activeTab === TAB_LBC) {
// call sendTip and then run the callback from the response
// second parameter is callback
sendTip(
params,
(response) => {
const { txid } = response;
// todo: why the setTimeout?
setTimeout(() => {
handleCreateComment(txid);
}, 1500);
setSuccessTip({ txid, tipAmount });
},
() => {
// reset the frontend so people can send a new comment
setIsSubmitting(false);
}
);
} else {
// setup variables for tip API // setup variables for tip API
let channelClaimId, tipChannelName; let channelClaimId, tipChannelName;
// if there is a signing channel it's on a file // if there is a signing channel it's on a file
@ -181,14 +161,43 @@ export function CommentCreate(props: Props) {
tipChannelName = claim.name; tipChannelName = claim.name;
} }
setIsSubmitting(true);
if (activeTab === TAB_LBC) {
// call sendTip and then run the callback from the response
// second parameter is callback
sendTip(
params,
(response) => {
const { txid } = response;
// todo: why the setTimeout?
setTimeout(() => {
handleCreateComment(txid);
}, 1500);
doToast({
message: __("You sent %tipAmount% LBRY Credits as a tip to %tipChannelName%, I'm sure they appreciate it!", {
tipAmount: tipAmount, // force show decimal places
tipChannelName,
}),
});
setSuccessTip({ txid, tipAmount });
},
() => {
// reset the frontend so people can send a new comment
setIsSubmitting(false);
}
);
} else {
const sourceClaimId = claim.claim_id; const sourceClaimId = claim.claim_id;
const roundedAmount = Math.round(tipAmount * 100) / 100; const roundedAmount = Math.round(tipAmount * 100) / 100;
Lbryio.call( Lbryio.call(
'customer', 'customer',
'tip', 'tip',
{ { // round to deal with floating point precision
amount: 100 * roundedAmount, // convert from dollars to cents amount: Math.round(100 * roundedAmount), // convert from dollars to cents
creator_channel_name: tipChannelName, // creator_channel_name creator_channel_name: tipChannelName, // creator_channel_name
creator_channel_claim_id: channelClaimId, creator_channel_claim_id: channelClaimId,
tipper_channel_name: activeChannelName, tipper_channel_name: activeChannelName,

View file

@ -22,7 +22,6 @@ type Props = {
fetchingComments: boolean, fetchingComments: boolean,
doSuperChatList: (string) => void, doSuperChatList: (string) => void,
superChats: Array<Comment>, superChats: Array<Comment>,
superChatsTotalAmount: number,
myChannels: ?Array<ChannelClaim>, myChannels: ?Array<ChannelClaim>,
}; };
@ -38,38 +37,27 @@ export default function LivestreamComments(props: Props) {
embed, embed,
doCommentSocketConnect, doCommentSocketConnect,
jessopb commented 2021-08-10 21:12:41 +02:00 (Migrated from github.com)
Review

Can we use better variable names rather than comments in cases like these?

Can we use better variable names rather than comments in cases like these?
jessopb commented 2021-08-10 21:13:51 +02:00 (Migrated from github.com)
Review

destructure props with const. we usually don't mutate passed props.

destructure props with const. we usually don't mutate passed props.
jessopb commented 2021-08-10 21:15:10 +02:00 (Migrated from github.com)
Review

is it even in props?

is it even in props?
doCommentSocketDisconnect, doCommentSocketDisconnect,
comments, comments: commentsByChronologicalOrder,
doCommentList, doCommentList,
fetchingComments, fetchingComments,
doSuperChatList, doSuperChatList,
superChats,
superChatsTotalAmount,
myChannels, myChannels,
superChats: superChatsByTipAmount,
} = props; } = props;
let superChatsFiatAmount, superChatsTotalAmount;
const commentsRef = React.createRef(); const commentsRef = React.createRef();
const [scrollBottom, setScrollBottom] = React.useState(true); const [scrollBottom, setScrollBottom] = React.useState(true);
const [viewMode, setViewMode] = React.useState(VIEW_MODE_CHAT); const [viewMode, setViewMode] = React.useState(VIEW_MODE_CHAT);
const [performedInitialScroll, setPerformedInitialScroll] = React.useState(false); const [performedInitialScroll, setPerformedInitialScroll] = React.useState(false);
const claimId = claim && claim.claim_id; const claimId = claim && claim.claim_id;
const commentsLength = comments && comments.length; const commentsLength = commentsByChronologicalOrder && commentsByChronologicalOrder.length;
const commentsToDisplay = viewMode === VIEW_MODE_CHAT ? comments : superChats; const commentsToDisplay = viewMode === VIEW_MODE_CHAT ? commentsByChronologicalOrder : superChatsByTipAmount;
const discussionElement = document.querySelector('.livestream__comments'); const discussionElement = document.querySelector('.livestream__comments');
const commentElement = document.querySelector('.livestream-comment'); const commentElement = document.querySelector('.livestream-comment');
// todo: implement comment_list --mine in SDK so redux can grab with selectCommentIsMine
function isMyComment(channelId: string) {
if (myChannels != null && channelId != null) {
for (let i = 0; i < myChannels.length; i++) {
if (myChannels[i].claim_id === channelId) {
return true;
}
}
}
return false;
}
React.useEffect(() => { React.useEffect(() => {
if (claimId) { if (claimId) {
doCommentList(uri, '', 1, 75); doCommentList(uri, '', 1, 75);
@ -115,6 +103,51 @@ export default function LivestreamComments(props: Props) {
} }
}, [commentsLength, discussionElement, handleScroll, performedInitialScroll, setPerformedInitialScroll]); }, [commentsLength, discussionElement, handleScroll, performedInitialScroll, setPerformedInitialScroll]);
// sum total amounts for fiat tips and lbc tips
if (superChatsByTipAmount) {
let fiatAmount = 0;
let LBCAmount = 0;
for (const superChat of superChatsByTipAmount) {
if (superChat.is_fiat) {
fiatAmount = fiatAmount + superChat.support_amount;
} else {
LBCAmount = LBCAmount + superChat.support_amount;
}
}
superChatsFiatAmount = fiatAmount;
superChatsTotalAmount = LBCAmount;
}
let superChatsReversed;
// array of superchats organized by fiat or not first, then support amount
if (superChatsByTipAmount) {
const clonedSuperchats = JSON.parse(JSON.stringify(superChatsByTipAmount));
// sort by fiat first then by support amount
superChatsReversed = clonedSuperchats.sort(function(a, b) {
// if both are fiat, organize by support
if (a.is_fiat === b.is_fiat) {
return b.support_amount - a.support_amount;
// otherwise, if they are not both fiat, put the fiat transaction first
} else {
return (a.is_fiat === b.is_fiat) ? 0 : a.is_fiat ? -1 : 1;
}
}).reverse();
}
// todo: implement comment_list --mine in SDK so redux can grab with selectCommentIsMine
function isMyComment(channelId: string) {
if (myChannels != null && channelId != null) {
for (let i = 0; i < myChannels.length; i++) {
if (myChannels[i].claim_id === channelId) {
return true;
}
}
}
return false;
}
if (!claim) { if (!claim) {
return null; return null;
} }
@ -130,41 +163,55 @@ export default function LivestreamComments(props: Props) {
<div className="card livestream__discussion"> <div className="card livestream__discussion">
<div className="card__header--between livestream-discussion__header"> <div className="card__header--between livestream-discussion__header">
<div className="livestream-discussion__title">{__('Live discussion')}</div> <div className="livestream-discussion__title">{__('Live discussion')}</div>
{superChatsTotalAmount > 0 && ( {(superChatsTotalAmount || 0) > 0 && (
<div className="recommended-content__toggles"> <div className="recommended-content__toggles">
{/* the superchats in chronological order button */}
<Button <Button
className={classnames('button-toggle', { className={classnames('button-toggle', {
'button-toggle--active': viewMode === VIEW_MODE_CHAT, 'button-toggle--active': viewMode === VIEW_MODE_CHAT,
})} })}
label={__('Chat')} label={__('Chat')}
onClick={() => setViewMode(VIEW_MODE_CHAT)} onClick={function() {
setViewMode(VIEW_MODE_CHAT);
const livestreamCommentsDiv = document.getElementsByClassName('livestream__comments')[0];
const divHeight = livestreamCommentsDiv.scrollHeight;
livestreamCommentsDiv.scrollTop = divHeight;
}}
/> />
{/* the button to show superchats listed by most to least support amount */}
<Button <Button
className={classnames('button-toggle', { className={classnames('button-toggle', {
'button-toggle--active': viewMode === VIEW_MODE_SUPER_CHAT, 'button-toggle--active': viewMode === VIEW_MODE_SUPER_CHAT,
})} })}
label={ label={
<> <>
<CreditAmount amount={superChatsTotalAmount} size={8} /> {__('Tipped')} <CreditAmount amount={superChatsTotalAmount || 0} size={8} /> /
<CreditAmount amount={superChatsFiatAmount || 0} size={8} isFiat /> {' '}{__('Tipped')}
</> </>
} }
onClick={() => setViewMode(VIEW_MODE_SUPER_CHAT)} onClick={function() {
setViewMode(VIEW_MODE_SUPER_CHAT);
const livestreamCommentsDiv = document.getElementsByClassName('livestream__comments')[0];
const divHeight = livestreamCommentsDiv.scrollHeight;
livestreamCommentsDiv.scrollTop = divHeight * -1;
}}
/> />
</div> </div>
)} )}
</div> </div>
<> <>
{fetchingComments && !comments && ( {fetchingComments && !commentsByChronologicalOrder && (
<div className="main--empty"> <div className="main--empty">
<Spinner /> <Spinner />
</div> </div>
)} )}
<div ref={commentsRef} className="livestream__comments-wrapper"> <div ref={commentsRef} className="livestream__comments-wrapper">
{viewMode === VIEW_MODE_CHAT && superChatsTotalAmount > 0 && superChats && ( {viewMode === VIEW_MODE_CHAT && superChatsByTipAmount && (superChatsTotalAmount || 0) > 0 && (
<div className="livestream-superchats__wrapper"> <div className="livestream-superchats__wrapper">
<div className="livestream-superchats__inner"> <div className="livestream-superchats__inner">
{superChats.map((superChat: Comment) => ( {superChatsByTipAmount.map((superChat: Comment) => (
<Tooltip key={superChat.comment_id} label={superChat.comment}> <Tooltip key={superChat.comment_id} label={superChat.comment}>
<div className="livestream-superchat"> <div className="livestream-superchat">
<div className="livestream-superchat__thumbnail"> <div className="livestream-superchat__thumbnail">
@ -187,9 +234,23 @@ export default function LivestreamComments(props: Props) {
</div> </div>
)} )}
{!fetchingComments && comments.length > 0 ? ( {/* top to bottom comment display */}
{!fetchingComments && commentsByChronologicalOrder.length > 0 ? (
<div className="livestream__comments"> <div className="livestream__comments">
{commentsToDisplay.map((comment) => ( {viewMode === VIEW_MODE_CHAT && commentsToDisplay.map((comment) => (
<LivestreamComment
key={comment.comment_id}
uri={uri}
authorUri={comment.channel_url}
commentId={comment.comment_id}
message={comment.comment}
supportAmount={comment.support_amount}
isFiat={comment.is_fiat}
commentIsMine={comment.channel_id && isMyComment(comment.channel_id)}
/>
))}
{viewMode === VIEW_MODE_SUPER_CHAT && superChatsReversed && superChatsReversed.map((comment) => (
<LivestreamComment <LivestreamComment
key={comment.comment_id} key={comment.comment_id}
uri={uri} uri={uri}

View file

@ -122,7 +122,7 @@ function WalletSendTip(props: Props) {
// check if creator has an account saved // check if creator has an account saved
React.useEffect(() => { React.useEffect(() => {
var tipInputElement = document.getElementById('tip-input'); const tipInputElement = document.getElementById('tip-input');
if (tipInputElement) { tipInputElement.focus() } if (tipInputElement) { tipInputElement.focus() }
}, []); }, []);
@ -141,6 +141,8 @@ function WalletSendTip(props: Props) {
.then((accountCheckResponse) => { .then((accountCheckResponse) => {
if (accountCheckResponse === true && canReceiveFiatTip !== true) { if (accountCheckResponse === true && canReceiveFiatTip !== true) {
setCanReceiveFiatTip(true); setCanReceiveFiatTip(true);
} else {
setCanReceiveFiatTip(false);
} }
}) })
.catch(function (error) { .catch(function (error) {
@ -169,7 +171,8 @@ function WalletSendTip(props: Props) {
} }
const claimTypeText = setClaimTypeText(); const claimTypeText = setClaimTypeText();
let iconToUse, explainerText; let iconToUse;
let explainerText = '';
if (activeTab === TAB_BOOST) { if (activeTab === TAB_BOOST) {
iconToUse = ICONS.LBC; iconToUse = ICONS.LBC;
explainerText = __('This refundable boost will improve the discoverability of this %claimTypeText% while active.', {claimTypeText}); explainerText = __('This refundable boost will improve the discoverability of this %claimTypeText% while active.', {claimTypeText});
@ -273,8 +276,8 @@ function WalletSendTip(props: Props) {
Lbryio.call( Lbryio.call(
'customer', 'customer',
'tip', 'tip',
{ { // round to fix issues with floating point numbers
amount: 100 * tipAmount, // convert from dollars to cents amount: Math.round(100 * tipAmount), // convert from dollars to cents
creator_channel_name: tipChannelName, // creator_channel_name creator_channel_name: tipChannelName, // creator_channel_name
creator_channel_claim_id: channelClaimId, creator_channel_claim_id: channelClaimId,
tipper_channel_name: sendAnonymously ? '' : activeChannelName, tipper_channel_name: sendAnonymously ? '' : activeChannelName,
@ -295,7 +298,7 @@ function WalletSendTip(props: Props) {
}); });
}) })
.catch(function(error) { .catch(function(error) {
var displayError = 'Sorry, there was an error in processing your payment!'; let displayError = 'Sorry, there was an error in processing your payment!';
if (error.message !== 'payment intent failed to confirm') { if (error.message !== 'payment intent failed to confirm') {
displayError = error.message; displayError = error.message;
@ -313,10 +316,53 @@ function WalletSendTip(props: Props) {
} }
} }
function handleCustomPriceChange(event: SyntheticInputEvent<*>) { const countDecimals = function(value) {
const tipAmount = parseFloat(event.target.value); const text = value.toString();
const index = text.indexOf('.');
return (text.length - index - 1);
};
function handleCustomPriceChange(event: SyntheticInputEvent<*>) {
let tipAmountAsString = event.target.value;
let tipAmount = parseFloat(tipAmountAsString);
const howManyDecimals = countDecimals(tipAmountAsString);
// fiat tip input
if (activeTab === TAB_FIAT) {
if (Number.isNaN(tipAmount)) {
setCustomTipAmount('');
}
// allow maximum of two decimal places
if (howManyDecimals > 2) {
tipAmount = Math.floor(tipAmount * 100) / 100;
}
// remove decimals, and then get number of digits
const howManyDigits = Math.trunc(tipAmount).toString().length;
if (howManyDigits > 4 && tipAmount !== 1000) {
setTipError('Amount cannot be over 1000 dollars');
setCustomTipAmount(tipAmount); setCustomTipAmount(tipAmount);
} else if (tipAmount > 1000) {
setTipError('Amount cannot be over 1000 dollars');
setCustomTipAmount(tipAmount);
} else {
setCustomTipAmount(tipAmount);
}
// LBC tip input
} else {
// TODO: this is a bit buggy, needs a touchup
// if (howManyDecimals > 9) {
// // only allows up to 8 decimal places
// tipAmount = Number(tipAmount.toString().match(/^-?\d+(?:\.\d{0,8})?/)[0]);
//
// setTipError('Please only use up to 8 decimals');
// }
setCustomTipAmount(tipAmount);
}
} }
function buildButtonText() { function buildButtonText() {
@ -331,8 +377,14 @@ function WalletSendTip(props: Props) {
return false; return false;
} }
function convertToTwoDecimals(number) {
return (Math.round(number * 100) / 100).toFixed(2);
}
const amountToShow = activeTab === TAB_FIAT ? convertToTwoDecimals(tipAmount) : tipAmount;
// if it's a valid number display it, otherwise do an empty string // if it's a valid number display it, otherwise do an empty string
const displayAmount = !isNan(tipAmount) ? tipAmount : ''; const displayAmount = !isNan(tipAmount) ? amountToShow : '';
if (activeTab === TAB_BOOST) { if (activeTab === TAB_BOOST) {
return (claimIsMine ? __('Boost Your %claimTypeText%', {claimTypeText}) : __('Boost This %claimTypeText%', {claimTypeText})); return (claimIsMine ? __('Boost Your %claimTypeText%', {claimTypeText}) : __('Boost This %claimTypeText%', {claimTypeText}));
@ -362,30 +414,7 @@ function WalletSendTip(props: Props) {
return ( return (
<Form onSubmit={handleSubmit}> <Form onSubmit={handleSubmit}>
{/* if there is no LBC balance, show user frontend to get credits */} {/* if there is no LBC balance, show user frontend to get credits */}
{noBalance ? ( {/* if there is lbc, the main tip/boost gui with the 3 tabs at the top */}
<Card
title={<I18nMessage tokens={{ lbc: <LbcSymbol size={22} /> }}>Supporting content requires %lbc%</I18nMessage>}
subtitle={
<I18nMessage tokens={{ lbc: <LbcSymbol /> }}>
With %lbc%, you can send tips to your favorite creators, or help boost their content for more people to
see.
</I18nMessage>
}
actions={
<div className="section__actions">
<Button
icon={ICONS.REWARDS}
button="primary"
label={__('Earn Rewards')}
navigate={`/$/${PAGES.REWARDS}`}
/>
<Button icon={ICONS.BUY} button="secondary" label={__('Buy/Swap Credits')} navigate={`/$/${PAGES.BUY}`} />
<Button button="link" label={__('Nevermind')} onClick={closeModal} />
</div>
}
/>
) : (
// if there is lbc, the main tip/boost gui with the 3 tabs at the top
<Card <Card
title={<LbcSymbol postfix={claimIsMine ? __('Boost Your %claimTypeText%', {claimTypeText}) : __('Support This %claimTypeText%', {claimTypeText})} size={22} />} title={<LbcSymbol postfix={claimIsMine ? __('Boost Your %claimTypeText%', {claimTypeText}) : __('Support This %claimTypeText%', {claimTypeText})} size={22} />}
subtitle={ subtitle={
@ -399,7 +428,7 @@ function WalletSendTip(props: Props) {
label={__('Tip')} label={__('Tip')}
button="alt" button="alt"
onClick={() => { onClick={() => {
var tipInputElement = document.getElementById('tip-input'); const tipInputElement = document.getElementById('tip-input');
if (tipInputElement) { tipInputElement.focus() } if (tipInputElement) { tipInputElement.focus() }
if (!isConfirming) { if (!isConfirming) {
setActiveTab(TAB_LBC); setActiveTab(TAB_LBC);
@ -415,7 +444,7 @@ function WalletSendTip(props: Props) {
label={__('Tip')} label={__('Tip')}
button="alt" button="alt"
onClick={() => { onClick={() => {
var tipInputElement = document.getElementById('tip-input'); const tipInputElement = document.getElementById('tip-input');
if (tipInputElement) { tipInputElement.focus() } if (tipInputElement) { tipInputElement.focus() }
if (!isConfirming) { if (!isConfirming) {
setActiveTab(TAB_FIAT); setActiveTab(TAB_FIAT);
@ -431,7 +460,7 @@ function WalletSendTip(props: Props) {
label={__('Boost')} label={__('Boost')}
button="alt" button="alt"
onClick={() => { onClick={() => {
var tipInputElement = document.getElementById('tip-input'); const tipInputElement = document.getElementById('tip-input');
if (tipInputElement) { tipInputElement.focus() } if (tipInputElement) { tipInputElement.focus() }
if (!isConfirming) { if (!isConfirming) {
setActiveTab(TAB_BOOST); setActiveTab(TAB_BOOST);
@ -444,7 +473,7 @@ function WalletSendTip(props: Props) {
{/* short explainer under the button */} {/* short explainer under the button */}
<div className="section__subtitle"> <div className="section__subtitle">
{explainerText} {explainerText + ' '}
{/* {activeTab === TAB_FIAT && !hasCardSaved && <Button navigate={`/$/${PAGES.SETTINGS_STRIPE_CARD}`} label={__('Add A Card')} button="link" />} */} {/* {activeTab === TAB_FIAT && !hasCardSaved && <Button navigate={`/$/${PAGES.SETTINGS_STRIPE_CARD}`} label={__('Add A Card')} button="link" />} */}
{<Button label={__('Learn more')} button="link" href="https://lbry.com/faq/tipping" />} {<Button label={__('Learn more')} button="link" href="https://lbry.com/faq/tipping" />}
</div> </div>
@ -464,7 +493,7 @@ function WalletSendTip(props: Props) {
</div> </div>
<div className="confirm__label">{setConfirmLabel()}</div> <div className="confirm__label">{setConfirmLabel()}</div>
<div className="confirm__value"> <div className="confirm__value">
{activeTab === TAB_FIAT ? <p>$ {tipAmount}</p> : <LbcSymbol postfix={tipAmount} size={22} />} {activeTab === TAB_FIAT ? <p>$ {(Math.round(tipAmount * 100) / 100).toFixed(2)}</p> : <LbcSymbol postfix={tipAmount} size={22} />}
</div> </div>
</div> </div>
</div> </div>
@ -479,8 +508,10 @@ function WalletSendTip(props: Props) {
<Button button="link" label={__('Cancel')} onClick={() => setIsConfirming(false)} /> <Button button="link" label={__('Cancel')} onClick={() => setIsConfirming(false)} />
</div> </div>
</> </>
) : ( // only show the prompt to earn more if its lbc or boost tab and no balance
<> // otherwise you can show the full prompt
) : (!((activeTab === TAB_LBC || activeTab === TAB_BOOST) && noBalance)
? <>
<div className="section"> <div className="section">
<ChannelSelector /> <ChannelSelector />
</div> </div>
@ -554,11 +585,13 @@ function WalletSendTip(props: Props) {
)} )}
</React.Fragment> </React.Fragment>
} }
className="form-field--price-amount"
error={tipError} error={tipError}
min="0" min="0"
step="any" step="any"
type="number" type="number"
style={{
width: activeTab === TAB_FIAT ? '99px' : '160px',
}}
placeholder="1.23" placeholder="1.23"
value={customTipAmount} value={customTipAmount}
onChange={(event) => handleCustomPriceChange(event)} onChange={(event) => handleCustomPriceChange(event)}
@ -592,10 +625,31 @@ function WalletSendTip(props: Props) {
<div className="help">{__('The payment will be made from your saved card')}</div> <div className="help">{__('The payment will be made from your saved card')}</div>
)} )}
</> </>
// if it's LBC and there is no balance, you can prompt to purchase LBC
: <Card
title={<I18nMessage tokens={{ lbc: <LbcSymbol size={22} /> }}>Supporting content requires %lbc%</I18nMessage>}
subtitle={
<I18nMessage tokens={{ lbc: <LbcSymbol /> }}>
With %lbc%, you can send tips to your favorite creators, or help boost their content for more people to
see.
</I18nMessage>
}
actions={
<div className="section__actions">
<Button
icon={ICONS.REWARDS}
button="primary"
label={__('Earn Rewards')}
navigate={`/$/${PAGES.REWARDS}`}
/>
<Button icon={ICONS.BUY} button="secondary" label={__('Buy/Swap Credits')} navigate={`/$/${PAGES.BUY}`} />
<Button button="link" label={__('Nevermind')} onClick={closeModal} />
</div>
}
/>
) )
} }
/> />
)}
</Form> </Form>
); );
} }

View file

@ -263,7 +263,6 @@ function WalletTipAmountSelector(props: Props) {
// </div> // </div>
// </> // </>
} }
className="form-field--price-amount"
error={tipError} error={tipError}
min="0" min="0"
step="any" step="any"

View file

@ -2,12 +2,14 @@ import { connect } from 'react-redux';
import { withRouter } from 'react-router'; import { withRouter } from 'react-router';
import StripeAccountConnection from './view'; import StripeAccountConnection from './view';
import { selectUser } from 'redux/selectors/user'; import { selectUser } from 'redux/selectors/user';
import { doToast } from 'redux/actions/notifications';
// function that receives state parameter and returns object of functions that accept state
const select = (state) => ({ const select = (state) => ({
user: selectUser(state), user: selectUser(state),
}); });
// const perform = (dispatch) => ({}); const perform = (dispatch) => ({
doToast: (options) => dispatch(doToast(options)),
});
export default withRouter(connect(select)(StripeAccountConnection)); export default withRouter(connect(select, perform)(StripeAccountConnection));

View file

@ -31,6 +31,8 @@ if (isDev) {
type Props = { type Props = {
source: string, source: string,
user: User, user: User,
doOpenModal: (string, {}) => void,
doToast: ({ message: string }) => void,
}; };
type State = { type State = {
@ -68,6 +70,8 @@ class StripeAccountConnection extends React.Component<Props, State> {
componentDidMount() { componentDidMount() {
const { user } = this.props; const { user } = this.props;
let doToast = this.props.doToast;
// $FlowFixMe // $FlowFixMe
this.experimentalUiEnabled = user && user.experimental_ui; this.experimentalUiEnabled = user && user.experimental_ui;
@ -127,10 +131,8 @@ class StripeAccountConnection extends React.Component<Props, State> {
).then((accountListResponse: any) => { ).then((accountListResponse: any) => {
// TODO type this // TODO type this
that.setState({ that.setState({
accountTransactions: accountListResponse, accountTransactions: accountListResponse.reverse(),
}); });
console.log(accountListResponse);
}); });
} }
@ -165,6 +167,9 @@ class StripeAccountConnection extends React.Component<Props, State> {
// get stripe link and set it on the frontend // get stripe link and set it on the frontend
getAndSetAccountLink(true); getAndSetAccountLink(true);
} else { } else {
// probably an error from stripe
const displayString = __('There was an error getting your account setup, please try again later');
doToast({ message: displayString, isError: true });
// not an error from Beamer, throw it // not an error from Beamer, throw it
throw new Error(error); throw new Error(error);
} }
@ -296,7 +301,7 @@ class StripeAccountConnection extends React.Component<Props, State> {
</thead> </thead>
<tbody> <tbody>
{accountTransactions && {accountTransactions &&
accountTransactions.reverse().map((transaction) => ( accountTransactions.map((transaction) => (
<tr key={transaction.name + transaction.created_at}> <tr key={transaction.name + transaction.created_at}>
<td>{moment(transaction.created_at).format('LLL')}</td> <td>{moment(transaction.created_at).format('LLL')}</td>
<td> <td>

View file

@ -19,6 +19,9 @@ if (STRIPE_PUBLIC_KEY.indexOf('pk_live') > -1) {
stripeEnvironment = 'live'; stripeEnvironment = 'live';
} }
const APIS_DOWN_ERROR_RESPONSE = __('There was an error from the server, please try again later');
const CARD_SETUP_ERROR_RESPONSE = __('There was an error getting your card setup, please try again later');
// eslint-disable-next-line flowtype/no-types-missing-file-annotation // eslint-disable-next-line flowtype/no-types-missing-file-annotation
type Props = { type Props = {
disabled: boolean, disabled: boolean,
@ -57,8 +60,6 @@ class SettingsStripeCard extends React.Component<Props, State> {
componentDidMount() { componentDidMount() {
let that = this; let that = this;
console.log(this.props);
let doToast = this.props.doToast; let doToast = this.props.doToast;
const script = document.createElement('script'); const script = document.createElement('script');
@ -100,8 +101,6 @@ class SettingsStripeCard extends React.Component<Props, State> {
let topOfDisplay = customer.email.split('@')[0]; let topOfDisplay = customer.email.split('@')[0];
let bottomOfDisplay = '@' + customer.email.split('@')[1]; let bottomOfDisplay = '@' + customer.email.split('@')[1];
console.log(customerStatusResponse.Customer);
let cardDetails = { let cardDetails = {
brand: card.brand, brand: card.brand,
expiryYear: card.exp_year, expiryYear: card.exp_year,
@ -133,8 +132,6 @@ class SettingsStripeCard extends React.Component<Props, State> {
}, },
'post' 'post'
).then((customerSetupResponse) => { ).then((customerSetupResponse) => {
console.log(customerSetupResponse);
clientSecret = customerSetupResponse.client_secret; clientSecret = customerSetupResponse.client_secret;
// instantiate stripe elements // instantiate stripe elements
@ -154,14 +151,10 @@ class SettingsStripeCard extends React.Component<Props, State> {
that.setState({ that.setState({
customerTransactions: customerTransactionsResponse, customerTransactions: customerTransactionsResponse,
}); });
console.log(customerTransactionsResponse);
}); });
// if the status call fails, either an actual error or need to run setup first // if the status call fails, either an actual error or need to run setup first
}) })
.catch(function (error) { .catch(function (error) {
console.log(error);
// errorString passed from the API (with a 403 error) // errorString passed from the API (with a 403 error)
const errorString = 'user as customer is not setup yet'; const errorString = 'user as customer is not setup yet';
@ -181,18 +174,17 @@ class SettingsStripeCard extends React.Component<Props, State> {
}, },
'post' 'post'
).then((customerSetupResponse) => { ).then((customerSetupResponse) => {
console.log(customerSetupResponse);
clientSecret = customerSetupResponse.client_secret; clientSecret = customerSetupResponse.client_secret;
// instantiate stripe elements // instantiate stripe elements
setupStripe(); setupStripe();
jessopb commented 2021-08-10 21:25:24 +02:00 (Migrated from github.com)
Review

Just put these like
CARD_SETUP_ERROR = __('blah');
API_SERVER_ERROR = __('bleg'); at the top.

Just put these like CARD_SETUP_ERROR = __('blah'); API_SERVER_ERROR = __('bleg'); at the top.
}); });
// 500 error from the backend being down
} else if (error === 'internal_apis_down') { } else if (error === 'internal_apis_down') {
var displayString = 'There was an error from the server, please let support know'; doToast({ message: APIS_DOWN_ERROR_RESPONSE, isError: true });
doToast({ message: displayString, isError: true });
} else { } else {
console.log('Unseen before error'); // probably an error from stripe
doToast({ message: CARD_SETUP_ERROR_RESPONSE, isError: true });
} }
}); });
}, 250); }, 250);
@ -261,8 +253,6 @@ class SettingsStripeCard extends React.Component<Props, State> {
}) })
.then(function (result) { .then(function (result) {
if (result.error) { if (result.error) {
console.log(result);
changeLoadingState(false); changeLoadingState(false);
var displayError = document.getElementById('card-errors'); var displayError = document.getElementById('card-errors');
displayError.textContent = result.error.message; displayError.textContent = result.error.message;
@ -346,8 +336,6 @@ class SettingsStripeCard extends React.Component<Props, State> {
}); });
}); });
console.log(result);
changeLoadingState(false); changeLoadingState(false);
}); });
}; };