Кнопка «Активировать триал» в rich-меню бота при платном триале ведёт в
миниапп: startapp=trial открывает дашборд, где TrialOfferCard показывает
цену и flow оплаты с баланса.
biome check, type-check и build проходят (pre-commit пропущен: biome не
установлен локально — бинарь есть только в CI).
StartParamNavigator обобщён до таблицы маршрутов: помимо admin_ticket_<id>
теперь разбираются renew_<id> → /subscriptions/<id>/renew и subscriptions →
/subscriptions — их шлют ссылки «Продлить» у истёкших подписок в rich-меню
бота (t.me/<bot>/<app>?startapp=…). Доступ по-прежнему ограничен гвардами
самих маршрутов.
biome check, type-check и build проходят (pre-commit пропущен: biome не
установлен локально как зависимость — бинарь есть только в CI).
Add an /admin/tickets/:ticketId route that pre-selects the ticket, and a
StartParamNavigator that maps a Telegram Mini App start param (admin_ticket_<id>,
delivered by a group-chat t.me/<bot>/<app>?startapp deep link) to that route.
This lets the bot's admin ticket notification buttons open the cabinet straight on
the ticket — web_app buttons for private chats, the startapp deep link for groups
where web_app is unavailable. Selection mirrors the URL param (cleared on the bare
/admin/tickets list). The static /admin/tickets/settings route still out-ranks the
new dynamic segment.
Pairs with bot feature BEDOLAGA-DEV/remnawave-bedolaga-telegram-bot#2988.
On a full-screen blocking overlay in the Telegram Mini App there were two exit
affordances: the native back button — which ran SPA navigation on the hidden
route underneath, so it didn't dismiss the block and kept flip-flopping between
Back and Close as the route changed — and a redundant in-page "Close" button on
the service-unavailable screen.
Make the native back button the single, stable exit: while any blocking screen
is active, TelegramBackButton shows one Back button whose click closes the Mini
App (closeTelegramApp) instead of navigating the SPA — no route change, no
Back/Close flip-flop. The normal route-based back button is restored once the
block clears. Remove the in-page Close button.
Регрессия после a62d689: Back прятался только при deep-link входе
(depthRef === 0). При внутреннем переходе на /subscriptions/:id (depth > 0)
показывался Back, а в single-tariff «назад» уходил в /subscriptions, который
тут же редиректит обратно на деталь (Subscriptions.tsx) — петля, и юзер видел
Back вместо Close.
Решение depth-независимое: если список-редирект всё равно отбрасывает на эту
же страницу (single-tariff, ≤1 подписки = listRedirectsToDetail, инверсия
listIsSafe из обработчика), деталь — это верхний экран. Прячем Back всегда,
Telegram показывает нативный Close. Петля невозможна по построению: без кнопки
Back обработчик navigate(-1) в редиректящий список просто не вызвать.
Multi-tariff и >1 подписки сохраняют реальный Back к осмысленному списку.
На iOS Telegram кнопка «Назад» на /subscriptions/:id казалась мёртвой:
fallback вёл на /subscriptions, который авто-редиректит обратно на деталь
(single-tariff, 1 подписка) — молчаливая петля. Причина — ненадёжный
window.history.state.idx, который мутируют собственные redirect'ы.
idx заменён на надёжный счётчик глубины по useNavigationType (PUSH +1,
POP −1, REPLACE без изменений, дедуп по location.key). Кнопка корректно
показывается/прячется, navigate(-1) берётся только при реальной истории.
Плюс fail-closed fallback: с детали уходим в '/' если список не доказанно
безопасен (multi-tariff или >1 подписки) — петля невозможна.
GitHub-issue #436.
Refines 3c2f650 — that commit broke the loop by suppressing the single-tariff
redirect on idx=0, but left the BackButton visible. The user still saw Back
on the detail page and needed two taps (Back → Close) to exit. Серёжа's
report was specifically that Back should not appear at all when you land on
your only subscription via a bot button.
Move the decision up to TelegramBackButton:
- Subscribe to the existing ['subscriptions-list'] query (deduped by
React Query, so no extra fetch).
- On /subscriptions/:id, when multi_tariff_enabled is false and there is
no in-app history (idx=0 deep-link entry), hide the BackButton. Telegram
then surfaces its native Close (X) in the header.
- Multi-tariff users still see BackButton on /subscriptions/:id deep-links
because their /subscriptions list is meaningful (they have many subs).
The 2bcba3b fallback-to-parent behaviour remains intact for unrelated
deep-links like /balance/top-up, /admin/*, etc.
Revert the defensive guard in Subscriptions.tsx — the loop can no longer be
triggered because no BackButton is shown to trigger it.
Behaviour summary:
flow BackButton exit cost
-------------------------------------- ---------- ---------
deep-link → /subscriptions/:id (1T) hidden Close, 1 tap
/ → /subscriptions → /subscriptions/:id shown Back → /, 1 tap
deep-link → /balance/top-up shown Back → /balance ✓
deep-link → /subscriptions/:id (multi) shown Back → /subscriptions list
Reported by Серёжа, bugs topic #599679.
Bot deep-links open the Mini App directly on nested routes (/admin,
/balance/top-up, /info, /profile, ...) where React Router history has a
single entry, so navigate(-1) was a no-op and the native back button
looked dead. Fall back to the derived parent route when there is no
in-app history (window.history.state.idx === 0).
- Fix stale initData comparison in clearStaleSessionIfNeeded that destroyed
valid refresh tokens on mobile WebView reopens
- Restrict X-Telegram-Init-Data header to auth endpoints only
- Close Mini App on auth retry to force fresh initData from Telegram
- Merge Connection page error/not-configured states for better UX
- Remove unnecessary comments across 40+ files (section dividers,
restating comments, noise catch block comments)
- Configure ESLint allowEmptyCatch to properly handle intentional
empty catch blocks (62 warnings resolved)
Prevent MutationObserver re-initialization on every render by moving
the options object out of JSX (was creating a new object reference
on each render of AppWithNavigator).
Back button was showing on all non-root routes including top-level
pages reachable from the bottom navigation menu (subscription, balance,
referral, support, wheel). These pages don't need a back button since
users navigate between them via the bottom tab bar.
- Обернуть каждый mount-вызов в safeMountSync() для защиты от
ConcurrentCallError при HMR/повторной инициализации
- Перенести requestFullscreen() внутрь mountViewport().then() — fullscreen
требует смонтированный viewport, ранее вызывался через dynamic import
с setTimeout до завершения mount
- Стабилизировать обработчик BackButton через useRef + useCallback —
navigate меняется каждый рендер, что вызывало постоянную
пере-подписку и потерю обработчика
Полный переход с нативного window.Telegram.WebApp API на @telegram-apps/sdk-react v3.
Bridge SDK оборачивал receiveEvent, из-за чего popup_closed не доходил до нативного
обработчика — это было основной причиной бага с двойным открытием попапа.
Изменения:
- Удалён @telegram-apps/react-router-integration (привязан к SDK v1)
- Инициализация SDK v3 в main.tsx: init(), restoreInitData(), mount всех компонентов
- AppWithNavigator: убран TelegramRouter (initNavigator + useIntegration), все платформы
на BrowserRouter, добавлен TelegramBackButton через SDK v3
- useTelegramSDK: детекция через retrieveLaunchParams(), реактивные значения через
useSignal() (fullscreen, viewport, safe area insets), initData через retrieveRawInitData()
- TelegramAdapter: полная перезапись — попапы через showPopup() (Promise-based, без
ручного onEvent/offEvent/timeout), кнопки через setMainButtonParams/showBackButton,
haptic через hapticFeedbackImpactOccurred, тема через themeParamsState с конвертацией
camelCase→snake_case, cloud storage через getCloudStorageItem/setCloudStorageItem
- api/client.ts: initData через retrieveRawInitData() вместо window.Telegram.WebApp.initData
- AppHeader: фото пользователя через initDataUser() сигнал
- ConnectionModal: openLink через SDK вместо window.Telegram.WebApp.openLink
- ColorPicker: детекция Telegram через isInTelegramWebApp()
- Удалён интерфейс TelegramWebApp (368 строк) и Window augmentation из vite-env.d.ts
- Убрано поле telegram из PlatformContext в types.ts
- Обновлён manualChunks в vite.config.ts
Replace manual BackButton management with official @telegram-apps/react-router-integration
to fix navigation issues (back button cycling and double-click).
Changes:
- Migrate from @tma.js/sdk-react to @telegram-apps/sdk-react
- Downgrade react-router-dom to v6 for package compatibility
- Add AppWithNavigator component with Telegram navigator integration
- Use browser history mode (hashMode: null) to prevent hash URLs
- Add multi-level platform detection (user data, platform, version)
- Add try-catch error handling for navigator attach/detach
- Simplify useBackButton hook to no-op (navigator handles automatically)
- Update vite config with new Telegram packages
The navigator automatically manages BackButton based on router history,
falling back to BrowserRouter in regular browser environment.