add oauth app connected

This commit is contained in:
lendry
2026-06-26 10:49:34 +03:00
parent d5e6b58955
commit b81c0cedbb
18 changed files with 838 additions and 59 deletions

View File

@@ -1,6 +1,6 @@
'use client';
import { useCallback, useEffect, useState } from 'react';
import { useCallback, useEffect, useRef, useState } from 'react';
import { LayoutGrid, Loader2, Send, X } from 'lucide-react';
import { BotMessageKeyboard } from '@/components/chat/bot-message-keyboard';
import { BotChatMessageContextMenu } from '@/components/chat/bot-chat-message-context-menu';
@@ -13,6 +13,7 @@ import {
fetchBotChatMessages,
sendBotMessage
} from '@/lib/api';
import { postEmbeddedAuthToIframe } from '@/lib/embedded-auth-bridge';
import { extractMessageReplyMarkup, serializeInlineKeyboardMarkup } from '@/lib/bot-reply-markup';
import { resolveComposerMenuButton, type ComposerMenuButton } from '@/lib/bot-menu-button';
import { cn } from '@/lib/utils';
@@ -279,6 +280,7 @@ export function FamilyBotChat({
<BotMiniAppSheet
url={internalMiniAppUrl}
title={composerMenuButton?.text ?? 'Mini App'}
authToken={token}
onClose={() => setInternalMiniAppUrl(null)}
/>
</div>
@@ -288,10 +290,41 @@ export function FamilyBotChat({
interface BotMiniAppSheetProps {
url: string | null;
title?: string;
authToken?: string | null;
authRefreshToken?: string | null;
authSessionId?: string | null;
onClose: () => void;
}
export function BotMiniAppSheet({ url, title = 'Mini App', onClose }: BotMiniAppSheetProps) {
export function BotMiniAppSheet({
url,
title = 'Mini App',
authToken,
authRefreshToken,
authSessionId,
onClose
}: BotMiniAppSheetProps) {
const iframeRef = useRef<HTMLIFrameElement>(null);
const pushAuthToIframe = useCallback(() => {
if (!iframeRef.current || !authToken) return;
postEmbeddedAuthToIframe(iframeRef.current, {
token: authToken,
refreshToken: authRefreshToken,
sessionId: authSessionId
});
}, [authRefreshToken, authSessionId, authToken]);
useEffect(() => {
function handleMessage(event: MessageEvent) {
if (event.origin !== window.location.origin) return;
if (event.data?.type !== 'lendry-id-embedded-auth-request') return;
pushAuthToIframe();
}
window.addEventListener('message', handleMessage);
return () => window.removeEventListener('message', handleMessage);
}, [pushAuthToIframe]);
if (!url) return null;
return (
<div className="fixed inset-0 z-[70] flex items-end justify-center bg-black/40 p-4 sm:items-center">
@@ -302,7 +335,14 @@ export function BotMiniAppSheet({ url, title = 'Mini App', onClose }: BotMiniApp
<X className="h-4 w-4" />
</Button>
</div>
<iframe src={url} title={title} className="min-h-0 flex-1 border-0" allow="clipboard-read; clipboard-write" />
<iframe
ref={iframeRef}
src={url}
title={title}
className="min-h-0 flex-1 border-0"
allow="clipboard-read; clipboard-write"
onLoad={pushAuthToIframe}
/>
</div>
</div>
);

View File

@@ -364,9 +364,11 @@ export function FamilyGroupView({ groupId }: { groupId: string }) {
const loadMessages = useCallback(async (roomId: string) => {
if (!token || isPinLocked) return;
const room = rooms.find((item) => item.id === roomId);
if (room?.type === 'BOT') return;
const response = await fetchChatMessages(roomId, token);
setMessages((response.messages ?? []).filter((message) => !message.isDeleted));
}, [isPinLocked, token]);
}, [isPinLocked, rooms, token]);
const loadPresence = useCallback(async () => {
if (!token || isPinLocked) return;
@@ -473,7 +475,7 @@ export function FamilyGroupView({ groupId }: { groupId: string }) {
if (event.type === 'chat_message') {
if (payload?.message) {
appendMessage(payload.message as ChatMessage);
} else if (activeRoomId) {
} else if (activeRoomId && !activeRoomIsBot) {
void loadMessages(activeRoomId);
}
void loadGroup();
@@ -489,7 +491,7 @@ export function FamilyGroupView({ groupId }: { groupId: string }) {
} else {
setMessages((current) => current.map((item) => (item.id === updated.id ? updated : item)));
}
} else if (activeRoomId) {
} else if (activeRoomId && !activeRoomIsBot) {
void loadMessages(activeRoomId);
}
void loadGroup();
@@ -504,14 +506,14 @@ export function FamilyGroupView({ groupId }: { groupId: string }) {
if (editingMessageId === deleted.id) {
cancelEditMessage();
}
} else if (activeRoomId) {
} else if (activeRoomId && !activeRoomIsBot) {
void loadMessages(activeRoomId);
}
void loadGroup();
return;
}
if (event.type === 'chat_read_receipt' && activeRoomId) {
if (event.type === 'chat_read_receipt' && activeRoomId && !activeRoomIsBot) {
if (readReceiptTimerRef.current) clearTimeout(readReceiptTimerRef.current);
readReceiptTimerRef.current = setTimeout(() => void loadMessages(activeRoomId), 400);
return;
@@ -1854,7 +1856,12 @@ export function FamilyGroupView({ groupId }: { groupId: string }) {
}}
onConfirm={() => void confirmDeleteMessages()}
/>
<BotMiniAppSheet url={miniAppUrl} title={miniAppTitle} onClose={() => setMiniAppUrl(null)} />
<BotMiniAppSheet
url={miniAppUrl}
title={miniAppTitle}
authToken={token}
onClose={() => setMiniAppUrl(null)}
/>
<Dialog open={familyMembersOpen} onOpenChange={setFamilyMembersOpen}>
<DialogContent className="rounded-[28px] sm:max-w-[480px]">

View File

@@ -1252,7 +1252,12 @@ export function MiniFamilyChat() {
event.target.value = '';
}}
/>
<BotMiniAppSheet url={miniAppUrl} title={miniAppTitle} onClose={() => setMiniAppUrl(null)} />
<BotMiniAppSheet
url={miniAppUrl}
title={miniAppTitle}
authToken={token}
onClose={() => setMiniAppUrl(null)}
/>
</>
);
}

View File

@@ -47,14 +47,6 @@ export function Sidebar({ active }: { active: string }) {
<FamilySidebarPanel />
</div>
</div>
<div className="space-y-2 text-[11px] text-[#667085]">
{user ? <p className="truncate font-medium text-[#1f2430]">{user.displayName}</p> : null}
<button type="button" onClick={logout} className="flex items-center gap-2 rounded-lg px-2 py-1 text-[#1f2430] hover:bg-[#f4f5f8]">
<LogOut className="h-3.5 w-3.5" />
Выйти
</button>
<p>© 2026</p>
</div>
</aside>
);
}