Add watcher status and additional typing.

This commit is contained in:
Patrick Fic
2025-03-21 11:28:30 -07:00
parent 6da5822197
commit 14e7c64eab
19 changed files with 385 additions and 81 deletions

17
.prettierrc.js Normal file
View File

@@ -0,0 +1,17 @@
export default {
printWidth: 120,
useTabs: false,
tabWidth: 2,
trailingComma: "none",
semi: true,
singleQuote: false,
bracketSpacing: true,
arrowParens: "always",
jsxSingleQuote: false,
bracketSameLine: false,
endOfLine: "lf"
// importOrder: ["^@core/(.*)$", "^@server/(.*)$", "^@ui/(.*)$", "^[./]"],
// importOrderSeparation: true,
// importOrderSortSpecifiers: true
};

View File

@@ -1,5 +1,5 @@
import tseslint from "@electron-toolkit/eslint-config-ts";
import eslintConfigPrettier from "@electron-toolkit/eslint-config-prettier"; import eslintConfigPrettier from "@electron-toolkit/eslint-config-prettier";
import tseslint from "@electron-toolkit/eslint-config-ts";
import eslintPluginReact from "eslint-plugin-react"; import eslintPluginReact from "eslint-plugin-react";
import eslintPluginReactHooks from "eslint-plugin-react-hooks"; import eslintPluginReactHooks from "eslint-plugin-react-hooks";
import eslintPluginReactRefresh from "eslint-plugin-react-refresh"; import eslintPluginReactRefresh from "eslint-plugin-react-refresh";
@@ -27,5 +27,5 @@ export default tseslint.config(
...eslintPluginReactRefresh.configs.vite.rules, ...eslintPluginReactRefresh.configs.vite.rules,
}, },
}, },
eslintConfigPrettier eslintConfigPrettier,
); );

View File

@@ -6,7 +6,7 @@ import errorTypeCheck from "../../util/errorTypeCheck";
import { DecodedStl, DecodedStlLine } from "./decode-stl.interface"; import { DecodedStl, DecodedStlLine } from "./decode-stl.interface";
const DecodeStl = async ( const DecodeStl = async (
extensionlessFilePath: string extensionlessFilePath: string,
): Promise<DecodedStl> => { ): Promise<DecodedStl> => {
let dbf: DBFFile | null = null; let dbf: DBFFile | null = null;
try { try {
@@ -47,7 +47,7 @@ const DecodeStl = async (
"TTL_TYPAMT", "TTL_TYPAMT",
"TTL_HRS", "TTL_HRS",
"TTL_AMT", "TTL_AMT",
]) ]),
); );
//Apply line by line adjustments. //Apply line by line adjustments.

View File

