From 483f3f4d0fbd13bdb2daddcc0c037c7b179e2677 Mon Sep 17 00:00:00 2001 From: c0mrade Date: Wed, 4 Feb 2026 07:18:43 +0300 Subject: [PATCH] Migrate to Telegram Navigator integration 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. --- package-lock.json | 243 +++++++++++++++------------- package.json | 5 +- src/AppWithNavigator.tsx | 102 ++++++++++++ src/main.tsx | 22 +-- src/platform/hooks/useBackButton.ts | 70 +++----- vite.config.ts | 6 +- 6 files changed, 268 insertions(+), 180 deletions(-) create mode 100644 src/AppWithNavigator.tsx diff --git a/package-lock.json b/package-lock.json index e25a042..d688d40 100644 --- a/package-lock.json +++ b/package-lock.json @@ -24,7 +24,8 @@ "@radix-ui/react-tooltip": "^1.2.8", "@radix-ui/react-visually-hidden": "^1.2.4", "@tanstack/react-query": "^5.8.0", - "@tma.js/sdk-react": "^3.0.15", + "@telegram-apps/react-router-integration": "^1.0.1", + "@telegram-apps/sdk-react": "^3.3.9", "axios": "^1.6.0", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", @@ -37,7 +38,7 @@ "react": "^18.2.0", "react-dom": "^18.2.0", "react-i18next": "^13.5.0", - "react-router-dom": "^7.13.0", + "react-router-dom": "^6.30.3", "tailwind-merge": "^3.4.0", "zustand": "^4.4.0" }, @@ -2266,6 +2267,15 @@ "integrity": "sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw==", "license": "MIT" }, + "node_modules/@remix-run/router": { + "version": "1.23.2", + "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.23.2.tgz", + "integrity": "sha512-Ic6m2U/rMjTkhERIa/0ZtXJP17QUi2CbWE7cqx4J58M8aA3QTfW+2UlQ4psvTX9IO1RfNVhK3pcpdjej7L+t2w==", + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/@rolldown/pluginutils": { "version": "1.0.0-beta.53", "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.53.tgz", @@ -2607,47 +2617,60 @@ "react": "^18 || ^19" } }, - "node_modules/@tma.js/bridge": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/@tma.js/bridge/-/bridge-2.2.2.tgz", - "integrity": "sha512-dL7iZzYP/VC4/qJYSXPI1GXLGP3gLTp10L7iGY6PcjB2F4xXS2JnHUoYvD2DgQG1ogjE93zWnVIMN7inx1v+Eg==", + "node_modules/@telegram-apps/bridge": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/@telegram-apps/bridge/-/bridge-2.11.0.tgz", + "integrity": "sha512-kBZjWRRp/lxKeQ8r8cDWUY9EjxUtyeh/9xTQjsjuGRsCR5XTO1cyVbvcvqzHn53csGx3aBs+fOR3Pk3b6w2JHg==", + "deprecated": "This package is not supported anymore. Use @tma.js/bridge instead", "license": "MIT", "dependencies": { - "@tma.js/signals": "^1.0.1", - "@tma.js/toolkit": "^1.0.4", - "@tma.js/transformers": "^1.1.3", - "@tma.js/types": "^1.0.2", - "better-promises": "^1.0.0", - "error-kid": "^1.0.2", - "fp-ts": "^2.16.11", + "@telegram-apps/signals": "^1.1.2", + "@telegram-apps/toolkit": "^2.1.3", + "@telegram-apps/transformers": "^2.2.6", + "@telegram-apps/types": "^2.0.3", + "better-promises": "^0.4.1", + "error-kid": "^0.0.7", "mitt": "^3.0.1", - "valibot": "^1.1.0" + "valibot": "1.0.0" } }, - "node_modules/@tma.js/sdk": { - "version": "3.1.6", - "resolved": "https://registry.npmjs.org/@tma.js/sdk/-/sdk-3.1.6.tgz", - "integrity": "sha512-NENkQa7G5N9kwbL20ErUMO9Fukxl9eVhcJ439PbCg2WYMk/x9oNbx2DhEzhX4xM718zSjYejmSj1oXCKk0PxYA==", + "node_modules/@telegram-apps/navigation": { + "version": "1.0.14", + "resolved": "https://registry.npmjs.org/@telegram-apps/navigation/-/navigation-1.0.14.tgz", + "integrity": "sha512-bqNgF/J8Po7ZtsELm3E1a6aPr7awwxO3sIqD8J6u12urOlGoW5+1KxKKbkPa58mgXuQdsltd8apI+OVy0IYiUA==", + "license": "MIT" + }, + "node_modules/@telegram-apps/react-router-integration": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@telegram-apps/react-router-integration/-/react-router-integration-1.0.1.tgz", + "integrity": "sha512-q5fC0ihXyUDqrSdLQ3u3gypmjI/rMU2mTHeAz9IU1F1bl5X+J17LxWmgLp+OlD0Y97bq2XYmc+UbKrYh+eN3Wg==", "license": "MIT", - "dependencies": { - "@tma.js/bridge": "^2.2.2", - "@tma.js/signals": "^1.0.1", - "@tma.js/toolkit": "^1.0.4", - "@tma.js/transformers": "^1.1.3", - "@tma.js/types": "^1.0.2", - "better-promises": "^1.0.0", - "error-kid": "^1.0.2", - "fp-ts": "^2.16.11", - "valibot": "^1.1.0" + "peerDependencies": { + "@telegram-apps/sdk": "^1", + "@types/react": "^17.0.0 || ^18.0.0", + "react": "^17.0.0 || ^18.0.0", + "react-router-dom": "^6.22.3" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } } }, - "node_modules/@tma.js/sdk-react": { - "version": "3.0.15", - "resolved": "https://registry.npmjs.org/@tma.js/sdk-react/-/sdk-react-3.0.15.tgz", - "integrity": "sha512-HsgqPINSBK87NIRZ+ZDol07QPhSQ3GOTofyZEH0xoaQjZWb9wVxUXwECa5ozRUdzWZI+Mgx8oD1pzrjyNkDjFA==", + "node_modules/@telegram-apps/sdk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@telegram-apps/sdk/-/sdk-1.1.3.tgz", + "integrity": "sha512-gvcXNttbCouDNTxjDUbO5FS/BdaHYO0ibSddQxiYitShPIDZDxGon0Eh3OSsgUIVPSQSNdXqkXaeqYgA+jeY0A==", + "license": "MIT", + "peer": true + }, + "node_modules/@telegram-apps/sdk-react": { + "version": "3.3.9", + "resolved": "https://registry.npmjs.org/@telegram-apps/sdk-react/-/sdk-react-3.3.9.tgz", + "integrity": "sha512-85jF1ICT8sYCNzXu19SqJrfrS8XslsvV14KUcToiyL7H5ZMXHt0JQKM+QJgULjnLjEswweQ4/7Bd7mNULf/BIg==", "license": "MIT", "dependencies": { - "@tma.js/sdk": "^3.1.6" + "@telegram-apps/sdk": "^3.11.8" }, "peerDependencies": { "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", @@ -2659,38 +2682,66 @@ } } }, - "node_modules/@tma.js/signals": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@tma.js/signals/-/signals-1.0.1.tgz", - "integrity": "sha512-i2HUuwGqL4BmM5KAklAUhMhlEgUOF+F4nMHHS/zxrmD0upHE/0CiXCEdQxVeeOGN6e2RrKlonA40sDtR6OUDCw==", + "node_modules/@telegram-apps/sdk-react/node_modules/@telegram-apps/sdk": { + "version": "3.11.8", + "resolved": "https://registry.npmjs.org/@telegram-apps/sdk/-/sdk-3.11.8.tgz", + "integrity": "sha512-vlpkYzMJCV9Cadsn9Q+/hbHNR39j5o96N9z4CjZ6AFHkkxOeOqVRrq9zRBw0gmffROkF2bU0WQxhRj8KZ/bPhw==", + "license": "MIT", + "dependencies": { + "@telegram-apps/bridge": "^2.11.0", + "@telegram-apps/navigation": "^1.0.14", + "@telegram-apps/signals": "^1.1.2", + "@telegram-apps/toolkit": "^2.1.3", + "@telegram-apps/transformers": "^2.2.6", + "@telegram-apps/types": "^2.0.3", + "better-promises": "^0.4.1", + "error-kid": "^0.0.7", + "valibot": "1.0.0" + } + }, + "node_modules/@telegram-apps/signals": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@telegram-apps/signals/-/signals-1.1.2.tgz", + "integrity": "sha512-1P1kdCLX7MfETGPxH7f3UZKIsdE7Tz5S7QmN4Km1sbYQMakD5Bi1NecSMR7/wnHp50gWMI1JzENcMtCEmouhSg==", "license": "MIT" }, - "node_modules/@tma.js/toolkit": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@tma.js/toolkit/-/toolkit-1.0.4.tgz", - "integrity": "sha512-6CDlkgc+43WD2jy6jog3B50yt9t+/wRwrN25zOa4iSw4IzfqoFjaQcJzTupBYaRxz19C3Rrir7p0/qStS/Z8vw==", + "node_modules/@telegram-apps/toolkit": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@telegram-apps/toolkit/-/toolkit-2.1.3.tgz", + "integrity": "sha512-LPUBL7hxQTOr+Dowyk9a1O82nQS4ja4+OYiYWtvqq5nNUHC6Gbbus0zGWCbFcj9CLnIzaeb5HWOg5iSnhoRcyg==", + "license": "MIT" + }, + "node_modules/@telegram-apps/transformers": { + "version": "2.2.6", + "resolved": "https://registry.npmjs.org/@telegram-apps/transformers/-/transformers-2.2.6.tgz", + "integrity": "sha512-MMBRs3demeBT9Cd614KKZmak7eEyNdEbfu99a0SwEEJe2oIODjJLrUxrhUcAOc5wvTRfrKka27VXVgruauLhdg==", + "deprecated": "This package is not supported anymore. Use @tma.js/transfomers instead", "license": "MIT", "dependencies": { - "better-promises": "^1.0.0", - "fp-ts": "^2.16.11" + "@telegram-apps/toolkit": "^2.1.3", + "@telegram-apps/types": "^2.0.3", + "valibot": "1.0.0-beta.14" } }, - "node_modules/@tma.js/transformers": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@tma.js/transformers/-/transformers-1.1.3.tgz", - "integrity": "sha512-zRL+fdo/NBpGqCJfl8ItAvcJIFT5Zy6UOYJwQPmUV5/gTHgmSvmGlrP43PeVi9XZ7Ggf4xbO6LUa4ehxozhMTA==", + "node_modules/@telegram-apps/transformers/node_modules/valibot": { + "version": "1.0.0-beta.14", + "resolved": "https://registry.npmjs.org/valibot/-/valibot-1.0.0-beta.14.tgz", + "integrity": "sha512-tLyV2rE5QL6U29MFy3xt4AqMrn+/HErcp2ZThASnQvPMwfSozjV1uBGKIGiegtZIGjinJqn0SlBdannf18wENA==", "license": "MIT", - "dependencies": { - "@tma.js/toolkit": "^1.0.4", - "@tma.js/types": "^1.0.2", - "fp-ts": "^2.16.11", - "valibot": "^1.1.0" + "peerDependencies": { + "typescript": ">=5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/@tma.js/types": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@tma.js/types/-/types-1.0.2.tgz", - "integrity": "sha512-qs4mi+U1xZmMQBdMhWAo1X4YqUJ/ae0y28s+GNCpQq58bsJo0h8rvyVOB1RwPvXogIY9+yribbZe6z3AIJmsAQ==", + "node_modules/@telegram-apps/types": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@telegram-apps/types/-/types-2.0.3.tgz", + "integrity": "sha512-pXh9BdnLZF3e2BGc4WL+RTRMAUpqKpaSP3Rs8rB4WyRwIoRSGWFKE4gtT/9m42LGixB8YVwdo/pJ+9XO765XEA==", + "deprecated": "This package is not supported anymore. Use @tma.js/types instead", "license": "MIT" }, "node_modules/@types/babel__core": { @@ -3282,12 +3333,12 @@ } }, "node_modules/better-promises": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/better-promises/-/better-promises-1.0.0.tgz", - "integrity": "sha512-gPgL2nRgeSbMIe3QpsYdKR3K0S9OZuphVuos60Eqsw8d/6GivOkyJ5D/zmnolJ6hzh7upnnflBUWUlQh4qpU2A==", + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/better-promises/-/better-promises-0.4.1.tgz", + "integrity": "sha512-cDW7eMvhvCoWzSih5o/OxsAgTUfP05yGMq77xNZUTmcZoNU9vEeFZJ+yJJi4lnocvxFrVFKsG0Yxt7ZnuYJEig==", "license": "MIT", "dependencies": { - "error-kid": "^1.0.1" + "error-kid": "^0.0.7" } }, "node_modules/binary-extensions": { @@ -3604,19 +3655,6 @@ "dev": true, "license": "MIT" }, - "node_modules/cookie": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.1.1.tgz", - "integrity": "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==", - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, "node_modules/cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", @@ -3757,9 +3795,9 @@ } }, "node_modules/error-kid": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/error-kid/-/error-kid-1.0.2.tgz", - "integrity": "sha512-Xvq0ZrY/azCbREWKt9E/3mXDF0MkuEVVvHnOKutUhtq2O8pyneEixQ4nSFkA19Xfn+/OVSg//iVG0dxIDj7DIA==", + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/error-kid/-/error-kid-0.0.7.tgz", + "integrity": "sha512-8mFs7ZaDWlBFgjOy4lHLMCe2+KZfZXGx5GOgh2VQ/M1CCIvSWzbl2OMl+5fdZgrgoUfhTeF+NPhmqfEvUhN8yw==", "license": "MIT" }, "node_modules/es-define-property": { @@ -4241,12 +4279,6 @@ "node": ">= 6" } }, - "node_modules/fp-ts": { - "version": "2.16.11", - "resolved": "https://registry.npmjs.org/fp-ts/-/fp-ts-2.16.11.tgz", - "integrity": "sha512-LaI+KaX2NFkfn1ZGHoKCmcfv7yrZsC3b8NtWsTVQeHkq4F27vI5igUuO53sxqDEa2gNQMHFPmpojDw/1zmUK7w==", - "license": "MIT" - }, "node_modules/fraction.js": { "version": "5.3.4", "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-5.3.4.tgz", @@ -5679,41 +5711,36 @@ } }, "node_modules/react-router": { - "version": "7.13.0", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.13.0.tgz", - "integrity": "sha512-PZgus8ETambRT17BUm/LL8lX3Of+oiLaPuVTRH3l1eLvSPpKO3AvhAEb5N7ihAFZQrYDqkvvWfFh9p0z9VsjLw==", + "version": "6.30.3", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.30.3.tgz", + "integrity": "sha512-XRnlbKMTmktBkjCLE8/XcZFlnHvr2Ltdr1eJX4idL55/9BbORzyZEaIkBFDhFGCEWBBItsVrDxwx3gnisMitdw==", "license": "MIT", "dependencies": { - "cookie": "^1.0.1", - "set-cookie-parser": "^2.6.0" + "@remix-run/router": "1.23.2" }, "engines": { - "node": ">=20.0.0" + "node": ">=14.0.0" }, "peerDependencies": { - "react": ">=18", - "react-dom": ">=18" - }, - "peerDependenciesMeta": { - "react-dom": { - "optional": true - } + "react": ">=16.8" } }, "node_modules/react-router-dom": { - "version": "7.13.0", - "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.13.0.tgz", - "integrity": "sha512-5CO/l5Yahi2SKC6rGZ+HDEjpjkGaG/ncEP7eWFTvFxbHP8yeeI0PxTDjimtpXYlR3b3i9/WIL4VJttPrESIf2g==", + "version": "6.30.3", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.30.3.tgz", + "integrity": "sha512-pxPcv1AczD4vso7G4Z3TKcvlxK7g7TNt3/FNGMhfqyntocvYKj+GCatfigGDjbLozC4baguJ0ReCigoDJXb0ag==", "license": "MIT", + "peer": true, "dependencies": { - "react-router": "7.13.0" + "@remix-run/router": "1.23.2", + "react-router": "6.30.3" }, "engines": { - "node": ">=20.0.0" + "node": ">=14.0.0" }, "peerDependencies": { - "react": ">=18", - "react-dom": ">=18" + "react": ">=16.8", + "react-dom": ">=16.8" } }, "node_modules/react-style-singleton": { @@ -5915,12 +5942,6 @@ "node": ">=10" } }, - "node_modules/set-cookie-parser": { - "version": "2.7.2", - "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.2.tgz", - "integrity": "sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==", - "license": "MIT" - }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -6414,9 +6435,9 @@ "license": "MIT" }, "node_modules/valibot": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/valibot/-/valibot-1.2.0.tgz", - "integrity": "sha512-mm1rxUsmOxzrwnX5arGS+U4T25RdvpPjPN4yR0u9pUBov9+zGVtO84tif1eY4r6zWxVxu3KzIyknJy3rxfRZZg==", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/valibot/-/valibot-1.0.0.tgz", + "integrity": "sha512-1Hc0ihzWxBar6NGeZv7fPLY0QuxFMyxwYR2sF1Blu7Wq7EnremwY2W02tit2ij2VJT8HcSkHAQqmFfl77f73Yw==", "license": "MIT", "peerDependencies": { "typescript": ">=5" diff --git a/package.json b/package.json index 1416f46..2b8e1bf 100644 --- a/package.json +++ b/package.json @@ -32,7 +32,8 @@ "@radix-ui/react-tooltip": "^1.2.8", "@radix-ui/react-visually-hidden": "^1.2.4", "@tanstack/react-query": "^5.8.0", - "@tma.js/sdk-react": "^3.0.15", + "@telegram-apps/react-router-integration": "^1.0.1", + "@telegram-apps/sdk-react": "^3.3.9", "axios": "^1.6.0", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", @@ -45,7 +46,7 @@ "react": "^18.2.0", "react-dom": "^18.2.0", "react-i18next": "^13.5.0", - "react-router-dom": "^7.13.0", + "react-router-dom": "^6.30.3", "tailwind-merge": "^3.4.0", "zustand": "^4.4.0" }, diff --git a/src/AppWithNavigator.tsx b/src/AppWithNavigator.tsx new file mode 100644 index 0000000..fb05276 --- /dev/null +++ b/src/AppWithNavigator.tsx @@ -0,0 +1,102 @@ +import { useEffect, useMemo } from 'react'; +import { BrowserRouter, Router } from 'react-router-dom'; +import { useIntegration } from '@telegram-apps/react-router-integration'; +import { initNavigator } from '@telegram-apps/sdk'; +import App from './App'; +import { PlatformProvider } from './platform/PlatformProvider'; +import { ThemeColorsProvider } from './providers/ThemeColorsProvider'; +import { WebSocketProvider } from './providers/WebSocketProvider'; +import { ToastProvider } from './components/Toast'; +import { TooltipProvider } from './components/primitives/Tooltip'; + +/** + * Check if running inside Telegram Mini App + * Uses multiple checks to reliably detect Telegram environment + */ +function isTelegramMiniApp(): boolean { + if (typeof window === 'undefined') return false; + + const webApp = window.Telegram?.WebApp; + if (!webApp) return false; + + // Check 1: initDataUnsafe should have user data in real Telegram + const hasUserData = webApp.initDataUnsafe?.user?.id !== undefined; + + // Check 2: Platform should not be 'unknown' (which is default in browser) + const validPlatform = webApp.platform !== 'unknown' && webApp.platform !== ''; + + // Check 3: Version should be present (SDK loads in Telegram only) + const hasVersion = webApp.version !== undefined && webApp.version !== ''; + + return hasUserData || (validPlatform && hasVersion); +} + +/** + * Component wrapper for Telegram navigator setup. + * Integrates Telegram Mini Apps navigator with React Router to provide + * automatic BackButton management based on navigation history. + * Falls back to BrowserRouter when not in Telegram Mini App. + */ +/** + * Navigator-based router for Telegram Mini App + */ +function TelegramRouter({ children }: { children: React.ReactNode }) { + const navigator = useMemo(() => initNavigator('app-navigation-state', { hashMode: null }), []); + const [location, reactNavigator] = useIntegration(navigator); + + useEffect(() => { + try { + navigator.attach(); + return () => { + try { + navigator.detach(); + } catch (err) { + console.warn('Failed to detach navigator:', err); + } + }; + } catch (err) { + console.warn('Failed to attach navigator:', err); + } + }, [navigator]); + + return ( + + {children} + + ); +} + +export function AppWithNavigator() { + const isTelegram = useMemo(() => { + const result = isTelegramMiniApp(); + console.log('[AppWithNavigator] Platform detection:', { + isTelegram: result, + platform: window.Telegram?.WebApp?.platform, + hasUser: window.Telegram?.WebApp?.initDataUnsafe?.user?.id !== undefined, + version: window.Telegram?.WebApp?.version, + }); + return result; + }, []); + + // Common app content + const appContent = ( + + + + + + + + + + + + ); + + // Use Telegram navigator in Mini App, BrowserRouter elsewhere + if (isTelegram) { + return {appContent}; + } + + return {appContent}; +} diff --git a/src/main.tsx b/src/main.tsx index 6897c17..ba8673c 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -1,13 +1,7 @@ import React from 'react'; import ReactDOM from 'react-dom/client'; -import { BrowserRouter } from 'react-router-dom'; import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; -import App from './App'; -import { PlatformProvider } from './platform/PlatformProvider'; -import { ThemeColorsProvider } from './providers/ThemeColorsProvider'; -import { WebSocketProvider } from './providers/WebSocketProvider'; -import { ToastProvider } from './components/Toast'; -import { TooltipProvider } from './components/primitives/Tooltip'; +import { AppWithNavigator } from './AppWithNavigator'; import { initLogoPreload } from './api/branding'; import { initTelegramSDK } from './hooks/useTelegramSDK'; import './i18n'; @@ -31,19 +25,7 @@ const queryClient = new QueryClient({ ReactDOM.createRoot(document.getElementById('root')!).render( - - - - - - - - - - - - - + , ); diff --git a/src/platform/hooks/useBackButton.ts b/src/platform/hooks/useBackButton.ts index ec0dcbe..8124fa0 100644 --- a/src/platform/hooks/useBackButton.ts +++ b/src/platform/hooks/useBackButton.ts @@ -1,6 +1,3 @@ -import { useEffect, useRef, useCallback } from 'react'; -import { usePlatform } from '@/platform/hooks/usePlatform'; - interface UseBackButtonOptions { /** * Whether the back button should be visible @@ -11,65 +8,46 @@ interface UseBackButtonOptions { /** * Hook to manage the Telegram BackButton - * Automatically shows/hides based on component lifecycle * - * @param onBack - Callback when back button is pressed - * @param options - Configuration options + * NOTE: This hook is now a no-op for backward compatibility. + * The Telegram Navigator (@telegram-apps/react-router-integration) handles + * BackButton visibility and navigation automatically based on router history. + * + * The navigator: + * - Shows BackButton when there's navigation history + * - Hides BackButton on root page + * - Handles back button clicks automatically + * + * @param onBack - (Ignored) Callback when back button is pressed + * @param options - (Ignored) Configuration options * * @example * ```tsx * function MyPage() { * const navigate = useNavigate(); - * useBackButton(() => navigate(-1)); + * useBackButton(() => navigate('/admin')); // No-op: navigator handles this * // ... * } * ``` */ export function useBackButton( - onBack: (() => void) | null | undefined, - options: UseBackButtonOptions = {}, + _onBack: (() => void) | null | undefined, + _options: UseBackButtonOptions = {}, ): void { - const { backButton, capabilities } = usePlatform(); - const { visible = true } = options; - - // Use ref to prevent callback recreation issues - const callbackRef = useRef(onBack); - callbackRef.current = onBack; - - // Stable callback wrapper - const handleBack = useCallback(() => { - callbackRef.current?.(); - }, []); - - useEffect(() => { - // If no native back button support, do nothing - if (!capabilities.hasBackButton) { - return; - } - - // If callback is null/undefined or visible is false, hide button - if (!onBack || !visible) { - backButton.hide(); - return; - } - - // Show the back button with our handler - backButton.show(handleBack); - - // Cleanup: hide button when component unmounts - return () => { - backButton.hide(); - }; - }, [backButton, capabilities.hasBackButton, handleBack, onBack, visible]); + // No-op: Navigator handles BackButton automatically + return; } /** * Hook to conditionally show back button based on navigation depth - * Useful for showing back button only when there's history to go back to * - * @param canGoBack - Whether navigation back is possible - * @param onBack - Callback when back button is pressed + * NOTE: This hook is now a no-op for backward compatibility. + * The navigator handles conditional visibility automatically. + * + * @param canGoBack - (Ignored) Whether navigation back is possible + * @param onBack - (Ignored) Callback when back button is pressed */ -export function useConditionalBackButton(canGoBack: boolean, onBack: () => void): void { - useBackButton(canGoBack ? onBack : null); +export function useConditionalBackButton(_canGoBack: boolean, _onBack: () => void): void { + // No-op: Navigator handles BackButton automatically + return; } diff --git a/vite.config.ts b/vite.config.ts index 0420b8d..9d07f2e 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -49,7 +49,11 @@ export default defineConfig({ '@radix-ui/react-visually-hidden', ], 'vendor-dnd': ['@dnd-kit/core', '@dnd-kit/sortable', '@dnd-kit/utilities'], - 'vendor-telegram': ['@tma.js/sdk-react'], + 'vendor-telegram': [ + '@telegram-apps/sdk', + '@telegram-apps/sdk-react', + '@telegram-apps/react-router-integration', + ], 'vendor-utils': [ 'axios', 'zustand',