Add Authentik sign-in, fix tab scroll insets, and polish multi-account auth.

Mobile app detects SSO per server, supports OAuth sign-in, and preserves saved
sessions when adding accounts. Tab screens get proper chrome layout and tab-bar
clearance with scrollable page headers.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-06-18 02:27:31 -04:00
parent 3daf123399
commit 0b2d65a4e9
21 changed files with 449 additions and 200 deletions
+32 -18
View File
@@ -50,6 +50,7 @@ export function AppLockProvider({ children }: { children: ReactNode }) {
const [biometricAvailable, setBiometricAvailable] = useState(false);
const [biometricLabel, setBiometricLabel] = useState("Biometrics");
const wasBackgrounded = useRef(false);
const biometricUnlockInProgress = useRef(false);
const hydrated = useRef(false);
useEffect(() => {
@@ -105,11 +106,17 @@ export function AppLockProvider({ children }: { children: ReactNode }) {
const subscription = AppState.addEventListener("change", (nextState: AppStateStatus) => {
if (!hydrated.current || !enabled || !activeAccountId) return;
if (nextState === "background" || nextState === "inactive") {
// Only true backgrounding should re-lock — `inactive` fires during Face ID,
// Control Center, and other system sheets and must not trigger another lock.
if (nextState === "background") {
wasBackgrounded.current = true;
}
if (nextState === "active" && wasBackgrounded.current) {
if (
nextState === "active" &&
wasBackgrounded.current &&
!biometricUnlockInProgress.current
) {
wasBackgrounded.current = false;
setIsLocked(true);
}
@@ -125,6 +132,7 @@ export function AppLockProvider({ children }: { children: ReactNode }) {
if (!stored || stored !== pin) {
return false;
}
wasBackgrounded.current = false;
setIsLocked(false);
return true;
},
@@ -136,24 +144,30 @@ export function AppLockProvider({ children }: { children: ReactNode }) {
return false;
}
const result = await LocalAuthentication.authenticateAsync({
promptMessage: "Unlock beenvoice",
cancelLabel: "Use PIN",
disableDeviceFallback: false,
biometricsSecurityLevel: "weak",
});
biometricUnlockInProgress.current = true;
try {
const result = await LocalAuthentication.authenticateAsync({
promptMessage: "Unlock beenvoice",
cancelLabel: "Use PIN",
disableDeviceFallback: false,
biometricsSecurityLevel: "weak",
});
if (!result.success) {
return false;
if (!result.success) {
return false;
}
if (!biometricEnabled) {
await setBiometricEnabled(activeAccountId, true);
setBiometricEnabledState(true);
}
wasBackgrounded.current = false;
setIsLocked(false);
return true;
} finally {
biometricUnlockInProgress.current = false;
}
if (!biometricEnabled) {
await setBiometricEnabled(activeAccountId, true);
setBiometricEnabledState(true);
}
setIsLocked(false);
return true;
}, [biometricAvailable, biometricEnabled, activeAccountId]);
const enableLock = useCallback(