This commit is contained in:
@@ -7,14 +7,12 @@ import { useReaderStore } from '@/store/readerStore';
|
||||
import { useBookDataStore } from '@/store/bookDataStore';
|
||||
import { useTranslation } from '@/hooks/useTranslation';
|
||||
import { KOSyncClient, KoSyncProgress } from '@/services/sync/KOSyncClient';
|
||||
import { Book, BookFormat } from '@/types/book';
|
||||
import { Book, FIXED_LAYOUT_FORMATS } from '@/types/book';
|
||||
import { BookDoc } from '@/libs/document';
|
||||
import { debounce } from '@/utils/debounce';
|
||||
import { eventDispatcher } from '@/utils/event';
|
||||
import { getCFIFromXPointer, XCFI } from '@/utils/xcfi';
|
||||
|
||||
const PAGINATED_FORMATS: Set<BookFormat> = new Set(['PDF', 'CBZ']);
|
||||
|
||||
type SyncState = 'idle' | 'checking' | 'conflict' | 'synced' | 'error';
|
||||
|
||||
export interface SyncDetails {
|
||||
@@ -62,7 +60,7 @@ export const useKOSync = (bookKey: string) => {
|
||||
let progressStr: string;
|
||||
let percentage: number;
|
||||
|
||||
if (PAGINATED_FORMATS.has(currentBook.format)) {
|
||||
if (FIXED_LAYOUT_FORMATS.has(currentBook.format)) {
|
||||
const page = (currentProgress.section?.current ?? 0) + 1;
|
||||
const totalPages = currentProgress.section?.total ?? 0;
|
||||
progressStr = page.toString();
|
||||
@@ -223,12 +221,12 @@ export const useKOSync = (bookKey: string) => {
|
||||
const localTimestamp = bookData?.config?.updatedAt || book.updatedAt;
|
||||
const remoteIsNewer = remote.timestamp * 1000 > localTimestamp;
|
||||
|
||||
const localIdentifier = PAGINATED_FORMATS.has(book.format)
|
||||
const localIdentifier = FIXED_LAYOUT_FORMATS.has(book.format)
|
||||
? progress.section?.current.toString()
|
||||
: progress.location;
|
||||
const isLocalCFI = localIdentifier?.startsWith('epubcfi');
|
||||
|
||||
const remoteIdentifier = PAGINATED_FORMATS.has(book.format)
|
||||
const remoteIdentifier = FIXED_LAYOUT_FORMATS.has(book.format)
|
||||
? (parseInt(remote.progress, 10) - 1).toString()
|
||||
: remote.progress.startsWith('epubcfi')
|
||||
? remote.progress
|
||||
@@ -265,7 +263,7 @@ export const useKOSync = (bookKey: string) => {
|
||||
const applyRemoteProgress = async () => {
|
||||
const view = getView(bookKey);
|
||||
if (view && remote.progress) {
|
||||
if (PAGINATED_FORMATS.has(book.format)) {
|
||||
if (FIXED_LAYOUT_FORMATS.has(book.format)) {
|
||||
const pageToGo = parseInt(remote.progress, 10);
|
||||
if (!isNaN(pageToGo)) view.select(pageToGo - 1);
|
||||
} else {
|
||||
@@ -312,7 +310,7 @@ export const useKOSync = (bookKey: string) => {
|
||||
let remotePreview = '';
|
||||
const remotePercentage = remote.percentage || 0;
|
||||
|
||||
if (PAGINATED_FORMATS.has(book.format)) {
|
||||
if (FIXED_LAYOUT_FORMATS.has(book.format)) {
|
||||
const localPageInfo = progress.section;
|
||||
const localPercentage =
|
||||
localPageInfo && localPageInfo.total > 0
|
||||
@@ -427,7 +425,7 @@ export const useKOSync = (bookKey: string) => {
|
||||
const bookDoc = conflictDetails?.bookDoc;
|
||||
|
||||
if (view && remote?.progress && currentBook) {
|
||||
if (PAGINATED_FORMATS.has(currentBook.format)) {
|
||||
if (FIXED_LAYOUT_FORMATS.has(currentBook.format)) {
|
||||
const localTotalPages = getProgress(bookKey)?.section?.total ?? 0;
|
||||
const remotePage = parseInt(remote.progress, 10);
|
||||
const remotePercentage = remote.percentage || 0;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useCallback, useEffect, useRef } from 'react';
|
||||
import { useAuth } from '@/context/AuthContext';
|
||||
import { useSync } from '@/hooks/useSync';
|
||||
import { BookConfig } from '@/types/book';
|
||||
import { BookConfig, FIXED_LAYOUT_FORMATS } from '@/types/book';
|
||||
import { useBookDataStore } from '@/store/bookDataStore';
|
||||
import { useReaderStore } from '@/store/readerStore';
|
||||
import { useSettingsStore } from '@/store/settingsStore';
|
||||
@@ -11,15 +11,15 @@ import { CFI } from '@/libs/document';
|
||||
import { debounce } from '@/utils/debounce';
|
||||
import { eventDispatcher } from '@/utils/event';
|
||||
import { DEFAULT_BOOK_SEARCH_CONFIG, SYNC_PROGRESS_INTERVAL_SEC } from '@/services/constants';
|
||||
import { getCFIFromXPointer, getXPointerFromCFI } from '@/utils/xcfi';
|
||||
|
||||
export const useProgressSync = (bookKey: string) => {
|
||||
const _ = useTranslation();
|
||||
const { getConfig, setConfig } = useBookDataStore();
|
||||
const { getConfig, setConfig, getBookData } = useBookDataStore();
|
||||
const { getView, getProgress } = useReaderStore();
|
||||
const { settings } = useSettingsStore();
|
||||
const { syncedConfigs, syncConfigs } = useSync(bookKey);
|
||||
const { user } = useAuth();
|
||||
const view = getView(bookKey);
|
||||
const config = getConfig(bookKey);
|
||||
const progress = getProgress(bookKey);
|
||||
|
||||
@@ -29,24 +29,38 @@ export const useProgressSync = (bookKey: string) => {
|
||||
const pushConfig = (bookKey: string, config: BookConfig | null) => {
|
||||
if (!config || !user) return;
|
||||
const bookHash = bookKey.split('-')[0]!;
|
||||
const newConfig = { bookHash, ...config };
|
||||
const newConfig = { ...config, bookHash };
|
||||
const compressedConfig = JSON.parse(
|
||||
serializeConfig(newConfig, settings.globalViewSettings, DEFAULT_BOOK_SEARCH_CONFIG),
|
||||
);
|
||||
delete compressedConfig.booknotes;
|
||||
syncConfigs([compressedConfig], bookHash, 'push');
|
||||
};
|
||||
|
||||
const pullConfig = (bookKey: string) => {
|
||||
if (!user) return;
|
||||
const bookHash = bookKey.split('-')[0]!;
|
||||
syncConfigs([], bookHash, 'pull');
|
||||
};
|
||||
const syncConfig = () => {
|
||||
|
||||
const syncConfig = async () => {
|
||||
if (!configPulled.current) {
|
||||
pullConfig(bookKey);
|
||||
} else {
|
||||
const config = getConfig(bookKey);
|
||||
if (config && config.progress && config.progress[0] > 0) {
|
||||
const view = getView(bookKey);
|
||||
const book = getBookData(bookKey)?.book;
|
||||
if (config && view && book && config.progress && config.progress[0] > 0) {
|
||||
try {
|
||||
const content = view.renderer.getContents()[0];
|
||||
if (content && !FIXED_LAYOUT_FORMATS.has(book.format)) {
|
||||
const { doc, index } = content;
|
||||
const xpointerResult = await getXPointerFromCFI(config.location!, doc, index || 0);
|
||||
config.xpointer = xpointerResult.xpointer;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to convert CFI to XPointer', error);
|
||||
}
|
||||
pushConfig(bookKey, config);
|
||||
}
|
||||
}
|
||||
@@ -91,28 +105,51 @@ export const useProgressSync = (bookKey: string) => {
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [progress]);
|
||||
|
||||
// Pull: proccess the pulled progress
|
||||
useEffect(() => {
|
||||
if (!configPulled.current && syncedConfigs) {
|
||||
configPulled.current = true;
|
||||
const syncedConfig = syncedConfigs.filter((c) => c.bookHash === bookKey.split('-')[0])[0];
|
||||
if (syncedConfig) {
|
||||
const configCFI = config?.location;
|
||||
const syncedCFI = syncedConfig.location;
|
||||
setConfig(bookKey, syncedConfig);
|
||||
if (syncedCFI && configCFI) {
|
||||
if (CFI.compare(configCFI, syncedCFI) < 0) {
|
||||
if (view) {
|
||||
view.goTo(syncedCFI);
|
||||
eventDispatcher.dispatch('hint', {
|
||||
bookKey,
|
||||
message: _('Reading Progress Synced'),
|
||||
});
|
||||
}
|
||||
const applyRemoteProgress = useCallback(async () => {
|
||||
if (!syncedConfigs || syncedConfigs.length === 0) return;
|
||||
const syncedConfig = syncedConfigs.filter((c) => c.bookHash === bookKey.split('-')[0])[0];
|
||||
if (syncedConfig) {
|
||||
const configCFI = config?.location;
|
||||
let remoteCFILocation = syncedConfig.location;
|
||||
const xPointer = syncedConfig.xpointer;
|
||||
const bookData = getBookData(bookKey);
|
||||
const view = getView(bookKey);
|
||||
if (xPointer && view && bookData && bookData.bookDoc) {
|
||||
const content = view.renderer.getContents()[0];
|
||||
const candidateCFI = await getCFIFromXPointer(
|
||||
xPointer,
|
||||
content?.doc,
|
||||
content?.index,
|
||||
bookData.bookDoc,
|
||||
);
|
||||
if (CFI.compare(remoteCFILocation, candidateCFI) < 0) {
|
||||
remoteCFILocation = candidateCFI;
|
||||
}
|
||||
}
|
||||
setConfig(bookKey, syncedConfig);
|
||||
if (remoteCFILocation && configCFI) {
|
||||
if (CFI.compare(configCFI, remoteCFILocation) < 0) {
|
||||
if (view) {
|
||||
view.goTo(remoteCFILocation);
|
||||
eventDispatcher.dispatch('hint', {
|
||||
bookKey,
|
||||
message: _('Reading Progress Synced'),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [syncedConfigs]);
|
||||
}, [syncedConfigs, config?.location]);
|
||||
|
||||
// Pull: proccess the pulled progress
|
||||
useEffect(() => {
|
||||
if (!configPulled.current && syncedConfigs) {
|
||||
configPulled.current = true;
|
||||
applyRemoteProgress().catch((error) => {
|
||||
console.error('Failed to apply remote progress', error);
|
||||
});
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [applyRemoteProgress]);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user