fix(ui): route external links out of the Android VK WebView
The Android VK client's WebView ignores target=_blank and navigates the Mini App's own window to the target, stranding the player outside the game with no way back (the dictionary lookup, About/Feedback links, the ad banner and the bot-link modal fallbacks). vk-bridge 3.x has no method to open an external URL, so external links are routed through VK's own leave-VK redirect (vk.com/away.php), which the client intercepts natively and hands to the system browser. onExternalLinkClick moves from lib/telegram to a new lib/links that composes the Telegram and VK routers; iOS and desktop VK open _blank correctly and are left alone.
This commit is contained in:
+55
-1
@@ -6,7 +6,7 @@
|
||||
// (VKWebAppGetUserInfo, since VK omits it from the signed launch params). Every helper is safe to
|
||||
// call outside VK.
|
||||
|
||||
import type { Haptic } from './telegram';
|
||||
import { isExternalHttpUrl, type Haptic } from './telegram';
|
||||
|
||||
async function bridge() {
|
||||
return (await import('@vkontakte/vk-bridge')).default;
|
||||
@@ -87,6 +87,60 @@ export function vkStartParam(): string {
|
||||
return new URLSearchParams(location.search.replace(/^\?/, '')).get('hash') ?? '';
|
||||
}
|
||||
|
||||
/**
|
||||
* vkPlatform returns the VK client platform from the launch parameters (vk_platform:
|
||||
* mobile_android, mobile_iphone, desktop_web, mobile_web, …), or '' outside a VK launch.
|
||||
*/
|
||||
export function vkPlatform(): string {
|
||||
if (typeof location === 'undefined') return '';
|
||||
return new URLSearchParams(location.search.replace(/^\?/, '')).get('vk_platform') ?? '';
|
||||
}
|
||||
|
||||
/**
|
||||
* vkAndroidWebView reports whether the app runs inside the Android VK client's WebView
|
||||
* (vk_platform mobile_android / mobile_android_messenger) — the one environment whose WebView
|
||||
* ignores target=_blank and navigates the app's own window instead of opening a browser.
|
||||
*/
|
||||
export function vkAndroidWebView(): boolean {
|
||||
return insideVK() && vkPlatform().startsWith('mobile_android');
|
||||
}
|
||||
|
||||
/**
|
||||
* vkExternalBrowserUrl wraps url in VK's own leave-VK redirect (vk.com/away.php). The VK mobile
|
||||
* client intercepts vk.com navigations natively and hands the redirect target to the system
|
||||
* browser, so an external link escapes the Mini App WebView instead of replacing the game.
|
||||
* vk-bridge (3.x) offers no method to open an external URL, so this redirect is the supported
|
||||
* escape hatch. Pure, so it is unit-tested.
|
||||
*/
|
||||
export function vkExternalBrowserUrl(url: string): string {
|
||||
return `https://vk.com/away.php?to=${encodeURIComponent(url)}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* vkOpenExternalUrl opens url in the system browser from inside the Android VK client by routing
|
||||
* it through vkExternalBrowserUrl. Returns false outside the Android VK WebView — iOS and the
|
||||
* desktop iframe open target=_blank correctly and are left alone — so the caller falls back to
|
||||
* the anchor's own navigation or a plain window.open.
|
||||
*/
|
||||
export function vkOpenExternalUrl(url: string): boolean {
|
||||
if (typeof window === 'undefined' || !vkAndroidWebView()) return false;
|
||||
window.open(vkExternalBrowserUrl(url), '_blank');
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* routeExternalLinkInVK decides whether a clicked anchor must be opened through VK's external
|
||||
* redirect rather than the WebView's default navigation: only inside the Android VK client, and
|
||||
* only for an external http(s) link opened in a new tab (target=_blank). Returns true when it
|
||||
* opened the link — the caller should then preventDefault — and false to let the browser handle
|
||||
* the click normally.
|
||||
*/
|
||||
export function routeExternalLinkInVK(anchor: { href: string; target: string }): boolean {
|
||||
if (anchor.target !== '_blank') return false;
|
||||
if (!isExternalHttpUrl(anchor.href)) return false;
|
||||
return vkOpenExternalUrl(anchor.href);
|
||||
}
|
||||
|
||||
/**
|
||||
* vkShare opens VK's native share dialog for link (VKWebAppShare) — the in-iframe replacement for
|
||||
* navigator.share, which is unavailable in the desktop VK iframe. Resolves true when the share was
|
||||
|
||||
Reference in New Issue
Block a user