create-better-t-stack 3.2.22 → 3.2.23-canary.2b547ed5

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 (84) hide show
  1. package/README.md +2 -2
  2. package/dist/cli.js +1 -1
  3. package/dist/index.d.ts +4 -2
  4. package/dist/index.js +1 -1
  5. package/dist/{src-WIwtBCHf.js → src-ehteLbIu.js} +105 -65
  6. package/package.json +1 -1
  7. package/templates/auth/better-auth/convex/backend/convex/auth.ts.hbs +5 -5
  8. package/templates/auth/better-auth/convex/backend/convex/http.ts.hbs +1 -1
  9. package/templates/auth/better-auth/convex/native/bare/components/sign-in.tsx.hbs +127 -0
  10. package/templates/auth/better-auth/convex/native/bare/components/sign-up.tsx.hbs +138 -0
  11. package/templates/auth/better-auth/convex/native/uniwind/components/sign-in.tsx.hbs +91 -0
  12. package/templates/auth/better-auth/convex/native/uniwind/components/sign-up.tsx.hbs +102 -0
  13. package/templates/auth/better-auth/native/bare/app/(drawer)/index.tsx.hbs +186 -0
  14. package/templates/auth/better-auth/native/bare/components/sign-in.tsx.hbs +131 -0
  15. package/templates/auth/better-auth/native/bare/components/sign-up.tsx.hbs +150 -0
  16. package/templates/auth/better-auth/native/unistyles/app/(drawer)/index.tsx.hbs +9 -1
  17. package/templates/auth/better-auth/native/unistyles/components/sign-in.tsx.hbs +5 -0
  18. package/templates/auth/better-auth/native/unistyles/components/sign-up.tsx.hbs +5 -0
  19. package/templates/auth/better-auth/native/uniwind/app/(drawer)/index.tsx.hbs +123 -0
  20. package/templates/auth/better-auth/native/uniwind/components/sign-in.tsx.hbs +90 -0
  21. package/templates/auth/better-auth/native/uniwind/components/sign-up.tsx.hbs +116 -0
  22. package/templates/auth/better-auth/server/base/src/index.ts.hbs +5 -5
  23. package/templates/examples/ai/native/bare/app/(drawer)/ai.tsx.hbs +287 -0
  24. package/templates/examples/ai/native/{nativewind → bare}/polyfills.js +1 -0
  25. package/templates/examples/ai/native/{nativewind → uniwind}/app/(drawer)/ai.tsx.hbs +52 -51
  26. package/templates/examples/ai/native/uniwind/polyfills.js +26 -0
  27. package/templates/examples/todo/native/bare/app/(drawer)/todos.tsx.hbs +430 -0
  28. package/templates/examples/todo/native/uniwind/app/(drawer)/todos.tsx.hbs +295 -0
  29. package/templates/extras/bunfig.toml.hbs +3 -3
  30. package/templates/frontend/native/bare/_gitignore +18 -0
  31. package/templates/frontend/native/{nativewind → bare}/app/(drawer)/(tabs)/_layout.tsx.hbs +7 -12
  32. package/templates/frontend/native/bare/app/(drawer)/(tabs)/index.tsx.hbs +43 -0
  33. package/templates/frontend/native/bare/app/(drawer)/(tabs)/two.tsx.hbs +43 -0
  34. package/templates/frontend/native/{nativewind → bare}/app/(drawer)/_layout.tsx.hbs +24 -1
  35. package/templates/frontend/native/bare/app/(drawer)/index.tsx.hbs +234 -0
  36. package/templates/frontend/native/bare/app/+not-found.tsx.hbs +65 -0
  37. package/templates/frontend/native/bare/app/_layout.tsx.hbs +163 -0
  38. package/templates/frontend/native/bare/app/modal.tsx.hbs +34 -0
  39. package/templates/frontend/native/{nativewind → bare}/app.json.hbs +1 -0
  40. package/templates/frontend/native/bare/components/container.tsx.hbs +25 -0
  41. package/templates/frontend/native/bare/components/header-button.tsx.hbs +47 -0
  42. package/templates/frontend/native/{nativewind → bare}/components/tabbar-icon.tsx.hbs +1 -0
  43. package/templates/frontend/native/{nativewind → bare}/lib/android-navigation-bar.tsx.hbs +1 -0
  44. package/templates/frontend/native/{nativewind → bare}/lib/constants.ts.hbs +1 -0
  45. package/templates/frontend/native/bare/lib/use-color-scheme.ts.hbs +20 -0
  46. package/templates/frontend/native/bare/metro.config.js.hbs +9 -0
  47. package/templates/frontend/native/{nativewind → bare}/package.json.hbs +1 -2
  48. package/templates/frontend/native/bare/tsconfig.json.hbs +11 -0
  49. package/templates/frontend/native/{nativewind → uniwind}/_gitignore +4 -8
  50. package/templates/frontend/native/uniwind/app/(drawer)/(tabs)/_layout.tsx.hbs +46 -0
  51. package/templates/frontend/native/uniwind/app/(drawer)/(tabs)/index.tsx.hbs +15 -0
  52. package/templates/frontend/native/uniwind/app/(drawer)/(tabs)/two.tsx.hbs +15 -0
  53. package/templates/frontend/native/uniwind/app/(drawer)/_layout.tsx.hbs +83 -0
  54. package/templates/frontend/native/uniwind/app/(drawer)/index.tsx.hbs +151 -0
  55. package/templates/frontend/native/uniwind/app/+not-found.tsx.hbs +32 -0
  56. package/templates/frontend/native/uniwind/app/_layout.tsx.hbs +131 -0
  57. package/templates/frontend/native/uniwind/app/modal.tsx.hbs +53 -0
  58. package/templates/frontend/native/uniwind/app.json.hbs +19 -0
  59. package/templates/frontend/native/uniwind/components/container.tsx.hbs +33 -0
  60. package/templates/frontend/native/uniwind/components/theme-toggle.tsx.hbs +35 -0
  61. package/templates/frontend/native/uniwind/contexts/app-theme-context.tsx.hbs +62 -0
  62. package/templates/frontend/native/uniwind/global.css +5 -0
  63. package/templates/frontend/native/uniwind/metro.config.js.hbs +13 -0
  64. package/templates/frontend/native/uniwind/package.json.hbs +54 -0
  65. package/templates/frontend/native/{nativewind → uniwind}/tsconfig.json.hbs +4 -8
  66. package/templates/auth/better-auth/convex/native/nativewind/components/sign-in.tsx.hbs +0 -86
  67. package/templates/auth/better-auth/convex/native/nativewind/components/sign-up.tsx.hbs +0 -97
  68. package/templates/auth/better-auth/native/nativewind/app/(drawer)/index.tsx.hbs +0 -95
  69. package/templates/auth/better-auth/native/nativewind/components/sign-in.tsx.hbs +0 -93
  70. package/templates/auth/better-auth/native/nativewind/components/sign-up.tsx.hbs +0 -104
  71. package/templates/examples/todo/native/nativewind/app/(drawer)/todos.tsx.hbs +0 -295
  72. package/templates/frontend/native/nativewind/app/(drawer)/(tabs)/index.tsx.hbs +0 -19
  73. package/templates/frontend/native/nativewind/app/(drawer)/(tabs)/two.tsx.hbs +0 -19
  74. package/templates/frontend/native/nativewind/app/(drawer)/index.tsx.hbs +0 -178
  75. package/templates/frontend/native/nativewind/app/+not-found.tsx.hbs +0 -29
  76. package/templates/frontend/native/nativewind/app/_layout.tsx.hbs +0 -175
  77. package/templates/frontend/native/nativewind/app/modal.tsx.hbs +0 -14
  78. package/templates/frontend/native/nativewind/babel.config.js.hbs +0 -14
  79. package/templates/frontend/native/nativewind/components/container.tsx.hbs +0 -8
  80. package/templates/frontend/native/nativewind/components/header-button.tsx.hbs +0 -26
  81. package/templates/frontend/native/nativewind/global.css +0 -50
  82. package/templates/frontend/native/nativewind/lib/use-color-scheme.ts.hbs +0 -12
  83. package/templates/frontend/native/nativewind/metro.config.js.hbs +0 -12
  84. package/templates/frontend/native/nativewind/tailwind.config.js.hbs +0 -59