@@ -15,7 +15,7 @@ export interface DecodedVeh {
impact2?: string; impact2?: string;
}; };
// Complete vehicle data object // Complete vehicle data object
vehicle: { data: VehicleRecordInterface }; vehicle?: { data: VehicleRecordInterface };
} }
export interface VehicleRecordInterface { export interface VehicleRecordInterface {

View File

@@ -1,10 +1,22 @@
import { UUID } from "crypto";
import log from "electron-log/main"; import log from "electron-log/main";
import fs from "fs";
import path from "path"; import path from "path";
import errorTypeCheck from "../../util/errorTypeCheck"; import errorTypeCheck from "../../util/errorTypeCheck";
import client from "../graphql/graphql-client";
import {
QUERY_JOB_BY_CLM_NO_TYPED,
QUERY_VEHICLE_BY_VIN_TYPED,
QueryJobByClmNoResult,
VehicleQueryResult,
} from "../graphql/queries";
import store from "../store/store";
import DecodeAD1 from "./decode-ad1"; import DecodeAD1 from "./decode-ad1";
import { DecodedAd1 } from "./decode-ad1.interface"; import { DecodedAd1 } from "./decode-ad1.interface";
import DecodeAD2 from "./decode-ad2"; import DecodeAD2 from "./decode-ad2";
import { DecodedAD2 } from "./decode-ad2.interface"; import { DecodedAD2 } from "./decode-ad2.interface";
import DecodeEnv from "./decode-env";
import { DecodedEnv } from "./decode-env.interface";
import DecodeLin from "./decode-lin"; import DecodeLin from "./decode-lin";
import { DecodedLin } from "./decode-lin.interface"; import { DecodedLin } from "./decode-lin.interface";
import DecodePfh from "./decode-pfh"; import DecodePfh from "./decode-pfh";
@@ -25,17 +37,12 @@ import DecodeTtl from "./decode-ttl";
import { DecodedTtl } from "./decode-ttl.interface"; import { DecodedTtl } from "./decode-ttl.interface";
import DecodeVeh from "./decode-veh"; import DecodeVeh from "./decode-veh";
import { DecodedVeh } from "./decode-veh.interface"; import { DecodedVeh } from "./decode-veh.interface";
import { DecodedEnv } from "./decode-env.interface";
import DecodeEnv from "./decode-env";
import fs from "fs";
import store from "../store/store";
import client from "../graphql/graphql-client";
async function ImportJob(filepath: string): Promise<void> { async function ImportJob(filepath: string): Promise<void> {
const parsedFilePath = path.parse(filepath); const parsedFilePath = path.parse(filepath);
const extensionlessFilePath = path.join( const extensionlessFilePath = path.join(
parsedFilePath.dir, parsedFilePath.dir,
parsedFilePath.name parsedFilePath.name,
); );
log.debug("Importing Job", extensionlessFilePath); log.debug("Importing Job", extensionlessFilePath);
@@ -56,7 +63,7 @@ async function ImportJob(filepath: string): Promise<void> {
const ttl: DecodedTtl = await DecodeTtl(extensionlessFilePath); const ttl: DecodedTtl = await DecodeTtl(extensionlessFilePath);
const pfp: DecodedPfp = await DecodePfp(extensionlessFilePath); const pfp: DecodedPfp = await DecodePfp(extensionlessFilePath);
const jobObject = { const jobObject: RawJobDataObject = {
...env, ...env,
...ad1, ...ad1,
...ad2, ...ad2,
@@ -96,17 +103,7 @@ async function ImportJob(filepath: string): Promise<void> {
//Build the request object //Build the request object
//Insert it //Insert it
const newAvailableJob = { const newAvailableJob: AvailableJobSchema = {
// newJob.uploaded_by = Auth.authlink.User.Email;
// newJob.bodyshopid = AppMetaData.ActiveShopId;
// newJob.cieca_id = item.Job.ciecaid;
// newJob.est_data = item.Job;
// newJob.ownr_name = item.Job.ownr_fn?.Value + " " + item.Job.ownr_ln?.Value + " " + item.Job.ownr_co_nm?.Value;
// newJob.ins_co_nm = item.Job.ins_co_nm?.Value;
// newJob.vehicle_info = item.Job.vehicle.data.v_model_yr?.Value + " " + item.Job.vehicle.data.v_make_desc?.Value + " " + item.Job.vehicle.data.v_model_desc?.Value;
// newJob.clm_no = item.Job.clm_no?.Value;
// newJob.clm_amt = item.Job.clm_total?.Value;
// newJob.source_system = item.Job.source_system?.Value;
uploaded_by: store.get("app.user.email"), uploaded_by: store.get("app.user.email"),
bodyshopid: store.get("app.bodyshop.id"), bodyshopid: store.get("app.bodyshop.id"),
cieca_id: jobObject.ciecaid, cieca_id: jobObject.ciecaid,
@@ -117,30 +114,68 @@ async function ImportJob(filepath: string): Promise<void> {
clm_no: jobObject.clm_no, clm_no: jobObject.clm_no,
clm_amt: jobObject.clm_total, clm_amt: jobObject.clm_total,
// source_system: jobObject.source_system, //TODO: Add back source system if needed. // source_system: jobObject.source_system, //TODO: Add back source system if needed.
issupplement: false,
jobid: null,
}; };
const existingVehicleId: uuid = await client.query () const existingVehicleRecord: VehicleQueryResult = await client.request(
// var vehuuid = await Utils.Queries.VehicleQueries.GetVehicleUuidByVin(item?.Job?.vehicle?.data?.v_vin?.Value ?? ""); QUERY_VEHICLE_BY_VIN_TYPED,
// if (!string.IsNullOrEmpty(vehuuid)) {
// { vin: jobObject.v_vin,
// newJob.est_data.vehicle = null; },
// newJob.est_data.vehicleid = vehuuid; );
// }
if (existingVehicleRecord.vehicles.length > 0) {
delete newAvailableJob.est_data.vehicle;
newAvailableJob.est_data.vehicleid = existingVehicleRecord.vehicles[0].id;
}
// string jobId = await Utils.Queries.JobsQueries.CheckSupplementByClaimNo(item.Job.clm_no?.Value ?? ""); console.log(newAvailableJob);
// if (!string.IsNullOrEmpty(jobId)) const existingJobRecord: QueryJobByClmNoResult = await client.request(
// { QUERY_JOB_BY_CLM_NO_TYPED,
// newJob.issupplement = true; { clm_no: jobObject.clm_no },
// newJob.jobid = jobId; );
// };
//Check if the vehicle exists, if it does, use that UUID, if not, keep it to insert it.
if (existingJobRecord.jobs.length > 0) {
newAvailableJob.issupplement = true;
newAvailableJob.jobid = existingJobRecord.jobs[0].id;
}
} catch (error) { } catch (error) {
log.error("Error encountered while decoding job. ", errorTypeCheck(error)); log.error("Error encountered while decoding job. ", errorTypeCheck(error));
} }
} }
export default ImportJob; export default ImportJob;
export interface RawJobDataObject
extends DecodedEnv,
DecodedAd1,
DecodedAD2,
DecodedVeh,
DecodedLin,
DecodedPfh,
DecodedPfl,
DecodedPft,
DecodedPfm,
DecodedPfo,
DecodedStl,
DecodedTtl,
DecodedPfp {
vehicleid?: UUID;
}
export interface AvailableJobSchema {
uploaded_by: string;
bodyshopid: UUID;
cieca_id?: string;
est_data: RawJobDataObject;
ownr_name: string;
ins_co_nm?: string;
vehicle_info: string;
clm_no?: string;
clm_amt: number;
source_system?: string | null;
issupplement: boolean;
jobid: UUID | null;
}

View File

@@ -1,6 +1,7 @@
import { UUID } from "crypto";
import { parse, TypedQueryDocumentNode } from "graphql"; import { parse, TypedQueryDocumentNode } from "graphql";
import { gql } from "graphql-request"; import { gql } from "graphql-request";
import { AvailableJobSchema } from "../decoder/decoder";
// Define types for the query result and variables // Define types for the query result and variables
export interface ActiveBodyshopQueryResult { export interface ActiveBodyshopQueryResult {
bodyshops: Array<{ bodyshops: Array<{
@@ -9,10 +10,8 @@ export interface ActiveBodyshopQueryResult {
region_config: string; region_config: string;
}>; }>;
} }
// No variables needed for this query // No variables needed for this query
interface ActiveBodyshopQueryVariables {} interface ActiveBodyshopQueryVariables {}
// Transform the string query into a TypedQueryDocumentNode // Transform the string query into a TypedQueryDocumentNode
export const QUERY_ACTIVE_BODYSHOP_TYPED: TypedQueryDocumentNode< export const QUERY_ACTIVE_BODYSHOP_TYPED: TypedQueryDocumentNode<
ActiveBodyshopQueryResult, ActiveBodyshopQueryResult,
@@ -25,7 +24,10 @@ export const QUERY_ACTIVE_BODYSHOP_TYPED: TypedQueryDocumentNode<
region_config region_config
} }
} }
`); `) as TypedQueryDocumentNode<
ActiveBodyshopQueryResult,
ActiveBodyshopQueryVariables
>;
export interface MasterdataQueryResult { export interface MasterdataQueryResult {
masterdata: Array<{ masterdata: Array<{
@@ -33,11 +35,9 @@ export interface MasterdataQueryResult {
key: string; key: string;
}>; }>;
} }
interface MasterdataQueryVariables { interface MasterdataQueryVariables {
key: string; key: string;
} }
export const QUERY_MASTERDATA_TYPED: TypedQueryDocumentNode< export const QUERY_MASTERDATA_TYPED: TypedQueryDocumentNode<
MasterdataQueryResult, MasterdataQueryResult,
MasterdataQueryVariables MasterdataQueryVariables
@@ -48,19 +48,16 @@ export const QUERY_MASTERDATA_TYPED: TypedQueryDocumentNode<
key key
} }
} }
`); `) as TypedQueryDocumentNode<MasterdataQueryResult, MasterdataQueryVariables>;
export interface VehicleQueryResult { export interface VehicleQueryResult {
masterdata: Array<{ vehicles: Array<{
value: string; id: UUID;
key: string;
}>; }>;
} }
interface VehicleQueryVariables { interface VehicleQueryVariables {
vin: string; vin: string;
} }
export const QUERY_VEHICLE_BY_VIN_TYPED: TypedQueryDocumentNode< export const QUERY_VEHICLE_BY_VIN_TYPED: TypedQueryDocumentNode<
VehicleQueryResult, VehicleQueryResult,
VehicleQueryVariables VehicleQueryVariables
@@ -70,4 +67,62 @@ export const QUERY_VEHICLE_BY_VIN_TYPED: TypedQueryDocumentNode<
id id
} }
} }
`); `) as TypedQueryDocumentNode<VehicleQueryResult, VehicleQueryVariables>;
export interface QueryJobByClmNoResult {
jobs: Array<{
id: UUID;
}>;
}
export interface QueryJobByClmNoVariables {
clm_no: string;
}
export const QUERY_JOB_BY_CLM_NO_TYPED: TypedQueryDocumentNode<
QueryJobByClmNoResult,
QueryJobByClmNoVariables
> = parse(gql`
query QUERY_JOB_BY_CLM_NO($clm_no: String!) {
jobs(where: { clm_no: { _eq: $clm_no } }) {
id
}
}
`) as TypedQueryDocumentNode<QueryJobByClmNoResult, QueryJobByClmNoVariables>;
export interface InsertAvailableJobResult {
returning: Array<{
id: UUID;
}>;
}
export interface InsertAvailableJobVariables {
jobInput: Array<AvailableJobSchema>;
}
export const INSERT_AVAILABLE_JOB_TYPED: TypedQueryDocumentNode<
InsertAvailableJobResult,
InsertAvailableJobVariables
> = parse(gql`
mutation INSERT_AVAILABLE_JOB($jobInput: [available_jobs_insert_input!]!) {
insert_available_jobs(
objects: $jobInput
on_conflict: {
constraint: available_jobs_clm_no_bodyshopid_key
update_columns: [
clm_amt
cieca_id
est_data
issupplement
ownr_name
source_system
supplement_number
vehicle_info
]
}
) {
returning {
id
}
}
}
`) as TypedQueryDocumentNode<
InsertAvailableJobResult,
InsertAvailableJobVariables
>;

