Skip to content

feat(site): warn user if they leave the editor without publishing #12406

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Mar 5, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Make it work for react router dom navigation
  • Loading branch information
BrunoQuaresma committed Mar 5, 2024
commit 44dba18c51a326d6f6e47ee03befbe38ec1d957a
5 changes: 3 additions & 2 deletions site/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import { QueryClient, QueryClientProvider } from "react-query";
import { type FC, type ReactNode, useEffect, useState } from "react";
import { HelmetProvider } from "react-helmet-async";
import { AppRouter } from "./AppRouter";
import { router } from "./router";
import { ThemeProvider } from "./contexts/ThemeProvider";
import { AuthProvider } from "./contexts/auth/AuthProvider";
import { ErrorBoundary } from "./components/ErrorBoundary/ErrorBoundary";
import { GlobalSnackbar } from "./components/GlobalSnackbar/GlobalSnackbar";
import "./theme/globalFonts";
import { ReactQueryDevtools } from "@tanstack/react-query-devtools";
import { RouterProvider } from "react-router-dom";

const defaultQueryClient = new QueryClient({
defaultOptions: {
Expand Down Expand Up @@ -61,7 +62,7 @@ export const App: FC = () => {
return (
<AppProviders>
<ErrorBoundary>
<AppRouter />
<RouterProvider router={router} />
</ErrorBoundary>
</AppProviders>
);
Expand Down
56 changes: 39 additions & 17 deletions site/src/pages/TemplateVersionEditorPage/TemplateVersionEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@ import Button from "@mui/material/Button";
import IconButton from "@mui/material/IconButton";
import Tooltip from "@mui/material/Tooltip";
import CreateIcon from "@mui/icons-material/AddOutlined";
import { Link as RouterLink } from "react-router-dom";
import {
Link as RouterLink,
unstable_usePrompt as usePrompt,
} from "react-router-dom";
import { type Interpolation, type Theme, useTheme } from "@emotion/react";
import { type FC, useCallback, useEffect, useRef, useState } from "react";
import AlertTitle from "@mui/material/AlertTitle";
Expand Down Expand Up @@ -65,8 +68,8 @@ export interface TemplateVersionEditorProps {
defaultFileTree: FileTree;
buildLogs?: ProvisionerJobLog[];
resources?: WorkspaceResource[];
isBuilding?: boolean;
canPublish?: boolean;
isBuilding: boolean;
canPublish: boolean;
onPreview: (files: FileTree) => Promise<void>;
onPublish: () => void;
onConfirmPublish: (data: PublishVersionData) => void;
Expand Down Expand Up @@ -179,20 +182,7 @@ export const TemplateVersionEditor: FC<TemplateVersionEditorProps> = ({
}
}, [buildLogs]);

useEffect(() => {
const onBeforeUnload = (e: BeforeUnloadEvent) => {
if (canPublish) {
e.preventDefault();
return "You have unpublished changes. Are you sure you want to leave?";
}
};

window.addEventListener("beforeunload", onBeforeUnload);

return () => {
window.removeEventListener("beforeunload", onBeforeUnload);
};
}, [canPublish]);
useLeaveSiteWarning(canPublish);

const canBuild = !isBuilding && dirty;

Expand Down Expand Up @@ -666,6 +656,38 @@ export const TemplateVersionEditor: FC<TemplateVersionEditorProps> = ({
);
};

const useLeaveSiteWarning = (enabled: boolean) => {
const MESSAGE =
"You have unpublished changes. Are you sure you want to leave?";

// This works for regular browser actions like close tab and back button
useEffect(() => {
const onBeforeUnload = (e: BeforeUnloadEvent) => {
if (enabled) {
e.preventDefault();
return MESSAGE;
}
};

window.addEventListener("beforeunload", onBeforeUnload);

return () => {
window.removeEventListener("beforeunload", onBeforeUnload);
};
}, [enabled]);

// This is used for react router navigation that is not triggered by the
// browser
usePrompt({
message: MESSAGE,
when: ({ nextLocation }) => {
// We need to check the path because we change the URL when new template
// version is created during builds
return enabled && !nextLocation.pathname.endsWith("/edit");
},
});
};

const styles = {
tab: (theme) => ({
"&:not(:disabled)": {
Expand Down
Loading