create-better-t-stack 3.1.7 → 3.1.8-canary.932e5243

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 (26) hide show
  1. package/dist/cli.js +1 -1
  2. package/dist/index.js +1 -1
  3. package/dist/{src-DeOVz-ZI.js → src-EpWSiOd8.js} +51 -13
  4. package/package.json +1 -1
  5. package/templates/auth/better-auth/convex/backend/convex/auth.ts.hbs +10 -2
  6. package/templates/auth/better-auth/convex/native/base/lib/auth-client.ts.hbs +19 -0
  7. package/templates/auth/better-auth/convex/native/nativewind/components/sign-in.tsx.hbs +86 -0
  8. package/templates/auth/better-auth/convex/native/nativewind/components/sign-up.tsx.hbs +97 -0
  9. package/templates/auth/better-auth/convex/native/unistyles/components/sign-in.tsx.hbs +127 -0
  10. package/templates/auth/better-auth/convex/native/unistyles/components/sign-up.tsx.hbs +145 -0
  11. package/templates/auth/better-auth/native/{native-base → base}/lib/auth-client.ts.hbs +3 -2
  12. package/templates/auth/better-auth/web/react/next/src/app/dashboard/page.tsx.hbs +11 -0
  13. package/templates/frontend/native/nativewind/app/(drawer)/index.tsx.hbs +151 -100
  14. package/templates/frontend/native/nativewind/app/_layout.tsx.hbs +21 -0
  15. package/templates/frontend/native/unistyles/app/(drawer)/index.tsx.hbs +201 -91
  16. package/templates/frontend/native/unistyles/app/_layout.tsx.hbs +28 -0
  17. /package/templates/frontend/native/{native-base → base}/assets/images/android-icon-background.png +0 -0
  18. /package/templates/frontend/native/{native-base → base}/assets/images/android-icon-foreground.png +0 -0
  19. /package/templates/frontend/native/{native-base → base}/assets/images/android-icon-monochrome.png +0 -0
  20. /package/templates/frontend/native/{native-base → base}/assets/images/favicon.png +0 -0
  21. /package/templates/frontend/native/{native-base → base}/assets/images/icon.png +0 -0
  22. /package/templates/frontend/native/{native-base → base}/assets/images/partial-react-logo.png +0 -0
  23. /package/templates/frontend/native/{native-base → base}/assets/images/react-logo.png +0 -0
  24. /package/templates/frontend/native/{native-base → base}/assets/images/react-logo@2x.png +0 -0
  25. /package/templates/frontend/native/{native-base → base}/assets/images/react-logo@3x.png +0 -0
  26. /package/templates/frontend/native/{native-base → base}/assets/images/splash-icon.png +0 -0
package/dist/cli.js CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { createBtsCli } from "./src-DeOVz-ZI.js";
2
+ import { createBtsCli } from "./src-EpWSiOd8.js";
3
3
 
4
4
  //#region src/cli.ts
5
5
  createBtsCli().run();
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
1
  #!/usr/bin/env node
2
- import { builder, createBtsCli, docs, init, router, sponsors } from "./src-DeOVz-ZI.js";
2
+ import { builder, createBtsCli, docs, init, router, sponsors } from "./src-EpWSiOd8.js";
3
3
 
4
4
  export { builder, createBtsCli, docs, init, router, sponsors };
