create-stackr 0.2.0 → 0.3.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/README.md +10 -0
- package/dist/prompts/features.d.ts +1 -1
- package/dist/prompts/features.d.ts.map +1 -1
- package/dist/prompts/features.js +34 -25
- package/dist/prompts/features.js.map +1 -1
- package/dist/prompts/index.js +33 -6
- package/dist/prompts/index.js.map +1 -1
- package/dist/prompts/preset.d.ts.map +1 -1
- package/dist/prompts/preset.js +69 -34
- package/dist/prompts/preset.js.map +1 -1
- package/dist/utils/template.js +1 -1
- package/dist/utils/template.js.map +1 -1
- package/dist/utils/validation.d.ts.map +1 -1
- package/dist/utils/validation.js +43 -1
- package/dist/utils/validation.js.map +1 -1
- package/package.json +1 -1
- package/templates/base/backend/controllers/rest-api/routes/auth.ts.ejs +33 -1
- package/templates/base/backend/controllers/rest-api/routes/oauth-web.ts.ejs +6 -0
- package/templates/base/backend/domain/user/repository.drizzle.ts +28 -1
- package/templates/base/backend/domain/user/repository.prisma.ts +25 -0
- package/templates/base/backend/drizzle/{schema.drizzle.ts → schema.drizzle.ts.ejs} +25 -0
- package/templates/base/backend/lib/auth.drizzle.ts.ejs +27 -18
- package/templates/base/backend/lib/auth.prisma.ts.ejs +24 -18
- package/templates/base/backend/package.json.ejs +29 -23
- package/templates/base/backend/prisma/schema.prisma.ejs +20 -0
- package/templates/base/mobile/app/+not-found.tsx +1 -1
- package/templates/base/mobile/app/_layout.tsx.ejs +95 -10
- package/templates/base/mobile/package.json.ejs +21 -13
- package/templates/base/mobile/src/components/ui/Button.tsx +5 -3
- package/templates/base/mobile/src/components/ui/Card.tsx +1 -1
- package/templates/base/mobile/src/components/ui/Input.tsx +1 -1
- package/templates/base/mobile/src/components/ui/Skeleton.tsx +1 -1
- package/templates/base/mobile/src/components/ui/Toast.tsx +106 -0
- package/templates/base/mobile/src/components/ui/{IconSymbol.tsx → icon-symbol.tsx} +7 -0
- package/templates/base/mobile/src/components/ui/{LoadingSpinner.tsx → loading-spinner.tsx} +1 -1
- package/templates/base/mobile/src/components/ui/{OnboardingLayout.tsx → onboarding-layout.tsx} +2 -2
- package/templates/base/mobile/src/components/ui/{PaywallLayout.tsx → paywall-layout.tsx} +3 -3
- package/templates/base/mobile/src/constants/Theme.ts +3 -3
- package/templates/base/mobile/src/context/{ThemeContext.tsx → theme-context.tsx} +2 -2
- package/templates/base/mobile/src/lib/auth-client.ts.ejs +18 -0
- package/templates/base/mobile/src/services/{sdkInitializer.ts.ejs → sdk-initializer.ts.ejs} +4 -4
- package/templates/base/web/.prettierignore +6 -0
- package/templates/base/web/.prettierrc +8 -0
- package/templates/base/web/eslint.config.mjs +31 -7
- package/templates/base/web/next.config.ts +50 -1
- package/templates/base/web/package.json.ejs +14 -2
- package/templates/base/web/src/app/globals.css +1 -1
- package/templates/base/web/src/app/layout.tsx.ejs +2 -0
- package/templates/base/web/src/components/auth/protected-route.tsx.ejs +32 -5
- package/templates/base/web/src/components/providers/device-session-setup.tsx.ejs +1 -1
- package/templates/base/web/src/hooks/use-device-session.ts.ejs +2 -2
- package/templates/base/web/src/lib/auth/actions.ts.ejs +438 -15
- package/templates/base/web/src/lib/auth/config.ts.ejs +13 -0
- package/templates/base/web/src/lib/auth/cookies.ts.ejs +61 -0
- package/templates/base/web/src/lib/device/actions.ts.ejs +2 -10
- package/templates/base/web/src/lib/device/types.ts +37 -0
- package/templates/base/web/src/proxy.ts.ejs +12 -2
- package/templates/base/web/src/store/{deviceSession.store.ts.ejs → device-session-store.ts.ejs} +1 -1
- package/templates/features/mobile/auth/app/(auth)/{_layout.tsx → _layout.tsx.ejs} +7 -0
- package/templates/features/mobile/auth/app/(auth)/{login.tsx → login.tsx.ejs} +9 -8
- package/templates/features/mobile/auth/app/(auth)/{register.tsx → register.tsx.ejs} +9 -8
- package/templates/features/mobile/auth/app/(auth)/two-factor-expired.tsx.ejs +88 -0
- package/templates/features/mobile/auth/app/(auth)/two-factor.tsx.ejs +259 -0
- package/templates/features/mobile/auth/app/(public)/_layout.tsx +9 -0
- package/templates/features/mobile/{tabs/app/(tabs)/index.tsx → auth/app/(public)/index.tsx.ejs} +76 -257
- package/templates/features/mobile/auth/app/(tabs)/settings/_layout.tsx.ejs +20 -0
- package/templates/features/mobile/auth/app/(tabs)/settings/index.tsx.ejs +137 -0
- package/templates/features/mobile/auth/app/(tabs)/settings/security/_layout.tsx.ejs +35 -0
- package/templates/features/mobile/auth/app/(tabs)/settings/security/index.tsx.ejs +147 -0
- package/templates/features/mobile/auth/app/(tabs)/settings/security/set-password.tsx.ejs +140 -0
- package/templates/features/mobile/auth/app/(tabs)/settings/security/two-factor-manage.tsx.ejs +366 -0
- package/templates/features/mobile/auth/app/(tabs)/settings/security/two-factor-setup.tsx.ejs +317 -0
- package/templates/features/mobile/auth/app/account.tsx.ejs +331 -0
- package/templates/features/mobile/auth/app/verify-email.tsx.ejs +315 -0
- package/templates/features/mobile/auth/components/auth/{LoginForm.tsx.ejs → login-form.tsx.ejs} +15 -3
- package/templates/features/mobile/auth/components/auth/protected-screen.tsx.ejs +56 -0
- package/templates/features/mobile/auth/components/auth/{RegisterForm.tsx.ejs → register-form.tsx.ejs} +5 -3
- package/templates/features/mobile/auth/hooks/{useAuth.ts.ejs → auth.ts.ejs} +255 -1
- package/templates/features/mobile/auth/hooks/two-factor-timeout.ts.ejs +56 -0
- package/templates/features/mobile/auth/services/{deviceSession.ts → device-session.ts} +2 -10
- package/templates/features/mobile/auth/store/{deviceSession.store.ts → device-session-store.ts} +14 -5
- package/templates/features/mobile/auth/types/device-session.ts +37 -0
- package/templates/features/mobile/onboarding/app/(onboarding)/page-1.tsx.ejs +3 -1
- package/templates/features/mobile/onboarding/app/(onboarding)/page-2.tsx.ejs +3 -1
- package/templates/features/mobile/onboarding/app/(onboarding)/page-3.tsx.ejs +6 -1
- package/templates/features/mobile/paywall/app/paywall.tsx +4 -4
- package/templates/features/mobile/tabs/app/(tabs)/_layout.tsx +0 -6
- package/templates/features/mobile/tabs/app/(tabs)/_layout.tsx.ejs +60 -0
- package/templates/features/mobile/tabs/app/(tabs)/index.tsx.ejs +264 -0
- package/templates/features/web/auth/app/(app)/layout.tsx.ejs +8 -22
- package/templates/features/web/auth/app/(auth)/login/page.tsx.ejs +19 -3
- package/templates/features/web/auth/app/(auth)/login/two-factor/page.tsx.ejs +24 -0
- package/templates/features/web/auth/app/(auth)/verify-email/page.tsx.ejs +117 -152
- package/templates/features/web/auth/app/{(app) → (protected)}/dashboard/dashboard-client.tsx.ejs +41 -1
- package/templates/features/web/auth/app/{(app) → (protected)}/dashboard/page.tsx.ejs +3 -1
- package/templates/features/web/auth/app/(protected)/layout.tsx.ejs +67 -0
- package/templates/features/web/auth/app/(protected)/settings/security/page.tsx.ejs +19 -0
- package/templates/features/web/auth/app/(protected)/settings/security/security-settings.tsx.ejs +73 -0
- package/templates/features/web/auth/app/{(app) → (protected)}/settings/sessions/page.tsx.ejs +2 -6
- package/templates/features/web/auth/app/auth/callback/route.ts.ejs +5 -1
- package/templates/features/web/auth/app/auth/session-expired/route.ts.ejs +39 -0
- package/templates/features/web/auth/app/auth/two-factor-expired/route.ts.ejs +22 -0
- package/templates/features/web/auth/components/auth/login-form.tsx.ejs +18 -0
- package/templates/features/web/auth/components/auth/two-factor-verify.tsx.ejs +173 -0
- package/templates/features/web/auth/components/settings/set-password-form.tsx.ejs +123 -0
- package/templates/features/web/auth/components/settings/two-factor-manage.tsx.ejs +253 -0
- package/templates/features/web/auth/components/settings/two-factor-setup.tsx.ejs +249 -0
- package/templates/features/web/auth/components/ui/checkbox.tsx +32 -0
- package/templates/features/web/auth/components/ui/dialog.tsx +143 -0
- package/templates/features/web/auth/components/ui/input-otp.tsx +71 -0
- package/templates/integrations/mobile/adjust/store/{adjust.store.ts → adjust-store.ts} +3 -3
- package/templates/integrations/mobile/att/store/{att.store.ts → att-store.ts} +2 -2
- package/templates/integrations/mobile/revenuecat/store/{revenuecat.store.ts → revenuecat-store.ts} +1 -1
- package/templates/integrations/mobile/scate/store/{scate.store.ts → scate-store.ts} +1 -1
- package/templates/base/mobile/src/components/ui/index.ts +0 -6
- package/templates/base/mobile/src/store/index.ts.ejs +0 -18
- package/templates/base/web/src/lib/auth/index.ts.ejs +0 -40
- package/templates/features/mobile/auth/components/auth/index.ts +0 -2
- package/templates/features/mobile/auth/hooks/index.ts.ejs +0 -1
- /package/templates/base/mobile/src/services/{errorService.ts → error-service.ts} +0 -0
- /package/templates/base/mobile/src/store/{ui.store.ts → ui-store.ts} +0 -0
- /package/templates/features/web/auth/app/{(app) → (protected)}/settings/sessions/sessions-client.tsx.ejs +0 -0
- /package/templates/integrations/mobile/adjust/services/{adjustService.ts.ejs → adjust-service.ts.ejs} +0 -0
- /package/templates/integrations/mobile/att/services/{attService.ts → att-service.ts} +0 -0
- /package/templates/integrations/mobile/att/services/{trackingPermissions.ts → tracking-permissions.ts} +0 -0
- /package/templates/integrations/mobile/revenuecat/services/{revenuecatService.ts.ejs → revenuecat-service.ts.ejs} +0 -0
- /package/templates/integrations/mobile/scate/services/{scateService.ts.ejs → scate-service.ts.ejs} +0 -0
package/templates/features/mobile/{tabs/app/(tabs)/index.tsx → auth/app/(public)/index.tsx.ejs}
RENAMED
|
@@ -1,69 +1,34 @@
|
|
|
1
|
-
import React, { useState
|
|
1
|
+
import React, { useMemo<% if (features.sessionManagement) { %>, useState<% } %> } from 'react';
|
|
2
2
|
import {
|
|
3
3
|
View,
|
|
4
4
|
Text,
|
|
5
5
|
StyleSheet,
|
|
6
6
|
SafeAreaView,
|
|
7
7
|
ScrollView,
|
|
8
|
-
Modal,
|
|
8
|
+
<% if (features.sessionManagement) { %> Modal,
|
|
9
9
|
Pressable,
|
|
10
|
-
Alert,
|
|
11
10
|
KeyboardAvoidingView,
|
|
12
11
|
Platform,
|
|
13
|
-
} from 'react-native';
|
|
12
|
+
<% } %>} from 'react-native';
|
|
14
13
|
import { router } from 'expo-router';
|
|
15
|
-
import Constants from 'expo-constants';
|
|
16
|
-
import { LinearGradient } from 'expo-linear-gradient';
|
|
17
|
-
import {
|
|
18
|
-
import {
|
|
19
|
-
|
|
20
|
-
import { IconSymbol } from '
|
|
21
|
-
import {
|
|
22
|
-
import {
|
|
23
|
-
|
|
24
|
-
|
|
14
|
+
<% if (features.sessionManagement) { %>import Constants from 'expo-constants';
|
|
15
|
+
<% } %>import { LinearGradient } from 'expo-linear-gradient';
|
|
16
|
+
import { Button } from '@/components/ui/button';
|
|
17
|
+
<% if (features.sessionManagement) { %>import { Input } from '@/components/ui/input';
|
|
18
|
+
<% } %>
|
|
19
|
+
import { IconSymbol } from '@/components/ui/icon-symbol';
|
|
20
|
+
import { useAppTheme, AppTheme } from '@/context/theme-context';
|
|
21
|
+
<% if (features.sessionManagement) { %>import { useSessionActions } from '@/store/device-session-store';
|
|
22
|
+
<% } %>
|
|
23
|
+
|
|
24
|
+
export default function LandingScreen() {
|
|
25
25
|
const theme = useAppTheme();
|
|
26
26
|
const styles = useMemo(() => createStyles(theme), [theme]);
|
|
27
|
-
|
|
28
|
-
const { user, signOut, deleteAccount, isLoading, isAuthenticated } = useAuth();
|
|
29
|
-
const { deleteSession, initializeSession } = useSessionActions();
|
|
30
|
-
const [showDeleteModal, setShowDeleteModal] = useState(false);
|
|
31
|
-
const [deleteConfirmText, setDeleteConfirmText] = useState('');
|
|
32
|
-
const [isDeleting, setIsDeleting] = useState(false);
|
|
27
|
+
<% if (features.sessionManagement) { %> const { deleteSession, initializeSession } = useSessionActions();
|
|
33
28
|
const [showDeleteSessionModal, setShowDeleteSessionModal] = useState(false);
|
|
34
29
|
const [deleteSessionConfirmText, setDeleteSessionConfirmText] = useState('');
|
|
35
30
|
const [isDeletingSession, setIsDeletingSession] = useState(false);
|
|
36
31
|
|
|
37
|
-
// Gradient colors based on theme
|
|
38
|
-
const gradientColors = theme.mode === 'dark'
|
|
39
|
-
? [theme.colors.card, 'transparent'] as const
|
|
40
|
-
: ['#ffffff', '#f8fafc'] as const; // Subtle fade for light mode
|
|
41
|
-
|
|
42
|
-
const handleLogout = async () => {
|
|
43
|
-
const result = await signOut();
|
|
44
|
-
if (!result.success) {
|
|
45
|
-
Alert.alert('Error', 'Failed to sign out. Please try again.');
|
|
46
|
-
}
|
|
47
|
-
};
|
|
48
|
-
|
|
49
|
-
const handleDeleteAccount = async () => {
|
|
50
|
-
if (deleteConfirmText !== 'DELETE') {
|
|
51
|
-
return;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
setIsDeleting(true);
|
|
55
|
-
const result = await deleteAccount();
|
|
56
|
-
setIsDeleting(false);
|
|
57
|
-
|
|
58
|
-
if (result.success) {
|
|
59
|
-
await signOut();
|
|
60
|
-
setShowDeleteModal(false);
|
|
61
|
-
setDeleteConfirmText('');
|
|
62
|
-
} else {
|
|
63
|
-
Alert.alert('Error', result.error || 'Failed to delete account. Please try again.');
|
|
64
|
-
}
|
|
65
|
-
};
|
|
66
|
-
|
|
67
32
|
const handleDeleteSession = async () => {
|
|
68
33
|
if (deleteSessionConfirmText !== 'DELETE') {
|
|
69
34
|
return;
|
|
@@ -76,10 +41,10 @@ export default function HomeScreen() {
|
|
|
76
41
|
setShowDeleteSessionModal(false);
|
|
77
42
|
setDeleteSessionConfirmText('');
|
|
78
43
|
|
|
79
|
-
if (
|
|
80
|
-
await initializeSession();
|
|
81
|
-
} else {
|
|
44
|
+
if (onboardingEnabled) {
|
|
82
45
|
router.replace('/(onboarding)/page-1');
|
|
46
|
+
} else {
|
|
47
|
+
await initializeSession();
|
|
83
48
|
}
|
|
84
49
|
} catch (error) {
|
|
85
50
|
console.error('Failed to delete/recreate session:', error);
|
|
@@ -89,8 +54,12 @@ export default function HomeScreen() {
|
|
|
89
54
|
setIsDeletingSession(false);
|
|
90
55
|
}
|
|
91
56
|
};
|
|
57
|
+
<% } %>
|
|
92
58
|
|
|
93
|
-
|
|
59
|
+
// Gradient colors based on theme
|
|
60
|
+
const gradientColors = theme.mode === 'dark'
|
|
61
|
+
? [theme.colors.card, 'transparent'] as const
|
|
62
|
+
: ['#ffffff', '#f8fafc'] as const;
|
|
94
63
|
|
|
95
64
|
const features = [
|
|
96
65
|
'Authentication system',
|
|
@@ -104,32 +73,24 @@ export default function HomeScreen() {
|
|
|
104
73
|
<SafeAreaView style={styles.container}>
|
|
105
74
|
<ScrollView contentContainerStyle={styles.scrollContent}>
|
|
106
75
|
<View style={styles.content}>
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
<
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
end={{ x: 0, y: 1 }}
|
|
115
|
-
>
|
|
116
|
-
<Text style={styles.greeting}>
|
|
117
|
-
Hello, {formatDisplayName(user.name)}!
|
|
118
|
-
</Text>
|
|
119
|
-
<Text style={styles.email}>{user.email}</Text>
|
|
120
|
-
<Text style={styles.userId}>User ID: {user.id}</Text>
|
|
121
|
-
</LinearGradient>
|
|
122
|
-
)}
|
|
76
|
+
{/* Hero Section */}
|
|
77
|
+
<View style={styles.heroSection}>
|
|
78
|
+
<Text style={styles.title}>Welcome to Your App!</Text>
|
|
79
|
+
<Text style={styles.subtitle}>
|
|
80
|
+
Get started by creating an account or signing in
|
|
81
|
+
</Text>
|
|
82
|
+
</View>
|
|
123
83
|
|
|
84
|
+
{/* Features Section */}
|
|
124
85
|
<LinearGradient
|
|
125
86
|
colors={gradientColors}
|
|
126
87
|
style={styles.section}
|
|
127
88
|
start={{ x: 0, y: 0 }}
|
|
128
89
|
end={{ x: 0, y: 1 }}
|
|
129
90
|
>
|
|
130
|
-
<Text style={styles.sectionTitle}>
|
|
91
|
+
<Text style={styles.sectionTitle}>What's Included</Text>
|
|
131
92
|
<Text style={styles.sectionText}>
|
|
132
|
-
This
|
|
93
|
+
This app comes with everything you need to get started quickly.
|
|
133
94
|
</Text>
|
|
134
95
|
|
|
135
96
|
<View style={styles.featureList}>
|
|
@@ -148,124 +109,34 @@ export default function HomeScreen() {
|
|
|
148
109
|
</View>
|
|
149
110
|
</LinearGradient>
|
|
150
111
|
|
|
112
|
+
{/* CTA Section */}
|
|
151
113
|
<View style={styles.actions}>
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
variant="primary"
|
|
175
|
-
onPress={() => router.push('/(auth)/login')}
|
|
176
|
-
style={styles.signInButton}
|
|
177
|
-
/>
|
|
178
|
-
|
|
179
|
-
<Button
|
|
180
|
-
title="Create Account"
|
|
181
|
-
variant="outline"
|
|
182
|
-
onPress={() => router.push('/(auth)/register')}
|
|
183
|
-
style={styles.createAccountButton}
|
|
184
|
-
/>
|
|
185
|
-
|
|
186
|
-
<Button
|
|
187
|
-
title="Delete Session"
|
|
188
|
-
variant="outline"
|
|
189
|
-
onPress={() => setShowDeleteSessionModal(true)}
|
|
190
|
-
style={styles.deleteSessionButton}
|
|
191
|
-
textStyle={styles.deleteSessionButtonText}
|
|
192
|
-
/>
|
|
193
|
-
</>
|
|
194
|
-
)}
|
|
114
|
+
<Button
|
|
115
|
+
title="Sign In"
|
|
116
|
+
variant="primary"
|
|
117
|
+
onPress={() => router.push('/(auth)/login')}
|
|
118
|
+
style={styles.signInButton}
|
|
119
|
+
/>
|
|
120
|
+
|
|
121
|
+
<Button
|
|
122
|
+
title="Create Account"
|
|
123
|
+
variant="outline"
|
|
124
|
+
onPress={() => router.push('/(auth)/register')}
|
|
125
|
+
style={styles.createAccountButton}
|
|
126
|
+
/>
|
|
127
|
+
<% if (features.sessionManagement) { %>
|
|
128
|
+
<Button
|
|
129
|
+
title="Delete Session"
|
|
130
|
+
variant="outline"
|
|
131
|
+
onPress={() => setShowDeleteSessionModal(true)}
|
|
132
|
+
style={styles.deleteSessionButton}
|
|
133
|
+
textStyle={styles.deleteSessionButtonText}
|
|
134
|
+
/>
|
|
135
|
+
<% } %>
|
|
195
136
|
</View>
|
|
196
137
|
</View>
|
|
197
138
|
</ScrollView>
|
|
198
|
-
|
|
199
|
-
{/* Delete Account Confirmation Modal */}
|
|
200
|
-
<Modal
|
|
201
|
-
visible={showDeleteModal}
|
|
202
|
-
transparent
|
|
203
|
-
animationType="fade"
|
|
204
|
-
onRequestClose={() => setShowDeleteModal(false)}
|
|
205
|
-
statusBarTranslucent
|
|
206
|
-
>
|
|
207
|
-
<KeyboardAvoidingView
|
|
208
|
-
behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
|
|
209
|
-
style={styles.keyboardAvoidingModal}
|
|
210
|
-
>
|
|
211
|
-
<Pressable
|
|
212
|
-
style={styles.modalOverlay}
|
|
213
|
-
onPress={() => setShowDeleteModal(false)}
|
|
214
|
-
>
|
|
215
|
-
<Pressable
|
|
216
|
-
style={styles.modalContent}
|
|
217
|
-
onPress={(e) => e.stopPropagation()}
|
|
218
|
-
>
|
|
219
|
-
<Text style={styles.modalTitle}>Delete Account</Text>
|
|
220
|
-
|
|
221
|
-
<View style={styles.warningContainer}>
|
|
222
|
-
<Text style={styles.warningTitle}>Warning</Text>
|
|
223
|
-
<Text style={styles.warningText}>
|
|
224
|
-
This action is permanent and cannot be undone. All your account data will be deleted immediately.
|
|
225
|
-
</Text>
|
|
226
|
-
</View>
|
|
227
|
-
|
|
228
|
-
<View style={styles.gdprNotice}>
|
|
229
|
-
<Text style={styles.gdprTitle}>Third-Party Data Notice</Text>
|
|
230
|
-
<Text style={styles.gdprText}>
|
|
231
|
-
Please note that data stored by third-party services (RevenueCat, Adjust, Scate) may persist on their servers.
|
|
232
|
-
</Text>
|
|
233
|
-
</View>
|
|
234
|
-
|
|
235
|
-
<Input
|
|
236
|
-
label="Type DELETE to confirm"
|
|
237
|
-
value={deleteConfirmText}
|
|
238
|
-
onChangeText={setDeleteConfirmText}
|
|
239
|
-
placeholder="DELETE"
|
|
240
|
-
autoCapitalize="characters"
|
|
241
|
-
containerStyle={styles.confirmInput}
|
|
242
|
-
/>
|
|
243
|
-
|
|
244
|
-
<View style={styles.modalActions}>
|
|
245
|
-
<Button
|
|
246
|
-
title="Cancel"
|
|
247
|
-
variant="outline"
|
|
248
|
-
onPress={() => {
|
|
249
|
-
setShowDeleteModal(false);
|
|
250
|
-
setDeleteConfirmText('');
|
|
251
|
-
}}
|
|
252
|
-
style={styles.modalButton}
|
|
253
|
-
/>
|
|
254
|
-
|
|
255
|
-
<Button
|
|
256
|
-
title={isDeleting ? "Deleting..." : "Delete"}
|
|
257
|
-
variant="primary"
|
|
258
|
-
onPress={handleDeleteAccount}
|
|
259
|
-
disabled={!isDeleteButtonEnabled}
|
|
260
|
-
loading={isDeleting}
|
|
261
|
-
style={StyleSheet.flatten([styles.modalButton, styles.deleteConfirmButton])}
|
|
262
|
-
/>
|
|
263
|
-
</View>
|
|
264
|
-
</Pressable>
|
|
265
|
-
</Pressable>
|
|
266
|
-
</KeyboardAvoidingView>
|
|
267
|
-
</Modal>
|
|
268
|
-
|
|
139
|
+
<% if (features.sessionManagement) { %>
|
|
269
140
|
{/* Delete Session Confirmation Modal */}
|
|
270
141
|
<Modal
|
|
271
142
|
visible={showDeleteSessionModal}
|
|
@@ -328,6 +199,7 @@ export default function HomeScreen() {
|
|
|
328
199
|
</Pressable>
|
|
329
200
|
</KeyboardAvoidingView>
|
|
330
201
|
</Modal>
|
|
202
|
+
<% } %>
|
|
331
203
|
</SafeAreaView>
|
|
332
204
|
);
|
|
333
205
|
}
|
|
@@ -347,45 +219,28 @@ const createStyles = (theme: AppTheme) => StyleSheet.create({
|
|
|
347
219
|
padding: theme.spacing[5],
|
|
348
220
|
},
|
|
349
221
|
|
|
222
|
+
heroSection: {
|
|
223
|
+
paddingVertical: theme.spacing[8],
|
|
224
|
+
alignItems: 'center',
|
|
225
|
+
},
|
|
226
|
+
|
|
350
227
|
title: {
|
|
351
228
|
fontSize: theme.typography.fontSize['3xl'],
|
|
352
229
|
fontWeight: '800',
|
|
353
230
|
color: theme.colors.text,
|
|
354
231
|
textAlign: 'center',
|
|
355
|
-
marginBottom: theme.spacing[
|
|
232
|
+
marginBottom: theme.spacing[3],
|
|
356
233
|
letterSpacing: -1,
|
|
357
234
|
},
|
|
358
235
|
|
|
359
|
-
|
|
360
|
-
// Removed solid background
|
|
361
|
-
borderRadius: theme.borderRadius.xl,
|
|
362
|
-
padding: theme.spacing[5],
|
|
363
|
-
marginBottom: theme.spacing[6],
|
|
364
|
-
alignItems: 'center',
|
|
365
|
-
borderWidth: 1,
|
|
366
|
-
borderColor: theme.colors.borderLight,
|
|
367
|
-
},
|
|
368
|
-
|
|
369
|
-
greeting: {
|
|
236
|
+
subtitle: {
|
|
370
237
|
fontSize: theme.typography.fontSize.lg,
|
|
371
|
-
fontWeight: '700',
|
|
372
|
-
color: theme.colors.text,
|
|
373
|
-
marginBottom: theme.spacing[2],
|
|
374
|
-
},
|
|
375
|
-
|
|
376
|
-
email: {
|
|
377
|
-
fontSize: theme.typography.fontSize.base,
|
|
378
238
|
color: theme.colors.textSecondary,
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
userId: {
|
|
383
|
-
fontSize: theme.typography.fontSize.sm,
|
|
384
|
-
color: theme.colors.textMuted,
|
|
239
|
+
textAlign: 'center',
|
|
240
|
+
lineHeight: 28,
|
|
385
241
|
},
|
|
386
242
|
|
|
387
243
|
section: {
|
|
388
|
-
// Removed solid background
|
|
389
244
|
borderRadius: theme.borderRadius.xl,
|
|
390
245
|
padding: theme.spacing[6],
|
|
391
246
|
marginBottom: theme.spacing[6],
|
|
@@ -437,29 +292,15 @@ const createStyles = (theme: AppTheme) => StyleSheet.create({
|
|
|
437
292
|
paddingTop: theme.spacing[5],
|
|
438
293
|
},
|
|
439
294
|
|
|
440
|
-
logoutButton: {
|
|
441
|
-
marginTop: theme.spacing[4],
|
|
442
|
-
},
|
|
443
|
-
|
|
444
|
-
deleteButton: {
|
|
445
|
-
marginTop: theme.spacing[3],
|
|
446
|
-
borderColor: theme.colors.error,
|
|
447
|
-
},
|
|
448
|
-
|
|
449
|
-
deleteButtonText: {
|
|
450
|
-
color: theme.colors.error,
|
|
451
|
-
},
|
|
452
|
-
|
|
453
295
|
signInButton: {
|
|
454
|
-
|
|
296
|
+
marginBottom: theme.spacing[3],
|
|
455
297
|
},
|
|
456
298
|
|
|
457
299
|
createAccountButton: {
|
|
458
|
-
|
|
459
|
-
},
|
|
460
|
-
|
|
300
|
+
<% if (features.sessionManagement) { %> marginBottom: theme.spacing[3],
|
|
301
|
+
<% } %> },
|
|
302
|
+
<% if (features.sessionManagement) { %>
|
|
461
303
|
deleteSessionButton: {
|
|
462
|
-
marginTop: theme.spacing[3],
|
|
463
304
|
borderColor: theme.colors.error,
|
|
464
305
|
},
|
|
465
306
|
|
|
@@ -501,13 +342,13 @@ const createStyles = (theme: AppTheme) => StyleSheet.create({
|
|
|
501
342
|
},
|
|
502
343
|
|
|
503
344
|
warningContainer: {
|
|
504
|
-
backgroundColor: 'transparent',
|
|
345
|
+
backgroundColor: 'transparent',
|
|
505
346
|
borderRadius: theme.borderRadius.lg,
|
|
506
347
|
padding: theme.spacing[4],
|
|
507
348
|
marginBottom: theme.spacing[5],
|
|
508
349
|
borderWidth: 1,
|
|
509
|
-
borderColor: theme.colors.warning,
|
|
510
|
-
borderStyle: 'dashed',
|
|
350
|
+
borderColor: theme.colors.warning,
|
|
351
|
+
borderStyle: 'dashed',
|
|
511
352
|
},
|
|
512
353
|
|
|
513
354
|
warningTitle: {
|
|
@@ -523,28 +364,6 @@ const createStyles = (theme: AppTheme) => StyleSheet.create({
|
|
|
523
364
|
lineHeight: 20,
|
|
524
365
|
},
|
|
525
366
|
|
|
526
|
-
gdprNotice: {
|
|
527
|
-
backgroundColor: 'transparent',
|
|
528
|
-
borderRadius: theme.borderRadius.lg,
|
|
529
|
-
padding: theme.spacing[4],
|
|
530
|
-
marginBottom: theme.spacing[5],
|
|
531
|
-
borderWidth: 1,
|
|
532
|
-
borderColor: theme.colors.info,
|
|
533
|
-
},
|
|
534
|
-
|
|
535
|
-
gdprTitle: {
|
|
536
|
-
fontSize: theme.typography.fontSize.sm,
|
|
537
|
-
fontWeight: '600',
|
|
538
|
-
color: theme.colors.info,
|
|
539
|
-
marginBottom: theme.spacing[2],
|
|
540
|
-
},
|
|
541
|
-
|
|
542
|
-
gdprText: {
|
|
543
|
-
fontSize: theme.typography.fontSize.xs,
|
|
544
|
-
color: theme.colors.textMuted,
|
|
545
|
-
lineHeight: 18,
|
|
546
|
-
},
|
|
547
|
-
|
|
548
367
|
confirmInput: {
|
|
549
368
|
marginBottom: theme.spacing[6],
|
|
550
369
|
},
|
|
@@ -560,6 +379,6 @@ const createStyles = (theme: AppTheme) => StyleSheet.create({
|
|
|
560
379
|
|
|
561
380
|
deleteConfirmButton: {
|
|
562
381
|
backgroundColor: theme.colors.error,
|
|
563
|
-
borderWidth: 0,
|
|
382
|
+
borderWidth: 0,
|
|
564
383
|
},
|
|
565
|
-
});
|
|
384
|
+
<% } %>});
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { Stack } from 'expo-router';
|
|
2
|
+
import { useAppTheme } from '@/context/theme-context';
|
|
3
|
+
|
|
4
|
+
export default function SettingsLayout() {
|
|
5
|
+
const theme = useAppTheme();
|
|
6
|
+
|
|
7
|
+
return (
|
|
8
|
+
<Stack
|
|
9
|
+
screenOptions={{
|
|
10
|
+
headerStyle: { backgroundColor: theme.colors.background },
|
|
11
|
+
headerTintColor: theme.colors.text,
|
|
12
|
+
headerShadowVisible: false,
|
|
13
|
+
headerBackTitleVisible: false,
|
|
14
|
+
}}
|
|
15
|
+
>
|
|
16
|
+
<Stack.Screen name="index" options={{ headerShown: false, title: 'Settings' }} />
|
|
17
|
+
<Stack.Screen name="security" options={{ headerShown: false }} />
|
|
18
|
+
</Stack>
|
|
19
|
+
);
|
|
20
|
+
}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import React, { useMemo } from 'react';
|
|
2
|
+
import { View, Text, StyleSheet, ScrollView, TouchableOpacity, SafeAreaView } from 'react-native';
|
|
3
|
+
import { useRouter } from 'expo-router';
|
|
4
|
+
import { useAuth } from '../../../src/hooks/auth';
|
|
5
|
+
import { IconSymbol } from '../../../src/components/ui/icon-symbol';
|
|
6
|
+
import { useAppTheme, AppTheme } from '@/context/theme-context';
|
|
7
|
+
|
|
8
|
+
export default function SettingsScreen() {
|
|
9
|
+
const router = useRouter();
|
|
10
|
+
const theme = useAppTheme();
|
|
11
|
+
const styles = useMemo(() => createStyles(theme), [theme]);
|
|
12
|
+
const { user } = useAuth();
|
|
13
|
+
|
|
14
|
+
return (
|
|
15
|
+
<SafeAreaView style={styles.container}>
|
|
16
|
+
<ScrollView contentContainerStyle={styles.scrollContent}>
|
|
17
|
+
<View style={styles.header}>
|
|
18
|
+
<Text style={styles.headerTitle}>Settings</Text>
|
|
19
|
+
<Text style={styles.headerSubtitle}>{user?.email}</Text>
|
|
20
|
+
</View>
|
|
21
|
+
|
|
22
|
+
<View style={styles.section}>
|
|
23
|
+
<Text style={styles.sectionTitle}>Account</Text>
|
|
24
|
+
|
|
25
|
+
<TouchableOpacity
|
|
26
|
+
style={styles.settingsItem}
|
|
27
|
+
onPress={() => router.push('/account')}
|
|
28
|
+
activeOpacity={0.7}
|
|
29
|
+
>
|
|
30
|
+
<View style={styles.settingsItemLeft}>
|
|
31
|
+
<View style={[styles.iconContainer, { backgroundColor: theme.mode === 'dark' ? 'rgba(255,255,255,0.08)' : 'rgba(0,0,0,0.04)' }]}>
|
|
32
|
+
<IconSymbol name="person.fill" size={20} color={theme.colors.text} />
|
|
33
|
+
</View>
|
|
34
|
+
<View style={styles.settingsItemContent}>
|
|
35
|
+
<Text style={styles.settingsItemText}>Account Details</Text>
|
|
36
|
+
<Text style={styles.settingsItemSubtext}>Manage your profile information</Text>
|
|
37
|
+
</View>
|
|
38
|
+
</View>
|
|
39
|
+
<IconSymbol name="chevron.right" size={18} color={theme.colors.textMuted} />
|
|
40
|
+
</TouchableOpacity>
|
|
41
|
+
|
|
42
|
+
<TouchableOpacity
|
|
43
|
+
style={styles.settingsItem}
|
|
44
|
+
onPress={() => router.push('/(tabs)/settings/security')}
|
|
45
|
+
activeOpacity={0.7}
|
|
46
|
+
>
|
|
47
|
+
<View style={styles.settingsItemLeft}>
|
|
48
|
+
<View style={[styles.iconContainer, { backgroundColor: theme.mode === 'dark' ? 'rgba(255,255,255,0.08)' : 'rgba(0,0,0,0.04)' }]}>
|
|
49
|
+
<IconSymbol name="lock.shield.fill" size={20} color={theme.colors.text} />
|
|
50
|
+
</View>
|
|
51
|
+
<View style={styles.settingsItemContent}>
|
|
52
|
+
<Text style={styles.settingsItemText}>Security</Text>
|
|
53
|
+
<Text style={styles.settingsItemSubtext}>Two-factor authentication & more</Text>
|
|
54
|
+
</View>
|
|
55
|
+
</View>
|
|
56
|
+
<IconSymbol name="chevron.right" size={18} color={theme.colors.textMuted} />
|
|
57
|
+
</TouchableOpacity>
|
|
58
|
+
</View>
|
|
59
|
+
</ScrollView>
|
|
60
|
+
</SafeAreaView>
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const createStyles = (theme: AppTheme) => StyleSheet.create({
|
|
65
|
+
container: {
|
|
66
|
+
flex: 1,
|
|
67
|
+
backgroundColor: theme.colors.background,
|
|
68
|
+
},
|
|
69
|
+
scrollContent: {
|
|
70
|
+
paddingBottom: theme.spacing[8],
|
|
71
|
+
},
|
|
72
|
+
header: {
|
|
73
|
+
padding: theme.spacing[6],
|
|
74
|
+
paddingTop: theme.spacing[4],
|
|
75
|
+
paddingBottom: theme.spacing[6],
|
|
76
|
+
},
|
|
77
|
+
headerTitle: {
|
|
78
|
+
fontSize: 32,
|
|
79
|
+
fontWeight: '700',
|
|
80
|
+
color: theme.colors.text,
|
|
81
|
+
letterSpacing: -0.5,
|
|
82
|
+
},
|
|
83
|
+
headerSubtitle: {
|
|
84
|
+
fontSize: theme.typography.fontSize.sm,
|
|
85
|
+
color: theme.colors.textSecondary,
|
|
86
|
+
marginTop: theme.spacing[1],
|
|
87
|
+
},
|
|
88
|
+
section: {
|
|
89
|
+
paddingHorizontal: theme.spacing[5],
|
|
90
|
+
marginBottom: theme.spacing[6],
|
|
91
|
+
},
|
|
92
|
+
sectionTitle: {
|
|
93
|
+
fontSize: 13,
|
|
94
|
+
fontWeight: '600',
|
|
95
|
+
color: theme.colors.textSecondary,
|
|
96
|
+
textTransform: 'uppercase',
|
|
97
|
+
letterSpacing: 0.5,
|
|
98
|
+
marginBottom: theme.spacing[3],
|
|
99
|
+
marginLeft: theme.spacing[1],
|
|
100
|
+
},
|
|
101
|
+
settingsItem: {
|
|
102
|
+
flexDirection: 'row',
|
|
103
|
+
alignItems: 'center',
|
|
104
|
+
justifyContent: 'space-between',
|
|
105
|
+
padding: theme.spacing[4],
|
|
106
|
+
backgroundColor: theme.colors.backgroundSecondary,
|
|
107
|
+
borderRadius: 16,
|
|
108
|
+
marginBottom: theme.spacing[3],
|
|
109
|
+
...theme.shadows.small,
|
|
110
|
+
},
|
|
111
|
+
settingsItemLeft: {
|
|
112
|
+
flexDirection: 'row',
|
|
113
|
+
alignItems: 'center',
|
|
114
|
+
flex: 1,
|
|
115
|
+
},
|
|
116
|
+
iconContainer: {
|
|
117
|
+
width: 40,
|
|
118
|
+
height: 40,
|
|
119
|
+
borderRadius: 10,
|
|
120
|
+
justifyContent: 'center',
|
|
121
|
+
alignItems: 'center',
|
|
122
|
+
},
|
|
123
|
+
settingsItemContent: {
|
|
124
|
+
marginLeft: theme.spacing[3],
|
|
125
|
+
flex: 1,
|
|
126
|
+
},
|
|
127
|
+
settingsItemText: {
|
|
128
|
+
fontSize: theme.typography.fontSize.base,
|
|
129
|
+
fontWeight: '500',
|
|
130
|
+
color: theme.colors.text,
|
|
131
|
+
},
|
|
132
|
+
settingsItemSubtext: {
|
|
133
|
+
fontSize: theme.typography.fontSize.xs,
|
|
134
|
+
color: theme.colors.textSecondary,
|
|
135
|
+
marginTop: 2,
|
|
136
|
+
},
|
|
137
|
+
});
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { Stack, useRouter } from 'expo-router';
|
|
2
|
+
import { TouchableOpacity } from 'react-native';
|
|
3
|
+
import { useAppTheme } from '@/context/theme-context';
|
|
4
|
+
import { IconSymbol } from '@/components/ui/icon-symbol';
|
|
5
|
+
|
|
6
|
+
export default function SecurityLayout() {
|
|
7
|
+
const theme = useAppTheme();
|
|
8
|
+
const router = useRouter();
|
|
9
|
+
|
|
10
|
+
const BackButton = () => (
|
|
11
|
+
<TouchableOpacity
|
|
12
|
+
onPress={() => router.back()}
|
|
13
|
+
style={{ marginRight: 16 }}
|
|
14
|
+
>
|
|
15
|
+
<IconSymbol name="chevron.left" size={24} color={theme.colors.text} />
|
|
16
|
+
</TouchableOpacity>
|
|
17
|
+
);
|
|
18
|
+
|
|
19
|
+
return (
|
|
20
|
+
<Stack
|
|
21
|
+
screenOptions={{
|
|
22
|
+
headerStyle: { backgroundColor: theme.colors.background },
|
|
23
|
+
headerTintColor: theme.colors.text,
|
|
24
|
+
headerShadowVisible: false,
|
|
25
|
+
headerBackTitleVisible: false,
|
|
26
|
+
headerLeft: () => <BackButton />,
|
|
27
|
+
}}
|
|
28
|
+
>
|
|
29
|
+
<Stack.Screen name="index" options={{ title: 'Security' }} />
|
|
30
|
+
<Stack.Screen name="two-factor-setup" options={{ title: 'Enable 2FA' }} />
|
|
31
|
+
<Stack.Screen name="two-factor-manage" options={{ title: 'Manage 2FA' }} />
|
|
32
|
+
<Stack.Screen name="set-password" options={{ title: 'Set Password' }} />
|
|
33
|
+
</Stack>
|
|
34
|
+
);
|
|
35
|
+
}
|