Files
IdP/apps/frontend/components/chat/bot-message-keyboard.tsx
2026-06-25 23:48:57 +03:00

110 lines
3.3 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
'use client';
import { useCallback, useEffect, useRef, useState } from 'react';
import { BotInlineKeyboard } from '@/components/chat/bot-inline-keyboard';
import { useToast } from '@/components/id/toast-provider';
import { useRealtime } from '@/components/notifications/realtime-provider';
import { submitBotCallback } from '@/lib/api';
const CALLBACK_ANSWER_TIMEOUT_MS = 30_000;
interface BotMessageKeyboardProps {
botRef: string;
messageId: number;
markup: unknown;
token: string | null;
botId?: string;
onOpenMiniApp?: (url: string) => void;
}
export function BotMessageKeyboard({
botRef,
messageId,
markup,
token,
botId,
onOpenMiniApp
}: BotMessageKeyboardProps) {
const { showToast } = useToast();
const { subscribe } = useRealtime();
const [loadingButtonKey, setLoadingButtonKey] = useState<string | null>(null);
const pendingQueriesRef = useRef(new Map<string, ReturnType<typeof setTimeout>>());
const clearPendingQuery = useCallback((callbackQueryId: string) => {
const timer = pendingQueriesRef.current.get(callbackQueryId);
if (timer) {
clearTimeout(timer);
pendingQueriesRef.current.delete(callbackQueryId);
}
setLoadingButtonKey(null);
}, []);
useEffect(() => {
return () => {
pendingQueriesRef.current.forEach((timer) => clearTimeout(timer));
pendingQueriesRef.current.clear();
};
}, []);
useEffect(() => {
return subscribe((event) => {
if (event.type !== 'bot_callback_answer') return;
const payload = event.payload ?? {};
const callbackQueryId = typeof payload.callbackQueryId === 'string' ? payload.callbackQueryId : null;
if (!callbackQueryId || !pendingQueriesRef.current.has(callbackQueryId)) return;
const eventBotId = typeof payload.botId === 'string' ? payload.botId : null;
if (botId && eventBotId && eventBotId !== botId) return;
clearPendingQuery(callbackQueryId);
const text = typeof payload.text === 'string' ? payload.text.trim() : '';
const showAlert = Boolean(payload.showAlert);
const url = typeof payload.url === 'string' ? payload.url.trim() : '';
if (url && onOpenMiniApp) {
onOpenMiniApp(url);
}
if (text) {
if (showAlert) {
window.alert(text);
} else {
showToast(text);
}
}
});
}, [botId, clearPendingQuery, onOpenMiniApp, showToast, subscribe]);
async function handleCallbackClick(callbackData: string, buttonKey: string) {
if (!token) return;
setLoadingButtonKey(buttonKey);
try {
const response = await submitBotCallback(botRef, messageId, callbackData, token);
const callbackQueryId = response.callbackQueryId;
if (!callbackQueryId) {
setLoadingButtonKey(null);
return;
}
const timer = setTimeout(() => clearPendingQuery(callbackQueryId), CALLBACK_ANSWER_TIMEOUT_MS);
pendingQueriesRef.current.set(callbackQueryId, timer);
} catch (error) {
setLoadingButtonKey(null);
showToast(error instanceof Error ? error.message : 'Не удалось отправить действие');
}
}
return (
<BotInlineKeyboard
markup={markup}
messageId={messageId}
loadingButtonKey={loadingButtonKey}
onCallbackClick={handleCallbackClick}
onOpenMiniApp={onOpenMiniApp}
/>
);
}