release: offline mode + local pass-and-play (hotseat) — proposed v1.12.0 #212
@@ -1,6 +1,7 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { onMount } from 'svelte';
|
import { onMount } from 'svelte';
|
||||||
import { gateway } from '../lib/gateway';
|
import { gateway } from '../lib/gateway';
|
||||||
|
import { gameSource, isLocalGameId } from '../lib/gamesource';
|
||||||
import { handleError, showToast } from '../lib/app.svelte';
|
import { handleError, showToast } from '../lib/app.svelte';
|
||||||
import { t } from '../lib/i18n/index.svelte';
|
import { t } from '../lib/i18n/index.svelte';
|
||||||
import { alphabetLetters } from '../lib/alphabet';
|
import { alphabetLetters } from '../lib/alphabet';
|
||||||
@@ -20,8 +21,10 @@
|
|||||||
|
|
||||||
onMount(async () => {
|
onMount(async () => {
|
||||||
try {
|
try {
|
||||||
// Include the alphabet so input sanitising + the check accept the variant's letters.
|
// Include the alphabet so input sanitising + the check accept the variant's letters. Routed
|
||||||
const st = await gateway.gameState(id, true);
|
// through gameSource so an offline (local) game resolves its state from the device, not the
|
||||||
|
// network.
|
||||||
|
const st = await gameSource(id).gameState(id, true);
|
||||||
variant = st.game.variant;
|
variant = st.game.variant;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
handleError(e);
|
handleError(e);
|
||||||
@@ -41,7 +44,7 @@
|
|||||||
cooling = true;
|
cooling = true;
|
||||||
setTimeout(() => (cooling = false), 5000);
|
setTimeout(() => (cooling = false), 5000);
|
||||||
try {
|
try {
|
||||||
const r = await gateway.checkWord(id, w, variant);
|
const r = await gameSource(id).checkWord(id, w, variant);
|
||||||
checked.set(w, r.legal);
|
checked.set(w, r.legal);
|
||||||
result = { word: w, legal: r.legal };
|
result = { word: w, legal: r.legal };
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@@ -77,7 +80,11 @@
|
|||||||
: t('game.wordIllegal', { word: result.word })}
|
: t('game.wordIllegal', { word: result.word })}
|
||||||
</p>
|
</p>
|
||||||
<div class="actions">
|
<div class="actions">
|
||||||
|
<!-- Complaints go to the admin over the network; a local (offline) game has no backend to
|
||||||
|
receive them, so the control is dropped there. -->
|
||||||
|
{#if !isLocalGameId(id)}
|
||||||
<button class="complain" onclick={complain}>{t('game.complain')}</button>
|
<button class="complain" onclick={complain}>{t('game.complain')}</button>
|
||||||
|
{/if}
|
||||||
{#if result.legal}
|
{#if result.legal}
|
||||||
<a
|
<a
|
||||||
class="lookup"
|
class="lookup"
|
||||||
|
|||||||
@@ -101,6 +101,18 @@
|
|||||||
// over the slower live stream; gating it keeps the card, its blink and the toast on one event.
|
// over the slower live stream; gating it keeps the card, its blink and the toast on one event.
|
||||||
if (app.lastEvent && app.lastEvent.kind !== 'heartbeat') void load();
|
if (app.lastEvent && app.lastEvent.kind !== 'heartbeat') void load();
|
||||||
});
|
});
|
||||||
|
// Reload when the deliberate offline mode flips (the toggle lives in Settings, so the lobby can
|
||||||
|
// stay mounted across the change): entering offline must immediately drop the online games and
|
||||||
|
// show only the device-local ones, and leaving it must refetch the online lobby. Guarded so the
|
||||||
|
// initial run — already covered by onMount — does not double-load.
|
||||||
|
let wasOffline = offlineMode.active;
|
||||||
|
$effect(() => {
|
||||||
|
const now = offlineMode.active;
|
||||||
|
if (now !== wasOffline) {
|
||||||
|
wasOffline = now;
|
||||||
|
void load();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
const myId = $derived(app.session?.userId ?? '');
|
const myId = $derived(app.session?.userId ?? '');
|
||||||
const groups = $derived(groupGames(games, myId, app.chatUnread));
|
const groups = $derived(groupGames(games, myId, app.chatUnread));
|
||||||
|
|||||||
Reference in New Issue
Block a user