diff --git a/flow-typed/user.js b/flow-typed/user.js index ac4ee7025..53a3e3690 100644 --- a/flow-typed/user.js +++ b/flow-typed/user.js @@ -28,6 +28,7 @@ declare type User = { device_types: Array, lbry_first_approved: boolean, experimental_ui: boolean, + fiat_enabled: boolean, odysee_live_enabled: boolean, odysee_live_disabled: boolean, global_mod: boolean, diff --git a/ui/component/stripeAccountConnection/view.jsx b/ui/component/stripeAccountConnection/view.jsx index 4bfc1b337..84cd46bf7 100644 --- a/ui/component/stripeAccountConnection/view.jsx +++ b/ui/component/stripeAccountConnection/view.jsx @@ -36,10 +36,11 @@ type State = { loading: boolean, content: ?string, stripeConnectionUrl: string, - alreadyUpdated: boolean, + // alreadyUpdated: boolean, accountConfirmed: boolean, + accountPendingConfirmation: boolean, accountNotConfirmedButReceivedTips: boolean, - unpaidBalance: string + unpaidBalance: number, }; class StripeAccountConnection extends React.Component { @@ -53,22 +54,30 @@ class StripeAccountConnection extends React.Component { accountPendingConfirmation: false, accountNotConfirmedButReceivedTips: false, unpaidBalance: 0, + stripeConnectionUrl: '', + // alreadyUpdated: false, }; } componentDidMount() { const { user } = this.props; + // $FlowFixMe this.experimentalUiEnabled = user && user.experimental_ui; var that = this; function getAndSetAccountLink(stillNeedToConfirmAccount) { - Lbryio.call('account', 'link', { - return_url: successStripeRedirectUrl, - refresh_url: failureStripeRedirectUrl, - environment: stripeEnvironment, - }, 'post').then(accountLinkResponse => { + Lbryio.call( + 'account', + 'link', + { + return_url: successStripeRedirectUrl, + refresh_url: failureStripeRedirectUrl, + environment: stripeEnvironment, + }, + 'post' + ).then((accountLinkResponse) => { // stripe link for user to navigate to and confirm account const stripeConnectionUrl = accountLinkResponse.url; @@ -87,54 +96,67 @@ class StripeAccountConnection extends React.Component { } // call the account status endpoint - Lbryio.call('account', 'status', { - environment: stripeEnvironment, - }, 'post').then(accountStatusResponse => { - const yetToBeCashedOutBalance = accountStatusResponse.total_received_unpaid; - if (yetToBeCashedOutBalance) { - that.setState({ - unpaidBalance: yetToBeCashedOutBalance, - }); - } + Lbryio.call( + 'account', + 'status', + { + environment: stripeEnvironment, + }, + 'post' + ) + .then((accountStatusResponse) => { + const yetToBeCashedOutBalance = accountStatusResponse.total_received_unpaid; + if (yetToBeCashedOutBalance) { + that.setState({ + unpaidBalance: yetToBeCashedOutBalance, + }); + } - // if charges already enabled, no need to generate an account link - if (accountStatusResponse.charges_enabled) { - // account has already been confirmed + // if charges already enabled, no need to generate an account link + if (accountStatusResponse.charges_enabled) { + // account has already been confirmed - that.setState({ - accountConfirmed: true, - }); - // user has not confirmed an account but have received payments - } else if (accountStatusResponse.total_received_unpaid > 0) { - that.setState({ - accountNotConfirmedButReceivedTips: true, - }); + that.setState({ + accountConfirmed: true, + }); + // user has not confirmed an account but have received payments + } else if (accountStatusResponse.total_received_unpaid > 0) { + that.setState({ + accountNotConfirmedButReceivedTips: true, + }); - getAndSetAccountLink(); + getAndSetAccountLink(); - // user has not received any amount or confirmed an account - } else { - // get stripe link and set it on the frontend - // pass true so it updates the frontend - getAndSetAccountLink(true); - } - }).catch(function(error) { - // errorString passed from the API (with a 403 error) - const errorString = 'account not linked to user, please link first'; + // user has not received any amount or confirmed an account + } else { + // get stripe link and set it on the frontend + // pass true so it updates the frontend + getAndSetAccountLink(true); + } + }) + .catch(function (error) { + // errorString passed from the API (with a 403 error) + const errorString = 'account not linked to user, please link first'; - // if it's beamer's error indicating the account is not linked yet - if (error.message.indexOf(errorString) > -1) { - // get stripe link and set it on the frontend - getAndSetAccountLink(); - } else { - // not an error from Beamer, throw it - throw new Error(error); - } - }); + // if it's beamer's error indicating the account is not linked yet + if (error.message.indexOf(errorString) > -1) { + // get stripe link and set it on the frontend + getAndSetAccountLink(); + } else { + // not an error from Beamer, throw it + throw new Error(error); + } + }); } render() { - const { stripeConnectionUrl, accountConfirmed, accountPendingConfirmation, unpaidBalance, accountNotConfirmedButReceivedTips } = this.state; + const { + stripeConnectionUrl, + accountConfirmed, + accountPendingConfirmation, + unpaidBalance, + accountNotConfirmedButReceivedTips, + } = this.state; const { user } = this.props; @@ -146,79 +168,85 @@ class StripeAccountConnection extends React.Component { body={
{/* show while waiting for account status */} - {!accountConfirmed && !accountPendingConfirmation && !accountNotConfirmedButReceivedTips && -
-
+ {!accountConfirmed && !accountPendingConfirmation && !accountNotConfirmedButReceivedTips && ( +
-

Getting your Bank Account Connection status...

-
-
-
- } - {/* user has yet to complete their integration */} - {!accountConfirmed && accountPendingConfirmation && -
-
-
-

Connect your Bank Account to Odysee to receive donations directly from users

-
- -
-
- } - { /* user has completed their integration */ } - {accountConfirmed && -
-
-
-

Congratulations! Your account has been connected with Odysee.

- {unpaidBalance > 0 ?

-

Your account balance is ${unpaidBalance / 100} USD. Functionality to view your transactions and withdraw your balance will be landing shortly.

-
:

-

Your account balance is $0 USD. When you receive a tip you will see it here.

-
} -
-
-
- } - { accountNotConfirmedButReceivedTips && -
-
-
-

Congratulations, you have already begun receiving tips on Odysee!

-

-

Your pending account balance is ${unpaidBalance / 100} USD. Functionality to view and receive your transactions will land soon.

-

-

Connect your Bank Account to be able to cash your pending balance out to your account.

+

Getting your Bank Account Connection status...

+
+
+
+ )} + {/* user has yet to complete their integration */} + {!accountConfirmed && accountPendingConfirmation && ( +
+
+
+

Connect your Bank Account to Odysee to receive donations directly from users

-
- } + )} + {/* user has completed their integration */} + {accountConfirmed && ( +
+
+
+

Congratulations! Your account has been connected with Odysee.

+ {unpaidBalance > 0 ? ( +
+
+

+ Your account balance is ${unpaidBalance / 100} USD. Functionality to view your transactions + and withdraw your balance will be landing shortly. +

+
+ ) : ( +
+
+

Your account balance is $0 USD. When you receive a tip you will see it here.

+
+ )} +
+
+
+ )} + {accountNotConfirmedButReceivedTips && ( +
+
+
+

Congratulations, you have already begun receiving tips on Odysee!

+
+
+

+ Your pending account balance is ${unpaidBalance / 100} USD. Functionality to view and receive + your transactions will land soon. +

+
+
+
+

Connect your Bank Account to be able to cash your pending balance out to your account.

+
+ +
+
+
+ )}
} /> ); } else { - return (<>); // probably null; + return <>; // probably null; } } } diff --git a/ui/page/settingsStripeCard/view.jsx b/ui/page/settingsStripeCard/view.jsx index ee0654054..af618f6c7 100644 --- a/ui/page/settingsStripeCard/view.jsx +++ b/ui/page/settingsStripeCard/view.jsx @@ -1,4 +1,4 @@ -// @flow +// restore flow /* eslint-disable no-undef */ /* eslint-disable react/prop-types */ import React from 'react'; @@ -19,14 +19,25 @@ if (STRIPE_PUBLIC_KEY.indexOf('pk_live') > -1) { stripeEnvironment = 'live'; } -type Props = { - disabled: boolean, - label: ?string, - email: ?string, -} +// type Props = { +// disabled: boolean, +// label: ?string, +// email: ?string, +// scriptFailedToLoad: boolean, +// }; +// +// type State = { +// open: boolean, +// currentFlowStage: string, +// customerTransactions: Array, +// pageTitle: string, +// userCardDetails: any, // fill this out +// scriptFailedToLoad: boolean, +// }; class CardVerify extends React.Component { - constructor(props: Props) { + constructor(props) { + // :Props super(props); this.state = { open: false, @@ -56,96 +67,118 @@ class CardVerify extends React.Component { // setting a timeout to let the client secret populate // TODO: fix this, should be a cleaner way - setTimeout(function() { + setTimeout(function () { // check if customer has card setup already - Lbryio.call('customer', 'status', { - environment: stripeEnvironment, - }, 'post').then(customerStatusResponse => { - // user has a card saved if their defaultPaymentMethod has an id - const defaultPaymentMethod = customerStatusResponse.Customer.invoice_settings.default_payment_method; - var userHasAlreadySetupPayment = Boolean(defaultPaymentMethod && defaultPaymentMethod.id); + Lbryio.call( + 'customer', + 'status', + { + environment: stripeEnvironment, + }, + 'post' + ) + .then((customerStatusResponse) => { + // user has a card saved if their defaultPaymentMethod has an id + const defaultPaymentMethod = customerStatusResponse.Customer.invoice_settings.default_payment_method; + var userHasAlreadySetupPayment = Boolean(defaultPaymentMethod && defaultPaymentMethod.id); - // show different frontend if user already has card - if (userHasAlreadySetupPayment) { - var card = customerStatusResponse.PaymentMethods[0].card; + // show different frontend if user already has card + if (userHasAlreadySetupPayment) { + var card = customerStatusResponse.PaymentMethods[0].card; - var cardDetails = { - brand: card.brand, - expiryYear: card.exp_year, - expiryMonth: card.exp_month, - lastFour: card.last4, - }; + var cardDetails = { + brand: card.brand, + expiryYear: card.exp_year, + expiryMonth: card.exp_month, + lastFour: card.last4, + }; - that.setState({ - currentFlowStage: 'cardConfirmed', - pageTitle: 'Tip History', - userCardDetails: cardDetails, - }); - - // get customer transactions - Lbryio.call('customer', 'list', { - environment: stripeEnvironment, - }, 'post').then(customerTransactionsResponse => { that.setState({ - customerTransactions: customerTransactionsResponse, + currentFlowStage: 'cardConfirmed', + pageTitle: 'Tip History', + userCardDetails: cardDetails, }); - console.log(customerTransactionsResponse); - }); + // get customer transactions + Lbryio.call( + 'customer', + 'list', + { + environment: stripeEnvironment, + }, + 'post' + ).then((customerTransactionsResponse) => { + that.setState({ + customerTransactions: customerTransactionsResponse, + }); - // otherwise, prompt them to save a card - } else { - that.setState({ - currentFlowStage: 'confirmingCard', - }); + console.log(customerTransactionsResponse); + }); - // get a payment method secret for frontend - Lbryio.call('customer', 'setup', { - environment: stripeEnvironment, - }, 'post').then(customerSetupResponse => { - console.log(customerSetupResponse); + // otherwise, prompt them to save a card + } else { + that.setState({ + currentFlowStage: 'confirmingCard', + }); - clientSecret = customerSetupResponse.client_secret; + // get a payment method secret for frontend + Lbryio.call( + 'customer', + 'setup', + { + environment: stripeEnvironment, + }, + 'post' + ).then((customerSetupResponse) => { + console.log(customerSetupResponse); - // instantiate stripe elements - setupStripe(); - }); - } - // if the status call fails, either an actual error or need to run setup first - }).catch(function(error) { - console.log(error); + clientSecret = customerSetupResponse.client_secret; - // errorString passed from the API (with a 403 error) - const errorString = 'user as customer is not setup yet'; + // instantiate stripe elements + setupStripe(); + }); + } + // if the status call fails, either an actual error or need to run setup first + }) + .catch(function (error) { + console.log(error); - // if it's beamer's error indicating the account is not linked yet - if (error.message.indexOf(errorString) > -1) { - // send them to save a card - that.setState({ - currentFlowStage: 'confirmingCard', - }); + // errorString passed from the API (with a 403 error) + const errorString = 'user as customer is not setup yet'; - // get a payment method secret for frontend - Lbryio.call('customer', 'setup', { - environment: stripeEnvironment, - }, 'post').then(customerSetupResponse => { - console.log(customerSetupResponse); + // if it's beamer's error indicating the account is not linked yet + if (error.message.indexOf(errorString) > -1) { + // send them to save a card + that.setState({ + currentFlowStage: 'confirmingCard', + }); - clientSecret = customerSetupResponse.client_secret; + // get a payment method secret for frontend + Lbryio.call( + 'customer', + 'setup', + { + environment: stripeEnvironment, + }, + 'post' + ).then((customerSetupResponse) => { + console.log(customerSetupResponse); - // instantiate stripe elements - setupStripe(); - }); - } else { - console.log('Unseen before error'); - } - }); + clientSecret = customerSetupResponse.client_secret; + + // instantiate stripe elements + setupStripe(); + }); + } else { + console.log('Unseen before error'); + } + }); }, 250); function setupStripe() { // TODO: have to fix this, using so that the script is available - setTimeout(function() { - var stripeElements = function(publicKey, setupIntent) { + setTimeout(function () { + var stripeElements = function (publicKey, setupIntent) { var stripe = Stripe(publicKey); var elements = stripe.elements(); @@ -154,8 +187,7 @@ class CardVerify extends React.Component { base: { fontSize: '16px', color: '#32325d', - fontFamily: - '-apple-system, BlinkMacSystemFont, Segoe UI, Roboto, sans-serif', + fontFamily: '-apple-system, BlinkMacSystemFont, Segoe UI, Roboto, sans-serif', fontSmoothing: 'antialiased', '::placeholder': { color: 'rgba(0,0,0,0.4)', @@ -168,17 +200,17 @@ class CardVerify extends React.Component { card.mount('#card-element'); // Element focus ring - card.on('focus', function() { + card.on('focus', function () { var el = document.getElementById('card-element'); el.classList.add('focused'); }); - card.on('blur', function() { + card.on('blur', function () { var el = document.getElementById('card-element'); el.classList.remove('focused'); }); - card.on('ready', function() { + card.on('ready', function () { card.focus(); }); @@ -198,29 +230,31 @@ class CardVerify extends React.Component { changeLoadingState(true); - stripe.confirmCardSetup(clientSecret, { - payment_method: { - card: card, - billing_details: { email: email }, - }, - }).then(function(result) { - if (result.error) { - console.log(result); + stripe + .confirmCardSetup(clientSecret, { + payment_method: { + card: card, + billing_details: { email: email }, + }, + }) + .then(function (result) { + if (result.error) { + console.log(result); - changeLoadingState(false); - var displayError = document.getElementById('card-errors'); - displayError.textContent = result.error.message; - } else { - // The PaymentMethod was successfully set up - // hide and show the proper divs - orderComplete(stripe, clientSecret); - } - }); + changeLoadingState(false); + var displayError = document.getElementById('card-errors'); + displayError.textContent = result.error.message; + } else { + // The PaymentMethod was successfully set up + // hide and show the proper divs + orderComplete(stripe, clientSecret); + } + }); } // Handle payment submission when user clicks the pay button. var button = document.getElementById('submit'); - button.addEventListener('click', function(event) { + button.addEventListener('click', function (event) { submitForm(event); }); @@ -237,24 +271,35 @@ class CardVerify extends React.Component { stripeElements(publicKey, clientSecret); // Show a spinner on payment submission - var changeLoadingState = function(isLoading) { + var changeLoadingState = function (isLoading) { if (isLoading) { + // $FlowFixMe document.querySelector('button').disabled = true; + // $FlowFixMe document.querySelector('#spinner').classList.remove('hidden'); + // $FlowFixMe document.querySelector('#button-text').classList.add('hidden'); } else { + // $FlowFixMe document.querySelector('button').disabled = false; + // $FlowFixMe document.querySelector('#spinner').classList.add('hidden'); + // $FlowFixMe document.querySelector('#button-text').classList.remove('hidden'); } }; // shows a success / error message when the payment is complete - var orderComplete = function(stripe, clientSecret) { - stripe.retrieveSetupIntent(clientSecret).then(function(result) { - Lbryio.call('customer', 'status', { - environment: stripeEnvironment, - }, 'post').then(customerStatusResponse => { + var orderComplete = function (stripe, clientSecret) { + stripe.retrieveSetupIntent(clientSecret).then(function (result) { + Lbryio.call( + 'customer', + 'status', + { + environment: stripeEnvironment, + }, + 'post' + ).then((customerStatusResponse) => { var card = customerStatusResponse.PaymentMethods[0].card; var cardDetails = { @@ -287,10 +332,16 @@ class CardVerify extends React.Component { } componentWillUnmount() { + // pretty sure this doesn't exist + // $FlowFixMe if (this.loadPromise) { + // $FlowFixMe this.loadPromise.reject(); } + // pretty sure this doesn't exist + // $FlowFixMe if (CardVerify.stripeHandler && this.state.open) { + // $FlowFixMe CardVerify.stripeHandler.close(); } } @@ -329,7 +380,6 @@ class CardVerify extends React.Component { const { currentFlowStage, customerTransactions, pageTitle, userCardDetails } = this.state; return ( -
{scriptFailedToLoad && ( @@ -337,76 +387,86 @@ class CardVerify extends React.Component { )}
- {currentFlowStage === 'loading' &&
- -
} + {currentFlowStage === 'loading' && ( +
+ +
+ )} - {currentFlowStage === 'confirmingCard' &&
-
-
-
- -
+ {currentFlowStage === 'confirmingCard' && ( +
+
+
+
+ +
+
+ -
-
} + )} - {currentFlowStage === 'cardConfirmed' &&
- -

Brand: {userCardDetails.brand.toUpperCase()}   - Last 4: {userCardDetails.lastFour}   - Expires: {userCardDetails.expiryMonth}/{userCardDetails.expiryYear}   -

- } - /> -
+ {currentFlowStage === 'cardConfirmed' && ( +
+ +

+ Brand: {userCardDetails.brand.toUpperCase()}   Last 4: {userCardDetails.lastFour}   + Expires: {userCardDetails.expiryMonth}/{userCardDetails.expiryYear}   +

+ + } + /> +
- {(!customerTransactions || customerTransactions.length === 0) && } - - {customerTransactions && customerTransactions.length > 0 &&
- - - - - - - - - - - {customerTransactions && - customerTransactions.map((transaction) => ( - - - - - - - ))} - -
{__('Date')}{<>{__('Receiving Channel Name')}}{__('Amount (USD)')} {__('Anonymous')}
{moment(transaction.created_at).format('LLL')}{transaction.channel_name}${transaction.tipped_amount / 100}{transaction.private_tip ? 'Yes' : 'No'}
-
} - />} -
} + {(!customerTransactions || customerTransactions.length === 0) && ( + + )} + {customerTransactions && customerTransactions.length > 0 && ( + +
+ + + + + + + + + + + {customerTransactions && + customerTransactions.map((transaction) => ( + + + + + + + ))} + +
{__('Date')}{<>{__('Receiving Channel Name')}}{__('Amount (USD)')} {__('Anonymous')}
{moment(transaction.created_at).format('LLL')}{transaction.channel_name}${transaction.tipped_amount / 100}{transaction.private_tip ? 'Yes' : 'No'}
+
+ + } + /> + )} +
+ )} ); }