mirror of
https://github.com/langgenius/dify.git
synced 2026-06-07 16:32:01 +08:00
fix(web): refine account avatar interactions (#36111)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
This commit is contained in:
parent
13c00ecfc4
commit
5edc682c4a
@ -1151,7 +1151,7 @@
|
||||
},
|
||||
"web/app/components/base/icons/src/vender/line/general/index.ts": {
|
||||
"no-barrel-files/no-barrel-files": {
|
||||
"count": 12
|
||||
"count": 11
|
||||
}
|
||||
},
|
||||
"web/app/components/base/icons/src/vender/line/images/index.ts": {
|
||||
|
||||
@ -8,7 +8,6 @@ import { Avatar } from '@langgenius/dify-ui/avatar'
|
||||
import { Button } from '@langgenius/dify-ui/button'
|
||||
import { Dialog, DialogContent } from '@langgenius/dify-ui/dialog'
|
||||
import { toast } from '@langgenius/dify-ui/toast'
|
||||
import { RiDeleteBin5Line, RiPencilLine } from '@remixicon/react'
|
||||
import * as React from 'react'
|
||||
import { useCallback, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
@ -29,9 +28,9 @@ const AvatarWithEdit = ({ onSave, ...props }: AvatarWithEditProps) => {
|
||||
const [isShowAvatarPicker, setIsShowAvatarPicker] = useState(false)
|
||||
const [uploading, setUploading] = useState(false)
|
||||
const [isShowDeleteConfirm, setIsShowDeleteConfirm] = useState(false)
|
||||
const [hoverArea, setHoverArea] = useState<string>('left')
|
||||
|
||||
const [onAvatarError, setOnAvatarError] = useState(false)
|
||||
const canDeleteAvatar = !!props.avatar && !onAvatarError
|
||||
|
||||
const handleImageInput: OnImageInput = useCallback(async (isCropped: boolean, fileOrTempUrl: string | File, croppedAreaPixels?: Area, fileName?: string) => {
|
||||
setInputImageInfo(
|
||||
@ -65,11 +64,16 @@ const AvatarWithEdit = ({ onSave, ...props }: AvatarWithEditProps) => {
|
||||
}
|
||||
}, [onSave, t])
|
||||
|
||||
const handleDeleteAvatarClick = useCallback(() => {
|
||||
setIsShowAvatarPicker(false)
|
||||
setIsShowDeleteConfirm(true)
|
||||
}, [])
|
||||
|
||||
const { handleLocalFileUpload } = useLocalFileUploader({
|
||||
limit: 3,
|
||||
disabled: false,
|
||||
onUpload: (imageFile: ImageFile) => {
|
||||
if (imageFile.progress === 100) {
|
||||
if (imageFile.progress === 100 && imageFile.fileId) {
|
||||
setUploading(false)
|
||||
setInputImageInfo(undefined)
|
||||
handleSaveAvatar(imageFile.fileId)
|
||||
@ -100,36 +104,17 @@ const AvatarWithEdit = ({ onSave, ...props }: AvatarWithEditProps) => {
|
||||
return (
|
||||
<>
|
||||
<div>
|
||||
<div className="group relative">
|
||||
<button
|
||||
type="button"
|
||||
aria-label={t('avatar.editAction', { ns: 'common' })}
|
||||
className="group relative inline-flex overflow-hidden rounded-full border-none bg-transparent p-0 outline-hidden hover:opacity-90 focus-visible:ring-2 focus-visible:ring-components-input-border-hover active:opacity-80"
|
||||
onClick={() => setIsShowAvatarPicker(true)}
|
||||
>
|
||||
<Avatar {...props} onLoadingStatusChange={status => setOnAvatarError(status === 'error')} />
|
||||
<div
|
||||
className="absolute inset-0 flex cursor-pointer items-center justify-center rounded-full bg-black/50 opacity-0 transition-opacity group-hover:opacity-100"
|
||||
onClick={() => {
|
||||
if (hoverArea === 'right' && !onAvatarError)
|
||||
setIsShowDeleteConfirm(true)
|
||||
else
|
||||
setIsShowAvatarPicker(true)
|
||||
}}
|
||||
onMouseMove={(e) => {
|
||||
const rect = e.currentTarget.getBoundingClientRect()
|
||||
const x = e.clientX - rect.left
|
||||
const isRight = x > rect.width / 2
|
||||
setHoverArea(isRight ? 'right' : 'left')
|
||||
}}
|
||||
>
|
||||
{hoverArea === 'right' && !onAvatarError
|
||||
? (
|
||||
<span className="text-xs text-white">
|
||||
<RiDeleteBin5Line />
|
||||
</span>
|
||||
)
|
||||
: (
|
||||
<span className="text-xs text-white">
|
||||
<RiPencilLine />
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<span className="pointer-events-none absolute inset-0 flex items-center justify-center rounded-full bg-black/50 text-white opacity-0 group-hover:opacity-100 motion-safe:transition-opacity">
|
||||
<span aria-hidden="true" className="i-ri-pencil-line size-5" />
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<Dialog open={isShowAvatarPicker} onOpenChange={open => !open && setIsShowAvatarPicker(false)}>
|
||||
@ -138,11 +123,16 @@ const AvatarWithEdit = ({ onSave, ...props }: AvatarWithEditProps) => {
|
||||
<Divider className="m-0" />
|
||||
|
||||
<div className="flex w-full items-center justify-center gap-2 p-3">
|
||||
<Button className="w-full" onClick={() => setIsShowAvatarPicker(false)}>
|
||||
{canDeleteAvatar && (
|
||||
<Button tone="destructive" className="shrink-0" onClick={handleDeleteAvatarClick}>
|
||||
{t('operation.delete', { ns: 'common' })}
|
||||
</Button>
|
||||
)}
|
||||
<Button className="min-w-0 flex-1" onClick={() => setIsShowAvatarPicker(false)}>
|
||||
{t('iconPicker.cancel', { ns: 'app' })}
|
||||
</Button>
|
||||
|
||||
<Button variant="primary" className="w-full" disabled={uploading || !inputImageInfo} loading={uploading} onClick={handleSelect}>
|
||||
<Button variant="primary" className="min-w-0 flex-1" disabled={uploading || !inputImageInfo} loading={uploading} onClick={handleSelect}>
|
||||
{t('iconPicker.ok', { ns: 'app' })}
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
@ -10,7 +10,6 @@ import {
|
||||
import { useSuspenseQuery } from '@tanstack/react-query'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { resetUser } from '@/app/components/base/amplitude/utils'
|
||||
import { LogOut01 } from '@/app/components/base/icons/src/vender/line/general'
|
||||
import PremiumBadge from '@/app/components/base/premium-badge'
|
||||
import { useProviderContext } from '@/context/provider-context'
|
||||
import { useRouter } from '@/next/navigation'
|
||||
@ -44,8 +43,8 @@ export default function AppSelector() {
|
||||
<DropdownMenuTrigger
|
||||
aria-label={userProfile.name}
|
||||
className={cn(
|
||||
'inline-flex items-center rounded-[20px] text-sm text-text-primary outline-hidden mobile:px-1',
|
||||
'hover:bg-components-panel-bg-blur focus-visible:bg-components-panel-bg-blur focus-visible:ring-1 focus-visible:ring-components-input-border-hover data-popup-open:bg-components-panel-bg-blur',
|
||||
'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} />
|
||||
@ -77,7 +76,7 @@ export default function AppSelector() {
|
||||
className="h-9 justify-start px-3"
|
||||
onClick={handleLogout}
|
||||
>
|
||||
<LogOut01 className="mr-1 flex h-4 w-4 text-text-tertiary" />
|
||||
<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>
|
||||
|
||||
@ -5,8 +5,6 @@ export { default as CodeAssistant } from './CodeAssistant'
|
||||
export { default as Link03 } from './Link03'
|
||||
export { default as LinkExternal02 } from './LinkExternal02'
|
||||
|
||||
export { default as LogOut01 } from './LogOut01'
|
||||
|
||||
export { default as MagicEdit } from './MagicEdit'
|
||||
|
||||
export { default as Pin02 } from './Pin02'
|
||||
|
||||
@ -101,6 +101,7 @@
|
||||
"appModes.completionApp": "Text Generator",
|
||||
"avatar.deleteDescription": "Are you sure you want to remove your profile picture? Your account will use the default initial avatar.",
|
||||
"avatar.deleteTitle": "Remove Avatar",
|
||||
"avatar.editAction": "Edit Avatar",
|
||||
"chat.citation.characters": "Characters:",
|
||||
"chat.citation.hitCount": "Retrieval count:",
|
||||
"chat.citation.hitScore": "Retrieval Score:",
|
||||
|
||||
@ -101,6 +101,7 @@
|
||||
"appModes.completionApp": "文本生成型应用",
|
||||
"avatar.deleteDescription": "确定要删除你的个人头像吗?你的账号将使用默认的首字母头像。",
|
||||
"avatar.deleteTitle": "删除头像",
|
||||
"avatar.editAction": "编辑头像",
|
||||
"chat.citation.characters": "字符:",
|
||||
"chat.citation.hitCount": "召回次数:",
|
||||
"chat.citation.hitScore": "召回得分:",
|
||||
|
||||
Loading…
Reference in New Issue
Block a user