import { Button, Checkbox, Dropdown, Label, Radio, Select, Spinner, TextInput, Textarea, ToggleSwitch } from 'flowbite-react' import { ChangeEvent, Dispatch, SetStateAction, useEffect, useRef, useState } from 'react' import { FormattedMessage, useIntl } from 'react-intl' import { FaEnvelope, FaGlobe, FaListCheck, FaLock, FaLockOpen, FaPaperPlane, FaPaperclip, FaXmark } from 'react-icons/fa6' import { Entity, MegalodonInterface } from 'megalodon' import { useToast } from '@/utils/toast' type Props = { client: MegalodonInterface in_reply_to?: Entity.Status } type Poll = { options: Array expires_in: number multiple: boolean } export default function Compose(props: Props) { const [body, setBody] = useState('') const [visibility, setVisibility] = useState<'public' | 'unlisted' | 'private' | 'direct'>('public') const [cw, setCW] = useState(false) const [spoiler, setSpoiler] = useState('') const [attachments, setAttachments] = useState>([]) const [poll, setPoll] = useState(null) const [loading, setLoading] = useState(false) const { formatMessage } = useIntl() const uploaderRef = useRef(null) const showToast = useToast() useEffect(() => { if (!cw) { setSpoiler('') } }, [cw]) useEffect(() => { if (props.in_reply_to) { const f = async () => { const myself = await props.client.verifyAccountCredentials() const mentionAccounts = [props.in_reply_to.account.acct, ...props.in_reply_to.mentions.map(a => a.acct)] .filter((a, i, self) => self.indexOf(a) === i) .filter(a => a !== myself.data.username) setBody(`${mentionAccounts.map(m => `@${m}`).join(' ')} `) setVisibility(props.in_reply_to.visibility) } f() } }, [props.in_reply_to]) const post = async () => { if (body.length === 0) return let options = { visibility: visibility } if (cw) { options = Object.assign({}, options, { spoiler_text: spoiler }) } if (attachments.length > 0) { options = Object.assign({}, options, { media_ids: attachments.map(m => m.id) }) } const sensitive = document.getElementById('sensitive') as HTMLInputElement if (sensitive && sensitive.checked) { options = Object.assign({}, options, { sensitive: sensitive.checked }) } if (poll && poll.options.length > 0) { options = Object.assign({}, options, { poll: poll }) } setLoading(true) try { await props.client.postStatus(body, options) reset() } finally { setLoading(false) } } const reset = () => { setBody('') setSpoiler('') setCW(false) setAttachments([]) setPoll(null) } const selectFile = () => { if (uploaderRef.current) { uploaderRef.current.click() } } const fileChanged = async (event: ChangeEvent) => { const file = event.target.files?.item(0) if (file === null || file === undefined) { return } if (!file.type.includes('image') && !file.type.includes('video')) { showToast({ text: formatMessage({ id: 'alert.validation.attachment_type' }), type: 'failure' }) return } setLoading(true) try { const res = await props.client.uploadMedia(file) setAttachments(current => [...current, res.data]) } catch { showToast({ text: formatMessage({ id: 'alert.upload_error' }), type: 'failure' }) } finally { setLoading(false) } } const removeFile = (index: number) => { setAttachments(current => current.filter((_, i) => i !== index)) } const togglePoll = () => { if (poll) { setPoll(null) } else { setPoll(defaultPoll()) } } return (
{cw && ( setSpoiler(ev.target.value)} placeholder={formatMessage({ id: 'compose.spoiler.placeholder' })} /> )}