// Standalone, no-login demo (no backend needed): take/pick a photo → it's your // card front; swipe → the back reveals a QR code + your info. Everything is // built in-memory on the device. import * as ImagePicker from "expo-image-picker"; import { Stack } from "expo-router"; import { StatusBar } from "expo-status-bar"; import { useState } from "react"; import { Image, Pressable, ScrollView, StyleSheet, Text, TextInput, View, } from "react-native"; import { CardViewer } from "../src/components/card/CardViewer"; import { newLayerId } from "../src/stores/cardStore"; import { CardDefinition, DEFAULT_SETTINGS, TextLayer } from "../src/types/card"; function textLayer(text: string, y: number, size: number, color: string): TextLayer { return { id: newLayerId(), type: "text", x: 0.08, y, width: 0.84, height: 0.12, opacity: 1, zIndex: 10, text, fontFamily: "System", fontWeight: 700, fontSize: size, lineHeight: size + 4, letterSpacing: 0, color, align: "left", }; } /// Build the demo card in memory: photo as the full-bleed face, QR + info on the /// back. function buildDemoCard(imageUri: string, name: string, title: string): CardDefinition { return { id: "demo", ownerId: "demo", handle: "demo", version: 1, face: { layers: [ textLayer(name, 0.78, 30, "#ffffff"), title ? textLayer(title, 0.87, 18, "#f0f0f2") : textLayer("", 0.87, 1, "#ffffff"), ], background: { type: "image", value: imageUri }, entryAnimation: "fade", }, back: { layers: [ { id: newLayerId(), type: "qr", x: 0.27, y: 0.14, width: 0.46, height: 0.31, opacity: 1, zIndex: 5, }, textLayer(name, 0.58, 26, "#f5f5f7"), title ? textLayer(title, 0.67, 17, "#9a9aa0") : textLayer("", 0.67, 1, "#fff"), ], background: { type: "solid", value: "#101014" }, entryAnimation: "fade", }, palette: { colors: [] }, settings: DEFAULT_SETTINGS, }; } export default function DemoScreen() { const [imageUri, setImageUri] = useState(null); const [name, setName] = useState("Omar Sobh"); const [title, setTitle] = useState("Founder & CEO"); const [url, setUrl] = useState("https://cardclaws.com/omar"); const [showing, setShowing] = useState(false); const pickFromLibrary = async () => { const res = await ImagePicker.launchImageLibraryAsync({ mediaTypes: ImagePicker.MediaTypeOptions.Images, allowsEditing: true, aspect: [2, 3], quality: 0.9, }); if (!res.canceled) setImageUri(res.assets[0].uri); }; const takePhoto = async () => { const perm = await ImagePicker.requestCameraPermissionsAsync(); if (!perm.granted) return; const res = await ImagePicker.launchCameraAsync({ allowsEditing: true, aspect: [2, 3], quality: 0.9, }); if (!res.canceled) setImageUri(res.assets[0].uri); }; if (showing && imageUri) { const card = buildDemoCard(imageUri, name, title); return ( {/* Hide the header + status bar so the card is truly full-bleed. */} ); } return ( Make your card {imageUri ? ( ) : ( Your photo becomes the card front )} Take photo Choose photo setShowing(true)} > Show my card ); } function Field({ label, ...props }: { label: string } & React.ComponentProps) { return ( {label} ); } const styles = StyleSheet.create({ root: { flex: 1, backgroundColor: "#0a0a0c" }, content: { padding: 20, gap: 14 }, h1: { color: "#f5f5f7", fontSize: 28, fontWeight: "800" }, preview: { width: "100%", aspectRatio: 2 / 3, borderRadius: 20, backgroundColor: "#15151a", maxHeight: 360, alignSelf: "center", }, previewEmpty: { alignItems: "center", justifyContent: "center" }, previewHint: { color: "#6b6b70" }, photoRow: { flexDirection: "row", gap: 12 }, photoBtn: { flex: 1, backgroundColor: "#222228", borderRadius: 14, paddingVertical: 14, alignItems: "center", }, photoText: { color: "#f5f5f7", fontWeight: "600", fontSize: 16 }, field: { gap: 6 }, label: { color: "#9a9aa0", fontSize: 14 }, input: { backgroundColor: "#15151a", color: "#f5f5f7", borderRadius: 12, paddingHorizontal: 14, paddingVertical: 12, fontSize: 16, }, cta: { backgroundColor: "#ff3b30", borderRadius: 16, paddingVertical: 16, alignItems: "center", marginTop: 8, }, ctaDisabled: { opacity: 0.4 }, ctaText: { color: "#fff", fontWeight: "700", fontSize: 17 }, viewerRoot: { flex: 1, backgroundColor: "#0a0a0c" }, swipeHint: { position: "absolute", bottom: 90, alignSelf: "center", color: "#9a9aa0", fontSize: 14, }, editBtn: { position: "absolute", bottom: 36, alignSelf: "center", backgroundColor: "#222228", borderRadius: 20, paddingHorizontal: 24, paddingVertical: 12, }, editText: { color: "#f5f5f7", fontWeight: "600" }, });