diff --git a/build.sbt b/build.sbt index 1c0540db64..d6207b92ec 100644 --- a/build.sbt +++ b/build.sbt @@ -115,7 +115,7 @@ name := "Equella" (ThisBuild / equellaMajor) := 2021 (ThisBuild / equellaMinor) := 2 -(ThisBuild / equellaPatch) := 0 +(ThisBuild / equellaPatch) := 1 (ThisBuild / equellaStream) := "Stable" (ThisBuild / equellaBuild) := buildConfig.value.getString("build.buildname") (ThisBuild / buildTimestamp) := Instant.now().getEpochSecond diff --git a/react-front-end/tsrc/legacycontent/LegacyContent.tsx b/react-front-end/tsrc/legacycontent/LegacyContent.tsx index 54b5bf3a97..304ab63f66 100644 --- a/react-front-end/tsrc/legacycontent/LegacyContent.tsx +++ b/react-front-end/tsrc/legacycontent/LegacyContent.tsx @@ -17,6 +17,7 @@ */ import { CircularProgress, Grid } from "@material-ui/core"; import Axios from "axios"; +import { isEqual } from "lodash"; import * as React from "react"; import { useContext } from "react"; import { v4 } from "uuid"; @@ -104,6 +105,21 @@ export interface LegacyContentProps extends BaseOEQRouteComponentProps { children?: never; } +interface LegacyContentSubmission { + /** + * Indicate whether there is a request submitted to `LegacyContentApi` already but not completed yet. + */ + submitting: boolean; + /** + * Where to send the form data. + */ + action?: string; + /** + * Payload of the submission. + */ + payload?: StateData; +} + export type SubmitResponse = | ExternalRedirect | LegacyContentResponse @@ -146,6 +162,9 @@ export const LegacyContent = React.memo(function LegacyContent({ }: LegacyContentProps) { const [content, setContent] = React.useState(); const [updatingContent, setUpdatingContent] = React.useState(true); + const submittingForm = React.useRef({ + submitting: false, + }); const { appErrorHandler } = useContext(AppRenderErrorContext); const baseUrl = document.getElementsByTagName("base")[0].href; @@ -225,6 +244,24 @@ export const LegacyContent = React.memo(function LegacyContent({ submitValues: StateData, callback?: (response: SubmitResponse) => void ) { + if (formAction) { + const { submitting, action, payload } = submittingForm.current; + if ( + submitting && + formAction === action && + isEqual(submitValues, payload) + ) { + console.error(`ignore redundant submission to ${formAction}`); + return; + } + + submittingForm.current = { + submitting: true, + action: formAction, + payload: submitValues, + }; + } + submitRequest(toRelativeUrl(formAction || pathname), submitValues) .then((content) => { // Clear raw mode saved in local storage after a login request is resolved. @@ -251,6 +288,11 @@ export const LegacyContent = React.memo(function LegacyContent({ ? fromAxiosResponse(error.response) : generateFromError(error); handleError(fullScreen, errorResponse); + }) + .finally(() => { + if (formAction) { + submittingForm.current = { submitting: false }; + } }); }