2023-11-13 22:32:39 +08:00
|
|
|
import type { FC } from 'react'
|
|
|
|
import { useState } from 'react'
|
|
|
|
import { useTranslation } from 'react-i18next'
|
2024-03-10 13:11:41 +08:00
|
|
|
import cn from 'classnames'
|
2023-11-13 22:32:39 +08:00
|
|
|
import Uploader from './uploader'
|
|
|
|
import ImageLinkInput from './image-link-input'
|
|
|
|
import { ImagePlus } from '@/app/components/base/icons/src/vender/line/images'
|
|
|
|
import { TransferMethod } from '@/types/app'
|
|
|
|
import {
|
|
|
|
PortalToFollowElem,
|
|
|
|
PortalToFollowElemContent,
|
|
|
|
PortalToFollowElemTrigger,
|
|
|
|
} from '@/app/components/base/portal-to-follow-elem'
|
|
|
|
import { Upload03 } from '@/app/components/base/icons/src/vender/line/general'
|
|
|
|
import type { ImageFile, VisionSettings } from '@/types/app'
|
|
|
|
|
|
|
|
type UploadOnlyFromLocalProps = {
|
|
|
|
onUpload: (imageFile: ImageFile) => void
|
|
|
|
disabled?: boolean
|
|
|
|
limit?: number
|
|
|
|
}
|
|
|
|
const UploadOnlyFromLocal: FC<UploadOnlyFromLocalProps> = ({
|
|
|
|
onUpload,
|
|
|
|
disabled,
|
|
|
|
limit,
|
|
|
|
}) => {
|
|
|
|
return (
|
|
|
|
<Uploader onUpload={onUpload} disabled={disabled} limit={limit}>
|
2024-03-10 13:11:41 +08:00
|
|
|
{hovering => (
|
|
|
|
<div
|
|
|
|
className={`
|
2023-11-13 22:32:39 +08:00
|
|
|
relative flex items-center justify-center w-8 h-8 rounded-lg cursor-pointer
|
|
|
|
${hovering && 'bg-gray-100'}
|
2024-03-10 13:11:41 +08:00
|
|
|
`}
|
|
|
|
>
|
|
|
|
<ImagePlus className="w-4 h-4 text-gray-500" />
|
|
|
|
</div>
|
|
|
|
)}
|
2023-11-13 22:32:39 +08:00
|
|
|
</Uploader>
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
type UploaderButtonProps = {
|
|
|
|
methods: VisionSettings['transfer_methods']
|
|
|
|
onUpload: (imageFile: ImageFile) => void
|
|
|
|
disabled?: boolean
|
|
|
|
limit?: number
|
|
|
|
}
|
|
|
|
const UploaderButton: FC<UploaderButtonProps> = ({
|
|
|
|
methods,
|
|
|
|
onUpload,
|
|
|
|
disabled,
|
|
|
|
limit,
|
|
|
|
}) => {
|
|
|
|
const { t } = useTranslation()
|
|
|
|
const [open, setOpen] = useState(false)
|
|
|
|
|
2024-03-10 13:11:41 +08:00
|
|
|
const hasUploadFromLocal = methods.find(
|
|
|
|
method => method === TransferMethod.local_file,
|
|
|
|
)
|
2023-11-13 22:32:39 +08:00
|
|
|
|
|
|
|
const handleUpload = (imageFile: ImageFile) => {
|
|
|
|
onUpload(imageFile)
|
|
|
|
}
|
|
|
|
|
2024-03-10 13:11:41 +08:00
|
|
|
const closePopover = () => setOpen(false)
|
|
|
|
|
2023-11-13 22:32:39 +08:00
|
|
|
const handleToggle = () => {
|
|
|
|
if (disabled)
|
|
|
|
return
|
|
|
|
|
|
|
|
setOpen(v => !v)
|
|
|
|
}
|
|
|
|
|
|
|
|
return (
|
|
|
|
<PortalToFollowElem
|
|
|
|
open={open}
|
|
|
|
onOpenChange={setOpen}
|
2024-03-10 13:11:41 +08:00
|
|
|
placement="top-start"
|
2023-11-13 22:32:39 +08:00
|
|
|
>
|
|
|
|
<PortalToFollowElemTrigger onClick={handleToggle}>
|
2024-03-10 13:11:41 +08:00
|
|
|
<button
|
|
|
|
type="button"
|
|
|
|
disabled={disabled}
|
|
|
|
className="relative flex items-center justify-center w-8 h-8 enabled:hover:bg-gray-100 rounded-lg disabled:cursor-not-allowed"
|
|
|
|
>
|
|
|
|
<ImagePlus className="w-4 h-4 text-gray-500" />
|
|
|
|
</button>
|
2023-11-13 22:32:39 +08:00
|
|
|
</PortalToFollowElemTrigger>
|
2024-03-10 13:11:41 +08:00
|
|
|
<PortalToFollowElemContent className="z-50">
|
|
|
|
<div className="p-2 w-[260px] bg-white rounded-lg border-[0.5px] border-gray-200 shadow-lg">
|
2024-04-08 18:51:46 +08:00
|
|
|
<ImageLinkInput onUpload={handleUpload} disabled={disabled} />
|
2024-03-10 13:11:41 +08:00
|
|
|
{hasUploadFromLocal && (
|
|
|
|
<>
|
|
|
|
<div className="flex items-center mt-2 px-2 text-xs font-medium text-gray-400">
|
|
|
|
<div className="mr-3 w-[93px] h-[1px] bg-gradient-to-l from-[#F3F4F6]" />
|
|
|
|
OR
|
|
|
|
<div className="ml-3 w-[93px] h-[1px] bg-gradient-to-r from-[#F3F4F6]" />
|
|
|
|
</div>
|
|
|
|
<Uploader
|
|
|
|
onUpload={handleUpload}
|
|
|
|
limit={limit}
|
|
|
|
closePopover={closePopover}
|
|
|
|
>
|
|
|
|
{hovering => (
|
|
|
|
<div
|
|
|
|
className={cn(
|
|
|
|
'flex items-center justify-center h-8 text-[13px] font-medium text-[#155EEF] rounded-lg cursor-pointer',
|
|
|
|
hovering && 'bg-primary-50',
|
|
|
|
)}
|
|
|
|
>
|
|
|
|
<Upload03 className="mr-1 w-4 h-4" />
|
|
|
|
{t('common.imageUploader.uploadFromComputer')}
|
|
|
|
</div>
|
|
|
|
)}
|
|
|
|
</Uploader>
|
|
|
|
</>
|
|
|
|
)}
|
2023-11-13 22:32:39 +08:00
|
|
|
</div>
|
|
|
|
</PortalToFollowElemContent>
|
|
|
|
</PortalToFollowElem>
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
type ChatImageUploaderProps = {
|
|
|
|
settings: VisionSettings
|
|
|
|
onUpload: (imageFile: ImageFile) => void
|
|
|
|
disabled?: boolean
|
|
|
|
}
|
|
|
|
const ChatImageUploader: FC<ChatImageUploaderProps> = ({
|
|
|
|
settings,
|
|
|
|
onUpload,
|
|
|
|
disabled,
|
|
|
|
}) => {
|
2024-03-10 13:11:41 +08:00
|
|
|
const onlyUploadLocal
|
|
|
|
= settings.transfer_methods.length === 1
|
|
|
|
&& settings.transfer_methods[0] === TransferMethod.local_file
|
2023-11-13 22:32:39 +08:00
|
|
|
|
|
|
|
if (onlyUploadLocal) {
|
|
|
|
return (
|
|
|
|
<UploadOnlyFromLocal
|
|
|
|
onUpload={onUpload}
|
|
|
|
disabled={disabled}
|
|
|
|
limit={+settings.image_file_size_limit!}
|
|
|
|
/>
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
return (
|
|
|
|
<UploaderButton
|
|
|
|
methods={settings.transfer_methods}
|
|
|
|
onUpload={onUpload}
|
|
|
|
disabled={disabled}
|
|
|
|
limit={+settings.image_file_size_limit!}
|
|
|
|
/>
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
export default ChatImageUploader
|