Add loader on OAuth2 consent page submit
Track a pending allow/deny action with Button loading spinners so users cannot double-submit while the approveConsent mutation runs or while the browser waits on the OAuth callback redirect. Signed-off-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: Bryan FRIMIN <bryan@frimin.fr>
This commit is contained in:
@@ -159,14 +159,14 @@ export default function ConsentPage(props: {
|
|||||||
const { __ } = useTranslate();
|
const { __ } = useTranslate();
|
||||||
const { toast } = useToast();
|
const { toast } = useToast();
|
||||||
const [deviceResult, setDeviceResult] = useState<"authorized" | "denied" | null>(null);
|
const [deviceResult, setDeviceResult] = useState<"authorized" | "denied" | null>(null);
|
||||||
|
const [pendingAction, setPendingAction] = useState<"allow" | "deny" | null>(null);
|
||||||
|
|
||||||
const data = usePreloadedQuery<ConsentPageQuery>(consentPageQuery, props.queryRef);
|
const data = usePreloadedQuery<ConsentPageQuery>(consentPageQuery, props.queryRef);
|
||||||
usePageTitle(__("Authorize Application"));
|
usePageTitle(__("Authorize Application"));
|
||||||
|
|
||||||
const { node: consent } = data;
|
const { node: consent } = data;
|
||||||
|
|
||||||
const [approveConsent, isInFlight]
|
const [approveConsent] = useMutation<ConsentPageMutation>(approveConsentMutation);
|
||||||
= useMutation<ConsentPageMutation>(approveConsentMutation);
|
|
||||||
|
|
||||||
const { oidcScopes, apiScopes } = useMemo(
|
const { oidcScopes, apiScopes } = useMemo(
|
||||||
() => partitionScopes(consent.scopes ?? []),
|
() => partitionScopes(consent.scopes ?? []),
|
||||||
@@ -180,7 +180,9 @@ export default function ConsentPage(props: {
|
|||||||
|
|
||||||
const handleAction = useCallback(
|
const handleAction = useCallback(
|
||||||
(approved: boolean) => {
|
(approved: boolean) => {
|
||||||
if (!consent.id) return;
|
if (!consent.id || pendingAction !== null) return;
|
||||||
|
|
||||||
|
setPendingAction(approved ? "allow" : "deny");
|
||||||
|
|
||||||
approveConsent({
|
approveConsent({
|
||||||
variables: {
|
variables: {
|
||||||
@@ -191,6 +193,7 @@ export default function ConsentPage(props: {
|
|||||||
},
|
},
|
||||||
onCompleted: (response, errors) => {
|
onCompleted: (response, errors) => {
|
||||||
if (errors) {
|
if (errors) {
|
||||||
|
setPendingAction(null);
|
||||||
toast({
|
toast({
|
||||||
title: __("Authorization failed"),
|
title: __("Authorization failed"),
|
||||||
description: formatError(
|
description: formatError(
|
||||||
@@ -203,6 +206,7 @@ export default function ConsentPage(props: {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!response.approveConsent) {
|
if (!response.approveConsent) {
|
||||||
|
setPendingAction(null);
|
||||||
toast({
|
toast({
|
||||||
title: __("Authorization failed"),
|
title: __("Authorization failed"),
|
||||||
description: __("Something went wrong. Please try again."),
|
description: __("Something went wrong. Please try again."),
|
||||||
@@ -221,6 +225,7 @@ export default function ConsentPage(props: {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
onError: (err) => {
|
onError: (err) => {
|
||||||
|
setPendingAction(null);
|
||||||
toast({
|
toast({
|
||||||
title: __("Error"),
|
title: __("Error"),
|
||||||
description:
|
description:
|
||||||
@@ -230,7 +235,7 @@ export default function ConsentPage(props: {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
[consent, approveConsent, __, toast],
|
[consent, approveConsent, __, toast, pendingAction],
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!consent.application || !consent.scopes) {
|
if (!consent.application || !consent.scopes) {
|
||||||
@@ -310,17 +315,19 @@ export default function ConsentPage(props: {
|
|||||||
<Button
|
<Button
|
||||||
variant="secondary"
|
variant="secondary"
|
||||||
className="flex-1 h-10"
|
className="flex-1 h-10"
|
||||||
disabled={isInFlight}
|
disabled={pendingAction !== null}
|
||||||
|
loading={pendingAction === "deny"}
|
||||||
onClick={() => handleAction(false)}
|
onClick={() => handleAction(false)}
|
||||||
>
|
>
|
||||||
{__("Deny")}
|
{__("Deny")}
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
className="flex-1 h-10"
|
className="flex-1 h-10"
|
||||||
disabled={isInFlight}
|
disabled={pendingAction !== null}
|
||||||
|
loading={pendingAction === "allow"}
|
||||||
onClick={() => handleAction(true)}
|
onClick={() => handleAction(true)}
|
||||||
>
|
>
|
||||||
{isInFlight ? __("Authorizing...") : __("Allow")}
|
{__("Allow")}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user