global update and global fix

This commit is contained in:
lendry
2026-06-25 23:48:57 +03:00
parent 3880c68d59
commit b0ea87e898
121 changed files with 13759 additions and 663 deletions

View 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}
/>
);
}