diff --git a/client/app/pages/queries/QuerySource.jsx b/client/app/pages/queries/QuerySource.jsx index 35630d910c..51ea92d072 100644 --- a/client/app/pages/queries/QuerySource.jsx +++ b/client/app/pages/queries/QuerySource.jsx @@ -39,6 +39,7 @@ import useDeleteVisualization from "./hooks/useDeleteVisualization"; import useFormatQuery from "./hooks/useFormatQuery"; import useUpdateQuery from "./hooks/useUpdateQuery"; import useUpdateQueryDescription from "./hooks/useUpdateQueryDescription"; +import useUnsavedChangesAlert from "./hooks/useUnsavedChangesAlert"; import "./QuerySource.less"; @@ -56,6 +57,8 @@ function QuerySource(props) { const [parameters, areParametersDirty, updateParametersDirtyFlag] = useQueryParameters(query); const [selectedVisualization, setSelectedVisualization] = useVisualizationTabHandler(query.visualizations); + useUnsavedChangesAlert(isDirty); + const { queryResult, queryResultData, diff --git a/client/app/pages/queries/hooks/useUnsavedChangesAlert.js b/client/app/pages/queries/hooks/useUnsavedChangesAlert.js new file mode 100644 index 0000000000..2d000e3961 --- /dev/null +++ b/client/app/pages/queries/hooks/useUnsavedChangesAlert.js @@ -0,0 +1,34 @@ +import { useRef, useEffect } from "react"; +import { $rootScope } from "@/services/ng"; + +// TODO: This should be revisited and probably re-implemented when replacing Angular router with sth else +export default function useUnsavedChangesAlert(shouldShowAlert = false) { + const shouldShowAlertRef = useRef(); + shouldShowAlertRef.current = shouldShowAlert; + + useEffect(() => { + const unloadMessage = "You will lose your changes if you leave"; + const confirmMessage = `${unloadMessage}\n\nAre you sure you want to leave this page?`; + // store original handler (if any) + const savedOnBeforeUnload = window.onbeforeunload; + + window.onbeforeunload = function onbeforeunload() { + return shouldShowAlertRef.current ? unloadMessage : undefined; + }; + + const unsubscribe = $rootScope.$on("$locationChangeStart", (event, next, current) => { + if (next.split("?")[0] === current.split("?")[0] || next.split("#")[0] === current.split("#")[0]) { + return; + } + + if (shouldShowAlertRef.current && !window.confirm(confirmMessage)) { + event.preventDefault(); + } + }); + + return () => { + window.onbeforeunload = savedOnBeforeUnload; + unsubscribe(); + }; + }, []); +}