Stage 17 #2: extend the offline soft-disable to all server-action buttons
CI / changes (pull_request) Successful in 1s
CI / unit (pull_request) Has been skipped
CI / integration (pull_request) Has been skipped
CI / ui (pull_request) Successful in 35s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 1m6s
CI / changes (pull_request) Successful in 1s
CI / unit (pull_request) Has been skipped
CI / integration (pull_request) Has been skipped
CI / ui (pull_request) Successful in 35s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 1m6s
Following the in-game bar, the Connecting indicator now also visually disables the
other proactive (server-sending) controls while offline: chat send + nudge, profile
save / link email|telegram / merge-confirm, friends (redeem, get-code, accept/decline,
unfriend, block, unblock), New Game (auto-match variant + send-invitation) and the
lobby hide ❌. Purely local controls (board/rack/reset, menus, navigation, settings,
copy-code) stay live. Each reads the global connection.online signal; full e2e + check
green.
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
<script lang="ts">
|
||||
import type { ChatMessage } from '../lib/model';
|
||||
import { t } from '../lib/i18n/index.svelte';
|
||||
import { connection } from '../lib/connection.svelte';
|
||||
|
||||
let {
|
||||
messages,
|
||||
@@ -55,11 +56,11 @@
|
||||
bind:value={text}
|
||||
onkeydown={(e) => e.key === 'Enter' && send()}
|
||||
/>
|
||||
<button class="iconbtn" onclick={send} disabled={busy} aria-label={t('chat.send')}>⬆️</button>
|
||||
<button class="iconbtn" onclick={send} disabled={busy || !connection.online} aria-label={t('chat.send')}>⬆️</button>
|
||||
{:else}
|
||||
<!-- A flex:1 caption keeps the nudge pinned right whether or not the cooldown text shows. -->
|
||||
<span class="cooldown">{nudgeOnCooldown ? t('chat.awaitingReply') : ''}</span>
|
||||
<button class="iconbtn" onclick={onnudge} disabled={busy || nudgeOnCooldown} aria-label={t('chat.nudgeAction')}>🛎️</button>
|
||||
<button class="iconbtn" onclick={onnudge} disabled={busy || nudgeOnCooldown || !connection.online} aria-label={t('chat.nudgeAction')}>🛎️</button>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -794,7 +794,7 @@
|
||||
<Modal title={t('game.confirmResign')} onclose={() => (resignOpen = false)}>
|
||||
<div class="confirm-row">
|
||||
<button class="cancel" onclick={() => (resignOpen = false)}>{t('common.cancel')}</button>
|
||||
<button class="danger" onclick={doResign}>{t('game.dropGame')}</button>
|
||||
<button class="danger" onclick={doResign} disabled={!connection.online}>{t('game.dropGame')}</button>
|
||||
</div>
|
||||
</Modal>
|
||||
{/if}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
import { onMount } from 'svelte';
|
||||
import Screen from '../components/Screen.svelte';
|
||||
import { app, handleError, refreshNotifications, showToast } from '../lib/app.svelte';
|
||||
import { connection } from '../lib/connection.svelte';
|
||||
import { gateway } from '../lib/gateway';
|
||||
import { t } from '../lib/i18n/index.svelte';
|
||||
import { friendCodeParam, shareLink } from '../lib/deeplink';
|
||||
@@ -95,7 +96,7 @@
|
||||
inputmode="numeric"
|
||||
maxlength="6"
|
||||
/>
|
||||
<button class="btn" onclick={redeem}>{t('friends.redeem')}</button>
|
||||
<button class="btn" onclick={redeem} disabled={!connection.online}>{t('friends.redeem')}</button>
|
||||
</div>
|
||||
{#if code}
|
||||
{@const tg = shareLink(friendCodeParam(code.code))}
|
||||
@@ -112,7 +113,7 @@
|
||||
{/if}
|
||||
</div>
|
||||
{:else}
|
||||
<button class="link" onclick={getCode}>{t('friends.getCode')}</button>
|
||||
<button class="link" onclick={getCode} disabled={!connection.online}>{t('friends.getCode')}</button>
|
||||
{/if}
|
||||
</section>
|
||||
|
||||
@@ -123,8 +124,8 @@
|
||||
<div class="item">
|
||||
<span class="who">{r.displayName}</span>
|
||||
<span class="acts">
|
||||
<button class="btn" onclick={() => respond(r.accountId, true)}>{t('friends.accept')}</button>
|
||||
<button class="ghost" onclick={() => respond(r.accountId, false)}>{t('friends.decline')}</button>
|
||||
<button class="btn" onclick={() => respond(r.accountId, true)} disabled={!connection.online}>{t('friends.accept')}</button>
|
||||
<button class="ghost" onclick={() => respond(r.accountId, false)} disabled={!connection.online}>{t('friends.decline')}</button>
|
||||
</span>
|
||||
</div>
|
||||
{/each}
|
||||
@@ -138,8 +139,8 @@
|
||||
<div class="item">
|
||||
<span class="who">{f.displayName}</span>
|
||||
<span class="acts">
|
||||
<button class="ghost" onclick={() => remove(f.accountId)}>{t('friends.unfriend')}</button>
|
||||
<button class="ghost danger" onclick={() => blockUser(f.accountId)}>{t('friends.block')}</button>
|
||||
<button class="ghost" onclick={() => remove(f.accountId)} disabled={!connection.online}>{t('friends.unfriend')}</button>
|
||||
<button class="ghost danger" onclick={() => blockUser(f.accountId)} disabled={!connection.online}>{t('friends.block')}</button>
|
||||
</span>
|
||||
</div>
|
||||
{/each}
|
||||
@@ -154,7 +155,7 @@
|
||||
{#each blocked as b (b.accountId)}
|
||||
<div class="item">
|
||||
<span class="who">{b.displayName}</span>
|
||||
<button class="ghost" onclick={() => unblock(b.accountId)}>{t('friends.unblock')}</button>
|
||||
<button class="ghost" onclick={() => unblock(b.accountId)} disabled={!connection.online}>{t('friends.unblock')}</button>
|
||||
</div>
|
||||
{/each}
|
||||
</section>
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
import Menu from '../components/Menu.svelte';
|
||||
import TabBar from '../components/TabBar.svelte';
|
||||
import { app, handleError, showToast } from '../lib/app.svelte';
|
||||
import { connection } from '../lib/connection.svelte';
|
||||
import { gateway } from '../lib/gateway';
|
||||
import { navigate } from '../lib/router.svelte';
|
||||
import { t, type MessageKey } from '../lib/i18n/index.svelte';
|
||||
@@ -191,7 +192,7 @@
|
||||
{#each group.list as g (g.id)}
|
||||
<div class="rowwrap" class:revealed={group.finished && revealedId === g.id}>
|
||||
{#if group.finished}
|
||||
<button class="del" onclick={() => hide(g.id)} aria-label={t('lobby.hideGame')}>❌</button>
|
||||
<button class="del" onclick={() => hide(g.id)} disabled={!connection.online} aria-label={t('lobby.hideGame')}>❌</button>
|
||||
{/if}
|
||||
<div class="row">
|
||||
<button
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
import Screen from '../components/Screen.svelte';
|
||||
import { gateway } from '../lib/gateway';
|
||||
import { app, handleError, showToast } from '../lib/app.svelte';
|
||||
import { connection } from '../lib/connection.svelte';
|
||||
import { navigate } from '../lib/router.svelte';
|
||||
import { t, type MessageKey } from '../lib/i18n/index.svelte';
|
||||
import type { AccountRef, Variant } from '../lib/model';
|
||||
@@ -144,7 +145,7 @@
|
||||
<p class="subtitle">{t('new.subtitle')}</p>
|
||||
<div class="variants">
|
||||
{#each variants as v (v.id)}
|
||||
<button class="variant" onclick={() => find(v.id)}>
|
||||
<button class="variant" onclick={() => find(v.id)} disabled={!connection.online}>
|
||||
<span class="vmain">
|
||||
<span class="vname">{t(v.label)}</span>
|
||||
{#if VARIANT_FLAG[v.id]}
|
||||
@@ -196,7 +197,7 @@
|
||||
</select>
|
||||
</label>
|
||||
</div>
|
||||
<button class="invite" disabled={selected.length === 0 || !inviteVariant} onclick={sendInvite}>{t('new.invite')}</button>
|
||||
<button class="invite" disabled={selected.length === 0 || !inviteVariant || !connection.online} onclick={sendInvite}>{t('new.invite')}</button>
|
||||
</div>
|
||||
{/if}
|
||||
{/if}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
import Modal from '../components/Modal.svelte';
|
||||
import Screen from '../components/Screen.svelte';
|
||||
import { app, applyLinkResult, handleError, logout, showToast } from '../lib/app.svelte';
|
||||
import { connection } from '../lib/connection.svelte';
|
||||
import { gateway } from '../lib/gateway';
|
||||
import { loginWidgetAvailable, requestTelegramLogin } from '../lib/telegram';
|
||||
import { t } from '../lib/i18n/index.svelte';
|
||||
@@ -209,7 +210,7 @@
|
||||
<span>{t('profile.notificationsInAppOnly')}</span>
|
||||
</label>
|
||||
<div class="formacts">
|
||||
<button type="submit" class="btn" disabled={!formValid}>{t('common.save')}</button>
|
||||
<button type="submit" class="btn" disabled={!formValid || !connection.online}>{t('common.save')}</button>
|
||||
</div>
|
||||
</form>
|
||||
{/if}
|
||||
@@ -226,7 +227,7 @@
|
||||
placeholder={t('login.emailPlaceholder')}
|
||||
type="email"
|
||||
/>
|
||||
<button class="ghost" onclick={requestEmail} disabled={!emailOk}>{t('login.sendCode')}</button>
|
||||
<button class="ghost" onclick={requestEmail} disabled={!emailOk || !connection.online}>{t('login.sendCode')}</button>
|
||||
</div>
|
||||
{:else}
|
||||
<div class="addrow">
|
||||
@@ -237,11 +238,11 @@
|
||||
inputmode="numeric"
|
||||
maxlength="6"
|
||||
/>
|
||||
<button class="btn" onclick={confirmEmail}>{t('common.ok')}</button>
|
||||
<button class="btn" onclick={confirmEmail} disabled={!connection.online}>{t('common.ok')}</button>
|
||||
</div>
|
||||
{/if}
|
||||
{#if telegramLinkable}
|
||||
<button class="ghost tg" onclick={linkTelegram}>{t('profile.linkTelegram')}</button>
|
||||
<button class="ghost tg" onclick={linkTelegram} disabled={!connection.online}>{t('profile.linkTelegram')}</button>
|
||||
{/if}
|
||||
</section>
|
||||
|
||||
@@ -257,7 +258,7 @@
|
||||
<p class="warn">{t('profile.mergeIrreversible')}</p>
|
||||
<div class="addrow end">
|
||||
<button class="ghost" onclick={() => (pendingMerge = null)}>{t('common.cancel')}</button>
|
||||
<button class="btn" onclick={confirmMerge}>{t('profile.mergeConfirm')}</button>
|
||||
<button class="btn" onclick={confirmMerge} disabled={!connection.online}>{t('profile.mergeConfirm')}</button>
|
||||
</div>
|
||||
</Modal>
|
||||
{/if}
|
||||
|
||||
Reference in New Issue
Block a user