Implement synchronization, offline data handling, and intelligent request routing

- Add services for offline data management, including `offline-data.service.ts`, ensuring data is saved to a local database and synced with the server when online.
- Introduce bidirectional `SyncService` for managing data synchronization with conflict resolution and retry mechanisms.
- Create `data.service.ts` to handle smart routing between local database and server API based on connectivity status.
- Update models and logic to support comprehensive synchronization for books, chapters, characters, and conversations.
- Implement event listeners for online/offline detection and automatic sync scheduling.
This commit is contained in:
natreex
2025-11-17 07:47:15 -05:00
parent 71067c6fa8
commit 09768aafcf
4 changed files with 1048 additions and 0 deletions

View File

@@ -0,0 +1,227 @@
import { BookProps, BookListProps } from '@/lib/models/Book';
import { CharacterProps } from '@/lib/models/Character';
import { Conversation } from '@/lib/models/QuillSense';
import { WorldProps } from '@/lib/models/World';
/**
* Service pour gérer les données avec cache local
* Sauvegarde automatiquement dans la DB locale (Electron) quand disponible
*/
/**
* Get all books - from local DB if offline, from server otherwise
*/
export async function getBooks(fetchFromServer: () => Promise<BookListProps[]>): Promise<BookListProps[]> {
if (!window.electron) {
return await fetchFromServer();
}
// TODO: Check if offline mode is enabled
const isOffline = false; // Replace with actual offline check
if (isOffline) {
// Fetch from local DB
const result = await window.electron.dbGetBooks();
if (result.success) {
return result.data || [];
}
throw new Error(result.error || 'Failed to get books from local DB');
} else {
// Fetch from server and save to local DB
const books = await fetchFromServer();
// Save to local DB in background
for (const book of books) {
try {
await window.electron.dbSaveBook(book);
} catch (error) {
console.error('Failed to save book to local DB:', error);
}
}
return books;
}
}
/**
* Get single book - from local DB if offline, from server otherwise
*/
export async function getBook(
bookId: string,
fetchFromServer: () => Promise<BookProps>
): Promise<BookProps> {
if (!window.electron) {
return await fetchFromServer();
}
const isOffline = false; // Replace with actual offline check
if (isOffline) {
const result = await window.electron.dbGetBook(bookId);
if (result.success && result.data) {
return result.data;
}
throw new Error(result.error || 'Book not found in local DB');
} else {
const book = await fetchFromServer();
// Save to local DB
try {
await window.electron.dbSaveBook(book);
} catch (error) {
console.error('Failed to save book to local DB:', error);
}
return book;
}
}
/**
* Save book - save to local DB and sync to server later if offline
*/
export async function saveBook(
book: BookProps,
authorId: string | undefined,
saveToServer: () => Promise<void>
): Promise<void> {
if (!window.electron) {
return await saveToServer();
}
const isOffline = false; // Replace with actual offline check
// Always save to local DB first
await window.electron.dbSaveBook(book, authorId);
if (!isOffline) {
// Also save to server
try {
await saveToServer();
} catch (error) {
console.error('Failed to save to server, will sync later:', error);
// Data is already in local DB, will be synced later
}
}
}
/**
* Get characters for a book
*/
export async function getCharacters(
bookId: string,
fetchFromServer: () => Promise<CharacterProps[]>
): Promise<CharacterProps[]> {
if (!window.electron) {
return await fetchFromServer();
}
const isOffline = false; // Replace with actual offline check
if (isOffline) {
const result = await window.electron.dbGetCharacters(bookId);
if (result.success) {
return result.data || [];
}
throw new Error(result.error || 'Failed to get characters from local DB');
} else {
const characters = await fetchFromServer();
// Save to local DB
for (const character of characters) {
try {
await window.electron.dbSaveCharacter(character, bookId);
} catch (error) {
console.error('Failed to save character to local DB:', error);
}
}
return characters;
}
}
/**
* Save character
*/
export async function saveCharacter(
character: CharacterProps,
bookId: string,
saveToServer: () => Promise<void>
): Promise<void> {
if (!window.electron) {
return await saveToServer();
}
const isOffline = false; // Replace with actual offline check
// Always save to local DB first
await window.electron.dbSaveCharacter(character, bookId);
if (!isOffline) {
try {
await saveToServer();
} catch (error) {
console.error('Failed to save to server, will sync later:', error);
}
}
}
/**
* Get conversations for a book
*/
export async function getConversations(
bookId: string,
fetchFromServer: () => Promise<Conversation[]>
): Promise<Conversation[]> {
if (!window.electron) {
return await fetchFromServer();
}
const isOffline = false; // Replace with actual offline check
if (isOffline) {
const result = await window.electron.dbGetConversations(bookId);
if (result.success) {
return result.data || [];
}
throw new Error(result.error || 'Failed to get conversations from local DB');
} else {
const conversations = await fetchFromServer();
// Save to local DB
for (const conversation of conversations) {
try {
await window.electron.dbSaveConversation(conversation, bookId);
} catch (error) {
console.error('Failed to save conversation to local DB:', error);
}
}
return conversations;
}
}
/**
* Save conversation
*/
export async function saveConversation(
conversation: Conversation,
bookId: string,
saveToServer: () => Promise<void>
): Promise<void> {
if (!window.electron) {
return await saveToServer();
}
const isOffline = false; // Replace with actual offline check
// Always save to local DB first
await window.electron.dbSaveConversation(conversation, bookId);
if (!isOffline) {
try {
await saveToServer();
} catch (error) {
console.error('Failed to save to server, will sync later:', error);
}
}
}