feature/IO-3066-1-scaffolding: Minor cleanup

This commit is contained in:
Dave Richer
2025-04-21 10:29:25 -04:00
parent b683d054ed
commit d4bde2db40
23 changed files with 110 additions and 50 deletions

6
.idea/prettier.xml generated Normal file
View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="PrettierConfiguration">
<option name="myConfigurationMode" value="AUTOMATIC" />
</component>
</project>

View File

@@ -16,7 +16,6 @@ import { NotificationProvider } from "./util/notificationContext";
const App: FC = () => {
const [user, setUser] = useState<User | boolean | null>(false);
useEffect(() => {
// Only set up the listener once when component mounts
if (auth.currentUser) {
@@ -66,7 +65,7 @@ const App: FC = () => {
) : (
<Badge.Ribbon
text={isTest && "Connected to Test"}
color={isTest && "red"}
color={isTest ? "red" : undefined}
>
<Layout.Content style={{ padding: "0 24px" }}>
<UpdateAvailable />

View File

@@ -1,10 +1,9 @@
/* eslint-disable react/prop-types */
//TODO: remove eslint-disable
import { FC } from "react";
import { Button, Result } from "antd";
import { FallbackProps } from "react-error-boundary";
import { useTranslation } from "react-i18next";
const ErrorBoundaryFallback: React.FC<FallbackProps> = ({
const ErrorBoundaryFallback: FC<FallbackProps> = ({
error,
resetErrorBoundary,
}) => {

View File

@@ -1,4 +1,6 @@
const Home: React.FC = () => {
import { FC } from "react";
const Home: FC = () => {
return (
<div>
<h1>Home</h1>

View File

@@ -1,10 +1,10 @@
import { FolderOpenFilled } from "@ant-design/icons";
import { Button, Card, Input, Space } from "antd";
import { useEffect, useState } from "react";
import { useEffect, useState, FC } from "react";
import { useTranslation } from "react-i18next";
import ipcTypes from "../../../../util/ipcTypes.json";
const SettingsEmsOutFilePath: React.FC = () => {
const SettingsEmsOutFilePath: FC = () => {
const { t } = useTranslation();
const [emsFilePath, setEmsFilePath] = useState<string | null>(null);

View File

@@ -1,10 +1,10 @@
import { FolderOpenFilled } from "@ant-design/icons";
import { Button, Card, Input, Space } from "antd";
import { useEffect, useState } from "react";
import { useEffect, useState, FC } from "react";
import { useTranslation } from "react-i18next";
import ipcTypes from "../../../../util/ipcTypes.json";
const SettingsPpcFilepath: React.FC = () => {
const SettingsPpcFilepath: FC = () => {
const { t } = useTranslation();
const [ppcFilePath, setPpcFilePath] = useState<string | null>(null);

View File

@@ -1,10 +1,10 @@
import { DeleteFilled, FileAddFilled } from "@ant-design/icons";
import { Button, Card, Space, Timeline } from "antd";
import { useEffect, useState } from "react";
import { useEffect, useState, FC } from "react";
import { useTranslation } from "react-i18next";
import ipcTypes from "../../../../util/ipcTypes.json";
const SettingsWatchedPaths: React.FC = () => {
const SettingsWatchedPaths: FC = () => {
const [watchedPaths, setWatchedPaths] = useState<string[]>([]);
const { t } = useTranslation();

View File

@@ -1,14 +1,14 @@
import { useAppSelector } from "@renderer/redux/reduxHooks";
import {
CheckCircleFilled,
ExclamationCircleFilled,
PlayCircleOutlined,
PauseCircleOutlined,
PlayCircleOutlined,
} from "@ant-design/icons";
import {
selectWatcherError,
selectWatcherStatus,
} from "@renderer/redux/app.slice";
import { useAppSelector } from "@renderer/redux/reduxHooks";
import {
Alert,
Badge,
@@ -20,7 +20,7 @@ import {
Space,
Switch,
} from "antd";
import { useEffect, useState } from "react";
import { FC, useEffect, useState } from "react";
import { useTranslation } from "react-i18next";
import ipcTypes from "../../../../util/ipcTypes.json";
@@ -29,7 +29,7 @@ const colSpans = {
sm: 24,
};
const SettingsWatcher: React.FC = () => {
const SettingsWatcher: FC = () => {
const { t } = useTranslation();
const isWatcherStarted = useAppSelector(selectWatcherStatus);
const watcherError = useAppSelector(selectWatcherError);

View File

@@ -1,4 +1,5 @@
import { Col, Row } from "antd";
import { FC } from "react";
import SettingsWatchedPaths from "./Settings.WatchedPaths";
import SettingsWatcher from "./Settings.Watcher";
import Welcome from "../Welcome/Welcome";
@@ -9,7 +10,7 @@ const colSpans = {
md: 12,
sm: 24,
};
const Settings: React.FC = () => {
const Settings: FC = () => {
return (
<Row gutter={[16, 16]}>
<Col span={24}>

View File

@@ -1,6 +1,6 @@
import { auth } from "@renderer/util/firebase";
import type { FormProps } from "antd";
import { Alert, Button, Form, Input, Space, Card, Typography } from "antd";
import { Alert, Button, Card, Form, Input, Typography } from "antd";
import log from "electron-log/renderer";
import { signInWithEmailAndPassword } from "firebase/auth";
import { useState } from "react";

View File

@@ -8,9 +8,9 @@ import { useAppSelector } from "@renderer/redux/reduxHooks";
import { Affix, Button, Card, Progress, Space, Statistic } from "antd";
import { useTranslation } from "react-i18next";
import ipcTypes from "../../../../util/ipcTypes.json";
import { useState } from "react";
import { useState, FC } from "react";
const UpdateAvailable: React.FC = () => {
const UpdateAvailable: FC = () => {
const { t } = useTranslation();
const isUpdateAvailable = useAppSelector(selectUpdateAvailable);

View File

@@ -1,4 +1,4 @@
import { useState } from "react";
import { JSX, useState } from "react";
function Versions(): JSX.Element {
const [versions] = useState(window.electron.process.versions);

View File

@@ -1,10 +1,11 @@
import { LogoutOutlined } from "@ant-design/icons";
import { auth } from "@renderer/util/firebase";
import { Button, Space, Typography } from "antd";
import _ from "lodash";
import { isEmpty } from "lodash";
import { JSX, useEffect, useState } from "react";
import { useTranslation } from "react-i18next";
import ipcTypes from "../../../../util/ipcTypes.json";
const Welcome = (): JSX.Element => {
const { t } = useTranslation();
const [shopName, setShopName] = useState<string | null>(null);
@@ -21,7 +22,7 @@ const Welcome = (): JSX.Element => {
<>
<Typography.Title level={4}>
{t("auth.labels.welcome", {
name: _.isEmpty(auth.currentUser?.displayName)
name: isEmpty(auth.currentUser?.displayName)
? auth.currentUser?.email
: `${auth.currentUser?.displayName} (${auth.currentUser?.email})`.trim(),
})}
@@ -33,7 +34,9 @@ const Welcome = (): JSX.Element => {
danger
icon={<LogoutOutlined />}
onClick={(): void => {
auth.signOut();
auth.signOut().catch((error) => {
console.error("Sign out error:", error);
});
}}
>
{t("navigation.signout")}

View File

@@ -5,6 +5,16 @@ import "./util/i18n";
import "./util/ipcRendererHandler";
import * as Sentry from "@sentry/electron/renderer";
// Extend the Window interface to include the api property
declare global {
interface Window {
api: {
isTest: () => boolean;
};
}
}
Sentry.init({
dsn: "https://ba41d22656999a8c1fd63bcb7df98650@o492140.ingest.us.sentry.io/4509074139447296",
});

View File

@@ -1,6 +1,7 @@
import { createSlice, PayloadAction } from "@reduxjs/toolkit";
import log from "electron-log/renderer";
import type { RootState } from "./redux-store";
interface AppState {
value: number;
watcher: {

View File

@@ -9,6 +9,7 @@ const store = configureStore({
// Infer the `RootState` and `AppDispatch` types from the store itself
export type RootState = ReturnType<typeof store.getState>;
// Inferred type: {posts: PostsState, comments: CommentsState, users: UsersState}
export type AppDispatch = typeof store.dispatch;
export type AppStore = typeof store;

View File

@@ -1,4 +1,12 @@
import React from "react";
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
type Timer = {
started: number | null;
lastInterval: number | null;
timeLeft: number;
timeToCount: number;
requestId: number;
};
const useCountDown = (
timeToCount = 60 * 1000,
@@ -12,10 +20,16 @@ const useCountDown = (
reset: () => void;
},
] => {
const [timeLeft, setTimeLeft] = React.useState(0);
const timer = React.useRef({});
const [timeLeft, setTimeLeft] = useState(0);
const timer = useRef<Timer>({
started: null,
lastInterval: null,
timeLeft: 0,
timeToCount: 0,
requestId: 0,
});
const run = (ts) => {
const run = (ts: number) => {
if (!timer.current.started) {
timer.current.started = ts;
timer.current.lastInterval = ts;
@@ -25,7 +39,7 @@ const useCountDown = (
interval,
timer.current.timeLeft || Infinity,
);
if (ts - timer.current.lastInterval >= localInterval) {
if (timer.current.lastInterval && ts - timer.current.lastInterval >= localInterval) {
timer.current.lastInterval += localInterval;
setTimeLeft((timeLeft) => {
timer.current.timeLeft = timeLeft - localInterval;
@@ -36,12 +50,18 @@ const useCountDown = (
if (ts - timer.current.started < timer.current.timeToCount) {
timer.current.requestId = window.requestAnimationFrame(run);
} else {
timer.current = {};
timer.current = {
started: null,
lastInterval: null,
timeLeft: 0,
timeToCount: 0,
requestId: 0,
};
setTimeLeft(0);
}
};
const start = React.useCallback(
const start = useCallback(
(ttc) => {
window.cancelAnimationFrame(timer.current.requestId);
@@ -57,14 +77,14 @@ const useCountDown = (
[],
);
const pause = React.useCallback(() => {
const pause = useCallback(() => {
window.cancelAnimationFrame(timer.current.requestId);
timer.current.started = null;
timer.current.lastInterval = null;
timer.current.timeToCount = timer.current.timeLeft;
}, []);
const resume = React.useCallback(
const resume = useCallback(
() => {
if (!timer.current.started && timer.current.timeLeft > 0) {
window.cancelAnimationFrame(timer.current.requestId);
@@ -75,20 +95,26 @@ const useCountDown = (
[],
);
const reset = React.useCallback(() => {
const reset = useCallback(() => {
if (timer.current.timeLeft) {
window.cancelAnimationFrame(timer.current.requestId);
timer.current = {};
timer.current = {
started: null,
lastInterval: null,
timeLeft: 0,
timeToCount: 0,
requestId: 0,
};
setTimeLeft(0);
}
}, []);
const actions = React.useMemo(
const actions = useMemo(
() => ({ start, pause, resume, reset }), // eslint-disable-next-line react-hooks/exhaustive-deps
[],
);
React.useEffect(() => {
useEffect(() => {
return () => window.cancelAnimationFrame(timer.current.requestId);
}, []);

View File

@@ -10,6 +10,7 @@ const httpLink: HttpLink = new HttpLink({
});
const middlewares = [];
const client: ApolloClient<any> = new ApolloClient({
link: ApolloLink.from(middlewares),
cache: new InMemoryCache(),

View File

@@ -6,13 +6,19 @@ const resources = {
en: enTranslations,
};
i18n.use(initReactI18next).init({
resources,
debug: import.meta.env.DEV,
lng: "en",
interpolation: {
escapeValue: false,
},
});
i18n
.use(initReactI18next)
.init({
resources,
debug: import.meta.env.DEV,
lng: "en",
interpolation: {
escapeValue: false,
},
})
.catch((err) => {
console.error("i18n initialization error:", err);
throw err;
});
export default i18n;

View File

@@ -1,4 +1,4 @@
//Set up all of the IPC handlers.
//Set up all the IPC handlers.
import {
setWatcherPolling,
updateAvailable,
@@ -40,6 +40,7 @@ ipcRenderer.on(ipcTypes.toRenderer.watcher.stopped, () => {
console.log("Watcher has stopped");
dispatch(watcherStopped());
});
ipcRenderer.on(
ipcTypes.toRenderer.watcher.error,
(_event: Electron.IpcRendererEvent, error: string) => {
@@ -58,6 +59,7 @@ ipcRenderer.on(ipcTypes.toRenderer.updates.checking, () => {
ipcRenderer.on(ipcTypes.toRenderer.updates.available, () => {
dispatch(updateAvailable());
});
ipcRenderer.on(
ipcTypes.toRenderer.updates.downloading,
(_event: Electron.IpcRendererEvent, arg) => {
@@ -70,6 +72,7 @@ ipcRenderer.on(
);
},
);
ipcRenderer.on(ipcTypes.toRenderer.updates.downloaded, () => {
dispatch(updateDownloaded());
});

View File

@@ -1,5 +1,4 @@
// eslint-disable-all
import { createContext, useContext } from "react";
import {createContext, FC, ReactNode, useContext} from "react";
import { notification } from "antd";
/**
@@ -23,10 +22,10 @@ export const useNotification = () => {
* - Provide `api` via the NotificationContext.
*/
interface NotificationProviderProps {
children?: React.ReactNode | React.ReactNode[];
children?: ReactNode | ReactNode[];
}
export const NotificationProvider: React.FC<NotificationProviderProps> = ({
export const NotificationProvider: FC<NotificationProviderProps> = ({
// eslint-disable-next-line react/prop-types
children, //TODO: Unable to resolve this. Adding an eslint disable.
}) => {
@@ -37,6 +36,7 @@ export const NotificationProvider: React.FC<NotificationProviderProps> = ({
});
return (
// @ts-ignore
<NotificationContext.Provider value={api}>
{/* contextHolder must be rendered in the DOM so notifications can appear */}
{contextHolder}

View File

@@ -33,4 +33,5 @@ function deepLowerCaseKeys<T = any>(obj: any): T {
{} as Record<string, any>,
) as T;
}
export default deepLowerCaseKeys;

View File

@@ -12,6 +12,7 @@ function errorTypeCheck(passedError: Error | unknown): ParsedError {
stack: errorStack,
};
}
export default errorTypeCheck;
export interface ParsedError {