Commit Graph

11 Commits

Author SHA1 Message Date
Ilia Denisov 54bc31c619 fix(ui): default the profileless client to Erudit only, not every variant
availableVariants fell back to all three variants when the player had no
stored preferences. A fresh offline native launch boots with no profile, so
New Game exposed the English game before the player opted in — contrary to the
backend's Erudit-only new-account default (docs/FUNCTIONAL.md). Fall back to
DEFAULT_VARIANTS (Erudit only) instead; all dictionaries stay bundled.
2026-07-14 21:57:21 +02:00
Ilia Denisov e3e4cedc77 feat(ui): Erudit blank tiles carry the star (✻) mark
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 54s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 1m17s
The Erudit variant's blank is the "звёздочка", so render it with a star.
An empty rack blank (and its drag ghost) shows ✻ centred; a placed blank
keeps its designated letter and carries ✻ where the (absent) point value
sits — on the board and in the Stats best-move tiles. The Scrabble variants
are unchanged. Gated by usesStarBlank() in lib/variants.ts.
2026-06-22 11:52:00 +02:00
Ilia Denisov 57c778f9b2 feat(telegram,game): single bot + per-user variant preferences
CI / changes (pull_request) Successful in 2s
CI / unit (pull_request) Successful in 10s
CI / integration (pull_request) Successful in 13s
CI / ui (pull_request) Successful in 54s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 57s
Collapse the two per-language Telegram bots into one unified bot and
replace language-based variant gating with explicit per-user variant
preferences.

- Telegram: one bot; drop service_language and the supported_languages
  set everywhere (DB, account, auth, FlatBuffers Session wire, gateway,
  connector proto). The single bot renders chat and out-of-app push in
  the recipient's preferred_language; remove the game-language push
  routing override (notify Intent.Language / push Event.language).
- Preferences: new accounts.variant_preferences (text[], DB default
  {erudit_ru}, CHECK non-empty + subset of the three variants). Gates
  the New Game picker, vs-AI and the friend invitation the player
  creates, enforced server-side (HTTP 400 otherwise); an invited friend
  may still accept any variant. Edited on the Settings screen; variants
  are Erudit-first everywhere.
- Admin: drop the per-bot language selectors (broadcast / send-to-user)
  and the feedback channel_lang column/field.
- Env/CI: collapse TELEGRAM_BOT_TOKEN_{EN,RU}, TELEGRAM_GAME_CHANNEL_ID_{EN,RU},
  VITE_TELEGRAM_LINK{,_EN,_RU} and VITE_TELEGRAM_GAME_CHANNEL_NAME_{EN,RU}
  to single unsuffixed names; drop GATEWAY_DEFAULT_SUPPORTED_LANGUAGES.
- Docs updated (ARCHITECTURE, FUNCTIONAL + _ru, platform/telegram, gateway,
  backend, ui, UI_DESIGN, PRERELEASE).

The migration squash is deferred to a follow-up PR.
2026-06-20 14:23:25 +02:00
Ilia Denisov 74455c7b12 feat: "multiple words per turn" rule for Russian games
CI / changes (pull_request) Successful in 2s
CI / unit (pull_request) Successful in 9s
CI / integration (pull_request) Successful in 15s
CI / ui (pull_request) Successful in 45s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 1m10s
Add a per-game rule chosen on New Game for Russian variants (default off = the
single-word rule; on = standard Scrabble). Off, only the main word along the play
direction is validated and scored; perpendicular cross-words are ignored,
including in robot move generation. The rule rides every create and enqueue
request and joins the matchmaking key, so games and auto-match stay one uniform
path; "Russian-only" is a UI affordance (English always sends standard and shows
no toggle).

- Engine: consume scrabble-solver v1.1.0's PlayOptions{IgnoreCrossWords}, threaded
  through engine.Options.MultipleWordsPerTurn -> playOpts() into validate, score
  and generate.
- Backend: thread the flag through game CreateParams/Game + store (games column),
  lobby InvitationSettings + invitation row, and the matchmaker queue key (variant
  + rule); persisted, so a rebuilt-from-journal game keeps it. Baseline migration
  gains multiple_words_per_turn (DB not versioned); jet regenerated.
- Edge: multiple_words_per_turn added to the EnqueueRequest / CreateInvitationRequest
  FlatBuffers tables (Go + TS regenerated) and threaded through the gateway.
