Stage 17 round 6 (#13/About): About screen content + app version from git describe
CI / changes (pull_request) Successful in 1s
CI / unit (pull_request) Successful in 8s
CI / integration (pull_request) Successful in 11s
CI / ui (pull_request) Successful in 29s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 56s
CI / changes (pull_request) Successful in 1s
CI / unit (pull_request) Successful in 8s
CI / integration (pull_request) Successful in 11s
CI / ui (pull_request) Successful in 29s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 56s
- About screen: prominent localized title (Scrabble / Эрудит (Скрэббл)), a rules link (en/ru Wikipedia), and the Random-game / Game-with-friends sections; copy lives in a shared aboutContent module (the landing will reuse it). The random-game move limit inlines the 24h auto-match clock. - App version: Vite define __APP_VERSION__ from VITE_APP_VERSION (default 'dev'), wired as a Docker build-arg sourced from `git describe --tags --always` in the deploy step — no manual version bumps. The fallback keeps a plain/local build working.
This commit is contained in:
@@ -285,6 +285,9 @@ jobs:
|
|||||||
mkdir -p "$conf"
|
mkdir -p "$conf"
|
||||||
cp -r caddy otelcol prometheus tempo grafana "$conf"/
|
cp -r caddy otelcol prometheus tempo grafana "$conf"/
|
||||||
export SCRABBLE_CONFIG_DIR="$conf"
|
export SCRABBLE_CONFIG_DIR="$conf"
|
||||||
|
# App version for the About screen: the git tag if present, else the short SHA
|
||||||
|
# (the test checkout is shallow/untagged, so this is the SHA here — fine).
|
||||||
|
export APP_VERSION="$(git -C "$GITHUB_WORKSPACE" describe --tags --always 2>/dev/null || echo dev)"
|
||||||
docker compose --ansi never build --progress plain
|
docker compose --ansi never build --progress plain
|
||||||
docker compose --ansi never up -d --remove-orphans
|
docker compose --ansi never up -d --remove-orphans
|
||||||
# The config-only services bind-mount the reseeded config dir. A plain `up -d`
|
# The config-only services bind-mount the reseeded config dir. A plain `up -d`
|
||||||
|
|||||||
@@ -79,6 +79,7 @@ services:
|
|||||||
VITE_TELEGRAM_BOT_ID: ${VITE_TELEGRAM_BOT_ID:-}
|
VITE_TELEGRAM_BOT_ID: ${VITE_TELEGRAM_BOT_ID:-}
|
||||||
VITE_TELEGRAM_LINK: ${VITE_TELEGRAM_LINK:-}
|
VITE_TELEGRAM_LINK: ${VITE_TELEGRAM_LINK:-}
|
||||||
VITE_GATEWAY_URL: ${VITE_GATEWAY_URL:-}
|
VITE_GATEWAY_URL: ${VITE_GATEWAY_URL:-}
|
||||||
|
VITE_APP_VERSION: ${APP_VERSION:-dev}
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
depends_on: [backend]
|
depends_on: [backend]
|
||||||
environment:
|
environment:
|
||||||
|
|||||||
+4
-1
@@ -17,12 +17,15 @@ WORKDIR /ui
|
|||||||
RUN corepack enable && corepack prepare pnpm@11.0.9 --activate
|
RUN corepack enable && corepack prepare pnpm@11.0.9 --activate
|
||||||
|
|
||||||
# Prod UI build vars (Vite reads VITE_-prefixed env at build; baked into the bundle).
|
# Prod UI build vars (Vite reads VITE_-prefixed env at build; baked into the bundle).
|
||||||
|
# VITE_APP_VERSION carries `git describe` for the About screen (defaults to "dev").
|
||||||
ARG VITE_TELEGRAM_BOT_ID=
|
ARG VITE_TELEGRAM_BOT_ID=
|
||||||
ARG VITE_TELEGRAM_LINK=
|
ARG VITE_TELEGRAM_LINK=
|
||||||
ARG VITE_GATEWAY_URL=
|
ARG VITE_GATEWAY_URL=
|
||||||
|
ARG VITE_APP_VERSION=
|
||||||
ENV VITE_TELEGRAM_BOT_ID=$VITE_TELEGRAM_BOT_ID \
|
ENV VITE_TELEGRAM_BOT_ID=$VITE_TELEGRAM_BOT_ID \
|
||||||
VITE_TELEGRAM_LINK=$VITE_TELEGRAM_LINK \
|
VITE_TELEGRAM_LINK=$VITE_TELEGRAM_LINK \
|
||||||
VITE_GATEWAY_URL=$VITE_GATEWAY_URL
|
VITE_GATEWAY_URL=$VITE_GATEWAY_URL \
|
||||||
|
VITE_APP_VERSION=$VITE_APP_VERSION
|
||||||
|
|
||||||
# Install with the lockfile first (the workspace file carries pnpm's build-script
|
# Install with the lockfile first (the workspace file carries pnpm's build-script
|
||||||
# approval for esbuild), then build. Committed src/gen/ means no codegen here.
|
# approval for esbuild), then build. Committed src/gen/ means no codegen here.
|
||||||
|
|||||||
@@ -0,0 +1,62 @@
|
|||||||
|
// Localised "About" / landing copy, shared by the About screen and the public landing
|
||||||
|
// page (Stage 17). Kept out of the flat i18n catalog because it is structured (a heading,
|
||||||
|
// a rules link, two bulleted sections) and only used in these two long-form places.
|
||||||
|
|
||||||
|
import type { Locale } from './i18n/index.svelte';
|
||||||
|
|
||||||
|
export interface AboutContent {
|
||||||
|
/** Prominent heading: "Scrabble" / "Эрудит (Скрэббл)". */
|
||||||
|
title: string;
|
||||||
|
rulesUrl: string;
|
||||||
|
/** Text before the rules link. */
|
||||||
|
rulesPrefix: string;
|
||||||
|
/** The rules link label. */
|
||||||
|
rulesLink: string;
|
||||||
|
randomTitle: string;
|
||||||
|
/** The "respect the opponent's time" note (rendered with a ❗️ prefix). */
|
||||||
|
randomRespect: string;
|
||||||
|
random: string[];
|
||||||
|
friendsTitle: string;
|
||||||
|
friends: string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* aboutContent returns the localised About/landing copy. hours is the auto-match move clock
|
||||||
|
* (backend game.DefaultTurnTimeout), inlined into the random-game time-limit bullet.
|
||||||
|
*/
|
||||||
|
export function aboutContent(locale: Locale, hours: number): AboutContent {
|
||||||
|
if (locale === 'ru') {
|
||||||
|
return {
|
||||||
|
title: 'Эрудит (Скрэббл)',
|
||||||
|
rulesUrl: 'https://ru.wikipedia.org/wiki/Скрэббл',
|
||||||
|
rulesPrefix: 'Основные ',
|
||||||
|
rulesLink: 'правила игры',
|
||||||
|
randomTitle: 'Случайная игра',
|
||||||
|
randomRespect: 'Уважайте личное время соперника, будьте терпеливы.',
|
||||||
|
random: [
|
||||||
|
'В игре двое соперников.',
|
||||||
|
'Каждому доступна 1 подсказка в новой партии.',
|
||||||
|
`Лимит времени на ход: ${hours} ч. 00 минут.`,
|
||||||
|
'Время отсутствия задаётся в профиле и продлевает лимит.',
|
||||||
|
],
|
||||||
|
friendsTitle: 'Игра с друзьями',
|
||||||
|
friends: ['До 4-х участников.', 'Количество подсказок регулируется.', 'Произвольный лимит времени.'],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
title: 'Scrabble',
|
||||||
|
rulesUrl: 'https://en.wikipedia.org/wiki/Scrabble',
|
||||||
|
rulesPrefix: 'Basic ',
|
||||||
|
rulesLink: 'game rules',
|
||||||
|
randomTitle: 'Random game',
|
||||||
|
randomRespect: "Respect your opponent's time, be patient.",
|
||||||
|
random: [
|
||||||
|
'Two opponents per game.',
|
||||||
|
'Each player gets 1 hint per new game.',
|
||||||
|
`Move time limit: ${hours} h 00 min.`,
|
||||||
|
'An away window set in your profile extends the limit.',
|
||||||
|
],
|
||||||
|
friendsTitle: 'Game with friends',
|
||||||
|
friends: ['Up to 4 players.', 'The number of hints is configurable.', 'A custom time limit.'],
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -1,14 +1,37 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import Screen from '../components/Screen.svelte';
|
import Screen from '../components/Screen.svelte';
|
||||||
import { t } from '../lib/i18n/index.svelte';
|
import { t } from '../lib/i18n/index.svelte';
|
||||||
|
import { app } from '../lib/app.svelte';
|
||||||
|
import { aboutContent } from '../lib/aboutContent';
|
||||||
|
|
||||||
const version = '0.7.0';
|
// 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>
|
</script>
|
||||||
|
|
||||||
<Screen title={t('about.title')} back="/">
|
<Screen title={t('about.title')} back="/">
|
||||||
<div class="page">
|
<div class="page">
|
||||||
<h2>{t('app.title')}</h2>
|
<h1>{c.title}</h1>
|
||||||
<p>{t('about.description')}</p>
|
<p>
|
||||||
|
{c.rulesPrefix}<a href={c.rulesUrl} target="_blank" rel="noopener noreferrer">{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>
|
<p class="muted">{t('about.version', { v: version })}</p>
|
||||||
</div>
|
</div>
|
||||||
</Screen>
|
</Screen>
|
||||||
@@ -16,8 +39,41 @@
|
|||||||
<style>
|
<style>
|
||||||
.page {
|
.page {
|
||||||
padding: var(--pad);
|
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 {
|
.muted {
|
||||||
color: var(--text-muted);
|
color: var(--text-muted);
|
||||||
|
font-size: 0.9rem;
|
||||||
|
margin: 0;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
Vendored
+3
@@ -9,3 +9,6 @@ interface ImportMetaEnv {
|
|||||||
interface ImportMeta {
|
interface ImportMeta {
|
||||||
readonly env: ImportMetaEnv;
|
readonly env: ImportMetaEnv;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** App version string, injected by Vite's define from `git describe` at build time. */
|
||||||
|
declare const __APP_VERSION__: string;
|
||||||
|
|||||||
@@ -12,6 +12,12 @@ export default defineConfig(({ mode }) => ({
|
|||||||
// Relative asset base so the one build serves under any path — the gateway maps the
|
// Relative asset base so the one build serves under any path — the gateway maps the
|
||||||
// Telegram Mini App to /telegram/ (the hash router is path-agnostic).
|
// Telegram Mini App to /telegram/ (the hash router is path-agnostic).
|
||||||
base: './',
|
base: './',
|
||||||
|
define: {
|
||||||
|
// App version shown on the About screen, injected at build time from `git describe`
|
||||||
|
// via a Docker build-arg (Stage 17). Falls back to "dev" for a plain local/mock build,
|
||||||
|
// so a missing build-arg never breaks the build.
|
||||||
|
__APP_VERSION__: JSON.stringify(process.env.VITE_APP_VERSION || 'dev'),
|
||||||
|
},
|
||||||
plugins: [svelte()],
|
plugins: [svelte()],
|
||||||
server: {
|
server: {
|
||||||
port: 5173,
|
port: 5173,
|
||||||
|
|||||||
Reference in New Issue
Block a user