feat: zap the winner button with lightning animation
Add ZAP WINNER button in FightViewer after fight ends. Server endpoint POST /api/payments/zap increments zapsReceived on winner bot. Show zap count on bot profile page. Add zaps_received column with migration. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
d1fe4d6e83
commit
d22d739666
@@ -9,6 +9,7 @@ import {
|
||||
setMusicIntensity, stopAllAudio,
|
||||
setMasterMute, isMasterMuted, ensureAudioContext,
|
||||
speakQuestion, speakAnswer, speakNarration,
|
||||
prefetchQuestion, prefetchAnswer, prefetchNarration,
|
||||
sfxRandomComedy, sfxRandomFail, sfxVineBoom, sfxEmotionalDamage,
|
||||
} from '../game/sounds'
|
||||
|
||||
@@ -70,6 +71,28 @@ const glitching = ref(false)
|
||||
const soundOn = ref(true)
|
||||
const insanityMode = ref(false)
|
||||
|
||||
// Zap the winner
|
||||
const zapSent = ref(false)
|
||||
const zapAnimating = ref(false)
|
||||
|
||||
async function zapWinner() {
|
||||
if (!props.fight.winnerId || zapSent.value || zapAnimating.value) return
|
||||
zapAnimating.value = true
|
||||
try {
|
||||
const res = await fetch('/api/payments/zap', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
winnerId: props.fight.winnerId,
|
||||
fightId: props.fight.id,
|
||||
amountSats: 21,
|
||||
}),
|
||||
})
|
||||
if (res.ok) zapSent.value = true
|
||||
} catch { /* ignore */ }
|
||||
setTimeout(() => { zapAnimating.value = false }, 800)
|
||||
}
|
||||
|
||||
// Reactions
|
||||
const REACTION_EMOJIS: Record<string, string> = {
|
||||
fist: '\u{1F44A}',
|
||||
@@ -758,6 +781,20 @@ async function _doReplay() {
|
||||
>
|
||||
{{ insanityMode ? 'INSANITY' : 'SKIP' }}
|
||||
</button>
|
||||
<!-- Zap winner button -->
|
||||
<button
|
||||
v-if="fight.status === 'finished' && fight.winnerId && !isReplaying"
|
||||
class="px-3 py-1.5 border font-display font-bold text-[10px] tracking-wider transition-all
|
||||
disabled:opacity-30 disabled:cursor-not-allowed"
|
||||
:class="zapSent
|
||||
? 'border-neon-yellow/50 text-neon-yellow bg-neon-yellow/10'
|
||||
: 'border-neon-yellow/30 text-neon-yellow hover:bg-neon-yellow/10 hover:border-neon-yellow/50'"
|
||||
:disabled="zapSent || zapAnimating"
|
||||
@click="zapWinner"
|
||||
>
|
||||
<span :class="{ 'zap-flash': zapAnimating }">{{ zapSent ? 'ZAPPED!' : 'ZAP WINNER' }}</span>
|
||||
</button>
|
||||
|
||||
<span class="font-pixel text-[10px] text-text-muted">{{ fight.status === 'finished' ? 'FINISHED' : fight.status.toUpperCase() }}</span>
|
||||
</div>
|
||||
|
||||
@@ -916,6 +953,18 @@ async function _doReplay() {
|
||||
100% { opacity: 0; transform: translateY(-140px) scale(0.6); }
|
||||
}
|
||||
|
||||
/* Zap lightning flash */
|
||||
.zap-flash {
|
||||
animation: zap-bolt 0.8s ease-out;
|
||||
}
|
||||
@keyframes zap-bolt {
|
||||
0% { opacity: 1; filter: brightness(1); }
|
||||
15% { opacity: 1; filter: brightness(3) drop-shadow(0 0 8px #ffe14d); }
|
||||
30% { opacity: 0.6; filter: brightness(1.5); }
|
||||
50% { opacity: 1; filter: brightness(2.5) drop-shadow(0 0 12px #ffe14d); }
|
||||
100% { opacity: 1; filter: brightness(1); }
|
||||
}
|
||||
|
||||
/* VHS scanline overlay on canvas */
|
||||
.glitch-container::after {
|
||||
content: '';
|
||||
|
||||
Reference in New Issue
Block a user