use auto sync

This commit is contained in:
m
2026-08-29 09:31:53 +02:00
parent e43aa2d793
commit 479c8ab61c
5 changed files with 327 additions and 93 deletions
+106 -13
View File
@@ -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 };
}