44 lines
1.6 KiB
TypeScript
44 lines
1.6 KiB
TypeScript
import { useEffect } from 'react';
|
|||
|
|
import { useStore } from 'jotai';
|
||
|
|
import { Descendant } from 'slate';
|
||
|
|
import { roomIdToMsgDraftAtomFamily } from '../state/room/roomInputDrafts';
|
||
|
|
import { DRAFT_MSG_KEY_PREFIX, hasMsgDraft } from '../utils/draft';
|
||
|
|
|
||
|
|
/**
|
||
|
|
* On startup, pre-fill the per-room message-draft atoms from their localStorage
|
||
|
|
* persistence (`draft-msg-<roomId>`). RoomInput only restores a draft into its
|
||
|
|
* atom when that room's composer mounts, so without this a background room's
|
||
|
|
* persisted draft wouldn't reflect in the room-nav draft indicator until the
|
||
|
|
* room is opened. Runs once; only touches the same atoms RoomInput restores from
|
||
|
|
* (identical content), so composer restore is unaffected.
|
||
|
|
*
|
||
|
|
* Thread drafts (key contains `::`) are skipped — the nav indicator is room-level.
|
||
|
|
*/
|
||
|
|
export function useHydrateMsgDrafts(): void {
|
||
|
|
const store = useStore();
|
||
|
|
|
||
|
|
useEffect(() => {
|
||
|
|
let keys: string[];
|
||
|
|
try {
|
||
|
|
keys = Object.keys(localStorage);
|
||
|
|
} catch {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
keys.forEach((key) => {
|
||
|
|
if (!key.startsWith(DRAFT_MSG_KEY_PREFIX)) return;
|
||
|
|
const draftKey = key.slice(DRAFT_MSG_KEY_PREFIX.length);
|
||
|
|
if (!draftKey || draftKey.includes('::')) return; // room-level drafts only
|
||
|
|
try {
|
||
|
|
const stored = localStorage.getItem(key);
|
||
|
|
if (!stored) return;
|
||
|
|
const nodes = JSON.parse(stored) as Descendant[];
|
||
|
|
if (Array.isArray(nodes) && hasMsgDraft(nodes)) {
|
||
|
|
store.set(roomIdToMsgDraftAtomFamily(draftKey), nodes);
|
||
|
|
}
|
||
|
|
} catch {
|
||
|
|
// Ignore a malformed stored draft.
|
||
|
|
}
|
||
|
|
});
|
||
|
|
}, [store]);
|
||
|
|
}
|