Skip to content
This repository was archived by the owner on Jan 22, 2026. It is now read-only.

Commit 3930922

Browse files
authored
Merge pull request #5956 from blockchain/GROWUX-1444
Send analytics when users fail or exit linking with Plaid
2 parents 9680ff7 + d56a968 commit 3930922

6 files changed

Lines changed: 158 additions & 87 deletions

File tree

packages/blockchain-wallet-v4-frontend/src/components/Flyout/index.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -108,15 +108,15 @@ class Flyout extends React.Component<Props> {
108108
shouldComponentUpdate = (nextProps) => !equals(this.props, nextProps)
109109

110110
render() {
111-
const { children, doNotHide, isOpen, userClickedOutside } = this.props
111+
const { children, doNotHide, isOpen, position, total, userClickedOutside } = this.props
112112

113113
return (
114114
<AnimatePresence>
115115
{isOpen && !userClickedOutside ? (
116116
<FlyoutModal
117117
doNotHide={doNotHide}
118-
total={this.props.total}
119-
position={this.props.position}
118+
total={total}
119+
position={position}
120120
animate={{ x: 0 }}
121121
exit={{ x: width }}
122122
initial={{ x: width }}

packages/blockchain-wallet-v4-frontend/src/data/analytics/types/index.ts

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,10 @@ import {
3838
Events as OnboardingAndVerificationEvents,
3939
TrackEventAction as OnboardingAndVerificationTrackEventAction
4040
} from './onboardingAndVerification'
41+
import {
42+
Events as PlaidClientEvents,
43+
TrackEventAction as PlaidClientTrackEventAction
44+
} from './plaid'
4145
import { Events as SellEvents, TrackEventAction as SellTrackEventAction } from './sell'
4246
import {
4347
Events as SendCryptoEvents,
@@ -62,24 +66,25 @@ const TRACK_EVENT = 'trackEvent'
6266

6367
type AnalyticsKey =
6468
| AccountRecoveryEvents
69+
| BuyEvents
6570
| ClientErrorEvents
71+
| CoinViewEvents
72+
| CowboysPromoEvents
6673
| DepositWithdrawalClientEvents
6774
| DexEvents
75+
| ExchangePromoEvents
6876
| InterestClientEvents
6977
| LoginEvents
7078
| MiscEvents
7179
| NftsEvents
7280
| OnboardingAndVerificationEvents
81+
| PlaidClientEvents
82+
| SellEvents
7383
| SendCryptoEvents
84+
| SwapEvents
7485
| TaxCenterEvents
75-
| CoinViewEvents
76-
| CowboysPromoEvents
7786
| ViewAndClickEvents
78-
| SwapEvents
7987
| WalletEarnEvents
80-
| BuyEvents
81-
| SellEvents
82-
| ExchangePromoEvents
8388

8489
const Analytics = {
8590
...AccountRecoveryEvents,
@@ -93,6 +98,7 @@ const Analytics = {
9398
...MiscEvents,
9499
...NftsEvents,
95100
...OnboardingAndVerificationEvents,
101+
...PlaidClientEvents,
96102
...SendCryptoEvents,
97103
...SwapEvents,
98104
...TaxCenterEvents,
@@ -104,32 +110,33 @@ const Analytics = {
104110

105111
// event properties
106112
type AnalyticsProperties =
113+
| DepositWithdrawalClientProperties
114+
| InterestClientProperties
107115
| OnboardingAndVerificationAnalyticsProperties
108116
| ViewAndClickAnalyticsProperties
109-
| InterestClientProperties
110-
| DepositWithdrawalClientProperties
111117

112118
// event actions
113119
type TrackEventAction =
114120
| AccountRecoveryTrackEventAction
121+
| BuyTrackEventAction
115122
| ClientErrorTrackEventAction
123+
| CowboysPromoTrackEventAction
116124
| DepositWithdrawalClientEventAction
117125
| DexEventAction
126+
| ExchangePromoTrackEventAction
118127
| InterestClientTrackEventAction
119128
| LoginTrackEventAction
120129
| MiscTrackEventAction
121130
| NftsTrackEventAction
122131
| OnboardingAndVerificationTrackEventAction
132+
| PlaidClientTrackEventAction
133+
| SellTrackEventAction
123134
| SendCryptoTrackEventAction
124135
| SwapTrackEventAction
125136
| TaxCenterTrackEventAction
126137
| TransactionsTrackEventAction
127138
| ViewAndClickTrackEventAction
128139
| WalletEarnTrackEventAction
129-
| CowboysPromoTrackEventAction
130-
| BuyTrackEventAction
131-
| SellTrackEventAction
132-
| ExchangePromoTrackEventAction
133140

134141
type AnalyticsTraits = {
135142
country?: string
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
export enum Events {
2+
PLAID_CLICK_OUTSIDE = 'User Clicked Outside Plaid Flyout',
3+
PLAID_ERROR = 'Error Linking With Plaid'
4+
}
5+
6+
type PlaidLinkingError = {
7+
key: Events.PLAID_ERROR
8+
properties: {
9+
error_code: string
10+
error_event_type: string
11+
error_message: string
12+
error_type: string
13+
institution_id: string
14+
institution_name: string
15+
link_session_id: string
16+
}
17+
}
18+
19+
type PlaidClickedOutside = {
20+
key: Events.PLAID_CLICK_OUTSIDE
21+
properties: {
22+
buy_sell_step: string
23+
modal_step: string
24+
origin: string
25+
}
26+
}
27+
28+
export type TrackEventAction = PlaidClickedOutside | PlaidLinkingError

packages/blockchain-wallet-v4-frontend/src/modals/Brokerage/Banks/AddBankPlaid/Handler/index.tsx

Lines changed: 38 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import DataError from 'components/DataError'
66
import { FlyoutWrapper } from 'components/Flyout'
77
import { GenericNabuErrorFlyout } from 'components/GenericNabuErrorFlyout'
88
import { actions, selectors } from 'data'
9-
import { AddBankStepType, BankPartners, PlaidSettlementErrorReasons } from 'data/types'
9+
import { AddBankStepType, Analytics, BankPartners, PlaidSettlementErrorReasons } from 'data/types'
1010
import { useRemote } from 'hooks'
1111
import { isNabuError } from 'services/errors'
1212

@@ -29,28 +29,51 @@ const Iframe = styled.iframe`
2929

3030
const Success: React.FC<Props> = ({ handleClose, paymentMethodId, reason }: Props) => {
3131
const dispatch = useDispatch()
32+
3233
const iFrameUrl = useSelector(selectors.components.brokerage.getPlaidWalletHelperLink)
33-
const { data, error, hasError } = useRemote(selectors.components.brokerage.getBankCredentials)
34+
35+
const {
36+
data,
37+
error: bankCredentialError,
38+
hasError: hasBankCredentialError
39+
} = useRemote(selectors.components.brokerage.getBankCredentials)
3440

3541
useEffect(() => {
36-
if (reason && paymentMethodId) {
37-
switch (reason) {
38-
case 'REQUIRES_UPDATE':
39-
dispatch(actions.components.brokerage.fetchBankRefreshCredentials(paymentMethodId))
40-
break
41-
default:
42-
break
42+
if (paymentMethodId && reason) {
43+
if (reason === 'REQUIRES_UPDATE') {
44+
dispatch(actions.components.brokerage.fetchBankRefreshCredentials(paymentMethodId))
4345
}
4446
} else {
4547
dispatch(actions.components.brokerage.setupBankTransferProvider())
4648
}
4749
}, [reason, dispatch, paymentMethodId])
50+
4851
const handlePostMessage = (event: MessageEvent) => {
4952
if (event.data.from !== 'plaid') return
5053
if (event.data.to !== 'sb') return
5154

52-
const { error, metadata, public_token } = event.data
53-
if (error) throw new Error(error)
55+
const { error: plaidError, metadata, public_token } = event.data
56+
57+
if (plaidError) {
58+
const { error_code, error_message, error_type, event } = plaidError
59+
const { institution, link_session_id } = metadata
60+
61+
dispatch(
62+
actions.analytics.trackEvent({
63+
key: Analytics.PLAID_ERROR,
64+
properties: {
65+
error_code,
66+
error_event_type: event ?? 'ON_EXIT',
67+
error_message,
68+
error_type,
69+
institution_id: institution.institution_id,
70+
institution_name: institution.name,
71+
link_session_id
72+
}
73+
})
74+
)
75+
}
76+
5477
if (!public_token) {
5578
return handleClose()
5679
}
@@ -73,12 +96,13 @@ const Success: React.FC<Props> = ({ handleClose, paymentMethodId, reason }: Prop
7396
}
7497
}, [])
7598

76-
if (isNabuError(error)) {
77-
return <GenericNabuErrorFlyout error={error} onDismiss={handleClose} />
99+
if (isNabuError(bankCredentialError)) {
100+
return <GenericNabuErrorFlyout error={bankCredentialError} onDismiss={handleClose} />
78101
}
79-
if (hasError) {
102+
if (hasBankCredentialError) {
80103
return <DataError />
81104
}
105+
82106
if (!data || data.partner !== BankPartners.PLAID) return null
83107

84108
return (

packages/blockchain-wallet-v4-frontend/src/modals/Brokerage/Banks/AddBankPlaid/index.tsx

Lines changed: 68 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1,75 +1,83 @@
1-
import React, { PureComponent } from 'react'
2-
import { connect, ConnectedProps } from 'react-redux'
1+
import React, { useEffect, useState } from 'react'
2+
import { connect, ConnectedProps, useDispatch } from 'react-redux'
33
import { compose } from 'redux'
44

5-
import DataError from 'components/DataError'
65
import Flyout, { duration, FlyoutChild } from 'components/Flyout'
7-
import { selectors } from 'data'
6+
import { actions, selectors } from 'data'
87
import { RootState } from 'data/rootReducer'
9-
import { AddBankStepType, ModalName } from 'data/types'
8+
import { AddBankStepType, Analytics, BuySellStepType, ModalName, ModalOriginType } from 'data/types'
109
import ModalEnhancer from 'providers/ModalEnhancer'
1110

1211
import { Loading, LoadingTextEnum } from '../../../components'
1312
import { ModalPropsType } from '../../../types'
1413
import AddBankStatus from '../AddBankStatus'
1514
import Handler from './Handler'
1615

17-
class Success extends PureComponent<ModalPropsType & LinkStatePropsType> {
18-
state: State = { show: false }
16+
const Success: React.FC<Props> = (props) => {
17+
const dispatch = useDispatch()
1918

20-
componentDidMount() {
21-
/* eslint-disable */
22-
this.setState({ show: true })
23-
/* eslint-enable */
24-
}
19+
const {
20+
brokerageMethod,
21+
buySellMethod,
22+
buySellStep,
23+
close,
24+
origin,
25+
reason,
26+
step,
27+
userClickedOutside
28+
} = props
2529

26-
handleClose = () => {
27-
this.setState({ show: false })
30+
const handleClose = () => {
2831
setTimeout(() => {
29-
this.props.close()
32+
close()
3033
}, duration)
3134
}
3235

33-
render(): React.ReactNode {
34-
return (
35-
<Flyout
36-
{...this.props}
37-
onClose={this.handleClose}
38-
isOpen={this.state.show}
39-
data-e2e='addBankModal'
40-
>
41-
{this.props.step === AddBankStepType.ADD_BANK && (
42-
<FlyoutChild>
43-
{this.props.buySellMethod?.id ? (
44-
<Handler
45-
handleClose={this.handleClose}
46-
reason={this.props.reason}
47-
paymentMethodId={this.props.buySellMethod.id}
48-
/>
49-
) : this.props.brokerageMethod?.id ? (
50-
<Handler
51-
handleClose={this.handleClose}
52-
reason={this.props.reason}
53-
paymentMethodId={this.props.brokerageMethod.id}
54-
/>
55-
) : (
56-
<Handler handleClose={this.handleClose} />
57-
)}
58-
</FlyoutChild>
59-
)}
60-
{this.props.step === AddBankStepType.ADD_BANK_STATUS && (
61-
<FlyoutChild>
62-
<AddBankStatus handleClose={this.handleClose} />
63-
</FlyoutChild>
64-
)}
65-
{this.props.step === AddBankStepType.LOADING && (
66-
<FlyoutChild>
67-
<Loading text={LoadingTextEnum.PROCESSING} />
68-
</FlyoutChild>
69-
)}
70-
</Flyout>
71-
)
72-
}
36+
// If user clicks outside in the process of linking, report to Amplitude
37+
useEffect(() => {
38+
if (userClickedOutside) {
39+
dispatch(
40+
actions.analytics.trackEvent({
41+
key: Analytics.PLAID_CLICK_OUTSIDE,
42+
properties: {
43+
buy_sell_step: buySellStep,
44+
modal_step: step,
45+
origin
46+
}
47+
})
48+
)
49+
}
50+
}, [userClickedOutside])
51+
52+
return (
53+
<Flyout {...props} isOpen data-e2e='addBankModal' onClose={handleClose}>
54+
{step === AddBankStepType.ADD_BANK && (
55+
<FlyoutChild>
56+
{buySellMethod?.id ? (
57+
<Handler handleClose={handleClose} reason={reason} paymentMethodId={buySellMethod.id} />
58+
) : brokerageMethod?.id ? (
59+
<Handler
60+
handleClose={handleClose}
61+
reason={reason}
62+
paymentMethodId={brokerageMethod.id}
63+
/>
64+
) : (
65+
<Handler handleClose={handleClose} />
66+
)}
67+
</FlyoutChild>
68+
)}
69+
{step === AddBankStepType.ADD_BANK_STATUS && (
70+
<FlyoutChild>
71+
<AddBankStatus handleClose={handleClose} />
72+
</FlyoutChild>
73+
)}
74+
{step === AddBankStepType.LOADING && (
75+
<FlyoutChild>
76+
<Loading text={LoadingTextEnum.PROCESSING} />
77+
</FlyoutChild>
78+
)}
79+
</Flyout>
80+
)
7381
}
7482

7583
const mapStateToProps = (state: RootState) => ({
@@ -86,10 +94,12 @@ const enhance = compose(
8694
connector
8795
)
8896

89-
type OwnProps = ModalPropsType
90-
type LinkStatePropsType = ReturnType<typeof mapStateToProps>
97+
type OwnProps = {
98+
buySellStep: keyof typeof BuySellStepType
99+
origin: ModalOriginType
100+
} & ModalPropsType
91101

92-
type State = { show: boolean }
102+
type LinkStatePropsType = ReturnType<typeof mapStateToProps>
93103

94104
export type Props = OwnProps & LinkStatePropsType & ConnectedProps<typeof connector>
95105

packages/blockchain-wallet-v4-frontend/src/scenes/Settings/General/LinkedWireBanks/template.success.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ const Success: React.FC<InjectedFormProps<{}, Props> & Props> = (props) => {
3939
(beneficiary) => beneficiary.currency in WalletFiatEnum
4040
)
4141

42+
if (!walletBeneficiaries.length) return <SettingContainer />
43+
4244
return (
4345
<SettingContainer>
4446
<SettingSummary>

0 commit comments

Comments
 (0)