mirror of
https://github.com/langgenius/dify.git
synced 2026-07-28 23:59:34 +08:00
90 lines
3.4 KiB
TypeScript
90 lines
3.4 KiB
TypeScript
'use client'
|
|
import { Avatar } from '@langgenius/dify-ui/avatar'
|
|
import { cn } from '@langgenius/dify-ui/cn'
|
|
import {
|
|
DropdownMenu,
|
|
DropdownMenuContent,
|
|
DropdownMenuItem,
|
|
DropdownMenuTrigger,
|
|
} from '@langgenius/dify-ui/dropdown-menu'
|
|
import { useSuspenseQuery } from '@tanstack/react-query'
|
|
import { useTranslation } from 'react-i18next'
|
|
import { resetUser } from '@/app/components/base/amplitude/utils'
|
|
import PremiumBadge from '@/app/components/base/premium-badge'
|
|
import { useProviderContext } from '@/context/provider-context'
|
|
import { userProfileQueryOptions } from '@/features/account-profile/client'
|
|
import { useRouter } from '@/next/navigation'
|
|
import { useLogout } from '@/service/use-common'
|
|
|
|
export default function AppSelector() {
|
|
const router = useRouter()
|
|
const { t } = useTranslation()
|
|
// Cache is hydrated by CommonLayoutHydrationBoundary; this hits cache synchronously.
|
|
const { data: userProfileResp } = useSuspenseQuery(userProfileQueryOptions())
|
|
const userProfile = userProfileResp.profile
|
|
const { isEducationAccount } = useProviderContext()
|
|
|
|
const { mutateAsync: logout } = useLogout()
|
|
|
|
if (!userProfile) return null
|
|
|
|
const handleLogout = async () => {
|
|
await logout()
|
|
|
|
resetUser()
|
|
// Tokens are now stored in cookies and cleared by backend
|
|
|
|
router.push('/signin')
|
|
}
|
|
|
|
return (
|
|
<DropdownMenu modal={false}>
|
|
<DropdownMenuTrigger
|
|
aria-label={userProfile.name}
|
|
className={cn(
|
|
'inline-flex size-8 items-center justify-center rounded-full border-none bg-transparent p-0 text-sm text-text-primary outline-hidden',
|
|
'hover:opacity-80 focus-visible:ring-1 focus-visible:ring-components-input-border-hover active:opacity-70 data-popup-open:opacity-80',
|
|
)}
|
|
>
|
|
<Avatar avatar={userProfile.avatar_url} name={userProfile.name} />
|
|
</DropdownMenuTrigger>
|
|
<DropdownMenuContent
|
|
placement="bottom-end"
|
|
sideOffset={4}
|
|
popupClassName="w-60 max-w-80 divide-y divide-divider-subtle bg-components-panel-bg-blur p-0"
|
|
>
|
|
<div className="p-1">
|
|
<div className="flex flex-nowrap items-center px-3 py-2">
|
|
<div className="min-w-0 grow">
|
|
<div className="system-md-medium break-all text-text-primary">
|
|
{userProfile.name}
|
|
{isEducationAccount && (
|
|
<PremiumBadge size="s" color="blue" className="ml-1 px-2!">
|
|
<span aria-hidden="true" className="mr-1 i-ri-graduation-cap-fill size-3" />
|
|
<span className="system-2xs-medium">EDU</span>
|
|
</PremiumBadge>
|
|
)}
|
|
</div>
|
|
<div className="system-xs-regular break-all text-text-tertiary">
|
|
{userProfile.email}
|
|
</div>
|
|
</div>
|
|
<Avatar avatar={userProfile.avatar_url} name={userProfile.name} />
|
|
</div>
|
|
</div>
|
|
<div className="p-1">
|
|
<DropdownMenuItem className="h-9 justify-start px-3" onClick={handleLogout}>
|
|
<span
|
|
aria-hidden="true"
|
|
className="mr-1 i-custom-vender-line-general-log-out-01 flex size-4 text-text-tertiary"
|
|
/>
|
|
<span className="text-[14px] font-normal text-text-secondary">
|
|
{t(($) => $['userProfile.logout'], { ns: 'common' })}
|
|
</span>
|
|
</DropdownMenuItem>
|
|
</div>
|
|
</DropdownMenuContent>
|
|
</DropdownMenu>
|
|
)
|
|
}
|