diff --git a/README.md b/README.md
index c8f4207..a44fe34 100644
--- a/README.md
+++ b/README.md
@@ -6,7 +6,7 @@ Mini Apps platform using the following technologies and libraries:
- [React](https://react.dev/)
- [TypeScript](https://www.typescriptlang.org/)
- [TON Connect](https://docs.ton.org/develop/dapps/ton-connect/overview)
-- [@tma.js SDK](https://docs.telegram-mini-apps.com/packages/tma-js-sdk)
+- [Telegram SDK](https://core.telegram.org/bots/webapps#initializing-mini-apps)
- [Telegram UI](https://github.com/Telegram-Mini-Apps/TelegramUI)
- [Vite](https://vitejs.dev/)
@@ -71,15 +71,6 @@ link (`http://localhost:5173/reactjs-template` in this example) in your browser:
![Application](assets/application.png)
-It is important to note that some libraries in this template, such as `@tma.js/sdk`, are not
-intended for use outside of Telegram.
-
-Nevertheless, they appear to function properly. This is because the `src/mockEnv.ts` file, which is
-imported in the application's entry point (`src/index.ts`), employs the `mockTelegramEnv` function
-to simulate the Telegram environment. This trick convinces the application that it is running in a
-Telegram-based environment. Therefore, be cautious not to use this function in production mode
-unless you fully understand its implications.
-
### Run Inside Telegram
Although it is possible to run the application outside of Telegram, it is recommended to develop it
@@ -243,5 +234,4 @@ project's information.
## Useful Links
- [Platform documentation](https://docs.telegram-mini-apps.com/)
-- [@tma.js/sdk-react documentation](https://docs.telegram-mini-apps.com/packages/tma-js-sdk-react)
- [Telegram developers community chat](https://t.me/devs)
diff --git a/package-lock.json b/package-lock.json
index 8b9a563..ba26563 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -9,9 +9,8 @@
"version": "0.0.1",
"dependencies": {
"@telegram-apps/telegram-ui": "^2.1.4",
- "@tma.js/react-router-integration": "^4.0.3",
- "@tma.js/sdk-react": "^2.2.3",
"@tonconnect/ui-react": "^2.0.5",
+ "@twa-dev/sdk": "^7.0.0",
"eruda": "^3.0.1",
"react": "^18.2.0",
"react-dom": "^18.2.0",
@@ -29,7 +28,6 @@
"eslint": "^8.57.0",
"eslint-plugin-react": "^7.34.1",
"gh-pages": "^6.1.1",
- "globals": "^15.2.0",
"typescript": "^5.4.5",
"typescript-eslint": "^7.8.0",
"vite": "^5.2.11",
@@ -1411,53 +1409,6 @@
"react-dom": "18.2.0"
}
},
- "node_modules/@tma.js/react-router-integration": {
- "version": "4.0.3",
- "resolved": "https://registry.npmjs.org/@tma.js/react-router-integration/-/react-router-integration-4.0.3.tgz",
- "integrity": "sha512-yORPUds45tLU3H8w11Yf/BdcZl/MBswL5ptjk0szb/1mi8oDKZFzVflxRaB5cw60ok2wuQlvbMMdqh3yG2PKpw==",
- "peerDependencies": {
- "@tma.js/sdk": "^2.5.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
- },
- "react-router-dom": {
- "optional": true
- }
- }
- },
- "node_modules/@tma.js/sdk": {
- "version": "2.5.1",
- "resolved": "https://registry.npmjs.org/@tma.js/sdk/-/sdk-2.5.1.tgz",
- "integrity": "sha512-uUFv3oINGRwkh4KbGxMq9OxDdSQOhH9O45i9i0aqen6pIakeiIxxzrF8Yhv2xSFWrn8V+gaOpr3IDrushlo5TA==",
- "peer": true
- },
- "node_modules/@tma.js/sdk-react": {
- "version": "2.2.3",
- "resolved": "https://registry.npmjs.org/@tma.js/sdk-react/-/sdk-react-2.2.3.tgz",
- "integrity": "sha512-/+itgbU5Z0BrMj31Dx2kfg8h/d00rP3Hyo606qZRFOQ9KodMd0xDazQO9AUPY7u1ATERBAY5Jh2dgHkQCLNaAw==",
- "dependencies": {
- "@tma.js/sdk": "2.5.0"
- },
- "peerDependencies": {
- "@types/react": "^17.0.0 || ^18.0.0",
- "react": "^17.0.0 || ^18.0.0"
- },
- "peerDependenciesMeta": {
- "@types/react": {
- "optional": true
- }
- }
- },
- "node_modules/@tma.js/sdk-react/node_modules/@tma.js/sdk": {
- "version": "2.5.0",
- "resolved": "https://registry.npmjs.org/@tma.js/sdk/-/sdk-2.5.0.tgz",
- "integrity": "sha512-9cccHzNADaIwAJwK8yL4WeTdx9JO7kAHvjN9Q6T3CQMx07bw5M2RNCaZ1+UGrQz9IWNa+GrnXkN/eIZEW+oHUw=="
- },
"node_modules/@tonconnect/isomorphic-eventsource": {
"version": "0.0.2",
"resolved": "https://registry.npmjs.org/@tonconnect/isomorphic-eventsource/-/isomorphic-eventsource-0.0.2.tgz",
@@ -1516,6 +1467,17 @@
"react-dom": ">=17.0.0"
}
},
+ "node_modules/@twa-dev/sdk": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/@twa-dev/sdk/-/sdk-7.0.0.tgz",
+ "integrity": "sha512-OZvqGGrdrgE0xU/IGjvsGKKouia5bTu6rA/BOOU6pJYLu776nQJs/HNFl7lwW16RONf1vvPqeV8XximsuzZ9Iw==",
+ "dependencies": {
+ "@twa-dev/types": "^7.0.0"
+ },
+ "peerDependencies": {
+ "react": ">=16.0.0"
+ }
+ },
"node_modules/@twa-dev/types": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/@twa-dev/types/-/types-7.0.0.tgz",
@@ -3200,18 +3162,6 @@
"node": "*"
}
},
- "node_modules/globals": {
- "version": "15.3.0",
- "resolved": "https://registry.npmjs.org/globals/-/globals-15.3.0.tgz",
- "integrity": "sha512-cCdyVjIUVTtX8ZsPkq1oCsOsLmGIswqnjZYMJJTGaNApj1yHtLSymKhwH51ttirREn75z3p4k051clwg7rvNKA==",
- "dev": true,
- "engines": {
- "node": ">=18"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
"node_modules/globalthis": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz",
diff --git a/package.json b/package.json
index 60d2304..b5ee803 100644
--- a/package.json
+++ b/package.json
@@ -15,9 +15,8 @@
},
"dependencies": {
"@telegram-apps/telegram-ui": "^2.1.4",
- "@tma.js/react-router-integration": "^4.0.3",
- "@tma.js/sdk-react": "^2.2.3",
"@tonconnect/ui-react": "^2.0.5",
+ "@twa-dev/sdk": "^7.0.0",
"eruda": "^3.0.1",
"react": "^18.2.0",
"react-dom": "^18.2.0",
@@ -35,7 +34,6 @@
"eslint": "^8.57.0",
"eslint-plugin-react": "^7.34.1",
"gh-pages": "^6.1.1",
- "globals": "^15.2.0",
"typescript": "^5.4.5",
"typescript-eslint": "^7.8.0",
"vite": "^5.2.11",
diff --git a/src/components/App.tsx b/src/components/App.tsx
index fe8f655..9c0068a 100644
--- a/src/components/App.tsx
+++ b/src/components/App.tsx
@@ -1,65 +1,52 @@
-import { useIntegration } from '@tma.js/react-router-integration';
-import {
- bindMiniAppCSSVars,
- bindThemeParamsCSSVars,
- bindViewportCSSVars,
- initNavigator, useLaunchParams,
- useMiniApp,
- useThemeParams,
- useViewport,
-} from '@tma.js/sdk-react';
+import WebApp from '@twa-dev/sdk';
import { AppRoot } from '@telegram-apps/telegram-ui';
-import { type FC, useEffect, useMemo } from 'react';
+import { type FC, useEffect } from 'react';
import {
Navigate,
Route,
- Router,
+ BrowserRouter,
Routes,
+ useLocation,
+ useNavigate,
} from 'react-router-dom';
import { routes } from '@/navigation/routes.tsx';
-export const App: FC = () => {
- const lp = useLaunchParams();
- const miniApp = useMiniApp();
- const themeParams = useThemeParams();
- const viewport = useViewport();
-
- useEffect(() => {
- return bindMiniAppCSSVars(miniApp, themeParams);
- }, [miniApp, themeParams]);
-
- useEffect(() => {
- return bindThemeParamsCSSVars(themeParams);
- }, [themeParams]);
+function BackButtonManipulator() {
+ const location = useLocation();
+ const navigate = useNavigate();
useEffect(() => {
- return viewport && bindViewportCSSVars(viewport);
- }, [viewport]);
+ function onClick() {
+ navigate(-1);
+ }
+ WebApp.BackButton.onClick(onClick);
- // Create new application navigator and attach it to the browser history, so it could modify
- // it and listen to its changes.
- const navigator = useMemo(() => initNavigator('app-navigation-state'), []);
- const [location, reactNavigator] = useIntegration(navigator);
+ return () => WebApp.BackButton.offClick(onClick);
+ }, [navigate]);
- // Don't forget to attach the navigator to allow it to control the BackButton state as well
- // as browser history.
useEffect(() => {
- navigator.attach();
- return () => navigator.detach();
- }, [navigator]);
-
- return (
-
-
-
- {routes.map((route) => )}
- }/>
-
-
-
- );
-};
+ if (location.pathname === '/') {
+ WebApp.BackButton.isVisible && WebApp.BackButton.hide();
+ } else {
+ !WebApp.BackButton.isVisible && WebApp.BackButton.show();
+ }
+ }, [location]);
+
+ return null;
+}
+
+export const App: FC = () => (
+
+
+
+
+ {routes.map((route) => )}
+ }/>
+
+
+
+);
diff --git a/src/components/DisplayData/DisplayData.tsx b/src/components/DisplayData/DisplayData.tsx
index 1bfe982..96aa246 100644
--- a/src/components/DisplayData/DisplayData.tsx
+++ b/src/components/DisplayData/DisplayData.tsx
@@ -1,4 +1,3 @@
-import { isRGB } from '@tma.js/sdk-react';
import { Cell, Checkbox, Section } from '@telegram-apps/telegram-ui';
import type { FC, ReactNode } from 'react';
@@ -25,13 +24,15 @@ export const DisplayData: FC = ({ header, rows }) => (
{rows.map((item, idx) => {
let valueNode: ReactNode;
+ console.log(item);
+
if (item.value === undefined) {
valueNode = empty;
} else {
if ('type' in item) {
valueNode = Open;
} else if (typeof item.value === 'string') {
- valueNode = isRGB(item.value)
+ valueNode = item.value.match(/^#[a-f0-9]{3,6}$/i)
?
: item.value;
} else if (typeof item.value === 'boolean') {
diff --git a/src/components/Link/Link.tsx b/src/components/Link/Link.tsx
index cd861db..1a7af67 100644
--- a/src/components/Link/Link.tsx
+++ b/src/components/Link/Link.tsx
@@ -1,5 +1,5 @@
-import { classNames, useUtils } from '@tma.js/sdk-react';
import { type FC, type MouseEventHandler, useCallback } from 'react';
+import WebApp from '@twa-dev/sdk';
import { Link as RouterLink, type LinkProps } from 'react-router-dom';
import './Link.css';
@@ -10,8 +10,6 @@ export const Link: FC = ({
to,
...rest
}) => {
- const utils = useUtils();
-
const onClick = useCallback>((e) => {
propsOnClick?.(e);
@@ -32,16 +30,16 @@ export const Link: FC = ({
if (isExternal) {
e.preventDefault();
- return utils.openLink(targetUrl.toString());
+ return WebApp.openLink(targetUrl.toString());
}
- }, [to, propsOnClick, utils]);
+ }, [to, propsOnClick]);
return (
);
};
diff --git a/src/components/RGB/RGB.tsx b/src/components/RGB/RGB.tsx
index a423f15..1d91835 100644
--- a/src/components/RGB/RGB.tsx
+++ b/src/components/RGB/RGB.tsx
@@ -1,14 +1,13 @@
-import { classNames, type RGB as RGBType } from '@tma.js/sdk-react';
import type { FC } from 'react';
import './RGB.css';
export type RGBProps = JSX.IntrinsicElements['div'] & {
- color: RGBType;
+ color: string;
};
export const RGB: FC = ({ color, className, ...rest }) => (
-
+
{color}
diff --git a/src/components/Root.tsx b/src/components/Root.tsx
index 990bb1f..c4f456a 100644
--- a/src/components/Root.tsx
+++ b/src/components/Root.tsx
@@ -1,6 +1,6 @@
-import { SDKProvider, useLaunchParams } from '@tma.js/sdk-react';
import { TonConnectUIProvider } from '@tonconnect/ui-react';
import { type FC, useEffect, useMemo } from 'react';
+import WebApp from '@twa-dev/sdk';
import { App } from '@/components/App.tsx';
import { ErrorBoundary } from '@/components/ErrorBoundary.tsx';
@@ -21,7 +21,7 @@ const ErrorBoundaryError: FC<{ error: unknown }> = ({ error }) => (
);
const Inner: FC = () => {
- const debug = useLaunchParams().startParam === 'debug';
+ const debug = WebApp.initDataUnsafe.start_param === 'debug';
const manifestUrl = useMemo(() => {
return new URL('tonconnect-manifest.json', window.location.href).toString();
}, []);
@@ -35,9 +35,7 @@ const Inner: FC = () => {
return (
-
-
-
+
);
};
diff --git a/src/index.tsx b/src/index.tsx
index f2fff80..877ee00 100644
--- a/src/index.tsx
+++ b/src/index.tsx
@@ -1,11 +1,8 @@
import ReactDOM from 'react-dom/client';
+import '@twa-dev/sdk';
import { Root } from '@/components/Root';
-// Uncomment this import in case, you would like to develop the application even outside
-// the Telegram application, just in your browser.
-import './mockEnv.ts';
-
import '@telegram-apps/telegram-ui/dist/styles.css';
import './index.css';
diff --git a/src/mockEnv.ts b/src/mockEnv.ts
deleted file mode 100644
index 9fcca87..0000000
--- a/src/mockEnv.ts
+++ /dev/null
@@ -1,68 +0,0 @@
-import { mockTelegramEnv, parseInitData, retrieveLaunchParams } from '@tma.js/sdk-react';
-
-// It is important, to mock the environment only for development purposes. When building the
-// application, import.meta.env.DEV will become false, and the code inside will be tree-shaken,
-// so you will not see it in your final bundle.
-if (import.meta.env.DEV) {
- let shouldMock: boolean;
-
- // Try to extract launch parameters to check if the current environment is Telegram-based.
- try {
- // If we are able to extract launch parameters, it means that we are already in the
- // Telegram environment. So, there is no need to mock it.
- retrieveLaunchParams();
-
- // We could previously mock the environment. In case we did, we should do it again. The reason
- // is the page could be reloaded, and we should apply mock again, because mocking also
- // enables modifying the window object.
- shouldMock = !!sessionStorage.getItem('____mocked');
- } catch (e) {
- shouldMock = true;
- }
-
- if (shouldMock) {
- const initDataRaw = new URLSearchParams([
- ['user', JSON.stringify({
- id: 99281932,
- first_name: 'Andrew',
- last_name: 'Rogue',
- username: 'rogue',
- language_code: 'en',
- is_premium: true,
- allows_write_to_pm: true,
- })],
- ['hash', '89d6079ad6762351f38c6dbbc41bb53048019256a9443988af7a48bcad16ba31'],
- ['auth_date', '1716922846'],
- ['start_param', 'debug'],
- ['chat_type', 'sender'],
- ['chat_instance', '8428209589180549439'],
- ]).toString();
-
- mockTelegramEnv({
- themeParams: {
- accentTextColor: '#6ab2f2',
- bgColor: '#17212b',
- buttonColor: '#5288c1',
- buttonTextColor: '#ffffff',
- destructiveTextColor: '#ec3942',
- headerBgColor: '#17212b',
- hintColor: '#708499',
- linkColor: '#6ab3f3',
- secondaryBgColor: '#232e3c',
- sectionBgColor: '#17212b',
- sectionHeaderTextColor: '#6ab3f3',
- subtitleTextColor: '#708499',
- textColor: '#f5f5f5',
- },
- initData: parseInitData(initDataRaw),
- initDataRaw,
- version: '7.2',
- platform: 'tdesktop',
- });
- sessionStorage.setItem('____mocked', '1');
-
- console.info(
- 'As long as the current environment was not considered as the Telegram-based one, it was mocked. Take a note, that you should not do it in production and current behavior is only specific to the development process. Environment mocking is also applied only in development mode. So, after building the application, you will not see this behavior and related warning, leading to crashing the application outside Telegram.',
- );
- }
-}
\ No newline at end of file
diff --git a/src/pages/InitDataPage/InitDataPage.tsx b/src/pages/InitDataPage/InitDataPage.tsx
index 2d985e3..3904e30 100644
--- a/src/pages/InitDataPage/InitDataPage.tsx
+++ b/src/pages/InitDataPage/InitDataPage.tsx
@@ -1,29 +1,36 @@
import { type FC, useMemo } from 'react';
-import { useInitData, useLaunchParams, type User } from '@tma.js/sdk-react';
import { List, Placeholder } from '@telegram-apps/telegram-ui';
+import WebApp from '@twa-dev/sdk';
+import type { WebAppUser } from '@twa-dev/types';
import { DisplayData, type DisplayDataRow } from '@/components/DisplayData/DisplayData.tsx';
import './InitDataPage.css';
-function getUserRows(user: User): DisplayDataRow[] {
+// TODO: @twa-dev/sdk is outdated, as well as @twa-dev/types.
+interface ExactWebAppUser extends WebAppUser {
+ allows_write_to_pm?: boolean;
+ added_to_attachment_menu?: boolean;
+}
+
+function getUserRows(user: ExactWebAppUser): DisplayDataRow[] {
return [
{ title: 'id', value: user.id.toString() },
{ title: 'username', value: user.username },
- { title: 'photo_url', value: user.photoUrl },
- { title: 'last_name', value: user.lastName },
- { title: 'first_name', value: user.firstName },
- { title: 'is_bot', value: user.isBot },
- { title: 'is_premium', value: user.isPremium },
- { title: 'language_code', value: user.languageCode },
- { title: 'allows_to_write_to_pm', value: user.allowsWriteToPm },
- { title: 'added_to_attachment_menu', value: user.addedToAttachmentMenu },
+ { title: 'photo_url', value: user.photo_url },
+ { title: 'last_name', value: user.last_name },
+ { title: 'first_name', value: user.first_name },
+ { title: 'is_bot', value: user.is_bot },
+ { title: 'is_premium', value: user.is_premium },
+ { title: 'language_code', value: user.language_code },
+ { title: 'allows_to_write_to_pm', value: user.allows_write_to_pm },
+ { title: 'added_to_attachment_menu', value: user.added_to_attachment_menu },
];
}
export const InitDataPage: FC = () => {
- const initDataRaw = useLaunchParams().initDataRaw;
- const initData = useInitData();
+ const initDataRaw = WebApp.initData;
+ const initData = WebApp.initDataUnsafe;
const initDataRows = useMemo(() => {
if (!initData || !initDataRaw) {
@@ -31,25 +38,23 @@ export const InitDataPage: FC = () => {
}
const {
hash,
- queryId,
- chatType,
- chatInstance,
- authDate,
- startParam,
- canSendAfter,
- canSendAfterDate,
+ start_param,
+ chat_instance,
+ chat_type,
+ auth_date,
+ can_send_after,
+ query_id,
} = initData;
return [
{ title: 'raw', value: initDataRaw },
- { title: 'auth_date', value: authDate.toLocaleString() },
- { title: 'auth_date (raw)', value: authDate.getTime() / 1000 },
+ { title: 'auth_date', value: new Date(auth_date * 1000).toLocaleString() },
+ { title: 'auth_date (raw)', value: auth_date },
{ title: 'hash', value: hash },
- { title: 'can_send_after', value: canSendAfterDate?.toISOString() },
- { title: 'can_send_after (raw)', value: canSendAfter },
- { title: 'query_id', value: queryId },
- { title: 'start_param', value: startParam },
- { title: 'chat_type', value: chatType },
- { title: 'chat_instance', value: chatInstance },
+ { title: 'can_send_after', value: can_send_after },
+ { title: 'query_id', value: query_id },
+ { title: 'start_param', value: start_param },
+ { title: 'chat_type', value: chat_type },
+ { title: 'chat_instance', value: chat_instance },
];
}, [initData, initDataRaw]);
@@ -65,14 +70,14 @@ export const InitDataPage: FC = () => {
if (!initData?.chat) {
return;
}
- const { id, title, type, username, photoUrl } = initData.chat;
+ const { id, title, type, username, photo_url } = initData.chat;
return [
{ title: 'id', value: id.toString() },
{ title: 'title', value: title },
{ title: 'type', value: type },
{ title: 'username', value: username },
- { title: 'photo_url', value: photoUrl },
+ { title: 'photo_url', value: photo_url },
];
}, [initData]);
@@ -88,7 +93,7 @@ export const InitDataPage: FC = () => {
style={{ display: 'block', width: '144px', height: '144px' }}
/>
- )
+ );
}
return (
@@ -97,5 +102,5 @@ export const InitDataPage: FC = () => {
{receiverRows && }
{chatRows && }
- )
+ );
};
diff --git a/src/pages/LaunchParamsPage/LaunchParamsPage.tsx b/src/pages/LaunchParamsPage/LaunchParamsPage.tsx
index c7431da..728a6b3 100644
--- a/src/pages/LaunchParamsPage/LaunchParamsPage.tsx
+++ b/src/pages/LaunchParamsPage/LaunchParamsPage.tsx
@@ -1,21 +1,17 @@
-import { useLaunchParams } from '@tma.js/sdk-react';
import { List } from '@telegram-apps/telegram-ui';
import type { FC } from 'react';
import { DisplayData } from '@/components/DisplayData/DisplayData.tsx';
+import WebApp from '@twa-dev/sdk';
export const LaunchParamsPage: FC = () => {
- const lp = useLaunchParams();
-
return (
{
const wallet = useTonWallet();
- const utils = useUtils();
if (!wallet) {
return (
@@ -60,7 +59,7 @@ export const TONConnectPage: FC = () => {
subtitle={wallet.appName}
onClick={(e) => {
e.preventDefault();
- utils.openLink(wallet.aboutUrl);
+ WebApp.openLink(wallet.aboutUrl);
}}
>
{wallet.name}
diff --git a/src/pages/ThemeParamsPage/ThemeParamsPage.tsx b/src/pages/ThemeParamsPage/ThemeParamsPage.tsx
index ce38539..25859c9 100644
--- a/src/pages/ThemeParamsPage/ThemeParamsPage.tsx
+++ b/src/pages/ThemeParamsPage/ThemeParamsPage.tsx
@@ -1,24 +1,17 @@
-import { useThemeParams } from '@tma.js/sdk-react';
+import WebApp from '@twa-dev/sdk';
import type { FC } from 'react';
import { List } from '@telegram-apps/telegram-ui';
import { DisplayData } from '@/components/DisplayData/DisplayData.tsx';
export const ThemeParamsPage: FC = () => {
- const themeParams = useThemeParams();
-
return (
({
- title: title
- .replace(/[A-Z]/g, (m) => `_${m.toLowerCase()}`)
- .replace(/background/, 'bg'),
- value,
- }))
+ .entries(WebApp.themeParams)
+ .map(([title, value]) => ({ title, value }))
}
/>