forked from akai/readest
88 lines
2.9 KiB
TypeScript
88 lines
2.9 KiB
TypeScript
import { useCallback, useEffect } from 'react';
|
|
import { useAuth } from '@/context/AuthContext';
|
|
import { useSync } from '@/hooks/useSync';
|
|
import { BookNote } from '@/types/book';
|
|
import { useBookDataStore } from '@/store/bookDataStore';
|
|
import { SYNC_NOTES_INTERVAL_SEC } from '@/services/constants';
|
|
import { throttle } from '@/utils/throttle';
|
|
|
|
export const useNotesSync = (bookKey: string) => {
|
|
const { user } = useAuth();
|
|
const { syncedNotes, syncNotes, lastSyncedAtNotes } = useSync(bookKey);
|
|
const { getConfig, setConfig, getBookData } = useBookDataStore();
|
|
|
|
const config = getConfig(bookKey);
|
|
|
|
const getNewNotes = useCallback(() => {
|
|
const config = getConfig(bookKey);
|
|
const book = getBookData(bookKey)?.book;
|
|
if (!config?.location || !book || !user) return {};
|
|
|
|
const bookNotes = config.booknotes ?? [];
|
|
const newNotes = bookNotes.filter(
|
|
(note) => lastSyncedAtNotes < note.updatedAt || lastSyncedAtNotes < (note.deletedAt ?? 0),
|
|
);
|
|
newNotes.forEach((note) => {
|
|
note.bookHash = book.hash;
|
|
note.metaHash = book.metaHash;
|
|
});
|
|
return {
|
|
notes: newNotes,
|
|
lastSyncedAt: lastSyncedAtNotes,
|
|
};
|
|
}, [user, bookKey, lastSyncedAtNotes, getConfig, getBookData]);
|
|
|
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
const handleAutoSync = useCallback(
|
|
throttle(
|
|
() => {
|
|
const book = getBookData(bookKey)?.book;
|
|
const newNotes = getNewNotes();
|
|
syncNotes(newNotes.notes, book?.hash, book?.metaHash, 'both');
|
|
},
|
|
SYNC_NOTES_INTERVAL_SEC * 1000,
|
|
{ emitLast: false },
|
|
),
|
|
[syncNotes],
|
|
);
|
|
|
|
useEffect(() => {
|
|
if (!config?.location || !user) return;
|
|
handleAutoSync();
|
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
}, [config?.booknotes, handleAutoSync]);
|
|
|
|
useEffect(() => {
|
|
const processNewNote = (note: BookNote) => {
|
|
const config = getConfig(bookKey);
|
|
const oldNotes = config?.booknotes ?? [];
|
|
const existingNote = oldNotes.find((oldNote) => oldNote.id === note.id);
|
|
if (existingNote) {
|
|
if (
|
|
existingNote.updatedAt < note.updatedAt ||
|
|
(existingNote.deletedAt ?? 0) < (note.deletedAt ?? 0)
|
|
) {
|
|
return { ...existingNote, ...note };
|
|
} else {
|
|
return { ...note, ...existingNote };
|
|
}
|
|
}
|
|
return note;
|
|
};
|
|
if (syncedNotes?.length && config) {
|
|
const book = getBookData(bookKey)?.book;
|
|
const newNotes = syncedNotes.filter(
|
|
(note) => note.bookHash === book?.hash || note.metaHash === book?.metaHash,
|
|
);
|
|
if (!newNotes.length) return;
|
|
const oldNotes = config.booknotes ?? [];
|
|
const mergedNotes = [
|
|
...oldNotes.filter((oldNote) => !newNotes.some((newNote) => newNote.id === oldNote.id)),
|
|
...newNotes.map(processNewNote),
|
|
];
|
|
setConfig(bookKey, { booknotes: mergedNotes });
|
|
}
|
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
}, [syncedNotes]);
|
|
};
|