From 3a15dd15b9640d3432e7d872d1d8e96f859949c8 Mon Sep 17 00:00:00 2001 From: AyrisAI Date: Thu, 20 Aug 2026 18:25:20 +0300 Subject: [PATCH] feat(mobile): build native in-app QR Stand Generator with theme switcher & A5 PDF / PNG exporter --- apps/mobile/package.json | 5 +- apps/mobile/src/app/menu/qr.tsx | 520 ++++++++++++++++++++++++++------ pnpm-lock.yaml | 172 ++++++++++- 3 files changed, 590 insertions(+), 107 deletions(-) diff --git a/apps/mobile/package.json b/apps/mobile/package.json index 332cb56..61fdf2e 100644 --- a/apps/mobile/package.json +++ b/apps/mobile/package.json @@ -25,7 +25,9 @@ "expo-haptics": "^14.0.1", "expo-image-picker": "~16.0.6", "expo-linking": "~7.0.5", + "expo-print": "^57.0.1", "expo-router": "~4.0.0", + "expo-sharing": "^57.0.13", "expo-splash-screen": "~0.29.24", "expo-status-bar": "~2.0.0", "react": "18.3.1", @@ -33,7 +35,8 @@ "react-native-purchases": "^8.2.0", "react-native-safe-area-context": "4.12.0", "react-native-screens": "~4.4.0", - "react-native-url-polyfill": "^2.0.0" + "react-native-url-polyfill": "^2.0.0", + "react-native-view-shot": "^5.1.1" }, "devDependencies": { "@babel/core": "^7.25.2", diff --git a/apps/mobile/src/app/menu/qr.tsx b/apps/mobile/src/app/menu/qr.tsx index 4163ee4..d28c825 100644 --- a/apps/mobile/src/app/menu/qr.tsx +++ b/apps/mobile/src/app/menu/qr.tsx @@ -1,10 +1,9 @@ -import { useEffect, useState } from "react"; +import { useEffect, useRef, useState } from "react"; import { router } from "expo-router"; import { ActivityIndicator, Alert, Image, - Linking, Pressable, ScrollView, Share, @@ -13,6 +12,10 @@ import { } 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 { QR_STAND_PRESETS, type QrStandPreset } from "@menulio/shared"; import { api } from "@/lib/api"; import { getActiveRestaurant, type ActiveRestaurant } from "@/lib/active-restaurant"; @@ -25,10 +28,20 @@ interface QrResponse { export default function QrScreen() { const [active, setActive] = useState(null); + const [restaurantName, setRestaurantName] = useState("RESTORAN"); const [qr, setQr] = useState(null); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const [copied, setCopied] = useState(false); + const [selectedKey, setSelectedKey] = useState("urban"); + const [categories, setCategories] = useState([]); + 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] || QR_STAND_PRESETS.urban)!; useEffect(() => { (async () => { @@ -39,6 +52,28 @@ export default function QrScreen() { 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 + } + + // Fetch categories for preview + if (activeRest.menuId) { + try { + const menuRes = await api.get<{ categories: { name: string }[] }>(`/menus/${activeRest.menuId}`); + if (menuRes?.categories?.length) { + setCategories(menuRes.categories.map((c) => c.name)); + } + } catch { + // ignore fallback + } + } } catch (err) { setError(err instanceof Error ? err.message : "QR oluşturulamadı"); } finally { @@ -47,6 +82,8 @@ export default function QrScreen() { })(); }, []); + const displayCategories = categories.length > 0 ? categories.slice(0, 4) : activePreset.categories; + async function handleCopy() { if (!qr?.targetUrl) return; await Clipboard.setStringAsync(qr.targetUrl); @@ -54,7 +91,7 @@ export default function QrScreen() { setTimeout(() => setCopied(false), 2000); } - async function handleShare() { + async function handleShareQuick() { if (!qr?.targetUrl) return; await Share.share({ title: "Dijital QR Menü", @@ -63,11 +100,124 @@ export default function QrScreen() { }); } + 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) return; + setExportingPdf(true); + try { + const restTitle = restaurantName.toUpperCase(); + const qrImageUri = `data:image/png;base64,${qr.pngBase64}`; + const catsHtml = displayCategories + .map( + (c, i) => + `
${c}
${ + i < displayCategories.length - 1 + ? '
' + : "" + }`, + ) + .join(""); + + const htmlContent = ` + + + + + + + +
+
${restTitle}
+
DİJİTAL MENÜ
KAMERANIZLA TARATIN
+
+ +
+
Telefonunuzun kamerasını doğrultun
+
${catsHtml}
+
+ + + `; + + 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 Hazırlanıyor... + QR Kod ve Şablonlar Hazırlanıyor... ); } @@ -89,25 +239,18 @@ export default function QrScreen() { ); } - function handleOpenStandGenerator() { - if (!active?.slug) return; - const standUrl = `https://menul.io/qr/${active.slug}`; - Linking.openURL(standUrl).catch(() => { - Alert.alert("Bilgi", `Stant Jeneratörü adresi: ${standUrl}`); - }); - } - return ( - - + + {/* Header Badge */} - DİJİTAL RESTORAN QR KODU + MASA QR STANT JENERATÖRÜ - - Masanıza Özel QR Kod + + Masa Stant Tasarımları - - Müşterileriniz bu QR kodu telefon kameralarıyla taratarak saniyeler içinde menünüzü inceleyebilir. + + Şablon seçin; QR kodunuz otomatik yerleşsin. Baskıya hazır A5 PDF veya PNG olarak indirin. - {/* QR Code Container Card */} + {/* Horizontal Theme Selector */} + + {Object.values(QR_STAND_PRESETS).map((preset) => { + const isSelected = preset.key === selectedKey; + return ( + setSelectedKey(preset.key)} + style={{ + backgroundColor: isSelected ? "#1C1917" : "#FFFFFF", + borderWidth: 1.5, + borderColor: isSelected ? "#1C1917" : "#E7E5E4", + borderRadius: 14, + paddingHorizontal: 16, + paddingVertical: 10, + flexDirection: "row", + alignItems: "center", + gap: 8, + }} + > + + + {preset.name} + + + ); + })} + + + {/* Live Stand Preview Wrapped in ViewShot */} - + + + {/* Top Restaurant Badge */} + + + {restaurantName.toUpperCase()} + + - - - {qr.targetUrl} - - + {/* Title Callout */} + + DİJİTAL MENÜ{"\n"} + KAMERANIZLA TARATIN + + + {/* Auto Placed QR Code Container */} + + + + + {/* Instructional Subtext */} + + Telefonunuzun kamerasını doğrultun + + + {/* Category Quick Preview List */} + + {displayCategories.map((cat, idx) => ( + + + {cat} + + {idx < displayCategories.length - 1 && ( + + )} + + ))} + + + - {/* Action Buttons */} - - {/* Stand Generator PDF / PNG Button */} + {/* Output Action Buttons */} + + {/* PDF Download Button */} ({ backgroundColor: "#C8A96B", borderRadius: 14, @@ -178,8 +467,8 @@ export default function QrScreen() { flexDirection: "row", alignItems: "center", justifyContent: "center", - gap: 10, - opacity: pressed ? 0.9 : 1, + gap: 8, + opacity: pressed || exportingPdf ? 0.85 : 1, shadowColor: "#C8A96B", shadowOffset: { width: 0, height: 4 }, shadowOpacity: 0.2, @@ -187,64 +476,95 @@ export default function QrScreen() { elevation: 4, })} > - - - Masa Stant Tasarımları & İndir (PDF / PNG) - + {exportingPdf ? ( + + ) : ( + <> + + + A5 Baskı PDF Dosyası İndir / Paylaş + + + )} - {/* Share Button */} + {/* PNG Export Button */} ({ backgroundColor: "#1C1917", borderRadius: 14, - padding: 16, - flexDirection: "row", - alignItems: "center", - justifyContent: "center", - gap: 10, - opacity: pressed ? 0.9 : 1, - shadowColor: "#000", - shadowOffset: { width: 0, height: 4 }, - shadowOpacity: 0.1, - shadowRadius: 10, - elevation: 4, - })} - > - - - QR Kodu ve Linki Paylaş - - - - {/* Copy Link Button */} - ({ - backgroundColor: "#FFFFFF", - borderWidth: 1.5, - borderColor: "#E7E5E4", - borderRadius: 14, padding: 15, flexDirection: "row", alignItems: "center", justifyContent: "center", gap: 8, - opacity: pressed ? 0.85 : 1, + opacity: pressed || exportingPng ? 0.85 : 1, })} > - - - {copied ? "Link Kopyalandı!" : "Menü Linkini Kopyala"} - + {exportingPng ? ( + + ) : ( + <> + + + PNG Görsel Olarak Kaydet / Paylaş + + + )} - {/* Back to Menu Editor */} + {/* Secondary Quick Share & Copy */} + + ({ + flex: 1, + backgroundColor: "#FFFFFF", + borderWidth: 1, + borderColor: "#E7E5E4", + borderRadius: 12, + padding: 12, + flexDirection: "row", + alignItems: "center", + justifyContent: "center", + gap: 6, + opacity: pressed ? 0.85 : 1, + })} + > + + Linki Paylaş + + + ({ + flex: 1, + backgroundColor: "#FFFFFF", + borderWidth: 1, + borderColor: "#E7E5E4", + borderRadius: 12, + padding: 12, + flexDirection: "row", + alignItems: "center", + justifyContent: "center", + gap: 6, + opacity: pressed ? 0.85 : 1, + })} + > + + + {copied ? "Kopyalandı!" : "Linki Kopyala"} + + + + + {/* Back button */} router.back()} style={{ padding: 12, alignItems: "center", flexDirection: "row", justifyContent: "center", gap: 6 }} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a4ceabb..1ba57e4 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -89,9 +89,15 @@ importers: expo-linking: specifier: ~7.0.5 version: 7.0.5(expo@52.0.49(@babel/core@7.29.7(supports-color@8.1.1))(@babel/preset-env@7.29.7(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@expo/metro-runtime@4.0.1(react-native@0.76.9(@babel/core@7.29.7(supports-color@8.1.1))(@babel/preset-env@7.29.7(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@18.3.31)(react@18.3.1)(supports-color@8.1.1)))(react-native@0.76.9(@babel/core@7.29.7(supports-color@8.1.1))(@babel/preset-env@7.29.7(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@18.3.31)(react@18.3.1)(supports-color@8.1.1))(react@18.3.1)(supports-color@8.1.1))(react-native@0.76.9(@babel/core@7.29.7(supports-color@8.1.1))(@babel/preset-env@7.29.7(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@18.3.31)(react@18.3.1)(supports-color@8.1.1))(react@18.3.1)(supports-color@8.1.1) + expo-print: + specifier: ^57.0.1 + version: 57.0.1(expo@52.0.49(@babel/core@7.29.7(supports-color@8.1.1))(@babel/preset-env@7.29.7(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@expo/metro-runtime@4.0.1(react-native@0.76.9(@babel/core@7.29.7(supports-color@8.1.1))(@babel/preset-env@7.29.7(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@18.3.31)(react@18.3.1)(supports-color@8.1.1)))(react-native@0.76.9(@babel/core@7.29.7(supports-color@8.1.1))(@babel/preset-env@7.29.7(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@18.3.31)(react@18.3.1)(supports-color@8.1.1))(react@18.3.1)(supports-color@8.1.1))(react-native@0.76.9(@babel/core@7.29.7(supports-color@8.1.1))(@babel/preset-env@7.29.7(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@18.3.31)(react@18.3.1)(supports-color@8.1.1)) expo-router: specifier: ~4.0.0 version: 4.0.22(1482d1318244a89f06fcfc2b6da7d585) + expo-sharing: + specifier: ^57.0.13 + version: 57.0.13(expo@52.0.49(@babel/core@7.29.7(supports-color@8.1.1))(@babel/preset-env@7.29.7(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@expo/metro-runtime@4.0.1(react-native@0.76.9(@babel/core@7.29.7(supports-color@8.1.1))(@babel/preset-env@7.29.7(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@18.3.31)(react@18.3.1)(supports-color@8.1.1)))(react-native@0.76.9(@babel/core@7.29.7(supports-color@8.1.1))(@babel/preset-env@7.29.7(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@18.3.31)(react@18.3.1)(supports-color@8.1.1))(react@18.3.1)(supports-color@8.1.1))(react-native@0.76.9(@babel/core@7.29.7(supports-color@8.1.1))(@babel/preset-env@7.29.7(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@18.3.31)(react@18.3.1)(supports-color@8.1.1))(react@18.3.1)(supports-color@8.1.1)(typescript@5.9.3) expo-splash-screen: specifier: ~0.29.24 version: 0.29.24(expo@52.0.49(@babel/core@7.29.7(supports-color@8.1.1))(@babel/preset-env@7.29.7(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@expo/metro-runtime@4.0.1(react-native@0.76.9(@babel/core@7.29.7(supports-color@8.1.1))(@babel/preset-env@7.29.7(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@18.3.31)(react@18.3.1)(supports-color@8.1.1)))(react-native@0.76.9(@babel/core@7.29.7(supports-color@8.1.1))(@babel/preset-env@7.29.7(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@18.3.31)(react@18.3.1)(supports-color@8.1.1))(react@18.3.1)(supports-color@8.1.1))(supports-color@8.1.1) @@ -116,6 +122,9 @@ importers: react-native-url-polyfill: specifier: ^2.0.0 version: 2.0.0(react-native@0.76.9(@babel/core@7.29.7(supports-color@8.1.1))(@babel/preset-env@7.29.7(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@18.3.31)(react@18.3.1)(supports-color@8.1.1)) + react-native-view-shot: + specifier: ^5.1.1 + version: 5.1.1(react-native@0.76.9(@babel/core@7.29.7(supports-color@8.1.1))(@babel/preset-env@7.29.7(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@18.3.31)(react@18.3.1)(supports-color@8.1.1))(react@18.3.1) devDependencies: '@babel/core': specifier: ^7.25.2 @@ -1262,12 +1271,18 @@ packages: '@expo/code-signing-certificates@0.0.6': resolution: {integrity: sha512-iNe0puxwBNEcuua9gmTGzq+SuMDa0iATai1FlFTMHJ/vUmKvN/V//drXoLJkVb5i5H3iE/n/qIJxyoBnXouD0w==} + '@expo/config-plugins@57.0.8': + resolution: {integrity: sha512-x6lx4s/19i39/+1dMPwb9tFCc4WR843dG5Yi+C64lhKL3YHX+6oKrT2Kv72PCEalnUY+usQDkB3NrLSrYOWtDg==} + '@expo/config-plugins@9.0.17': resolution: {integrity: sha512-m24F1COquwOm7PBl5wRbkT9P9DviCXe0D7S7nQsolfbhdCWuvMkfXeoWmgjtdhy7sDlOyIgBrAdnB6MfsWKqIg==} '@expo/config-types@52.0.5': resolution: {integrity: sha512-AMDeuDLHXXqd8W+0zSjIt7f37vUd/BP8p43k68NHpyAvQO+z8mbQZm3cNQVAMySeayK2XoPigAFB1JF2NFajaA==} + '@expo/config-types@57.0.2': + resolution: {integrity: sha512-ewW08OonrcRIsRKIlFvvcmmafE5zemb1ocu3HkNwtVPyRtj2w42pZCAkMIROYpcVBaPnc3mDT9UZDzwXWC3i6g==} + '@expo/config@10.0.11': resolution: {integrity: sha512-nociJ4zr/NmbVfMNe9j/+zRlt7wz/siISu7PjdWE4WE+elEGxWWxsGzltdJG0llzrM+khx8qUiFK5aiVcdMBww==} @@ -1311,9 +1326,20 @@ packages: '@expo/plist@0.2.2': resolution: {integrity: sha512-ZZGvTO6vEWq02UAPs3LIdja+HRO18+LRI5QuDl6Hs3Ps7KX7xU6Y6kjahWKY37Rx2YjNpX07dGpBFzzC+vKa2g==} + '@expo/plist@0.8.1': + resolution: {integrity: sha512-3gTReGIUm0oRaMClsAJYxBnVPCl6fVpsl8HS+DTVxDhW4GyVyxg9E/Znm3BvcHtUJ51RJJI14pC1wvrNilCRHw==} + '@expo/prebuild-config@8.2.0': resolution: {integrity: sha512-CxiPpd980s0jyxi7eyN3i/7YKu3XL+8qPjBZUCYtc0+axpGweqIkq2CslyLSKHyqVyH/zlPkbVgWdyiYavFS5Q==} + '@expo/require-utils@57.0.4': + resolution: {integrity: sha512-e7xbg/9BTQcsZE/oErafZXtI7kh5IgfasLJ97J5sFSzX2cA74pDvdlhW1KHVSaDkQyQv6h1LSLhsY7dEeOk7hw==} + peerDependencies: + typescript: ^5.0.0 || ^5.0.0-0 || ^6.0.0 || ^7.0.0 + peerDependenciesMeta: + typescript: + optional: true + '@expo/rudder-sdk-node@1.1.1': resolution: {integrity: sha512-uy/hS/awclDJ1S88w9UGpc6Nm9XnNUjzOAAib1A3PVAnGQIwebg8DpFqOthFBTlZxeuV/BKbZ5jmTbtNZkp1WQ==} engines: {node: '>=12'} @@ -2055,6 +2081,10 @@ packages: engines: {node: '>=10.0.0'} deprecated: this version has critical issues, please update to the latest version + '@xmldom/xmldom@0.8.14': + resolution: {integrity: sha512-T4EDRUBVZYRldYApjEJiU0e1stYWaRAX7CuSnKzrpwdZKo53zGV8/pqfzV6FfwNl9YThD2OumQYvqtvjvgG7aQ==} + engines: {node: '>=10.0.0'} + '@xmldom/xmldom@0.9.11': resolution: {integrity: sha512-tW8bcK3hsG0/uqSnNz6TK4BkcuZSezoU7DlnYssILmZDktPnSHHuDJJFM0AJv+13gz2r0iGdrj6qqKeUnxXEDg==} engines: {node: '>=14.6'} @@ -2265,6 +2295,10 @@ packages: balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + balanced-match@4.0.4: + resolution: {integrity: sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==} + engines: {node: 18 || 20 || >=22} + base64-arraybuffer@1.0.2: resolution: {integrity: sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ==} engines: {node: '>= 0.6.0'} @@ -2309,6 +2343,10 @@ packages: brace-expansion@2.1.4: resolution: {integrity: sha512-hGfVzPxthbf3+2yjg/RBs60cB0FhqBS/zvdV/4wn4/BmN0bNMMHPc4V/BbFieqf1TKAGGAHnY4eSjajCl0f2Xg==} + brace-expansion@5.0.9: + resolution: {integrity: sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg==} + engines: {node: 20 || >=22} + braces@3.0.3: resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} engines: {node: '>=8'} @@ -2856,6 +2894,12 @@ packages: expo-modules-core@2.2.3: resolution: {integrity: sha512-01QqZzpP/wWlxnNly4G06MsOBUTbMDj02DQigZoXfDh80vd/rk3/uVXqnZgOdLSggTs6DnvOgAUy0H2q30XdUg==} + expo-print@57.0.1: + resolution: {integrity: sha512-cEfxM+sV5suZyqbXsOmDGRHD+UmfAh2GWabbakdkl1s70L4MFiObPtC73ATXNjeUe+mlzxLhudoTxsuyJxk8Mg==} + peerDependencies: + expo: '*' + react-native: '*' + expo-router@4.0.22: resolution: {integrity: sha512-cbl6pWUMMZl5sZaHSMmxhQi+kTC6Yzj0ATaD2j6MiLF6VQyr5IcWes3V0UxPtI08boTX0i+76/fnP5fZ3eGQYQ==} peerDependencies: @@ -2875,6 +2919,13 @@ packages: react-native-reanimated: optional: true + expo-sharing@57.0.13: + resolution: {integrity: sha512-8kRUBikuR+c90++PF4PaP8pj+McUNi7AIZkwmn0iXcA8QobkJDSAjSB3fP8qoFm8bzHDhyibHIlODTAsxrMYMw==} + peerDependencies: + expo: '*' + react: '*' + react-native: '*' + expo-splash-screen@0.29.24: resolution: {integrity: sha512-k2rdjbb3Qeg4g104Sdz6+qXXYba8QgiuZRSxHX8IpsSYiiTU48BmCCGy12sN+O1B+sD1/+WPL4duCa1Fy6+Y4g==} peerDependencies: @@ -3101,6 +3152,10 @@ packages: resolution: {integrity: sha512-7yetJWqbS9sbn0vIfliPsFgoXMKn/YMF+Wuiog97x+urnSRRRZ7xB+uVkwGKzRgq9CDFfMQnE9ruL5DHv9c6Xg==} engines: {node: '>=6'} + getenv@2.0.0: + resolution: {integrity: sha512-VilgtJj/ALgGY77fiLam5iD336eSWi96Q15JSAG1zi8NRBysm3LXKdGnHb4m5cuyxvOLQQKWpBZAT6ni4FI2iQ==} + engines: {node: '>=6'} + glob-parent@5.1.2: resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} engines: {node: '>= 6'} @@ -3114,6 +3169,10 @@ packages: deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me hasBin: true + glob@13.0.6: + resolution: {integrity: sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==} + engines: {node: 18 || 20 || >=22} + glob@7.2.3: resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me @@ -3555,6 +3614,10 @@ packages: lru-cache@10.4.3: resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} + lru-cache@11.5.2: + resolution: {integrity: sha512-4pfM1Ff0x50o0tQwb5ucw/RzNyD0/YJME6IVcStalZuMWxdt3sR3huStTtxz4PUmvZfRguvDejasvQ2kifR11g==} + engines: {node: 20 || >=22} + lru-cache@5.1.1: resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} @@ -3684,6 +3747,10 @@ packages: resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} engines: {node: '>=6'} + minimatch@10.2.6: + resolution: {integrity: sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==} + engines: {node: 18 || 20 || >=22} + minimatch@3.1.5: resolution: {integrity: sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==} @@ -3963,6 +4030,10 @@ packages: resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} engines: {node: '>=16 || 14 >=14.18'} + path-scurry@2.0.2: + resolution: {integrity: sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==} + engines: {node: 18 || 20 || >=22} + path-type@4.0.0: resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} engines: {node: '>=8'} @@ -4226,6 +4297,13 @@ packages: peerDependencies: react-native: '*' + react-native-view-shot@5.1.1: + resolution: {integrity: sha512-jOTLQrMGNBgv/nRA81VR2x2AdiCGoCjc63c9a5P4A0gStCbh9oDeJ6W+GtRxxQmxXXf6LDSrvG+mmWYulIrOkg==} + engines: {node: '>=20.19.4', npm: '>=10'} + peerDependencies: + react: '>=18.0.0' + react-native: '>=0.76.0' + react-native@0.76.9: resolution: {integrity: sha512-+LRwecWmTDco7OweGsrECIqJu0iyrREd6CTCgC/uLLYipiHvk+MH9nd6drFtCw/6Blz6eoKTcH9YTTJusNtrWg==} engines: {node: '>=18'} @@ -6224,6 +6302,25 @@ snapshots: dependencies: node-forge: 1.4.0 + '@expo/config-plugins@57.0.8(supports-color@8.1.1)(typescript@5.9.3)': + dependencies: + '@expo/config-types': 57.0.2 + '@expo/json-file': 11.0.1 + '@expo/plist': 0.8.1 + '@expo/require-utils': 57.0.4(supports-color@8.1.1)(typescript@5.9.3) + '@expo/sdk-runtime-versions': 1.0.0 + chalk: 4.1.2 + debug: 4.4.3(supports-color@8.1.1) + getenv: 2.0.0 + glob: 13.0.6 + semver: 7.8.5 + slugify: 1.6.9 + xcode: 3.0.1 + xml2js: 0.6.0 + transitivePeerDependencies: + - supports-color + - typescript + '@expo/config-plugins@9.0.17(supports-color@8.1.1)': dependencies: '@expo/config-types': 52.0.5 @@ -6245,6 +6342,8 @@ snapshots: '@expo/config-types@52.0.5': {} + '@expo/config-types@57.0.2': {} + '@expo/config@10.0.11(supports-color@8.1.1)': dependencies: '@babel/code-frame': 7.10.4 @@ -6370,6 +6469,12 @@ snapshots: base64-js: 1.5.1 xmlbuilder: 14.0.0 + '@expo/plist@0.8.1': + dependencies: + '@xmldom/xmldom': 0.8.14 + base64-js: 1.5.1 + xmlbuilder: 15.1.1 + '@expo/prebuild-config@8.2.0(supports-color@8.1.1)': dependencies: '@expo/config': 10.0.11(supports-color@8.1.1) @@ -6386,6 +6491,16 @@ snapshots: transitivePeerDependencies: - supports-color + '@expo/require-utils@57.0.4(supports-color@8.1.1)(typescript@5.9.3)': + dependencies: + '@babel/code-frame': 7.29.7 + '@babel/core': 7.29.7(supports-color@8.1.1) + '@babel/plugin-transform-modules-commonjs': 7.29.7(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + '@expo/rudder-sdk-node@1.1.1': dependencies: '@expo/bunyan': 4.0.1 @@ -7145,6 +7260,8 @@ snapshots: '@xmldom/xmldom@0.7.13': {} + '@xmldom/xmldom@0.8.14': {} + '@xmldom/xmldom@0.9.11': {} abort-controller@3.0.0: @@ -7379,8 +7496,9 @@ snapshots: balanced-match@1.0.2: {} - base64-arraybuffer@1.0.2: - optional: true + balanced-match@4.0.4: {} + + base64-arraybuffer@1.0.2: {} base64-js@1.5.1: {} @@ -7419,6 +7537,10 @@ snapshots: dependencies: balanced-match: 1.0.2 + brace-expansion@5.0.9: + dependencies: + balanced-match: 4.0.4 + braces@3.0.3: dependencies: fill-range: 7.1.1 @@ -7709,7 +7831,6 @@ snapshots: css-line-break@2.1.0: dependencies: utrie: 1.0.2 - optional: true cssesc@3.0.0: {} @@ -8028,6 +8149,11 @@ snapshots: dependencies: invariant: 2.2.4 + expo-print@57.0.1(expo@52.0.49(@babel/core@7.29.7(supports-color@8.1.1))(@babel/preset-env@7.29.7(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@expo/metro-runtime@4.0.1(react-native@0.76.9(@babel/core@7.29.7(supports-color@8.1.1))(@babel/preset-env@7.29.7(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@18.3.31)(react@18.3.1)(supports-color@8.1.1)))(react-native@0.76.9(@babel/core@7.29.7(supports-color@8.1.1))(@babel/preset-env@7.29.7(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@18.3.31)(react@18.3.1)(supports-color@8.1.1))(react@18.3.1)(supports-color@8.1.1))(react-native@0.76.9(@babel/core@7.29.7(supports-color@8.1.1))(@babel/preset-env@7.29.7(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@18.3.31)(react@18.3.1)(supports-color@8.1.1)): + dependencies: + expo: 52.0.49(@babel/core@7.29.7(supports-color@8.1.1))(@babel/preset-env@7.29.7(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@expo/metro-runtime@4.0.1(react-native@0.76.9(@babel/core@7.29.7(supports-color@8.1.1))(@babel/preset-env@7.29.7(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@18.3.31)(react@18.3.1)(supports-color@8.1.1)))(react-native@0.76.9(@babel/core@7.29.7(supports-color@8.1.1))(@babel/preset-env@7.29.7(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@18.3.31)(react@18.3.1)(supports-color@8.1.1))(react@18.3.1)(supports-color@8.1.1) + react-native: 0.76.9(@babel/core@7.29.7(supports-color@8.1.1))(@babel/preset-env@7.29.7(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@18.3.31)(react@18.3.1)(supports-color@8.1.1) + expo-router@4.0.22(1482d1318244a89f06fcfc2b6da7d585): dependencies: '@expo/metro-runtime': 4.0.1(react-native@0.76.9(@babel/core@7.29.7(supports-color@8.1.1))(@babel/preset-env@7.29.7(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@18.3.31)(react@18.3.1)(supports-color@8.1.1)) @@ -8055,6 +8181,18 @@ snapshots: - react-native - supports-color + expo-sharing@57.0.13(expo@52.0.49(@babel/core@7.29.7(supports-color@8.1.1))(@babel/preset-env@7.29.7(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@expo/metro-runtime@4.0.1(react-native@0.76.9(@babel/core@7.29.7(supports-color@8.1.1))(@babel/preset-env@7.29.7(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@18.3.31)(react@18.3.1)(supports-color@8.1.1)))(react-native@0.76.9(@babel/core@7.29.7(supports-color@8.1.1))(@babel/preset-env@7.29.7(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@18.3.31)(react@18.3.1)(supports-color@8.1.1))(react@18.3.1)(supports-color@8.1.1))(react-native@0.76.9(@babel/core@7.29.7(supports-color@8.1.1))(@babel/preset-env@7.29.7(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@18.3.31)(react@18.3.1)(supports-color@8.1.1))(react@18.3.1)(supports-color@8.1.1)(typescript@5.9.3): + dependencies: + '@expo/config-plugins': 57.0.8(supports-color@8.1.1)(typescript@5.9.3) + '@expo/config-types': 57.0.2 + '@expo/plist': 0.8.1 + expo: 52.0.49(@babel/core@7.29.7(supports-color@8.1.1))(@babel/preset-env@7.29.7(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@expo/metro-runtime@4.0.1(react-native@0.76.9(@babel/core@7.29.7(supports-color@8.1.1))(@babel/preset-env@7.29.7(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@18.3.31)(react@18.3.1)(supports-color@8.1.1)))(react-native@0.76.9(@babel/core@7.29.7(supports-color@8.1.1))(@babel/preset-env@7.29.7(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@18.3.31)(react@18.3.1)(supports-color@8.1.1))(react@18.3.1)(supports-color@8.1.1) + react: 18.3.1 + react-native: 0.76.9(@babel/core@7.29.7(supports-color@8.1.1))(@babel/preset-env@7.29.7(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@18.3.31)(react@18.3.1)(supports-color@8.1.1) + transitivePeerDependencies: + - supports-color + - typescript + expo-splash-screen@0.29.24(expo@52.0.49(@babel/core@7.29.7(supports-color@8.1.1))(@babel/preset-env@7.29.7(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@expo/metro-runtime@4.0.1(react-native@0.76.9(@babel/core@7.29.7(supports-color@8.1.1))(@babel/preset-env@7.29.7(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@18.3.31)(react@18.3.1)(supports-color@8.1.1)))(react-native@0.76.9(@babel/core@7.29.7(supports-color@8.1.1))(@babel/preset-env@7.29.7(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@18.3.31)(react@18.3.1)(supports-color@8.1.1))(react@18.3.1)(supports-color@8.1.1))(supports-color@8.1.1): dependencies: '@expo/prebuild-config': 8.2.0(supports-color@8.1.1) @@ -8343,6 +8481,8 @@ snapshots: getenv@1.0.0: {} + getenv@2.0.0: {} + glob-parent@5.1.2: dependencies: is-glob: 4.0.3 @@ -8360,6 +8500,12 @@ snapshots: package-json-from-dist: 1.0.1 path-scurry: 1.11.1 + glob@13.0.6: + dependencies: + minimatch: 10.2.6 + minipass: 7.1.3 + path-scurry: 2.0.2 + glob@7.2.3: dependencies: fs.realpath: 1.0.0 @@ -8418,7 +8564,6 @@ snapshots: dependencies: css-line-break: 2.1.0 text-segmentation: 1.0.3 - optional: true http-errors@2.0.1: dependencies: @@ -8793,6 +8938,8 @@ snapshots: lru-cache@10.4.3: {} + lru-cache@11.5.2: {} + lru-cache@5.1.1: dependencies: yallist: 3.1.1 @@ -9025,6 +9172,10 @@ snapshots: mimic-fn@2.1.0: {} + minimatch@10.2.6: + dependencies: + brace-expansion: 5.0.9 + minimatch@3.1.5: dependencies: brace-expansion: 1.1.18 @@ -9269,6 +9420,11 @@ snapshots: lru-cache: 10.4.3 minipass: 7.1.3 + path-scurry@2.0.2: + dependencies: + lru-cache: 11.5.2 + minipass: 7.1.3 + path-type@4.0.0: {} pathe@2.0.3: {} @@ -9528,6 +9684,12 @@ snapshots: react-native: 0.76.9(@babel/core@7.29.7(supports-color@8.1.1))(@babel/preset-env@7.29.7(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@18.3.31)(react@18.3.1)(supports-color@8.1.1) whatwg-url-without-unicode: 8.0.0-3 + react-native-view-shot@5.1.1(react-native@0.76.9(@babel/core@7.29.7(supports-color@8.1.1))(@babel/preset-env@7.29.7(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@18.3.31)(react@18.3.1)(supports-color@8.1.1))(react@18.3.1): + dependencies: + html2canvas: 1.4.1 + react: 18.3.1 + react-native: 0.76.9(@babel/core@7.29.7(supports-color@8.1.1))(@babel/preset-env@7.29.7(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@18.3.31)(react@18.3.1)(supports-color@8.1.1) + react-native@0.76.9(@babel/core@7.29.7(supports-color@8.1.1))(@babel/preset-env@7.29.7(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@18.3.31)(react@18.3.1)(supports-color@8.1.1): dependencies: '@jest/create-cache-key-function': 29.7.0 @@ -10071,7 +10233,6 @@ snapshots: text-segmentation@1.0.3: dependencies: utrie: 1.0.2 - optional: true thenify-all@1.6.0: dependencies: @@ -10216,7 +10377,6 @@ snapshots: utrie@1.0.2: dependencies: base64-arraybuffer: 1.0.2 - optional: true uuid@7.0.3: {}