global update and global fix
This commit is contained in:
109
apps/frontend/components/chat/bot-message-keyboard.tsx
Normal file
109
apps/frontend/components/chat/bot-message-keyboard.tsx
Normal file
@@ -0,0 +1,109 @@
|
||||
'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}
|
||||
/>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user