Add chokidar watcher settings.
This commit is contained in:
@@ -1,13 +1,15 @@
|
|||||||
import { ipcMain } from "electron";
|
import { ipcMain } from "electron";
|
||||||
import ipcTypes from "../../util/ipcTypes.json";
|
|
||||||
import log from "electron-log/main";
|
import log from "electron-log/main";
|
||||||
import { ipcMainHandleAuthStateChanged } from "./ipcMainHandler.user";
|
import ipcTypes from "../../util/ipcTypes.json";
|
||||||
|
import { StartWatcher } from "../watcher/watcher";
|
||||||
import {
|
import {
|
||||||
SettingsWatchedFilePathsGet,
|
|
||||||
SettingsWatchedFilePathsAdd,
|
SettingsWatchedFilePathsAdd,
|
||||||
|
SettingsWatchedFilePathsGet,
|
||||||
|
SettingsWatchedFilePathsRemove,
|
||||||
} from "./ipcMainHandler.settings";
|
} from "./ipcMainHandler.settings";
|
||||||
// Log all IPC messages and their payloads
|
import { ipcMainHandleAuthStateChanged } from "./ipcMainHandler.user";
|
||||||
|
|
||||||
|
// Log all IPC messages and their payloads
|
||||||
const logIpcMessages = () => {
|
const logIpcMessages = () => {
|
||||||
// Get all message types from ipcTypes.toMain
|
// Get all message types from ipcTypes.toMain
|
||||||
Object.keys(ipcTypes.toMain).forEach((key) => {
|
Object.keys(ipcTypes.toMain).forEach((key) => {
|
||||||
@@ -37,8 +39,10 @@ ipcMain.on(ipcTypes.toMain.test, (payload: any) =>
|
|||||||
console.log("** Verify that ipcMain is loaded and working.", payload)
|
console.log("** Verify that ipcMain is loaded and working.", payload)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
//Auth handler
|
||||||
ipcMain.on(ipcTypes.toMain.authStateChanged, ipcMainHandleAuthStateChanged);
|
ipcMain.on(ipcTypes.toMain.authStateChanged, ipcMainHandleAuthStateChanged);
|
||||||
|
|
||||||
|
//Settings Handlers
|
||||||
ipcMain.handle(
|
ipcMain.handle(
|
||||||
ipcTypes.toMain.settings.filepaths.get,
|
ipcTypes.toMain.settings.filepaths.get,
|
||||||
SettingsWatchedFilePathsGet
|
SettingsWatchedFilePathsGet
|
||||||
@@ -47,4 +51,14 @@ ipcMain.handle(
|
|||||||
ipcTypes.toMain.settings.filepaths.add,
|
ipcTypes.toMain.settings.filepaths.add,
|
||||||
SettingsWatchedFilePathsAdd
|
SettingsWatchedFilePathsAdd
|
||||||
);
|
);
|
||||||
|
ipcMain.handle(
|
||||||
|
ipcTypes.toMain.settings.filepaths.remove,
|
||||||
|
SettingsWatchedFilePathsRemove
|
||||||
|
);
|
||||||
|
|
||||||
|
//Watcher Handlers
|
||||||
|
ipcMain.on(ipcTypes.toMain.watcher.start, () => {
|
||||||
|
StartWatcher();
|
||||||
|
});
|
||||||
|
|
||||||
logIpcMessages();
|
logIpcMessages();
|
||||||
|
|||||||
@@ -16,16 +16,31 @@ const SettingsWatchedFilePathsAdd = async (event: IpcMainInvokeEvent) => {
|
|||||||
if (!result.canceled) {
|
if (!result.canceled) {
|
||||||
Store.set(
|
Store.set(
|
||||||
"settings.filepaths",
|
"settings.filepaths",
|
||||||
_.union(result.filePaths, Store.get("filepaths"))
|
_.union(result.filePaths, Store.get("settings.filepaths"))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return Store.get("settings.filepaths");
|
return Store.get("settings.filepaths");
|
||||||
};
|
};
|
||||||
|
const SettingsWatchedFilePathsRemove = async (
|
||||||
|
event: IpcMainInvokeEvent,
|
||||||
|
path: string
|
||||||
|
) => {
|
||||||
|
Store.set(
|
||||||
|
"settings.filepaths",
|
||||||
|
_.without(Store.get("settings.filepaths"), path)
|
||||||
|
);
|
||||||
|
|
||||||
|
return Store.get("settings.filepaths");
|
||||||
|
};
|
||||||
|
|
||||||
const SettingsWatchedFilePathsGet = async (event: IpcMainInvokeEvent) => {
|
const SettingsWatchedFilePathsGet = async (event: IpcMainInvokeEvent) => {
|
||||||
const filepaths = Store.get("settings.filepaths");
|
const filepaths = Store.get("settings.filepaths");
|
||||||
return filepaths;
|
return filepaths;
|
||||||
};
|
};
|
||||||
|
|
||||||
export { SettingsWatchedFilePathsAdd, SettingsWatchedFilePathsGet };
|
export {
|
||||||
|
SettingsWatchedFilePathsAdd,
|
||||||
|
SettingsWatchedFilePathsGet,
|
||||||
|
SettingsWatchedFilePathsRemove,
|
||||||
|
};
|
||||||
|
|||||||
110
src/main/watcher/watcher.ts
Normal file
110
src/main/watcher/watcher.ts
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
import chokidar, { FSWatcher } from "chokidar";
|
||||||
|
import { Notification } from "electron";
|
||||||
|
import log from "electron-log/main";
|
||||||
|
import path from "path";
|
||||||
|
import errorTypeCheck from "../../util/errorTypeCheck";
|
||||||
|
import store from "../store/store";
|
||||||
|
|
||||||
|
var watcher: FSWatcher;
|
||||||
|
|
||||||
|
async function StartWatcher(): Promise<boolean> {
|
||||||
|
const filePaths = store.get("settings.filepaths") || [];
|
||||||
|
log.info("Use polling? ", store.get("settings.polling").enabled);
|
||||||
|
if (filePaths.length === 0) {
|
||||||
|
new Notification({
|
||||||
|
//TODO: Add Translations
|
||||||
|
title: "Watcher cannot start",
|
||||||
|
body: "Please set the appropriate file paths and try again.",
|
||||||
|
}).show();
|
||||||
|
log.warn("Cannot start watcher. No file paths set.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (watcher) {
|
||||||
|
try {
|
||||||
|
log.info("Trying to close watcher - it already existed.");
|
||||||
|
await watcher.close();
|
||||||
|
|
||||||
|
log.info("Watcher closed successfully!");
|
||||||
|
} catch (error) {
|
||||||
|
log.error("Error trying to close Watcher.", error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
watcher = chokidar.watch(filePaths, {
|
||||||
|
ignored: (filepath, stats) => {
|
||||||
|
const p = path.parse(filepath);
|
||||||
|
|
||||||
|
return !stats?.isFile() && p.ext !== "" && p.ext.toUpperCase() !== ".ENV";
|
||||||
|
},
|
||||||
|
usePolling: store.get("settings.polling").enabled || false,
|
||||||
|
interval: store.get("settings.polling").pollingInterval || 1000,
|
||||||
|
persistent: true,
|
||||||
|
ignoreInitial: true,
|
||||||
|
awaitWriteFinish: {
|
||||||
|
pollInterval: 500,
|
||||||
|
stabilityThreshold: 2000,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
watcher
|
||||||
|
.on("add", async function (path) {
|
||||||
|
console.log("File", path, "has been added");
|
||||||
|
HandleNewFile(path);
|
||||||
|
})
|
||||||
|
// .on("addDir", function (path) {
|
||||||
|
// console.log("Directory", path, "has been added");
|
||||||
|
// })
|
||||||
|
.on("change", async function (path) {
|
||||||
|
console.log("File", path, "has been changed");
|
||||||
|
HandleNewFile(path);
|
||||||
|
})
|
||||||
|
// .on("unlink", function (path) {
|
||||||
|
// console.log("File", path, "has been removed");
|
||||||
|
// })
|
||||||
|
// .on("unlinkDir", function (path) {
|
||||||
|
// console.log("Directory", path, "has been removed");
|
||||||
|
// })
|
||||||
|
.on("error", function (error) {
|
||||||
|
log.error("Error in Watcher", errorTypeCheck(error));
|
||||||
|
})
|
||||||
|
.on("ready", onWatcherReady)
|
||||||
|
.on("raw", function (event, path, details) {
|
||||||
|
// This event should be triggered everytime something happens.
|
||||||
|
// console.log("Raw event info:", event, path, details);
|
||||||
|
});
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function onWatcherReady() {
|
||||||
|
log.info("Watcher ready!");
|
||||||
|
// const b = BrowserWindow.getAllWindows()[0];
|
||||||
|
// b.webContents.send(ipcTypes.default.fileWatcher.toRenderer.startSuccess);
|
||||||
|
new Notification({
|
||||||
|
title: "Watcher Started",
|
||||||
|
body: "Newly exported estimates will be automatically uploaded.",
|
||||||
|
}).show();
|
||||||
|
log.info("Confirmed watched paths:", watcher.getWatched());
|
||||||
|
}
|
||||||
|
|
||||||
|
async function StopWatcher(): Promise<boolean> {
|
||||||
|
if (watcher) {
|
||||||
|
await watcher.close();
|
||||||
|
log.info("Watcher stopped.");
|
||||||
|
|
||||||
|
new Notification({
|
||||||
|
title: "RPS Watcher Stopped",
|
||||||
|
body: "Estimates will not be automatically uploaded.",
|
||||||
|
}).show();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function HandleNewFile(path) {
|
||||||
|
//await ImportJob(path);
|
||||||
|
log.log("Received a new file", path);
|
||||||
|
}
|
||||||
|
|
||||||
|
export { StartWatcher, StopWatcher, watcher };
|
||||||
@@ -1,7 +1,8 @@
|
|||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import ipcTypes from "../../../../util/ipcTypes.json";
|
import ipcTypes from "../../../../util/ipcTypes.json";
|
||||||
import { Button } from "antd";
|
import { Button, Space } from "antd";
|
||||||
|
import { DeleteFilled } from "@ant-design/icons";
|
||||||
|
|
||||||
const SettingsWatchedPaths: React.FC = () => {
|
const SettingsWatchedPaths: React.FC = () => {
|
||||||
const [watchedPaths, setWatchedPaths] = useState<string[]>([]);
|
const [watchedPaths, setWatchedPaths] = useState<string[]>([]);
|
||||||
@@ -23,10 +24,30 @@ const SettingsWatchedPaths: React.FC = () => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleRemovePath = (path: string) => {
|
||||||
|
window.electron.ipcRenderer
|
||||||
|
.invoke(ipcTypes.toMain.settings.filepaths.remove, path)
|
||||||
|
.then((paths: string[]) => {
|
||||||
|
setWatchedPaths(paths);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<div>Currently Watched paths</div>
|
<div>Currently Watched paths</div>
|
||||||
<ul>{watchedPaths?.map((path, index) => <li key={index}>{path}</li>)}</ul>
|
<ul>
|
||||||
|
{watchedPaths?.map((path, index) => (
|
||||||
|
<li key={index}>
|
||||||
|
<Space>
|
||||||
|
{path}
|
||||||
|
<Button
|
||||||
|
icon={<DeleteFilled />}
|
||||||
|
onClick={() => handleRemovePath(path)}
|
||||||
|
/>
|
||||||
|
</Space>
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
<Button onClick={handleAddPath}>{t("settings.actions.addpath")}</Button>
|
<Button onClick={handleAddPath}>{t("settings.actions.addpath")}</Button>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
16
src/renderer/src/components/Settings/Settings.Watcher.tsx
Normal file
16
src/renderer/src/components/Settings/Settings.Watcher.tsx
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import { Button } from "antd";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
import ipcTypes from "../../../../util/ipcTypes.json";
|
||||||
|
|
||||||
|
const SettingsWatcher: React.FC = () => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
const handleStart = () => {
|
||||||
|
window.electron.ipcRenderer.send(ipcTypes.toMain.watcher.start);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Button onClick={handleStart}>{t("settings.actions.startwatcher")}</Button>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
export default SettingsWatcher;
|
||||||
@@ -1,9 +1,11 @@
|
|||||||
import SettingsWatchedPaths from "./Settings.WatchedPaths";
|
import SettingsWatchedPaths from "./Settings.WatchedPaths";
|
||||||
|
import SettingsWatcher from "./Settings.Watcher";
|
||||||
|
|
||||||
const Settings: React.FC = () => {
|
const Settings: React.FC = () => {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<SettingsWatchedPaths />
|
<SettingsWatchedPaths />
|
||||||
|
<SettingsWatcher />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -2,7 +2,9 @@ function errorTypeCheck(passedError: any): ParsedError {
|
|||||||
const errorMessage =
|
const errorMessage =
|
||||||
passedError instanceof Error ? passedError.message : String(passedError);
|
passedError instanceof Error ? passedError.message : String(passedError);
|
||||||
const errorStack =
|
const errorStack =
|
||||||
passedError instanceof Error ? passedError.stack : "unknown";
|
passedError instanceof Error
|
||||||
|
? (passedError.stack ?? "")
|
||||||
|
: String(passedError);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
message: errorMessage,
|
message: errorMessage,
|
||||||
|
|||||||
@@ -2,10 +2,15 @@
|
|||||||
"toMain": {
|
"toMain": {
|
||||||
"test": "toMain_test",
|
"test": "toMain_test",
|
||||||
"authStateChanged": "toMain_authStateChanged",
|
"authStateChanged": "toMain_authStateChanged",
|
||||||
|
"watcher": {
|
||||||
|
"start": "toMain_watcher_start",
|
||||||
|
"stop": "toMain_watcher_stop"
|
||||||
|
},
|
||||||
"settings": {
|
"settings": {
|
||||||
"filepaths": {
|
"filepaths": {
|
||||||
"get": "toMain_settings_filepaths_get",
|
"get": "toMain_settings_filepaths_get",
|
||||||
"add": "toMain_settings_filepaths_add"
|
"add": "toMain_settings_filepaths_add",
|
||||||
|
"remove": "toMain_settings_filepaths_remove"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user