Fix Live Activity lock screen rendering and polish multi-account auth.

Flatten widget layouts and use system colors so banner and expanded regions render on vibrant lock screens; migrate auth sessions per account to prevent double sign-in; scope app lock PIN to accounts; default clock description to "Clock In"; add architecture docs and deferred form validation on auth screens.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-06-18 01:23:36 -04:00
parent e6ea3d7c5d
commit 32ffe782ea
35 changed files with 1659 additions and 442 deletions
+33 -11
View File
@@ -11,6 +11,7 @@ import {
} from "react-native";
import { FullScreen } from "@/components/Screen";
import { AuthBackground } from "@/components/AppBackground";
import { AuthServerPicker } from "@/components/AuthServerPicker";
import { HeadingText } from "@/components/Logo";
import { Button } from "@/components/ui/Button";
import { Card } from "@/components/ui/Card";
@@ -20,6 +21,7 @@ import { useAppTheme } from "@/contexts/ThemeContext";
import { resetPassword } from "@/lib/auth-api";
import type { ThemeColors } from "@/lib/theme-palette";
import { useThemedStyles } from "@/lib/use-themed-styles";
import { isRequiredString, isValidPassword } from "@/lib/form-validation";
export default function ResetPasswordScreen() {
const styles = useThemedStyles(createResetPasswordStyles);
@@ -30,6 +32,7 @@ export default function ResetPasswordScreen() {
const [error, setError] = useState<string | null>(null);
const [success, setSuccess] = useState(false);
const [loading, setLoading] = useState(false);
const [serverReady, setServerReady] = useState(true);
useEffect(() => {
if (typeof tokenParam === "string" && tokenParam.length > 0) {
@@ -37,19 +40,25 @@ export default function ResetPasswordScreen() {
}
}, [tokenParam]);
const tokenError = isRequiredString(token) ? undefined : "Reset token is required";
const passwordError = isValidPassword(password)
? undefined
: password
? "Password must be at least 8 characters"
: "Password is required";
const confirmError =
confirmPassword && password !== confirmPassword ? "Passwords do not match" : undefined;
const canSubmit =
serverReady &&
isRequiredString(token) &&
isValidPassword(password) &&
password === confirmPassword &&
confirmPassword.length > 0;
async function handleSubmit() {
if (!canSubmit) return;
setError(null);
if (password.length < 8) {
setError("Password must be at least 8 characters");
return;
}
if (password !== confirmPassword) {
setError("Passwords do not match");
return;
}
setLoading(true);
try {
@@ -74,6 +83,8 @@ export default function ResetPasswordScreen() {
<Text style={styles.back}> Back</Text>
</Pressable>
<AuthServerPicker onReadyChange={setServerReady} />
<Card style={styles.card}>
<View style={styles.header}>
<HeadingText style={styles.title}>Set new password</HeadingText>
@@ -101,6 +112,8 @@ export default function ResetPasswordScreen() {
value={token}
onChangeText={setToken}
placeholder="Paste token from email"
required
error={tokenError}
/>
<Input
label="New password"
@@ -108,6 +121,8 @@ export default function ResetPasswordScreen() {
value={password}
onChangeText={setPassword}
placeholder="At least 8 characters"
required
error={passwordError}
/>
<Input
label="Confirm password"
@@ -115,11 +130,18 @@ export default function ResetPasswordScreen() {
value={confirmPassword}
onChangeText={setConfirmPassword}
placeholder="Repeat password"
required
error={confirmError}
/>
{error ? <Text style={styles.error}>{error}</Text> : null}
<Button title="Update password" loading={loading} onPress={handleSubmit} />
<Button
title="Update password"
loading={loading}
disabled={!canSubmit}
onPress={handleSubmit}
/>
</View>
)}
</Card>