diff --git a/apps/frontend/app/mini-apps/bot-create/content.tsx b/apps/frontend/app/mini-apps/bot-create/content.tsx index a781419..f9d1e3b 100644 --- a/apps/frontend/app/mini-apps/bot-create/content.tsx +++ b/apps/frontend/app/mini-apps/bot-create/content.tsx @@ -2,21 +2,20 @@ import { useMemo, useState } from 'react'; import { Check, Copy, Loader2, Bot } from 'lucide-react'; -import { useAuth } from '@/components/id/auth-provider'; import { useToast } from '@/components/id/toast-provider'; +import { usePublicSettings } from '@/components/id/public-settings-provider'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; import { createManagedBot, getApiErrorMessage } from '@/lib/api'; -import { useEmbeddedAuthFallback } from '@/lib/embedded-auth-bridge'; +import { useMiniAppAuth } from '@/hooks/use-mini-app-auth'; import { cn } from '@/lib/utils'; type Step = 'name' | 'username' | 'done'; export function BotCreateMiniAppContent() { - const { token, isPinLocked, isLoading } = useAuth(); - const embeddedToken = useEmbeddedAuthFallback(); + const { projectName } = usePublicSettings(); + const { effectiveToken, canUse, authReady, waitingForBridge, isLoading } = useMiniAppAuth(); const { showToast } = useToast(); - const effectiveToken = token ?? embeddedToken; const [step, setStep] = useState('name'); const [name, setName] = useState(''); @@ -24,11 +23,11 @@ export function BotCreateMiniAppContent() { const [creating, setCreating] = useState(false); const [createdBot, setCreatedBot] = useState<{ username: string; token: string; botId: string } | null>(null); - const canUse = Boolean(effectiveToken && !isPinLocked); + const canUseSession = canUse; const usernamePreview = useMemo(() => `${username.replace(/_bot$/i, '').trim()}_bot`, [username]); async function handleCreate() { - if (!canUse || !effectiveToken) return; + if (!canUseSession || !effectiveToken) return; const trimmedName = name.trim(); const trimmedUsername = username.replace(/_bot$/i, '').trim(); if (!trimmedName) { @@ -64,7 +63,7 @@ export function BotCreateMiniAppContent() { showToast('Токен скопирован'); } - if (isLoading && !effectiveToken) { + if ((isLoading || waitingForBridge || !authReady) && !effectiveToken) { return (
@@ -73,10 +72,10 @@ export function BotCreateMiniAppContent() { ); } - if (!canUse) { + if (!canUseSession) { return (
- Войдите в Lendry ID, чтобы создать бота + Войдите в {projectName}, чтобы создать бота
); } diff --git a/apps/frontend/app/mini-apps/bot-manage/content.tsx b/apps/frontend/app/mini-apps/bot-manage/content.tsx index f9e83bd..88c3759 100644 --- a/apps/frontend/app/mini-apps/bot-manage/content.tsx +++ b/apps/frontend/app/mini-apps/bot-manage/content.tsx @@ -3,7 +3,7 @@ import { useCallback, useEffect, useMemo, useState } from 'react'; import { useSearchParams } from 'next/navigation'; import { Copy, ImageIcon, LayoutGrid, Loader2, RefreshCw, TextQuote, UserCircle2 } from 'lucide-react'; -import { useAuth } from '@/components/id/auth-provider'; +import { usePublicSettings } from '@/components/id/public-settings-provider'; import { useToast } from '@/components/id/toast-provider'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; @@ -14,7 +14,7 @@ import { updateManagedBot, updateManagedBotProfile } from '@/lib/api'; -import { useEmbeddedAuthFallback } from '@/lib/embedded-auth-bridge'; +import { useMiniAppAuth } from '@/hooks/use-mini-app-auth'; import { parseComposerMenuButtonJson } from '@/lib/bot-menu-button'; function FieldTextarea({ @@ -78,9 +78,8 @@ function SectionCard({ export function BotManageMiniAppContent() { const searchParams = useSearchParams(); const botId = searchParams.get('botId') ?? ''; - const { token, isPinLocked, isLoading } = useAuth(); - const embeddedToken = useEmbeddedAuthFallback(); - const effectiveToken = token ?? embeddedToken; + const { projectName } = usePublicSettings(); + const { effectiveToken, canUse: canUseSession, authReady, waitingForBridge, isLoading } = useMiniAppAuth(); const { showToast } = useToast(); const [loading, setLoading] = useState(true); @@ -97,7 +96,7 @@ export function BotManageMiniAppContent() { const [tokenPrefix, setTokenPrefix] = useState(''); const [newToken, setNewToken] = useState(null); - const canUse = Boolean(effectiveToken && !isPinLocked && botId); + const canUse = Boolean(canUseSession && botId); const loadBot = useCallback(async () => { if (!canUse || !effectiveToken) return; @@ -219,10 +218,19 @@ export function BotManageMiniAppContent() { ); } + if ((waitingForBridge || !authReady) && !effectiveToken) { + return ( +
+ + Загрузка... +
+ ); + } + if (!canUse) { return (
- Войдите в Lendry ID, чтобы управлять ботом + Войдите в {projectName}, чтобы управлять ботом
); } diff --git a/apps/frontend/components/family/family-bot-chat.tsx b/apps/frontend/components/family/family-bot-chat.tsx index 74b7408..b844e35 100644 --- a/apps/frontend/components/family/family-bot-chat.tsx +++ b/apps/frontend/components/family/family-bot-chat.tsx @@ -13,7 +13,9 @@ import { fetchBotChatMessages, sendBotMessage } from '@/lib/api'; -import { postEmbeddedAuthToIframe } from '@/lib/embedded-auth-bridge'; +import { postEmbeddedAuthToIframe, EMBEDDED_AUTH_REQUEST } from '@/lib/embedded-auth-bridge'; +import { AUTH_REFRESH_KEY, AUTH_SESSION_KEY, AUTH_TOKEN_KEY } from '@/lib/api'; +import { normalizeMiniAppUrl, resolveMiniAppTargetOrigin } from '@/lib/mini-app-url'; import { extractMessageReplyMarkup, serializeInlineKeyboardMarkup } from '@/lib/bot-reply-markup'; import { resolveComposerMenuButton, type ComposerMenuButton } from '@/lib/bot-menu-button'; import { cn } from '@/lib/utils'; @@ -305,27 +307,44 @@ export function BotMiniAppSheet({ onClose }: BotMiniAppSheetProps) { const iframeRef = useRef(null); + const normalizedUrl = url ? normalizeMiniAppUrl(url) : null; + const resolvedAuthToken = + authToken ?? (typeof window !== 'undefined' ? window.localStorage.getItem(AUTH_TOKEN_KEY) : null); const pushAuthToIframe = useCallback(() => { - if (!iframeRef.current || !authToken) return; + if (!iframeRef.current || !resolvedAuthToken) return; postEmbeddedAuthToIframe(iframeRef.current, { - token: authToken, - refreshToken: authRefreshToken, - sessionId: authSessionId + token: resolvedAuthToken, + refreshToken: authRefreshToken ?? window.localStorage.getItem(AUTH_REFRESH_KEY), + sessionId: authSessionId ?? window.localStorage.getItem(AUTH_SESSION_KEY) }); - }, [authRefreshToken, authSessionId, authToken]); + }, [authRefreshToken, authSessionId, resolvedAuthToken]); useEffect(() => { function handleMessage(event: MessageEvent) { - if (event.origin !== window.location.origin) return; - if (event.data?.type !== 'lendry-id-embedded-auth-request') return; + const iframeOrigin = iframeRef.current + ? resolveMiniAppTargetOrigin(iframeRef.current.getAttribute('src') || iframeRef.current.src, window.location.origin) + : window.location.origin; + if (event.origin !== iframeOrigin && event.origin !== window.location.origin) return; + if (event.data?.type !== EMBEDDED_AUTH_REQUEST) return; pushAuthToIframe(); } window.addEventListener('message', handleMessage); return () => window.removeEventListener('message', handleMessage); }, [pushAuthToIframe]); - if (!url) return null; + useEffect(() => { + if (!normalizedUrl || !resolvedAuthToken) return; + pushAuthToIframe(); + const interval = window.setInterval(pushAuthToIframe, 350); + const timeout = window.setTimeout(() => window.clearInterval(interval), 3500); + return () => { + window.clearInterval(interval); + window.clearTimeout(timeout); + }; + }, [normalizedUrl, pushAuthToIframe, resolvedAuthToken]); + + if (!normalizedUrl) return null; return (
@@ -337,7 +356,7 @@ export function BotMiniAppSheet({