feat(landing): VK entry logo + restore the Telegram channel build-arg

Add the VK Mini App logo next to the Telegram one on the landing hero,
linked via the new VITE_VK_APP_LINK build-arg (full URL, wired through
compose, CI and prod-deploy from TEST_/PROD_VITE_VK_APP_LINK).

Also restore the landing's Telegram link itself: commit 57c778f
collapsed the per-language vars to VITE_TELEGRAM_GAME_CHANNEL_NAME in
compose/CI but left gateway/Dockerfile with the stale _EN/_RU ARGs and
without the plain one, so the built bundle saw the var as undefined and
dead-code-eliminated the whole channel-link branch — deployed landings
(prod included) have shown no Telegram logo since.
This commit is contained in:
Ilia Denisov
2026-07-02 00:10:02 +02:00
parent db17287113
commit 5f574a765d
14 changed files with 80 additions and 26 deletions
+25 -12
View File
@@ -4,7 +4,7 @@
import { i18n, localeFrom, setLocale, t, type Locale } from './lib/i18n/index.svelte';
import { loadPrefs, savePrefs, type Prefs } from './lib/session';
import { aboutContent } from './lib/aboutContent';
import { telegramChannelLink } from './lib/landing';
import { telegramChannelLink, vkAppLink } from './lib/landing';
// Standalone landing page, the public entry at "/" (the game SPA lives at /app/ and
// /telegram/). It reuses the app's theme/i18n/prefs leaf modules — not the app store — so it
@@ -17,6 +17,7 @@
const about = $derived(aboutContent(i18n.locale, 24)); // 24h = the auto-match move clock
const tgLink = $derived(telegramChannelLink());
const vkLink = $derived(vkAppLink());
const locales: { code: Locale; label: string }[] = [
{ code: 'en', label: '🇬🇧 English' },
{ code: 'ru', label: '🇷🇺 Русский' },
@@ -72,10 +73,19 @@
<section class="hero">
<h1>{about.title}</h1>
<p class="tagline">{t('landing.tagline')}</p>
{#if tgLink}
<a class="tg" href={tgLink} target="_blank" rel="noopener noreferrer" aria-label={t('landing.playTelegram')}>
<img src="telegram-logo.svg" alt="" width="64" height="64" />
</a>
{#if tgLink || vkLink}
<div class="entries">
{#if tgLink}
<a class="entry" href={tgLink} target="_blank" rel="noopener noreferrer" aria-label={t('landing.playTelegram')}>
<img src="telegram-logo.svg" alt="" width="64" height="64" />
</a>
{/if}
{#if vkLink}
<a class="entry" href={vkLink} target="_blank" rel="noopener noreferrer" aria-label={t('landing.playVK')}>
<img src="vk-logo.svg" alt="" width="64" height="64" />
</a>
{/if}
</div>
{/if}
</section>
@@ -190,19 +200,22 @@
color: var(--text-muted);
font-size: 1.05rem;
}
/* The Telegram entry is just the bigger logo (no button chrome, no caption); the link
keeps an aria-label for assistive tech. */
.tg {
/* The platform entries are just the bigger logos in a row (no button chrome, no captions);
each link keeps an aria-label for assistive tech. */
.entries {
align-self: center;
display: inline-flex;
display: flex;
gap: 20px;
margin-top: 8px;
border-radius: 50%;
}
.tg img {
.entry {
display: inline-flex;
}
.entry img {
display: block;
transition: transform 0.12s ease;
}
.tg:hover img {
.entry:hover img {
transform: scale(1.06);
}
.info {
+1
View File
@@ -205,6 +205,7 @@ export const en = {
'landing.tagline': 'Play Scrabble with friends or a smart robot — in your browser or on Telegram.',
'landing.playTelegram': 'Play in Telegram',
'landing.playVK': 'Play on VK',
'lang.en': 'English',
'lang.ru': 'Русский',
+1
View File
@@ -205,6 +205,7 @@ export const ru: Record<MessageKey, string> = {
'landing.tagline': 'Играй в Скрэббл с друзьями или умным роботом — в браузере или в Telegram.',
'landing.playTelegram': 'Играть в Telegram',
'landing.playVK': 'Играть во ВКонтакте',
'lang.en': 'English',
'lang.ru': 'Русский',
+15 -1
View File
@@ -1,5 +1,5 @@
import { afterEach, describe, expect, it, vi } from 'vitest';
import { telegramChannelLink } from './landing';
import { telegramChannelLink, vkAppLink } from './landing';
describe('telegramChannelLink', () => {
afterEach(() => vi.unstubAllEnvs());
@@ -14,3 +14,17 @@ describe('telegramChannelLink', () => {
expect(telegramChannelLink()).toBeNull();
});
});
describe('vkAppLink', () => {
afterEach(() => vi.unstubAllEnvs());
it('returns the configured VK app link verbatim', () => {
vi.stubEnv('VITE_VK_APP_LINK', 'https://vk.com/app54655464');
expect(vkAppLink()).toBe('https://vk.com/app54655464');
});
it('returns null when the link is unset or blank', () => {
vi.stubEnv('VITE_VK_APP_LINK', ' ');
expect(vkAppLink()).toBeNull();
});
});
+13 -1
View File
@@ -1,5 +1,5 @@
// Pure helpers for the public landing page, kept out of the Svelte component so the
// Telegram-channel link selection is unit-testable.
// platform-entry link selection is unit-testable.
/**
* telegramChannelLink returns the t.me link for the single bot's game channel, or null
@@ -13,3 +13,15 @@ export function telegramChannelLink(): string | null {
const name = (raw as string | undefined)?.trim().replace(/^@/, '');
return name ? `https://t.me/${name}` : null;
}
/**
* vkAppLink returns the VK Mini App link for the landing "Play on VK" entry, or null when it is
* not configured. The link is a build-time var (VITE_VK_APP_LINK, the full URL
* `https://vk.com/app<id>`) because the test and prod contours run the same VK app but the var
* mirrors the per-contour Telegram entries.
*/
export function vkAppLink(): string | null {
const raw = import.meta.env.VITE_VK_APP_LINK;
const link = (raw as string | undefined)?.trim();
return link ? link : null;
}