refactor: extract shared FightData interface to fight/types.ts
Move FightData, FightRound, FightBot, FightArenaInfo types from local definitions in FightViewer.vue to shared fight/types.ts. Replace `any` typing in HumanFightPage, FightPage, useFightPolling, and useFightCache with proper typed interfaces. Fix null-safety guards exposed by typing. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
2f7c10e4b8
commit
945a776a5a
@@ -139,8 +139,8 @@ async function initLiveScene() {
|
||||
try {
|
||||
liveScene = await createFightScene({
|
||||
canvas: liveCanvas.value,
|
||||
botA: { name: data.botA.name, seed: data.botA.avatarSeed || data.botA.name, tier: data.botA.tier, archetype: data.botA.archetype, customization: data.botA.customization, wins: data.botA.wins, losses: data.botA.losses },
|
||||
botB: { name: data.botB.name, seed: data.botB.avatarSeed || data.botB.name, tier: data.botB.tier, archetype: data.botB.archetype, customization: data.botB.customization, wins: data.botB.wins, losses: data.botB.losses },
|
||||
botA: { name: data.botA.name, seed: data.botA.avatarSeed || data.botA.name, tier: data.botA.tier, archetype: data.botA.archetype, customization: data.botA.customization ?? undefined, wins: data.botA.wins, losses: data.botA.losses },
|
||||
botB: { name: data.botB.name, seed: data.botB.avatarSeed || data.botB.name, tier: data.botB.tier, archetype: data.botB.archetype, customization: data.botB.customization ?? undefined, wins: data.botB.wins, losses: data.botB.losses },
|
||||
arena: data.arena,
|
||||
})
|
||||
} catch (err) {
|
||||
@@ -219,7 +219,7 @@ async function showLiveOverlay(text: string, color: string, duration: number) {
|
||||
|
||||
async function handleRoundEnd(data: any) {
|
||||
const fd = liveFightData.value
|
||||
if (!fd) {
|
||||
if (!fd || !fd.botA || !fd.botB) {
|
||||
pendingSSEEvents.value.push({ type: 'round_end', data })
|
||||
return
|
||||
}
|
||||
@@ -297,7 +297,7 @@ async function handleRoundEnd(data: any) {
|
||||
|
||||
async function handleFightEnd(data: any) {
|
||||
const fd = liveFightData.value
|
||||
if (!fd) {
|
||||
if (!fd || !fd.botA || !fd.botB) {
|
||||
pendingSSEEvents.value.push({ type: 'fight_end', data })
|
||||
return
|
||||
}
|
||||
@@ -746,7 +746,7 @@ function stopAutoBattle() {
|
||||
<div class="h-full bg-neon-pink transition-all duration-500 ml-auto" :style="{ width: `${liveHpB}%` }" />
|
||||
</div>
|
||||
<span class="font-mono font-bold text-[10px] w-5 text-left tabular-nums" :class="liveHpB > 50 ? 'text-neon-pink' : liveHpB > 20 ? 'text-neon-yellow' : 'text-ko'">{{ liveHpB }}</span>
|
||||
<p class="font-marker text-[10px] tracking-wider truncate text-right text-neon-pink flex-1 min-w-0">{{ liveFightData.botB.name }}</p>
|
||||
<p class="font-marker text-[10px] tracking-wider truncate text-right text-neon-pink flex-1 min-w-0">{{ liveFightData.botB?.name }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Desktop: single-row layout -->
|
||||
@@ -762,7 +762,7 @@ function stopAutoBattle() {
|
||||
<div class="flex-1 h-5 bg-black/50 rounded-sm overflow-hidden border border-neon-pink/30">
|
||||
<div class="h-full bg-gradient-to-l from-neon-pink to-neon-purple transition-all duration-500 ml-auto" :style="{ width: `${liveHpB}%` }" />
|
||||
</div>
|
||||
<p class="font-marker text-sm tracking-wider truncate text-right text-neon-pink flex-shrink-0 max-w-[20%]">{{ liveFightData.botB.name }}</p>
|
||||
<p class="font-marker text-sm tracking-wider truncate text-right text-neon-pink flex-shrink-0 max-w-[20%]">{{ liveFightData.botB?.name }}</p>
|
||||
</div>
|
||||
<div class="flex items-center justify-between mt-0.5">
|
||||
<span class="font-pixel text-[9px]" :class="tierClass(liveFightData.botA.tier || 0)">{{ Math.round(liveFightData.botA.eloRating || 0) }}</span>
|
||||
@@ -771,7 +771,7 @@ function stopAutoBattle() {
|
||||
<span v-if="liveFightData.mode === 'ranked'" class="text-neon-cyan"> | ⚡{{ liveFightData.potSats || 42 }} SATS</span>
|
||||
<span v-if="spectatorCount > 0" class="text-neon-cyan"> | {{ spectatorCount }} watching</span>
|
||||
</span>
|
||||
<span class="font-pixel text-[9px]" :class="tierClass(liveFightData.botB.tier || 0)">{{ Math.round(liveFightData.botB.eloRating || 0) }}</span>
|
||||
<span class="font-pixel text-[9px]" :class="tierClass(liveFightData.botB?.tier || 0)">{{ Math.round(liveFightData.botB?.eloRating || 0) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -900,7 +900,7 @@ function stopAutoBattle() {
|
||||
<div class="h-full bg-neon-pink transition-all duration-500 ml-auto" :style="{ width: `${liveHpB}%` }" />
|
||||
</div>
|
||||
<span class="font-mono font-bold text-[10px] w-5 text-left tabular-nums" :class="liveHpB > 50 ? 'text-neon-pink' : liveHpB > 20 ? 'text-neon-yellow' : 'text-ko'">{{ liveHpB }}</span>
|
||||
<p class="font-marker text-[10px] tracking-wider truncate text-right text-neon-pink flex-1 min-w-0">{{ liveFightData.botB.name }}</p>
|
||||
<p class="font-marker text-[10px] tracking-wider truncate text-right text-neon-pink flex-1 min-w-0">{{ liveFightData.botB?.name }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Desktop: single-row layout -->
|
||||
@@ -916,7 +916,7 @@ function stopAutoBattle() {
|
||||
<div class="flex-1 h-5 bg-black/50 rounded-sm overflow-hidden border border-neon-pink/30">
|
||||
<div class="h-full bg-gradient-to-l from-neon-pink to-neon-purple transition-all duration-500 ml-auto" :style="{ width: `${liveHpB}%` }" />
|
||||
</div>
|
||||
<p class="font-marker text-sm tracking-wider truncate text-right text-neon-pink flex-shrink-0 max-w-[20%]">{{ liveFightData.botB.name }}</p>
|
||||
<p class="font-marker text-sm tracking-wider truncate text-right text-neon-pink flex-shrink-0 max-w-[20%]">{{ liveFightData.botB?.name }}</p>
|
||||
</div>
|
||||
<div class="flex items-center justify-between mt-0.5">
|
||||
<span class="font-pixel text-[9px]" :class="tierClass(liveFightData.botA.tier || 0)">{{ Math.round(liveFightData.botA.eloRating || 0) }}</span>
|
||||
@@ -924,7 +924,7 @@ function stopAutoBattle() {
|
||||
{{ liveFightData.arenaInfo?.name }} | R{{ liveCurrentRound }}
|
||||
<span v-if="spectatorCount > 0" class="text-neon-cyan ml-1">| {{ spectatorCount }} watching</span>
|
||||
</span>
|
||||
<span class="font-pixel text-[9px]" :class="tierClass(liveFightData.botB.tier || 0)">{{ Math.round(liveFightData.botB.eloRating || 0) }}</span>
|
||||
<span class="font-pixel text-[9px]" :class="tierClass(liveFightData.botB?.tier || 0)">{{ Math.round(liveFightData.botB?.eloRating || 0) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -3,13 +3,14 @@ import { ref, computed, onMounted, onUnmounted, nextTick } from 'vue'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import { useNostr } from '../composables/useNostr'
|
||||
import FightViewer from '../components/FightViewer.vue'
|
||||
import type { FightData } from '../game/fight/types'
|
||||
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
const { bot: myBot, isLoggedIn } = useNostr()
|
||||
|
||||
const fightId = ref(route.params.fightId as string)
|
||||
const fight = ref<any>(null)
|
||||
const fight = ref<FightData | null>(null)
|
||||
const phase = ref<'waiting' | 'challenge' | 'submitted' | 'between' | 'finished' | 'replay' | 'error'>('waiting')
|
||||
const error = ref('')
|
||||
|
||||
@@ -198,7 +199,7 @@ async function loadFight() {
|
||||
opponentName.value = enemy?.name || 'Unknown'
|
||||
}
|
||||
if (data.rounds) {
|
||||
roundResults.value = data.rounds.map((r: any) => {
|
||||
roundResults.value = data.rounds.map((r: { roundNumber: number; winnerId: string | null }) => {
|
||||
return { round: r.roundNumber, won: r.winnerId === myBotId.value, hpA: 0, hpB: 0 }
|
||||
})
|
||||
myHp.value = amSideA.value ? data.botAHp : data.botBHp
|
||||
|
||||
Reference in New Issue
Block a user