feat: show rejoin link when bot is already in a fight
- Track active fight IDs per bot in orchestrator (Set → Map) - Return fightId in "already in fight" error responses (409) - Frontend shows "REJOIN FIGHT" link instead of generic error Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
64273cf145
commit
e31d49898b
@@ -21,6 +21,7 @@ const step = ref<string>('login')
|
||||
const isHumanMode = ref(false)
|
||||
const selectedHumanSeed = ref('baby_fighter_1')
|
||||
const error = ref('')
|
||||
const activeFightLink = ref('')
|
||||
const rateLimitCountdown = ref(0)
|
||||
let rateLimitTimer: ReturnType<typeof setInterval> | null = null
|
||||
const isJoining = ref(false)
|
||||
@@ -105,6 +106,7 @@ const archetypeList = [
|
||||
function handleError(e: unknown, fallback: string) {
|
||||
const msg = e instanceof Error ? e.message : fallback
|
||||
error.value = msg
|
||||
activeFightLink.value = ''
|
||||
// Parse "Slow down" with retry seconds from the error message or check for countdown pattern
|
||||
if (msg.toLowerCase().includes('too many requests') || msg.toLowerCase().includes('slow down')) {
|
||||
startRateLimitTimer(msg)
|
||||
@@ -394,8 +396,13 @@ async function fight() {
|
||||
return // Don't reset flag — navigation will unmount component
|
||||
} else {
|
||||
const data = await res.json()
|
||||
const msg = data.retryAfterSec ? `${data.error} (${data.retryAfterSec}s)` : (data.error || 'Failed to join.')
|
||||
handleError(new Error(msg), 'Failed to join.')
|
||||
if (data.fightId) {
|
||||
activeFightLink.value = data.fightId
|
||||
error.value = 'You\'re already in a fight!'
|
||||
} else {
|
||||
const msg = data.retryAfterSec ? `${data.error} (${data.retryAfterSec}s)` : (data.error || 'Failed to join.')
|
||||
handleError(new Error(msg), 'Failed to join.')
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
error.value = 'Network error.'
|
||||
@@ -443,8 +450,13 @@ async function practice() {
|
||||
return // Don't reset flag — navigation will unmount component
|
||||
} else {
|
||||
const data = await res.json()
|
||||
const msg = data.retryAfterSec ? `${data.error} (${data.retryAfterSec}s)` : (data.error || 'Failed to start practice fight.')
|
||||
handleError(new Error(msg), 'Practice fight failed.')
|
||||
if (data.fightId) {
|
||||
activeFightLink.value = data.fightId
|
||||
error.value = 'You\'re already in a fight!'
|
||||
} else {
|
||||
const msg = data.retryAfterSec ? `${data.error} (${data.retryAfterSec}s)` : (data.error || 'Failed to start practice fight.')
|
||||
handleError(new Error(msg), 'Practice fight failed.')
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
error.value = 'Network error.'
|
||||
@@ -1144,6 +1156,14 @@ function handleSignOut() {
|
||||
<!-- Error display -->
|
||||
<div v-if="error" class="mt-4 p-3 border-2 border-ko/30 bg-ko/5 text-center">
|
||||
<p class="font-mono text-xs text-ko">{{ error }}</p>
|
||||
<RouterLink
|
||||
v-if="activeFightLink"
|
||||
:to="`/arena/${activeFightLink}`"
|
||||
class="inline-block mt-2 px-6 py-2 bg-neon-cyan/10 border-2 border-neon-cyan text-neon-cyan
|
||||
font-display font-black text-xs tracking-widest hover:bg-neon-cyan/20 transition-all neon-border-cyan"
|
||||
>
|
||||
REJOIN FIGHT
|
||||
</RouterLink>
|
||||
<div v-if="rateLimitCountdown > 0" class="mt-2 flex items-center justify-center gap-2">
|
||||
<div class="w-4 h-4 border-2 border-neon-purple/60 border-t-neon-purple rounded-full animate-spin" />
|
||||
<p class="font-display font-bold text-sm text-neon-purple tracking-wider">
|
||||
|
||||
Reference in New Issue
Block a user