Files
supabase/apps/studio/lib/profile.tsx
T
626eb30e77 Assistant action orientated approach (#38806)
* update onboarding

* update model and fix part issue

* action orientated assistant

* fix tool

* lock

* remove unused filter

* fix tests

* fix again

* update package

* update container

* fix tests

* refactor(ai assistant): break out message markdown and profile picture

* wip

* refactor(ai assistant): break up message component

* refactor: break ai assistant message down into multiple files

* refactor: simplify ReportBlock state

* fix: styling of draggable report block header

When the drag handle is showing, it overlaps with the block header.
Decrease the opacity of the header so the handle can be seen and the two
can be distinguished.

* fix: minor tweaks to tool ui

* refactor: simplify DisplayBlockRenderer state

* fix: remove double deploy button in edge function block

When the confirm footer is shown, the deploy button on the top right should be
hidden (not just disabled) to avoid confusion.

* refactor, test: message sanitization by opt-in level

Refactor the message sanitization to have more type safety and be more testable.
Add tests to ensure:

- Message sanitization always runs on generate-v4
- Message sanitization correctly works by opt-in level

* Fix conflicts in pnpm lock

* Couple of nits and refactors

* Revert casing for report block snippet

* adjust sanitised prompt

* Fix tests

---------

Co-authored-by: Charis Lam <26616127+charislam@users.noreply.github.com>
Co-authored-by: Joshen Lim <joshenlimek@gmail.com>
2025-09-29 03:57:36 +00:00

147 lines
4.8 KiB
TypeScript

import * as Sentry from '@sentry/nextjs'
import { useRouter } from 'next/router'
import { PropsWithChildren, createContext, useContext, useMemo } from 'react'
import { toast } from 'sonner'
import { useIsLoggedIn, useUser } from 'common'
import { usePermissionsQuery } from 'data/permissions/permissions-query'
import { useProfileCreateMutation } from 'data/profile/profile-create-mutation'
import { useProfileIdentitiesQuery } from 'data/profile/profile-identities-query'
import { useProfileQuery } from 'data/profile/profile-query'
import type { Profile } from 'data/profile/types'
import { useSendEventMutation } from 'data/telemetry/send-event-mutation'
import type { ResponseError } from 'types'
import { useSignOut } from './auth'
import { getGitHubProfileImgUrl } from './github'
export type ProfileContextType = {
profile: Profile | undefined
error: ResponseError | null
isLoading: boolean
isError: boolean
isSuccess: boolean
}
export const ProfileContext = createContext<ProfileContextType>({
profile: undefined,
error: null,
isLoading: true,
isError: false,
isSuccess: false,
})
export const ProfileProvider = ({ children }: PropsWithChildren<{}>) => {
const user = useUser()
const isLoggedIn = useIsLoggedIn()
const router = useRouter()
const signOut = useSignOut()
const { mutate: sendEvent } = useSendEventMutation()
const { mutate: createProfile, isLoading: isCreatingProfile } = useProfileCreateMutation({
onSuccess: () => {
sendEvent({ action: 'sign_up', properties: { category: 'conversion' } })
if (user) {
// Send an event to GTM, will do nothing if GTM is not enabled
const thisWindow = window as any
thisWindow.dataLayer = thisWindow.dataLayer || []
thisWindow.dataLayer.push({
event: 'sign_up',
email: user.email,
})
}
},
onError: (error) => {
if (error.code === 409) {
// [Joshen] There's currently an assumption that createProfile is getting triggered
// multiple times unnecessarily, although the tracing the code i can't see why this might
// be happening unless GET profile is somehow returning `User's profile not found` incorrectly
// Adding a Sentry capture + toast in hopes to catch this while developing on local / staging
Sentry.captureMessage('Profile already exists: ' + error.message)
if (process.env.NEXT_PUBLIC_ENVIRONMENT !== 'prod') {
toast.error('[DEV] createProfile called despite profile already exists: ' + error.message)
}
} else {
Sentry.captureMessage('Failed to create users profile: ' + error.message)
toast.error('Failed to create your profile. Please refresh to try again.')
}
},
})
// Track telemetry for the current user
const {
error,
data: profile,
isLoading: isLoadingProfile,
isError,
isSuccess,
} = useProfileQuery({
enabled: isLoggedIn,
onError(err) {
// if the user does not yet exist, create a profile for them
if (err.message === "User's profile not found") {
createProfile()
}
// [Alaister] If the user has a bad auth token, auth-js won't know about it
// and will think the user is authenticated. Since fetching the profile happens
// on every page load, we can check for a 401 here and sign the user out if
// they have a bad token.
if (err.code === 401) {
signOut().then(() => router.push('/sign-in'))
}
},
})
const { isInitialLoading: isLoadingPermissions } = usePermissionsQuery({ enabled: isLoggedIn })
const value = useMemo(() => {
const isLoading = isLoadingProfile || isCreatingProfile || isLoadingPermissions
return {
error,
profile,
isLoading,
isError,
isSuccess,
}
}, [
isLoadingProfile,
isCreatingProfile,
isLoadingPermissions,
profile,
error,
isError,
isSuccess,
])
return <ProfileContext.Provider value={value}>{children}</ProfileContext.Provider>
}
export const useProfile = () => useContext(ProfileContext)
export function useProfileNameAndPicture(): {
username?: string
primaryEmail?: string
avatarUrl?: string
isLoading: boolean
} {
const { profile, isLoading: isLoadingProfile } = useProfile()
const { data: identitiesData, isLoading: isLoadingIdentities } = useProfileIdentitiesQuery()
const username = profile?.username
const isGitHubProfile = profile?.auth0_id.startsWith('github')
const gitHubUsername = isGitHubProfile
? identitiesData?.identities.find((x) => x.provider === 'github')?.identity_data?.user_name
: undefined
const avatarUrl = isGitHubProfile ? getGitHubProfileImgUrl(gitHubUsername) : undefined
return {
username: profile?.username,
primaryEmail: profile?.primary_email,
avatarUrl,
isLoading: isLoadingProfile || isLoadingIdentities,
}
}