mirror of
https://github.com/maybe-finance/maybe.git
synced 2025-08-09 07:25:19 +02:00
feat: remove Intercom usage
This commit is contained in:
parent
d59484a537
commit
361d07cd79
19 changed files with 18 additions and 255 deletions
|
@ -1,28 +1,9 @@
|
|||
import { useAuth0 } from '@auth0/auth0-react'
|
||||
import { useIntercom } from '@maybe-finance/client/shared'
|
||||
import { useRouter } from 'next/router'
|
||||
import { useEffect } from 'react'
|
||||
import * as Sentry from '@sentry/react'
|
||||
|
||||
export default function APM() {
|
||||
const { user } = useAuth0()
|
||||
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
|
||||
useEffect(() => {
|
||||
|
|
|
@ -48,15 +48,6 @@ export default function Meta() {
|
|||
href="https://cdn.jsdelivr.net/npm/remixicon@2.5.0/fonts/remixicon.css"
|
||||
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>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -10,7 +10,6 @@ const env = {
|
|||
process.env.NEXT_PUBLIC_LD_CLIENT_SIDE_ID || 'REPLACE_THIS',
|
||||
NEXT_PUBLIC_SENTRY_DSN: process.env.NEXT_PUBLIC_SENTRY_DSN,
|
||||
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
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { BrowserUtil, usePlaid } from '@maybe-finance/client/shared'
|
||||
import { usePlaid } from '@maybe-finance/client/shared'
|
||||
import { Disclosure, Transition } from '@headlessui/react'
|
||||
import { RiArrowUpSFill } from 'react-icons/ri'
|
||||
import { Button, LoadingSpinner } from '@maybe-finance/design-system'
|
||||
|
@ -56,14 +56,9 @@ export default function OAuth() {
|
|||
</li>
|
||||
<li>
|
||||
Still not working?{' '}
|
||||
<button
|
||||
onClick={() =>
|
||||
BrowserUtil.showIntercom()
|
||||
}
|
||||
className="underline text-cyan"
|
||||
>
|
||||
<a className="underline text-cyan" href="mailto:hello@maybe.co">
|
||||
Let us know!
|
||||
</button>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</Disclosure.Panel>
|
||||
|
|
|
@ -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(
|
||||
'/',
|
||||
endpoint.create({
|
||||
|
|
|
@ -65,8 +65,6 @@ const envSchema = z.object({
|
|||
.string()
|
||||
.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_PREMIUM_MONTHLY_PRICE_ID: z.string().default('price_REPLACE_THIS'),
|
||||
|
|
|
@ -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(
|
||||
StringParameter.fromSecureStringParameterAttributes(
|
||||
this,
|
||||
|
|
|
@ -13,7 +13,6 @@ import {
|
|||
RiMore2Fill,
|
||||
} from 'react-icons/ri'
|
||||
import { HiOutlineSparkles } from 'react-icons/hi'
|
||||
import { BrowserUtil } from '@maybe-finance/client/shared'
|
||||
import Link from 'next/link'
|
||||
import { AnimatePresence, motion } from 'framer-motion'
|
||||
|
||||
|
@ -399,13 +398,11 @@ export function SidebarOnboarding({ onClose, onHide }: Props) {
|
|||
</Disclosure>
|
||||
|
||||
<p className="text-sm text-gray-100">
|
||||
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>
|
||||
If you have any issues with connecting accounts,{' '}
|
||||
<a className="text-cyan underline" href="mailto:hello@maybe.co">
|
||||
please let us know
|
||||
</a>
|
||||
.
|
||||
</p>
|
||||
</>
|
||||
)
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { useAuth0 } from '@auth0/auth0-react'
|
||||
import { BoxIcon, BrowserUtil, linkAuth0AccountCtx, useUserApi } from '@maybe-finance/client/shared'
|
||||
import { BoxIcon, linkAuth0AccountCtx, useUserApi } from '@maybe-finance/client/shared'
|
||||
import { Button, DialogV2 } from '@maybe-finance/design-system'
|
||||
import { useQueryClient } from '@tanstack/react-query'
|
||||
import { useState } from 'react'
|
||||
|
@ -253,12 +253,9 @@ function LinkError({ onClose, error }: { onClose(): void; error: string }) {
|
|||
|
||||
<p className="mb-2 text-gray-50 text-base">{error}</p>
|
||||
|
||||
<button
|
||||
className="underline text-cyan text-base mb-6"
|
||||
onClick={BrowserUtil.showIntercom}
|
||||
>
|
||||
<a className="underline text-cyan text-base mb-6" href="mailto:hello@maybe.co">
|
||||
Please contact us.
|
||||
</button>
|
||||
</a>
|
||||
|
||||
<div className="flex w-full gap-4">
|
||||
<Button fullWidth onClick={onClose}>
|
||||
|
|
|
@ -8,10 +8,9 @@ import type {
|
|||
} from '@tanstack/react-query'
|
||||
|
||||
import { useMemo } from 'react'
|
||||
import sumBy from 'lodash/sumBy'
|
||||
import toast from 'react-hot-toast'
|
||||
import { useInfiniteQuery, useMutation, useQuery, useQueryClient } from '@tanstack/react-query'
|
||||
import { useAxiosWithAuth, useIntercom } from '..'
|
||||
import { useAxiosWithAuth } from '..'
|
||||
import { invalidateAccountQueries } from '../utils'
|
||||
|
||||
const AccountApi = (axios: AxiosInstance) => ({
|
||||
|
@ -135,7 +134,6 @@ export function useAccountApi() {
|
|||
const queryClient = useQueryClient()
|
||||
const { axios } = useAxiosWithAuth()
|
||||
const api = useMemo(() => AccountApi(axios), [axios])
|
||||
const { update: updateIntercom } = useIntercom()
|
||||
|
||||
const useAccounts = (
|
||||
options?: Omit<
|
||||
|
@ -152,13 +150,6 @@ export function useAccountApi() {
|
|||
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,
|
||||
})
|
||||
|
|
|
@ -38,11 +38,6 @@ const UserApi = (
|
|||
return data
|
||||
},
|
||||
|
||||
async getIntercomMetadata() {
|
||||
const { data } = await axios.get<SharedType.UserIntercomMetadata>('/users/intercom')
|
||||
return data
|
||||
},
|
||||
|
||||
async update(userData: SharedType.UpdateUser) {
|
||||
const { data } = await axios.put<SharedType.User>('/users', userData)
|
||||
return data
|
||||
|
@ -254,22 +249,6 @@ export function useUserApi() {
|
|||
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 = (
|
||||
options?: Omit<
|
||||
UseQueryOptions<SharedType.User, unknown, SharedType.User, any[]>,
|
||||
|
@ -471,7 +450,6 @@ export function useUserApi() {
|
|||
useNetWorthSeries,
|
||||
useInsights,
|
||||
useCurrentNetWorth,
|
||||
useIntercomMetadata,
|
||||
useProfile,
|
||||
useUpdateProfile,
|
||||
useAuth0Profile,
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
export * from './useAxiosWithAuth'
|
||||
export * from './useDebounce'
|
||||
export * from './useFinicity'
|
||||
export * from './useIntercom'
|
||||
export * from './useInterval'
|
||||
export * from './useLastUpdated'
|
||||
export * from './useLocalStorage'
|
||||
|
|
|
@ -5,12 +5,10 @@ import type {
|
|||
ConnectCancelEvent,
|
||||
ConnectDoneEvent,
|
||||
ConnectErrorEvent,
|
||||
ConnectRouteEvent,
|
||||
} from '@finicity/connect-web-sdk'
|
||||
import { useFinicityApi } from '../api'
|
||||
import { useAccountContext, useUserAccountContext } from '../providers'
|
||||
import { useLogger } from './useLogger'
|
||||
import { BrowserUtil } from '..'
|
||||
|
||||
export function useFinicity() {
|
||||
const logger = useLogger()
|
||||
|
@ -35,9 +33,6 @@ export function useFinicity() {
|
|||
FinicityConnect.launch(link, {
|
||||
onDone(evt: ConnectDoneEvent) {
|
||||
logger.debug(`Finicity Connect onDone event`, evt)
|
||||
BrowserUtil.trackIntercomEvent('FINICITY_CONNECT_DONE', {
|
||||
...evt,
|
||||
})
|
||||
setExpectingAccounts(true)
|
||||
},
|
||||
onError(evt: ConnectErrorEvent) {
|
||||
|
@ -50,25 +45,15 @@ export function useFinicity() {
|
|||
'finicity.error.reason': evt.reason,
|
||||
},
|
||||
})
|
||||
BrowserUtil.trackIntercomEvent('FINICITY_CONNECT_ERROR', {
|
||||
...evt,
|
||||
})
|
||||
},
|
||||
onCancel(evt: ConnectCancelEvent) {
|
||||
logger.debug(`Finicity Connect onCancel event`, evt)
|
||||
BrowserUtil.trackIntercomEvent('FINICITY_CONNECT_CANCEL', {
|
||||
...evt,
|
||||
})
|
||||
},
|
||||
onUser(evt: any) {
|
||||
BrowserUtil.trackIntercomEvent('FINICITY_CONNECT_USER_ACTION', {
|
||||
...evt,
|
||||
})
|
||||
onUser() {
|
||||
// ...
|
||||
},
|
||||
onRoute(evt: ConnectRouteEvent) {
|
||||
BrowserUtil.trackIntercomEvent('FINICITY_CONNECT_ROUTE_EVENT', {
|
||||
...evt,
|
||||
})
|
||||
onRoute() {
|
||||
// ...
|
||||
},
|
||||
})
|
||||
},
|
||||
|
|
|
@ -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 }
|
||||
}
|
|
@ -2,7 +2,6 @@ import * as Sentry from '@sentry/react'
|
|||
import { DateTime } from 'luxon'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { usePlaidLink } from 'react-plaid-link'
|
||||
import { BrowserUtil } from '..'
|
||||
import { usePlaidApi } from '../api'
|
||||
import { useAccountContext } from '../providers'
|
||||
import { useLogger } from './useLogger'
|
||||
|
@ -68,22 +67,8 @@ export function usePlaid(mode: 'default' | 'oauth' = 'default') {
|
|||
}
|
||||
},
|
||||
// https://plaid.com/docs/link/web/#onexit
|
||||
onExit: (error, metadata) => {
|
||||
if (error) {
|
||||
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,
|
||||
})
|
||||
onExit: () => {
|
||||
// ...
|
||||
},
|
||||
// https://plaid.com/docs/link/web/#onevent
|
||||
onEvent: (event, metadata) => {
|
||||
|
@ -97,9 +82,6 @@ export function usePlaid(mode: 'default' | 'oauth' = 'default') {
|
|||
},
|
||||
})
|
||||
|
||||
// Capture all events to Intercom
|
||||
BrowserUtil.trackIntercomEvent(event, metadata)
|
||||
|
||||
logger.debug(
|
||||
`Plaid link event: ${event} for session ID ${metadata.link_session_id}`,
|
||||
metadata
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
export * from './image-loaders'
|
||||
export * from './intercom'
|
||||
export * from './browser-utils'
|
||||
export * from './account-utils'
|
||||
export * from './agreement-utils'
|
||||
|
|
|
@ -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')
|
||||
}
|
|
@ -20,7 +20,6 @@ import type { IAccountQueryService } from '../account'
|
|||
import type { SharedType } from '@maybe-finance/shared'
|
||||
import { CopyObjectCommand, MetadataDirective } from '@aws-sdk/client-s3'
|
||||
import { DateTime } from 'luxon'
|
||||
import crypto from 'crypto'
|
||||
import { DbUtil } from '@maybe-finance/server/shared'
|
||||
import { DateUtil } from '@maybe-finance/shared'
|
||||
import { flatten } from 'lodash'
|
||||
|
@ -279,22 +278,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']) {
|
||||
return this.prisma.agreement.findMany({
|
||||
distinct: 'type',
|
||||
|
|
|
@ -227,10 +227,6 @@ export type UserSubscription = {
|
|||
currentPeriodEnd: DateTime | null
|
||||
}
|
||||
|
||||
export type UserIntercomMetadata = {
|
||||
hash?: string
|
||||
}
|
||||
|
||||
export type UserMemberCardDetails = {
|
||||
memberNumber: number
|
||||
name: string
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue