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:
93
app/page.tsx
93
app/page.tsx
@@ -35,12 +35,36 @@ import OfflinePinSetup from "@/components/offline/OfflinePinSetup";
|
||||
import OfflinePinVerify from "@/components/offline/OfflinePinVerify";
|
||||
import {SyncedBook, BookSyncCompare, compareBookSyncs} from "@/lib/models/SyncedBook";
|
||||
import {BooksSyncContext} from "@/context/BooksSyncContext";
|
||||
import useSyncBooks from "@/hooks/useSyncBooks";
|
||||
import {LocalSyncQueueContext, LocalSyncOperation} from "@/context/SyncQueueContext";
|
||||
|
||||
const messagesMap = {
|
||||
fr: frMessages,
|
||||
en: enMessages
|
||||
};
|
||||
|
||||
function AutoSyncOnReconnect() {
|
||||
const {offlineMode} = useContext(OfflineContext);
|
||||
const {syncAllToServer, refreshBooks, booksToSyncToServer} = useSyncBooks();
|
||||
const [pendingSync, setPendingSync] = useState<boolean>(false);
|
||||
|
||||
useEffect((): void => {
|
||||
if (!offlineMode.isOffline) {
|
||||
setPendingSync(true);
|
||||
refreshBooks();
|
||||
}
|
||||
}, [offlineMode.isOffline]);
|
||||
|
||||
useEffect((): void => {
|
||||
if (pendingSync && booksToSyncToServer.length > 0) {
|
||||
syncAllToServer();
|
||||
setPendingSync(false);
|
||||
}
|
||||
}, [booksToSyncToServer, pendingSync]);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
function ScribeContent() {
|
||||
const t = useTranslations();
|
||||
const {lang: locale} = useContext(LangContext);
|
||||
@@ -79,7 +103,48 @@ function ScribeContent() {
|
||||
const [homeStepsGuide, setHomeStepsGuide] = useState<boolean>(false);
|
||||
const [showPinSetup, setShowPinSetup] = useState<boolean>(false);
|
||||
const [showPinVerify, setShowPinVerify] = useState<boolean>(false);
|
||||
|
||||
|
||||
const [localSyncQueue, setLocalSyncQueue] = useState<LocalSyncOperation[]>([]);
|
||||
const [isQueueProcessing, setIsQueueProcessing] = useState<boolean>(false);
|
||||
|
||||
|
||||
function addToLocalSyncQueue(channel: string, data: Record<string, unknown>): void {
|
||||
const operation: LocalSyncOperation = {
|
||||
id: `${Date.now()}-${Math.random().toString(36).substring(2, 11)}`,
|
||||
channel,
|
||||
data,
|
||||
timestamp: Date.now(),
|
||||
};
|
||||
setLocalSyncQueue((prev: LocalSyncOperation[]): LocalSyncOperation[] => [...prev, operation]);
|
||||
}
|
||||
|
||||
useEffect((): void => {
|
||||
if (localSyncQueue.length === 0 || isQueueProcessing) {
|
||||
return;
|
||||
}
|
||||
|
||||
async function processQueue(): Promise<void> {
|
||||
setIsQueueProcessing(true);
|
||||
|
||||
const queueCopy: LocalSyncOperation[] = [...localSyncQueue];
|
||||
|
||||
for (const operation of queueCopy) {
|
||||
try {
|
||||
await window.electron.invoke(operation.channel, operation.data);
|
||||
setLocalSyncQueue((prev: LocalSyncOperation[]): LocalSyncOperation[] =>
|
||||
prev.filter((op: LocalSyncOperation): boolean => op.id !== operation.id)
|
||||
);
|
||||
} catch (error) {
|
||||
console.error(`[LocalSyncQueue] Failed to process operation ${operation.channel}:`, error);
|
||||
}
|
||||
}
|
||||
|
||||
setIsQueueProcessing(false);
|
||||
}
|
||||
|
||||
processQueue().then();
|
||||
}, [localSyncQueue, isQueueProcessing]);
|
||||
|
||||
const homeSteps: GuideStep[] = [
|
||||
{
|
||||
id: 0,
|
||||
@@ -212,6 +277,7 @@ function ScribeContent() {
|
||||
console.log('bookSyncDiffsFromServer', bookSyncDiffsFromServer);
|
||||
console.log('bookSyncDiffsToServer', bookSyncDiffsToServer);
|
||||
}, [localSyncedBooks, serverSyncedBooks,localOnlyBooks, bookSyncDiffsFromServer, bookSyncDiffsToServer]);
|
||||
|
||||
|
||||
async function getBooks(): Promise<void> {
|
||||
try {
|
||||
@@ -313,7 +379,6 @@ function ScribeContent() {
|
||||
setHomeStepsGuide(false);
|
||||
}
|
||||
} else {
|
||||
// Mode offline: stocker dans localStorage
|
||||
const completedGuides = JSON.parse(localStorage.getItem('completedGuides') || '[]');
|
||||
if (!completedGuides.includes('home-basic')) {
|
||||
completedGuides.push('home-basic');
|
||||
@@ -535,10 +600,17 @@ function ScribeContent() {
|
||||
|
||||
return (
|
||||
<SessionContext.Provider value={{session: session, setSession: setSession}}>
|
||||
<BooksSyncContext.Provider value={{serverSyncedBooks, localSyncedBooks, booksToSyncFromServer:bookSyncDiffsFromServer, booksToSyncToServer:bookSyncDiffsToServer, setServerOnlyBooks, setLocalOnlyBooks, serverOnlyBooks, localOnlyBooks}}>
|
||||
<BookContext.Provider value={{book: currentBook, setBook: setCurrentBook}}>
|
||||
<ChapterContext.Provider value={{chapter: currentChapter, setChapter: setCurrentChapter}}>
|
||||
<AIUsageContext.Provider value={{
|
||||
<LocalSyncQueueContext.Provider value={{
|
||||
queue: localSyncQueue,
|
||||
setQueue: setLocalSyncQueue,
|
||||
addToQueue: addToLocalSyncQueue,
|
||||
isProcessing: isQueueProcessing,
|
||||
}}>
|
||||
<BooksSyncContext.Provider value={{serverSyncedBooks, localSyncedBooks, booksToSyncFromServer:bookSyncDiffsFromServer, booksToSyncToServer:bookSyncDiffsToServer, setServerSyncedBooks, setLocalSyncedBooks, setServerOnlyBooks, setLocalOnlyBooks, serverOnlyBooks, localOnlyBooks}}>
|
||||
<AutoSyncOnReconnect/>
|
||||
<BookContext.Provider value={{book: currentBook, setBook: setCurrentBook}}>
|
||||
<ChapterContext.Provider value={{chapter: currentChapter, setChapter: setCurrentChapter}}>
|
||||
<AIUsageContext.Provider value={{
|
||||
totalCredits: currentCredits,
|
||||
setTotalCredits: setCurrentCredits,
|
||||
totalPrice: amountSpent,
|
||||
@@ -586,10 +658,11 @@ function ScribeContent() {
|
||||
/>
|
||||
)
|
||||
}
|
||||
</AIUsageContext.Provider>
|
||||
</ChapterContext.Provider>
|
||||
</BookContext.Provider>
|
||||
</BooksSyncContext.Provider>
|
||||
</AIUsageContext.Provider>
|
||||
</ChapterContext.Provider>
|
||||
</BookContext.Provider>
|
||||
</BooksSyncContext.Provider>
|
||||
</LocalSyncQueueContext.Provider>
|
||||
</SessionContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user