fix(ui): use the guest stubs' "sign in" wording, and link it in Stats
CI / changes (pull_request) Successful in 3s
CI / unit (pull_request) Successful in 11s
CI / integration (pull_request) Successful in 21s
CI / ui (pull_request) Successful in 1m15s
CI / conformance (pull_request) Successful in 10s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 1m57s
CI / changes (pull_request) Successful in 3s
CI / unit (pull_request) Successful in 11s
CI / integration (pull_request) Successful in 21s
CI / ui (pull_request) Successful in 1m15s
CI / conformance (pull_request) Successful in 10s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 1m57s
The New Game hint said "Register" where the Stats and Profile guest stubs say "Sign in". Move the shared word into common.signInLink so the two screens cannot drift, and turn the Stats stub's opening word into the same link to the profile screen.
This commit is contained in:
@@ -285,7 +285,7 @@ func (s *Store) AttachIdentity(ctx context.Context, accountID uuid.UUID, kind, e
|
|||||||
// for an already-durable account.
|
// for an already-durable account.
|
||||||
//
|
//
|
||||||
// The promotion also widens the guest's narrow variant set (GuestVariantPreferences,
|
// The promotion also widens the guest's narrow variant set (GuestVariantPreferences,
|
||||||
// Эрудит alone) to the registered default, so a player who registers from the New Game
|
// Эрудит alone) to the registered default, so a player who signs in from the New Game
|
||||||
// hint really does gain Russian Scrabble. A guest who changed the set themselves is left
|
// hint really does gain Russian Scrabble. A guest who changed the set themselves is left
|
||||||
// alone: only the untouched guest default is replaced.
|
// alone: only the untouched guest default is replaced.
|
||||||
func (s *Store) ClearGuest(ctx context.Context, accountID uuid.UUID) error {
|
func (s *Store) ClearGuest(ctx context.Context, accountID uuid.UUID) error {
|
||||||
|
|||||||
@@ -249,7 +249,7 @@ arrive from a platform rather than completing a mandatory registration).
|
|||||||
**registered** account defaults to the two Russian-alphabet games — **Erudit + Russian
|
**registered** account defaults to the two Russian-alphabet games — **Erudit + Russian
|
||||||
Scrabble** (a DB column default); a **guest** is deliberately narrower and starts on
|
Scrabble** (a DB column default); a **guest** is deliberately narrower and starts on
|
||||||
**Erudit only** (`account.ProvisionGuest` writes the set explicitly), with New Game inviting
|
**Erudit only** (`account.ProvisionGuest` writes the set explicitly), with New Game inviting
|
||||||
the guest to register for the rest. Registering promotes the set to the registered default
|
the guest to sign in for the rest. Signing in promotes the set to the registered default
|
||||||
(`account.ClearGuest`), but only while the guest still carries the untouched guest set —
|
(`account.ClearGuest`), but only while the guest still carries the untouched guest set —
|
||||||
a player who chose their own set is never overruled, and **existing accounts are not
|
a player who chose their own set is never overruled, and **existing accounts are not
|
||||||
backfilled** when the default moves. At least one variant must stay selected. The picker is
|
backfilled** when the default moves. At least one variant must stay selected. The picker is
|
||||||
|
|||||||
+2
-1
@@ -455,7 +455,8 @@ variants — Erudite, Russian Scrabble and English Scrabble, shown **Erudite-fir
|
|||||||
yourself to be matched into; a **newly registered account starts with Erudite + Russian
|
yourself to be matched into; a **newly registered account starts with Erudite + Russian
|
||||||
Scrabble**, and arriving through the **promo bot's deep link** also enables **English Scrabble**
|
Scrabble**, and arriving through the **promo bot's deep link** also enables **English Scrabble**
|
||||||
when your Telegram language is not Russian. A **guest** starts with **Erudite only** and New Game
|
when your Telegram language is not Russian. A **guest** starts with **Erudite only** and New Game
|
||||||
invites them to register for the rest; registering widens the account to the registered default.
|
invites them to **sign in** for the rest (the same wording and link as the Stats guest stub, both
|
||||||
|
routing to Settings → Profile); signing in widens the account to the registered default.
|
||||||
Changing the defaults never rewrites accounts that already exist. You must keep
|
Changing the defaults never rewrites accounts that already exist. You must keep
|
||||||
**at least one** selected. This list is exactly what **New Game** offers when you start a game
|
**at least one** selected. This list is exactly what **New Game** offers when you start a game
|
||||||
(auto-match, an AI game, or a friend invitation you create) — a variant you have not enabled is
|
(auto-match, an AI game, or a friend invitation you create) — a variant you have not enabled is
|
||||||
|
|||||||
@@ -462,8 +462,8 @@ Telegram. Внутри VK Mini App те же настройки — и язык
|
|||||||
которые ты разрешаешь себя подбирать; **новый зарегистрированный аккаунт стартует с Эрудитом и
|
которые ты разрешаешь себя подбирать; **новый зарегистрированный аккаунт стартует с Эрудитом и
|
||||||
русским Scrabble**, а вход по **диплинку промо-бота** дополнительно включает **английский
|
русским Scrabble**, а вход по **диплинку промо-бота** дополнительно включает **английский
|
||||||
Scrabble**, если язык Telegram не русский. **Гость** стартует **только с Эрудитом**, и «Новая игра»
|
Scrabble**, если язык Telegram не русский. **Гость** стартует **только с Эрудитом**, и «Новая игра»
|
||||||
предлагает ему зарегистрироваться ради остальных; регистрация расширяет аккаунт до набора
|
предлагает ему **войти** ради остальных (та же формулировка и ссылка, что и у гостевой заглушки в
|
||||||
зарегистрированного. Смена набора по умолчанию никогда не переписывает уже существующие аккаунты.
|
статистике, обе ведут в Settings → Profile); вход расширяет аккаунт до набора зарегистрированного. Смена набора по умолчанию никогда не переписывает уже существующие аккаунты.
|
||||||
Нужно оставить выбранным **хотя бы один**. Именно этот список предлагает **Новая игра**, когда ты
|
Нужно оставить выбранным **хотя бы один**. Именно этот список предлагает **Новая игра**, когда ты
|
||||||
запускаешь партию (авто-подбор, игра с ИИ или приглашение друга, которое ты создаёшь), — не
|
запускаешь партию (авто-подбор, игра с ИИ или приглашение друга, которое ты создаёшь), — не
|
||||||
включённый вариант не предлагается, и сервер его отклоняет. На партии, в которые тебя
|
включённый вариант не предлагается, и сервер его отклоняет. На партии, в которые тебя
|
||||||
|
|||||||
@@ -41,6 +41,10 @@ export const en = {
|
|||||||
'common.you': 'You',
|
'common.you': 'You',
|
||||||
'common.guest': 'Guest',
|
'common.guest': 'Guest',
|
||||||
'common.save': 'Save',
|
'common.save': 'Save',
|
||||||
|
// The opening word of a guest's invitation to sign in, rendered as a link to the profile
|
||||||
|
// screen (New Game, Stats). Shared so the two screens cannot drift apart; each screen keeps
|
||||||
|
// its own tail, which carries its leading space — Svelte trims literal edge whitespace.
|
||||||
|
'common.signInLink': 'Sign in',
|
||||||
|
|
||||||
'login.title': 'Sign in',
|
'login.title': 'Sign in',
|
||||||
'login.guest': 'Play as guest',
|
'login.guest': 'Play as guest',
|
||||||
@@ -83,10 +87,8 @@ export const en = {
|
|||||||
'new.moveLimit': 'Move time: {n} h 00 min',
|
'new.moveLimit': 'Move time: {n} h 00 min',
|
||||||
'new.searchHint':
|
'new.searchHint':
|
||||||
'Finding an opponent can sometimes take a while. If you do not want to wait, close the app after starting the game and come back in a couple of minutes.',
|
'Finding an opponent can sometimes take a while. If you do not want to wait, close the app after starting the game and come back in a couple of minutes.',
|
||||||
// The guest's invitation to register, shown under the single variant a guest is offered. It is
|
// The tail of the guest's invitation to sign in (common.signInLink opens it), shown under the
|
||||||
// two keys because the first word is a link to the profile screen; the tail carries its own
|
// single variant a guest is offered.
|
||||||
// leading space, since Svelte trims literal whitespace at the edges of markup.
|
|
||||||
'new.guestVariantHintLink': 'Register',
|
|
||||||
'new.guestVariantHint': ' to play the Russian or English Scrabble.',
|
'new.guestVariantHint': ' to play the Russian or English Scrabble.',
|
||||||
|
|
||||||
// First-run coachmark hints (components/Coachmark.svelte). The leading emoji in each comment
|
// First-run coachmark hints (components/Coachmark.svelte). The leading emoji in each comment
|
||||||
@@ -420,7 +422,8 @@ export const en = {
|
|||||||
'stats.winRate': 'Win rate',
|
'stats.winRate': 'Win rate',
|
||||||
'stats.maxGame': 'Best game',
|
'stats.maxGame': 'Best game',
|
||||||
'stats.maxWord': 'Best move',
|
'stats.maxWord': 'Best move',
|
||||||
'stats.guestHint': 'Sign in to track your statistics.',
|
// The tail of the guest's invitation to sign in (common.signInLink opens it).
|
||||||
|
'stats.guestHint': ' to track your statistics.',
|
||||||
|
|
||||||
'game.exportGcg': 'Export game',
|
'game.exportGcg': 'Export game',
|
||||||
'game.exportChoice': 'Export the finished game as:',
|
'game.exportChoice': 'Export the finished game as:',
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ export const ru: Record<MessageKey, string> = {
|
|||||||
'common.you': 'Вы',
|
'common.you': 'Вы',
|
||||||
'common.guest': 'Гость',
|
'common.guest': 'Гость',
|
||||||
'common.save': 'Сохранить',
|
'common.save': 'Сохранить',
|
||||||
|
'common.signInLink': 'Войдите',
|
||||||
|
|
||||||
'login.title': 'Вход',
|
'login.title': 'Вход',
|
||||||
'login.guest': 'Играть как гость',
|
'login.guest': 'Играть как гость',
|
||||||
@@ -84,7 +85,6 @@ export const ru: Record<MessageKey, string> = {
|
|||||||
'new.moveLimit': 'Время на ход: {n} ч. 00 мин.',
|
'new.moveLimit': 'Время на ход: {n} ч. 00 мин.',
|
||||||
'new.searchHint':
|
'new.searchHint':
|
||||||
'Иногда поиск соперника может занять некоторое время. Если не захотите ждать после начала игры, можете вернуться в приложение через несколько минут.',
|
'Иногда поиск соперника может занять некоторое время. Если не захотите ждать после начала игры, можете вернуться в приложение через несколько минут.',
|
||||||
'new.guestVariantHintLink': 'Зарегистрируйтесь',
|
|
||||||
'new.guestVariantHint': ', чтобы играть в русский или английский вариант Скрэббл.',
|
'new.guestVariantHint': ', чтобы играть в русский или английский вариант Скрэббл.',
|
||||||
|
|
||||||
// Подсказки первого запуска (components/Coachmark.svelte).
|
// Подсказки первого запуска (components/Coachmark.svelte).
|
||||||
@@ -417,7 +417,7 @@ export const ru: Record<MessageKey, string> = {
|
|||||||
'stats.winRate': 'Доля побед',
|
'stats.winRate': 'Доля побед',
|
||||||
'stats.maxGame': 'Лучшая игра',
|
'stats.maxGame': 'Лучшая игра',
|
||||||
'stats.maxWord': 'Лучший ход',
|
'stats.maxWord': 'Лучший ход',
|
||||||
'stats.guestHint': 'Войдите, чтобы вести статистику.',
|
'stats.guestHint': ', чтобы вести статистику.',
|
||||||
|
|
||||||
'game.exportGcg': 'Экспорт партии',
|
'game.exportGcg': 'Экспорт партии',
|
||||||
'game.exportChoice': 'Экспортировать завершённую партию как:',
|
'game.exportChoice': 'Экспортировать завершённую партию как:',
|
||||||
|
|||||||
@@ -90,11 +90,11 @@ export function availableVariants(preferences: Variant[] | undefined): VariantOp
|
|||||||
return ALL_VARIANTS.filter((v) => effective.includes(v.id));
|
return ALL_VARIANTS.filter((v) => effective.includes(v.id));
|
||||||
}
|
}
|
||||||
|
|
||||||
// showRegisterHint reports whether New Game invites the player to register in order to unlock the
|
// showRegisterHint reports whether New Game invites the player to sign in in order to unlock the
|
||||||
// other games, given whether they are a guest (isGuest), how many variants they are offered
|
// other games, given whether they are a guest (isGuest), how many variants they are offered
|
||||||
// (offered) and whether the app is in offline mode (offline). It is the guest's case alone: a
|
// (offered) and whether the app is in offline mode (offline). It is the guest's case alone: a
|
||||||
// guest is created with Erudit only, while registering widens the account to both Russian-alphabet
|
// guest is created with Erudit only, while signing in widens the account to both Russian-alphabet
|
||||||
// games. Offline it is suppressed — registration needs the network, so the link would be dead.
|
// games. Offline it is suppressed — signing in needs the network, so the link would be dead.
|
||||||
export function showRegisterHint(isGuest: boolean, offered: number, offline: boolean): boolean {
|
export function showRegisterHint(isGuest: boolean, offered: number, offline: boolean): boolean {
|
||||||
return isGuest && !offline && offered === 1;
|
return isGuest && !offline && offered === 1;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -396,11 +396,12 @@
|
|||||||
</button>
|
</button>
|
||||||
{/each}
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
<!-- A guest is offered Erudit alone; registering widens the account to both Russian games
|
<!-- A guest is offered Erudit alone; signing in widens the account to both Russian games
|
||||||
(and Settings then offers English), so point them at the profile screen. -->
|
(and Settings then offers English), so point them at the profile screen — the same
|
||||||
|
wording and link the Stats guest stub uses. -->
|
||||||
{#if showRegisterHint(guest, variants.length, offlineMode.active)}
|
{#if showRegisterHint(guest, variants.length, offlineMode.active)}
|
||||||
<p class="guesthint">
|
<p class="guesthint">
|
||||||
<button class="hintlink" onclick={() => navigate('/profile')}>{t('new.guestVariantHintLink')}</button>{t('new.guestVariantHint')}
|
<button class="hintlink" onclick={() => navigate('/profile')}>{t('common.signInLink')}</button>{t('new.guestVariantHint')}
|
||||||
</p>
|
</p>
|
||||||
{/if}
|
{/if}
|
||||||
{#if variants.some((v) => supportsMultipleWordsToggle(v.id))}
|
{#if variants.some((v) => supportsMultipleWordsToggle(v.id))}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
import WordTiles from '../components/WordTiles.svelte';
|
import WordTiles from '../components/WordTiles.svelte';
|
||||||
import { app, handleError } from '../lib/app.svelte';
|
import { app, handleError } from '../lib/app.svelte';
|
||||||
import { gateway } from '../lib/gateway';
|
import { gateway } from '../lib/gateway';
|
||||||
|
import { navigate } from '../lib/router.svelte';
|
||||||
import { t, i18n, type MessageKey } from '../lib/i18n/index.svelte';
|
import { t, i18n, type MessageKey } from '../lib/i18n/index.svelte';
|
||||||
import { gamesPlayed, hintSharePercent, winRate } from '../lib/stats';
|
import { gamesPlayed, hintSharePercent, winRate } from '../lib/stats';
|
||||||
import { ALL_VARIANTS, variantNameKey } from '../lib/variants';
|
import { ALL_VARIANTS, variantNameKey } from '../lib/variants';
|
||||||
@@ -56,7 +57,11 @@
|
|||||||
<Screen title={t('stats.title')} back="/">
|
<Screen title={t('stats.title')} back="/">
|
||||||
<div class="page">
|
<div class="page">
|
||||||
{#if app.profile?.isGuest}
|
{#if app.profile?.isGuest}
|
||||||
<p class="muted">{t('stats.guestHint')}</p>
|
<!-- The opening word links to the profile screen, where a guest signs in (the same wording
|
||||||
|
and link New Game shows a guest under its lone variant). -->
|
||||||
|
<p class="muted">
|
||||||
|
<button class="hintlink" onclick={() => navigate('/profile')}>{t('common.signInLink')}</button>{t('stats.guestHint')}
|
||||||
|
</p>
|
||||||
{:else if stats}
|
{:else if stats}
|
||||||
<div class="grid">
|
<div class="grid">
|
||||||
{#each cards as c (c.key)}
|
{#each cards as c (c.key)}
|
||||||
@@ -89,6 +94,16 @@
|
|||||||
.muted {
|
.muted {
|
||||||
color: var(--text-muted);
|
color: var(--text-muted);
|
||||||
}
|
}
|
||||||
|
/* The inline link-styled button opening the guest stub (mirrors New Game's .hintlink). */
|
||||||
|
.hintlink {
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
padding: 0;
|
||||||
|
color: var(--accent);
|
||||||
|
font: inherit;
|
||||||
|
text-decoration: underline;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
.grid {
|
.grid {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(2, 1fr);
|
grid-template-columns: repeat(2, 1fr);
|
||||||
|
|||||||
Reference in New Issue
Block a user