Enhance synchronization logic and offline handling
- Refactor components to support conditional offline and online CRUD operations. - Introduce `addToQueue` mechanism for syncing offline changes to the server. - Add `isChapterContentExist` method and related existence checks in repositories. - Consolidate data structures and streamline book, chapter, character, and guideline synchronization workflows. - Encrypt additional character fields and adjust repository inserts for offline data.
This commit is contained in:
@@ -3,13 +3,8 @@ import {faCloud, faCloudArrowDown, faCloudArrowUp, faSpinner} from "@fortawesome
|
||||
import {useTranslations} from "next-intl";
|
||||
import {useState, useContext} from "react";
|
||||
import OfflineContext, {OfflineContextType} from "@/context/OfflineContext";
|
||||
import System from "@/lib/models/System";
|
||||
import {SessionContext, SessionContextProps} from "@/context/SessionContext";
|
||||
import {LangContext} from "@/context/LangContext";
|
||||
import {CompleteBook} from "@/lib/models/Book";
|
||||
import {BooksSyncContext, BooksSyncContextProps, SyncType} from "@/context/BooksSyncContext";
|
||||
import {AlertContext, AlertContextProps} from "@/context/AlertContext";
|
||||
import {BookSyncCompare, SyncedBook} from "@/lib/models/SyncedBook";
|
||||
import {SyncType} from "@/context/BooksSyncContext";
|
||||
import useSyncBooks from "@/hooks/useSyncBooks";
|
||||
|
||||
interface SyncBookProps {
|
||||
bookId: string;
|
||||
@@ -18,148 +13,43 @@ interface SyncBookProps {
|
||||
|
||||
export default function SyncBook({bookId, status}: SyncBookProps) {
|
||||
const t = useTranslations();
|
||||
const {session} = useContext<SessionContextProps>(SessionContext);
|
||||
const {lang} = useContext(LangContext);
|
||||
const {errorMessage} = useContext<AlertContextProps>(AlertContext);
|
||||
const {isCurrentlyOffline} = useContext<OfflineContextType>(OfflineContext);
|
||||
const [isLoading, setIsLoading] = useState<boolean>(false);
|
||||
const [currentStatus, setCurrentStatus] = useState<SyncType>(status);
|
||||
const {booksToSyncToServer, booksToSyncFromServer,serverSyncedBooks,localSyncedBooks,setLocalOnlyBooks, setServerOnlyBooks} = useContext<BooksSyncContextProps>(BooksSyncContext)
|
||||
const {upload: hookUpload, download: hookDownload, syncFromServer: hookSyncFromServer, syncToServer: hookSyncToServer} = useSyncBooks();
|
||||
|
||||
const isOffline: boolean = isCurrentlyOffline();
|
||||
|
||||
async function upload(): Promise<void> {
|
||||
if (isOffline) {
|
||||
return;
|
||||
}
|
||||
if (isOffline) return;
|
||||
setIsLoading(true);
|
||||
try {
|
||||
const bookToSync: CompleteBook = await window.electron.invoke<CompleteBook>('db:book:uploadToServer', bookId);
|
||||
if (!bookToSync) {
|
||||
errorMessage(t("bookCard.uploadError"));
|
||||
return;
|
||||
}
|
||||
const response: boolean = await System.authPostToServer('book/sync/upload', {
|
||||
book: bookToSync
|
||||
}, session.accessToken, lang);
|
||||
if (!response) {
|
||||
errorMessage(t("bookCard.uploadError"));
|
||||
return;
|
||||
}
|
||||
setCurrentStatus('synced');
|
||||
setLocalOnlyBooks((prevBooks: SyncedBook[]): SyncedBook[] => {
|
||||
return prevBooks.filter((book: SyncedBook): boolean => book.id !== bookId)
|
||||
});
|
||||
} catch (e:unknown) {
|
||||
if (e instanceof Error) {
|
||||
errorMessage(e.message);
|
||||
} else {
|
||||
errorMessage(t("bookCard.uploadError"));
|
||||
}
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
const success = await hookUpload(bookId);
|
||||
if (success) setCurrentStatus('synced');
|
||||
setIsLoading(false);
|
||||
}
|
||||
|
||||
async function download(): Promise<void> {
|
||||
if (isOffline) {
|
||||
return;
|
||||
}
|
||||
if (isOffline) return;
|
||||
setIsLoading(true);
|
||||
try {
|
||||
const response: CompleteBook = await System.authGetQueryToServer('book/sync/download', session.accessToken, lang, {bookId});
|
||||
if (!response) {
|
||||
errorMessage(t("bookCard.downloadError"));
|
||||
return;
|
||||
}
|
||||
const syncStatus:boolean = await window.electron.invoke<boolean>('db:book:syncSave', response);
|
||||
if (!syncStatus) {
|
||||
errorMessage(t("bookCard.downloadError"));
|
||||
return;
|
||||
}
|
||||
setCurrentStatus('synced');
|
||||
setServerOnlyBooks((prevBooks: SyncedBook[]): SyncedBook[] => {
|
||||
return prevBooks.filter((book: SyncedBook): boolean => book.id !== bookId)
|
||||
});
|
||||
} catch (e:unknown) {
|
||||
if (e instanceof Error) {
|
||||
errorMessage(e.message);
|
||||
} else {
|
||||
errorMessage(t("bookCard.downloadError"));
|
||||
}
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
const success = await hookDownload(bookId);
|
||||
if (success) setCurrentStatus('synced');
|
||||
setIsLoading(false);
|
||||
}
|
||||
|
||||
async function syncFromServer(): Promise<void> {
|
||||
if (isOffline) {
|
||||
return;
|
||||
}
|
||||
if (isOffline) return;
|
||||
setIsLoading(true);
|
||||
try {
|
||||
const bookToFetch:BookSyncCompare|undefined = booksToSyncFromServer.find((book:BookSyncCompare):boolean => book.id === bookId);
|
||||
if (!bookToFetch) {
|
||||
errorMessage(t("bookCard.syncFromServerError"));
|
||||
return;
|
||||
}
|
||||
const response: CompleteBook = await System.authPostToServer('book/sync/server-to-client', {
|
||||
bookToSync: bookToFetch
|
||||
}, session.accessToken, lang);
|
||||
if (!response) {
|
||||
errorMessage(t("bookCard.syncFromServerError"));
|
||||
return;
|
||||
}
|
||||
const syncStatus:boolean = await window.electron.invoke<boolean>('db:book:sync:toClient', response);
|
||||
if (!syncStatus) {
|
||||
errorMessage(t("bookCard.syncFromServerError"));
|
||||
return;
|
||||
}
|
||||
setCurrentStatus('synced');
|
||||
} catch (e:unknown) {
|
||||
if (e instanceof Error) {
|
||||
errorMessage(e.message);
|
||||
} else {
|
||||
errorMessage(t("bookCard.syncFromServerError"));
|
||||
}
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
const success = await hookSyncFromServer(bookId);
|
||||
if (success) setCurrentStatus('synced');
|
||||
setIsLoading(false);
|
||||
}
|
||||
|
||||
async function syncToServer(): Promise<void> {
|
||||
if (isOffline) {
|
||||
return;
|
||||
}
|
||||
if (isOffline) return;
|
||||
setIsLoading(true);
|
||||
try {
|
||||
const bookToFetch:BookSyncCompare|undefined = booksToSyncToServer.find((book:BookSyncCompare):boolean => book.id === bookId);
|
||||
if (!bookToFetch) {
|
||||
errorMessage(t("bookCard.syncToServerError"));
|
||||
return;
|
||||
}
|
||||
const bookToSync: CompleteBook = await window.electron.invoke<CompleteBook>('db:book:sync:toServer', bookToFetch);
|
||||
if (!bookToSync) {
|
||||
errorMessage(t("bookCard.syncToServerError"));
|
||||
return;
|
||||
}
|
||||
const response: boolean = await System.authPatchToServer('book/sync/client-to-server', {
|
||||
book: bookToSync
|
||||
}, session.accessToken, lang);
|
||||
if (!response) {
|
||||
errorMessage(t("bookCard.syncToServerError"));
|
||||
return;
|
||||
}
|
||||
setCurrentStatus('synced');
|
||||
} catch (e:unknown) {
|
||||
if (e instanceof Error) {
|
||||
errorMessage(e.message);
|
||||
} else {
|
||||
errorMessage(t("bookCard.syncToServerError"));
|
||||
}
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
const success = await hookSyncToServer(bookId);
|
||||
if (success) setCurrentStatus('synced');
|
||||
setIsLoading(false);
|
||||
}
|
||||
|
||||
if (isLoading) {
|
||||
|
||||
Reference in New Issue
Block a user