- store unsent draft text per chat in zustand - restore draft when switching chats - clear draft after successful send
This commit is contained in:
@@ -7,6 +7,9 @@ import { buildWsUrl } from "../utils/ws";
|
|||||||
export function MessageComposer() {
|
export function MessageComposer() {
|
||||||
const activeChatId = useChatStore((s) => s.activeChatId);
|
const activeChatId = useChatStore((s) => s.activeChatId);
|
||||||
const me = useAuthStore((s) => s.me);
|
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 addOptimisticMessage = useChatStore((s) => s.addOptimisticMessage);
|
||||||
const confirmMessageByClientId = useChatStore((s) => s.confirmMessageByClientId);
|
const confirmMessageByClientId = useChatStore((s) => s.confirmMessageByClientId);
|
||||||
const removeOptimisticMessage = useChatStore((s) => s.removeOptimisticMessage);
|
const removeOptimisticMessage = useChatStore((s) => s.removeOptimisticMessage);
|
||||||
@@ -25,6 +28,19 @@ export function MessageComposer() {
|
|||||||
const [previewUrl, setPreviewUrl] = useState<string | null>(null);
|
const [previewUrl, setPreviewUrl] = useState<string | null>(null);
|
||||||
const [isRecording, setIsRecording] = useState(false);
|
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(() => {
|
useEffect(() => {
|
||||||
return () => {
|
return () => {
|
||||||
if (previewUrl) {
|
if (previewUrl) {
|
||||||
@@ -64,6 +80,7 @@ export function MessageComposer() {
|
|||||||
const message = await sendMessageWithClientId(activeChatId, textValue, "text", clientMessageId, replyToMessageId);
|
const message = await sendMessageWithClientId(activeChatId, textValue, "text", clientMessageId, replyToMessageId);
|
||||||
confirmMessageByClientId(activeChatId, clientMessageId, message);
|
confirmMessageByClientId(activeChatId, clientMessageId, message);
|
||||||
setText("");
|
setText("");
|
||||||
|
clearDraft(activeChatId);
|
||||||
setReplyToMessage(activeChatId, null);
|
setReplyToMessage(activeChatId, null);
|
||||||
const ws = getWs();
|
const ws = getWs();
|
||||||
ws?.send(JSON.stringify({ event: "typing_stop", payload: { chat_id: activeChatId } }));
|
ws?.send(JSON.stringify({ event: "typing_stop", payload: { chat_id: activeChatId } }));
|
||||||
@@ -284,7 +301,11 @@ export function MessageComposer() {
|
|||||||
placeholder="Write a message..."
|
placeholder="Write a message..."
|
||||||
value={text}
|
value={text}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
setText(e.target.value);
|
const next = e.target.value;
|
||||||
|
setText(next);
|
||||||
|
if (activeChatId) {
|
||||||
|
setDraft(activeChatId, next);
|
||||||
|
}
|
||||||
if (activeChatId) {
|
if (activeChatId) {
|
||||||
const ws = getWs();
|
const ws = getWs();
|
||||||
ws?.send(JSON.stringify({ event: "typing_start", payload: { chat_id: activeChatId } }));
|
ws?.send(JSON.stringify({ event: "typing_start", payload: { chat_id: activeChatId } }));
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ interface ChatState {
|
|||||||
chats: Chat[];
|
chats: Chat[];
|
||||||
activeChatId: number | null;
|
activeChatId: number | null;
|
||||||
messagesByChat: Record<number, Message[]>;
|
messagesByChat: Record<number, Message[]>;
|
||||||
|
draftsByChat: Record<number, string>;
|
||||||
typingByChat: Record<number, number[]>;
|
typingByChat: Record<number, number[]>;
|
||||||
replyToByChat: Record<number, Message | null>;
|
replyToByChat: Record<number, Message | null>;
|
||||||
unreadBoundaryByChat: Record<number, number>;
|
unreadBoundaryByChat: Record<number, number>;
|
||||||
@@ -38,12 +39,15 @@ interface ChatState {
|
|||||||
setReplyToMessage: (chatId: number, message: Message | null) => void;
|
setReplyToMessage: (chatId: number, message: Message | null) => void;
|
||||||
updateChatPinnedMessage: (chatId: number, pinnedMessageId: number | null) => void;
|
updateChatPinnedMessage: (chatId: number, pinnedMessageId: number | null) => void;
|
||||||
applyPresenceEvent: (chatId: number, userId: number, isOnline: boolean, lastSeenAt?: string) => 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<ChatState>((set, get) => ({
|
export const useChatStore = create<ChatState>((set, get) => ({
|
||||||
chats: [],
|
chats: [],
|
||||||
activeChatId: null,
|
activeChatId: null,
|
||||||
messagesByChat: {},
|
messagesByChat: {},
|
||||||
|
draftsByChat: {},
|
||||||
typingByChat: {},
|
typingByChat: {},
|
||||||
replyToByChat: {},
|
replyToByChat: {},
|
||||||
unreadBoundaryByChat: {},
|
unreadBoundaryByChat: {},
|
||||||
@@ -272,5 +276,21 @@ export const useChatStore = create<ChatState>((set, get) => ({
|
|||||||
}
|
}
|
||||||
return chat;
|
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 };
|
||||||
|
})
|
||||||
}));
|
}));
|
||||||
|
|||||||
Reference in New Issue
Block a user