Files
vyryi-front/src/pages/HomePage.tsx
Redsandy f0733d6918 Refactor game API and introduce Forest Fortune game
- Updated API types to replace `GameStub` with `GameInfo` and added `SessionSpinResult` and `SpinOutcome` types.
- Modified API requests in `api.ts` to use the new game types.
- Implemented the `ForestFortuneGame` component with game logic, including spinning mechanics and win animations.
- Created supporting components: `CoinFlight`, `ReelColumn`, `WinCounter`, and `WinLinesOverlay` for game UI.
- Added CSS styles for the Forest Fortune game layout and animations.
- Updated `GamePage` and `HomePage` to integrate the new game and reflect its status correctly.
2026-08-12 15:27:27 +03:00

121 lines
4.0 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { useEffect, useState } from 'react'
import { Link, Navigate } from 'react-router-dom'
import { api } from '../api'
import { useAuth } from '../auth'
import type { GameInfo } from '../types'
import { GATE_FROM_KEY } from '../vfx/gateCaps'
const DEPOSIT_AMOUNTS = [100, 500, 1000]
export function HomePage() {
const { user, loading, setUser } = useAuth()
const [games, setGames] = useState<GameInfo[]>([])
const [depositOpen, setDepositOpen] = useState(false)
const [busy, setBusy] = useState(false)
const [message, setMessage] = useState<string | null>(null)
const [fromGate, setFromGate] = useState(false)
useEffect(() => {
void api.listGames().then(setGames).catch(() => setGames([]))
}, [])
useEffect(() => {
try {
if (sessionStorage.getItem(GATE_FROM_KEY) === '1') {
sessionStorage.removeItem(GATE_FROM_KEY)
setFromGate(true)
}
} catch {
/* ignore */
}
}, [])
if (!loading && user && !user.agreement_accepted) {
return <Navigate to="/" replace />
}
if (!loading && !user) {
return <Navigate to="/" replace />
}
async function deposit(amount: number) {
setBusy(true)
setMessage(null)
try {
const wallet = await api.deposit(amount)
if (user) {
setUser({ ...user, balance: wallet.balance })
}
setMessage(`Зачислено ${amount}`)
setDepositOpen(false)
} catch (err) {
setMessage(err instanceof Error ? err.message : 'Ошибка пополнения')
} finally {
setBusy(false)
}
}
return (
<main className={fromGate ? 'home home--from-gate' : 'home'}>
<header className="home__top">
<div className="home__brand-row">
<img src="/logo.png" alt="" className="home__mark" />
<span className="brand brand--sm">Vyryi</span>
</div>
<div className="balance-panel">
<span className="balance-panel__label">Баланс</span>
<strong className="balance-panel__value">{user?.balance ?? '—'}</strong>
<button type="button" className="btn btn--gold btn--sm" onClick={() => setDepositOpen(true)}>
Пополнить
</button>
</div>
</header>
{message && <p className="toast">{message}</p>}
<section className="games" aria-labelledby="games-title">
<h2 id="games-title">Слоты</h2>
<p className="section-lead">Лесная Удача уже открыта остальные ждут своего часа.</p>
<ul className="games__list">
{games.map((game) => (
<li key={game.slug}>
<Link to={`/games/${game.slug}`} className="game-link">
<span className="game-link__title">{game.title}</span>
<span className="game-link__status">
{game.status === 'available' ? 'Открыто' : 'Скоро'}
</span>
<span className="game-link__desc">{game.description}</span>
</Link>
</li>
))}
</ul>
</section>
{depositOpen && (
<div className="modal" role="dialog" aria-modal="true" aria-labelledby="deposit-title">
<div className="modal__panel">
<h3 id="deposit-title">Пополнение</h3>
<p className="section-lead">Тестовая заглушка. Вывод недоступен.</p>
<div className="deposit-grid">
{DEPOSIT_AMOUNTS.map((amount) => (
<button
key={amount}
type="button"
className="btn btn--outline"
disabled={busy}
onClick={() => void deposit(amount)}
>
+{amount}
</button>
))}
</div>
<button type="button" className="btn btn--ghost" onClick={() => setDepositOpen(false)}>
Закрыть
</button>
</div>
</div>
)}
</main>
)
}