@mr.dj2u/library-registry 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.
Files changed (86) hide show
  1. package/README.md +3 -0
  2. package/assets/mds/env/convex.env.example +1 -0
  3. package/assets/mds/env/firebase.env.example +6 -0
  4. package/assets/mds/env/supabase.env.example +3 -0
  5. package/assets/mds/project/auth-base.md +17 -0
  6. package/assets/mds/project/auth-convex.md +33 -0
  7. package/assets/mds/project/auth-firebase.md +22 -0
  8. package/assets/mds/project/auth-supabase.md +46 -0
  9. package/assets/mds/src/app/(auth)/reset-password.tsx +5 -0
  10. package/assets/mds/src/app/(auth)/sign-in.tsx +5 -0
  11. package/assets/mds/src/app/(auth)/sign-up.tsx +5 -0
  12. package/assets/mds/src/app/legal/updates.tsx +1 -0
  13. package/assets/mds/src/app/onboarding/complete.tsx +2 -0
  14. package/assets/mds/src/app/onboarding/features.tsx +1 -0
  15. package/assets/mds/src/app/onboarding/legal.tsx +2 -0
  16. package/assets/mds/src/app/onboarding.tsx +1 -1
  17. package/assets/mds/src/app/settings.tsx +10 -1
  18. package/assets/mds/src/db/adapter.ts +195 -0
  19. package/assets/mds/src/db/firebase.ts +77 -0
  20. package/assets/mds/src/db/index.firebase.ts +19 -0
  21. package/assets/mds/src/db/index.supabase.ts +22 -0
  22. package/assets/mds/src/db/supabase.ts +337 -0
  23. package/assets/mds/src/features/auth/adapters/base-auth-adapter.tsx +82 -0
  24. package/assets/mds/src/features/auth/adapters/convex-auth-adapter.tsx +156 -0
  25. package/assets/mds/src/features/auth/adapters/firebase-auth-adapter.tsx +147 -0
  26. package/assets/mds/src/features/auth/adapters/supabase-auth-adapter.tsx +166 -0
  27. package/assets/mds/src/features/auth/auth-guard-logic.ts +19 -0
  28. package/assets/mds/src/features/auth/auth-guard.tsx +77 -0
  29. package/assets/mds/src/features/auth/auth-provider.tsx +60 -0
  30. package/assets/mds/src/features/auth/auth-screen.tsx +265 -0
  31. package/assets/mds/src/features/auth/auth-types.ts +45 -0
  32. package/assets/mds/src/features/exposition/data-screen.tsx +4 -2
  33. package/assets/mds/src/features/exposition/expo-sdk-56-screen.tsx +119 -76
  34. package/assets/mds/src/features/exposition/exposition-screen.tsx +10 -11
  35. package/assets/mds/src/features/exposition/stylist-screen.tsx +62 -11
  36. package/assets/mds/src/features/legal/legal-acceptance-adapter.ts +190 -0
  37. package/assets/mds/src/features/legal/legal-acceptance-config.ts +79 -0
  38. package/assets/mds/src/features/legal/legal-agreement-screen.tsx +9 -4
  39. package/assets/mds/src/features/legal/legal-document-modal.tsx +7 -3
  40. package/assets/mds/src/features/legal/legal-document-view.tsx +1 -0
  41. package/assets/mds/src/features/legal/legal-documents.ts +17 -3
  42. package/assets/mds/src/features/legal/legal-update-screen.tsx +297 -0
  43. package/assets/mds/src/features/legal/use-legal-acceptance.ts +1 -38
  44. package/assets/mds/src/features/onboarding/complete-screen.tsx +138 -0
  45. package/assets/mds/src/features/onboarding/features-screen.tsx +129 -0
  46. package/assets/mds/src/features/onboarding/legal-review-screen.tsx +242 -0
  47. package/assets/mds/src/features/onboarding/onboarding-config-with-legal.ts +104 -0
  48. package/assets/mds/src/features/onboarding/onboarding-config.ts +104 -0
  49. package/assets/mds/src/features/onboarding/onboarding-persistence-sync.tsx +1 -0
  50. package/assets/mds/src/features/onboarding/welcome-screen.tsx +130 -0
  51. package/assets/mds/src/features/onboarding-state/adapters/memory-onboarding-state-adapter.ts +15 -0
  52. package/assets/mds/src/features/onboarding-state/adapters/supabase-onboarding-state-adapter.ts +45 -0
  53. package/assets/mds/src/features/onboarding-state/adapters/zustand-onboarding-state-adapter.ts +16 -0
  54. package/assets/mds/src/features/onboarding-state/adapters/zustand-supabase-onboarding-state-adapter.ts +53 -0
  55. package/assets/mds/src/features/onboarding-state/onboarding-state-core.ts +108 -0
  56. package/assets/mds/src/features/onboarding-state/onboarding-state-memory.ts +106 -0
  57. package/assets/mds/src/features/onboarding-state/onboarding-state-supabase.ts +246 -0
  58. package/assets/mds/src/features/onboarding-state/onboarding-state-types.ts +64 -0
  59. package/assets/mds/src/features/onboarding-state/onboarding-state-zustand-supabase.ts +120 -0
  60. package/assets/mds/src/features/onboarding-state/onboarding-state-zustand.ts +71 -0
  61. package/assets/mds/src/features/onboarding-state/onboarding-state.ts +22 -0
  62. package/assets/mds/src/features/onboarding-state/onboarding-store.ts +39 -0
  63. package/assets/mds/src/features/settings/settings-screen-logic.ts +122 -0
  64. package/assets/mds/src/features/settings/settings-screen.tsx +274 -28
  65. package/assets/mds/src/services/convex.ts +40 -0
  66. package/assets/mds/src/services/firebase.ts +70 -0
  67. package/assets/mds/src/services/supabase.ts +50 -0
  68. package/assets/mds/src/theme/color-utils.ts +42 -0
  69. package/assets/mds/src/theme/provider.tsx +15 -5
  70. package/assets/mds/src/types/database.ts +13 -0
  71. package/assets/mds/supabase/migrations/0001_mds_auth_onboarding.sql +107 -0
  72. package/dist/catalog.d.ts.map +1 -1
  73. package/dist/catalog.js +444 -29
  74. package/dist/catalog.js.map +1 -1
  75. package/dist/types.d.ts +5 -1
  76. package/dist/types.d.ts.map +1 -1
  77. package/package.json +1 -1
  78. package/assets/mds/src/app/onboarding/account-setup.tsx +0 -1
  79. package/assets/mds/src/app/onboarding/agreement.tsx +0 -1
  80. package/assets/mds/src/app/onboarding/terms.tsx +0 -1
  81. package/assets/mds/src/features/onboarding/account-setup-screen.tsx +0 -54
  82. package/assets/mds/src/features/onboarding/agreement-screen.tsx +0 -6
  83. package/assets/mds/src/features/onboarding/components/legal-document-view.tsx +0 -103
  84. package/assets/mds/src/features/onboarding/legal-documents.ts +0 -80
  85. package/assets/mds/src/features/onboarding/onboarding-screen.tsx +0 -167
  86. package/assets/mds/src/features/onboarding/terms-screen.tsx +0 -6
