diff --git a/web/src/components/MessageComposer.tsx b/web/src/components/MessageComposer.tsx index 92810da..7a20f06 100644 --- a/web/src/components/MessageComposer.tsx +++ b/web/src/components/MessageComposer.tsx @@ -7,6 +7,9 @@ import { buildWsUrl } from "../utils/ws"; export function MessageComposer() { const activeChatId = useChatStore((s) => s.activeChatId); const me = useAuthStore((s) => s.me); + const draftsByChat = useChatStore((s) => s.draftsByChat); + const setDraft = useChatStore((s) => s.setDraft); + const clearDraft = useChatStore((s) => s.clearDraft); const addOptimisticMessage = useChatStore((s) => s.addOptimisticMessage); const confirmMessageByClientId = useChatStore((s) => s.confirmMessageByClientId); const removeOptimisticMessage = useChatStore((s) => s.removeOptimisticMessage); @@ -25,6 +28,19 @@ export function MessageComposer() { const [previewUrl, setPreviewUrl] = useState(null); const [isRecording, setIsRecording] = useState(false); + useEffect(() => { + if (!activeChatId) { + if (text !== "") { + setText(""); + } + return; + } + const draft = draftsByChat[activeChatId] ?? ""; + if (draft !== text) { + setText(draft); + } + }, [activeChatId, draftsByChat, text]); + useEffect(() => { return () => { if (previewUrl) { @@ -64,6 +80,7 @@ export function MessageComposer() { const message = await sendMessageWithClientId(activeChatId, textValue, "text", clientMessageId, replyToMessageId); confirmMessageByClientId(activeChatId, clientMessageId, message); setText(""); + clearDraft(activeChatId); setReplyToMessage(activeChatId, null); const ws = getWs(); ws?.send(JSON.stringify({ event: "typing_stop", payload: { chat_id: activeChatId } })); @@ -284,7 +301,11 @@ export function MessageComposer() { placeholder="Write a message..." value={text} onChange={(e) => { - setText(e.target.value); + const next = e.target.value; + setText(next); + if (activeChatId) { + setDraft(activeChatId, next); + } if (activeChatId) { const ws = getWs(); ws?.send(JSON.stringify({ event: "typing_start", payload: { chat_id: activeChatId } })); diff --git a/web/src/store/chatStore.ts b/web/src/store/chatStore.ts index c0b5f31..8a261bf 100644 --- a/web/src/store/chatStore.ts +++ b/web/src/store/chatStore.ts @@ -6,6 +6,7 @@ interface ChatState { chats: Chat[]; activeChatId: number | null; messagesByChat: Record; + draftsByChat: Record; typingByChat: Record; replyToByChat: Record; unreadBoundaryByChat: Record; @@ -38,12 +39,15 @@ interface ChatState { setReplyToMessage: (chatId: number, message: Message | null) => void; updateChatPinnedMessage: (chatId: number, pinnedMessageId: number | null) => void; applyPresenceEvent: (chatId: number, userId: number, isOnline: boolean, lastSeenAt?: string) => void; + setDraft: (chatId: number, text: string) => void; + clearDraft: (chatId: number) => void; } export const useChatStore = create((set, get) => ({ chats: [], activeChatId: null, messagesByChat: {}, + draftsByChat: {}, typingByChat: {}, replyToByChat: {}, unreadBoundaryByChat: {}, @@ -272,5 +276,21 @@ export const useChatStore = create((set, get) => ({ } return chat; }) - })) + })), + setDraft: (chatId, text) => + set((state) => ({ + draftsByChat: { + ...state.draftsByChat, + [chatId]: text + } + })), + clearDraft: (chatId) => + set((state) => { + if (!(chatId in state.draftsByChat)) { + return state; + } + const next = { ...state.draftsByChat }; + delete next[chatId]; + return { draftsByChat: next }; + }) }));