@@ -627,7 +627,9 @@ async function getAuthChoice(auth, hasDatabase, backend, frontend) {
627
627
  const supportedBetterAuthFrontends = frontend?.some((f) => [
628
628
  "tanstack-router",
629
629
  "tanstack-start",
630
- "next"
630
+ "next",
631
+ "native-nativewind",
632
+ "native-unistyles"
631
633
  ].includes(f));
632
634
  const hasClerkCompatibleFrontends = frontend?.some((f) => [
633
635
  "react-router",
@@ -1388,7 +1390,7 @@ const getLatestCLIVersion = () => {
1388
1390
  */
1389
1391
  function isTelemetryEnabled() {
1390
1392
  const BTS_TELEMETRY_DISABLED = process.env.BTS_TELEMETRY_DISABLED;
1391
- const BTS_TELEMETRY = "1";
1393
+ const BTS_TELEMETRY = "0";
1392
1394
  if (BTS_TELEMETRY_DISABLED !== void 0) return BTS_TELEMETRY_DISABLED !== "1";
1393
1395
  if (BTS_TELEMETRY !== void 0) return BTS_TELEMETRY === "1";
1394
1396
  return true;
@@ -1396,8 +1398,8 @@ function isTelemetryEnabled() {
1396
1398
 
1397
1399
  //#endregion
1398
1400
  //#region src/utils/analytics.ts
1399
- const POSTHOG_API_KEY = "phc_8ZUxEwwfKMajJLvxz1daGd931dYbQrwKNficBmsdIrs";
1400
- const POSTHOG_HOST = "https://us.i.posthog.com";
1401
+ const POSTHOG_API_KEY = "random";
1402
+ const POSTHOG_HOST = "random";
1401
1403
  function generateSessionId() {
1402
1404
  const rand = Math.random().toString(36).slice(2);
1403
1405
  return `cli_${Date.now().toString(36)}${rand}`;
@@ -1750,9 +1752,11 @@ function validateConvexConstraints(config, providedFlags) {
1750
1752
  const supportedFrontends = [
1751
1753
  "tanstack-router",
1752
1754
  "tanstack-start",
1753
- "next"
1755
+ "next",
1756
+ "native-nativewind",
1757
+ "native-unistyles"
1754
1758
  ];
1755
- if (!config.frontend?.some((f) => supportedFrontends.includes(f))) exitWithError("Better-Auth with Convex backend is only supported with TanStack Router, TanStack Start, or Next.js frontends. Please use '--auth clerk' or '--auth none'.");
1759
+ if (!config.frontend?.some((f) => supportedFrontends.includes(f))) exitWithError("Better-Auth with Convex backend requires a supported frontend (TanStack Router, TanStack Start, Next.js, or Native).");
1756
1760
  }
1757
1761
  }
1758
1762
  function validateBackendNoneConstraints(config, providedFlags) {
@@ -2798,7 +2802,7 @@ async function setupFrontendTemplates(projectDir, context) {
2798
2802
  if (hasNativeWind || hasUnistyles) {
2799
2803
  const nativeAppDir = path.join(projectDir, "apps/native");
2800
2804
  await fs.ensureDir(nativeAppDir);
2801
- const nativeBaseCommonDir = path.join(PKG_ROOT, "templates/frontend/native/native-base");
2805
+ const nativeBaseCommonDir = path.join(PKG_ROOT, "templates/frontend/native/base");
2802
2806
  if (await fs.pathExists(nativeBaseCommonDir)) await processAndCopyFiles("**/*", nativeBaseCommonDir, nativeAppDir, context);
2803
2807
  let nativeFrameworkPath = "";
2804
2808
  if (hasNativeWind) nativeFrameworkPath = "nativewind";
@@ -2934,6 +2938,17 @@ async function setupAuthTemplate(projectDir, context) {
2934
2938
  if (await fs.pathExists(convexBetterAuthWebSrc)) await processAndCopyFiles("**/*", convexBetterAuthWebSrc, webAppDir, context);
2935
2939
  }
2936
2940
  }
2941
+ if (nativeAppDirExists) {
2942
+ const convexBetterAuthNativeBaseSrc = path.join(PKG_ROOT, "templates/auth/better-auth/convex/native/base");
2943
+ if (await fs.pathExists(convexBetterAuthNativeBaseSrc)) await processAndCopyFiles("**/*", convexBetterAuthNativeBaseSrc, nativeAppDir, context);
2944
+ let nativeFrameworkPath = "";
2945
+ if (hasNativeWind) nativeFrameworkPath = "nativewind";
2946
+ else if (hasUnistyles) nativeFrameworkPath = "unistyles";
2947
+ if (nativeFrameworkPath) {
2948
+ const convexBetterAuthNativeFrameworkSrc = path.join(PKG_ROOT, `templates/auth/better-auth/convex/native/${nativeFrameworkPath}`);
2949
+ if (await fs.pathExists(convexBetterAuthNativeFrameworkSrc)) await processAndCopyFiles("**/*", convexBetterAuthNativeFrameworkSrc, nativeAppDir, context);
2950
+ }
2951
+ }
2937
2952
  return;
2938
2953
  }
2939
2954
  if ((serverAppDirExists || context.backend === "self") && context.backend !== "convex") {
@@ -2983,7 +2998,7 @@ async function setupAuthTemplate(projectDir, context) {
2983
2998
  }
2984
2999
  }
2985
3000
  if (hasNative && nativeAppDirExists) {
2986
- const authNativeBaseSrc = path.join(PKG_ROOT, `templates/auth/${authProvider}/native/native-base`);
3001
+ const authNativeBaseSrc = path.join(PKG_ROOT, `templates/auth/${authProvider}/native/base`);
2987
3002
  if (await fs.pathExists(authNativeBaseSrc)) await processAndCopyFiles("**/*", authNativeBaseSrc, nativeAppDir, context);
2988
3003
  let nativeFrameworkAuthPath = "";
2989
3004
  if (hasNativeWind) nativeFrameworkAuthPath = "nativewind";
@@ -4467,11 +4482,20 @@ async function setupAuth(config) {
4467
4482
  }
4468
4483
  if (auth === "better-auth") {
4469
4484
  const convexBackendDir = path.join(projectDir, "packages/backend");
4470
- if (await fs.pathExists(convexBackendDir)) await addPackageDependency({
4471
- dependencies: ["better-auth", "@convex-dev/better-auth"],
4472
- customDependencies: { "better-auth": "1.3.27" },
4473
- projectDir: convexBackendDir
4474
- });
4485
+ const convexBackendDirExists = await fs.pathExists(convexBackendDir);
4486
+ const hasNativeForBA = frontend.includes("native-nativewind") || frontend.includes("native-unistyles");
4487
+ if (convexBackendDirExists) {
4488
+ await addPackageDependency({
4489
+ dependencies: ["better-auth", "@convex-dev/better-auth"],
4490
+ customDependencies: { "better-auth": "1.3.27" },
4491
+ projectDir: convexBackendDir
4492
+ });
4493
+ if (hasNativeForBA) await addPackageDependency({
4494
+ dependencies: ["@better-auth/expo"],
4495
+ customDependencies: { "@better-auth/expo": "1.3.27" },
4496
+ projectDir: convexBackendDir
4497
+ });
4498
+ }
4475
4499
  if (clientDirExists) {
4476
4500
  const hasNextJs = frontend.includes("next");
4477
4501
  const hasTanStackStart = frontend.includes("tanstack-start");
@@ -4492,6 +4516,20 @@ async function setupAuth(config) {
4492
4516
  projectDir: clientDir
4493
4517
  });
4494
4518
  }
4519
+ const hasNativeWind$1 = frontend.includes("native-nativewind");
4520
+ const hasUnistyles$1 = frontend.includes("native-unistyles");
4521
+ if (nativeDirExists && (hasNativeWind$1 || hasUnistyles$1)) await addPackageDependency({
4522
+ dependencies: [
4523
+ "better-auth",
4524
+ "@better-auth/expo",
4525
+ "@convex-dev/better-auth"
4526
+ ],
4527
+ customDependencies: {
4528
+ "better-auth": "1.3.27",
4529
+ "@better-auth/expo": "1.3.27"
4530
+ },
4531
+ projectDir: nativeDir
4532
+ });
4495
4533
  }
4496
4534
  const hasNativeWind = frontend.includes("native-nativewind");
4497
4535
  const hasUnistyles = frontend.includes("native-unistyles");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-better-t-stack",
3
- "version": "3.1.7",
3
+ "version": "3.1.8-canary.932e5243",
4
4
  "description": "A modern CLI tool for scaffolding end-to-end type-safe TypeScript projects with best practices and customizable configurations",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -1,5 +1,8 @@
1
1
  import { createClient, type GenericCtx } from "@convex-dev/better-auth";
2
- {{#if (or (includes frontend "tanstack-start") (includes frontend "next"))}}
2
+ {{#if (or (includes frontend "native-nativewind") (includes frontend "native-unistyles"))}}
3
+ import { convex } from "@convex-dev/better-auth/plugins";
4
+ import { expo } from "@better-auth/expo";
5
+ {{else if (or (includes frontend "tanstack-start") (includes frontend "next"))}}
3
6
  import { convex } from "@convex-dev/better-auth/plugins";
4
7
  {{else}}
5
8
  import { convex, crossDomain } from "@convex-dev/better-auth/plugins";
@@ -21,7 +24,9 @@ export const createAuth = (
21
24
  logger: {
22
25
  disabled: optionsOnly,
23
26
  },
24
- {{#if (or (includes frontend "tanstack-start") (includes frontend "next"))}}
27
+ {{#if (or (includes frontend "native-nativewind") (includes frontend "native-unistyles"))}}
28
+ trustedOrigins: ["mybettertapp://"],
29
+ {{else if (or (includes frontend "tanstack-start") (includes frontend "next"))}}
25
30
  baseURL: siteUrl,
26
31
  trustedOrigins: [siteUrl],
27
32
  {{else}}
@@ -33,6 +38,9 @@ export const createAuth = (
33
38
  requireEmailVerification: false,
34
39
  },
35
40
  plugins: [
41
+ {{#if (or (includes frontend "native-nativewind") (includes frontend "native-unistyles"))}}
42
+ expo(),
43
+ {{/if}}
36
44
  {{#unless (or (includes frontend "tanstack-start") (includes frontend "next"))}}
37
45
  crossDomain({ siteUrl }),
38
46
  {{/unless}}
@@ -0,0 +1,19 @@
1
+ import { createAuthClient } from "better-auth/react";
2
+ import { anonymousClient } from "better-auth/client/plugins";
3
+ import { convexClient } from "@convex-dev/better-auth/client/plugins";
4
+ import { expoClient } from "@better-auth/expo/client";
5
+ import Constants from "expo-constants";
6
+ import * as SecureStore from "expo-secure-store";
7
+
8
+ export const authClient = createAuthClient({
9
+ baseURL: process.env.EXPO_PUBLIC_CONVEX_SITE_URL,
10
+ plugins: [
11
+ anonymousClient(),
12
+ expoClient({
13
+ scheme: Constants.expoConfig?.scheme as string,
14
+ storagePrefix: Constants.expoConfig?.scheme as string,
15
+ storage: SecureStore,
16
+ }),
17
+ convexClient(),
18
+ ],
19
+ });
@@ -0,0 +1,86 @@
1
+ import { authClient } from "@/lib/auth-client";
2
+ import { useState } from "react";
3
+ import {
4
+ ActivityIndicator,
5
+ Text,
6
+ TextInput,
7
+ TouchableOpacity,
8
+ View,
9
+ } from "react-native";
10
+
11
+ export function SignIn() {
12
+ const [email, setEmail] = useState("");
13
+ const [password, setPassword] = useState("");
14
+ const [isLoading, setIsLoading] = useState(false);
15
+ const [error, setError] = useState<string | null>(null);
16
+
17
+ const handleLogin = async () => {
18
+ setIsLoading(true);
19
+ setError(null);
20
+
21
+ await authClient.signIn.email(
22
+ {
23
+ email,
24
+ password,
25
+ },
26
+ {
27
+ onError: (error) => {
28
+ setError(error.error?.message || "Failed to sign in");
29
+ setIsLoading(false);
30
+ },
31
+ onSuccess: () => {
32
+ setEmail("");
33
+ setPassword("");
34
+ },
35
+ onFinished: () => {
36
+ setIsLoading(false);
37
+ },
38
+ },
39
+ );
40
+ };
41
+
42
+ return (
43
+ <View className="mt-6 p-4 bg-card rounded-lg border border-border">
44
+ <Text className="text-lg font-semibold text-foreground mb-4">
45
+ Sign In
46
+ </Text>
47
+
48
+ {error && (
49
+ <View className="mb-4 p-3 bg-destructive/10 rounded-md">
50
+ <Text className="text-destructive text-sm">{error}</Text>
51
+ </View>
52
+ )}
53
+
54
+ <TextInput
55
+ className="mb-3 p-4 rounded-md bg-input text-foreground border border-input"
56
+ placeholder="Email"
57
+ value={email}
58
+ onChangeText={setEmail}
59
+ placeholderTextColor="#9CA3AF"
60
+ keyboardType="email-address"
61
+ autoCapitalize="none"
62
+ />
63
+
64
+ <TextInput
65
+ className="mb-4 p-4 rounded-md bg-input text-foreground border border-input"
66
+ placeholder="Password"
67
+ value={password}
68
+ onChangeText={setPassword}
69
+ placeholderTextColor="#9CA3AF"
70
+ secureTextEntry
71
+ />
72
+
73
+ <TouchableOpacity
74
+ onPress={handleLogin}
75
+ disabled={isLoading}
76
+ className="bg-primary p-4 rounded-md flex-row justify-center items-center"
77
+ >
78
+ {isLoading ? (
79
+ <ActivityIndicator size="small" color="#fff" />
80
+ ) : (
81
+ <Text className="text-primary-foreground font-medium">Sign In</Text>
82
+ )}
83
+ </TouchableOpacity>
84
+ </View>
85
+ );
86
+ }
@@ -0,0 +1,97 @@
1
+ import { authClient } from "@/lib/auth-client";
2
+ import { useState } from "react";
3
+ import {
4
+ ActivityIndicator,
5
+ Text,
6
+ TextInput,
7
+ TouchableOpacity,
8
+ View,
9
+ } from "react-native";
10
+
11
+ export function SignUp() {
12
+ const [name, setName] = useState("");
13
+ const [email, setEmail] = useState("");
14
+ const [password, setPassword] = useState("");
15
+ const [isLoading, setIsLoading] = useState(false);
16
+ const [error, setError] = useState<string | null>(null);
17
+
18
+ const handleSignUp = async () => {
19
+ setIsLoading(true);
20
+ setError(null);
21
+
22
+ await authClient.signUp.email(
23
+ {
24
+ name,
25
+ email,
26
+ password,
27
+ },
28
+ {
29
+ onError: (error) => {
30
+ setError(error.error?.message || "Failed to sign up");
31
+ setIsLoading(false);
32
+ },
33
+ onSuccess: () => {
34
+ setName("");
35
+ setEmail("");
36
+ setPassword("");
37
+ },
38
+ onFinished: () => {
39
+ setIsLoading(false);
40
+ },
41
+ },
42
+ );
43
+ };
44
+
45
+ return (
46
+ <View className="mt-6 p-4 bg-card rounded-lg border border-border">
47
+ <Text className="text-lg font-semibold text-foreground mb-4">
48
+ Create Account
49
+ </Text>
50
+
51
+ {error && (
52
+ <View className="mb-4 p-3 bg-destructive/10 rounded-md">
53
+ <Text className="text-destructive text-sm">{error}</Text>
54
+ </View>
55
+ )}
56
+
57
+ <TextInput
58
+ className="mb-3 p-4 rounded-md bg-input text-foreground border border-input"
59
+ placeholder="Name"
60
+ value={name}
61
+ onChangeText={setName}
62
+ placeholderTextColor="#9CA3AF"
63
+ />
64
+
65
+ <TextInput
66
+ className="mb-3 p-4 rounded-md bg-input text-foreground border border-input"
67
+ placeholder="Email"
68
+ value={email}
69
+ onChangeText={setEmail}
70
+ placeholderTextColor="#9CA3AF"
71
+ keyboardType="email-address"
72
+ autoCapitalize="none"
73
+ />
74
+
75
+ <TextInput
76
+ className="mb-4 p-4 rounded-md bg-input text-foreground border border-input"
77
+ placeholder="Password"
78
+ value={password}
79
+ onChangeText={setPassword}
80
+ placeholderTextColor="#9CA3AF"
81
+ secureTextEntry
82
+ />
83
+
84
+ <TouchableOpacity
85
+ onPress={handleSignUp}
86
+ disabled={isLoading}
87
+ className="bg-primary p-4 rounded-md flex-row justify-center items-center"
88
+ >
89
+ {isLoading ? (
90
+ <ActivityIndicator size="small" color="#fff" />
91
+ ) : (
92
+ <Text className="text-primary-foreground font-medium">Sign Up</Text>
93
+ )}
94
+ </TouchableOpacity>
95
+ </View>
96
+ );
97
+ }
@@ -0,0 +1,127 @@
1
+ import { authClient } from "@/lib/auth-client";
2
+ import { useState } from "react";
3
+ import {
4
+ ActivityIndicator,
5
+ Text,
6
+ TextInput,
7
+ TouchableOpacity,
8
+ View,
9
+ } from "react-native";
10
+ import { StyleSheet } from "react-native-unistyles";
11
+
12
+ export function SignIn() {
13
+ const [email, setEmail] = useState("");
14
+ const [password, setPassword] = useState("");
15
+ const [isLoading, setIsLoading] = useState(false);
16
+ const [error, setError] = useState<string | null>(null);
17
+
18
+ const handleLogin = async () => {
19
+ setIsLoading(true);
20
+ setError(null);
21
+
22
+ await authClient.signIn.email(
23
+ {
24
+ email,
25
+ password,
26
+ },
27
+ {
28
+ onError: (error) => {
29
+ setError(error.error?.message || "Failed to sign in");
30
+ setIsLoading(false);
31
+ },
32
+ onSuccess: () => {
33
+ setEmail("");
34
+ setPassword("");
35
+ },
36
+ onFinished: () => {
37
+ setIsLoading(false);
38
+ },
39
+ },
40
+ );
41
+ };
42
+
43
+ return (
44
+ <View style={styles.container}>
45
+ <Text style={styles.title}>Sign In</Text>
46
+
47
+ {error && (
48
+ <View style={styles.errorContainer}>
49
+ <Text style={styles.errorText}>{error}</Text>
50
+ </View>
51
+ )}
52
+
53
+ <TextInput
54
+ style={styles.input}
55
+ placeholder="Email"
56
+ value={email}
57
+ onChangeText={setEmail}
58
+ keyboardType="email-address"
59
+ autoCapitalize="none"
60
+ />
61
+
62
+ <TextInput
63
+ style={styles.input}
64
+ placeholder="Password"
65
+ value={password}
66
+ onChangeText={setPassword}
67
+ secureTextEntry
68
+ />
69
+
70
+ <TouchableOpacity
71
+ onPress={handleLogin}
72
+ disabled={isLoading}
73
+ style={styles.button}
74
+ >
75
+ {isLoading ? (
76
+ <ActivityIndicator size="small" color="#fff" />
77
+ ) : (
78
+ <Text style={styles.buttonText}>Sign In</Text>
79
+ )}
80
+ </TouchableOpacity>
81
+ </View>
82
+ );
83
+ }
84
+
85
+ const styles = StyleSheet.create((theme) => ({
86
+ container: {
87
+ marginTop: 24,
88
+ padding: 16,
89
+ borderRadius: 8,
90
+ borderWidth: 1,
91
+ borderColor: theme.colors.border,
92
+ },
93
+ title: {
94
+ fontSize: 18,
95
+ fontWeight: "600",
96
+ color: theme.colors.typography,
97
+ marginBottom: 16,
98
+ },
99
+ errorContainer: {
100
+ marginBottom: 16,
101
+ padding: 12,
102
+ borderRadius: 6,
103
+ },
104
+ errorText: {
105
+ color: theme.colors.destructive,
106
+ fontSize: 14,
107
+ },
108
+ input: {
109
+ marginBottom: 12,
110
+ padding: 16,
111
+ borderRadius: 6,
112
+ color: theme.colors.typography,
113
+ borderWidth: 1,
114
+ borderColor: theme.colors.border,
115
+ },
116
+ button: {
117
+ backgroundColor: theme.colors.primary,
118
+ padding: 16,
119
+ borderRadius: 6,
120
+ flexDirection: "row",
121
+ justifyContent: "center",
122
+ alignItems: "center",
123
+ },
124
+ buttonText: {
125
+ fontWeight: "500",
126
+ },
127
+ }));
@@ -0,0 +1,145 @@
1
+ import { authClient } from "@/lib/auth-client";
2
+ import { useState } from "react";
3
+ import {
4
+ ActivityIndicator,
5
+ Text,
6
+ TextInput,
7
+ TouchableOpacity,
8
+ View,
9
+ } from "react-native";
10
+ import { StyleSheet } from "react-native-unistyles";
11
+
12
+ export function SignUp() {
13
+ const [name, setName] = useState("");
14
+ const [email, setEmail] = useState("");
15
+ const [password, setPassword] = useState("");
16
+ const [isLoading, setIsLoading] = useState(false);
17
+ const [error, setError] = useState<string | null>(null);
18
+
19
+ const handleSignUp = async () => {
20
+ setIsLoading(true);
21
+ setError(null);
22
+
23
+ await authClient.signUp.email(
24
+ {
25
+ name,
26
+ email,
27
+ password,
28
+ },
29
+ {
30
+ onError: (error) => {
31
+ setError(error.error?.message || "Failed to sign up");
32
+ setIsLoading(false);
33
+ },
34
+ onSuccess: () => {
35
+ setName("");
36
+ setEmail("");
37
+ setPassword("");
38
+ },
39
+ onFinished: () => {
40
+ setIsLoading(false);
41
+ },
42
+ },
43
+ );
44
+ };
45
+
46
+ return (
47
+ <View style={styles.container}>
48
+ <Text style={styles.title}>Create Account</Text>
49
+
50
+ {error && (
51
+ <View style={styles.errorContainer}>
52
+ <Text style={styles.errorText}>{error}</Text>
53
+ </View>
54
+ )}
55
+
56
+ <TextInput
57
+ style={styles.input}
58
+ placeholder="Name"
59
+ value={name}
60
+ onChangeText={setName}
61
+ />
62
+
63
+ <TextInput
64
+ style={styles.input}
65
+ placeholder="Email"
66
+ value={email}
67
+ onChangeText={setEmail}
68
+ keyboardType="email-address"
69
+ autoCapitalize="none"
70
+ />
71
+
72
+ <TextInput
73
+ style={styles.inputLast}
74
+ placeholder="Password"
75
+ value={password}
76
+ onChangeText={setPassword}
77
+ secureTextEntry
78
+ />
79
+
80
+ <TouchableOpacity
81
+ onPress={handleSignUp}
82
+ disabled={isLoading}
83
+ style={styles.button}
84
+ >
85
+ {isLoading ? (
86
+ <ActivityIndicator size="small" color="#fff" />
87
+ ) : (
88
+ <Text style={styles.buttonText}>Sign Up</Text>
89
+ )}
90
+ </TouchableOpacity>
91
+ </View>
92
+ );
93
+ }
94
+
95
+ const styles = StyleSheet.create((theme) => ({
96
+ container: {
97
+ marginTop: 24,
98
+ padding: 16,
99
+ borderRadius: 8,
100
+ borderWidth: 1,
101
+ borderColor: theme.colors.border,
102
+ },
103
+ title: {
104
+ fontSize: 18,
105
+ fontWeight: "600",
106
+ color: theme.colors.typography,
107
+ marginBottom: 16,
108
+ },
109
+ errorContainer: {
110
+ marginBottom: 16,
111
+ padding: 12,
112
+ borderRadius: 6,
113
+ },
114
+ errorText: {
115
+ color: theme.colors.destructive,
116
+ fontSize: 14,
117
+ },
118
+ input: {
119
+ marginBottom: 12,
120
+ padding: 16,
121
+ borderRadius: 6,
122
+ color: theme.colors.typography,
123
+ borderWidth: 1,
124
+ borderColor: theme.colors.border,
125
+ },
126
+ inputLast: {
127
+ marginBottom: 16,
128
+ padding: 16,
129
+ borderRadius: 6,
130
+ color: theme.colors.typography,
131
+ borderWidth: 1,
132
+ borderColor: theme.colors.border,
133
+ },
134
+ button: {
135
+ backgroundColor: theme.colors.primary,
136
+ padding: 16,
137
+ borderRadius: 6,
138
+ flexDirection: "row",
139
+ justifyContent: "center",
140
+ alignItems: "center",
141
+ },
142
+ buttonText: {
143
+ fontWeight: "500",
144
+ },
145
+ }));
@@ -1,13 +1,14 @@
1
1
  import { expoClient } from "@better-auth/expo/client";
2
2
  import { createAuthClient } from "better-auth/react";
3
3
  import * as SecureStore from "expo-secure-store";
4
+ import Constants from "expo-constants";
4
5
 
5
6
  export const authClient = createAuthClient({
6
7
  baseURL: process.env.EXPO_PUBLIC_SERVER_URL,
7
8
  plugins: [
8
9
  expoClient({
9
- scheme: "mybettertapp",
10
- storagePrefix: "{{projectName}}",
10
+ scheme: Constants.expoConfig?.scheme as string,
11
+ storagePrefix: Constants.expoConfig?.scheme as string,
11
12
  storage: SecureStore,
12
13
  }),
13
14
  ],