1
0
Fork 0
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:
Michaël De Boey 2024-01-11 10:02:06 +01:00
parent d59484a537
commit 361d07cd79
No known key found for this signature in database
19 changed files with 18 additions and 255 deletions

View file

@ -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(() => {

View file

@ -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>
)
}

View file

@ -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

View file

@ -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>

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(
'/',
endpoint.create({

View file

@ -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'),

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(
StringParameter.fromSecureStringParameterAttributes(
this,

View file

@ -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>
</>
)

View file

@ -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}>

View file

@ -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,
})

View file

@ -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,

View file

@ -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'

View file

@ -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() {
// ...
},
})
},

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

@ -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

View file

@ -1,5 +1,4 @@
export * from './image-loaders'
export * from './intercom'
export * from './browser-utils'
export * from './account-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

@ -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',

View file

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