Initial scaffold: Expo SDK 56, expo-router, expo-sqlite, NativeWind
This commit is contained in:
@@ -0,0 +1,140 @@
|
||||
import { useState } from "react";
|
||||
import { View, Text, Pressable, SafeAreaView } from "react-native";
|
||||
import * as SecureStore from "expo-secure-store";
|
||||
import { router } from "expo-router";
|
||||
import { Colors } from "@/constants/theme";
|
||||
|
||||
const STEPS = [
|
||||
{
|
||||
key: "homeView",
|
||||
question: "When you open the app…",
|
||||
options: [
|
||||
{ value: "dashboard", label: "Show recent incidents", desc: "Dashboard with the last 10 events" },
|
||||
{ value: "capture", label: "Go straight to capture", desc: "New incident form opens immediately" },
|
||||
],
|
||||
},
|
||||
{
|
||||
key: "inputMode",
|
||||
question: "How do you prefer to input incidents?",
|
||||
options: [
|
||||
{ value: "form", label: "Typed form", desc: "Fill in each field manually" },
|
||||
{ value: "voice", label: "Voice dictation", desc: "Speak and let the app transcribe" },
|
||||
],
|
||||
},
|
||||
{
|
||||
key: "gitea",
|
||||
question: "Do you want to push incidents to Gitea?",
|
||||
options: [
|
||||
{ value: "later", label: "Configure later", desc: "Set it up in Settings anytime" },
|
||||
{ value: "now", label: "Set up now", desc: "You'll be redirected to Settings" },
|
||||
],
|
||||
},
|
||||
] as const;
|
||||
|
||||
export default function OnboardingScreen() {
|
||||
const [step, setStep] = useState(0);
|
||||
const [answers, setAnswers] = useState<Record<string, string>>({});
|
||||
|
||||
const current = STEPS[step];
|
||||
|
||||
async function choose(value: string) {
|
||||
const next = { ...answers, [current.key]: value };
|
||||
setAnswers(next);
|
||||
|
||||
if (step < STEPS.length - 1) {
|
||||
setStep((s) => s + 1);
|
||||
} else {
|
||||
await SecureStore.setItemAsync("pref_home_view", next.homeView ?? "dashboard");
|
||||
await SecureStore.setItemAsync("pref_input_mode", next.inputMode ?? "form");
|
||||
await SecureStore.setItemAsync("onboarding_done", "true");
|
||||
|
||||
if (next.gitea === "now") {
|
||||
router.replace("/settings");
|
||||
} else {
|
||||
router.replace("/");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const progress = ((step + 1) / STEPS.length) * 100;
|
||||
|
||||
return (
|
||||
<SafeAreaView style={{ flex: 1, backgroundColor: Colors.bg }}>
|
||||
<View style={{ flex: 1, padding: 24, justifyContent: "center" }}>
|
||||
{/* Progress bar */}
|
||||
<View
|
||||
style={{
|
||||
height: 3,
|
||||
backgroundColor: Colors.border,
|
||||
borderRadius: 2,
|
||||
marginBottom: 48,
|
||||
}}
|
||||
>
|
||||
<View
|
||||
style={{
|
||||
height: 3,
|
||||
width: `${progress}%`,
|
||||
backgroundColor: Colors.primary,
|
||||
borderRadius: 2,
|
||||
}}
|
||||
/>
|
||||
</View>
|
||||
|
||||
<Text
|
||||
style={{
|
||||
color: Colors.textDim,
|
||||
fontSize: 12,
|
||||
fontWeight: "700",
|
||||
textTransform: "uppercase",
|
||||
letterSpacing: 1,
|
||||
marginBottom: 12,
|
||||
}}
|
||||
>
|
||||
{step + 1} / {STEPS.length}
|
||||
</Text>
|
||||
|
||||
<Text
|
||||
style={{
|
||||
color: Colors.text1,
|
||||
fontSize: 26,
|
||||
fontWeight: "700",
|
||||
marginBottom: 32,
|
||||
lineHeight: 34,
|
||||
}}
|
||||
>
|
||||
{current.question}
|
||||
</Text>
|
||||
|
||||
<View style={{ gap: 12 }}>
|
||||
{current.options.map((opt) => (
|
||||
<Pressable
|
||||
key={opt.value}
|
||||
onPress={() => choose(opt.value)}
|
||||
style={{
|
||||
backgroundColor: Colors.surface,
|
||||
borderRadius: 12,
|
||||
padding: 20,
|
||||
borderWidth: 1,
|
||||
borderColor: Colors.border,
|
||||
}}
|
||||
>
|
||||
<Text
|
||||
style={{
|
||||
color: Colors.text1,
|
||||
fontSize: 17,
|
||||
fontWeight: "600",
|
||||
marginBottom: 4,
|
||||
}}
|
||||
>
|
||||
{opt.label}
|
||||
</Text>
|
||||
<Text style={{ color: Colors.text2, fontSize: 14 }}>
|
||||
{opt.desc}
|
||||
</Text>
|
||||
</Pressable>
|
||||
))}
|
||||
</View>
|
||||
</View>
|
||||
</SafeAreaView>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user