import { useQuery } from "@tanstack/react-query"; import { useLocalSearchParams, useRouter } from "expo-router"; import React, { useEffect, useRef, 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 { NotificationCarousel } from "../src/components/NotificationCarousel"; import { PopularQuestionsList } from "../src/components/PopularQuestionsList"; import { QuickActions } from "../src/components/QuickActions"; import { ScreenShell } from "../src/components/ScreenShell"; import { clearPendingTextIntent, createPendingTextIntent, savePendingTextIntent, savePendingFileIntent, type PendingTextIntent, } from "../src/pending-intent"; import { DEFAULT_MESSAGE_MAX_LENGTH, normalizeMessageText } from "../src/message-text"; import { publicApi } 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] = useState(false); const [afterNotificationAuth, setAfterNotificationAuth] = useState<(() => Promise) | undefined>(); const { authorize: authorizeParam } = useLocalSearchParams<{ authorize?: string }>(); const handledAuthorizeParam = useRef(false); const router = useRouter(); useEffect(() => { if (authorizeParam === "1" && !handledAuthorizeParam.current && authStatus !== "authenticated") { handledAuthorizeParam.current = true; setConsentOpen(true); } }, [authStatus, authorizeParam]); const openChatWithText = (intent: PendingTextIntent) => { setSendError(undefined); savePendingTextIntent(intent); setMessage(""); setPending(null); router.push("/dialogs?pending=1"); }; const send = async (text: string) => { const normalized = normalizeMessageText(text); if (!normalized) return; const maxLength = config.data?.messages?.max_text_length ?? DEFAULT_MESSAGE_MAX_LENGTH; if (normalized.length > maxLength) { setSendError(new Error(`Сообщение слишком длинное. Максимум — ${maxLength} символов.`)); return; } const intent = createPendingTextIntent(normalized); if (authStatus !== "authenticated") { setPending(intent); savePendingTextIntent(intent); setConsentOpen(true); return; } openChatWithText(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; } setSendError(undefined); savePendingFileIntent({ file, dialogKey: crypto.randomUUID(), messageKey: crypto.randomUUID(), }); router.push("/dialogs?pending=1"); }; 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) openChatWithText(pending); else if (authorized && afterNotificationAuth) await afterNotificationAuth(); } catch (error) { setSendError(error); } finally { setAfterNotificationAuth(undefined); } }; 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} { setAfterNotificationAuth(afterAuth ? () => afterAuth : undefined); setConsentOpen(true); }} /> { setMessage(text); void send(text); }} /> void send(message)} sending={sending} value={message} maxLength={config.data?.messages?.max_text_length ?? DEFAULT_MESSAGE_MAX_LENGTH} /> {Boolean(sendError) && ( )} {consentOpen && ( void accept(accepted)} onCancel={() => { setConsentOpen(false); setPending(null); setAfterNotificationAuth(undefined); clearPendingTextIntent(); }} /> )} ); } const spacing = 16;