205 lines
5.8 KiB
TypeScript
205 lines
5.8 KiB
TypeScript
import { useApolloClient, useMutation, useQuery } from '@vue/apollo-composable'
|
|
import {
|
|
settingsNewEmailVerificationMutation,
|
|
settingsDeleteUserEmailMutation,
|
|
settingsCreateUserEmailMutation
|
|
} from '~/lib/settings/graphql/mutations'
|
|
import { userEmailsQuery } from '~/lib/user/graphql/queries'
|
|
import {
|
|
convertThrowIntoFetchResult,
|
|
getFirstErrorMessage,
|
|
getCacheId,
|
|
modifyObjectField
|
|
} from '~/lib/common/helpers/graphql'
|
|
import type { UserEmail } from '~/lib/common/generated/gql/graphql'
|
|
import { useGlobalToast } from '~/lib/common/composables/toast'
|
|
import { useMixpanel } from '~/lib/core/composables/mp'
|
|
import {
|
|
verifyEmailRoute,
|
|
homeRoute,
|
|
settingsUserRoutes
|
|
} from '~/lib/common/helpers/route'
|
|
import { verifyEmailMutation } from '~/lib/user/graphql/mutations'
|
|
|
|
export function useUserEmails() {
|
|
const { triggerNotification } = useGlobalToast()
|
|
const mixpanel = useMixpanel()
|
|
const { result } = useQuery(userEmailsQuery)
|
|
const route = useRoute()
|
|
const apollo = useApolloClient().client
|
|
const { activeUser } = useActiveUser()
|
|
|
|
const { mutate: resendMutation } = useMutation(settingsNewEmailVerificationMutation)
|
|
const { mutate: deleteMutation } = useMutation(settingsDeleteUserEmailMutation)
|
|
const { mutate: createMutation } = useMutation(settingsCreateUserEmailMutation)
|
|
const { mutate: verifyMutation } = useMutation(verifyEmailMutation)
|
|
|
|
// Simple array of all emails
|
|
const emails = computed(() => result.value?.activeUser?.emails ?? ([] as UserEmail[]))
|
|
|
|
// Helper computed properties for common queries
|
|
const unverifiedPrimaryEmail = computed(
|
|
() => emails.value.find((e) => e.primary && !e.verified) || null
|
|
)
|
|
|
|
const unverifiedEmails = computed(() => emails.value.filter((e) => !e.verified))
|
|
|
|
const addUserEmail = async (email: string) => {
|
|
const result = await createMutation({
|
|
input: { email }
|
|
}).catch(convertThrowIntoFetchResult)
|
|
|
|
if (result?.data) {
|
|
mixpanel.track('Email Added')
|
|
navigateTo(verifyEmailRoute)
|
|
return true
|
|
}
|
|
|
|
const errorMessage = getFirstErrorMessage(result?.errors)
|
|
triggerNotification({
|
|
type: ToastNotificationType.Danger,
|
|
title: 'Error adding email',
|
|
description: errorMessage
|
|
})
|
|
return false
|
|
}
|
|
|
|
const resendVerificationEmail = async (email: UserEmail) => {
|
|
const result = await resendMutation({
|
|
input: { id: email.id }
|
|
}).catch(convertThrowIntoFetchResult)
|
|
|
|
if (result?.data) {
|
|
triggerNotification({
|
|
type: ToastNotificationType.Success,
|
|
title: `Verification email sent to ${email.email}`
|
|
})
|
|
navigateTo(verifyEmailRoute)
|
|
return true
|
|
}
|
|
|
|
const errorMessage = getFirstErrorMessage(result?.errors)
|
|
triggerNotification({
|
|
type: ToastNotificationType.Danger,
|
|
title: 'Error sending verification email',
|
|
description: errorMessage
|
|
})
|
|
return false
|
|
}
|
|
|
|
const deleteUserEmail = async (options: {
|
|
email: UserEmail
|
|
hideToast?: boolean
|
|
}) => {
|
|
const { email, hideToast } = options
|
|
const result = await deleteMutation({
|
|
input: { id: email.id }
|
|
}).catch(convertThrowIntoFetchResult)
|
|
|
|
if (result?.data) {
|
|
if (!hideToast) {
|
|
triggerNotification({
|
|
type: ToastNotificationType.Success,
|
|
title: 'Deleted email',
|
|
description: email.email
|
|
})
|
|
}
|
|
mixpanel.track('Email Deleted')
|
|
|
|
// If we're on the verify email page and there are no more unverified emails, redirect home
|
|
if (route.path === verifyEmailRoute && unverifiedEmails.value.length === 0) {
|
|
navigateTo(homeRoute)
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
const errorMessage = getFirstErrorMessage(result?.errors)
|
|
triggerNotification({
|
|
type: ToastNotificationType.Danger,
|
|
title: 'Error deleting email',
|
|
description: errorMessage
|
|
})
|
|
return false
|
|
}
|
|
|
|
const verifyUserEmail = async (email: UserEmail, code: string) => {
|
|
mixpanel.track('Email Verification Started', {
|
|
email: email.email,
|
|
isPrimary: email.primary
|
|
})
|
|
|
|
const result = await verifyMutation({
|
|
input: { email: email.email, code }
|
|
}).catch(convertThrowIntoFetchResult)
|
|
|
|
const activeUserId = computed(() => activeUser.value?.id)
|
|
|
|
if (result?.data?.activeUserMutations?.emailMutations?.verify) {
|
|
if (!activeUserId.value) return
|
|
|
|
mixpanel.track('Email Verified', {
|
|
email: email.email,
|
|
isPrimary: email.primary
|
|
})
|
|
|
|
// Update UserEmail verified status in cache
|
|
modifyObjectField(
|
|
apollo.cache,
|
|
getCacheId('UserEmail', email.id),
|
|
'verified',
|
|
() => true
|
|
)
|
|
|
|
// Only update User verified status if this is the primary email
|
|
if (email.primary) {
|
|
modifyObjectField(
|
|
apollo.cache,
|
|
getCacheId('User', activeUserId.value),
|
|
'verified',
|
|
() => true
|
|
)
|
|
navigateTo(homeRoute)
|
|
} else {
|
|
navigateTo(settingsUserRoutes.emails)
|
|
}
|
|
|
|
modifyObjectField(
|
|
apollo.cache,
|
|
getCacheId('User', activeUserId.value),
|
|
'discoverableWorkspaces',
|
|
({ helpers: { evict } }) => evict()
|
|
)
|
|
|
|
triggerNotification({
|
|
type: ToastNotificationType.Success,
|
|
title: 'Email verified',
|
|
description: 'Your email has been successfully verified'
|
|
})
|
|
return true
|
|
}
|
|
|
|
mixpanel.track('Email Verification Failed', {
|
|
email: email.email,
|
|
isPrimary: email.primary
|
|
})
|
|
|
|
triggerNotification({
|
|
type: ToastNotificationType.Danger,
|
|
title: 'Verification failed',
|
|
description: 'The verification code you entered is incorrect or expired'
|
|
})
|
|
return false
|
|
}
|
|
|
|
return {
|
|
emails,
|
|
unverifiedPrimaryEmail,
|
|
unverifiedEmails,
|
|
addUserEmail,
|
|
resendVerificationEmail,
|
|
deleteUserEmail,
|
|
verifyUserEmail
|
|
}
|
|
}
|