@entropy-softworks/ui 2026.7.31 → 2026.8.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/CHANGELOG.md +25 -0
- package/lib/components/button.d.ts +11 -2
- package/lib/components/button.d.ts.map +1 -1
- package/lib/components/button.jsx +14 -4
- package/lib/components/button.jsx.map +1 -1
- package/lib/context/AppConfigContext.d.ts +19 -0
- package/lib/context/AppConfigContext.d.ts.map +1 -1
- package/lib/context/AppConfigContext.jsx.map +1 -1
- package/lib/screens/auth/AccountPickerScreen.d.ts.map +1 -1
- package/lib/screens/auth/AccountPickerScreen.jsx +3 -2
- package/lib/screens/auth/AccountPickerScreen.jsx.map +1 -1
- package/lib/screens/auth/AuthScreen.d.ts +45 -1
- package/lib/screens/auth/AuthScreen.d.ts.map +1 -1
- package/lib/screens/auth/AuthScreen.jsx +69 -27
- package/lib/screens/auth/AuthScreen.jsx.map +1 -1
- package/lib/screens/auth/ConsentScreen.d.ts.map +1 -1
- package/lib/screens/auth/ConsentScreen.jsx +2 -2
- package/lib/screens/auth/ConsentScreen.jsx.map +1 -1
- package/lib/screens/auth/ErrorScreen.d.ts.map +1 -1
- package/lib/screens/auth/ErrorScreen.jsx +2 -2
- 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 +2 -2
- 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 +7 -6
- package/lib/screens/auth/ForgotPasswordScreen.jsx.map +1 -1
- package/lib/screens/auth/ImportDataScreen.d.ts.map +1 -1
- package/lib/screens/auth/ImportDataScreen.jsx +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 +7 -6
- 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 +3 -2
- package/lib/screens/auth/MfaChallengeScreen.jsx.map +1 -1
- package/lib/screens/auth/MfaEnrollScreen.d.ts.map +1 -1
- package/lib/screens/auth/MfaEnrollScreen.jsx +9 -8
- package/lib/screens/auth/MfaEnrollScreen.jsx.map +1 -1
- package/lib/screens/auth/PasskeyEnrollScreen.d.ts.map +1 -1
- package/lib/screens/auth/PasskeyEnrollScreen.jsx +2 -2
- 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 +3 -2
- package/lib/screens/auth/PrivacyPolicyScreen.jsx.map +1 -1
- package/lib/screens/auth/RegisterScreen.d.ts.map +1 -1
- package/lib/screens/auth/RegisterScreen.jsx +6 -5
- package/lib/screens/auth/RegisterScreen.jsx.map +1 -1
- package/lib/screens/auth/ResetPasswordScreen.d.ts.map +1 -1
- package/lib/screens/auth/ResetPasswordScreen.jsx +6 -5
- 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 +6 -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 +3 -2
- package/lib/screens/auth/TermsOfServiceScreen.jsx.map +1 -1
- package/lib/screens/auth/VerifyEmailScreen.d.ts.map +1 -1
- package/lib/screens/auth/VerifyEmailScreen.jsx +5 -4
- package/lib/screens/auth/VerifyEmailScreen.jsx.map +1 -1
- package/lib/screens/auth/WelcomeScreen.d.ts.map +1 -1
- package/lib/screens/auth/WelcomeScreen.jsx +2 -2
- package/lib/screens/auth/WelcomeScreen.jsx.map +1 -1
- package/lib/services/auth.service.d.ts +9 -0
- package/lib/services/auth.service.d.ts.map +1 -1
- package/lib/services/auth.service.js.map +1 -1
- package/lib/services/index.d.ts +1 -0
- package/lib/services/index.d.ts.map +1 -1
- package/lib/services/index.js +2 -0
- package/lib/services/index.js.map +1 -1
- package/lib/services/telemetry.d.ts +30 -0
- package/lib/services/telemetry.d.ts.map +1 -0
- package/lib/services/telemetry.js +72 -0
- package/lib/services/telemetry.js.map +1 -0
- package/package.json +1 -1
- package/src/components/button.tsx +32 -1
- package/src/context/AppConfigContext.tsx +19 -0
- package/src/screens/auth/AccountPickerScreen.tsx +4 -2
- package/src/screens/auth/AuthScreen.tsx +159 -31
- package/src/screens/auth/ConsentScreen.tsx +4 -2
- package/src/screens/auth/ErrorScreen.tsx +4 -2
- package/src/screens/auth/ExpiredLinkScreen.tsx +4 -2
- package/src/screens/auth/ForgotPasswordScreen.tsx +10 -6
- package/src/screens/auth/ImportDataScreen.tsx +2 -1
- package/src/screens/auth/LoginScreen.tsx +10 -6
- package/src/screens/auth/MfaChallengeScreen.tsx +3 -2
- package/src/screens/auth/MfaEnrollScreen.tsx +15 -8
- package/src/screens/auth/PasskeyEnrollScreen.tsx +4 -2
- package/src/screens/auth/PrivacyPolicyScreen.tsx +4 -2
- package/src/screens/auth/RegisterScreen.tsx +8 -5
- package/src/screens/auth/ResetPasswordScreen.tsx +9 -5
- package/src/screens/auth/ServerConfigScreen.tsx +6 -5
- package/src/screens/auth/TermsOfServiceScreen.tsx +4 -2
- package/src/screens/auth/VerifyEmailScreen.tsx +8 -4
- package/src/screens/auth/WelcomeScreen.tsx +4 -2
- package/src/services/__tests__/telemetry.test.ts +81 -0
- package/src/services/auth.service.ts +9 -0
- package/src/services/index.ts +9 -0
- package/src/services/telemetry.ts +107 -0
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,31 @@ are CalVer — `YYYY.M.D` of the publish date, with leading zeros dropped (npm's
|
|
|
7
7
|
them), matching the `entropy-auth` crate. The `0.1.0` entry below predates the switch and was never
|
|
8
8
|
published to npm.
|
|
9
9
|
|
|
10
|
+
## 2026.8.2
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
|
|
14
|
+
- `registerTelemetry` and the rest of the telemetry registry were exported from
|
|
15
|
+
the root barrel twice — once through `export * from "./services"` and once by
|
|
16
|
+
name. Metro's CommonJS interop resolved the duplicate ambiguously and
|
|
17
|
+
consumers got `undefined` at runtime despite the types checking. The root
|
|
18
|
+
barrel now re-exports them only through `./services`.
|
|
19
|
+
|
|
20
|
+
## 2026.8.1
|
|
21
|
+
|
|
22
|
+
### Added
|
|
23
|
+
|
|
24
|
+
- Telemetry registry (`registerTelemetry`, `trackClick`, `withClickTracking`). The
|
|
25
|
+
package reports interactions to a sink the host app supplies; it knows nothing
|
|
26
|
+
about batching, authentication or transport. No sink registered means every
|
|
27
|
+
entry point is a no-op, so consumers that do not opt in are unaffected.
|
|
28
|
+
- `telemetryId` prop on `Button` and `IconButton`. Reports a stable,
|
|
29
|
+
author-assigned id when the control is activated. Wired through `onPress`, so
|
|
30
|
+
a disabled or loading button records nothing.
|
|
31
|
+
- All 69 interactive controls across the 18 auth screens carry a `telemetryId`,
|
|
32
|
+
making sign-in, sign-up, MFA and consent measurable by consuming apps without
|
|
33
|
+
each of them re-tagging the packaged screens.
|
|
34
|
+
|
|
10
35
|
## [Unreleased]
|
|
11
36
|
|
|
12
37
|
## [2026.7.31] — 2026-07-31
|
|
@@ -10,7 +10,16 @@ export interface ButtonProps extends Omit<PressableProps, "className" | "style">
|
|
|
10
10
|
disabled?: boolean;
|
|
11
11
|
/** Override the loading spinner colour (otherwise derived from the variant). */
|
|
12
12
|
spinnerColor?: string;
|
|
13
|
+
/**
|
|
14
|
+
* Stable, author-assigned id reported when this control is activated, e.g.
|
|
15
|
+
* `"login.submit"`. Omit it and nothing is tracked.
|
|
16
|
+
*
|
|
17
|
+
* Deliberately not derived from the button's label: copy changes over time,
|
|
18
|
+
* differs per locale, and can interpolate user data — all of which would
|
|
19
|
+
* either break a dashboard or leak into one.
|
|
20
|
+
*/
|
|
21
|
+
telemetryId?: string;
|
|
13
22
|
}
|
|
14
|
-
export declare function Button({ variant, size, className, textClassName, children, isLoading, disabled, spinnerColor, onPressIn, onPressOut, ...props }: ButtonProps): React.JSX.Element;
|
|
15
|
-
export declare function IconButton({ size, children, className, ...props }: ButtonProps): React.JSX.Element;
|
|
23
|
+
export declare function Button({ variant, size, className, textClassName, children, isLoading, disabled, spinnerColor, telemetryId, onPress, onPressIn, onPressOut, ...props }: ButtonProps): React.JSX.Element;
|
|
24
|
+
export declare function IconButton({ size, children, className, telemetryId, onPress, ...props }: ButtonProps): React.JSX.Element;
|
|
16
25
|
//# sourceMappingURL=button.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"button.d.ts","sourceRoot":"","sources":["../../src/components/button.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAgC,KAAK,cAAc,EAAkB,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"button.d.ts","sourceRoot":"","sources":["../../src/components/button.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAgC,KAAK,cAAc,EAAkB,MAAM,cAAc,CAAC;AAMjG,MAAM,WAAW,WAAY,SAAQ,IAAI,CAAC,cAAc,EAAE,WAAW,GAAG,OAAO,CAAC;IAC9E,OAAO,CAAC,EAAE,SAAS,GAAG,OAAO,GAAG,SAAS,GAAG,aAAa,CAAC;IAC1D,IAAI,CAAC,EAAE,IAAI,GAAG,SAAS,GAAG,IAAI,CAAC;IAC/B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC3B,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,gFAAgF;IAChF,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;;;;;OAOG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAsBD,wBAAgB,MAAM,CAAC,EACrB,OAAmB,EACnB,IAAgB,EAChB,SAAS,EACT,aAAa,EACb,QAAQ,EACR,SAAiB,EACjB,QAAgB,EAChB,YAAY,EACZ,WAAW,EACX,OAAO,EACP,SAAS,EACT,UAAU,EACV,GAAG,KAAK,EACT,EAAE,WAAW,qBAuEb;AAED,wBAAgB,UAAU,CAAC,EACzB,IAAgB,EAChB,QAAQ,EACR,SAAS,EACT,WAAW,EACX,OAAO,EACP,GAAG,KAAK,EACT,EAAE,WAAW,qBAgCb"}
|
|
@@ -3,6 +3,7 @@ import { ActivityIndicator, Pressable, Text as RNText } from "react-native";
|
|
|
3
3
|
import Animated, { useAnimatedStyle, useSharedValue, withTiming } from "react-native-reanimated";
|
|
4
4
|
import { useColorScheme } from "nativewind";
|
|
5
5
|
import { cn } from "../utils/cn";
|
|
6
|
+
import { withClickTracking } from "../services/telemetry";
|
|
6
7
|
const sizeClass = { sm: "h-10 px-4", default: "h-12 px-4", lg: "h-14 px-6" };
|
|
7
8
|
const textSizeClass = { sm: "text-sm", default: "text-base", lg: "text-lg" };
|
|
8
9
|
// Colours are driven by the active theme's CSS variables (`--primary`,
|
|
@@ -20,10 +21,15 @@ const fgClass = {
|
|
|
20
21
|
outline: "text-foreground",
|
|
21
22
|
ghost: "text-foreground",
|
|
22
23
|
};
|
|
23
|
-
export function Button({ variant = "default", size = "default", className, textClassName, children, isLoading = false, disabled = false, spinnerColor, onPressIn, onPressOut, ...props }) {
|
|
24
|
+
export function Button({ variant = "default", size = "default", className, textClassName, children, isLoading = false, disabled = false, spinnerColor, telemetryId, onPress, onPressIn, onPressOut, ...props }) {
|
|
24
25
|
const { colorScheme } = useColorScheme();
|
|
25
26
|
const isDark = colorScheme === "dark";
|
|
26
27
|
const isDisabled = disabled || isLoading;
|
|
28
|
+
// Wrapped rather than reported from a press listener so a disabled or
|
|
29
|
+
// loading button records nothing: Pressable does not fire onPress in either
|
|
30
|
+
// state, and counting presses the user never actually completed would
|
|
31
|
+
// overstate every funnel it appears in.
|
|
32
|
+
const handlePress = withClickTracking(telemetryId, onPress);
|
|
27
33
|
// Reanimated feedback shared by every button: hover (web) lifts slightly,
|
|
28
34
|
// press dips + dims — two distinct, layered responses.
|
|
29
35
|
const pressed = useSharedValue(0);
|
|
@@ -43,7 +49,7 @@ export function Button({ variant = "default", size = "default", className, textC
|
|
|
43
49
|
? "#FFFFFF"
|
|
44
50
|
: "#000000");
|
|
45
51
|
return (<Animated.View style={pressStyle} className={isDisabled ? "opacity-50" : undefined}>
|
|
46
|
-
<Pressable className={cn("flex-row items-center justify-center rounded-lg", sizeClass[size], bgClass[variant], className)} disabled={isDisabled} onPressIn={(e) => {
|
|
52
|
+
<Pressable className={cn("flex-row items-center justify-center rounded-lg", sizeClass[size], bgClass[variant], className)} disabled={isDisabled} onPress={handlePress} onPressIn={(e) => {
|
|
47
53
|
pressed.value = withTiming(1, { duration: 90 });
|
|
48
54
|
onPressIn?.(e);
|
|
49
55
|
}} onPressOut={(e) => {
|
|
@@ -56,7 +62,11 @@ export function Button({ variant = "default", size = "default", className, textC
|
|
|
56
62
|
</Pressable>
|
|
57
63
|
</Animated.View>);
|
|
58
64
|
}
|
|
59
|
-
export function IconButton({ size = "default", children, className, ...props }) {
|
|
65
|
+
export function IconButton({ size = "default", children, className, telemetryId, onPress, ...props }) {
|
|
66
|
+
// IconButton accepts ButtonProps, so telemetryId type-checks on it whether or
|
|
67
|
+
// not it is honoured. Wiring it here means a tagged icon button reports,
|
|
68
|
+
// rather than silently being the one control that never appears.
|
|
69
|
+
const handlePress = withClickTracking(telemetryId, onPress);
|
|
60
70
|
const dim = { sm: 40, default: 48, lg: 56 }[size];
|
|
61
71
|
const isDisabled = props.disabled || props.isLoading;
|
|
62
72
|
const pressed = useSharedValue(0);
|
|
@@ -65,7 +75,7 @@ export function IconButton({ size = "default", children, className, ...props })
|
|
|
65
75
|
transform: [{ scale: 1 + hovered.value * 0.06 - pressed.value * 0.08 }],
|
|
66
76
|
}));
|
|
67
77
|
return (<Animated.View style={pressStyle} className={isDisabled ? "opacity-50" : undefined}>
|
|
68
|
-
<Pressable className={cn("items-center justify-center rounded-lg", className)} style={{ width: dim, height: dim }} disabled={isDisabled} onPressIn={() => (pressed.value = withTiming(1, { duration: 90 }))} onPressOut={() => (pressed.value = withTiming(0, { duration: 150 }))} onHoverIn={() => (hovered.value = withTiming(1, { duration: 120 }))} onHoverOut={() => (hovered.value = withTiming(0, { duration: 160 }))} {...props} focusable={!isDisabled} accessibilityState={{ disabled: isDisabled, busy: props.isLoading }}>
|
|
78
|
+
<Pressable className={cn("items-center justify-center rounded-lg", className)} style={{ width: dim, height: dim }} disabled={isDisabled} onPress={handlePress} onPressIn={() => (pressed.value = withTiming(1, { duration: 90 }))} onPressOut={() => (pressed.value = withTiming(0, { duration: 150 }))} onHoverIn={() => (hovered.value = withTiming(1, { duration: 120 }))} onHoverOut={() => (hovered.value = withTiming(0, { duration: 160 }))} {...props} focusable={!isDisabled} accessibilityState={{ disabled: isDisabled, busy: props.isLoading }}>
|
|
69
79
|
{children}
|
|
70
80
|
</Pressable>
|
|
71
81
|
</Animated.View>);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"button.jsx","sourceRoot":"","sources":["../../src/components/button.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,iBAAiB,EAAE,SAAS,EAAuB,IAAI,IAAI,MAAM,EAAE,MAAM,cAAc,CAAC;AACjG,OAAO,QAAQ,EAAE,EAAE,gBAAgB,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AACjG,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAC5C,OAAO,EAAE,EAAE,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"button.jsx","sourceRoot":"","sources":["../../src/components/button.tsx"],"names":[],"mappings":"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"}
|
|
@@ -25,6 +25,12 @@ export interface AppRoutes {
|
|
|
25
25
|
export type LogoComponent = React.ComponentType<{
|
|
26
26
|
width?: number;
|
|
27
27
|
height?: number;
|
|
28
|
+
/**
|
|
29
|
+
* Ink colour for a monochrome mark. The auth screen passes the resolved
|
|
30
|
+
* theme foreground, so a single-colour logo inverts with the theme instead
|
|
31
|
+
* of each logo having to resolve the colour scheme for itself.
|
|
32
|
+
*/
|
|
33
|
+
color?: string;
|
|
28
34
|
style?: StyleProp<ViewStyle> | StyleProp<ImageStyle>;
|
|
29
35
|
}>;
|
|
30
36
|
export interface AppConfig {
|
|
@@ -47,6 +53,19 @@ export interface AppConfig {
|
|
|
47
53
|
termsText?: string;
|
|
48
54
|
/** Optional privacy-policy markdown / plain text. */
|
|
49
55
|
privacyText?: string;
|
|
56
|
+
/**
|
|
57
|
+
* The logo already spells the product name (a wordmark rather than a glyph).
|
|
58
|
+
*
|
|
59
|
+
* When true the auth screen renders the mark on its own, at its natural
|
|
60
|
+
* aspect ratio, instead of pairing a square glyph with the app name — which
|
|
61
|
+
* would otherwise print the name twice and squash a wide mark into a square.
|
|
62
|
+
*/
|
|
63
|
+
logoIsWordmark?: boolean;
|
|
64
|
+
/**
|
|
65
|
+
* Rendered width of a wordmark logo, in px. Defaults to 160. Only consulted
|
|
66
|
+
* when `logoIsWordmark` is set; a glyph logo keeps its square 72px pairing.
|
|
67
|
+
*/
|
|
68
|
+
logoWidth?: number;
|
|
50
69
|
}
|
|
51
70
|
export declare function AppConfigProvider({ config, children, }: {
|
|
52
71
|
config: AppConfig;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AppConfigContext.d.ts","sourceRoot":"","sources":["../../src/context/AppConfigContext.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA6C,MAAM,OAAO,CAAC;AAClE,OAAO,EAAE,KAAK,IAAI,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,EAAE,KAAK,UAAU,EAAE,KAAK,SAAS,EAAE,KAAK,SAAS,EAAE,MAAM,cAAc,CAAC;AAE/E;;;;GAIG;AACH,MAAM,WAAW,SAAS;IACxB,OAAO,EAAE,IAAI,CAAC;IACd,KAAK,EAAE,IAAI,CAAC;IACZ,QAAQ,EAAE,IAAI,CAAC;IACf,cAAc,EAAE,IAAI,CAAC;IACrB,aAAa,EAAE,IAAI,CAAC;IACpB,WAAW,EAAE,IAAI,CAAC;IAClB,YAAY,EAAE,IAAI,CAAC;IACnB,SAAS,EAAE,IAAI,CAAC;IAChB,cAAc,EAAE,IAAI,CAAC;IACrB,aAAa,EAAE,IAAI,CAAC;IACpB,YAAY,EAAE,IAAI,CAAC;IACnB,UAAU,EAAE,IAAI,CAAC;IACjB,yEAAyE;IACzE,IAAI,EAAE,IAAI,CAAC;CACZ;AAED,MAAM,MAAM,aAAa,GAAG,KAAK,CAAC,aAAa,CAAC;IAC9C,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,GAAG,SAAS,CAAC,UAAU,CAAC,CAAC;CACtD,CAAC,CAAC;AAEH,MAAM,WAAW,SAAS;IACxB,4EAA4E;IAC5E,OAAO,EAAE,MAAM,CAAC;IAChB,qDAAqD;IACrD,IAAI,EAAE,aAAa,CAAC;IACpB,yBAAyB;IACzB,MAAM,EAAE,SAAS,CAAC;IAClB;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;;OAIG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,qDAAqD;IACrD,WAAW,CAAC,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"AppConfigContext.d.ts","sourceRoot":"","sources":["../../src/context/AppConfigContext.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA6C,MAAM,OAAO,CAAC;AAClE,OAAO,EAAE,KAAK,IAAI,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,EAAE,KAAK,UAAU,EAAE,KAAK,SAAS,EAAE,KAAK,SAAS,EAAE,MAAM,cAAc,CAAC;AAE/E;;;;GAIG;AACH,MAAM,WAAW,SAAS;IACxB,OAAO,EAAE,IAAI,CAAC;IACd,KAAK,EAAE,IAAI,CAAC;IACZ,QAAQ,EAAE,IAAI,CAAC;IACf,cAAc,EAAE,IAAI,CAAC;IACrB,aAAa,EAAE,IAAI,CAAC;IACpB,WAAW,EAAE,IAAI,CAAC;IAClB,YAAY,EAAE,IAAI,CAAC;IACnB,SAAS,EAAE,IAAI,CAAC;IAChB,cAAc,EAAE,IAAI,CAAC;IACrB,aAAa,EAAE,IAAI,CAAC;IACpB,YAAY,EAAE,IAAI,CAAC;IACnB,UAAU,EAAE,IAAI,CAAC;IACjB,yEAAyE;IACzE,IAAI,EAAE,IAAI,CAAC;CACZ;AAED,MAAM,MAAM,aAAa,GAAG,KAAK,CAAC,aAAa,CAAC;IAC9C,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;;OAIG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,GAAG,SAAS,CAAC,UAAU,CAAC,CAAC;CACtD,CAAC,CAAC;AAEH,MAAM,WAAW,SAAS;IACxB,4EAA4E;IAC5E,OAAO,EAAE,MAAM,CAAC;IAChB,qDAAqD;IACrD,IAAI,EAAE,aAAa,CAAC;IACpB,yBAAyB;IACzB,MAAM,EAAE,SAAS,CAAC;IAClB;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;;OAIG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,qDAAqD;IACrD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;;;OAMG;IACH,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAID,wBAAgB,iBAAiB,CAAC,EAChC,MAAM,EACN,QAAQ,GACT,EAAE;IACD,MAAM,EAAE,SAAS,CAAC;IAClB,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;CAC3B,qBAGA;AAED,wBAAgB,YAAY,IAAI,SAAS,CAQxC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AppConfigContext.jsx","sourceRoot":"","sources":["../../src/context/AppConfigContext.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,aAAa,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"AppConfigContext.jsx","sourceRoot":"","sources":["../../src/context/AppConfigContext.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,aAAa,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,OAAO,CAAC;AAyElE,MAAM,gBAAgB,GAAG,aAAa,CAAmB,IAAI,CAAC,CAAC;AAE/D,MAAM,UAAU,iBAAiB,CAAC,EAChC,MAAM,EACN,QAAQ,GAIT;IACC,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC,MAAM,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC;IAC9C,OAAO,CAAC,gBAAgB,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,gBAAgB,CAAC,QAAQ,CAAC,CAAC;AACzF,CAAC;AAED,MAAM,UAAU,YAAY;IAC1B,MAAM,GAAG,GAAG,UAAU,CAAC,gBAAgB,CAAC,CAAC;IACzC,IAAI,CAAC,GAAG,EAAE,CAAC;QACT,MAAM,IAAI,KAAK,CACb,gFAAgF,CACjF,CAAC;IACJ,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AccountPickerScreen.d.ts","sourceRoot":"","sources":["../../../src/screens/auth/AccountPickerScreen.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"AccountPickerScreen.d.ts","sourceRoot":"","sources":["../../../src/screens/auth/AccountPickerScreen.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAiB1B,iDAAiD;AACjD,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,wBAAwB;IACvC,QAAQ,EAAE,eAAe,EAAE,CAAC;IAC5B,sCAAsC;IACtC,QAAQ,EAAE,CAAC,OAAO,EAAE,eAAe,KAAK,IAAI,CAAC;IAC7C,wCAAwC;IACxC,YAAY,EAAE,MAAM,IAAI,CAAC;IACzB,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,EAClC,QAAQ,EACR,QAAQ,EACR,YAAY,EACZ,SAAS,GACV,EAAE,wBAAwB,qBA0D1B;AAED,eAAe,mBAAmB,CAAC"}
|
|
@@ -6,6 +6,7 @@ import { useColorScheme } from "nativewind";
|
|
|
6
6
|
import { MutedText, Text } from "../../components/text";
|
|
7
7
|
import { Button } from "../../components/button";
|
|
8
8
|
import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, } from "../../components/card";
|
|
9
|
+
import { withClickTracking } from "../../services/telemetry";
|
|
9
10
|
/**
|
|
10
11
|
* "Choose an account" screen for multi-session SSO (OIDC `prompt=select_account`
|
|
11
12
|
* style). Presentational: the host app supplies the accounts and handles the
|
|
@@ -26,7 +27,7 @@ export function AccountPickerScreen({ accounts, onSelect, onUseAnother, isLoadin
|
|
|
26
27
|
</CardHeader>
|
|
27
28
|
|
|
28
29
|
<CardContent className="gap-2">
|
|
29
|
-
{accounts.length === 0 ? (<MutedText className="text-sm">No signed-in accounts.</MutedText>) : (accounts.map((a) => (<Pressable key={a.userId} onPress={() => onSelect(a)} disabled={isLoading} accessibilityRole="button" accessibilityLabel={`Continue as ${a.username}`} className="rounded-md border border-border px-3 py-3">
|
|
30
|
+
{accounts.length === 0 ? (<MutedText className="text-sm">No signed-in accounts.</MutedText>) : (accounts.map((a) => (<Pressable key={a.userId} onPress={withClickTracking("auth.account_picker.on_select", () => onSelect(a))} disabled={isLoading} accessibilityRole="button" accessibilityLabel={`Continue as ${a.username}`} className="rounded-md border border-border px-3 py-3">
|
|
30
31
|
<Text className="text-sm text-foreground">
|
|
31
32
|
{a.displayName || a.username}
|
|
32
33
|
</Text>
|
|
@@ -35,7 +36,7 @@ export function AccountPickerScreen({ accounts, onSelect, onUseAnother, isLoadin
|
|
|
35
36
|
</CardContent>
|
|
36
37
|
|
|
37
38
|
<CardFooter className="border-t-0">
|
|
38
|
-
<Button className="w-full" variant="outline" onPress={onUseAnother} accessibilityLabel="Use another account" accessibilityRole="button">
|
|
39
|
+
<Button className="w-full" variant="outline" telemetryId="auth.account_picker.use_another" onPress={onUseAnother} accessibilityLabel="Use another account" accessibilityRole="button">
|
|
39
40
|
Use another account
|
|
40
41
|
</Button>
|
|
41
42
|
</CardFooter>
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AccountPickerScreen.jsx","sourceRoot":"","sources":["../../../src/screens/auth/AccountPickerScreen.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AACrD,OAAO,EAAE,YAAY,EAAE,MAAM,gCAAgC,CAAC;AAC9D,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAC5C,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AACjD,OAAO,EACL,IAAI,EACJ,WAAW,EACX,eAAe,EACf,UAAU,EACV,UAAU,EACV,SAAS,GACV,MAAM,uBAAuB,CAAC;
|
|
1
|
+
{"version":3,"file":"AccountPickerScreen.jsx","sourceRoot":"","sources":["../../../src/screens/auth/AccountPickerScreen.tsx"],"names":[],"mappings":"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"}
|
|
@@ -46,6 +46,50 @@ export interface AuthScreenProps {
|
|
|
46
46
|
decoration?: React.ReactNode;
|
|
47
47
|
/** Welcome-view tagline/subtitle. Defaults to AppConfig `tagline`. */
|
|
48
48
|
welcomeCtaLabel?: string;
|
|
49
|
+
/**
|
|
50
|
+
* Show the Sign Up half of the toggle. Defaults to `true`.
|
|
51
|
+
*
|
|
52
|
+
* Set `false` where the server has no self-service registration — an
|
|
53
|
+
* identity provider whose tenant is `closed`, for instance. The toggle is
|
|
54
|
+
* hidden rather than disabled, because a control that cannot ever succeed is
|
|
55
|
+
* worse than no control.
|
|
56
|
+
*/
|
|
57
|
+
signUpEnabled?: boolean;
|
|
58
|
+
/**
|
|
59
|
+
* Collect an invite code on the Sign Up form and require it. For servers
|
|
60
|
+
* whose registration is invite-only.
|
|
61
|
+
*/
|
|
62
|
+
requireInviteCode?: boolean;
|
|
63
|
+
/**
|
|
64
|
+
* Override registration entirely. When provided this runs instead of
|
|
65
|
+
* `useAuth().register`, and the screen shows `signUpSuccessMessage` rather
|
|
66
|
+
* than assuming the user is now signed in.
|
|
67
|
+
*
|
|
68
|
+
* This exists because registration is not uniform across the estate: the
|
|
69
|
+
* single-tenant apps sign you straight in, while the multi-tenant IdP creates
|
|
70
|
+
* an unverified account, emails a link, and leaves you signed out. Reshaping
|
|
71
|
+
* the shared `AuthService.register` to cover both would have changed the
|
|
72
|
+
* contract vault and simple-finance already depend on.
|
|
73
|
+
*/
|
|
74
|
+
onSignUp?: (input: {
|
|
75
|
+
email: string;
|
|
76
|
+
username: string;
|
|
77
|
+
password: string;
|
|
78
|
+
displayName: string;
|
|
79
|
+
inviteCode?: string;
|
|
80
|
+
}) => Promise<void>;
|
|
81
|
+
/** Shown after a successful `onSignUp`. */
|
|
82
|
+
signUpSuccessMessage?: string;
|
|
83
|
+
/**
|
|
84
|
+
* Render the welcome stage (logo, decoration, "Get Started") before the form.
|
|
85
|
+
* Defaults to `true`.
|
|
86
|
+
*
|
|
87
|
+
* Set `false` to land straight on the form. An app whose only purpose at this
|
|
88
|
+
* URL is to authenticate has nothing to introduce, and a button that only
|
|
89
|
+
* reveals the form it precedes is pure friction. The in-card "← Back" is
|
|
90
|
+
* hidden too, since there is no stage behind it.
|
|
91
|
+
*/
|
|
92
|
+
showWelcome?: boolean;
|
|
49
93
|
}
|
|
50
94
|
/**
|
|
51
95
|
* Single consolidated unauthenticated screen with three animated stages:
|
|
@@ -54,6 +98,6 @@ export interface AuthScreenProps {
|
|
|
54
98
|
* are themed popups and a connection-settings dialog can be folded in. Preserves
|
|
55
99
|
* the passkey ceremony + non-PRF unlock and the HIBP/strength checks.
|
|
56
100
|
*/
|
|
57
|
-
export declare function AuthScreen({ passwordLabel, passwordPlaceholder, socialAuth, passkeyEnabled, passkeyUnlockPlaceholder, passkeyUnlockTitle, passkeyUnlockDescription, settings, transition, decoration, welcomeCtaLabel, }?: AuthScreenProps): React.JSX.Element;
|
|
101
|
+
export declare function AuthScreen({ passwordLabel, passwordPlaceholder, socialAuth, passkeyEnabled, passkeyUnlockPlaceholder, passkeyUnlockTitle, passkeyUnlockDescription, settings, transition, decoration, welcomeCtaLabel, signUpEnabled, requireInviteCode, onSignUp, signUpSuccessMessage, showWelcome, }?: AuthScreenProps): React.JSX.Element;
|
|
58
102
|
export default AuthScreen;
|
|
59
103
|
//# sourceMappingURL=AuthScreen.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AuthScreen.d.ts","sourceRoot":"","sources":["../../../src/screens/auth/AuthScreen.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA4D,MAAM,OAAO,CAAC;AAGjF,OAAO,QASN,MAAM,yBAAyB,CAAC;
|
|
1
|
+
{"version":3,"file":"AuthScreen.d.ts","sourceRoot":"","sources":["../../../src/screens/auth/AuthScreen.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA4D,MAAM,OAAO,CAAC;AAGjF,OAAO,QASN,MAAM,yBAAyB,CAAC;AAsCjC,KAAK,iBAAiB,GAAG,KAAK,CAAC,cAAc,CAAC,OAAO,QAAQ,CAAC,IAAI,CAAC,CAAC;AAEpE;;;;;;GAMG;AACH,MAAM,WAAW,kBAAkB;IACjC,MAAM,CAAC,EAAE,iBAAiB,CAAC,QAAQ,CAAC,CAAC;IACrC,QAAQ,CAAC,EAAE,iBAAiB,CAAC,UAAU,CAAC,CAAC;IACzC,OAAO,CAAC,EAAE,iBAAiB,CAAC,SAAS,CAAC,CAAC;CACxC;AAaD,MAAM,WAAW,eAAe;IAC9B,mCAAmC;IACnC,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,yCAAyC;IACzC,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,kEAAkE;IAClE,UAAU,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC7B;;;OAGG;IACH,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,wBAAwB,CAAC,EAAE,MAAM,CAAC;IAClC,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,wBAAwB,CAAC,EAAE,MAAM,CAAC;IAClC;;;OAGG;IACH,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,OAAO,EAAE,MAAM,IAAI,CAAA;KAAE,KAAK,KAAK,CAAC,SAAS,CAAC;IACjF,yDAAyD;IACzD,UAAU,CAAC,EAAE,kBAAkB,CAAC;IAChC;;;;OAIG;IACH,UAAU,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC7B,sEAAsE;IACtE,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB;;;;;;;OAOG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB;;;OAGG;IACH,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B;;;;;;;;;;OAUG;IACH,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE;QACjB,KAAK,EAAE,MAAM,CAAC;QACd,QAAQ,EAAE,MAAM,CAAC;QACjB,QAAQ,EAAE,MAAM,CAAC;QACjB,WAAW,EAAE,MAAM,CAAC;QACpB,UAAU,CAAC,EAAE,MAAM,CAAC;KACrB,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACpB,2CAA2C;IAC3C,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B;;;;;;;;OAQG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED;;;;;;GAMG;AACH,wBAAgB,UAAU,CAAC,EACzB,aAA0B,EAC1B,mBAA2C,EAC3C,UAAU,EACV,cAAsB,EACtB,wBAAuD,EACvD,kBAAwC,EACxC,wBAAsJ,EACtJ,QAAQ,EACR,UAAU,EACV,UAAU,EACV,eAA+B,EAC/B,aAAoB,EACpB,iBAAyB,EACzB,QAAQ,EACR,oBAA+F,EAC/F,WAAkB,GACnB,GAAE,eAAoB,qBA03BtB;AAED,eAAe,UAAU,CAAC"}
|
|
@@ -2,6 +2,7 @@ import React, { useCallback, useEffect, useMemo, useRef, useState } from "react"
|
|
|
2
2
|
import { KeyboardAvoidingView, Platform, ScrollView, View } from "react-native";
|
|
3
3
|
import { SafeAreaView } from "react-native-safe-area-context";
|
|
4
4
|
import Animated, { Easing, FadeInDown, FadeOutUp, LinearTransition, runOnJS, useAnimatedStyle, useSharedValue, withTiming, } from "react-native-reanimated";
|
|
5
|
+
import { withClickTracking } from "../../services/telemetry";
|
|
5
6
|
import { StatusBar } from "expo-status-bar";
|
|
6
7
|
import { Ionicons } from "@expo/vector-icons";
|
|
7
8
|
import { useColorScheme } from "nativewind";
|
|
@@ -45,21 +46,23 @@ const STAGE_IN_MS = 360;
|
|
|
45
46
|
* are themed popups and a connection-settings dialog can be folded in. Preserves
|
|
46
47
|
* the passkey ceremony + non-PRF unlock and the HIBP/strength checks.
|
|
47
48
|
*/
|
|
48
|
-
export function AuthScreen({ passwordLabel = "Password", passwordPlaceholder = "Enter your password", socialAuth, passkeyEnabled = false, passkeyUnlockPlaceholder = "Enter your master password", passkeyUnlockTitle = "Unlock your vault", passkeyUnlockDescription = "Signed in successfully. This passkey can't unlock the vault on its own — enter your master password to decrypt your data.", settings, transition, decoration, welcomeCtaLabel = "Get Started", } = {}) {
|
|
49
|
+
export function AuthScreen({ passwordLabel = "Password", passwordPlaceholder = "Enter your password", socialAuth, passkeyEnabled = false, passkeyUnlockPlaceholder = "Enter your master password", passkeyUnlockTitle = "Unlock your vault", passkeyUnlockDescription = "Signed in successfully. This passkey can't unlock the vault on its own — enter your master password to decrypt your data.", settings, transition, decoration, welcomeCtaLabel = "Get Started", signUpEnabled = true, requireInviteCode = false, onSignUp, signUpSuccessMessage = "Account created. Check your email to verify the address, then sign in.", showWelcome = true, } = {}) {
|
|
49
50
|
const { login: authLogin, register: authRegister, loginWithPasskey, unlockWithPassword, } = useAuth();
|
|
50
51
|
const router = useNavigation();
|
|
51
|
-
const { appName, Logo, tagline, routes, termsText, privacyText } = useAppConfig();
|
|
52
|
+
const { appName, Logo, tagline, routes, termsText, privacyText, logoIsWordmark, logoWidth, } = useAppConfig();
|
|
52
53
|
const { colorScheme } = useColorScheme();
|
|
53
54
|
const isDark = colorScheme === "dark";
|
|
54
55
|
const t = useMemo(() => ({ ...DEFAULT_TRANSITION, ...transition }), [transition]);
|
|
55
56
|
const [mode, setMode] = useState("signin");
|
|
56
|
-
const [
|
|
57
|
+
const [inviteCode, setInviteCode] = useState("");
|
|
58
|
+
const [signedUp, setSignedUp] = useState(null);
|
|
59
|
+
const [stage, setStage] = useState(showWelcome ? "welcome" : "auth");
|
|
57
60
|
const isSignup = mode === "signup";
|
|
58
61
|
// Stage morph: `stage` is the target; `displayStage` is what's rendered.
|
|
59
62
|
// On a stage change we fade/scale the content out, swap displayStage at zero
|
|
60
63
|
// opacity (so the centered-welcome → top-anchored-form layout change is
|
|
61
64
|
// invisible — no teleport, no overlap of the two states), then fade in.
|
|
62
|
-
const [displayStage, setDisplayStage] = useState("welcome");
|
|
65
|
+
const [displayStage, setDisplayStage] = useState(showWelcome ? "welcome" : "auth");
|
|
63
66
|
// Fade-through: `stage` is the target, `displayStage` is what's rendered. On
|
|
64
67
|
// a change the content fades/scales out, swaps at zero opacity (so the
|
|
65
68
|
// position change between stages is never seen), then fades back in.
|
|
@@ -153,6 +156,9 @@ export function AuthScreen({ passwordLabel = "Password", passwordPlaceholder = "
|
|
|
153
156
|
else if (password !== confirmPassword) {
|
|
154
157
|
next.confirmPassword = "Passwords do not match";
|
|
155
158
|
}
|
|
159
|
+
if (requireInviteCode && !inviteCode.trim()) {
|
|
160
|
+
next.inviteCode = "An invite code is required to create an account here";
|
|
161
|
+
}
|
|
156
162
|
}
|
|
157
163
|
return next;
|
|
158
164
|
};
|
|
@@ -220,7 +226,26 @@ export function AuthScreen({ passwordLabel = "Password", passwordPlaceholder = "
|
|
|
220
226
|
}
|
|
221
227
|
try {
|
|
222
228
|
const username = (email.split("@")[0] || "user").replace(/[^a-zA-Z0-9_]/g, "_").toLowerCase();
|
|
223
|
-
|
|
229
|
+
if (onSignUp) {
|
|
230
|
+
// The host owns the contract. It may leave the user signed out (the
|
|
231
|
+
// IdP emails a verification link), so show the notice rather than
|
|
232
|
+
// assuming a session now exists.
|
|
233
|
+
await onSignUp({
|
|
234
|
+
email: email.trim().toLowerCase(),
|
|
235
|
+
username,
|
|
236
|
+
password,
|
|
237
|
+
displayName: displayName.trim(),
|
|
238
|
+
inviteCode: requireInviteCode ? inviteCode.trim() : undefined,
|
|
239
|
+
});
|
|
240
|
+
setSignedUp(signUpSuccessMessage);
|
|
241
|
+
setPassword("");
|
|
242
|
+
setConfirmPassword("");
|
|
243
|
+
setInviteCode("");
|
|
244
|
+
setMode("signin");
|
|
245
|
+
}
|
|
246
|
+
else {
|
|
247
|
+
await authRegister(email.trim().toLowerCase(), username, password, displayName.trim());
|
|
248
|
+
}
|
|
224
249
|
await Haptics.notificationAsync(Haptics.NotificationFeedbackType.Success);
|
|
225
250
|
}
|
|
226
251
|
catch (error) {
|
|
@@ -370,7 +395,7 @@ export function AuthScreen({ passwordLabel = "Password", passwordPlaceholder = "
|
|
|
370
395
|
(thumb-reachable) so users don't stretch to the corner. */}
|
|
371
396
|
<View className="mt-4 h-10 flex-row items-center justify-between">
|
|
372
397
|
<View />
|
|
373
|
-
{settings ? (<IconButton variant="ghost" size="sm" onPress={() => setSettingsOpen(true)} accessibilityLabel="Connection settings" accessibilityRole="button">
|
|
398
|
+
{settings ? (<IconButton variant="ghost" size="sm" telemetryId="auth.auth.settings_open" onPress={() => setSettingsOpen(true)} accessibilityLabel="Connection settings" accessibilityRole="button">
|
|
374
399
|
<Ionicons name="settings-outline" size={22} color={isDark ? "#A3A3A3" : "#737373"}/>
|
|
375
400
|
</IconButton>) : (<View />)}
|
|
376
401
|
</View>
|
|
@@ -385,10 +410,14 @@ export function AuthScreen({ passwordLabel = "Password", passwordPlaceholder = "
|
|
|
385
410
|
morphStyle,
|
|
386
411
|
]}>
|
|
387
412
|
<View className="mb-6 items-center">
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
413
|
+
{logoIsWordmark ? (
|
|
414
|
+
// The mark carries the name; height is left to the
|
|
415
|
+
// component so a wide wordmark keeps its aspect ratio, and
|
|
416
|
+
// the ink colour comes from the already-resolved theme.
|
|
417
|
+
<Logo width={logoWidth ?? 160} color={isDark ? "#FFFFFF" : "#000000"}/>) : (<View className="flex-row items-center">
|
|
418
|
+
<Logo width={72} height={72} style={{ marginRight: -2 }}/>
|
|
419
|
+
<Heading className="text-4xl">{appName}</Heading>
|
|
420
|
+
</View>)}
|
|
392
421
|
{tagline ? (<Text className="mt-1 font-mono text-base text-muted-foreground">
|
|
393
422
|
{tagline}
|
|
394
423
|
</Text>) : null}
|
|
@@ -397,7 +426,7 @@ export function AuthScreen({ passwordLabel = "Password", passwordPlaceholder = "
|
|
|
397
426
|
{onWelcome ? (<View style={{ width: "100%", alignItems: "center" }}>
|
|
398
427
|
{decoration ? (<View className="mb-6 w-full items-center">{decoration}</View>) : null}
|
|
399
428
|
<View style={{ width: "100%", maxWidth: 320 }}>
|
|
400
|
-
<Button className="w-full" onPress={() => setStage("auth")} accessibilityLabel={welcomeCtaLabel} accessibilityRole="button" testID="welcome-get-started">
|
|
429
|
+
<Button className="w-full" telemetryId="auth.auth.stage" onPress={() => setStage("auth")} accessibilityLabel={welcomeCtaLabel} accessibilityRole="button" testID="welcome-get-started">
|
|
401
430
|
{welcomeCtaLabel}
|
|
402
431
|
</Button>
|
|
403
432
|
</View>
|
|
@@ -426,23 +455,24 @@ export function AuthScreen({ passwordLabel = "Password", passwordPlaceholder = "
|
|
|
426
455
|
</>)}
|
|
427
456
|
</CardContent>
|
|
428
457
|
<CardFooter className="flex-col gap-3 border-t-0 pt-0">
|
|
429
|
-
{forgotSent ? (<Button className="w-full" onPress={goAuth} accessibilityLabel="Back to sign in">
|
|
458
|
+
{forgotSent ? (<Button className="w-full" telemetryId="auth.auth.go_auth" onPress={goAuth} accessibilityLabel="Back to sign in">
|
|
430
459
|
Back to Sign In
|
|
431
460
|
</Button>) : (<>
|
|
432
|
-
<Button className="w-full" onPress={handleForgot} isLoading={isLoading} accessibilityLabel="Send reset link">
|
|
461
|
+
<Button className="w-full" telemetryId="auth.auth.forgot" onPress={handleForgot} isLoading={isLoading} accessibilityLabel="Send reset link">
|
|
433
462
|
Send reset link
|
|
434
463
|
</Button>
|
|
435
|
-
<Text className="mt-1 text-center font-mono text-sm text-muted-foreground" onPress={goAuth} accessibilityRole="button" accessibilityLabel="Back to sign in">
|
|
464
|
+
<Text className="mt-1 text-center font-mono text-sm text-muted-foreground" onPress={withClickTracking("auth.auth.go_auth_2", goAuth)} accessibilityRole="button" accessibilityLabel="Back to sign in">
|
|
436
465
|
← Back
|
|
437
466
|
</Text>
|
|
438
467
|
</>)}
|
|
439
468
|
</CardFooter>
|
|
440
469
|
</>) : (<>
|
|
441
470
|
<CardHeader className="gap-4">
|
|
442
|
-
<SegmentedToggle value={mode} onChange={switchMode} options={[
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
471
|
+
{signUpEnabled ? (<SegmentedToggle value={mode} onChange={switchMode} options={[
|
|
472
|
+
{ key: "signin", label: "Sign In" },
|
|
473
|
+
{ key: "signup", label: "Sign Up" },
|
|
474
|
+
]}/>) : null}
|
|
475
|
+
{signedUp ? (<MutedText className="text-sm leading-5">{signedUp}</MutedText>) : null}
|
|
446
476
|
<CardTitle>
|
|
447
477
|
{isSignup ? "Create your account" : `Sign in to ${appName}`}
|
|
448
478
|
</CardTitle>
|
|
@@ -452,7 +482,7 @@ export function AuthScreen({ passwordLabel = "Password", passwordPlaceholder = "
|
|
|
452
482
|
<Animated.View layout={t.layout}>
|
|
453
483
|
<CardContent className="gap-3 pb-3">
|
|
454
484
|
{!isSignup && passkeyEnabled ? (<Animated.View entering={t.entering} exiting={t.exiting} layout={t.layout}>
|
|
455
|
-
<Button variant="outline" className="w-full" onPress={handlePasskeyLogin} isLoading={passkeyStep === "passkey-loading"} accessibilityLabel="Sign in with passkey" accessibilityRole="button" testID="login-passkey-button">
|
|
485
|
+
<Button variant="outline" className="w-full" telemetryId="auth.auth.passkey_login" onPress={handlePasskeyLogin} isLoading={passkeyStep === "passkey-loading"} accessibilityLabel="Sign in with passkey" accessibilityRole="button" testID="login-passkey-button">
|
|
456
486
|
<View className="flex-row items-center justify-center">
|
|
457
487
|
<Ionicons name="key-outline" size={20} color={isDark ? "#FFFFFF" : "#000000"} className="mr-2"/>
|
|
458
488
|
<Text className="text-base font-medium">
|
|
@@ -503,12 +533,21 @@ export function AuthScreen({ passwordLabel = "Password", passwordPlaceholder = "
|
|
|
503
533
|
clearField("confirmPassword");
|
|
504
534
|
}} placeholder="Re-enter your password" autoComplete="password-new" returnKeyType="done" onSubmitEditing={handleSubmit} invalid={!!errors.confirmPassword} accessibilityLabel="Confirm password" testID="auth-confirm-password-input"/>
|
|
505
535
|
</ShakeField>
|
|
536
|
+
{requireInviteCode ? (<View className="mt-3">
|
|
537
|
+
<ShakeField shake={confirmShake} label="Invite code">
|
|
538
|
+
<Input value={inviteCode} onChangeText={(text) => {
|
|
539
|
+
setInviteCode(text);
|
|
540
|
+
clearField("inviteCode");
|
|
541
|
+
}} placeholder="Paste your invite code" autoCapitalize="none" autoCorrect={false} returnKeyType="done" onSubmitEditing={handleSubmit} invalid={!!errors.inviteCode} accessibilityLabel="Invite code" testID="auth-invite-code-input"/>
|
|
542
|
+
</ShakeField>
|
|
543
|
+
<FormError message={errors.inviteCode}/>
|
|
544
|
+
</View>) : null}
|
|
506
545
|
</Animated.View>) : (<Animated.View key="forgot" entering={t.entering} exiting={t.exiting} layout={t.layout} className="items-end">
|
|
507
|
-
<Text className="font-mono text-sm underline" onPress={() => {
|
|
546
|
+
<Text className="font-mono text-sm underline" onPress={withClickTracking("auth.auth.stage_2", () => {
|
|
508
547
|
setStage("forgot");
|
|
509
548
|
setForgotSent(false);
|
|
510
549
|
setErrors({});
|
|
511
|
-
}} accessibilityRole="link" accessibilityLabel="Forgot password">
|
|
550
|
+
})} accessibilityRole="link" accessibilityLabel="Forgot password">
|
|
512
551
|
Forgot password?
|
|
513
552
|
</Text>
|
|
514
553
|
</Animated.View>)}
|
|
@@ -517,7 +556,7 @@ export function AuthScreen({ passwordLabel = "Password", passwordPlaceholder = "
|
|
|
517
556
|
<CardFooter className="flex-col gap-3 border-t-0 pt-0">
|
|
518
557
|
<FormError message={firstError}/>
|
|
519
558
|
|
|
520
|
-
<Button className="w-full" onPress={handleSubmit} isLoading={isLoading} accessibilityLabel={isSignup ? "Create account" : "Sign in"} accessibilityRole="button" testID="auth-submit">
|
|
559
|
+
<Button className="w-full" telemetryId="auth.auth.submit" onPress={handleSubmit} isLoading={isLoading} accessibilityLabel={isSignup ? "Create account" : "Sign in"} accessibilityRole="button" testID="auth-submit">
|
|
521
560
|
{isSignup ? "Create Account" : "Sign In"}
|
|
522
561
|
</Button>
|
|
523
562
|
|
|
@@ -527,8 +566,11 @@ export function AuthScreen({ passwordLabel = "Password", passwordPlaceholder = "
|
|
|
527
566
|
</>) : null}
|
|
528
567
|
|
|
529
568
|
{/* In-card back — thumb-reachable, so users don't
|
|
530
|
-
have to stretch to the top-left corner arrow.
|
|
531
|
-
|
|
569
|
+
have to stretch to the top-left corner arrow.
|
|
570
|
+
Hidden when there is no welcome stage behind it. */}
|
|
571
|
+
<Text className={showWelcome
|
|
572
|
+
? "mt-1 text-center font-mono text-sm text-muted-foreground"
|
|
573
|
+
: "hidden"} onPress={withClickTracking("auth.auth.stage_3", () => setStage("welcome"))} accessibilityRole="button" accessibilityLabel="Back">
|
|
532
574
|
← Back
|
|
533
575
|
</Text>
|
|
534
576
|
</CardFooter>
|
|
@@ -542,11 +584,11 @@ export function AuthScreen({ passwordLabel = "Password", passwordPlaceholder = "
|
|
|
542
584
|
<View className="mb-6 mt-4">
|
|
543
585
|
<MutedText className="text-center text-xs leading-5">
|
|
544
586
|
By continuing, you agree to our{" "}
|
|
545
|
-
<Text className="text-xs text-foreground underline" onPress={() => setLegal("terms")} accessibilityRole="link" accessibilityLabel="Terms of Service">
|
|
587
|
+
<Text className="text-xs text-foreground underline" onPress={withClickTracking("auth.auth.legal", () => setLegal("terms"))} accessibilityRole="link" accessibilityLabel="Terms of Service">
|
|
546
588
|
Terms of Service
|
|
547
589
|
</Text>{" "}
|
|
548
590
|
and{" "}
|
|
549
|
-
<Text className="text-xs text-foreground underline" onPress={() => setLegal("privacy")} accessibilityRole="link" accessibilityLabel="Privacy Policy">
|
|
591
|
+
<Text className="text-xs text-foreground underline" onPress={withClickTracking("auth.auth.legal_2", () => setLegal("privacy"))} accessibilityRole="link" accessibilityLabel="Privacy Policy">
|
|
550
592
|
Privacy Policy
|
|
551
593
|
</Text>
|
|
552
594
|
</MutedText>
|
|
@@ -576,7 +618,7 @@ export function AuthScreen({ passwordLabel = "Password", passwordPlaceholder = "
|
|
|
576
618
|
}} placeholder={passkeyUnlockPlaceholder} error={unlockError} autoComplete="password" returnKeyType="done" onSubmitEditing={handleUnlockAfterPasskey} accessibilityLabel="Master password" testID="login-passkey-unlock-input"/>
|
|
577
619
|
</View>
|
|
578
620
|
<DialogFooter style={{ justifyContent: "center" }}>
|
|
579
|
-
<DialogButton variant="primary" onPress={isLoading ? () => { } : handleUnlockAfterPasskey} accessibilityLabel="Unlock" accessibilityRole="button">
|
|
621
|
+
<DialogButton variant="primary" onPress={withClickTracking("auth.auth.action", isLoading ? () => { } : handleUnlockAfterPasskey)} accessibilityLabel="Unlock" accessibilityRole="button">
|
|
580
622
|
{isLoading ? "Unlocking…" : "Unlock"}
|
|
581
623
|
</DialogButton>
|
|
582
624
|
</DialogFooter>
|