View File

@@ -3,7 +3,7 @@ import log from "electron-log/main";
import path from "path"; import path from "path";
import ipcTypes from "../../util/ipcTypes.json"; import ipcTypes from "../../util/ipcTypes.json";
import ImportJob from "../decoder/decoder"; import ImportJob from "../decoder/decoder";
import { StartWatcher } from "../watcher/watcher"; import { StartWatcher, StopWatcher } from "../watcher/watcher";
import { import {
SettingsWatchedFilePathsAdd, SettingsWatchedFilePathsAdd,
SettingsWatchedFilePathsGet, SettingsWatchedFilePathsGet,
@@ -83,4 +83,8 @@ ipcMain.on(ipcTypes.toMain.watcher.start, () => {
StartWatcher(); StartWatcher();
}); });
ipcMain.on(ipcTypes.toMain.watcher.stop, () => {
StopWatcher();
});
logIpcMessages(); logIpcMessages();

View File

@@ -2,6 +2,7 @@ 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";
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.
@@ -18,6 +19,7 @@ const SettingsWatchedFilePathsAdd = async (): Promise<string[]> => {
"settings.filepaths", "settings.filepaths",
_.union(result.filePaths, Store.get("settings.filepaths")) _.union(result.filePaths, Store.get("settings.filepaths"))
); );
addWatcherPath(result.filePaths);
} }
return Store.get("settings.filepaths"); return Store.get("settings.filepaths");
@@ -30,7 +32,7 @@ const SettingsWatchedFilePathsRemove = async (
"settings.filepaths", "settings.filepaths",
_.without(Store.get("settings.filepaths"), path) _.without(Store.get("settings.filepaths"), path)
); );
removeWatcherPath(path);
return Store.get("settings.filepaths"); return Store.get("settings.filepaths");
}; };