@@ -0,0 +1,265 @@
1
+ import { Link, useRouter } from 'expo-router';
2
+ import { useMemo, useState } from 'react';
3
+ import { Platform, Pressable, ScrollView, StyleSheet, Text, TextInput, View } from 'react-native';
4
+
5
+ import { getReadableTextColor } from '../../theme/color-utils';
6
+ import { useAppTheme } from '../../theme/provider';
7
+ import { useAuth } from './auth-provider';
8
+
9
+ type AuthScreenMode = 'sign-in' | 'sign-up' | 'reset-password';
10
+
11
+ const screenCopy = {
12
+ 'sign-in': {
13
+ eyebrow: 'Welcome back',
14
+ title: 'Sign in',
15
+ action: 'Sign in',
16
+ },
17
+ 'sign-up': {
18
+ eyebrow: 'Create account',
19
+ title: 'Sign up',
20
+ action: 'Create account',
21
+ },
22
+ 'reset-password': {
23
+ eyebrow: 'Account recovery',
24
+ title: 'Reset password',
25
+ action: 'Send reset link',
26
+ },
27
+ } satisfies Record<AuthScreenMode, { eyebrow: string; title: string; action: string }>;
28
+
29
+ export function AuthScreen({ mode }: { mode: AuthScreenMode }) {
30
+ const theme = useAppTheme();
31
+ const auth = useAuth();
32
+ const router = useRouter();
33
+ const colors = theme.activeColors;
34
+ const primaryForeground = getReadableTextColor(colors.primary, theme.colors.light.text);
35
+ const copy = screenCopy[mode];
36
+ const [email, setEmail] = useState('');
37
+ const [password, setPassword] = useState('');
38
+ const [submitting, setSubmitting] = useState(false);
39
+ const [message, setMessage] = useState('');
40
+ const [error, setError] = useState('');
41
+ const showPassword = mode !== 'reset-password';
42
+ const canSubmit = useMemo(() => {
43
+ if (!email.trim()) return false;
44
+ return showPassword ? password.length >= 8 : true;
45
+ }, [email, password, showPassword]);
46
+
47
+ async function submit() {
48
+ if (!canSubmit || submitting) return;
49
+ setSubmitting(true);
50
+ setMessage('');
51
+ setError('');
52
+ const trimmedEmail = email.trim();
53
+ const result =
54
+ mode === 'sign-in'
55
+ ? await auth.signIn({ email: trimmedEmail, password })
56
+ : mode === 'sign-up'
57
+ ? await auth.signUp({ email: trimmedEmail, password })
58
+ : await auth.requestPasswordReset(trimmedEmail);
59
+
60
+ setSubmitting(false);
61
+ if (!result.ok) {
62
+ setError(result.error);
63
+ return;
64
+ }
65
+ setMessage(result.message ?? 'Done.');
66
+ if (mode !== 'reset-password') {
67
+ router.replace('/');
68
+ }
69
+ }
70
+
71
+ return (
72
+ <ScrollView
73
+ contentInsetAdjustmentBehavior="automatic"
74
+ contentContainerStyle={styles.content}
75
+ keyboardShouldPersistTaps="handled"
76
+ style={[styles.screen, { backgroundColor: colors.background }]}
77
+ >
78
+ <View style={styles.header}>
79
+ <Text style={[styles.eyebrow, { color: colors.primary }]}>{copy.eyebrow}</Text>
80
+ <Text
81
+ style={[
82
+ styles.title,
83
+ {
84
+ color: colors.text,
85
+ fontFamily: theme.typography.fontTitle,
86
+ fontSize: Math.max(28, theme.typography.headingSize + 6),
87
+ },
88
+ ]}
89
+ >
90
+ {copy.title}
91
+ </Text>
92
+ </View>
93
+
94
+ <View
95
+ style={[
96
+ styles.form,
97
+ {
98
+ backgroundColor: colors.surface,
99
+ borderColor: colors.primary,
100
+ borderRadius: theme.layout.radius,
101
+ },
102
+ ]}
103
+ >
104
+ <View style={styles.field}>
105
+ <Text style={[styles.label, { color: colors.text }]}>Email</Text>
106
+ <TextInput
107
+ autoCapitalize="none"
108
+ autoComplete="email"
109
+ keyboardType="email-address"
110
+ onChangeText={setEmail}
111
+ placeholder="you@example.com"
112
+ placeholderTextColor={colors.text}
113
+ style={[
114
+ styles.input,
115
+ {
116
+ backgroundColor: colors.background,
117
+ borderColor: colors.primary,
118
+ color: colors.text,
119
+ },
120
+ ]}
121
+ textContentType="emailAddress"
122
+ value={email}
123
+ />
124
+ </View>
125
+
126
+ {showPassword ? (
127
+ <View style={styles.field}>
128
+ <Text style={[styles.label, { color: colors.text }]}>Password</Text>
129
+ <TextInput
130
+ autoCapitalize="none"
131
+ autoComplete={mode === 'sign-in' ? 'current-password' : 'new-password'}
132
+ onChangeText={setPassword}
133
+ placeholder="At least 8 characters"
134
+ placeholderTextColor={colors.text}
135
+ secureTextEntry
136
+ style={[
137
+ styles.input,
138
+ {
139
+ backgroundColor: colors.background,
140
+ borderColor: colors.primary,
141
+ color: colors.text,
142
+ },
143
+ ]}
144
+ textContentType={mode === 'sign-in' ? 'password' : 'newPassword'}
145
+ value={password}
146
+ />
147
+ </View>
148
+ ) : null}
149
+
150
+ {error || auth.error ? <Text style={styles.errorText}>{error || auth.error}</Text> : null}
151
+ {message ? (
152
+ <Text style={[styles.messageText, { color: colors.text }]}>{message}</Text>
153
+ ) : null}
154
+
155
+ <Pressable
156
+ accessibilityRole="button"
157
+ disabled={!canSubmit || submitting}
158
+ onPress={submit}
159
+ style={StyleSheet.flatten([
160
+ styles.primaryButton,
161
+ {
162
+ backgroundColor: canSubmit ? colors.primary : '#9ca3af',
163
+ borderRadius: theme.layout.radius,
164
+ opacity: submitting ? 0.7 : 1,
165
+ },
166
+ ])}
167
+ >
168
+ <Text style={[styles.primaryButtonText, { color: primaryForeground }]}>
169
+ {submitting ? 'Working...' : copy.action}
170
+ </Text>
171
+ </Pressable>
172
+ </View>
173
+
174
+ <View style={styles.links}>
175
+ {mode !== 'sign-in' ? (
176
+ <Link href="/sign-in" style={[styles.link, { color: colors.primary }]}>
177
+ Sign in
178
+ </Link>
179
+ ) : null}
180
+ {mode !== 'sign-up' ? (
181
+ <Link href="/sign-up" style={[styles.link, { color: colors.primary }]}>
182
+ Create account
183
+ </Link>
184
+ ) : null}
185
+ {mode !== 'reset-password' ? (
186
+ <Link href="/reset-password" style={[styles.link, { color: colors.primary }]}>
187
+ Reset password
188
+ </Link>
189
+ ) : null}
190
+ </View>
191
+ </ScrollView>
192
+ );
193
+ }
194
+
195
+ const styles = StyleSheet.create({
196
+ screen: {
197
+ flex: 1,
198
+ },
199
+ content: {
200
+ flexGrow: 1,
201
+ gap: 18,
202
+ justifyContent: 'center',
203
+ padding: 20,
204
+ paddingTop: Platform.OS === 'web' ? 84 : 28,
205
+ },
206
+ header: {
207
+ gap: 8,
208
+ },
209
+ eyebrow: {
210
+ fontSize: 13,
211
+ fontWeight: '900',
212
+ textTransform: 'uppercase',
213
+ },
214
+ title: {
215
+ fontSize: 34,
216
+ fontWeight: '900',
217
+ lineHeight: 40,
218
+ },
219
+ form: {
220
+ borderWidth: 1,
221
+ gap: 14,
222
+ padding: 16,
223
+ },
224
+ field: {
225
+ gap: 6,
226
+ },
227
+ label: {
228
+ fontSize: 13,
229
+ fontWeight: '900',
230
+ },
231
+ input: {
232
+ borderWidth: 1,
233
+ fontSize: 15,
234
+ minHeight: 48,
235
+ paddingHorizontal: 12,
236
+ },
237
+ primaryButton: {
238
+ alignItems: 'center',
239
+ justifyContent: 'center',
240
+ minHeight: 48,
241
+ paddingHorizontal: 16,
242
+ },
243
+ primaryButtonText: {
244
+ fontSize: 15,
245
+ fontWeight: '900',
246
+ },
247
+ errorText: {
248
+ color: '#b91c1c',
249
+ fontSize: 14,
250
+ lineHeight: 20,
251
+ },
252
+ messageText: {
253
+ fontSize: 14,
254
+ lineHeight: 20,
255
+ },
256
+ links: {
257
+ flexDirection: 'row',
258
+ flexWrap: 'wrap',
259
+ gap: 14,
260
+ },
261
+ link: {
262
+ fontSize: 14,
263
+ fontWeight: '800',
264
+ },
265
+ });
@@ -0,0 +1,45 @@
1
+ import type { ReactNode } from 'react';
2
+
3
+ export type AuthProviderId = 'base' | 'supabase' | 'firebase' | 'convex';
4
+
5
+ export interface AuthUser {
6
+ id: string;
7
+ email?: string | null;
8
+ name?: string | null;
9
+ provider?: AuthProviderId | string | null;
10
+ metadata?: Record<string, unknown>;
11
+ }
12
+
13
+ export interface AuthSession {
14
+ user: AuthUser;
15
+ accessToken?: string | null;
16
+ }
17
+
18
+ export interface AuthAdapterState {
19
+ isLoading: boolean;
20
+ session: AuthSession | null;
21
+ error?: string | null;
22
+ }
23
+
24
+ export interface AuthActionInput {
25
+ email: string;
26
+ password: string;
27
+ }
28
+
29
+ export type AuthActionResult =
30
+ | { ok: true; message?: string }
31
+ | { ok: false; error: string };
32
+
33
+ export interface AuthAdapter {
34
+ provider: AuthProviderId;
35
+ state: AuthAdapterState;
36
+ refreshSession(): Promise<void>;
37
+ signInWithEmailPassword(input: AuthActionInput): Promise<AuthActionResult>;
38
+ signUpWithEmailPassword(input: AuthActionInput): Promise<AuthActionResult>;
39
+ requestPasswordReset(email: string): Promise<AuthActionResult>;
40
+ signOut(): Promise<AuthActionResult>;
41
+ }
42
+
43
+ export interface AuthAdapterProviderProps {
44
+ children: ReactNode;
45
+ }
@@ -3,6 +3,7 @@ import { Pressable, ScrollView, StyleSheet, Text, View } from 'react-native';
3
3
 
