import { useEffect, useRef, useState } from "react"; import { router } from "expo-router"; import { ActivityIndicator, Alert, Pressable, ScrollView, Share, Text, View, } from "react-native"; import { Ionicons } from "@expo/vector-icons"; import * as Clipboard from "expo-clipboard"; import ViewShot from "react-native-view-shot"; import * as Sharing from "expo-sharing"; import * as Print from "expo-print"; import { SvgXml } from "react-native-svg"; import { QR_STAND_PRESETS, type QrStandPreset } from "@menulio/shared"; import { api } from "@/lib/api"; import { getActiveRestaurant, type ActiveRestaurant } from "@/lib/active-restaurant"; import { MOBILE_SVG_TEMPLATES } from "@/lib/svg-templates"; interface QrResponse { id: string; redirectUrl: string; targetUrl: string; pngBase64: string; } export default function QrScreen() { const [active, setActive] = useState(null); const [restaurantName, setRestaurantName] = useState("Gourmet Bistro"); const [qr, setQr] = useState(null); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const [copied, setCopied] = useState(false); const [selectedKey, setSelectedKey] = useState("qr-4"); const [exportingPng, setExportingPng] = useState(false); const [exportingPdf, setExportingPdf] = useState(false); // eslint-disable-next-line @typescript-eslint/no-explicit-any const viewShotRef = useRef(null); const activePreset: QrStandPreset = (QR_STAND_PRESETS[selectedKey] || Object.values(QR_STAND_PRESETS)[0])!; const currentSvgRaw = MOBILE_SVG_TEMPLATES[selectedKey] || MOBILE_SVG_TEMPLATES["qr-4"] || Object.values(MOBILE_SVG_TEMPLATES)[0] || ""; // Inject the live QR code image directly into the SVG's #qr-placeholder group // Yorum satırlarını temizle + iç içe taglarını doğru saymak için depth tracking kullan function buildSvgWithQr(svgRaw: string, pngBase64: string): string { // react-native-svg XML parser desteklemez // Ayrıca text node'lardaki & < gibi entity'leri decode et const clean = svgRaw .replace(//g, "") .replace(/&/g, "&") .replace(/</g, "<") .replace(/>/g, ">") .replace(/'/g, "'") .replace(/"/g, '"'); const startMarker = ' taglarını sayarak doğru kapanış 'yi bul let depth = 0; let i = startIdx; let endIdx = -1; while (i < clean.length - 3) { if (clean[i] === "<") { if (clean[i + 1] === "g" && (clean[i + 2] === " " || clean[i + 2] === ">")) { depth++; } else if (clean[i + 1] === "/" && clean[i + 2] === "g" && clean[i + 3] === ">") { depth--; if (depth === 0) { endIdx = i + 4; // uzunluğu = 4 break; } } } i++; } if (endIdx === -1) return clean; const qrGroup = ` `; return clean.substring(0, startIdx) + qrGroup + clean.substring(endIdx); } useEffect(() => { (async () => { const activeRest = await getActiveRestaurant(); if (!activeRest) return; setActive(activeRest); try { const data = await api.post(`/restaurants/${activeRest.restaurantId}/qr`); setQr(data); // Fetch detailed restaurant name try { const restDetail = await api.get<{ name?: string }>(`/restaurants/${activeRest.restaurantId}`); if (restDetail?.name) { setRestaurantName(restDetail.name); } } catch { // fallback } } catch (err) { setError(err instanceof Error ? err.message : "QR oluşturulamadı"); } finally { setLoading(false); } })(); }, []); async function handleCopy() { if (!qr?.targetUrl) return; await Clipboard.setStringAsync(qr.targetUrl); setCopied(true); setTimeout(() => setCopied(false), 2000); } async function handleShareQuick() { if (!qr?.targetUrl) return; await Share.share({ title: "Dijital QR Menü", message: `${qr.targetUrl}\nMenümüzü incelemek için QR kodu taratın veya linke tıklayın.`, url: qr.targetUrl, }); } async function handleExportPng() { if (!viewShotRef.current) return; setExportingPng(true); try { const uri = await viewShotRef.current.capture?.(); if (uri) { await Sharing.shareAsync(uri, { mimeType: "image/png", dialogTitle: "Masa Stant QR Görselini Paylaş / Kaydet", }); } } catch { Alert.alert("Hata", "Görsel oluşturulamadı."); } finally { setExportingPng(false); } } async function handleExportPdf() { if (!qr || !active || !currentSvgRaw) return; setExportingPdf(true); try { const qrImageUri = `data:image/png;base64,${qr.pngBase64}`; // Inject QR code directly into the SVG for print-ready A5 PDF const svgWithQr = currentSvgRaw.replace( /]*>[\s\S]*?<\/g>/, ` ` ); const htmlContent = `
${svgWithQr}
`; const { uri } = await Print.printToFileAsync({ html: htmlContent, width: 420, height: 595, }); await Sharing.shareAsync(uri, { mimeType: "application/pdf", dialogTitle: "A5 Baskıya Hazır PDF İndir / Paylaş", }); } catch { Alert.alert("Hata", "PDF oluşturulamadı."); } finally { setExportingPdf(false); } } if (loading) { return ( QR Kod ve Şablonlar Hazırlanıyor... ); } if (error || !qr) { return ( {error ?? "QR kod yüklenemedi"} router.back()} style={{ marginTop: 20, backgroundColor: "#1C1917", paddingHorizontal: 20, paddingVertical: 10, borderRadius: 8 }} > Geri Dön ); } return ( {/* Header Badge */} MASA QR STANT JENERATÖRÜ Masa Stant Tasarımları Şablon seçin; QR kodunuz otomatik yerleşsin. Baskıya hazır A5 PDF veya PNG olarak indirin. {/* Horizontal Theme Selector */} {Object.values(QR_STAND_PRESETS).length > 1 && ( {Object.values(QR_STAND_PRESETS).map((preset) => { const isSelected = preset.key === selectedKey; return ( setSelectedKey(preset.key)} style={{ backgroundColor: isSelected ? "#D4AF37" : "#1A1A1E", borderWidth: 1.5, borderColor: isSelected ? "#D4AF37" : "#2B2B32", borderRadius: 14, paddingHorizontal: 16, paddingVertical: 10, flexDirection: "row", alignItems: "center", gap: 8, }} > {preset.name} ); })} )} {/* Live Stand Preview Wrapped in ViewShot */} {/* SVG with QR code injected directly — no overlay, pixel-perfect */} {currentSvgRaw ? ( ) : null} {/* Output Action Buttons */} {/* PDF Download Button */} ({ backgroundColor: "#D4AF37", borderRadius: 14, padding: 16, flexDirection: "row", alignItems: "center", justifyContent: "center", gap: 8, opacity: pressed || exportingPdf ? 0.85 : 1, shadowColor: "#D4AF37", shadowOffset: { width: 0, height: 4 }, shadowOpacity: 0.2, shadowRadius: 8, elevation: 3, })} > {exportingPdf ? ( ) : ( <> PDF Olarak İndir (A5 Matbaa Baskı) )} {/* PNG Download Button */} ({ backgroundColor: "#1A1A1E", borderWidth: 1.5, borderColor: "#2B2B32", borderRadius: 14, padding: 16, flexDirection: "row", alignItems: "center", justifyContent: "center", gap: 8, opacity: pressed || exportingPng ? 0.85 : 1, })} > {exportingPng ? ( ) : ( <> PNG Olarak Kaydet / Paylaş )} {/* Direct Link Share Button */} ({ backgroundColor: "#1A1A1E", borderWidth: 1, borderColor: "#2B2B32", borderRadius: 14, padding: 14, flexDirection: "row", alignItems: "center", justifyContent: "center", gap: 8, opacity: pressed ? 0.85 : 1, })} > Menü Linkini Paylaş {/* Quick Copy Link Box */} {qr.targetUrl} ); }