Refactor and extend offline synchronization logic across components and services

- Integrated sync queue mechanisms with `LocalSyncQueueContext` for offline data handling.
- Updated key sync-related services (e.g., book, chapter, series) to support offline-first functionality.
- Removed redundant database fetch methods to optimize repository logic and improve maintainability.
- Enhanced Tauri IPC usage for sync operations and removed legacy methods in Rust services.
This commit is contained in:
natreex
2026-03-30 21:06:58 -04:00
parent b9606e899a
commit dbbe33b19b
22 changed files with 295 additions and 293 deletions

View File

@@ -5,6 +5,9 @@ import {SessionContext, SessionContextProps} from "@/context/SessionContext";
import {AlertContext, AlertContextProps} from "@/context/AlertContext";
import {BookContext, BookContextProps} from "@/context/BookContext";
import OfflineContext, {OfflineContextType} from '@/context/OfflineContext';
import {BooksSyncContext, BooksSyncContextProps} from '@/context/BooksSyncContext';
import {SyncedBook} from '@/lib/types/synced-book';
import {LocalSyncQueueContext, LocalSyncQueueContextProps} from '@/context/SyncQueueContext';
import {isDesktop} from '@/lib/configs';
import {apiDelete, apiGet, apiPatch, apiPost} from '@/lib/api/client';
import * as tauri from '@/lib/tauri';
@@ -57,6 +60,8 @@ export function LocationComponent(props: LocationComponentProps, ref: React.Ref<
const {successMessage, errorMessage}: AlertContextProps = useContext<AlertContextProps>(AlertContext);
const {book, setBook}: BookContextProps = useContext<BookContextProps>(BookContext);
const {isCurrentlyOffline} = useContext(OfflineContext);
const {addToQueue}: LocalSyncQueueContextProps = useContext<LocalSyncQueueContextProps>(LocalSyncQueueContext);
const {localSyncedBooks}: BooksSyncContextProps = useContext<BooksSyncContextProps>(BooksSyncContext);
const currentEntityId: string = entityId || book?.bookId || '';
const useLocal: boolean = isDesktop && (isCurrentlyOffline() || !!book?.localBook);
@@ -120,6 +125,9 @@ export function LocationComponent(props: LocationComponentProps, ref: React.Ref<
toolName: 'locations',
enabled: enabled
}, token, lang);
if (!useLocal && isDesktop && localSyncedBooks.find((sb: SyncedBook): boolean => sb.id === currentEntityId)) {
addToQueue('update_book_tool_setting', {bookId: currentEntityId, toolName: 'locations', enabled});
}
if (response && setBook && book) {
setToolEnabled(enabled);
setBook({
@@ -218,6 +226,9 @@ export function LocationComponent(props: LocationComponentProps, ref: React.Ref<
sectionId = useLocal
? await tauri.addLocationSection(newSectionName, currentEntityId)
: await apiPost<string>('location/section/add', {bookId: currentEntityId, locationName: newSectionName}, token, lang);
if (!useLocal && isDesktop && localSyncedBooks.find((sb: SyncedBook): boolean => sb.id === currentEntityId)) {
addToQueue('add_location_section', {bookId: currentEntityId, sectionId, locationName: newSectionName});
}
if (!sectionId) {
errorMessage(t('locationComponent.errorUnknownAddSection'));
return;
@@ -258,6 +269,9 @@ export function LocationComponent(props: LocationComponentProps, ref: React.Ref<
elementId = useLocal
? await tauri.addLocationElement(sectionId, newElementNames[sectionId])
: await apiPost<string>('location/element/add', {bookId: currentEntityId, locationId: sectionId, elementName: newElementNames[sectionId]}, token, lang);
if (!useLocal && isDesktop && localSyncedBooks.find((sb: SyncedBook): boolean => sb.id === currentEntityId)) {
addToQueue('add_location_element', {bookId: currentEntityId, locationId: sectionId, elementId, elementName: newElementNames[sectionId]});
}
if (!elementId) {
errorMessage(t('locationComponent.errorUnknownAddElement'));
return;
@@ -325,6 +339,9 @@ export function LocationComponent(props: LocationComponentProps, ref: React.Ref<
subElementId = useLocal
? await tauri.addLocationSubElement(parentElementId, newSubElementNames[elementIndex])
: await apiPost<string>('location/sub-element/add', {elementId: parentElementId, subElementName: newSubElementNames[elementIndex]}, token, lang);
if (!useLocal && isDesktop && localSyncedBooks.find((sb: SyncedBook): boolean => sb.id === currentEntityId)) {
addToQueue('add_location_subelement', {elementId: parentElementId, subElementId, subElementName: newSubElementNames[elementIndex]});
}
if (!subElementId) {
errorMessage(t('locationComponent.errorUnknownAddSubElement'));
return;
@@ -381,6 +398,9 @@ export function LocationComponent(props: LocationComponentProps, ref: React.Ref<
success = useLocal
? await tauri.deleteLocationElement(elementId!, currentEntityId, deletedAt)
: await apiDelete<boolean>('location/element/delete', {elementId: elementId}, token, lang);
if (!useLocal && isDesktop && localSyncedBooks.find((sb: SyncedBook): boolean => sb.id === currentEntityId)) {
addToQueue('delete_location_element', {elementId, bookId: currentEntityId, deletedAt});
}
}
if (!success) {
errorMessage(t('locationComponent.errorUnknownDeleteElement'));
@@ -417,6 +437,9 @@ export function LocationComponent(props: LocationComponentProps, ref: React.Ref<
success = useLocal
? await tauri.deleteLocationSubElement(subElementId!, currentEntityId, deletedAt)
: await apiDelete<boolean>('location/sub-element/delete', {subElementId: subElementId}, token, lang);
if (!useLocal && isDesktop && localSyncedBooks.find((sb: SyncedBook): boolean => sb.id === currentEntityId)) {
addToQueue('delete_location_subelement', {subElementId, bookId: currentEntityId, deletedAt});
}
}
if (!success) {
errorMessage(t('locationComponent.errorUnknownDeleteSubElement'));
@@ -447,6 +470,9 @@ export function LocationComponent(props: LocationComponentProps, ref: React.Ref<
success = useLocal
? await tauri.deleteLocationSection(sectionId, currentEntityId, deletedAt)
: await apiDelete<boolean>('location/delete', {locationId: sectionId}, token, lang);
if (!useLocal && isDesktop && localSyncedBooks.find((sb: SyncedBook): boolean => sb.id === currentEntityId)) {
addToQueue('delete_location', {locationId: sectionId, bookId: currentEntityId, deletedAt});
}
}
if (!success) {
errorMessage(t('locationComponent.errorUnknownDeleteSection'));
@@ -468,6 +494,9 @@ export function LocationComponent(props: LocationComponentProps, ref: React.Ref<
const response: boolean = useLocal
? await tauri.updateLocations(sections) as boolean
: await apiPost<boolean>(`location/update`, {locations: sections}, token, lang);
if (!useLocal && isDesktop && localSyncedBooks.find((sb: SyncedBook): boolean => sb.id === currentEntityId)) {
addToQueue('update_locations', {locations: sections});
}
if (!response) {
errorMessage(t('locationComponent.errorUnknownSave'));
return;