4
4
  import { ExpositionNotice } from '../../components/exposition';
5
5
  import { addLocalTask, getLocalAppSnapshot } from '../../services/local-data';
6
+ import { getReadableTextColor } from '../../theme/color-utils';
6
7
  import { useAppTheme } from '../../theme/provider';
7
8
 
8
9
  import type { appSnapshot } from '../../data/mock-app';
@@ -12,6 +13,7 @@ type Snapshot = typeof appSnapshot;
12
13
  export default function DataScreen() {
13
14
  const theme = useAppTheme();
14
15
  const colors = theme.activeColors;
16
+ const secondaryForeground = getReadableTextColor(colors.secondary, theme.colors.light.text);
15
17
  const [snapshot, setSnapshot] = useState<Snapshot | null>(null);
16
18
 
17
19
  useEffect(() => {
@@ -51,9 +53,9 @@ export default function DataScreen() {
51
53
  onPress={addTask}
52
54
  style={[
53
55
  styles.button,
54
- { backgroundColor: colors.primary, borderRadius: theme.layout.radius },
56
+ { backgroundColor: colors.secondary, borderRadius: theme.layout.radius },
55
57
  ]}>
56
- <Text style={styles.buttonText}>Insert a local task</Text>
58
+ <Text style={[styles.buttonText, { color: secondaryForeground }]}>Insert a local task</Text>
57
59
  </Pressable>
58
60
  {snapshot?.tasks.map((task) => (
59
61
  <View