2026-02-04 05:03:06 +03:00
|
|
|
|
// hooks/useTelegramWebApp.ts
|
|
|
|
|
|
// Hook для интеграции с Telegram WebApp API
|
|
|
|
|
|
|
|
|
|
|
|
import { useEffect, useState } from 'react';
|
|
|
|
|
|
|
|
|
|
|
|
declare global {
|
|
|
|
|
|
interface Window {
|
|
|
|
|
|
Telegram?: {
|
|
|
|
|
|
WebApp: {
|
|
|
|
|
|
ready: () => void;
|
|
|
|
|
|
expand: () => void;
|
|
|
|
|
|
close: () => void;
|
|
|
|
|
|
isExpanded: boolean;
|
|
|
|
|
|
viewportHeight: number;
|
|
|
|
|
|
viewportStableHeight: number;
|
|
|
|
|
|
headerColor: string;
|
|
|
|
|
|
backgroundColor: string;
|
|
|
|
|
|
isClosingConfirmationEnabled: boolean;
|
|
|
|
|
|
platform: string;
|
|
|
|
|
|
version: string;
|
|
|
|
|
|
|
|
|
|
|
|
MainButton: {
|
|
|
|
|
|
text: string;
|
|
|
|
|
|
color: string;
|
|
|
|
|
|
textColor: string;
|
|
|
|
|
|
isVisible: boolean;
|
|
|
|
|
|
isActive: boolean;
|
|
|
|
|
|
isProgressVisible: boolean;
|
|
|
|
|
|
setText: (text: string) => void;
|
|
|
|
|
|
onClick: (callback: () => void) => void;
|
|
|
|
|
|
offClick: (callback: () => void) => void;
|
|
|
|
|
|
show: () => void;
|
|
|
|
|
|
hide: () => void;
|
|
|
|
|
|
enable: () => void;
|
|
|
|
|
|
disable: () => void;
|
|
|
|
|
|
showProgress: (leaveActive?: boolean) => void;
|
|
|
|
|
|
hideProgress: () => void;
|
|
|
|
|
|
setParams: (params: { text?: string; color?: string; text_color?: string; is_active?: boolean; is_visible?: boolean }) => void;
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
BackButton: {
|
|
|
|
|
|
isVisible: boolean;
|
|
|
|
|
|
onClick: (callback: () => void) => void;
|
|
|
|
|
|
offClick: (callback: () => void) => void;
|
|
|
|
|
|
show: () => void;
|
|
|
|
|
|
hide: () => void;
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
HapticFeedback: {
|
|
|
|
|
|
impactOccurred: (style: 'light' | 'medium' | 'heavy' | 'rigid' | 'soft') => void;
|
|
|
|
|
|
notificationOccurred: (type: 'error' | 'success' | 'warning') => void;
|
|
|
|
|
|
selectionChanged: () => void;
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
setHeaderColor: (color: string) => void;
|
|
|
|
|
|
setBackgroundColor: (color: string) => void;
|
|
|
|
|
|
enableClosingConfirmation: () => void;
|
|
|
|
|
|
disableClosingConfirmation: () => void;
|
|
|
|
|
|
|
|
|
|
|
|
showPopup: (params: {
|
|
|
|
|
|
title?: string;
|
|
|
|
|
|
message: string;
|
|
|
|
|
|
buttons: Array<{ id?: string; type: string; text?: string }>;
|
|
|
|
|
|
}, callback?: (buttonId: string) => void) => void;
|
|
|
|
|
|
|
|
|
|
|
|
showAlert: (message: string, callback?: () => void) => void;
|
|
|
|
|
|
showConfirm: (message: string, callback?: (confirmed: boolean) => void) => void;
|
|
|
|
|
|
|
|
|
|
|
|
openLink: (url: string, options?: { try_instant_view?: boolean }) => void;
|
|
|
|
|
|
openTelegramLink: (url: string) => void;
|
|
|
|
|
|
openInvoice: (url: string, callback?: (status: string) => void) => void;
|
|
|
|
|
|
|
|
|
|
|
|
shareToStory: (media_url: string, params?: {
|
|
|
|
|
|
text?: string;
|
|
|
|
|
|
widget_link?: {
|
|
|
|
|
|
url: string;
|
|
|
|
|
|
name?: string;
|
|
|
|
|
|
};
|
|
|
|
|
|
}) => void;
|
|
|
|
|
|
|
|
|
|
|
|
switchInlineQuery: (query: string, choose_chat_types?: string[]) => void;
|
|
|
|
|
|
|
|
|
|
|
|
sendData: (data: string) => void;
|
|
|
|
|
|
|
|
|
|
|
|
initData: string;
|
|
|
|
|
|
initDataUnsafe: {
|
|
|
|
|
|
query_id?: string;
|
|
|
|
|
|
user?: {
|
|
|
|
|
|
id: number;
|
|
|
|
|
|
first_name: string;
|
|
|
|
|
|
last_name?: string;
|
|
|
|
|
|
username?: string;
|
|
|
|
|
|
language_code?: string;
|
|
|
|
|
|
is_premium?: boolean;
|
|
|
|
|
|
photo_url?: string;
|
|
|
|
|
|
};
|
|
|
|
|
|
receiver?: {
|
|
|
|
|
|
id: number;
|
|
|
|
|
|
first_name: string;
|
|
|
|
|
|
last_name?: string;
|
|
|
|
|
|
username?: string;
|
|
|
|
|
|
language_code?: string;
|
|
|
|
|
|
is_premium?: boolean;
|
|
|
|
|
|
};
|
|
|
|
|
|
chat?: {
|
|
|
|
|
|
id: number;
|
|
|
|
|
|
type: string;
|
|
|
|
|
|
title: string;
|
|
|
|
|
|
username?: string;
|
|
|
|
|
|
photo_url?: string;
|
|
|
|
|
|
};
|
|
|
|
|
|
chat_type?: string;
|
|
|
|
|
|
chat_instance?: string;
|
|
|
|
|
|
start_param?: string;
|
|
|
|
|
|
can_send_after?: number;
|
|
|
|
|
|
auth_date: number;
|
|
|
|
|
|
hash: string;
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
themeParams: {
|
|
|
|
|
|
bg_color?: string;
|
|
|
|
|
|
text_color?: string;
|
|
|
|
|
|
hint_color?: string;
|
|
|
|
|
|
link_color?: string;
|
|
|
|
|
|
button_color?: string;
|
|
|
|
|
|
button_text_color?: string;
|
|
|
|
|
|
secondary_bg_color?: string;
|
|
|
|
|
|
header_bg_color?: string;
|
|
|
|
|
|
accent_text_color?: string;
|
|
|
|
|
|
section_bg_color?: string;
|
|
|
|
|
|
section_header_text_color?: string;
|
|
|
|
|
|
subtitle_text_color?: string;
|
|
|
|
|
|
destructive_text_color?: string;
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
colorScheme: 'light' | 'dark';
|
|
|
|
|
|
|
|
|
|
|
|
isVersionAtLeast: (version: string) => boolean;
|
|
|
|
|
|
|
|
|
|
|
|
onEvent: (eventType: string, eventHandler: () => void) => void;
|
|
|
|
|
|
offEvent: (eventType: string, eventHandler: () => void) => void;
|
|
|
|
|
|
};
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
export function useTelegramWebApp() {
|
2026-02-08 18:59:02 +03:00
|
|
|
|
const [webApp, setWebApp] = useState<any>(null);
|
2026-02-04 05:03:06 +03:00
|
|
|
|
const [isReady, setIsReady] = useState(false);
|
|
|
|
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
|
// Проверяем доступность Telegram WebApp только на клиенте
|
|
|
|
|
|
if (typeof window === 'undefined') return;
|
|
|
|
|
|
|
|
|
|
|
|
const tg = window.Telegram?.WebApp;
|
|
|
|
|
|
if (!tg) {
|
|
|
|
|
|
console.warn('[TelegramWebApp] Not running in Telegram');
|
|
|
|
|
|
setIsReady(true); // Всё равно помечаем как ready для dev
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
console.log('[TelegramWebApp] Initializing...', {
|
|
|
|
|
|
platform: tg.platform,
|
|
|
|
|
|
version: tg.version,
|
|
|
|
|
|
colorScheme: tg.colorScheme,
|
|
|
|
|
|
user: tg.initDataUnsafe.user,
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
// Инициализация Telegram WebApp
|
|
|
|
|
|
tg.ready();
|
|
|
|
|
|
tg.expand();
|
|
|
|
|
|
|
|
|
|
|
|
// Настройка темы
|
|
|
|
|
|
tg.setHeaderColor('bg_color');
|
|
|
|
|
|
tg.setBackgroundColor('#18222d');
|
|
|
|
|
|
|
|
|
|
|
|
// Отключить подтверждение закрытия (можно включить при необходимости)
|
|
|
|
|
|
tg.disableClosingConfirmation();
|
|
|
|
|
|
|
|
|
|
|
|
setWebApp(tg);
|
|
|
|
|
|
setIsReady(true);
|
|
|
|
|
|
|
|
|
|
|
|
// Cleanup
|
|
|
|
|
|
return () => {
|
|
|
|
|
|
tg.MainButton.hide();
|
|
|
|
|
|
tg.BackButton.hide();
|
|
|
|
|
|
};
|
|
|
|
|
|
}, []);
|
|
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
|
webApp,
|
|
|
|
|
|
isReady,
|
|
|
|
|
|
isTelegram: !!webApp,
|
|
|
|
|
|
user: webApp?.initDataUnsafe.user,
|
|
|
|
|
|
platform: webApp?.platform,
|
|
|
|
|
|
colorScheme: webApp?.colorScheme,
|
|
|
|
|
|
themeParams: webApp?.themeParams,
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Helper функции для удобства
|
|
|
|
|
|
|
|
|
|
|
|
export function useTelegramMainButton(
|
|
|
|
|
|
text: string,
|
|
|
|
|
|
onClick: () => void,
|
|
|
|
|
|
options?: {
|
|
|
|
|
|
color?: string;
|
|
|
|
|
|
textColor?: string;
|
|
|
|
|
|
enabled?: boolean;
|
|
|
|
|
|
}
|
|
|
|
|
|
) {
|
|
|
|
|
|
const { webApp } = useTelegramWebApp();
|
|
|
|
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
|
if (!webApp) return;
|
|
|
|
|
|
|
|
|
|
|
|
webApp.MainButton.setText(text);
|
|
|
|
|
|
webApp.MainButton.onClick(onClick);
|
|
|
|
|
|
|
|
|
|
|
|
if (options?.color) {
|
|
|
|
|
|
webApp.MainButton.color = options.color;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (options?.textColor) {
|
|
|
|
|
|
webApp.MainButton.textColor = options.textColor;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (options?.enabled !== undefined) {
|
|
|
|
|
|
if (options.enabled) {
|
|
|
|
|
|
webApp.MainButton.enable();
|
|
|
|
|
|
} else {
|
|
|
|
|
|
webApp.MainButton.disable();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
webApp.MainButton.show();
|
|
|
|
|
|
|
|
|
|
|
|
return () => {
|
|
|
|
|
|
webApp.MainButton.offClick(onClick);
|
|
|
|
|
|
webApp.MainButton.hide();
|
|
|
|
|
|
};
|
|
|
|
|
|
}, [webApp, text, onClick, options]);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
export function useTelegramBackButton(onClick: () => void) {
|
|
|
|
|
|
const { webApp } = useTelegramWebApp();
|
|
|
|
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
|
if (!webApp) return;
|
|
|
|
|
|
|
|
|
|
|
|
webApp.BackButton.onClick(onClick);
|
|
|
|
|
|
webApp.BackButton.show();
|
|
|
|
|
|
|
|
|
|
|
|
return () => {
|
|
|
|
|
|
webApp.BackButton.offClick(onClick);
|
|
|
|
|
|
webApp.BackButton.hide();
|
|
|
|
|
|
};
|
|
|
|
|
|
}, [webApp, onClick]);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
export function useTelegramHaptic() {
|
|
|
|
|
|
const { webApp } = useTelegramWebApp();
|
|
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
|
impact: (style: 'light' | 'medium' | 'heavy' | 'rigid' | 'soft' = 'medium') => {
|
|
|
|
|
|
webApp?.HapticFeedback.impactOccurred(style);
|
|
|
|
|
|
},
|
|
|
|
|
|
notification: (type: 'error' | 'success' | 'warning') => {
|
|
|
|
|
|
webApp?.HapticFeedback.notificationOccurred(type);
|
|
|
|
|
|
},
|
|
|
|
|
|
selection: () => {
|
|
|
|
|
|
webApp?.HapticFeedback.selectionChanged();
|
|
|
|
|
|
},
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|