mirror of
https://github.com/MODSetter/SurfSense.git
synced 2026-07-14 22:52:15 +02:00
feat: add renderer readiness signaling and update IPC channels for folder sync
This commit is contained in:
parent
1ef0d913e7
commit
5d6e3ffb7b
4 changed files with 48 additions and 20 deletions
|
|
@ -16,4 +16,5 @@ export const IPC_CHANNELS = {
|
||||||
FOLDER_SYNC_WATCHER_READY: 'folder-sync:watcher-ready',
|
FOLDER_SYNC_WATCHER_READY: 'folder-sync:watcher-ready',
|
||||||
FOLDER_SYNC_PAUSE: 'folder-sync:pause',
|
FOLDER_SYNC_PAUSE: 'folder-sync:pause',
|
||||||
FOLDER_SYNC_RESUME: 'folder-sync:resume',
|
FOLDER_SYNC_RESUME: 'folder-sync:resume',
|
||||||
|
FOLDER_SYNC_RENDERER_READY: 'folder-sync:renderer-ready',
|
||||||
} as const;
|
} as const;
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ import {
|
||||||
getWatcherStatus,
|
getWatcherStatus,
|
||||||
pauseWatcher,
|
pauseWatcher,
|
||||||
resumeWatcher,
|
resumeWatcher,
|
||||||
|
markRendererReady,
|
||||||
} from '../modules/folder-watcher';
|
} from '../modules/folder-watcher';
|
||||||
|
|
||||||
export function registerIpcHandlers(): void {
|
export function registerIpcHandlers(): void {
|
||||||
|
|
@ -44,4 +45,8 @@ export function registerIpcHandlers(): void {
|
||||||
ipcMain.handle(IPC_CHANNELS.FOLDER_SYNC_PAUSE, () => pauseWatcher());
|
ipcMain.handle(IPC_CHANNELS.FOLDER_SYNC_PAUSE, () => pauseWatcher());
|
||||||
|
|
||||||
ipcMain.handle(IPC_CHANNELS.FOLDER_SYNC_RESUME, () => resumeWatcher());
|
ipcMain.handle(IPC_CHANNELS.FOLDER_SYNC_RESUME, () => resumeWatcher());
|
||||||
|
|
||||||
|
ipcMain.handle(IPC_CHANNELS.FOLDER_SYNC_RENDERER_READY, () => {
|
||||||
|
markRendererReady();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ export interface WatchedFolderConfig {
|
||||||
name: string;
|
name: string;
|
||||||
excludePatterns: string[];
|
excludePatterns: string[];
|
||||||
fileExtensions: string[] | null;
|
fileExtensions: string[] | null;
|
||||||
connectorId: number;
|
rootFolderId: number | null;
|
||||||
searchSpaceId: number;
|
searchSpaceId: number;
|
||||||
active: boolean;
|
active: boolean;
|
||||||
}
|
}
|
||||||
|
|
@ -34,6 +34,25 @@ let watchers: Map<string, WatcherEntry> = new Map();
|
||||||
*/
|
*/
|
||||||
const mtimeMaps: Map<string, MtimeMap> = new Map();
|
const mtimeMaps: Map<string, MtimeMap> = new Map();
|
||||||
|
|
||||||
|
let rendererReady = false;
|
||||||
|
const pendingEvents: any[] = [];
|
||||||
|
|
||||||
|
export function markRendererReady() {
|
||||||
|
rendererReady = true;
|
||||||
|
for (const event of pendingEvents) {
|
||||||
|
sendToRenderer(IPC_CHANNELS.FOLDER_SYNC_FILE_CHANGED, event);
|
||||||
|
}
|
||||||
|
pendingEvents.length = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
function sendFileChangedEvent(data: any) {
|
||||||
|
if (rendererReady) {
|
||||||
|
sendToRenderer(IPC_CHANNELS.FOLDER_SYNC_FILE_CHANGED, data);
|
||||||
|
} else {
|
||||||
|
pendingEvents.push(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async function getStore() {
|
async function getStore() {
|
||||||
if (!store) {
|
if (!store) {
|
||||||
const { default: Store } = await import('electron-store');
|
const { default: Store } = await import('electron-store');
|
||||||
|
|
@ -83,7 +102,6 @@ function walkFolderMtimes(config: WatchedFolderConfig): MtimeMap {
|
||||||
for (const entry of entries) {
|
for (const entry of entries) {
|
||||||
const name = entry.name;
|
const name = entry.name;
|
||||||
|
|
||||||
// Skip dotfiles/dotdirs and excluded names
|
|
||||||
if (name.startsWith('.') || excludes.has(name)) continue;
|
if (name.startsWith('.') || excludes.has(name)) continue;
|
||||||
|
|
||||||
const full = path.join(dir, name);
|
const full = path.join(dir, name);
|
||||||
|
|
@ -131,7 +149,6 @@ async function startWatcher(config: WatchedFolderConfig) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load persisted mtime map into memory before starting the watcher
|
|
||||||
const ms = await getMtimeStore();
|
const ms = await getMtimeStore();
|
||||||
const storedMap: MtimeMap = ms.get(config.path) ?? {};
|
const storedMap: MtimeMap = ms.get(config.path) ?? {};
|
||||||
mtimeMaps.set(config.path, { ...storedMap });
|
mtimeMaps.set(config.path, { ...storedMap });
|
||||||
|
|
@ -156,45 +173,49 @@ async function startWatcher(config: WatchedFolderConfig) {
|
||||||
watcher.on('ready', () => {
|
watcher.on('ready', () => {
|
||||||
ready = true;
|
ready = true;
|
||||||
|
|
||||||
// Detect offline changes by diffing current filesystem against stored mtime map
|
|
||||||
const currentMap = walkFolderMtimes(config);
|
const currentMap = walkFolderMtimes(config);
|
||||||
const storedSnapshot = loadMtimeMap(config.path);
|
const storedSnapshot = loadMtimeMap(config.path);
|
||||||
const now = Date.now();
|
const now = Date.now();
|
||||||
|
|
||||||
|
// Track which files are unchanged so we can selectively update the mtime map
|
||||||
|
const unchangedMap: MtimeMap = {};
|
||||||
|
|
||||||
for (const [rel, currentMtime] of Object.entries(currentMap)) {
|
for (const [rel, currentMtime] of Object.entries(currentMap)) {
|
||||||
const storedMtime = storedSnapshot[rel];
|
const storedMtime = storedSnapshot[rel];
|
||||||
if (storedMtime === undefined) {
|
if (storedMtime === undefined) {
|
||||||
// New file added while app was closed
|
sendFileChangedEvent({
|
||||||
sendToRenderer(IPC_CHANNELS.FOLDER_SYNC_FILE_CHANGED, {
|
rootFolderId: config.rootFolderId,
|
||||||
connectorId: config.connectorId,
|
|
||||||
searchSpaceId: config.searchSpaceId,
|
searchSpaceId: config.searchSpaceId,
|
||||||
folderPath: config.path,
|
folderPath: config.path,
|
||||||
|
folderName: config.name,
|
||||||
relativePath: rel,
|
relativePath: rel,
|
||||||
fullPath: path.join(config.path, rel),
|
fullPath: path.join(config.path, rel),
|
||||||
action: 'add',
|
action: 'add',
|
||||||
timestamp: now,
|
timestamp: now,
|
||||||
});
|
});
|
||||||
} else if (Math.abs(currentMtime - storedMtime) >= MTIME_TOLERANCE_S * 1000) {
|
} else if (Math.abs(currentMtime - storedMtime) >= MTIME_TOLERANCE_S * 1000) {
|
||||||
// File modified while app was closed
|
sendFileChangedEvent({
|
||||||
sendToRenderer(IPC_CHANNELS.FOLDER_SYNC_FILE_CHANGED, {
|
rootFolderId: config.rootFolderId,
|
||||||
connectorId: config.connectorId,
|
|
||||||
searchSpaceId: config.searchSpaceId,
|
searchSpaceId: config.searchSpaceId,
|
||||||
folderPath: config.path,
|
folderPath: config.path,
|
||||||
|
folderName: config.name,
|
||||||
relativePath: rel,
|
relativePath: rel,
|
||||||
fullPath: path.join(config.path, rel),
|
fullPath: path.join(config.path, rel),
|
||||||
action: 'change',
|
action: 'change',
|
||||||
timestamp: now,
|
timestamp: now,
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
unchangedMap[rel] = currentMtime;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const rel of Object.keys(storedSnapshot)) {
|
for (const rel of Object.keys(storedSnapshot)) {
|
||||||
if (!(rel in currentMap)) {
|
if (!(rel in currentMap)) {
|
||||||
// File deleted while app was closed
|
sendFileChangedEvent({
|
||||||
sendToRenderer(IPC_CHANNELS.FOLDER_SYNC_FILE_CHANGED, {
|
rootFolderId: config.rootFolderId,
|
||||||
connectorId: config.connectorId,
|
|
||||||
searchSpaceId: config.searchSpaceId,
|
searchSpaceId: config.searchSpaceId,
|
||||||
folderPath: config.path,
|
folderPath: config.path,
|
||||||
|
folderName: config.name,
|
||||||
relativePath: rel,
|
relativePath: rel,
|
||||||
fullPath: path.join(config.path, rel),
|
fullPath: path.join(config.path, rel),
|
||||||
action: 'unlink',
|
action: 'unlink',
|
||||||
|
|
@ -203,12 +224,13 @@ async function startWatcher(config: WatchedFolderConfig) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Replace stored map with current filesystem state
|
// Only update the mtime map for unchanged files; changed files keep their
|
||||||
mtimeMaps.set(config.path, currentMap);
|
// stored mtime so they'll be re-detected if the app crashes before indexing.
|
||||||
|
mtimeMaps.set(config.path, unchangedMap);
|
||||||
persistMtimeMap(config.path);
|
persistMtimeMap(config.path);
|
||||||
|
|
||||||
sendToRenderer(IPC_CHANNELS.FOLDER_SYNC_WATCHER_READY, {
|
sendToRenderer(IPC_CHANNELS.FOLDER_SYNC_WATCHER_READY, {
|
||||||
connectorId: config.connectorId,
|
rootFolderId: config.rootFolderId,
|
||||||
folderPath: config.path,
|
folderPath: config.path,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
@ -226,7 +248,6 @@ async function startWatcher(config: WatchedFolderConfig) {
|
||||||
if (!config.fileExtensions.includes(ext)) return;
|
if (!config.fileExtensions.includes(ext)) return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Keep mtime map in sync with live changes
|
|
||||||
const map = mtimeMaps.get(config.path);
|
const map = mtimeMaps.get(config.path);
|
||||||
if (map) {
|
if (map) {
|
||||||
if (action === 'unlink') {
|
if (action === 'unlink') {
|
||||||
|
|
@ -241,10 +262,11 @@ async function startWatcher(config: WatchedFolderConfig) {
|
||||||
persistMtimeMap(config.path);
|
persistMtimeMap(config.path);
|
||||||
}
|
}
|
||||||
|
|
||||||
sendToRenderer(IPC_CHANNELS.FOLDER_SYNC_FILE_CHANGED, {
|
sendFileChangedEvent({
|
||||||
connectorId: config.connectorId,
|
rootFolderId: config.rootFolderId,
|
||||||
searchSpaceId: config.searchSpaceId,
|
searchSpaceId: config.searchSpaceId,
|
||||||
folderPath: config.path,
|
folderPath: config.path,
|
||||||
|
folderName: config.name,
|
||||||
relativePath,
|
relativePath,
|
||||||
fullPath: filePath,
|
fullPath: filePath,
|
||||||
action,
|
action,
|
||||||
|
|
@ -311,7 +333,6 @@ export async function removeWatchedFolder(
|
||||||
|
|
||||||
stopWatcher(folderPath);
|
stopWatcher(folderPath);
|
||||||
|
|
||||||
// Clean up persisted mtime map for this folder
|
|
||||||
mtimeMaps.delete(folderPath);
|
mtimeMaps.delete(folderPath);
|
||||||
const ms = await getMtimeStore();
|
const ms = await getMtimeStore();
|
||||||
ms.delete(folderPath);
|
ms.delete(folderPath);
|
||||||
|
|
|
||||||
|
|
@ -44,4 +44,5 @@ contextBridge.exposeInMainWorld('electronAPI', {
|
||||||
},
|
},
|
||||||
pauseWatcher: () => ipcRenderer.invoke(IPC_CHANNELS.FOLDER_SYNC_PAUSE),
|
pauseWatcher: () => ipcRenderer.invoke(IPC_CHANNELS.FOLDER_SYNC_PAUSE),
|
||||||
resumeWatcher: () => ipcRenderer.invoke(IPC_CHANNELS.FOLDER_SYNC_RESUME),
|
resumeWatcher: () => ipcRenderer.invoke(IPC_CHANNELS.FOLDER_SYNC_RESUME),
|
||||||
|
signalRendererReady: () => ipcRenderer.invoke(IPC_CHANNELS.FOLDER_SYNC_RENDERER_READY),
|
||||||
});
|
});
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue