Files
readest/apps/readest-app/src/app/reader/hooks/useProgressSync.ts
T
Huang Xin dfa0468d24 release: hot fix version 0.9.21 (#551)
* sync: update partial config to sync more actively

* rtl: make the order of go back/forward buttons consistent

* fix: wheel events conflict with touchpad scroll on macOS

* release: hot fix version 0.9.21
2025-03-09 20:22:47 +01:00

128 lines
4.3 KiB
TypeScript

import { useEffect, useRef } from 'react';
import { useAuth } from '@/context/AuthContext';
import { useSync } from '@/hooks/useSync';
import { BookConfig } from '@/types/book';
import { useBookDataStore } from '@/store/bookDataStore';
import { useReaderStore } from '@/store/readerStore';
import { useSettingsStore } from '@/store/settingsStore';
import { useTranslation } from '@/hooks/useTranslation';
import { serializeConfig } from '@/utils/serializer';
import { CFI } from '@/libs/document';
import { eventDispatcher } from '@/utils/event';
import { DEFAULT_BOOK_SEARCH_CONFIG, SYNC_PROGRESS_INTERVAL_SEC } from '@/services/constants';
export const useProgressSync = (bookKey: string) => {
const _ = useTranslation();
const { getConfig, setConfig } = 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);
// flag to prevent accidental sync without first pulling the config
const configSynced = useRef(false);
const firstPulled = useRef(false);
const pushConfig = (bookKey: string, config: BookConfig | null) => {
if (!config || !user) return;
const bookHash = bookKey.split('-')[0]!;
const newConfig = { bookHash, ...config };
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 = () => {
if (!configSynced.current) {
pullConfig(bookKey);
} else {
if (config && config.progress && config.progress[0] > 0) {
pushConfig(bookKey, config);
}
}
};
const handleSyncBookProgress = (event: CustomEvent) => {
const { bookKey: syncBookKey } = event.detail;
if (syncBookKey === bookKey) {
syncConfig();
}
};
useEffect(() => {
eventDispatcher.on('sync-book-progress', handleSyncBookProgress);
return () => {
eventDispatcher.off('sync-book-progress', handleSyncBookProgress);
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [bookKey]);
useEffect(() => {
if (!progress || firstPulled.current) return;
firstPulled.current = true;
pullConfig(bookKey);
return () => {
syncConfig();
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [progress]);
const lastProgressSyncTime = useRef<number>(0);
const syncTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);
useEffect(() => {
if (!config?.location || !user) return;
const now = Date.now();
const timeSinceLastSync = now - lastProgressSyncTime.current;
if (timeSinceLastSync > SYNC_PROGRESS_INTERVAL_SEC * 1000) {
lastProgressSyncTime.current = now;
syncConfig();
} else {
if (syncTimeoutRef.current) clearTimeout(syncTimeoutRef.current);
syncTimeoutRef.current = setTimeout(
() => {
lastProgressSyncTime.current = Date.now();
syncTimeoutRef.current = null;
syncConfig();
},
SYNC_PROGRESS_INTERVAL_SEC * 1000 - timeSinceLastSync,
);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [config]);
// sync progress once when the book is opened
useEffect(() => {
if (!configSynced.current && syncedConfigs) {
configSynced.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'),
});
}
}
}
}
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [syncedConfigs]);
};