feat(chat): персистентные черновики сообщений — текст переживает перезагрузку страницы
This commit is contained in:
@@ -14,6 +14,7 @@ import {
|
||||
import { useToast } from '@/hooks/use-toast';
|
||||
import { useAuth } from '@/hooks/useAuth';
|
||||
import { useEvents } from '@/hooks/useEvents';
|
||||
import { usePersistentDraft } from '@/hooks/usePersistentDraft';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { AutoResizeTextarea } from '@/components/ui/auto-resize-textarea';
|
||||
import { ScrollArea } from '@/components/ui/scroll-area';
|
||||
@@ -178,8 +179,9 @@ const TaskChat = ({ taskId }: TaskChatProps) => {
|
||||
const textareaRef = useRef<HTMLTextAreaElement>(null);
|
||||
const scrollAreaRef = useRef<HTMLDivElement>(null);
|
||||
const markReadTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
||||
// Composer: локальное состояние вместо react-hook-form (форма одного поля)
|
||||
const [messageText, setMessageText] = useState('');
|
||||
// Composer: локальное состояние вместо react-hook-form (форма одного поля).
|
||||
// Черновик персистентный: переживает перезагрузку страницы (SW-деплой, редирект авторизации)
|
||||
const [messageText, setMessageText] = usePersistentDraft(`draft:task:${taskId}`);
|
||||
const [mentionedUserIds, setMentionedUserIds] = useState<number[]>([]);
|
||||
const [mentionedBotIds, setMentionedBotIds] = useState<number[]>([]);
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ import { getAll as getAllQueued } from '@/lib/offlineQueue';
|
||||
import { reportActiveChat, clearActiveChat, setActiveChatInSw, clearActiveChatInSw } from '@/lib/pwa';
|
||||
import { useAuth } from '@/hooks/useAuth';
|
||||
import { useEvents } from '@/hooks/useEvents';
|
||||
import { usePersistentDraft } from '@/hooks/usePersistentDraft';
|
||||
import { useToast } from '@/hooks/use-toast';
|
||||
import { isPWAInstalled } from '@/lib/pwa';
|
||||
import type { ChatAttachment } from '@/components/chat/ChatAttachments';
|
||||
@@ -35,7 +36,8 @@ export function useChatController() {
|
||||
const [groupMembersOpen, setGroupMembersOpen] = useState(false);
|
||||
const [replyTo, setReplyTo] = useState<ConvMessage | null>(null);
|
||||
const [editingMsg, setEditingMsg] = useState<ConvMessage | null>(null);
|
||||
const [messageText, setMessageText] = useState('');
|
||||
// Черновик персистентный: переживает перезагрузку страницы (SW-деплой, редирект авторизации)
|
||||
const [messageText, setMessageText] = usePersistentDraft(`draft:conv:${activeConvId ?? 'none'}`);
|
||||
const [editText, setEditText] = useState('');
|
||||
const [mentionedUserIds, setMentionedUserIds] = useState<number[]>([]);
|
||||
const [mentionSearch, setMentionSearch] = useState<string | null>(null);
|
||||
@@ -724,7 +726,7 @@ export function useChatController() {
|
||||
setActiveConvId(id);
|
||||
setReplyTo(null);
|
||||
setEditingMsg(null);
|
||||
setMessageText('');
|
||||
// Черновик не очищаем: usePersistentDraft восстановит сохранённый текст этого диалога
|
||||
setSearchQuery('');
|
||||
markReadMutation.mutate(id);
|
||||
if (isMobile) setMobileView('chat');
|
||||
|
||||
32
client/src/hooks/usePersistentDraft.ts
Normal file
32
client/src/hooks/usePersistentDraft.ts
Normal file
@@ -0,0 +1,32 @@
|
||||
import { useState, useEffect, useRef, useCallback } from 'react';
|
||||
|
||||
/**
|
||||
* Черновик сообщения с персистентностью в localStorage.
|
||||
*
|
||||
* Текст неотправленного сообщения живёт только в памяти (useState) и пропадает
|
||||
* при перезагрузке страницы (обновление Service Worker после деплоя, редирект
|
||||
* авторизации, дискард вкладки). Хук сохраняет текст по ключу и восстанавливает
|
||||
* при монтировании и при смене ключа (другой чат). Очистка — обычным setValue('').
|
||||
*/
|
||||
export function usePersistentDraft(storageKey: string): [string, (value: string) => void] {
|
||||
const [value, setValue] = useState<string>(() => {
|
||||
try { return localStorage.getItem(storageKey) ?? ''; } catch { return ''; }
|
||||
});
|
||||
const keyRef = useRef(storageKey);
|
||||
keyRef.current = storageKey;
|
||||
|
||||
// Смена чата — восстанавливаем черновик этого чата
|
||||
useEffect(() => {
|
||||
try { setValue(localStorage.getItem(storageKey) ?? ''); } catch { setValue(''); }
|
||||
}, [storageKey]);
|
||||
|
||||
const set = useCallback((next: string) => {
|
||||
setValue(next);
|
||||
try {
|
||||
if (next) localStorage.setItem(keyRef.current, next);
|
||||
else localStorage.removeItem(keyRef.current);
|
||||
} catch { /* localStorage недоступен (приватный режим и т.п.) */ }
|
||||
}, []);
|
||||
|
||||
return [value, set];
|
||||
}
|
||||
Reference in New Issue
Block a user