@@ -0,0 +1,131 @@
1
+ import { authClient } from "@/lib/auth-client";
2
+ {{#if (eq api "trpc")}}
3
+ import { queryClient } from "@/utils/trpc";
4
+ {{/if}}
5
+ {{#if (eq api "orpc")}}
6
+ import { queryClient } from "@/utils/orpc";
7
+ {{/if}}
8
+ import { useState } from "react";
9
+ import {
10
+ ActivityIndicator,
11
+ Text,
12
+ TextInput,
13
+ TouchableOpacity,
14
+ View,
15
+ StyleSheet,
16
+ } from "react-native";
17
+ import { useColorScheme } from "@/lib/use-color-scheme";
18
+ import { NAV_THEME } from "@/lib/constants";
19
+
20
+ function SignIn() {
21
+ const { colorScheme } = useColorScheme();
22
+ const theme = colorScheme === "dark" ? NAV_THEME.dark : NAV_THEME.light;
23
+ const [form, setForm] = useState({ email: "", password: "" });
24
+ const [isLoading, setIsLoading] = useState(false);
25
+ const [error, setError] = useState<string | null>(null);
26
+
27
+ function handleFormChange(field: "email" | "password", value: string) {
28
+ setForm(prev => ({ ...prev, [field]: value }));
29
+ }
30
+
31
+ async function handleLogin() {
32
+ setIsLoading(true);
33
+ setError(null);
34
+
35
+ await authClient.signIn.email(
36
+ {
37
+ email: form.email,
38
+ password: form.password,
39
+ },
40
+ {
41
+ onError(error) {
42
+ setError(error.error?.message || "Failed to sign in");
43
+ setIsLoading(false);
44
+ },
45
+ onSuccess() {
46
+ setForm({ email: "", password: "" });
47
+ {{#if (eq api "orpc")}}
48
+ queryClient.refetchQueries();
49
+ {{/if}}
50
+ {{#if (eq api "trpc")}}
51
+ queryClient.refetchQueries();
52
+ {{/if}}
53
+ },
54
+ onFinished() {
55
+ setIsLoading(false);
56
+ },
57
+ }
58
+ );
59
+ }
60
+
61
+ return (
62
+ <View style={[styles.card, { backgroundColor: theme.card, borderColor: theme.border }]}>
63
+ <Text style={[styles.title, { color: theme.text }]}>Sign In</Text>
64
+
65
+ {error ? (
66
+ <View style={[styles.errorContainer, { backgroundColor: theme.notification + "20" }]}>
67
+ <Text style={[styles.errorText, { color: theme.notification }]}>{error}</Text>
68
+ </View>
69
+ ) : null}
70
+
71
+ <TextInput style={[ styles.input, { color: theme.text, borderColor: theme.border, backgroundColor:
72
+ theme.background }, ]} placeholder="Email" placeholderTextColor={theme.text} value={form.email}
73
+ onChangeText={value=> handleFormChange("email", value)}
74
+ keyboardType="email-address"
75
+ autoCapitalize="none"
76
+ />
77
+
78
+ <TextInput style={[ styles.input, { color: theme.text, borderColor: theme.border, backgroundColor:
79
+ theme.background }, ]} placeholder="Password" placeholderTextColor={theme.text} value={form.password}
80
+ onChangeText={value=> handleFormChange("password", value)}
81
+ secureTextEntry
82
+ />
83
+
84
+ <TouchableOpacity onPress={handleLogin} disabled={isLoading} style={[ styles.button, { backgroundColor:
85
+ theme.primary, opacity: isLoading ? 0.5 : 1 }, ]}>
86
+ {isLoading ? (
87
+ <ActivityIndicator size="small" color="#ffffff" />
88
+ ) : (
89
+ <Text style={styles.buttonText}>Sign In</Text>
90
+ )}
91
+ </TouchableOpacity>
92
+ </View>
93
+ );
94
+ }
95
+
96
+ const styles = StyleSheet.create({
97
+ card: {
98
+ marginTop: 16,
99
+ padding: 16,
100
+ borderWidth: 1,
101
+ },
102
+ title: {
103
+ fontSize: 18,
104
+ fontWeight: "bold",
105
+ marginBottom: 12,
106
+ },
107
+ errorContainer: {
108
+ marginBottom: 12,
109
+ padding: 8,
110
+ },
111
+ errorText: {
112
+ fontSize: 14,
113
+ },
114
+ input: {
115
+ borderWidth: 1,
116
+ padding: 12,
117
+ fontSize: 16,
118
+ marginBottom: 12,
119
+ },
120
+ button: {
121
+ padding: 12,
122
+ alignItems: "center",
123
+ justifyContent: "center",
124
+ },
125
+ buttonText: {
126
+ color: "#ffffff",
127
+ fontSize: 16,
128
+ },
129
+ });
130
+
131
+ export { SignIn };
@@ -0,0 +1,150 @@
1
+ import { authClient } from "@/lib/auth-client";
2
+ {{#if (eq api "trpc")}}
3
+ import { queryClient } from "@/utils/trpc";
4
+ {{/if}}
5
+ {{#if (eq api "orpc")}}
6
+ import { queryClient } from "@/utils/orpc";
7
+ {{/if}}
8
+ import { useState } from "react";
9
+ import {
10
+ ActivityIndicator,
11
+ Text,
12
+ TextInput,
13
+ TouchableOpacity,
14
+ View,
15
+ StyleSheet,
16
+ } from "react-native";
17
+ import { useColorScheme } from "@/lib/use-color-scheme";
18
+ import { NAV_THEME } from "@/lib/constants";
19
+
20
+ function SignUp() {
21
+ const { colorScheme } = useColorScheme();
22
+ const theme = colorScheme === "dark" ? NAV_THEME.dark : NAV_THEME.light;
23
+ const [name, setName] = useState("");
24
+ const [email, setEmail] = useState("");
25
+ const [password, setPassword] = useState("");
26
+ const [isLoading, setIsLoading] = useState(false);
27
+ const [error, setError] = useState<string | null>(null);
28
+
29
+ async function handleSignUp() {
30
+ setIsLoading(true);
31
+ setError(null);
32
+
33
+ await authClient.signUp.email(
34
+ {
35
+ name,
36
+ email,
37
+ password,
38
+ },
39
+ {
40
+ onError(error) {
41
+ setError(error.error?.message || "Failed to sign up");
42
+ setIsLoading(false);
43
+ },
44
+ onSuccess() {
45
+ setName("");
46
+ setEmail("");
47
+ setPassword("");
48
+ {{#if (eq api "orpc")}}
49
+ queryClient.refetchQueries();
50
+ {{/if}}
51
+ {{#if (eq api "trpc")}}
52
+ queryClient.refetchQueries();
53
+ {{/if}}
54
+ },
55
+ onFinished() {
56
+ setIsLoading(false);
57
+ },
58
+ }
59
+ );
60
+ }
61
+
62
+ return (
63
+ <View style={[styles.card, { backgroundColor: theme.card, borderColor: theme.border }]}>
64
+ <Text style={[styles.title, { color: theme.text }]}>Create Account</Text>
65
+
66
+ {error ? (
67
+ <View style={[styles.errorContainer, { backgroundColor: theme.notification + "20" }]}>
68
+ <Text style={[styles.errorText, { color: theme.notification }]}>{error}</Text>
69
+ </View>
70
+ ) : null}
71
+
72
+ <TextInput
73
+ style={[styles.input, { color: theme.text, borderColor: theme.border, backgroundColor: theme.background }]}
74
+ placeholder="Name"
75
+ placeholderTextColor={theme.text}
76
+ value={name}
77
+ onChangeText={setName}
78
+ />
79
+
80
+ <TextInput
81
+ style={[styles.input, { color: theme.text, borderColor: theme.border, backgroundColor: theme.background }]}
82
+ placeholder="Email"
83
+ placeholderTextColor={theme.text}
84
+ value={email}
85
+ onChangeText={setEmail}
86
+ keyboardType="email-address"
87
+ autoCapitalize="none"
88
+ />
89
+
90
+ <TextInput
91
+ style={[styles.input, { color: theme.text, borderColor: theme.border, backgroundColor: theme.background }]}
92
+ placeholder="Password"
93
+ placeholderTextColor={theme.text}
94
+ value={password}
95
+ onChangeText={setPassword}
96
+ secureTextEntry
97
+ />
98
+
99
+ <TouchableOpacity
100
+ onPress={handleSignUp}
101
+ disabled={isLoading}
102
+ style={[styles.button, { backgroundColor: theme.primary, opacity: isLoading ? 0.5 : 1 }]}
103
+ >
104
+ {isLoading ? (
105
+ <ActivityIndicator size="small" color="#ffffff" />
106
+ ) : (
107
+ <Text style={styles.buttonText}>Sign Up</Text>
108
+ )}
109
+ </TouchableOpacity>
110
+ </View>
111
+ );
112
+ }
113
+
114
+ const styles = StyleSheet.create({
115
+ card: {
116
+ marginTop: 16,
117
+ padding: 16,
118
+ borderWidth: 1,
119
+ },
120
+ title: {
121
+ fontSize: 18,
122
+ fontWeight: "bold",
123
+ marginBottom: 12,
124
+ },
125
+ errorContainer: {
126
+ marginBottom: 12,
127
+ padding: 8,
128
+ },
129
+ errorText: {
130
+ fontSize: 14,
131
+ },
132
+ input: {
133
+ borderWidth: 1,
134
+ padding: 12,
135
+ fontSize: 16,
136
+ marginBottom: 12,
137
+ },
138
+ button: {
139
+ padding: 12,
140
+ alignItems: "center",
141
+ justifyContent: "center",
142
+ },
143
+ buttonText: {
144
+ color: "#ffffff",
145
+ fontSize: 16,
146
+ },
147
+ });
148
+
149
+ export { SignUp };
150
+
@@ -1,5 +1,4 @@
1
1
  import { authClient } from "@/lib/auth-client";
2
- import { useQuery } from "@tanstack/react-query";
3
2
  import { ScrollView, Text, TouchableOpacity, View } from "react-native";
4
3
  import { StyleSheet } from "react-native-unistyles";
5
4
 
@@ -7,9 +6,11 @@ import { Container } from "@/components/container";
7
6
  import { SignIn } from "@/components/sign-in";
8
7
  import { SignUp } from "@/components/sign-up";
9
8
  {{#if (eq api "orpc")}}
9
+ import { useQuery } from "@tanstack/react-query";
10
10
  import { queryClient, orpc } from "@/utils/orpc";
11
11
  {{/if}}
12
12
  {{#if (eq api "trpc")}}
13
+ import { useQuery } from "@tanstack/react-query";
13
14
  import { queryClient, trpc } from "@/utils/trpc";
14
15
  {{/if}}
15
16
 
@@ -43,13 +44,19 @@ export default function Home() {
43
44
  style={styles.signOutButton}
44
45
  onPress={() => {
45
46
  authClient.signOut();
47
+ {{#if (eq api "orpc")}}
48
+ queryClient.invalidateQueries();
49
+ {{/if}}
50
+ {{#if (eq api "trpc")}}
46
51
  queryClient.invalidateQueries();
52
+ {{/if}}
47
53
  }}
48
54
  >
49
55
  <Text style={styles.signOutButtonText}>Sign Out</Text>
50
56
  </TouchableOpacity>
51
57
  </View>
52
58
  ) : null}
59
+ {{#unless (eq api "none")}}
53
60
  <View style={styles.apiStatusCard}>
54
61
  <Text style={styles.cardTitle}>API Status</Text>
55
62
  <View style={styles.apiStatusRow}>
@@ -80,6 +87,7 @@ export default function Home() {
80
87
  </View>
81
88
  )}
82
89
  </View>
90
+ {{/unless}}
83
91
  {!session?.user && (
84
92
  <>
85
93
  <SignIn />
@@ -38,7 +38,12 @@ export function SignIn() {
38
38
  onSuccess: () => {
39
39
  setEmail("");
40
40
  setPassword("");
41
+ {{#if (eq api "orpc")}}
41
42
  queryClient.refetchQueries();
43
+ {{/if}}
44
+ {{#if (eq api "trpc")}}
45
+ queryClient.refetchQueries();
46
+ {{/if}}
42
47
  },
43
48
  onFinished: () => {
44
49
  setIsLoading(false);
@@ -41,7 +41,12 @@ export function SignUp() {
41
41
  setName("");
42
42
  setEmail("");
43
43
  setPassword("");
44
+ {{#if (eq api "orpc")}}
44
45
  queryClient.refetchQueries();
46
+ {{/if}}
47
+ {{#if (eq api "trpc")}}
48
+ queryClient.refetchQueries();
49
+ {{/if}}
45
50
  },
46
51
  onFinished: () => {
47
52
  setIsLoading(false);
@@ -0,0 +1,123 @@
1
+ import { Text, View, Pressable } from "react-native";
2
+ import { Container } from "@/components/container";
3
+ import { authClient } from "@/lib/auth-client";
4
+ import { Ionicons } from "@expo/vector-icons";
5
+ import { Card, Chip, useThemeColor } from "heroui-native";
6
+ import { SignIn } from "@/components/sign-in";
7
+ import { SignUp } from "@/components/sign-up";
8
+ {{#if (eq api "orpc")}}
9
+ import { useQuery } from "@tanstack/react-query";
10
+ import { queryClient, orpc } from "@/utils/orpc";
11
+ {{/if}}
12
+ {{#if (eq api "trpc")}}
13
+ import { useQuery } from "@tanstack/react-query";
14
+ import { queryClient, trpc } from "@/utils/trpc";
15
+ {{/if}}
16
+
17
+ export default function Home() {
18
+ {{#if (eq api "orpc")}}
19
+ const healthCheck = useQuery(orpc.healthCheck.queryOptions());
20
+ const privateData = useQuery(orpc.privateData.queryOptions());
21
+ const isConnected = healthCheck?.data === "OK";
22
+ const isLoading = healthCheck?.isLoading;
23
+ {{/if}}
24
+ {{#if (eq api "trpc")}}
25
+ const healthCheck = useQuery(trpc.healthCheck.queryOptions());
26
+ const privateData = useQuery(trpc.privateData.queryOptions());
27
+ const isConnected = healthCheck?.data === "OK";
28
+ const isLoading = healthCheck?.isLoading;
29
+ {{/if}}
30
+ const { data: session } = authClient.useSession();
31
+
32
+ const mutedColor = useThemeColor("muted");
33
+ const successColor = useThemeColor("success");
34
+ const dangerColor = useThemeColor("danger");
35
+ const foregroundColor = useThemeColor("foreground");
36
+
37
+ return (
38
+ <Container className="p-6">
39
+ <View className="py-4 mb-6">
40
+ <Text className="text-4xl font-bold text-foreground mb-2">
41
+ BETTER T STACK
42
+ </Text>
43
+ </View>
44
+
45
+ {session?.user ? (
46
+ <Card variant="secondary" className="mb-6 p-4">
47
+ <Text className="text-foreground text-base mb-2">
48
+ Welcome, <Text className="font-medium">{session.user.name}</Text>
49
+ </Text>
50
+ <Text className="text-muted text-sm mb-4">
51
+ {session.user.email}
52
+ </Text>
53
+ <Pressable className="bg-danger py-3 px-4 rounded-lg self-start active:opacity-70" onPress={()=> {
54
+ authClient.signOut();
55
+ {{#if (eq api "orpc")}}
56
+ queryClient.invalidateQueries();
57
+ {{/if}}
58
+ {{#if (eq api "trpc")}}
59
+ queryClient.invalidateQueries();
60
+ {{/if}}
61
+ }}
62
+ >
63
+ <Text className="text-foreground font-medium">Sign Out</Text>
64
+ </Pressable>
65
+ </Card>
66
+ ) : null}
67
+
68
+ {{#unless (eq api "none")}}
69
+ <Card variant="secondary" className="p-6">
70
+ <View className="flex-row items-center justify-between mb-4">
71
+ <Card.Title>System Status</Card.Title>
72
+ <Chip variant="secondary" color={isConnected ? "success" : "danger" } size="sm">
73
+ <Chip.Label>{isConnected ? "LIVE" : "OFFLINE"}</Chip.Label>
74
+ </Chip>
75
+ </View>
76
+
77
+ <Card className="p-4">
78
+ <View className="flex-row items-center">
79
+ <View className={`w-3 h-3 rounded-full mr-3 ${isConnected ? "bg-success" : "bg-muted" }`} />
80
+ <View className="flex-1">
81
+ <Text className="text-foreground font-medium mb-1">
82
+ {{#if (eq api "orpc")}}ORPC{{else}}TRPC{{/if}} Backend
83
+ </Text>
84
+ <Card.Description>
85
+ {isLoading
86
+ ? "Checking connection..."
87
+ : isConnected
88
+ ? "Connected to API"
89
+ : "API Disconnected"}
90
+ </Card.Description>
91
+ </View>
92
+ {isLoading && (
93
+ <Ionicons name="hourglass-outline" size={20} color={mutedColor} />
94
+ )}
95
+ {!isLoading && isConnected && (
96
+ <Ionicons name="checkmark-circle" size={20} color={successColor} />
97
+ )}
98
+ {!isLoading && !isConnected && (
99
+ <Ionicons name="close-circle" size={20} color={dangerColor} />
100
+ )}
101
+ </View>
102
+ </Card>
103
+ </Card>
104
+
105
+ <Card variant="secondary" className="mt-6 p-4">
106
+ <Card.Title className="mb-3">Private Data</Card.Title>
107
+ {privateData && (
108
+ <Card.Description>
109
+ {privateData.data?.message}
110
+ </Card.Description>
111
+ )}
112
+ </Card>
113
+ {{/unless}}
114
+
115
+ {!session?.user && (
116
+ <>
117
+ <SignIn />
118
+ <SignUp />
119
+ </>
120
+ )}
121
+ </Container>
122
+ );
123
+ }
@@ -0,0 +1,90 @@
1
+ import { authClient } from "@/lib/auth-client";
2
+ {{#if (eq api "trpc")}}
3
+ import { queryClient } from "@/utils/trpc";
4
+ {{/if}}
5
+ {{#if (eq api "orpc")}}
6
+ import { queryClient } from "@/utils/orpc";
7
+ {{/if}}
8
+ import { useState } from "react";
9
+ import {
10
+ ActivityIndicator,
11
+ Text,
12
+ TextInput,
13
+ Pressable,
14
+ View,
15
+ } from "react-native";
16
+ import { Card, useThemeColor } from "heroui-native";
17
+
18
+ function SignIn() {
19
+ const [email, setEmail] = useState("");
20
+ const [password, setPassword] = useState("");
21
+ const [isLoading, setIsLoading] = useState(false);
22
+ const [error, setError] = useState<string | null>(null);
23
+
24
+ const mutedColor = useThemeColor("muted");
25
+ const accentColor = useThemeColor("accent");
26
+ const foregroundColor = useThemeColor("foreground");
27
+ const dangerColor = useThemeColor("danger");
28
+
29
+ async function handleLogin() {
30
+ setIsLoading(true);
31
+ setError(null);
32
+
33
+ await authClient.signIn.email(
34
+ {
35
+ email,
36
+ password,
37
+ },
38
+ {
39
+ onError(error) {
40
+ setError(error.error?.message || "Failed to sign in");
41
+ setIsLoading(false);
42
+ },
43
+ onSuccess() {
44
+ setEmail("");
45
+ setPassword("");
46
+ {{#if (eq api "orpc")}}
47
+ queryClient.refetchQueries();
48
+ {{/if}}
49
+ {{#if (eq api "trpc")}}
50
+ queryClient.refetchQueries();
51
+ {{/if}}
52
+ },
53
+ onFinished() {
54
+ setIsLoading(false);
55
+ },
56
+ }
57
+ );
58
+ }
59
+
60
+ return (
61
+ <Card variant="secondary" className="mt-6 p-4">
62
+ <Card.Title className="mb-4">Sign In</Card.Title>
63
+
64
+ {error ? (
65
+ <View className="mb-4 p-3 bg-danger/10 rounded-lg">
66
+ <Text className="text-danger text-sm">{error}</Text>
67
+ </View>
68
+ ) : null}
69
+
70
+ <TextInput className="mb-3 py-3 px-4 rounded-lg bg-surface text-foreground border border-divider"
71
+ placeholder="Email" value={email} onChangeText={setEmail} placeholderTextColor={mutedColor}
72
+ keyboardType="email-address" autoCapitalize="none" />
73
+
74
+ <TextInput className="mb-4 py-3 px-4 rounded-lg bg-surface text-foreground border border-divider"
75
+ placeholder="Password" value={password} onChangeText={setPassword} placeholderTextColor={mutedColor}
76
+ secureTextEntry />
77
+
78
+ <Pressable onPress={handleLogin} disabled={isLoading}
79
+ className="bg-accent p-4 rounded-lg flex-row justify-center items-center active:opacity-70">
80
+ {isLoading ? (
81
+ <ActivityIndicator size="small" color={foregroundColor} />
82
+ ) : (
83
+ <Text className="text-foreground font-medium">Sign In</Text>
84
+ )}
85
+ </Pressable>
86
+ </Card>
87
+ );
88
+ }
89
+
90
+ export { SignIn };
@@ -0,0 +1,116 @@
1
+ import { authClient } from "@/lib/auth-client";
2
+ {{#if (eq api "trpc")}}
3
+ import { queryClient } from "@/utils/trpc";
4
+ {{/if}}
5
+ {{#if (eq api "orpc")}}
6
+ import { queryClient } from "@/utils/orpc";
7
+ {{/if}}
8
+ import { useState } from "react";
9
+ import {
10
+ ActivityIndicator,
11
+ Text,
12
+ TextInput,
13
+ Pressable,
14
+ View,
15
+ } from "react-native";
16
+ import { Card, useThemeColor } from "heroui-native";
17
+
18
+ function signUpHandler({
19
+ name,
20
+ email,
21
+ password,
22
+ setError,
23
+ setIsLoading,
24
+ setName,
25
+ setEmail,
26
+ setPassword,
27
+ }) {
28
+ setIsLoading(true);
29
+ setError(null);
30
+
31
+ authClient.signUp.email(
32
+ {
33
+ name,
34
+ email,
35
+ password,
36
+ },
37
+ {
38
+ onError(error) {
39
+ setError(error.error?.message || "Failed to sign up");
40
+ setIsLoading(false);
41
+ },
42
+ onSuccess() {
43
+ setName("");
44
+ setEmail("");
45
+ setPassword("");
46
+ {{#if (eq api "orpc")}}
47
+ queryClient.refetchQueries();
48
+ {{/if}}
49
+ {{#if (eq api "trpc")}}
50
+ queryClient.refetchQueries();
51
+ {{/if}}
52
+ },
53
+ onFinished() {
54
+ setIsLoading(false);
55
+ },
56
+ }
57
+ );
58
+ }
59
+
60
+ export function SignUp() {
61
+ const [name, setName] = useState("");
62
+ const [email, setEmail] = useState("");
63
+ const [password, setPassword] = useState("");
64
+ const [isLoading, setIsLoading] = useState(false);
65
+ const [error, setError] = useState<string | null>(null);
66
+
67
+ const mutedColor = useThemeColor("muted");
68
+ const accentColor = useThemeColor("accent");
69
+ const foregroundColor = useThemeColor("foreground");
70
+ const dangerColor = useThemeColor("danger");
71
+
72
+ function handlePress() {
73
+ signUpHandler({
74
+ name,
75
+ email,
76
+ password,
77
+ setError,
78
+ setIsLoading,
79
+ setName,
80
+ setEmail,
81
+ setPassword,
82
+ });
83
+ }
84
+
85
+ return (
86
+ <Card variant="secondary" className="mt-6 p-4">
87
+ <Card.Title className="mb-4">Create Account</Card.Title>
88
+
89
+ {error && (
90
+ <View className="mb-4 p-3 bg-danger/10 rounded-lg">
91
+ <Text className="text-danger text-sm">{error}</Text>
92
+ </View>
93
+ )}
94
+
95
+ <TextInput className="mb-3 py-3 px-4 rounded-lg bg-surface text-foreground border border-divider" placeholder="Name"
96
+ value={name} onChangeText={setName} placeholderTextColor={mutedColor} />
97
+
98
+ <TextInput className="mb-3 py-3 px-4 rounded-lg bg-surface text-foreground border border-divider"
99
+ placeholder="Email" value={email} onChangeText={setEmail} placeholderTextColor={mutedColor}
100
+ keyboardType="email-address" autoCapitalize="none" />
101
+
102
+ <TextInput className="mb-4 py-3 px-4 rounded-lg bg-surface text-foreground border border-divider"
103
+ placeholder="Password" value={password} onChangeText={setPassword} placeholderTextColor={mutedColor}
104
+ secureTextEntry />
105
+
106
+ <Pressable onPress={handlePress} disabled={isLoading}
107
+ className="bg-accent p-4 rounded-lg flex-row justify-center items-center active:opacity-70">
108
+ {isLoading ? (
109
+ <ActivityIndicator size="small" color={foregroundColor} />
110
+ ) : (
111
+ <Text className="text-foreground font-medium">Sign Up</Text>
112
+ )}
113
+ </Pressable>
114
+ </Card>
115
+ );
116
+ }