View File

@@ -1,10 +1,11 @@
import chokidar, { FSWatcher } from "chokidar"; import chokidar, { FSWatcher } from "chokidar";
import { Notification } from "electron"; import { BrowserWindow, Notification } from "electron";
import log from "electron-log/main"; import log from "electron-log/main";
import path from "path"; import path from "path";
import errorTypeCheck from "../../util/errorTypeCheck"; import errorTypeCheck from "../../util/errorTypeCheck";
import store from "../store/store"; import ipcTypes from "../../util/ipcTypes.json";
import ImportJob from "../decoder/decoder"; import ImportJob from "../decoder/decoder";
import store from "../store/store";
let watcher: FSWatcher; let watcher: FSWatcher;
@@ -67,6 +68,10 @@ async function StartWatcher(): Promise<boolean> {
// }) // })
.on("error", function (error) { .on("error", function (error) {
log.error("Error in Watcher", errorTypeCheck(error)); log.error("Error in Watcher", errorTypeCheck(error));
// mainWindow.webContents.send(
// ipcTypes.toRenderer.watcher.error,
// errorTypeCheck(error)
// );
}) })
.on("ready", onWatcherReady) .on("ready", onWatcherReady)
.on("raw", function (event, path, details) { .on("raw", function (event, path, details) {
@@ -77,24 +82,37 @@ async function StartWatcher(): Promise<boolean> {
return true; return true;
} }
function removeWatcherPath(path: string): void {
watcher.unwatch(path);
log.debug(`Stopped watching path: ${path}`);
}
function addWatcherPath(path: string | string[]): void {
watcher.add(path);
log.debug(`Started watching path: ${path}`);
}
function onWatcherReady(): void { function onWatcherReady(): void {
log.info("Watcher ready!"); const mainWindow = BrowserWindow.getAllWindows()[0]; //TODO: Filter to only main window once a proper key has been set.
// const b = BrowserWindow.getAllWindows()[0];
// b.webContents.send(ipcTypes.default.fileWatcher.toRenderer.startSuccess);
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);
} }
async function StopWatcher(): Promise<boolean> { async function StopWatcher(): Promise<boolean> {
const mainWindow = BrowserWindow.getAllWindows()[0]; //TODO: Filter to only main window once a proper key has been set.
if (watcher) { if (watcher) {
await watcher.close(); await watcher.close();
log.info("Watcher stopped."); log.info("Watcher stopped.");
mainWindow.webContents.send(ipcTypes.toRenderer.watcher.stopped);
new Notification({ new Notification({
title: "RPS Watcher Stopped", title: "Watcher Stopped",
body: "Estimates will not be automatically uploaded.", body: "Estimates will not be automatically uploaded.",
}).show(); }).show();
return true; return true;
@@ -107,4 +125,10 @@ async function HandleNewFile(path): Promise<void> {
log.log("Received a new file", path); log.log("Received a new file", path);
} }
export { StartWatcher, StopWatcher, watcher }; export {
StartWatcher,
StopWatcher,
watcher,
removeWatcherPath,
addWatcherPath,
};

View File

@@ -1,16 +1,50 @@
import { Button } from "antd"; import {
CheckCircleOutlined,
ExclamationCircleOutlined,
} from "@ant-design/icons";
import {
selectWatcherError,
selectWatcherStatus,
} from "@renderer/redux/app.slice";
import { useAppDispatch, useAppSelector } from "@renderer/redux/reduxHooks";
import { Button, Space } from "antd";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import ipcTypes from "../../../../util/ipcTypes.json"; import ipcTypes from "../../../../util/ipcTypes.json";
const SettingsWatcher: React.FC = () => { const SettingsWatcher: React.FC = () => {
const { t } = useTranslation(); const { t } = useTranslation();
const isWatcherStarted = useAppSelector(selectWatcherStatus);
const watcherError = useAppSelector(selectWatcherError);
const dispatch = useAppDispatch();
const handleStart = (): void => { const handleStart = (): void => {
window.electron.ipcRenderer.send(ipcTypes.toMain.watcher.start); window.electron.ipcRenderer.send(ipcTypes.toMain.watcher.start);
}; };
const handleStop = (): void => {
window.electron.ipcRenderer.send(ipcTypes.toMain.watcher.stop);
};
return ( return (
<Button onClick={handleStart}>{t("settings.actions.startwatcher")}</Button> <>
<Button onClick={handleStart}>
{t("settings.actions.startwatcher")}
</Button>
<Button onClick={handleStop}>{t("settings.actions.stopwatcher")}</Button>
{isWatcherStarted}
{watcherError}
{isWatcherStarted ? (
<Space>
<CheckCircleOutlined style={{ color: "green" }} />
{t("settings.labels.started")}
</Space>
) : (
<Space>
<ExclamationCircleOutlined style={{ color: "tomato" }} />
{t("settings.labels.stopped")}
</Space>
)}
</>
); );
}; };
export default SettingsWatcher; export default SettingsWatcher;

View File

@@ -1,46 +1,58 @@
import { createSlice, PayloadAction } from "@reduxjs/toolkit"; import { createSlice, PayloadAction } from "@reduxjs/toolkit";
import log from "electron-log/renderer";
import type { RootState } from "./redux-store"; import type { RootState } from "./redux-store";
// Define a type for the slice state
interface AppState { interface AppState {
value: number; value: number;
watcher: {
started: boolean;
error: string | null;
};
} }
// Define the initial state using that type // Define the initial state using that type
const initialState: AppState = { const initialState: AppState = {
value: 0, value: 0,
watcher: {
started: false,
error: null,
},
}; };
export const appSlice = createSlice({ export const appSlice = createSlice({
name: "counter", name: "app",
// `createSlice` will infer the state type from the `initialState` argument // `createSlice` will infer the state type from the `initialState` argument
initialState, initialState,
reducers: { reducers: {
increment: (state) => { watcherStarted: (state) => {
state.value += 1; state.watcher.started = true;
}, },
decrement: (state) => { watcherStopped: (state) => {
state.value -= 1; state.watcher.started = false;
}, },
// Use the PayloadAction type to declare the contents of `action.payload` watcherError: (state, action: PayloadAction<string>) => {
incrementByAmount: (state, action: PayloadAction<number>) => { state.watcher.error = action.payload;
state.value += action.payload; state.watcher.started = false;
log.error("[Redux] AppSlice: Watcher Error", action.payload);
}, },
}, },
}); });
export const { increment, decrement, incrementByAmount } = appSlice.actions; export const { watcherError, watcherStarted, watcherStopped } =
appSlice.actions;
// Other code such as selectors can use the imported `RootState` type // Other code such as selectors can use the imported `RootState` type
export const selectCount = (state: RootState): number => state.app.value; export const selectWatcherStatus = (state: RootState): boolean =>
state.app.watcher.started;
export const selectWatcherError = (state: RootState): string | null =>
state.app.watcher.error;
//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) => {
dispatch(increment()); //dispatch(watcherStarted());
//Some sort of async action. //Some sort of async action.
// dispatch(incrementByAmount(100));
dispatch(incrementByAmount(100));
}; };
export default appSlice.reducer; export default appSlice.reducer;

View File

@@ -1,9 +1,10 @@
import type { TypedUseSelectorHook } from "react-redux"; import type { TypedUseSelectorHook } from "react-redux";
import { useDispatch, useSelector, useStore } from "react-redux"; import { useDispatch, useSelector, useStore } from "react-redux";
import type { AppDispatch, AppStore, RootState } from "./redux-store"; import type { AppDispatch, AppStore, RootState } from "./redux-store";
import store from "./redux-store";
//Use these custom hooks to access the Redux store from your component with type safety. //Use these custom hooks to access the Redux store from your component with type safety.
export const useAppDispatch: () => AppDispatch = useDispatch; export type AppDispatch = typeof store.dispatch;
export const useAppDispatch = useDispatch.withTypes<AppDispatch>(); // Ex
export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector; export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector;
export const useAppStore: () => AppStore = useStore; export const useAppStore: () => AppStore = useStore;

View File

@@ -1,8 +1,15 @@
//Set up all of the IPC handlers. //Set up all of the IPC handlers.
import {
watcherError,
watcherStarted,
watcherStopped,
} from "@renderer/redux/app.slice";
import store from "@renderer/redux/redux-store";
import ipcTypes from "../../../util/ipcTypes.json"; import ipcTypes from "../../../util/ipcTypes.json";
import { auth } from "./firebase"; import { auth } from "./firebase";
const ipcRenderer = window.electron.ipcRenderer; const ipcRenderer = window.electron.ipcRenderer;
const dispatch = store.dispatch;
ipcRenderer.on( ipcRenderer.on(
ipcTypes.toRenderer.test, ipcTypes.toRenderer.test,
@@ -19,3 +26,29 @@ ipcRenderer.on(
ipcRenderer.send(ipcTypes.toMain.user.getTokenResponse, token); ipcRenderer.send(ipcTypes.toMain.user.getTokenResponse, token);
} }
); );
ipcRenderer.on(
ipcTypes.toRenderer.watcher.started,
(event: Electron.IpcRendererEvent, arg) => {
console.log("Watcher has started");
console.log(arg);
dispatch(watcherStarted());
}
);
ipcRenderer.on(
ipcTypes.toRenderer.watcher.stopped,
(event: Electron.IpcRendererEvent, arg) => {
console.log("Watcher has stopped");
console.log(arg);
dispatch(watcherStopped());
}
);
ipcRenderer.on(
ipcTypes.toRenderer.watcher.error,
(event: Electron.IpcRendererEvent, error: string) => {
console.log("Watcher has encountered an error");
console.log(error);
dispatch(watcherError(error));
}
);

View File

@@ -22,6 +22,11 @@
}, },
"toRenderer": { "toRenderer": {
"test": "toRenderer_test", "test": "toRenderer_test",
"watcher": {
"started": "toRenderer_watcher_started",
"stopped": "toRenderer_watcher_stopped",
"error": "toRenderer_watcher_error"
},
"user": { "user": {
"getToken": "toRenderer_user_getToken" "getToken": "toRenderer_user_getToken"
} }

View File

@@ -6,7 +6,14 @@
}, },
"settings": { "settings": {
"actions": { "actions": {
"addpath": "Add path" "addpath": "Add path",
"startwatcher": "Start Watcher",
"stopwatcher": "Stop Watcher\n"
},
"labels": {
"started": "Started",
"stopped": "Stopped",
"watcherstatus": "Watcher Status"
} }
} }
} }

