Add deletedAt timestamps to delete operations for better audit tracking

- Updated delete methods across hooks and components to include `deletedAt: System.timeStampInSeconds()`.
- Refactored synchronized delete logic to pass `deletedAt` for both offline and online states.
- Improved synchronization workflows to include `deletedAt` in server and IPC requests.
- Enhanced destructuring patterns for cleaner and more consistent request data.
This commit is contained in:
natreex
2026-02-09 17:12:03 -05:00
parent 209dc6f85a
commit 49bb6e06f5
14 changed files with 146 additions and 92 deletions

View File

@@ -1,5 +1,5 @@
'use client';
import {useContext, useEffect, useState} from 'react';
import {useCallback, useContext, useEffect, useRef, useState} from 'react';
import {BookContext} from "@/context/BookContext";
import {ChapterProps} from "@/lib/models/Chapter";
import {ChapterContext} from '@/context/ChapterContext';
@@ -67,59 +67,57 @@ const messagesMap = {
function AutoSyncOnReconnect() {
const {offlineMode} = useContext(OfflineContext);
const {syncAllToServer: syncAllBooksToServer, refreshBooks, booksToSyncToServer} = useSyncBooks();
const {syncAllToServer: syncAllSeriesToServer, refreshSeries, seriesToSyncToServer} = useSyncSeries();
const [pendingSync, setPendingSync] = useState<boolean>(false);
const [isRefreshing, setIsRefreshing] = useState<boolean>(false);
const {session} = useContext(SessionContext);
const {syncAllToServer: syncAllBooksToServer, syncAllFromServer: syncAllBooksFromServer, refreshBooks, booksToSyncToServer, booksToSyncFromServer} = useSyncBooks();
const {syncAllToServer: syncAllSeriesToServer, syncAllFromServer: syncAllSeriesFromServer, refreshSeries, seriesToSyncToServer, seriesToSyncFromServer} = useSyncSeries();
const isSyncingRef = useRef<boolean>(false);
const hasRefreshedRef = useRef<boolean>(false);
const saveLastOnlineTimestamp = (): void => {
const saveLastOnlineTimestamp = useCallback((): void => {
const timestamp: number = Math.floor(Date.now() / 1000);
localStorage.setItem('lastOnlineTimestamp', timestamp.toString());
};
}, []);
// Refresh sync data when online + authenticated + DB ready
useEffect((): void => {
if (!offlineMode.isOffline) {
setPendingSync(true);
setIsRefreshing(true);
Promise.all([refreshBooks(), refreshSeries()]).then(() => {
setIsRefreshing(false);
if (!offlineMode.isOffline && session.isConnected && offlineMode.isDatabaseInitialized) {
hasRefreshedRef.current = true;
Promise.all([refreshBooks(), refreshSeries()]);
}
}, [offlineMode.isOffline, session.isConnected, offlineMode.isDatabaseInitialized]);
// Auto-sync when diffs become available (reactive, no flags)
useEffect((): void => {
if (offlineMode.isOffline || !session.isConnected || isSyncingRef.current || !hasRefreshedRef.current) return;
const syncPromises: Promise<void>[] = [];
if (booksToSyncToServer.length > 0) syncPromises.push(syncAllBooksToServer());
if (booksToSyncFromServer.length > 0) syncPromises.push(syncAllBooksFromServer());
if (seriesToSyncToServer.length > 0) syncPromises.push(syncAllSeriesToServer());
if (seriesToSyncFromServer.length > 0) syncPromises.push(syncAllSeriesFromServer());
if (syncPromises.length > 0) {
isSyncingRef.current = true;
Promise.all(syncPromises).then((): void => {
saveLastOnlineTimestamp();
isSyncingRef.current = false;
}).catch((): void => {
isSyncingRef.current = false;
});
}
}, [offlineMode.isOffline]);
useEffect((): void => {
if (pendingSync && !isRefreshing) {
const syncPromises: Promise<void>[] = [];
if (booksToSyncToServer.length > 0) {
syncPromises.push(syncAllBooksToServer());
}
if (seriesToSyncToServer.length > 0) {
syncPromises.push(syncAllSeriesToServer());
}
if (syncPromises.length > 0) {
Promise.all(syncPromises).then(() => {
saveLastOnlineTimestamp();
});
} else {
saveLastOnlineTimestamp();
}
setPendingSync(false);
}
}, [booksToSyncToServer, seriesToSyncToServer, pendingSync, isRefreshing]);
}, [booksToSyncToServer, booksToSyncFromServer, seriesToSyncToServer, seriesToSyncFromServer]);
// Update lastOnlineTimestamp every 5 minutes while online
useEffect((): (() => void) | void => {
if (!offlineMode.isOffline) {
if (!offlineMode.isOffline && session.isConnected) {
const intervalId: NodeJS.Timeout = setInterval((): void => {
saveLastOnlineTimestamp();
}, 5 * 60 * 1000); // 5 minutes
}, 5 * 60 * 1000);
return (): void => clearInterval(intervalId);
}
}, [offlineMode.isOffline]);
}, [offlineMode.isOffline, session.isConnected, saveLastOnlineTimestamp]);
return null;
}
@@ -284,8 +282,6 @@ function ScribeContent() {
useEffect((): void => {
if (session.isConnected) {
refreshBooks().then()
refreshSeries().then()
setIsTermsAccepted(session.user?.termsAccepted ?? false);
setHomeStepsGuide(User.guideTourDone(session.user?.guideTour ?? [], 'home-basic'));
setIsLoading(false);