Players see their reports answered in the lobby ledger
Reports gain ids; replies live in the same JSONL, folding onto their report when read (legacy reports answer to their timestamp). The lobby asks with the same seat-token proof as the games ledger — wake-free — and lists each report with its reply and status, or 'the wizards are studying the moment'. deploy/feedback-reply.sh answers one from here. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015RCWSTnb1KYTPyL4GmhGnF
This commit is contained in:
co-authored by
Claude Fable 5
parent
eabc10f18c
commit
5abd0ba60e
@@ -2017,6 +2017,24 @@
|
||||
onclick={() => net.forgetSeat(seat.roomId)}>×</button>
|
||||
</div>
|
||||
{/each}
|
||||
{#if net.feedbackReports.length > 0}
|
||||
<div class="reports-head">your reports to the wizards</div>
|
||||
{#each net.feedbackReports as r (r.id)}
|
||||
<div class="report-row" class:answered={!!r.reply}>
|
||||
<span class="ledger-code">{r.roomId}</span>
|
||||
<div class="report-body">
|
||||
<div class="report-text">“{r.happened}”</div>
|
||||
{#if r.reply}
|
||||
<div class="report-reply">
|
||||
<span class="report-status">{r.reply.status}</span> — {r.reply.text}
|
||||
</div>
|
||||
{:else}
|
||||
<div class="report-reply pending">the wizards are studying the moment…</div>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
{/each}
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
@@ -2880,6 +2898,33 @@
|
||||
.cast-mini.nullified { opacity: 0.45; filter: grayscale(0.8); }
|
||||
.hand-slot { display: flex; cursor: grab; }
|
||||
.hand-slot:active { cursor: grabbing; }
|
||||
.reports-head {
|
||||
margin-top: 0.9rem;
|
||||
font-family: "Caveat", cursive;
|
||||
font-size: 1.25rem;
|
||||
color: #6b5a41;
|
||||
}
|
||||
.report-row {
|
||||
display: flex;
|
||||
gap: 0.6rem;
|
||||
align-items: baseline;
|
||||
padding: 0.35rem 0;
|
||||
border-top: 1px dotted #b7ad92;
|
||||
text-align: left;
|
||||
}
|
||||
.report-body { min-width: 0; }
|
||||
.report-text { font-size: 0.9rem; color: #43331f; }
|
||||
.report-reply { font-size: 0.85rem; color: #6b5a41; margin-top: 0.15rem; }
|
||||
.report-reply.pending { font-style: italic; color: #a49c86; }
|
||||
.report-status {
|
||||
text-transform: uppercase;
|
||||
font-size: 0.72rem;
|
||||
letter-spacing: 0.06em;
|
||||
color: #1a9c46;
|
||||
border: 1px solid #1a9c46;
|
||||
border-radius: 3px;
|
||||
padding: 0 0.3rem;
|
||||
}
|
||||
.mast-audience { font-size: 0.85rem; color: #a49c86; white-space: nowrap; }
|
||||
.mind-read { max-width: min(92vw, 46rem); }
|
||||
.caution-reason { padding: 0.15rem 0; }
|
||||
|
||||
@@ -501,6 +501,12 @@ class Net {
|
||||
this.stats = msg.stats;
|
||||
break;
|
||||
}
|
||||
case "feedbackList":
|
||||
this.feedbackReports = msg.reports ?? [];
|
||||
break;
|
||||
case "feedbackReceived":
|
||||
this.refreshFeedback();
|
||||
break;
|
||||
case "games": {
|
||||
this.games = msg.games;
|
||||
for (const g of msg.games as GameSummary[]) {
|
||||
@@ -600,6 +606,16 @@ class Net {
|
||||
this.send({ type: "feedback", happened, expected });
|
||||
}
|
||||
|
||||
/** Your reports and the wizards' replies, proven by seat tokens. */
|
||||
feedbackReports = $state<{
|
||||
id: string; at: string; roomId: string; seq: number; round: number | null;
|
||||
happened: string; expected: string;
|
||||
reply?: { at: string; text: string; status: string };
|
||||
}[]>([]);
|
||||
refreshFeedback(): void {
|
||||
if (this.seats.length > 0) this.send({ type: "myFeedback", seats: $state.snapshot(this.seats) });
|
||||
}
|
||||
|
||||
requestTransferCode(): void {
|
||||
this.send({ type: "makeTransfer" });
|
||||
}
|
||||
@@ -661,6 +677,7 @@ class Net {
|
||||
startGamePolling(): void {
|
||||
if (this.pollTimer) return;
|
||||
this.refreshGames();
|
||||
this.refreshFeedback();
|
||||
this.pollTimer = setInterval(() => this.refreshGames(), 45_000);
|
||||
}
|
||||
stopGamePolling(): void {
|
||||
|
||||
Reference in New Issue
Block a user