create-croissant 0.1.47 → 0.1.49

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.
Files changed (37) hide show
  1. package/package.json +1 -1
  2. package/template/apps/mobile/README.md +3 -3
  3. package/template/apps/mobile/app/(tabs)/_layout.tsx +17 -25
  4. package/template/apps/mobile/app/(tabs)/explore.tsx +104 -337
  5. package/template/apps/mobile/app/(tabs)/index.tsx +85 -99
  6. package/template/apps/mobile/app/_layout.tsx +13 -32
  7. package/template/apps/mobile/app/modal.tsx +29 -0
  8. package/template/apps/mobile/app.json +6 -14
  9. package/template/apps/mobile/components/external-link.tsx +5 -5
  10. package/template/apps/mobile/components/haptic-tab.tsx +4 -4
  11. package/template/apps/mobile/components/hello-wave.tsx +4 -5
  12. package/template/apps/mobile/components/parallax-scroll-view.tsx +13 -15
  13. package/template/apps/mobile/components/themed-text.tsx +14 -14
  14. package/template/apps/mobile/components/themed-view.tsx +3 -3
  15. package/template/apps/mobile/components/ui/collapsible.tsx +13 -14
  16. package/template/apps/mobile/components/ui/icon-symbol.ios.tsx +4 -4
  17. package/template/apps/mobile/components/ui/icon-symbol.tsx +9 -9
  18. package/template/apps/mobile/constants/theme.ts +19 -19
  19. package/template/apps/mobile/hooks/use-color-scheme.ts +1 -1
  20. package/template/apps/mobile/hooks/use-color-scheme.web.ts +3 -3
  21. package/template/apps/mobile/hooks/use-theme-color.ts +4 -4
  22. package/template/apps/mobile/package.json +26 -38
  23. package/template/apps/mobile/scripts/reset-project.js +2 -2
  24. package/template/apps/mobile/tsconfig.json +9 -2
  25. package/template/apps/platform/src/routes/api/auth/$.ts +1 -1
  26. package/template/apps/platform/src/routes/api/rpc.$.ts +2 -2
  27. package/template/package.json +12 -14
  28. package/template/pnpm-workspace.yaml +8 -0
  29. package/template/tsconfig.json +1 -2
  30. package/template/apps/mobile/app/(tabs)/account.tsx +0 -147
  31. package/template/apps/mobile/app/index.tsx +0 -129
  32. package/template/apps/mobile/app/login.tsx +0 -135
  33. package/template/apps/mobile/app/signup.tsx +0 -144
  34. package/template/apps/mobile/components/ui/button.tsx +0 -86
  35. package/template/apps/mobile/components/ui/input.tsx +0 -56
  36. package/template/apps/mobile/lib/auth-client.ts +0 -14
  37. package/template/apps/mobile/lib/orpc.ts +0 -28
