Spaces:
Build error
Build error
import type { FC } from 'react' | |
import { useCallback, useState } from 'react' | |
import { useTranslation } from 'react-i18next' | |
import type { Area } from 'react-easy-crop' | |
import Modal from '../modal' | |
import Divider from '../divider' | |
import Button from '../button' | |
import { ImagePlus } from '../icons/src/vender/line/images' | |
import { useLocalFileUploader } from '../image-uploader/hooks' | |
import EmojiPickerInner from '../emoji-picker/Inner' | |
import Uploader from './Uploader' | |
import s from './style.module.css' | |
import getCroppedImg from './utils' | |
import type { AppIconType, ImageFile } from '@/types/app' | |
import cn from '@/utils/classnames' | |
import { DISABLE_UPLOAD_IMAGE_AS_ICON } from '@/config' | |
export type AppIconEmojiSelection = { | |
type: 'emoji' | |
icon: string | |
background: string | |
} | |
export type AppIconImageSelection = { | |
type: 'image' | |
fileId: string | |
url: string | |
} | |
export type AppIconSelection = AppIconEmojiSelection | AppIconImageSelection | |
type AppIconPickerProps = { | |
onSelect?: (payload: AppIconSelection) => void | |
onClose?: () => void | |
className?: string | |
} | |
const AppIconPicker: FC<AppIconPickerProps> = ({ | |
onSelect, | |
onClose, | |
className, | |
}) => { | |
const { t } = useTranslation() | |
const tabs = [ | |
{ key: 'emoji', label: t('app.iconPicker.emoji'), icon: <span className="text-lg">🤖</span> }, | |
{ key: 'image', label: t('app.iconPicker.image'), icon: <ImagePlus /> }, | |
] | |
const [activeTab, setActiveTab] = useState<AppIconType>('emoji') | |
const [emoji, setEmoji] = useState<{ emoji: string; background: string }>() | |
const handleSelectEmoji = useCallback((emoji: string, background: string) => { | |
setEmoji({ emoji, background }) | |
}, [setEmoji]) | |
const [uploading, setUploading] = useState<boolean>() | |
const { handleLocalFileUpload } = useLocalFileUploader({ | |
limit: 3, | |
disabled: false, | |
onUpload: (imageFile: ImageFile) => { | |
if (imageFile.fileId) { | |
setUploading(false) | |
onSelect?.({ | |
type: 'image', | |
fileId: imageFile.fileId, | |
url: imageFile.url, | |
}) | |
} | |
}, | |
}) | |
const [imageCropInfo, setImageCropInfo] = useState<{ tempUrl: string; croppedAreaPixels: Area; fileName: string }>() | |
const handleImageCropped = async (tempUrl: string, croppedAreaPixels: Area, fileName: string) => { | |
setImageCropInfo({ tempUrl, croppedAreaPixels, fileName }) | |
} | |
const [uploadImageInfo, setUploadImageInfo] = useState<{ file?: File }>() | |
const handleUpload = async (file?: File) => { | |
setUploadImageInfo({ file }) | |
} | |
const handleSelect = async () => { | |
if (activeTab === 'emoji') { | |
if (emoji) { | |
onSelect?.({ | |
type: 'emoji', | |
icon: emoji.emoji, | |
background: emoji.background, | |
}) | |
} | |
} | |
else { | |
if (!imageCropInfo && !uploadImageInfo) | |
return | |
setUploading(true) | |
if (imageCropInfo.file) { | |
handleLocalFileUpload(imageCropInfo.file) | |
return | |
} | |
const blob = await getCroppedImg(imageCropInfo.tempUrl, imageCropInfo.croppedAreaPixels, imageCropInfo.fileName) | |
const file = new File([blob], imageCropInfo.fileName, { type: blob.type }) | |
handleLocalFileUpload(file) | |
} | |
} | |
return <Modal | |
onClose={() => { }} | |
isShow | |
closable={false} | |
wrapperClassName={className} | |
className={cn(s.container, '!w-[362px] !p-0')} | |
> | |
{!DISABLE_UPLOAD_IMAGE_AS_ICON && <div className="p-2 pb-0 w-full"> | |
<div className='p-1 flex items-center justify-center gap-2 bg-background-body rounded-xl'> | |
{tabs.map(tab => ( | |
<button | |
key={tab.key} | |
className={` | |
p-2 flex-1 flex justify-center items-center h-8 rounded-xl text-sm shrink-0 font-medium | |
${activeTab === tab.key && 'bg-components-main-nav-nav-button-bg-active shadow-md'} | |
`} | |
onClick={() => setActiveTab(tab.key as AppIconType)} | |
> | |
{tab.icon} {tab.label} | |
</button> | |
))} | |
</div> | |
</div>} | |
<Divider className='m-0' /> | |
<EmojiPickerInner className={activeTab === 'emoji' ? 'block' : 'hidden'} onSelect={handleSelectEmoji} /> | |
<Uploader className={activeTab === 'image' ? 'block' : 'hidden'} onImageCropped={handleImageCropped} onUpload={handleUpload}/> | |
<Divider className='m-0' /> | |
<div className='w-full flex items-center justify-center p-3 gap-2'> | |
<Button className='w-full' onClick={() => onClose?.()}> | |
{t('app.iconPicker.cancel')} | |
</Button> | |
<Button variant="primary" className='w-full' disabled={uploading} loading={uploading} onClick={handleSelect}> | |
{t('app.iconPicker.ok')} | |
</Button> | |
</div> | |
</Modal> | |
} | |
export default AppIconPicker | |