d86e0223738f6bc101a9e1f4b89929af97ec90a2
415 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
d86e022373 |
Merge pull request 'feat(telegram): bot support relay — per-user forum topics' (#130) from feature/telegram-support-relay into development
CI / unit (push) Successful in 10s
CI / integration (push) Successful in 15s
CI / gate (push) Successful in 0s
CI / deploy (push) Successful in 1m22s
CI / changes (push) Successful in 2s
CI / ui (push) Successful in 56s
CI / changes (pull_request) Successful in 2s
CI / unit (pull_request) Successful in 10s
CI / integration (pull_request) Successful in 15s
CI / ui (pull_request) Successful in 55s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Has been skipped
|
||
|
|
6a602aefae |
feat(telegram): bot support relay — per-user forum topics
CI / changes (pull_request) Successful in 2s
CI / unit (pull_request) Successful in 12s
CI / integration (pull_request) Successful in 16s
CI / ui (pull_request) Successful in 56s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 1m20s
Users who DM the bot anything but /start are relayed into a private forum supergroup, one topic per user. Operators (the chat's admins) reply in the topic and the bot copies it back to the user; an info card opening each topic carries a Block/Unblock toggle and a Clear button. State is a small JSON file on a new /data volume — the bot host has no database. Off by default (TELEGRAM_SUPPORT_CHAT_ID=0): the prod bot is unchanged until the operator sets the chat id and adds the bot as a forum admin. - internal/support: concurrency-safe JSON store (topic map, block list, relayed message ids) with field-targeted mutators and atomic save - bot/support.go: relay both ways via copyMessage, short-TTL admin cache, callback buttons, per-user topic-create lock, loop guard (skip the bot's own posts), reopen a deleted topic on the next message - config + compose + CI/prod-deploy: TELEGRAM_SUPPORT_CHAT_ID per contour + TELEGRAM_SUPPORT_STATE_DIR; bot-state named volume; /data pre-owned by UID 65532 so a fresh volume is writable under distroless - docs: ARCHITECTURE §15 + decision record, FUNCTIONAL (+ru), README |
||
|
|
e6277dcd43 |
Merge pull request 'fix(ui): Android Telegram nav — windowed mode, no close guard' (#128) from feature/telegram-android-nav-fixes into development
CI / changes (push) Successful in 2s
CI / unit (pull_request) Has been skipped
CI / integration (pull_request) Has been skipped
CI / ui (pull_request) Successful in 56s
CI / gate (pull_request) Successful in 0s
CI / unit (push) Has been skipped
CI / integration (push) Has been skipped
CI / ui (push) Successful in 56s
CI / gate (push) Successful in 0s
CI / deploy (push) Successful in 1m18s
CI / changes (pull_request) Successful in 2s
CI / deploy (pull_request) Has been skipped
|
||
|
|
37070c3cb7 |
feat(ui): drop Telegram fullscreen; own back chevron everywhere; hidden debug panel
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 56s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 2m13s
Finalises the Telegram Mini App navigation work after on-device testing (Pixel 10 / Android 17 + iOS, fresh beta clients): - Remove requestFullscreen entirely. Immersive fullscreen hid Telegram's native header (and its BackButton) and the Android system swipe-back minimised the app; the owner prefers the windowed full-size (expand) presentation, so the app never requests fullscreen on any platform now. - The app's own back chevron (Header, showBack = !!back) drives back-navigation on every platform; the native Telegram BackButton is dropped — it does not render in the windowed Mini App (backVisible=false on iOS and Android), so relying on it lost back navigation (notably none on iOS). - Replace the temporary always-on diagnostic overlay with a hidden debug panel (components/DebugPanel): ten quick taps on the header title open it; it shows a privacy-safe client diagnostic snapshot (app version, locale, online, userId, Telegram chrome / viewport / SDK state — no secrets, no IP) and shares it via the OS share sheet / clipboard; a tap anywhere except Share dismisses it. - Drop the now-dead telegramRequestFullscreen / telegramBackButton / isTelegramAndroid helpers and the iOS-fullscreen unit test. Telegram has no native non-modal notification API (only modal showPopup / showAlert), so in-app toasts stay ours. Docs: UI_DESIGN.md. |
||
|
|
53d6883ffd |
fix(ui): own back chevron in Telegram on all platforms; drop the native BackButton
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 55s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 1m30s
The Telegram native BackButton does not render in the windowed Mini App (the owner's emulator + a fresh beta TG report backVisible=false on both iOS and Android), so relying on it lost back navigation — iOS had no back affordance at all. Show the app's own back chevron whenever there is a back target, on every platform (Header showBack = !!back), and drop the now-dead native BackButton effect (App.svelte). The native close control stays — a windowed Mini App cannot hide it (no Telegram API). WIP: the temp lobby diagnostic overlay and the requestFullscreen no-op remain for the owner's emulator test; finalize after confirmation. |
||
|
|
93c57b3558 |
test(ui): TEMP-skip the iOS fullscreen unit test (requestFullscreen is a no-op for the owner test; restore with the iOS path)
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 56s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 1m30s
|
||
|
|
6f00c2f41d |
chore(ui): TEMP disable fullscreen for testing + Android back chevron + BackButton diag
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) Failing after 8s
CI / gate (pull_request) Failing after 0s
CI / deploy (pull_request) Has been skipped
WIP for the Android nav investigation (TEMP bits reverted before merge): - telegramRequestFullscreen: temporarily a no-op (incl. iOS) so the owner can confirm the Android "fullscreen look" is Telegram's own Mini App presentation, not our requestFullscreen (isFullscreen is already false on Android). - Header: show the app's own back chevron in Telegram on Android, where the native BackButton does not render — a reliable tap-back. [keep] - Diagnostic overlay moved app-wide (pointer-events:none) and now reports BackButton state (req/present/visible) + viewport geometry, to see whether the native BackButton can capture the Android system swipe-back. |
||
|
|
79766438a2 |
chore(ui): TEMP lobby diagnostic for the Android fullscreen issue
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 56s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 1m19s
Renders Telegram viewport/fullscreen state (isFullscreen, isExpanded, viewport heights, innerH vs screenH, safe-area insets) in the lobby, inside Telegram only, to diagnose why the app still opens fullscreen on Android with requestFullscreen now iOS-only and no persisted state (fresh TG + test account). REVERT before merge. |
||
|
|
6aa5023b24 |
fix(ui): Android Telegram nav — windowed mode, no close guard
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 56s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 1m18s
Three Android Mini App issues, all in the Telegram chrome: - Entering a game showed no native back button, and the Android system swipe-back minimised the app instead of navigating. Root cause: immersive fullscreen (requestFullscreen). On Android, fullscreen replaces the native header — and its BackButton, which also captures the system back — with a bare close/menu pill, so back navigation has no control to land on. Request fullscreen on iOS only; Android stays windowed, keeping the native header + BackButton (and the system swipe-back that routes to it). iOS is unchanged. - Closing the game board always prompted "changes that you made may not be saved", even on a board just opened and untouched. The close-confirmation was armed unconditionally on game mount. Remove it entirely: move drafts auto-save (debounced during play + flushed on destroy), so nothing is lost on close. Drops the now-unused telegramClosingConfirmation + isMobilePlatform helpers and their SDK interface fields. Docs: UI_DESIGN.md. |
||
|
|
508dc870ec |
Merge pull request 'feat(ui): diagnostic screen on /telegram/ instead of the landing bounce' (#126) from feature/telegram-launch-diagnostic into development
CI / ui (push) Successful in 56s
CI / gate (push) Successful in 0s
CI / deploy (push) Successful in 1m16s
CI / ui (pull_request) Successful in 56s
CI / gate (pull_request) Successful in 0s
CI / changes (push) Successful in 2s
CI / unit (push) Has been skipped
CI / integration (push) Has been skipped
CI / changes (pull_request) Successful in 2s
CI / unit (pull_request) Has been skipped
CI / integration (pull_request) Has been skipped
CI / deploy (pull_request) Has been skipped
|
||
|
|
e3899d4755 |
feat(ui): record the SDK load outcome in the launch diagnostic
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 55s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 1m16s
The diagnostic showed only sdk: yes/no (window.Telegram presence), not why the SDK was absent. Capture how the dynamic telegram-web-app.js load resolved — present / loaded / no-webapp / error / timeout — and surface it as "sdk-load: <outcome>". error/timeout pinpoint a blocked or hanging telegram.org (the prime suspect for an empty launch); no-webapp a loaded-but-broken script. loadTelegramSDK records the outcome (telegramSdkOutcome); collectTelegramDiag carries it into the screen. Unit tests cover each outcome; the blocked-script e2e now asserts sdk-load: error. |
||
|
|
ae5090b851 |
fix(ui): load telegram-web-app.js dynamically with a timeout
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 55s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 1m18s
The Telegram SDK was a render-blocking <script src="telegram.org/..."> in the shared index.html shell, so it ran on every entry (/telegram/, /app/, native). On a network that blocks telegram.org — common where Telegram itself reaches users only over a proxy — the script hangs forever, stranding the whole page, including the launch-diagnostic screen meant to surface exactly this failure. This is the likely root cause of the Android "won't open" reports (all launch methods fail identically; iOS on a different network works). Remove the head <script> and load the SDK dynamically (lib/telegram.ts loadTelegramSDK) with a 10s timeout, only on a Telegram entry (the /telegram/ path or a tgWebApp launch fragment). The SPA — served from our own reachable origin — boots first and controls the load: on a block/error it falls through to the diagnostic screen (reporting sdk: no) instead of hanging, and Retry re-attempts. /app/ and the native build no longer touch telegram.org. Pin the SDK to the version the official page recommends (?62) for the newer client features the app already uses (fullscreen, safe areas, swipe guard). Tests: loadTelegramSDK unit tests (present / error / timeout); an e2e that aborts the script fetch and asserts the diagnostic still renders. |
||
|
|
0c5d3808d7 |
feat(ui): diagnostic screen on /telegram/ instead of the landing bounce
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 55s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 1m22s
A Mini App launch on /telegram/ without sign-in data (empty initData) used to
location.replace('/') — bouncing the visitor to the marketing landing. That
destroyed all diagnosability and, on some Android clients that reach the entry
with no initData, simply looked like the app refusing to open.
Replace the bounce with a compact, privacy-safe launch-error screen
(screens/TelegramLaunchError.svelte) that renders a one-screenshot diagnostic
snapshot captured at the moment of failure: SDK/WebApp presence, Telegram
platform/version, whether initData is empty, whether the URL fragment carried
tgWebAppData, the initData field NAMES present/missing (never the signed
values, never an IP), and OS/mobile/browser via User-Agent Client Hints plus
the full User-Agent. A Share button delivers it through the OS share sheet
(clipboard copy on desktop, reusing the GCG no-webview-strand guard); Retry
re-checks in place to recover a late initData without a reload (which would
discard the launch fragment).
This is the instrument to root-cause the Android empty-initData failure, which
is not reproducible in Playwright.
Tests: collectTelegramDiag + shareText/pickTextShare unit tests; the /telegram/
e2e now asserts the diagnostic screen, not a redirect. Docs: ARCHITECTURE.md +
UI_DESIGN.md updated.
|
||
|
|
18db62e19d |
Merge pull request 'feat(ui): friends list as lobby-style rows with kebab + confirm modals' (#123) from feature/friends-list-kebab-confirm into development
CI / ui (push) Successful in 56s
CI / gate (push) Successful in 0s
CI / changes (pull_request) Successful in 3s
CI / unit (pull_request) Successful in 10s
CI / changes (push) Successful in 2s
CI / unit (push) Has been skipped
CI / integration (push) Has been skipped
CI / deploy (push) Successful in 1m18s
CI / integration (pull_request) Successful in 14s
CI / ui (pull_request) Successful in 56s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Has been skipped
|
||
|
|
d4e34efa80 |
test(ui): e2e for the friends-list kebab, confirm modals and outside-tap
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 56s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 1m20s
Cover the reworked Settings -> Friends interactions in the mock e2e (Chromium + WebKit): the row kebab slides open the block/remove icons and an outside tap collapses it; blocking confirms (naming the friend) and moves them to Blocked; removing confirms and drops the friendship. |
||
|
|
12ff6dad86 |
feat(ui): close the friends kebab on an outside tap
CI / changes (pull_request) Successful in 3s
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 1m30s
A slid-open friend row now collapses when the user taps anywhere outside its action buttons (taps on a kebab are skipped so its own toggle still drives the open/close). Uses the same capture-phase window pointerdown idiom as Screen, active only while a row is revealed. |
||
|
|
5a80696fe8 |
feat(ui): friends list as lobby-style rows with kebab + confirm modals
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 53s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 1m31s
Settings -> Friends previously rendered each friend as a bordered card with two always-visible text buttons (Remove / Block) that fired immediately. Rework the whole screen to the lobby's visual language: one-line rows split by hairline separators across all three sections (friends, incoming requests, blocked). Each friend row gains a right-hand kebab that slides the row open to reveal two icon actions split by a vertical divider -- block (no-entry) and remove (cross) -- mirroring the lobby's slide-to-reveal. Both actions now require a confirmation modal; since the slide moves a short name off-screen, the modal keeps a generic title and shows the friend's name in the body, above the buttons, so a long name cannot stretch the sheet. Incoming keeps its accept/decline buttons and blocked keeps unblock, inline on their rows. Add the friends.actions / friends.blockConfirm / friends.unfriendConfirm keys to both i18n catalogs and document the flow in FUNCTIONAL (+_ru). |
||
|
|
d6401bb76c | Merge pull request 'fix(deploy): force-recreate caddy on its roll so config-only changes apply' (#122) from feature/prod-deploy-force-recreate-caddy into development | ||
|
|
e0a5753f1a |
fix(deploy): force-recreate caddy on its roll so config-only changes apply
CI / changes (pull_request) Successful in 2s
CI / unit (pull_request) Successful in 10s
CI / integration (pull_request) Successful in 15s
CI / ui (pull_request) Successful in 54s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 1m20s
The prod rolling deploy rolls each service with `compose up -d --no-deps <svc>`. For caddy that is a no-op on a config-only release: its image is pinned (caddy:2-alpine, no $TAG), so the compose definition is unchanged between releases, compose treats the container as current and does not recreate it, and admin is off so there is no hot reload. The new bind-mounted Caddyfile is seeded to the host but never loaded -- the v1.2.2 `Alt-Svc: clear` edge fix deployed green yet did not take effect until caddy was restarted by hand. Force a recreate for caddy on its roll (every other service already recreates on its new $TAG image), so a bind-mounted Caddyfile change always applies. Costs a ~1-2s caddy blip per deploy, acceptable for the infrequent manual prod rollout. |
||
|
|
ba57687430 |
Merge pull request 'fix(grafana): real byte thresholds for the Database size stat' (#120) from feature/grafana-db-size-thresholds into development
CI / changes (push) Successful in 2s
CI / changes (pull_request) Successful in 2s
CI / unit (push) Successful in 10s
CI / integration (push) Successful in 16s
CI / ui (push) Successful in 54s
CI / unit (pull_request) Successful in 10s
CI / integration (pull_request) Successful in 15s
CI / ui (pull_request) Successful in 54s
CI / gate (push) Successful in 0s
CI / deploy (push) Successful in 1m20s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Has been skipped
|
||
|
|
6cb88b28c4 | Merge pull request 'fix(edge): suppress dead HTTP/3 advert with Alt-Svc: clear' (#119) from feature/edge-suppress-http3-altsvc into development | ||
|
|
46d569720c |
fix(grafana): give "Database size" stat real byte thresholds
CI / changes (pull_request) Successful in 2s
CI / unit (pull_request) Successful in 10s
CI / integration (pull_request) Successful in 17s
CI / ui (pull_request) Successful in 54s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 1m18s
The "Database size" stat had no thresholds, so Grafana applied its stat default (green base, red at >=80). The query is pg_database_size_bytes, so a healthy ~9 MiB database (9.4M >> 80) rendered permanently RED on the Scrabble - Resources dashboard (test + prod), reading as a false alert; the neighbouring percentunit cache-hit stat stayed green only because its 0..1 values fall under 80. Add absolute byte thresholds sized to the 40 GiB prod disk (4.6 GiB used, observability bounded -- Tempo <=1 GiB, Prometheus 7d -- so the DB is the only unbounded grower): green up to 8 GiB, yellow at 8 GiB (~20% of disk), red at 16 GiB (~40%), an early warning with ample runway before the disk tightens, not a panic line. Cosmetic panel coloring only; there are no Grafana alert rules provisioned. |
||
|
|
9253b1bdca |
fix(edge): suppress dead HTTP/3 advert with Alt-Svc: clear
CI / changes (pull_request) Successful in 2s
CI / unit (pull_request) Successful in 10s
CI / integration (pull_request) Successful in 17s
CI / ui (pull_request) Successful in 54s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 1m21s
Caddy enables HTTP/3 by default on any TLS listener and emits Alt-Svc: h3=":443"; ma=2592000, but UDP/443 is never reachable: the prod compose maps only "443:443" (TCP) and ufw opens 443/tcp (test contour: the host caddy publishes only :443/tcp). A client that cached the 30-day advert tries QUIC first on later opens, gets no response, and waits for the QUIC attempt to time out before falling back to h2 -- which surfaced as the Telegram Mini App intermittently hanging on load (a barely-noticeable pause up to a blank window). The h2/TCP serving path itself is healthy (~10ms TTFB). Emit Alt-Svc: clear site-wide at the contour caddy so clients actively drop any cached alternative and stay on h2/h1. This caddy terminates TLS in prod (the fix target); in the test contour it serves plain :80 and the host caddy re-stamps its own Alt-Svc, so the live test fix lives in the host caddy. Add docs/EDGE_HTTP3.md (symptom, diagnosis method, verify, and option B -- serving h3 for real -- if it recurs) and link it from ARCHITECTURE.md. |
||
|
|
9d1ca213d6 |
Merge pull request 'fix(i18n): banner/push follow the interface language even without a Settings change' (#118) from feature/banner-language-followup into development
CI / changes (push) Successful in 2s
CI / changes (pull_request) Successful in 2s
CI / unit (push) Has been skipped
CI / integration (push) Has been skipped
CI / ui (push) Successful in 54s
CI / unit (pull_request) Successful in 9s
CI / integration (pull_request) Successful in 17s
CI / ui (pull_request) Successful in 53s
CI / gate (push) Successful in 0s
CI / deploy (push) Successful in 1m16s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Has been skipped
|
||
|
|
1f78bb274b |
Merge pull request 'feat(telegram): localized /start welcome with channel & chat follow links' (#117) from feature/bot-welcome-localized into development
CI / changes (push) Successful in 2s
CI / changes (pull_request) Successful in 2s
CI / unit (push) Successful in 10s
CI / integration (push) Successful in 16s
CI / ui (push) Has been skipped
CI / unit (pull_request) Successful in 10s
CI / integration (pull_request) Successful in 15s
CI / ui (pull_request) Successful in 54s
CI / gate (push) Successful in 0s
CI / gate (pull_request) Successful in 0s
CI / deploy (push) Successful in 1m21s
CI / deploy (pull_request) Has been skipped
|
||
|
|
81b716569f |
fix(i18n): reconcile preferred_language to the interface locale on every adopt
CI / changes (pull_request) Successful in 2s
CI / unit (pull_request) Successful in 10s
CI / integration (pull_request) Successful in 16s
CI / ui (pull_request) Successful in 54s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 1m22s
A user who never changed the language in Settings kept their account at the creation-time preferred_language seed (e.g. en from the Telegram launch language_code) even after switching the device to another language: the UI followed the device (ru) but the ad banner and out-of-app push — both resolved server-side from preferred_language — stayed en. The on-adopt reconcile was gated on an explicit local choice (localeLocked), so a system-guess locale was never pushed through. Reconcile preferred_language to the active interface locale (app.locale) on every session adopt and link, regardless of how the locale was chosen; persistLanguageToServer already self-gates (a no-op for guests and when already equal), so there is no steady-state write. The banner and push are the only server-rendered language surfaces and both read preferred_language, so this keeps the whole interface consistent — not just the banner. Drop the now-dead localeLocked flag (the reconcile guards were its only readers; the saved prefs.locale still restores the UI choice per device). Trade-off: preferred_language now follows the most-recently-opened device, so an explicit choice on one device can be overwritten by a system guess on another (the "explicit" mark is local, per-device); making it globally sticky would need a DB flag. Docs: ARCHITECTURE §4 + the profile field. |
||
|
|
aa330b726e |
feat(telegram): localized /start welcome with channel & chat follow links
CI / changes (pull_request) Successful in 2s
CI / unit (pull_request) Successful in 10s
CI / integration (pull_request) Successful in 15s
CI / ui (pull_request) Has been skipped
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 1m17s
The main bot answered /start with a single English line ("Tap to open Scrabble.").
Localize it: Russian or English by the sender's reported Telegram language
(Message.from.language_code, which the Bot API carries on the message itself — there is
no separate user-update event — English fallback), with the longer welcome copy and a
localized launch button ("Открыть «Эрудит»" / "Open “Erudite”").
The welcome links the game channel and the discussion chat by their public @username,
resolved once at startup from the configured TELEGRAM_GAME_CHANNEL_ID / TELEGRAM_CHAT_ID
via getChat and cached. A handle that is unset, private, or unreadable degrades to a
generic noun ("the channel" / "our chat") rather than a dangling "@", so the paragraph
always reads cleanly (the bot's info screen still lists the real links). Adds
GameChannelID to bot.Config (wired from the existing config) for the channel handle.
Tests: startText localization + handle embedding + per-slot generic fallback; handleStart
language selection; resolveWelcomeHandles. README updated.
|
||
|
|
d5369a0188 |
Merge pull request 'feat(account): seed the time zone from the client's detected offset at creation' (#116) from feature/account-seed-timezone into development
CI / changes (push) Successful in 2s
CI / changes (pull_request) Successful in 1s
CI / unit (push) Successful in 9s
CI / integration (push) Successful in 15s
CI / ui (push) Successful in 54s
CI / unit (pull_request) Successful in 10s
CI / integration (pull_request) Successful in 14s
CI / ui (pull_request) Successful in 54s
CI / gate (push) Successful in 0s
CI / deploy (push) Successful in 1m18s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Has been skipped
|
||
|
|
170a6ae9ef |
Merge pull request 'feat(feedback): capture app version + browser zone; Filed time in three zones' (#115) from feature/feedback-version into development
CI / changes (push) Successful in 1s
CI / changes (pull_request) Successful in 2s
CI / unit (push) Successful in 10s
CI / integration (push) Successful in 14s
CI / ui (push) Successful in 55s
CI / unit (pull_request) Successful in 10s
CI / integration (pull_request) Successful in 14s
CI / ui (pull_request) Successful in 53s
CI / gate (push) Successful in 0s
CI / gate (pull_request) Successful in 0s
CI / deploy (push) Successful in 1m21s
CI / deploy (pull_request) Has been skipped
|
||
|
|
ef2c2d1eb9 |
feat(account): seed the time zone from the client's detected offset at creation
CI / changes (pull_request) Successful in 3s
CI / unit (pull_request) Successful in 9s
CI / integration (pull_request) Successful in 18s
CI / ui (pull_request) Successful in 54s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 1m18s
A new account's time_zone defaulted to 'UTC' until the player saved a profile, so the robot's sleep window and the turn-timeout away-window sweeper — both anchored to the account zone via account.ResolveZone — ran on UTC for every fresh player, skewing robot-game timing until a manual Settings save. Seed the zone at creation instead, from the client's detected "±HH:MM" offset. - Carry browser_tz on the three account-creating auth requests (TelegramLoginRequest, GuestLoginRequest, EmailRequestRequest — the email account is provisioned at the code-request step, not at login) through the fbs envelope (+ Go/TS codegen), the gateway transcode + backend client, and the backend auth handlers into ProvisionTelegram / ProvisionGuest / ProvisionEmail. - create() now writes time_zone explicitly: the validated detected offset, or 'UTC' (equal to the column default) when absent or malformed — deterministic, never guessed. The column is already NOT NULL DEFAULT 'UTC', so no migration is needed and existing accounts keep 'UTC'. An existing account is never overwritten on re-login. - A detected zero offset is stored as "+00:00" (the zone is known and equals UTC), distinct from the "UTC" default that means "unknown" — which the feedback console's three-zone Filed display already reflects. - Guard the guest handler against an empty payload (the bootstrap historically carried none) so it degrades to no-seed rather than panicking in GetRootAs*. - Tests: zone seeding across Telegram/guest/email plus the "+00:00"/malformed/empty cases and the not-overwrite rule; codec round-trip for the three auth encoders. ARCHITECTURE + FUNCTIONAL(+ru) updated. |
||
|
|
004aca4e97 |
feat(feedback): capture the browser UTC offset; Filed time in three zones
CI / changes (pull_request) Successful in 3s
CI / unit (pull_request) Successful in 10s
CI / integration (pull_request) Successful in 15s
CI / ui (pull_request) Successful in 55s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 1m20s
The account time zone defaults to UTC until a player saves a profile, so a report's Filed time could only render in UTC even for a player clearly in another zone. Capture the client's detected "±HH:MM" offset (browser_tz) with each submission and show the Filed time in three zones in the operator console — UTC, the browser offset detected at submit, and the sender's saved profile zone — each shown "N/A" when not known, so the operator can tell what is certainly known from what is merely defaulted. - Thread browser_tz through the fbs envelope (+ Go/TS codegen), the gateway transcode + backend client, and the backend feedback service/store; add the column via migration 00004 (additive, image-rollback-safe). - Fix fmtTimeIn to resolve "±HH:MM" offsets via account.ResolveZone; time.LoadLocation alone silently fell back to UTC for offset zones, which is the second reason a "+02:00" sender showed only UTC. - Update ARCHITECTURE/FUNCTIONAL(+ru) docs and the feedback integration test. |
||
|
|
b78ce42922 |
feat(feedback): capture the app version; show version + local Filed time
CI / changes (pull_request) Successful in 2s
CI / unit (pull_request) Successful in 10s
CI / integration (pull_request) Successful in 16s
CI / ui (pull_request) Successful in 54s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 1m19s
Each feedback submission now carries the client app version (__APP_VERSION__), snapshotted like the interface language: FlatBuffers FeedbackSubmitRequest gains a version field → gateway transcode → backend, persisted in a new nullable feedback_messages.app_version column (migration 00003, additive so an image rollback stays DB-safe). The operator console detail shows the app version and renders the Filed time in UTC plus the sender's time zone (fmtTimeIn). Touches: fbs schema + regenerated Go/TS codegen, codec + transport (the client attaches its build), gateway transcode + backendclient, feedback store/service, admin view + template, docs (ARCHITECTURE §15, FUNCTIONAL + _ru). Verified: feedback integration tests (migration + version round-trip), codec round-trip, check/unit/build green. |
||
|
|
08c2c5f660 |
Merge pull request 'fix(i18n): banner/push language follows the device's saved choice' (#113) from feature/banner-language-sync into development
CI / changes (push) Successful in 2s
CI / unit (push) Has been skipped
CI / integration (push) Has been skipped
CI / ui (push) Successful in 54s
CI / gate (push) Successful in 0s
CI / changes (pull_request) Successful in 2s
CI / deploy (push) Successful in 1m19s
CI / unit (pull_request) Successful in 10s
CI / integration (pull_request) Successful in 17s
CI / ui (pull_request) Successful in 54s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Has been skipped
|
||
|
|
06cc4c1edc | Merge pull request 'feat(ads): seed the house banner with the curated tip set' (#112) from feature/ad-tips-default-banner into development | ||
|
|
90f0424de2 |
fix(i18n): reconcile preferred_language with the device's saved language
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 53s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 1m18s
The UI language follows the device (the local choice / system guess) and is deliberately not overridden from the account, but the advertising banner and out-of-app push routing are resolved server-side from preferred_language. A saved device choice the account had not recorded — picked while a guest, or differing from the Telegram system-language seed — left the banner (and pushes) in the wrong language until a Settings change rewrote preferred_language. On profile load (adoptSession and the in-place link path) push the saved local choice to the account when it differs (new pure helper languageNeedsServerSync; no-op for guests and when already equal), so the banner and pushes match the visible UI from the first open. Unit-tested. |
||
|
|
c36543e54e |
feat(ads): seed the house banner with the curated tip set
CI / changes (pull_request) Successful in 2s
CI / unit (pull_request) Successful in 10s
CI / integration (pull_request) Successful in 15s
CI / ui (pull_request) Has been skipped
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 1m17s
Migration 00002 replaces the default (house) campaign's single seed tip with the 47 curated, language-agnostic Scrabble tips — one bilingual ad_messages row each (body_en + body_ru), which the client round-robins per ARCHITECTURE §ads. Data-only: the ad_messages schema is unchanged, so a backend image rollback stays DB-safe. Down restores the original single seed tip. Verified up/down against a throwaway Postgres (47 rows; apostrophes escaped). |
||
|
|
be1627936f |
Merge pull request 'chore(deploy): pin dictionary seed to v1.3.0' (#110) from feature/pin-dict-v130 into development
CI / changes (push) Successful in 2s
CI / integration (pull_request) Successful in 16s
CI / ui (pull_request) Successful in 54s
CI / unit (push) Successful in 10s
CI / integration (push) Successful in 14s
CI / ui (push) Successful in 53s
CI / gate (push) Successful in 0s
CI / deploy (push) Successful in 1m19s
CI / changes (pull_request) Successful in 2s
CI / unit (pull_request) Successful in 10s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Has been skipped
|
||
|
|
1ba52dd0b4 |
refactor(deploy): make DICT_VERSION a required build arg (single-sourced)
CI / changes (pull_request) Successful in 3s
CI / unit (pull_request) Successful in 9s
CI / integration (pull_request) Successful in 17s
CI / ui (pull_request) Successful in 54s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 1m28s
Drop the literal version default from the build files (backend Dockerfile both
stages, loadtest Dockerfile, the compose build-arg) so the release tag is not
duplicated as a stale-prone default a newcomer can't tell from the real source.
DICT_VERSION is now required: compose uses ${DICT_VERSION:?…} and the Dockerfiles
have no ARG default, so a missing value fails loudly instead of baking a stale tag.
The tag lives only in its genuine sources — ci.yaml env (CI tests), the Gitea
TEST_/PROD_DICT_VERSION variables (deploy seed) and deploy/.env.example (local).
Adds a "Bumping the dictionary version" section to deploy/README and fixes the bare
docker-build examples (CLAUDE.md, README.md, loadtest/README) to pass --build-arg.
|
||
|
|
bb0e3e17e5 |
chore(deploy): pin dictionary seed to v1.3.0
CI / changes (pull_request) Successful in 2s
CI / unit (pull_request) Successful in 10s
CI / integration (pull_request) Successful in 14s
CI / ui (pull_request) Successful in 54s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 1m22s
Bump DICT_VERSION v1.2.1 -> v1.3.0 across the seed surface: .env.example, the compose build-arg default, both backend Dockerfile stages, the loadtest Dockerfile, the CI dawg-download version, and the deploy/backend docs. v1.3.0 drops the abbreviation class from the Russian word list (scrabble-dictionary #6). This only seeds a FRESH volume; a live contour/prod volume is unaffected (the .seed_version marker wins — seed-drift guard) and moves to v1.3.0 through the admin console (ARCHITECTURE §5). Per-contour deploy still overrides via TEST_/PROD_DICT_VERSION. |
||
|
|
1ef2bde395 | Merge pull request 'feat(ui): Erudit blank tiles carry the star (✻) mark' (#109) from feature/erudit-blank-star into development | ||
|
|
62f66735a3 |
fix(ui): nudge the rack blank star up a pixel
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 53s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 1m19s
By eye the centred star still read a hair low; subtract 1px from its top offset (top: calc(0.5% - 1px)). |
||
|
|
81680a1d5e |
fix(ui): raise the rack blank star to centre on the letters
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 55s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 1m18s
Top-aligning it still read a touch low; move the empty-blank star up (top 8% -> 0.5%) so its ink centres against the rack letters' block, matching the board tile's centred mark. Size unchanged. |
||
|
|
a393561d79 |
fix(ui): align the Erudit blank star with neighbouring tiles
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 1m21s
Rack: the empty-blank star is now top-anchored level with the letters and a touch larger (was centred, sitting low). Board and Stats best-move tiles: the placed-blank star's ink is centred on the value digits' line (was slightly high). CSS-only nudges; pixel offsets measured against the rendered glyphs. |
||
|
|
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. |
||
|
|
91de26d80b |
Merge pull request 'Finalize docs to production + lobby/new-game UI tweaks + Telegram name fallback' (#107) from feature/finalize-docs into development
CI / unit (push) Successful in 10s
CI / changes (push) Successful in 2s
CI / integration (push) Successful in 16s
CI / ui (push) Successful in 56s
CI / gate (push) Successful in 0s
CI / unit (pull_request) Successful in 11s
CI / deploy (push) Successful in 1m22s
CI / changes (pull_request) Successful in 2s
CI / integration (pull_request) Successful in 16s
CI / ui (pull_request) Successful in 57s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Has been skipped
|
||
|
|
6b6362a629 |
fix(account): Telegram display-name falls back to the @username verbatim
CI / changes (pull_request) Successful in 3s
CI / unit (pull_request) Successful in 12s
CI / integration (pull_request) Successful in 16s
CI / ui (pull_request) Successful in 57s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 1m22s
When the Telegram first name yields no usable letters, fall back to the @username taken whole (trimmed + length-capped, never character-stripped like the real name) rather than a sanitized form; the generated placeholder is reached only when no username is set. Precedence: real name -> @username (verbatim) -> placeholder. |
||
|
|
8a06fbc3c7 |
feat(ui): lobby invitation card redesign + new-game tweaks
- Lobby friend-invitation card: icon-only checkmark/cross actions stacked in a min-width right column; the middle column (From <name> + flag + variant rules, like New Game) grows and wraps. The cross now opens a decline-confirmation modal (mirroring the in-game resign confirm) instead of declining on first tap. - New Game with a friend: a lone offered variant is pre-selected and its picker disabled (nothing else to choose); relabel 'Тип игры' -> 'Вариант' and 'Подсказок на игрока' -> 'Подсказки'. - Quick game: pin the Start button to the bottom of the screen, mirroring the friend-game Send-invitation button. |
||
|
|
48b06f4594 |
docs: finalize documentation to the production state
CI / changes (pull_request) Successful in 2s
CI / unit (pull_request) Successful in 10s
CI / integration (pull_request) Successful in 16s
CI / ui (pull_request) Successful in 57s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 1m21s
The project is live in production, so the staged-development scaffolding is removed. - Delete the staged trackers PLAN.md and PRERELEASE.md. - Rewrite CLAUDE.md: drop the per-stage workflow; codify the ongoing development principles (How we work) and the production model (Branching, CI & production): manual prod-deploy / prod-rollback, semver release tags, Ansible provisioning, expand-contract migrations. - De-stage the living docs (README, ARCHITECTURE, TESTING, deploy/ansible, loadtest, platform/telegram READMEs) and the docker-compose tuning comments: drop the Stage N / R1-R7 / pre-release labels, keep every number and rationale, and fix the now-dangling PLAN.md / PRERELEASE.md references to describe the current state. - Reword stale 'later stage' Go doc comments for subsystems that have shipped. |
||
|
|
40d8f06588 |
Merge pull request 'Deploy v2 — release versioning + visible deploy jobs + manual rollback' (#105) from feature/release-versioning into development
CI / changes (push) Successful in 2s
CI / unit (push) Successful in 11s
CI / integration (push) Successful in 15s
CI / ui (push) Successful in 57s
CI / changes (pull_request) Successful in 2s
CI / gate (push) Successful in 0s
CI / unit (pull_request) Successful in 10s
CI / integration (pull_request) Successful in 16s
CI / ui (pull_request) Successful in 56s
CI / deploy (push) Successful in 1m19s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Has been skipped
|
||
|
|
c59e522732 |
feat(deploy): visible prod-deploy jobs + manual prod-rollback
CI / changes (pull_request) Successful in 2s
CI / unit (pull_request) Successful in 11s
CI / integration (pull_request) Successful in 16s
CI / ui (pull_request) Successful in 57s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 1m23s
- prod-deploy.yaml is now four visible sequential jobs (build -> deploy-main -> deploy-bot -> verify) so the rollout stages show in the Actions UI; the per-service rolling stays in the deploy-main log. - prod-rollback.yaml: a separate manual workflow_dispatch. Leave target_version blank to roll back to the previous deployed version (the host now tracks DEPLOYED_TAG + PREVIOUS_TAG), or pick a release tag. Re-deploys an already published image rolling + health-gated, image-only (no rebuild, no DB migration). - prod-deploy.sh tracks the previous tag (commit_tag) for the blank-input rollback. - Docs: ARCHITECTURE §13 + deploy/README runbook cover versioning + rollback. |