Add watcher polling settings.
This commit is contained in:
@@ -8,6 +8,8 @@ import {
|
|||||||
SettingsWatchedFilePathsAdd,
|
SettingsWatchedFilePathsAdd,
|
||||||
SettingsWatchedFilePathsGet,
|
SettingsWatchedFilePathsGet,
|
||||||
SettingsWatchedFilePathsRemove,
|
SettingsWatchedFilePathsRemove,
|
||||||
|
SettingsWatcherPollingGet,
|
||||||
|
SettingsWatcherPollingSet,
|
||||||
} from "./ipcMainHandler.settings";
|
} from "./ipcMainHandler.settings";
|
||||||
import { ipcMainHandleAuthStateChanged } from "./ipcMainHandler.user";
|
import { ipcMainHandleAuthStateChanged } from "./ipcMainHandler.user";
|
||||||
import { autoUpdater } from "electron-updater";
|
import { autoUpdater } from "electron-updater";
|
||||||
@@ -78,6 +80,14 @@ ipcMain.handle(
|
|||||||
ipcTypes.toMain.settings.filepaths.remove,
|
ipcTypes.toMain.settings.filepaths.remove,
|
||||||
SettingsWatchedFilePathsRemove,
|
SettingsWatchedFilePathsRemove,
|
||||||
);
|
);
|
||||||
|
ipcMain.handle(
|
||||||
|
ipcTypes.toMain.settings.watcher.getpolling,
|
||||||
|
SettingsWatcherPollingGet,
|
||||||
|
);
|
||||||
|
ipcMain.handle(
|
||||||
|
ipcTypes.toMain.settings.watcher.setpolling,
|
||||||
|
SettingsWatcherPollingSet,
|
||||||
|
);
|
||||||
|
|
||||||
//Watcher Handlers
|
//Watcher Handlers
|
||||||
ipcMain.on(ipcTypes.toMain.watcher.start, () => {
|
ipcMain.on(ipcTypes.toMain.watcher.start, () => {
|
||||||
|
|||||||
@@ -2,7 +2,13 @@ import { BrowserWindow, dialog, IpcMainInvokeEvent } from "electron";
|
|||||||
import log from "electron-log/main";
|
import log from "electron-log/main";
|
||||||
import _ from "lodash";
|
import _ from "lodash";
|
||||||
import Store from "../store/store";
|
import Store from "../store/store";
|
||||||
import { addWatcherPath, removeWatcherPath, watcher } from "../watcher/watcher";
|
import {
|
||||||
|
addWatcherPath,
|
||||||
|
removeWatcherPath,
|
||||||
|
StartWatcher,
|
||||||
|
StopWatcher,
|
||||||
|
watcher,
|
||||||
|
} from "../watcher/watcher";
|
||||||
|
|
||||||
const SettingsWatchedFilePathsAdd = async (): Promise<string[]> => {
|
const SettingsWatchedFilePathsAdd = async (): Promise<string[]> => {
|
||||||
const mainWindow = BrowserWindow.getAllWindows()[0]; //TODO: Filter to only main window once a proper key has been set.
|
const mainWindow = BrowserWindow.getAllWindows()[0]; //TODO: Filter to only main window once a proper key has been set.
|
||||||
@@ -41,8 +47,39 @@ const SettingsWatchedFilePathsGet = async (): Promise<string[]> => {
|
|||||||
return filepaths;
|
return filepaths;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const SettingsWatcherPollingGet = async (): Promise<{
|
||||||
|
enabled: boolean;
|
||||||
|
interval: number;
|
||||||
|
}> => {
|
||||||
|
const pollingEnabled: { enabled: boolean; interval: number } =
|
||||||
|
Store.get("settings.polling");
|
||||||
|
return { enabled: pollingEnabled.enabled, interval: pollingEnabled.interval };
|
||||||
|
};
|
||||||
|
const SettingsWatcherPollingSet = async (
|
||||||
|
event: IpcMainInvokeEvent,
|
||||||
|
pollingSettings: {
|
||||||
|
enabled: boolean;
|
||||||
|
interval: number;
|
||||||
|
},
|
||||||
|
): Promise<{
|
||||||
|
enabled: boolean;
|
||||||
|
interval: number;
|
||||||
|
}> => {
|
||||||
|
log.info("Polling set", pollingSettings);
|
||||||
|
const { enabled, interval } = pollingSettings;
|
||||||
|
Store.set("settings.polling", { enabled, interval });
|
||||||
|
|
||||||
|
//Restart the watcher with these new settings.
|
||||||
|
await StopWatcher();
|
||||||
|
StartWatcher();
|
||||||
|
|
||||||
|
return { enabled, interval };
|
||||||
|
};
|
||||||
|
|
||||||
export {
|
export {
|
||||||
SettingsWatchedFilePathsAdd,
|
SettingsWatchedFilePathsAdd,
|
||||||
SettingsWatchedFilePathsGet,
|
SettingsWatchedFilePathsGet,
|
||||||
SettingsWatchedFilePathsRemove,
|
SettingsWatchedFilePathsRemove,
|
||||||
|
SettingsWatcherPollingGet,
|
||||||
|
SettingsWatcherPollingSet,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import Store from "electron-store";
|
import Store from "electron-store";
|
||||||
|
|
||||||
const store = new Store({
|
const store = new Store({
|
||||||
defaults: {
|
defaults: {
|
||||||
settings: {
|
settings: {
|
||||||
@@ -6,7 +7,7 @@ const store = new Store({
|
|||||||
runWatcherOnStartup: true,
|
runWatcherOnStartup: true,
|
||||||
polling: {
|
polling: {
|
||||||
enabled: false,
|
enabled: false,
|
||||||
pollingInterval: 30000,
|
interval: 30000,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
app: {
|
app: {
|
||||||
@@ -25,4 +26,10 @@ const store = new Store({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// store.onDidAnyChange((newValue, oldValue) => {
|
||||||
|
// console.log("Settings changed", newValue, oldValue);
|
||||||
|
// const mainWindow = BrowserWindow.getAllWindows()[0];
|
||||||
|
// mainWindow?.webContents.send(ipcTypes.toRenderer.store.didChange, newValue);
|
||||||
|
// });
|
||||||
|
|
||||||
export default store;
|
export default store;
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import ipcTypes from "../../util/ipcTypes.json";
|
|||||||
import ImportJob from "../decoder/decoder";
|
import ImportJob from "../decoder/decoder";
|
||||||
import store from "../store/store";
|
import store from "../store/store";
|
||||||
|
|
||||||
let watcher: FSWatcher;
|
let watcher: FSWatcher | null;
|
||||||
|
|
||||||
async function StartWatcher(): Promise<boolean> {
|
async function StartWatcher(): Promise<boolean> {
|
||||||
const filePaths: string[] = store.get("settings.filepaths") || [];
|
const filePaths: string[] = store.get("settings.filepaths") || [];
|
||||||
@@ -33,13 +33,19 @@ async function StartWatcher(): Promise<boolean> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const pollingSettings =
|
||||||
|
(store.get("settings.polling") as {
|
||||||
|
enabled?: boolean;
|
||||||
|
interval?: number;
|
||||||
|
}) || {};
|
||||||
|
|
||||||
watcher = chokidar.watch(filePaths, {
|
watcher = chokidar.watch(filePaths, {
|
||||||
ignored: (filepath, stats) => {
|
ignored: (filepath, stats) => {
|
||||||
const p = path.parse(filepath);
|
const p = path.parse(filepath);
|
||||||
return !stats?.isFile() && p.ext !== "" && p.ext.toUpperCase() !== ".ENV"; //Only watch for .ENV files.
|
return !stats?.isFile() && p.ext !== "" && p.ext.toUpperCase() !== ".ENV"; //Only watch for .ENV files.
|
||||||
},
|
},
|
||||||
usePolling: store.get("settings.polling").enabled || false,
|
usePolling: pollingSettings.enabled || false,
|
||||||
interval: store.get("settings.polling").pollingInterval || 1000,
|
interval: pollingSettings.interval || 30000,
|
||||||
persistent: true,
|
persistent: true,
|
||||||
ignoreInitial: true,
|
ignoreInitial: true,
|
||||||
awaitWriteFinish: {
|
awaitWriteFinish: {
|
||||||
@@ -73,34 +79,39 @@ async function StartWatcher(): Promise<boolean> {
|
|||||||
// errorTypeCheck(error)
|
// errorTypeCheck(error)
|
||||||
// );
|
// );
|
||||||
})
|
})
|
||||||
.on("ready", onWatcherReady)
|
.on("ready", onWatcherReady);
|
||||||
.on("raw", function (event, path, details) {
|
// .on("raw", function (event, path, details) {
|
||||||
// This event should be triggered everytime something happens.
|
// // This event should be triggered everytime something happens.
|
||||||
// console.log("Raw event info:", event, path, details);
|
// // console.log("Raw event info:", event, path, details);
|
||||||
});
|
// });
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
function removeWatcherPath(path: string): void {
|
function removeWatcherPath(path: string): void {
|
||||||
watcher.unwatch(path);
|
if (watcher) {
|
||||||
log.debug(`Stopped watching path: ${path}`);
|
watcher.unwatch(path);
|
||||||
|
log.debug(`Stopped watching path: ${path}`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function addWatcherPath(path: string | string[]): void {
|
function addWatcherPath(path: string | string[]): void {
|
||||||
watcher.add(path);
|
if (watcher) {
|
||||||
log.debug(`Started watching path: ${path}`);
|
watcher.add(path);
|
||||||
|
log.debug(`Started watching path: ${path}`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function onWatcherReady(): void {
|
function onWatcherReady(): void {
|
||||||
const mainWindow = BrowserWindow.getAllWindows()[0]; //TODO: Filter to only main window once a proper key has been set.
|
if (watcher) {
|
||||||
|
const mainWindow = BrowserWindow.getAllWindows()[0]; //TODO: Filter to only main window once a proper key has been set.
|
||||||
new Notification({
|
new Notification({
|
||||||
title: "Watcher Started",
|
title: "Watcher Started",
|
||||||
body: "Newly exported estimates will be automatically uploaded.",
|
body: "Newly exported estimates will be automatically uploaded.",
|
||||||
}).show();
|
}).show();
|
||||||
log.info("Confirmed watched paths:", watcher.getWatched());
|
log.info("Confirmed watched paths:", watcher.getWatched());
|
||||||
mainWindow.webContents.send(ipcTypes.toRenderer.watcher.started);
|
mainWindow.webContents.send(ipcTypes.toRenderer.watcher.started);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function StopWatcher(): Promise<boolean> {
|
async function StopWatcher(): Promise<boolean> {
|
||||||
|
|||||||
@@ -7,15 +7,39 @@ import {
|
|||||||
selectWatcherStatus,
|
selectWatcherStatus,
|
||||||
} from "@renderer/redux/app.slice";
|
} from "@renderer/redux/app.slice";
|
||||||
import { useAppSelector } from "@renderer/redux/reduxHooks";
|
import { useAppSelector } from "@renderer/redux/reduxHooks";
|
||||||
import { Alert, Button, Card, Space, Typography } from "antd";
|
import { Alert, Button, Card, InputNumber, Space, Switch } from "antd";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import ipcTypes from "../../../../util/ipcTypes.json";
|
import ipcTypes from "../../../../util/ipcTypes.json";
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
|
||||||
const SettingsWatcher: React.FC = () => {
|
const SettingsWatcher: React.FC = () => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const isWatcherStarted = useAppSelector(selectWatcherStatus);
|
const isWatcherStarted = useAppSelector(selectWatcherStatus);
|
||||||
const watcherError = useAppSelector(selectWatcherError);
|
const watcherError = useAppSelector(selectWatcherError);
|
||||||
|
|
||||||
|
const [pollingState, setPollingState] = useState<{
|
||||||
|
enabled: boolean;
|
||||||
|
interval: number;
|
||||||
|
}>({
|
||||||
|
enabled: false,
|
||||||
|
interval: 0,
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log("*** ~ pollingState:", pollingState);
|
||||||
|
const getPollingStateFromStore = (): void => {
|
||||||
|
window.electron.ipcRenderer
|
||||||
|
.invoke(ipcTypes.toMain.settings.watcher.getpolling)
|
||||||
|
.then((storePollingState: { enabled: boolean; interval: number }) => {
|
||||||
|
console.log("*** ~ .then ~ storePollingState:", storePollingState);
|
||||||
|
setPollingState(storePollingState);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
//Get state first time it renders.
|
||||||
|
useEffect(() => {
|
||||||
|
getPollingStateFromStore();
|
||||||
|
}, []);
|
||||||
|
|
||||||
const handleStart = (): void => {
|
const handleStart = (): void => {
|
||||||
window.electron.ipcRenderer.send(ipcTypes.toMain.watcher.start);
|
window.electron.ipcRenderer.send(ipcTypes.toMain.watcher.start);
|
||||||
};
|
};
|
||||||
@@ -24,6 +48,31 @@ const SettingsWatcher: React.FC = () => {
|
|||||||
window.electron.ipcRenderer.send(ipcTypes.toMain.watcher.stop);
|
window.electron.ipcRenderer.send(ipcTypes.toMain.watcher.stop);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const toggleWatcherMode = (checked: boolean): void => {
|
||||||
|
window.electron.ipcRenderer
|
||||||
|
.invoke(ipcTypes.toMain.settings.watcher.setpolling, {
|
||||||
|
enabled: !checked,
|
||||||
|
interval: pollingState.interval,
|
||||||
|
})
|
||||||
|
.then((storePollingState: { enabled: boolean; interval: number }) => {
|
||||||
|
setPollingState(storePollingState);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const handlePollingIntervalChange = (value: number | null): void => {
|
||||||
|
if (value) {
|
||||||
|
window.electron.ipcRenderer
|
||||||
|
.invoke(ipcTypes.toMain.settings.watcher.setpolling, {
|
||||||
|
enabled: pollingState.enabled,
|
||||||
|
interval: value,
|
||||||
|
})
|
||||||
|
.then((storePollingState: { enabled: boolean; interval: number }) => {
|
||||||
|
setPollingState(storePollingState);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
getPollingStateFromStore();
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card title={t("settings.labels.watcherstatus")}>
|
<Card title={t("settings.labels.watcherstatus")}>
|
||||||
<Space>
|
<Space>
|
||||||
@@ -47,6 +96,19 @@ const SettingsWatcher: React.FC = () => {
|
|||||||
{t("settings.labels.stopped")}
|
{t("settings.labels.stopped")}
|
||||||
</Space>
|
</Space>
|
||||||
)}
|
)}
|
||||||
|
<Switch
|
||||||
|
checked={!pollingState.enabled}
|
||||||
|
onChange={toggleWatcherMode}
|
||||||
|
checkedChildren={t("settings.labels.watchermoderealtime")}
|
||||||
|
unCheckedChildren={t("settings.labels.watchermodepolling")}
|
||||||
|
/>
|
||||||
|
<InputNumber
|
||||||
|
title={t("settings.labels.pollinginterval")}
|
||||||
|
disabled={!pollingState.enabled}
|
||||||
|
min={1000}
|
||||||
|
value={pollingState.interval}
|
||||||
|
onChange={handlePollingIntervalChange}
|
||||||
|
/>
|
||||||
{watcherError && <Alert message={watcherError} />}
|
{watcherError && <Alert message={watcherError} />}
|
||||||
</Space>
|
</Space>
|
||||||
</Card>
|
</Card>
|
||||||
|
|||||||
@@ -1,13 +1,15 @@
|
|||||||
import { createSlice, PayloadAction } from "@reduxjs/toolkit";
|
import { createSlice, PayloadAction } from "@reduxjs/toolkit";
|
||||||
import log from "electron-log/renderer";
|
import log from "electron-log/renderer";
|
||||||
import type { RootState } from "./redux-store";
|
import type { RootState } from "./redux-store";
|
||||||
import { update } from "lodash";
|
|
||||||
import { notification } from "antd";
|
|
||||||
interface AppState {
|
interface AppState {
|
||||||
value: number;
|
value: number;
|
||||||
watcher: {
|
watcher: {
|
||||||
started: boolean;
|
started: boolean;
|
||||||
error: string | null;
|
error: string | null;
|
||||||
|
polling: {
|
||||||
|
enabled: boolean;
|
||||||
|
interval: number;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
updates: {
|
updates: {
|
||||||
available: boolean;
|
available: boolean;
|
||||||
@@ -24,6 +26,10 @@ const initialState: AppState = {
|
|||||||
watcher: {
|
watcher: {
|
||||||
started: false,
|
started: false,
|
||||||
error: null,
|
error: null,
|
||||||
|
polling: {
|
||||||
|
enabled: false,
|
||||||
|
interval: 30000,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
updates: {
|
updates: {
|
||||||
available: false,
|
available: false,
|
||||||
@@ -58,16 +64,26 @@ export const appSlice = createSlice({
|
|||||||
state.updates.available = true;
|
state.updates.available = true;
|
||||||
state.updates.checking = false;
|
state.updates.checking = false;
|
||||||
},
|
},
|
||||||
updateProgress: (state, action) => {
|
updateProgress: (
|
||||||
|
state,
|
||||||
|
action: PayloadAction<{ progress: number; speed: number }>,
|
||||||
|
) => {
|
||||||
state.updates.available = true;
|
state.updates.available = true;
|
||||||
state.updates.progress = action?.progress;
|
state.updates.progress = action.payload.progress;
|
||||||
state.updates.speed = action?.speed;
|
state.updates.speed = action.payload.speed;
|
||||||
},
|
},
|
||||||
updateDownloaded: (state) => {
|
updateDownloaded: (state) => {
|
||||||
state.updates.completed = true;
|
state.updates.completed = true;
|
||||||
state.updates.progress = 100;
|
state.updates.progress = 100;
|
||||||
state.updates.speed = 0;
|
state.updates.speed = 0;
|
||||||
},
|
},
|
||||||
|
setWatcherPolling: (
|
||||||
|
state,
|
||||||
|
action: PayloadAction<{ enabled: boolean; interval: number }>,
|
||||||
|
) => {
|
||||||
|
state.watcher.polling.enabled = action.payload.enabled;
|
||||||
|
state.watcher.polling.interval = action.payload.interval;
|
||||||
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -79,6 +95,7 @@ export const {
|
|||||||
updateChecking,
|
updateChecking,
|
||||||
updateDownloaded,
|
updateDownloaded,
|
||||||
updateProgress,
|
updateProgress,
|
||||||
|
setWatcherPolling,
|
||||||
} = appSlice.actions;
|
} = appSlice.actions;
|
||||||
|
|
||||||
// Other code such as selectors can use the imported `RootState` type
|
// Other code such as selectors can use the imported `RootState` type
|
||||||
@@ -100,6 +117,13 @@ export const selectAppUpdateSpeed = (state: RootState): number =>
|
|||||||
export const selectAppUpdateCompleted = (state: RootState): boolean =>
|
export const selectAppUpdateCompleted = (state: RootState): boolean =>
|
||||||
state.app.updates.completed;
|
state.app.updates.completed;
|
||||||
|
|
||||||
|
export const selectWatcherPolling = (
|
||||||
|
state: RootState,
|
||||||
|
): {
|
||||||
|
enabled: boolean;
|
||||||
|
interval: number;
|
||||||
|
} => state.app.watcher.polling;
|
||||||
|
|
||||||
//Async Functions - Thunks
|
//Async Functions - Thunks
|
||||||
// Define a thunk that dispatches those action creators
|
// Define a thunk that dispatches those action creators
|
||||||
// const fetchUsers = () => async (dispatch) => {
|
// const fetchUsers = () => async (dispatch) => {
|
||||||
@@ -108,12 +132,4 @@ export const selectAppUpdateCompleted = (state: RootState): boolean =>
|
|||||||
// // dispatch(incrementByAmount(100));
|
// // dispatch(incrementByAmount(100));
|
||||||
// };
|
// };
|
||||||
|
|
||||||
const updateAvailableThunk = () => async (dispatch) => {
|
|
||||||
notification.info({
|
|
||||||
message: "Update Available",
|
|
||||||
key: "app-update",
|
|
||||||
description: "An update is available for download.",
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export default appSlice.reducer;
|
export default appSlice.reducer;
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
//Set up all of the IPC handlers.
|
//Set up all of the IPC handlers.
|
||||||
import {
|
import {
|
||||||
|
selectWatcherPolling,
|
||||||
|
setWatcherPolling,
|
||||||
updateAvailable,
|
updateAvailable,
|
||||||
updateChecking,
|
updateChecking,
|
||||||
updateDownloaded,
|
updateDownloaded,
|
||||||
@@ -84,3 +86,7 @@ ipcRenderer.on(
|
|||||||
dispatch(updateDownloaded());
|
dispatch(updateDownloaded());
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
ipcRenderer.on(ipcTypes.toRenderer.watcher.polling, (event, arg) => {
|
||||||
|
dispatch(setWatcherPolling({ enabled: arg.enabled, interval: arg.interval }));
|
||||||
|
});
|
||||||
|
|||||||
@@ -19,6 +19,10 @@
|
|||||||
"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"
|
"remove": "toMain_settings_filepaths_remove"
|
||||||
|
},
|
||||||
|
"watcher": {
|
||||||
|
"getpolling": "toMain_settings_watcher_getpolling",
|
||||||
|
"setpolling": "toMain_settings_watcher_setpolling"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"user": {
|
"user": {
|
||||||
@@ -30,7 +34,8 @@
|
|||||||
"watcher": {
|
"watcher": {
|
||||||
"started": "toRenderer_watcher_started",
|
"started": "toRenderer_watcher_started",
|
||||||
"stopped": "toRenderer_watcher_stopped",
|
"stopped": "toRenderer_watcher_stopped",
|
||||||
"error": "toRenderer_watcher_error"
|
"error": "toRenderer_watcher_error",
|
||||||
|
"polling": "toRenderer_watcher_polling"
|
||||||
},
|
},
|
||||||
"updates": {
|
"updates": {
|
||||||
"checking": "toRenderer_updates_checking",
|
"checking": "toRenderer_updates_checking",
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"toolbar": {
|
"toolbar": {
|
||||||
"help": "Help"
|
"help": "Help"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,27 +1,29 @@
|
|||||||
{
|
{
|
||||||
"translation": {
|
"translation": {
|
||||||
"navigation": {
|
"navigation": {
|
||||||
"home": "Home",
|
"home": "Home",
|
||||||
"settings": "Settings"
|
"settings": "Settings"
|
||||||
},
|
},
|
||||||
"settings": {
|
"settings": {
|
||||||
"actions": {
|
"actions": {
|
||||||
"addpath": "Add path",
|
"addpath": "Add path",
|
||||||
"startwatcher": "Start Watcher",
|
"startwatcher": "Start Watcher",
|
||||||
"stopwatcher": "Stop Watcher\n"
|
"stopwatcher": "Stop Watcher\n"
|
||||||
},
|
},
|
||||||
"labels": {
|
"labels": {
|
||||||
"started": "Started",
|
"started": "Started",
|
||||||
"stopped": "Stopped",
|
"stopped": "Stopped",
|
||||||
"watchedpaths": "Watched Paths",
|
"watchedpaths": "Watched Paths",
|
||||||
"watcherstatus": "Watcher Status"
|
"watchermodepolling": "Polling",
|
||||||
}
|
"watchermoderealtime": "Real Time",
|
||||||
},
|
"watcherstatus": "Watcher Status"
|
||||||
"updates": {
|
}
|
||||||
"apply": "Apply Update",
|
},
|
||||||
"available": "An update is available.",
|
"updates": {
|
||||||
"download": "Download Update",
|
"apply": "Apply Update",
|
||||||
"downloading": "An update is downloading."
|
"available": "An update is available.",
|
||||||
}
|
"download": "Download Update",
|
||||||
}
|
"downloading": "An update is downloading."
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -165,6 +165,32 @@
|
|||||||
</translation>
|
</translation>
|
||||||
</translations>
|
</translations>
|
||||||
</concept_node>
|
</concept_node>
|
||||||
|
<concept_node>
|
||||||
|
<name>watchermodepolling</name>
|
||||||
|
<definition_loaded>false</definition_loaded>
|
||||||
|
<description></description>
|
||||||
|
<comment></comment>
|
||||||
|
<default_text></default_text>
|
||||||
|
<translations>
|
||||||
|
<translation>
|
||||||
|
<language>en-US</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
</translations>
|
||||||
|
</concept_node>
|
||||||
|
<concept_node>
|
||||||
|
<name>watchermoderealtime</name>
|
||||||
|
<definition_loaded>false</definition_loaded>
|
||||||
|
<description></description>
|
||||||
|
<comment></comment>
|
||||||
|
<default_text></default_text>
|
||||||
|
<translations>
|
||||||
|
<translation>
|
||||||
|
<language>en-US</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
</translations>
|
||||||
|
</concept_node>
|
||||||
<concept_node>
|
<concept_node>
|
||||||
<name>watcherstatus</name>
|
<name>watcherstatus</name>
|
||||||
<definition_loaded>false</definition_loaded>
|
<definition_loaded>false</definition_loaded>
|
||||||
|
|||||||
Reference in New Issue
Block a user