diff --git a/packages/webapp/src/containers/Banking/Plaid/PlaidLanchLink.tsx b/packages/webapp/src/containers/Banking/Plaid/PlaidLanchLink.tsx index a925181da..fb0cef281 100644 --- a/packages/webapp/src/containers/Banking/Plaid/PlaidLanchLink.tsx +++ b/packages/webapp/src/containers/Banking/Plaid/PlaidLanchLink.tsx @@ -1,5 +1,4 @@ // @ts-nocheck -import { usePlaidExchangeToken } from '@/hooks/query'; import React, { useEffect } from 'react'; import { usePlaidLink, @@ -10,28 +9,28 @@ import { PlaidLinkOnEventMetadata, PlaidLinkStableEvent, } from 'react-plaid-link'; -import { useHistory } from 'react-router-dom'; -// import { exchangeToken, setItemState } from '../services/api'; -// import { useItems, useLink, useErrors } from '../services'; +import { logEvent } from './_utils'; +import { usePlaidExchangeToken } from '@/hooks/query'; +import { useResetBankingPlaidToken } from '@/hooks/state/banking'; -interface LaunchLinkProps { - isOauth?: boolean; +interface PlaidLaunchLinkProps { token: string; - userId: number; itemId?: number | null; children?: React.ReactNode; } -// Uses the usePlaidLink hook to manage the Plaid Link creation. See https://github.com/plaid/react-plaid-link for full usage instructions. -// The link token passed to usePlaidLink cannot be null. It must be generated outside of this component. In this sample app, the link token -// is generated in the link context in client/src/services/link.js. - -export function LaunchLink(props: LaunchLinkProps) { - const history = useHistory(); - // const { getItemsByUser, getItemById } = useItems(); - // const { generateLinkToken, deleteLinkToken } = useLink(); - // const { setError, resetError } = useErrors(); - +/** + * Uses the usePlaidLink hook to manage the Plaid Link creation. + * See https://github.com/plaid/react-plaid-link for full usage instructions. + * The link token passed to usePlaidLink cannot be null. + * It must be generated outside of this component. In this sample app, the link token + * is generated in the link context in client/src/services/link.js. + * + * @param {PlaidLaunchLinkProps} props + * @returns {React.ReactNode} + */ +export function LaunchLink(props: PlaidLaunchLinkProps) { + const resetPlaidToken = useResetBankingPlaidToken(); const { mutateAsync: exchangeAccessToken } = usePlaidExchangeToken(); // define onSuccess, onExit and onEvent functions as configs for Plaid Link creation @@ -40,7 +39,7 @@ export function LaunchLink(props: LaunchLinkProps) { metadata: PlaidLinkOnSuccessMetadata, ) => { // log and save metatdata - // logSuccess(metadata, props.userId); + logSuccess(metadata); if (props.itemId != null) { // update mode: no need to exchange public token // await setItemState(props.itemId, 'good'); @@ -48,39 +47,26 @@ export function LaunchLink(props: LaunchLinkProps) { // getItemById(props.itemId, true); // regular link mode: exchange public token for access token } else { - // call to Plaid api endpoint: /item/public_token/exchange in order to obtain access_token which is then stored with the created item - debugger; - await exchangeAccessToken({ public_token: publicToken, institution_id: metadata.institution.institution_id, }); - // await exchangeToken( - // publicToken, - // metadata.institution, - // metadata.accounts, - // props.userId, - // ); - // getItemsByUser(props.userId, true); } // resetError(); - // deleteLinkToken(props.userId, null); - history.push(`/user/${props.userId}`); + resetPlaidToken(); }; + // Handle other error codes, see https://plaid.com/docs/errors/ const onExit = async ( error: PlaidLinkError | null, metadata: PlaidLinkOnExitMetadata, ) => { // log and save error and metatdata - // logExit(error, metadata, props.userId); - if (error != null && error.error_code === 'INVALID_LINK_TOKEN') { - // await generateLinkToken(props.userId, props.itemId); - } + logExit(error, metadata, props.userId); if (error != null) { // setError(error.error_code, error.display_message || error.error_message); } - // to handle other error codes, see https://plaid.com/docs/errors/ + resetPlaidToken(); }; const onEvent = async ( @@ -91,7 +77,7 @@ export function LaunchLink(props: LaunchLinkProps) { if (eventName === 'ERROR' && metadata.error_code != null) { // setError(metadata.error_code, ' '); } - // logEvent(eventName, metadata); + logEvent(eventName, metadata); }; const config: PlaidLinkOptionsWithLinkToken = { @@ -101,31 +87,14 @@ export function LaunchLink(props: LaunchLinkProps) { token: props.token, }; - if (props.isOauth) { - // add additional receivedRedirectUri config when handling an OAuth reidrect - config.receivedRedirectUri = window.location.href; - } const { open, ready } = usePlaidLink(config); useEffect(() => { // initiallizes Link automatically - if (props.isOauth && ready) { - open(); - } else if (ready) { - // regular, non-OAuth case: - // set link token, userId and itemId in local storage for use if needed later by OAuth - - localStorage.setItem( - 'oauthConfig', - JSON.stringify({ - userId: props.userId, - itemId: props.itemId, - token: props.token, - }), - ); + if (ready) { open(); } - }, [ready, open, props.isOauth, props.userId, props.itemId, props.token]); + }, [ready, open, props.itemId, props.token]); return <>; } diff --git a/packages/webapp/src/containers/Banking/Plaid/_utils.ts b/packages/webapp/src/containers/Banking/Plaid/_utils.ts new file mode 100644 index 000000000..cef95bba2 --- /dev/null +++ b/packages/webapp/src/containers/Banking/Plaid/_utils.ts @@ -0,0 +1,30 @@ +import { + PlaidLinkError, + PlaidLinkOnEventMetadata, + PlaidLinkOnExitMetadata, + PlaidLinkOnSuccessMetadata, + PlaidLinkStableEvent, +} from 'react-plaid-link'; + +export const logEvent = ( + eventName: PlaidLinkStableEvent | string, + metadata: + | PlaidLinkOnEventMetadata + | PlaidLinkOnSuccessMetadata + | PlaidLinkOnExitMetadata, + error?: PlaidLinkError | null, +) => { + console.log(`Link Event: ${eventName}`, metadata, error); +}; + +export const logSuccess = async ({ + institution, + accounts, + link_session_id, +}: PlaidLinkOnSuccessMetadata) => { + logEvent('onSuccess', { + institution, + accounts, + link_session_id, + }); +}; diff --git a/packages/webapp/src/containers/CashFlow/CashFlowAccounts/CashflowAccountsPlaidLink.tsx b/packages/webapp/src/containers/CashFlow/CashFlowAccounts/CashflowAccountsPlaidLink.tsx index b3a927f9b..5eeb72ddd 100644 --- a/packages/webapp/src/containers/CashFlow/CashFlowAccounts/CashflowAccountsPlaidLink.tsx +++ b/packages/webapp/src/containers/CashFlow/CashFlowAccounts/CashflowAccountsPlaidLink.tsx @@ -4,5 +4,5 @@ import { useGetBankingPlaidToken } from '@/hooks/state/banking'; export function CashflowAccountsPlaidLink() { const plaidToken = useGetBankingPlaidToken(); - return ; + return ; } diff --git a/packages/webapp/src/hooks/state/banking.ts b/packages/webapp/src/hooks/state/banking.ts index d7499dfd7..9b6b356ca 100644 --- a/packages/webapp/src/hooks/state/banking.ts +++ b/packages/webapp/src/hooks/state/banking.ts @@ -1,4 +1,8 @@ -import { getPlaidToken, setPlaidId } from '@/store/banking/banking.reducer'; +import { + getPlaidToken, + setPlaidId, + resetPlaidId, +} from '@/store/banking/banking.reducer'; import { useCallback } from 'react'; import { useDispatch, useSelector } from 'react-redux'; @@ -18,3 +22,11 @@ export const useGetBankingPlaidToken = () => { return plaidToken; }; + +export const useResetBankingPlaidToken = () => { + const dispatch = useDispatch(); + + return useCallback(() => { + dispatch(resetPlaidId()); + }, [dispatch]); +}; diff --git a/packages/webapp/src/store/banking/banking.reducer.ts b/packages/webapp/src/store/banking/banking.reducer.ts index 93023cdc0..d6a842d32 100644 --- a/packages/webapp/src/store/banking/banking.reducer.ts +++ b/packages/webapp/src/store/banking/banking.reducer.ts @@ -13,8 +13,11 @@ export const PlaidSlice = createSlice({ setPlaidId: (state: StorePlaidState, action: PayloadAction) => { state.plaidToken = action.payload; }, + resetPlaidId: (state: StorePlaidState) => { + state.plaidToken = ''; + } }, }); -export const { setPlaidId } = PlaidSlice.actions; -export const getPlaidToken = (state: any) => state.plaid.plaidToken; +export const { setPlaidId, resetPlaidId } = PlaidSlice.actions; +export const getPlaidToken = (state: any) => state.plaid.plaidToken; \ No newline at end of file