@mr.dj2u/library-registry 0.0.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/LICENSE +21 -0
- package/README.md +42 -0
- package/THIRD_PARTY_NOTICES.md +90 -0
- package/assets/create-expo-app/sdk-56/assets/images/expo-badge-white.png +0 -0
- package/assets/create-expo-app/sdk-56/assets/images/expo-badge.png +0 -0
- package/assets/create-expo-app/sdk-56/assets/images/expo-logo.png +0 -0
- package/assets/create-expo-app/sdk-56/assets/images/logo-glow.png +0 -0
- package/assets/create-expo-app/sdk-56/assets/images/react-logo.png +0 -0
- package/assets/create-expo-app/sdk-56/assets/images/react-logo@2x.png +0 -0
- package/assets/create-expo-app/sdk-56/assets/images/react-logo@3x.png +0 -0
- package/assets/create-expo-app/sdk-56/assets/images/tabIcons/explore.png +0 -0
- package/assets/create-expo-app/sdk-56/assets/images/tabIcons/explore@2x.png +0 -0
- package/assets/create-expo-app/sdk-56/assets/images/tabIcons/explore@3x.png +0 -0
- package/assets/create-expo-app/sdk-56/assets/images/tabIcons/home.png +0 -0
- package/assets/create-expo-app/sdk-56/assets/images/tabIcons/home@2x.png +0 -0
- package/assets/create-expo-app/sdk-56/assets/images/tabIcons/home@3x.png +0 -0
- package/assets/create-expo-app/sdk-56/assets/images/tutorial-web.png +0 -0
- package/assets/create-expo-app/sdk-56/src/app/_layout.tsx +15 -0
- package/assets/create-expo-app/sdk-56/src/app/explore.tsx +180 -0
- package/assets/create-expo-app/sdk-56/src/app/index.tsx +98 -0
- package/assets/create-expo-app/sdk-56/src/components/animated-icon.module.css +6 -0
- package/assets/create-expo-app/sdk-56/src/components/animated-icon.tsx +132 -0
- package/assets/create-expo-app/sdk-56/src/components/animated-icon.web.tsx +108 -0
- package/assets/create-expo-app/sdk-56/src/components/app-tabs.tsx +32 -0
- package/assets/create-expo-app/sdk-56/src/components/app-tabs.web.tsx +115 -0
- package/assets/create-expo-app/sdk-56/src/components/external-link.tsx +25 -0
- package/assets/create-expo-app/sdk-56/src/components/hint-row.tsx +35 -0
- package/assets/create-expo-app/sdk-56/src/components/themed-text.tsx +73 -0
- package/assets/create-expo-app/sdk-56/src/components/themed-view.tsx +16 -0
- package/assets/create-expo-app/sdk-56/src/components/ui/collapsible.tsx +65 -0
- package/assets/create-expo-app/sdk-56/src/components/web-badge.tsx +43 -0
- package/assets/create-expo-app/sdk-56/src/constants/theme.ts +65 -0
- package/assets/create-expo-app/sdk-56/src/global.css +9 -0
- package/assets/create-expo-app/sdk-56/src/hooks/use-color-scheme.ts +1 -0
- package/assets/create-expo-app/sdk-56/src/hooks/use-color-scheme.web.ts +21 -0
- package/assets/create-expo-app/sdk-56/src/hooks/use-theme.ts +14 -0
- package/assets/create-expo-stack/2.21.3/base/components/BackButton.tsx +23 -0
- package/assets/create-expo-stack/2.21.3/base/components/Button.tsx +42 -0
- package/assets/create-expo-stack/2.21.3/base/components/Container.tsx +14 -0
- package/assets/create-expo-stack/2.21.3/base/components/EditScreenInfo.tsx +36 -0
- package/assets/create-expo-stack/2.21.3/base/components/HeaderButton.tsx +31 -0
- package/assets/create-expo-stack/2.21.3/base/components/ScreenContent.tsx +40 -0
- package/assets/create-expo-stack/2.21.3/base/components/TabBarIcon.tsx +15 -0
- package/assets/create-expo-stack/2.21.3-mrdj.2/src/components/nativewindui/ActivityIndicator.tsx +10 -0
- package/assets/create-expo-stack/2.21.3-mrdj.2/src/components/nativewindui/Avatar.tsx +32 -0
- package/assets/create-expo-stack/2.21.3-mrdj.2/src/components/nativewindui/Button.tsx +141 -0
- package/assets/create-expo-stack/2.21.3-mrdj.2/src/components/nativewindui/DatePicker/DatePicker.android.tsx +77 -0
- package/assets/create-expo-stack/2.21.3-mrdj.2/src/components/nativewindui/DatePicker/DatePicker.tsx +23 -0
- package/assets/create-expo-stack/2.21.3-mrdj.2/src/components/nativewindui/DatePicker/index.ts +1 -0
- package/assets/create-expo-stack/2.21.3-mrdj.2/src/components/nativewindui/Icon/Icon.ios.tsx +55 -0
- package/assets/create-expo-stack/2.21.3-mrdj.2/src/components/nativewindui/Icon/Icon.tsx +58 -0
- package/assets/create-expo-stack/2.21.3-mrdj.2/src/components/nativewindui/Icon/index.ts +16 -0
- package/assets/create-expo-stack/2.21.3-mrdj.2/src/components/nativewindui/Icon/types.ts +19 -0
- package/assets/create-expo-stack/2.21.3-mrdj.2/src/components/nativewindui/Picker.tsx +42 -0
- package/assets/create-expo-stack/2.21.3-mrdj.2/src/components/nativewindui/ProgressIndicator.tsx +68 -0
- package/assets/create-expo-stack/2.21.3-mrdj.2/src/components/nativewindui/Slider.tsx +28 -0
- package/assets/create-expo-stack/2.21.3-mrdj.2/src/components/nativewindui/Text.tsx +55 -0
- package/assets/create-expo-stack/2.21.3-mrdj.2/src/components/nativewindui/ThemeToggle.tsx +33 -0
- package/assets/create-expo-stack/2.21.3-mrdj.2/src/components/nativewindui/Toggle.tsx +20 -0
- package/assets/create-expo-stack/2.21.3-mrdj.2/src/lib/cn.ts +6 -0
- package/assets/create-expo-stack/2.21.3-mrdj.2/src/lib/useColorScheme.tsx +21 -0
- package/assets/create-expo-stack/2.21.3-mrdj.2/src/lib/useHeaderSearchBar.tsx +33 -0
- package/assets/create-expo-stack/2.21.3-mrdj.2/src/theme/colors.ts +123 -0
- package/assets/create-expo-stack/2.21.3-mrdj.2/src/theme/index.ts +32 -0
- package/assets/create-expo-stack/2.21.3-mrdj.2/src/theme/with-opacity.ts +155 -0
- package/assets/mds/routes/drawer-tabs/data.tsx +1 -0
- package/assets/mds/routes/drawer-tabs/index.tsx +1 -0
- package/assets/mds/routes/drawer-tabs/nativewindui.tsx +1 -0
- package/assets/mds/routes/drawer-tabs/sdk-56.tsx +1 -0
- package/assets/mds/routes/drawer-tabs/stylist.tsx +1 -0
- package/assets/mds/scripts/stylist-sync-android.mjs +39 -0
- package/assets/mds/src/app/exposition/data.tsx +1 -0
- package/assets/mds/src/app/exposition/index.tsx +1 -0
- package/assets/mds/src/app/exposition/nativewindui.tsx +1 -0
- package/assets/mds/src/app/exposition/sdk-56.tsx +1 -0
- package/assets/mds/src/app/exposition/stylist-sync+api.ts +241 -0
- package/assets/mds/src/app/exposition/stylist.tsx +1 -0
- package/assets/mds/src/app/onboarding/account-setup.tsx +1 -0
- package/assets/mds/src/app/onboarding/agreement.tsx +1 -0
- package/assets/mds/src/app/onboarding/terms.tsx +1 -0
- package/assets/mds/src/app/onboarding.tsx +1 -0
- package/assets/mds/src/app/settings.tsx +1 -0
- package/assets/mds/src/components/exposition/index.ts +8 -0
- package/assets/mds/src/components/exposition/notice.tsx +3 -0
- package/assets/mds/src/components/exposition/package-card.tsx +76 -0
- package/assets/mds/src/components/swmansion/animated-pressable.tsx +54 -0
- package/assets/mds/src/components/swmansion/gesture-card.tsx +54 -0
- package/assets/mds/src/components/swmansion/keyboard-form.tsx +73 -0
- package/assets/mds/src/components/swmansion/screens-card.tsx +40 -0
- package/assets/mds/src/components/swmansion/software-mansion-logo.tsx +15 -0
- package/assets/mds/src/components/swmansion/svg-mark.tsx +16 -0
- package/assets/mds/src/data/mock-app.ts +21 -0
- package/assets/mds/src/features/exposition/data-screen.tsx +152 -0
- package/assets/mds/src/features/exposition/embedded-fonts.ts +72 -0
- package/assets/mds/src/features/exposition/expo-sdk-56-screen.tsx +709 -0
- package/assets/mds/src/features/exposition/exposition-screen.tsx +191 -0
- package/assets/mds/src/features/exposition/nativewindui-screen.tsx +125 -0
- package/assets/mds/src/features/exposition/stylist-screen.tsx +3456 -0
- package/assets/mds/src/features/onboarding/account-setup-screen.tsx +54 -0
- package/assets/mds/src/features/onboarding/agreement-screen.tsx +6 -0
- package/assets/mds/src/features/onboarding/components/legal-document-view.tsx +103 -0
- package/assets/mds/src/features/onboarding/legal-documents.ts +80 -0
- package/assets/mds/src/features/onboarding/onboarding-screen.tsx +167 -0
- package/assets/mds/src/features/onboarding/terms-screen.tsx +6 -0
- package/assets/mds/src/features/settings/settings-screen.tsx +57 -0
- package/assets/mds/src/services/local-data.native.ts +103 -0
- package/assets/mds/src/services/local-data.ts +38 -0
- package/assets/mds/src/theme/font-assets.ts +3 -0
- package/assets/mds/src/theme/provider.tsx +59 -0
- package/assets/mds/src/theme/tokens.ts +180 -0
- package/dist/catalog.d.ts +3 -0
- package/dist/catalog.d.ts.map +1 -0
- package/dist/catalog.js +1110 -0
- package/dist/catalog.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -0
- package/dist/registry.d.ts +7 -0
- package/dist/registry.d.ts.map +1 -0
- package/dist/registry.js +479 -0
- package/dist/registry.js.map +1 -0
- package/dist/types.d.ts +169 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/validation.d.ts +6 -0
- package/dist/validation.d.ts.map +1 -0
- package/dist/validation.js +209 -0
- package/dist/validation.js.map +1 -0
- package/package.json +51 -0
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { useRouter } from 'expo-router';
|
|
2
|
+
import { Pressable, StyleSheet, Text, View } from 'react-native';
|
|
3
|
+
|
|
4
|
+
export default function AccountSetupScreen() {
|
|
5
|
+
const router = useRouter();
|
|
6
|
+
|
|
7
|
+
return (
|
|
8
|
+
<View style={styles.screen}>
|
|
9
|
+
<Text style={styles.title}>Account setup</Text>
|
|
10
|
+
<Text style={styles.body}>
|
|
11
|
+
This is the production-ready handoff point after legal acceptance. Replace this with your
|
|
12
|
+
real auth and profile onboarding flow.
|
|
13
|
+
</Text>
|
|
14
|
+
<Pressable
|
|
15
|
+
accessibilityRole="button"
|
|
16
|
+
onPress={() => router.replace('/')}
|
|
17
|
+
style={styles.homeButton}>
|
|
18
|
+
<Text style={styles.homeButtonText}>Continue to home</Text>
|
|
19
|
+
</Pressable>
|
|
20
|
+
</View>
|
|
21
|
+
);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const styles = StyleSheet.create({
|
|
25
|
+
screen: {
|
|
26
|
+
backgroundColor: '#ffffff',
|
|
27
|
+
flex: 1,
|
|
28
|
+
gap: 12,
|
|
29
|
+
padding: 20,
|
|
30
|
+
},
|
|
31
|
+
title: {
|
|
32
|
+
color: '#111827',
|
|
33
|
+
fontSize: 26,
|
|
34
|
+
fontWeight: '800',
|
|
35
|
+
},
|
|
36
|
+
body: {
|
|
37
|
+
color: '#4b5563',
|
|
38
|
+
fontSize: 15,
|
|
39
|
+
lineHeight: 22,
|
|
40
|
+
},
|
|
41
|
+
homeButton: {
|
|
42
|
+
alignItems: 'center',
|
|
43
|
+
backgroundColor: '#2563eb',
|
|
44
|
+
borderRadius: 12,
|
|
45
|
+
marginTop: 12,
|
|
46
|
+
paddingHorizontal: 18,
|
|
47
|
+
paddingVertical: 14,
|
|
48
|
+
},
|
|
49
|
+
homeButtonText: {
|
|
50
|
+
color: '#ffffff',
|
|
51
|
+
fontSize: 16,
|
|
52
|
+
fontWeight: '800',
|
|
53
|
+
},
|
|
54
|
+
});
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { ScrollView, StyleSheet, Text, View } from 'react-native';
|
|
2
|
+
|
|
3
|
+
import type { LegalDocument } from '../legal-documents';
|
|
4
|
+
|
|
5
|
+
interface LegalDocumentViewProps {
|
|
6
|
+
document: LegalDocument;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
function LegalDocumentMeta({ label, value }: { label: string; value: string }) {
|
|
10
|
+
return (
|
|
11
|
+
<View style={styles.metaItem}>
|
|
12
|
+
<Text style={styles.metaLabel}>{label}</Text>
|
|
13
|
+
<Text style={styles.metaValue}>{value}</Text>
|
|
14
|
+
</View>
|
|
15
|
+
);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function LegalSectionItem({ title, body }: { title: string; body: string }) {
|
|
19
|
+
return (
|
|
20
|
+
<View style={styles.section}>
|
|
21
|
+
<Text style={styles.sectionTitle}>{title}</Text>
|
|
22
|
+
<Text style={styles.sectionBody}>{body}</Text>
|
|
23
|
+
</View>
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function LegalDocumentView({ document }: LegalDocumentViewProps) {
|
|
28
|
+
return (
|
|
29
|
+
<ScrollView contentContainerStyle={styles.content} style={styles.screen}>
|
|
30
|
+
<Text style={styles.title}>{document.title}</Text>
|
|
31
|
+
<Text style={styles.summary}>{document.summary}</Text>
|
|
32
|
+
<View style={styles.metaRow}>
|
|
33
|
+
<LegalDocumentMeta label="Effective" value={document.effectiveDate} />
|
|
34
|
+
<LegalDocumentMeta label="Last updated" value={document.lastUpdated} />
|
|
35
|
+
</View>
|
|
36
|
+
{document.sections.map((section) => (
|
|
37
|
+
<LegalSectionItem key={section.id} title={section.title} body={section.body} />
|
|
38
|
+
))}
|
|
39
|
+
</ScrollView>
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const styles = StyleSheet.create({
|
|
44
|
+
screen: {
|
|
45
|
+
backgroundColor: '#f8fafc',
|
|
46
|
+
flex: 1,
|
|
47
|
+
},
|
|
48
|
+
content: {
|
|
49
|
+
gap: 14,
|
|
50
|
+
padding: 20,
|
|
51
|
+
paddingTop: 84,
|
|
52
|
+
},
|
|
53
|
+
title: {
|
|
54
|
+
color: '#0f172a',
|
|
55
|
+
fontSize: 28,
|
|
56
|
+
fontWeight: '800',
|
|
57
|
+
},
|
|
58
|
+
summary: {
|
|
59
|
+
color: '#334155',
|
|
60
|
+
fontSize: 15,
|
|
61
|
+
lineHeight: 22,
|
|
62
|
+
},
|
|
63
|
+
metaRow: {
|
|
64
|
+
flexDirection: 'row',
|
|
65
|
+
gap: 10,
|
|
66
|
+
},
|
|
67
|
+
metaItem: {
|
|
68
|
+
backgroundColor: '#e2e8f0',
|
|
69
|
+
borderRadius: 10,
|
|
70
|
+
gap: 2,
|
|
71
|
+
paddingHorizontal: 10,
|
|
72
|
+
paddingVertical: 8,
|
|
73
|
+
},
|
|
74
|
+
metaLabel: {
|
|
75
|
+
color: '#475569',
|
|
76
|
+
fontSize: 11,
|
|
77
|
+
fontWeight: '700',
|
|
78
|
+
textTransform: 'uppercase',
|
|
79
|
+
},
|
|
80
|
+
metaValue: {
|
|
81
|
+
color: '#0f172a',
|
|
82
|
+
fontSize: 13,
|
|
83
|
+
fontWeight: '700',
|
|
84
|
+
},
|
|
85
|
+
section: {
|
|
86
|
+
backgroundColor: '#ffffff',
|
|
87
|
+
borderColor: '#e2e8f0',
|
|
88
|
+
borderRadius: 12,
|
|
89
|
+
borderWidth: 1,
|
|
90
|
+
gap: 7,
|
|
91
|
+
padding: 14,
|
|
92
|
+
},
|
|
93
|
+
sectionTitle: {
|
|
94
|
+
color: '#0f172a',
|
|
95
|
+
fontSize: 17,
|
|
96
|
+
fontWeight: '800',
|
|
97
|
+
},
|
|
98
|
+
sectionBody: {
|
|
99
|
+
color: '#334155',
|
|
100
|
+
fontSize: 14,
|
|
101
|
+
lineHeight: 21,
|
|
102
|
+
},
|
|
103
|
+
});
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
export interface LegalDocumentSection {
|
|
2
|
+
id: string;
|
|
3
|
+
title: string;
|
|
4
|
+
body: string;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export interface LegalDocument {
|
|
8
|
+
id: 'agreement' | 'terms';
|
|
9
|
+
title: string;
|
|
10
|
+
summary: string;
|
|
11
|
+
effectiveDate: string;
|
|
12
|
+
lastUpdated: string;
|
|
13
|
+
sections: LegalDocumentSection[];
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export const onboardingLegalDocuments: Record<'agreement' | 'terms', LegalDocument> = {
|
|
17
|
+
agreement: {
|
|
18
|
+
id: 'agreement',
|
|
19
|
+
title: 'User Agreement',
|
|
20
|
+
summary: 'Agreement template for onboarding consent and account usage.',
|
|
21
|
+
effectiveDate: '2026-05-24',
|
|
22
|
+
lastUpdated: '2026-05-24',
|
|
23
|
+
sections: [
|
|
24
|
+
{
|
|
25
|
+
id: 'scope',
|
|
26
|
+
title: 'Scope',
|
|
27
|
+
body: 'This agreement covers access to [APP NAME], account conduct, and baseline obligations between [COMPANY NAME] and each user.',
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
id: 'usage',
|
|
31
|
+
title: 'Acceptable Use',
|
|
32
|
+
body: 'Users agree not to misuse the service, attempt unauthorized access, or submit harmful content.',
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
id: 'privacy',
|
|
36
|
+
title: 'Privacy and Data',
|
|
37
|
+
body: 'User data is handled according to the published privacy notice. Replace this section with your final privacy commitments and retention policy.',
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
id: 'termination',
|
|
41
|
+
title: 'Termination',
|
|
42
|
+
body: 'Either party may terminate usage under the conditions described in this section. Add jurisdiction-specific language before production launch.',
|
|
43
|
+
},
|
|
44
|
+
],
|
|
45
|
+
},
|
|
46
|
+
terms: {
|
|
47
|
+
id: 'terms',
|
|
48
|
+
title: 'Terms of Service',
|
|
49
|
+
summary: 'Near-blank, production-oriented terms starter for legal review.',
|
|
50
|
+
effectiveDate: '2026-05-24',
|
|
51
|
+
lastUpdated: '2026-05-24',
|
|
52
|
+
sections: [
|
|
53
|
+
{
|
|
54
|
+
id: 'eligibility',
|
|
55
|
+
title: 'Eligibility',
|
|
56
|
+
body: 'Users must meet age and legal capacity requirements for their jurisdiction.',
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
id: 'accounts',
|
|
60
|
+
title: 'Accounts',
|
|
61
|
+
body: 'Users are responsible for account credentials and activity performed through their account.',
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
id: 'payments',
|
|
65
|
+
title: 'Payments and Billing',
|
|
66
|
+
body: 'If applicable, describe pricing, billing intervals, refunds, and failed payment handling.',
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
id: 'liability',
|
|
70
|
+
title: 'Disclaimers and Liability',
|
|
71
|
+
body: 'Define limitations of liability and service disclaimers with legal counsel.',
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
id: 'governing-law',
|
|
75
|
+
title: 'Governing Law',
|
|
76
|
+
body: 'Specify governing law, venue, and dispute resolution expectations.',
|
|
77
|
+
},
|
|
78
|
+
],
|
|
79
|
+
},
|
|
80
|
+
};
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import { Link, useRouter } from 'expo-router';
|
|
2
|
+
import { useMemo, useState } from 'react';
|
|
3
|
+
import { Pressable, StyleSheet, Switch, Text, View } from 'react-native';
|
|
4
|
+
|
|
5
|
+
import { onboardingLegalDocuments } from './legal-documents';
|
|
6
|
+
|
|
7
|
+
export default function OnboardingScreen() {
|
|
8
|
+
const [acceptedAgreement, setAcceptedAgreement] = useState(false);
|
|
9
|
+
const [acceptedTerms, setAcceptedTerms] = useState(false);
|
|
10
|
+
const router = useRouter();
|
|
11
|
+
const canContinue = acceptedAgreement && acceptedTerms;
|
|
12
|
+
const agreementUpdated = useMemo(
|
|
13
|
+
() => new Date(onboardingLegalDocuments.agreement.lastUpdated).toLocaleDateString(),
|
|
14
|
+
[]
|
|
15
|
+
);
|
|
16
|
+
const termsUpdated = useMemo(
|
|
17
|
+
() => new Date(onboardingLegalDocuments.terms.lastUpdated).toLocaleDateString(),
|
|
18
|
+
[]
|
|
19
|
+
);
|
|
20
|
+
|
|
21
|
+
return (
|
|
22
|
+
<View style={styles.screen}>
|
|
23
|
+
<Text style={styles.title}>Legal onboarding</Text>
|
|
24
|
+
<Text style={styles.body}>
|
|
25
|
+
Review and approve the Agreement and Terms before continuing in your real auth or profile
|
|
26
|
+
flow.
|
|
27
|
+
</Text>
|
|
28
|
+
<View style={styles.card}>
|
|
29
|
+
<View style={styles.rowTop}>
|
|
30
|
+
<Text style={styles.cardTitle}>Agreement</Text>
|
|
31
|
+
<Text style={styles.meta}>{agreementUpdated}</Text>
|
|
32
|
+
</View>
|
|
33
|
+
<Text style={styles.cardBody}>
|
|
34
|
+
A compact starter agreement with fill-in fields your team can finalize.
|
|
35
|
+
</Text>
|
|
36
|
+
<View style={styles.rowBottom}>
|
|
37
|
+
<Link href="/onboarding/agreement" asChild>
|
|
38
|
+
<Pressable accessibilityRole="button" style={styles.linkButton}>
|
|
39
|
+
<Text style={styles.linkButtonText}>View agreement</Text>
|
|
40
|
+
</Pressable>
|
|
41
|
+
</Link>
|
|
42
|
+
<View style={styles.acceptWrap}>
|
|
43
|
+
<Text style={styles.acceptText}>Accepted</Text>
|
|
44
|
+
<Switch value={acceptedAgreement} onValueChange={setAcceptedAgreement} />
|
|
45
|
+
</View>
|
|
46
|
+
</View>
|
|
47
|
+
</View>
|
|
48
|
+
<View style={styles.card}>
|
|
49
|
+
<View style={styles.rowTop}>
|
|
50
|
+
<Text style={styles.cardTitle}>Terms of service</Text>
|
|
51
|
+
<Text style={styles.meta}>{termsUpdated}</Text>
|
|
52
|
+
</View>
|
|
53
|
+
<Text style={styles.cardBody}>
|
|
54
|
+
Production-safe baseline terms with placeholders for business specifics.
|
|
55
|
+
</Text>
|
|
56
|
+
<View style={styles.rowBottom}>
|
|
57
|
+
<Link href="/onboarding/terms" asChild>
|
|
58
|
+
<Pressable accessibilityRole="button" style={styles.linkButton}>
|
|
59
|
+
<Text style={styles.linkButtonText}>View terms</Text>
|
|
60
|
+
</Pressable>
|
|
61
|
+
</Link>
|
|
62
|
+
<View style={styles.acceptWrap}>
|
|
63
|
+
<Text style={styles.acceptText}>Accepted</Text>
|
|
64
|
+
<Switch value={acceptedTerms} onValueChange={setAcceptedTerms} />
|
|
65
|
+
</View>
|
|
66
|
+
</View>
|
|
67
|
+
</View>
|
|
68
|
+
<Pressable
|
|
69
|
+
accessibilityRole="button"
|
|
70
|
+
disabled={!canContinue}
|
|
71
|
+
onPress={() => {
|
|
72
|
+
if (canContinue) router.push('/onboarding/account-setup');
|
|
73
|
+
}}
|
|
74
|
+
style={[styles.ctaButton, !canContinue && styles.ctaButtonDisabled]}>
|
|
75
|
+
<Text style={styles.ctaButtonText}>Continue to account setup</Text>
|
|
76
|
+
</Pressable>
|
|
77
|
+
</View>
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const styles = StyleSheet.create({
|
|
82
|
+
screen: {
|
|
83
|
+
backgroundColor: '#ffffff',
|
|
84
|
+
flex: 1,
|
|
85
|
+
gap: 14,
|
|
86
|
+
padding: 20,
|
|
87
|
+
},
|
|
88
|
+
title: {
|
|
89
|
+
color: '#111827',
|
|
90
|
+
fontSize: 26,
|
|
91
|
+
fontWeight: '800',
|
|
92
|
+
},
|
|
93
|
+
body: {
|
|
94
|
+
color: '#4b5563',
|
|
95
|
+
fontSize: 15,
|
|
96
|
+
lineHeight: 22,
|
|
97
|
+
},
|
|
98
|
+
card: {
|
|
99
|
+
backgroundColor: '#ffffff',
|
|
100
|
+
borderColor: '#d1d5db',
|
|
101
|
+
borderRadius: 12,
|
|
102
|
+
borderWidth: 1,
|
|
103
|
+
gap: 8,
|
|
104
|
+
padding: 14,
|
|
105
|
+
},
|
|
106
|
+
rowTop: {
|
|
107
|
+
alignItems: 'center',
|
|
108
|
+
flexDirection: 'row',
|
|
109
|
+
justifyContent: 'space-between',
|
|
110
|
+
},
|
|
111
|
+
rowBottom: {
|
|
112
|
+
alignItems: 'center',
|
|
113
|
+
flexDirection: 'row',
|
|
114
|
+
justifyContent: 'space-between',
|
|
115
|
+
},
|
|
116
|
+
cardTitle: {
|
|
117
|
+
color: '#111827',
|
|
118
|
+
fontSize: 18,
|
|
119
|
+
fontWeight: '800',
|
|
120
|
+
},
|
|
121
|
+
cardBody: {
|
|
122
|
+
color: '#4b5563',
|
|
123
|
+
fontSize: 14,
|
|
124
|
+
lineHeight: 20,
|
|
125
|
+
},
|
|
126
|
+
meta: {
|
|
127
|
+
color: '#6b7280',
|
|
128
|
+
fontSize: 12,
|
|
129
|
+
fontWeight: '700',
|
|
130
|
+
},
|
|
131
|
+
linkButton: {
|
|
132
|
+
backgroundColor: '#111827',
|
|
133
|
+
borderRadius: 9,
|
|
134
|
+
paddingHorizontal: 12,
|
|
135
|
+
paddingVertical: 8,
|
|
136
|
+
},
|
|
137
|
+
linkButtonText: {
|
|
138
|
+
color: '#ffffff',
|
|
139
|
+
fontSize: 13,
|
|
140
|
+
fontWeight: '700',
|
|
141
|
+
},
|
|
142
|
+
acceptWrap: {
|
|
143
|
+
alignItems: 'center',
|
|
144
|
+
flexDirection: 'row',
|
|
145
|
+
gap: 8,
|
|
146
|
+
},
|
|
147
|
+
acceptText: {
|
|
148
|
+
color: '#111827',
|
|
149
|
+
fontSize: 13,
|
|
150
|
+
fontWeight: '700',
|
|
151
|
+
},
|
|
152
|
+
ctaButton: {
|
|
153
|
+
alignItems: 'center',
|
|
154
|
+
backgroundColor: '#0f172a',
|
|
155
|
+
borderRadius: 12,
|
|
156
|
+
marginTop: 'auto',
|
|
157
|
+
paddingVertical: 14,
|
|
158
|
+
},
|
|
159
|
+
ctaButtonDisabled: {
|
|
160
|
+
backgroundColor: '#9ca3af',
|
|
161
|
+
},
|
|
162
|
+
ctaButtonText: {
|
|
163
|
+
color: '#ffffff',
|
|
164
|
+
fontSize: 15,
|
|
165
|
+
fontWeight: '800',
|
|
166
|
+
},
|
|
167
|
+
});
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { StyleSheet, Text, View } from 'react-native';
|
|
2
|
+
|
|
3
|
+
import { KeyboardForm } from '../../components/swmansion/keyboard-form';
|
|
4
|
+
import { useAppTheme } from '../../theme/provider';
|
|
5
|
+
|
|
6
|
+
export default function SettingsScreen() {
|
|
7
|
+
const theme = useAppTheme();
|
|
8
|
+
const colors = theme.activeColors;
|
|
9
|
+
|
|
10
|
+
return (
|
|
11
|
+
<View style={[styles.screen, { backgroundColor: colors.background }]}>
|
|
12
|
+
<View style={styles.header}>
|
|
13
|
+
<Text
|
|
14
|
+
style={[
|
|
15
|
+
styles.title,
|
|
16
|
+
{
|
|
17
|
+
color: colors.text,
|
|
18
|
+
fontFamily: theme.typography.fontFamily,
|
|
19
|
+
fontWeight:
|
|
20
|
+
theme.typography.fontFamily === 'System' ||
|
|
21
|
+
theme.typography.fontFamily === 'monospace'
|
|
22
|
+
? '800'
|
|
23
|
+
: 'normal',
|
|
24
|
+
},
|
|
25
|
+
]}>
|
|
26
|
+
Settings
|
|
27
|
+
</Text>
|
|
28
|
+
<Text style={[styles.body, { color: colors.text }]}>
|
|
29
|
+
Keyboard Controller is ready for form-heavy screens.
|
|
30
|
+
</Text>
|
|
31
|
+
</View>
|
|
32
|
+
<KeyboardForm />
|
|
33
|
+
</View>
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const styles = StyleSheet.create({
|
|
38
|
+
screen: {
|
|
39
|
+
backgroundColor: '#ffffff',
|
|
40
|
+
flex: 1,
|
|
41
|
+
padding: 20,
|
|
42
|
+
},
|
|
43
|
+
header: {
|
|
44
|
+
marginBottom: 12,
|
|
45
|
+
},
|
|
46
|
+
title: {
|
|
47
|
+
color: '#111827',
|
|
48
|
+
fontSize: 26,
|
|
49
|
+
fontWeight: '800',
|
|
50
|
+
},
|
|
51
|
+
body: {
|
|
52
|
+
color: '#4b5563',
|
|
53
|
+
fontSize: 14,
|
|
54
|
+
lineHeight: 20,
|
|
55
|
+
marginTop: 4,
|
|
56
|
+
},
|
|
57
|
+
});
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import * as SQLite from 'expo-sqlite';
|
|
2
|
+
|
|
3
|
+
import { appSnapshot } from '../data/mock-app';
|
|
4
|
+
|
|
5
|
+
import type { AppTask } from '../data/mock-app';
|
|
6
|
+
|
|
7
|
+
const dbPromise = SQLite.openDatabaseAsync('exposition.db');
|
|
8
|
+
let sqliteUnavailable = false;
|
|
9
|
+
let memoryTasks: AppTask[] = [...appSnapshot.tasks];
|
|
10
|
+
|
|
11
|
+
async function getDb() {
|
|
12
|
+
if (sqliteUnavailable) {
|
|
13
|
+
return null;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
try {
|
|
17
|
+
return await dbPromise;
|
|
18
|
+
} catch {
|
|
19
|
+
sqliteUnavailable = true;
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export async function ensureLocalDataReady(): Promise<void> {
|
|
25
|
+
const db = await getDb();
|
|
26
|
+
if (!db) {
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
try {
|
|
31
|
+
await db.execAsync(`
|
|
32
|
+
CREATE TABLE IF NOT EXISTS exposition_tasks (
|
|
33
|
+
id TEXT PRIMARY KEY NOT NULL,
|
|
34
|
+
title TEXT NOT NULL,
|
|
35
|
+
status TEXT NOT NULL
|
|
36
|
+
);
|
|
37
|
+
`);
|
|
38
|
+
const row = await db.getFirstAsync<{ count: number }>(
|
|
39
|
+
'SELECT COUNT(*) as count FROM exposition_tasks'
|
|
40
|
+
);
|
|
41
|
+
if ((row?.count ?? 0) > 0) {
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
for (const task of appSnapshot.tasks) {
|
|
46
|
+
await db.runAsync(
|
|
47
|
+
'INSERT INTO exposition_tasks (id, title, status) VALUES (?, ?, ?)',
|
|
48
|
+
task.id,
|
|
49
|
+
task.title,
|
|
50
|
+
task.status
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
} catch {
|
|
54
|
+
sqliteUnavailable = true;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export async function getLocalAppSnapshot(): Promise<typeof appSnapshot> {
|
|
59
|
+
await ensureLocalDataReady();
|
|
60
|
+
const db = await getDb();
|
|
61
|
+
if (!db) {
|
|
62
|
+
return { ...appSnapshot, tasks: memoryTasks };
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
try {
|
|
66
|
+
const tasks = await db.getAllAsync<AppTask>(
|
|
67
|
+
'SELECT id, title, status FROM exposition_tasks ORDER BY id'
|
|
68
|
+
);
|
|
69
|
+
return {
|
|
70
|
+
...appSnapshot,
|
|
71
|
+
tasks,
|
|
72
|
+
};
|
|
73
|
+
} catch {
|
|
74
|
+
sqliteUnavailable = true;
|
|
75
|
+
return { ...appSnapshot, tasks: memoryTasks };
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export async function addLocalTask(
|
|
80
|
+
title = 'Try the local DB adapter'
|
|
81
|
+
): Promise<typeof appSnapshot> {
|
|
82
|
+
await ensureLocalDataReady();
|
|
83
|
+
const db = await getDb();
|
|
84
|
+
const id = `task-${Date.now()}`;
|
|
85
|
+
if (!db) {
|
|
86
|
+
memoryTasks = [...memoryTasks, { id, title, status: 'todo' }];
|
|
87
|
+
return { ...appSnapshot, tasks: memoryTasks };
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
try {
|
|
91
|
+
await db.runAsync(
|
|
92
|
+
'INSERT INTO exposition_tasks (id, title, status) VALUES (?, ?, ?)',
|
|
93
|
+
id,
|
|
94
|
+
title,
|
|
95
|
+
'todo'
|
|
96
|
+
);
|
|
97
|
+
return getLocalAppSnapshot();
|
|
98
|
+
} catch {
|
|
99
|
+
sqliteUnavailable = true;
|
|
100
|
+
memoryTasks = [...memoryTasks, { id, title, status: 'todo' }];
|
|
101
|
+
return { ...appSnapshot, tasks: memoryTasks };
|
|
102
|
+
}
|
|
103
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { appSnapshot } from '../data/mock-app';
|
|
2
|
+
|
|
3
|
+
import type { AppTask } from '../data/mock-app';
|
|
4
|
+
|
|
5
|
+
let tasks: AppTask[] = [...appSnapshot.tasks];
|
|
6
|
+
|
|
7
|
+
export async function ensureLocalDataReady(): Promise<void> {
|
|
8
|
+
tasks = tasks.length > 0 ? tasks : [...appSnapshot.tasks];
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export async function getLocalAppSnapshot(): Promise<typeof appSnapshot> {
|
|
12
|
+
await ensureLocalDataReady();
|
|
13
|
+
return {
|
|
14
|
+
...appSnapshot,
|
|
15
|
+
tasks,
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export async function addLocalTask(
|
|
20
|
+
title = 'Try the local data adapter'
|
|
21
|
+
): Promise<typeof appSnapshot> {
|
|
22
|
+
await ensureLocalDataReady();
|
|
23
|
+
tasks = [
|
|
24
|
+
...tasks,
|
|
25
|
+
{
|
|
26
|
+
id: `task-${Date.now()}`,
|
|
27
|
+
title,
|
|
28
|
+
status: 'todo',
|
|
29
|
+
},
|
|
30
|
+
];
|
|
31
|
+
return getLocalAppSnapshot();
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export const dataAdapterNotes = [
|
|
35
|
+
'This default adapter is web-safe and keeps generated apps runnable immediately.',
|
|
36
|
+
'Native builds use local-data.native.ts for the Expo SQLite demo.',
|
|
37
|
+
'Keep screens behind this adapter boundary so SQLite or Supabase can be swapped later.',
|
|
38
|
+
];
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createContext,
|
|
3
|
+
useContext,
|
|
4
|
+
useMemo,
|
|
5
|
+
useState,
|
|
6
|
+
type Dispatch,
|
|
7
|
+
type ReactNode,
|
|
8
|
+
type SetStateAction,
|
|
9
|
+
} from 'react';
|
|
10
|
+
|
|
11
|
+
import defaultThemeTokens, {
|
|
12
|
+
type StylistColorPalette,
|
|
13
|
+
type StylistColorScheme,
|
|
14
|
+
type StylistThemeTokens,
|
|
15
|
+
} from './tokens';
|
|
16
|
+
|
|
17
|
+
export type AppThemeValue = StylistThemeTokens & {
|
|
18
|
+
activeScheme: StylistColorScheme;
|
|
19
|
+
activeColors: StylistColorPalette;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const AppThemeContext = createContext<AppThemeValue>({
|
|
23
|
+
...defaultThemeTokens,
|
|
24
|
+
activeScheme: defaultThemeTokens.colorSystem.previewScheme,
|
|
25
|
+
activeColors: defaultThemeTokens.colors[defaultThemeTokens.colorSystem.previewScheme],
|
|
26
|
+
});
|
|
27
|
+
const AppThemeSetterContext = createContext<Dispatch<SetStateAction<StylistThemeTokens>> | null>(
|
|
28
|
+
null
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
export function AppThemeProvider({ children }: { children: ReactNode }) {
|
|
32
|
+
const [theme, setTheme] = useState<StylistThemeTokens>(defaultThemeTokens);
|
|
33
|
+
const value = useMemo<AppThemeValue>(() => {
|
|
34
|
+
const activeScheme = theme.colorSystem.previewScheme;
|
|
35
|
+
return {
|
|
36
|
+
...theme,
|
|
37
|
+
activeScheme,
|
|
38
|
+
activeColors: theme.colors[activeScheme],
|
|
39
|
+
};
|
|
40
|
+
}, [theme]);
|
|
41
|
+
|
|
42
|
+
return (
|
|
43
|
+
<AppThemeSetterContext.Provider value={setTheme}>
|
|
44
|
+
<AppThemeContext.Provider value={value}>{children}</AppThemeContext.Provider>
|
|
45
|
+
</AppThemeSetterContext.Provider>
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export function useAppTheme() {
|
|
50
|
+
return useContext(AppThemeContext);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export function useSetAppTheme() {
|
|
54
|
+
const setTheme = useContext(AppThemeSetterContext);
|
|
55
|
+
if (!setTheme) {
|
|
56
|
+
throw new Error('useSetAppTheme must be used inside AppThemeProvider.');
|
|
57
|
+
}
|
|
58
|
+
return setTheme;
|
|
59
|
+
}
|