Compare commits

...

4 Commits

Author SHA1 Message Date
Vikeo
4130e069bb icon 2025-02-23 23:43:24 +01:00
Vikeo
e7e5882859 add basic timer that always is showing 2025-02-23 23:42:01 +01:00
Viktor Rådberg
c6039c2a53 update deps (#40)
* update deps

* fix lint
2025-02-22 12:15:28 +01:00
Viktor Rådberg
6d6da2ad79 fix commander damage text not being centered on chrome (#39) 2025-02-22 11:45:35 +01:00
10 changed files with 4154 additions and 3670 deletions

View File

@@ -1,7 +1,7 @@
{ {
"name": "life-trinket", "name": "life-trinket",
"private": true, "private": true,
"version": "1.0.0", "version": "1.0.1",
"type": "commonjs", "type": "commonjs",
"engines": { "engines": {
"node": ">=20", "node": ">=20",
@@ -17,39 +17,42 @@
"deploy": "bun run build && firebase deploy --only hosting" "deploy": "bun run build && firebase deploy --only hosting"
}, },
"dependencies": { "dependencies": {
"firebase": "^10.3.0", "@mui/icons-material": "^6.4.5",
"@mui/material": "^6.4.5",
"firebase": "^10.14.1",
"ga-4-react": "^0.1.281", "ga-4-react": "^0.1.281",
"react": "^18.2.0", "react": "^18.3.1",
"react-dom": "^18.2.0", "react-dom": "^18.3.1",
"react-screen-wake-lock": "^3.0.2", "react-screen-wake-lock": "^3.0.2",
"react-swipeable": "^7.0.1", "react-swipeable": "^7.0.2",
"react-twc": "^1.3.0", "react-timer-hook": "^3.0.8",
"semver": "^7.6.2", "react-twc": "^1.4.2",
"zod": "^3.22.4" "semver": "^7.7.1",
"zod": "^3.24.2"
}, },
"devDependencies": { "devDependencies": {
"@emotion/react": "^11.11.4", "@emotion/react": "^11.14.0",
"@emotion/styled": "^11.11.5", "@emotion/styled": "^11.14.0",
"@savvywombat/tailwindcss-grid-areas": "^4.0.0", "@savvywombat/tailwindcss-grid-areas": "^4.0.0",
"@svgr/cli": "^8.1.0", "@svgr/cli": "^8.1.0",
"@types/react": "^18.3.1", "@types/react": "^18.3.18",
"@types/react-dom": "^18.3.0", "@types/react-dom": "^18.3.5",
"@types/semver": "^7.5.8", "@types/semver": "^7.5.8",
"@typescript-eslint/eslint-plugin": "^7.8.0", "@typescript-eslint/eslint-plugin": "^7.18.0",
"@typescript-eslint/parser": "^7.8.0", "@typescript-eslint/parser": "^7.18.0",
"@vitejs/plugin-react-swc": "^3.6.0", "@vitejs/plugin-react-swc": "^3.8.0",
"autoprefixer": "^10.4.19", "autoprefixer": "^10.4.20",
"eslint": "^8.45.0", "eslint": "^8.57.1",
"eslint-plugin-react-hooks": "^4.6.2", "eslint-plugin-react-hooks": "^4.6.2",
"eslint-plugin-react-refresh": "^0.4.6", "eslint-plugin-react-refresh": "^0.4.19",
"firebase-tools": "^13.7.5", "firebase-tools": "^13.31.2",
"install": "^0.13.0", "install": "^0.13.0",
"postcss": "^8.4.38", "postcss": "^8.5.3",
"prettier": "2.8.8", "prettier": "2.8.8",
"prop-types": "^15.8.1", "prop-types": "^15.8.1",
"tailwindcss": "^3.4.3", "tailwindcss": "^3.4.17",
"typescript": "^5.4.5", "typescript": "^5.7.3",
"vite": "^5.2.10", "vite": "^5.4.14",
"vite-plugin-pwa": "^0.20.0" "vite-plugin-pwa": "^0.20.5"
} }
} }

7619
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -30,7 +30,7 @@ const CommanderDamageButton = twc.button<RotationButtonProps>((props) => [
]); ]);
const CommanderDamageTextContainer = twc.div<RotationDivProps>((props) => [ const CommanderDamageTextContainer = twc.div<RotationDivProps>((props) => [
'relative top-1/2 left-1/2 tabular-nums pointer-events-none select-none webkit-user-select-none', 'relative -translate-y-1/2 top-1/2 left-1/2 tabular-nums pointer-events-none select-none webkit-user-select-none',
props.$rotation === Rotation.SideFlipped || props.$rotation === Rotation.Side props.$rotation === Rotation.SideFlipped || props.$rotation === Rotation.Side
? 'rotate-[270deg]' ? 'rotate-[270deg]'
: '', : '',

View File

@@ -2,5 +2,4 @@ import { twc } from 'react-twc';
export const Paragraph = twc.p`text-text-primary`; export const Paragraph = twc.p`text-text-primary`;
// eslint-disable-next-line react-refresh/only-export-components
export const H1 = twc.h1`text-text-primary;`; export const H1 = twc.h1`text-text-primary;`;

View File

@@ -0,0 +1,37 @@
import { useStopwatch } from 'react-timer-hook';
import { TimerStyle, TimerWrapper } from './Timer';
import { Time } from './Time';
import PlayArrowIcon from '@mui/icons-material/PlayArrow';
import PauseIcon from '@mui/icons-material/Pause';
export const NegativeStopWatch = ({
autoStart = false,
}: {
autoStart?: boolean;
}) => {
// Initialize the timer using the useTimer hook.
const { seconds, minutes, hours, isRunning, start, pause } = useStopwatch({
autoStart,
});
return (
<TimerWrapper>
<TimerStyle>
{'-'}
<Time time={{ hours, minutes, seconds }} />
<div>
{!isRunning && (
<button onClick={start}>
<PlayArrowIcon />
</button>
)}
{isRunning && (
<button onClick={pause}>
<PauseIcon />
</button>
)}
</div>
</TimerStyle>
</TimerWrapper>
);
};

View File

@@ -0,0 +1,9 @@
import { formatTime } from '../../../Utils/formatTime';
export const Time = ({
time,
}: {
time: { hours: number; minutes: number; seconds: number };
}) => {
return <div className="tabular-nums">{formatTime({ time })}</div>;
};

View File

@@ -0,0 +1,84 @@
import { useState } from 'react';
import { useTimer } from 'react-timer-hook';
import { NegativeStopWatch } from './NegativeStopWatch';
import { twc } from 'react-twc';
import PauseIcon from '@mui/icons-material/Pause';
import RestartAltIcon from '@mui/icons-material/RestartAlt';
import PlayArrowIcon from '@mui/icons-material/PlayArrow';
import { Time } from './Time';
export const TimerWrapper = twc.div`
absolute z-50 top-0 w-screen flex items-center flex-col
`;
export const TimerStyle = twc.div`
flex gap-2 bg-interface-recentDifference-background py-1 px-4 rounded-full
`;
const TIMER_MINUTES = 50;
export const Timer = () => {
// Set the default expiry time to 50 minutes from now.
const defaultExpiry = new Date();
defaultExpiry.setMinutes(defaultExpiry.getMinutes() + TIMER_MINUTES);
const [hasStarted, setHasStarted] = useState(false);
// Initialize the timer using the useTimer hook.
const { seconds, minutes, hours, isRunning, start, pause, restart } =
useTimer({
expiryTimestamp: defaultExpiry,
onExpire: () => console.warn('Timer expired'),
autoStart: false,
});
// Function to restart the timer for 50 minutes.
const handleRestart = () => {
const time = new Date();
time.setMinutes(time.getMinutes() + TIMER_MINUTES);
restart(time);
};
const handleStart = () => {
setHasStarted(true);
start();
};
const handlePause = () => {
setHasStarted(false);
pause();
};
if (hasStarted && !isRunning) {
return <NegativeStopWatch autoStart />;
}
return (
<TimerWrapper>
<TimerStyle>
<Time
time={{
hours,
minutes,
seconds,
}}
/>
<div>
{!isRunning && (
<button onClick={handleStart}>
<PlayArrowIcon />
</button>
)}
{isRunning && (
<button onClick={handlePause}>
<PauseIcon />
</button>
)}
<button onClick={handleRestart}>
<RestartAltIcon />
</button>
</div>
</TimerStyle>
</TimerWrapper>
);
};

View File

@@ -6,6 +6,7 @@ import { PreStartMode } from '../../Types/Settings';
import LifeCounter from '../LifeCounter/LifeCounter'; import LifeCounter from '../LifeCounter/LifeCounter';
import { RoulettePlayerCard } from '../PreStartGame/Games/RandomKing/RoulettePlayerCard'; import { RoulettePlayerCard } from '../PreStartGame/Games/RandomKing/RoulettePlayerCard';
import { GridLayout } from '../Views/Play'; import { GridLayout } from '../Views/Play';
import { Timer } from '../Misc/Timer/Timer';
const getGridArea = (player: PlayerType) => { const getGridArea = (player: PlayerType) => {
switch (player.index) { switch (player.index) {
@@ -36,6 +37,7 @@ export const Players = ({ gridLayout }: { gridLayout: GridLayout }) => {
return ( return (
<PlayersWrapper> <PlayersWrapper>
<div className={`grid w-full h-full gap-1 box-border ${gridLayout} `}> <div className={`grid w-full h-full gap-1 box-border ${gridLayout} `}>
<Timer />
{players.map((player) => { {players.map((player) => {
const gridArea = getGridArea(player); const gridArea = getGridArea(player);
return ( return (

View File

@@ -19,6 +19,7 @@ export const Play = () => {
useGlobalSettings(); useGlobalSettings();
let gridLayout: GridLayout; let gridLayout: GridLayout;
switch (players.length) { switch (players.length) {
case 1: case 1:
if (initialGameSettings?.orientation === Orientation.Portrait) { if (initialGameSettings?.orientation === Orientation.Portrait) {
@@ -101,7 +102,6 @@ export const Play = () => {
settings.preStartMode !== PreStartMode.None && settings.preStartMode !== PreStartMode.None &&
!playing && !playing &&
settings.showStartingPlayer && <PreStart />} settings.showStartingPlayer && <PreStart />}
<Players gridLayout={gridLayout} /> <Players gridLayout={gridLayout} />
</MainWrapper> </MainWrapper>
); );

17
src/Utils/formatTime.ts Normal file
View File

@@ -0,0 +1,17 @@
export const formatTime = ({
time,
}: {
time: { hours: number; minutes: number; seconds: number };
}) => {
const h = time.hours.toString().padStart(2, '0');
const m = time.minutes.toString().padStart(2, '0');
const s = time.seconds.toString().padStart(2, '0');
if (time.hours > 0) {
return `${h}:${m}:${s}`;
} else if (time.minutes > 0) {
return `${m}:${s}`;
} else {
return s;
}
};