import { useQuery } from "@tanstack/react-query"; import { useRouter } from "expo-router"; import React, { useState } from "react"; import { ScrollView, Text, View } from "react-native"; import { useApp } from "../src/app-context"; import { AppHeader } from "../src/components/AppHeader"; import { ChatInputBar } from "../src/components/ChatInputBar"; import { ConsentModal } from "../src/components/ConsentModal"; import { HanLogo } from "../src/components/HanLogo"; import { PopularQuestionsList } from "../src/components/PopularQuestionsList"; import { QuickActions } from "../src/components/QuickActions"; import { ScreenShell } from "../src/components/ScreenShell"; import { clearPendingTextIntent, createPendingTextIntent, savePendingTextIntent, type PendingTextIntent, } from "../src/pending-intent"; import { dialogApi, publicApi, uploadAttachment } from "../src/services"; import type { Consents } from "../src/types"; import { ErrorNotice, Loading, styles } from "../src/ui"; export default function HomeScreen() { const { authStatus, authorize } = useApp(); const config = useQuery({ queryKey: ["public-config"], queryFn: publicApi.config }); const content = useQuery({ queryKey: ["public-content"], queryFn: publicApi.content }); const [consentOpen, setConsentOpen] = useState(false); const [pending, setPending] = useState(null); const [message, setMessage] = useState(""); const [sendError, setSendError] = useState(); const [sending, setSending] = useState(false); const router = useRouter(); const sendAuthenticated = async (intent: PendingTextIntent) => { setSending(true); setSendError(undefined); try { const dialog = await dialogApi.create(intent.dialogKey); await dialogApi.sendText(dialog.dialog_id, intent.text, intent.messageKey); setMessage(""); setPending(null); clearPendingTextIntent(); router.push(`/dialogs/${dialog.dialog_id}`); } catch (error) { setSendError(error); } finally { setSending(false); } }; const send = async (text: string) => { const normalized = text.trim(); if (!normalized) return; if (normalized.length > 4000) { setSendError(new Error("Сообщение слишком длинное. Максимум — 4000 символов.")); return; } const intent = createPendingTextIntent(normalized); if (authStatus !== "authenticated") { setPending(intent); savePendingTextIntent(intent); setConsentOpen(true); return; } await sendAuthenticated(intent); }; const chooseFile = () => { if (authStatus !== "authenticated") { setConsentOpen(true); return; } if (typeof document === "undefined") { setSendError(new Error("Выбор файла в этой сборке доступен только в web.")); return; } const input = document.createElement("input"); input.type = "file"; input.accept = "image/*,application/pdf"; input.onchange = () => { const file = input.files?.[0]; if (file) void sendFile(file); }; input.click(); }; const sendFile = async (file: File) => { const limits = config.data?.attachments; const max = (limits?.max_size_mb ?? 5) * 1024 * 1024; const allowed = limits?.allowed_mime_types ?? ["image/jpeg", "image/png", "image/webp", "application/pdf"]; if (file.size > max || !allowed.includes(file.type)) { setSendError(new Error("Недопустимый тип файла или превышен допустимый размер.")); return; } setSending(true); setSendError(undefined); try { const dialog = await dialogApi.create(crypto.randomUUID()); const uploaded = await uploadAttachment(dialog.dialog_id, file); await dialogApi.sendFile( dialog.dialog_id, uploaded.attachmentId, uploaded.checksum, crypto.randomUUID(), ); router.push(`/dialogs/${dialog.dialog_id}`); } catch (error) { setSendError(error); } finally { setSending(false); } }; const accept = async (accepted: { personal_data: boolean; user_agreement: boolean; marketing: boolean; }) => { const versions = config.data?.consents; const consents: Consents = { personal_data: { accepted: accepted.personal_data, version: versions?.personal_data?.version ?? "current" }, user_agreement: { accepted: accepted.user_agreement, version: versions?.user_agreement?.version ?? "current" }, marketing: { accepted: accepted.marketing, version: versions?.marketing?.version ?? "current" }, }; setConsentOpen(false); try { const authorized = await authorize(consents); if (authorized && pending) await sendAuthenticated(pending); } catch (error) { setSendError(error); } }; const welcome = content.data?.texts.welcome; const questions = content.data?.popular_questions ?? []; return ( {(config.isLoading || content.isLoading) && } {(config.error || content.error) && ( { void config.refetch(); void content.refetch(); }} /> )} {welcome ? ( {welcome} ) : null} { setMessage(text); void send(text); }} /> void send(message)} sending={sending} value={message} /> {sendError && ( )} {consentOpen && ( void accept(accepted)} onCancel={() => { setConsentOpen(false); setPending(null); clearPendingTextIntent(); }} /> )} ); } const spacing = 16;