@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,204 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Home Screen
|
|
3
|
+
*
|
|
4
|
+
* Main dashboard/home screen with content feed.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import React from "react";
|
|
8
|
+
import { View, ScrollView, Pressable, RefreshControl } from "react-native";
|
|
9
|
+
import {
|
|
10
|
+
Text,
|
|
11
|
+
Avatar,
|
|
12
|
+
Card,
|
|
13
|
+
CardHeader,
|
|
14
|
+
CardContent,
|
|
15
|
+
CardFooter,
|
|
16
|
+
Badge,
|
|
17
|
+
Skeleton,
|
|
18
|
+
SkeletonCircle,
|
|
19
|
+
} from "@/components/ui";
|
|
20
|
+
|
|
21
|
+
interface Post {
|
|
22
|
+
id: string;
|
|
23
|
+
author: {
|
|
24
|
+
name: string;
|
|
25
|
+
avatar?: string;
|
|
26
|
+
isVerified?: boolean;
|
|
27
|
+
};
|
|
28
|
+
content: string;
|
|
29
|
+
timestamp: string;
|
|
30
|
+
likes: number;
|
|
31
|
+
comments: number;
|
|
32
|
+
isLiked?: boolean;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
interface HomeScreenProps {
|
|
36
|
+
posts?: Post[];
|
|
37
|
+
loading?: boolean;
|
|
38
|
+
refreshing?: boolean;
|
|
39
|
+
onRefresh?: () => void;
|
|
40
|
+
onPostPress?: (post: Post) => void;
|
|
41
|
+
onProfilePress?: () => void;
|
|
42
|
+
onNotificationsPress?: () => void;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const mockPosts: Post[] = [
|
|
46
|
+
{
|
|
47
|
+
id: "1",
|
|
48
|
+
author: { name: "Jane Smith", isVerified: true },
|
|
49
|
+
content:
|
|
50
|
+
"Just shipped a new feature using Uniwind! The Tailwind CSS integration for React Native is a game-changer. Build UIs so much faster now!",
|
|
51
|
+
timestamp: "2h ago",
|
|
52
|
+
likes: 42,
|
|
53
|
+
comments: 8,
|
|
54
|
+
isLiked: true,
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
id: "2",
|
|
58
|
+
author: { name: "Alex Johnson" },
|
|
59
|
+
content:
|
|
60
|
+
"Working on something exciting with Teardown CLI. The developer experience is incredible - fast builds, great templates, and now with Uniwind support!",
|
|
61
|
+
timestamp: "4h ago",
|
|
62
|
+
likes: 28,
|
|
63
|
+
comments: 5,
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
id: "3",
|
|
67
|
+
author: { name: "Sam Wilson", isVerified: true },
|
|
68
|
+
content:
|
|
69
|
+
"Pro tip: Use the prebuilt components from the template as a starting point. They're fully customizable and follow best practices.",
|
|
70
|
+
timestamp: "6h ago",
|
|
71
|
+
likes: 156,
|
|
72
|
+
comments: 23,
|
|
73
|
+
},
|
|
74
|
+
];
|
|
75
|
+
|
|
76
|
+
export function HomeScreen({
|
|
77
|
+
posts = mockPosts,
|
|
78
|
+
loading = false,
|
|
79
|
+
refreshing = false,
|
|
80
|
+
onRefresh,
|
|
81
|
+
onPostPress,
|
|
82
|
+
onProfilePress,
|
|
83
|
+
onNotificationsPress,
|
|
84
|
+
}: HomeScreenProps): React.JSX.Element {
|
|
85
|
+
return (
|
|
86
|
+
<View className="flex-1 bg-background">
|
|
87
|
+
{/* Header */}
|
|
88
|
+
<View className="flex-row items-center justify-between px-6 py-4 border-b border-border">
|
|
89
|
+
<Pressable onPress={onProfilePress}>
|
|
90
|
+
<Avatar fallback="U" size="sm" />
|
|
91
|
+
</Pressable>
|
|
92
|
+
<Text variant="h4">Home</Text>
|
|
93
|
+
<Pressable onPress={onNotificationsPress} className="relative">
|
|
94
|
+
<Text className="text-xl">🔔</Text>
|
|
95
|
+
<View className="absolute -top-1 -right-1 h-3 w-3 rounded-full bg-destructive" />
|
|
96
|
+
</Pressable>
|
|
97
|
+
</View>
|
|
98
|
+
|
|
99
|
+
{/* Content */}
|
|
100
|
+
<ScrollView
|
|
101
|
+
className="flex-1"
|
|
102
|
+
contentContainerClassName="p-4 gap-4"
|
|
103
|
+
refreshControl={
|
|
104
|
+
<RefreshControl refreshing={refreshing} onRefresh={onRefresh} />
|
|
105
|
+
}
|
|
106
|
+
>
|
|
107
|
+
{loading ? (
|
|
108
|
+
// Loading skeleton
|
|
109
|
+
<>
|
|
110
|
+
<PostSkeleton />
|
|
111
|
+
<PostSkeleton />
|
|
112
|
+
<PostSkeleton />
|
|
113
|
+
</>
|
|
114
|
+
) : (
|
|
115
|
+
// Posts
|
|
116
|
+
posts.map((post) => (
|
|
117
|
+
<PostCard
|
|
118
|
+
key={post.id}
|
|
119
|
+
post={post}
|
|
120
|
+
onPress={() => onPostPress?.(post)}
|
|
121
|
+
/>
|
|
122
|
+
))
|
|
123
|
+
)}
|
|
124
|
+
</ScrollView>
|
|
125
|
+
</View>
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function PostCard({
|
|
130
|
+
post,
|
|
131
|
+
onPress,
|
|
132
|
+
}: {
|
|
133
|
+
post: Post;
|
|
134
|
+
onPress?: () => void;
|
|
135
|
+
}): React.JSX.Element {
|
|
136
|
+
return (
|
|
137
|
+
<Pressable onPress={onPress}>
|
|
138
|
+
<Card>
|
|
139
|
+
<CardHeader className="flex-row items-center gap-3 pb-2">
|
|
140
|
+
<Avatar fallback={post.author.name} size="md" />
|
|
141
|
+
<View className="flex-1">
|
|
142
|
+
<View className="flex-row items-center gap-2">
|
|
143
|
+
<Text variant="large">{post.author.name}</Text>
|
|
144
|
+
{post.author.isVerified && (
|
|
145
|
+
<Badge variant="default" className="h-5">
|
|
146
|
+
<Text className="text-xs text-primary-foreground">✓</Text>
|
|
147
|
+
</Badge>
|
|
148
|
+
)}
|
|
149
|
+
</View>
|
|
150
|
+
<Text variant="muted">{post.timestamp}</Text>
|
|
151
|
+
</View>
|
|
152
|
+
</CardHeader>
|
|
153
|
+
|
|
154
|
+
<CardContent>
|
|
155
|
+
<Text>{post.content}</Text>
|
|
156
|
+
</CardContent>
|
|
157
|
+
|
|
158
|
+
<CardFooter className="gap-6 pt-2">
|
|
159
|
+
<Pressable className="flex-row items-center gap-2">
|
|
160
|
+
<Text className={post.isLiked ? "text-destructive" : ""}>
|
|
161
|
+
{post.isLiked ? "❤️" : "🤍"}
|
|
162
|
+
</Text>
|
|
163
|
+
<Text variant="muted">{post.likes}</Text>
|
|
164
|
+
</Pressable>
|
|
165
|
+
|
|
166
|
+
<Pressable className="flex-row items-center gap-2">
|
|
167
|
+
<Text>💬</Text>
|
|
168
|
+
<Text variant="muted">{post.comments}</Text>
|
|
169
|
+
</Pressable>
|
|
170
|
+
|
|
171
|
+
<Pressable className="flex-row items-center gap-2">
|
|
172
|
+
<Text>📤</Text>
|
|
173
|
+
</Pressable>
|
|
174
|
+
</CardFooter>
|
|
175
|
+
</Card>
|
|
176
|
+
</Pressable>
|
|
177
|
+
);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
function PostSkeleton(): React.JSX.Element {
|
|
181
|
+
return (
|
|
182
|
+
<Card>
|
|
183
|
+
<CardHeader className="flex-row items-center gap-3 pb-2">
|
|
184
|
+
<SkeletonCircle size={40} />
|
|
185
|
+
<View className="flex-1 gap-2">
|
|
186
|
+
<Skeleton className="h-4 w-32" />
|
|
187
|
+
<Skeleton className="h-3 w-20" />
|
|
188
|
+
</View>
|
|
189
|
+
</CardHeader>
|
|
190
|
+
<CardContent className="gap-2">
|
|
191
|
+
<Skeleton className="h-4 w-full" />
|
|
192
|
+
<Skeleton className="h-4 w-full" />
|
|
193
|
+
<Skeleton className="h-4 w-3/4" />
|
|
194
|
+
</CardContent>
|
|
195
|
+
<CardFooter className="gap-6 pt-2">
|
|
196
|
+
<Skeleton className="h-4 w-12" />
|
|
197
|
+
<Skeleton className="h-4 w-12" />
|
|
198
|
+
<Skeleton className="h-4 w-8" />
|
|
199
|
+
</CardFooter>
|
|
200
|
+
</Card>
|
|
201
|
+
);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
export default HomeScreen;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Screens Barrel Export
|
|
3
|
+
*
|
|
4
|
+
* Pre-built screens using Uniwind components.
|
|
5
|
+
* Use these as starting points for your app.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
// Onboarding
|
|
9
|
+
export { WelcomeScreen } from "./welcome";
|
|
10
|
+
|
|
11
|
+
// Authentication
|
|
12
|
+
export { LoginScreen, RegisterScreen } from "./auth";
|
|
13
|
+
|
|
14
|
+
// Main App
|
|
15
|
+
export { HomeScreen } from "./home";
|
|
16
|
+
export { ProfileScreen } from "./profile";
|
|
17
|
+
export { SettingsScreen } from "./settings";
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Profile Screen
|
|
3
|
+
*
|
|
4
|
+
* User profile display with edit capabilities.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import React from "react";
|
|
8
|
+
import { View, ScrollView, Pressable } from "react-native";
|
|
9
|
+
import {
|
|
10
|
+
Text,
|
|
11
|
+
Button,
|
|
12
|
+
Avatar,
|
|
13
|
+
Card,
|
|
14
|
+
CardContent,
|
|
15
|
+
Badge,
|
|
16
|
+
Separator,
|
|
17
|
+
} from "@/components/ui";
|
|
18
|
+
|
|
19
|
+
interface ProfileScreenProps {
|
|
20
|
+
user?: {
|
|
21
|
+
name: string;
|
|
22
|
+
email: string;
|
|
23
|
+
avatar?: string;
|
|
24
|
+
bio?: string;
|
|
25
|
+
location?: string;
|
|
26
|
+
joinedDate?: string;
|
|
27
|
+
isPro?: boolean;
|
|
28
|
+
};
|
|
29
|
+
stats?: {
|
|
30
|
+
posts: number;
|
|
31
|
+
followers: number;
|
|
32
|
+
following: number;
|
|
33
|
+
};
|
|
34
|
+
onEditProfile?: () => void;
|
|
35
|
+
onSettings?: () => void;
|
|
36
|
+
onLogout?: () => void;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function ProfileScreen({
|
|
40
|
+
user = {
|
|
41
|
+
name: "John Doe",
|
|
42
|
+
email: "john@example.com",
|
|
43
|
+
bio: "Building amazing apps with React Native and Tailwind CSS",
|
|
44
|
+
location: "San Francisco, CA",
|
|
45
|
+
joinedDate: "January 2024",
|
|
46
|
+
isPro: true,
|
|
47
|
+
},
|
|
48
|
+
stats = {
|
|
49
|
+
posts: 42,
|
|
50
|
+
followers: 1234,
|
|
51
|
+
following: 567,
|
|
52
|
+
},
|
|
53
|
+
onEditProfile,
|
|
54
|
+
onSettings,
|
|
55
|
+
onLogout,
|
|
56
|
+
}: ProfileScreenProps): React.JSX.Element {
|
|
57
|
+
return (
|
|
58
|
+
<ScrollView className="flex-1 bg-background">
|
|
59
|
+
{/* Header */}
|
|
60
|
+
<View className="bg-primary/5 pb-6 pt-4">
|
|
61
|
+
<View className="flex-row items-center justify-between px-6">
|
|
62
|
+
<Text variant="h3">Profile</Text>
|
|
63
|
+
<Pressable onPress={onSettings} className="p-2">
|
|
64
|
+
<Text className="text-xl">⚙️</Text>
|
|
65
|
+
</Pressable>
|
|
66
|
+
</View>
|
|
67
|
+
|
|
68
|
+
{/* Profile Info */}
|
|
69
|
+
<View className="items-center mt-6 px-6">
|
|
70
|
+
<Avatar
|
|
71
|
+
fallback={user.name}
|
|
72
|
+
size="xl"
|
|
73
|
+
className="border-4 border-background"
|
|
74
|
+
/>
|
|
75
|
+
<View className="items-center mt-4 gap-1">
|
|
76
|
+
<View className="flex-row items-center gap-2">
|
|
77
|
+
<Text variant="h3">{user.name}</Text>
|
|
78
|
+
{user.isPro && <Badge variant="default">PRO</Badge>}
|
|
79
|
+
</View>
|
|
80
|
+
<Text variant="muted">{user.email}</Text>
|
|
81
|
+
</View>
|
|
82
|
+
</View>
|
|
83
|
+
|
|
84
|
+
{/* Stats */}
|
|
85
|
+
<View className="flex-row justify-center gap-8 mt-6">
|
|
86
|
+
<StatItem label="Posts" value={stats.posts} />
|
|
87
|
+
<StatItem label="Followers" value={stats.followers} />
|
|
88
|
+
<StatItem label="Following" value={stats.following} />
|
|
89
|
+
</View>
|
|
90
|
+
|
|
91
|
+
{/* Actions */}
|
|
92
|
+
<View className="flex-row gap-3 mt-6 px-6">
|
|
93
|
+
<Button variant="outline" className="flex-1" onPress={onEditProfile}>
|
|
94
|
+
Edit Profile
|
|
95
|
+
</Button>
|
|
96
|
+
<Button variant="outline" size="icon">
|
|
97
|
+
<Text>📤</Text>
|
|
98
|
+
</Button>
|
|
99
|
+
</View>
|
|
100
|
+
</View>
|
|
101
|
+
|
|
102
|
+
{/* Content */}
|
|
103
|
+
<View className="p-6 gap-4">
|
|
104
|
+
{/* Bio Card */}
|
|
105
|
+
<Card>
|
|
106
|
+
<CardContent className="gap-4 py-4">
|
|
107
|
+
<View className="flex-row items-center gap-2">
|
|
108
|
+
<Text>📝</Text>
|
|
109
|
+
<Text variant="label">About</Text>
|
|
110
|
+
</View>
|
|
111
|
+
<Text>{user.bio}</Text>
|
|
112
|
+
</CardContent>
|
|
113
|
+
</Card>
|
|
114
|
+
|
|
115
|
+
{/* Details Card */}
|
|
116
|
+
<Card>
|
|
117
|
+
<CardContent className="gap-4 py-4">
|
|
118
|
+
<View className="flex-row items-center gap-2">
|
|
119
|
+
<Text>ℹ️</Text>
|
|
120
|
+
<Text variant="label">Details</Text>
|
|
121
|
+
</View>
|
|
122
|
+
|
|
123
|
+
<View className="gap-3">
|
|
124
|
+
<DetailRow icon="📍" label="Location" value={user.location || "Not set"} />
|
|
125
|
+
<Separator />
|
|
126
|
+
<DetailRow icon="📅" label="Joined" value={user.joinedDate || "Unknown"} />
|
|
127
|
+
</View>
|
|
128
|
+
</CardContent>
|
|
129
|
+
</Card>
|
|
130
|
+
|
|
131
|
+
{/* Quick Actions */}
|
|
132
|
+
<Card>
|
|
133
|
+
<CardContent className="gap-1 py-2">
|
|
134
|
+
<ActionRow icon="🔔" label="Notifications" onPress={() => {}} />
|
|
135
|
+
<Separator />
|
|
136
|
+
<ActionRow icon="🔒" label="Privacy & Security" onPress={() => {}} />
|
|
137
|
+
<Separator />
|
|
138
|
+
<ActionRow icon="❓" label="Help & Support" onPress={() => {}} />
|
|
139
|
+
</CardContent>
|
|
140
|
+
</Card>
|
|
141
|
+
|
|
142
|
+
{/* Logout */}
|
|
143
|
+
<Button variant="destructive" fullWidth onPress={onLogout}>
|
|
144
|
+
Sign Out
|
|
145
|
+
</Button>
|
|
146
|
+
</View>
|
|
147
|
+
</ScrollView>
|
|
148
|
+
);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function StatItem({ label, value }: { label: string; value: number }): React.JSX.Element {
|
|
152
|
+
const formatNumber = (num: number): string => {
|
|
153
|
+
if (num >= 1000) {
|
|
154
|
+
return `${(num / 1000).toFixed(1)}k`;
|
|
155
|
+
}
|
|
156
|
+
return num.toString();
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
return (
|
|
160
|
+
<View className="items-center">
|
|
161
|
+
<Text variant="h3">{formatNumber(value)}</Text>
|
|
162
|
+
<Text variant="muted">{label}</Text>
|
|
163
|
+
</View>
|
|
164
|
+
);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function DetailRow({
|
|
168
|
+
icon,
|
|
169
|
+
label,
|
|
170
|
+
value,
|
|
171
|
+
}: {
|
|
172
|
+
icon: string;
|
|
173
|
+
label: string;
|
|
174
|
+
value: string;
|
|
175
|
+
}): React.JSX.Element {
|
|
176
|
+
return (
|
|
177
|
+
<View className="flex-row items-center justify-between">
|
|
178
|
+
<View className="flex-row items-center gap-2">
|
|
179
|
+
<Text>{icon}</Text>
|
|
180
|
+
<Text variant="muted">{label}</Text>
|
|
181
|
+
</View>
|
|
182
|
+
<Text>{value}</Text>
|
|
183
|
+
</View>
|
|
184
|
+
);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
function ActionRow({
|
|
188
|
+
icon,
|
|
189
|
+
label,
|
|
190
|
+
onPress,
|
|
191
|
+
}: {
|
|
192
|
+
icon: string;
|
|
193
|
+
label: string;
|
|
194
|
+
onPress: () => void;
|
|
195
|
+
}): React.JSX.Element {
|
|
196
|
+
return (
|
|
197
|
+
<Pressable
|
|
198
|
+
className="flex-row items-center justify-between py-3 active:bg-muted rounded-lg px-2 -mx-2"
|
|
199
|
+
onPress={onPress}
|
|
200
|
+
>
|
|
201
|
+
<View className="flex-row items-center gap-3">
|
|
202
|
+
<Text className="text-lg">{icon}</Text>
|
|
203
|
+
<Text>{label}</Text>
|
|
204
|
+
</View>
|
|
205
|
+
<Text className="text-muted-foreground">›</Text>
|
|
206
|
+
</Pressable>
|
|
207
|
+
);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
export default ProfileScreen;
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Settings Screen
|
|
3
|
+
*
|
|
4
|
+
* App settings and preferences.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import React, { useState } from "react";
|
|
8
|
+
import { View, ScrollView, Switch, Pressable } from "react-native";
|
|
9
|
+
import { Text, Card, CardContent, Separator, Badge } from "@/components/ui";
|
|
10
|
+
|
|
11
|
+
interface SettingsScreenProps {
|
|
12
|
+
onBack?: () => void;
|
|
13
|
+
version?: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function SettingsScreen({
|
|
17
|
+
onBack,
|
|
18
|
+
version = "1.0.0",
|
|
19
|
+
}: SettingsScreenProps): React.JSX.Element {
|
|
20
|
+
const [notifications, setNotifications] = useState(true);
|
|
21
|
+
const [darkMode, setDarkMode] = useState(false);
|
|
22
|
+
const [haptics, setHaptics] = useState(true);
|
|
23
|
+
const [analytics, setAnalytics] = useState(true);
|
|
24
|
+
|
|
25
|
+
return (
|
|
26
|
+
<ScrollView className="flex-1 bg-background">
|
|
27
|
+
{/* Header */}
|
|
28
|
+
<View className="flex-row items-center gap-4 px-6 py-4 border-b border-border">
|
|
29
|
+
<Pressable onPress={onBack} className="p-2 -ml-2">
|
|
30
|
+
<Text className="text-xl">←</Text>
|
|
31
|
+
</Pressable>
|
|
32
|
+
<Text variant="h3">Settings</Text>
|
|
33
|
+
</View>
|
|
34
|
+
|
|
35
|
+
<View className="p-6 gap-6">
|
|
36
|
+
{/* Preferences */}
|
|
37
|
+
<SettingsSection title="Preferences">
|
|
38
|
+
<ToggleRow
|
|
39
|
+
icon="🔔"
|
|
40
|
+
label="Push Notifications"
|
|
41
|
+
description="Receive push notifications"
|
|
42
|
+
value={notifications}
|
|
43
|
+
onValueChange={setNotifications}
|
|
44
|
+
/>
|
|
45
|
+
<Separator />
|
|
46
|
+
<ToggleRow
|
|
47
|
+
icon="🌙"
|
|
48
|
+
label="Dark Mode"
|
|
49
|
+
description="Use dark color theme"
|
|
50
|
+
value={darkMode}
|
|
51
|
+
onValueChange={setDarkMode}
|
|
52
|
+
/>
|
|
53
|
+
<Separator />
|
|
54
|
+
<ToggleRow
|
|
55
|
+
icon="📳"
|
|
56
|
+
label="Haptic Feedback"
|
|
57
|
+
description="Enable haptic feedback"
|
|
58
|
+
value={haptics}
|
|
59
|
+
onValueChange={setHaptics}
|
|
60
|
+
/>
|
|
61
|
+
</SettingsSection>
|
|
62
|
+
|
|
63
|
+
{/* Account */}
|
|
64
|
+
<SettingsSection title="Account">
|
|
65
|
+
<LinkRow icon="👤" label="Edit Profile" onPress={() => {}} />
|
|
66
|
+
<Separator />
|
|
67
|
+
<LinkRow icon="🔐" label="Change Password" onPress={() => {}} />
|
|
68
|
+
<Separator />
|
|
69
|
+
<LinkRow icon="📧" label="Email Preferences" onPress={() => {}} />
|
|
70
|
+
<Separator />
|
|
71
|
+
<LinkRow
|
|
72
|
+
icon="💳"
|
|
73
|
+
label="Subscription"
|
|
74
|
+
badge="PRO"
|
|
75
|
+
onPress={() => {}}
|
|
76
|
+
/>
|
|
77
|
+
</SettingsSection>
|
|
78
|
+
|
|
79
|
+
{/* Privacy */}
|
|
80
|
+
<SettingsSection title="Privacy & Data">
|
|
81
|
+
<ToggleRow
|
|
82
|
+
icon="📊"
|
|
83
|
+
label="Analytics"
|
|
84
|
+
description="Help improve the app"
|
|
85
|
+
value={analytics}
|
|
86
|
+
onValueChange={setAnalytics}
|
|
87
|
+
/>
|
|
88
|
+
<Separator />
|
|
89
|
+
<LinkRow icon="🛡️" label="Privacy Policy" onPress={() => {}} />
|
|
90
|
+
<Separator />
|
|
91
|
+
<LinkRow icon="📋" label="Terms of Service" onPress={() => {}} />
|
|
92
|
+
<Separator />
|
|
93
|
+
<LinkRow
|
|
94
|
+
icon="🗑️"
|
|
95
|
+
label="Delete Account"
|
|
96
|
+
destructive
|
|
97
|
+
onPress={() => {}}
|
|
98
|
+
/>
|
|
99
|
+
</SettingsSection>
|
|
100
|
+
|
|
101
|
+
{/* Support */}
|
|
102
|
+
<SettingsSection title="Support">
|
|
103
|
+
<LinkRow icon="❓" label="Help Center" onPress={() => {}} />
|
|
104
|
+
<Separator />
|
|
105
|
+
<LinkRow icon="💬" label="Contact Support" onPress={() => {}} />
|
|
106
|
+
<Separator />
|
|
107
|
+
<LinkRow icon="⭐" label="Rate the App" onPress={() => {}} />
|
|
108
|
+
</SettingsSection>
|
|
109
|
+
|
|
110
|
+
{/* About */}
|
|
111
|
+
<SettingsSection title="About">
|
|
112
|
+
<View className="flex-row items-center justify-between py-3">
|
|
113
|
+
<View className="flex-row items-center gap-3">
|
|
114
|
+
<Text className="text-lg">📱</Text>
|
|
115
|
+
<Text>Version</Text>
|
|
116
|
+
</View>
|
|
117
|
+
<Text variant="muted">{version}</Text>
|
|
118
|
+
</View>
|
|
119
|
+
<Separator />
|
|
120
|
+
<LinkRow icon="📜" label="Licenses" onPress={() => {}} />
|
|
121
|
+
</SettingsSection>
|
|
122
|
+
|
|
123
|
+
{/* Footer */}
|
|
124
|
+
<View className="items-center py-8">
|
|
125
|
+
<Text variant="muted">Made with ❤️ using Teardown CLI</Text>
|
|
126
|
+
<Text variant="muted" className="mt-1">
|
|
127
|
+
Powered by Uniwind + Tailwind CSS
|
|
128
|
+
</Text>
|
|
129
|
+
</View>
|
|
130
|
+
</View>
|
|
131
|
+
</ScrollView>
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function SettingsSection({
|
|
136
|
+
title,
|
|
137
|
+
children,
|
|
138
|
+
}: {
|
|
139
|
+
title: string;
|
|
140
|
+
children: React.ReactNode;
|
|
141
|
+
}): React.JSX.Element {
|
|
142
|
+
return (
|
|
143
|
+
<View className="gap-2">
|
|
144
|
+
<Text variant="label" className="text-muted-foreground px-1">
|
|
145
|
+
{title}
|
|
146
|
+
</Text>
|
|
147
|
+
<Card>
|
|
148
|
+
<CardContent className="py-2">{children}</CardContent>
|
|
149
|
+
</Card>
|
|
150
|
+
</View>
|
|
151
|
+
);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function ToggleRow({
|
|
155
|
+
icon,
|
|
156
|
+
label,
|
|
157
|
+
description,
|
|
158
|
+
value,
|
|
159
|
+
onValueChange,
|
|
160
|
+
}: {
|
|
161
|
+
icon: string;
|
|
162
|
+
label: string;
|
|
163
|
+
description?: string;
|
|
164
|
+
value: boolean;
|
|
165
|
+
onValueChange: (value: boolean) => void;
|
|
166
|
+
}): React.JSX.Element {
|
|
167
|
+
return (
|
|
168
|
+
<View className="flex-row items-center justify-between py-3">
|
|
169
|
+
<View className="flex-row items-center gap-3 flex-1">
|
|
170
|
+
<Text className="text-lg">{icon}</Text>
|
|
171
|
+
<View className="flex-1">
|
|
172
|
+
<Text>{label}</Text>
|
|
173
|
+
{description && <Text variant="muted">{description}</Text>}
|
|
174
|
+
</View>
|
|
175
|
+
</View>
|
|
176
|
+
<Switch
|
|
177
|
+
value={value}
|
|
178
|
+
onValueChange={onValueChange}
|
|
179
|
+
trackColor={{ false: "#e2e8f0", true: "#3b82f6" }}
|
|
180
|
+
thumbColor="#ffffff"
|
|
181
|
+
/>
|
|
182
|
+
</View>
|
|
183
|
+
);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
function LinkRow({
|
|
187
|
+
icon,
|
|
188
|
+
label,
|
|
189
|
+
badge,
|
|
190
|
+
destructive,
|
|
191
|
+
onPress,
|
|
192
|
+
}: {
|
|
193
|
+
icon: string;
|
|
194
|
+
label: string;
|
|
195
|
+
badge?: string;
|
|
196
|
+
destructive?: boolean;
|
|
197
|
+
onPress: () => void;
|
|
198
|
+
}): React.JSX.Element {
|
|
199
|
+
return (
|
|
200
|
+
<Pressable
|
|
201
|
+
className="flex-row items-center justify-between py-3 active:bg-muted rounded-lg px-2 -mx-2"
|
|
202
|
+
onPress={onPress}
|
|
203
|
+
>
|
|
204
|
+
<View className="flex-row items-center gap-3">
|
|
205
|
+
<Text className="text-lg">{icon}</Text>
|
|
206
|
+
<Text className={destructive ? "text-destructive" : ""}>{label}</Text>
|
|
207
|
+
</View>
|
|
208
|
+
<View className="flex-row items-center gap-2">
|
|
209
|
+
{badge && <Badge variant="default">{badge}</Badge>}
|
|
210
|
+
<Text className="text-muted-foreground">›</Text>
|
|
211
|
+
</View>
|
|
212
|
+
</Pressable>
|
|
213
|
+
);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
export default SettingsScreen;
|