Skip to content

Commit

Permalink
add self-updating timer provider
Browse files Browse the repository at this point in the history
  • Loading branch information
abhi-arya1 committed Jan 3, 2024
1 parent 8a5c840 commit 8c95a92
Show file tree
Hide file tree
Showing 6 changed files with 193 additions and 96 deletions.
6 changes: 4 additions & 2 deletions src/components/HMD_LINK/conn_strength.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
<div className="pt-3">
<div className={`pt-3 ${className}`}>
<div
className={`text-sm flex flex-row bg-gray-300 rounded-xl p-2 items-center justify-center ${
ping > 15 ? 'border-2 border-red-500' : ''
Expand Down
22 changes: 22 additions & 0 deletions src/components/PAGES/telemetry_page.tsx
Original file line number Diff line number Diff line change
@@ -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 (
<div className="h-full flex flex-row gap-x-4 items-center justify-center">
<Timers />
<Button
onClick={() => {
navigate('/');
}}
>
Back to Home
</Button>
</div>
);
}

export default TelemetryPage;
114 changes: 25 additions & 89 deletions src/components/UI_AND_UX/timing.tsx
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -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 (
<div className="flex flex-row">
<div className="text-4xl font-semibold mb-4">
{this.formatTime(time)}
</div>
<IconButton
aria-label="start-stop"
onClick={this.handleStartStop}
className="text-gray-200 h-9 pl-4 hover:text-gray-10"
>
{isRunning ? <Pause /> : <PlayArrow />}
</IconButton>
<IconButton
aria-label="start-stop"
onClick={this.handleReset}
className="text-gray-200 h-9 pl-3 hover:text-gray-10"
>
<Loop />
</IconButton>
</div>
);
}
return (
<div className="flex flex-row">
<div className="text-4xl font-semibold mb-4">{formattedTime}</div>
<IconButton
aria-label="start-stop"
onClick={handleStartStop}
className="text-gray-200 h-9 pl-4 hover:text-gray-10"
>
{isRunning ? <Pause /> : <PlayArrow />}
</IconButton>
<IconButton
aria-label="start-stop"
onClick={handleReset}
className="text-gray-200 h-9 pl-3 hover:text-gray-10"
>
<Loop />
</IconButton>
</div>
);
}

function Timers() {
Expand Down
108 changes: 108 additions & 0 deletions src/providers/stopwatch_provider.tsx
Original file line number Diff line number Diff line change
@@ -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<StopwatchContextType>(defaultContext);

export const useStopwatch = () => useContext(StopwatchContext);

export const StopwatchProvider: FunctionComponent<StopwatchProviderProps> = ({
children,
}) => {
const [startTime, setStartTime] = useState<number | null>(null);
const [time, setTime] = useState<number>(0);
const [isRunning, setIsRunning] = useState<boolean>(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 (
<StopwatchContext.Provider
value={{
time,
isRunning,
formattedTime,
handleStartStop,
handleReset,
}}
>
{children}
</StopwatchContext.Provider>
);
};
8 changes: 8 additions & 0 deletions src/renderer/App.css
Original file line number Diff line number Diff line change
Expand Up @@ -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 */

31 changes: 26 additions & 5 deletions src/renderer/App.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,23 @@
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';
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 (
<div className="h-full flex flex-row gap-x-4">
<div className="flex flex-col items-left pl-3 justify-center">
Expand Down Expand Up @@ -41,7 +51,15 @@ function MainPage() {
<PanicButton />
<ConnectionStrength desc="EVA 1" ping={25} />
<ConnectionStrength desc="EVA 2" ping={5} />
<ConnectionStrength desc="ROVER" ping={10} />
<ConnectionStrength desc="ROVER" ping={10} className="pb-3" />
<Button
className="bg-gray-300 text-white rounded-xl p-2 normal-case hover:bg-gray-500 custom-text-shadow"
onClick={() => {
navigate('/telemetry');
}}
>
Raw Telemetry
</Button>
</div>
<div className="pl-2 bg-gray-200 flex-grow rounded-l-2xl">Test</div>
</div>
Expand All @@ -51,9 +69,12 @@ function MainPage() {
export default function App() {
return (
<Router>
<Routes>
<Route path="/" element={<MainPage />} />
</Routes>
<StopwatchProvider>
<Routes>
<Route path="/" element={<MainPage />} />
<Route path="/telemetry" element={<TelemetryPage />} />
</Routes>
</StopwatchProvider>
</Router>
);
}

0 comments on commit 8c95a92

Please sign in to comment.