fix(export): VK Android viewer+clipboard; no share title on iOS
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 1m3s
CI / conformance (pull_request) Successful in 9s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 1m56s
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 1m3s
CI / conformance (pull_request) Successful in 9s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 1m56s
Round-5 on-device findings: - VK Android's DownloadFile hangs regardless of Content-Length and Range/206 (PNG stalls ~80%, GCG at 0%) — the download route is abandoned there: the PNG opens in VK's native image viewer (VKWebAppShowImages — its save works on-device) and the GCG returns to the clipboard copy (the pre-URL route, always solid). VK iOS keeps VKWebAppDownloadFile (verified perfect); the desktop iframe keeps plain anchor downloads. - The iOS share sheet no longer carries a title: iOS pasted the 'game-<hash>.png' filename as accompanying text next to the image.
This commit is contained in:
+19
-6
@@ -23,7 +23,7 @@
|
||||
import { alphabetLetters, hasAlphabet } from '../lib/alphabet';
|
||||
import { hintsLeft } from '../lib/hints';
|
||||
import { downloadUrl, shareOrDownloadGcg, shareUrlAsFile } from '../lib/share';
|
||||
import { insideVK, vkCopyText, vkDownloadFile, vkPlatform } from '../lib/vk';
|
||||
import { insideVK, vkAndroidWebView, vkCopyText, vkDownloadFile, vkPlatform, vkShowImages } from '../lib/vk';
|
||||
import { getCachedGame, setCachedGame, setCachedDraft, type CachedGame } from '../lib/gamecache';
|
||||
import { patchLobbyGame } from '../lib/lobbycache';
|
||||
import { applyGameOver, applyMoveDelta, applyOpponentJoined, type DeltaResult } from '../lib/gamedelta';
|
||||
@@ -939,8 +939,11 @@
|
||||
// TG iOS our modal chooser → the OS share sheet (WKWebView has
|
||||
// navigator.share; a native-popup callback could not open it — no
|
||||
// user activation — so the chooser stays the app modal here).
|
||||
// VK mobile our modal (VK has no native chooser) → VKWebAppDownloadFile for
|
||||
// VK iOS our modal (VK has no native chooser) → VKWebAppDownloadFile for
|
||||
// both formats — the client lands in its native share/preview flow.
|
||||
// VK Android our modal → the PNG opens in VK's native image viewer (its save
|
||||
// works; the Android DownloadFile hangs), the GCG copies to the
|
||||
// clipboard (the pre-URL route, always solid there).
|
||||
// VK desktop iframe our modal → plain anchor downloads (an ordinary browser).
|
||||
// mobile browsers our modal → the OS share sheet with the fetched file.
|
||||
// desktop browsers our modal → plain anchor download.
|
||||
@@ -998,10 +1001,20 @@
|
||||
const mime = kind === 'png' ? 'image/png' : 'text/plain';
|
||||
if (insideTelegram() && !tgShareSheet && telegramDownloadFile(url, filename)) return;
|
||||
if (insideVK()) {
|
||||
// Desktop iframe: an ordinary browser, both formats download by anchor. Mobile
|
||||
// clients take VKWebAppDownloadFile into VK's native share/preview flow; a failed
|
||||
// bridge call falls back to the anchor.
|
||||
if (!vkPlatform().startsWith('desktop') && (await vkDownloadFile(url, filename))) return;
|
||||
// Per-VK-client delivery (each branch owner-verified): the iOS client's
|
||||
// VKWebAppDownloadFile lands in a native share flow and is perfect; the ANDROID
|
||||
// client's download hangs indefinitely, so the PNG opens in VK's native image
|
||||
// viewer (its "save" works) and the GCG goes to the clipboard; the desktop
|
||||
// iframe is an ordinary browser — plain anchor downloads.
|
||||
if (vkAndroidWebView()) {
|
||||
if (kind === 'png' && (await vkShowImages(url))) return;
|
||||
if (kind === 'gcg') {
|
||||
void exportGcgLegacy();
|
||||
return;
|
||||
}
|
||||
} else if (!vkPlatform().startsWith('desktop') && (await vkDownloadFile(url, filename))) {
|
||||
return;
|
||||
}
|
||||
downloadUrl(url, filename);
|
||||
return;
|
||||
}
|
||||
|
||||
+3
-1
@@ -114,7 +114,9 @@ export async function shareUrlAsFile(url: string, filename: string, mime: string
|
||||
const file = new File([await resp.blob()], filename, { type: mime });
|
||||
if (nav.canShare({ files: [file] })) {
|
||||
try {
|
||||
await nav.share({ files: [file], title: filename });
|
||||
// No title: iOS pastes it as accompanying text next to the image; the named
|
||||
// file speaks for itself.
|
||||
await nav.share({ files: [file] });
|
||||
} catch {
|
||||
/* cancelled — intentionally a no-op */
|
||||
}
|
||||
|
||||
@@ -155,6 +155,21 @@ export async function vkShare(link: string): Promise<boolean> {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* vkShowImages opens VK's native image viewer on url — on the Android client this is the
|
||||
* PNG delivery: the viewer's own "save" works there, while VKWebAppDownloadFile hangs
|
||||
* indefinitely (on-device finding). Resolves false on any failure, so the caller can
|
||||
* fall back.
|
||||
*/
|
||||
export async function vkShowImages(url: string): Promise<boolean> {
|
||||
try {
|
||||
await (await bridge()).send('VKWebAppShowImages', { images: [url] });
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* vkDownloadFile downloads url as filename through the VK client (VKWebAppDownloadFile) —
|
||||
* the mobile in-app file delivery, where the webview ignores <a download>. Resolves false
|
||||
|
||||
Reference in New Issue
Block a user