fix(mobile): inject QR directly into SVG XML for pixel-perfect placement
@@ -3,7 +3,6 @@ import { router } from "expo-router";
|
|||||||
import {
|
import {
|
||||||
ActivityIndicator,
|
ActivityIndicator,
|
||||||
Alert,
|
Alert,
|
||||||
Image,
|
|
||||||
Pressable,
|
Pressable,
|
||||||
ScrollView,
|
ScrollView,
|
||||||
Share,
|
Share,
|
||||||
@@ -45,6 +44,23 @@ export default function QrScreen() {
|
|||||||
const activePreset: QrStandPreset = (QR_STAND_PRESETS[selectedKey] || Object.values(QR_STAND_PRESETS)[0])!;
|
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] || "";
|
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
|
||||||
|
function buildSvgWithQr(svgRaw: string, pngBase64: string): string {
|
||||||
|
const qrImgTag = `<g id="qr-placeholder" transform="translate(90, 215)">
|
||||||
|
<rect width="220" height="220" rx="20" fill="#FFFFFF"/>
|
||||||
|
<image href="data:image/png;base64,${pngBase64}" x="10" y="10" width="200" height="200" preserveAspectRatio="xMidYMid meet"/>
|
||||||
|
</g>`;
|
||||||
|
// Replace the entire qr-placeholder group
|
||||||
|
const replaced = svgRaw.replace(
|
||||||
|
/<g id="qr-placeholder"[\s\S]*?<\/g>/,
|
||||||
|
qrImgTag
|
||||||
|
);
|
||||||
|
// Also ensure the SVG scales properly by setting width/height to 100%
|
||||||
|
return replaced
|
||||||
|
.replace(/width="400"/, 'width="100%"')
|
||||||
|
.replace(/height="650"/, 'height="100%"');
|
||||||
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
(async () => {
|
(async () => {
|
||||||
const activeRest = await getActiveRestaurant();
|
const activeRest = await getActiveRestaurant();
|
||||||
@@ -301,47 +317,25 @@ export default function QrScreen() {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<ViewShot ref={viewShotRef} options={{ format: "png", quality: 1.0 }}>
|
<ViewShot ref={viewShotRef} options={{ format: "png", quality: 1.0 }}>
|
||||||
|
{/* SVG with QR code injected directly — no overlay, pixel-perfect */}
|
||||||
<View
|
<View
|
||||||
style={{
|
style={{
|
||||||
width: 300,
|
width: 300,
|
||||||
height: 487.5,
|
height: 487.5,
|
||||||
borderRadius: 24,
|
borderRadius: 24,
|
||||||
overflow: "hidden",
|
overflow: "hidden",
|
||||||
position: "relative",
|
|
||||||
backgroundColor: "#0C0C0E",
|
backgroundColor: "#0C0C0E",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{/* Exact Vector SVG Artwork */}
|
|
||||||
{currentSvgRaw ? (
|
{currentSvgRaw ? (
|
||||||
<SvgXml xml={currentSvgRaw} width={300} height={487.5} />
|
<SvgXml
|
||||||
) : null}
|
xml={buildSvgWithQr(currentSvgRaw, qr.pngBase64)}
|
||||||
|
width={300}
|
||||||
{/* Centered QR Code Box Overlay */}
|
height={487.5}
|
||||||
<View
|
viewBox="0 0 400 650"
|
||||||
style={{
|
preserveAspectRatio="xMidYMid meet"
|
||||||
position: "absolute",
|
|
||||||
top: 161.25, // (215 / 650) * 487.5
|
|
||||||
left: 67.5, // (90 / 400) * 300
|
|
||||||
width: 165, // (220 / 400) * 300
|
|
||||||
height: 165,
|
|
||||||
borderRadius: 15,
|
|
||||||
backgroundColor: "#FFFFFF",
|
|
||||||
padding: 8,
|
|
||||||
alignItems: "center",
|
|
||||||
justifyContent: "center",
|
|
||||||
shadowColor: "#000",
|
|
||||||
shadowOffset: { width: 0, height: 4 },
|
|
||||||
shadowOpacity: 0.25,
|
|
||||||
shadowRadius: 8,
|
|
||||||
elevation: 4,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Image
|
|
||||||
source={{ uri: `data:image/png;base64,${qr.pngBase64}` }}
|
|
||||||
style={{ width: "100%", height: "100%", borderRadius: 8 }}
|
|
||||||
resizeMode="contain"
|
|
||||||
/>
|
/>
|
||||||
</View>
|
) : null}
|
||||||
</View>
|
</View>
|
||||||
</ViewShot>
|
</ViewShot>
|
||||||
</View>
|
</View>
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 4.5 KiB After Width: | Height: | Size: 4.5 KiB |
|
Before Width: | Height: | Size: 4.0 KiB After Width: | Height: | Size: 4.0 KiB |
|
Before Width: | Height: | Size: 7.1 KiB After Width: | Height: | Size: 7.1 KiB |
@@ -0,0 +1,275 @@
|
|||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
viewBox="0 0 400 650"
|
||||||
|
width="400"
|
||||||
|
height="650"
|
||||||
|
>
|
||||||
|
<g id="layer-background">
|
||||||
|
<!-- Koyu Doku / Kontrast Arka Plan -->
|
||||||
|
<rect
|
||||||
|
id="bg-canvas"
|
||||||
|
x="0"
|
||||||
|
y="0"
|
||||||
|
width="400"
|
||||||
|
height="650"
|
||||||
|
fill="#252C36"
|
||||||
|
/>
|
||||||
|
</g>
|
||||||
|
|
||||||
|
<g id="layer-card">
|
||||||
|
<!-- Sıcak Parşömen / Vintage Krem Ana Kart -->
|
||||||
|
<rect
|
||||||
|
id="main-card"
|
||||||
|
x="20"
|
||||||
|
y="20"
|
||||||
|
width="360"
|
||||||
|
height="610"
|
||||||
|
rx="20"
|
||||||
|
fill="#F6EFE2"
|
||||||
|
stroke="#3A2312"
|
||||||
|
stroke-width="1.5"
|
||||||
|
/>
|
||||||
|
<!-- Çift İnce Vintage Çerçeve -->
|
||||||
|
<rect
|
||||||
|
x="28"
|
||||||
|
y="28"
|
||||||
|
width="344"
|
||||||
|
height="594"
|
||||||
|
rx="14"
|
||||||
|
fill="none"
|
||||||
|
stroke="#8C6239"
|
||||||
|
stroke-width="1"
|
||||||
|
stroke-opacity="0.4"
|
||||||
|
/>
|
||||||
|
<rect
|
||||||
|
x="32"
|
||||||
|
y="32"
|
||||||
|
width="336"
|
||||||
|
height="586"
|
||||||
|
rx="10"
|
||||||
|
fill="none"
|
||||||
|
stroke="#8C6239"
|
||||||
|
stroke-width="0.5"
|
||||||
|
stroke-opacity="0.25"
|
||||||
|
/>
|
||||||
|
</g>
|
||||||
|
|
||||||
|
<g id="layer-icons">
|
||||||
|
<!-- Sol Üst: Botanik Kahve / Çay Dalı -->
|
||||||
|
<g transform="translate(36, 36) scale(0.75)" stroke="#3A2312" stroke-width="1.4" fill="none" stroke-linecap="round">
|
||||||
|
<path d="M 0 0 C 15 15 25 35 30 65" />
|
||||||
|
<path d="M 10 12 C 5 2 20 -2 22 8 C 22 18 10 12 10 12 Z" fill="#EADCC7" />
|
||||||
|
<path d="M 18 27 C 12 20 28 14 32 23 C 32 32 18 27 18 27 Z" fill="#EADCC7" />
|
||||||
|
<path d="M 24 45 C 32 38 45 48 38 56 C 30 62 24 45 24 45 Z" fill="#EADCC7" />
|
||||||
|
</g>
|
||||||
|
|
||||||
|
<!-- Sağ Üst: Kuşbakışı Latte Art Fincanı -->
|
||||||
|
<g transform="translate(340, 52)">
|
||||||
|
<!-- Tabak -->
|
||||||
|
<circle cx="0" cy="0" r="32" fill="#EADCC7" stroke="#3A2312" stroke-width="1.2" />
|
||||||
|
<circle cx="0" cy="0" r="26" fill="#F6EFE2" stroke="#8C6239" stroke-width="0.8" stroke-dasharray="2 2" />
|
||||||
|
<!-- Fincan Kenarı & Kahve -->
|
||||||
|
<circle cx="0" cy="0" r="21" fill="#7A4B26" stroke="#3A2312" stroke-width="1.4" />
|
||||||
|
<!-- Latte Art / Rosetta Yaprakları -->
|
||||||
|
<path d="M 0 10 C -8 4 -4 -6 0 -12 C 4 -6 8 4 0 10 Z" fill="#F6EFE2" />
|
||||||
|
<path d="M 0 4 C -5 0 -2 -5 0 -8 C 2 -5 5 0 0 4 Z" fill="#7A4B26" />
|
||||||
|
<circle cx="0" cy="-14" r="1.8" fill="#F6EFE2" />
|
||||||
|
</g>
|
||||||
|
|
||||||
|
<!-- Sol Orta: Küçük Kahve Fincanı & Tabağı -->
|
||||||
|
<g transform="translate(48, 250)">
|
||||||
|
<ellipse cx="0" cy="0" rx="26" ry="26" fill="#EADCC7" stroke="#3A2312" stroke-width="1" />
|
||||||
|
<circle cx="0" cy="0" r="17" fill="#6A3B18" stroke="#3A2312" stroke-width="1.2" />
|
||||||
|
<!-- Basit Kalp Latte Deseni -->
|
||||||
|
<path d="M -5 -3 C -8 -7 0 -10 0 -4 C 0 -10 8 -7 5 -3 C 2 3 0 7 0 7 C 0 7 -2 3 -5 -3 Z" fill="#F6EFE2" />
|
||||||
|
<!-- Fincan Kulpu -->
|
||||||
|
<path d="M 17 -4 C 23 -4 23 4 17 4" fill="none" stroke="#3A2312" stroke-width="1.5" />
|
||||||
|
</g>
|
||||||
|
|
||||||
|
<!-- Sağ Orta: Dikey Yaprak İllüstrasyonu -->
|
||||||
|
<g transform="translate(345, 230) scale(0.7)" stroke="#3A2312" stroke-width="1.3" fill="none">
|
||||||
|
<path d="M 10 0 C 0 30 -5 70 5 110" />
|
||||||
|
<path d="M 6 25 C 18 15 28 28 14 36 C 6 36 6 25 6 25 Z" fill="#EADCC7" />
|
||||||
|
<path d="M 2 60 C -12 48 -20 62 -8 70 C 2 70 2 60 2 60 Z" fill="#EADCC7" />
|
||||||
|
<path d="M 5 88 C 18 78 24 92 12 100 C 5 100 5 88 5 88 Z" fill="#EADCC7" />
|
||||||
|
</g>
|
||||||
|
|
||||||
|
<!-- Üst Başlık Yan Süsleme Çizgileri -->
|
||||||
|
<path d="M 142 66 C 148 64 154 68 160 66" fill="none" stroke="#8C6239" stroke-width="1.2" stroke-linecap="round" />
|
||||||
|
<path d="M 240 66 C 246 68 252 64 258 66" fill="none" stroke="#8C6239" stroke-width="1.2" stroke-linecap="round" />
|
||||||
|
|
||||||
|
<!-- Sol Alt: Çekirdek ve Yaprak Grubu -->
|
||||||
|
<g transform="translate(42, 530) scale(0.65)" stroke="#3A2312" stroke-width="1.4" fill="none">
|
||||||
|
<path d="M 0 110 C 20 70 15 30 0 0" />
|
||||||
|
<path d="M 8 30 C 24 15 35 30 18 42 Z" fill="#EADCC7" />
|
||||||
|
<path d="M 12 70 C 30 55 40 72 20 84 Z" fill="#EADCC7" />
|
||||||
|
</g>
|
||||||
|
|
||||||
|
<!-- Kahve Çekirdekleri Motifleri -->
|
||||||
|
<!-- Sol Alt Çekirdek 1 -->
|
||||||
|
<g transform="translate(108, 595) rotate(-20) scale(0.8)">
|
||||||
|
<ellipse cx="0" cy="0" rx="9" ry="6.5" fill="#CBB296" stroke="#3A2312" stroke-width="1.2" />
|
||||||
|
<path d="M -7 0 Q 0 3 0 0 Q 0 -3 7 0" fill="none" stroke="#3A2312" stroke-width="1.2" />
|
||||||
|
</g>
|
||||||
|
<!-- Sol Alt Çekirdek 2 -->
|
||||||
|
<g transform="translate(128, 584) rotate(35) scale(0.8)">
|
||||||
|
<ellipse cx="0" cy="0" rx="9" ry="6.5" fill="#CBB296" stroke="#3A2312" stroke-width="1.2" />
|
||||||
|
<path d="M -7 0 Q 0 3 0 0 Q 0 -3 7 0" fill="none" stroke="#3A2312" stroke-width="1.2" />
|
||||||
|
</g>
|
||||||
|
<!-- Sağ Orta Çekirdek -->
|
||||||
|
<g transform="translate(336, 360) rotate(-45) scale(0.7)">
|
||||||
|
<ellipse cx="0" cy="0" rx="9" ry="6.5" fill="#CBB296" stroke="#3A2312" stroke-width="1.2" />
|
||||||
|
<path d="M -7 0 Q 0 3 0 0 Q 0 -3 7 0" fill="none" stroke="#3A2312" stroke-width="1.2" />
|
||||||
|
</g>
|
||||||
|
|
||||||
|
<!-- Sağ Alt: Sıcak Kahve Fincanı İllüstrasyonu -->
|
||||||
|
<g transform="translate(325, 590)">
|
||||||
|
<!-- Tabak -->
|
||||||
|
<ellipse cx="0" cy="18" rx="28" ry="7" fill="#EADCC7" stroke="#3A2312" stroke-width="1.2" />
|
||||||
|
<!-- Fincan Gövdesi -->
|
||||||
|
<path d="M -18 2 C -18 16 18 16 18 2 Z" fill="#F6EFE2" stroke="#3A2312" stroke-width="1.4" />
|
||||||
|
<ellipse cx="0" cy="2" rx="18" ry="4" fill="#6A3B18" stroke="#3A2312" stroke-width="1.2" />
|
||||||
|
<!-- Kulp -->
|
||||||
|
<path d="M 16 4 C 24 4 24 12 16 14" fill="none" stroke="#3A2312" stroke-width="1.3" />
|
||||||
|
<!-- Buhar Çizgileri -->
|
||||||
|
<path d="M -6 -4 C -8 -9 -4 -12 -6 -17" fill="none" stroke="#8C6239" stroke-width="1" stroke-linecap="round" />
|
||||||
|
<path d="M 2 -5 C 0 -10 4 -13 2 -19" fill="none" stroke="#8C6239" stroke-width="1" stroke-linecap="round" />
|
||||||
|
</g>
|
||||||
|
|
||||||
|
<!-- QR Alanı İnce Köşe Süsleri -->
|
||||||
|
<path d="M 80 205 C 80 195 80 195 90 195" fill="none" stroke="#3A2312" stroke-width="2" stroke-linecap="round" />
|
||||||
|
<path d="M 320 205 C 320 195 320 195 310 195" fill="none" stroke="#3A2312" stroke-width="2" stroke-linecap="round" />
|
||||||
|
<path d="M 80 445 C 80 455 80 455 90 455" fill="none" stroke="#3A2312" stroke-width="2" stroke-linecap="round" />
|
||||||
|
<path d="M 320 445 C 320 455 320 455 310 455" fill="none" stroke="#3A2312" stroke-width="2" stroke-linecap="round" />
|
||||||
|
|
||||||
|
<!-- Alt Est. Bölümü Çizgileri -->
|
||||||
|
<line x1="145" y1="608" x2="175" y2="608" stroke="#8C6239" stroke-width="0.8" />
|
||||||
|
<line x1="225" y1="608" x2="255" y2="608" stroke="#8C6239" stroke-width="0.8" />
|
||||||
|
</g>
|
||||||
|
|
||||||
|
<g id="qr-placeholder" transform="translate(90, 215)">
|
||||||
|
<!-- QR Kod Arka Planı (Hafif Sıcak Ton) -->
|
||||||
|
<rect
|
||||||
|
width="220"
|
||||||
|
height="220"
|
||||||
|
rx="20"
|
||||||
|
fill="#FBF8F2"
|
||||||
|
stroke="#D8C7B0"
|
||||||
|
stroke-width="1.5"
|
||||||
|
/>
|
||||||
|
<rect
|
||||||
|
x="10"
|
||||||
|
y="10"
|
||||||
|
width="200"
|
||||||
|
height="200"
|
||||||
|
rx="14"
|
||||||
|
fill="#F5EFE6"
|
||||||
|
stroke="#D8C7B0"
|
||||||
|
stroke-dasharray="4 3"
|
||||||
|
/>
|
||||||
|
<!-- QR Merkezine Kahve Çekirdeği İkonu Rozeti -->
|
||||||
|
<circle cx="110" cy="98" r="18" fill="#FBF8F2" stroke="#8C6239" stroke-width="1" />
|
||||||
|
<g transform="translate(110, 98) scale(0.65)">
|
||||||
|
<ellipse cx="-5" cy="2" rx="7" ry="5" fill="#3A2312" transform="rotate(-30)" />
|
||||||
|
<ellipse cx="5" cy="0" rx="7" ry="5" fill="#3A2312" transform="rotate(40)" />
|
||||||
|
</g>
|
||||||
|
<text
|
||||||
|
x="110"
|
||||||
|
y="132"
|
||||||
|
font-family="Inter, Arial, sans-serif"
|
||||||
|
font-size="11"
|
||||||
|
font-weight="700"
|
||||||
|
letter-spacing="1"
|
||||||
|
fill="#8C6239"
|
||||||
|
text-anchor="middle"
|
||||||
|
>QR KOD ALANI</text>
|
||||||
|
</g>
|
||||||
|
|
||||||
|
<g id="layer-texts">
|
||||||
|
<!-- Üst Başlık Takısı -->
|
||||||
|
<text
|
||||||
|
x="200"
|
||||||
|
y="70"
|
||||||
|
font-family="'Playfair Display', Georgia, serif"
|
||||||
|
font-size="14"
|
||||||
|
font-style="italic"
|
||||||
|
font-weight="700"
|
||||||
|
letter-spacing="4"
|
||||||
|
fill="#3A2312"
|
||||||
|
text-anchor="middle"
|
||||||
|
>THE</text>
|
||||||
|
|
||||||
|
<!-- Ana Tipografi: El Yazısı / Retro Tabela Havası -->
|
||||||
|
<text
|
||||||
|
x="200"
|
||||||
|
y="114"
|
||||||
|
font-family="'Playfair Display', 'Brush Script MT', 'Great Vibes', Georgia, cursive, serif"
|
||||||
|
font-size="34"
|
||||||
|
font-weight="800"
|
||||||
|
letter-spacing="0.5"
|
||||||
|
fill="#2C1A0E"
|
||||||
|
text-anchor="middle"
|
||||||
|
>The Daily Grind</text>
|
||||||
|
|
||||||
|
<!-- Alt Kategori Başlığı -->
|
||||||
|
<text
|
||||||
|
x="200"
|
||||||
|
y="146"
|
||||||
|
font-family="Inter, Arial, sans-serif"
|
||||||
|
font-size="13"
|
||||||
|
font-weight="800"
|
||||||
|
letter-spacing="3"
|
||||||
|
fill="#3A2312"
|
||||||
|
text-anchor="middle"
|
||||||
|
>COFFEE & KITCHEN</text>
|
||||||
|
|
||||||
|
<!-- Büyük ve Belirgin Çağrı Metni -->
|
||||||
|
<text
|
||||||
|
x="200"
|
||||||
|
y="488"
|
||||||
|
font-family="Inter, Arial, sans-serif"
|
||||||
|
font-size="19"
|
||||||
|
font-weight="900"
|
||||||
|
letter-spacing="1.5"
|
||||||
|
fill="#2C1A0E"
|
||||||
|
text-anchor="middle"
|
||||||
|
>SCAN TO VIEW MENU</text>
|
||||||
|
|
||||||
|
<!-- Restoran & WiFi Bilgi Bandı -->
|
||||||
|
<text
|
||||||
|
x="200"
|
||||||
|
y="520"
|
||||||
|
font-family="Inter, Arial, sans-serif"
|
||||||
|
font-size="10.5"
|
||||||
|
font-weight="700"
|
||||||
|
letter-spacing="0.5"
|
||||||
|
fill="#3A2312"
|
||||||
|
text-anchor="middle"
|
||||||
|
>DAILY GRIND CAFE | WIFI: DAILY_GRIND_GUEST</text>
|
||||||
|
|
||||||
|
<!-- Şifre ve Masa Detayı -->
|
||||||
|
<text
|
||||||
|
x="200"
|
||||||
|
y="542"
|
||||||
|
font-family="Inter, Arial, sans-serif"
|
||||||
|
font-size="9"
|
||||||
|
font-weight="600"
|
||||||
|
letter-spacing="1"
|
||||||
|
fill="#8C6239"
|
||||||
|
text-anchor="middle"
|
||||||
|
>ŞİFRE: coffee2026 • MASA NO: 04</text>
|
||||||
|
|
||||||
|
<!-- Kuruluş / Est. Yılı -->
|
||||||
|
<text
|
||||||
|
x="200"
|
||||||
|
y="611"
|
||||||
|
font-family="'Playfair Display', Georgia, serif"
|
||||||
|
font-size="11"
|
||||||
|
font-style="italic"
|
||||||
|
font-weight="700"
|
||||||
|
letter-spacing="2"
|
||||||
|
fill="#3A2312"
|
||||||
|
text-anchor="middle"
|
||||||
|
>EST. 2018</text>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 9.3 KiB |