create-croissant 0.1.47 → 0.1.48
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/template/apps/platform/src/routes/api/auth/$.ts +1 -1
- package/template/apps/platform/src/routes/api/rpc.$.ts +2 -2
- package/template/package.json +2 -7
- package/template/tsconfig.json +1 -2
- package/template/apps/mobile/.vscode/extensions.json +0 -1
- package/template/apps/mobile/.vscode/settings.json +0 -7
- package/template/apps/mobile/README.md +0 -50
- package/template/apps/mobile/app/(tabs)/_layout.tsx +0 -43
- package/template/apps/mobile/app/(tabs)/account.tsx +0 -147
- package/template/apps/mobile/app/(tabs)/explore.tsx +0 -345
- package/template/apps/mobile/app/(tabs)/index.tsx +0 -112
- package/template/apps/mobile/app/_layout.tsx +0 -43
- package/template/apps/mobile/app/index.tsx +0 -129
- package/template/apps/mobile/app/login.tsx +0 -135
- package/template/apps/mobile/app/signup.tsx +0 -144
- package/template/apps/mobile/app.json +0 -56
- package/template/apps/mobile/assets/images/android-icon-background.png +0 -0
- package/template/apps/mobile/assets/images/android-icon-foreground.png +0 -0
- package/template/apps/mobile/assets/images/android-icon-monochrome.png +0 -0
- package/template/apps/mobile/assets/images/favicon.png +0 -0
- package/template/apps/mobile/assets/images/icon.png +0 -0
- package/template/apps/mobile/assets/images/partial-react-logo.png +0 -0
- package/template/apps/mobile/assets/images/react-logo.png +0 -0
- package/template/apps/mobile/assets/images/react-logo@2x.png +0 -0
- package/template/apps/mobile/assets/images/react-logo@3x.png +0 -0
- package/template/apps/mobile/assets/images/splash-icon.png +0 -0
- package/template/apps/mobile/components/external-link.tsx +0 -25
- package/template/apps/mobile/components/haptic-tab.tsx +0 -18
- package/template/apps/mobile/components/hello-wave.tsx +0 -20
- package/template/apps/mobile/components/parallax-scroll-view.tsx +0 -81
- package/template/apps/mobile/components/themed-text.tsx +0 -60
- package/template/apps/mobile/components/themed-view.tsx +0 -14
- package/template/apps/mobile/components/ui/button.tsx +0 -86
- package/template/apps/mobile/components/ui/collapsible.tsx +0 -46
- package/template/apps/mobile/components/ui/icon-symbol.ios.tsx +0 -32
- package/template/apps/mobile/components/ui/icon-symbol.tsx +0 -41
- package/template/apps/mobile/components/ui/input.tsx +0 -56
- package/template/apps/mobile/constants/theme.ts +0 -53
- package/template/apps/mobile/hooks/use-color-scheme.ts +0 -1
- package/template/apps/mobile/hooks/use-color-scheme.web.ts +0 -21
- package/template/apps/mobile/hooks/use-theme-color.ts +0 -21
- package/template/apps/mobile/lib/auth-client.ts +0 -14
- package/template/apps/mobile/lib/orpc.ts +0 -28
- package/template/apps/mobile/package.json +0 -57
- package/template/apps/mobile/scripts/reset-project.js +0 -112
- package/template/apps/mobile/tsconfig.json +0 -13
|
@@ -1,112 +0,0 @@
|
|
|
1
|
-
import { useEffect } from "react";
|
|
2
|
-
import { View, Text, StyleSheet, ScrollView, Platform } from "react-native";
|
|
3
|
-
import { useRouter } from "expo-router";
|
|
4
|
-
import { authClient } from "@/lib/auth-client";
|
|
5
|
-
import { useSecretData } from "@workspace/orpc/react";
|
|
6
|
-
import { Button } from "@/components/ui/button";
|
|
7
|
-
|
|
8
|
-
export default function DashboardScreen() {
|
|
9
|
-
const router = useRouter();
|
|
10
|
-
const { data: session, isPending: isAuthPending } = authClient.useSession();
|
|
11
|
-
|
|
12
|
-
const { data: secretData, isLoading: isLoadingSecret, error: secretError } = useSecretData({
|
|
13
|
-
enabled: !!session,
|
|
14
|
-
});
|
|
15
|
-
|
|
16
|
-
useEffect(() => {
|
|
17
|
-
if (!isAuthPending && !session) {
|
|
18
|
-
router.replace("/login");
|
|
19
|
-
}
|
|
20
|
-
}, [session, isAuthPending, router]);
|
|
21
|
-
|
|
22
|
-
const handleSignOut = async () => {
|
|
23
|
-
await authClient.signOut();
|
|
24
|
-
router.replace("/");
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
if (isAuthPending) {
|
|
28
|
-
return (
|
|
29
|
-
<View style={styles.center}>
|
|
30
|
-
<Text>Loading dashboard...</Text>
|
|
31
|
-
</View>
|
|
32
|
-
);
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
return (
|
|
36
|
-
<ScrollView style={styles.container} contentContainerStyle={styles.content}>
|
|
37
|
-
<Text style={styles.title}>Dashboard</Text>
|
|
38
|
-
<Text style={styles.welcome}>Welcome, {session?.user?.name}!</Text>
|
|
39
|
-
<Text style={styles.description}>
|
|
40
|
-
This is a protected page. Only authenticated users can see this.
|
|
41
|
-
</Text>
|
|
42
|
-
|
|
43
|
-
<View style={styles.secureBox}>
|
|
44
|
-
<Text style={styles.secureTitle}>Secure oRPC Data:</Text>
|
|
45
|
-
{isLoadingSecret ? (
|
|
46
|
-
<Text style={styles.secureContent}>Loading secret data...</Text>
|
|
47
|
-
) : secretError ? (
|
|
48
|
-
<Text style={[styles.secureContent, styles.errorText]}>
|
|
49
|
-
Error: {secretError.message || "Unknown error"}
|
|
50
|
-
</Text>
|
|
51
|
-
) : (
|
|
52
|
-
<Text style={styles.secureContent}>{secretData?.secret}</Text>
|
|
53
|
-
)}
|
|
54
|
-
</View>
|
|
55
|
-
|
|
56
|
-
<Button variant="destructive" onPress={handleSignOut} style={styles.signOutBtn}>
|
|
57
|
-
Sign Out
|
|
58
|
-
</Button>
|
|
59
|
-
</ScrollView>
|
|
60
|
-
);
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
const styles = StyleSheet.create({
|
|
64
|
-
container: {
|
|
65
|
-
flex: 1,
|
|
66
|
-
backgroundColor: "#fff",
|
|
67
|
-
},
|
|
68
|
-
content: {
|
|
69
|
-
padding: 24,
|
|
70
|
-
},
|
|
71
|
-
center: {
|
|
72
|
-
flex: 1,
|
|
73
|
-
justifyContent: "center",
|
|
74
|
-
alignItems: "center",
|
|
75
|
-
},
|
|
76
|
-
title: {
|
|
77
|
-
fontSize: 28,
|
|
78
|
-
fontWeight: "bold",
|
|
79
|
-
marginBottom: 8,
|
|
80
|
-
},
|
|
81
|
-
welcome: {
|
|
82
|
-
fontSize: 18,
|
|
83
|
-
marginBottom: 8,
|
|
84
|
-
},
|
|
85
|
-
description: {
|
|
86
|
-
fontSize: 14,
|
|
87
|
-
color: "#666",
|
|
88
|
-
marginBottom: 24,
|
|
89
|
-
},
|
|
90
|
-
secureBox: {
|
|
91
|
-
padding: 16,
|
|
92
|
-
backgroundColor: "#f9f9f9",
|
|
93
|
-
borderRadius: 8,
|
|
94
|
-
borderWidth: 1,
|
|
95
|
-
borderColor: "#eee",
|
|
96
|
-
marginBottom: 24,
|
|
97
|
-
},
|
|
98
|
-
secureTitle: {
|
|
99
|
-
fontWeight: "600",
|
|
100
|
-
marginBottom: 8,
|
|
101
|
-
},
|
|
102
|
-
secureContent: {
|
|
103
|
-
fontFamily: Platform.OS === "ios" ? "Courier" : "monospace",
|
|
104
|
-
fontSize: 12,
|
|
105
|
-
},
|
|
106
|
-
errorText: {
|
|
107
|
-
color: "#ef4444",
|
|
108
|
-
},
|
|
109
|
-
signOutBtn: {
|
|
110
|
-
marginTop: 12,
|
|
111
|
-
},
|
|
112
|
-
});
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
import { DarkTheme, DefaultTheme, ThemeProvider } from "@react-navigation/native";
|
|
2
|
-
import { Stack } from "expo-router";
|
|
3
|
-
import { StatusBar } from "expo-status-bar";
|
|
4
|
-
import "react-native-reanimated";
|
|
5
|
-
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
|
6
|
-
import { ORPCProvider } from "@workspace/orpc/react";
|
|
7
|
-
import { orpc } from "@/lib/orpc";
|
|
8
|
-
|
|
9
|
-
import { useColorScheme } from "@/hooks/use-color-scheme";
|
|
10
|
-
|
|
11
|
-
const queryClient = new QueryClient({
|
|
12
|
-
defaultOptions: {
|
|
13
|
-
mutations: {
|
|
14
|
-
onError: (error) => {
|
|
15
|
-
console.error("Global Mutation Error:", error);
|
|
16
|
-
},
|
|
17
|
-
},
|
|
18
|
-
},
|
|
19
|
-
});
|
|
20
|
-
|
|
21
|
-
export const unstable_settings = {
|
|
22
|
-
anchor: "(tabs)",
|
|
23
|
-
};
|
|
24
|
-
|
|
25
|
-
export default function RootLayout() {
|
|
26
|
-
const colorScheme = useColorScheme();
|
|
27
|
-
|
|
28
|
-
return (
|
|
29
|
-
<QueryClientProvider client={queryClient}>
|
|
30
|
-
<ORPCProvider client={orpc}>
|
|
31
|
-
<ThemeProvider value={colorScheme === "dark" ? DarkTheme : DefaultTheme}>
|
|
32
|
-
<Stack>
|
|
33
|
-
<Stack.Screen name="index" options={{ headerShown: false }} />
|
|
34
|
-
<Stack.Screen name="(tabs)" options={{ headerShown: false }} />
|
|
35
|
-
<Stack.Screen name="login" options={{ title: "Login" }} />
|
|
36
|
-
<Stack.Screen name="signup" options={{ title: "Sign Up" }} />
|
|
37
|
-
</Stack>
|
|
38
|
-
<StatusBar style="auto" />
|
|
39
|
-
</ThemeProvider>
|
|
40
|
-
</ORPCProvider>
|
|
41
|
-
</QueryClientProvider>
|
|
42
|
-
);
|
|
43
|
-
}
|
|
@@ -1,129 +0,0 @@
|
|
|
1
|
-
import { View, Text, StyleSheet, ScrollView } from "react-native";
|
|
2
|
-
import { useRouter } from "expo-router";
|
|
3
|
-
import { Button } from "@/components/ui/button";
|
|
4
|
-
import { usePlanets, useHello } from "@workspace/orpc/react";
|
|
5
|
-
|
|
6
|
-
export default function LandingScreen() {
|
|
7
|
-
const router = useRouter();
|
|
8
|
-
const { data: helloData, isLoading: isHelloLoading } = useHello("Croissant Stack Mobile");
|
|
9
|
-
const { data: planets = [], isLoading: isPlanetsLoading } = usePlanets();
|
|
10
|
-
|
|
11
|
-
return (
|
|
12
|
-
<ScrollView style={styles.container} contentContainerStyle={styles.content}>
|
|
13
|
-
<View style={styles.header}>
|
|
14
|
-
<Text style={styles.title}>Project ready!</Text>
|
|
15
|
-
<Text style={styles.subtitle}>
|
|
16
|
-
oRPC integration: <Text style={styles.bold}>
|
|
17
|
-
{isHelloLoading ? "Loading..." : helloData?.message}
|
|
18
|
-
</Text>
|
|
19
|
-
</Text>
|
|
20
|
-
</View>
|
|
21
|
-
|
|
22
|
-
<View style={styles.section}>
|
|
23
|
-
<Text style={styles.sectionTitle}>Planets from Database:</Text>
|
|
24
|
-
{isPlanetsLoading ? (
|
|
25
|
-
<Text style={styles.loading}>Loading planets...</Text>
|
|
26
|
-
) : planets.length === 0 ? (
|
|
27
|
-
<Text style={styles.empty}>
|
|
28
|
-
No planets found in the database. Run `db:push` and seed data if needed.
|
|
29
|
-
</Text>
|
|
30
|
-
) : (
|
|
31
|
-
planets.map((planet: any) => (
|
|
32
|
-
<View key={planet.id} style={styles.planetCard}>
|
|
33
|
-
<Text style={styles.planetName}>{planet.name}</Text>
|
|
34
|
-
<Text style={styles.planetDesc}>{planet.description}</Text>
|
|
35
|
-
</View>
|
|
36
|
-
))
|
|
37
|
-
)}
|
|
38
|
-
</View>
|
|
39
|
-
|
|
40
|
-
<View style={styles.footer}>
|
|
41
|
-
<Button onPress={() => router.push("/login")} style={styles.button}>
|
|
42
|
-
Go to Login
|
|
43
|
-
</Button>
|
|
44
|
-
<Button
|
|
45
|
-
onPress={() => router.push("/(tabs)")}
|
|
46
|
-
variant="outline"
|
|
47
|
-
style={styles.button}
|
|
48
|
-
>
|
|
49
|
-
Go to Dashboard
|
|
50
|
-
</Button>
|
|
51
|
-
<Text style={styles.footerText}>
|
|
52
|
-
You may now add components and start building.
|
|
53
|
-
</Text>
|
|
54
|
-
</View>
|
|
55
|
-
</ScrollView>
|
|
56
|
-
);
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
const styles = StyleSheet.create({
|
|
60
|
-
container: {
|
|
61
|
-
flex: 1,
|
|
62
|
-
backgroundColor: "#fff",
|
|
63
|
-
},
|
|
64
|
-
content: {
|
|
65
|
-
padding: 24,
|
|
66
|
-
paddingTop: 60,
|
|
67
|
-
},
|
|
68
|
-
header: {
|
|
69
|
-
marginBottom: 32,
|
|
70
|
-
},
|
|
71
|
-
title: {
|
|
72
|
-
fontSize: 28,
|
|
73
|
-
fontWeight: "bold",
|
|
74
|
-
marginBottom: 8,
|
|
75
|
-
},
|
|
76
|
-
subtitle: {
|
|
77
|
-
fontSize: 16,
|
|
78
|
-
color: "#666",
|
|
79
|
-
},
|
|
80
|
-
bold: {
|
|
81
|
-
fontWeight: "bold",
|
|
82
|
-
color: "#000",
|
|
83
|
-
},
|
|
84
|
-
section: {
|
|
85
|
-
marginBottom: 32,
|
|
86
|
-
},
|
|
87
|
-
sectionTitle: {
|
|
88
|
-
fontSize: 20,
|
|
89
|
-
fontWeight: "600",
|
|
90
|
-
marginBottom: 16,
|
|
91
|
-
},
|
|
92
|
-
planetCard: {
|
|
93
|
-
padding: 16,
|
|
94
|
-
borderRadius: 8,
|
|
95
|
-
borderWidth: 1,
|
|
96
|
-
borderColor: "#eee",
|
|
97
|
-
marginBottom: 12,
|
|
98
|
-
backgroundColor: "#fafafa",
|
|
99
|
-
},
|
|
100
|
-
planetName: {
|
|
101
|
-
fontSize: 16,
|
|
102
|
-
fontWeight: "bold",
|
|
103
|
-
marginBottom: 4,
|
|
104
|
-
},
|
|
105
|
-
planetDesc: {
|
|
106
|
-
fontSize: 14,
|
|
107
|
-
color: "#666",
|
|
108
|
-
},
|
|
109
|
-
loading: {
|
|
110
|
-
fontStyle: "italic",
|
|
111
|
-
color: "#999",
|
|
112
|
-
},
|
|
113
|
-
empty: {
|
|
114
|
-
fontStyle: "italic",
|
|
115
|
-
color: "#999",
|
|
116
|
-
},
|
|
117
|
-
footer: {
|
|
118
|
-
gap: 12,
|
|
119
|
-
},
|
|
120
|
-
button: {
|
|
121
|
-
width: "100%",
|
|
122
|
-
},
|
|
123
|
-
footerText: {
|
|
124
|
-
marginTop: 16,
|
|
125
|
-
textAlign: "center",
|
|
126
|
-
color: "#999",
|
|
127
|
-
fontSize: 14,
|
|
128
|
-
},
|
|
129
|
-
});
|
|
@@ -1,135 +0,0 @@
|
|
|
1
|
-
import { useState } from "react";
|
|
2
|
-
import { View, StyleSheet, KeyboardAvoidingView, Platform, ScrollView, Text } from "react-native";
|
|
3
|
-
import { useRouter } from "expo-router";
|
|
4
|
-
import { authClient } from "@/lib/auth-client";
|
|
5
|
-
import { Button } from "@/components/ui/button";
|
|
6
|
-
import { Input } from "@/components/ui/input";
|
|
7
|
-
|
|
8
|
-
export default function LoginScreen() {
|
|
9
|
-
const router = useRouter();
|
|
10
|
-
const [email, setEmail] = useState("");
|
|
11
|
-
const [password, setPassword] = useState("");
|
|
12
|
-
const [loading, setLoading] = useState(false);
|
|
13
|
-
const [error, setError] = useState<string | null>(null);
|
|
14
|
-
|
|
15
|
-
const handleLogin = async () => {
|
|
16
|
-
if (!email || !password) {
|
|
17
|
-
setError("Please fill in all fields");
|
|
18
|
-
return;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
setLoading(true);
|
|
22
|
-
setError(null);
|
|
23
|
-
try {
|
|
24
|
-
const { error } = await authClient.signIn.email({
|
|
25
|
-
email,
|
|
26
|
-
password,
|
|
27
|
-
});
|
|
28
|
-
|
|
29
|
-
if (error) {
|
|
30
|
-
setError(error.message || "Invalid credentials");
|
|
31
|
-
} else {
|
|
32
|
-
router.replace("/(tabs)");
|
|
33
|
-
}
|
|
34
|
-
} catch (err) {
|
|
35
|
-
setError(err instanceof Error ? err.message : "An error occurred during login");
|
|
36
|
-
} finally {
|
|
37
|
-
setLoading(false);
|
|
38
|
-
}
|
|
39
|
-
};
|
|
40
|
-
|
|
41
|
-
return (
|
|
42
|
-
<KeyboardAvoidingView
|
|
43
|
-
behavior={Platform.OS === "ios" ? "padding" : "height"}
|
|
44
|
-
style={styles.container}
|
|
45
|
-
>
|
|
46
|
-
<ScrollView contentContainerStyle={styles.scrollContent}>
|
|
47
|
-
<View style={styles.header}>
|
|
48
|
-
<Text style={styles.title}>Welcome Back</Text>
|
|
49
|
-
<Text style={styles.subtitle}>Sign in to your account</Text>
|
|
50
|
-
</View>
|
|
51
|
-
|
|
52
|
-
<View style={styles.form}>
|
|
53
|
-
<Input
|
|
54
|
-
label="Email"
|
|
55
|
-
placeholder="email@example.com"
|
|
56
|
-
value={email}
|
|
57
|
-
onChangeText={setEmail}
|
|
58
|
-
autoCapitalize="none"
|
|
59
|
-
keyboardType="email-address"
|
|
60
|
-
/>
|
|
61
|
-
|
|
62
|
-
<Input
|
|
63
|
-
label="Password"
|
|
64
|
-
placeholder="••••••••"
|
|
65
|
-
value={password}
|
|
66
|
-
onChangeText={setPassword}
|
|
67
|
-
secureTextEntry
|
|
68
|
-
/>
|
|
69
|
-
|
|
70
|
-
{error && <Text style={styles.errorText}>{error}</Text>}
|
|
71
|
-
|
|
72
|
-
<Button
|
|
73
|
-
onPress={handleLogin}
|
|
74
|
-
loading={loading}
|
|
75
|
-
>
|
|
76
|
-
Sign In
|
|
77
|
-
</Button>
|
|
78
|
-
|
|
79
|
-
<View style={styles.footer}>
|
|
80
|
-
<Text style={styles.footerText}>Don't have an account? </Text>
|
|
81
|
-
<Text
|
|
82
|
-
style={styles.link}
|
|
83
|
-
onPress={() => router.push("/signup")}
|
|
84
|
-
>
|
|
85
|
-
Sign Up
|
|
86
|
-
</Text>
|
|
87
|
-
</View>
|
|
88
|
-
</View>
|
|
89
|
-
</ScrollView>
|
|
90
|
-
</KeyboardAvoidingView>
|
|
91
|
-
);
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
const styles = StyleSheet.create({
|
|
95
|
-
container: {
|
|
96
|
-
flex: 1,
|
|
97
|
-
backgroundColor: "#fff",
|
|
98
|
-
},
|
|
99
|
-
scrollContent: {
|
|
100
|
-
flexGrow: 1,
|
|
101
|
-
padding: 24,
|
|
102
|
-
justifyContent: "center",
|
|
103
|
-
},
|
|
104
|
-
header: {
|
|
105
|
-
marginBottom: 40,
|
|
106
|
-
},
|
|
107
|
-
title: {
|
|
108
|
-
fontSize: 32,
|
|
109
|
-
fontWeight: "bold",
|
|
110
|
-
marginBottom: 8,
|
|
111
|
-
},
|
|
112
|
-
subtitle: {
|
|
113
|
-
fontSize: 16,
|
|
114
|
-
color: "#666",
|
|
115
|
-
},
|
|
116
|
-
form: {
|
|
117
|
-
gap: 20,
|
|
118
|
-
},
|
|
119
|
-
errorText: {
|
|
120
|
-
color: "#ef4444",
|
|
121
|
-
fontSize: 14,
|
|
122
|
-
},
|
|
123
|
-
footer: {
|
|
124
|
-
flexDirection: "row",
|
|
125
|
-
justifyContent: "center",
|
|
126
|
-
marginTop: 20,
|
|
127
|
-
},
|
|
128
|
-
footerText: {
|
|
129
|
-
color: "#666",
|
|
130
|
-
},
|
|
131
|
-
link: {
|
|
132
|
-
color: "#000",
|
|
133
|
-
fontWeight: "bold",
|
|
134
|
-
},
|
|
135
|
-
});
|
|
@@ -1,144 +0,0 @@
|
|
|
1
|
-
import { useState } from "react";
|
|
2
|
-
import { View, StyleSheet, KeyboardAvoidingView, Platform, ScrollView, Text } from "react-native";
|
|
3
|
-
import { useRouter } from "expo-router";
|
|
4
|
-
import { authClient } from "@/lib/auth-client";
|
|
5
|
-
import { Button } from "@/components/ui/button";
|
|
6
|
-
import { Input } from "@/components/ui/input";
|
|
7
|
-
|
|
8
|
-
export default function SignupScreen() {
|
|
9
|
-
const router = useRouter();
|
|
10
|
-
const [name, setName] = useState("");
|
|
11
|
-
const [email, setEmail] = useState("");
|
|
12
|
-
const [password, setPassword] = useState("");
|
|
13
|
-
const [loading, setLoading] = useState(false);
|
|
14
|
-
const [error, setError] = useState<string | null>(null);
|
|
15
|
-
|
|
16
|
-
const handleSignup = async () => {
|
|
17
|
-
if (!name || !email || !password) {
|
|
18
|
-
setError("Please fill in all fields");
|
|
19
|
-
return;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
setLoading(true);
|
|
23
|
-
setError(null);
|
|
24
|
-
try {
|
|
25
|
-
const { error } = await authClient.signUp.email({
|
|
26
|
-
email,
|
|
27
|
-
password,
|
|
28
|
-
name,
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
if (error) {
|
|
32
|
-
setError(error.message || "An error occurred during signup");
|
|
33
|
-
} else {
|
|
34
|
-
router.replace("/(tabs)");
|
|
35
|
-
}
|
|
36
|
-
} catch (err) {
|
|
37
|
-
setError(err instanceof Error ? err.message : "An error occurred during signup");
|
|
38
|
-
} finally {
|
|
39
|
-
setLoading(false);
|
|
40
|
-
}
|
|
41
|
-
};
|
|
42
|
-
|
|
43
|
-
return (
|
|
44
|
-
<KeyboardAvoidingView
|
|
45
|
-
behavior={Platform.OS === "ios" ? "padding" : "height"}
|
|
46
|
-
style={styles.container}
|
|
47
|
-
>
|
|
48
|
-
<ScrollView contentContainerStyle={styles.scrollContent}>
|
|
49
|
-
<View style={styles.header}>
|
|
50
|
-
<Text style={styles.title}>Create Account</Text>
|
|
51
|
-
<Text style={styles.subtitle}>Join the Croissant Stack</Text>
|
|
52
|
-
</View>
|
|
53
|
-
|
|
54
|
-
<View style={styles.form}>
|
|
55
|
-
<Input
|
|
56
|
-
label="Name"
|
|
57
|
-
placeholder="John Doe"
|
|
58
|
-
value={name}
|
|
59
|
-
onChangeText={setName}
|
|
60
|
-
/>
|
|
61
|
-
|
|
62
|
-
<Input
|
|
63
|
-
label="Email"
|
|
64
|
-
placeholder="email@example.com"
|
|
65
|
-
value={email}
|
|
66
|
-
onChangeText={setEmail}
|
|
67
|
-
autoCapitalize="none"
|
|
68
|
-
keyboardType="email-address"
|
|
69
|
-
/>
|
|
70
|
-
|
|
71
|
-
<Input
|
|
72
|
-
label="Password"
|
|
73
|
-
placeholder="••••••••"
|
|
74
|
-
value={password}
|
|
75
|
-
onChangeText={setPassword}
|
|
76
|
-
secureTextEntry
|
|
77
|
-
/>
|
|
78
|
-
|
|
79
|
-
{error && <Text style={styles.errorText}>{error}</Text>}
|
|
80
|
-
|
|
81
|
-
<Button
|
|
82
|
-
onPress={handleSignup}
|
|
83
|
-
loading={loading}
|
|
84
|
-
>
|
|
85
|
-
Sign Up
|
|
86
|
-
</Button>
|
|
87
|
-
|
|
88
|
-
<View style={styles.footer}>
|
|
89
|
-
<Text style={styles.footerText}>Already have an account? </Text>
|
|
90
|
-
<Text
|
|
91
|
-
style={styles.link}
|
|
92
|
-
onPress={() => router.push("/login")}
|
|
93
|
-
>
|
|
94
|
-
Sign In
|
|
95
|
-
</Text>
|
|
96
|
-
</View>
|
|
97
|
-
</View>
|
|
98
|
-
</ScrollView>
|
|
99
|
-
</KeyboardAvoidingView>
|
|
100
|
-
);
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
const styles = StyleSheet.create({
|
|
104
|
-
container: {
|
|
105
|
-
flex: 1,
|
|
106
|
-
backgroundColor: "#fff",
|
|
107
|
-
},
|
|
108
|
-
scrollContent: {
|
|
109
|
-
flexGrow: 1,
|
|
110
|
-
padding: 24,
|
|
111
|
-
justifyContent: "center",
|
|
112
|
-
},
|
|
113
|
-
header: {
|
|
114
|
-
marginBottom: 40,
|
|
115
|
-
},
|
|
116
|
-
title: {
|
|
117
|
-
fontSize: 32,
|
|
118
|
-
fontWeight: "bold",
|
|
119
|
-
marginBottom: 8,
|
|
120
|
-
},
|
|
121
|
-
subtitle: {
|
|
122
|
-
fontSize: 16,
|
|
123
|
-
color: "#666",
|
|
124
|
-
},
|
|
125
|
-
form: {
|
|
126
|
-
gap: 20,
|
|
127
|
-
},
|
|
128
|
-
errorText: {
|
|
129
|
-
color: "#ef4444",
|
|
130
|
-
fontSize: 14,
|
|
131
|
-
},
|
|
132
|
-
footer: {
|
|
133
|
-
flexDirection: "row",
|
|
134
|
-
justifyContent: "center",
|
|
135
|
-
marginTop: 20,
|
|
136
|
-
},
|
|
137
|
-
footerText: {
|
|
138
|
-
color: "#666",
|
|
139
|
-
},
|
|
140
|
-
link: {
|
|
141
|
-
color: "#000",
|
|
142
|
-
fontWeight: "bold",
|
|
143
|
-
},
|
|
144
|
-
});
|
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"expo": {
|
|
3
|
-
"name": "mobile",
|
|
4
|
-
"slug": "mobile",
|
|
5
|
-
"version": "1.0.0",
|
|
6
|
-
"orientation": "portrait",
|
|
7
|
-
"icon": "./assets/images/icon.png",
|
|
8
|
-
"scheme": "mobile",
|
|
9
|
-
"userInterfaceStyle": "automatic",
|
|
10
|
-
"newArchEnabled": true,
|
|
11
|
-
"ios": {
|
|
12
|
-
"supportsTablet": true,
|
|
13
|
-
"infoPlist": {
|
|
14
|
-
"NSAppTransportSecurity": {
|
|
15
|
-
"NSAllowsLocalNetworking": true
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
},
|
|
19
|
-
"android": {
|
|
20
|
-
"adaptiveIcon": {
|
|
21
|
-
"backgroundColor": "#E6F4FE",
|
|
22
|
-
"foregroundImage": "./assets/images/android-icon-foreground.png",
|
|
23
|
-
"backgroundImage": "./assets/images/android-icon-background.png",
|
|
24
|
-
"monochromeImage": "./assets/images/android-icon-monochrome.png"
|
|
25
|
-
},
|
|
26
|
-
"edgeToEdgeEnabled": true,
|
|
27
|
-
"predictiveBackGestureEnabled": false
|
|
28
|
-
},
|
|
29
|
-
"web": {
|
|
30
|
-
"output": "single",
|
|
31
|
-
"favicon": "./assets/images/favicon.png",
|
|
32
|
-
"bundler": "metro"
|
|
33
|
-
},
|
|
34
|
-
"plugins": [
|
|
35
|
-
"expo-router",
|
|
36
|
-
[
|
|
37
|
-
"expo-splash-screen",
|
|
38
|
-
{
|
|
39
|
-
"image": "./assets/images/splash-icon.png",
|
|
40
|
-
"imageWidth": 200,
|
|
41
|
-
"resizeMode": "contain",
|
|
42
|
-
"backgroundColor": "#ffffff",
|
|
43
|
-
"dark": {
|
|
44
|
-
"backgroundColor": "#000000"
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
],
|
|
48
|
-
"expo-font",
|
|
49
|
-
"expo-image",
|
|
50
|
-
"expo-web-browser"
|
|
51
|
-
],
|
|
52
|
-
"experiments": {
|
|
53
|
-
"typedRoutes": true
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
}
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import { Href, Link } from "expo-router";
|
|
2
|
-
import { openBrowserAsync, WebBrowserPresentationStyle } from "expo-web-browser";
|
|
3
|
-
import { type ComponentProps } from "react";
|
|
4
|
-
|
|
5
|
-
type Props = Omit<ComponentProps<typeof Link>, "href"> & { href: Href & string };
|
|
6
|
-
|
|
7
|
-
export function ExternalLink({ href, ...rest }: Props) {
|
|
8
|
-
return (
|
|
9
|
-
<Link
|
|
10
|
-
target="_blank"
|
|
11
|
-
{...rest}
|
|
12
|
-
href={href}
|
|
13
|
-
onPress={async (event) => {
|
|
14
|
-
if (process.env.EXPO_OS !== "web") {
|
|
15
|
-
// Prevent the default behavior of linking to the default browser on native.
|
|
16
|
-
event.preventDefault();
|
|
17
|
-
// Open the link in an in-app browser.
|
|
18
|
-
await openBrowserAsync(href, {
|
|
19
|
-
presentationStyle: WebBrowserPresentationStyle.AUTOMATIC,
|
|
20
|
-
});
|
|
21
|
-
}
|
|
22
|
-
}}
|
|
23
|
-
/>
|
|
24
|
-
);
|
|
25
|
-
}
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import { BottomTabBarButtonProps } from "@react-navigation/bottom-tabs";
|
|
2
|
-
import { PlatformPressable } from "@react-navigation/elements";
|
|
3
|
-
import * as Haptics from "expo-haptics";
|
|
4
|
-
|
|
5
|
-
export function HapticTab(props: BottomTabBarButtonProps) {
|
|
6
|
-
return (
|
|
7
|
-
<PlatformPressable
|
|
8
|
-
{...props}
|
|
9
|
-
onPressIn={(ev) => {
|
|
10
|
-
if (process.env.EXPO_OS === "ios") {
|
|
11
|
-
// Add a soft haptic feedback when pressing down on the tabs.
|
|
12
|
-
Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light);
|
|
13
|
-
}
|
|
14
|
-
props.onPressIn?.(ev);
|
|
15
|
-
}}
|
|
16
|
-
/>
|
|
17
|
-
);
|
|
18
|
-
}
|