@ramonclaudio/create-vexpo 0.1.0 → 0.1.2
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 -10
- package/dist/index.js +8 -7
- package/dist/templates/default/.eas/workflows/asc-events.yml +9 -6
- package/dist/templates/default/.eas/workflows/deploy-production.yml +28 -15
- package/dist/templates/default/.eas/workflows/e2e-tests.yml +3 -2
- package/dist/templates/default/.eas/workflows/pr-preview.yml +12 -21
- package/dist/templates/default/.eas/workflows/release.yml +3 -7
- package/dist/templates/default/.eas/workflows/rollback.yml +54 -28
- package/dist/templates/default/.eas/workflows/rollout.yml +27 -33
- package/dist/templates/default/.eas/workflows/rotate-apple-jwt.yml +1 -5
- package/dist/templates/default/.eas/workflows/testflight.yml +3 -7
- package/dist/templates/default/.github/workflows/check.yml +20 -12
- package/dist/templates/default/.maestro/launch.yaml +19 -10
- package/dist/templates/default/AGENTS.md +25 -8
- package/dist/templates/default/DESIGN.md +14 -10
- package/dist/templates/default/README.md +83 -78
- package/dist/templates/default/SETUP.md +159 -152
- package/dist/templates/default/__tests__/convex/_auth-harness.test.ts +112 -0
- package/dist/templates/default/__tests__/convex/_harness.ts +132 -0
- package/dist/templates/default/__tests__/convex/appAttest.test.ts +172 -0
- package/dist/templates/default/__tests__/convex/appAttestStore.test.ts +48 -0
- package/dist/templates/default/__tests__/convex/pushTokens-remove.test.ts +106 -0
- package/dist/templates/default/__tests__/convex/pushTokens-upsert.test.ts +146 -0
- package/dist/templates/default/__tests__/convex/users-deleteAccount.test.ts +140 -0
- package/dist/templates/default/__tests__/convex/users-deleteAvatar.test.ts +104 -0
- package/dist/templates/default/__tests__/convex/users-getMe.test.ts +98 -0
- package/dist/templates/default/__tests__/convex/users-getUser.test.ts +120 -0
- package/dist/templates/default/__tests__/convex/users-hardDeleteExpired.test.ts +67 -0
- package/dist/templates/default/__tests__/convex/users-restoreAccount.test.ts +96 -0
- package/dist/templates/default/__tests__/convex/users-updateAvatar.test.ts +92 -0
- package/dist/templates/default/__tests__/convex/users-updateProfile.test.ts +126 -0
- package/dist/templates/default/__tests__/convex/webhook.test.ts +31 -0
- package/dist/templates/default/__tests__/lib/deep-link.test.ts +51 -6
- package/dist/templates/default/__tests__/lib/schemas.test.ts +205 -0
- package/dist/templates/default/__tests__/lib/text-style.test.ts +31 -0
- package/dist/templates/default/_env.example +7 -7
- package/dist/templates/default/_gitattributes +1 -1
- package/dist/templates/default/_gitignore +17 -2
- package/dist/templates/default/_npmrc +7 -0
- package/dist/templates/default/_oxlintrc.json +1 -1
- package/dist/templates/default/app-store/accessibility.config.json +20 -0
- package/dist/templates/default/app-store/privacy.config.json +27 -0
- package/dist/templates/default/app.config.ts +105 -33
- package/dist/templates/default/app.json +1 -9
- package/dist/templates/default/convex/_generated/api.d.ts +12 -0
- package/dist/templates/default/convex/admin.ts +0 -13
- package/dist/templates/default/convex/appAttest.ts +467 -0
- package/dist/templates/default/convex/appAttestStore.ts +141 -0
- package/dist/templates/default/convex/apple.ts +53 -0
- package/dist/templates/default/convex/auth.ts +6 -45
- package/dist/templates/default/convex/constants.ts +2 -7
- package/dist/templates/default/convex/crons.ts +12 -5
- package/dist/templates/default/convex/email.ts +4 -24
- package/dist/templates/default/convex/env.ts +0 -4
- package/dist/templates/default/convex/errors.ts +0 -7
- package/dist/templates/default/convex/functions.ts +0 -26
- package/dist/templates/default/convex/http.ts +3 -5
- package/dist/templates/default/convex/log.ts +2 -25
- package/dist/templates/default/convex/pushSender.ts +145 -0
- package/dist/templates/default/convex/pushTokens.ts +110 -13
- package/dist/templates/default/convex/rateLimit.ts +8 -39
- package/dist/templates/default/convex/schema.ts +48 -5
- package/dist/templates/default/convex/tsconfig.json +1 -0
- package/dist/templates/default/convex/users.ts +143 -61
- package/dist/templates/default/convex/validators.ts +1 -38
- package/dist/templates/default/convex/webhook.ts +1 -31
- package/dist/templates/default/convex.json +1 -2
- package/dist/templates/default/metro.config.js +9 -1
- package/dist/templates/default/package.json +67 -70
- package/dist/templates/default/plugins/README.md +5 -1
- package/dist/templates/default/scripts/README.md +9 -9
- package/dist/templates/default/scripts/_run.mjs +3 -20
- package/dist/templates/default/scripts/clean.ts +81 -69
- package/dist/templates/default/scripts/gen-update-cert.mjs +98 -0
- package/dist/templates/default/{app → src/app}/(app)/(tabs)/(home)/index.tsx +21 -6
- package/dist/templates/default/{app → src/app}/(app)/(tabs)/(home,search)/_layout.tsx +9 -8
- package/dist/templates/default/{app → src/app}/(app)/(tabs)/(search)/index.tsx +26 -24
- package/dist/templates/default/{app → src/app}/(app)/(tabs)/_layout.tsx +3 -4
- package/dist/templates/default/{app → src/app}/(app)/(tabs)/settings/_layout.tsx +10 -6
- package/dist/templates/default/{app → src/app}/(app)/(tabs)/settings/index.tsx +81 -51
- package/dist/templates/default/{app → src/app}/(app)/(tabs)/settings/preferences.tsx +72 -12
- package/dist/templates/default/src/app/(app)/_layout.tsx +147 -0
- package/dist/templates/default/{app/(auth) → src/app/(app)/auth}/_layout.tsx +4 -5
- package/dist/templates/default/{app/(auth) → src/app/(app)/auth}/forgot-password.tsx +15 -9
- package/dist/templates/default/{app/(auth) → src/app/(app)/auth}/reset-password.tsx +88 -14
- package/dist/templates/default/{app/(auth) → src/app/(app)/auth}/sign-in.tsx +65 -35
- package/dist/templates/default/{app/(auth) → src/app/(app)/auth}/sign-up.tsx +131 -196
- package/dist/templates/default/src/app/(app)/debug.tsx +479 -0
- package/dist/templates/default/{app → src/app}/(app)/help.tsx +76 -64
- package/dist/templates/default/{app → src/app}/(app)/linked.tsx +21 -27
- package/dist/templates/default/{app → src/app}/(app)/privacy.tsx +35 -8
- package/dist/templates/default/src/app/(app)/profile/change-password.tsx +264 -0
- package/dist/templates/default/{app/(app)/profile.tsx → src/app/(app)/profile/index.tsx} +179 -255
- package/dist/templates/default/src/app/(app)/restore-account.tsx +192 -0
- package/dist/templates/default/src/app/(app)/sessions.tsx +287 -0
- package/dist/templates/default/src/app/(app)/welcome.tsx +194 -0
- package/dist/templates/default/src/app/+native-intent.tsx +25 -0
- package/dist/templates/default/src/app/+not-found.tsx +43 -0
- package/dist/templates/default/{app → src/app}/_layout.tsx +28 -37
- package/dist/templates/default/src/components/auth/apple-button.tsx +51 -0
- package/dist/templates/default/{components → src/components}/auth/otp-verification.tsx +79 -58
- package/dist/templates/default/{components → src/components}/auth/password-field.tsx +74 -18
- package/dist/templates/default/src/components/auth/segmented-toggle.tsx +71 -0
- package/dist/templates/default/src/components/ui/content-unavailable.tsx +81 -0
- package/dist/templates/default/src/components/ui/convex-error.tsx +21 -0
- package/dist/templates/default/src/components/ui/error-boundary.tsx +89 -0
- package/dist/templates/default/{components → src/components}/ui/loading-screen.tsx +5 -4
- package/dist/templates/default/{components → src/components}/ui/material.tsx +50 -17
- package/dist/templates/default/src/components/ui/offline-banner.tsx +59 -0
- package/dist/templates/default/{components → src/components}/ui/prominent-button.tsx +8 -11
- package/dist/templates/default/{components → src/components}/ui/skeleton.tsx +31 -13
- package/dist/templates/default/src/components/ui/status-text.tsx +64 -0
- package/dist/templates/default/src/components/ui/update-banner.tsx +85 -0
- package/dist/templates/default/{constants → src/constants}/layout.ts +0 -6
- package/dist/templates/default/{constants → src/constants}/theme.ts +49 -64
- package/dist/templates/default/{constants → src/constants}/ui.ts +13 -4
- package/dist/templates/default/src/hooks/use-debounce.ts +12 -0
- package/dist/templates/default/src/hooks/use-deep-link.ts +51 -0
- package/dist/templates/default/src/hooks/use-delete-account.ts +35 -0
- package/dist/templates/default/src/hooks/use-motion-screen-options.ts +13 -0
- package/dist/templates/default/src/hooks/use-network.ts +34 -0
- package/dist/templates/default/{hooks → src/hooks}/use-notifications.ts +39 -30
- package/dist/templates/default/src/hooks/use-reduce-transparency.ts +30 -0
- package/dist/templates/default/{hooks → src/hooks}/use-theme.ts +0 -5
- package/dist/templates/default/src/lib/appAttest.ts +78 -0
- package/dist/templates/default/src/lib/assets.ts +9 -0
- package/dist/templates/default/src/lib/deep-link.ts +82 -0
- package/dist/templates/default/{lib → src/lib}/dev-menu.ts +0 -4
- package/dist/templates/default/{lib → src/lib}/device.ts +1 -13
- package/dist/templates/default/{lib → src/lib}/dynamic-font.ts +13 -10
- package/dist/templates/default/src/lib/dynamic-symbol-size.ts +33 -0
- package/dist/templates/default/src/lib/masks.ts +21 -0
- package/dist/templates/default/src/lib/native-state.ts +20 -0
- package/dist/templates/default/{lib → src/lib}/notifications.ts +7 -45
- package/dist/templates/default/{lib → src/lib}/preferences.ts +0 -2
- package/dist/templates/default/{lib → src/lib}/schemas.ts +19 -16
- package/dist/templates/default/src/lib/text-style.ts +20 -0
- package/dist/templates/default/{lib → src/lib}/updates.ts +0 -7
- package/dist/templates/default/store.config.json +1 -1
- package/dist/templates/default/tsconfig.json +3 -1
- package/dist/templates/default/vitest.config.ts +8 -1
- package/package.json +5 -5
- package/dist/templates/default/app/(app)/_layout.tsx +0 -73
- package/dist/templates/default/app/(app)/debug.tsx +0 -389
- package/dist/templates/default/app/(app)/sessions.tsx +0 -191
- package/dist/templates/default/app/(app)/welcome.tsx +0 -140
- package/dist/templates/default/app/+native-intent.tsx +0 -14
- package/dist/templates/default/app/+not-found.tsx +0 -51
- package/dist/templates/default/bun.lock +0 -1860
- package/dist/templates/default/components/auth/segmented-toggle.tsx +0 -47
- package/dist/templates/default/components/ui/convex-error.tsx +0 -32
- package/dist/templates/default/components/ui/error-boundary.tsx +0 -57
- package/dist/templates/default/components/ui/offline-banner.tsx +0 -58
- package/dist/templates/default/components/ui/status-text.tsx +0 -49
- package/dist/templates/default/components/ui/update-banner.tsx +0 -82
- package/dist/templates/default/fingerprint.config.js +0 -9
- package/dist/templates/default/hooks/use-debounce.ts +0 -20
- package/dist/templates/default/hooks/use-deep-link.ts +0 -43
- package/dist/templates/default/hooks/use-network.ts +0 -11
- package/dist/templates/default/lib/assets.ts +0 -17
- package/dist/templates/default/lib/deep-link.ts +0 -71
- package/dist/templates/default/patches/PR-368.patch +0 -91
- package/dist/templates/default/patches/convex-dev-better-auth-0.12.2.tgz +0 -0
- /package/dist/templates/default/{hooks → src/hooks}/use-navigation-tracking.ts +0 -0
- /package/dist/templates/default/{hooks → src/hooks}/use-onboarding.ts +0 -0
- /package/dist/templates/default/{hooks → src/hooks}/use-reduced-motion.ts +0 -0
- /package/dist/templates/default/{hooks → src/hooks}/use-updates.ts +0 -0
- /package/dist/templates/default/{lib → src/lib}/a11y.ts +0 -0
- /package/dist/templates/default/{lib → src/lib}/app.ts +0 -0
- /package/dist/templates/default/{lib → src/lib}/auth-client.ts +0 -0
- /package/dist/templates/default/{lib → src/lib}/convex-auth.tsx +0 -0
- /package/dist/templates/default/{lib → src/lib}/env.ts +0 -0
- /package/dist/templates/default/{lib → src/lib}/haptics.ts +0 -0
- /package/dist/templates/default/{lib → src/lib}/storage.ts +0 -0
|
@@ -11,20 +11,22 @@ import {
|
|
|
11
11
|
Spacer,
|
|
12
12
|
Image,
|
|
13
13
|
Text,
|
|
14
|
-
|
|
14
|
+
DisclosureGroup,
|
|
15
15
|
} from "@expo/ui/swift-ui";
|
|
16
16
|
import {
|
|
17
|
+
accessibilityHidden,
|
|
17
18
|
background,
|
|
18
19
|
buttonStyle,
|
|
19
20
|
clipShape,
|
|
20
21
|
cornerRadius,
|
|
21
22
|
foregroundStyle,
|
|
22
23
|
frame,
|
|
23
|
-
onTapGesture,
|
|
24
24
|
padding,
|
|
25
25
|
tint,
|
|
26
26
|
} from "@expo/ui/swift-ui/modifiers";
|
|
27
27
|
import { useDynamicFont } from "@/lib/dynamic-font";
|
|
28
|
+
import { useSymbolSize } from "@/lib/dynamic-symbol-size";
|
|
29
|
+
import { ContentUnavailable } from "@/components/ui/content-unavailable";
|
|
28
30
|
import { Button as ButtonTokens } from "@/constants/layout";
|
|
29
31
|
|
|
30
32
|
import { ErrorText } from "@/components/ui/status-text";
|
|
@@ -41,10 +43,12 @@ const support = (Constants.expoConfig?.extra?.support ?? {}) as SupportConfig;
|
|
|
41
43
|
|
|
42
44
|
const FAQ_ITEMS = [
|
|
43
45
|
{
|
|
46
|
+
id: "delete-account",
|
|
44
47
|
question: "How do I delete my account?",
|
|
45
48
|
answer: "Go to Settings, then Delete Account. This will permanently remove all your data.",
|
|
46
49
|
},
|
|
47
50
|
{
|
|
51
|
+
id: "notifications",
|
|
48
52
|
question: "Why aren't notifications working?",
|
|
49
53
|
answer:
|
|
50
54
|
"Make sure notifications are enabled in Settings, then Notifications. You must use a physical device.",
|
|
@@ -53,10 +57,15 @@ const FAQ_ITEMS = [
|
|
|
53
57
|
|
|
54
58
|
export default function HelpScreen() {
|
|
55
59
|
const dfont = useDynamicFont();
|
|
60
|
+
const symbolSize = useSymbolSize();
|
|
56
61
|
const colors = useColors();
|
|
57
62
|
const [searchText, setSearchText] = useState("");
|
|
58
63
|
const [expanded, setExpanded] = useState<Record<string, boolean>>({});
|
|
59
|
-
const [
|
|
64
|
+
const [linkError, setLinkError] = useState<string | null>(null);
|
|
65
|
+
const toggleExpanded = (question: string, next: boolean) => {
|
|
66
|
+
haptics.selection();
|
|
67
|
+
setExpanded((m) => ({ ...m, [question]: next }));
|
|
68
|
+
};
|
|
60
69
|
|
|
61
70
|
const filteredFaq = searchText
|
|
62
71
|
? FAQ_ITEMS.filter(
|
|
@@ -68,41 +77,51 @@ export default function HelpScreen() {
|
|
|
68
77
|
|
|
69
78
|
const issuesUrl = support.issuesUrl || support.githubUrl;
|
|
70
79
|
|
|
71
|
-
const handleOpenIssues = () => {
|
|
80
|
+
const handleOpenIssues = async () => {
|
|
72
81
|
if (!issuesUrl) return;
|
|
73
82
|
haptics.light();
|
|
74
|
-
|
|
83
|
+
setLinkError(null);
|
|
84
|
+
const canOpen = await canOpenURL(issuesUrl);
|
|
85
|
+
if (canOpen) {
|
|
86
|
+
openURL(issuesUrl);
|
|
87
|
+
} else {
|
|
88
|
+
haptics.error();
|
|
89
|
+
setLinkError("Couldn't open the issues page.");
|
|
90
|
+
}
|
|
75
91
|
};
|
|
76
92
|
|
|
77
93
|
const handleOpenEmail = async () => {
|
|
78
94
|
if (!support.email) return;
|
|
79
95
|
haptics.light();
|
|
80
|
-
|
|
81
|
-
const url = `mailto:${support.email}?subject
|
|
96
|
+
setLinkError(null);
|
|
97
|
+
const url = `mailto:${support.email}?subject=${encodeURIComponent("App Support")}`;
|
|
82
98
|
const canOpen = await canOpenURL(url);
|
|
83
99
|
if (canOpen) {
|
|
84
100
|
openURL(url);
|
|
85
101
|
} else {
|
|
86
102
|
haptics.error();
|
|
87
|
-
|
|
103
|
+
setLinkError(`No email app configured. Contact ${support.email} directly.`);
|
|
88
104
|
}
|
|
89
105
|
};
|
|
90
106
|
|
|
91
107
|
type SFSymbol = NonNullable<ComponentProps<typeof Image>["systemName"]>;
|
|
92
108
|
|
|
93
109
|
const rowButton = ({
|
|
110
|
+
testID,
|
|
94
111
|
label,
|
|
95
112
|
systemImage,
|
|
96
113
|
onPress,
|
|
97
114
|
}: {
|
|
115
|
+
testID: string;
|
|
98
116
|
label: string;
|
|
99
117
|
systemImage: SFSymbol;
|
|
100
118
|
onPress: () => void;
|
|
101
119
|
}) => (
|
|
102
120
|
<Button
|
|
121
|
+
testID={testID}
|
|
103
122
|
modifiers={[
|
|
104
123
|
buttonStyle("plain"),
|
|
105
|
-
frame({ maxWidth:
|
|
124
|
+
frame({ maxWidth: Infinity }),
|
|
106
125
|
background(colors.muted as string),
|
|
107
126
|
clipShape("capsule"),
|
|
108
127
|
]}
|
|
@@ -112,11 +131,16 @@ export default function HelpScreen() {
|
|
|
112
131
|
spacing={12}
|
|
113
132
|
alignment="center"
|
|
114
133
|
modifiers={[
|
|
115
|
-
frame({ maxWidth:
|
|
134
|
+
frame({ maxWidth: Infinity, minHeight: ButtonTokens.height }),
|
|
116
135
|
padding({ horizontal: 16 }),
|
|
117
136
|
]}
|
|
118
137
|
>
|
|
119
|
-
<Image
|
|
138
|
+
<Image
|
|
139
|
+
systemName={systemImage}
|
|
140
|
+
size={symbolSize(18)}
|
|
141
|
+
color={colors.foreground as string}
|
|
142
|
+
modifiers={[accessibilityHidden(true)]}
|
|
143
|
+
/>
|
|
120
144
|
<Text
|
|
121
145
|
modifiers={[
|
|
122
146
|
dfont({ size: 16, weight: "medium" }),
|
|
@@ -126,7 +150,12 @@ export default function HelpScreen() {
|
|
|
126
150
|
{label}
|
|
127
151
|
</Text>
|
|
128
152
|
<Spacer />
|
|
129
|
-
<Image
|
|
153
|
+
<Image
|
|
154
|
+
systemName="chevron.right"
|
|
155
|
+
size={symbolSize(13)}
|
|
156
|
+
color={colors.mutedForeground as string}
|
|
157
|
+
modifiers={[accessibilityHidden(true)]}
|
|
158
|
+
/>
|
|
130
159
|
</HStack>
|
|
131
160
|
</Button>
|
|
132
161
|
);
|
|
@@ -148,19 +177,20 @@ export default function HelpScreen() {
|
|
|
148
177
|
/>
|
|
149
178
|
</Stack.Toolbar>
|
|
150
179
|
) : null}
|
|
151
|
-
<Host style={{ flex: 1, backgroundColor: colors.background }}>
|
|
180
|
+
<Host testID="help-screen" style={{ flex: 1, backgroundColor: colors.background }}>
|
|
152
181
|
<ScrollView modifiers={[tint(colors.primary as string)]}>
|
|
153
182
|
<VStack
|
|
154
183
|
spacing={12}
|
|
155
184
|
alignment="leading"
|
|
156
185
|
modifiers={[padding({ horizontal: 24, top: 24, bottom: 40 })]}
|
|
157
186
|
>
|
|
158
|
-
{
|
|
187
|
+
{linkError ? <ErrorText testID="help-link-error">{linkError}</ErrorText> : null}
|
|
159
188
|
|
|
160
189
|
{(support.email || issuesUrl) && (
|
|
161
190
|
<VStack spacing={8} modifiers={[frame({ maxWidth: Infinity })]}>
|
|
162
191
|
{support.email
|
|
163
192
|
? rowButton({
|
|
193
|
+
testID: "help-email-support",
|
|
164
194
|
label: "Email Support",
|
|
165
195
|
systemImage: "envelope.fill",
|
|
166
196
|
onPress: handleOpenEmail,
|
|
@@ -168,6 +198,7 @@ export default function HelpScreen() {
|
|
|
168
198
|
: null}
|
|
169
199
|
{issuesUrl
|
|
170
200
|
? rowButton({
|
|
201
|
+
testID: "help-report-issue",
|
|
171
202
|
label: "Report an Issue",
|
|
172
203
|
systemImage: "exclamationmark.bubble.fill",
|
|
173
204
|
onPress: handleOpenIssues,
|
|
@@ -177,7 +208,8 @@ export default function HelpScreen() {
|
|
|
177
208
|
)}
|
|
178
209
|
|
|
179
210
|
{filteredFaq.length === 0 ? (
|
|
180
|
-
<
|
|
211
|
+
<ContentUnavailable
|
|
212
|
+
testID="help-faq-empty"
|
|
181
213
|
title="No results"
|
|
182
214
|
systemImage="magnifyingglass"
|
|
183
215
|
description="Try a different search term"
|
|
@@ -185,6 +217,7 @@ export default function HelpScreen() {
|
|
|
185
217
|
) : (
|
|
186
218
|
<VStack spacing={8} modifiers={[frame({ maxWidth: Infinity })]}>
|
|
187
219
|
<Text
|
|
220
|
+
testID="help-faq-heading"
|
|
188
221
|
modifiers={[
|
|
189
222
|
dfont({ size: 13, weight: "semibold" }),
|
|
190
223
|
foregroundStyle(colors.mutedForeground as string),
|
|
@@ -193,57 +226,36 @@ export default function HelpScreen() {
|
|
|
193
226
|
>
|
|
194
227
|
FREQUENTLY ASKED
|
|
195
228
|
</Text>
|
|
196
|
-
{filteredFaq.map((item) =>
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
]}
|
|
229
|
+
{filteredFaq.map((item) => (
|
|
230
|
+
<VStack
|
|
231
|
+
key={item.question}
|
|
232
|
+
alignment="leading"
|
|
233
|
+
modifiers={[
|
|
234
|
+
frame({ maxWidth: Infinity }),
|
|
235
|
+
padding({ horizontal: 20, vertical: 4 }),
|
|
236
|
+
background(colors.muted as string),
|
|
237
|
+
cornerRadius(20),
|
|
238
|
+
]}
|
|
239
|
+
>
|
|
240
|
+
<DisclosureGroup
|
|
241
|
+
testID={`help-faq-${item.id}`}
|
|
242
|
+
label={item.question}
|
|
243
|
+
isExpanded={!!expanded[item.question]}
|
|
244
|
+
onIsExpandedChange={(v) => toggleExpanded(item.question, v)}
|
|
245
|
+
modifiers={[dfont({ size: 16, weight: "medium" })]}
|
|
213
246
|
>
|
|
214
|
-
<
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
247
|
+
<Text
|
|
248
|
+
modifiers={[
|
|
249
|
+
dfont({ size: 14 }),
|
|
250
|
+
foregroundStyle(colors.mutedForeground as string),
|
|
251
|
+
padding({ vertical: 8 }),
|
|
252
|
+
]}
|
|
218
253
|
>
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
>
|
|
225
|
-
{item.question}
|
|
226
|
-
</Text>
|
|
227
|
-
<Spacer />
|
|
228
|
-
<Image
|
|
229
|
-
systemName={isOpen ? "chevron.up" : "chevron.down"}
|
|
230
|
-
size={13}
|
|
231
|
-
color={colors.mutedForeground as string}
|
|
232
|
-
/>
|
|
233
|
-
</HStack>
|
|
234
|
-
{isOpen ? (
|
|
235
|
-
<Text
|
|
236
|
-
modifiers={[
|
|
237
|
-
dfont({ size: 14 }),
|
|
238
|
-
foregroundStyle(colors.mutedForeground as string),
|
|
239
|
-
]}
|
|
240
|
-
>
|
|
241
|
-
{item.answer}
|
|
242
|
-
</Text>
|
|
243
|
-
) : null}
|
|
244
|
-
</VStack>
|
|
245
|
-
);
|
|
246
|
-
})}
|
|
254
|
+
{item.answer}
|
|
255
|
+
</Text>
|
|
256
|
+
</DisclosureGroup>
|
|
257
|
+
</VStack>
|
|
258
|
+
))}
|
|
247
259
|
</VStack>
|
|
248
260
|
)}
|
|
249
261
|
</VStack>
|
|
@@ -1,13 +1,5 @@
|
|
|
1
1
|
import { useLocalSearchParams } from "expo-router";
|
|
2
|
-
import {
|
|
3
|
-
Host,
|
|
4
|
-
ScrollView,
|
|
5
|
-
Text,
|
|
6
|
-
VStack,
|
|
7
|
-
HStack,
|
|
8
|
-
Spacer,
|
|
9
|
-
ContentUnavailableView,
|
|
10
|
-
} from "@expo/ui/swift-ui";
|
|
2
|
+
import { Host, ScrollView, Text, VStack, LabeledContent } from "@expo/ui/swift-ui";
|
|
11
3
|
import {
|
|
12
4
|
background,
|
|
13
5
|
cornerRadius,
|
|
@@ -20,6 +12,7 @@ import {
|
|
|
20
12
|
} from "@expo/ui/swift-ui/modifiers";
|
|
21
13
|
import { useDynamicFont } from "@/lib/dynamic-font";
|
|
22
14
|
|
|
15
|
+
import { ContentUnavailable } from "@/components/ui/content-unavailable";
|
|
23
16
|
import { useColors } from "@/hooks/use-theme";
|
|
24
17
|
|
|
25
18
|
export default function LinkedScreen() {
|
|
@@ -30,7 +23,7 @@ export default function LinkedScreen() {
|
|
|
30
23
|
const entries = Object.entries(params).filter(([, v]) => v != null);
|
|
31
24
|
|
|
32
25
|
return (
|
|
33
|
-
<Host style={{ flex: 1, backgroundColor: colors.background }}>
|
|
26
|
+
<Host testID="linked-screen" style={{ flex: 1, backgroundColor: colors.background }}>
|
|
34
27
|
<ScrollView
|
|
35
28
|
modifiers={[scrollDismissesKeyboard("interactively"), tint(colors.primary as string)]}
|
|
36
29
|
>
|
|
@@ -40,7 +33,7 @@ export default function LinkedScreen() {
|
|
|
40
33
|
modifiers={[padding({ horizontal: 24, top: 24, bottom: 40 })]}
|
|
41
34
|
>
|
|
42
35
|
<VStack spacing={6} alignment="leading">
|
|
43
|
-
<Text modifiers={[dfont({ size: 22, weight: "bold" })]}>
|
|
36
|
+
<Text testID="linked-title" modifiers={[dfont({ size: 22, weight: "bold" })]}>
|
|
44
37
|
You got here via a deep link
|
|
45
38
|
</Text>
|
|
46
39
|
<Text
|
|
@@ -65,31 +58,31 @@ export default function LinkedScreen() {
|
|
|
65
58
|
spacing={0}
|
|
66
59
|
alignment="leading"
|
|
67
60
|
modifiers={[
|
|
68
|
-
frame({ maxWidth:
|
|
61
|
+
frame({ maxWidth: Infinity }),
|
|
69
62
|
background(colors.muted as string),
|
|
70
63
|
cornerRadius(20),
|
|
71
64
|
]}
|
|
72
65
|
>
|
|
73
66
|
{entries.map(([key, value]) => (
|
|
74
|
-
<
|
|
67
|
+
<LabeledContent
|
|
75
68
|
key={key}
|
|
76
|
-
|
|
77
|
-
|
|
69
|
+
label={
|
|
70
|
+
<Text
|
|
71
|
+
modifiers={[
|
|
72
|
+
dfont({ size: 15 }),
|
|
73
|
+
foregroundStyle(colors.mutedForeground as string),
|
|
74
|
+
]}
|
|
75
|
+
>
|
|
76
|
+
{key}
|
|
77
|
+
</Text>
|
|
78
|
+
}
|
|
78
79
|
modifiers={[
|
|
79
|
-
frame({ maxWidth:
|
|
80
|
+
frame({ maxWidth: Infinity }),
|
|
80
81
|
padding({ horizontal: 16, vertical: 12 }),
|
|
81
82
|
]}
|
|
82
83
|
>
|
|
83
84
|
<Text
|
|
84
|
-
|
|
85
|
-
dfont({ size: 15 }),
|
|
86
|
-
foregroundStyle(colors.mutedForeground as string),
|
|
87
|
-
]}
|
|
88
|
-
>
|
|
89
|
-
{key}
|
|
90
|
-
</Text>
|
|
91
|
-
<Spacer />
|
|
92
|
-
<Text
|
|
85
|
+
testID={`linked-param-${key}`}
|
|
93
86
|
modifiers={[
|
|
94
87
|
dfont({ size: 13, design: "monospaced" }),
|
|
95
88
|
foregroundStyle(colors.foreground as string),
|
|
@@ -98,12 +91,13 @@ export default function LinkedScreen() {
|
|
|
98
91
|
>
|
|
99
92
|
{Array.isArray(value) ? value.join(", ") : String(value)}
|
|
100
93
|
</Text>
|
|
101
|
-
</
|
|
94
|
+
</LabeledContent>
|
|
102
95
|
))}
|
|
103
96
|
</VStack>
|
|
104
97
|
</VStack>
|
|
105
98
|
) : (
|
|
106
|
-
<
|
|
99
|
+
<ContentUnavailable
|
|
100
|
+
testID="linked-empty"
|
|
107
101
|
title="No params"
|
|
108
102
|
systemImage="link.badge.plus"
|
|
109
103
|
description="This deep link didn't include any parameters."
|
|
@@ -12,6 +12,8 @@ import {
|
|
|
12
12
|
Toggle,
|
|
13
13
|
} from "@expo/ui/swift-ui";
|
|
14
14
|
import {
|
|
15
|
+
accessibilityHidden,
|
|
16
|
+
accessibilityLabel,
|
|
15
17
|
background,
|
|
16
18
|
buttonStyle,
|
|
17
19
|
clipShape,
|
|
@@ -21,6 +23,7 @@ import {
|
|
|
21
23
|
tint,
|
|
22
24
|
} from "@expo/ui/swift-ui/modifiers";
|
|
23
25
|
import { useDynamicFont } from "@/lib/dynamic-font";
|
|
26
|
+
import { useSymbolSize } from "@/lib/dynamic-symbol-size";
|
|
24
27
|
import { Button as ButtonTokens } from "@/constants/layout";
|
|
25
28
|
|
|
26
29
|
import { haptics } from "@/lib/haptics";
|
|
@@ -28,6 +31,7 @@ import { useColors } from "@/hooks/use-theme";
|
|
|
28
31
|
|
|
29
32
|
export default function PrivacyScreen() {
|
|
30
33
|
const dfont = useDynamicFont();
|
|
34
|
+
const symbolSize = useSymbolSize();
|
|
31
35
|
const colors = useColors();
|
|
32
36
|
const [analyticsEnabled, setAnalyticsEnabled] = useState(true);
|
|
33
37
|
|
|
@@ -39,12 +43,14 @@ export default function PrivacyScreen() {
|
|
|
39
43
|
type SFSymbol = NonNullable<ComponentProps<typeof Image>["systemName"]>;
|
|
40
44
|
|
|
41
45
|
const rowButton = ({
|
|
46
|
+
testID,
|
|
42
47
|
label,
|
|
43
48
|
systemImage,
|
|
44
49
|
onPress,
|
|
45
50
|
chevron = true,
|
|
46
51
|
trailing,
|
|
47
52
|
}: {
|
|
53
|
+
testID: string;
|
|
48
54
|
label: string;
|
|
49
55
|
systemImage: SFSymbol;
|
|
50
56
|
onPress: () => void;
|
|
@@ -52,9 +58,10 @@ export default function PrivacyScreen() {
|
|
|
52
58
|
trailing?: React.ReactNode;
|
|
53
59
|
}) => (
|
|
54
60
|
<Button
|
|
61
|
+
testID={testID}
|
|
55
62
|
modifiers={[
|
|
56
63
|
buttonStyle("plain"),
|
|
57
|
-
frame({ maxWidth:
|
|
64
|
+
frame({ maxWidth: Infinity }),
|
|
58
65
|
background(colors.muted as string),
|
|
59
66
|
clipShape("capsule"),
|
|
60
67
|
]}
|
|
@@ -64,11 +71,16 @@ export default function PrivacyScreen() {
|
|
|
64
71
|
spacing={12}
|
|
65
72
|
alignment="center"
|
|
66
73
|
modifiers={[
|
|
67
|
-
frame({ maxWidth:
|
|
74
|
+
frame({ maxWidth: Infinity, minHeight: ButtonTokens.height }),
|
|
68
75
|
padding({ horizontal: 16 }),
|
|
69
76
|
]}
|
|
70
77
|
>
|
|
71
|
-
<Image
|
|
78
|
+
<Image
|
|
79
|
+
systemName={systemImage}
|
|
80
|
+
size={symbolSize(18)}
|
|
81
|
+
color={colors.foreground as string}
|
|
82
|
+
modifiers={[accessibilityHidden(true)]}
|
|
83
|
+
/>
|
|
72
84
|
<Text
|
|
73
85
|
modifiers={[
|
|
74
86
|
dfont({ size: 16, weight: "medium" }),
|
|
@@ -80,14 +92,19 @@ export default function PrivacyScreen() {
|
|
|
80
92
|
<Spacer />
|
|
81
93
|
{trailing ??
|
|
82
94
|
(chevron ? (
|
|
83
|
-
<Image
|
|
95
|
+
<Image
|
|
96
|
+
systemName="chevron.right"
|
|
97
|
+
size={symbolSize(13)}
|
|
98
|
+
color={colors.mutedForeground as string}
|
|
99
|
+
modifiers={[accessibilityHidden(true)]}
|
|
100
|
+
/>
|
|
84
101
|
) : null)}
|
|
85
102
|
</HStack>
|
|
86
103
|
</Button>
|
|
87
104
|
);
|
|
88
105
|
|
|
89
106
|
return (
|
|
90
|
-
<Host style={{ flex: 1, backgroundColor: colors.background }}>
|
|
107
|
+
<Host testID="privacy-screen" style={{ flex: 1, backgroundColor: colors.background }}>
|
|
91
108
|
<ScrollView modifiers={[tint(colors.primary as string)]}>
|
|
92
109
|
<VStack
|
|
93
110
|
spacing={12}
|
|
@@ -96,16 +113,19 @@ export default function PrivacyScreen() {
|
|
|
96
113
|
>
|
|
97
114
|
<VStack spacing={8} modifiers={[frame({ maxWidth: Infinity })]}>
|
|
98
115
|
{rowButton({
|
|
116
|
+
testID: "privacy-camera-photos",
|
|
99
117
|
label: "Camera & Photos",
|
|
100
118
|
systemImage: "camera.fill",
|
|
101
119
|
onPress: handleOpenSettings,
|
|
102
120
|
})}
|
|
103
121
|
{rowButton({
|
|
122
|
+
testID: "privacy-notifications",
|
|
104
123
|
label: "Notifications",
|
|
105
124
|
systemImage: "bell.fill",
|
|
106
125
|
onPress: handleOpenSettings,
|
|
107
126
|
})}
|
|
108
127
|
{rowButton({
|
|
128
|
+
testID: "privacy-system-settings",
|
|
109
129
|
label: "System Settings",
|
|
110
130
|
systemImage: "gear",
|
|
111
131
|
onPress: handleOpenSettings,
|
|
@@ -117,13 +137,18 @@ export default function PrivacyScreen() {
|
|
|
117
137
|
spacing={12}
|
|
118
138
|
alignment="center"
|
|
119
139
|
modifiers={[
|
|
120
|
-
frame({ maxWidth:
|
|
140
|
+
frame({ maxWidth: Infinity, minHeight: ButtonTokens.height }),
|
|
121
141
|
padding({ horizontal: 16 }),
|
|
122
142
|
background(colors.muted as string),
|
|
123
143
|
clipShape("capsule"),
|
|
124
144
|
]}
|
|
125
145
|
>
|
|
126
|
-
<Image
|
|
146
|
+
<Image
|
|
147
|
+
systemName="chart.bar.fill"
|
|
148
|
+
size={symbolSize(18)}
|
|
149
|
+
color={colors.foreground as string}
|
|
150
|
+
modifiers={[accessibilityHidden(true)]}
|
|
151
|
+
/>
|
|
127
152
|
<Text
|
|
128
153
|
modifiers={[
|
|
129
154
|
dfont({ size: 16, weight: "medium" }),
|
|
@@ -134,16 +159,18 @@ export default function PrivacyScreen() {
|
|
|
134
159
|
</Text>
|
|
135
160
|
<Spacer />
|
|
136
161
|
<Toggle
|
|
162
|
+
testID="privacy-share-analytics"
|
|
137
163
|
isOn={analyticsEnabled}
|
|
138
164
|
onIsOnChange={(v) => {
|
|
139
165
|
haptics.selection();
|
|
140
166
|
setAnalyticsEnabled(v);
|
|
141
167
|
}}
|
|
142
|
-
modifiers={[tint(colors.primary as string)]}
|
|
168
|
+
modifiers={[tint(colors.primary as string), accessibilityLabel("Share analytics")]}
|
|
143
169
|
/>
|
|
144
170
|
</HStack>
|
|
145
171
|
|
|
146
172
|
<Text
|
|
173
|
+
testID="privacy-data-disclaimer"
|
|
147
174
|
modifiers={[
|
|
148
175
|
dfont({ size: 13 }),
|
|
149
176
|
foregroundStyle(colors.mutedForeground as string),
|