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:
+70
-1
@@ -1,5 +1,15 @@
|
||||
import { afterEach, describe, expect, it, vi } from 'vitest';
|
||||
import { insideVK, onVKPath, vkAppId, vkLaunchParams, vkStartParam, appearanceForBg } from './vk';
|
||||
import {
|
||||
insideVK,
|
||||
onVKPath,
|
||||
routeExternalLinkInVK,
|
||||
vkAndroidWebView,
|
||||
vkAppId,
|
||||
vkExternalBrowserUrl,
|
||||
vkLaunchParams,
|
||||
vkStartParam,
|
||||
appearanceForBg,
|
||||
} from './vk';
|
||||
|
||||
describe('vk launch detection', () => {
|
||||
afterEach(() => vi.unstubAllGlobals());
|
||||
@@ -50,6 +60,65 @@ describe('vk launch params', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('vk external links', () => {
|
||||
afterEach(() => vi.unstubAllGlobals());
|
||||
|
||||
function stubVK(platform: string) {
|
||||
const open = vi.fn();
|
||||
vi.stubGlobal('location', {
|
||||
pathname: '/vk/',
|
||||
search: `?vk_user_id=1&vk_platform=${platform}&sign=x`,
|
||||
href: 'https://app.example/vk/',
|
||||
origin: 'https://app.example',
|
||||
});
|
||||
vi.stubGlobal('window', { open });
|
||||
return open;
|
||||
}
|
||||
|
||||
it('wraps a URL in the away.php redirect with the target encoded', () => {
|
||||
const url = 'https://gramota.ru/poisk?query=кот&mode=slovari';
|
||||
expect(vkExternalBrowserUrl(url)).toBe(`https://vk.com/away.php?to=${encodeURIComponent(url)}`);
|
||||
});
|
||||
|
||||
it('detects the Android VK WebView from vk_platform', () => {
|
||||
stubVK('mobile_android');
|
||||
expect(vkAndroidWebView()).toBe(true);
|
||||
stubVK('mobile_android_messenger');
|
||||
expect(vkAndroidWebView()).toBe(true);
|
||||
stubVK('mobile_iphone');
|
||||
expect(vkAndroidWebView()).toBe(false);
|
||||
stubVK('desktop_web');
|
||||
expect(vkAndroidWebView()).toBe(false);
|
||||
});
|
||||
|
||||
it('is not the Android WebView without a signed VK launch', () => {
|
||||
vi.stubGlobal('location', { pathname: '/vk/', search: '?vk_platform=mobile_android' });
|
||||
expect(vkAndroidWebView()).toBe(false);
|
||||
});
|
||||
|
||||
it('routes an external _blank link through the away redirect on Android', () => {
|
||||
const open = stubVK('mobile_android');
|
||||
expect(routeExternalLinkInVK({ href: 'https://gramota.ru/poisk?query=кот', target: '_blank' })).toBe(true);
|
||||
expect(open).toHaveBeenCalledWith(
|
||||
`https://vk.com/away.php?to=${encodeURIComponent('https://gramota.ru/poisk?query=кот')}`,
|
||||
'_blank',
|
||||
);
|
||||
});
|
||||
|
||||
it('leaves same-origin and non-_blank links to the browser', () => {
|
||||
const open = stubVK('mobile_android');
|
||||
expect(routeExternalLinkInVK({ href: 'https://app.example/lobby', target: '_blank' })).toBe(false);
|
||||
expect(routeExternalLinkInVK({ href: 'https://gramota.ru', target: '' })).toBe(false);
|
||||
expect(open).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('does nothing outside the Android VK client (iOS / desktop open _blank fine)', () => {
|
||||
const open = stubVK('mobile_iphone');
|
||||
expect(routeExternalLinkInVK({ href: 'https://gramota.ru', target: '_blank' })).toBe(false);
|
||||
expect(open).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('appearanceForBg', () => {
|
||||
it('maps a dark background to the dark appearance (light status-bar icons)', () => {
|
||||
expect(appearanceForBg('#0f1115')).toBe('dark');
|
||||
|
||||
Reference in New Issue
Block a user