Extract auth into hooks/useAuth; move form state into its pages
Build and Push Docker Image / build-and-push (push) Successful in 6m28s
Build and Push Docker Image / build-and-push (push) Successful in 6m28s
- useAuth owns authUser/authServerDown, the startup session check, and signIn/logout, so every sign-in path runs the same follow-up (switch local storage to the account, load reader ink). Previously reader ink only loaded on a page refresh, not right after logging in. - Login/2FA form state now lives in AuthPage, and 2FA setup, password change, and podcast name state in SettingsPage (23 fewer context values; typing in these forms no longer re-renders all of App). - Add tests for form sign-in, 2FA, session restore + logout, and the Settings podcast-name prefill. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
009fa1e78f
commit
49ffefed54
5 files changed
+162
-113
No files matched your search
+11
-16
@@ -1,19 +1,16 @@
|
||||
import { useState } from 'react';
|
||||
import { useApp } from '../context/AppContext.js';
|
||||
import { loginUser, registerUser, verifyMfaLogin, logoutUser } from '../syncService.js';
|
||||
import { switchStorageUser } from '../utils/bibleUtils.jsx';
|
||||
|
||||
export default function AuthPage() {
|
||||
const {
|
||||
authMode, setAuthMode,
|
||||
authForm, setAuthForm,
|
||||
authError, setAuthError,
|
||||
authBusy, setAuthBusy,
|
||||
authMfaPending, setAuthMfaPending,
|
||||
authMfaCode, setAuthMfaCode,
|
||||
authMfaUseBackup, setAuthMfaUseBackup,
|
||||
setAuthUser,
|
||||
setProjectIndex,
|
||||
} = useApp();
|
||||
const { signIn } = useApp();
|
||||
const [authMode, setAuthMode] = useState('login'); // 'login' | 'register'
|
||||
const [authForm, setAuthForm] = useState({ email: '', password: '' });
|
||||
const [authError, setAuthError] = useState('');
|
||||
const [authBusy, setAuthBusy] = useState(false);
|
||||
const [authMfaPending, setAuthMfaPending] = useState(false); // password ok, waiting on TOTP/backup code
|
||||
const [authMfaCode, setAuthMfaCode] = useState('');
|
||||
const [authMfaUseBackup, setAuthMfaUseBackup] = useState(false);
|
||||
|
||||
const submitAuth = async (e) => {
|
||||
e.preventDefault();
|
||||
@@ -30,8 +27,7 @@ export default function AuthPage() {
|
||||
setAuthMfaPending(true);
|
||||
return;
|
||||
}
|
||||
setAuthUser(result.data);
|
||||
setProjectIndex(switchStorageUser(result.data.id));
|
||||
signIn(result.data);
|
||||
};
|
||||
|
||||
const submitMfa = async (e) => {
|
||||
@@ -48,8 +44,7 @@ export default function AuthPage() {
|
||||
}
|
||||
setAuthMfaPending(false);
|
||||
setAuthMfaCode('');
|
||||
setAuthUser(result.data);
|
||||
setProjectIndex(switchStorageUser(result.data.id));
|
||||
signIn(result.data);
|
||||
};
|
||||
|
||||
if (authMfaPending) {
|
||||
|
||||
+15
-14
@@ -1,3 +1,4 @@
|
||||
import { useState } from 'react';
|
||||
import { useApp } from '../context/AppContext.js';
|
||||
import {
|
||||
startMfaSetup,
|
||||
@@ -12,21 +13,21 @@ export default function SettingsPage() {
|
||||
authUser, setAuthUser,
|
||||
authStatus,
|
||||
goHome,
|
||||
mfaSetup, setMfaSetup,
|
||||
mfaSetupCode, setMfaSetupCode,
|
||||
mfaSetupError, setMfaSetupError,
|
||||
mfaSetupBusy, setMfaSetupBusy,
|
||||
mfaBackupCodes, setMfaBackupCodes,
|
||||
mfaDisablePassword, setMfaDisablePassword,
|
||||
mfaDisableError, setMfaDisableError,
|
||||
podcastNameInput, setPodcastNameInput,
|
||||
podcastNameSaving, setPodcastNameSaving,
|
||||
podcastNameSaved, setPodcastNameSaved,
|
||||
changePasswordForm, setChangePasswordForm,
|
||||
changePasswordBusy, setChangePasswordBusy,
|
||||
changePasswordError, setChangePasswordError,
|
||||
changePasswordSaved, setChangePasswordSaved,
|
||||
} = useApp();
|
||||
const [mfaSetup, setMfaSetup] = useState(null); // { secret, qrCodeDataUrl } while setup is in progress
|
||||
const [mfaSetupCode, setMfaSetupCode] = useState('');
|
||||
const [mfaSetupError, setMfaSetupError] = useState('');
|
||||
const [mfaSetupBusy, setMfaSetupBusy] = useState(false);
|
||||
const [mfaBackupCodes, setMfaBackupCodes] = useState(null); // shown once, right after enabling
|
||||
const [mfaDisablePassword, setMfaDisablePassword] = useState('');
|
||||
const [mfaDisableError, setMfaDisableError] = useState('');
|
||||
const [podcastNameInput, setPodcastNameInput] = useState(authUser?.podcastName ?? '');
|
||||
const [podcastNameSaving, setPodcastNameSaving] = useState(false);
|
||||
const [podcastNameSaved, setPodcastNameSaved] = useState(false);
|
||||
const [changePasswordForm, setChangePasswordForm] = useState({ current: '', next: '', confirm: '' });
|
||||
const [changePasswordBusy, setChangePasswordBusy] = useState(false);
|
||||
const [changePasswordError, setChangePasswordError] = useState('');
|
||||
const [changePasswordSaved, setChangePasswordSaved] = useState(false);
|
||||
|
||||
const startSetup = async () => {
|
||||
setMfaSetupError('');
|
||||
|
||||
Reference in new issue
Block a user