()
| 231 | * Uses custom events for same-tab sync and storage events for cross-tab sync |
| 232 | */ |
| 233 | export function useNewChatDrafts(): NewChatDraft[] { |
| 234 | const [drafts, setDrafts] = useState<NewChatDraft[]>(() => getNewChatDrafts()) |
| 235 | |
| 236 | useEffect(() => { |
| 237 | const handleChange = (e?: Event) => { |
| 238 | // For storage events, only react to draft-related keys |
| 239 | // This prevents re-renders when other localStorage keys change (e.g., sub-chat active state) |
| 240 | if (e instanceof StorageEvent) { |
| 241 | if (!e.key?.startsWith("new-chat-draft-")) { |
| 242 | return |
| 243 | } |
| 244 | } |
| 245 | |
| 246 | const newDrafts = getNewChatDrafts() |
| 247 | // Only update state if drafts actually changed (compare by content) |
| 248 | setDrafts((prev) => { |
| 249 | if (prev.length !== newDrafts.length) return newDrafts |
| 250 | const prevIds = prev.map((d) => d.id).sort().join(",") |
| 251 | const newIds = newDrafts.map((d) => d.id).sort().join(",") |
| 252 | if (prevIds !== newIds) return newDrafts |
| 253 | // Also compare text content |
| 254 | const prevTexts = prev.map((d) => `${d.id}:${d.text}`).sort().join("|") |
| 255 | const newTexts = newDrafts.map((d) => `${d.id}:${d.text}`).sort().join("|") |
| 256 | if (prevTexts !== newTexts) return newDrafts |
| 257 | return prev // No change, return previous reference |
| 258 | }) |
| 259 | } |
| 260 | |
| 261 | // Listen for custom event (same-tab changes) |
| 262 | window.addEventListener(DRAFTS_CHANGE_EVENT, handleChange) |
| 263 | // Listen for storage event (cross-tab changes) |
| 264 | window.addEventListener("storage", handleChange) |
| 265 | |
| 266 | return () => { |
| 267 | window.removeEventListener(DRAFTS_CHANGE_EVENT, handleChange) |
| 268 | window.removeEventListener("storage", handleChange) |
| 269 | } |
| 270 | }, []) |
| 271 | |
| 272 | return drafts |
| 273 | } |
| 274 | |
| 275 | /** |
| 276 | * Hook to get sub-chat drafts cache with automatic updates |
no test coverage detected