@entropy-softworks/ui 2026.9.11 → 2026.910.1
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 +2 -2
- package/lib/components/error-boundary.d.ts.map +1 -1
- package/lib/components/error-boundary.jsx +3 -1
- package/lib/components/error-boundary.jsx.map +1 -1
- package/lib/components/password-generator.d.ts.map +1 -1
- package/lib/components/password-generator.jsx +15 -5
- package/lib/components/password-generator.jsx.map +1 -1
- package/lib/components/settings-row.d.ts.map +1 -1
- package/lib/components/settings-row.jsx +3 -1
- package/lib/components/settings-row.jsx.map +1 -1
- package/lib/hooks/usePasswordGenerator.d.ts.map +1 -1
- package/lib/hooks/usePasswordGenerator.js +1 -1
- package/lib/hooks/usePasswordGenerator.js.map +1 -1
- package/lib/screens/auth/AccountPickerScreen.d.ts.map +1 -1
- package/lib/screens/auth/AccountPickerScreen.jsx +1 -3
- package/lib/screens/auth/AccountPickerScreen.jsx.map +1 -1
- package/lib/screens/auth/AuthScreen.d.ts +8 -3
- package/lib/screens/auth/AuthScreen.d.ts.map +1 -1
- package/lib/screens/auth/AuthScreen.jsx +29 -14
- package/lib/screens/auth/AuthScreen.jsx.map +1 -1
- package/lib/screens/auth/ConsentScreen.jsx.map +1 -1
- package/lib/screens/auth/ErrorScreen.jsx.map +1 -1
- package/lib/screens/auth/ExpiredLinkScreen.d.ts.map +1 -1
- package/lib/screens/auth/ExpiredLinkScreen.jsx +1 -1
- package/lib/screens/auth/ExpiredLinkScreen.jsx.map +1 -1
- package/lib/screens/auth/ForgotPasswordScreen.d.ts.map +1 -1
- package/lib/screens/auth/ForgotPasswordScreen.jsx.map +1 -1
- package/lib/screens/auth/ImportDataScreen.jsx.map +1 -1
- package/lib/screens/auth/LoginScreen.d.ts.map +1 -1
- package/lib/screens/auth/LoginScreen.jsx.map +1 -1
- package/lib/screens/auth/MfaChallengeScreen.d.ts.map +1 -1
- package/lib/screens/auth/MfaChallengeScreen.jsx.map +1 -1
- package/lib/screens/auth/PasskeyEnrollScreen.d.ts.map +1 -1
- package/lib/screens/auth/PasskeyEnrollScreen.jsx +3 -5
- package/lib/screens/auth/PasskeyEnrollScreen.jsx.map +1 -1
- package/lib/screens/auth/PrivacyPolicyScreen.d.ts.map +1 -1
- package/lib/screens/auth/PrivacyPolicyScreen.jsx.map +1 -1
- package/lib/screens/auth/ResetPasswordScreen.d.ts.map +1 -1
- package/lib/screens/auth/ResetPasswordScreen.jsx.map +1 -1
- package/lib/screens/auth/ServerConfigScreen.d.ts.map +1 -1
- package/lib/screens/auth/ServerConfigScreen.jsx +15 -5
- package/lib/screens/auth/ServerConfigScreen.jsx.map +1 -1
- package/lib/screens/auth/TermsOfServiceScreen.d.ts.map +1 -1
- package/lib/screens/auth/TermsOfServiceScreen.jsx.map +1 -1
- package/lib/screens/auth/WelcomeScreen.d.ts.map +1 -1
- package/lib/screens/auth/WelcomeScreen.jsx.map +1 -1
- package/package.json +1 -1
- package/src/components/error-boundary.tsx +3 -1
- package/src/components/password-generator.tsx +15 -5
- package/src/components/settings-row.tsx +3 -1
- package/src/hooks/usePasswordGenerator.ts +8 -1
- package/src/screens/auth/AccountPickerScreen.tsx +2 -4
- package/src/screens/auth/AuthScreen.tsx +41 -21
- package/src/screens/auth/ConsentScreen.tsx +2 -2
- package/src/screens/auth/ErrorScreen.tsx +2 -2
- package/src/screens/auth/ExpiredLinkScreen.tsx +3 -9
- package/src/screens/auth/ForgotPasswordScreen.tsx +6 -4
- package/src/screens/auth/ImportDataScreen.tsx +1 -1
- package/src/screens/auth/LoginScreen.tsx +13 -6
- package/src/screens/auth/MfaChallengeScreen.tsx +4 -1
- package/src/screens/auth/PasskeyEnrollScreen.tsx +5 -7
- package/src/screens/auth/PrivacyPolicyScreen.tsx +9 -3
- package/src/screens/auth/ResetPasswordScreen.tsx +6 -4
- package/src/screens/auth/ServerConfigScreen.tsx +19 -6
- package/src/screens/auth/TermsOfServiceScreen.tsx +9 -3
- package/src/screens/auth/WelcomeScreen.tsx +11 -4
- package/src/services/telemetry.ts +3 -3
|
@@ -75,9 +75,7 @@ export function AccountPickerScreen({
|
|
|
75
75
|
accessibilityLabel={`Continue as ${a.username}`}
|
|
76
76
|
className="rounded-md border border-border px-3 py-3"
|
|
77
77
|
>
|
|
78
|
-
<Text className="text-sm text-foreground">
|
|
79
|
-
{a.displayName || a.username}
|
|
80
|
-
</Text>
|
|
78
|
+
<Text className="text-sm text-foreground">{a.displayName || a.username}</Text>
|
|
81
79
|
<MutedText className="text-xs">{a.email}</MutedText>
|
|
82
80
|
</Pressable>
|
|
83
81
|
))
|
|
@@ -89,7 +87,7 @@ export function AccountPickerScreen({
|
|
|
89
87
|
className="w-full"
|
|
90
88
|
variant="outline"
|
|
91
89
|
telemetryId="auth.account_picker.use_another"
|
|
92
|
-
|
|
90
|
+
onPress={onUseAnother}
|
|
93
91
|
accessibilityLabel="Use another account"
|
|
94
92
|
accessibilityRole="button"
|
|
95
93
|
>
|
|
@@ -280,13 +280,18 @@ export interface AuthScreenProps {
|
|
|
280
280
|
*/
|
|
281
281
|
showWelcome?: boolean;
|
|
282
282
|
/**
|
|
283
|
-
*
|
|
283
|
+
* Page heading, rendered ABOVE the card, e.g.
|
|
284
|
+
* "Log into your Entropy account to continue."
|
|
284
285
|
*
|
|
285
286
|
* Undefined renders no heading, which is the right default for an app whose
|
|
286
287
|
* own login the user reached deliberately -- the surrounding brand already
|
|
287
288
|
* says where they are. Set it on an identity provider, where the user is
|
|
288
|
-
* frequently redirected in from a relying application and
|
|
289
|
-
*
|
|
289
|
+
* frequently redirected in from a relying application and this is the only
|
|
290
|
+
* thing on screen that can say whose credentials to type.
|
|
291
|
+
*
|
|
292
|
+
* It used to render inside the card header, where it sat over the
|
|
293
|
+
* Sign In / Sign Up toggle and read as a label for the selected mode. Above
|
|
294
|
+
* the card it is the page's heading and the card is purely the form.
|
|
290
295
|
*/
|
|
291
296
|
title?: string;
|
|
292
297
|
/** Optional line under `title`. Ignored unless `title` is set. */
|
|
@@ -1072,6 +1077,34 @@ export function AuthScreen({
|
|
|
1072
1077
|
{cardBackdrop}
|
|
1073
1078
|
</View>
|
|
1074
1079
|
) : null}
|
|
1080
|
+
{/* ── WHOSE login this is, ABOVE the card ──────────────
|
|
1081
|
+
Without it the card is a pair of unlabelled fields under
|
|
1082
|
+
a Sign In / Sign Up toggle: fine when the user arrived
|
|
1083
|
+
from the app they are signing into, actively confusing
|
|
1084
|
+
when an OIDC redirect dropped them here from somewhere
|
|
1085
|
+
else -- the case an identity provider is FOR.
|
|
1086
|
+
|
|
1087
|
+
Above the card rather than in its header, which is where
|
|
1088
|
+
this started. In the header it sat directly over the
|
|
1089
|
+
mode toggle and read as a label for the selected mode
|
|
1090
|
+
("Log in to Entropy" above a Sign In / Sign Up switch
|
|
1091
|
+
restates the switch). Out here it is unambiguously the
|
|
1092
|
+
page's own heading, the card below it is purely the
|
|
1093
|
+
form, and the brand backdrop has something to sit
|
|
1094
|
+
behind.
|
|
1095
|
+
|
|
1096
|
+
Width is pinned to the card's 360px so the two are one
|
|
1097
|
+
column, and the text is centred to match. */}
|
|
1098
|
+
{title ? (
|
|
1099
|
+
<View className="w-full max-w-[360px] gap-1 pb-5">
|
|
1100
|
+
<Heading className="text-center text-xl">{title}</Heading>
|
|
1101
|
+
{subtitle ? (
|
|
1102
|
+
<MutedText className="text-center text-sm leading-5">
|
|
1103
|
+
{subtitle}
|
|
1104
|
+
</MutedText>
|
|
1105
|
+
) : null}
|
|
1106
|
+
</View>
|
|
1107
|
+
) : null}
|
|
1075
1108
|
<Card className="w-full max-w-[360px]">
|
|
1076
1109
|
{displayStage === "forgot" ? (
|
|
1077
1110
|
<>
|
|
@@ -1153,24 +1186,11 @@ export function AuthScreen({
|
|
|
1153
1186
|
that otherwise all collects above the footer.
|
|
1154
1187
|
Applies to both modes — the header is shared. */}
|
|
1155
1188
|
<CardHeader className="gap-5 pb-3 pt-5">
|
|
1156
|
-
{/*
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
OIDC redirect dropped them here from somewhere
|
|
1162
|
-
else -- the case an identity provider is FOR.
|
|
1163
|
-
Rendered above the toggle so it reads as a
|
|
1164
|
-
title for the card rather than a label for
|
|
1165
|
-
whichever mode happens to be selected. */}
|
|
1166
|
-
{title ? (
|
|
1167
|
-
<View className="gap-1">
|
|
1168
|
-
<CardTitle>{title}</CardTitle>
|
|
1169
|
-
{subtitle ? (
|
|
1170
|
-
<MutedText className="text-sm leading-5">{subtitle}</MutedText>
|
|
1171
|
-
) : null}
|
|
1172
|
-
</View>
|
|
1173
|
-
) : null}
|
|
1189
|
+
{/* The heading moved OUT of the card -- see the
|
|
1190
|
+
block above <Card>. Inside here it read as a
|
|
1191
|
+
label for whichever toggle mode was selected;
|
|
1192
|
+
above the card it reads as the page saying whose
|
|
1193
|
+
login this is, which is what it is for. */}
|
|
1174
1194
|
{signUpEnabled ? (
|
|
1175
1195
|
<SegmentedToggle<Mode>
|
|
1176
1196
|
value={mode}
|
|
@@ -93,7 +93,7 @@ export function ConsentScreen({
|
|
|
93
93
|
<Button
|
|
94
94
|
className="w-full"
|
|
95
95
|
telemetryId="auth.consent.allow"
|
|
96
|
-
|
|
96
|
+
onPress={onAllow}
|
|
97
97
|
isLoading={isLoading}
|
|
98
98
|
accessibilityLabel={`Allow ${clientName}`}
|
|
99
99
|
accessibilityRole="button"
|
|
@@ -104,7 +104,7 @@ export function ConsentScreen({
|
|
|
104
104
|
className="w-full"
|
|
105
105
|
variant="outline"
|
|
106
106
|
telemetryId="auth.consent.deny"
|
|
107
|
-
|
|
107
|
+
onPress={onDeny}
|
|
108
108
|
accessibilityLabel="Deny access"
|
|
109
109
|
accessibilityRole="button"
|
|
110
110
|
>
|
|
@@ -68,7 +68,7 @@ export function ErrorScreen({
|
|
|
68
68
|
<Button
|
|
69
69
|
className="w-full"
|
|
70
70
|
telemetryId="auth.error.retry"
|
|
71
|
-
|
|
71
|
+
onPress={onRetry}
|
|
72
72
|
accessibilityLabel="Try again"
|
|
73
73
|
accessibilityRole="button"
|
|
74
74
|
>
|
|
@@ -80,7 +80,7 @@ export function ErrorScreen({
|
|
|
80
80
|
className="w-full"
|
|
81
81
|
variant="outline"
|
|
82
82
|
telemetryId="auth.error.home"
|
|
83
|
-
|
|
83
|
+
onPress={onHome}
|
|
84
84
|
accessibilityLabel={homeLabel}
|
|
85
85
|
accessibilityRole="button"
|
|
86
86
|
>
|
|
@@ -4,13 +4,7 @@ import { SafeAreaView } from "react-native-safe-area-context";
|
|
|
4
4
|
import { StatusBar } from "expo-status-bar";
|
|
5
5
|
import { useColorScheme } from "nativewind";
|
|
6
6
|
import { Button } from "../../components/button";
|
|
7
|
-
import {
|
|
8
|
-
Card,
|
|
9
|
-
CardDescription,
|
|
10
|
-
CardFooter,
|
|
11
|
-
CardHeader,
|
|
12
|
-
CardTitle,
|
|
13
|
-
} from "../../components/card";
|
|
7
|
+
import { Card, CardDescription, CardFooter, CardHeader, CardTitle } from "../../components/card";
|
|
14
8
|
|
|
15
9
|
export interface ExpiredLinkScreenProps {
|
|
16
10
|
title?: string;
|
|
@@ -59,7 +53,7 @@ export function ExpiredLinkScreen({
|
|
|
59
53
|
<Button
|
|
60
54
|
className="w-full"
|
|
61
55
|
telemetryId="auth.expired_link.request_new"
|
|
62
|
-
|
|
56
|
+
onPress={onRequestNew}
|
|
63
57
|
accessibilityLabel={requestLabel}
|
|
64
58
|
accessibilityRole="button"
|
|
65
59
|
>
|
|
@@ -71,7 +65,7 @@ export function ExpiredLinkScreen({
|
|
|
71
65
|
className="w-full"
|
|
72
66
|
variant="outline"
|
|
73
67
|
telemetryId="auth.expired_link.home"
|
|
74
|
-
|
|
68
|
+
onPress={onHome}
|
|
75
69
|
accessibilityLabel={homeLabel}
|
|
76
70
|
accessibilityRole="button"
|
|
77
71
|
>
|
|
@@ -81,7 +81,7 @@ export function ForgotPasswordScreen() {
|
|
|
81
81
|
<IconButton
|
|
82
82
|
variant="ghost"
|
|
83
83
|
telemetryId="auth.forgot_password.back"
|
|
84
|
-
|
|
84
|
+
onPress={() => router.back()}
|
|
85
85
|
accessibilityLabel="Go back"
|
|
86
86
|
accessibilityRole="button"
|
|
87
87
|
>
|
|
@@ -123,7 +123,7 @@ export function ForgotPasswordScreen() {
|
|
|
123
123
|
<Button
|
|
124
124
|
className="w-full"
|
|
125
125
|
telemetryId="auth.forgot_password.submit"
|
|
126
|
-
|
|
126
|
+
onPress={handleSubmit}
|
|
127
127
|
isLoading={isLoading}
|
|
128
128
|
accessibilityLabel="Reset your password"
|
|
129
129
|
accessibilityRole="button"
|
|
@@ -135,7 +135,9 @@ export function ForgotPasswordScreen() {
|
|
|
135
135
|
Remember your password?{" "}
|
|
136
136
|
<Text
|
|
137
137
|
className="text-sm text-foreground underline"
|
|
138
|
-
onPress={withClickTracking("auth.forgot_password.push", () =>
|
|
138
|
+
onPress={withClickTracking("auth.forgot_password.push", () =>
|
|
139
|
+
router.push(routes.login)
|
|
140
|
+
)}
|
|
139
141
|
accessibilityRole="link"
|
|
140
142
|
accessibilityLabel="Sign in"
|
|
141
143
|
>
|
|
@@ -149,7 +151,7 @@ export function ForgotPasswordScreen() {
|
|
|
149
151
|
<Button
|
|
150
152
|
className="w-full"
|
|
151
153
|
telemetryId="auth.forgot_password.push_2"
|
|
152
|
-
|
|
154
|
+
onPress={() =>
|
|
153
155
|
router.push({
|
|
154
156
|
pathname: routes.resetPassword,
|
|
155
157
|
params: { email },
|
|
@@ -96,7 +96,7 @@ export function ImportDataScreen({ renderImport }: ImportDataScreenProps = {}) {
|
|
|
96
96
|
<Button
|
|
97
97
|
className="w-full"
|
|
98
98
|
telemetryId="auth.import_data.complete_and_navigate"
|
|
99
|
-
|
|
99
|
+
onPress={completeAndNavigate}
|
|
100
100
|
accessibilityLabel={`Continue to ${appName}`}
|
|
101
101
|
accessibilityRole="button"
|
|
102
102
|
testID="import-continue"
|
|
@@ -282,7 +282,7 @@ export function LoginScreen({
|
|
|
282
282
|
<IconButton
|
|
283
283
|
variant="ghost"
|
|
284
284
|
telemetryId="auth.login.push"
|
|
285
|
-
|
|
285
|
+
onPress={() => router.push(routes.welcome)}
|
|
286
286
|
className="self-start"
|
|
287
287
|
accessibilityLabel="Go back"
|
|
288
288
|
accessibilityRole="button"
|
|
@@ -318,7 +318,7 @@ export function LoginScreen({
|
|
|
318
318
|
variant="outline"
|
|
319
319
|
className="w-full"
|
|
320
320
|
telemetryId="auth.login.passkey_login"
|
|
321
|
-
|
|
321
|
+
onPress={handlePasskeyLogin}
|
|
322
322
|
isLoading={passkeyStep === "passkey-loading"}
|
|
323
323
|
accessibilityLabel="Sign in with passkey"
|
|
324
324
|
accessibilityRole="button"
|
|
@@ -378,7 +378,9 @@ export function LoginScreen({
|
|
|
378
378
|
<View className="items-end">
|
|
379
379
|
<Text
|
|
380
380
|
className="font-mono text-sm underline"
|
|
381
|
-
onPress={withClickTracking("auth.login.push_2", () =>
|
|
381
|
+
onPress={withClickTracking("auth.login.push_2", () =>
|
|
382
|
+
router.push(routes.forgotPassword)
|
|
383
|
+
)}
|
|
382
384
|
accessibilityRole="link"
|
|
383
385
|
accessibilityLabel="Forgot password"
|
|
384
386
|
>
|
|
@@ -392,7 +394,9 @@ export function LoginScreen({
|
|
|
392
394
|
Don't have an account?{" "}
|
|
393
395
|
<Text
|
|
394
396
|
className="text-sm text-foreground underline"
|
|
395
|
-
onPress={withClickTracking("auth.login.push_3", () =>
|
|
397
|
+
onPress={withClickTracking("auth.login.push_3", () =>
|
|
398
|
+
router.push(routes.register)
|
|
399
|
+
)}
|
|
396
400
|
accessibilityRole="link"
|
|
397
401
|
accessibilityLabel="Sign up"
|
|
398
402
|
>
|
|
@@ -405,7 +409,7 @@ export function LoginScreen({
|
|
|
405
409
|
<Button
|
|
406
410
|
className="w-full"
|
|
407
411
|
telemetryId="auth.login.login"
|
|
408
|
-
|
|
412
|
+
onPress={handleLogin}
|
|
409
413
|
isLoading={isLoading}
|
|
410
414
|
accessibilityLabel="Sign in"
|
|
411
415
|
accessibilityRole="button"
|
|
@@ -473,7 +477,10 @@ export function LoginScreen({
|
|
|
473
477
|
<DialogFooter style={{ justifyContent: "center" }}>
|
|
474
478
|
<DialogButton
|
|
475
479
|
variant="primary"
|
|
476
|
-
onPress={withClickTracking(
|
|
480
|
+
onPress={withClickTracking(
|
|
481
|
+
"auth.login.action",
|
|
482
|
+
isLoading ? () => {} : handleUnlockAfterPasskey
|
|
483
|
+
)}
|
|
477
484
|
accessibilityLabel="Unlock"
|
|
478
485
|
accessibilityRole="button"
|
|
479
486
|
>
|
|
@@ -195,7 +195,10 @@ export function MfaChallengeScreen() {
|
|
|
195
195
|
</Pressable>
|
|
196
196
|
|
|
197
197
|
<Pressable
|
|
198
|
-
onPress={withClickTracking(
|
|
198
|
+
onPress={withClickTracking(
|
|
199
|
+
"auth.mfa_challenge.toggle_recovery_mode",
|
|
200
|
+
toggleRecoveryMode
|
|
201
|
+
)}
|
|
199
202
|
className="mt-5 items-center"
|
|
200
203
|
hitSlop={{ top: 10, bottom: 10, left: 10, right: 10 }}
|
|
201
204
|
accessibilityRole="button"
|
|
@@ -50,9 +50,8 @@ export function PasskeyEnrollScreen({
|
|
|
50
50
|
<CardHeader>
|
|
51
51
|
<CardTitle>Add a passkey</CardTitle>
|
|
52
52
|
<CardDescription>
|
|
53
|
-
Sign in faster and more securely next time with a passkey — your
|
|
54
|
-
|
|
55
|
-
to remember or phish.
|
|
53
|
+
Sign in faster and more securely next time with a passkey — your device unlocks it
|
|
54
|
+
with your fingerprint, face, or PIN. No password to remember or phish.
|
|
56
55
|
</CardDescription>
|
|
57
56
|
</CardHeader>
|
|
58
57
|
|
|
@@ -61,8 +60,7 @@ export function PasskeyEnrollScreen({
|
|
|
61
60
|
<Text className="text-sm text-destructive dark:text-red-900">{error}</Text>
|
|
62
61
|
) : (
|
|
63
62
|
<MutedText className="text-sm">
|
|
64
|
-
You can always add or remove passkeys later in your account
|
|
65
|
-
settings.
|
|
63
|
+
You can always add or remove passkeys later in your account settings.
|
|
66
64
|
</MutedText>
|
|
67
65
|
)}
|
|
68
66
|
</CardContent>
|
|
@@ -71,7 +69,7 @@ export function PasskeyEnrollScreen({
|
|
|
71
69
|
<Button
|
|
72
70
|
className="w-full"
|
|
73
71
|
telemetryId="auth.passkey_enroll.enroll"
|
|
74
|
-
|
|
72
|
+
onPress={onEnroll}
|
|
75
73
|
isLoading={isLoading}
|
|
76
74
|
accessibilityLabel="Add a passkey"
|
|
77
75
|
accessibilityRole="button"
|
|
@@ -82,7 +80,7 @@ export function PasskeyEnrollScreen({
|
|
|
82
80
|
className="w-full"
|
|
83
81
|
variant="ghost"
|
|
84
82
|
telemetryId="auth.passkey_enroll.skip"
|
|
85
|
-
|
|
83
|
+
onPress={onSkip}
|
|
86
84
|
accessibilityLabel="Not now"
|
|
87
85
|
accessibilityRole="button"
|
|
88
86
|
>
|
|
@@ -62,8 +62,12 @@ export function PrivacyPolicyScreen({
|
|
|
62
62
|
<View className="flex-1" style={{ backgroundColor: isDark ? "#000000" : "#FFFFFF" }}>
|
|
63
63
|
<View className="px-6 pt-4 pb-6">
|
|
64
64
|
<View className="flex-row items-center justify-between mb-6">
|
|
65
|
-
<IconButton
|
|
66
|
-
|
|
65
|
+
<IconButton
|
|
66
|
+
variant="ghost"
|
|
67
|
+
size="sm"
|
|
68
|
+
telemetryId="auth.privacy_policy.back"
|
|
69
|
+
onPress={() => router.back()}
|
|
70
|
+
>
|
|
67
71
|
<Ionicons name="arrow-back" size={20} color={isDark ? "#FFFFFF" : "#000000"} />
|
|
68
72
|
</IconButton>
|
|
69
73
|
|
|
@@ -191,7 +195,9 @@ export function PrivacyPolicyScreen({
|
|
|
191
195
|
<Text
|
|
192
196
|
className="font-mono text-sm underline"
|
|
193
197
|
style={{ color: isDark ? "#6BA3E5" : "#4A90E2" }}
|
|
194
|
-
onPress={withClickTracking("auth.privacy_policy.open_u_r_l", () =>
|
|
198
|
+
onPress={withClickTracking("auth.privacy_policy.open_u_r_l", () =>
|
|
199
|
+
Linking.openURL(`mailto:${supportEmail}`)
|
|
200
|
+
)}
|
|
195
201
|
>
|
|
196
202
|
{supportEmail}
|
|
197
203
|
</Text>
|
|
@@ -230,7 +230,7 @@ export function ResetPasswordScreen({ encryptionWarning }: ResetPasswordScreenPr
|
|
|
230
230
|
<IconButton
|
|
231
231
|
variant="ghost"
|
|
232
232
|
telemetryId="auth.reset_password.step"
|
|
233
|
-
|
|
233
|
+
onPress={() => (step === "password" ? setStep("code") : router.back())}
|
|
234
234
|
accessibilityLabel="Go back"
|
|
235
235
|
accessibilityRole="button"
|
|
236
236
|
>
|
|
@@ -273,7 +273,7 @@ export function ResetPasswordScreen({ encryptionWarning }: ResetPasswordScreenPr
|
|
|
273
273
|
<Button
|
|
274
274
|
className="w-full"
|
|
275
275
|
telemetryId="auth.reset_password.code_submit"
|
|
276
|
-
|
|
276
|
+
onPress={handleCodeSubmit}
|
|
277
277
|
isLoading={isLoading}
|
|
278
278
|
accessibilityLabel="Continue"
|
|
279
279
|
accessibilityRole="button"
|
|
@@ -285,7 +285,9 @@ export function ResetPasswordScreen({ encryptionWarning }: ResetPasswordScreenPr
|
|
|
285
285
|
Back to{" "}
|
|
286
286
|
<Text
|
|
287
287
|
className="text-sm text-foreground underline"
|
|
288
|
-
onPress={withClickTracking("auth.reset_password.push", () =>
|
|
288
|
+
onPress={withClickTracking("auth.reset_password.push", () =>
|
|
289
|
+
router.push(routes.login)
|
|
290
|
+
)}
|
|
289
291
|
accessibilityRole="link"
|
|
290
292
|
accessibilityLabel="Sign in"
|
|
291
293
|
>
|
|
@@ -351,7 +353,7 @@ export function ResetPasswordScreen({ encryptionWarning }: ResetPasswordScreenPr
|
|
|
351
353
|
<Button
|
|
352
354
|
className="w-full"
|
|
353
355
|
telemetryId="auth.reset_password.password_submit"
|
|
354
|
-
|
|
356
|
+
onPress={handlePasswordSubmit}
|
|
355
357
|
isLoading={isLoading}
|
|
356
358
|
accessibilityLabel="Reset password"
|
|
357
359
|
accessibilityRole="button"
|
|
@@ -251,7 +251,9 @@ export function ServerConfigScreen({
|
|
|
251
251
|
>
|
|
252
252
|
<Ionicons name="close" size={24} color={isDark ? "white" : "black"} />
|
|
253
253
|
</Pressable>
|
|
254
|
-
<Text className="text-lg font-semibold text-foreground dark:text-white">
|
|
254
|
+
<Text className="text-lg font-semibold text-foreground dark:text-white">
|
|
255
|
+
Server Configuration
|
|
256
|
+
</Text>
|
|
255
257
|
<View className="h-10 w-10" />
|
|
256
258
|
</View>
|
|
257
259
|
|
|
@@ -275,7 +277,9 @@ export function ServerConfigScreen({
|
|
|
275
277
|
</View>
|
|
276
278
|
|
|
277
279
|
<View className="mb-6">
|
|
278
|
-
<Text className="mb-2 text-sm font-medium text-foreground dark:text-white">
|
|
280
|
+
<Text className="mb-2 text-sm font-medium text-foreground dark:text-white">
|
|
281
|
+
Server URL
|
|
282
|
+
</Text>
|
|
279
283
|
<View className="flex-row items-center rounded-lg border border-border dark:border-neutral-800 bg-background dark:bg-black px-3 py-3">
|
|
280
284
|
<Ionicons
|
|
281
285
|
name="globe-outline"
|
|
@@ -345,7 +349,10 @@ export function ServerConfigScreen({
|
|
|
345
349
|
</Pressable>
|
|
346
350
|
|
|
347
351
|
<Pressable
|
|
348
|
-
onPress={withClickTracking(
|
|
352
|
+
onPress={withClickTracking(
|
|
353
|
+
"auth.server_config.clear_configuration",
|
|
354
|
+
handleClearConfiguration
|
|
355
|
+
)}
|
|
349
356
|
accessibilityRole="button"
|
|
350
357
|
accessibilityLabel="Clear all settings"
|
|
351
358
|
className="active:scale-98 mb-3 w-full rounded-lg border border-destructive dark:border-red-900 bg-background dark:bg-black px-6 py-3"
|
|
@@ -370,12 +377,18 @@ export function ServerConfigScreen({
|
|
|
370
377
|
</Pressable>
|
|
371
378
|
|
|
372
379
|
<View className="rounded-lg bg-card dark:bg-neutral-900/50 p-4">
|
|
373
|
-
<Text className="mb-3 text-sm font-semibold text-foreground dark:text-white">
|
|
380
|
+
<Text className="mb-3 text-sm font-semibold text-foreground dark:text-white">
|
|
381
|
+
Need Help?
|
|
382
|
+
</Text>
|
|
374
383
|
<View className="space-y-2">
|
|
375
384
|
{bullets.map((bullet, idx) => (
|
|
376
385
|
<View key={idx} className="flex-row items-start">
|
|
377
|
-
<Text className="mr-2 text-sm text-muted-foreground dark:text-neutral-400"
|
|
378
|
-
|
|
386
|
+
<Text className="mr-2 text-sm text-muted-foreground dark:text-neutral-400">
|
|
387
|
+
•
|
|
388
|
+
</Text>
|
|
389
|
+
<Text className="flex-1 text-sm text-muted-foreground dark:text-neutral-400">
|
|
390
|
+
{bullet}
|
|
391
|
+
</Text>
|
|
379
392
|
</View>
|
|
380
393
|
))}
|
|
381
394
|
</View>
|
|
@@ -74,8 +74,12 @@ export function TermsOfServiceScreen({
|
|
|
74
74
|
<View className="flex-1" style={{ backgroundColor: isDark ? "#000000" : "#FFFFFF" }}>
|
|
75
75
|
<View className="px-6 pt-4 pb-6">
|
|
76
76
|
<View className="flex-row items-center justify-between mb-6">
|
|
77
|
-
<IconButton
|
|
78
|
-
|
|
77
|
+
<IconButton
|
|
78
|
+
variant="ghost"
|
|
79
|
+
size="sm"
|
|
80
|
+
telemetryId="auth.terms_of_service.back"
|
|
81
|
+
onPress={() => router.back()}
|
|
82
|
+
>
|
|
79
83
|
<Ionicons name="arrow-back" size={20} color={isDark ? "#FFFFFF" : "#000000"} />
|
|
80
84
|
</IconButton>
|
|
81
85
|
|
|
@@ -168,7 +172,9 @@ export function TermsOfServiceScreen({
|
|
|
168
172
|
<Text
|
|
169
173
|
className="font-mono text-sm underline"
|
|
170
174
|
style={{ color: isDark ? "#6BA3E5" : "#4A90E2" }}
|
|
171
|
-
onPress={withClickTracking("auth.terms_of_service.open_u_r_l", () =>
|
|
175
|
+
onPress={withClickTracking("auth.terms_of_service.open_u_r_l", () =>
|
|
176
|
+
Linking.openURL(`mailto:${supportEmail}`)
|
|
177
|
+
)}
|
|
172
178
|
>
|
|
173
179
|
{supportEmail}
|
|
174
180
|
</Text>
|
|
@@ -50,12 +50,19 @@ export function WelcomeScreen({ decoration, topAccessory }: WelcomeScreenProps)
|
|
|
50
50
|
) : null}
|
|
51
51
|
|
|
52
52
|
<View className="w-full max-w-[360px] gap-3">
|
|
53
|
-
<Button
|
|
54
|
-
|
|
53
|
+
<Button
|
|
54
|
+
className="w-full"
|
|
55
|
+
telemetryId="auth.welcome.push"
|
|
56
|
+
onPress={() => router.push(routes.login)}
|
|
57
|
+
>
|
|
55
58
|
Sign In
|
|
56
59
|
</Button>
|
|
57
|
-
<Button
|
|
58
|
-
|
|
60
|
+
<Button
|
|
61
|
+
variant="outline"
|
|
62
|
+
className="w-full"
|
|
63
|
+
telemetryId="auth.welcome.push_2"
|
|
64
|
+
onPress={() => router.push(routes.register)}
|
|
65
|
+
>
|
|
59
66
|
Create Account
|
|
60
67
|
</Button>
|
|
61
68
|
</View>
|
|
@@ -70,11 +70,11 @@ export function trackClick(element: string, route?: string): void {
|
|
|
70
70
|
*/
|
|
71
71
|
export function withClickTracking<F extends (...args: never[]) => unknown>(
|
|
72
72
|
element: string | undefined,
|
|
73
|
-
handler: F
|
|
73
|
+
handler: F
|
|
74
74
|
): F;
|
|
75
75
|
export function withClickTracking<F extends (...args: never[]) => unknown>(
|
|
76
76
|
element: string | undefined,
|
|
77
|
-
handler: F | null | undefined
|
|
77
|
+
handler: F | null | undefined
|
|
78
78
|
): F | undefined;
|
|
79
79
|
/**
|
|
80
80
|
* Wrap a press handler so activating the element is reported first.
|
|
@@ -88,7 +88,7 @@ export function withClickTracking<F extends (...args: never[]) => unknown>(
|
|
|
88
88
|
*/
|
|
89
89
|
export function withClickTracking(
|
|
90
90
|
element: string | undefined,
|
|
91
|
-
handler: ((...args: never[]) => unknown) | null | undefined
|
|
91
|
+
handler: ((...args: never[]) => unknown) | null | undefined
|
|
92
92
|
): ((...args: never[]) => unknown) | undefined {
|
|
93
93
|
// Untagged controls keep their original handler rather than gaining a
|
|
94
94
|
// closure per render for nothing.
|