db17287113
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.
82 lines
3.5 KiB
Svelte
82 lines
3.5 KiB
Svelte
<script lang="ts">
|
|
import { app, dismissWelcomeRedeem } from '../lib/app.svelte';
|
|
import { router } from '../lib/router.svelte';
|
|
import { t } from '../lib/i18n/index.svelte';
|
|
import { botUsername } from '../lib/deeplink';
|
|
import { insideTelegram, telegramDialogsAvailable, telegramOpenLink, telegramShowPopup } from '../lib/telegram';
|
|
import { openExternalUrl } from '../lib/links';
|
|
import { BOT_BUTTON_ID, botInfoPopup } from '../lib/nativedialogs';
|
|
import Modal from './Modal.svelte';
|
|
|
|
// The single bot's @username, for the deep link.
|
|
const username = $derived(botUsername());
|
|
// Greet the arriving player by their own display name.
|
|
const name = $derived(app.profile?.displayName || app.session?.displayName || '');
|
|
// Interpolate the name, then split the rest around the {bot} token so the bot handle renders
|
|
// as an inline link (the {name} value is substituted first; {bot} is left for the split).
|
|
const parts = $derived(t('friends.welcomeRedeem', { name }).split('{bot}'));
|
|
|
|
function openBot() {
|
|
if (username) {
|
|
const url = `https://t.me/${username}`;
|
|
// Inside Telegram, openTelegramLink navigates to the bot chat natively; elsewhere fall
|
|
// back to a new tab (routed out of the Android VK WebView by openExternalUrl).
|
|
if (!telegramOpenLink(url)) openExternalUrl(url);
|
|
}
|
|
dismissWelcomeRedeem();
|
|
}
|
|
|
|
// Native path: when the greeting fires inside the Mini App with native popups, present Telegram's
|
|
// own popup instead of the in-app modal. insideTelegram()/dialogs are evaluated at fire time, not
|
|
// captured at init: this component mounts before bootstrap loads the SDK, so a captured value
|
|
// would be stale. The popup's "open bot" button opens the bot chat; any other dismissal clears it;
|
|
// the `shown` guard fires it once per greeting.
|
|
// The greeting is raised during boot; wait until the loading cover for the current route is gone —
|
|
// the tile splash on the lobby (splashDone), the plain loading screen elsewhere (app.ready) — so
|
|
// the native popup never appears over the splash.
|
|
const ready = $derived(router.route.name === 'lobby' ? app.splashDone : app.ready);
|
|
let shown = false;
|
|
$effect(() => {
|
|
if (app.welcomeRedeem && !shown && ready && insideTelegram() && telegramDialogsAvailable()) {
|
|
shown = true;
|
|
void telegramShowPopup(
|
|
botInfoPopup(t('friends.welcomeRedeemTitle'), t('friends.welcomeRedeem', { name }), username ?? '', t('common.ok')),
|
|
).then((id) => (id === BOT_BUTTON_ID ? openBot() : dismissWelcomeRedeem()));
|
|
} else if (!app.welcomeRedeem) {
|
|
shown = false;
|
|
}
|
|
});
|
|
</script>
|
|
|
|
{#if app.welcomeRedeem && ready && !(insideTelegram() && telegramDialogsAvailable())}
|
|
<Modal title={t('friends.welcomeRedeemTitle')} onclose={dismissWelcomeRedeem}>
|
|
<p class="msg">{parts[0]}{#if username}<button type="button" class="bot" onclick={openBot}>@{username}</button>{/if}{parts[1] ?? ''}</p>
|
|
<button class="ok" onclick={dismissWelcomeRedeem}>{t('common.ok')}</button>
|
|
</Modal>
|
|
{/if}
|
|
|
|
<style>
|
|
.msg {
|
|
margin: 0 0 16px;
|
|
line-height: 1.5;
|
|
/* The greeting and the bot sentence are separated by a blank line in the message. */
|
|
white-space: pre-line;
|
|
}
|
|
.bot {
|
|
background: none;
|
|
border: none;
|
|
padding: 0;
|
|
font: inherit;
|
|
color: var(--accent);
|
|
cursor: pointer;
|
|
}
|
|
.ok {
|
|
width: 100%;
|
|
padding: 10px 12px;
|
|
border: 1px solid var(--accent);
|
|
background: var(--accent);
|
|
color: var(--accent-text);
|
|
border-radius: var(--radius-sm);
|
|
}
|
|
</style>
|