@@ -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,86 +0,0 @@
1
- import React from "react";
2
- import { TouchableOpacity, Text, StyleSheet, ActivityIndicator, ViewStyle, StyleProp } from "react-native";
3
-
4
- interface ButtonProps {
5
- onPress: () => void;
6
- children: React.ReactNode;
7
- disabled?: boolean;
8
- loading?: boolean;
9
- variant?: "primary" | "secondary" | "outline" | "destructive";
10
- style?: StyleProp<ViewStyle>;
11
- }
12
-
13
- export function Button({
14
- onPress,
15
- children,
16
- disabled,
17
- loading,
18
- variant = "primary",
19
- style
20
- }: ButtonProps) {
21
- const containerStyle = [
22
- styles.button,
23
- styles[variant],
24
- disabled && styles.disabled,
25
- style,
26
- ];
27
-
28
- const textStyle = [
29
- styles.text,
30
- variant === "outline" && styles.outlineText,
31
- disabled && styles.disabledText,
32
- ];
33
-
34
- return (
35
- <TouchableOpacity
36
- style={containerStyle}
37
- onPress={onPress}
38
- disabled={disabled || loading}
39
- >
40
- {loading ? (
41
- <ActivityIndicator color={variant === "outline" ? "#000" : "#fff"} />
42
- ) : (
43
- <Text style={textStyle}>{children}</Text>
44
- )}
45
- </TouchableOpacity>
46
- );
47
- }
48
-
49
- const styles = StyleSheet.create({
50
- button: {
51
- height: 48,
52
- borderRadius: 8,
53
- justifyContent: "center",
54
- alignItems: "center",
55
- paddingHorizontal: 16,
56
- },
57
- primary: {
58
- backgroundColor: "#000",
59
- },
60
- secondary: {
61
- backgroundColor: "#f0f0f0",
62
- },
63
- outline: {
64
- backgroundColor: "transparent",
65
- borderWidth: 1,
66
- borderColor: "#e0e0e0",
67
- },
68
- destructive: {
69
- backgroundColor: "#ef4444",
70
- },
71
- disabled: {
72
- backgroundColor: "#ccc",
73
- borderColor: "#ccc",
74
- },
75
- text: {
76
- color: "#fff",
77
- fontSize: 16,
78
- fontWeight: "600",
79
- },
80
- outlineText: {
81
- color: "#000",
82
- },
83
- disabledText: {
84
- color: "#999",
85
- },
86
- });
@@ -1,56 +0,0 @@
1
- import React from "react";
2
- import { TextInput, StyleSheet, TextInputProps, View, Text } from "react-native";
3
-
4
- interface InputProps extends TextInputProps {
5
- label?: string;
6
- error?: string;
7
- }
8
-
9
- export function Input({ label, error, style, ...props }: InputProps) {
10
- return (
11
- <View style={styles.container}>
12
- {label && <Text style={styles.label}>{label}</Text>}
13
- <TextInput
14
- style={[
15
- styles.input,
16
- error && styles.inputError,
17
- style,
18
- ]}
19
- placeholderTextColor="#999"
20
- {...props}
21
- />
22
- {error && <Text style={styles.errorText}>{error}</Text>}
23
- </View>
24
- );
25
- }
26
-
27
- const styles = StyleSheet.create({
28
- container: {
29
- width: "100%",
30
- marginBottom: 16,
31
- },
32
- label: {
33
- fontSize: 14,
34
- fontWeight: "500",
35
- marginBottom: 8,
36
- color: "#333",
37
- },
38
- input: {
39
- height: 48,
40
- borderWidth: 1,
41
- borderColor: "#e0e0e0",
42
- borderRadius: 8,
43
- paddingHorizontal: 12,
44
- fontSize: 16,
45
- color: "#000",
46
- backgroundColor: "#fff",
47
- },
48
- inputError: {
49
- borderColor: "#ff4444",
50
- },
51
- errorText: {
52
- color: "#ff4444",
53
- fontSize: 12,
54
- marginTop: 4,
55
- },
56
- });
@@ -1,14 +0,0 @@
1
- import { createAuthClient } from "better-auth/react";
2
- import { expoClient } from "@better-auth/expo/client";
3
- import * as SecureStore from "expo-secure-store";
4
-
5
- export const authClient = createAuthClient({
6
- baseURL: process.env.EXPO_PUBLIC_BETTER_AUTH_URL || "https://platform.local",
7
- plugins: [
8
- expoClient({
9
- scheme: "mobile",
10
- storagePrefix: "mobile",
11
- storage: SecureStore,
12
- }),
13
- ],
14
- });
@@ -1,28 +0,0 @@
1
- import { createORPCClient } from '@orpc/client'
2
- import { RPCLink } from '@orpc/client/fetch'
3
- import { RouterClient } from '@orpc/server'
4
- import { router } from '@workspace/orpc/router'
5
- import { authClient } from './auth-client'
6
-
7
- export const link = new RPCLink({
8
- url: `https://platform.local/api/rpc`,
9
- async fetch(request, init) {
10
- const { fetch } = await import('expo/fetch')
11
- const headers = new Map<string, string>();
12
- const cookies = authClient.getCookie();
13
- if (cookies) {
14
- headers.set("Cookie", cookies);
15
- }
16
-
17
- const resp = await fetch(request.url, {
18
- body: request.body,
19
- headers: Object.fromEntries(headers),
20
- method: request.method,
21
- ...init,
22
- })
23
-
24
- return resp
25
- },
26
- })
27
-
28
- export const orpc: RouterClient<typeof router> = createORPCClient(link)