fix(auth): surface validation errors in email login/register forms

Validation failure was previously silent — the form's submit() returned
without dispatching or showing anything on screen, so users could not tell
why the login/register button appeared to do nothing.

Changes:
- Add validationError local state in EmailLoginForm and EmailRegisterForm
- Display first validation error via AuthErrorMessage, prioritised over
  the server's globalError (validation errors feel more immediate)
- Clear validation error on field change (standard "error fades as you
  fix it" UX) — implemented as onChange handlers, not useEffect, to
  avoid React's cascading-render anti-pattern
- Add observability to the entire email flow (was previously silent end
  to end): form submit ENTRY, validator rejection, actor ENTRY, actor
  DONE — mirrors the logging style already used by guestLoginActor
- Add entry: assign({ errorMessage: null }) to loadingEmailLogin and
  loadingEmailRegister so stale errors clear on transition into the
  loading state (matches loadingGuestLogin)
This commit is contained in:
2026-06-16 17:29:58 +08:00
parent 2792d0c9c5
commit 200fb1642f
5 changed files with 138 additions and 32 deletions
+7 -5
View File
@@ -83,21 +83,21 @@ export const authMachine = setup({
},
// 启动 / 恢复时检查登录态 —— 由 <AuthStatusChecker /> 派发
AuthStatusCheckSubmitted: "checkingAuthStatus",
// 显式游客登录 —— 由 splash UI 派发(不自动)
AuthGuestLoginSubmitted: "loadingGuestLogin",
AuthEmailLoginSubmitted: "loadingEmailLogin",
AuthEmailRegisterSubmitted: "loadingEmailRegister",
AuthGoogleLoginSubmitted: "loadingOAuth",
AuthFacebookLoginSubmitted: "loadingOAuth",
// OAuth 回调后 sync 入口 —— 由 <OAuthSessionSync /> 派发
AuthGoogleSyncSubmitted: "syncingGoogleBackend",
AuthFacebookSyncSubmitted: "syncingFacebookBackend",
AuthAppleLoginSubmitted: {
actions: assign({
errorMessage: "Apple login not implemented",
}),
},
// OAuth 回调后 sync 入口 —— 由 <OAuthSessionSync /> 派发
AuthGoogleSyncSubmitted: "syncingGoogleBackend",
AuthFacebookSyncSubmitted: "syncingFacebookBackend",
// splash / auth screen 跳完 /chat 后清flag
AuthClearPendingRedirect: {
actions: assign({ pendingRedirect: false }),
@@ -151,6 +151,7 @@ export const authMachine = setup({
},
loadingEmailLogin: {
entry: assign({ errorMessage: null }),
invoke: {
src: "emailLogin",
input: ({ event }) => {
@@ -177,6 +178,7 @@ export const authMachine = setup({
},
loadingEmailRegister: {
entry: assign({ errorMessage: null }),
invoke: {
src: "emailRegisterThenLogin",
input: ({ event }) => {