@teardown/cli 1.2.38 → 2.0.41
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/bin/teardown.js +11 -1
- package/package.json +77 -57
- package/src/cli/commands/init.ts +254 -0
- package/src/cli/commands/plugins.ts +93 -0
- package/src/cli/commands/prebuild.ts +168 -0
- package/src/cli/commands/run.ts +727 -0
- package/src/cli/commands/start.ts +87 -0
- package/src/cli/commands/validate.ts +62 -0
- package/src/cli/index.ts +59 -0
- package/src/config/index.ts +45 -0
- package/src/config/loader.ts +366 -0
- package/src/config/schema.ts +235 -0
- package/src/config/types.ts +322 -0
- package/src/index.ts +177 -0
- package/src/pipeline/cache.ts +179 -0
- package/src/pipeline/index.ts +10 -0
- package/src/pipeline/stages.ts +692 -0
- package/src/plugins/base.ts +370 -0
- package/src/plugins/capabilities/biometrics.ts +64 -0
- package/src/plugins/capabilities/bluetooth.ts +86 -0
- package/src/plugins/capabilities/calendar.ts +57 -0
- package/src/plugins/capabilities/camera.ts +77 -0
- package/src/plugins/capabilities/contacts.ts +57 -0
- package/src/plugins/capabilities/deep-linking.ts +124 -0
- package/src/plugins/capabilities/firebase.ts +138 -0
- package/src/plugins/capabilities/index.ts +96 -0
- package/src/plugins/capabilities/location.ts +87 -0
- package/src/plugins/capabilities/photo-library.ts +80 -0
- package/src/plugins/capabilities/push-notifications.ts +98 -0
- package/src/plugins/capabilities/sign-in-with-apple.ts +53 -0
- package/src/plugins/context.ts +220 -0
- package/src/plugins/index.ts +26 -0
- package/src/plugins/resolver.ts +321 -0
- package/src/templates/generator.ts +507 -0
- package/src/templates/index.ts +9 -0
- package/src/templates/paths.ts +25 -0
- package/src/transformers/android/gradle.ts +400 -0
- package/src/transformers/android/index.ts +19 -0
- package/src/transformers/android/manifest.ts +506 -0
- package/src/transformers/index.ts +39 -0
- package/src/transformers/ios/entitlements.ts +283 -0
- package/src/transformers/ios/index.ts +10 -0
- package/src/transformers/ios/pbxproj.ts +267 -0
- package/src/transformers/ios/plist.ts +198 -0
- package/src/utils/fs.ts +429 -0
- package/src/utils/index.ts +21 -0
- package/src/utils/logger.ts +203 -0
- package/templates/.gitignore +63 -0
- package/templates/Gemfile +3 -0
- package/templates/android/app/build.gradle.kts +97 -0
- package/templates/android/app/proguard-rules.pro +10 -0
- package/templates/android/app/src/main/AndroidManifest.xml +26 -0
- package/templates/android/app/src/main/java/com/appname/MainActivity.kt +22 -0
- package/templates/android/app/src/main/java/com/appname/MainApplication.kt +44 -0
- package/templates/android/app/src/main/res/values/strings.xml +3 -0
- package/templates/android/app/src/main/res/values/styles.xml +7 -0
- package/templates/android/build.gradle.kts +44 -0
- package/templates/android/gradle.properties +39 -0
- package/templates/android/settings.gradle.kts +12 -0
- package/templates/babel.config.js +15 -0
- package/templates/index.js +7 -0
- package/templates/ios/.xcode.env +11 -0
- package/templates/ios/AppName/AppDelegate.swift +25 -0
- package/templates/ios/AppName/AppName-Bridging-Header.h +4 -0
- package/templates/ios/AppName/AppName.entitlements +6 -0
- package/templates/ios/AppName/Images.xcassets/AppIcon.appiconset/Contents.json +35 -0
- package/templates/ios/AppName/Images.xcassets/Contents.json +6 -0
- package/templates/ios/AppName/Info.plist +49 -0
- package/templates/ios/AppName/LaunchScreen.storyboard +38 -0
- package/templates/ios/AppName.xcodeproj/project.pbxproj +402 -0
- package/templates/ios/AppName.xcodeproj/xcshareddata/xcschemes/AppName.xcscheme +78 -0
- package/templates/ios/Podfile +35 -0
- package/templates/metro.config.js +41 -0
- package/templates/package.json +57 -0
- package/templates/react-native.config.js +8 -0
- package/templates/src/app/index.tsx +34 -0
- package/templates/src/assets/fonts/.gitkeep +1 -0
- package/templates/src/assets/images/.gitkeep +1 -0
- package/templates/src/components/ui/accordion.tsx +114 -0
- package/templates/src/components/ui/avatar.tsx +75 -0
- package/templates/src/components/ui/button.tsx +93 -0
- package/templates/src/components/ui/card.tsx +120 -0
- package/templates/src/components/ui/checkbox.tsx +133 -0
- package/templates/src/components/ui/chip.tsx +95 -0
- package/templates/src/components/ui/dialog.tsx +134 -0
- package/templates/src/components/ui/divider.tsx +67 -0
- package/templates/src/components/ui/error-view.tsx +82 -0
- package/templates/src/components/ui/form-field.tsx +101 -0
- package/templates/src/components/ui/index.ts +100 -0
- package/templates/src/components/ui/popover.tsx +92 -0
- package/templates/src/components/ui/pressable-feedback.tsx +88 -0
- package/templates/src/components/ui/radio-group.tsx +153 -0
- package/templates/src/components/ui/scroll-shadow.tsx +108 -0
- package/templates/src/components/ui/select.tsx +165 -0
- package/templates/src/components/ui/skeleton-group.tsx +97 -0
- package/templates/src/components/ui/skeleton.tsx +87 -0
- package/templates/src/components/ui/spinner.tsx +87 -0
- package/templates/src/components/ui/surface.tsx +95 -0
- package/templates/src/components/ui/switch.tsx +124 -0
- package/templates/src/components/ui/tabs.tsx +154 -0
- package/templates/src/components/ui/text-field.tsx +106 -0
- package/templates/src/components/ui/toast.tsx +129 -0
- package/templates/src/contexts/.gitkeep +2 -0
- package/templates/src/core/clients/api/api.client.ts +113 -0
- package/templates/src/core/clients/api/index.ts +1 -0
- package/templates/src/core/clients/storage/index.ts +1 -0
- package/templates/src/core/clients/storage/storage.client.ts +121 -0
- package/templates/src/core/constants/index.ts +19 -0
- package/templates/src/core/core.ts +40 -0
- package/templates/src/core/index.ts +10 -0
- package/templates/src/global.css +87 -0
- package/templates/src/hooks/index.ts +6 -0
- package/templates/src/hooks/use-debounce.ts +23 -0
- package/templates/src/hooks/use-mounted.ts +21 -0
- package/templates/src/index.ts +28 -0
- package/templates/src/lib/index.ts +5 -0
- package/templates/src/lib/utils.ts +115 -0
- package/templates/src/modules/.gitkeep +6 -0
- package/templates/src/navigation/index.ts +8 -0
- package/templates/src/navigation/navigation-provider.tsx +36 -0
- package/templates/src/navigation/router.tsx +137 -0
- package/templates/src/providers/app.provider.tsx +29 -0
- package/templates/src/providers/index.ts +5 -0
- package/templates/src/routes/(tabs)/_layout.tsx +42 -0
- package/templates/src/routes/(tabs)/explore.tsx +161 -0
- package/templates/src/routes/(tabs)/home.tsx +138 -0
- package/templates/src/routes/(tabs)/profile.tsx +151 -0
- package/templates/src/routes/_layout.tsx +18 -0
- package/templates/src/routes/settings.tsx +194 -0
- package/templates/src/screens/auth/index.ts +6 -0
- package/templates/src/screens/auth/login.tsx +165 -0
- package/templates/src/screens/auth/register.tsx +203 -0
- package/templates/src/screens/home.tsx +204 -0
- package/templates/src/screens/index.ts +17 -0
- package/templates/src/screens/profile.tsx +210 -0
- package/templates/src/screens/settings.tsx +216 -0
- package/templates/src/screens/welcome.tsx +101 -0
- package/templates/src/styles/index.ts +103 -0
- package/templates/src/types/common.ts +71 -0
- package/templates/src/types/index.ts +5 -0
- package/templates/tsconfig.json +14 -0
- package/README.md +0 -15
- package/assets/favicon.ico +0 -0
- package/dist/commands/dev/dev.js +0 -55
- package/dist/commands/init/init-teardown.js +0 -26
- package/dist/index.js +0 -20
- package/dist/modules/dev/dev-menu/keyboard-handler.js +0 -138
- package/dist/modules/dev/dev-menu/open-debugger-keyboard-handler.js +0 -105
- package/dist/modules/dev/dev-server/cdp/cdp.adapter.js +0 -12
- package/dist/modules/dev/dev-server/cdp/index.js +0 -18
- package/dist/modules/dev/dev-server/cdp/types.js +0 -2
- package/dist/modules/dev/dev-server/dev-server-checker.js +0 -72
- package/dist/modules/dev/dev-server/dev-server.js +0 -269
- package/dist/modules/dev/dev-server/inspector/device.event-reporter.js +0 -165
- package/dist/modules/dev/dev-server/inspector/device.js +0 -577
- package/dist/modules/dev/dev-server/inspector/inspector.js +0 -204
- package/dist/modules/dev/dev-server/inspector/types.js +0 -2
- package/dist/modules/dev/dev-server/inspector/wss/servers/debugger-connection.server.js +0 -61
- package/dist/modules/dev/dev-server/inspector/wss/servers/device-connection.server.js +0 -64
- package/dist/modules/dev/dev-server/plugins/devtools.plugin.js +0 -50
- package/dist/modules/dev/dev-server/plugins/favicon.plugin.js +0 -19
- package/dist/modules/dev/dev-server/plugins/multipart.plugin.js +0 -62
- package/dist/modules/dev/dev-server/plugins/systrace.plugin.js +0 -28
- package/dist/modules/dev/dev-server/plugins/types.js +0 -2
- package/dist/modules/dev/dev-server/plugins/wss/index.js +0 -19
- package/dist/modules/dev/dev-server/plugins/wss/servers/web-socket-api.server.js +0 -66
- package/dist/modules/dev/dev-server/plugins/wss/servers/web-socket-debugger.server.js +0 -128
- package/dist/modules/dev/dev-server/plugins/wss/servers/web-socket-dev-client.server.js +0 -75
- package/dist/modules/dev/dev-server/plugins/wss/servers/web-socket-events.server.js +0 -198
- package/dist/modules/dev/dev-server/plugins/wss/servers/web-socket-hmr.server.js +0 -120
- package/dist/modules/dev/dev-server/plugins/wss/servers/web-socket-message.server.js +0 -357
- package/dist/modules/dev/dev-server/plugins/wss/types.js +0 -2
- package/dist/modules/dev/dev-server/plugins/wss/web-socket-router.js +0 -57
- package/dist/modules/dev/dev-server/plugins/wss/web-socket-server-adapter.js +0 -26
- package/dist/modules/dev/dev-server/plugins/wss/web-socket-server.js +0 -46
- package/dist/modules/dev/dev-server/plugins/wss/wss.plugin.js +0 -55
- package/dist/modules/dev/dev-server/sybmolicate/sybmolicate.plugin.js +0 -36
- package/dist/modules/dev/dev-server/sybmolicate/types.js +0 -2
- package/dist/modules/dev/terminal/base.terminal.reporter.js +0 -78
- package/dist/modules/dev/terminal/terminal.reporter.js +0 -76
- package/dist/modules/dev/types.js +0 -2
- package/dist/modules/dev/utils/log.js +0 -73
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Settings Screen
|
|
3
|
+
*
|
|
4
|
+
* App settings and preferences screen.
|
|
5
|
+
* This is a stack screen outside of the tab navigator.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import React from "react";
|
|
9
|
+
import { View, ScrollView, Switch, Pressable } from "react-native";
|
|
10
|
+
import { defineScreen } from "@teardown/navigation/primitives";
|
|
11
|
+
import { useTypedNavigation } from "@teardown/navigation/hooks";
|
|
12
|
+
import {
|
|
13
|
+
Text,
|
|
14
|
+
Button,
|
|
15
|
+
Card,
|
|
16
|
+
CardHeader,
|
|
17
|
+
CardTitle,
|
|
18
|
+
CardContent,
|
|
19
|
+
Separator,
|
|
20
|
+
} from "@/components/ui";
|
|
21
|
+
|
|
22
|
+
function SettingsScreen(): React.JSX.Element {
|
|
23
|
+
const navigation = useTypedNavigation();
|
|
24
|
+
const [notifications, setNotifications] = React.useState(true);
|
|
25
|
+
const [darkMode, setDarkMode] = React.useState(false);
|
|
26
|
+
const [analytics, setAnalytics] = React.useState(true);
|
|
27
|
+
const [biometrics, setBiometrics] = React.useState(false);
|
|
28
|
+
|
|
29
|
+
return (
|
|
30
|
+
<ScrollView className="flex-1 bg-background" contentContainerClassName="p-6 gap-6">
|
|
31
|
+
{/* Preferences Section */}
|
|
32
|
+
<View className="gap-2">
|
|
33
|
+
<Text variant="label" className="px-1">PREFERENCES</Text>
|
|
34
|
+
<Card>
|
|
35
|
+
<CardContent className="p-0">
|
|
36
|
+
<SettingRow
|
|
37
|
+
icon="🔔"
|
|
38
|
+
title="Push Notifications"
|
|
39
|
+
description="Receive push notifications"
|
|
40
|
+
value={notifications}
|
|
41
|
+
onValueChange={setNotifications}
|
|
42
|
+
/>
|
|
43
|
+
<Separator />
|
|
44
|
+
<SettingRow
|
|
45
|
+
icon="🌙"
|
|
46
|
+
title="Dark Mode"
|
|
47
|
+
description="Use dark color theme"
|
|
48
|
+
value={darkMode}
|
|
49
|
+
onValueChange={setDarkMode}
|
|
50
|
+
/>
|
|
51
|
+
<Separator />
|
|
52
|
+
<SettingRow
|
|
53
|
+
icon="📊"
|
|
54
|
+
title="Analytics"
|
|
55
|
+
description="Help improve the app"
|
|
56
|
+
value={analytics}
|
|
57
|
+
onValueChange={setAnalytics}
|
|
58
|
+
/>
|
|
59
|
+
<Separator />
|
|
60
|
+
<SettingRow
|
|
61
|
+
icon="🔐"
|
|
62
|
+
title="Biometric Lock"
|
|
63
|
+
description="Require Face ID or Touch ID"
|
|
64
|
+
value={biometrics}
|
|
65
|
+
onValueChange={setBiometrics}
|
|
66
|
+
/>
|
|
67
|
+
</CardContent>
|
|
68
|
+
</Card>
|
|
69
|
+
</View>
|
|
70
|
+
|
|
71
|
+
{/* Storage Section */}
|
|
72
|
+
<View className="gap-2">
|
|
73
|
+
<Text variant="label" className="px-1">STORAGE</Text>
|
|
74
|
+
<Card>
|
|
75
|
+
<CardContent className="gap-4 py-4">
|
|
76
|
+
<View className="flex-row justify-between">
|
|
77
|
+
<Text>Cache Size</Text>
|
|
78
|
+
<Text variant="muted">24.5 MB</Text>
|
|
79
|
+
</View>
|
|
80
|
+
<Button variant="outline" size="sm">
|
|
81
|
+
Clear Cache
|
|
82
|
+
</Button>
|
|
83
|
+
</CardContent>
|
|
84
|
+
</Card>
|
|
85
|
+
</View>
|
|
86
|
+
|
|
87
|
+
{/* About Section */}
|
|
88
|
+
<View className="gap-2">
|
|
89
|
+
<Text variant="label" className="px-1">ABOUT</Text>
|
|
90
|
+
<Card>
|
|
91
|
+
<CardContent className="p-0">
|
|
92
|
+
<InfoRow icon="📱" title="Version" value="1.0.0" />
|
|
93
|
+
<Separator />
|
|
94
|
+
<InfoRow icon="🔨" title="Build" value="1" />
|
|
95
|
+
<Separator />
|
|
96
|
+
<InfoRow icon="⚛️" title="React Native" value="0.76.9" />
|
|
97
|
+
</CardContent>
|
|
98
|
+
</Card>
|
|
99
|
+
</View>
|
|
100
|
+
|
|
101
|
+
{/* Legal Section */}
|
|
102
|
+
<View className="gap-2">
|
|
103
|
+
<Text variant="label" className="px-1">LEGAL</Text>
|
|
104
|
+
<Card>
|
|
105
|
+
<CardContent className="p-0">
|
|
106
|
+
<LinkRow icon="📄" title="Terms of Service" />
|
|
107
|
+
<Separator />
|
|
108
|
+
<LinkRow icon="🔒" title="Privacy Policy" />
|
|
109
|
+
<Separator />
|
|
110
|
+
<LinkRow icon="📜" title="Licenses" />
|
|
111
|
+
</CardContent>
|
|
112
|
+
</Card>
|
|
113
|
+
</View>
|
|
114
|
+
|
|
115
|
+
{/* Back Button */}
|
|
116
|
+
<Button variant="secondary" onPress={() => navigation.goBack()} fullWidth>
|
|
117
|
+
Go Back
|
|
118
|
+
</Button>
|
|
119
|
+
</ScrollView>
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function SettingRow({
|
|
124
|
+
icon,
|
|
125
|
+
title,
|
|
126
|
+
description,
|
|
127
|
+
value,
|
|
128
|
+
onValueChange,
|
|
129
|
+
}: {
|
|
130
|
+
icon: string;
|
|
131
|
+
title: string;
|
|
132
|
+
description: string;
|
|
133
|
+
value: boolean;
|
|
134
|
+
onValueChange: (value: boolean) => void;
|
|
135
|
+
}): React.JSX.Element {
|
|
136
|
+
return (
|
|
137
|
+
<View className="flex-row items-center px-4 py-3">
|
|
138
|
+
<Text className="text-xl mr-3">{icon}</Text>
|
|
139
|
+
<View className="flex-1 mr-4">
|
|
140
|
+
<Text variant="large">{title}</Text>
|
|
141
|
+
<Text variant="muted">{description}</Text>
|
|
142
|
+
</View>
|
|
143
|
+
<Switch
|
|
144
|
+
value={value}
|
|
145
|
+
onValueChange={onValueChange}
|
|
146
|
+
trackColor={{ false: "#d1d5db", true: "#bfdbfe" }}
|
|
147
|
+
thumbColor={value ? "#3b82f6" : "#f3f4f6"}
|
|
148
|
+
/>
|
|
149
|
+
</View>
|
|
150
|
+
);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function InfoRow({
|
|
154
|
+
icon,
|
|
155
|
+
title,
|
|
156
|
+
value,
|
|
157
|
+
}: {
|
|
158
|
+
icon: string;
|
|
159
|
+
title: string;
|
|
160
|
+
value: string;
|
|
161
|
+
}): React.JSX.Element {
|
|
162
|
+
return (
|
|
163
|
+
<View className="flex-row items-center px-4 py-3">
|
|
164
|
+
<Text className="text-xl mr-3">{icon}</Text>
|
|
165
|
+
<Text variant="large" className="flex-1">{title}</Text>
|
|
166
|
+
<Text variant="muted">{value}</Text>
|
|
167
|
+
</View>
|
|
168
|
+
);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
function LinkRow({
|
|
172
|
+
icon,
|
|
173
|
+
title,
|
|
174
|
+
}: {
|
|
175
|
+
icon: string;
|
|
176
|
+
title: string;
|
|
177
|
+
}): React.JSX.Element {
|
|
178
|
+
return (
|
|
179
|
+
<Pressable className="flex-row items-center px-4 py-3 active:bg-muted/50">
|
|
180
|
+
<Text className="text-xl mr-3">{icon}</Text>
|
|
181
|
+
<Text variant="large" className="flex-1">{title}</Text>
|
|
182
|
+
<Text className="text-muted-foreground">›</Text>
|
|
183
|
+
</Pressable>
|
|
184
|
+
);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
export default defineScreen({
|
|
188
|
+
component: SettingsScreen,
|
|
189
|
+
options: {
|
|
190
|
+
title: "Settings",
|
|
191
|
+
headerShown: true,
|
|
192
|
+
animation: "slide_from_right",
|
|
193
|
+
},
|
|
194
|
+
});
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Login Screen
|
|
3
|
+
*
|
|
4
|
+
* Email/password login with social auth options.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import React, { useState } from "react";
|
|
8
|
+
import { View, ScrollView, Pressable, KeyboardAvoidingView, Platform } from "react-native";
|
|
9
|
+
import {
|
|
10
|
+
Text,
|
|
11
|
+
Button,
|
|
12
|
+
Input,
|
|
13
|
+
Card,
|
|
14
|
+
CardHeader,
|
|
15
|
+
CardTitle,
|
|
16
|
+
CardDescription,
|
|
17
|
+
CardContent,
|
|
18
|
+
CardFooter,
|
|
19
|
+
Separator,
|
|
20
|
+
} from "@/components/ui";
|
|
21
|
+
|
|
22
|
+
interface LoginScreenProps {
|
|
23
|
+
onLogin?: (email: string, password: string) => void;
|
|
24
|
+
onForgotPassword?: () => void;
|
|
25
|
+
onRegister?: () => void;
|
|
26
|
+
onSocialLogin?: (provider: "google" | "apple" | "github") => void;
|
|
27
|
+
loading?: boolean;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function LoginScreen({
|
|
31
|
+
onLogin,
|
|
32
|
+
onForgotPassword,
|
|
33
|
+
onRegister,
|
|
34
|
+
onSocialLogin,
|
|
35
|
+
loading = false,
|
|
36
|
+
}: LoginScreenProps): React.JSX.Element {
|
|
37
|
+
const [email, setEmail] = useState("");
|
|
38
|
+
const [password, setPassword] = useState("");
|
|
39
|
+
const [errors, setErrors] = useState<{ email?: string; password?: string }>({});
|
|
40
|
+
|
|
41
|
+
const validate = (): boolean => {
|
|
42
|
+
const newErrors: { email?: string; password?: string } = {};
|
|
43
|
+
|
|
44
|
+
if (!email) {
|
|
45
|
+
newErrors.email = "Email is required";
|
|
46
|
+
} else if (!/\S+@\S+\.\S+/.test(email)) {
|
|
47
|
+
newErrors.email = "Please enter a valid email";
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (!password) {
|
|
51
|
+
newErrors.password = "Password is required";
|
|
52
|
+
} else if (password.length < 6) {
|
|
53
|
+
newErrors.password = "Password must be at least 6 characters";
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
setErrors(newErrors);
|
|
57
|
+
return Object.keys(newErrors).length === 0;
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
const handleLogin = () => {
|
|
61
|
+
if (validate()) {
|
|
62
|
+
onLogin?.(email, password);
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
return (
|
|
67
|
+
<KeyboardAvoidingView
|
|
68
|
+
behavior={Platform.OS === "ios" ? "padding" : "height"}
|
|
69
|
+
className="flex-1 bg-background"
|
|
70
|
+
>
|
|
71
|
+
<ScrollView
|
|
72
|
+
contentContainerClassName="flex-grow justify-center p-6"
|
|
73
|
+
keyboardShouldPersistTaps="handled"
|
|
74
|
+
>
|
|
75
|
+
<Card className="w-full max-w-md self-center">
|
|
76
|
+
<CardHeader className="items-center">
|
|
77
|
+
<View className="mb-4 h-16 w-16 items-center justify-center rounded-2xl bg-primary">
|
|
78
|
+
<Text className="text-2xl font-bold text-primary-foreground">T</Text>
|
|
79
|
+
</View>
|
|
80
|
+
<CardTitle>Welcome back</CardTitle>
|
|
81
|
+
<CardDescription>Sign in to your account to continue</CardDescription>
|
|
82
|
+
</CardHeader>
|
|
83
|
+
|
|
84
|
+
<CardContent className="gap-4">
|
|
85
|
+
{/* Social Login Buttons */}
|
|
86
|
+
<View className="gap-3">
|
|
87
|
+
<Button
|
|
88
|
+
variant="outline"
|
|
89
|
+
fullWidth
|
|
90
|
+
onPress={() => onSocialLogin?.("google")}
|
|
91
|
+
>
|
|
92
|
+
<View className="flex-row items-center gap-2">
|
|
93
|
+
<Text>🔵</Text>
|
|
94
|
+
<Text className="font-semibold text-foreground">Continue with Google</Text>
|
|
95
|
+
</View>
|
|
96
|
+
</Button>
|
|
97
|
+
|
|
98
|
+
<Button
|
|
99
|
+
variant="outline"
|
|
100
|
+
fullWidth
|
|
101
|
+
onPress={() => onSocialLogin?.("apple")}
|
|
102
|
+
>
|
|
103
|
+
<View className="flex-row items-center gap-2">
|
|
104
|
+
<Text></Text>
|
|
105
|
+
<Text className="font-semibold text-foreground">Continue with Apple</Text>
|
|
106
|
+
</View>
|
|
107
|
+
</Button>
|
|
108
|
+
</View>
|
|
109
|
+
|
|
110
|
+
{/* Divider */}
|
|
111
|
+
<View className="flex-row items-center gap-4 my-2">
|
|
112
|
+
<Separator className="flex-1" />
|
|
113
|
+
<Text variant="muted">or</Text>
|
|
114
|
+
<Separator className="flex-1" />
|
|
115
|
+
</View>
|
|
116
|
+
|
|
117
|
+
{/* Email/Password Form */}
|
|
118
|
+
<View className="gap-4">
|
|
119
|
+
<Input
|
|
120
|
+
label="Email"
|
|
121
|
+
placeholder="you@example.com"
|
|
122
|
+
value={email}
|
|
123
|
+
onChangeText={setEmail}
|
|
124
|
+
error={errors.email}
|
|
125
|
+
keyboardType="email-address"
|
|
126
|
+
autoCapitalize="none"
|
|
127
|
+
autoComplete="email"
|
|
128
|
+
/>
|
|
129
|
+
|
|
130
|
+
<View>
|
|
131
|
+
<Input
|
|
132
|
+
label="Password"
|
|
133
|
+
placeholder="Enter your password"
|
|
134
|
+
value={password}
|
|
135
|
+
onChangeText={setPassword}
|
|
136
|
+
error={errors.password}
|
|
137
|
+
secureTextEntry
|
|
138
|
+
autoComplete="password"
|
|
139
|
+
/>
|
|
140
|
+
<Pressable onPress={onForgotPassword} className="mt-2 self-end">
|
|
141
|
+
<Text className="text-sm text-primary">Forgot password?</Text>
|
|
142
|
+
</Pressable>
|
|
143
|
+
</View>
|
|
144
|
+
</View>
|
|
145
|
+
</CardContent>
|
|
146
|
+
|
|
147
|
+
<CardFooter className="flex-col gap-4">
|
|
148
|
+
<Button fullWidth loading={loading} onPress={handleLogin}>
|
|
149
|
+
Sign In
|
|
150
|
+
</Button>
|
|
151
|
+
|
|
152
|
+
<View className="flex-row items-center justify-center gap-1">
|
|
153
|
+
<Text variant="muted">Don't have an account?</Text>
|
|
154
|
+
<Pressable onPress={onRegister}>
|
|
155
|
+
<Text className="font-semibold text-primary">Sign up</Text>
|
|
156
|
+
</Pressable>
|
|
157
|
+
</View>
|
|
158
|
+
</CardFooter>
|
|
159
|
+
</Card>
|
|
160
|
+
</ScrollView>
|
|
161
|
+
</KeyboardAvoidingView>
|
|
162
|
+
);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
export default LoginScreen;
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Register Screen
|
|
3
|
+
*
|
|
4
|
+
* Account registration with email/password.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import React, { useState } from "react";
|
|
8
|
+
import { View, ScrollView, Pressable, KeyboardAvoidingView, Platform } from "react-native";
|
|
9
|
+
import {
|
|
10
|
+
Text,
|
|
11
|
+
Button,
|
|
12
|
+
Input,
|
|
13
|
+
Card,
|
|
14
|
+
CardHeader,
|
|
15
|
+
CardTitle,
|
|
16
|
+
CardDescription,
|
|
17
|
+
CardContent,
|
|
18
|
+
CardFooter,
|
|
19
|
+
Separator,
|
|
20
|
+
} from "@/components/ui";
|
|
21
|
+
|
|
22
|
+
interface RegisterScreenProps {
|
|
23
|
+
onRegister?: (name: string, email: string, password: string) => void;
|
|
24
|
+
onLogin?: () => void;
|
|
25
|
+
onSocialLogin?: (provider: "google" | "apple" | "github") => void;
|
|
26
|
+
loading?: boolean;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function RegisterScreen({
|
|
30
|
+
onRegister,
|
|
31
|
+
onLogin,
|
|
32
|
+
onSocialLogin,
|
|
33
|
+
loading = false,
|
|
34
|
+
}: RegisterScreenProps): React.JSX.Element {
|
|
35
|
+
const [name, setName] = useState("");
|
|
36
|
+
const [email, setEmail] = useState("");
|
|
37
|
+
const [password, setPassword] = useState("");
|
|
38
|
+
const [confirmPassword, setConfirmPassword] = useState("");
|
|
39
|
+
const [errors, setErrors] = useState<{
|
|
40
|
+
name?: string;
|
|
41
|
+
email?: string;
|
|
42
|
+
password?: string;
|
|
43
|
+
confirmPassword?: string;
|
|
44
|
+
}>({});
|
|
45
|
+
|
|
46
|
+
const validate = (): boolean => {
|
|
47
|
+
const newErrors: typeof errors = {};
|
|
48
|
+
|
|
49
|
+
if (!name.trim()) {
|
|
50
|
+
newErrors.name = "Name is required";
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (!email) {
|
|
54
|
+
newErrors.email = "Email is required";
|
|
55
|
+
} else if (!/\S+@\S+\.\S+/.test(email)) {
|
|
56
|
+
newErrors.email = "Please enter a valid email";
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (!password) {
|
|
60
|
+
newErrors.password = "Password is required";
|
|
61
|
+
} else if (password.length < 8) {
|
|
62
|
+
newErrors.password = "Password must be at least 8 characters";
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (!confirmPassword) {
|
|
66
|
+
newErrors.confirmPassword = "Please confirm your password";
|
|
67
|
+
} else if (password !== confirmPassword) {
|
|
68
|
+
newErrors.confirmPassword = "Passwords do not match";
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
setErrors(newErrors);
|
|
72
|
+
return Object.keys(newErrors).length === 0;
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
const handleRegister = () => {
|
|
76
|
+
if (validate()) {
|
|
77
|
+
onRegister?.(name, email, password);
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
return (
|
|
82
|
+
<KeyboardAvoidingView
|
|
83
|
+
behavior={Platform.OS === "ios" ? "padding" : "height"}
|
|
84
|
+
className="flex-1 bg-background"
|
|
85
|
+
>
|
|
86
|
+
<ScrollView
|
|
87
|
+
contentContainerClassName="flex-grow justify-center p-6"
|
|
88
|
+
keyboardShouldPersistTaps="handled"
|
|
89
|
+
>
|
|
90
|
+
<Card className="w-full max-w-md self-center">
|
|
91
|
+
<CardHeader className="items-center">
|
|
92
|
+
<View className="mb-4 h-16 w-16 items-center justify-center rounded-2xl bg-primary">
|
|
93
|
+
<Text className="text-2xl font-bold text-primary-foreground">T</Text>
|
|
94
|
+
</View>
|
|
95
|
+
<CardTitle>Create an account</CardTitle>
|
|
96
|
+
<CardDescription>Enter your details to get started</CardDescription>
|
|
97
|
+
</CardHeader>
|
|
98
|
+
|
|
99
|
+
<CardContent className="gap-4">
|
|
100
|
+
{/* Social Login Buttons */}
|
|
101
|
+
<View className="gap-3">
|
|
102
|
+
<Button
|
|
103
|
+
variant="outline"
|
|
104
|
+
fullWidth
|
|
105
|
+
onPress={() => onSocialLogin?.("google")}
|
|
106
|
+
>
|
|
107
|
+
<View className="flex-row items-center gap-2">
|
|
108
|
+
<Text>🔵</Text>
|
|
109
|
+
<Text className="font-semibold text-foreground">Continue with Google</Text>
|
|
110
|
+
</View>
|
|
111
|
+
</Button>
|
|
112
|
+
|
|
113
|
+
<Button
|
|
114
|
+
variant="outline"
|
|
115
|
+
fullWidth
|
|
116
|
+
onPress={() => onSocialLogin?.("apple")}
|
|
117
|
+
>
|
|
118
|
+
<View className="flex-row items-center gap-2">
|
|
119
|
+
<Text></Text>
|
|
120
|
+
<Text className="font-semibold text-foreground">Continue with Apple</Text>
|
|
121
|
+
</View>
|
|
122
|
+
</Button>
|
|
123
|
+
</View>
|
|
124
|
+
|
|
125
|
+
{/* Divider */}
|
|
126
|
+
<View className="flex-row items-center gap-4 my-2">
|
|
127
|
+
<Separator className="flex-1" />
|
|
128
|
+
<Text variant="muted">or</Text>
|
|
129
|
+
<Separator className="flex-1" />
|
|
130
|
+
</View>
|
|
131
|
+
|
|
132
|
+
{/* Registration Form */}
|
|
133
|
+
<View className="gap-4">
|
|
134
|
+
<Input
|
|
135
|
+
label="Full Name"
|
|
136
|
+
placeholder="John Doe"
|
|
137
|
+
value={name}
|
|
138
|
+
onChangeText={setName}
|
|
139
|
+
error={errors.name}
|
|
140
|
+
autoCapitalize="words"
|
|
141
|
+
autoComplete="name"
|
|
142
|
+
/>
|
|
143
|
+
|
|
144
|
+
<Input
|
|
145
|
+
label="Email"
|
|
146
|
+
placeholder="you@example.com"
|
|
147
|
+
value={email}
|
|
148
|
+
onChangeText={setEmail}
|
|
149
|
+
error={errors.email}
|
|
150
|
+
keyboardType="email-address"
|
|
151
|
+
autoCapitalize="none"
|
|
152
|
+
autoComplete="email"
|
|
153
|
+
/>
|
|
154
|
+
|
|
155
|
+
<Input
|
|
156
|
+
label="Password"
|
|
157
|
+
placeholder="Create a password"
|
|
158
|
+
value={password}
|
|
159
|
+
onChangeText={setPassword}
|
|
160
|
+
error={errors.password}
|
|
161
|
+
hint="Must be at least 8 characters"
|
|
162
|
+
secureTextEntry
|
|
163
|
+
autoComplete="new-password"
|
|
164
|
+
/>
|
|
165
|
+
|
|
166
|
+
<Input
|
|
167
|
+
label="Confirm Password"
|
|
168
|
+
placeholder="Confirm your password"
|
|
169
|
+
value={confirmPassword}
|
|
170
|
+
onChangeText={setConfirmPassword}
|
|
171
|
+
error={errors.confirmPassword}
|
|
172
|
+
secureTextEntry
|
|
173
|
+
autoComplete="new-password"
|
|
174
|
+
/>
|
|
175
|
+
</View>
|
|
176
|
+
|
|
177
|
+
{/* Terms */}
|
|
178
|
+
<Text variant="muted" className="text-center">
|
|
179
|
+
By creating an account, you agree to our{" "}
|
|
180
|
+
<Text className="text-primary">Terms of Service</Text> and{" "}
|
|
181
|
+
<Text className="text-primary">Privacy Policy</Text>
|
|
182
|
+
</Text>
|
|
183
|
+
</CardContent>
|
|
184
|
+
|
|
185
|
+
<CardFooter className="flex-col gap-4">
|
|
186
|
+
<Button fullWidth loading={loading} onPress={handleRegister}>
|
|
187
|
+
Create Account
|
|
188
|
+
</Button>
|
|
189
|
+
|
|
190
|
+
<View className="flex-row items-center justify-center gap-1">
|
|
191
|
+
<Text variant="muted">Already have an account?</Text>
|
|
192
|
+
<Pressable onPress={onLogin}>
|
|
193
|
+
<Text className="font-semibold text-primary">Sign in</Text>
|
|
194
|
+
</Pressable>
|
|
195
|
+
</View>
|
|
196
|
+
</CardFooter>
|
|
197
|
+
</Card>
|
|
198
|
+
</ScrollView>
|
|
199
|
+
</KeyboardAvoidingView>
|
|
200
|
+
);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
export default RegisterScreen;
|