feat: add delete avatar functionality with confirmation modal (#24127)
Co-authored-by: crazywoola <427733928@qq.com>
This commit is contained in:
@@ -4,7 +4,7 @@ import type { Area } from 'react-easy-crop'
|
|||||||
import React, { useCallback, useState } from 'react'
|
import React, { useCallback, useState } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { useContext } from 'use-context-selector'
|
import { useContext } from 'use-context-selector'
|
||||||
import { RiPencilLine } from '@remixicon/react'
|
import { RiDeleteBin5Line, RiPencilLine } from '@remixicon/react'
|
||||||
import { updateUserProfile } from '@/service/common'
|
import { updateUserProfile } from '@/service/common'
|
||||||
import { ToastContext } from '@/app/components/base/toast'
|
import { ToastContext } from '@/app/components/base/toast'
|
||||||
import ImageInput, { type OnImageInput } from '@/app/components/base/app-icon-picker/ImageInput'
|
import ImageInput, { type OnImageInput } from '@/app/components/base/app-icon-picker/ImageInput'
|
||||||
@@ -27,6 +27,8 @@ const AvatarWithEdit = ({ onSave, ...props }: AvatarWithEditProps) => {
|
|||||||
const [inputImageInfo, setInputImageInfo] = useState<InputImageInfo>()
|
const [inputImageInfo, setInputImageInfo] = useState<InputImageInfo>()
|
||||||
const [isShowAvatarPicker, setIsShowAvatarPicker] = useState(false)
|
const [isShowAvatarPicker, setIsShowAvatarPicker] = useState(false)
|
||||||
const [uploading, setUploading] = useState(false)
|
const [uploading, setUploading] = useState(false)
|
||||||
|
const [isShowDeleteConfirm, setIsShowDeleteConfirm] = useState(false)
|
||||||
|
const [hoverArea, setHoverArea] = useState<string>('left')
|
||||||
|
|
||||||
const handleImageInput: OnImageInput = useCallback(async (isCropped: boolean, fileOrTempUrl: string | File, croppedAreaPixels?: Area, fileName?: string) => {
|
const handleImageInput: OnImageInput = useCallback(async (isCropped: boolean, fileOrTempUrl: string | File, croppedAreaPixels?: Area, fileName?: string) => {
|
||||||
setInputImageInfo(
|
setInputImageInfo(
|
||||||
@@ -48,6 +50,18 @@ const AvatarWithEdit = ({ onSave, ...props }: AvatarWithEditProps) => {
|
|||||||
}
|
}
|
||||||
}, [notify, onSave, t])
|
}, [notify, onSave, t])
|
||||||
|
|
||||||
|
const handleDeleteAvatar = useCallback(async () => {
|
||||||
|
try {
|
||||||
|
await updateUserProfile({ url: 'account/avatar', body: { avatar: '' } })
|
||||||
|
notify({ type: 'success', message: t('common.actionMsg.modifiedSuccessfully') })
|
||||||
|
setIsShowDeleteConfirm(false)
|
||||||
|
onSave?.()
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
notify({ type: 'error', message: (e as Error).message })
|
||||||
|
}
|
||||||
|
}, [notify, onSave, t])
|
||||||
|
|
||||||
const { handleLocalFileUpload } = useLocalFileUploader({
|
const { handleLocalFileUpload } = useLocalFileUploader({
|
||||||
limit: 3,
|
limit: 3,
|
||||||
disabled: false,
|
disabled: false,
|
||||||
@@ -86,12 +100,21 @@ const AvatarWithEdit = ({ onSave, ...props }: AvatarWithEditProps) => {
|
|||||||
<div className="group relative">
|
<div className="group relative">
|
||||||
<Avatar {...props} />
|
<Avatar {...props} />
|
||||||
<div
|
<div
|
||||||
onClick={() => { setIsShowAvatarPicker(true) }}
|
|
||||||
className="absolute inset-0 flex cursor-pointer items-center justify-center rounded-full bg-black/50 opacity-0 transition-opacity group-hover:opacity-100"
|
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={() => hoverArea === 'right' ? setIsShowDeleteConfirm(true) : 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')
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<span className="text-xs text-white">
|
{hoverArea === 'right' ? <span className="text-xs text-white">
|
||||||
|
<RiDeleteBin5Line />
|
||||||
|
</span> : <span className="text-xs text-white">
|
||||||
<RiPencilLine />
|
<RiPencilLine />
|
||||||
</span>
|
</span>}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -115,6 +138,26 @@ const AvatarWithEdit = ({ onSave, ...props }: AvatarWithEditProps) => {
|
|||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</Modal>
|
</Modal>
|
||||||
|
|
||||||
|
<Modal
|
||||||
|
closable
|
||||||
|
className="!w-[362px] !p-6"
|
||||||
|
isShow={isShowDeleteConfirm}
|
||||||
|
onClose={() => setIsShowDeleteConfirm(false)}
|
||||||
|
>
|
||||||
|
<div className="title-2xl-semi-bold mb-3 text-text-primary">{t('common.avatar.deleteTitle')}</div>
|
||||||
|
<p className="mb-8 text-text-secondary">{t('common.avatar.deleteDescription')}</p>
|
||||||
|
|
||||||
|
<div className="flex w-full items-center justify-center gap-2">
|
||||||
|
<Button className="w-full" onClick={() => setIsShowDeleteConfirm(false)}>
|
||||||
|
{t('common.operation.cancel')}
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<Button variant="warning" className="w-full" onClick={handleDeleteAvatar}>
|
||||||
|
{t('common.operation.delete')}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</Modal>
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@@ -709,6 +709,10 @@ const translation = {
|
|||||||
pagination: {
|
pagination: {
|
||||||
perPage: 'Items per page',
|
perPage: 'Items per page',
|
||||||
},
|
},
|
||||||
|
avatar: {
|
||||||
|
deleteTitle: 'Remove Avatar',
|
||||||
|
deleteDescription: 'Are you sure you want to remove your profile picture? Your account will use the default initial avatar.',
|
||||||
|
},
|
||||||
imageInput: {
|
imageInput: {
|
||||||
dropImageHere: 'Drop your image here, or',
|
dropImageHere: 'Drop your image here, or',
|
||||||
browse: 'browse',
|
browse: 'browse',
|
||||||
|
@@ -709,6 +709,10 @@ const translation = {
|
|||||||
pagination: {
|
pagination: {
|
||||||
perPage: '每页显示',
|
perPage: '每页显示',
|
||||||
},
|
},
|
||||||
|
avatar: {
|
||||||
|
deleteTitle: '删除头像',
|
||||||
|
deleteDescription: '确定要删除你的个人头像吗?你的账号将使用默认的首字母头像。',
|
||||||
|
},
|
||||||
imageInput: {
|
imageInput: {
|
||||||
dropImageHere: '将图片拖放到此处,或',
|
dropImageHere: '将图片拖放到此处,或',
|
||||||
browse: '浏览',
|
browse: '浏览',
|
||||||
|
Reference in New Issue
Block a user