Revert "Lobby: CSS 3D dice tray at the top right next to the chat"
Back to the card-style dice tray above the hotbar. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HJhXuLD2Z6hv3Wqeq9UL9K
This commit is contained in:
@@ -1,6 +1,5 @@
|
||||
import { useCallback, useEffect, useRef, useState } from 'react'
|
||||
import PaperSheet from './PaperSheet.jsx'
|
||||
import DiceTray from './DiceTray.jsx'
|
||||
|
||||
const J = { 'Content-Type': 'application/json' }
|
||||
const send = (url, method, body) => fetch(url, { method, headers: J, body: body === undefined ? undefined : JSON.stringify(body) })
|
||||
@@ -129,6 +128,7 @@ function Card({ m, isDm, onOpen, onKick }) {
|
||||
|
||||
const clock = (t) => new Date(t * 1000).toLocaleTimeString('en-GB', { hour: '2-digit', minute: '2-digit' })
|
||||
const DICE = [4, 6, 8, 10, 12, 20, 100]
|
||||
const reduced = () => window.matchMedia?.('(prefers-reduced-motion: reduce)').matches
|
||||
|
||||
// Chat, whispers, event log and dice rolls arrive through one feed that is polled while the page is open.
|
||||
function useFeed(id, onEvent, onRoll) {
|
||||
@@ -271,6 +271,44 @@ function DiceBar({ id, to, poll, isDm }) {
|
||||
)
|
||||
}
|
||||
|
||||
function Die({ sides, value, delay }) {
|
||||
const [shown, setShown] = useState(reduced() ? value : null)
|
||||
useEffect(() => {
|
||||
if (reduced()) return
|
||||
const t0 = Date.now(), run = 650 + delay
|
||||
const iv = setInterval(() => {
|
||||
if (Date.now() - t0 >= run) { setShown(value); clearInterval(iv) } else setShown(1 + Math.floor(Math.random() * sides))
|
||||
}, 70)
|
||||
return () => clearInterval(iv)
|
||||
}, [sides, value, delay])
|
||||
const rolling = shown !== value
|
||||
return (
|
||||
<span className={`die d${sides} ${rolling ? 'rolling' : 'landed'}`} style={{ animationDelay: `${delay % 200}ms` }}>
|
||||
<span>{shown ?? '?'}</span>
|
||||
</span>
|
||||
)
|
||||
}
|
||||
|
||||
function DiceTray({ rolls, me }) {
|
||||
if (!rolls.length) return null
|
||||
return (
|
||||
<div className="dicetray" aria-hidden="true">
|
||||
{rolls.map((m) => {
|
||||
const t = rollText(m)
|
||||
return (
|
||||
<div key={m.key} className="trayroll">
|
||||
<div className="small">{m.mine ? 'You' : m.from} rolled <strong>{t.dice}</strong>{m.to ? (m.to === m.from ? ' · only you' : ` · private${m.mine ? ` to ${m.to}` : ''}`) : ''}</div>
|
||||
<div className="trayrow">
|
||||
{m.roll.results.map((v, i) => <Die key={i} sides={m.roll.sides} value={v} delay={i * 120} />)}
|
||||
{t.n > 1 && <span className="traytotal">= {t.sum}</span>}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
// ---- sheet of one character, refreshed regularly so the DM sees changes the player saves
|
||||
function CharTab({ cid, owner, rules, onOpen, name }) {
|
||||
const [data, setData] = useState(null)
|
||||
@@ -321,8 +359,8 @@ function LobbyView({ id, rules, user, onOpen }) {
|
||||
|
||||
const onRoll = useCallback((m) => {
|
||||
const key = `${m.id}`
|
||||
setTray((t) => [...t.slice(-1), { ...m, key }])
|
||||
setTimeout(() => setTray((t) => t.filter((x) => x.key !== key)), 10000)
|
||||
setTray((t) => [...t.slice(-2), { ...m, key }])
|
||||
setTimeout(() => setTray((t) => t.filter((x) => x.key !== key)), 9000)
|
||||
}, [])
|
||||
const feed = useFeed(id, load, onRoll)
|
||||
|
||||
@@ -424,7 +462,7 @@ function LobbyView({ id, rules, user, onOpen }) {
|
||||
</aside>
|
||||
</div>
|
||||
|
||||
<DiceTray rolls={tray} />
|
||||
<DiceTray rolls={tray} me={user} />
|
||||
<DiceBar id={id} to={dest} poll={feed.poll} isDm={lobby.is_dm} />
|
||||
</section>
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user