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:
+52
-8
@@ -1,4 +1,4 @@
|
||||
import { Link, router } from "expo-router";
|
||||
import { Link } from "expo-router";
|
||||
import { useState } from "react";
|
||||
import {
|
||||
KeyboardAvoidingView,
|
||||
@@ -10,6 +10,7 @@ import {
|
||||
} from "react-native";
|
||||
import { FullScreen } from "@/components/Screen";
|
||||
import { AuthBackground } from "@/components/AppBackground";
|
||||
import { AuthServerPicker } from "@/components/AuthServerPicker";
|
||||
import { HeadingText, Logo } from "@/components/Logo";
|
||||
import { Button } from "@/components/ui/Button";
|
||||
import { Card } from "@/components/ui/Card";
|
||||
@@ -19,10 +20,12 @@ import { useAccounts } from "@/contexts/AccountsContext";
|
||||
import { useAuthClient } from "@/contexts/AuthContext";
|
||||
import { useAppTheme } from "@/contexts/ThemeContext";
|
||||
import { registerAccount } from "@/lib/auth-api";
|
||||
import { finalizeAuthenticatedAccount } from "@/lib/auth-storage";
|
||||
import { isRequiredString, isValidEmail, isValidPassword, useFieldVisibility } from "@/lib/form-validation";
|
||||
|
||||
export default function RegisterScreen() {
|
||||
const authClient = useAuthClient();
|
||||
const { apiUrl, registerAccount: saveAccount } = useAccounts();
|
||||
const { apiUrl, activeAccountId, registerAccount: saveAccount } = useAccounts();
|
||||
const { colors } = useAppTheme();
|
||||
const [firstName, setFirstName] = useState("");
|
||||
const [lastName, setLastName] = useState("");
|
||||
@@ -30,8 +33,31 @@ export default function RegisterScreen() {
|
||||
const [password, setPassword] = useState("");
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [serverReady, setServerReady] = useState(true);
|
||||
const { touch, visible, markSubmitted } = useFieldVisibility();
|
||||
|
||||
const firstNameError = isRequiredString(firstName) ? undefined : "First name is required";
|
||||
const lastNameError = isRequiredString(lastName) ? undefined : "Last name is required";
|
||||
const emailValidationError = isValidEmail(email)
|
||||
? undefined
|
||||
: email.trim()
|
||||
? "Enter a valid email"
|
||||
: "Email is required";
|
||||
const passwordValidationError = isValidPassword(password)
|
||||
? undefined
|
||||
: password
|
||||
? "Password must be at least 8 characters"
|
||||
: "Password is required";
|
||||
const canRegister =
|
||||
isRequiredString(firstName) &&
|
||||
isRequiredString(lastName) &&
|
||||
isValidEmail(email) &&
|
||||
isValidPassword(password) &&
|
||||
serverReady;
|
||||
|
||||
async function handleRegister() {
|
||||
markSubmitted();
|
||||
if (!canRegister) return;
|
||||
setError(null);
|
||||
setLoading(true);
|
||||
|
||||
@@ -49,22 +75,22 @@ export default function RegisterScreen() {
|
||||
});
|
||||
|
||||
if (signInError) {
|
||||
router.replace("/(auth)/sign-in");
|
||||
setError(signInError.message || "Account created but sign-in failed. Try signing in.");
|
||||
return;
|
||||
}
|
||||
|
||||
const session = await authClient.getSession();
|
||||
const user = session.data?.user;
|
||||
if (user) {
|
||||
await saveAccount({
|
||||
instanceUrl: apiUrl,
|
||||
await finalizeAuthenticatedAccount({
|
||||
apiUrl,
|
||||
userId: user.id,
|
||||
email: user.email,
|
||||
name: user.name,
|
||||
activeAccountId,
|
||||
registerAccount: saveAccount,
|
||||
});
|
||||
}
|
||||
|
||||
router.replace("/(app)");
|
||||
} catch (err) {
|
||||
setError(err instanceof Error ? err.message : "Registration failed");
|
||||
} finally {
|
||||
@@ -83,6 +109,7 @@ export default function RegisterScreen() {
|
||||
contentContainerStyle={styles.container}
|
||||
keyboardShouldPersistTaps="handled"
|
||||
>
|
||||
<AuthServerPicker onReadyChange={setServerReady} />
|
||||
<Card style={styles.card}>
|
||||
<View style={styles.header}>
|
||||
<Logo size="lg" />
|
||||
@@ -99,8 +126,11 @@ export default function RegisterScreen() {
|
||||
label="First name"
|
||||
value={firstName}
|
||||
onChangeText={setFirstName}
|
||||
onBlur={() => touch("firstName")}
|
||||
autoComplete="given-name"
|
||||
placeholder="Jane"
|
||||
required
|
||||
error={visible("firstName") ? firstNameError : undefined}
|
||||
/>
|
||||
</View>
|
||||
<View style={styles.half}>
|
||||
@@ -108,8 +138,11 @@ export default function RegisterScreen() {
|
||||
label="Last name"
|
||||
value={lastName}
|
||||
onChangeText={setLastName}
|
||||
onBlur={() => touch("lastName")}
|
||||
autoComplete="family-name"
|
||||
placeholder="Doe"
|
||||
required
|
||||
error={visible("lastName") ? lastNameError : undefined}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
@@ -121,7 +154,10 @@ export default function RegisterScreen() {
|
||||
keyboardType="email-address"
|
||||
value={email}
|
||||
onChangeText={setEmail}
|
||||
onBlur={() => touch("email")}
|
||||
placeholder="you@example.com"
|
||||
required
|
||||
error={visible("email") ? emailValidationError : undefined}
|
||||
/>
|
||||
<Input
|
||||
label="Password"
|
||||
@@ -129,14 +165,22 @@ export default function RegisterScreen() {
|
||||
autoComplete="new-password"
|
||||
value={password}
|
||||
onChangeText={setPassword}
|
||||
onBlur={() => touch("password")}
|
||||
placeholder="At least 8 characters"
|
||||
required
|
||||
error={visible("password") ? passwordValidationError : undefined}
|
||||
/>
|
||||
|
||||
{error ? (
|
||||
<Text style={[styles.error, { color: colors.destructive }]}>{error}</Text>
|
||||
) : null}
|
||||
|
||||
<Button title="Create Account" loading={loading} onPress={handleRegister} />
|
||||
<Button
|
||||
title="Create Account"
|
||||
loading={loading}
|
||||
disabled={!canRegister}
|
||||
onPress={handleRegister}
|
||||
/>
|
||||
</View>
|
||||
|
||||
<Text style={[styles.footer, { color: colors.mutedForeground }]}>
|
||||
|
||||
Reference in New Issue
Block a user