Initialize Vyryi front-end project with React, Vite, and TypeScript. Add essential files including configuration, environment variables, and basic structure. Implement authentication, routing, and API interaction for a Telegram Mini App UI. Include README and styling for a cohesive user experience.
This commit is contained in:
118
src/pages/HomePage.tsx
Normal file
118
src/pages/HomePage.tsx
Normal file
@@ -0,0 +1,118 @@
|
||||
import { useEffect, useState } from 'react'
|
||||
import { Link, Navigate } from 'react-router-dom'
|
||||
import { api } from '../api'
|
||||
import { useAuth } from '../auth'
|
||||
import type { GameStub } 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<GameStub[]>([])
|
||||
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">Скоро</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>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user