Begin Hnefatafl from the game kit
This commit is contained in:
@@ -0,0 +1,165 @@
|
||||
<script lang="ts">
|
||||
import { api, type Room } from '$lib/net/room.svelte';
|
||||
|
||||
let { room, open = $bindable(false) }: { room: Room; open?: boolean } = $props();
|
||||
|
||||
let happened = $state('');
|
||||
let expected = $state('');
|
||||
let image = $state<File | null>(null);
|
||||
let preview = $state<string | null>(null);
|
||||
let busy = $state(false);
|
||||
let sent = $state(false);
|
||||
let error = $state('');
|
||||
|
||||
function pickImage(e: Event) {
|
||||
const file = (e.currentTarget as HTMLInputElement).files?.[0] ?? null;
|
||||
if (preview) URL.revokeObjectURL(preview);
|
||||
if (file && file.size > 2_500_000) {
|
||||
error = 'That picture is over 2.5 MB; a smaller one, please.';
|
||||
image = null;
|
||||
preview = null;
|
||||
return;
|
||||
}
|
||||
error = '';
|
||||
image = file;
|
||||
preview = file ? URL.createObjectURL(file) : null;
|
||||
}
|
||||
|
||||
function close() {
|
||||
open = false;
|
||||
if (preview) URL.revokeObjectURL(preview);
|
||||
happened = '';
|
||||
expected = '';
|
||||
image = null;
|
||||
preview = null;
|
||||
sent = false;
|
||||
error = '';
|
||||
}
|
||||
|
||||
async function send(e: SubmitEvent) {
|
||||
e.preventDefault();
|
||||
const r = room;
|
||||
if (!happened.trim() || busy) return;
|
||||
busy = true;
|
||||
error = '';
|
||||
try {
|
||||
const { id } = await api.report(r.roomId, r.token, happened.trim(), expected.trim());
|
||||
if (image) await api.reportImage(id, image).catch(() => (error = 'The report went; the picture did not.'));
|
||||
sent = true;
|
||||
setTimeout(close, 2200);
|
||||
} catch (err) {
|
||||
error = err instanceof Error ? err.message : 'The report did not reach the keeper.';
|
||||
} finally {
|
||||
busy = false;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if open}
|
||||
<div class="scrim" role="presentation" onclick={(ev) => ev.target === ev.currentTarget && close()}>
|
||||
<form class="slip" aria-labelledby="report-heading" onsubmit={send}>
|
||||
<h2 id="report-heading">Something amiss?</h2>
|
||||
{#if sent}
|
||||
<p class="thanks">Recorded beside the room's ledger, with the round. Thank you; the keeper will study the moment.{#if error} {error}{/if}</p>
|
||||
{:else}
|
||||
<label class="field">
|
||||
<span>What happened?</span>
|
||||
<textarea bind:value={happened} rows="3" maxlength="2000" placeholder="e.g. my move went in but nothing changed"></textarea>
|
||||
</label>
|
||||
<label class="field">
|
||||
<span>What did you expect?</span>
|
||||
<textarea bind:value={expected} rows="2" maxlength="2000" placeholder="e.g. a point for me"></textarea>
|
||||
</label>
|
||||
<label class="field picture">
|
||||
<span>A screenshot, if you have one</span>
|
||||
<input type="file" accept="image/png,image/jpeg,image/webp" onchange={pickImage} />
|
||||
{#if preview}<img class="preview" src={preview} alt="The screenshot you chose" />{/if}
|
||||
</label>
|
||||
<p class="muted small">The room code and the round ride along; the keeper can replay the game to this moment.</p>
|
||||
{#if error}<p class="warning">{error}</p>{/if}
|
||||
<div class="actions">
|
||||
<button type="submit" class="commit small" disabled={!happened.trim() || busy}>{busy ? 'Sending' : 'Send it'}</button>
|
||||
<button type="button" class="link" onclick={close}>never mind</button>
|
||||
</div>
|
||||
{/if}
|
||||
</form>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<style>
|
||||
.scrim {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
z-index: 20;
|
||||
background: rgba(0, 0, 0, 0.55);
|
||||
display: grid;
|
||||
place-items: center;
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
.slip {
|
||||
width: min(100%, 30rem);
|
||||
max-height: 100%;
|
||||
overflow-y: auto;
|
||||
background: var(--slate);
|
||||
border: 1px solid var(--rule-strong);
|
||||
border-radius: 6px;
|
||||
padding: 1rem 1.2rem 1.2rem;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 1.3rem;
|
||||
margin-bottom: 0.6rem;
|
||||
}
|
||||
|
||||
.field {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.25rem;
|
||||
margin-top: 0.7rem;
|
||||
}
|
||||
|
||||
|
||||
.picture input {
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.preview {
|
||||
max-height: 8rem;
|
||||
max-width: 100%;
|
||||
border: 1px solid var(--rule);
|
||||
border-radius: 3px;
|
||||
margin-top: 0.3rem;
|
||||
}
|
||||
|
||||
.small {
|
||||
font-size: 0.85rem;
|
||||
margin-top: 0.6rem;
|
||||
}
|
||||
|
||||
.warning {
|
||||
color: var(--blood);
|
||||
margin-top: 0.4rem;
|
||||
}
|
||||
|
||||
.thanks {
|
||||
color: var(--bone);
|
||||
}
|
||||
|
||||
.actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 1rem;
|
||||
margin-top: 0.9rem;
|
||||
}
|
||||
|
||||
|
||||
.link {
|
||||
background: none;
|
||||
border: 0;
|
||||
color: var(--bone-dim);
|
||||
text-decoration: underline dotted;
|
||||
padding: 0;
|
||||
font: inherit;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user