diff --git a/src/components/HMD_LINK/conn_strength.tsx b/src/components/HMD_LINK/conn_strength.tsx index 3ce6d0e6..5a887013 100644 --- a/src/components/HMD_LINK/conn_strength.tsx +++ b/src/components/HMD_LINK/conn_strength.tsx @@ -4,13 +4,15 @@ import { SignalHigh, SignalLow, SignalMedium } from 'lucide-react'; interface ConnStrengthProps { desc: string; ping: number; + // eslint-disable-next-line react/require-default-props + className?: string; } -function ConnectionStrength({ desc, ping }: ConnStrengthProps) { +function ConnectionStrength({ desc, ping, className = '' }: ConnStrengthProps) { // eslint-disable-next-line @typescript-eslint/no-unused-vars, prefer-const return ( -
+
15 ? 'border-2 border-red-500' : '' diff --git a/src/components/PAGES/telemetry_page.tsx b/src/components/PAGES/telemetry_page.tsx new file mode 100644 index 00000000..f8fa103c --- /dev/null +++ b/src/components/PAGES/telemetry_page.tsx @@ -0,0 +1,22 @@ +import { Button } from '@mui/material'; +import { useNavigate } from 'react-router-dom'; +import Timers from '../UI_AND_UX/timing'; + +function TelemetryPage() { + const navigate = useNavigate(); + + return ( +
+ + +
+ ); +} + +export default TelemetryPage; diff --git a/src/components/UI_AND_UX/timing.tsx b/src/components/UI_AND_UX/timing.tsx index 59232bef..6cad57a1 100644 --- a/src/components/UI_AND_UX/timing.tsx +++ b/src/components/UI_AND_UX/timing.tsx @@ -1,9 +1,10 @@ /* eslint-disable react/destructuring-assignment */ /* eslint-disable max-classes-per-file */ -import { Loop, Pause, PlayArrow } from '@mui/icons-material'; -import { IconButton } from '@mui/material'; import React from 'react'; import 'tailwindcss/tailwind.css'; +import { Loop, Pause, PlayArrow } from '@mui/icons-material'; +import { IconButton } from '@mui/material'; +import { useStopwatch } from '../../providers/stopwatch_provider'; type ClockState = { date: Date; @@ -43,94 +44,29 @@ class Clock extends React.Component<{}, ClockState> { } } -interface StopwatchState { - time: number; - isRunning: boolean; -} - -class Stopwatch extends React.Component<{}, StopwatchState> { - interval: number | null = null; - - constructor(props: {}) { - super(props); - this.state = { - time: 0, - isRunning: false, - }; - } - - componentDidUpdate(prevProps: {}, prevState: StopwatchState) { - if (this.state.isRunning && !prevState.isRunning) { - this.startTimer(); - } else if (!this.state.isRunning && prevState.isRunning) { - this.stopTimer(); - } - } - - componentWillUnmount() { - this.stopTimer(); - } - - startTimer = () => { - this.interval = window.setInterval(() => { - this.setState((prevState) => ({ time: prevState.time + 1000 })); - }, 1000); - }; +function Stopwatch() { + const { isRunning, formattedTime, handleStartStop, handleReset } = + useStopwatch(); - stopTimer = () => { - if (this.interval !== null) { - window.clearInterval(this.interval); - this.interval = null; - } - }; - - handleReset = () => { - this.stopTimer(); - this.setState({ time: 0, isRunning: false }); - }; - - handleStartStop = () => { - this.setState((prevState) => ({ isRunning: !prevState.isRunning })); - }; - - // eslint-disable-next-line class-methods-use-this - formatTime = (time: number) => { - const hours = Math.floor(time / 3600000); - const minutes = Math.floor((time - hours * 3600000) / 60000); - const seconds = Math.floor( - (time - hours * 3600000 - minutes * 60000) / 1000, - ); - - return `${hours.toString().padStart(2, '0')}:${minutes - .toString() - .padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`; - }; - - render() { - const { time, isRunning } = this.state; - - return ( -
-
- {this.formatTime(time)} -
- - {isRunning ? : } - - - - -
- ); - } + return ( +
+
{formattedTime}
+ + {isRunning ? : } + + + + +
+ ); } function Timers() { diff --git a/src/providers/stopwatch_provider.tsx b/src/providers/stopwatch_provider.tsx new file mode 100644 index 00000000..9691288c --- /dev/null +++ b/src/providers/stopwatch_provider.tsx @@ -0,0 +1,108 @@ +/* eslint-disable react-hooks/exhaustive-deps */ +/* eslint-disable react/jsx-no-constructed-context-values */ +/* eslint-disable react/function-component-definition */ +import React, { + createContext, + useState, + useContext, + FunctionComponent, + useCallback, + ReactNode, + useEffect, +} from 'react'; + +interface StopwatchContextType { + time: number; + isRunning: boolean; + formattedTime: string; + handleStartStop: () => void; + handleReset: () => void; +} + +const defaultContext: StopwatchContextType = { + time: 0, + isRunning: false, + formattedTime: '00:00:00', + handleStartStop: () => {}, + handleReset: () => {}, +}; + +interface StopwatchProviderProps { + children: ReactNode; +} + +const StopwatchContext = createContext(defaultContext); + +export const useStopwatch = () => useContext(StopwatchContext); + +export const StopwatchProvider: FunctionComponent = ({ + children, +}) => { + const [startTime, setStartTime] = useState(null); + const [time, setTime] = useState(0); + const [isRunning, setIsRunning] = useState(false); + + // eslint-disable-next-line @typescript-eslint/no-shadow + const formatTime = useCallback((time: number): string => { + const hours = Math.floor(time / 3600000); + const minutes = Math.floor((time % 3600000) / 60000); + const seconds = Math.floor((time % 60000) / 1000); + return `${hours.toString().padStart(2, '0')}:${minutes + .toString() + .padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`; + }, []); + + useEffect(() => { + let interval: number | null = null; + + if (isRunning) { + interval = window.setInterval(() => { + if (startTime !== null) { + const now = Date.now(); + setTime(now - startTime); + } + }, 1000); + } else if (!isRunning && interval !== null) { + window.clearInterval(interval); + } + + return () => { + if (interval !== null) { + window.clearInterval(interval); + } + }; + }, [isRunning, startTime]); + + const handleStartStop = useCallback(() => { + if (isRunning) { + setIsRunning(false); + } else { + setStartTime((prevStartTime) => + prevStartTime !== null ? prevStartTime : Date.now() - time, + ); + setIsRunning(true); + } + }, [isRunning, time, startTime]); + + const handleReset = useCallback(() => { + setIsRunning(false); + setTime(0); + setStartTime(null); + }, []); + + const formattedTime = formatTime(time); + + return ( + + {children} + + ); +}; diff --git a/src/renderer/App.css b/src/renderer/App.css index 463b330f..d464fe3c 100644 --- a/src/renderer/App.css +++ b/src/renderer/App.css @@ -21,6 +21,14 @@ body filter: drop-shadow(0 0 3px #000000); /* Adjust color and size as needed */ } +.custom-text-shadow { + text-shadow: + -1px -1px 0 #000, + 1px -1px 0 #000, + -1px 1px 0 #000, + 1px 1px 0 #000; +} + /* HTML Color Grey Codes: https://www.w3schools.com/colors/colors_shades.asp */ diff --git a/src/renderer/App.tsx b/src/renderer/App.tsx index 28e256c9..687df49f 100644 --- a/src/renderer/App.tsx +++ b/src/renderer/App.tsx @@ -1,4 +1,10 @@ -import { MemoryRouter as Router, Routes, Route } from 'react-router-dom'; +import { + MemoryRouter as Router, + Routes, + Route, + useNavigate, +} from 'react-router-dom'; +import { Button } from '@mui/material/'; import './App.css'; import Timers from '../components/UI_AND_UX/timing'; import GitHubButton from '../components/UI_AND_UX/github_button'; @@ -6,8 +12,12 @@ import EvaTelemetry from '../components/EVA/eva_telemetry'; import PanicButton from '../components/HMD_LINK/panic_button'; import ConnectionStrength from '../components/HMD_LINK/conn_strength'; import EVALiveView from '../components/HMD_LINK/eva_live_view'; +import TelemetryPage from '../components/PAGES/telemetry_page'; +import { StopwatchProvider } from '../providers/stopwatch_provider'; function MainPage() { + const navigate = useNavigate(); + return (
@@ -41,7 +51,15 @@ function MainPage() { - + +
Test
@@ -51,9 +69,12 @@ function MainPage() { export default function App() { return ( - - } /> - + + + } /> + } /> + + ); }