- UI: a "Multiple words per turn" toggle on New Game, shown for Russian variants
  only (auto-match and friend invite), default off; English silently sends standard.
- Tests: backend engine/matchmaker; UI unit (gating) + Playwright e2e (solver
  corner-case + GCG fixtures ship in v1.1.0). Docs + PRERELEASE tracker updated.
2026-06-12 02:17:30 +02:00
Ilia Denisov 8881214213 R6(a): de-stage code, docs, READMEs; split stage6_test
Mechanical, behaviour-preserving removal of Stage N / TODO-N / phase (RN)
references from comments, doc-comments, service READMEs, the current-state docs
(ARCHITECTURE, FUNCTIONAL+_ru, TESTING, UI_DESIGN), config-file comments, and the
.fbs/.proto schema comments. PLAN.md / PRERELEASE.md / CLAUDE.md keep the stage
history.

- Rename the only stage-named identifiers: registerStage8 -> registerSocialOps,
  registerStage11 -> registerLinkOps (gateway transcode).
- Split stage6_test.go: TestEmailLoginFlow -> email_test.go,
  TestGuestAutoMatchLeavesNoStats (+ provisionGuest) -> account_test.go.
- Regenerated proto bindings (push.pb.go, telegram_grpc.pb.go) from the de-staged
  .proto comments; FB Go/TS bindings unchanged (flatc strips schema comments).

go build/vet/gofmt clean across modules; integration typecheck and pnpm check green.
2026-06-10 16:56:03 +02:00
Ilia Denisov 26aa154547 R1: schema & naming reset — squash migrations, rename variants
CI / changes (pull_request) Successful in 2s
CI / unit (pull_request) Successful in 9s
CI / integration (pull_request) Successful in 11s
CI / ui (pull_request) Successful in 37s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 1m8s
Squash the 12 goose migrations into one 00001_baseline.sql (there is no prod
data; verified schema-identical to the chain via a pg_dump diff + the green
integration suite) and rename the game-variant labels
english/russian_scrabble/erudit -> scrabble_en/scrabble_ru/erudit_ru across the
backend, the FlatBuffers wire values and the UI.

