mirror of
https://github.com/chillpadclub/bedolaga-cabinet.git
synced 2026-07-28 09:33:46 +00:00
fix(navigation): make Telegram back button work on deep-link entry
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).
This commit is contained in:
@@ -15,6 +15,7 @@ import { WebSocketProvider } from './providers/WebSocketProvider';
|
|||||||
import { ToastProvider } from './components/Toast';
|
import { ToastProvider } from './components/Toast';
|
||||||
import { TooltipProvider } from './components/primitives/Tooltip';
|
import { TooltipProvider } from './components/primitives/Tooltip';
|
||||||
import { isInTelegramWebApp } from './hooks/useTelegramSDK';
|
import { isInTelegramWebApp } from './hooks/useTelegramSDK';
|
||||||
|
import { hasInAppHistory, getFallbackParentPath } from './utils/navigation';
|
||||||
|
|
||||||
const TWEMOJI_OPTIONS = { className: 'twemoji', folder: 'svg', ext: '.svg' } as const;
|
const TWEMOJI_OPTIONS = { className: 'twemoji', folder: 'svg', ext: '.svg' } as const;
|
||||||
|
|
||||||
@@ -30,6 +31,8 @@ function TelegramBackButton() {
|
|||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const navigateRef = useRef(navigate);
|
const navigateRef = useRef(navigate);
|
||||||
navigateRef.current = navigate;
|
navigateRef.current = navigate;
|
||||||
|
const pathnameRef = useRef(location.pathname);
|
||||||
|
pathnameRef.current = location.pathname;
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const isTopLevel = location.pathname === '' || BOTTOM_NAV_PATHS.includes(location.pathname);
|
const isTopLevel = location.pathname === '' || BOTTOM_NAV_PATHS.includes(location.pathname);
|
||||||
@@ -44,7 +47,14 @@ function TelegramBackButton() {
|
|||||||
|
|
||||||
// Stable handler — ref prevents re-subscription on every render
|
// Stable handler — ref prevents re-subscription on every render
|
||||||
const handler = useCallback(() => {
|
const handler = useCallback(() => {
|
||||||
navigateRef.current(-1);
|
// When opened via a bot deep-link directly on a nested route, there is no
|
||||||
|
// in-app history and navigate(-1) is a no-op — the back button looks dead.
|
||||||
|
// Fall back to the parent route so it always navigates somewhere sensible.
|
||||||
|
if (hasInAppHistory()) {
|
||||||
|
navigateRef.current(-1);
|
||||||
|
} else {
|
||||||
|
navigateRef.current(getFallbackParentPath(pathnameRef.current), { replace: true });
|
||||||
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|||||||
38
src/utils/navigation.ts
Normal file
38
src/utils/navigation.ts
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
/**
|
||||||
|
* Navigation helpers for back-button behavior.
|
||||||
|
*
|
||||||
|
* The Telegram Mini App (and any deep-link entry) can be opened directly on a
|
||||||
|
* nested route — e.g. a bot button that opens `/admin` or `/balance/top-up`.
|
||||||
|
* In that case React Router's history stack holds a single entry, so a plain
|
||||||
|
* `navigate(-1)` has nothing to go back to and the back button appears dead.
|
||||||
|
* These helpers let callers detect that situation and fall back to a sensible
|
||||||
|
* parent route instead.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* React Router stores the current position in the history stack on
|
||||||
|
* `window.history.state.idx`. The first/entry record is `0`. When `idx === 0`
|
||||||
|
* there is no in-app history to go back to (the page is the deep-link entry).
|
||||||
|
*/
|
||||||
|
export function hasInAppHistory(): boolean {
|
||||||
|
const idx = (window.history.state as { idx?: number } | null)?.idx ?? 0;
|
||||||
|
return idx > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Derive a parent route by dropping the last path segment.
|
||||||
|
*
|
||||||
|
* /admin/users/123 → /admin/users
|
||||||
|
* /balance/top-up → /balance
|
||||||
|
* /info → /
|
||||||
|
* / → /
|
||||||
|
*
|
||||||
|
* Used as the back-button target when there is no in-app history. If the
|
||||||
|
* derived path is not a real route the app's catch-all redirects to `/`,
|
||||||
|
* so the user is never left stuck.
|
||||||
|
*/
|
||||||
|
export function getFallbackParentPath(pathname: string): string {
|
||||||
|
const segments = pathname.replace(/\/+$/, '').split('/').filter(Boolean);
|
||||||
|
const parent = segments.slice(0, -1);
|
||||||
|
return parent.length ? '/' + parent.join('/') : '/';
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user