View File

@@ -95,6 +95,76 @@
</translation> </translation>
</translations> </translations>
</concept_node> </concept_node>
<concept_node>
<name>startwatcher</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>stopwatcher</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>
</children>
</folder_node>
<folder_node>
<name>labels</name>
<children>
<concept_node>
<name>started</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>stopped</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>watcherstatus</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>
</children> </children>
</folder_node> </folder_node>
</children> </children>

View File

@@ -1,4 +1,7 @@
{ {
"files": [], "files": [],
"references": [{ "path": "./tsconfig.node.json" }, { "path": "./tsconfig.web.json" }] "references": [
{ "path": "./tsconfig.node.json" },
{ "path": "./tsconfig.web.json" }
]
} }

View File

@@ -9,6 +9,7 @@
"tests/index.spec.ts" "tests/index.spec.ts"
], ],
"compilerOptions": { "compilerOptions": {
"resolveJsonModule": true,
"composite": true, "composite": true,
"types": ["electron-vite/node"] "types": ["electron-vite/node"]
} }

View File

@@ -8,6 +8,7 @@
"src/util/**/*" "src/util/**/*"
], ],
"compilerOptions": { "compilerOptions": {
"resolveJsonModule": true,
"composite": true, "composite": true,
"jsx": "react-jsx", "jsx": "react-jsx",
"baseUrl": ".", "baseUrl": ".",