gitdataai/src/hooks/useRoomDraft.ts
2026-04-15 09:08:09 +08:00

127 lines
3.4 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { useState, useCallback, useEffect, useRef } from 'react';
const DRAFT_STORAGE_KEY = 'room-drafts';
const AUTO_SAVE_INTERVAL = 3000; // 3 seconds
interface DraftData {
content: string;
savedAt: string;
}
interface UseRoomDraftReturn {
draft: string;
setDraft: (content: string) => void;
clearDraft: () => void;
lastSavedAt: string | null;
}
export function useRoomDraft(roomId: string | null): UseRoomDraftReturn {
const [draft, setDraftState] = useState('');
const [lastSavedAt, setLastSavedAt] = useState<string | null>(null);
const saveTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);
const draftRef = useRef(draft);
const roomIdRef = useRef(roomId);
draftRef.current = draft;
roomIdRef.current = roomId;
// Load draft from localStorage on mount
useEffect(() => {
if (!roomId) {
setDraftState('');
setLastSavedAt(null);
return;
}
try {
const stored = localStorage.getItem(DRAFT_STORAGE_KEY);
if (stored) {
const drafts: Record<string, DraftData> = JSON.parse(stored);
const roomDraft = drafts[roomId];
if (roomDraft) {
setDraftState(roomDraft.content);
setLastSavedAt(roomDraft.savedAt);
}
}
} catch (err) {
console.error('Failed to load draft:', err);
}
}, [roomId]);
// Auto-save draft to localStorage
const saveDraft = useCallback((content: string, targetRoomId: string | null) => {
if (!targetRoomId) return;
try {
const stored = localStorage.getItem(DRAFT_STORAGE_KEY);
const drafts: Record<string, DraftData> = stored ? JSON.parse(stored) : {};
drafts[targetRoomId] = {
content,
savedAt: new Date().toISOString(),
};
localStorage.setItem(DRAFT_STORAGE_KEY, JSON.stringify(drafts));
setLastSavedAt(new Date().toISOString());
} catch (err) {
console.error('Failed to save draft:', err);
}
}, []);
// Debounced auto-save
const setDraft = useCallback((content: string) => {
setDraftState(content);
// Clear existing timeout
if (saveTimeoutRef.current) {
clearTimeout(saveTimeoutRef.current);
}
saveTimeoutRef.current = setTimeout(() => {
// 使用最新的 roomId通过 ref
saveDraft(content, roomIdRef.current);
}, AUTO_SAVE_INTERVAL);
}, [saveDraft]);
// Clear draft
const clearDraft = useCallback(() => {
if (!roomId) return;
try {
const stored = localStorage.getItem(DRAFT_STORAGE_KEY);
if (stored) {
const drafts: Record<string, DraftData> = JSON.parse(stored);
delete drafts[roomId];
localStorage.setItem(DRAFT_STORAGE_KEY, JSON.stringify(drafts));
}
} catch (err) {
console.error('Failed to clear draft:', err);
}
setDraftState('');
setLastSavedAt(null);
}, [roomId]);
// Cleanup on unmount or roomId change
useEffect(() => {
return () => {
if (saveTimeoutRef.current) {
clearTimeout(saveTimeoutRef.current);
}
// Save immediately on unmount/roomId change if there's content
// 使用 ref 中的最新值而不是闭包中的值
const currentDraft = draftRef.current;
const currentRoomId = roomIdRef.current;
if (currentDraft && currentRoomId) {
saveDraft(currentDraft, currentRoomId);
}
};
}, [roomId, saveDraft]);
return {
draft,
setDraft,
clearDraft,
lastSavedAt,
};
}