diff --git a/web/src/components/AvatarPicker.tsx b/web/src/components/AvatarPicker.tsx index 4f6711f6..1492fa25 100644 --- a/web/src/components/AvatarPicker.tsx +++ b/web/src/components/AvatarPicker.tsx @@ -4,6 +4,7 @@ import { showToasts, t } from 'tailchat-shared'; import { Avatar } from 'antd'; import { Icon } from '@iconify/react'; import { ModalAvatarCropper } from './modals/AvatarCropper'; +import { isGIF } from '@/utils/file-helper'; interface AvatarPickerProps { className?: string; @@ -16,35 +17,45 @@ interface AvatarPickerProps { */ export const AvatarPicker: React.FC = React.memo((props) => { const fileRef = useRef(null); - const [cropUrl, setCropUrl] = useState(props.imageUrl || ''); // 裁剪后并使用的url + const [avatarUrl, setAvatarUrl] = useState(props.imageUrl || ''); // 裁剪后并使用的url/或者未经裁剪的 gif url + + const updateAvatar = (imageBlobUrl: string) => { + setAvatarUrl(imageBlobUrl); + + if (typeof props.onChange === 'function') { + props.onChange(imageBlobUrl); + } + }; const handleSelectFile = (e: React.ChangeEvent) => { if (e.target.files && e.target.files.length > 0) { - const reader = new FileReader(); - reader.addEventListener('load', () => { - if (reader.result) { - const key = openModal( - { - closeModal(key); - setCropUrl(croppedImageBlobUrl); + const pickedFile = e.target.files[0]; - if (typeof props.onChange === 'function') { - props.onChange(croppedImageBlobUrl); - } - }} - />, - { - maskClosable: false, - closable: true, - } - ); - } else { - showToasts(t('文件读取失败'), 'error'); - } - }); - reader.readAsDataURL(e.target.files[0]); + if (isGIF(pickedFile)) { + updateAvatar(URL.createObjectURL(pickedFile)); + } else { + const reader = new FileReader(); + reader.addEventListener('load', () => { + if (reader.result) { + const key = openModal( + { + closeModal(key); + updateAvatar(croppedImageBlobUrl); + }} + />, + { + maskClosable: false, + closable: true, + } + ); + } else { + showToasts(t('文件读取失败'), 'error'); + } + }); + reader.readAsDataURL(pickedFile); + } // 清理选中状态 e.target.files = null; @@ -71,7 +82,7 @@ export const AvatarPicker: React.FC = React.memo((props) => { } - src={cropUrl} + src={avatarUrl} /> )} diff --git a/web/src/components/Modal.tsx b/web/src/components/Modal.tsx index 3379a07d..ad4d7030 100644 --- a/web/src/components/Modal.tsx +++ b/web/src/components/Modal.tsx @@ -88,7 +88,7 @@ export const Modal: React.FC = React.memo((props) => { {/* Inner */}
diff --git a/web/src/utils/file-helper.ts b/web/src/utils/file-helper.ts index eb740deb..de28a022 100644 --- a/web/src/utils/file-helper.ts +++ b/web/src/utils/file-helper.ts @@ -114,3 +114,12 @@ export async function openFile( fileEl.click(); }); } + +/** + * Judge GIF File type by mime type + * @param file File object + * @returns if passed file object is a gif image. + */ +export const isGIF = (file: File): boolean => { + return file.type === 'image/gif'; +};