Files
scrabble-game/ui/src/screens/About.svelte
T
Ilia Denisov bd0482c376
CI / changes (pull_request) Successful in 2s
CI / unit (pull_request) Has been skipped
CI / integration (pull_request) Has been skipped
CI / ui (pull_request) Successful in 49s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 59s
feat(ui): route all in-app external links through Telegram openLink
Extend the openLink routing from the dictionary lookup to every external
link shown inside the Mini App, so none triggers the WebView's 'open this
link?' confirmation. A shared onExternalLinkClick handler resolves the anchor
via closest() (so it also works delegated on {@html} content), backed by a
pure routeExternalLinkInTelegram decision: only inside Telegram, only an
external http(s) target=_blank link, excluding same-origin/in-app and t.me
links (t.me keeps openTelegramLink). Applied to the word-check lookup, the
About rules link, the Feedback operator-reply links, and the feature-gated
announcement banner.

Outside Telegram every anchor keeps its native target=_blank.
2026-06-15 18:29:53 +02:00

108 lines
2.4 KiB
Svelte

<script lang="ts">
import { t } from '../lib/i18n/index.svelte';
import { app } from '../lib/app.svelte';
import { navigate } from '../lib/router.svelte';
import { aboutContent } from '../lib/aboutContent';
import { onExternalLinkClick } from '../lib/telegram';
// The auto-match move clock (mirrors backend game.DefaultTurnTimeout = 24h).
const AUTO_MATCH_HOURS = 24;
const version = __APP_VERSION__;
const c = $derived(aboutContent(app.locale, AUTO_MATCH_HOURS));
</script>
<div class="page">
<h1>{c.title}</h1>
<p>
{c.rulesPrefix}<a href={c.rulesUrl} target="_blank" rel="noopener noreferrer" onclick={onExternalLinkClick}>{c.rulesLink}</a>.
</p>
<section>
<h2>{c.randomTitle}</h2>
<p class="respect">❗️{c.randomRespect}</p>
<ul>
{#each c.random as item (item)}<li>{item}</li>{/each}
</ul>
</section>
<section>
<h2>{c.friendsTitle}</h2>
<ul>
{#each c.friends as item (item)}<li>{item}</li>{/each}
</ul>
</section>
<p class="muted">{t('about.version', { v: version })}</p>
{#if !(app.profile?.isGuest ?? true)}
<button class="feedback" onclick={() => navigate('/feedback')}>
{t('feedback.title')}{#if app.feedbackReplyUnread}<span class="fbadge">1</span>{/if}
</button>
{/if}
</div>
<style>
.page {
padding: var(--pad);
display: flex;
flex-direction: column;
gap: 14px;
}
h1 {
margin: 0;
font-size: 1.5rem;
}
h2 {
margin: 0 0 6px;
font-size: 1.05rem;
color: var(--text-muted);
}
section {
display: flex;
flex-direction: column;
gap: 4px;
}
ul {
margin: 0;
padding-left: 20px;
display: flex;
flex-direction: column;
gap: 4px;
}
a {
color: var(--accent);
}
.respect {
margin: 0;
font-weight: 600;
}
.muted {
color: var(--text-muted);
font-size: 0.9rem;
margin: 0;
}
.feedback {
align-self: flex-start;
padding: 12px 18px;
border: 1px solid var(--accent);
background: var(--accent);
color: var(--accent-text);
border-radius: var(--radius);
font-weight: 600;
}
/* The reply badge sits on the accent button, so it inverts the accent colours. */
.fbadge {
display: inline-block;
margin-left: 8px;
min-width: 18px;
padding: 0 5px;
border-radius: 999px;
background: var(--accent-text);
color: var(--accent);
font-size: 0.78rem;
font-weight: 700;
line-height: 1.55;
text-align: center;
}
</style>