- Introduce new error messages for syncing and book deletion in `en.json`. - Update `DeleteBook` to support local-only deletion and synced book management. - Refine offline/online behavior with `deleteLocalToo` checkbox and update related state handling. - Extend repository and IPC methods to handle optional IDs for updates. - Add `SyncQueueContext` for queueing offline changes and improving synchronization workflows. - Enhance refined text generation logic in `DraftCompanion` and `GhostWriter` components. - Replace PUT with PATCH for world updates to align with API expectations. - Streamline `AlertBox` by integrating dynamic translation keys for deletion prompts.
207 lines
8.1 KiB
TypeScript
207 lines
8.1 KiB
TypeScript
import {useContext} from 'react';
|
|
import System from '@/lib/models/System';
|
|
import {SessionContext} from '@/context/SessionContext';
|
|
import {LangContext} from '@/context/LangContext';
|
|
import {AlertContext} from '@/context/AlertContext';
|
|
import OfflineContext from '@/context/OfflineContext';
|
|
import {BooksSyncContext} from '@/context/BooksSyncContext';
|
|
import {CompleteBook} from '@/lib/models/Book';
|
|
import {BookSyncCompare, SyncedBook} from '@/lib/models/SyncedBook';
|
|
import {useTranslations} from 'next-intl';
|
|
|
|
export default function useSyncBooks() {
|
|
const t = useTranslations();
|
|
const {session} = useContext(SessionContext);
|
|
const {lang} = useContext(LangContext);
|
|
const {errorMessage} = useContext(AlertContext);
|
|
const {isCurrentlyOffline, offlineMode} = useContext(OfflineContext);
|
|
const {
|
|
booksToSyncToServer,
|
|
booksToSyncFromServer,
|
|
localOnlyBooks,
|
|
serverOnlyBooks,
|
|
setLocalOnlyBooks,
|
|
setServerOnlyBooks,
|
|
setServerSyncedBooks,
|
|
setLocalSyncedBooks
|
|
} = useContext(BooksSyncContext);
|
|
|
|
async function upload(bookId: string): Promise<boolean> {
|
|
if (isCurrentlyOffline()) return false;
|
|
|
|
try {
|
|
const bookToSync: CompleteBook = await window.electron.invoke<CompleteBook>('db:book:uploadToServer', bookId);
|
|
if (!bookToSync) {
|
|
errorMessage(t('bookCard.uploadError'));
|
|
return false;
|
|
}
|
|
const response: boolean = await System.authPostToServer('book/sync/upload', {
|
|
book: bookToSync
|
|
}, session.accessToken, lang);
|
|
if (!response) {
|
|
errorMessage(t('bookCard.uploadError'));
|
|
return false;
|
|
}
|
|
const uploadedBook: SyncedBook | undefined = localOnlyBooks.find((book: SyncedBook): boolean => book.id === bookId);
|
|
setLocalOnlyBooks((prevBooks: SyncedBook[]): SyncedBook[] => {
|
|
return prevBooks.filter((book: SyncedBook): boolean => book.id !== bookId);
|
|
});
|
|
if (uploadedBook) {
|
|
setLocalSyncedBooks((prev: SyncedBook[]): SyncedBook[] => [...prev, uploadedBook]);
|
|
setServerSyncedBooks((prev: SyncedBook[]): SyncedBook[] => [...prev, uploadedBook]);
|
|
}
|
|
return true;
|
|
} catch (e: unknown) {
|
|
if (e instanceof Error) {
|
|
errorMessage(e.message);
|
|
} else {
|
|
errorMessage(t('bookCard.uploadError'));
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
|
|
async function download(bookId: string): Promise<boolean> {
|
|
if (isCurrentlyOffline()) return false;
|
|
|
|
try {
|
|
const response: CompleteBook = await System.authGetQueryToServer('book/sync/download', session.accessToken, lang, {bookId});
|
|
if (!response) {
|
|
errorMessage(t('bookCard.downloadError'));
|
|
return false;
|
|
}
|
|
const syncStatus: boolean = await window.electron.invoke<boolean>('db:book:syncSave', response);
|
|
if (!syncStatus) {
|
|
errorMessage(t('bookCard.downloadError'));
|
|
return false;
|
|
}
|
|
const downloadedBook: SyncedBook | undefined = serverOnlyBooks.find((book: SyncedBook): boolean => book.id === bookId);
|
|
setServerOnlyBooks((prevBooks: SyncedBook[]): SyncedBook[] => {
|
|
return prevBooks.filter((book: SyncedBook): boolean => book.id !== bookId);
|
|
});
|
|
if (downloadedBook) {
|
|
setLocalSyncedBooks((prev: SyncedBook[]): SyncedBook[] => [...prev, downloadedBook]);
|
|
setServerSyncedBooks((prev: SyncedBook[]): SyncedBook[] => [...prev, downloadedBook]);
|
|
}
|
|
return true;
|
|
} catch (e: unknown) {
|
|
if (e instanceof Error) {
|
|
errorMessage(e.message);
|
|
} else {
|
|
errorMessage(t('bookCard.downloadError'));
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
|
|
async function syncFromServer(bookId: string): Promise<boolean> {
|
|
if (isCurrentlyOffline()) return false;
|
|
|
|
try {
|
|
const bookToFetch: BookSyncCompare | undefined = booksToSyncFromServer.find((book: BookSyncCompare): boolean => book.id === bookId);
|
|
if (!bookToFetch) {
|
|
errorMessage(t('bookCard.syncFromServerError'));
|
|
return false;
|
|
}
|
|
const response: CompleteBook = await System.authPostToServer('book/sync/server-to-client', {
|
|
bookToSync: bookToFetch
|
|
}, session.accessToken, lang);
|
|
if (!response) {
|
|
errorMessage(t('bookCard.syncFromServerError'));
|
|
return false;
|
|
}
|
|
const syncStatus: boolean = await window.electron.invoke<boolean>('db:book:sync:toClient', response);
|
|
if (!syncStatus) {
|
|
errorMessage(t('bookCard.syncFromServerError'));
|
|
return false;
|
|
}
|
|
return true;
|
|
} catch (e: unknown) {
|
|
if (e instanceof Error) {
|
|
errorMessage(e.message);
|
|
} else {
|
|
errorMessage(t('bookCard.syncFromServerError'));
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
|
|
async function syncToServer(bookId: string): Promise<boolean> {
|
|
if (isCurrentlyOffline()) return false;
|
|
|
|
try {
|
|
const bookToFetch: BookSyncCompare | undefined = booksToSyncToServer.find((book: BookSyncCompare): boolean => book.id === bookId);
|
|
if (!bookToFetch) {
|
|
errorMessage(t('bookCard.syncToServerError'));
|
|
return false;
|
|
}
|
|
const bookToSync: CompleteBook = await window.electron.invoke<CompleteBook>('db:book:sync:toServer', bookToFetch);
|
|
if (!bookToSync) {
|
|
errorMessage(t('bookCard.syncToServerError'));
|
|
return false;
|
|
}
|
|
const response: boolean = await System.authPatchToServer('book/sync/client-to-server', {
|
|
book: bookToSync
|
|
}, session.accessToken, lang);
|
|
if (!response) {
|
|
errorMessage(t('bookCard.syncToServerError'));
|
|
return false;
|
|
}
|
|
return true;
|
|
} catch (e: unknown) {
|
|
if (e instanceof Error) {
|
|
errorMessage(e.message);
|
|
} else {
|
|
errorMessage(t('bookCard.syncToServerError'));
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
|
|
async function syncAllToServer(): Promise<void> {
|
|
for (const diff of booksToSyncToServer) {
|
|
await syncToServer(diff.id);
|
|
}
|
|
}
|
|
|
|
async function refreshBooks(): Promise<void> {
|
|
try {
|
|
let localBooksResponse: SyncedBook[] = [];
|
|
let serverBooksResponse: SyncedBook[] = [];
|
|
|
|
if (!isCurrentlyOffline()) {
|
|
if (offlineMode.isDatabaseInitialized) {
|
|
localBooksResponse = await window.electron.invoke<SyncedBook[]>('db:books:synced');
|
|
}
|
|
serverBooksResponse = await System.authGetQueryToServer<SyncedBook[]>('books/synced', session.accessToken, lang);
|
|
} else {
|
|
if (offlineMode.isDatabaseInitialized) {
|
|
localBooksResponse = await window.electron.invoke<SyncedBook[]>('db:books:synced');
|
|
}
|
|
}
|
|
|
|
setServerSyncedBooks(serverBooksResponse);
|
|
setLocalSyncedBooks(localBooksResponse);
|
|
} catch (e: unknown) {
|
|
if (e instanceof Error) {
|
|
errorMessage(e.message);
|
|
} else {
|
|
errorMessage(t('bookCard.refreshError'));
|
|
}
|
|
}
|
|
}
|
|
|
|
return {
|
|
upload,
|
|
download,
|
|
syncFromServer,
|
|
syncToServer,
|
|
syncAllToServer,
|
|
refreshBooks,
|
|
localOnlyBooks,
|
|
serverOnlyBooks,
|
|
booksToSyncToServer,
|
|
booksToSyncFromServer
|
|
};
|
|
}
|