dawg filenames and the Go enum identifiers are unchanged; the i18n display keys
are kept. Adds PRERELEASE.md (the R1-R7 pre-release tracker), linked from
CLAUDE.md. Contour DB wipe and the scrabble-dictionary tidy are follow-ups.
2026-06-09 12:09:50 +02:00
Ilia Denisov d3657fdf5c Stage 17 round 6 (#11/#12): quick-game variant plaques with rules, flag, and move-limit
CI / changes (pull_request) Successful in 1s
CI / unit (pull_request) Successful in 8s
CI / integration (pull_request) Successful in 13s
CI / ui (pull_request) Successful in 30s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 55s
Each auto-match variant is now a lobby-style plaque: the display name with a flag on the
right (🇺🇸 / 🇷🇺; Erudit uses a bundled minimalist USSR flag SVG) and a one-line rules
summary below — bag size, the ё rule, and bonus differences, sourced from the engine
rulesets (Scrabble 100 · Скрэббл 104, ё a letter · Эрудит 131, ё=е, no centre ×2, +15).
The move-time limit (24h auto-match clock) is shown under the buttons. e2e locks it.

(Multiple-words-per-move is the same for every variant, so it is described in About/landing
rather than repeated on each button.)
2026-06-07 11:48:19 +02:00
Ilia Denisov 512ad4dfb9 Stage 17 round 6 (cluster 1): profile, tap flash, variant naming, chat/nudge by turn
CI / changes (pull_request) Successful in 1s
CI / unit (pull_request) Successful in 9s
CI / integration (pull_request) Successful in 11s
CI / ui (pull_request) Successful in 30s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 1m3s
- Profile: drop the hint-balance line.
- Board: no mobile tap flash on a cell tap (-webkit-tap-highlight-color: transparent),
  matching the web click; the only intentional cell animation stays the last-word flash.
- Variant names keyed by the game's alphabet, not the UI language: english -> Scrabble
  always, russian_scrabble -> Скрэббл always (unlocalized, never collide), erudit localized.
- Chat/nudge are mutually exclusive by turn: the message field + Send show on your turn,
  the nudge replaces them on the opponent's turn; while the nudge cooldown is active the
  button is disabled with a grey 'awaiting reply' caption to its left.
2026-06-07 11:18:25 +02:00
Ilia Denisov 29d1193a0a Stage 17 round 5 — board interaction & UI polish
CI / changes (pull_request) Successful in 2s
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 1m17s
- Even zoom: interpolate the board scroll toward a pre-clamped target as the real width
  grows/shrinks, so it magnifies A->B in one motion instead of lurching and snapping back
  near the edges/centre. Recentre only on a zoom toggle, never on a focus change — so a
  2nd+ placed tile and a hovered dragged tile no longer jump the board.
- Drag: highlight the aimed-at empty cell as a drop target; hover-hold auto-zoom now
  fires only for the first (zoom-in) hold.
- Pinch zoom: two-finger spread/close toggles zoom toward the pinch midpoint (preventDefault
  only for two touches, so one-finger scroll stays native); a second finger aborts a drag.
- Shuffle hop capped at 0.3s and disabled under reduce-motion.
- Make-move is a borderless icon button, disabled while the pending word is known illegal.
- Variant display names: english & russian_scrabble -> Scrabble/Скрэббл, erudit ->
  Erudite/Эрудит; the in-game title shows the variant name (was always 'Scrabble').
2026-06-07 09:34:07 +02:00
Ilia Denisov 1d0bafaabb Stage 17: UI defect fixes (russian variant, Telegram theme/nav/banner, reconnect, hint zoom, plaque, history, transitions, per-game cache)
- #6 align the UI variant id to the backend canonical 'russian_scrabble' (type, variants, Lobby, mock, tests) — fixes the New->Russian 400
- #11/#12 inside Telegram force the colour scheme from WebApp.colorScheme (over OS prefers-color-scheme, fixing the Telegram Desktop breakage) and hide the theme switcher
- #14/#15 nav bar takes Telegram's bg; announcement banner gets a dedicated subtle --ad-bg accent token
- #16 suppress the reconnect banner while backgrounded and silently reconnect the live stream on return to the foreground
- #17 hint zoom scrolls to the placement's bounding box, not the top-left
- #19/#20 players plaque: active seat raised with side shadows, others sunk; tap toggles history
- #21/#23 history: scrollbar-gutter:stable (no word jitter); fixed-height drawer pins the bottom shadow to the board
- #3 (UI) disable nudge on the player's own turn
- #18a directional screen slide transitions (forward in from the right, back reveals the lobby)
- #13 per-game in-memory cache: instant render on re-entry + background refresh
- e2e: openGame waits for the slide transition to settle
2026-06-06 10:23:42 +02:00
Ilia Denisov e9f836db87 Stage 15: dual Telegram bots & language-gated variants
Tests · Go / test (push) Successful in 9s
Tests · Integration / integration (push) Successful in 10s
Tests · UI / test (push) Successful in 20s
Tests · Go / test (pull_request) Successful in 8s
Tests · Integration / integration (pull_request) Successful in 11s
Tests · UI / test (pull_request) Successful in 19s
Service-agnostic refinement of the owner's idea: the sign-in service returns a
set of supported game languages with the user identity, and the lobby gates the
New Game variant choice by it (en -> English; ru -> Russian + Эрудит).

- Connector hosts two bots in one container (one per service language, each its
  own token + game channel; the same telegram_id spans both). ValidateInitData
  tries each token and returns the validating bot's service_language +
  supported_languages. Per-language config (TELEGRAM_BOT_TOKEN_EN/_RU, channels).
- supported_languages rides the Session (fbs, session-scoped, not persisted); the
  UI offers only the matching variants on New Game — gating only the START of a
  new game (auto-match + friend invite), not accept/open/play; backend does not
  enforce.
- service_language persisted (accounts.service_language, migration 00010, written
  every login, last-login-wins) and routes the user-facing Notify push back
  through the right bot (push-target coalesces with preferred_language).
- Admin SendToUser/SendToGameChannel gain an operator-chosen language selector in
  the console (unrelated to ValidateInitData).
- Non-Telegram logins carry the gateway default set
  (GATEWAY_DEFAULT_SUPPORTED_LANGUAGES, all variants).

Wire (committed regen): ValidateInitDataResponse +service_language
+supported_languages; Session +supported_languages; SendToUser/SendToGameChannel
+language. Docs (ARCHITECTURE/FUNCTIONAL/_ru/READMEs) + PLAN updated; stage marked done.
2026-06-05 09:35:53 +02:00