fix(game): own export chooser modal; PNG option outside in-app webviews only
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 59s
CI / conformance (pull_request) Successful in 9s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 1m26s
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 59s
CI / conformance (pull_request) Successful in 9s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 1m26s
Telegram's native showPopup delivers its callback with no user activation, so navigator.share (TG iOS) and clipboard writes (TG Android GCG copy) silently fail from it — the chooser is now always the app's own modal, keeping the button click's gesture alive for the delivery APIs. The data:URL preview modal is dropped: the Android TG/VK long-press menu mangles data: URLs (dead download, black-screen open, base64 clipboard garbage), so a binary PNG has no working client-side route in those webviews at all. The image option is withheld there until the server-rendered signed-URL delivery (Telegram downloadFile / VKWebAppDownloadFile) lands; the plain web and mobile browsers keep it. On-device findings by the owner on the test contour (TG iOS, TG Android, VK Android).
This commit is contained in:
+9
-7
@@ -322,8 +322,9 @@ Finished games are archived in a dictionary-independent form and exportable in
|
||||
**two formats behind one 📤 button** — the GCG file and a rendered **PNG image** of
|
||||
the final position; the export is offered **only once a game is finished**, and
|
||||
never for an honest-AI practice game (a live game's export would leak the move
|
||||
journal; an AI game is throwaway). The format chooser is native where the platform
|
||||
has one (Telegram's popup), the app's own modal elsewhere.
|
||||
journal; an AI game is throwaway). The format chooser is always the app's **own
|
||||
modal** — deliberately not Telegram's native popup, whose callback runs without
|
||||
user activation and silently breaks the share/clipboard delivery it leads to.
|
||||
|
||||
The **image** is rendered on the client (Canvas 2D, always the light theme): the
|
||||
final board with classic coordinate axes A..O / 1..15 on the left — premium squares
|
||||
@@ -342,11 +343,12 @@ dead space.
|
||||
Delivery per format: the **GCG** file is Web-Shared where the platform supports it;
|
||||
on an Android in-app client (Telegram / VK), which has neither Web Share nor a
|
||||
working file download, it copies the GCG text to the clipboard (with a confirming
|
||||
toast); otherwise it downloads the file. The **PNG** shares or downloads the same
|
||||
way where possible, but a binary image has no clipboard-text fallback — those
|
||||
in-app webviews (and the desktop VK iframe) get a **preview modal** instead: the
|
||||
image with a long-press / right-click save hint and a copy-image button where the
|
||||
clipboard-image API exists. Statistics (durable accounts only):
|
||||
toast); otherwise it downloads the file. The **PNG** is Web-Shared or downloaded the
|
||||
same way, but a binary image has no clipboard-text fallback at all — so the image
|
||||
option is currently **withheld inside the in-app webviews** (Telegram / VK) and
|
||||
offered on the plain web and mobile browsers only; it returns there with the
|
||||
server-rendered signed-URL delivery (Telegram `downloadFile` /
|
||||
`VKWebAppDownloadFile`). Statistics (durable accounts only):
|
||||
wins, losses, draws, max points in a game, and max points for a single move (the
|
||||
best play, which already includes every word it formed plus the all-tiles bonus). It
|
||||
also shows the player's **move count** (their plays — passes and exchanges do not
|
||||
|
||||
@@ -331,8 +331,9 @@ Telegram.
|
||||
**двух форматах за одной кнопкой 📤** — файл GCG и отрисованная **PNG-картинка**
|
||||
финальной позиции; экспорт доступен **только после завершения партии** и никогда —
|
||||
для тренировочной партии с ИИ (экспорт идущей партии раскрыл бы журнал ходов, а
|
||||
партия с ИИ одноразовая). Выбор формата — нативный диалог там, где платформа его
|
||||
даёт (попап Telegram), иначе собственный модал приложения.
|
||||
партия с ИИ одноразовая). Выбор формата — всегда **собственный модал** приложения,
|
||||
сознательно не нативный попап Telegram: его коллбек приходит без user activation и
|
||||
молча ломает доставку через share/буфер, к которой ведёт выбор.
|
||||
|
||||
**Картинка** рендерится на клиенте (Canvas 2D, всегда светлая тема): финальная
|
||||
доска с классическими осями координат A..O / 1..15 слева — бонус-клетки чистой
|
||||
@@ -350,11 +351,12 @@ Telegram.
|
||||
Доставка по форматам: файлом **GCG** клиент делится через Web Share там, где
|
||||
платформа это поддерживает; в Android-приложении (Telegram / VK), где нет ни Web
|
||||
Share, ни рабочей загрузки файла, копирует текст GCG в буфер обмена (с
|
||||
подтверждающим тостом); иначе скачивает файл. **PNG** делится и скачивается так
|
||||
же, где возможно, но у бинарной картинки нет текстового фолбэка через буфер — в
|
||||
этих встроенных webview (и в десктопном iframe VK) вместо него **модал-превью**:
|
||||
картинка с подсказкой «долгое нажатие / правый клик — сохранить» и кнопкой
|
||||
копирования картинки там, где есть clipboard-image API. Статистика (только у постоянных аккаунтов):
|
||||
подтверждающим тостом); иначе скачивает файл. **PNG** делится и скачивается так же,
|
||||
но у бинарной картинки нет текстового фолбэка через буфер вовсе — поэтому пункт
|
||||
«картинка» пока **скрыт во встроенных webview** (Telegram / VK) и предлагается
|
||||
только в обычном вебе и мобильных браузерах; туда он вернётся с серверным рендером
|
||||
и доставкой по подписанному URL (Telegram `downloadFile` /
|
||||
`VKWebAppDownloadFile`). Статистика (только у постоянных аккаунтов):
|
||||
победы, поражения, ничьи, макс. очков за партию и макс. очков за один ход (лучший
|
||||
ход, уже включающий все образованные им слова и бонус за все фишки). Также
|
||||
показываются **число ходов** игрока (его выкладки — пасы и обмены не считаются) и
|
||||
|
||||
+10
-8
@@ -406,14 +406,16 @@ enabled on the first, uncached load) and flip in place when an event refreshes t
|
||||
- **History / export**: the in-game slide-down history lays each move out in a per-seat grid
|
||||
(the word(s) and the move score, no running total); the 📤 in the history header appears
|
||||
only once the game is finished — never in an honest-AI game (throwaway practice) — and
|
||||
opens a **format chooser**: Telegram's native popup inside Telegram (image / GCG / cancel),
|
||||
the app's own modal elsewhere (the accent button leads with the image, the GCG file is the
|
||||
quiet alternative; GCG needs the connection, the image renders locally). The GCG file goes
|
||||
by Web Share where available, a clipboard copy in an Android in-app WebView (Telegram / VK,
|
||||
which has neither Web Share nor a working download), else a Blob download. The image goes by
|
||||
Web Share / Blob download too, but on those in-app WebViews (and the desktop VK iframe) it
|
||||
opens a **preview modal** instead — the PNG as a data: URL with a long-press / right-click
|
||||
save hint and a copy-image button where `ClipboardItem` exists. Confirming a resign reveals
|
||||
opens the **format chooser**: always the app's own modal, never Telegram's native popup
|
||||
(a `showPopup` callback carries no user activation, which silently breaks the
|
||||
`navigator.share` / clipboard delivery it leads to — on-device finding). The accent button
|
||||
leads with the image where offered, the GCG file is the quiet alternative (GCG needs the
|
||||
connection, the image renders locally). The GCG file goes by Web Share where available, a
|
||||
clipboard copy in an Android in-app WebView (Telegram / VK, which has neither Web Share nor
|
||||
a working download), else a Blob download. The image option shows **only outside the in-app
|
||||
WebViews** for now (a binary PNG has no working route there — no Web Share, a dead
|
||||
`<a download>`, a text-only clipboard, and the long-press menu mangles data: URLs); it
|
||||
returns there with the server-rendered signed-URL delivery. Confirming a resign reveals
|
||||
the full board: it closes the history drawer (portrait) and zooms the board out.
|
||||
- **Export image** (`lib/gameimage.ts`, Canvas 2D, dynamically imported): always the light
|
||||
palette (pinned constants mirroring `app.css`), the final board with classic axes A..O /
|
||||
|
||||
Reference in New Issue
Block a user