@better-t-stack/template-generator 3.20.2 → 3.21.0
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/dist/index.d.mts +3 -0
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +2150 -963
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
package/dist/index.mjs
CHANGED
|
@@ -705,7 +705,10 @@ const dependencyVersionMap = {
|
|
|
705
705
|
"@tanstack/vue-query": "^5.90.2",
|
|
706
706
|
"@tanstack/react-query-devtools": "^5.91.1",
|
|
707
707
|
"@tanstack/react-query": "^5.90.12",
|
|
708
|
+
"@tanstack/react-form": "^1.28.0",
|
|
708
709
|
"@tanstack/react-router-ssr-query": "^1.142.7",
|
|
710
|
+
"@tanstack/solid-form": "^1.28.0",
|
|
711
|
+
"@tanstack/svelte-form": "^1.28.0",
|
|
709
712
|
"@tanstack/solid-query": "^5.87.4",
|
|
710
713
|
"@tanstack/solid-query-devtools": "^5.87.4",
|
|
711
714
|
"@tanstack/solid-router-devtools": "^1.131.44",
|
|
@@ -1261,6 +1264,9 @@ function processConvexAuthDeps(vfs, config) {
|
|
|
1261
1264
|
const hasNextJs = frontend.includes("next");
|
|
1262
1265
|
const hasTanStackStart = frontend.includes("tanstack-start");
|
|
1263
1266
|
const hasViteReact = frontend.some((f) => ["tanstack-router", "react-router"].includes(f));
|
|
1267
|
+
const hasSolid = frontend.includes("solid");
|
|
1268
|
+
const hasSvelte = frontend.includes("svelte");
|
|
1269
|
+
const hasReactWebAuthForms = hasNextJs || hasTanStackStart || hasViteReact;
|
|
1264
1270
|
if (auth === "clerk") {
|
|
1265
1271
|
if (webExists) {
|
|
1266
1272
|
if (hasNextJs) addPackageDependency({
|
|
@@ -1299,19 +1305,37 @@ function processConvexAuthDeps(vfs, config) {
|
|
|
1299
1305
|
customDependencies: { "@better-auth/expo": "1.4.9" }
|
|
1300
1306
|
});
|
|
1301
1307
|
}
|
|
1302
|
-
if (webExists)
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
+
if (webExists) {
|
|
1309
|
+
addPackageDependency({
|
|
1310
|
+
vfs,
|
|
1311
|
+
packagePath: webPath,
|
|
1312
|
+
dependencies: ["better-auth", "@convex-dev/better-auth"],
|
|
1313
|
+
customDependencies: { "better-auth": "1.4.9" }
|
|
1314
|
+
});
|
|
1315
|
+
if (hasReactWebAuthForms) addPackageDependency({
|
|
1316
|
+
vfs,
|
|
1317
|
+
packagePath: webPath,
|
|
1318
|
+
dependencies: ["@tanstack/react-form"]
|
|
1319
|
+
});
|
|
1320
|
+
if (hasSolid) addPackageDependency({
|
|
1321
|
+
vfs,
|
|
1322
|
+
packagePath: webPath,
|
|
1323
|
+
dependencies: ["@tanstack/solid-form"]
|
|
1324
|
+
});
|
|
1325
|
+
if (hasSvelte) addPackageDependency({
|
|
1326
|
+
vfs,
|
|
1327
|
+
packagePath: webPath,
|
|
1328
|
+
dependencies: ["@tanstack/svelte-form"]
|
|
1329
|
+
});
|
|
1330
|
+
}
|
|
1308
1331
|
if (nativeExists && hasNative) addPackageDependency({
|
|
1309
1332
|
vfs,
|
|
1310
1333
|
packagePath: nativePath,
|
|
1311
1334
|
dependencies: [
|
|
1312
1335
|
"better-auth",
|
|
1313
1336
|
"@better-auth/expo",
|
|
1314
|
-
"@convex-dev/better-auth"
|
|
1337
|
+
"@convex-dev/better-auth",
|
|
1338
|
+
"@tanstack/react-form"
|
|
1315
1339
|
],
|
|
1316
1340
|
customDependencies: {
|
|
1317
1341
|
"better-auth": "1.4.9",
|
|
@@ -1343,6 +1367,14 @@ function processStandardAuthDeps(vfs, config) {
|
|
|
1343
1367
|
"solid",
|
|
1344
1368
|
"astro"
|
|
1345
1369
|
].includes(f));
|
|
1370
|
+
const hasReactWebAuthForms = frontend.some((f) => [
|
|
1371
|
+
"react-router",
|
|
1372
|
+
"tanstack-router",
|
|
1373
|
+
"tanstack-start",
|
|
1374
|
+
"next"
|
|
1375
|
+
].includes(f));
|
|
1376
|
+
const hasSolid = frontend.includes("solid");
|
|
1377
|
+
const hasSvelte = frontend.includes("svelte");
|
|
1346
1378
|
if (auth === "better-auth") {
|
|
1347
1379
|
if (authExists) {
|
|
1348
1380
|
addPackageDependency({
|
|
@@ -1356,15 +1388,36 @@ function processStandardAuthDeps(vfs, config) {
|
|
|
1356
1388
|
dependencies: ["@better-auth/expo"]
|
|
1357
1389
|
});
|
|
1358
1390
|
}
|
|
1359
|
-
if (hasWebFrontend && webExists)
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1391
|
+
if (hasWebFrontend && webExists) {
|
|
1392
|
+
addPackageDependency({
|
|
1393
|
+
vfs,
|
|
1394
|
+
packagePath: webPath,
|
|
1395
|
+
dependencies: ["better-auth"]
|
|
1396
|
+
});
|
|
1397
|
+
if (hasReactWebAuthForms) addPackageDependency({
|
|
1398
|
+
vfs,
|
|
1399
|
+
packagePath: webPath,
|
|
1400
|
+
dependencies: ["@tanstack/react-form"]
|
|
1401
|
+
});
|
|
1402
|
+
if (hasSolid) addPackageDependency({
|
|
1403
|
+
vfs,
|
|
1404
|
+
packagePath: webPath,
|
|
1405
|
+
dependencies: ["@tanstack/solid-form"]
|
|
1406
|
+
});
|
|
1407
|
+
if (hasSvelte) addPackageDependency({
|
|
1408
|
+
vfs,
|
|
1409
|
+
packagePath: webPath,
|
|
1410
|
+
dependencies: ["@tanstack/svelte-form"]
|
|
1411
|
+
});
|
|
1412
|
+
}
|
|
1364
1413
|
if (hasNative && nativeExists) addPackageDependency({
|
|
1365
1414
|
vfs,
|
|
1366
1415
|
packagePath: nativePath,
|
|
1367
|
-
dependencies: [
|
|
1416
|
+
dependencies: [
|
|
1417
|
+
"better-auth",
|
|
1418
|
+
"@better-auth/expo",
|
|
1419
|
+
"@tanstack/react-form"
|
|
1420
|
+
]
|
|
1368
1421
|
});
|
|
1369
1422
|
}
|
|
1370
1423
|
}
|
|
@@ -5135,91 +5188,187 @@ export const get = query({
|
|
|
5135
5188
|
});
|
|
5136
5189
|
`],
|
|
5137
5190
|
["auth/better-auth/convex/native/bare/components/sign-in.tsx.hbs", `import { authClient } from "@/lib/auth-client";
|
|
5191
|
+
import { useForm } from "@tanstack/react-form";
|
|
5138
5192
|
import { useState } from "react";
|
|
5139
5193
|
import {
|
|
5140
5194
|
ActivityIndicator,
|
|
5195
|
+
StyleSheet,
|
|
5141
5196
|
Text,
|
|
5142
5197
|
TextInput,
|
|
5143
5198
|
TouchableOpacity,
|
|
5144
5199
|
View,
|
|
5145
|
-
StyleSheet,
|
|
5146
5200
|
} from "react-native";
|
|
5147
|
-
import { useColorScheme } from "@/lib/use-color-scheme";
|
|
5148
5201
|
import { NAV_THEME } from "@/lib/constants";
|
|
5202
|
+
import { useColorScheme } from "@/lib/use-color-scheme";
|
|
5203
|
+
import z from "zod";
|
|
5204
|
+
|
|
5205
|
+
const signInSchema = z.object({
|
|
5206
|
+
email: z
|
|
5207
|
+
.string()
|
|
5208
|
+
.trim()
|
|
5209
|
+
.min(1, "Email is required")
|
|
5210
|
+
.email("Enter a valid email address"),
|
|
5211
|
+
password: z
|
|
5212
|
+
.string()
|
|
5213
|
+
.min(1, "Password is required")
|
|
5214
|
+
.min(8, "Use at least 8 characters"),
|
|
5215
|
+
});
|
|
5216
|
+
|
|
5217
|
+
function getErrorMessage(error: unknown): string | null {
|
|
5218
|
+
if (!error) return null;
|
|
5219
|
+
|
|
5220
|
+
if (typeof error === "string") {
|
|
5221
|
+
return error;
|
|
5222
|
+
}
|
|
5223
|
+
|
|
5224
|
+
if (Array.isArray(error)) {
|
|
5225
|
+
for (const issue of error) {
|
|
5226
|
+
const message = getErrorMessage(issue);
|
|
5227
|
+
if (message) {
|
|
5228
|
+
return message;
|
|
5229
|
+
}
|
|
5230
|
+
}
|
|
5231
|
+
return null;
|
|
5232
|
+
}
|
|
5233
|
+
|
|
5234
|
+
if (typeof error === "object" && error !== null) {
|
|
5235
|
+
const maybeError = error as { message?: unknown };
|
|
5236
|
+
if (typeof maybeError.message === "string") {
|
|
5237
|
+
return maybeError.message;
|
|
5238
|
+
}
|
|
5239
|
+
}
|
|
5240
|
+
|
|
5241
|
+
return null;
|
|
5242
|
+
}
|
|
5149
5243
|
|
|
5150
5244
|
function SignIn() {
|
|
5151
5245
|
const { colorScheme } = useColorScheme();
|
|
5152
5246
|
const theme = colorScheme === "dark" ? NAV_THEME.dark : NAV_THEME.light;
|
|
5153
|
-
const [email, setEmail] = useState("");
|
|
5154
|
-
const [password, setPassword] = useState("");
|
|
5155
|
-
const [isLoading, setIsLoading] = useState(false);
|
|
5156
5247
|
const [error, setError] = useState<string | null>(null);
|
|
5157
5248
|
|
|
5158
|
-
|
|
5159
|
-
|
|
5160
|
-
|
|
5161
|
-
|
|
5162
|
-
|
|
5163
|
-
|
|
5164
|
-
|
|
5165
|
-
|
|
5166
|
-
|
|
5167
|
-
|
|
5168
|
-
|
|
5169
|
-
|
|
5170
|
-
|
|
5171
|
-
},
|
|
5172
|
-
onSuccess() {
|
|
5173
|
-
setEmail("");
|
|
5174
|
-
setPassword("");
|
|
5249
|
+
const form = useForm({
|
|
5250
|
+
defaultValues: {
|
|
5251
|
+
email: "",
|
|
5252
|
+
password: "",
|
|
5253
|
+
},
|
|
5254
|
+
validators: {
|
|
5255
|
+
onSubmit: signInSchema,
|
|
5256
|
+
},
|
|
5257
|
+
onSubmit: async ({ value, formApi }) => {
|
|
5258
|
+
await authClient.signIn.email(
|
|
5259
|
+
{
|
|
5260
|
+
email: value.email.trim(),
|
|
5261
|
+
password: value.password,
|
|
5175
5262
|
},
|
|
5176
|
-
|
|
5177
|
-
|
|
5263
|
+
{
|
|
5264
|
+
onError(error) {
|
|
5265
|
+
setError(error.error?.message || "Failed to sign in");
|
|
5266
|
+
},
|
|
5267
|
+
onSuccess() {
|
|
5268
|
+
setError(null);
|
|
5269
|
+
formApi.reset();
|
|
5270
|
+
},
|
|
5178
5271
|
},
|
|
5179
|
-
|
|
5180
|
-
|
|
5181
|
-
}
|
|
5272
|
+
);
|
|
5273
|
+
},
|
|
5274
|
+
});
|
|
5182
5275
|
|
|
5183
5276
|
return (
|
|
5184
5277
|
<View style={[styles.card, { backgroundColor: theme.card, borderColor: theme.border }]}>
|
|
5185
5278
|
<Text style={[styles.title, { color: theme.text }]}>Sign In</Text>
|
|
5186
5279
|
|
|
5187
|
-
|
|
5188
|
-
|
|
5189
|
-
|
|
5190
|
-
|
|
5191
|
-
|
|
5280
|
+
<form.Subscribe
|
|
5281
|
+
selector={(state) => ({
|
|
5282
|
+
isSubmitting: state.isSubmitting,
|
|
5283
|
+
validationError: getErrorMessage(state.errorMap.onSubmit),
|
|
5284
|
+
})}
|
|
5285
|
+
>
|
|
5286
|
+
{({ isSubmitting, validationError }) => {
|
|
5287
|
+
const formError = error ?? validationError;
|
|
5192
5288
|
|
|
5193
|
-
|
|
5194
|
-
|
|
5195
|
-
|
|
5196
|
-
|
|
5197
|
-
|
|
5198
|
-
|
|
5199
|
-
|
|
5200
|
-
autoCapitalize="none"
|
|
5201
|
-
/>
|
|
5289
|
+
return (
|
|
5290
|
+
<>
|
|
5291
|
+
{formError ? (
|
|
5292
|
+
<View style={[styles.errorContainer, { backgroundColor: theme.notification + "20" }]}>
|
|
5293
|
+
<Text style={[styles.errorText, { color: theme.notification }]}>{formError}</Text>
|
|
5294
|
+
</View>
|
|
5295
|
+
) : null}
|
|
5202
5296
|
|
|
5203
|
-
|
|
5204
|
-
|
|
5205
|
-
|
|
5206
|
-
|
|
5207
|
-
|
|
5208
|
-
|
|
5209
|
-
|
|
5210
|
-
|
|
5297
|
+
<form.Field name="email">
|
|
5298
|
+
{(field) => (
|
|
5299
|
+
<TextInput
|
|
5300
|
+
style={[
|
|
5301
|
+
styles.input,
|
|
5302
|
+
{
|
|
5303
|
+
color: theme.text,
|
|
5304
|
+
borderColor: theme.border,
|
|
5305
|
+
backgroundColor: theme.background,
|
|
5306
|
+
},
|
|
5307
|
+
]}
|
|
5308
|
+
placeholder="Email"
|
|
5309
|
+
placeholderTextColor={theme.text}
|
|
5310
|
+
value={field.state.value}
|
|
5311
|
+
onBlur={field.handleBlur}
|
|
5312
|
+
onChangeText={(value) => {
|
|
5313
|
+
field.handleChange(value);
|
|
5314
|
+
if (error) {
|
|
5315
|
+
setError(null);
|
|
5316
|
+
}
|
|
5317
|
+
}}
|
|
5318
|
+
keyboardType="email-address"
|
|
5319
|
+
autoCapitalize="none"
|
|
5320
|
+
/>
|
|
5321
|
+
)}
|
|
5322
|
+
</form.Field>
|
|
5211
5323
|
|
|
5212
|
-
|
|
5213
|
-
|
|
5214
|
-
|
|
5215
|
-
|
|
5216
|
-
|
|
5217
|
-
|
|
5218
|
-
|
|
5219
|
-
|
|
5220
|
-
|
|
5221
|
-
|
|
5222
|
-
|
|
5324
|
+
<form.Field name="password">
|
|
5325
|
+
{(field) => (
|
|
5326
|
+
<TextInput
|
|
5327
|
+
style={[
|
|
5328
|
+
styles.input,
|
|
5329
|
+
{
|
|
5330
|
+
color: theme.text,
|
|
5331
|
+
borderColor: theme.border,
|
|
5332
|
+
backgroundColor: theme.background,
|
|
5333
|
+
},
|
|
5334
|
+
]}
|
|
5335
|
+
placeholder="Password"
|
|
5336
|
+
placeholderTextColor={theme.text}
|
|
5337
|
+
value={field.state.value}
|
|
5338
|
+
onBlur={field.handleBlur}
|
|
5339
|
+
onChangeText={(value) => {
|
|
5340
|
+
field.handleChange(value);
|
|
5341
|
+
if (error) {
|
|
5342
|
+
setError(null);
|
|
5343
|
+
}
|
|
5344
|
+
}}
|
|
5345
|
+
secureTextEntry
|
|
5346
|
+
onSubmitEditing={form.handleSubmit}
|
|
5347
|
+
/>
|
|
5348
|
+
)}
|
|
5349
|
+
</form.Field>
|
|
5350
|
+
|
|
5351
|
+
<TouchableOpacity
|
|
5352
|
+
onPress={form.handleSubmit}
|
|
5353
|
+
disabled={isSubmitting}
|
|
5354
|
+
style={[
|
|
5355
|
+
styles.button,
|
|
5356
|
+
{
|
|
5357
|
+
backgroundColor: theme.primary,
|
|
5358
|
+
opacity: isSubmitting ? 0.5 : 1,
|
|
5359
|
+
},
|
|
5360
|
+
]}
|
|
5361
|
+
>
|
|
5362
|
+
{isSubmitting ? (
|
|
5363
|
+
<ActivityIndicator size="small" color="#ffffff" />
|
|
5364
|
+
) : (
|
|
5365
|
+
<Text style={styles.buttonText}>Sign In</Text>
|
|
5366
|
+
)}
|
|
5367
|
+
</TouchableOpacity>
|
|
5368
|
+
</>
|
|
5369
|
+
);
|
|
5370
|
+
}}
|
|
5371
|
+
</form.Subscribe>
|
|
5223
5372
|
</View>
|
|
5224
5373
|
);
|
|
5225
5374
|
}
|
|
@@ -5260,105 +5409,221 @@ const styles = StyleSheet.create({
|
|
|
5260
5409
|
});
|
|
5261
5410
|
|
|
5262
5411
|
export { SignIn };
|
|
5263
|
-
|
|
5264
5412
|
`],
|
|
5265
5413
|
["auth/better-auth/convex/native/bare/components/sign-up.tsx.hbs", `import { authClient } from "@/lib/auth-client";
|
|
5414
|
+
import { useForm } from "@tanstack/react-form";
|
|
5266
5415
|
import { useState } from "react";
|
|
5267
5416
|
import {
|
|
5268
5417
|
ActivityIndicator,
|
|
5418
|
+
StyleSheet,
|
|
5269
5419
|
Text,
|
|
5270
5420
|
TextInput,
|
|
5271
5421
|
TouchableOpacity,
|
|
5272
5422
|
View,
|
|
5273
|
-
StyleSheet,
|
|
5274
5423
|
} from "react-native";
|
|
5275
|
-
import { useColorScheme } from "@/lib/use-color-scheme";
|
|
5276
5424
|
import { NAV_THEME } from "@/lib/constants";
|
|
5425
|
+
import { useColorScheme } from "@/lib/use-color-scheme";
|
|
5426
|
+
import z from "zod";
|
|
5427
|
+
|
|
5428
|
+
const signUpSchema = z.object({
|
|
5429
|
+
name: z
|
|
5430
|
+
.string()
|
|
5431
|
+
.trim()
|
|
5432
|
+
.min(1, "Name is required")
|
|
5433
|
+
.min(2, "Name must be at least 2 characters"),
|
|
5434
|
+
email: z
|
|
5435
|
+
.string()
|
|
5436
|
+
.trim()
|
|
5437
|
+
.min(1, "Email is required")
|
|
5438
|
+
.email("Enter a valid email address"),
|
|
5439
|
+
password: z
|
|
5440
|
+
.string()
|
|
5441
|
+
.min(1, "Password is required")
|
|
5442
|
+
.min(8, "Use at least 8 characters"),
|
|
5443
|
+
});
|
|
5444
|
+
|
|
5445
|
+
function getErrorMessage(error: unknown): string | null {
|
|
5446
|
+
if (!error) return null;
|
|
5447
|
+
|
|
5448
|
+
if (typeof error === "string") {
|
|
5449
|
+
return error;
|
|
5450
|
+
}
|
|
5451
|
+
|
|
5452
|
+
if (Array.isArray(error)) {
|
|
5453
|
+
for (const issue of error) {
|
|
5454
|
+
const message = getErrorMessage(issue);
|
|
5455
|
+
if (message) {
|
|
5456
|
+
return message;
|
|
5457
|
+
}
|
|
5458
|
+
}
|
|
5459
|
+
return null;
|
|
5460
|
+
}
|
|
5461
|
+
|
|
5462
|
+
if (typeof error === "object" && error !== null) {
|
|
5463
|
+
const maybeError = error as { message?: unknown };
|
|
5464
|
+
if (typeof maybeError.message === "string") {
|
|
5465
|
+
return maybeError.message;
|
|
5466
|
+
}
|
|
5467
|
+
}
|
|
5468
|
+
|
|
5469
|
+
return null;
|
|
5470
|
+
}
|
|
5277
5471
|
|
|
5278
5472
|
function SignUp() {
|
|
5279
5473
|
const { colorScheme } = useColorScheme();
|
|
5280
5474
|
const theme = colorScheme === "dark" ? NAV_THEME.dark : NAV_THEME.light;
|
|
5281
|
-
const [name, setName] = useState("");
|
|
5282
|
-
const [email, setEmail] = useState("");
|
|
5283
|
-
const [password, setPassword] = useState("");
|
|
5284
|
-
const [isLoading, setIsLoading] = useState(false);
|
|
5285
5475
|
const [error, setError] = useState<string | null>(null);
|
|
5286
5476
|
|
|
5287
|
-
|
|
5288
|
-
|
|
5289
|
-
|
|
5290
|
-
|
|
5291
|
-
|
|
5292
|
-
|
|
5293
|
-
|
|
5294
|
-
|
|
5295
|
-
|
|
5296
|
-
|
|
5297
|
-
|
|
5298
|
-
|
|
5299
|
-
|
|
5300
|
-
|
|
5301
|
-
|
|
5302
|
-
onSuccess() {
|
|
5303
|
-
setName("");
|
|
5304
|
-
setEmail("");
|
|
5305
|
-
setPassword("");
|
|
5477
|
+
const form = useForm({
|
|
5478
|
+
defaultValues: {
|
|
5479
|
+
name: "",
|
|
5480
|
+
email: "",
|
|
5481
|
+
password: "",
|
|
5482
|
+
},
|
|
5483
|
+
validators: {
|
|
5484
|
+
onSubmit: signUpSchema,
|
|
5485
|
+
},
|
|
5486
|
+
onSubmit: async ({ value, formApi }) => {
|
|
5487
|
+
await authClient.signUp.email(
|
|
5488
|
+
{
|
|
5489
|
+
name: value.name.trim(),
|
|
5490
|
+
email: value.email.trim(),
|
|
5491
|
+
password: value.password,
|
|
5306
5492
|
},
|
|
5307
|
-
|
|
5308
|
-
|
|
5493
|
+
{
|
|
5494
|
+
onError(error) {
|
|
5495
|
+
setError(error.error?.message || "Failed to sign up");
|
|
5496
|
+
},
|
|
5497
|
+
onSuccess() {
|
|
5498
|
+
setError(null);
|
|
5499
|
+
formApi.reset();
|
|
5500
|
+
},
|
|
5309
5501
|
},
|
|
5310
|
-
|
|
5311
|
-
|
|
5312
|
-
}
|
|
5502
|
+
);
|
|
5503
|
+
},
|
|
5504
|
+
});
|
|
5313
5505
|
|
|
5314
5506
|
return (
|
|
5315
5507
|
<View style={[styles.card, { backgroundColor: theme.card, borderColor: theme.border }]}>
|
|
5316
5508
|
<Text style={[styles.title, { color: theme.text }]}>Create Account</Text>
|
|
5317
5509
|
|
|
5318
|
-
|
|
5319
|
-
|
|
5320
|
-
|
|
5321
|
-
|
|
5322
|
-
|
|
5510
|
+
<form.Subscribe
|
|
5511
|
+
selector={(state) => ({
|
|
5512
|
+
isSubmitting: state.isSubmitting,
|
|
5513
|
+
validationError: getErrorMessage(state.errorMap.onSubmit),
|
|
5514
|
+
})}
|
|
5515
|
+
>
|
|
5516
|
+
{({ isSubmitting, validationError }) => {
|
|
5517
|
+
const formError = error ?? validationError;
|
|
5323
5518
|
|
|
5324
|
-
|
|
5325
|
-
|
|
5326
|
-
|
|
5327
|
-
|
|
5328
|
-
|
|
5329
|
-
|
|
5330
|
-
|
|
5519
|
+
return (
|
|
5520
|
+
<>
|
|
5521
|
+
{formError ? (
|
|
5522
|
+
<View style={[styles.errorContainer, { backgroundColor: theme.notification + "20" }]}>
|
|
5523
|
+
<Text style={[styles.errorText, { color: theme.notification }]}>{formError}</Text>
|
|
5524
|
+
</View>
|
|
5525
|
+
) : null}
|
|
5331
5526
|
|
|
5332
|
-
|
|
5333
|
-
|
|
5334
|
-
|
|
5335
|
-
|
|
5336
|
-
|
|
5337
|
-
|
|
5338
|
-
|
|
5339
|
-
|
|
5340
|
-
|
|
5527
|
+
<form.Field name="name">
|
|
5528
|
+
{(field) => (
|
|
5529
|
+
<TextInput
|
|
5530
|
+
style={[
|
|
5531
|
+
styles.input,
|
|
5532
|
+
{
|
|
5533
|
+
color: theme.text,
|
|
5534
|
+
borderColor: theme.border,
|
|
5535
|
+
backgroundColor: theme.background,
|
|
5536
|
+
},
|
|
5537
|
+
]}
|
|
5538
|
+
placeholder="Name"
|
|
5539
|
+
placeholderTextColor={theme.text}
|
|
5540
|
+
value={field.state.value}
|
|
5541
|
+
onBlur={field.handleBlur}
|
|
5542
|
+
onChangeText={(value) => {
|
|
5543
|
+
field.handleChange(value);
|
|
5544
|
+
if (error) {
|
|
5545
|
+
setError(null);
|
|
5546
|
+
}
|
|
5547
|
+
}}
|
|
5548
|
+
/>
|
|
5549
|
+
)}
|
|
5550
|
+
</form.Field>
|
|
5341
5551
|
|
|
5342
|
-
|
|
5343
|
-
|
|
5344
|
-
|
|
5345
|
-
|
|
5346
|
-
|
|
5347
|
-
|
|
5348
|
-
|
|
5349
|
-
|
|
5552
|
+
<form.Field name="email">
|
|
5553
|
+
{(field) => (
|
|
5554
|
+
<TextInput
|
|
5555
|
+
style={[
|
|
5556
|
+
styles.input,
|
|
5557
|
+
{
|
|
5558
|
+
color: theme.text,
|
|
5559
|
+
borderColor: theme.border,
|
|
5560
|
+
backgroundColor: theme.background,
|
|
5561
|
+
},
|
|
5562
|
+
]}
|
|
5563
|
+
placeholder="Email"
|
|
5564
|
+
placeholderTextColor={theme.text}
|
|
5565
|
+
value={field.state.value}
|
|
5566
|
+
onBlur={field.handleBlur}
|
|
5567
|
+
onChangeText={(value) => {
|
|
5568
|
+
field.handleChange(value);
|
|
5569
|
+
if (error) {
|
|
5570
|
+
setError(null);
|
|
5571
|
+
}
|
|
5572
|
+
}}
|
|
5573
|
+
keyboardType="email-address"
|
|
5574
|
+
autoCapitalize="none"
|
|
5575
|
+
/>
|
|
5576
|
+
)}
|
|
5577
|
+
</form.Field>
|
|
5350
5578
|
|
|
5351
|
-
|
|
5352
|
-
|
|
5353
|
-
|
|
5354
|
-
|
|
5355
|
-
|
|
5356
|
-
|
|
5357
|
-
|
|
5358
|
-
|
|
5359
|
-
|
|
5360
|
-
|
|
5361
|
-
|
|
5579
|
+
<form.Field name="password">
|
|
5580
|
+
{(field) => (
|
|
5581
|
+
<TextInput
|
|
5582
|
+
style={[
|
|
5583
|
+
styles.input,
|
|
5584
|
+
{
|
|
5585
|
+
color: theme.text,
|
|
5586
|
+
borderColor: theme.border,
|
|
5587
|
+
backgroundColor: theme.background,
|
|
5588
|
+
},
|
|
5589
|
+
]}
|
|
5590
|
+
placeholder="Password"
|
|
5591
|
+
placeholderTextColor={theme.text}
|
|
5592
|
+
value={field.state.value}
|
|
5593
|
+
onBlur={field.handleBlur}
|
|
5594
|
+
onChangeText={(value) => {
|
|
5595
|
+
field.handleChange(value);
|
|
5596
|
+
if (error) {
|
|
5597
|
+
setError(null);
|
|
5598
|
+
}
|
|
5599
|
+
}}
|
|
5600
|
+
secureTextEntry
|
|
5601
|
+
onSubmitEditing={form.handleSubmit}
|
|
5602
|
+
/>
|
|
5603
|
+
)}
|
|
5604
|
+
</form.Field>
|
|
5605
|
+
|
|
5606
|
+
<TouchableOpacity
|
|
5607
|
+
onPress={form.handleSubmit}
|
|
5608
|
+
disabled={isSubmitting}
|
|
5609
|
+
style={[
|
|
5610
|
+
styles.button,
|
|
5611
|
+
{
|
|
5612
|
+
backgroundColor: theme.primary,
|
|
5613
|
+
opacity: isSubmitting ? 0.5 : 1,
|
|
5614
|
+
},
|
|
5615
|
+
]}
|
|
5616
|
+
>
|
|
5617
|
+
{isSubmitting ? (
|
|
5618
|
+
<ActivityIndicator size="small" color="#ffffff" />
|
|
5619
|
+
) : (
|
|
5620
|
+
<Text style={styles.buttonText}>Sign Up</Text>
|
|
5621
|
+
)}
|
|
5622
|
+
</TouchableOpacity>
|
|
5623
|
+
</>
|
|
5624
|
+
);
|
|
5625
|
+
}}
|
|
5626
|
+
</form.Subscribe>
|
|
5362
5627
|
</View>
|
|
5363
5628
|
);
|
|
5364
5629
|
}
|
|
@@ -5399,7 +5664,6 @@ const styles = StyleSheet.create({
|
|
|
5399
5664
|
});
|
|
5400
5665
|
|
|
5401
5666
|
export { SignUp };
|
|
5402
|
-
|
|
5403
5667
|
`],
|
|
5404
5668
|
["auth/better-auth/convex/native/base/lib/auth-client.ts.hbs", `import { createAuthClient } from "better-auth/react";
|
|
5405
5669
|
import { convexClient } from "@convex-dev/better-auth/client/plugins";
|
|
@@ -5421,6 +5685,7 @@ export const authClient = createAuthClient({
|
|
|
5421
5685
|
});
|
|
5422
5686
|
`],
|
|
5423
5687
|
["auth/better-auth/convex/native/unistyles/components/sign-in.tsx.hbs", `import { authClient } from "@/lib/auth-client";
|
|
5688
|
+
import { useForm } from "@tanstack/react-form";
|
|
5424
5689
|
import { useState } from "react";
|
|
5425
5690
|
import {
|
|
5426
5691
|
ActivityIndicator,
|
|
@@ -5430,76 +5695,151 @@ import {
|
|
|
5430
5695
|
View,
|
|
5431
5696
|
} from "react-native";
|
|
5432
5697
|
import { StyleSheet } from "react-native-unistyles";
|
|
5698
|
+
import z from "zod";
|
|
5433
5699
|
|
|
5434
|
-
|
|
5435
|
-
|
|
5436
|
-
|
|
5437
|
-
|
|
5438
|
-
|
|
5700
|
+
const signInSchema = z.object({
|
|
5701
|
+
email: z
|
|
5702
|
+
.string()
|
|
5703
|
+
.trim()
|
|
5704
|
+
.min(1, "Email is required")
|
|
5705
|
+
.email("Enter a valid email address"),
|
|
5706
|
+
password: z
|
|
5707
|
+
.string()
|
|
5708
|
+
.min(1, "Password is required")
|
|
5709
|
+
.min(8, "Use at least 8 characters"),
|
|
5710
|
+
});
|
|
5439
5711
|
|
|
5440
|
-
|
|
5441
|
-
|
|
5442
|
-
setError(null);
|
|
5712
|
+
function getErrorMessage(error: unknown): string | null {
|
|
5713
|
+
if (!error) return null;
|
|
5443
5714
|
|
|
5444
|
-
|
|
5445
|
-
|
|
5446
|
-
|
|
5447
|
-
|
|
5448
|
-
|
|
5449
|
-
|
|
5450
|
-
|
|
5451
|
-
|
|
5452
|
-
|
|
5453
|
-
|
|
5454
|
-
|
|
5455
|
-
|
|
5456
|
-
|
|
5715
|
+
if (typeof error === "string") {
|
|
5716
|
+
return error;
|
|
5717
|
+
}
|
|
5718
|
+
|
|
5719
|
+
if (Array.isArray(error)) {
|
|
5720
|
+
for (const issue of error) {
|
|
5721
|
+
const message = getErrorMessage(issue);
|
|
5722
|
+
if (message) {
|
|
5723
|
+
return message;
|
|
5724
|
+
}
|
|
5725
|
+
}
|
|
5726
|
+
return null;
|
|
5727
|
+
}
|
|
5728
|
+
|
|
5729
|
+
if (typeof error === "object" && error !== null) {
|
|
5730
|
+
const maybeError = error as { message?: unknown };
|
|
5731
|
+
if (typeof maybeError.message === "string") {
|
|
5732
|
+
return maybeError.message;
|
|
5733
|
+
}
|
|
5734
|
+
}
|
|
5735
|
+
|
|
5736
|
+
return null;
|
|
5737
|
+
}
|
|
5738
|
+
|
|
5739
|
+
export function SignIn() {
|
|
5740
|
+
const [error, setError] = useState<string | null>(null);
|
|
5741
|
+
|
|
5742
|
+
const form = useForm({
|
|
5743
|
+
defaultValues: {
|
|
5744
|
+
email: "",
|
|
5745
|
+
password: "",
|
|
5746
|
+
},
|
|
5747
|
+
validators: {
|
|
5748
|
+
onSubmit: signInSchema,
|
|
5749
|
+
},
|
|
5750
|
+
onSubmit: async ({ value, formApi }) => {
|
|
5751
|
+
await authClient.signIn.email(
|
|
5752
|
+
{
|
|
5753
|
+
email: value.email.trim(),
|
|
5754
|
+
password: value.password,
|
|
5457
5755
|
},
|
|
5458
|
-
|
|
5459
|
-
|
|
5756
|
+
{
|
|
5757
|
+
onError(error) {
|
|
5758
|
+
setError(error.error?.message || "Failed to sign in");
|
|
5759
|
+
},
|
|
5760
|
+
onSuccess() {
|
|
5761
|
+
setError(null);
|
|
5762
|
+
formApi.reset();
|
|
5763
|
+
},
|
|
5460
5764
|
},
|
|
5461
|
-
|
|
5462
|
-
|
|
5463
|
-
};
|
|
5765
|
+
);
|
|
5766
|
+
},
|
|
5767
|
+
});
|
|
5464
5768
|
|
|
5465
5769
|
return (
|
|
5466
5770
|
<View style={styles.container}>
|
|
5467
5771
|
<Text style={styles.title}>Sign In</Text>
|
|
5468
5772
|
|
|
5469
|
-
|
|
5470
|
-
|
|
5471
|
-
|
|
5472
|
-
|
|
5473
|
-
|
|
5474
|
-
|
|
5475
|
-
|
|
5476
|
-
|
|
5477
|
-
placeholder="Email"
|
|
5478
|
-
value={email}
|
|
5479
|
-
onChangeText={setEmail}
|
|
5480
|
-
keyboardType="email-address"
|
|
5481
|
-
autoCapitalize="none"
|
|
5482
|
-
/>
|
|
5773
|
+
<form.Subscribe
|
|
5774
|
+
selector={(state) => ({
|
|
5775
|
+
isSubmitting: state.isSubmitting,
|
|
5776
|
+
validationError: getErrorMessage(state.errorMap.onSubmit),
|
|
5777
|
+
})}
|
|
5778
|
+
>
|
|
5779
|
+
{({ isSubmitting, validationError }) => {
|
|
5780
|
+
const formError = error ?? validationError;
|
|
5483
5781
|
|
|
5484
|
-
|
|
5485
|
-
|
|
5486
|
-
|
|
5487
|
-
|
|
5488
|
-
|
|
5489
|
-
|
|
5490
|
-
|
|
5782
|
+
return (
|
|
5783
|
+
<>
|
|
5784
|
+
{formError ? (
|
|
5785
|
+
<View style={styles.errorContainer}>
|
|
5786
|
+
<Text style={styles.errorText}>{formError}</Text>
|
|
5787
|
+
</View>
|
|
5788
|
+
) : null}
|
|
5789
|
+
|
|
5790
|
+
<form.Field name="email">
|
|
5791
|
+
{(field) => (
|
|
5792
|
+
<TextInput
|
|
5793
|
+
style={styles.input}
|
|
5794
|
+
placeholder="Email"
|
|
5795
|
+
value={field.state.value}
|
|
5796
|
+
onBlur={field.handleBlur}
|
|
5797
|
+
onChangeText={(value) => {
|
|
5798
|
+
field.handleChange(value);
|
|
5799
|
+
if (error) {
|
|
5800
|
+
setError(null);
|
|
5801
|
+
}
|
|
5802
|
+
}}
|
|
5803
|
+
keyboardType="email-address"
|
|
5804
|
+
autoCapitalize="none"
|
|
5805
|
+
/>
|
|
5806
|
+
)}
|
|
5807
|
+
</form.Field>
|
|
5808
|
+
|
|
5809
|
+
<form.Field name="password">
|
|
5810
|
+
{(field) => (
|
|
5811
|
+
<TextInput
|
|
5812
|
+
style={styles.input}
|
|
5813
|
+
placeholder="Password"
|
|
5814
|
+
value={field.state.value}
|
|
5815
|
+
onBlur={field.handleBlur}
|
|
5816
|
+
onChangeText={(value) => {
|
|
5817
|
+
field.handleChange(value);
|
|
5818
|
+
if (error) {
|
|
5819
|
+
setError(null);
|
|
5820
|
+
}
|
|
5821
|
+
}}
|
|
5822
|
+
secureTextEntry
|
|
5823
|
+
onSubmitEditing={form.handleSubmit}
|
|
5824
|
+
/>
|
|
5825
|
+
)}
|
|
5826
|
+
</form.Field>
|
|
5491
5827
|
|
|
5492
|
-
|
|
5493
|
-
|
|
5494
|
-
|
|
5495
|
-
|
|
5496
|
-
|
|
5497
|
-
|
|
5498
|
-
|
|
5499
|
-
|
|
5500
|
-
|
|
5501
|
-
|
|
5502
|
-
|
|
5828
|
+
<TouchableOpacity
|
|
5829
|
+
onPress={form.handleSubmit}
|
|
5830
|
+
disabled={isSubmitting}
|
|
5831
|
+
style={styles.button}
|
|
5832
|
+
>
|
|
5833
|
+
{isSubmitting ? (
|
|
5834
|
+
<ActivityIndicator size="small" color="#fff" />
|
|
5835
|
+
) : (
|
|
5836
|
+
<Text style={styles.buttonText}>Sign In</Text>
|
|
5837
|
+
)}
|
|
5838
|
+
</TouchableOpacity>
|
|
5839
|
+
</>
|
|
5840
|
+
);
|
|
5841
|
+
}}
|
|
5842
|
+
</form.Subscribe>
|
|
5503
5843
|
</View>
|
|
5504
5844
|
);
|
|
5505
5845
|
}
|
|
@@ -5549,6 +5889,7 @@ const styles = StyleSheet.create((theme) => ({
|
|
|
5549
5889
|
}));
|
|
5550
5890
|
`],
|
|
5551
5891
|
["auth/better-auth/convex/native/unistyles/components/sign-up.tsx.hbs", `import { authClient } from "@/lib/auth-client";
|
|
5892
|
+
import { useForm } from "@tanstack/react-form";
|
|
5552
5893
|
import { useState } from "react";
|
|
5553
5894
|
import {
|
|
5554
5895
|
ActivityIndicator,
|
|
@@ -5558,86 +5899,175 @@ import {
|
|
|
5558
5899
|
View,
|
|
5559
5900
|
} from "react-native";
|
|
5560
5901
|
import { StyleSheet } from "react-native-unistyles";
|
|
5902
|
+
import z from "zod";
|
|
5903
|
+
|
|
5904
|
+
const signUpSchema = z.object({
|
|
5905
|
+
name: z
|
|
5906
|
+
.string()
|
|
5907
|
+
.trim()
|
|
5908
|
+
.min(1, "Name is required")
|
|
5909
|
+
.min(2, "Name must be at least 2 characters"),
|
|
5910
|
+
email: z
|
|
5911
|
+
.string()
|
|
5912
|
+
.trim()
|
|
5913
|
+
.min(1, "Email is required")
|
|
5914
|
+
.email("Enter a valid email address"),
|
|
5915
|
+
password: z
|
|
5916
|
+
.string()
|
|
5917
|
+
.min(1, "Password is required")
|
|
5918
|
+
.min(8, "Use at least 8 characters"),
|
|
5919
|
+
});
|
|
5920
|
+
|
|
5921
|
+
function getErrorMessage(error: unknown): string | null {
|
|
5922
|
+
if (!error) return null;
|
|
5923
|
+
|
|
5924
|
+
if (typeof error === "string") {
|
|
5925
|
+
return error;
|
|
5926
|
+
}
|
|
5927
|
+
|
|
5928
|
+
if (Array.isArray(error)) {
|
|
5929
|
+
for (const issue of error) {
|
|
5930
|
+
const message = getErrorMessage(issue);
|
|
5931
|
+
if (message) {
|
|
5932
|
+
return message;
|
|
5933
|
+
}
|
|
5934
|
+
}
|
|
5935
|
+
return null;
|
|
5936
|
+
}
|
|
5937
|
+
|
|
5938
|
+
if (typeof error === "object" && error !== null) {
|
|
5939
|
+
const maybeError = error as { message?: unknown };
|
|
5940
|
+
if (typeof maybeError.message === "string") {
|
|
5941
|
+
return maybeError.message;
|
|
5942
|
+
}
|
|
5943
|
+
}
|
|
5944
|
+
|
|
5945
|
+
return null;
|
|
5946
|
+
}
|
|
5561
5947
|
|
|
5562
5948
|
export function SignUp() {
|
|
5563
|
-
const [name, setName] = useState("");
|
|
5564
|
-
const [email, setEmail] = useState("");
|
|
5565
|
-
const [password, setPassword] = useState("");
|
|
5566
|
-
const [isLoading, setIsLoading] = useState(false);
|
|
5567
5949
|
const [error, setError] = useState<string | null>(null);
|
|
5568
5950
|
|
|
5569
|
-
const
|
|
5570
|
-
|
|
5571
|
-
|
|
5572
|
-
|
|
5573
|
-
|
|
5574
|
-
|
|
5575
|
-
|
|
5576
|
-
|
|
5577
|
-
|
|
5578
|
-
|
|
5579
|
-
|
|
5580
|
-
|
|
5581
|
-
|
|
5582
|
-
|
|
5583
|
-
|
|
5584
|
-
onSuccess: () => {
|
|
5585
|
-
setName("");
|
|
5586
|
-
setEmail("");
|
|
5587
|
-
setPassword("");
|
|
5951
|
+
const form = useForm({
|
|
5952
|
+
defaultValues: {
|
|
5953
|
+
name: "",
|
|
5954
|
+
email: "",
|
|
5955
|
+
password: "",
|
|
5956
|
+
},
|
|
5957
|
+
validators: {
|
|
5958
|
+
onSubmit: signUpSchema,
|
|
5959
|
+
},
|
|
5960
|
+
onSubmit: async ({ value, formApi }) => {
|
|
5961
|
+
await authClient.signUp.email(
|
|
5962
|
+
{
|
|
5963
|
+
name: value.name.trim(),
|
|
5964
|
+
email: value.email.trim(),
|
|
5965
|
+
password: value.password,
|
|
5588
5966
|
},
|
|
5589
|
-
|
|
5590
|
-
|
|
5967
|
+
{
|
|
5968
|
+
onError(error) {
|
|
5969
|
+
setError(error.error?.message || "Failed to sign up");
|
|
5970
|
+
},
|
|
5971
|
+
onSuccess() {
|
|
5972
|
+
setError(null);
|
|
5973
|
+
formApi.reset();
|
|
5974
|
+
},
|
|
5591
5975
|
},
|
|
5592
|
-
|
|
5593
|
-
|
|
5594
|
-
};
|
|
5976
|
+
);
|
|
5977
|
+
},
|
|
5978
|
+
});
|
|
5595
5979
|
|
|
5596
5980
|
return (
|
|
5597
5981
|
<View style={styles.container}>
|
|
5598
5982
|
<Text style={styles.title}>Create Account</Text>
|
|
5599
5983
|
|
|
5600
|
-
|
|
5601
|
-
|
|
5602
|
-
|
|
5603
|
-
|
|
5604
|
-
|
|
5605
|
-
|
|
5606
|
-
|
|
5607
|
-
|
|
5608
|
-
placeholder="Name"
|
|
5609
|
-
value={name}
|
|
5610
|
-
onChangeText={setName}
|
|
5611
|
-
/>
|
|
5612
|
-
|
|
5613
|
-
<TextInput
|
|
5614
|
-
style={styles.input}
|
|
5615
|
-
placeholder="Email"
|
|
5616
|
-
value={email}
|
|
5617
|
-
onChangeText={setEmail}
|
|
5618
|
-
keyboardType="email-address"
|
|
5619
|
-
autoCapitalize="none"
|
|
5620
|
-
/>
|
|
5984
|
+
<form.Subscribe
|
|
5985
|
+
selector={(state) => ({
|
|
5986
|
+
isSubmitting: state.isSubmitting,
|
|
5987
|
+
validationError: getErrorMessage(state.errorMap.onSubmit),
|
|
5988
|
+
})}
|
|
5989
|
+
>
|
|
5990
|
+
{({ isSubmitting, validationError }) => {
|
|
5991
|
+
const formError = error ?? validationError;
|
|
5621
5992
|
|
|
5622
|
-
|
|
5623
|
-
|
|
5624
|
-
|
|
5625
|
-
|
|
5626
|
-
|
|
5627
|
-
|
|
5628
|
-
|
|
5993
|
+
return (
|
|
5994
|
+
<>
|
|
5995
|
+
{formError ? (
|
|
5996
|
+
<View style={styles.errorContainer}>
|
|
5997
|
+
<Text style={styles.errorText}>{formError}</Text>
|
|
5998
|
+
</View>
|
|
5999
|
+
) : null}
|
|
6000
|
+
|
|
6001
|
+
<form.Field name="name">
|
|
6002
|
+
{(field) => (
|
|
6003
|
+
<TextInput
|
|
6004
|
+
style={styles.input}
|
|
6005
|
+
placeholder="Name"
|
|
6006
|
+
value={field.state.value}
|
|
6007
|
+
onBlur={field.handleBlur}
|
|
6008
|
+
onChangeText={(value) => {
|
|
6009
|
+
field.handleChange(value);
|
|
6010
|
+
if (error) {
|
|
6011
|
+
setError(null);
|
|
6012
|
+
}
|
|
6013
|
+
}}
|
|
6014
|
+
/>
|
|
6015
|
+
)}
|
|
6016
|
+
</form.Field>
|
|
6017
|
+
|
|
6018
|
+
<form.Field name="email">
|
|
6019
|
+
{(field) => (
|
|
6020
|
+
<TextInput
|
|
6021
|
+
style={styles.input}
|
|
6022
|
+
placeholder="Email"
|
|
6023
|
+
value={field.state.value}
|
|
6024
|
+
onBlur={field.handleBlur}
|
|
6025
|
+
onChangeText={(value) => {
|
|
6026
|
+
field.handleChange(value);
|
|
6027
|
+
if (error) {
|
|
6028
|
+
setError(null);
|
|
6029
|
+
}
|
|
6030
|
+
}}
|
|
6031
|
+
keyboardType="email-address"
|
|
6032
|
+
autoCapitalize="none"
|
|
6033
|
+
/>
|
|
6034
|
+
)}
|
|
6035
|
+
</form.Field>
|
|
6036
|
+
|
|
6037
|
+
<form.Field name="password">
|
|
6038
|
+
{(field) => (
|
|
6039
|
+
<TextInput
|
|
6040
|
+
style={styles.inputLast}
|
|
6041
|
+
placeholder="Password"
|
|
6042
|
+
value={field.state.value}
|
|
6043
|
+
onBlur={field.handleBlur}
|
|
6044
|
+
onChangeText={(value) => {
|
|
6045
|
+
field.handleChange(value);
|
|
6046
|
+
if (error) {
|
|
6047
|
+
setError(null);
|
|
6048
|
+
}
|
|
6049
|
+
}}
|
|
6050
|
+
secureTextEntry
|
|
6051
|
+
onSubmitEditing={form.handleSubmit}
|
|
6052
|
+
/>
|
|
6053
|
+
)}
|
|
6054
|
+
</form.Field>
|
|
5629
6055
|
|
|
5630
|
-
|
|
5631
|
-
|
|
5632
|
-
|
|
5633
|
-
|
|
5634
|
-
|
|
5635
|
-
|
|
5636
|
-
|
|
5637
|
-
|
|
5638
|
-
|
|
5639
|
-
|
|
5640
|
-
|
|
6056
|
+
<TouchableOpacity
|
|
6057
|
+
onPress={form.handleSubmit}
|
|
6058
|
+
disabled={isSubmitting}
|
|
6059
|
+
style={styles.button}
|
|
6060
|
+
>
|
|
6061
|
+
{isSubmitting ? (
|
|
6062
|
+
<ActivityIndicator size="small" color="#fff" />
|
|
6063
|
+
) : (
|
|
6064
|
+
<Text style={styles.buttonText}>Sign Up</Text>
|
|
6065
|
+
)}
|
|
6066
|
+
</TouchableOpacity>
|
|
6067
|
+
</>
|
|
6068
|
+
);
|
|
6069
|
+
}}
|
|
6070
|
+
</form.Subscribe>
|
|
5641
6071
|
</View>
|
|
5642
6072
|
);
|
|
5643
6073
|
}
|
|
@@ -5695,161 +6125,351 @@ const styles = StyleSheet.create((theme) => ({
|
|
|
5695
6125
|
}));
|
|
5696
6126
|
`],
|
|
5697
6127
|
["auth/better-auth/convex/native/uniwind/components/sign-in.tsx.hbs", `import { authClient } from "@/lib/auth-client";
|
|
5698
|
-
import {
|
|
5699
|
-
import {
|
|
5700
|
-
import {
|
|
6128
|
+
import { useForm } from "@tanstack/react-form";
|
|
6129
|
+
import { useRef } from "react";
|
|
6130
|
+
import { Text, TextInput, View } from "react-native";
|
|
6131
|
+
import { Button, FieldError, Input, Label, Spinner, Surface, TextField, useToast } from "heroui-native";
|
|
6132
|
+
import z from "zod";
|
|
5701
6133
|
|
|
5702
|
-
|
|
5703
|
-
|
|
5704
|
-
|
|
5705
|
-
|
|
5706
|
-
|
|
6134
|
+
const signInSchema = z.object({
|
|
6135
|
+
email: z
|
|
6136
|
+
.string()
|
|
6137
|
+
.trim()
|
|
6138
|
+
.min(1, "Email is required")
|
|
6139
|
+
.email("Enter a valid email address"),
|
|
6140
|
+
password: z
|
|
6141
|
+
.string()
|
|
6142
|
+
.min(1, "Password is required")
|
|
6143
|
+
.min(8, "Use at least 8 characters"),
|
|
6144
|
+
});
|
|
5707
6145
|
|
|
5708
|
-
|
|
5709
|
-
|
|
5710
|
-
setError(null);
|
|
6146
|
+
function getErrorMessage(error: unknown): string | null {
|
|
6147
|
+
if (!error) return null;
|
|
5711
6148
|
|
|
5712
|
-
|
|
5713
|
-
|
|
5714
|
-
|
|
5715
|
-
|
|
5716
|
-
|
|
5717
|
-
|
|
5718
|
-
|
|
5719
|
-
|
|
5720
|
-
|
|
5721
|
-
|
|
5722
|
-
|
|
5723
|
-
|
|
5724
|
-
|
|
6149
|
+
if (typeof error === "string") {
|
|
6150
|
+
return error;
|
|
6151
|
+
}
|
|
6152
|
+
|
|
6153
|
+
if (Array.isArray(error)) {
|
|
6154
|
+
for (const issue of error) {
|
|
6155
|
+
const message = getErrorMessage(issue);
|
|
6156
|
+
if (message) {
|
|
6157
|
+
return message;
|
|
6158
|
+
}
|
|
6159
|
+
}
|
|
6160
|
+
return null;
|
|
6161
|
+
}
|
|
6162
|
+
|
|
6163
|
+
if (typeof error === "object" && error !== null) {
|
|
6164
|
+
const maybeError = error as { message?: unknown };
|
|
6165
|
+
if (typeof maybeError.message === "string") {
|
|
6166
|
+
return maybeError.message;
|
|
6167
|
+
}
|
|
6168
|
+
}
|
|
6169
|
+
|
|
6170
|
+
return null;
|
|
6171
|
+
}
|
|
6172
|
+
|
|
6173
|
+
export function SignIn() {
|
|
6174
|
+
const passwordInputRef = useRef<TextInput>(null);
|
|
6175
|
+
const { toast } = useToast();
|
|
6176
|
+
|
|
6177
|
+
const form = useForm({
|
|
6178
|
+
defaultValues: {
|
|
6179
|
+
email: "",
|
|
6180
|
+
password: "",
|
|
6181
|
+
},
|
|
6182
|
+
validators: {
|
|
6183
|
+
onSubmit: signInSchema,
|
|
6184
|
+
},
|
|
6185
|
+
onSubmit: async ({ value, formApi }) => {
|
|
6186
|
+
await authClient.signIn.email(
|
|
6187
|
+
{
|
|
6188
|
+
email: value.email.trim(),
|
|
6189
|
+
password: value.password,
|
|
5725
6190
|
},
|
|
5726
|
-
|
|
5727
|
-
|
|
6191
|
+
{
|
|
6192
|
+
onError(error) {
|
|
6193
|
+
toast.show({
|
|
6194
|
+
variant: "danger",
|
|
6195
|
+
label: error.error?.message || "Failed to sign in",
|
|
6196
|
+
});
|
|
6197
|
+
},
|
|
6198
|
+
onSuccess() {
|
|
6199
|
+
formApi.reset();
|
|
6200
|
+
toast.show({
|
|
6201
|
+
variant: "success",
|
|
6202
|
+
label: "Signed in successfully",
|
|
6203
|
+
});
|
|
6204
|
+
},
|
|
5728
6205
|
},
|
|
5729
|
-
|
|
5730
|
-
|
|
5731
|
-
};
|
|
6206
|
+
);
|
|
6207
|
+
},
|
|
6208
|
+
});
|
|
5732
6209
|
|
|
5733
6210
|
return (
|
|
5734
6211
|
<Surface variant="secondary" className="p-4 rounded-lg">
|
|
5735
6212
|
<Text className="text-foreground font-medium mb-4">Sign In</Text>
|
|
5736
6213
|
|
|
5737
|
-
<
|
|
5738
|
-
{
|
|
5739
|
-
|
|
5740
|
-
|
|
5741
|
-
|
|
5742
|
-
|
|
5743
|
-
|
|
5744
|
-
|
|
5745
|
-
value={email}
|
|
5746
|
-
onChangeText={setEmail}
|
|
5747
|
-
placeholder="email@example.com"
|
|
5748
|
-
keyboardType="email-address"
|
|
5749
|
-
autoCapitalize="none"
|
|
5750
|
-
/>
|
|
5751
|
-
</TextField>
|
|
5752
|
-
|
|
5753
|
-
<TextField>
|
|
5754
|
-
<TextField.Label>Password</TextField.Label>
|
|
5755
|
-
<TextField.Input
|
|
5756
|
-
value={password}
|
|
5757
|
-
onChangeText={setPassword}
|
|
5758
|
-
placeholder="••••••••"
|
|
5759
|
-
secureTextEntry
|
|
5760
|
-
/>
|
|
5761
|
-
</TextField>
|
|
6214
|
+
<form.Subscribe
|
|
6215
|
+
selector={(state) => ({
|
|
6216
|
+
isSubmitting: state.isSubmitting,
|
|
6217
|
+
validationError: getErrorMessage(state.errorMap.onSubmit),
|
|
6218
|
+
})}
|
|
6219
|
+
>
|
|
6220
|
+
{({ isSubmitting, validationError }) => {
|
|
6221
|
+
const formError = validationError;
|
|
5762
6222
|
|
|
5763
|
-
|
|
5764
|
-
|
|
5765
|
-
|
|
5766
|
-
|
|
6223
|
+
return (
|
|
6224
|
+
<>
|
|
6225
|
+
<FieldError isInvalid={!!formError} className="mb-3">
|
|
6226
|
+
{formError}
|
|
6227
|
+
</FieldError>
|
|
6228
|
+
|
|
6229
|
+
<View className="gap-3">
|
|
6230
|
+
<form.Field name="email">
|
|
6231
|
+
{(field) => (
|
|
6232
|
+
<TextField>
|
|
6233
|
+
<Label>Email</Label>
|
|
6234
|
+
<Input
|
|
6235
|
+
value={field.state.value}
|
|
6236
|
+
onBlur={field.handleBlur}
|
|
6237
|
+
onChangeText={field.handleChange}
|
|
6238
|
+
placeholder="email@example.com"
|
|
6239
|
+
keyboardType="email-address"
|
|
6240
|
+
autoCapitalize="none"
|
|
6241
|
+
autoComplete="email"
|
|
6242
|
+
textContentType="emailAddress"
|
|
6243
|
+
returnKeyType="next"
|
|
6244
|
+
blurOnSubmit={false}
|
|
6245
|
+
onSubmitEditing={() => {
|
|
6246
|
+
passwordInputRef.current?.focus();
|
|
6247
|
+
}}
|
|
6248
|
+
/>
|
|
6249
|
+
</TextField>
|
|
6250
|
+
)}
|
|
6251
|
+
</form.Field>
|
|
6252
|
+
|
|
6253
|
+
<form.Field name="password">
|
|
6254
|
+
{(field) => (
|
|
6255
|
+
<TextField>
|
|
6256
|
+
<Label>Password</Label>
|
|
6257
|
+
<Input
|
|
6258
|
+
ref={passwordInputRef}
|
|
6259
|
+
value={field.state.value}
|
|
6260
|
+
onBlur={field.handleBlur}
|
|
6261
|
+
onChangeText={field.handleChange}
|
|
6262
|
+
placeholder="••••••••"
|
|
6263
|
+
secureTextEntry
|
|
6264
|
+
autoComplete="password"
|
|
6265
|
+
textContentType="password"
|
|
6266
|
+
returnKeyType="go"
|
|
6267
|
+
onSubmitEditing={form.handleSubmit}
|
|
6268
|
+
/>
|
|
6269
|
+
</TextField>
|
|
6270
|
+
)}
|
|
6271
|
+
</form.Field>
|
|
6272
|
+
|
|
6273
|
+
<Button onPress={form.handleSubmit} isDisabled={isSubmitting} className="mt-1">
|
|
6274
|
+
{isSubmitting ? <Spinner size="sm" color="default" /> : <Button.Label>Sign In</Button.Label>}
|
|
6275
|
+
</Button>
|
|
6276
|
+
</View>
|
|
6277
|
+
</>
|
|
6278
|
+
);
|
|
6279
|
+
}}
|
|
6280
|
+
</form.Subscribe>
|
|
5767
6281
|
</Surface>
|
|
5768
6282
|
);
|
|
5769
6283
|
}
|
|
5770
6284
|
`],
|
|
5771
6285
|
["auth/better-auth/convex/native/uniwind/components/sign-up.tsx.hbs", `import { authClient } from "@/lib/auth-client";
|
|
5772
|
-
import {
|
|
5773
|
-
import {
|
|
5774
|
-
import {
|
|
6286
|
+
import { useForm } from "@tanstack/react-form";
|
|
6287
|
+
import { useRef } from "react";
|
|
6288
|
+
import { Text, TextInput, View } from "react-native";
|
|
6289
|
+
import { Button, FieldError, Input, Label, Spinner, Surface, TextField, useToast } from "heroui-native";
|
|
6290
|
+
import z from "zod";
|
|
5775
6291
|
|
|
5776
|
-
|
|
5777
|
-
|
|
5778
|
-
|
|
5779
|
-
|
|
5780
|
-
|
|
5781
|
-
|
|
6292
|
+
const signUpSchema = z.object({
|
|
6293
|
+
name: z
|
|
6294
|
+
.string()
|
|
6295
|
+
.trim()
|
|
6296
|
+
.min(1, "Name is required")
|
|
6297
|
+
.min(2, "Name must be at least 2 characters"),
|
|
6298
|
+
email: z
|
|
6299
|
+
.string()
|
|
6300
|
+
.trim()
|
|
6301
|
+
.min(1, "Email is required")
|
|
6302
|
+
.email("Enter a valid email address"),
|
|
6303
|
+
password: z
|
|
6304
|
+
.string()
|
|
6305
|
+
.min(1, "Password is required")
|
|
6306
|
+
.min(8, "Use at least 8 characters"),
|
|
6307
|
+
});
|
|
5782
6308
|
|
|
5783
|
-
|
|
5784
|
-
|
|
5785
|
-
setError(null);
|
|
6309
|
+
function getErrorMessage(error: unknown): string | null {
|
|
6310
|
+
if (!error) return null;
|
|
5786
6311
|
|
|
5787
|
-
|
|
5788
|
-
|
|
5789
|
-
|
|
5790
|
-
|
|
5791
|
-
|
|
5792
|
-
|
|
5793
|
-
|
|
5794
|
-
|
|
5795
|
-
|
|
5796
|
-
|
|
5797
|
-
|
|
5798
|
-
|
|
5799
|
-
|
|
5800
|
-
|
|
5801
|
-
|
|
6312
|
+
if (typeof error === "string") {
|
|
6313
|
+
return error;
|
|
6314
|
+
}
|
|
6315
|
+
|
|
6316
|
+
if (Array.isArray(error)) {
|
|
6317
|
+
for (const issue of error) {
|
|
6318
|
+
const message = getErrorMessage(issue);
|
|
6319
|
+
if (message) {
|
|
6320
|
+
return message;
|
|
6321
|
+
}
|
|
6322
|
+
}
|
|
6323
|
+
return null;
|
|
6324
|
+
}
|
|
6325
|
+
|
|
6326
|
+
if (typeof error === "object" && error !== null) {
|
|
6327
|
+
const maybeError = error as { message?: unknown };
|
|
6328
|
+
if (typeof maybeError.message === "string") {
|
|
6329
|
+
return maybeError.message;
|
|
6330
|
+
}
|
|
6331
|
+
}
|
|
6332
|
+
|
|
6333
|
+
return null;
|
|
6334
|
+
}
|
|
6335
|
+
|
|
6336
|
+
export function SignUp() {
|
|
6337
|
+
const emailInputRef = useRef<TextInput>(null);
|
|
6338
|
+
const passwordInputRef = useRef<TextInput>(null);
|
|
6339
|
+
const { toast } = useToast();
|
|
6340
|
+
|
|
6341
|
+
const form = useForm({
|
|
6342
|
+
defaultValues: {
|
|
6343
|
+
name: "",
|
|
6344
|
+
email: "",
|
|
6345
|
+
password: "",
|
|
6346
|
+
},
|
|
6347
|
+
validators: {
|
|
6348
|
+
onSubmit: signUpSchema,
|
|
6349
|
+
},
|
|
6350
|
+
onSubmit: async ({ value, formApi }) => {
|
|
6351
|
+
await authClient.signUp.email(
|
|
6352
|
+
{
|
|
6353
|
+
name: value.name.trim(),
|
|
6354
|
+
email: value.email.trim(),
|
|
6355
|
+
password: value.password,
|
|
5802
6356
|
},
|
|
5803
|
-
|
|
5804
|
-
|
|
6357
|
+
{
|
|
6358
|
+
onError(error) {
|
|
6359
|
+
toast.show({
|
|
6360
|
+
variant: "danger",
|
|
6361
|
+
label: error.error?.message || "Failed to sign up",
|
|
6362
|
+
});
|
|
6363
|
+
},
|
|
6364
|
+
onSuccess() {
|
|
6365
|
+
formApi.reset();
|
|
6366
|
+
toast.show({
|
|
6367
|
+
variant: "success",
|
|
6368
|
+
label: "Account created successfully",
|
|
6369
|
+
});
|
|
6370
|
+
},
|
|
5805
6371
|
},
|
|
5806
|
-
|
|
5807
|
-
|
|
5808
|
-
};
|
|
6372
|
+
);
|
|
6373
|
+
},
|
|
6374
|
+
});
|
|
5809
6375
|
|
|
5810
6376
|
return (
|
|
5811
6377
|
<Surface variant="secondary" className="p-4 rounded-lg">
|
|
5812
6378
|
<Text className="text-foreground font-medium mb-4">Create Account</Text>
|
|
5813
6379
|
|
|
5814
|
-
<
|
|
5815
|
-
{
|
|
5816
|
-
|
|
5817
|
-
|
|
5818
|
-
|
|
5819
|
-
|
|
5820
|
-
|
|
5821
|
-
|
|
5822
|
-
</TextField>
|
|
5823
|
-
|
|
5824
|
-
<TextField>
|
|
5825
|
-
<TextField.Label>Email</TextField.Label>
|
|
5826
|
-
<TextField.Input
|
|
5827
|
-
value={email}
|
|
5828
|
-
onChangeText={setEmail}
|
|
5829
|
-
placeholder="email@example.com"
|
|
5830
|
-
keyboardType="email-address"
|
|
5831
|
-
autoCapitalize="none"
|
|
5832
|
-
/>
|
|
5833
|
-
</TextField>
|
|
5834
|
-
|
|
5835
|
-
<TextField>
|
|
5836
|
-
<TextField.Label>Password</TextField.Label>
|
|
5837
|
-
<TextField.Input
|
|
5838
|
-
value={password}
|
|
5839
|
-
onChangeText={setPassword}
|
|
5840
|
-
placeholder="••••••••"
|
|
5841
|
-
secureTextEntry
|
|
5842
|
-
/>
|
|
5843
|
-
</TextField>
|
|
6380
|
+
<form.Subscribe
|
|
6381
|
+
selector={(state) => ({
|
|
6382
|
+
isSubmitting: state.isSubmitting,
|
|
6383
|
+
validationError: getErrorMessage(state.errorMap.onSubmit),
|
|
6384
|
+
})}
|
|
6385
|
+
>
|
|
6386
|
+
{({ isSubmitting, validationError }) => {
|
|
6387
|
+
const formError = validationError;
|
|
5844
6388
|
|
|
5845
|
-
|
|
5846
|
-
|
|
5847
|
-
|
|
5848
|
-
|
|
5849
|
-
|
|
5850
|
-
|
|
5851
|
-
|
|
5852
|
-
|
|
6389
|
+
return (
|
|
6390
|
+
<>
|
|
6391
|
+
<FieldError isInvalid={!!formError} className="mb-3">
|
|
6392
|
+
{formError}
|
|
6393
|
+
</FieldError>
|
|
6394
|
+
|
|
6395
|
+
<View className="gap-3">
|
|
6396
|
+
<form.Field name="name">
|
|
6397
|
+
{(field) => (
|
|
6398
|
+
<TextField>
|
|
6399
|
+
<Label>Name</Label>
|
|
6400
|
+
<Input
|
|
6401
|
+
value={field.state.value}
|
|
6402
|
+
onBlur={field.handleBlur}
|
|
6403
|
+
onChangeText={field.handleChange}
|
|
6404
|
+
placeholder="John Doe"
|
|
6405
|
+
autoComplete="name"
|
|
6406
|
+
textContentType="name"
|
|
6407
|
+
returnKeyType="next"
|
|
6408
|
+
blurOnSubmit={false}
|
|
6409
|
+
onSubmitEditing={() => {
|
|
6410
|
+
emailInputRef.current?.focus();
|
|
6411
|
+
}}
|
|
6412
|
+
/>
|
|
6413
|
+
</TextField>
|
|
6414
|
+
)}
|
|
6415
|
+
</form.Field>
|
|
6416
|
+
|
|
6417
|
+
<form.Field name="email">
|
|
6418
|
+
{(field) => (
|
|
6419
|
+
<TextField>
|
|
6420
|
+
<Label>Email</Label>
|
|
6421
|
+
<Input
|
|
6422
|
+
ref={emailInputRef}
|
|
6423
|
+
value={field.state.value}
|
|
6424
|
+
onBlur={field.handleBlur}
|
|
6425
|
+
onChangeText={field.handleChange}
|
|
6426
|
+
placeholder="email@example.com"
|
|
6427
|
+
keyboardType="email-address"
|
|
6428
|
+
autoCapitalize="none"
|
|
6429
|
+
autoComplete="email"
|
|
6430
|
+
textContentType="emailAddress"
|
|
6431
|
+
returnKeyType="next"
|
|
6432
|
+
blurOnSubmit={false}
|
|
6433
|
+
onSubmitEditing={() => {
|
|
6434
|
+
passwordInputRef.current?.focus();
|
|
6435
|
+
}}
|
|
6436
|
+
/>
|
|
6437
|
+
</TextField>
|
|
6438
|
+
)}
|
|
6439
|
+
</form.Field>
|
|
6440
|
+
|
|
6441
|
+
<form.Field name="password">
|
|
6442
|
+
{(field) => (
|
|
6443
|
+
<TextField>
|
|
6444
|
+
<Label>Password</Label>
|
|
6445
|
+
<Input
|
|
6446
|
+
ref={passwordInputRef}
|
|
6447
|
+
value={field.state.value}
|
|
6448
|
+
onBlur={field.handleBlur}
|
|
6449
|
+
onChangeText={field.handleChange}
|
|
6450
|
+
placeholder="••••••••"
|
|
6451
|
+
secureTextEntry
|
|
6452
|
+
autoComplete="new-password"
|
|
6453
|
+
textContentType="newPassword"
|
|
6454
|
+
returnKeyType="go"
|
|
6455
|
+
onSubmitEditing={form.handleSubmit}
|
|
6456
|
+
/>
|
|
6457
|
+
</TextField>
|
|
6458
|
+
)}
|
|
6459
|
+
</form.Field>
|
|
6460
|
+
|
|
6461
|
+
<Button onPress={form.handleSubmit} isDisabled={isSubmitting} className="mt-1">
|
|
6462
|
+
{isSubmitting ? (
|
|
6463
|
+
<Spinner size="sm" color="default" />
|
|
6464
|
+
) : (
|
|
6465
|
+
<Button.Label>Create Account</Button.Label>
|
|
6466
|
+
)}
|
|
6467
|
+
</Button>
|
|
6468
|
+
</View>
|
|
6469
|
+
</>
|
|
6470
|
+
);
|
|
6471
|
+
}}
|
|
6472
|
+
</form.Subscribe>
|
|
5853
6473
|
</Surface>
|
|
5854
6474
|
);
|
|
5855
6475
|
}
|
|
@@ -7329,130 +7949,234 @@ import { queryClient } from "@/utils/trpc";
|
|
|
7329
7949
|
{{#if (eq api "orpc")}}
|
|
7330
7950
|
import { queryClient } from "@/utils/orpc";
|
|
7331
7951
|
{{/if}}
|
|
7952
|
+
import { useForm } from "@tanstack/react-form";
|
|
7332
7953
|
import { useState } from "react";
|
|
7333
7954
|
import {
|
|
7334
|
-
ActivityIndicator,
|
|
7335
|
-
|
|
7336
|
-
|
|
7337
|
-
|
|
7338
|
-
|
|
7339
|
-
|
|
7955
|
+
ActivityIndicator,
|
|
7956
|
+
StyleSheet,
|
|
7957
|
+
Text,
|
|
7958
|
+
TextInput,
|
|
7959
|
+
TouchableOpacity,
|
|
7960
|
+
View,
|
|
7340
7961
|
} from "react-native";
|
|
7341
|
-
import { useColorScheme } from "@/lib/use-color-scheme";
|
|
7342
7962
|
import { NAV_THEME } from "@/lib/constants";
|
|
7963
|
+
import { useColorScheme } from "@/lib/use-color-scheme";
|
|
7964
|
+
import z from "zod";
|
|
7343
7965
|
|
|
7344
|
-
|
|
7345
|
-
|
|
7346
|
-
|
|
7347
|
-
|
|
7348
|
-
|
|
7349
|
-
|
|
7966
|
+
const signInSchema = z.object({
|
|
7967
|
+
email: z
|
|
7968
|
+
.string()
|
|
7969
|
+
.trim()
|
|
7970
|
+
.min(1, "Email is required")
|
|
7971
|
+
.email("Enter a valid email address"),
|
|
7972
|
+
password: z
|
|
7973
|
+
.string()
|
|
7974
|
+
.min(1, "Password is required")
|
|
7975
|
+
.min(8, "Use at least 8 characters"),
|
|
7976
|
+
});
|
|
7350
7977
|
|
|
7351
|
-
|
|
7352
|
-
|
|
7978
|
+
function getErrorMessage(error: unknown): string | null {
|
|
7979
|
+
if (!error) return null;
|
|
7980
|
+
|
|
7981
|
+
if (typeof error === "string") {
|
|
7982
|
+
return error;
|
|
7983
|
+
}
|
|
7984
|
+
|
|
7985
|
+
if (Array.isArray(error)) {
|
|
7986
|
+
for (const issue of error) {
|
|
7987
|
+
const message = getErrorMessage(issue);
|
|
7988
|
+
if (message) {
|
|
7989
|
+
return message;
|
|
7990
|
+
}
|
|
7353
7991
|
}
|
|
7992
|
+
return null;
|
|
7993
|
+
}
|
|
7354
7994
|
|
|
7355
|
-
|
|
7356
|
-
|
|
7357
|
-
|
|
7995
|
+
if (typeof error === "object" && error !== null) {
|
|
7996
|
+
const maybeError = error as { message?: unknown };
|
|
7997
|
+
if (typeof maybeError.message === "string") {
|
|
7998
|
+
return maybeError.message;
|
|
7999
|
+
}
|
|
8000
|
+
}
|
|
7358
8001
|
|
|
7359
|
-
|
|
7360
|
-
|
|
7361
|
-
|
|
7362
|
-
|
|
7363
|
-
|
|
7364
|
-
|
|
7365
|
-
|
|
7366
|
-
|
|
7367
|
-
|
|
8002
|
+
return null;
|
|
8003
|
+
}
|
|
8004
|
+
|
|
8005
|
+
function SignIn() {
|
|
8006
|
+
const { colorScheme } = useColorScheme();
|
|
8007
|
+
const theme = colorScheme === "dark" ? NAV_THEME.dark : NAV_THEME.light;
|
|
8008
|
+
const [error, setError] = useState<string | null>(null);
|
|
8009
|
+
|
|
8010
|
+
const form = useForm({
|
|
8011
|
+
defaultValues: {
|
|
8012
|
+
email: "",
|
|
8013
|
+
password: "",
|
|
7368
8014
|
},
|
|
7369
|
-
|
|
7370
|
-
|
|
7371
|
-
{{#if (eq api "orpc")}}
|
|
7372
|
-
queryClient.refetchQueries();
|
|
7373
|
-
{{/if}}
|
|
7374
|
-
{{#if (eq api "trpc")}}
|
|
7375
|
-
queryClient.refetchQueries();
|
|
7376
|
-
{{/if}}
|
|
8015
|
+
validators: {
|
|
8016
|
+
onSubmit: signInSchema,
|
|
7377
8017
|
},
|
|
7378
|
-
|
|
7379
|
-
|
|
8018
|
+
onSubmit: async ({ value, formApi }) => {
|
|
8019
|
+
await authClient.signIn.email(
|
|
8020
|
+
{
|
|
8021
|
+
email: value.email.trim(),
|
|
8022
|
+
password: value.password,
|
|
8023
|
+
},
|
|
8024
|
+
{
|
|
8025
|
+
onError(error) {
|
|
8026
|
+
setError(error.error?.message || "Failed to sign in");
|
|
8027
|
+
},
|
|
8028
|
+
onSuccess() {
|
|
8029
|
+
setError(null);
|
|
8030
|
+
formApi.reset();
|
|
8031
|
+
{{#if (eq api "orpc")}}
|
|
8032
|
+
queryClient.refetchQueries();
|
|
8033
|
+
{{/if}}
|
|
8034
|
+
{{#if (eq api "trpc")}}
|
|
8035
|
+
queryClient.refetchQueries();
|
|
8036
|
+
{{/if}}
|
|
8037
|
+
},
|
|
8038
|
+
},
|
|
8039
|
+
);
|
|
7380
8040
|
},
|
|
7381
|
-
|
|
7382
|
-
);
|
|
7383
|
-
}
|
|
8041
|
+
});
|
|
7384
8042
|
|
|
7385
|
-
|
|
8043
|
+
return (
|
|
7386
8044
|
<View style={[styles.card, { backgroundColor: theme.card, borderColor: theme.border }]}>
|
|
7387
|
-
|
|
8045
|
+
<Text style={[styles.title, { color: theme.text }]}>Sign In</Text>
|
|
7388
8046
|
|
|
7389
|
-
|
|
7390
|
-
|
|
7391
|
-
|
|
7392
|
-
|
|
7393
|
-
)
|
|
8047
|
+
<form.Subscribe
|
|
8048
|
+
selector={(state) => ({
|
|
8049
|
+
isSubmitting: state.isSubmitting,
|
|
8050
|
+
validationError: getErrorMessage(state.errorMap.onSubmit),
|
|
8051
|
+
})}
|
|
8052
|
+
>
|
|
8053
|
+
{({ isSubmitting, validationError }) => {
|
|
8054
|
+
const formError = error ?? validationError;
|
|
7394
8055
|
|
|
7395
|
-
|
|
7396
|
-
|
|
7397
|
-
|
|
7398
|
-
|
|
7399
|
-
|
|
7400
|
-
|
|
8056
|
+
return (
|
|
8057
|
+
<>
|
|
8058
|
+
{formError ? (
|
|
8059
|
+
<View style={[styles.errorContainer, { backgroundColor: theme.notification + "20" }]}>
|
|
8060
|
+
<Text style={[styles.errorText, { color: theme.notification }]}>{formError}</Text>
|
|
8061
|
+
</View>
|
|
8062
|
+
) : null}
|
|
7401
8063
|
|
|
7402
|
-
|
|
7403
|
-
|
|
7404
|
-
|
|
7405
|
-
|
|
7406
|
-
|
|
8064
|
+
<form.Field name="email">
|
|
8065
|
+
{(field) => (
|
|
8066
|
+
<TextInput
|
|
8067
|
+
style={[
|
|
8068
|
+
styles.input,
|
|
8069
|
+
{
|
|
8070
|
+
color: theme.text,
|
|
8071
|
+
borderColor: theme.border,
|
|
8072
|
+
backgroundColor: theme.background,
|
|
8073
|
+
},
|
|
8074
|
+
]}
|
|
8075
|
+
placeholder="Email"
|
|
8076
|
+
placeholderTextColor={theme.text}
|
|
8077
|
+
value={field.state.value}
|
|
8078
|
+
onBlur={field.handleBlur}
|
|
8079
|
+
onChangeText={(value) => {
|
|
8080
|
+
field.handleChange(value);
|
|
8081
|
+
if (error) {
|
|
8082
|
+
setError(null);
|
|
8083
|
+
}
|
|
8084
|
+
}}
|
|
8085
|
+
keyboardType="email-address"
|
|
8086
|
+
autoCapitalize="none"
|
|
8087
|
+
/>
|
|
8088
|
+
)}
|
|
8089
|
+
</form.Field>
|
|
7407
8090
|
|
|
7408
|
-
|
|
7409
|
-
|
|
7410
|
-
|
|
7411
|
-
|
|
7412
|
-
|
|
7413
|
-
|
|
7414
|
-
|
|
7415
|
-
|
|
8091
|
+
<form.Field name="password">
|
|
8092
|
+
{(field) => (
|
|
8093
|
+
<TextInput
|
|
8094
|
+
style={[
|
|
8095
|
+
styles.input,
|
|
8096
|
+
{
|
|
8097
|
+
color: theme.text,
|
|
8098
|
+
borderColor: theme.border,
|
|
8099
|
+
backgroundColor: theme.background,
|
|
8100
|
+
},
|
|
8101
|
+
]}
|
|
8102
|
+
placeholder="Password"
|
|
8103
|
+
placeholderTextColor={theme.text}
|
|
8104
|
+
value={field.state.value}
|
|
8105
|
+
onBlur={field.handleBlur}
|
|
8106
|
+
onChangeText={(value) => {
|
|
8107
|
+
field.handleChange(value);
|
|
8108
|
+
if (error) {
|
|
8109
|
+
setError(null);
|
|
8110
|
+
}
|
|
8111
|
+
}}
|
|
8112
|
+
secureTextEntry
|
|
8113
|
+
onSubmitEditing={form.handleSubmit}
|
|
8114
|
+
/>
|
|
8115
|
+
)}
|
|
8116
|
+
</form.Field>
|
|
8117
|
+
|
|
8118
|
+
<TouchableOpacity
|
|
8119
|
+
onPress={form.handleSubmit}
|
|
8120
|
+
disabled={isSubmitting}
|
|
8121
|
+
style={[
|
|
8122
|
+
styles.button,
|
|
8123
|
+
{
|
|
8124
|
+
backgroundColor: theme.primary,
|
|
8125
|
+
opacity: isSubmitting ? 0.5 : 1,
|
|
8126
|
+
},
|
|
8127
|
+
]}
|
|
8128
|
+
>
|
|
8129
|
+
{isSubmitting ? (
|
|
8130
|
+
<ActivityIndicator size="small" color="#ffffff" />
|
|
8131
|
+
) : (
|
|
8132
|
+
<Text style={styles.buttonText}>Sign In</Text>
|
|
8133
|
+
)}
|
|
8134
|
+
</TouchableOpacity>
|
|
8135
|
+
</>
|
|
8136
|
+
);
|
|
8137
|
+
}}
|
|
8138
|
+
</form.Subscribe>
|
|
7416
8139
|
</View>
|
|
7417
|
-
|
|
7418
|
-
|
|
8140
|
+
);
|
|
8141
|
+
}
|
|
7419
8142
|
|
|
7420
|
-
|
|
7421
|
-
|
|
8143
|
+
const styles = StyleSheet.create({
|
|
8144
|
+
card: {
|
|
7422
8145
|
marginTop: 16,
|
|
7423
8146
|
padding: 16,
|
|
7424
8147
|
borderWidth: 1,
|
|
7425
|
-
|
|
7426
|
-
|
|
8148
|
+
},
|
|
8149
|
+
title: {
|
|
7427
8150
|
fontSize: 18,
|
|
7428
8151
|
fontWeight: "bold",
|
|
7429
8152
|
marginBottom: 12,
|
|
7430
|
-
|
|
7431
|
-
|
|
8153
|
+
},
|
|
8154
|
+
errorContainer: {
|
|
7432
8155
|
marginBottom: 12,
|
|
7433
8156
|
padding: 8,
|
|
7434
|
-
|
|
7435
|
-
|
|
8157
|
+
},
|
|
8158
|
+
errorText: {
|
|
7436
8159
|
fontSize: 14,
|
|
7437
|
-
|
|
7438
|
-
|
|
8160
|
+
},
|
|
8161
|
+
input: {
|
|
7439
8162
|
borderWidth: 1,
|
|
7440
8163
|
padding: 12,
|
|
7441
8164
|
fontSize: 16,
|
|
7442
8165
|
marginBottom: 12,
|
|
7443
|
-
|
|
7444
|
-
|
|
8166
|
+
},
|
|
8167
|
+
button: {
|
|
7445
8168
|
padding: 12,
|
|
7446
8169
|
alignItems: "center",
|
|
7447
8170
|
justifyContent: "center",
|
|
7448
|
-
|
|
7449
|
-
|
|
8171
|
+
},
|
|
8172
|
+
buttonText: {
|
|
7450
8173
|
color: "#ffffff",
|
|
7451
8174
|
fontSize: 16,
|
|
7452
|
-
|
|
7453
|
-
|
|
8175
|
+
},
|
|
8176
|
+
});
|
|
7454
8177
|
|
|
7455
|
-
|
|
8178
|
+
export { SignIn };
|
|
8179
|
+
`],
|
|
7456
8180
|
["auth/better-auth/native/bare/components/sign-up.tsx.hbs", `import { authClient } from "@/lib/auth-client";
|
|
7457
8181
|
{{#if (eq api "trpc")}}
|
|
7458
8182
|
import { queryClient } from "@/utils/trpc";
|
|
@@ -7460,108 +8184,225 @@ import { queryClient } from "@/utils/trpc";
|
|
|
7460
8184
|
{{#if (eq api "orpc")}}
|
|
7461
8185
|
import { queryClient } from "@/utils/orpc";
|
|
7462
8186
|
{{/if}}
|
|
8187
|
+
import { useForm } from "@tanstack/react-form";
|
|
7463
8188
|
import { useState } from "react";
|
|
7464
8189
|
import {
|
|
7465
8190
|
ActivityIndicator,
|
|
8191
|
+
StyleSheet,
|
|
7466
8192
|
Text,
|
|
7467
8193
|
TextInput,
|
|
7468
8194
|
TouchableOpacity,
|
|
7469
8195
|
View,
|
|
7470
|
-
StyleSheet,
|
|
7471
8196
|
} from "react-native";
|
|
7472
|
-
import { useColorScheme } from "@/lib/use-color-scheme";
|
|
7473
8197
|
import { NAV_THEME } from "@/lib/constants";
|
|
8198
|
+
import { useColorScheme } from "@/lib/use-color-scheme";
|
|
8199
|
+
import z from "zod";
|
|
8200
|
+
|
|
8201
|
+
const signUpSchema = z.object({
|
|
8202
|
+
name: z
|
|
8203
|
+
.string()
|
|
8204
|
+
.trim()
|
|
8205
|
+
.min(1, "Name is required")
|
|
8206
|
+
.min(2, "Name must be at least 2 characters"),
|
|
8207
|
+
email: z
|
|
8208
|
+
.string()
|
|
8209
|
+
.trim()
|
|
8210
|
+
.min(1, "Email is required")
|
|
8211
|
+
.email("Enter a valid email address"),
|
|
8212
|
+
password: z
|
|
8213
|
+
.string()
|
|
8214
|
+
.min(1, "Password is required")
|
|
8215
|
+
.min(8, "Use at least 8 characters"),
|
|
8216
|
+
});
|
|
8217
|
+
|
|
8218
|
+
function getErrorMessage(error: unknown): string | null {
|
|
8219
|
+
if (!error) return null;
|
|
8220
|
+
|
|
8221
|
+
if (typeof error === "string") {
|
|
8222
|
+
return error;
|
|
8223
|
+
}
|
|
8224
|
+
|
|
8225
|
+
if (Array.isArray(error)) {
|
|
8226
|
+
for (const issue of error) {
|
|
8227
|
+
const message = getErrorMessage(issue);
|
|
8228
|
+
if (message) {
|
|
8229
|
+
return message;
|
|
8230
|
+
}
|
|
8231
|
+
}
|
|
8232
|
+
return null;
|
|
8233
|
+
}
|
|
8234
|
+
|
|
8235
|
+
if (typeof error === "object" && error !== null) {
|
|
8236
|
+
const maybeError = error as { message?: unknown };
|
|
8237
|
+
if (typeof maybeError.message === "string") {
|
|
8238
|
+
return maybeError.message;
|
|
8239
|
+
}
|
|
8240
|
+
}
|
|
8241
|
+
|
|
8242
|
+
return null;
|
|
8243
|
+
}
|
|
7474
8244
|
|
|
7475
8245
|
function SignUp() {
|
|
7476
8246
|
const { colorScheme } = useColorScheme();
|
|
7477
8247
|
const theme = colorScheme === "dark" ? NAV_THEME.dark : NAV_THEME.light;
|
|
7478
|
-
const [name, setName] = useState("");
|
|
7479
|
-
const [email, setEmail] = useState("");
|
|
7480
|
-
const [password, setPassword] = useState("");
|
|
7481
|
-
const [isLoading, setIsLoading] = useState(false);
|
|
7482
8248
|
const [error, setError] = useState<string | null>(null);
|
|
7483
8249
|
|
|
7484
|
-
|
|
7485
|
-
|
|
7486
|
-
|
|
7487
|
-
|
|
7488
|
-
|
|
7489
|
-
|
|
7490
|
-
|
|
7491
|
-
|
|
7492
|
-
|
|
7493
|
-
|
|
7494
|
-
|
|
7495
|
-
|
|
7496
|
-
|
|
7497
|
-
|
|
7498
|
-
|
|
7499
|
-
onSuccess() {
|
|
7500
|
-
setName("");
|
|
7501
|
-
setEmail("");
|
|
7502
|
-
setPassword("");
|
|
7503
|
-
{{#if (eq api "orpc")}}
|
|
7504
|
-
queryClient.refetchQueries();
|
|
7505
|
-
{{/if}}
|
|
7506
|
-
{{#if (eq api "trpc")}}
|
|
7507
|
-
queryClient.refetchQueries();
|
|
7508
|
-
{{/if}}
|
|
8250
|
+
const form = useForm({
|
|
8251
|
+
defaultValues: {
|
|
8252
|
+
name: "",
|
|
8253
|
+
email: "",
|
|
8254
|
+
password: "",
|
|
8255
|
+
},
|
|
8256
|
+
validators: {
|
|
8257
|
+
onSubmit: signUpSchema,
|
|
8258
|
+
},
|
|
8259
|
+
onSubmit: async ({ value, formApi }) => {
|
|
8260
|
+
await authClient.signUp.email(
|
|
8261
|
+
{
|
|
8262
|
+
name: value.name.trim(),
|
|
8263
|
+
email: value.email.trim(),
|
|
8264
|
+
password: value.password,
|
|
7509
8265
|
},
|
|
7510
|
-
|
|
7511
|
-
|
|
8266
|
+
{
|
|
8267
|
+
onError(error) {
|
|
8268
|
+
setError(error.error?.message || "Failed to sign up");
|
|
8269
|
+
},
|
|
8270
|
+
onSuccess() {
|
|
8271
|
+
setError(null);
|
|
8272
|
+
formApi.reset();
|
|
8273
|
+
{{#if (eq api "orpc")}}
|
|
8274
|
+
queryClient.refetchQueries();
|
|
8275
|
+
{{/if}}
|
|
8276
|
+
{{#if (eq api "trpc")}}
|
|
8277
|
+
queryClient.refetchQueries();
|
|
8278
|
+
{{/if}}
|
|
8279
|
+
},
|
|
7512
8280
|
},
|
|
7513
|
-
|
|
7514
|
-
|
|
7515
|
-
}
|
|
8281
|
+
);
|
|
8282
|
+
},
|
|
8283
|
+
});
|
|
7516
8284
|
|
|
7517
8285
|
return (
|
|
7518
8286
|
<View style={[styles.card, { backgroundColor: theme.card, borderColor: theme.border }]}>
|
|
7519
8287
|
<Text style={[styles.title, { color: theme.text }]}>Create Account</Text>
|
|
7520
8288
|
|
|
7521
|
-
|
|
7522
|
-
|
|
7523
|
-
|
|
7524
|
-
|
|
7525
|
-
|
|
8289
|
+
<form.Subscribe
|
|
8290
|
+
selector={(state) => ({
|
|
8291
|
+
isSubmitting: state.isSubmitting,
|
|
8292
|
+
validationError: getErrorMessage(state.errorMap.onSubmit),
|
|
8293
|
+
})}
|
|
8294
|
+
>
|
|
8295
|
+
{({ isSubmitting, validationError }) => {
|
|
8296
|
+
const formError = error ?? validationError;
|
|
7526
8297
|
|
|
7527
|
-
|
|
7528
|
-
|
|
7529
|
-
|
|
7530
|
-
|
|
7531
|
-
|
|
7532
|
-
|
|
7533
|
-
|
|
8298
|
+
return (
|
|
8299
|
+
<>
|
|
8300
|
+
{formError ? (
|
|
8301
|
+
<View style={[styles.errorContainer, { backgroundColor: theme.notification + "20" }]}>
|
|
8302
|
+
<Text style={[styles.errorText, { color: theme.notification }]}>{formError}</Text>
|
|
8303
|
+
</View>
|
|
8304
|
+
) : null}
|
|
7534
8305
|
|
|
7535
|
-
|
|
7536
|
-
|
|
7537
|
-
|
|
7538
|
-
|
|
7539
|
-
|
|
7540
|
-
|
|
7541
|
-
|
|
7542
|
-
|
|
7543
|
-
|
|
8306
|
+
<form.Field name="name">
|
|
8307
|
+
{(field) => (
|
|
8308
|
+
<TextInput
|
|
8309
|
+
style={[
|
|
8310
|
+
styles.input,
|
|
8311
|
+
{
|
|
8312
|
+
color: theme.text,
|
|
8313
|
+
borderColor: theme.border,
|
|
8314
|
+
backgroundColor: theme.background,
|
|
8315
|
+
},
|
|
8316
|
+
]}
|
|
8317
|
+
placeholder="Name"
|
|
8318
|
+
placeholderTextColor={theme.text}
|
|
8319
|
+
value={field.state.value}
|
|
8320
|
+
onBlur={field.handleBlur}
|
|
8321
|
+
onChangeText={(value) => {
|
|
8322
|
+
field.handleChange(value);
|
|
8323
|
+
if (error) {
|
|
8324
|
+
setError(null);
|
|
8325
|
+
}
|
|
8326
|
+
}}
|
|
8327
|
+
/>
|
|
8328
|
+
)}
|
|
8329
|
+
</form.Field>
|
|
7544
8330
|
|
|
7545
|
-
|
|
7546
|
-
|
|
7547
|
-
|
|
7548
|
-
|
|
7549
|
-
|
|
7550
|
-
|
|
7551
|
-
|
|
7552
|
-
|
|
8331
|
+
<form.Field name="email">
|
|
8332
|
+
{(field) => (
|
|
8333
|
+
<TextInput
|
|
8334
|
+
style={[
|
|
8335
|
+
styles.input,
|
|
8336
|
+
{
|
|
8337
|
+
color: theme.text,
|
|
8338
|
+
borderColor: theme.border,
|
|
8339
|
+
backgroundColor: theme.background,
|
|
8340
|
+
},
|
|
8341
|
+
]}
|
|
8342
|
+
placeholder="Email"
|
|
8343
|
+
placeholderTextColor={theme.text}
|
|
8344
|
+
value={field.state.value}
|
|
8345
|
+
onBlur={field.handleBlur}
|
|
8346
|
+
onChangeText={(value) => {
|
|
8347
|
+
field.handleChange(value);
|
|
8348
|
+
if (error) {
|
|
8349
|
+
setError(null);
|
|
8350
|
+
}
|
|
8351
|
+
}}
|
|
8352
|
+
keyboardType="email-address"
|
|
8353
|
+
autoCapitalize="none"
|
|
8354
|
+
/>
|
|
8355
|
+
)}
|
|
8356
|
+
</form.Field>
|
|
7553
8357
|
|
|
7554
|
-
|
|
7555
|
-
|
|
7556
|
-
|
|
7557
|
-
|
|
7558
|
-
|
|
7559
|
-
|
|
7560
|
-
|
|
7561
|
-
|
|
7562
|
-
|
|
7563
|
-
|
|
7564
|
-
|
|
8358
|
+
<form.Field name="password">
|
|
8359
|
+
{(field) => (
|
|
8360
|
+
<TextInput
|
|
8361
|
+
style={[
|
|
8362
|
+
styles.input,
|
|
8363
|
+
{
|
|
8364
|
+
color: theme.text,
|
|
8365
|
+
borderColor: theme.border,
|
|
8366
|
+
backgroundColor: theme.background,
|
|
8367
|
+
},
|
|
8368
|
+
]}
|
|
8369
|
+
placeholder="Password"
|
|
8370
|
+
placeholderTextColor={theme.text}
|
|
8371
|
+
value={field.state.value}
|
|
8372
|
+
onBlur={field.handleBlur}
|
|
8373
|
+
onChangeText={(value) => {
|
|
8374
|
+
field.handleChange(value);
|
|
8375
|
+
if (error) {
|
|
8376
|
+
setError(null);
|
|
8377
|
+
}
|
|
8378
|
+
}}
|
|
8379
|
+
secureTextEntry
|
|
8380
|
+
onSubmitEditing={form.handleSubmit}
|
|
8381
|
+
/>
|
|
8382
|
+
)}
|
|
8383
|
+
</form.Field>
|
|
8384
|
+
|
|
8385
|
+
<TouchableOpacity
|
|
8386
|
+
onPress={form.handleSubmit}
|
|
8387
|
+
disabled={isSubmitting}
|
|
8388
|
+
style={[
|
|
8389
|
+
styles.button,
|
|
8390
|
+
{
|
|
8391
|
+
backgroundColor: theme.primary,
|
|
8392
|
+
opacity: isSubmitting ? 0.5 : 1,
|
|
8393
|
+
},
|
|
8394
|
+
]}
|
|
8395
|
+
>
|
|
8396
|
+
{isSubmitting ? (
|
|
8397
|
+
<ActivityIndicator size="small" color="#ffffff" />
|
|
8398
|
+
) : (
|
|
8399
|
+
<Text style={styles.buttonText}>Sign Up</Text>
|
|
8400
|
+
)}
|
|
8401
|
+
</TouchableOpacity>
|
|
8402
|
+
</>
|
|
8403
|
+
);
|
|
8404
|
+
}}
|
|
8405
|
+
</form.Subscribe>
|
|
7565
8406
|
</View>
|
|
7566
8407
|
);
|
|
7567
8408
|
}
|
|
@@ -7602,7 +8443,6 @@ const styles = StyleSheet.create({
|
|
|
7602
8443
|
});
|
|
7603
8444
|
|
|
7604
8445
|
export { SignUp };
|
|
7605
|
-
|
|
7606
8446
|
`],
|
|
7607
8447
|
["auth/better-auth/native/base/lib/auth-client.ts.hbs", `import { expoClient } from "@better-auth/expo/client";
|
|
7608
8448
|
import { createAuthClient } from "better-auth/react";
|
|
@@ -7816,6 +8656,7 @@ import { queryClient } from "@/utils/trpc";
|
|
|
7816
8656
|
{{#if (eq api "orpc")}}
|
|
7817
8657
|
import { queryClient } from "@/utils/orpc";
|
|
7818
8658
|
{{/if}}
|
|
8659
|
+
import { useForm } from "@tanstack/react-form";
|
|
7819
8660
|
import { useState } from "react";
|
|
7820
8661
|
import {
|
|
7821
8662
|
ActivityIndicator,
|
|
@@ -7825,82 +8666,157 @@ import {
|
|
|
7825
8666
|
View,
|
|
7826
8667
|
} from "react-native";
|
|
7827
8668
|
import { StyleSheet } from "react-native-unistyles";
|
|
8669
|
+
import z from "zod";
|
|
8670
|
+
|
|
8671
|
+
const signInSchema = z.object({
|
|
8672
|
+
email: z
|
|
8673
|
+
.string()
|
|
8674
|
+
.trim()
|
|
8675
|
+
.min(1, "Email is required")
|
|
8676
|
+
.email("Enter a valid email address"),
|
|
8677
|
+
password: z
|
|
8678
|
+
.string()
|
|
8679
|
+
.min(1, "Password is required")
|
|
8680
|
+
.min(8, "Use at least 8 characters"),
|
|
8681
|
+
});
|
|
8682
|
+
|
|
8683
|
+
function getErrorMessage(error: unknown): string | null {
|
|
8684
|
+
if (!error) return null;
|
|
8685
|
+
|
|
8686
|
+
if (typeof error === "string") {
|
|
8687
|
+
return error;
|
|
8688
|
+
}
|
|
8689
|
+
|
|
8690
|
+
if (Array.isArray(error)) {
|
|
8691
|
+
for (const issue of error) {
|
|
8692
|
+
const message = getErrorMessage(issue);
|
|
8693
|
+
if (message) {
|
|
8694
|
+
return message;
|
|
8695
|
+
}
|
|
8696
|
+
}
|
|
8697
|
+
return null;
|
|
8698
|
+
}
|
|
8699
|
+
|
|
8700
|
+
if (typeof error === "object" && error !== null) {
|
|
8701
|
+
const maybeError = error as { message?: unknown };
|
|
8702
|
+
if (typeof maybeError.message === "string") {
|
|
8703
|
+
return maybeError.message;
|
|
8704
|
+
}
|
|
8705
|
+
}
|
|
8706
|
+
|
|
8707
|
+
return null;
|
|
8708
|
+
}
|
|
7828
8709
|
|
|
7829
8710
|
export function SignIn() {
|
|
7830
|
-
const [email, setEmail] = useState("");
|
|
7831
|
-
const [password, setPassword] = useState("");
|
|
7832
|
-
const [isLoading, setIsLoading] = useState(false);
|
|
7833
8711
|
const [error, setError] = useState<string | null>(null);
|
|
7834
8712
|
|
|
7835
|
-
const
|
|
7836
|
-
|
|
7837
|
-
|
|
7838
|
-
|
|
7839
|
-
|
|
7840
|
-
|
|
7841
|
-
|
|
7842
|
-
|
|
7843
|
-
|
|
7844
|
-
|
|
7845
|
-
|
|
7846
|
-
|
|
7847
|
-
|
|
7848
|
-
},
|
|
7849
|
-
onSuccess: () => {
|
|
7850
|
-
setEmail("");
|
|
7851
|
-
setPassword("");
|
|
7852
|
-
{{#if (eq api "orpc")}}
|
|
7853
|
-
queryClient.refetchQueries();
|
|
7854
|
-
{{/if}}
|
|
7855
|
-
{{#if (eq api "trpc")}}
|
|
7856
|
-
queryClient.refetchQueries();
|
|
7857
|
-
{{/if}}
|
|
8713
|
+
const form = useForm({
|
|
8714
|
+
defaultValues: {
|
|
8715
|
+
email: "",
|
|
8716
|
+
password: "",
|
|
8717
|
+
},
|
|
8718
|
+
validators: {
|
|
8719
|
+
onSubmit: signInSchema,
|
|
8720
|
+
},
|
|
8721
|
+
onSubmit: async ({ value, formApi }) => {
|
|
8722
|
+
await authClient.signIn.email(
|
|
8723
|
+
{
|
|
8724
|
+
email: value.email.trim(),
|
|
8725
|
+
password: value.password,
|
|
7858
8726
|
},
|
|
7859
|
-
|
|
7860
|
-
|
|
8727
|
+
{
|
|
8728
|
+
onError(error) {
|
|
8729
|
+
setError(error.error?.message || "Failed to sign in");
|
|
8730
|
+
},
|
|
8731
|
+
onSuccess() {
|
|
8732
|
+
setError(null);
|
|
8733
|
+
formApi.reset();
|
|
8734
|
+
{{#if (eq api "orpc")}}
|
|
8735
|
+
queryClient.refetchQueries();
|
|
8736
|
+
{{/if}}
|
|
8737
|
+
{{#if (eq api "trpc")}}
|
|
8738
|
+
queryClient.refetchQueries();
|
|
8739
|
+
{{/if}}
|
|
8740
|
+
},
|
|
7861
8741
|
},
|
|
7862
|
-
|
|
7863
|
-
|
|
7864
|
-
};
|
|
8742
|
+
);
|
|
8743
|
+
},
|
|
8744
|
+
});
|
|
7865
8745
|
|
|
7866
8746
|
return (
|
|
7867
8747
|
<View style={styles.container}>
|
|
7868
8748
|
<Text style={styles.title}>Sign In</Text>
|
|
7869
8749
|
|
|
7870
|
-
|
|
7871
|
-
|
|
7872
|
-
|
|
7873
|
-
|
|
7874
|
-
|
|
7875
|
-
|
|
7876
|
-
|
|
7877
|
-
|
|
7878
|
-
placeholder="Email"
|
|
7879
|
-
value={email}
|
|
7880
|
-
onChangeText={setEmail}
|
|
7881
|
-
keyboardType="email-address"
|
|
7882
|
-
autoCapitalize="none"
|
|
7883
|
-
/>
|
|
8750
|
+
<form.Subscribe
|
|
8751
|
+
selector={(state) => ({
|
|
8752
|
+
isSubmitting: state.isSubmitting,
|
|
8753
|
+
validationError: getErrorMessage(state.errorMap.onSubmit),
|
|
8754
|
+
})}
|
|
8755
|
+
>
|
|
8756
|
+
{({ isSubmitting, validationError }) => {
|
|
8757
|
+
const formError = error ?? validationError;
|
|
7884
8758
|
|
|
7885
|
-
|
|
7886
|
-
|
|
7887
|
-
|
|
7888
|
-
|
|
7889
|
-
|
|
7890
|
-
|
|
7891
|
-
|
|
8759
|
+
return (
|
|
8760
|
+
<>
|
|
8761
|
+
{formError ? (
|
|
8762
|
+
<View style={styles.errorContainer}>
|
|
8763
|
+
<Text style={styles.errorText}>{formError}</Text>
|
|
8764
|
+
</View>
|
|
8765
|
+
) : null}
|
|
8766
|
+
|
|
8767
|
+
<form.Field name="email">
|
|
8768
|
+
{(field) => (
|
|
8769
|
+
<TextInput
|
|
8770
|
+
style={styles.input}
|
|
8771
|
+
placeholder="Email"
|
|
8772
|
+
value={field.state.value}
|
|
8773
|
+
onBlur={field.handleBlur}
|
|
8774
|
+
onChangeText={(value) => {
|
|
8775
|
+
field.handleChange(value);
|
|
8776
|
+
if (error) {
|
|
8777
|
+
setError(null);
|
|
8778
|
+
}
|
|
8779
|
+
}}
|
|
8780
|
+
keyboardType="email-address"
|
|
8781
|
+
autoCapitalize="none"
|
|
8782
|
+
/>
|
|
8783
|
+
)}
|
|
8784
|
+
</form.Field>
|
|
8785
|
+
|
|
8786
|
+
<form.Field name="password">
|
|
8787
|
+
{(field) => (
|
|
8788
|
+
<TextInput
|
|
8789
|
+
style={styles.input}
|
|
8790
|
+
placeholder="Password"
|
|
8791
|
+
value={field.state.value}
|
|
8792
|
+
onBlur={field.handleBlur}
|
|
8793
|
+
onChangeText={(value) => {
|
|
8794
|
+
field.handleChange(value);
|
|
8795
|
+
if (error) {
|
|
8796
|
+
setError(null);
|
|
8797
|
+
}
|
|
8798
|
+
}}
|
|
8799
|
+
secureTextEntry
|
|
8800
|
+
onSubmitEditing={form.handleSubmit}
|
|
8801
|
+
/>
|
|
8802
|
+
)}
|
|
8803
|
+
</form.Field>
|
|
7892
8804
|
|
|
7893
|
-
|
|
7894
|
-
|
|
7895
|
-
|
|
7896
|
-
|
|
7897
|
-
|
|
7898
|
-
|
|
7899
|
-
|
|
7900
|
-
|
|
7901
|
-
|
|
7902
|
-
|
|
7903
|
-
|
|
8805
|
+
<TouchableOpacity
|
|
8806
|
+
onPress={form.handleSubmit}
|
|
8807
|
+
disabled={isSubmitting}
|
|
8808
|
+
style={styles.button}
|
|
8809
|
+
>
|
|
8810
|
+
{isSubmitting ? (
|
|
8811
|
+
<ActivityIndicator size="small" color="#fff" />
|
|
8812
|
+
) : (
|
|
8813
|
+
<Text style={styles.buttonText}>Sign In</Text>
|
|
8814
|
+
)}
|
|
8815
|
+
</TouchableOpacity>
|
|
8816
|
+
</>
|
|
8817
|
+
);
|
|
8818
|
+
}}
|
|
8819
|
+
</form.Subscribe>
|
|
7904
8820
|
</View>
|
|
7905
8821
|
);
|
|
7906
8822
|
}
|
|
@@ -7956,6 +8872,7 @@ import { queryClient } from "@/utils/trpc";
|
|
|
7956
8872
|
{{#if (eq api "orpc")}}
|
|
7957
8873
|
import { queryClient } from "@/utils/orpc";
|
|
7958
8874
|
{{/if}}
|
|
8875
|
+
import { useForm } from "@tanstack/react-form";
|
|
7959
8876
|
import { useState } from "react";
|
|
7960
8877
|
import {
|
|
7961
8878
|
ActivityIndicator,
|
|
@@ -7965,92 +8882,181 @@ import {
|
|
|
7965
8882
|
View,
|
|
7966
8883
|
} from "react-native";
|
|
7967
8884
|
import { StyleSheet } from "react-native-unistyles";
|
|
8885
|
+
import z from "zod";
|
|
8886
|
+
|
|
8887
|
+
const signUpSchema = z.object({
|
|
8888
|
+
name: z
|
|
8889
|
+
.string()
|
|
8890
|
+
.trim()
|
|
8891
|
+
.min(1, "Name is required")
|
|
8892
|
+
.min(2, "Name must be at least 2 characters"),
|
|
8893
|
+
email: z
|
|
8894
|
+
.string()
|
|
8895
|
+
.trim()
|
|
8896
|
+
.min(1, "Email is required")
|
|
8897
|
+
.email("Enter a valid email address"),
|
|
8898
|
+
password: z
|
|
8899
|
+
.string()
|
|
8900
|
+
.min(1, "Password is required")
|
|
8901
|
+
.min(8, "Use at least 8 characters"),
|
|
8902
|
+
});
|
|
8903
|
+
|
|
8904
|
+
function getErrorMessage(error: unknown): string | null {
|
|
8905
|
+
if (!error) return null;
|
|
8906
|
+
|
|
8907
|
+
if (typeof error === "string") {
|
|
8908
|
+
return error;
|
|
8909
|
+
}
|
|
8910
|
+
|
|
8911
|
+
if (Array.isArray(error)) {
|
|
8912
|
+
for (const issue of error) {
|
|
8913
|
+
const message = getErrorMessage(issue);
|
|
8914
|
+
if (message) {
|
|
8915
|
+
return message;
|
|
8916
|
+
}
|
|
8917
|
+
}
|
|
8918
|
+
return null;
|
|
8919
|
+
}
|
|
8920
|
+
|
|
8921
|
+
if (typeof error === "object" && error !== null) {
|
|
8922
|
+
const maybeError = error as { message?: unknown };
|
|
8923
|
+
if (typeof maybeError.message === "string") {
|
|
8924
|
+
return maybeError.message;
|
|
8925
|
+
}
|
|
8926
|
+
}
|
|
8927
|
+
|
|
8928
|
+
return null;
|
|
8929
|
+
}
|
|
7968
8930
|
|
|
7969
8931
|
export function SignUp() {
|
|
7970
|
-
const [name, setName] = useState("");
|
|
7971
|
-
const [email, setEmail] = useState("");
|
|
7972
|
-
const [password, setPassword] = useState("");
|
|
7973
|
-
const [isLoading, setIsLoading] = useState(false);
|
|
7974
8932
|
const [error, setError] = useState<string | null>(null);
|
|
7975
8933
|
|
|
7976
|
-
const
|
|
7977
|
-
|
|
7978
|
-
|
|
7979
|
-
|
|
7980
|
-
|
|
7981
|
-
|
|
7982
|
-
|
|
7983
|
-
|
|
7984
|
-
|
|
7985
|
-
|
|
7986
|
-
|
|
7987
|
-
|
|
7988
|
-
|
|
7989
|
-
|
|
7990
|
-
|
|
7991
|
-
onSuccess: () => {
|
|
7992
|
-
setName("");
|
|
7993
|
-
setEmail("");
|
|
7994
|
-
setPassword("");
|
|
7995
|
-
{{#if (eq api "orpc")}}
|
|
7996
|
-
queryClient.refetchQueries();
|
|
7997
|
-
{{/if}}
|
|
7998
|
-
{{#if (eq api "trpc")}}
|
|
7999
|
-
queryClient.refetchQueries();
|
|
8000
|
-
{{/if}}
|
|
8934
|
+
const form = useForm({
|
|
8935
|
+
defaultValues: {
|
|
8936
|
+
name: "",
|
|
8937
|
+
email: "",
|
|
8938
|
+
password: "",
|
|
8939
|
+
},
|
|
8940
|
+
validators: {
|
|
8941
|
+
onSubmit: signUpSchema,
|
|
8942
|
+
},
|
|
8943
|
+
onSubmit: async ({ value, formApi }) => {
|
|
8944
|
+
await authClient.signUp.email(
|
|
8945
|
+
{
|
|
8946
|
+
name: value.name.trim(),
|
|
8947
|
+
email: value.email.trim(),
|
|
8948
|
+
password: value.password,
|
|
8001
8949
|
},
|
|
8002
|
-
|
|
8003
|
-
|
|
8950
|
+
{
|
|
8951
|
+
onError(error) {
|
|
8952
|
+
setError(error.error?.message || "Failed to sign up");
|
|
8953
|
+
},
|
|
8954
|
+
onSuccess() {
|
|
8955
|
+
setError(null);
|
|
8956
|
+
formApi.reset();
|
|
8957
|
+
{{#if (eq api "orpc")}}
|
|
8958
|
+
queryClient.refetchQueries();
|
|
8959
|
+
{{/if}}
|
|
8960
|
+
{{#if (eq api "trpc")}}
|
|
8961
|
+
queryClient.refetchQueries();
|
|
8962
|
+
{{/if}}
|
|
8963
|
+
},
|
|
8004
8964
|
},
|
|
8005
|
-
|
|
8006
|
-
|
|
8007
|
-
};
|
|
8965
|
+
);
|
|
8966
|
+
},
|
|
8967
|
+
});
|
|
8008
8968
|
|
|
8009
8969
|
return (
|
|
8010
8970
|
<View style={styles.container}>
|
|
8011
8971
|
<Text style={styles.title}>Create Account</Text>
|
|
8012
8972
|
|
|
8013
|
-
|
|
8014
|
-
|
|
8015
|
-
|
|
8016
|
-
|
|
8017
|
-
|
|
8018
|
-
|
|
8019
|
-
|
|
8020
|
-
|
|
8021
|
-
placeholder="Name"
|
|
8022
|
-
value={name}
|
|
8023
|
-
onChangeText={setName}
|
|
8024
|
-
/>
|
|
8025
|
-
|
|
8026
|
-
<TextInput
|
|
8027
|
-
style={styles.input}
|
|
8028
|
-
placeholder="Email"
|
|
8029
|
-
value={email}
|
|
8030
|
-
onChangeText={setEmail}
|
|
8031
|
-
keyboardType="email-address"
|
|
8032
|
-
autoCapitalize="none"
|
|
8033
|
-
/>
|
|
8973
|
+
<form.Subscribe
|
|
8974
|
+
selector={(state) => ({
|
|
8975
|
+
isSubmitting: state.isSubmitting,
|
|
8976
|
+
validationError: getErrorMessage(state.errorMap.onSubmit),
|
|
8977
|
+
})}
|
|
8978
|
+
>
|
|
8979
|
+
{({ isSubmitting, validationError }) => {
|
|
8980
|
+
const formError = error ?? validationError;
|
|
8034
8981
|
|
|
8035
|
-
|
|
8036
|
-
|
|
8037
|
-
|
|
8038
|
-
|
|
8039
|
-
|
|
8040
|
-
|
|
8041
|
-
|
|
8982
|
+
return (
|
|
8983
|
+
<>
|
|
8984
|
+
{formError ? (
|
|
8985
|
+
<View style={styles.errorContainer}>
|
|
8986
|
+
<Text style={styles.errorText}>{formError}</Text>
|
|
8987
|
+
</View>
|
|
8988
|
+
) : null}
|
|
8989
|
+
|
|
8990
|
+
<form.Field name="name">
|
|
8991
|
+
{(field) => (
|
|
8992
|
+
<TextInput
|
|
8993
|
+
style={styles.input}
|
|
8994
|
+
placeholder="Name"
|
|
8995
|
+
value={field.state.value}
|
|
8996
|
+
onBlur={field.handleBlur}
|
|
8997
|
+
onChangeText={(value) => {
|
|
8998
|
+
field.handleChange(value);
|
|
8999
|
+
if (error) {
|
|
9000
|
+
setError(null);
|
|
9001
|
+
}
|
|
9002
|
+
}}
|
|
9003
|
+
/>
|
|
9004
|
+
)}
|
|
9005
|
+
</form.Field>
|
|
9006
|
+
|
|
9007
|
+
<form.Field name="email">
|
|
9008
|
+
{(field) => (
|
|
9009
|
+
<TextInput
|
|
9010
|
+
style={styles.input}
|
|
9011
|
+
placeholder="Email"
|
|
9012
|
+
value={field.state.value}
|
|
9013
|
+
onBlur={field.handleBlur}
|
|
9014
|
+
onChangeText={(value) => {
|
|
9015
|
+
field.handleChange(value);
|
|
9016
|
+
if (error) {
|
|
9017
|
+
setError(null);
|
|
9018
|
+
}
|
|
9019
|
+
}}
|
|
9020
|
+
keyboardType="email-address"
|
|
9021
|
+
autoCapitalize="none"
|
|
9022
|
+
/>
|
|
9023
|
+
)}
|
|
9024
|
+
</form.Field>
|
|
9025
|
+
|
|
9026
|
+
<form.Field name="password">
|
|
9027
|
+
{(field) => (
|
|
9028
|
+
<TextInput
|
|
9029
|
+
style={styles.inputLast}
|
|
9030
|
+
placeholder="Password"
|
|
9031
|
+
value={field.state.value}
|
|
9032
|
+
onBlur={field.handleBlur}
|
|
9033
|
+
onChangeText={(value) => {
|
|
9034
|
+
field.handleChange(value);
|
|
9035
|
+
if (error) {
|
|
9036
|
+
setError(null);
|
|
9037
|
+
}
|
|
9038
|
+
}}
|
|
9039
|
+
secureTextEntry
|
|
9040
|
+
onSubmitEditing={form.handleSubmit}
|
|
9041
|
+
/>
|
|
9042
|
+
)}
|
|
9043
|
+
</form.Field>
|
|
8042
9044
|
|
|
8043
|
-
|
|
8044
|
-
|
|
8045
|
-
|
|
8046
|
-
|
|
8047
|
-
|
|
8048
|
-
|
|
8049
|
-
|
|
8050
|
-
|
|
8051
|
-
|
|
8052
|
-
|
|
8053
|
-
|
|
9045
|
+
<TouchableOpacity
|
|
9046
|
+
onPress={form.handleSubmit}
|
|
9047
|
+
disabled={isSubmitting}
|
|
9048
|
+
style={styles.button}
|
|
9049
|
+
>
|
|
9050
|
+
{isSubmitting ? (
|
|
9051
|
+
<ActivityIndicator size="small" color="#fff" />
|
|
9052
|
+
) : (
|
|
9053
|
+
<Text style={styles.buttonText}>Sign Up</Text>
|
|
9054
|
+
)}
|
|
9055
|
+
</TouchableOpacity>
|
|
9056
|
+
</>
|
|
9057
|
+
);
|
|
9058
|
+
}}
|
|
9059
|
+
</form.Subscribe>
|
|
8054
9060
|
</View>
|
|
8055
9061
|
);
|
|
8056
9062
|
}
|
|
@@ -8237,86 +9243,171 @@ import { queryClient } from "@/utils/trpc";
|
|
|
8237
9243
|
{{#if (eq api "orpc")}}
|
|
8238
9244
|
import { queryClient } from "@/utils/orpc";
|
|
8239
9245
|
{{/if}}
|
|
8240
|
-
import {
|
|
8241
|
-
import {
|
|
8242
|
-
import {
|
|
9246
|
+
import { useForm } from "@tanstack/react-form";
|
|
9247
|
+
import { useRef } from "react";
|
|
9248
|
+
import { Text, TextInput, View } from "react-native";
|
|
9249
|
+
import { Button, FieldError, Input, Label, Spinner, Surface, TextField, useToast } from "heroui-native";
|
|
9250
|
+
import z from "zod";
|
|
8243
9251
|
|
|
8244
|
-
|
|
8245
|
-
|
|
8246
|
-
|
|
8247
|
-
|
|
8248
|
-
|
|
9252
|
+
const signInSchema = z.object({
|
|
9253
|
+
email: z
|
|
9254
|
+
.string()
|
|
9255
|
+
.trim()
|
|
9256
|
+
.min(1, "Email is required")
|
|
9257
|
+
.email("Enter a valid email address"),
|
|
9258
|
+
password: z
|
|
9259
|
+
.string()
|
|
9260
|
+
.min(1, "Password is required")
|
|
9261
|
+
.min(8, "Use at least 8 characters"),
|
|
9262
|
+
});
|
|
8249
9263
|
|
|
8250
|
-
|
|
8251
|
-
|
|
8252
|
-
setError(null);
|
|
9264
|
+
function getErrorMessage(error: unknown): string | null {
|
|
9265
|
+
if (!error) return null;
|
|
8253
9266
|
|
|
8254
|
-
|
|
8255
|
-
|
|
8256
|
-
email,
|
|
8257
|
-
password,
|
|
8258
|
-
},
|
|
8259
|
-
{
|
|
8260
|
-
onError(error) {
|
|
8261
|
-
setError(error.error?.message || "Failed to sign in");
|
|
8262
|
-
setIsLoading(false);
|
|
8263
|
-
},
|
|
8264
|
-
onSuccess() {
|
|
8265
|
-
setEmail("");
|
|
8266
|
-
setPassword("");
|
|
8267
|
-
{{#if (eq api "orpc")}}
|
|
8268
|
-
queryClient.refetchQueries();
|
|
8269
|
-
{{/if}}
|
|
8270
|
-
{{#if (eq api "trpc")}}
|
|
8271
|
-
queryClient.refetchQueries();
|
|
8272
|
-
{{/if}}
|
|
8273
|
-
},
|
|
8274
|
-
onFinished() {
|
|
8275
|
-
setIsLoading(false);
|
|
8276
|
-
},
|
|
9267
|
+
if (typeof error === "string") {
|
|
9268
|
+
return error;
|
|
8277
9269
|
}
|
|
8278
|
-
|
|
9270
|
+
|
|
9271
|
+
if (Array.isArray(error)) {
|
|
9272
|
+
for (const issue of error) {
|
|
9273
|
+
const message = getErrorMessage(issue);
|
|
9274
|
+
if (message) {
|
|
9275
|
+
return message;
|
|
9276
|
+
}
|
|
9277
|
+
}
|
|
9278
|
+
return null;
|
|
9279
|
+
}
|
|
9280
|
+
|
|
9281
|
+
if (typeof error === "object" && error !== null) {
|
|
9282
|
+
const maybeError = error as { message?: unknown };
|
|
9283
|
+
if (typeof maybeError.message === "string") {
|
|
9284
|
+
return maybeError.message;
|
|
9285
|
+
}
|
|
8279
9286
|
}
|
|
8280
9287
|
|
|
9288
|
+
return null;
|
|
9289
|
+
}
|
|
9290
|
+
|
|
9291
|
+
function SignIn() {
|
|
9292
|
+
const passwordInputRef = useRef<TextInput>(null);
|
|
9293
|
+
const { toast } = useToast();
|
|
9294
|
+
|
|
9295
|
+
const form = useForm({
|
|
9296
|
+
defaultValues: {
|
|
9297
|
+
email: "",
|
|
9298
|
+
password: "",
|
|
9299
|
+
},
|
|
9300
|
+
validators: {
|
|
9301
|
+
onSubmit: signInSchema,
|
|
9302
|
+
},
|
|
9303
|
+
onSubmit: async ({ value, formApi }) => {
|
|
9304
|
+
await authClient.signIn.email(
|
|
9305
|
+
{
|
|
9306
|
+
email: value.email.trim(),
|
|
9307
|
+
password: value.password,
|
|
9308
|
+
},
|
|
9309
|
+
{
|
|
9310
|
+
onError(error) {
|
|
9311
|
+
toast.show({
|
|
9312
|
+
variant: "danger",
|
|
9313
|
+
label: error.error?.message || "Failed to sign in",
|
|
9314
|
+
});
|
|
9315
|
+
},
|
|
9316
|
+
onSuccess() {
|
|
9317
|
+
formApi.reset();
|
|
9318
|
+
toast.show({
|
|
9319
|
+
variant: "success",
|
|
9320
|
+
label: "Signed in successfully",
|
|
9321
|
+
});
|
|
9322
|
+
{{#if (eq api "orpc")}}
|
|
9323
|
+
queryClient.refetchQueries();
|
|
9324
|
+
{{/if}}
|
|
9325
|
+
{{#if (eq api "trpc")}}
|
|
9326
|
+
queryClient.refetchQueries();
|
|
9327
|
+
{{/if}}
|
|
9328
|
+
},
|
|
9329
|
+
},
|
|
9330
|
+
);
|
|
9331
|
+
},
|
|
9332
|
+
});
|
|
9333
|
+
|
|
8281
9334
|
return (
|
|
8282
|
-
|
|
8283
|
-
|
|
8284
|
-
|
|
8285
|
-
<ErrorView isInvalid={!!error} className="mb-3">
|
|
8286
|
-
{error}
|
|
8287
|
-
</ErrorView>
|
|
8288
|
-
|
|
8289
|
-
<View className="gap-3">
|
|
8290
|
-
<TextField>
|
|
8291
|
-
<TextField.Label>Email</TextField.Label>
|
|
8292
|
-
<TextField.Input
|
|
8293
|
-
value={email}
|
|
8294
|
-
onChangeText={setEmail}
|
|
8295
|
-
placeholder="email@example.com"
|
|
8296
|
-
keyboardType="email-address"
|
|
8297
|
-
autoCapitalize="none"
|
|
8298
|
-
/>
|
|
8299
|
-
</TextField>
|
|
8300
|
-
|
|
8301
|
-
<TextField>
|
|
8302
|
-
<TextField.Label>Password</TextField.Label>
|
|
8303
|
-
<TextField.Input
|
|
8304
|
-
value={password}
|
|
8305
|
-
onChangeText={setPassword}
|
|
8306
|
-
placeholder="••••••••"
|
|
8307
|
-
secureTextEntry
|
|
8308
|
-
/>
|
|
8309
|
-
</TextField>
|
|
9335
|
+
<Surface variant="secondary" className="p-4 rounded-lg">
|
|
9336
|
+
<Text className="text-foreground font-medium mb-4">Sign In</Text>
|
|
8310
9337
|
|
|
8311
|
-
<
|
|
8312
|
-
{
|
|
8313
|
-
|
|
8314
|
-
|
|
8315
|
-
|
|
9338
|
+
<form.Subscribe
|
|
9339
|
+
selector={(state) => ({
|
|
9340
|
+
isSubmitting: state.isSubmitting,
|
|
9341
|
+
validationError: getErrorMessage(state.errorMap.onSubmit),
|
|
9342
|
+
})}
|
|
9343
|
+
>
|
|
9344
|
+
{({ isSubmitting, validationError }) => {
|
|
9345
|
+
const formError = validationError;
|
|
9346
|
+
|
|
9347
|
+
return (
|
|
9348
|
+
<>
|
|
9349
|
+
<FieldError isInvalid={!!formError} className="mb-3">
|
|
9350
|
+
{formError}
|
|
9351
|
+
</FieldError>
|
|
9352
|
+
|
|
9353
|
+
<View className="gap-3">
|
|
9354
|
+
<form.Field name="email">
|
|
9355
|
+
{(field) => (
|
|
9356
|
+
<TextField>
|
|
9357
|
+
<Label>Email</Label>
|
|
9358
|
+
<Input
|
|
9359
|
+
value={field.state.value}
|
|
9360
|
+
onBlur={field.handleBlur}
|
|
9361
|
+
onChangeText={field.handleChange}
|
|
9362
|
+
placeholder="email@example.com"
|
|
9363
|
+
keyboardType="email-address"
|
|
9364
|
+
autoCapitalize="none"
|
|
9365
|
+
autoComplete="email"
|
|
9366
|
+
textContentType="emailAddress"
|
|
9367
|
+
returnKeyType="next"
|
|
9368
|
+
blurOnSubmit={false}
|
|
9369
|
+
onSubmitEditing={() => {
|
|
9370
|
+
passwordInputRef.current?.focus();
|
|
9371
|
+
}}
|
|
9372
|
+
/>
|
|
9373
|
+
</TextField>
|
|
9374
|
+
)}
|
|
9375
|
+
</form.Field>
|
|
9376
|
+
|
|
9377
|
+
<form.Field name="password">
|
|
9378
|
+
{(field) => (
|
|
9379
|
+
<TextField>
|
|
9380
|
+
<Label>Password</Label>
|
|
9381
|
+
<Input
|
|
9382
|
+
ref={passwordInputRef}
|
|
9383
|
+
value={field.state.value}
|
|
9384
|
+
onBlur={field.handleBlur}
|
|
9385
|
+
onChangeText={field.handleChange}
|
|
9386
|
+
placeholder="••••••••"
|
|
9387
|
+
secureTextEntry
|
|
9388
|
+
autoComplete="password"
|
|
9389
|
+
textContentType="password"
|
|
9390
|
+
returnKeyType="go"
|
|
9391
|
+
onSubmitEditing={form.handleSubmit}
|
|
9392
|
+
/>
|
|
9393
|
+
</TextField>
|
|
9394
|
+
)}
|
|
9395
|
+
</form.Field>
|
|
9396
|
+
|
|
9397
|
+
<Button onPress={form.handleSubmit} isDisabled={isSubmitting} className="mt-1">
|
|
9398
|
+
{isSubmitting ? <Spinner size="sm" color="default" /> : <Button.Label>Sign In</Button.Label>}
|
|
9399
|
+
</Button>
|
|
9400
|
+
</View>
|
|
9401
|
+
</>
|
|
9402
|
+
);
|
|
9403
|
+
}}
|
|
9404
|
+
</form.Subscribe>
|
|
9405
|
+
</Surface>
|
|
8316
9406
|
);
|
|
8317
|
-
|
|
9407
|
+
}
|
|
8318
9408
|
|
|
8319
|
-
|
|
9409
|
+
export { SignIn };
|
|
9410
|
+
`],
|
|
8320
9411
|
["auth/better-auth/native/uniwind/components/sign-up.tsx.hbs", `import { authClient } from "@/lib/auth-client";
|
|
8321
9412
|
{{#if (eq api "trpc")}}
|
|
8322
9413
|
import { queryClient } from "@/utils/trpc";
|
|
@@ -8324,127 +9415,203 @@ import { queryClient } from "@/utils/trpc";
|
|
|
8324
9415
|
{{#if (eq api "orpc")}}
|
|
8325
9416
|
import { queryClient } from "@/utils/orpc";
|
|
8326
9417
|
{{/if}}
|
|
8327
|
-
import {
|
|
8328
|
-
import {
|
|
8329
|
-
import {
|
|
8330
|
-
|
|
8331
|
-
|
|
8332
|
-
|
|
8333
|
-
|
|
8334
|
-
|
|
8335
|
-
|
|
8336
|
-
|
|
8337
|
-
|
|
8338
|
-
|
|
8339
|
-
|
|
8340
|
-
|
|
8341
|
-
|
|
8342
|
-
|
|
8343
|
-
|
|
8344
|
-
|
|
8345
|
-
|
|
8346
|
-
|
|
8347
|
-
|
|
8348
|
-
|
|
8349
|
-
|
|
8350
|
-
|
|
8351
|
-
|
|
8352
|
-
|
|
8353
|
-
|
|
8354
|
-
|
|
8355
|
-
|
|
8356
|
-
|
|
8357
|
-
|
|
8358
|
-
|
|
8359
|
-
|
|
8360
|
-
|
|
8361
|
-
|
|
8362
|
-
|
|
8363
|
-
}
|
|
8364
|
-
|
|
8365
|
-
|
|
8366
|
-
|
|
8367
|
-
|
|
8368
|
-
{
|
|
8369
|
-
|
|
8370
|
-
|
|
8371
|
-
|
|
8372
|
-
|
|
8373
|
-
|
|
8374
|
-
|
|
8375
|
-
onFinished() {
|
|
8376
|
-
setIsLoading(false);
|
|
8377
|
-
},
|
|
8378
|
-
}
|
|
8379
|
-
);
|
|
9418
|
+
import { useForm } from "@tanstack/react-form";
|
|
9419
|
+
import { useRef } from "react";
|
|
9420
|
+
import { Text, TextInput, View } from "react-native";
|
|
9421
|
+
import { Button, FieldError, Input, Label, Spinner, Surface, TextField, useToast } from "heroui-native";
|
|
9422
|
+
import z from "zod";
|
|
9423
|
+
|
|
9424
|
+
const signUpSchema = z.object({
|
|
9425
|
+
name: z
|
|
9426
|
+
.string()
|
|
9427
|
+
.trim()
|
|
9428
|
+
.min(1, "Name is required")
|
|
9429
|
+
.min(2, "Name must be at least 2 characters"),
|
|
9430
|
+
email: z
|
|
9431
|
+
.string()
|
|
9432
|
+
.trim()
|
|
9433
|
+
.min(1, "Email is required")
|
|
9434
|
+
.email("Enter a valid email address"),
|
|
9435
|
+
password: z
|
|
9436
|
+
.string()
|
|
9437
|
+
.min(1, "Password is required")
|
|
9438
|
+
.min(8, "Use at least 8 characters"),
|
|
9439
|
+
});
|
|
9440
|
+
|
|
9441
|
+
function getErrorMessage(error: unknown): string | null {
|
|
9442
|
+
if (!error) return null;
|
|
9443
|
+
|
|
9444
|
+
if (typeof error === "string") {
|
|
9445
|
+
return error;
|
|
9446
|
+
}
|
|
9447
|
+
|
|
9448
|
+
if (Array.isArray(error)) {
|
|
9449
|
+
for (const issue of error) {
|
|
9450
|
+
const message = getErrorMessage(issue);
|
|
9451
|
+
if (message) {
|
|
9452
|
+
return message;
|
|
9453
|
+
}
|
|
9454
|
+
}
|
|
9455
|
+
return null;
|
|
9456
|
+
}
|
|
9457
|
+
|
|
9458
|
+
if (typeof error === "object" && error !== null) {
|
|
9459
|
+
const maybeError = error as { message?: unknown };
|
|
9460
|
+
if (typeof maybeError.message === "string") {
|
|
9461
|
+
return maybeError.message;
|
|
9462
|
+
}
|
|
9463
|
+
}
|
|
9464
|
+
|
|
9465
|
+
return null;
|
|
8380
9466
|
}
|
|
8381
9467
|
|
|
8382
9468
|
export function SignUp() {
|
|
8383
|
-
const
|
|
8384
|
-
const
|
|
8385
|
-
const
|
|
8386
|
-
|
|
8387
|
-
const
|
|
8388
|
-
|
|
8389
|
-
|
|
8390
|
-
|
|
8391
|
-
|
|
8392
|
-
|
|
8393
|
-
|
|
8394
|
-
|
|
8395
|
-
|
|
8396
|
-
|
|
8397
|
-
|
|
8398
|
-
|
|
9469
|
+
const emailInputRef = useRef<TextInput>(null);
|
|
9470
|
+
const passwordInputRef = useRef<TextInput>(null);
|
|
9471
|
+
const { toast } = useToast();
|
|
9472
|
+
|
|
9473
|
+
const form = useForm({
|
|
9474
|
+
defaultValues: {
|
|
9475
|
+
name: "",
|
|
9476
|
+
email: "",
|
|
9477
|
+
password: "",
|
|
9478
|
+
},
|
|
9479
|
+
validators: {
|
|
9480
|
+
onSubmit: signUpSchema,
|
|
9481
|
+
},
|
|
9482
|
+
onSubmit: async ({ value, formApi }) => {
|
|
9483
|
+
await authClient.signUp.email(
|
|
9484
|
+
{
|
|
9485
|
+
name: value.name.trim(),
|
|
9486
|
+
email: value.email.trim(),
|
|
9487
|
+
password: value.password,
|
|
9488
|
+
},
|
|
9489
|
+
{
|
|
9490
|
+
onError(error) {
|
|
9491
|
+
toast.show({
|
|
9492
|
+
variant: "danger",
|
|
9493
|
+
label: error.error?.message || "Failed to sign up",
|
|
9494
|
+
});
|
|
9495
|
+
},
|
|
9496
|
+
onSuccess() {
|
|
9497
|
+
formApi.reset();
|
|
9498
|
+
toast.show({
|
|
9499
|
+
variant: "success",
|
|
9500
|
+
label: "Account created successfully",
|
|
9501
|
+
});
|
|
9502
|
+
{{#if (eq api "orpc")}}
|
|
9503
|
+
queryClient.refetchQueries();
|
|
9504
|
+
{{/if}}
|
|
9505
|
+
{{#if (eq api "trpc")}}
|
|
9506
|
+
queryClient.refetchQueries();
|
|
9507
|
+
{{/if}}
|
|
9508
|
+
},
|
|
9509
|
+
},
|
|
9510
|
+
);
|
|
9511
|
+
},
|
|
8399
9512
|
});
|
|
8400
|
-
}
|
|
8401
9513
|
|
|
8402
9514
|
return (
|
|
8403
|
-
|
|
8404
|
-
|
|
8405
|
-
|
|
8406
|
-
<ErrorView isInvalid={!!error} className="mb-3">
|
|
8407
|
-
{error}
|
|
8408
|
-
</ErrorView>
|
|
8409
|
-
|
|
8410
|
-
<View className="gap-3">
|
|
8411
|
-
<TextField>
|
|
8412
|
-
<TextField.Label>Name</TextField.Label>
|
|
8413
|
-
<TextField.Input value={name} onChangeText={setName} placeholder="John Doe" />
|
|
8414
|
-
</TextField>
|
|
8415
|
-
|
|
8416
|
-
<TextField>
|
|
8417
|
-
<TextField.Label>Email</TextField.Label>
|
|
8418
|
-
<TextField.Input
|
|
8419
|
-
value={email}
|
|
8420
|
-
onChangeText={setEmail}
|
|
8421
|
-
placeholder="email@example.com"
|
|
8422
|
-
keyboardType="email-address"
|
|
8423
|
-
autoCapitalize="none"
|
|
8424
|
-
/>
|
|
8425
|
-
</TextField>
|
|
8426
|
-
|
|
8427
|
-
<TextField>
|
|
8428
|
-
<TextField.Label>Password</TextField.Label>
|
|
8429
|
-
<TextField.Input
|
|
8430
|
-
value={password}
|
|
8431
|
-
onChangeText={setPassword}
|
|
8432
|
-
placeholder="••••••••"
|
|
8433
|
-
secureTextEntry
|
|
8434
|
-
/>
|
|
8435
|
-
</TextField>
|
|
9515
|
+
<Surface variant="secondary" className="p-4 rounded-lg">
|
|
9516
|
+
<Text className="text-foreground font-medium mb-4">Create Account</Text>
|
|
8436
9517
|
|
|
8437
|
-
<
|
|
8438
|
-
{
|
|
8439
|
-
|
|
8440
|
-
|
|
8441
|
-
|
|
8442
|
-
|
|
8443
|
-
|
|
8444
|
-
|
|
8445
|
-
|
|
9518
|
+
<form.Subscribe
|
|
9519
|
+
selector={(state) => ({
|
|
9520
|
+
isSubmitting: state.isSubmitting,
|
|
9521
|
+
validationError: getErrorMessage(state.errorMap.onSubmit),
|
|
9522
|
+
})}
|
|
9523
|
+
>
|
|
9524
|
+
{({ isSubmitting, validationError }) => {
|
|
9525
|
+
const formError = validationError;
|
|
9526
|
+
|
|
9527
|
+
return (
|
|
9528
|
+
<>
|
|
9529
|
+
<FieldError isInvalid={!!formError} className="mb-3">
|
|
9530
|
+
{formError}
|
|
9531
|
+
</FieldError>
|
|
9532
|
+
|
|
9533
|
+
<View className="gap-3">
|
|
9534
|
+
<form.Field name="name">
|
|
9535
|
+
{(field) => (
|
|
9536
|
+
<TextField>
|
|
9537
|
+
<Label>Name</Label>
|
|
9538
|
+
<Input
|
|
9539
|
+
value={field.state.value}
|
|
9540
|
+
onBlur={field.handleBlur}
|
|
9541
|
+
onChangeText={field.handleChange}
|
|
9542
|
+
placeholder="John Doe"
|
|
9543
|
+
autoComplete="name"
|
|
9544
|
+
textContentType="name"
|
|
9545
|
+
returnKeyType="next"
|
|
9546
|
+
blurOnSubmit={false}
|
|
9547
|
+
onSubmitEditing={() => {
|
|
9548
|
+
emailInputRef.current?.focus();
|
|
9549
|
+
}}
|
|
9550
|
+
/>
|
|
9551
|
+
</TextField>
|
|
9552
|
+
)}
|
|
9553
|
+
</form.Field>
|
|
9554
|
+
|
|
9555
|
+
<form.Field name="email">
|
|
9556
|
+
{(field) => (
|
|
9557
|
+
<TextField>
|
|
9558
|
+
<Label>Email</Label>
|
|
9559
|
+
<Input
|
|
9560
|
+
ref={emailInputRef}
|
|
9561
|
+
value={field.state.value}
|
|
9562
|
+
onBlur={field.handleBlur}
|
|
9563
|
+
onChangeText={field.handleChange}
|
|
9564
|
+
placeholder="email@example.com"
|
|
9565
|
+
keyboardType="email-address"
|
|
9566
|
+
autoCapitalize="none"
|
|
9567
|
+
autoComplete="email"
|
|
9568
|
+
textContentType="emailAddress"
|
|
9569
|
+
returnKeyType="next"
|
|
9570
|
+
blurOnSubmit={false}
|
|
9571
|
+
onSubmitEditing={() => {
|
|
9572
|
+
passwordInputRef.current?.focus();
|
|
9573
|
+
}}
|
|
9574
|
+
/>
|
|
9575
|
+
</TextField>
|
|
9576
|
+
)}
|
|
9577
|
+
</form.Field>
|
|
9578
|
+
|
|
9579
|
+
<form.Field name="password">
|
|
9580
|
+
{(field) => (
|
|
9581
|
+
<TextField>
|
|
9582
|
+
<Label>Password</Label>
|
|
9583
|
+
<Input
|
|
9584
|
+
ref={passwordInputRef}
|
|
9585
|
+
value={field.state.value}
|
|
9586
|
+
onBlur={field.handleBlur}
|
|
9587
|
+
onChangeText={field.handleChange}
|
|
9588
|
+
placeholder="••••••••"
|
|
9589
|
+
secureTextEntry
|
|
9590
|
+
autoComplete="new-password"
|
|
9591
|
+
textContentType="newPassword"
|
|
9592
|
+
returnKeyType="go"
|
|
9593
|
+
onSubmitEditing={form.handleSubmit}
|
|
9594
|
+
/>
|
|
9595
|
+
</TextField>
|
|
9596
|
+
)}
|
|
9597
|
+
</form.Field>
|
|
9598
|
+
|
|
9599
|
+
<Button onPress={form.handleSubmit} isDisabled={isSubmitting} className="mt-1">
|
|
9600
|
+
{isSubmitting ? (
|
|
9601
|
+
<Spinner size="sm" color="default" />
|
|
9602
|
+
) : (
|
|
9603
|
+
<Button.Label>Create Account</Button.Label>
|
|
9604
|
+
)}
|
|
9605
|
+
</Button>
|
|
9606
|
+
</View>
|
|
9607
|
+
</>
|
|
9608
|
+
);
|
|
9609
|
+
}}
|
|
9610
|
+
</form.Subscribe>
|
|
9611
|
+
</Surface>
|
|
8446
9612
|
);
|
|
8447
|
-
|
|
9613
|
+
}
|
|
9614
|
+
`],
|
|
8448
9615
|
["auth/better-auth/server/base/_gitignore", `# dependencies (bun install)
|
|
8449
9616
|
node_modules
|
|
8450
9617
|
|
|
@@ -16373,7 +17540,7 @@ import {
|
|
|
16373
17540
|
} from "@convex-dev/agent/react";
|
|
16374
17541
|
import { api } from "@{{projectName}}/backend/convex/_generated/api";
|
|
16375
17542
|
import { useMutation } from "convex/react";
|
|
16376
|
-
import { Button,
|
|
17543
|
+
import { Button, Separator, Spinner, Surface, Input, TextField, useThemeColor } from "heroui-native";
|
|
16377
17544
|
import { useRef, useEffect, useState } from "react";
|
|
16378
17545
|
import {
|
|
16379
17546
|
View,
|
|
@@ -16446,7 +17613,7 @@ export default function AIScreen() {
|
|
|
16446
17613
|
};
|
|
16447
17614
|
|
|
16448
17615
|
return (
|
|
16449
|
-
<Container>
|
|
17616
|
+
<Container isScrollable={false}>
|
|
16450
17617
|
<KeyboardAvoidingView
|
|
16451
17618
|
className="flex-1"
|
|
16452
17619
|
behavior={Platform.OS === "ios" ? "padding" : "height"}
|
|
@@ -16461,19 +17628,21 @@ export default function AIScreen() {
|
|
|
16461
17628
|
ref={scrollViewRef}
|
|
16462
17629
|
className="flex-1 mb-4"
|
|
16463
17630
|
showsVerticalScrollIndicator={false}
|
|
17631
|
+
contentContainerStyle=\\{{ flexGrow: 1, paddingBottom: 8 }}
|
|
17632
|
+
keyboardShouldPersistTaps="handled"
|
|
16464
17633
|
>
|
|
16465
17634
|
{!messages || messages.length === 0 ? (
|
|
16466
|
-
<
|
|
17635
|
+
<Surface variant="secondary" className="flex-1 justify-center items-center py-8 rounded-xl">
|
|
16467
17636
|
<Ionicons name="chatbubble-ellipses-outline" size={32} color={mutedColor} />
|
|
16468
17637
|
<Text className="text-muted text-sm mt-3">Ask me anything to get started</Text>
|
|
16469
|
-
</
|
|
17638
|
+
</Surface>
|
|
16470
17639
|
) : (
|
|
16471
|
-
<View className="gap-
|
|
17640
|
+
<View className="gap-3">
|
|
16472
17641
|
{messages.map((message: UIMessage) => (
|
|
16473
17642
|
<Surface
|
|
16474
17643
|
key={message.key}
|
|
16475
17644
|
variant={message.role === "user" ? "tertiary" : "secondary"}
|
|
16476
|
-
className={\`p-3 rounded-
|
|
17645
|
+
className={\`p-3 rounded-xl \${message.role === "user" ? "ml-8" : "mr-8"}\`}
|
|
16477
17646
|
>
|
|
16478
17647
|
<Text className="text-xs font-medium mb-1 text-muted">
|
|
16479
17648
|
{message.role === "user" ? "You" : "AI"}
|
|
@@ -16497,21 +17666,21 @@ export default function AIScreen() {
|
|
|
16497
17666
|
)}
|
|
16498
17667
|
</ScrollView>
|
|
16499
17668
|
|
|
16500
|
-
<
|
|
17669
|
+
<Separator className="mb-3" />
|
|
16501
17670
|
|
|
16502
17671
|
<View className="flex-row items-center gap-2">
|
|
16503
17672
|
<View className="flex-1">
|
|
16504
|
-
|
|
16505
|
-
|
|
16506
|
-
|
|
16507
|
-
|
|
16508
|
-
|
|
16509
|
-
|
|
16510
|
-
|
|
16511
|
-
|
|
16512
|
-
|
|
16513
|
-
|
|
16514
|
-
|
|
17673
|
+
<TextField>
|
|
17674
|
+
<Input
|
|
17675
|
+
value={input}
|
|
17676
|
+
onChangeText={setInput}
|
|
17677
|
+
placeholder="Type a message..."
|
|
17678
|
+
onSubmitEditing={onSubmit}
|
|
17679
|
+
editable={!isLoading}
|
|
17680
|
+
returnKeyType="send"
|
|
17681
|
+
/>
|
|
17682
|
+
</TextField>
|
|
17683
|
+
</View>
|
|
16515
17684
|
<Button
|
|
16516
17685
|
isIconOnly
|
|
16517
17686
|
variant={input.trim() && !isLoading ? "primary" : "secondary"}
|
|
@@ -16545,7 +17714,7 @@ import { DefaultChatTransport } from "ai";
|
|
|
16545
17714
|
import { fetch as expoFetch } from "expo/fetch";
|
|
16546
17715
|
import { Ionicons } from "@expo/vector-icons";
|
|
16547
17716
|
import { Container } from "@/components/container";
|
|
16548
|
-
import { Button,
|
|
17717
|
+
import { Button, Separator, FieldError, Spinner, Surface, Input, TextField, useThemeColor } from "heroui-native";
|
|
16549
17718
|
import { env } from "@{{projectName}}/env/native";
|
|
16550
17719
|
|
|
16551
17720
|
const generateAPIUrl = (relativePath: string) => {
|
|
@@ -16561,7 +17730,7 @@ const generateAPIUrl = (relativePath: string) => {
|
|
|
16561
17730
|
|
|
16562
17731
|
export default function AIScreen() {
|
|
16563
17732
|
const [input, setInput] = useState("");
|
|
16564
|
-
const { messages, error, sendMessage } = useChat({
|
|
17733
|
+
const { messages, error, sendMessage, status } = useChat({
|
|
16565
17734
|
transport: new DefaultChatTransport({
|
|
16566
17735
|
fetch: expoFetch as unknown as typeof globalThis.fetch,
|
|
16567
17736
|
api: generateAPIUrl("/ai"),
|
|
@@ -16571,6 +17740,7 @@ export default function AIScreen() {
|
|
|
16571
17740
|
const scrollViewRef = useRef<ScrollView>(null);
|
|
16572
17741
|
const foregroundColor = useThemeColor("foreground");
|
|
16573
17742
|
const mutedColor = useThemeColor("muted");
|
|
17743
|
+
const isBusy = status === "submitted" || status === "streaming";
|
|
16574
17744
|
|
|
16575
17745
|
useEffect(() => {
|
|
16576
17746
|
scrollViewRef.current?.scrollToEnd({ animated: true });
|
|
@@ -16578,7 +17748,7 @@ export default function AIScreen() {
|
|
|
16578
17748
|
|
|
16579
17749
|
const onSubmit = () => {
|
|
16580
17750
|
const value = input.trim();
|
|
16581
|
-
if (value) {
|
|
17751
|
+
if (value && !isBusy) {
|
|
16582
17752
|
sendMessage({ text: value });
|
|
16583
17753
|
setInput("");
|
|
16584
17754
|
}
|
|
@@ -16586,17 +17756,17 @@ export default function AIScreen() {
|
|
|
16586
17756
|
|
|
16587
17757
|
if (error) {
|
|
16588
17758
|
return (
|
|
16589
|
-
<Container>
|
|
17759
|
+
<Container isScrollable={false}>
|
|
16590
17760
|
<View className="flex-1 justify-center items-center px-4">
|
|
16591
17761
|
<Surface variant="secondary" className="p-4 rounded-lg">
|
|
16592
|
-
<
|
|
17762
|
+
<FieldError isInvalid>
|
|
16593
17763
|
<Text className="text-danger text-center font-medium mb-1">
|
|
16594
17764
|
{error.message}
|
|
16595
17765
|
</Text>
|
|
16596
17766
|
<Text className="text-muted text-center text-xs">
|
|
16597
17767
|
Please check your connection and try again.
|
|
16598
17768
|
</Text>
|
|
16599
|
-
</
|
|
17769
|
+
</FieldError>
|
|
16600
17770
|
</Surface>
|
|
16601
17771
|
</View>
|
|
16602
17772
|
</Container>
|
|
@@ -16604,7 +17774,7 @@ export default function AIScreen() {
|
|
|
16604
17774
|
}
|
|
16605
17775
|
|
|
16606
17776
|
return (
|
|
16607
|
-
<Container>
|
|
17777
|
+
<Container isScrollable={false}>
|
|
16608
17778
|
<KeyboardAvoidingView
|
|
16609
17779
|
className="flex-1"
|
|
16610
17780
|
behavior={Platform.OS === "ios" ? "padding" : "height"}
|
|
@@ -16619,19 +17789,21 @@ export default function AIScreen() {
|
|
|
16619
17789
|
ref={scrollViewRef}
|
|
16620
17790
|
className="flex-1 mb-4"
|
|
16621
17791
|
showsVerticalScrollIndicator={false}
|
|
17792
|
+
contentContainerStyle=\\{{ flexGrow: 1, paddingBottom: 8 }}
|
|
17793
|
+
keyboardShouldPersistTaps="handled"
|
|
16622
17794
|
>
|
|
16623
17795
|
{messages.length === 0 ? (
|
|
16624
|
-
<
|
|
17796
|
+
<Surface variant="secondary" className="flex-1 justify-center items-center py-8 rounded-xl">
|
|
16625
17797
|
<Ionicons name="chatbubble-ellipses-outline" size={32} color={mutedColor} />
|
|
16626
17798
|
<Text className="text-muted text-sm mt-3">Ask me anything to get started</Text>
|
|
16627
|
-
</
|
|
17799
|
+
</Surface>
|
|
16628
17800
|
) : (
|
|
16629
|
-
<View className="gap-
|
|
17801
|
+
<View className="gap-3">
|
|
16630
17802
|
{messages.map((message) => (
|
|
16631
17803
|
<Surface
|
|
16632
17804
|
key={message.id}
|
|
16633
17805
|
variant={message.role === "user" ? "tertiary" : "secondary"}
|
|
16634
|
-
className={\`p-3 rounded-
|
|
17806
|
+
className={\`p-3 rounded-xl \${message.role === "user" ? "ml-8" : "mr-8"}\`}
|
|
16635
17807
|
>
|
|
16636
17808
|
<Text className="text-xs font-medium mb-1 text-muted">
|
|
16637
17809
|
{message.role === "user" ? "You" : "AI"}
|
|
@@ -16657,35 +17829,45 @@ export default function AIScreen() {
|
|
|
16657
17829
|
</View>
|
|
16658
17830
|
</Surface>
|
|
16659
17831
|
))}
|
|
17832
|
+
{isBusy && (
|
|
17833
|
+
<Surface variant="secondary" className="p-3 mr-8 rounded-xl">
|
|
17834
|
+
<Text className="text-xs font-medium mb-1 text-muted">AI</Text>
|
|
17835
|
+
<View className="flex-row items-center gap-2">
|
|
17836
|
+
<Spinner size="sm" />
|
|
17837
|
+
<Text className="text-muted text-sm">Thinking...</Text>
|
|
17838
|
+
</View>
|
|
17839
|
+
</Surface>
|
|
17840
|
+
)}
|
|
16660
17841
|
</View>
|
|
16661
17842
|
)}
|
|
16662
17843
|
</ScrollView>
|
|
16663
17844
|
|
|
16664
|
-
<
|
|
17845
|
+
<Separator className="mb-3" />
|
|
16665
17846
|
|
|
16666
17847
|
<View className="flex-row items-center gap-2">
|
|
16667
17848
|
<View className="flex-1">
|
|
16668
17849
|
<TextField>
|
|
16669
|
-
<
|
|
17850
|
+
<Input
|
|
16670
17851
|
value={input}
|
|
16671
17852
|
onChangeText={setInput}
|
|
16672
17853
|
placeholder="Type a message..."
|
|
16673
17854
|
onSubmitEditing={onSubmit}
|
|
16674
|
-
|
|
17855
|
+
returnKeyType="send"
|
|
17856
|
+
editable={!isBusy}
|
|
16675
17857
|
/>
|
|
16676
17858
|
</TextField>
|
|
16677
17859
|
</View>
|
|
16678
17860
|
<Button
|
|
16679
17861
|
isIconOnly
|
|
16680
|
-
variant={input.trim() ? "primary" : "secondary"}
|
|
17862
|
+
variant={input.trim() && !isBusy ? "primary" : "secondary"}
|
|
16681
17863
|
onPress={onSubmit}
|
|
16682
|
-
isDisabled={!input.trim()}
|
|
17864
|
+
isDisabled={!input.trim() || isBusy}
|
|
16683
17865
|
size="sm"
|
|
16684
17866
|
>
|
|
16685
17867
|
<Ionicons
|
|
16686
17868
|
name="arrow-up"
|
|
16687
17869
|
size={18}
|
|
16688
|
-
color={input.trim() ? foregroundColor : mutedColor}
|
|
17870
|
+
color={input.trim() && !isBusy ? foregroundColor : mutedColor}
|
|
16689
17871
|
/>
|
|
16690
17872
|
</Button>
|
|
16691
17873
|
</View>
|
|
@@ -18795,7 +19977,7 @@ import { Container } from "@/components/container";
|
|
|
18795
19977
|
import { trpc } from "@/utils/trpc";
|
|
18796
19978
|
{{/if}}
|
|
18797
19979
|
{{/unless}}
|
|
18798
|
-
import { Button, Checkbox, Chip, Spinner, Surface, TextField, useThemeColor } from "heroui-native";
|
|
19980
|
+
import { Button, Checkbox, Chip, Spinner, Surface, Input, TextField, useThemeColor } from "heroui-native";
|
|
18799
19981
|
|
|
18800
19982
|
export default function TodosScreen() {
|
|
18801
19983
|
const [newTodoText, setNewTodoText] = useState("");
|
|
@@ -18922,7 +20104,7 @@ export default function TodosScreen() {
|
|
|
18922
20104
|
<View className="flex-row items-center gap-2">
|
|
18923
20105
|
<View className="flex-1">
|
|
18924
20106
|
<TextField>
|
|
18925
|
-
<
|
|
20107
|
+
<Input
|
|
18926
20108
|
value={newTodoText}
|
|
18927
20109
|
onChangeText={setNewTodoText}
|
|
18928
20110
|
placeholder="Add a new task..."
|
|
@@ -22498,7 +23680,6 @@ module.exports = config;
|
|
|
22498
23680
|
"@react-navigation/bottom-tabs": "^7.2.0",
|
|
22499
23681
|
"@react-navigation/drawer": "^7.1.1",
|
|
22500
23682
|
"@react-navigation/native": "^7.0.14",
|
|
22501
|
-
"@tanstack/react-form": "^1.0.5",
|
|
22502
23683
|
"@tanstack/react-query": "^5.85.5",
|
|
22503
23684
|
{{#if (includes examples "ai")}}
|
|
22504
23685
|
"@stardazed/streams-text-encoding": "^1.0.2",
|
|
@@ -22532,7 +23713,6 @@ module.exports = config;
|
|
|
22532
23713
|
},
|
|
22533
23714
|
"private": true
|
|
22534
23715
|
}
|
|
22535
|
-
|
|
22536
23716
|
`],
|
|
22537
23717
|
["frontend/native/bare/tsconfig.json.hbs", `{
|
|
22538
23718
|
"extends": "expo/tsconfig.base",
|
|
@@ -23560,7 +24740,6 @@ module.exports = config;
|
|
|
23560
24740
|
"@stardazed/streams-text-encoding": "^1.0.2",
|
|
23561
24741
|
"@ungap/structured-clone": "^1.3.0",
|
|
23562
24742
|
{{/if}}
|
|
23563
|
-
"@tanstack/react-form": "^1.0.5",
|
|
23564
24743
|
"babel-preset-expo": "~54.0.10",
|
|
23565
24744
|
"expo": "~54.0.33",
|
|
23566
24745
|
"expo-constants": "~18.0.8",
|
|
@@ -24107,7 +25286,7 @@ import { api } from "@{{projectName}}/backend/convex/_generated/api";
|
|
|
24107
25286
|
{{#unless (or (eq backend "none") (and (eq backend "convex") (eq auth "better-auth")))}}
|
|
24108
25287
|
import { Ionicons } from "@expo/vector-icons";
|
|
24109
25288
|
{{/unless}}
|
|
24110
|
-
import { Button, Chip,
|
|
25289
|
+
import { Button, Chip, Separator, Spinner, Surface, useThemeColor } from "heroui-native";
|
|
24111
25290
|
|
|
24112
25291
|
export default function Home() {
|
|
24113
25292
|
{{#if (eq api "orpc")}}
|
|
@@ -24143,8 +25322,8 @@ const isLoading = healthCheck?.isLoading;
|
|
|
24143
25322
|
{{/unless}}
|
|
24144
25323
|
|
|
24145
25324
|
return (
|
|
24146
|
-
<Container className="
|
|
24147
|
-
<View className="py-6 mb-
|
|
25325
|
+
<Container className="px-4 pb-4">
|
|
25326
|
+
<View className="py-6 mb-5">
|
|
24148
25327
|
<Text className="text-3xl font-semibold text-foreground tracking-tight">
|
|
24149
25328
|
Better T Stack
|
|
24150
25329
|
</Text>
|
|
@@ -24152,7 +25331,7 @@ return (
|
|
|
24152
25331
|
</View>
|
|
24153
25332
|
|
|
24154
25333
|
{{#unless (or (eq backend "none") (and (eq backend "convex") (eq auth "better-auth")))}}
|
|
24155
|
-
<Surface variant="secondary" className="p-4 rounded-
|
|
25334
|
+
<Surface variant="secondary" className="p-4 rounded-xl">
|
|
24156
25335
|
<View className="flex-row items-center justify-between mb-3">
|
|
24157
25336
|
<Text className="text-foreground font-medium">System Status</Text>
|
|
24158
25337
|
<Chip variant="secondary" color={isConnected ? "success" : "danger" } size="sm">
|
|
@@ -24162,9 +25341,9 @@ return (
|
|
|
24162
25341
|
</Chip>
|
|
24163
25342
|
</View>
|
|
24164
25343
|
|
|
24165
|
-
<
|
|
25344
|
+
<Separator className="mb-3" />
|
|
24166
25345
|
|
|
24167
|
-
<Surface variant="tertiary" className="p-3 rounded-
|
|
25346
|
+
<Surface variant="tertiary" className="p-3 rounded-lg">
|
|
24168
25347
|
<View className="flex-row items-center">
|
|
24169
25348
|
<View className={\`w-2 h-2 rounded-full mr-3 \${ isConnected ? "bg-success" : "bg-muted" }\`} />
|
|
24170
25349
|
<View className="flex-1">
|
|
@@ -24199,7 +25378,7 @@ return (
|
|
|
24199
25378
|
|
|
24200
25379
|
{{#if (and (eq backend "convex") (eq auth "clerk"))}}
|
|
24201
25380
|
<Authenticated>
|
|
24202
|
-
<Surface variant="secondary" className="mt-
|
|
25381
|
+
<Surface variant="secondary" className="mt-5 p-4 rounded-xl">
|
|
24203
25382
|
<View className="flex-row items-center justify-between">
|
|
24204
25383
|
<View className="flex-1">
|
|
24205
25384
|
<Text className="text-foreground font-medium">{user?.emailAddresses[0].emailAddress}</Text>
|
|
@@ -24228,14 +25407,14 @@ return (
|
|
|
24228
25407
|
|
|
24229
25408
|
{{#if (and (eq backend "convex") (eq auth "better-auth"))}}
|
|
24230
25409
|
{user ? (
|
|
24231
|
-
<Surface variant="secondary" className="mb-4 p-4 rounded-
|
|
25410
|
+
<Surface variant="secondary" className="mb-4 p-4 rounded-xl">
|
|
24232
25411
|
<View className="flex-row items-center justify-between">
|
|
24233
25412
|
<View className="flex-1">
|
|
24234
25413
|
<Text className="text-foreground font-medium">{user.name}</Text>
|
|
24235
25414
|
<Text className="text-muted text-xs mt-0.5">{user.email}</Text>
|
|
24236
25415
|
</View>
|
|
24237
25416
|
<Button
|
|
24238
|
-
variant="
|
|
25417
|
+
variant="danger"
|
|
24239
25418
|
size="sm"
|
|
24240
25419
|
onPress={() => {
|
|
24241
25420
|
authClient.signOut();
|
|
@@ -24246,7 +25425,7 @@ return (
|
|
|
24246
25425
|
</View>
|
|
24247
25426
|
</Surface>
|
|
24248
25427
|
) : null}
|
|
24249
|
-
<Surface variant="secondary" className="p-4 rounded-
|
|
25428
|
+
<Surface variant="secondary" className="p-4 rounded-xl">
|
|
24250
25429
|
<Text className="text-foreground font-medium mb-2">API Status</Text>
|
|
24251
25430
|
<View className="flex-row items-center gap-2">
|
|
24252
25431
|
<View className={\`w-2 h-2 rounded-full \${healthCheck==="OK" ? "bg-success" : "bg-danger" }\`} />
|
|
@@ -24260,7 +25439,7 @@ return (
|
|
|
24260
25439
|
</View>
|
|
24261
25440
|
</Surface>
|
|
24262
25441
|
{!user && (
|
|
24263
|
-
<View className="mt-
|
|
25442
|
+
<View className="mt-5 gap-4">
|
|
24264
25443
|
<SignIn />
|
|
24265
25444
|
<SignUp />
|
|
24266
25445
|
</View>
|
|
@@ -24268,7 +25447,8 @@ return (
|
|
|
24268
25447
|
{{/if}}
|
|
24269
25448
|
</Container>
|
|
24270
25449
|
);
|
|
24271
|
-
}
|
|
25450
|
+
}
|
|
25451
|
+
`],
|
|
24272
25452
|
["frontend/native/uniwind/app/+not-found.tsx.hbs", `import { Link, Stack } from "expo-router";
|
|
24273
25453
|
import { Button, Surface } from "heroui-native";
|
|
24274
25454
|
import { Text, View } from "react-native";
|
|
@@ -24337,36 +25517,49 @@ export default Modal;
|
|
|
24337
25517
|
`],
|
|
24338
25518
|
["frontend/native/uniwind/components/container.tsx.hbs", `import { cn } from "heroui-native";
|
|
24339
25519
|
import { type PropsWithChildren } from "react";
|
|
24340
|
-
import { ScrollView, View, type ViewProps } from "react-native";
|
|
25520
|
+
import { ScrollView, View, type ScrollViewProps, type ViewProps } from "react-native";
|
|
24341
25521
|
import Animated, { type AnimatedProps } from "react-native-reanimated";
|
|
24342
25522
|
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
|
24343
25523
|
|
|
24344
25524
|
const AnimatedView = Animated.createAnimatedComponent(View);
|
|
24345
25525
|
|
|
24346
25526
|
type Props = AnimatedProps<ViewProps> & {
|
|
24347
|
-
|
|
25527
|
+
className?: string;
|
|
25528
|
+
isScrollable?: boolean;
|
|
25529
|
+
scrollViewProps?: Omit<ScrollViewProps, "contentContainerStyle">;
|
|
24348
25530
|
};
|
|
24349
25531
|
|
|
24350
25532
|
export function Container({
|
|
24351
|
-
|
|
24352
|
-
|
|
24353
|
-
|
|
25533
|
+
children,
|
|
25534
|
+
className,
|
|
25535
|
+
isScrollable = true,
|
|
25536
|
+
scrollViewProps,
|
|
25537
|
+
...props
|
|
24354
25538
|
}: PropsWithChildren<Props>) {
|
|
24355
|
-
|
|
25539
|
+
const insets = useSafeAreaInsets();
|
|
24356
25540
|
|
|
24357
|
-
|
|
24358
|
-
|
|
24359
|
-
|
|
24360
|
-
|
|
24361
|
-
|
|
24362
|
-
|
|
24363
|
-
|
|
24364
|
-
|
|
24365
|
-
|
|
24366
|
-
|
|
24367
|
-
|
|
24368
|
-
|
|
24369
|
-
|
|
25541
|
+
return (
|
|
25542
|
+
<AnimatedView
|
|
25543
|
+
className={cn("flex-1 bg-background", className)}
|
|
25544
|
+
style=\\{{
|
|
25545
|
+
paddingBottom: insets.bottom,
|
|
25546
|
+
}}
|
|
25547
|
+
{...props}
|
|
25548
|
+
>
|
|
25549
|
+
{isScrollable ? (
|
|
25550
|
+
<ScrollView
|
|
25551
|
+
contentContainerStyle=\\{{ flexGrow: 1 }}
|
|
25552
|
+
keyboardShouldPersistTaps="handled"
|
|
25553
|
+
contentInsetAdjustmentBehavior="automatic"
|
|
25554
|
+
{...scrollViewProps}
|
|
25555
|
+
>
|
|
25556
|
+
{children}
|
|
25557
|
+
</ScrollView>
|
|
25558
|
+
) : (
|
|
25559
|
+
<View className="flex-1">{children}</View>
|
|
25560
|
+
)}
|
|
25561
|
+
</AnimatedView>
|
|
25562
|
+
);
|
|
24370
25563
|
}
|
|
24371
25564
|
`],
|
|
24372
25565
|
["frontend/native/uniwind/components/theme-toggle.tsx.hbs", `import { Ionicons } from '@expo/vector-icons';
|
|
@@ -24476,17 +25669,17 @@ export function useAppTheme() {
|
|
|
24476
25669
|
`],
|
|
24477
25670
|
["frontend/native/uniwind/metro.config.js.hbs", `const { getDefaultConfig } = require("expo/metro-config");
|
|
24478
25671
|
const { withUniwindConfig } = require("uniwind/metro");
|
|
25672
|
+
const { wrapWithReanimatedMetroConfig } = require("react-native-reanimated/metro-config");
|
|
24479
25673
|
|
|
24480
25674
|
/** @type {import('expo/metro-config').MetroConfig} */
|
|
24481
25675
|
const config = getDefaultConfig(__dirname);
|
|
24482
25676
|
|
|
24483
|
-
const uniwindConfig = withUniwindConfig(config, {
|
|
25677
|
+
const uniwindConfig = withUniwindConfig(wrapWithReanimatedMetroConfig(config), {
|
|
24484
25678
|
cssEntryFile: "./global.css",
|
|
24485
25679
|
dtsFile: "./uniwind-types.d.ts",
|
|
24486
25680
|
});
|
|
24487
25681
|
|
|
24488
25682
|
module.exports = uniwindConfig;
|
|
24489
|
-
|
|
24490
25683
|
`],
|
|
24491
25684
|
["frontend/native/uniwind/package.json.hbs", `{
|
|
24492
25685
|
"name": "native",
|
|
@@ -24520,7 +25713,7 @@ module.exports = uniwindConfig;
|
|
|
24520
25713
|
"expo-router": "~6.0.14",
|
|
24521
25714
|
"expo-secure-store": "~15.0.7",
|
|
24522
25715
|
"expo-status-bar": "~3.0.8",
|
|
24523
|
-
"heroui-native": "^1.0.0-
|
|
25716
|
+
"heroui-native": "^1.0.0-rc.1",
|
|
24524
25717
|
"react": "19.1.0",
|
|
24525
25718
|
"react-dom": "19.1.0",
|
|
24526
25719
|
"react-native": "0.81.5",
|
|
@@ -24535,13 +25728,14 @@ module.exports = uniwindConfig;
|
|
|
24535
25728
|
"tailwind-merge": "^3.4.0",
|
|
24536
25729
|
"tailwind-variants": "^3.2.2",
|
|
24537
25730
|
"tailwindcss": "^4.1.18",
|
|
24538
|
-
"uniwind": "^1.
|
|
25731
|
+
"uniwind": "^1.3.0"
|
|
24539
25732
|
},
|
|
24540
25733
|
"devDependencies": {
|
|
24541
25734
|
"@types/node": "^24.10.0",
|
|
24542
25735
|
"@types/react": "~19.1.0"
|
|
24543
25736
|
}
|
|
24544
|
-
}
|
|
25737
|
+
}
|
|
25738
|
+
`],
|
|
24545
25739
|
["frontend/native/uniwind/tsconfig.json.hbs", `{
|
|
24546
25740
|
"extends": "expo/tsconfig.base",
|
|
24547
25741
|
"compilerOptions": {
|
|
@@ -24894,7 +26088,6 @@ initOpenNextCloudflareForDev();
|
|
|
24894
26088
|
"dependencies": {
|
|
24895
26089
|
"@base-ui/react": "^1.0.0",
|
|
24896
26090
|
"shadcn": "^3.6.2",
|
|
24897
|
-
"@tanstack/react-form": "^1.27.3",
|
|
24898
26091
|
"class-variance-authority": "^0.7.1",
|
|
24899
26092
|
"clsx": "^2.1.1",
|
|
24900
26093
|
"lucide-react": "^0.546.0",
|
|
@@ -25279,7 +26472,6 @@ export function ThemeProvider({
|
|
|
25279
26472
|
"@react-router/fs-routes": "^7.10.1",
|
|
25280
26473
|
"@react-router/node": "^7.10.1",
|
|
25281
26474
|
"@react-router/serve": "^7.10.1",
|
|
25282
|
-
"@tanstack/react-form": "^1.27.3",
|
|
25283
26475
|
"class-variance-authority": "^0.7.1",
|
|
25284
26476
|
"clsx": "^2.1.1",
|
|
25285
26477
|
"isbot": "^5.1.28",
|
|
@@ -25708,7 +26900,6 @@ export default defineConfig({
|
|
|
25708
26900
|
"@hookform/resolvers": "^5.1.1",
|
|
25709
26901
|
"@base-ui/react": "^1.0.0",
|
|
25710
26902
|
"shadcn": "^3.6.2",
|
|
25711
|
-
"@tanstack/react-form": "^1.12.3",
|
|
25712
26903
|
"@tailwindcss/vite": "^4.0.15",
|
|
25713
26904
|
"@tanstack/react-router": "^1.141.1",
|
|
25714
26905
|
"class-variance-authority": "^0.7.1",
|
|
@@ -26109,7 +27300,6 @@ export default defineConfig({
|
|
|
26109
27300
|
"dependencies": {
|
|
26110
27301
|
"@base-ui/react": "^1.0.0",
|
|
26111
27302
|
"shadcn": "^3.6.2",
|
|
26112
|
-
"@tanstack/react-form": "^1.23.5",
|
|
26113
27303
|
"@tailwindcss/vite": "^4.1.8",
|
|
26114
27304
|
"@tanstack/react-query": "^5.80.6",
|
|
26115
27305
|
"@tanstack/react-router": "^1.141.1",
|
|
@@ -27542,7 +28732,6 @@ dist-ssr
|
|
|
27542
28732
|
"dependencies": {
|
|
27543
28733
|
"@tailwindcss/vite": "^4.1.13",
|
|
27544
28734
|
"@tanstack/router-plugin": "^1.131.44",
|
|
27545
|
-
"@tanstack/solid-form": "^1.20.0",
|
|
27546
28735
|
"@tanstack/solid-router": "^1.131.44",
|
|
27547
28736
|
"lucide-solid": "^0.544.0",
|
|
27548
28737
|
"solid-js": "^1.9.9",
|
|
@@ -27864,9 +29053,7 @@ vite.config.ts.timestamp-*
|
|
|
27864
29053
|
"tailwindcss": "^4.1.12",
|
|
27865
29054
|
"vite": "^7.1.2"
|
|
27866
29055
|
},
|
|
27867
|
-
"dependencies": {
|
|
27868
|
-
"@tanstack/svelte-form": "^1.19.2"
|
|
27869
|
-
}
|
|
29056
|
+
"dependencies": {}
|
|
27870
29057
|
}
|
|
27871
29058
|
`],
|
|
27872
29059
|
["frontend/svelte/src/app.css", `@import "tailwindcss";
|