diff --git a/app/AppContext.tsx b/app/AppContext.tsx index c1f30dc..3205f5c 100644 --- a/app/AppContext.tsx +++ b/app/AppContext.tsx @@ -1,41 +1,34 @@ 'use client'; -import { createContext, useContext, useEffect, useState, ReactNode } from 'react'; +import { createContext, useContext, useEffect, useState } from 'react'; import useSocket from '@/hooks/useSocket'; -import type { GameUpdate, Tilt } from '@/types'; + +import { GAME_START, LOCAL_DEFAULTS } from '@/constants'; +import type { Dispatch, ReactNode, SetStateAction } from 'react'; +import type { GameUpdate, LocalSettings, Settings, Tilt } from '@/types'; const AppContext = createContext(undefined); -const gameStart: GameUpdate = { - dmID: '', - spectatorID: '', - cards: [], - settings: { - positionBack: false, - positionFront: false, - prophecy: false, - notes: false, - cardStyle: 'color', - }, - tilts: Array.from({ length: 5 }, () => []), -}; - export interface AppContext { gameData: GameUpdate; + isDM: boolean; noGame: boolean; - tilt: Tilt[]; selectCardIndex: number; + settings: Settings; + tilt: Tilt[]; emitFlip: (cardIndex: number) => void; emitSettings: (gameData: GameUpdate) => void; emitRedraw: (cardIndex: number) => void; emitSelect: (cardID: string) => void; setGameID: (gameID: string) => void; + setLocalSettings: Dispatch>; setSelectCardIndex: (cardIndex: number) => void; setTilt: (tilt: Tilt[]) => void; } export function AppProvider({ children }: { children: ReactNode }) { - const [gameData, setGameData] = useState(gameStart); + const [gameData, setGameData] = useState({ ...GAME_START }); + const [localSettings, setLocalSettings] = useState(() => ({ ...LOCAL_DEFAULTS })); const [gameID, setGameID] = useState(''); const [noGame, setNoGame] = useState(false); const [selectCardIndex, setSelectCardIndex] = useState(-1); @@ -61,16 +54,22 @@ export function AppProvider({ children }: { children: ReactNode }) { emitSelect(selectCardIndex, cardID); }; + const { dmID } = gameData; + const isDM = !!dmID; + const appInterface = { gameData, + isDM, noGame, - tilt, selectCardIndex, + settings: { ...gameData.settings, ...localSettings }, + tilt, emitFlip, emitSettings, emitRedraw, emitSelect: handleSelect, setGameID, + setLocalSettings, setSelectCardIndex, setTilt, }; diff --git a/components/Settings.tsx b/components/Settings.tsx index 83487a5..16cbb53 100644 --- a/components/Settings.tsx +++ b/components/Settings.tsx @@ -10,7 +10,9 @@ import CopyButton from '@/components/CopyButton'; import GitHubButton from '@/components/GitHubButton'; import Scrim from '@/components/Scrim'; import Switch from '@/components/Switch'; -import { CardStyle } from '@/types'; + +import { LOCAL_SETTINGS, SPECTATOR_SETTINGS } from '@/constants'; +import type { CardStyle, LocalSettings } from '@/types'; const cinzel = Cinzel_Decorative({ variable: '--font-cinzel', @@ -22,19 +24,20 @@ const cardStyleOptions: CardStyle[] = ['standard', 'color', 'grayscale']; export default function Settings() { const [open, setOpen] = useState(false); - const { gameData, emitSettings } = useAppContext(); + const { gameData, isDM, settings, emitSettings, setLocalSettings } = useAppContext(); - const { dmID } = gameData; - const isDM = !!dmID; - - const togglePermission = (key: string) => { - emitSettings({ - ...gameData, - settings: { - ...gameData.settings, - [key]: !gameData.settings[key], - }, - }); + const togglePermission = (key: keyof LocalSettings) => { + if (LOCAL_SETTINGS.includes(key)) { + setLocalSettings((prev) => ({ ...prev, [key]: !prev[key] })); + } else if (isDM) { + emitSettings({ + ...gameData, + settings: { + ...gameData.settings, + [key]: !gameData.settings[key], + }, + }); + } }; const tuneRadio = (cardStyle: CardStyle) => { @@ -47,14 +50,25 @@ export default function Settings() { }); }; + const Icon = () => ( + + ); + const Links = () => ( <> - + {isDM && ( + + )} ( <> - {Object.entries(gameData.settings) + {Object.entries(settings) .filter(([_key, value]) => typeof value === 'boolean') + .filter(([key]) => isDM || SPECTATOR_SETTINGS.includes(key)) .map(([key, value]) => ( togglePermission(key)} /> ))} ); - const CardStyle = () => ( -
-
Card style:
-
- {cardStyleOptions.map((option, index) => ( - + ))} +
+
+ ) : null; - return isDM ? ( + return (
setOpen((prev) => !prev)} @@ -122,12 +138,7 @@ export default function Settings() {
- + - ) : null; + ); } diff --git a/components/TiltCard.tsx b/components/TiltCard.tsx index c9bce43..bdc51e6 100644 --- a/components/TiltCard.tsx +++ b/components/TiltCard.tsx @@ -12,37 +12,45 @@ export default function TiltCard({ className?: string; }) { const cardRef = useRef(null); - const { gameData, setTilt } = useAppContext(); + const { + gameData, + settings: { tilt }, + setTilt, + } = useAppContext(); useEffect(() => { const card = cardRef.current; if (!card) return; - const tilt = gameData.tilts[cardIndex]; - if (!tilt) { - card.style.transform = `rotateX(0deg) rotateY(0deg)`; - return; + if (tilt) { + const tilt = gameData.tilts[cardIndex]; + if (!tilt) { + card.style.transform = `rotateX(0deg) rotateY(0deg)`; + return; + } + + const tilted = tilt.filter(({ rotateX, rotateY }) => rotateX || rotateY); + + const { totalX, totalY } = tilted.reduce( + ({ totalX, totalY }, { rotateX, rotateY }) => ({ + totalX: totalX + rotateX, + totalY: totalY + rotateY, + }), + { totalX: 0, totalY: 0 }, + ); + + const rotateX = totalX / tilted.length; + const rotateY = totalY / tilted.length; + + if (rotateX || rotateY) { + card.style.transform = `rotateX(${rotateX}deg) rotateY(${rotateY}deg)`; + } else { + card.style.transform = `rotateX(0deg) rotateY(0deg)`; + } + } else if (card.style.transform !== 'rotateX(0deg) rotateY(0deg)') { + card.style.transform = 'rotateX(0deg) rotateY(0deg)'; } - - const tilted = tilt.filter(({ rotateX, rotateY }) => rotateX || rotateY); - - const { totalX, totalY } = tilted.reduce( - ({ totalX, totalY }, { rotateX, rotateY }) => ({ - totalX: totalX + rotateX, - totalY: totalY + rotateY, - }), - { totalX: 0, totalY: 0 }, - ); - - const rotateX = totalX / tilted.length; - const rotateY = totalY / tilted.length; - - if (rotateX || rotateY) { - card.style.transform = `rotateX(${rotateX}deg) rotateY(${rotateY}deg)`; - } else { - card.style.transform = `rotateX(0deg) rotateY(0deg)`; - } - }, [gameData]); + }, [tilt, gameData]); const handleMouseMove = (e: React.MouseEvent) => { const card = cardRef.current; @@ -68,7 +76,11 @@ export default function TiltCard({ }; return ( -
+
{children}
diff --git a/constants/index.ts b/constants/index.ts new file mode 100644 index 0000000..2721031 --- /dev/null +++ b/constants/index.ts @@ -0,0 +1,33 @@ +export * from '@/constants/standardCards'; +export * from '@/constants/tarokka'; +export * from '@/constants/tarokkaCards'; +export * from '@/constants/time'; + +import type { GameUpdate, LocalSettings, Settings } from '@/types'; + +export const SETTINGS: Settings = { + cardStyle: 'color', + notes: false, + positionBack: false, + positionFront: false, + prophecy: false, + tilt: true, + remoteTilt: false, +}; + +export const GAME_START: GameUpdate = { + dmID: '', + spectatorID: '', + cards: [], + settings: SETTINGS, + tilts: Array.from({ length: 5 }, () => []), +}; + +export const LOCAL_DEFAULTS: LocalSettings = { + tilt: true, + remoteTilt: true, +}; + +export const LOCAL_SETTINGS = ['tilt', 'remoteTilt']; + +export const SPECTATOR_SETTINGS = ['tilt', 'remoteTilt']; diff --git a/lib/GameStore.ts b/lib/GameStore.ts index c13d114..64d7fe6 100644 --- a/lib/GameStore.ts +++ b/lib/GameStore.ts @@ -1,7 +1,8 @@ import Deck from '@/lib/TarokkaDeck'; import generateID from '@/tools/simpleID'; import parseMilliseconds from '@/tools/parseMilliseconds'; -import { HOUR, DAY } from '@/constants/time'; + +import { HOUR, DAY, SETTINGS } from '@/constants'; import { GameState, GameUpdate, Settings, Tilt } from '@/types'; const deck = new Deck(); @@ -84,13 +85,7 @@ export default class GameStore { players: new Set(), cards: deck.getHand(), lastUpdated: Date.now(), - settings: { - positionBack: true, - positionFront: true, - prophecy: true, - notes: true, - cardStyle: 'color', - }, + settings: SETTINGS, tilts: Array.from({ length: 5 }, () => []), }; diff --git a/types/index.ts b/types/index.ts index b98319f..cfbd125 100644 --- a/types/index.ts +++ b/types/index.ts @@ -4,11 +4,18 @@ export type CardStyle = 'standard' | 'color' | 'grayscale'; export type Deck = 'high' | 'common' | 'both' | 'back' | 'all'; export interface Settings { + cardStyle: CardStyle; + notes: boolean; positionBack: boolean; positionFront: boolean; prophecy: boolean; - notes: boolean; - cardStyle: CardStyle; + tilt: boolean; + remoteTilt: boolean; +} + +export interface LocalSettings { + tilt: boolean; + remoteTilt: boolean; } export interface StandardCard {