use auto sync
This commit is contained in:
+106
-13
@@ -1,8 +1,22 @@
|
||||
import { useState, useEffect, useCallback } from 'react';
|
||||
import { useState, useEffect, useCallback, useSyncExternalStore } from 'react';
|
||||
import { createMMKV } from 'react-native-mmkv';
|
||||
import { fileStore } from '../services/fileStore';
|
||||
|
||||
export type SyncProgress = {
|
||||
files: Array<{ id: string; name: string }>;
|
||||
currentIndex: number;
|
||||
} | null;
|
||||
|
||||
const syncStateStorage = createMMKV({ id: 'vaultdrop-sync-state' });
|
||||
const PROGRESS_KEY = 'sync_progress';
|
||||
|
||||
let syncRunning = false;
|
||||
let cancelRequested = false;
|
||||
const listeners = new Set<() => void>();
|
||||
|
||||
function notify() {
|
||||
listeners.forEach((l) => l());
|
||||
}
|
||||
|
||||
export function getIsSyncing(): boolean {
|
||||
return syncStateStorage.getString('is_syncing') === 'true';
|
||||
@@ -10,24 +24,98 @@ export function getIsSyncing(): boolean {
|
||||
|
||||
export function setIsSyncing(value: boolean) {
|
||||
syncStateStorage.set('is_syncing', value ? 'true' : 'false');
|
||||
notify();
|
||||
}
|
||||
|
||||
export function getSyncProgress(): SyncProgress {
|
||||
return parseProgress(syncStateStorage.getString(PROGRESS_KEY));
|
||||
}
|
||||
|
||||
function parseProgress(raw: string | undefined): SyncProgress {
|
||||
if (!raw) return null;
|
||||
try {
|
||||
const parsed = JSON.parse(raw) as { files: Array<{ id: string; name: string }>; currentIndex: number };
|
||||
if (!parsed || !Array.isArray(parsed.files)) return null;
|
||||
return parsed;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export function setSyncProgress(value: SyncProgress) {
|
||||
if (value === null) {
|
||||
syncStateStorage.remove(PROGRESS_KEY);
|
||||
} else {
|
||||
syncStateStorage.set(PROGRESS_KEY, JSON.stringify(value));
|
||||
}
|
||||
notify();
|
||||
}
|
||||
|
||||
export function isSyncLoopRunning(): boolean {
|
||||
return syncRunning;
|
||||
}
|
||||
|
||||
export function setSyncLoopRunning(value: boolean) {
|
||||
syncRunning = value;
|
||||
}
|
||||
|
||||
export function requestSyncCancel() {
|
||||
cancelRequested = true;
|
||||
}
|
||||
|
||||
export function isSyncCancelRequested(): boolean {
|
||||
return cancelRequested;
|
||||
}
|
||||
|
||||
export function consumeSyncCancel(): boolean {
|
||||
const value = cancelRequested;
|
||||
cancelRequested = false;
|
||||
return value;
|
||||
}
|
||||
|
||||
export function resetSyncState() {
|
||||
syncStateStorage.remove('is_syncing');
|
||||
syncStateStorage.remove(PROGRESS_KEY);
|
||||
syncRunning = false;
|
||||
cancelRequested = false;
|
||||
notify();
|
||||
}
|
||||
|
||||
function subscribe(listener: () => void): () => void {
|
||||
listeners.add(listener);
|
||||
return () => listeners.delete(listener);
|
||||
}
|
||||
|
||||
let cachedIsSyncing = getIsSyncing();
|
||||
let lastIsSyncingRaw: string | undefined;
|
||||
|
||||
function getIsSyncingSnapshot(): boolean {
|
||||
const raw = syncStateStorage.getString('is_syncing');
|
||||
if (raw !== lastIsSyncingRaw) {
|
||||
lastIsSyncingRaw = raw;
|
||||
cachedIsSyncing = raw === 'true';
|
||||
}
|
||||
return cachedIsSyncing;
|
||||
}
|
||||
|
||||
let cachedProgress = getSyncProgress();
|
||||
let lastProgressRaw: string | undefined;
|
||||
|
||||
function getSyncProgressSnapshot(): SyncProgress {
|
||||
const raw = syncStateStorage.getString(PROGRESS_KEY);
|
||||
if (raw !== lastProgressRaw) {
|
||||
lastProgressRaw = raw;
|
||||
cachedProgress = parseProgress(raw);
|
||||
}
|
||||
return cachedProgress;
|
||||
}
|
||||
|
||||
export function useSyncQueue() {
|
||||
const isSyncing = useSyncExternalStore(subscribe, getIsSyncingSnapshot, getIsSyncingSnapshot);
|
||||
const [pendingCount, setPendingCount] = useState(0);
|
||||
const [isSyncing, setIsSyncingState] = useState(() => getIsSyncing());
|
||||
|
||||
const refresh = useCallback(() => {
|
||||
const registry = fileStore.getAllLocal();
|
||||
let count = 0;
|
||||
|
||||
for (const entry of registry) {
|
||||
if (entry.backendId || !entry.localUri) continue;
|
||||
if (entry.syncStatus !== 'local' && entry.syncStatus !== 'error') continue;
|
||||
count++;
|
||||
}
|
||||
|
||||
setPendingCount(count);
|
||||
setIsSyncingState(getIsSyncing());
|
||||
setPendingCount(fileStore.countPendingSync());
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -38,3 +126,8 @@ export function useSyncQueue() {
|
||||
|
||||
return { pendingCount, isSyncing, refresh };
|
||||
}
|
||||
|
||||
export function useSyncProgress() {
|
||||
const syncProgress = useSyncExternalStore(subscribe, getSyncProgressSnapshot, getSyncProgressSnapshot);
|
||||
return { syncProgress };
|
||||
}
|
||||
Reference in New Issue
Block a user