1- import React , { useCallback , useContext , useState } from 'react' ;
1+ import React , {
2+ startTransition ,
3+ useCallback ,
4+ useContext ,
5+ useActionState ,
6+ } from 'react' ;
27import PropTypes from 'prop-types' ;
38import classNames from 'classnames' ;
49import { SpinnerBig } from './Spinner' ;
@@ -7,95 +12,73 @@ import { HttpError, LoginError } from '../errors';
712import { ConfigurationContext } from '../helpers/configuration' ;
813import { LocalizationContext } from '../helpers/i18n' ;
914
10- function handleLogIn ( {
11- event,
15+ async function handleLogIn ( {
1216 configuration,
1317 navigate,
14- setLoading,
1518 username,
1619 password,
1720 enableOffline,
1821 returnLocation,
1922} ) {
20- event . preventDefault ( ) ;
21-
22- setLoading ( true ) ;
23-
24- selfoss
25- . login ( { configuration, username, password, enableOffline } )
26- . then ( ( ) => {
27- navigate ( returnLocation ) ;
28- } )
29- . catch ( ( err ) => {
30- const message =
31- err instanceof LoginError
32- ? selfoss . app . _ ( 'login_invalid_credentials' )
33- : selfoss . app . _ ( 'login_error_generic' , {
34- errorMessage :
35- err instanceof HttpError
36- ? `HTTP ${ err . response . status } ${ err . message } `
37- : err . message ,
38- } ) ;
39- navigate ( '/sign/in' , {
40- replace : true ,
41- state : {
42- error : message ,
43- } ,
44- } ) ;
45- } )
46- . finally ( ( ) => {
47- setLoading ( false ) ;
23+ try {
24+ await selfoss . login ( {
25+ configuration,
26+ username,
27+ password,
28+ enableOffline,
29+ } ) ;
30+ navigate ( returnLocation ) ;
31+ } catch ( err ) {
32+ const message =
33+ err instanceof LoginError
34+ ? selfoss . app . _ ( 'login_invalid_credentials' )
35+ : selfoss . app . _ ( 'login_error_generic' , {
36+ errorMessage :
37+ err instanceof HttpError
38+ ? `HTTP ${ err . response . status } ${ err . message } `
39+ : err . message ,
40+ } ) ;
41+ navigate ( '/sign/in' , {
42+ replace : true ,
43+ state : {
44+ error : message ,
45+ } ,
4846 } ) ;
47+ }
4948}
5049
5150export default function LoginForm ( { offlineEnabled } ) {
52- const [ username , setUsername ] = useState ( '' ) ;
53- const [ password , setPassword ] = useState ( '' ) ;
54- const [ loading , setLoading ] = useState ( false ) ;
55- const [ enableOffline , setEnableOffline ] = useState ( offlineEnabled ) ;
56-
5751 const configuration = useContext ( ConfigurationContext ) ;
5852 const navigate = useNavigate ( ) ;
5953 const location = useLocation ( ) ;
6054 const error = location ?. state ?. error ;
6155 const returnLocation = location ?. state ?. returnLocation ?? '/' ;
6256
63- const formOnSubmit = useCallback (
64- ( event ) =>
65- handleLogIn ( {
66- event,
57+ const [ , submitAction , loading ] = useActionState (
58+ async ( _previousState , formData ) => {
59+ const username = formData . get ( 'username' ) ;
60+ const password = formData . get ( 'password' ) ;
61+ const enableOffline = formData . get ( 'enableoffline' ) ;
62+ await handleLogIn ( {
6763 configuration,
6864 navigate,
69- setLoading,
7065 username,
7166 password,
7267 enableOffline,
7368 returnLocation,
74- } ) ,
75- [
76- configuration ,
77- navigate ,
78- username ,
79- password ,
80- enableOffline ,
81- returnLocation ,
82- ] ,
83- ) ;
84-
85- const usernameOnChange = useCallback (
86- ( event ) => setUsername ( event . target . value ) ,
87- [ ] ,
88- ) ;
89-
90- const passwordOnChange = useCallback (
91- ( event ) => setPassword ( event . target . value ) ,
92- [ ] ,
69+ } ) ;
70+ return null ;
71+ } ,
72+ null ,
9373 ) ;
9474
95- const offlineOnChange = useCallback (
96- ( event ) => setEnableOffline ( event . target . checked ) ,
97- [ setEnableOffline ] ,
98- ) ;
75+ const formOnSubmit = useCallback ( ( event ) => {
76+ // Unlike `action` prop, `onSubmit` avoids clearing the form on submit.
77+ // https://github.com/facebook/react/issues/29034#issuecomment-2143595195
78+ event . preventDefault ( ) ;
79+ const formData = new FormData ( event . target ) ;
80+ startTransition ( ( ) => submitAction ( formData ) ) ;
81+ } , [ ] ) ;
9982
10083 const _ = useContext ( LocalizationContext ) ;
10184
@@ -120,8 +103,6 @@ export default function LoginForm({ offlineEnabled }) {
120103 id = "username"
121104 accessKey = "u"
122105 autoComplete = "username"
123- onChange = { usernameOnChange }
124- value = { username }
125106 autoFocus
126107 required
127108 />
@@ -134,8 +115,6 @@ export default function LoginForm({ offlineEnabled }) {
134115 id = "password"
135116 accessKey = "p"
136117 autoComplete = "current-password"
137- onChange = { passwordOnChange }
138- value = { password }
139118 />
140119 </ li >
141120 < li >
@@ -148,8 +127,7 @@ export default function LoginForm({ offlineEnabled }) {
148127 name = "enableoffline"
149128 id = "enableoffline"
150129 accessKey = "o"
151- onChange = { offlineOnChange }
152- checked = { enableOffline }
130+ defaultChecked = { offlineEnabled }
153131 /> { ' ' }
154132 < span className = "badge-experimental" >
155133 { _ ( 'experimental' ) }
0 commit comments