From a3a041ef6f7911a3d2e6f0d680d6f84826cc24a9 Mon Sep 17 00:00:00 2001 From: Guangdong Liu Date: Mon, 18 Aug 2025 21:35:20 +0800 Subject: [PATCH] feat: add delete avatar functionality with confirmation modal (#24127) Co-authored-by: crazywoola <427733928@qq.com> --- .../account/account-page/AvatarWithEdit.tsx | 51 +++++++++++++++++-- web/i18n/en-US/common.ts | 4 ++ web/i18n/zh-Hans/common.ts | 4 ++ 3 files changed, 55 insertions(+), 4 deletions(-) diff --git a/web/app/account/account-page/AvatarWithEdit.tsx b/web/app/account/account-page/AvatarWithEdit.tsx index 41a6971bf..88e3a7b34 100644 --- a/web/app/account/account-page/AvatarWithEdit.tsx +++ b/web/app/account/account-page/AvatarWithEdit.tsx @@ -4,7 +4,7 @@ import type { Area } from 'react-easy-crop' import React, { useCallback, useState } from 'react' import { useTranslation } from 'react-i18next' import { useContext } from 'use-context-selector' -import { RiPencilLine } from '@remixicon/react' +import { RiDeleteBin5Line, RiPencilLine } from '@remixicon/react' import { updateUserProfile } from '@/service/common' import { ToastContext } from '@/app/components/base/toast' 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() const [isShowAvatarPicker, setIsShowAvatarPicker] = useState(false) const [uploading, setUploading] = useState(false) + const [isShowDeleteConfirm, setIsShowDeleteConfirm] = useState(false) + const [hoverArea, setHoverArea] = useState('left') const handleImageInput: OnImageInput = useCallback(async (isCropped: boolean, fileOrTempUrl: string | File, croppedAreaPixels?: Area, fileName?: string) => { setInputImageInfo( @@ -48,6 +50,18 @@ const AvatarWithEdit = ({ onSave, ...props }: AvatarWithEditProps) => { } }, [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({ limit: 3, disabled: false, @@ -86,12 +100,21 @@ const AvatarWithEdit = ({ onSave, ...props }: AvatarWithEditProps) => {
{ 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" + 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') + }} > - + {hoverArea === 'right' ? + + : - + } +
@@ -115,6 +138,26 @@ const AvatarWithEdit = ({ onSave, ...props }: AvatarWithEditProps) => { + + setIsShowDeleteConfirm(false)} + > +
{t('common.avatar.deleteTitle')}
+

{t('common.avatar.deleteDescription')}

+ +
+ + + +
+
) } diff --git a/web/i18n/en-US/common.ts b/web/i18n/en-US/common.ts index d9b17dcba..cd585dcf8 100644 --- a/web/i18n/en-US/common.ts +++ b/web/i18n/en-US/common.ts @@ -709,6 +709,10 @@ const translation = { pagination: { 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: { dropImageHere: 'Drop your image here, or', browse: 'browse', diff --git a/web/i18n/zh-Hans/common.ts b/web/i18n/zh-Hans/common.ts index e51b84c37..6198363f2 100644 --- a/web/i18n/zh-Hans/common.ts +++ b/web/i18n/zh-Hans/common.ts @@ -709,6 +709,10 @@ const translation = { pagination: { perPage: '每页显示', }, + avatar: { + deleteTitle: '删除头像', + deleteDescription: '确定要删除你的个人头像吗?你的账号将使用默认的首字母头像。', + }, imageInput: { dropImageHere: '将图片拖放到此处,或', browse: '浏览',