From d1cbe7091ac72cc18464ada04c872f008eb89cf1 Mon Sep 17 00:00:00 2001 From: david raistrick <1108844+keen99@users.noreply.github.com> Date: Wed, 1 Jul 2026 16:02:35 -0400 Subject: [PATCH] refactor: App.js imports shared turn funcs (DRY), delete duplicates Delete duplicate consts (DEFAULT_MAX_HP/INIT_MOD/MONSTER_DEFAULT_INIT_MOD, generateId, rollD20, formatInitMod) + funcs (sortParticipantsByInitiative, computeTurnOrderAfterRemoval, computeTurnOrderAfterAddition) from App.js. Import from @ttrpg/shared (1-list model). Kills second drift source. CRA resolves @ttrpg/shared via npm workspaces symlink. Build green. --- src/App.js | 47 ++++------------------------------------------- 1 file changed, 4 insertions(+), 43 deletions(-) diff --git a/src/App.js b/src/App.js index 2c1ae77..0a97649 100644 --- a/src/App.js +++ b/src/App.js @@ -1,4 +1,5 @@ import React, { useState, useEffect, useRef, useMemo } from 'react'; +import * as shared from '@ttrpg/shared'; import { initializeApp } from './storage'; import { getAuth, signInAnonymously, onAuthStateChanged, signInWithCustomToken } from './storage'; import { getFirestore, doc, setDoc, addDoc, collection, onSnapshot, updateDoc, deleteDoc, query, orderBy, limit, writeBatch, getStorage, getStorageMode } from './storage'; @@ -46,9 +47,7 @@ if (typeof document !== 'undefined') { // ============================================================================ const APP_VERSION = 'v0.3'; -const DEFAULT_MAX_HP = 10; -const DEFAULT_INIT_MOD = 0; -const MONSTER_DEFAULT_INIT_MOD = 2; +const { DEFAULT_MAX_HP, DEFAULT_INIT_MOD, MONSTER_DEFAULT_INIT_MOD, generateId, rollD20, formatInitMod, sortParticipantsByInitiative, computeTurnOrderAfterRemoval, computeTurnOrderAfterAddition } = shared; const ROLL_DISPLAY_DURATION = 5000; const CONDITIONS = [ @@ -152,24 +151,8 @@ const getPath = { // UTILITY FUNCTIONS // ============================================================================ -const generateId = () => crypto.randomUUID(); -const rollD20 = () => Math.floor(Math.random() * 20) + 1; - -const formatInitMod = (mod) => { - if (mod === undefined || mod === null) return 'N/A'; - return mod >= 0 ? `+${mod}` : `${mod}`; -}; - -const sortParticipantsByInitiative = (participants, originalOrder) => { - return [...participants].sort((a, b) => { - if (a.initiative === b.initiative) { - const indexA = originalOrder.findIndex(p => p.id === a.id); - const indexB = originalOrder.findIndex(p => p.id === b.id); - return indexA - indexB; - } - return b.initiative - a.initiative; - }); -}; +// generateId, rollD20, formatInitMod, sortParticipantsByInitiative, +// computeTurnOrderAfterRemoval/Addition: imported from @ttrpg/shared (1-list model). const LOG_QUERY = [orderBy('timestamp', 'desc'), limit(500)]; @@ -184,28 +167,6 @@ const logAction = async (message, context = {}, undoData = null) => { } }; -// Returns turnOrderIds/currentTurnParticipantId updates when a participant leaves active combat. -const computeTurnOrderAfterRemoval = (encounter, removedId, updatedParticipants) => { - if (!encounter.isStarted) return {}; - const currentIds = encounter.turnOrderIds || []; - const newIds = currentIds.filter(id => id !== removedId); - const updates = { turnOrderIds: newIds }; - if (encounter.currentTurnParticipantId === removedId) { - const removedPos = currentIds.indexOf(removedId); - const candidates = [...currentIds.slice(removedPos + 1), ...currentIds.slice(0, removedPos)]; - const nextId = candidates.find(id => updatedParticipants.find(p => p.id === id && p.isActive)) ?? null; - updates.currentTurnParticipantId = nextId; - } - return updates; -}; - -// Returns turnOrderIds update when a participant re-enters active combat mid-encounter. -const computeTurnOrderAfterAddition = (encounter, addedId) => { - if (!encounter.isStarted) return {}; - const currentIds = encounter.turnOrderIds || []; - if (currentIds.includes(addedId)) return {}; - return { turnOrderIds: [...currentIds, addedId] }; -}; // ============================================================================ // CUSTOM HOOKS