feat: update tg integration
All checks were successful
Deploy to VPS (dist) / deploy (push) Successful in 1m35s

This commit is contained in:
Hewston Fox
2026-03-18 01:24:50 +02:00
parent 9f0ff8c4e5
commit 16b85048f9
5 changed files with 251 additions and 119 deletions

View File

@@ -1,22 +1,87 @@
import * as tg from "@telegram-apps/sdk-react";
import * as tg from "@tma.js/sdk-react";
export const STORAGE_KEYS = {
authToken: "authToken",
} as const;
export type StorageKey = (typeof STORAGE_KEYS)[keyof typeof STORAGE_KEYS];
const MOCKED_START_PARAM = `debug+${Math.random().toString(36).substring(2, 15)}`;
tg.mockTelegramEnv({
launchParams: {
tgWebAppData: new URLSearchParams({
user: JSON.stringify({
id: 1,
first_name: "Pavel",
is_bot: false,
last_name: "Durov",
username: "durov",
language_code: "en",
is_premium: true,
photo_url:
"https://media4.giphy.com/media/v1.Y2lkPTZjMDliOTUyeXF1MzYyY2pwMjR2YWFhNDhqdXBsc216MWo2aW9pczNnNXM2ZmZmbCZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/xUPGcHc4I3wICqp8bu/giphy.gif",
added_to_attachment_menu: false,
allows_write_to_pm: true,
} satisfies tg.User),
hash: "",
signature: "",
auth_date: Date.now().toString(),
}),
tgWebAppStartParam: MOCKED_START_PARAM,
tgWebAppThemeParams: {},
tgWebAppVersion: "8",
tgWebAppPlatform: "android",
},
});
type WithChecks<Result> = {
ifAvailable: (...args: any[]) => { ok: true; data: Result } | { ok: false };
};
const isPromise = <T>(value: T | Promise<T>): value is Promise<T> =>
value && typeof value === "object" && "then" in value && typeof value.then === "function";
const promisify = <T>(value: T | Promise<T>): Promise<T> =>
isPromise(value) ? value : Promise.resolve(value);
const isTMA = () => tg.retrieveLaunchParams().tgWebAppStartParam !== MOCKED_START_PARAM;
const fallbackImplementation = <
T extends ((...args: any) => any) & WithChecks<any>,
Async extends boolean,
Params extends Parameters<T>,
Result extends T extends WithChecks<infer Result> ? Result : never,
F extends (...args: Params) => Async extends true ? Result | Awaited<Result> : Result,
>(
async: Async,
args: Params,
cb: T,
onErr: F,
): Async extends true ? Promise<Result> : Awaited<Result> => {
if (isTMA()) {
const res = cb.ifAvailable.apply(null, args);
const returnValue = (res.ok ? res.data : onErr.apply(null, args)) as Result;
if (!async) return returnValue as Async extends true ? Promise<Result> : Awaited<Result>;
return promisify(returnValue).catch(() => onErr.apply(null, args)) as Async extends true
? Promise<Result>
: Awaited<Result>;
} else {
const result = onErr.apply(null, args);
return (async ? promisify(result) : result) as Async extends true
? Promise<Result>
: Awaited<Result>;
}
};
export default {
init: () => {
try {
tg.setDebug(import.meta.env.DEV);
tg.init({ acceptCustomStyles: true });
tg.requestFullscreen();
tg.disableVerticalSwipes();
tg.expandViewport();
tg.setMiniAppHeaderColor("#000000");
} catch {
console.warn("Telegram SDK not available in browser.");
}
tg.setDebug(import.meta.env.DEV);
tg.init({ acceptCustomStyles: true });
tg.viewport.requestFullscreen.ifAvailable();
tg.swipeBehavior.disableVertical.ifAvailable();
tg.viewport.expand.ifAvailable();
tg.miniApp.setHeaderColor.ifAvailable("#000000");
console.log(isTMA() ? "Telegram Mini App initialized" : "TMA Debug mode in Web initialized");
},
openLink(url: string | URL, options?: tg.OpenLinkOptions) {
tg.openLink.ifAvailable(url, options);
@@ -28,25 +93,32 @@ export default {
},
initData: tg.initData,
storage: {
clear() {
localStorage.clear();
tg.cloudStorage.clear.ifAvailable();
async clear() {
return fallbackImplementation(true, [], tg.cloudStorage.clear, localStorage.clear);
},
getItem(key: StorageKey, options?: tg.InvokeCustomMethodOptions) {
return tg.cloudStorage
.getItem(key, options)
.catch(
() =>
localStorage.getItem(key) ?? tg.AbortablePromise.reject(new Error("Item not found")),
);
async getItem(key: StorageKey, options?: tg.InvokeCustomMethodFpOptions) {
return fallbackImplementation(
true,
[key, options],
tg.cloudStorage.getItem,
(key) => localStorage.getItem(key) ?? "",
);
},
setItem(key: StorageKey, value: string, options?: tg.InvokeCustomMethodOptions) {
localStorage.setItem(key, value);
tg.cloudStorage.setItem.ifAvailable(key, value, options);
async setItem(key: StorageKey, value: string, options?: tg.InvokeCustomMethodFpOptions) {
return fallbackImplementation(
true,
[key, value, options],
tg.cloudStorage.setItem,
localStorage.setItem,
);
},
deleteItem(key: StorageKey, options?: tg.InvokeCustomMethodOptions) {
tg.cloudStorage.deleteItem.ifAvailable(key, options);
localStorage.removeItem(key);
async deleteItem(key: StorageKey, options?: tg.InvokeCustomMethodFpOptions) {
return fallbackImplementation(
true,
[key, options],
tg.cloudStorage.deleteItem,
localStorage.removeItem as typeof tg.cloudStorage.deleteItem,
);
},
},
};