do not render the frame before its session exists
Saving the connection showed an error that a refresh then cleared. The effect that re-mints the frame session re-ran, but `state` was left at 'ready' from the previous pass — so the iframe mounted immediately, before the cookie existed, and the first request inside it answered 401. By the time anyone refreshed, the mint had finished. A stale 'ready' is worse than a spinner: it renders something broken confidently. Also stays on the form after the URL-only step, where there is still a token to set. Bouncing to a frame that immediately sends you back is worse than not moving.
This commit is contained in:
@@ -81,10 +81,16 @@ export const GiteaConnection = ({ state, onSaved }: Props) => {
|
|||||||
setError(null);
|
setError(null);
|
||||||
try {
|
try {
|
||||||
await put('/gitea/_config', state.isOwner === false ? { token } : { url, token });
|
await put('/gitea/_config', state.isOwner === false ? { token } : { url, token });
|
||||||
|
// ORDER MATTERS. `onSaved` re-reads the connection and re-mints the frame session; navigating
|
||||||
|
// first would swap to the frame while that is still in flight, and the frame would load against a
|
||||||
|
// session that does not exist yet.
|
||||||
onSaved();
|
onSaved();
|
||||||
// Back to the frame. Saving from `/gitea/connection` otherwise leaves you looking at the form you
|
// Back to the frame. Saving from `/gitea/connection` otherwise leaves you looking at the form you
|
||||||
// just submitted, which reads as nothing having happened.
|
// just submitted, which reads as nothing having happened.
|
||||||
navigate('/gitea');
|
//
|
||||||
|
// Only when there is a frame to go back TO: after the URL-only step there is still a token to
|
||||||
|
// set, and bouncing to a frame that immediately sends you back is worse than staying put.
|
||||||
|
if (!needsUrl) navigate('/gitea');
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
setError(err instanceof Error ? err.message : 'could not save');
|
setError(err instanceof Error ? err.message : 'could not save');
|
||||||
} finally {
|
} finally {
|
||||||
|
|||||||
@@ -38,6 +38,15 @@ export const GiteaFrame = () => {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
let cancelled = false;
|
let cancelled = false;
|
||||||
|
// BACK TO 'opening' on every re-run, not just the first.
|
||||||
|
//
|
||||||
|
// Without this, saving the connection left `state` at 'ready' from the previous pass, so the iframe
|
||||||
|
// mounted immediately — before the session cookie for it had been minted. The first request inside
|
||||||
|
// the frame answered 401 and showed an error that a refresh then cleared, because by then the mint
|
||||||
|
// had finished.
|
||||||
|
//
|
||||||
|
// A stale 'ready' is worse than a spinner: it renders something broken confidently.
|
||||||
|
setState('opening');
|
||||||
// Not in a dependency array: `useClient()` returns a new object every render, so depending on `get`
|
// Not in a dependency array: `useClient()` returns a new object every render, so depending on `get`
|
||||||
// or `post` would re-run this on every keystroke elsewhere in the app.
|
// or `post` would re-run this on every keystroke elsewhere in the app.
|
||||||
void (async () => {
|
void (async () => {
|
||||||
|
|||||||
Reference in New Issue
Block a user