diff --git a/owmods_gui/frontend/src/components/common/StyledErrorBoundary.tsx b/owmods_gui/frontend/src/components/common/StyledErrorBoundary.tsx index 7775a9aa..8a15a6c1 100644 --- a/owmods_gui/frontend/src/components/common/StyledErrorBoundary.tsx +++ b/owmods_gui/frontend/src/components/common/StyledErrorBoundary.tsx @@ -4,9 +4,9 @@ import { Box, Button, Paper, Typography, useTheme } from "@mui/material"; import { ComponentType, ReactNode, useEffect, useMemo } from "react"; import { ErrorBoundary, withErrorBoundary } from "react-error-boundary"; import { TranslationKey } from "./TranslationContext"; -import { commands } from "@commands"; import { Event } from "@types"; import { listen } from "@events"; +import { onError } from "../../errorHandling"; export interface StyledErrorBoundaryProps { children: ReactNode; @@ -18,18 +18,6 @@ export interface StyledErrorBoundaryProps { fixButtonKey?: TranslationKey; } -export const simpleOnError = (err: string) => { - commands.logError({ - err: err.toString() - }); -}; - -export const onError = (err: unknown, info: { componentStack: string }) => { - commands.logError({ - err: `${err?.toString() ?? "null"}\nAt: ${info.componentStack}` - }); -}; - const fallback = (options: Omit) => function Fallback({ error, @@ -127,10 +115,4 @@ export const withStyledErrorBoundary = ( onError }); -export const basicFallbackRender = ({ error }: { error: unknown }) => ( -

{`Fatal Error: ${error}`}

-); - export default StyledErrorBoundary; diff --git a/owmods_gui/frontend/src/components/logs/LogApp.tsx b/owmods_gui/frontend/src/components/logs/LogApp.tsx index 56f699ad..d81b4faa 100644 --- a/owmods_gui/frontend/src/components/logs/LogApp.tsx +++ b/owmods_gui/frontend/src/components/logs/LogApp.tsx @@ -5,12 +5,12 @@ import { useGetTranslation } from "@hooks"; import { dialog } from "@tauri-apps/api"; import { getCurrent } from "@tauri-apps/api/window"; import { SocketMessageType } from "@types"; -import { useState, useCallback, useEffect } from "react"; +import { useState, useCallback, useEffect, memo } from "react"; import LogHeader from "./LogHeader"; import { Container, useTheme } from "@mui/material"; import LogTable from "./LogTable"; import { listen } from "@events"; -import { simpleOnError } from "@components/common/StyledErrorBoundary"; +import { simpleOnError } from "../../errorHandling"; export type LogFilter = keyof typeof SocketMessageType | "Any"; export type LogLines = number[]; @@ -25,9 +25,7 @@ const getFilterToPass = (activeFilter: LogFilter) => { } }; -const LogApp = ({ port }: { port: number }) => { - const [status, guiConfig] = hooks.getGuiConfig("guiConfigReload"); - +const InnerLogApp = memo(function InnerLogApp({ port }: { port: number }) { const [activeFilter, setActiveFilter] = useState("Any"); const [activeSearch, setActiveSearch] = useState(""); const [logLines, setLogLines] = useState([]); @@ -45,15 +43,6 @@ const LogApp = ({ port }: { port: number }) => { .catch(() => null); }, [activeFilter, activeSearch, port]); - useEffect(() => { - const logsTitleTranslation = TranslationMap[guiConfig?.language ?? "English"]["LOGS_TITLE"]; - if (logsTitleTranslation) { - thisWindow - .setTitle(logsTitleTranslation.replace("$port$", port.toString())) - .catch(simpleOnError); - } - }, [guiConfig?.language, port]); - const onClear = useCallback(() => { commands.clearLogs({ port }).catch(simpleOnError); setLogLines([]); @@ -78,34 +67,51 @@ const LogApp = ({ port }: { port: number }) => { fetchLogLines(); }, [activeFilter, activeSearch, fetchLogLines]); + return ( + + + + + ); +}); + +const LogApp = ({ port }: { port: number }) => { + const [status, guiConfig] = hooks.getGuiConfig("guiConfigReload"); + + useEffect(() => { + const logsTitleTranslation = TranslationMap[guiConfig?.language ?? "English"]["LOGS_TITLE"]; + if (logsTitleTranslation) { + thisWindow + .setTitle(logsTitleTranslation.replace("$port$", port.toString())) + .catch(simpleOnError); + } + }, [guiConfig?.language, port]); + return ( - - - - + ); }; diff --git a/owmods_gui/frontend/src/components/logs/LogFilters.tsx b/owmods_gui/frontend/src/components/logs/LogFilters.tsx new file mode 100644 index 00000000..1e67da3d --- /dev/null +++ b/owmods_gui/frontend/src/components/logs/LogFilters.tsx @@ -0,0 +1,62 @@ +import FilterInput from "@components/common/FilterInput"; +import { TranslationKey } from "@components/common/TranslationContext"; +import { useGetTranslation } from "@hooks"; +import { Box, FormControl, InputLabel, Select, MenuItem, useTheme } from "@mui/material"; +import { SocketMessageType } from "@types"; +import { memo } from "react"; +import { LogFilter } from "./LogApp"; + +export interface LogFilterProps { + activeFilter: LogFilter; + setActiveFilter: (filter: LogFilter) => void; + activeSearch: string; + setActiveSearch: (newSearch: string) => void; +} + +const LogFilters = memo(function LogFilter(props: LogFilterProps) { + const theme = useTheme(); + const getTranslation = useGetTranslation(); + + const labelId = "logs-filter-label"; + + return ( + <> + + props.setActiveSearch(v)} + label={getTranslation("SEARCH_LOGS")} + /> + + + + {getTranslation("FILTER")} + + + + + ); +}); + +export default LogFilters; diff --git a/owmods_gui/frontend/src/components/logs/LogHeader.tsx b/owmods_gui/frontend/src/components/logs/LogHeader.tsx index 18a1f9e6..2b10e5fd 100644 --- a/owmods_gui/frontend/src/components/logs/LogHeader.tsx +++ b/owmods_gui/frontend/src/components/logs/LogHeader.tsx @@ -1,23 +1,10 @@ import { useGetTranslation } from "@hooks"; import { memo } from "react"; import { LogFilter } from "./LogApp"; -import { - Box, - FormControl, - IconButton, - InputLabel, - MenuItem, - Paper, - Select, - Toolbar, - Typography, - useTheme -} from "@mui/material"; -import FilterInput from "@components/common/FilterInput"; -import { TranslationKey } from "@components/common/TranslationContext"; -import { SocketMessageType } from "@types"; +import { Box, IconButton, Paper, Toolbar, Typography, useTheme } from "@mui/material"; import { DeleteSweepRounded } from "@mui/icons-material"; import ODTooltip from "@components/common/ODTooltip"; +import LogFilters from "./LogFilters"; export interface LogHeaderProps { logsLen: number; @@ -28,49 +15,19 @@ export interface LogHeaderProps { onClear: () => void; } -const LogHeader = memo(function LogHeader({ setActiveSearch, ...props }: LogHeaderProps) { +const LogHeader = memo(function LogHeader(props: LogHeaderProps) { const theme = useTheme(); const getTranslation = useGetTranslation(); - const labelId = "logs-filter-label"; - return ( - - setActiveSearch(v)} - label={getTranslation("SEARCH_LOGS")} - /> - - - - {getTranslation("FILTER")} - - - + {getTranslation("LOG_COUNT", { count: props.logsLen.toString() })} diff --git a/owmods_gui/frontend/src/components/logs/LogTable.tsx b/owmods_gui/frontend/src/components/logs/LogTable.tsx index d04a8e49..710d8860 100644 --- a/owmods_gui/frontend/src/components/logs/LogTable.tsx +++ b/owmods_gui/frontend/src/components/logs/LogTable.tsx @@ -1,6 +1,6 @@ import { TableCell, useTheme } from "@mui/material"; import { TableContainer, Paper, Table, TableBody, TableHead, TableRow } from "@mui/material"; -import { forwardRef, memo, useRef } from "react"; +import { forwardRef, memo, useMemo, useRef } from "react"; import { TableProps, TableVirtuoso, VirtuosoHandle } from "react-virtuoso"; import { LogLines } from "./LogApp"; import { useGetTranslation } from "@hooks"; @@ -45,6 +45,21 @@ const LogTable = memo(function LogTable(props: LogTableProps) { const virtuoso = useRef(null); + const grey = theme.palette.grey[900]; + + const header = useMemo( + () => + function HeaderRow() { + return ( + + {getTranslation("SENDER")} + {getTranslation("LOG_MESSAGE")} + + ); + }, + [getTranslation, grey] + ); + return ( ( - - {getTranslation("SENDER")} - {getTranslation("LOG_MESSAGE")} - - )} + fixedHeaderContent={header} itemContent={(_, data) => ( )} diff --git a/owmods_gui/frontend/src/components/main/MainApp.tsx b/owmods_gui/frontend/src/components/main/MainApp.tsx index 825caa42..ab1c35dd 100644 --- a/owmods_gui/frontend/src/components/main/MainApp.tsx +++ b/owmods_gui/frontend/src/components/main/MainApp.tsx @@ -10,7 +10,8 @@ import { getCurrent } from "@tauri-apps/api/window"; import AppAlert from "./AppAlert"; import BaseApp from "@components/common/BaseApp"; import OwmlModal from "./OwmlModal"; -import StyledErrorBoundary, { simpleOnError } from "@components/common/StyledErrorBoundary"; +import StyledErrorBoundary from "@components/common/StyledErrorBoundary"; +import { simpleOnError } from "../../errorHandling"; import { useErrorBoundary } from "react-error-boundary"; import FileDrop from "./FileDrop"; import { Event } from "@types"; @@ -37,41 +38,18 @@ const Pane = memo(function Pane(props: { ); }); -const MainApp = () => { +const InnerMainApp = memo(function InnerMainApp() { const [selectedTab, setSelectedTab] = useState("local"); - const [status, guiConfig] = hooks.getGuiConfig("guiConfigReload"); - - const errorBound = useErrorBoundary(); const [filter, setFilter] = useState(""); const [tags, setTags] = useState([]); - useEffect(() => { - commands.initialSetup({}, false).catch((e) => errorBound.showBoundary(e)); - }, [errorBound]); - - useEffect(() => { - if (guiConfig?.language !== null) { - getCurrent() - .setTitle( - TranslationMap[guiConfig?.language ?? "English"]["APP_TITLE"] ?? - "Outer Wilds Mod Manager (*)" - ) - .catch(simpleOnError); - } - }, [guiConfig?.language]); - const onTabChange = useCallback((newVal: string) => { setSelectedTab(newVal as ModsTab); }, []); return ( - + <> @@ -108,6 +86,38 @@ const MainApp = () => { + + ); +}); + +const MainApp = () => { + const [status, guiConfig] = hooks.getGuiConfig("guiConfigReload"); + + const errorBound = useErrorBoundary(); + + useEffect(() => { + if (guiConfig?.language !== null) { + getCurrent() + .setTitle( + TranslationMap[guiConfig?.language ?? "English"]["APP_TITLE"] ?? + "Outer Wilds Mod Manager (*)" + ) + .catch(simpleOnError); + } + }, [guiConfig?.language]); + + useEffect(() => { + commands.initialSetup({}, false).catch((e) => errorBound.showBoundary(e)); + }, [errorBound]); + + return ( + + ); }; diff --git a/owmods_gui/frontend/src/components/main/mods/remote/RemoteModRow.tsx b/owmods_gui/frontend/src/components/main/mods/remote/RemoteModRow.tsx index 12de2b5b..13d2706c 100644 --- a/owmods_gui/frontend/src/components/main/mods/remote/RemoteModRow.tsx +++ b/owmods_gui/frontend/src/components/main/mods/remote/RemoteModRow.tsx @@ -4,7 +4,7 @@ import { commands, hooks } from "@commands"; import { useGetTranslation } from "@hooks"; import { dialog } from "@tauri-apps/api"; import RemoteModActions from "./RemoteModActions"; -import { simpleOnError } from "@components/common/StyledErrorBoundary"; +import { simpleOnError } from "../../../../errorHandling"; export interface RemoteModRowProps { uniqueName: string; diff --git a/owmods_gui/frontend/src/components/main/top-bar/StartGameButton.tsx b/owmods_gui/frontend/src/components/main/top-bar/StartGameButton.tsx index de3d1026..a11bc121 100644 --- a/owmods_gui/frontend/src/components/main/top-bar/StartGameButton.tsx +++ b/owmods_gui/frontend/src/components/main/top-bar/StartGameButton.tsx @@ -4,7 +4,7 @@ import { useCallback, useState } from "react"; import { commands } from "@commands"; import { useGetTranslation } from "@hooks"; import { dialog } from "@tauri-apps/api"; -import { simpleOnError } from "@components/common/StyledErrorBoundary"; +import { simpleOnError } from "../../../errorHandling"; const StartGameButton = () => { const getTranslation = useGetTranslation(); diff --git a/owmods_gui/frontend/src/components/main/top-bar/overflow/Export.tsx b/owmods_gui/frontend/src/components/main/top-bar/overflow/Export.tsx index ae62bb16..a1d5210a 100644 --- a/owmods_gui/frontend/src/components/main/top-bar/overflow/Export.tsx +++ b/owmods_gui/frontend/src/components/main/top-bar/overflow/Export.tsx @@ -5,7 +5,7 @@ import { NorthEastRounded } from "@mui/icons-material"; import { MenuItem, ListItemIcon, ListItemText } from "@mui/material"; import { dialog } from "@tauri-apps/api"; import { commands } from "@commands"; -import { simpleOnError } from "@components/common/StyledErrorBoundary"; +import { simpleOnError } from "../../../../errorHandling"; const Export = memo(function Export({ onClick }: ModalProps) { const getTranslation = useGetTranslation(); diff --git a/owmods_gui/frontend/src/components/main/top-bar/overflow/Import.tsx b/owmods_gui/frontend/src/components/main/top-bar/overflow/Import.tsx index 4eea57aa..df7bfa0e 100644 --- a/owmods_gui/frontend/src/components/main/top-bar/overflow/Import.tsx +++ b/owmods_gui/frontend/src/components/main/top-bar/overflow/Import.tsx @@ -18,7 +18,7 @@ import { } from "@mui/material"; import { OpenFileInput } from "@components/common/FileInput"; import { commands } from "@commands"; -import { simpleOnError } from "@components/common/StyledErrorBoundary"; +import { simpleOnError } from "../../../../errorHandling"; const Import = memo(function Import({ onClick }: ModalProps) { const getTranslation = useGetTranslation(); diff --git a/owmods_gui/frontend/src/components/main/top-bar/overflow/InstallFrom.tsx b/owmods_gui/frontend/src/components/main/top-bar/overflow/InstallFrom.tsx index 44985568..8d83a632 100644 --- a/owmods_gui/frontend/src/components/main/top-bar/overflow/InstallFrom.tsx +++ b/owmods_gui/frontend/src/components/main/top-bar/overflow/InstallFrom.tsx @@ -26,7 +26,7 @@ import { commands } from "@commands"; import { getCurrent } from "@tauri-apps/api/window"; import { OpenFileInput } from "@components/common/FileInput"; import { listen } from "@events"; -import { simpleOnError } from "@components/common/StyledErrorBoundary"; +import { simpleOnError } from "../../../../errorHandling"; type SourceType = "UNIQUE_NAME" | "URL" | "ZIP"; diff --git a/owmods_gui/frontend/src/components/main/top-bar/settings/SettingsForm.tsx b/owmods_gui/frontend/src/components/main/top-bar/settings/SettingsForm.tsx index e48a567c..8d368d59 100644 --- a/owmods_gui/frontend/src/components/main/top-bar/settings/SettingsForm.tsx +++ b/owmods_gui/frontend/src/components/main/top-bar/settings/SettingsForm.tsx @@ -17,7 +17,7 @@ import SettingsSelect from "./SettingsSelect"; import SettingsText from "./SettingsText"; import SettingsCheck from "./SettingsCheck"; import SettingsHeader from "./SettingsHeader"; -import { simpleOnError } from "@components/common/StyledErrorBoundary"; +import { simpleOnError } from "../../../../errorHandling"; const LanguageArr = Object.values(Language); const ThemeArr = Object.values(Theme); diff --git a/owmods_gui/frontend/src/errorHandling.tsx b/owmods_gui/frontend/src/errorHandling.tsx new file mode 100644 index 00000000..0569eed3 --- /dev/null +++ b/owmods_gui/frontend/src/errorHandling.tsx @@ -0,0 +1,19 @@ +import { commands } from "@commands"; + +export const simpleOnError = (err: string) => { + commands.logError({ + err: err.toString() + }); +}; + +export const onError = (err: unknown, info: { componentStack: string }) => { + commands.logError({ + err: `${err?.toString() ?? "null"}\nAt: ${info.componentStack}` + }); +}; + +export const basicFallbackRender = ({ error }: { error: unknown }) => ( +

{`Fatal Error: ${error}`}

+); diff --git a/owmods_gui/frontend/src/events.ts b/owmods_gui/frontend/src/events.ts index ede1d851..20b53f3f 100644 --- a/owmods_gui/frontend/src/events.ts +++ b/owmods_gui/frontend/src/events.ts @@ -1,4 +1,4 @@ -import { simpleOnError } from "@components/common/StyledErrorBoundary"; +import { simpleOnError } from "./errorHandling"; import { listen as tauriListen, emit as tauriEmit } from "@tauri-apps/api/event"; import { Event } from "@types"; diff --git a/owmods_gui/frontend/src/logs.tsx b/owmods_gui/frontend/src/logs.tsx index 79bec568..953006f6 100644 --- a/owmods_gui/frontend/src/logs.tsx +++ b/owmods_gui/frontend/src/logs.tsx @@ -1,25 +1,25 @@ import ReactDOM from "react-dom/client"; import { commands } from "@commands"; -import LogApp from "@components/logs/LogApp"; import { ErrorBoundary } from "react-error-boundary"; -import { - basicFallbackRender, - onError, - simpleOnError -} from "@components/common/StyledErrorBoundary"; +import { basicFallbackRender, onError, simpleOnError } from "./errorHandling"; import { listen } from "@events"; +import React from "react"; let port = 0; +const LogApp = React.lazy(() => import("@components/logs/LogApp")); + listen("gameStart", (inPort) => { if (port !== 0) return; port = inPort; ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render( - - - + + + + + ); }); diff --git a/owmods_gui/frontend/src/main.tsx b/owmods_gui/frontend/src/main.tsx index 73d99e21..36a3822d 100644 --- a/owmods_gui/frontend/src/main.tsx +++ b/owmods_gui/frontend/src/main.tsx @@ -1,13 +1,16 @@ -import React from "react"; +import React, { Suspense } from "react"; import ReactDOM from "react-dom/client"; -import App from "@components/main/MainApp"; import { ErrorBoundary } from "react-error-boundary"; -import { basicFallbackRender, onError } from "@components/common/StyledErrorBoundary"; +import { basicFallbackRender, onError } from "./errorHandling"; + +const App = React.lazy(() => import("@components/main/MainApp")); ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render( - + + + );