@entropy-softworks/ui 2026.8.15 → 2026.8.18
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/auth/LockOverlay.jsx +1 -1
- package/lib/components/auth/LockOverlay.jsx.map +1 -1
- package/lib/components/auth/ProtectedRoute.jsx +1 -1
- package/lib/components/auth/ProtectedRoute.jsx.map +1 -1
- package/lib/components/card.jsx +1 -1
- package/lib/components/card.jsx.map +1 -1
- package/lib/components/context-menu.jsx +1 -1
- package/lib/components/context-menu.jsx.map +1 -1
- package/lib/components/data-table.jsx +2 -2
- package/lib/components/data-table.jsx.map +1 -1
- package/lib/components/dropdown-menu.jsx +4 -4
- package/lib/components/dropdown-menu.jsx.map +1 -1
- package/lib/components/entropy-mark.jsx +1 -1
- package/lib/components/entropy-mark.jsx.map +1 -1
- package/lib/components/error-boundary.jsx +2 -2
- package/lib/components/error-boundary.jsx.map +1 -1
- package/lib/components/pagination.jsx +1 -1
- package/lib/components/pagination.jsx.map +1 -1
- package/lib/components/password-generator.jsx +18 -18
- package/lib/components/password-generator.jsx.map +1 -1
- package/lib/components/pin-pad.d.ts.map +1 -1
- package/lib/components/pin-pad.jsx +5 -5
- package/lib/components/pin-pad.jsx.map +1 -1
- package/lib/components/popover.jsx +1 -1
- package/lib/components/popover.jsx.map +1 -1
- package/lib/components/settings-row.jsx +2 -2
- package/lib/components/settings-row.jsx.map +1 -1
- package/lib/hooks/useAutoLock.d.ts +22 -1
- package/lib/hooks/useAutoLock.d.ts.map +1 -1
- package/lib/hooks/useAutoLock.js +7 -1
- package/lib/hooks/useAutoLock.js.map +1 -1
- package/lib/screens/auth/AccountPickerScreen.jsx +1 -1
- package/lib/screens/auth/AccountPickerScreen.jsx.map +1 -1
- package/lib/screens/auth/AuthScreen.jsx +2 -2
- package/lib/screens/auth/AuthScreen.jsx.map +1 -1
- package/lib/screens/auth/ConsentScreen.jsx +3 -3
- package/lib/screens/auth/ConsentScreen.jsx.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.jsx +2 -2
- package/lib/screens/auth/ExpiredLinkScreen.jsx.map +1 -1
- package/lib/screens/auth/ForgotPasswordScreen.jsx +3 -3
- package/lib/screens/auth/ForgotPasswordScreen.jsx.map +1 -1
- package/lib/screens/auth/ImportDataScreen.jsx +2 -2
- package/lib/screens/auth/ImportDataScreen.jsx.map +1 -1
- package/lib/screens/auth/LoginScreen.jsx +2 -2
- package/lib/screens/auth/LoginScreen.jsx.map +1 -1
- package/lib/screens/auth/MfaEnrollScreen.jsx +3 -3
- package/lib/screens/auth/MfaEnrollScreen.jsx.map +1 -1
- package/lib/screens/auth/PasskeyEnrollScreen.jsx +3 -3
- package/lib/screens/auth/PasskeyEnrollScreen.jsx.map +1 -1
- package/lib/screens/auth/RegisterScreen.jsx +2 -2
- package/lib/screens/auth/RegisterScreen.jsx.map +1 -1
- package/lib/screens/auth/ResetPasswordScreen.jsx +3 -3
- package/lib/screens/auth/ResetPasswordScreen.jsx.map +1 -1
- package/lib/screens/auth/ServerConfigScreen.jsx +23 -23
- package/lib/screens/auth/ServerConfigScreen.jsx.map +1 -1
- package/lib/screens/auth/VerifyEmailScreen.jsx +1 -1
- package/lib/screens/auth/VerifyEmailScreen.jsx.map +1 -1
- package/lib/screens/auth/WelcomeScreen.jsx +1 -1
- package/lib/screens/auth/WelcomeScreen.jsx.map +1 -1
- package/package.json +1 -1
- package/src/components/auth/LockOverlay.tsx +1 -1
- package/src/components/auth/ProtectedRoute.tsx +1 -1
- package/src/components/card.tsx +1 -1
- package/src/components/context-menu.tsx +1 -1
- package/src/components/data-table.tsx +2 -2
- package/src/components/dropdown-menu.tsx +5 -5
- package/src/components/entropy-mark.tsx +1 -1
- package/src/components/error-boundary.tsx +2 -2
- package/src/components/pagination.tsx +1 -1
- package/src/components/password-generator.tsx +18 -18
- package/src/components/pin-pad.tsx +7 -5
- package/src/components/popover.tsx +1 -1
- package/src/components/settings-row.tsx +2 -2
- package/src/hooks/useAutoLock.ts +32 -2
- package/src/screens/auth/AccountPickerScreen.tsx +1 -1
- package/src/screens/auth/AuthScreen.tsx +2 -2
- package/src/screens/auth/ConsentScreen.tsx +3 -3
- package/src/screens/auth/ErrorScreen.tsx +2 -2
- package/src/screens/auth/ExpiredLinkScreen.tsx +2 -2
- package/src/screens/auth/ForgotPasswordScreen.tsx +3 -3
- package/src/screens/auth/ImportDataScreen.tsx +2 -2
- package/src/screens/auth/LoginScreen.tsx +2 -2
- package/src/screens/auth/MfaEnrollScreen.tsx +3 -3
- package/src/screens/auth/PasskeyEnrollScreen.tsx +3 -3
- package/src/screens/auth/RegisterScreen.tsx +2 -2
- package/src/screens/auth/ResetPasswordScreen.tsx +3 -3
- package/src/screens/auth/ServerConfigScreen.tsx +23 -23
- package/src/screens/auth/VerifyEmailScreen.tsx +1 -1
- package/src/screens/auth/WelcomeScreen.tsx +1 -1
|
@@ -15,7 +15,7 @@ const DropdownMenuContent = React.forwardRef<
|
|
|
15
15
|
ref={ref}
|
|
16
16
|
sideOffset={sideOffset}
|
|
17
17
|
className={cn(
|
|
18
|
-
"z-50 min-w-[8rem] overflow-hidden rounded-md border border-border bg-popover p-1",
|
|
18
|
+
"z-50 min-w-[8rem] overflow-hidden rounded-md border border-border dark:border-neutral-800 bg-popover dark:bg-black p-1",
|
|
19
19
|
className
|
|
20
20
|
)}
|
|
21
21
|
style={
|
|
@@ -50,8 +50,8 @@ const DropdownMenuItem = React.forwardRef<
|
|
|
50
50
|
<DropdownMenuPrimitive.Item
|
|
51
51
|
ref={ref}
|
|
52
52
|
className={cn(
|
|
53
|
-
"relative flex flex-row items-center rounded-sm px-2 py-1.5 web:cursor-pointer web:outline-none active:bg-accent",
|
|
54
|
-
"web:hover:bg-accent web:focus:bg-accent",
|
|
53
|
+
"relative flex flex-row items-center rounded-sm px-2 py-1.5 web:cursor-pointer web:outline-none active:bg-accent dark:active:bg-neutral-800",
|
|
54
|
+
"web:hover:bg-accent web:focus:bg-accent dark:web:hover:bg-neutral-800 dark:web:focus:bg-neutral-800",
|
|
55
55
|
props.disabled && "opacity-50 web:pointer-events-none",
|
|
56
56
|
className
|
|
57
57
|
)}
|
|
@@ -91,7 +91,7 @@ const DropdownMenuLabel = React.forwardRef<
|
|
|
91
91
|
>(({ className, ...props }, ref) => (
|
|
92
92
|
<DropdownMenuPrimitive.Label
|
|
93
93
|
ref={ref}
|
|
94
|
-
className={cn("px-2 py-1.5 text-sm font-semibold text-foreground", className)}
|
|
94
|
+
className={cn("px-2 py-1.5 text-sm font-semibold text-foreground dark:text-white", className)}
|
|
95
95
|
{...props}
|
|
96
96
|
/>
|
|
97
97
|
));
|
|
@@ -103,7 +103,7 @@ const DropdownMenuSeparator = React.forwardRef<
|
|
|
103
103
|
>(({ className, ...props }, ref) => (
|
|
104
104
|
<DropdownMenuPrimitive.Separator
|
|
105
105
|
ref={ref}
|
|
106
|
-
className={cn("-mx-1 my-1 h-px bg-border", className)}
|
|
106
|
+
className={cn("-mx-1 my-1 h-px bg-border dark:bg-neutral-800", className)}
|
|
107
107
|
{...props}
|
|
108
108
|
/>
|
|
109
109
|
));
|
|
@@ -129,7 +129,7 @@ export function EntropyByline({
|
|
|
129
129
|
{/* Flat at rest; press reveals the echo-cascade (the website's hover
|
|
130
130
|
animation, touch-ported — see entropy-mark-animated.tsx). */}
|
|
131
131
|
<EntropyMarkAnimated size={size} variant={variant} scheme={scheme} />
|
|
132
|
-
<Text className="font-mono text-xs text-muted-foreground">{label}</Text>
|
|
132
|
+
<Text className="font-mono text-xs text-muted-foreground dark:text-neutral-400">{label}</Text>
|
|
133
133
|
</View>
|
|
134
134
|
);
|
|
135
135
|
}
|
|
@@ -53,9 +53,9 @@ export class ErrorBoundary extends React.Component<Props, State> {
|
|
|
53
53
|
return (
|
|
54
54
|
this.props.fallback ?? (
|
|
55
55
|
<View className="flex-1 items-center justify-center p-8">
|
|
56
|
-
<Text className="mb-4 text-lg font-semibold text-foreground">Something went wrong</Text>
|
|
56
|
+
<Text className="mb-4 text-lg font-semibold text-foreground dark:text-white">Something went wrong</Text>
|
|
57
57
|
<Button onPress={this.handleReset}>
|
|
58
|
-
<Text className="text-primary-foreground">Try Again</Text>
|
|
58
|
+
<Text className="text-primary-foreground dark:text-black">Try Again</Text>
|
|
59
59
|
</Button>
|
|
60
60
|
</View>
|
|
61
61
|
)
|
|
@@ -116,7 +116,7 @@ export function Pagination({
|
|
|
116
116
|
<DropdownMenuContent>
|
|
117
117
|
{pageSizeOptions.map((size) => (
|
|
118
118
|
<DropdownMenuItem key={size} onPress={() => onPageSizeChange(size)}>
|
|
119
|
-
<MutedText className="text-sm text-foreground">{`${size} / page`}</MutedText>
|
|
119
|
+
<MutedText className="text-sm text-foreground dark:text-white">{`${size} / page`}</MutedText>
|
|
120
120
|
</DropdownMenuItem>
|
|
121
121
|
))}
|
|
122
122
|
</DropdownMenuContent>
|
|
@@ -157,9 +157,9 @@ export const PasswordGenerator = React.memo(function PasswordGenerator({
|
|
|
157
157
|
|
|
158
158
|
return (
|
|
159
159
|
<>
|
|
160
|
-
<View className="mb-4 rounded-lg border border-border bg-card p-4 dark:bg-neutral-900/50">
|
|
160
|
+
<View className="mb-4 rounded-lg border border-border dark:border-neutral-800 bg-card p-4 dark:bg-neutral-900/50">
|
|
161
161
|
<View className="mb-3 flex-row items-center justify-between">
|
|
162
|
-
<Text className="text-sm font-medium text-muted-foreground">Generated Password</Text>
|
|
162
|
+
<Text className="text-sm font-medium text-muted-foreground dark:text-neutral-400">Generated Password</Text>
|
|
163
163
|
<View className="flex-row items-center">
|
|
164
164
|
<View
|
|
165
165
|
className="h-2 w-2 rounded-full"
|
|
@@ -171,9 +171,9 @@ export const PasswordGenerator = React.memo(function PasswordGenerator({
|
|
|
171
171
|
</View>
|
|
172
172
|
</View>
|
|
173
173
|
|
|
174
|
-
<View className="mb-2 rounded-lg bg-background p-3">
|
|
174
|
+
<View className="mb-2 rounded-lg bg-background dark:bg-black p-3">
|
|
175
175
|
<Text
|
|
176
|
-
className="font-mono text-base text-foreground"
|
|
176
|
+
className="font-mono text-base text-foreground dark:text-white"
|
|
177
177
|
selectable
|
|
178
178
|
numberOfLines={2}
|
|
179
179
|
testID="generator-output"
|
|
@@ -187,7 +187,7 @@ export const PasswordGenerator = React.memo(function PasswordGenerator({
|
|
|
187
187
|
{breach === "checking" ? (
|
|
188
188
|
<>
|
|
189
189
|
<ActivityIndicator size="small" color={isDark ? "#a3a3a3" : "#737373"} />
|
|
190
|
-
<Text className="text-xs text-muted-foreground">Checking breach database…</Text>
|
|
190
|
+
<Text className="text-xs text-muted-foreground dark:text-neutral-400">Checking breach database…</Text>
|
|
191
191
|
</>
|
|
192
192
|
) : breach === "safe" ? (
|
|
193
193
|
<>
|
|
@@ -203,7 +203,7 @@ export const PasswordGenerator = React.memo(function PasswordGenerator({
|
|
|
203
203
|
size={12}
|
|
204
204
|
color={isDark ? "#a3a3a3" : "#737373"}
|
|
205
205
|
/>
|
|
206
|
-
<Text className="text-xs text-muted-foreground">
|
|
206
|
+
<Text className="text-xs text-muted-foreground dark:text-neutral-400">
|
|
207
207
|
Couldn't check breaches (offline)
|
|
208
208
|
</Text>
|
|
209
209
|
</>
|
|
@@ -213,14 +213,14 @@ export const PasswordGenerator = React.memo(function PasswordGenerator({
|
|
|
213
213
|
<View className="flex-row gap-2">
|
|
214
214
|
<Pressable
|
|
215
215
|
onPress={handleGenerate}
|
|
216
|
-
className="active:scale-98 flex-1 rounded-lg bg-primary px-4 py-3"
|
|
216
|
+
className="active:scale-98 flex-1 rounded-lg bg-primary dark:bg-white px-4 py-3"
|
|
217
217
|
accessibilityRole="button"
|
|
218
218
|
accessibilityLabel="Generate password"
|
|
219
219
|
testID="generator-generate"
|
|
220
220
|
>
|
|
221
221
|
<View className="flex-row items-center justify-center">
|
|
222
222
|
<Ionicons name="refresh" size={18} color={isDark ? "black" : "white"} />
|
|
223
|
-
<Text className="ml-2 font-semibold text-primary-foreground">Generate</Text>
|
|
223
|
+
<Text className="ml-2 font-semibold text-primary-foreground dark:text-black">Generate</Text>
|
|
224
224
|
</View>
|
|
225
225
|
</Pressable>
|
|
226
226
|
|
|
@@ -228,14 +228,14 @@ export const PasswordGenerator = React.memo(function PasswordGenerator({
|
|
|
228
228
|
<Pressable
|
|
229
229
|
onPress={handleCopy}
|
|
230
230
|
disabled={!password}
|
|
231
|
-
className="active:scale-98 rounded-lg bg-secondary px-4 py-3"
|
|
231
|
+
className="active:scale-98 rounded-lg bg-secondary dark:bg-neutral-800 px-4 py-3"
|
|
232
232
|
accessibilityRole="button"
|
|
233
233
|
accessibilityLabel="Copy password"
|
|
234
234
|
accessibilityState={{ disabled: !password }}
|
|
235
235
|
>
|
|
236
236
|
<View className="flex-row items-center justify-center">
|
|
237
237
|
<Ionicons name="copy-outline" size={18} color={isDark ? "white" : "black"} />
|
|
238
|
-
<Text className="ml-2 font-semibold text-foreground">Copy</Text>
|
|
238
|
+
<Text className="ml-2 font-semibold text-foreground dark:text-white">Copy</Text>
|
|
239
239
|
</View>
|
|
240
240
|
</Pressable>
|
|
241
241
|
)}
|
|
@@ -244,27 +244,27 @@ export const PasswordGenerator = React.memo(function PasswordGenerator({
|
|
|
244
244
|
<Pressable
|
|
245
245
|
onPress={() => onSelect(password)}
|
|
246
246
|
disabled={!password}
|
|
247
|
-
className="active:scale-98 rounded-lg bg-secondary px-4 py-3"
|
|
247
|
+
className="active:scale-98 rounded-lg bg-secondary dark:bg-neutral-800 px-4 py-3"
|
|
248
248
|
accessibilityRole="button"
|
|
249
249
|
accessibilityLabel="Use password"
|
|
250
250
|
accessibilityState={{ disabled: !password }}
|
|
251
251
|
>
|
|
252
252
|
<View className="flex-row items-center justify-center">
|
|
253
253
|
<Ionicons name="checkmark" size={18} color={isDark ? "white" : "black"} />
|
|
254
|
-
<Text className="ml-2 font-semibold text-foreground">Use</Text>
|
|
254
|
+
<Text className="ml-2 font-semibold text-foreground dark:text-white">Use</Text>
|
|
255
255
|
</View>
|
|
256
256
|
</Pressable>
|
|
257
257
|
)}
|
|
258
258
|
</View>
|
|
259
259
|
</View>
|
|
260
260
|
|
|
261
|
-
<View className="rounded-lg border border-border bg-card p-4 dark:bg-neutral-900/50">
|
|
262
|
-
<Text className="mb-4 text-base font-semibold text-foreground">Options</Text>
|
|
261
|
+
<View className="rounded-lg border border-border dark:border-neutral-800 bg-card p-4 dark:bg-neutral-900/50">
|
|
262
|
+
<Text className="mb-4 text-base font-semibold text-foreground dark:text-white">Options</Text>
|
|
263
263
|
|
|
264
264
|
<View className="mb-5">
|
|
265
265
|
<View className="mb-2 flex-row items-center justify-between">
|
|
266
|
-
<Text className="text-sm text-foreground">Password Length</Text>
|
|
267
|
-
<Text className="font-mono text-sm font-semibold text-primary">{length}</Text>
|
|
266
|
+
<Text className="text-sm text-foreground dark:text-white">Password Length</Text>
|
|
267
|
+
<Text className="font-mono text-sm font-semibold text-primary dark:text-white">{length}</Text>
|
|
268
268
|
</View>
|
|
269
269
|
<Slider
|
|
270
270
|
value={length}
|
|
@@ -280,8 +280,8 @@ export const PasswordGenerator = React.memo(function PasswordGenerator({
|
|
|
280
280
|
accessibilityRole="adjustable"
|
|
281
281
|
/>
|
|
282
282
|
<View className="mt-1 flex-row justify-between">
|
|
283
|
-
<Text className="text-xs text-muted-foreground">8</Text>
|
|
284
|
-
<Text className="text-xs text-muted-foreground">128</Text>
|
|
283
|
+
<Text className="text-xs text-muted-foreground dark:text-neutral-400">8</Text>
|
|
284
|
+
<Text className="text-xs text-muted-foreground dark:text-neutral-400">128</Text>
|
|
285
285
|
</View>
|
|
286
286
|
</View>
|
|
287
287
|
|
|
@@ -107,9 +107,9 @@ export function PinPad({
|
|
|
107
107
|
className={
|
|
108
108
|
i < pin.length
|
|
109
109
|
? error
|
|
110
|
-
? "h-3.5 w-3.5 rounded-full bg-destructive"
|
|
111
|
-
: "h-3.5 w-3.5 rounded-full bg-primary"
|
|
112
|
-
: "h-3.5 w-3.5 rounded-full border-2 border-border"
|
|
110
|
+
? "h-3.5 w-3.5 rounded-full bg-destructive dark:bg-red-900"
|
|
111
|
+
: "h-3.5 w-3.5 rounded-full bg-primary dark:bg-white"
|
|
112
|
+
: "h-3.5 w-3.5 rounded-full border-2 border-border dark:border-neutral-800"
|
|
113
113
|
}
|
|
114
114
|
/>
|
|
115
115
|
))}
|
|
@@ -144,11 +144,13 @@ function Key({
|
|
|
144
144
|
return (
|
|
145
145
|
<Pressable
|
|
146
146
|
onPress={onPress}
|
|
147
|
-
className="h-20 w-20 items-center justify-center rounded-full active:bg-secondary"
|
|
147
|
+
className="h-20 w-20 items-center justify-center rounded-full active:bg-secondary dark:active:bg-neutral-800"
|
|
148
148
|
accessibilityRole="button"
|
|
149
149
|
accessibilityLabel={label ?? "key"}
|
|
150
150
|
>
|
|
151
|
-
{icon ??
|
|
151
|
+
{icon ?? (
|
|
152
|
+
<Text className="font-sans-medium text-2xl text-foreground dark:text-white">{label}</Text>
|
|
153
|
+
)}
|
|
152
154
|
</Pressable>
|
|
153
155
|
);
|
|
154
156
|
}
|
|
@@ -23,7 +23,7 @@ const PopoverContent = React.forwardRef<
|
|
|
23
23
|
align={align}
|
|
24
24
|
side={side}
|
|
25
25
|
className={cn(
|
|
26
|
-
"z-50 w-72 rounded-lg border border-border p-4",
|
|
26
|
+
"z-50 w-72 rounded-lg border border-border dark:border-neutral-800 p-4",
|
|
27
27
|
isDark ? "bg-neutral-900" : "bg-white",
|
|
28
28
|
Platform.OS === "web" && "shadow-lg",
|
|
29
29
|
className
|
|
@@ -72,7 +72,7 @@ export function SettingsRow({
|
|
|
72
72
|
<Text
|
|
73
73
|
className={cn(
|
|
74
74
|
"text-sm font-medium",
|
|
75
|
-
destructive ? "text-destructive" : "text-foreground"
|
|
75
|
+
destructive ? "text-destructive dark:text-red-900" : "text-foreground"
|
|
76
76
|
)}
|
|
77
77
|
>
|
|
78
78
|
{label}
|
|
@@ -113,7 +113,7 @@ export function SettingsSection({ title, destructive, children }: SettingsSectio
|
|
|
113
113
|
<Text
|
|
114
114
|
className={cn(
|
|
115
115
|
"mb-2 text-xs font-semibold tracking-wider",
|
|
116
|
-
destructive ? "text-destructive" : "text-muted-foreground"
|
|
116
|
+
destructive ? "text-destructive dark:text-red-900" : "text-muted-foreground dark:text-neutral-400"
|
|
117
117
|
)}
|
|
118
118
|
>
|
|
119
119
|
{title}
|
package/src/hooks/useAutoLock.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useEffect } from "react";
|
|
1
|
+
import { useCallback, useEffect, useRef } from "react";
|
|
2
2
|
import { AppState as RNAppState, Platform } from "react-native";
|
|
3
3
|
|
|
4
4
|
const DEFAULT_IDLE_LOCK_MS = 10 * 60 * 1000;
|
|
@@ -18,14 +18,37 @@ export interface UseAutoLockOptions {
|
|
|
18
18
|
idleLockMs?: number;
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
+
export interface UseAutoLockResult {
|
|
22
|
+
/**
|
|
23
|
+
* Call on any real user interaction to reset the idle clock. Required
|
|
24
|
+
* on native: unlike web (which listens on `window` for
|
|
25
|
+
* mousemove/touchstart/scroll/keydown directly), RN has no global
|
|
26
|
+
* "any touch happened anywhere" event — a touch inside a `Pressable`,
|
|
27
|
+
* `ScrollView`, etc. never reaches `window`, because there is no
|
|
28
|
+
* `window` to reach. Without a caller wiring this up (e.g. a capture-
|
|
29
|
+
* phase `onStartShouldSetResponderCapture` at the app root, returning
|
|
30
|
+
* `false` so it never actually claims the touch), the idle timer is
|
|
31
|
+
* armed once at mount and never reset — the session locks exactly
|
|
32
|
+
* `idleLockMs` after mount/foreground REGARDLESS of how actively the
|
|
33
|
+
* app is being used. Confirmed for real: a vault mid-active-use lost
|
|
34
|
+
* its key mid-session, and every item silently failed to decrypt
|
|
35
|
+
* (`Promise.allSettled` in the caller's list-fetch treats a decrypt
|
|
36
|
+
* failure as "drop this item", not "the vault relocked"), which reads
|
|
37
|
+
* to the user as vault items simply vanishing — no error, no visible
|
|
38
|
+
* cause, on 2026-08-17.
|
|
39
|
+
*/
|
|
40
|
+
notifyActivity: () => void;
|
|
41
|
+
}
|
|
42
|
+
|
|
21
43
|
/**
|
|
22
44
|
* Auto-lock a session after a period of inactivity. Generic over the
|
|
23
45
|
* lock predicate — vault wires `KeyManager.isUnlocked` / `KeyManager.lock`,
|
|
24
46
|
* other apps wire their own state. Web uses an idle-timer only; native
|
|
25
47
|
* additionally locks immediately when the app goes to the background.
|
|
26
48
|
*/
|
|
27
|
-
export function useAutoLock(options: UseAutoLockOptions):
|
|
49
|
+
export function useAutoLock(options: UseAutoLockOptions): UseAutoLockResult {
|
|
28
50
|
const { isUnlocked, onLock, isEnabled, idleLockMs = DEFAULT_IDLE_LOCK_MS } = options;
|
|
51
|
+
const onActivityRef = useRef<() => void>(() => {});
|
|
29
52
|
|
|
30
53
|
useEffect(() => {
|
|
31
54
|
let idleTimer: ReturnType<typeof setTimeout> | null = null;
|
|
@@ -64,6 +87,7 @@ export function useAutoLock(options: UseAutoLockOptions): void {
|
|
|
64
87
|
lastActivityAt = Date.now();
|
|
65
88
|
armIdleTimer();
|
|
66
89
|
};
|
|
90
|
+
onActivityRef.current = onActivity;
|
|
67
91
|
|
|
68
92
|
// On web, RN Web's AppState mirrors document visibility — tab-hidden
|
|
69
93
|
// becomes 'background', which would otherwise fire an immediate lock.
|
|
@@ -107,4 +131,10 @@ export function useAutoLock(options: UseAutoLockOptions): void {
|
|
|
107
131
|
};
|
|
108
132
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
109
133
|
}, []);
|
|
134
|
+
|
|
135
|
+
const notifyActivity = useCallback(() => {
|
|
136
|
+
onActivityRef.current();
|
|
137
|
+
}, []);
|
|
138
|
+
|
|
139
|
+
return { notifyActivity };
|
|
110
140
|
}
|
|
@@ -47,7 +47,7 @@ export function AccountPickerScreen({
|
|
|
47
47
|
const isDark = colorScheme === "dark";
|
|
48
48
|
|
|
49
49
|
return (
|
|
50
|
-
<SafeAreaView className="flex-1 bg-background">
|
|
50
|
+
<SafeAreaView className="flex-1 bg-background dark:bg-black">
|
|
51
51
|
<StatusBar style={isDark ? "light" : "dark"} />
|
|
52
52
|
<ScrollView
|
|
53
53
|
className="flex-1"
|
|
@@ -666,7 +666,7 @@ export function AuthScreen({
|
|
|
666
666
|
</>
|
|
667
667
|
)}
|
|
668
668
|
</CardContent>
|
|
669
|
-
<CardFooter className="flex-col gap-3 border-t-0 pt-0">
|
|
669
|
+
<CardFooter className="flex-col items-stretch gap-3 border-t-0 pt-0">
|
|
670
670
|
{forgotSent ? (
|
|
671
671
|
<Button
|
|
672
672
|
className="w-full"
|
|
@@ -908,7 +908,7 @@ export function AuthScreen({
|
|
|
908
908
|
)}
|
|
909
909
|
</CardContent>
|
|
910
910
|
|
|
911
|
-
<CardFooter className="flex-col gap-3 border-t-0 pt-0">
|
|
911
|
+
<CardFooter className="flex-col items-stretch gap-3 border-t-0 pt-0">
|
|
912
912
|
<FormError message={firstError} />
|
|
913
913
|
|
|
914
914
|
<Button
|
|
@@ -51,7 +51,7 @@ export function ConsentScreen({
|
|
|
51
51
|
const isDark = colorScheme === "dark";
|
|
52
52
|
|
|
53
53
|
return (
|
|
54
|
-
<SafeAreaView className="flex-1 bg-background">
|
|
54
|
+
<SafeAreaView className="flex-1 bg-background dark:bg-black">
|
|
55
55
|
<StatusBar style={isDark ? "light" : "dark"} />
|
|
56
56
|
<ScrollView
|
|
57
57
|
className="flex-1"
|
|
@@ -85,11 +85,11 @@ export function ConsentScreen({
|
|
|
85
85
|
))
|
|
86
86
|
)}
|
|
87
87
|
{error ? (
|
|
88
|
-
<Text className="text-sm text-destructive">{error}</Text>
|
|
88
|
+
<Text className="text-sm text-destructive dark:text-red-900">{error}</Text>
|
|
89
89
|
) : null}
|
|
90
90
|
</CardContent>
|
|
91
91
|
|
|
92
|
-
<CardFooter className="flex-col gap-3 border-t-0">
|
|
92
|
+
<CardFooter className="flex-col items-stretch gap-3 border-t-0">
|
|
93
93
|
<Button
|
|
94
94
|
className="w-full"
|
|
95
95
|
telemetryId="auth.consent.allow"
|
|
@@ -43,7 +43,7 @@ export function ErrorScreen({
|
|
|
43
43
|
const isDark = colorScheme === "dark";
|
|
44
44
|
|
|
45
45
|
return (
|
|
46
|
-
<SafeAreaView className="flex-1 bg-background">
|
|
46
|
+
<SafeAreaView className="flex-1 bg-background dark:bg-black">
|
|
47
47
|
<StatusBar style={isDark ? "light" : "dark"} />
|
|
48
48
|
<ScrollView
|
|
49
49
|
className="flex-1"
|
|
@@ -63,7 +63,7 @@ export function ErrorScreen({
|
|
|
63
63
|
) : null}
|
|
64
64
|
|
|
65
65
|
{onRetry || onHome ? (
|
|
66
|
-
<CardFooter className="flex-col gap-3 border-t-0">
|
|
66
|
+
<CardFooter className="flex-col items-stretch gap-3 border-t-0">
|
|
67
67
|
{onRetry ? (
|
|
68
68
|
<Button
|
|
69
69
|
className="w-full"
|
|
@@ -40,7 +40,7 @@ export function ExpiredLinkScreen({
|
|
|
40
40
|
const isDark = colorScheme === "dark";
|
|
41
41
|
|
|
42
42
|
return (
|
|
43
|
-
<SafeAreaView className="flex-1 bg-background">
|
|
43
|
+
<SafeAreaView className="flex-1 bg-background dark:bg-black">
|
|
44
44
|
<StatusBar style={isDark ? "light" : "dark"} />
|
|
45
45
|
<ScrollView
|
|
46
46
|
className="flex-1"
|
|
@@ -54,7 +54,7 @@ export function ExpiredLinkScreen({
|
|
|
54
54
|
</CardHeader>
|
|
55
55
|
|
|
56
56
|
{onRequestNew || onHome ? (
|
|
57
|
-
<CardFooter className="flex-col gap-3 border-t-0">
|
|
57
|
+
<CardFooter className="flex-col items-stretch gap-3 border-t-0">
|
|
58
58
|
{onRequestNew ? (
|
|
59
59
|
<Button
|
|
60
60
|
className="w-full"
|
|
@@ -70,7 +70,7 @@ export function ForgotPasswordScreen() {
|
|
|
70
70
|
};
|
|
71
71
|
|
|
72
72
|
return (
|
|
73
|
-
<SafeAreaView className="flex-1 bg-background">
|
|
73
|
+
<SafeAreaView className="flex-1 bg-background dark:bg-black">
|
|
74
74
|
<StatusBar style={isDark ? "light" : "dark"} />
|
|
75
75
|
<ScrollView
|
|
76
76
|
className="flex-1"
|
|
@@ -119,7 +119,7 @@ export function ForgotPasswordScreen() {
|
|
|
119
119
|
/>
|
|
120
120
|
</CardContent>
|
|
121
121
|
|
|
122
|
-
<CardFooter className="flex-col gap-3 border-t-0">
|
|
122
|
+
<CardFooter className="flex-col items-stretch gap-3 border-t-0">
|
|
123
123
|
<Button
|
|
124
124
|
className="w-full"
|
|
125
125
|
telemetryId="auth.forgot_password.submit"
|
|
@@ -145,7 +145,7 @@ export function ForgotPasswordScreen() {
|
|
|
145
145
|
</CardFooter>
|
|
146
146
|
</>
|
|
147
147
|
) : (
|
|
148
|
-
<CardFooter className="flex-col gap-3 border-t-0">
|
|
148
|
+
<CardFooter className="flex-col items-stretch gap-3 border-t-0">
|
|
149
149
|
<Button
|
|
150
150
|
className="w-full"
|
|
151
151
|
telemetryId="auth.forgot_password.push_2"
|
|
@@ -55,7 +55,7 @@ export function ImportDataScreen({ renderImport }: ImportDataScreenProps = {}) {
|
|
|
55
55
|
|
|
56
56
|
if (renderImport) {
|
|
57
57
|
return (
|
|
58
|
-
<SafeAreaView className="flex-1 bg-background">
|
|
58
|
+
<SafeAreaView className="flex-1 bg-background dark:bg-black">
|
|
59
59
|
<StatusBar style={isDark ? "light" : "dark"} />
|
|
60
60
|
{renderImport({ onComplete: completeAndNavigate, onSkip: completeAndNavigate })}
|
|
61
61
|
</SafeAreaView>
|
|
@@ -63,7 +63,7 @@ export function ImportDataScreen({ renderImport }: ImportDataScreenProps = {}) {
|
|
|
63
63
|
}
|
|
64
64
|
|
|
65
65
|
return (
|
|
66
|
-
<SafeAreaView className="flex-1 bg-background">
|
|
66
|
+
<SafeAreaView className="flex-1 bg-background dark:bg-black">
|
|
67
67
|
<StatusBar style={isDark ? "light" : "dark"} />
|
|
68
68
|
<ScrollView
|
|
69
69
|
className="flex-1"
|
|
@@ -269,7 +269,7 @@ export function LoginScreen({
|
|
|
269
269
|
|
|
270
270
|
return (
|
|
271
271
|
<>
|
|
272
|
-
<SafeAreaView className="flex-1 bg-background">
|
|
272
|
+
<SafeAreaView className="flex-1 bg-background dark:bg-black">
|
|
273
273
|
<StatusBar style={isDark ? "light" : "dark"} />
|
|
274
274
|
<ScrollView
|
|
275
275
|
className="flex-1"
|
|
@@ -387,7 +387,7 @@ export function LoginScreen({
|
|
|
387
387
|
</View>
|
|
388
388
|
</CardContent>
|
|
389
389
|
|
|
390
|
-
<CardFooter className="flex-col gap-3 border-t-0">
|
|
390
|
+
<CardFooter className="flex-col items-stretch gap-3 border-t-0">
|
|
391
391
|
<MutedText className="text-center text-sm">
|
|
392
392
|
Don't have an account?{" "}
|
|
393
393
|
<Text
|
|
@@ -160,7 +160,7 @@ export function MfaEnrollScreen({ authenticatorAppPromo }: MfaEnrollScreenProps
|
|
|
160
160
|
};
|
|
161
161
|
|
|
162
162
|
return (
|
|
163
|
-
<SafeAreaView className="flex-1 bg-background">
|
|
163
|
+
<SafeAreaView className="flex-1 bg-background dark:bg-black">
|
|
164
164
|
<StatusBar style={isDark ? "light" : "dark"} />
|
|
165
165
|
<ScrollView
|
|
166
166
|
className="flex-1"
|
|
@@ -189,7 +189,7 @@ export function MfaEnrollScreen({ authenticatorAppPromo }: MfaEnrollScreenProps
|
|
|
189
189
|
<CardContent className="gap-3">{authenticatorAppPromo}</CardContent>
|
|
190
190
|
) : null}
|
|
191
191
|
|
|
192
|
-
<CardFooter className="flex-col gap-3 border-t-0">
|
|
192
|
+
<CardFooter className="flex-col items-stretch gap-3 border-t-0">
|
|
193
193
|
<Button
|
|
194
194
|
className="w-full"
|
|
195
195
|
telemetryId="auth.mfa_enroll.start_setup"
|
|
@@ -293,7 +293,7 @@ export function MfaEnrollScreen({ authenticatorAppPromo }: MfaEnrollScreenProps
|
|
|
293
293
|
</View>
|
|
294
294
|
</CardContent>
|
|
295
295
|
|
|
296
|
-
<CardFooter className="flex-col gap-3 border-t-0">
|
|
296
|
+
<CardFooter className="flex-col items-stretch gap-3 border-t-0">
|
|
297
297
|
<Button
|
|
298
298
|
className="w-full"
|
|
299
299
|
telemetryId="auth.mfa_enroll.verify_code"
|
|
@@ -39,7 +39,7 @@ export function PasskeyEnrollScreen({
|
|
|
39
39
|
const isDark = colorScheme === "dark";
|
|
40
40
|
|
|
41
41
|
return (
|
|
42
|
-
<SafeAreaView className="flex-1 bg-background">
|
|
42
|
+
<SafeAreaView className="flex-1 bg-background dark:bg-black">
|
|
43
43
|
<StatusBar style={isDark ? "light" : "dark"} />
|
|
44
44
|
<ScrollView
|
|
45
45
|
className="flex-1"
|
|
@@ -58,7 +58,7 @@ export function PasskeyEnrollScreen({
|
|
|
58
58
|
|
|
59
59
|
<CardContent className="gap-2">
|
|
60
60
|
{error ? (
|
|
61
|
-
<Text className="text-sm text-destructive">{error}</Text>
|
|
61
|
+
<Text className="text-sm text-destructive dark:text-red-900">{error}</Text>
|
|
62
62
|
) : (
|
|
63
63
|
<MutedText className="text-sm">
|
|
64
64
|
You can always add or remove passkeys later in your account
|
|
@@ -67,7 +67,7 @@ export function PasskeyEnrollScreen({
|
|
|
67
67
|
)}
|
|
68
68
|
</CardContent>
|
|
69
69
|
|
|
70
|
-
<CardFooter className="flex-col gap-3 border-t-0">
|
|
70
|
+
<CardFooter className="flex-col items-stretch gap-3 border-t-0">
|
|
71
71
|
<Button
|
|
72
72
|
className="w-full"
|
|
73
73
|
telemetryId="auth.passkey_enroll.enroll"
|
|
@@ -210,7 +210,7 @@ export function RegisterScreen({
|
|
|
210
210
|
};
|
|
211
211
|
|
|
212
212
|
return (
|
|
213
|
-
<SafeAreaView className="flex-1 bg-background">
|
|
213
|
+
<SafeAreaView className="flex-1 bg-background dark:bg-black">
|
|
214
214
|
<StatusBar style={isDark ? "light" : "dark"} />
|
|
215
215
|
<KeyboardAvoidingView
|
|
216
216
|
behavior={Platform.OS === "ios" ? "padding" : "height"}
|
|
@@ -381,7 +381,7 @@ export function RegisterScreen({
|
|
|
381
381
|
</ShakeField>
|
|
382
382
|
</CardContent>
|
|
383
383
|
|
|
384
|
-
<CardFooter className="flex-col gap-3 border-t-0">
|
|
384
|
+
<CardFooter className="flex-col items-stretch gap-3 border-t-0">
|
|
385
385
|
<MutedText className="text-center text-sm">
|
|
386
386
|
Already have an account?{" "}
|
|
387
387
|
<Text
|
|
@@ -219,7 +219,7 @@ export function ResetPasswordScreen({ encryptionWarning }: ResetPasswordScreenPr
|
|
|
219
219
|
};
|
|
220
220
|
|
|
221
221
|
return (
|
|
222
|
-
<SafeAreaView className="flex-1 bg-background">
|
|
222
|
+
<SafeAreaView className="flex-1 bg-background dark:bg-black">
|
|
223
223
|
<StatusBar style={isDark ? "light" : "dark"} />
|
|
224
224
|
<ScrollView
|
|
225
225
|
className="flex-1"
|
|
@@ -267,7 +267,7 @@ export function ResetPasswordScreen({ encryptionWarning }: ResetPasswordScreenPr
|
|
|
267
267
|
</ShakeField>
|
|
268
268
|
</CardContent>
|
|
269
269
|
|
|
270
|
-
<CardFooter className="flex-col gap-3 border-t-0">
|
|
270
|
+
<CardFooter className="flex-col items-stretch gap-3 border-t-0">
|
|
271
271
|
<FormError message={firstError} />
|
|
272
272
|
|
|
273
273
|
<Button
|
|
@@ -345,7 +345,7 @@ export function ResetPasswordScreen({ encryptionWarning }: ResetPasswordScreenPr
|
|
|
345
345
|
</ShakeField>
|
|
346
346
|
</CardContent>
|
|
347
347
|
|
|
348
|
-
<CardFooter className="flex-col gap-3 border-t-0">
|
|
348
|
+
<CardFooter className="flex-col items-stretch gap-3 border-t-0">
|
|
349
349
|
<FormError message={firstError} />
|
|
350
350
|
|
|
351
351
|
<Button
|