1
0
Fork 0
mirror of https://github.com/maybe-finance/maybe.git synced 2025-08-09 07:25:19 +02:00

removed intercom references

This commit is contained in:
Tamir 2024-01-10 22:13:12 -08:00
parent cfd07abac9
commit bbe6ec6b75
22 changed files with 69 additions and 378 deletions

View file

@ -1,5 +1,4 @@
import { useAuth0 } from '@auth0/auth0-react' import { useAuth0 } from '@auth0/auth0-react'
import { useIntercom } from '@maybe-finance/client/shared'
import { useRouter } from 'next/router' import { useRouter } from 'next/router'
import { useEffect } from 'react' import { useEffect } from 'react'
import * as Sentry from '@sentry/react' import * as Sentry from '@sentry/react'
@ -7,22 +6,6 @@ import * as Sentry from '@sentry/react'
export default function APM() { export default function APM() {
const { user } = useAuth0() const { user } = useAuth0()
const router = useRouter() const router = useRouter()
const intercom = useIntercom()
// Boot intercom
useEffect(() => {
const isBooted = intercom.boot()
const handleRouteChange = () => {
if (isBooted) {
intercom.update()
}
}
router.events.on('routeChangeComplete', handleRouteChange)
return () => router.events.off('routeChangeComplete', handleRouteChange)
}, [intercom, router.events])
// Identify Sentry user // Identify Sentry user
useEffect(() => { useEffect(() => {

View file

@ -48,15 +48,6 @@ export default function Meta() {
href="https://cdn.jsdelivr.net/npm/remixicon@2.5.0/fonts/remixicon.css" href="https://cdn.jsdelivr.net/npm/remixicon@2.5.0/fonts/remixicon.css"
rel="stylesheet" rel="stylesheet"
/> />
{/* Intercom */}
<script
type="text/javascript"
dangerouslySetInnerHTML={{
__html: `window.INTERCOM_APP_ID='${env.NEXT_PUBLIC_INTERCOM_APP_ID}';(function(){var w=window;var ic=w.Intercom;if(typeof ic==="function"){ic('reattach_activator');ic('update',w.intercomSettings);}else{var d=document;var i=function(){i.c(arguments);};i.q=[];i.c=function(args){i.q.push(args);};w.Intercom=i;var l=function(){var s=d.createElement('script');s.type='text/javascript';s.async=true;s.src='https://widget.intercom.io/widget/' + window.INTERCOM_APP_ID;var x=d.getElementsByTagName('script')[0];x.parentNode.insertBefore(s, x);};if(document.readyState==='complete'){l();}else if(w.attachEvent){w.attachEvent('onload',l);}else{w.addEventListener('load',l,false);}}})();`,
}}
/>
{/* End Intercom */}
</Head> </Head>
) )
} }

View file

@ -1,16 +1,12 @@
const env = { const env = {
NEXT_PUBLIC_API_URL: process.env.NEXT_PUBLIC_API_URL || 'http://localhost:3333', NEXT_PUBLIC_API_URL: process.env.NEXT_PUBLIC_API_URL || 'http://localhost:3333',
NEXT_PUBLIC_AUTH0_DOMAIN: NEXT_PUBLIC_AUTH0_DOMAIN: process.env.NEXT_PUBLIC_AUTH0_DOMAIN || 'REPLACE_THIS',
process.env.NEXT_PUBLIC_AUTH0_DOMAIN || 'REPLACE_THIS', NEXT_PUBLIC_AUTH0_CLIENT_ID: process.env.NEXT_PUBLIC_AUTH0_CLIENT_ID || 'REPLACE_THIS',
NEXT_PUBLIC_AUTH0_CLIENT_ID:
process.env.NEXT_PUBLIC_AUTH0_CLIENT_ID || 'REPLACE_THIS',
NEXT_PUBLIC_AUTH0_AUDIENCE: NEXT_PUBLIC_AUTH0_AUDIENCE:
process.env.NEXT_PUBLIC_AUTH0_AUDIENCE || 'https://maybe-finance-api/v1', process.env.NEXT_PUBLIC_AUTH0_AUDIENCE || 'https://maybe-finance-api/v1',
NEXT_PUBLIC_LD_CLIENT_SIDE_ID: NEXT_PUBLIC_LD_CLIENT_SIDE_ID: process.env.NEXT_PUBLIC_LD_CLIENT_SIDE_ID || 'REPLACE_THIS',
process.env.NEXT_PUBLIC_LD_CLIENT_SIDE_ID || 'REPLACE_THIS',
NEXT_PUBLIC_SENTRY_DSN: process.env.NEXT_PUBLIC_SENTRY_DSN, NEXT_PUBLIC_SENTRY_DSN: process.env.NEXT_PUBLIC_SENTRY_DSN,
NEXT_PUBLIC_SENTRY_ENV: process.env.NEXT_PUBLIC_SENTRY_ENV, NEXT_PUBLIC_SENTRY_ENV: process.env.NEXT_PUBLIC_SENTRY_ENV,
NEXT_PUBLIC_INTERCOM_APP_ID: process.env.NEXT_PUBLIC_INTERCOM_APP_ID || 'REPLACE_THIS',
} }
export default env export default env

View file

@ -33,7 +33,7 @@ Sentry.init({
}) })
// Providers and components only relevant to a logged-in user // Providers and components only relevant to a logged-in user
const WithAuth = withAuthenticationRequired(function ({ children }: PropsWithChildren) { const WithAuth = function ({ children }: PropsWithChildren) {
return ( return (
<ModalManager> <ModalManager>
<UserAccountContextProvider> <UserAccountContextProvider>
@ -46,7 +46,7 @@ const WithAuth = withAuthenticationRequired(function ({ children }: PropsWithChi
</UserAccountContextProvider> </UserAccountContextProvider>
</ModalManager> </ModalManager>
) )
}) }
export default function App({ export default function App({
Component: Page, Component: Page,

View file

@ -11,16 +11,16 @@ export default function OAuth() {
// If our backend doesn't return a token to re-initialize with, show user troubleshooting message // If our backend doesn't return a token to re-initialize with, show user troubleshooting message
if (fetchTokenError) { if (fetchTokenError) {
return ( return (
<div className="fixed h-full w-full flex flex-col items-center gap-4 mt-48"> <div className="fixed flex flex-col items-center w-full h-full gap-4 mt-48">
<LoadingSpinner /> <LoadingSpinner />
{fetchTokenError && ( {fetchTokenError && (
<> <>
<h4>Stuck on this page?</h4> <h4>Stuck on this page?</h4>
<div className="mx-auto w-full max-w-md rounded-2xl bg-gray-800 p-2"> <div className="w-full max-w-md p-2 mx-auto bg-gray-800 rounded-2xl">
<Disclosure defaultOpen> <Disclosure defaultOpen>
{({ open }) => ( {({ open }) => (
<> <>
<Disclosure.Button className="flex w-full items-center justify-between rounded-lg bg-gray-700 px-4 py-2 text-left text-sm font-medium text-purple-900 hover:bg-purple-200 focus:outline-none focus-visible:ring focus-visible:ring-purple-500 focus-visible:ring-opacity-75"> <Disclosure.Button className="flex items-center justify-between w-full px-4 py-2 text-sm font-medium text-left text-purple-900 bg-gray-700 rounded-lg hover:bg-purple-200 focus:outline-none focus-visible:ring focus-visible:ring-purple-500 focus-visible:ring-opacity-75">
<span>Why did this happen?</span> <span>Why did this happen?</span>
<RiArrowUpSFill <RiArrowUpSFill
className={`${ className={`${
@ -47,24 +47,14 @@ export default function OAuth() {
stuck. stuck.
</p> </p>
<ul className="mt-4 list-disc ml-4"> <ul className="mt-4 ml-4 list-disc">
<li> <li>
Try connecting this account on a desktop Try connecting this account on a desktop
device. We have a mobile app on our roadmap, device. We have a mobile app on our roadmap,
but for the time being, desktop browsers but for the time being, desktop browsers
will be your most reliable experience. will be your most reliable experience.
</li> </li>
<li> <li>Still not working? Let us know!</li>
Still not working?{' '}
<button
onClick={() =>
BrowserUtil.showIntercom()
}
className="underline text-cyan"
>
Let us know!
</button>
</li>
</ul> </ul>
</Disclosure.Panel> </Disclosure.Panel>
</Transition> </Transition>

View file

@ -145,19 +145,6 @@ router.get(
}) })
) )
router.get(
'/intercom',
endpoint.create({
resolve: async ({ ctx }) => {
if (!ctx.user || !ctx.user.id) {
throw new Error('User not found')
}
return ctx.userService.getIntercomMetadata(ctx.user.id, env.NX_INTERCOM_SECRET)
},
})
)
router.put( router.put(
'/', '/',
endpoint.create({ endpoint.create({

View file

@ -67,25 +67,13 @@ const envSchema = z.object({
.string() .string()
.default(process.env.NODE_ENV === 'development' ? 'dev' : 'combined'), .default(process.env.NODE_ENV === 'development' ? 'dev' : 'combined'),
NX_INTERCOM_SECRET: z.string().optional(), NX_STRIPE_SECRET_KEY: z.string().default('REPLACE_THIS'),
NX_STRIPE_WEBHOOK_SECRET: z.string().default('whsec_REPLACE_THIS'),
NX_STRIPE_SECRET_KEY: z
.string()
.default(
'REPLACE_THIS'
),
NX_STRIPE_WEBHOOK_SECRET: z
.string()
.default('whsec_REPLACE_THIS'),
NX_STRIPE_PREMIUM_MONTHLY_PRICE_ID: z.string().default('price_REPLACE_THIS'), NX_STRIPE_PREMIUM_MONTHLY_PRICE_ID: z.string().default('price_REPLACE_THIS'),
NX_STRIPE_PREMIUM_YEARLY_PRICE_ID: z.string().default('price_REPLACE_THIS'), NX_STRIPE_PREMIUM_YEARLY_PRICE_ID: z.string().default('price_REPLACE_THIS'),
NX_CDN_PRIVATE_BUCKET: z NX_CDN_PRIVATE_BUCKET: z.string().default('REPLACE_THIS'),
.string() NX_CDN_PUBLIC_BUCKET: z.string().default('REPLACE_THIS'),
.default('REPLACE_THIS'),
NX_CDN_PUBLIC_BUCKET: z
.string()
.default('REPLACE_THIS'),
// Key to secrets manager value // Key to secrets manager value
NX_CDN_SIGNER_SECRET_ID: z.string().default('/apps/maybe-app/CLOUDFRONT_SIGNER1_PRIV'), NX_CDN_SIGNER_SECRET_ID: z.string().default('/apps/maybe-app/CLOUDFRONT_SIGNER1_PRIV'),

View file

@ -179,15 +179,6 @@ export class ServerStack extends Stack {
} }
) )
), ),
NX_INTERCOM_SECRET: ECSSecret.fromSsmParameter(
StringParameter.fromSecureStringParameterAttributes(
this,
'IntercomSecretParam',
{
parameterName: '/providers/NX_INTERCOM_SECRET',
}
)
),
NX_STRIPE_SECRET_KEY: ECSSecret.fromSsmParameter( NX_STRIPE_SECRET_KEY: ECSSecret.fromSsmParameter(
StringParameter.fromSecureStringParameterAttributes( StringParameter.fromSecureStringParameterAttributes(
this, this,

View file

@ -24,11 +24,11 @@ type DescriptionProps = {
function Description({ summary, examples }: DescriptionProps) { function Description({ summary, examples }: DescriptionProps) {
return ( return (
<div className="text-gray-50 text-base mt-2"> <div className="mt-2 text-base text-gray-50">
<p className="mb-4">{summary}</p> <p className="mb-4">{summary}</p>
<ul> <ul>
{examples.map((example) => ( {examples.map((example) => (
<li key={example} className="list-disc ml-6"> <li key={example} className="ml-6 list-disc">
{example} {example}
</li> </li>
))} ))}
@ -123,7 +123,7 @@ export function SidebarOnboarding({ onClose, onHide }: Props) {
if (onboarding.isLoading) { if (onboarding.isLoading) {
return ( return (
<div className="w-full flex justify-center items-center h-full"> <div className="flex items-center justify-center w-full h-full">
<LoadingSpinner /> <LoadingSpinner />
</div> </div>
) )
@ -196,7 +196,7 @@ export function SidebarOnboarding({ onClose, onHide }: Props) {
initial={{ width: 0 }} initial={{ width: 0 }}
animate={{ width: `${syncProgress.progress * 100}%` }} animate={{ width: `${syncProgress.progress * 100}%` }}
transition={{ ease: 'easeOut', duration: 0.5 }} transition={{ ease: 'easeOut', duration: 0.5 }}
className="h-full rounded-full bg-gray-100" className="h-full bg-gray-100 rounded-full"
></motion.div> ></motion.div>
) : ( ) : (
<motion.div <motion.div
@ -225,7 +225,7 @@ export function SidebarOnboarding({ onClose, onHide }: Props) {
</div> </div>
<div className="relative h-2 bg-gray-600 rounded-sm"> <div className="relative h-2 bg-gray-600 rounded-sm">
<div <div
className="absolute inset-0 bg-cyan h-2 rounded-sm" className="absolute inset-0 h-2 rounded-sm bg-cyan"
style={{ style={{
width: `${percent * 100}%`, width: `${percent * 100}%`,
}} }}
@ -233,7 +233,7 @@ export function SidebarOnboarding({ onClose, onHide }: Props) {
</div> </div>
</div> </div>
<div className="flex flex-col items-start gap-2 text-base pr-4 -mr-4 custom-gray-scroll"> <div className="flex flex-col items-start gap-2 pr-4 -mr-4 text-base custom-gray-scroll">
{accountSteps.map((step, idx) => { {accountSteps.map((step, idx) => {
const description = getDescriptionComponent(step.key) const description = getDescriptionComponent(step.key)
@ -252,7 +252,7 @@ export function SidebarOnboarding({ onClose, onHide }: Props) {
open ? 'text-white' : 'text-gray-100' open ? 'text-white' : 'text-gray-100'
)} )}
> >
<div className="flex items-center text-left leading-5 gap-3 mr-auto"> <div className="flex items-center gap-3 mr-auto leading-5 text-left">
<div <div
className={classNames( className={classNames(
'rounded-full w-[28px] h-[28px] flex items-center justify-center shrink-0', 'rounded-full w-[28px] h-[28px] flex items-center justify-center shrink-0',
@ -264,7 +264,7 @@ export function SidebarOnboarding({ onClose, onHide }: Props) {
{step.isComplete || step.isMarkedComplete ? ( {step.isComplete || step.isMarkedComplete ? (
<RiCheckFill size={20} className="text-cyan" /> <RiCheckFill size={20} className="text-cyan" />
) : ( ) : (
<span className="font-medium text-sm"> <span className="text-sm font-medium">
{idx + 1} {idx + 1}
</span> </span>
)} )}
@ -306,7 +306,7 @@ export function SidebarOnboarding({ onClose, onHide }: Props) {
<div className="bg-gray-600 my-4 h-[1px]"></div> <div className="bg-gray-600 my-4 h-[1px]"></div>
{step.isComplete ? ( {step.isComplete ? (
<p className="text-gray-50 text-sm"> <p className="text-sm text-gray-50">
This step has been automatically marked complete This step has been automatically marked complete
since you've added at least 1 of these account since you've added at least 1 of these account
types. types.
@ -354,7 +354,7 @@ export function SidebarOnboarding({ onClose, onHide }: Props) {
<Disclosure defaultOpen> <Disclosure defaultOpen>
{({ open }) => ( {({ open }) => (
<div className={classNames('p-3 rounded-lg bg-grape bg-opacity-10 text-base')}> <div className={classNames('p-3 rounded-lg bg-grape bg-opacity-10 text-base')}>
<Disclosure.Button className="flex items-center gap-2 text-grape w-full font-medium"> <Disclosure.Button className="flex items-center w-full gap-2 font-medium text-grape">
<HiOutlineSparkles size={24} /> <HiOutlineSparkles size={24} />
<span className="mr-auto">Bonus</span> <span className="mr-auto">Bonus</span>
{open ? <RiArrowUpSFill size={18} /> : <RiArrowDownSFill size={18} />} {open ? <RiArrowUpSFill size={18} /> : <RiArrowDownSFill size={18} />}
@ -399,13 +399,7 @@ export function SidebarOnboarding({ onClose, onHide }: Props) {
</Disclosure> </Disclosure>
<p className="text-sm text-gray-100"> <p className="text-sm text-gray-100">
If you have any issues with connecting accounts, please let us know{' '} If you have any issues with connecting accounts, please let us know.
<button
onClick={() => BrowserUtil.showIntercom()}
className="text-cyan cursor-pointer hover:underline"
>
via live chat
</button>
</p> </p>
</> </>
) )

View file

@ -126,9 +126,9 @@ function PromptStep({
<> <>
<BoxIcon icon={RiLink} /> <BoxIcon icon={RiLink} />
<h4 className="text-white mt-6 mb-2">Link accounts?</h4> <h4 className="mt-6 mb-2 text-white">Link accounts?</h4>
<p className="mb-6 text-gray-50 text-base"> <p className="mb-6 text-base text-gray-50">
We found an {secondaryProvider === 'apple' ? 'Apple ' : ' '} account using the same We found an {secondaryProvider === 'apple' ? 'Apple ' : ' '} account using the same
email address as this one in our system. Do you want to link it? email address as this one in our system. Do you want to link it?
</p> </p>
@ -140,7 +140,7 @@ function PromptStep({
{secondaryProvider === 'apple' ? ( {secondaryProvider === 'apple' ? (
<button <button
onClick={onNext} onClick={onNext}
className="w-2/4 flex items-center px-4 py-2 rounded text-base bg-white text-black shadow hover:bg-gray-25 focus:bg-gray-25 focus:ring-gray-600 font-medium" className="flex items-center w-2/4 px-4 py-2 text-base font-medium text-black bg-white rounded shadow hover:bg-gray-25 focus:bg-gray-25 focus:ring-gray-600"
> >
<RiAppleFill className="w-4 h-4 mx-2" /> Link with Apple <RiAppleFill className="w-4 h-4 mx-2" /> Link with Apple
</button> </button>
@ -165,7 +165,7 @@ function ConfirmStep({
<> <>
<BoxIcon icon={RiLink} /> <BoxIcon icon={RiLink} />
<h4 className="text-white my-6 animate-pulse">Authentication in progress...</h4> <h4 className="my-6 text-white animate-pulse">Authentication in progress...</h4>
<Button fullWidth variant="secondary" onClick={onCancel}> <Button fullWidth variant="secondary" onClick={onCancel}>
Cancel Cancel
@ -178,7 +178,7 @@ function ConfirmStep({
<> <>
<BoxIcon icon={isLoading ? RiLinkUnlink : RiLink} /> <BoxIcon icon={isLoading ? RiLinkUnlink : RiLink} />
<h4 className="text-white mt-6 mb-2"> <h4 className="mt-6 mb-2 text-white">
{isLoading ? 'Linking accounts ...' : 'Continue linking accounts?'} {isLoading ? 'Linking accounts ...' : 'Continue linking accounts?'}
</h4> </h4>
@ -198,7 +198,7 @@ function ConfirmStep({
process by unlinking the account in your settings.{' '} process by unlinking the account in your settings.{' '}
</p> </p>
<p className="text-white mt-4">No data will be deleted.</p> <p className="mt-4 text-white">No data will be deleted.</p>
</> </>
)} )}
</div> </div>
@ -229,9 +229,9 @@ function LinkComplete({ onClose }: { onClose(): void }) {
<> <>
<BoxIcon icon={RiCheckLine} variant="teal" /> <BoxIcon icon={RiCheckLine} variant="teal" />
<h4 className="text-white mt-6 mb-2">Accounts linked successfully!</h4> <h4 className="mt-6 mb-2 text-white">Accounts linked successfully!</h4>
<p className="mb-6 text-gray-50 text-base"> <p className="mb-6 text-base text-gray-50">
Your accounts have been linked and the data has been merged successfully. Your accounts have been linked and the data has been merged successfully.
</p> </p>
@ -249,16 +249,11 @@ function LinkError({ onClose, error }: { onClose(): void; error: string }) {
<> <>
<BoxIcon icon={RiLink} variant="red" /> <BoxIcon icon={RiLink} variant="red" />
<h4 className="text-white mt-6 mb-2">Account linking failed</h4> <h4 className="mt-6 mb-2 text-white">Account linking failed</h4>
<p className="mb-2 text-gray-50 text-base">{error}</p> <p className="mb-2 text-base text-gray-50">{error}</p>
<button <button className="mb-6 text-base underline text-cyan">Please contact us.</button>
className="underline text-cyan text-base mb-6"
onClick={BrowserUtil.showIntercom}
>
Please contact us.
</button>
<div className="flex w-full gap-4"> <div className="flex w-full gap-4">
<Button fullWidth onClick={onClose}> <Button fullWidth onClick={onClose}>

View file

@ -11,7 +11,7 @@ import { useMemo } from 'react'
import sumBy from 'lodash/sumBy' import sumBy from 'lodash/sumBy'
import toast from 'react-hot-toast' import toast from 'react-hot-toast'
import { useInfiniteQuery, useMutation, useQuery, useQueryClient } from '@tanstack/react-query' import { useInfiniteQuery, useMutation, useQuery, useQueryClient } from '@tanstack/react-query'
import { useAxiosWithAuth, useIntercom } from '..' import { useAxiosWithAuth } from '..'
import { invalidateAccountQueries } from '../utils' import { invalidateAccountQueries } from '../utils'
const AccountApi = (axios: AxiosInstance) => ({ const AccountApi = (axios: AxiosInstance) => ({
@ -135,33 +135,6 @@ export function useAccountApi() {
const queryClient = useQueryClient() const queryClient = useQueryClient()
const { axios } = useAxiosWithAuth() const { axios } = useAxiosWithAuth()
const api = useMemo(() => AccountApi(axios), [axios]) const api = useMemo(() => AccountApi(axios), [axios])
const { update: updateIntercom } = useIntercom()
const useAccounts = (
options?: Omit<
UseQueryOptions<
SharedType.AccountsResponse,
unknown,
SharedType.AccountsResponse,
string[]
>,
'queryKey' | 'queryFn' | 'staleTime'
>
) =>
useQuery(['accounts'], api.getAccounts, {
staleTime: staleTimes.accounts,
onSuccess: (...args) => {
if (options?.onSuccess) options.onSuccess(...args)
const [{ accounts, connections }] = args
updateIntercom({
'Manual Accounts': accounts.length,
'Connected Accounts': sumBy(connections, (c) => c.accounts.length),
Connections: connections.length,
})
},
...options,
})
const useAccount = ( const useAccount = (
id: Account['id'], id: Account['id'],

View file

@ -38,11 +38,6 @@ const UserApi = (
return data return data
}, },
async getIntercomMetadata() {
const { data } = await axios.get<SharedType.UserIntercomMetadata>('/users/intercom')
return data
},
async update(userData: SharedType.UpdateUser) { async update(userData: SharedType.UpdateUser) {
const { data } = await axios.put<SharedType.User>('/users', userData) const { data } = await axios.put<SharedType.User>('/users', userData)
return data return data
@ -254,22 +249,6 @@ export function useUserApi() {
staleTime: staleTimes.insights, staleTime: staleTimes.insights,
}) })
const useIntercomMetadata = (
options?: Omit<
UseQueryOptions<
SharedType.UserIntercomMetadata,
unknown,
SharedType.UserIntercomMetadata,
any[]
>,
'queryKey' | 'queryFn'
>
) =>
useQuery(['users', 'intercom-metadata'], api.getIntercomMetadata, {
refetchOnWindowFocus: false,
...options,
})
const useProfile = ( const useProfile = (
options?: Omit< options?: Omit<
UseQueryOptions<SharedType.User, unknown, SharedType.User, any[]>, UseQueryOptions<SharedType.User, unknown, SharedType.User, any[]>,
@ -471,7 +450,6 @@ export function useUserApi() {
useNetWorthSeries, useNetWorthSeries,
useInsights, useInsights,
useCurrentNetWorth, useCurrentNetWorth,
useIntercomMetadata,
useProfile, useProfile,
useUpdateProfile, useUpdateProfile,
useAuth0Profile, useAuth0Profile,

View file

@ -1,7 +1,6 @@
export * from './useAxiosWithAuth' export * from './useAxiosWithAuth'
export * from './useDebounce' export * from './useDebounce'
export * from './useFinicity' export * from './useFinicity'
export * from './useIntercom'
export * from './useInterval' export * from './useInterval'
export * from './useLastUpdated' export * from './useLastUpdated'
export * from './useLocalStorage' export * from './useLocalStorage'

View file

@ -35,9 +35,6 @@ export function useFinicity() {
FinicityConnect.launch(link, { FinicityConnect.launch(link, {
onDone(evt: ConnectDoneEvent) { onDone(evt: ConnectDoneEvent) {
logger.debug(`Finicity Connect onDone event`, evt) logger.debug(`Finicity Connect onDone event`, evt)
BrowserUtil.trackIntercomEvent('FINICITY_CONNECT_DONE', {
...evt,
})
setExpectingAccounts(true) setExpectingAccounts(true)
}, },
onError(evt: ConnectErrorEvent) { onError(evt: ConnectErrorEvent) {
@ -50,25 +47,15 @@ export function useFinicity() {
'finicity.error.reason': evt.reason, 'finicity.error.reason': evt.reason,
}, },
}) })
BrowserUtil.trackIntercomEvent('FINICITY_CONNECT_ERROR', {
...evt,
})
}, },
onCancel(evt: ConnectCancelEvent) { onCancel(evt: ConnectCancelEvent) {
logger.debug(`Finicity Connect onCancel event`, evt) logger.debug(`Finicity Connect onCancel event`, evt)
BrowserUtil.trackIntercomEvent('FINICITY_CONNECT_CANCEL', {
...evt,
})
}, },
onUser(evt: any) { onUser(evt: any) {
BrowserUtil.trackIntercomEvent('FINICITY_CONNECT_USER_ACTION', { //Nothing
...evt,
})
}, },
onRoute(evt: ConnectRouteEvent) { onRoute(evt: ConnectRouteEvent) {
BrowserUtil.trackIntercomEvent('FINICITY_CONNECT_ROUTE_EVENT', { //Nothing
...evt,
})
}, },
}) })
}, },

View file

@ -1,50 +0,0 @@
import { useCallback } from 'react'
import { useAuth0 } from '@auth0/auth0-react'
import type { SharedType } from '@maybe-finance/shared'
import { useUserApi } from '../api'
import { BrowserUtil } from '..'
export function useIntercom() {
const { user, isAuthenticated } = useAuth0<SharedType.Auth0ReactUser>()
const { useIntercomMetadata } = useUserApi()
const { data: intercomMetadata } = useIntercomMetadata({ enabled: isAuthenticated })
const boot = useCallback(
(data?: BrowserUtil.IntercomData) => {
if (!user?.sub || !intercomMetadata?.hash) return false
BrowserUtil.bootIntercom({
user_id: user.sub,
user_hash: intercomMetadata.hash,
email: user.email,
name: user.name,
last_request_at: Math.floor(new Date().getTime() / 1000),
...data,
})
return true
},
[user, intercomMetadata]
)
const update = useCallback(
(data?: BrowserUtil.IntercomData, updateLastRequestAt = true) => {
if (!user?.sub || !intercomMetadata?.hash) return
BrowserUtil.updateIntercom({
user_id: user.sub,
user_hash: intercomMetadata.hash,
email: user.email,
name: user.name,
last_request_at: updateLastRequestAt
? Math.floor(new Date().getTime() / 1000)
: undefined,
...data,
})
},
[user, intercomMetadata]
)
return { boot, update }
}

View file

@ -69,21 +69,7 @@ export function usePlaid(mode: 'default' | 'oauth' = 'default') {
}, },
// https://plaid.com/docs/link/web/#onexit // https://plaid.com/docs/link/web/#onexit
onExit: (error, metadata) => { onExit: (error, metadata) => {
if (error) { //Nothing
const { error_code, error_type, error_message, display_message } = error
BrowserUtil.trackIntercomEvent(`PLAID_LINK_EXIT_ERROR`, {
error_type,
error_code,
error_message,
display_message,
reference: 'https://plaid.com/docs/errors/',
})
}
BrowserUtil.trackIntercomEvent('PLAID_EXIT_EVENT', {
...error,
...metadata,
})
}, },
// https://plaid.com/docs/link/web/#onevent // https://plaid.com/docs/link/web/#onevent
onEvent: (event, metadata) => { onEvent: (event, metadata) => {
@ -97,9 +83,6 @@ export function usePlaid(mode: 'default' | 'oauth' = 'default') {
}, },
}) })
// Capture all events to Intercom
BrowserUtil.trackIntercomEvent(event, metadata)
logger.debug( logger.debug(
`Plaid link event: ${event} for session ID ${metadata.link_session_id}`, `Plaid link event: ${event} for session ID ${metadata.link_session_id}`,
metadata metadata

View file

@ -1,5 +1,4 @@
export * from './image-loaders' export * from './image-loaders'
export * from './intercom'
export * from './browser-utils' export * from './browser-utils'
export * from './account-utils' export * from './account-utils'
export * from './agreement-utils' export * from './agreement-utils'

View file

@ -1,36 +0,0 @@
export type IntercomData = {
user_id?: string
user_hash?: string
email?: string
name?: string
last_request_at?: number
'Manual Accounts'?: number
'Connected Accounts'?: number
Connections?: number
}
export function bootIntercom(data?: IntercomData) {
const w = window as any
w.Intercom('boot', {
app_id: w.INTERCOM_APP_ID,
...data,
})
}
export function updateIntercom(data?: IntercomData) {
;(window as any).Intercom('update', {
...data,
})
}
export function trackIntercomEvent(name: string, data: Record<string, any>) {
;(window as any).Intercom('trackEvent', name, {
...data,
})
}
export function showIntercom() {
;(window as any).Intercom('show')
}

View file

@ -280,22 +280,6 @@ export class UserService implements IUserService {
} }
} }
async getIntercomMetadata(
userId: User['id'],
secret?: string
): Promise<SharedType.UserIntercomMetadata> {
const { auth0Id } = await this.prisma.user.findUniqueOrThrow({
select: { auth0Id: true },
where: { id: userId },
})
return {
hash: secret
? crypto.createHmac('sha256', secret).update(auth0Id).digest('hex')
: undefined,
}
}
async getSignedAgreements(userId: User['id']) { async getSignedAgreements(userId: User['id']) {
return this.prisma.agreement.findMany({ return this.prisma.agreement.findMany({
distinct: 'type', distinct: 'type',

View file

@ -227,10 +227,6 @@ export type UserSubscription = {
currentPeriodEnd: DateTime | null currentPeriodEnd: DateTime | null
} }
export type UserIntercomMetadata = {
hash?: string
}
export type UserMemberCardDetails = { export type UserMemberCardDetails = {
memberNumber: number memberNumber: number
name: string name: string

View file

@ -0,0 +1,25 @@
-- DropIndex
DROP INDEX "account_balance_date_idx";
-- DropIndex
DROP INDEX "security_pricing_date_idx";
-- AlterTable
ALTER TABLE "account" ALTER COLUMN "category" DROP DEFAULT,
ALTER COLUMN "classification" DROP DEFAULT;
-- AlterTable
ALTER TABLE "investment_transaction" ALTER COLUMN "flow" DROP DEFAULT,
ALTER COLUMN "category" DROP DEFAULT;
-- AlterTable
ALTER TABLE "transaction" ALTER COLUMN "flow" DROP DEFAULT;
-- AlterTable
ALTER TABLE "user" ALTER COLUMN "trial_end" SET DEFAULT NOW() + interval '14 days';
-- CreateIndex
CREATE INDEX "account_balance_date_idx" ON "account_balance"("date");
-- CreateIndex
CREATE INDEX "security_pricing_date_idx" ON "security_pricing"("date");

View file

@ -7707,7 +7707,7 @@ async@^3.0.0:
resolved "https://registry.yarnpkg.com/async/-/async-3.2.4.tgz#2d22e00f8cddeb5fde5dd33522b56d1cf569a81c" resolved "https://registry.yarnpkg.com/async/-/async-3.2.4.tgz#2d22e00f8cddeb5fde5dd33522b56d1cf569a81c"
integrity sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ== integrity sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==
async@^3.1.0, async@^3.2.0, async@^3.2.3: async@^3.2.0, async@^3.2.3:
version "3.2.3" version "3.2.3"
resolved "https://registry.yarnpkg.com/async/-/async-3.2.3.tgz#ac53dafd3f4720ee9e8a160628f18ea91df196c9" resolved "https://registry.yarnpkg.com/async/-/async-3.2.3.tgz#ac53dafd3f4720ee9e8a160628f18ea91df196c9"
integrity sha512-spZRyzKL5l5BZQrr/6m/SqFdBN0q3OCI0f9rjfBzCMBIP4p75P620rR3gTmaksNOhmzgdxcaxdNfMy6anrbM0g== integrity sha512-spZRyzKL5l5BZQrr/6m/SqFdBN0q3OCI0f9rjfBzCMBIP4p75P620rR3gTmaksNOhmzgdxcaxdNfMy6anrbM0g==
@ -8072,7 +8072,7 @@ balanced-match@^1.0.0:
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee"
integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==
base64-js@^1.0.2, base64-js@^1.3.0, base64-js@^1.3.1: base64-js@^1.0.2, base64-js@^1.3.1:
version "1.5.1" version "1.5.1"
resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a"
integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==
@ -9046,7 +9046,7 @@ clone-response@^1.0.2:
dependencies: dependencies:
mimic-response "^1.0.0" mimic-response "^1.0.0"
clone@2.x, clone@^2.1.2: clone@^2.1.2:
version "2.1.2" version "2.1.2"
resolved "https://registry.yarnpkg.com/clone/-/clone-2.1.2.tgz#1b7f4b9f591f1e8f83670401600345a02887435f" resolved "https://registry.yarnpkg.com/clone/-/clone-2.1.2.tgz#1b7f4b9f591f1e8f83670401600345a02887435f"
integrity sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18= integrity sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18=
@ -11472,11 +11472,6 @@ extsprintf@^1.2.0:
resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.1.tgz#8d172c064867f235c0c84a596806d279bf4bcc07" resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.1.tgz#8d172c064867f235c0c84a596806d279bf4bcc07"
integrity sha512-Wrk35e8ydCKDj/ArClo1VrPVmN8zph5V4AtHwIuHhvMXsKf73UT3BOD+azBIW+3wOJ4FhEH7zyaJCFvChjYvMA== integrity sha512-Wrk35e8ydCKDj/ArClo1VrPVmN8zph5V4AtHwIuHhvMXsKf73UT3BOD+azBIW+3wOJ4FhEH7zyaJCFvChjYvMA==
fast-deep-equal@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz#7b05218ddf9667bf7f370bf7fdb2cb15fdd0aa49"
integrity sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=
fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3:
version "3.1.3" version "3.1.3"
resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525"
@ -14694,51 +14689,6 @@ language-tags@^1.0.5:
dependencies: dependencies:
language-subtag-registry "~0.3.2" language-subtag-registry "~0.3.2"
launchdarkly-eventsource@1.4.4:
version "1.4.4"
resolved "https://registry.yarnpkg.com/launchdarkly-eventsource/-/launchdarkly-eventsource-1.4.4.tgz#fa595af8602e487c61520787170376c6a1104459"
integrity sha512-GL+r2Y3WccJlhFyL2buNKel+9VaMnYpbE/FfCkOST5jSNSFodahlxtGyrE8o7R+Qhobyq0Ree4a7iafJDQi9VQ==
launchdarkly-js-client-sdk@2.20.2:
version "2.20.2"
resolved "https://registry.yarnpkg.com/launchdarkly-js-client-sdk/-/launchdarkly-js-client-sdk-2.20.2.tgz#4c5a72cc8e2d0b5dbb3d4dd31a30e36b243b9e8f"
integrity sha512-KAAb1nnpAiQF4dCohe0d1M7QSxhJfa9v6+5PJUPANAVZ3EwX/y6aL7mK8Mik2IrL5I654mseXxidfSbnHVMBXA==
dependencies:
escape-string-regexp "^1.0.5"
launchdarkly-js-sdk-common "3.5.1"
launchdarkly-js-sdk-common@3.5.1:
version "3.5.1"
resolved "https://registry.yarnpkg.com/launchdarkly-js-sdk-common/-/launchdarkly-js-sdk-common-3.5.1.tgz#25966a5b25311bba4db7c9f3807031166b97545d"
integrity sha512-MTIn5XkREKCb87A78QTQXNEPaDaC35PzW1mU8bTf1hdxgm78LiDtXjvtKvVk+oU5tqFrH7dZrLKMCGkt03VaKw==
dependencies:
base64-js "^1.3.0"
fast-deep-equal "^2.0.1"
uuid "^3.3.2"
launchdarkly-node-server-sdk@^6.4.3:
version "6.4.3"
resolved "https://registry.yarnpkg.com/launchdarkly-node-server-sdk/-/launchdarkly-node-server-sdk-6.4.3.tgz#7a35caee3709d70776173fcd814fa0cd88de1270"
integrity sha512-n7L2mAAcKqUUKhCGHRd6soVD3Ws4ME4R+MNShWL0lBBY8yGASIaPAW4rGmkob/hs+grCNRXn8HHlZCufecaX4w==
dependencies:
async "^3.1.0"
launchdarkly-eventsource "1.4.4"
lru-cache "^6.0.0"
node-cache "^5.1.0"
semver "^7.3.0"
tunnel "0.0.6"
uuid "^8.3.2"
launchdarkly-react-client-sdk@^2.25.1:
version "2.25.1"
resolved "https://registry.yarnpkg.com/launchdarkly-react-client-sdk/-/launchdarkly-react-client-sdk-2.25.1.tgz#fdfbb532e239227e1f64efa1dccff42ae0ce5533"
integrity sha512-OejNEZs8QvW1q/qn/ey1o6lfdLw3aW8tWfLucUmRTeazp0MtBXARnEZoqHWGmgRkRlxkTul3HqecksnoXC/wBg==
dependencies:
hoist-non-react-statics "^3.3.2"
launchdarkly-js-client-sdk "2.20.2"
lodash.camelcase "^4.3.0"
uuid "^3.3.2"
lazy-ass@^1.6.0: lazy-ass@^1.6.0:
version "1.6.0" version "1.6.0"
resolved "https://registry.yarnpkg.com/lazy-ass/-/lazy-ass-1.6.0.tgz#7999655e8646c17f089fdd187d150d3324d54513" resolved "https://registry.yarnpkg.com/lazy-ass/-/lazy-ass-1.6.0.tgz#7999655e8646c17f089fdd187d150d3324d54513"
@ -15968,13 +15918,6 @@ node-addon-api@^3.2.1:
resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-3.2.1.tgz#81325e0a2117789c0128dab65e7e38f07ceba161" resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-3.2.1.tgz#81325e0a2117789c0128dab65e7e38f07ceba161"
integrity sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A== integrity sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A==
node-cache@^5.1.0:
version "5.1.2"
resolved "https://registry.yarnpkg.com/node-cache/-/node-cache-5.1.2.tgz#f264dc2ccad0a780e76253a694e9fd0ed19c398d"
integrity sha512-t1QzWwnk4sjLWaQAS8CHgOJ+RAfmHpxFWmc36IWTiWHQfs0w5JDMBS1b1ZxQteo0vVVuWJvIUKHDkkeK7vIGCg==
dependencies:
clone "2.x"
node-dir@^0.1.10: node-dir@^0.1.10:
version "0.1.17" version "0.1.17"
resolved "https://registry.yarnpkg.com/node-dir/-/node-dir-0.1.17.tgz#5f5665d93351335caabef8f1c554516cf5f1e4e5" resolved "https://registry.yarnpkg.com/node-dir/-/node-dir-0.1.17.tgz#5f5665d93351335caabef8f1c554516cf5f1e4e5"
@ -18989,7 +18932,7 @@ semver@7.3.4:
dependencies: dependencies:
lru-cache "^6.0.0" lru-cache "^6.0.0"
semver@7.x, semver@^7.3.0: semver@7.x:
version "7.3.5" version "7.3.5"
resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.5.tgz#0b621c879348d8998e4b0e4be94b3f12e6018ef7" resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.5.tgz#0b621c879348d8998e4b0e4be94b3f12e6018ef7"
integrity sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ== integrity sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==
@ -20565,11 +20508,6 @@ tunnel-agent@^0.6.0:
dependencies: dependencies:
safe-buffer "^5.0.1" safe-buffer "^5.0.1"
tunnel@0.0.6:
version "0.0.6"
resolved "https://registry.yarnpkg.com/tunnel/-/tunnel-0.0.6.tgz#72f1314b34a5b192db012324df2cc587ca47f92c"
integrity sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==
tweetnacl@^0.14.3, tweetnacl@~0.14.0: tweetnacl@^0.14.3, tweetnacl@~0.14.0:
version "0.14.5" version "0.14.5"
resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64"