import { useCallback, useEffect, useRef, useState } from 'react' import { useConversationContext } from '../contexts/conversation_context' import type { FileAttachment } from '../types/resources/denormalized_attachment_resource_for_create' import { useAsyncStorage } from './use_async_storage' interface MessageDraft { text: string attachments: FileAttachment[] lastUpdated: number } const DRAFT_STORAGE_KEY = 'chat_message_drafts' const DRAFT_EXPIRY_HOURS = 720 // 30 days const DRAFT_EXPIRY_MS = DRAFT_EXPIRY_HOURS * 60 * 60 * 1000 const ATTACHMENT_EXPIRY_HOURS = 48 // 2 days const ATTACHMENT_EXPIRY_MS = ATTACHMENT_EXPIRY_HOURS * 60 * 60 * 1000 export function useMessageDraft() { const { conversationId, currentPageReplyRootId } = useConversationContext() const conversationKey = currentPageReplyRootId ? `${conversationId}/threads/${currentPageReplyRootId}` : String(conversationId) const [allDrafts, setAllDrafts] = useAsyncStorage>( DRAFT_STORAGE_KEY, {} ) const [draft, setDraft] = useState(() => { const storedDraft = allDrafts[conversationKey] || null // Filter expired attachments if (storedDraft?.attachments) { const now = Date.now() const validAttachments = storedDraft.attachments.filter(attachment => { if (!attachment.uploadedAt) return false return now - attachment.uploadedAt < ATTACHMENT_EXPIRY_MS }) return { ...storedDraft, attachments: validAttachments, } } return storedDraft }) // Clean up expired drafts only once on mount - React 18 recommended pattern const hasCleanedUp = useRef(false) useEffect(() => { if (hasCleanedUp.current) return const now = Date.now() const validDrafts: Record = {} Object.entries(allDrafts).forEach(([id, draftData]) => { if (draftData?.lastUpdated && now - draftData.lastUpdated < DRAFT_EXPIRY_MS) { validDrafts[id] = draftData } }) // If we filtered out any drafts, update storage if (Object.keys(validDrafts).length !== Object.keys(allDrafts).length) { setAllDrafts(validDrafts) } hasCleanedUp.current = true }, [allDrafts, setAllDrafts]) const saveDraft = useCallback( (text: string, attachments: FileAttachment[]) => { const hasContent = text.trim().length > 0 || attachments.length > 0 const updatedDrafts = { ...allDrafts } if (hasContent) { // Convert attachments to serializable format (similar to web implementation) const normalizedAttachments = attachments .filter(att => att.id && att.status === 'success') .map(att => ({ ...att, file: { uri: att.file.uri, name: att.file.name, type: att.file.type, size: att.file.size, width: att.file.width, height: att.file.height, }, })) const newDraft: MessageDraft = { text, attachments: normalizedAttachments, lastUpdated: Date.now(), } updatedDrafts[conversationKey] = newDraft setDraft(newDraft) } else { delete updatedDrafts[conversationKey] setDraft(null) } setAllDrafts(updatedDrafts) }, [conversationKey, allDrafts, setAllDrafts] ) const clearDraft = useCallback(() => { const updatedDrafts = { ...allDrafts } delete updatedDrafts[conversationKey] setAllDrafts(updatedDrafts) setDraft(null) }, [conversationKey, allDrafts, setAllDrafts]) return { draft, saveDraft, clearDraft, } }