Skip to content
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

feat: Pesist FlowEditor state on route changes #3671

Merged
merged 9 commits into from
Sep 17, 2024
11 changes: 2 additions & 9 deletions editor.planx.uk/src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,12 @@ import ErrorPage from "pages/ErrorPage";
import { AnalyticsProvider } from "pages/FlowEditor/lib/analytics/provider";
import React, { Suspense, useEffect } from "react";
import { createRoot } from "react-dom/client";
import { NotFoundBoundary, Router, useLoadingRoute, View } from "react-navi";
import { NotFoundBoundary, Router, View } from "react-navi";
import HelmetProvider from "react-navi-helmet-async";
import { ToastContainer } from "react-toastify";

// init airbrake before everything else
import * as airbrake from "./airbrake";
import DelayedLoadingIndicator from "./components/DelayedLoadingIndicator";
import { client } from "./lib/graphql";
import navigation from "./lib/navigation";
import { defaultTheme } from "./theme";
Expand Down Expand Up @@ -55,8 +54,6 @@ const hasJWT = (): boolean | void => {
const Layout: React.FC<{
children: React.ReactNode;
}> = ({ children }) => {
const isLoading = useLoadingRoute();

useEffect(() => {
const observer = new MutationObserver(() => {
// set the page title based on whatever heading is currently shown
Expand All @@ -82,11 +79,7 @@ const Layout: React.FC<{
<StyledEngineProvider injectFirst>
<ThemeProvider theme={defaultTheme}>
<NotFoundBoundary render={() => <ErrorPage title="Not found" />}>
{isLoading ? (
<DelayedLoadingIndicator msDelayBeforeVisible={500} />
) : (
children
)}
{children}
</NotFoundBoundary>
</ThemeProvider>
</StyledEngineProvider>
Expand Down

This file was deleted.

This file was deleted.

5 changes: 5 additions & 0 deletions editor.planx.uk/src/pages/Login.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ import Button from "@mui/material/Button";
import Container from "@mui/material/Container";
import { styled } from "@mui/material/styles";
import Typography from "@mui/material/Typography";
import DelayedLoadingIndicator from "components/DelayedLoadingIndicator";
import React from "react";
import { useLoadingRoute } from "react-navi";

const Wrapper = styled(Box)(({ theme }) => ({
width: "100vw",
Expand Down Expand Up @@ -46,6 +48,9 @@ const LoginButton = styled(Button)(({ theme }) => ({
}));

const Login: React.FC = () => {
const isLoading = useLoadingRoute();
if (isLoading) return <DelayedLoadingIndicator />;
Comment on lines +51 to +52
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Without this, the login page flashed on initial load of the application.


return (
<Wrapper>
<LoginContainer>
Expand Down
6 changes: 5 additions & 1 deletion editor.planx.uk/src/pages/layout/FlowEditorLayout.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
import ErrorFallback from "components/ErrorFallback";
import FlowEditor from "pages/FlowEditor";
import React, { PropsWithChildren } from "react";
import { ErrorBoundary } from "react-error-boundary";

const FlowEditorLayout: React.FC<PropsWithChildren> = ({ children }) => (
<ErrorBoundary FallbackComponent={ErrorFallback}>{children}</ErrorBoundary>
<ErrorBoundary FallbackComponent={ErrorFallback}>
<FlowEditor />
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the single FlowEditor instance, attached to the layout, which persists regardless of route.

{children}
</ErrorBoundary>
);

export default FlowEditorLayout;
120 changes: 5 additions & 115 deletions editor.planx.uk/src/routes/flow.tsx
Original file line number Diff line number Diff line change
@@ -1,36 +1,25 @@
import { gql } from "@apollo/client";
import {
ComponentType as TYPES,
FlowStatus,
} from "@opensystemslab/planx-core/types";
import { ComponentType as TYPES } from "@opensystemslab/planx-core/types";
import natsort from "natsort";
import {
compose,
lazy,
map,
Matcher,
mount,
NaviRequest,
redirect,
route,
withData,
withView,
} from "navi";
import DataManagerSettings from "pages/FlowEditor/components/Settings/DataManagerSettings";
import ServiceFlags from "pages/FlowEditor/components/Settings/ServiceFlags";
import ServiceSettings from "pages/FlowEditor/components/Settings/ServiceSettings";
import Submissions from "pages/FlowEditor/components/Settings/Submissions";
import mapAccum from "ramda/src/mapAccum";
import React from "react";
import { View } from "react-navi";

import { client } from "../lib/graphql";
import FlowEditor from "../pages/FlowEditor";
import components from "../pages/FlowEditor/components/forms";
import FormModal from "../pages/FlowEditor/components/forms/FormModal";
import { SLUGS } from "../pages/FlowEditor/data/types";
import { useStore } from "../pages/FlowEditor/lib/store";
import type { Flow, FlowSettings } from "../types";
import type { Flow } from "../types";
import { makeTitle } from "./utils";
import { flowEditorView } from "./views/flowEditor";

Expand Down Expand Up @@ -179,44 +168,6 @@ const nodeRoutes = mount({
"/:parent/nodes/:id/edit": editNode,
});

const SettingsContainer = () => <View />;

interface GetFlowSettings {
flows: {
id: string;
settings: FlowSettings;
status: FlowStatus;
}[];
}

export const getFlowSettings = async (req: NaviRequest) => {
const {
data: {
flows: [{ settings, status }],
},
} = await client.query<GetFlowSettings>({
query: gql`
query GetFlow($slug: String!, $team_slug: String!) {
flows(
limit: 1
where: { slug: { _eq: $slug }, team: { slug: { _eq: $team_slug } } }
) {
id
settings
status
}
}
`,
variables: {
slug: req.params.flow,
team_slug: req.params.team,
},
});

useStore.getState().setFlowSettings(settings);
useStore.getState().setFlowStatus(status);
};

const routes = compose(
withData((req) => ({
flow: req.params.flow.split(",")[0],
Expand All @@ -228,73 +179,12 @@ const routes = compose(
"/": route(async (req) => {
return {
title: makeTitle([req.params.team, req.params.flow].join("/")),
view: () => {
const [flow, ...breadcrumbs] = req.params.flow.split(",");
return (
<FlowEditor key={flow} flow={flow} breadcrumbs={breadcrumbs} />
);
},
// Default view of FlowEditor (single instance held in layout)
view: () => null,
};
}),

"/feedback": lazy(() => import("./feedback")),

"/nodes": compose(
withView((req) => {
const [flow, ...breadcrumbs] = req.params.flow.split(",");
return (
<>
<FlowEditor key={flow} flow={flow} breadcrumbs={breadcrumbs} />
<View />
</>
);
}),
nodeRoutes,
),

"/service": compose(
withView(SettingsContainer),

route(async (req) => ({
getData: await getFlowSettings(req),
title: makeTitle(
[req.params.team, req.params.flow, "service"].join("/"),
),
view: ServiceSettings,
})),
),

"/service-flags": compose(
withView(SettingsContainer),

route(async (req) => ({
getData: await getFlowSettings(req),
title: makeTitle(
[req.params.team, req.params.flow, "service-flags"].join("/"),
),
view: ServiceFlags,
})),
),

"/data": compose(
withView(SettingsContainer),

route(async (req) => ({
title: makeTitle([req.params.team, req.params.flow, "data"].join("/")),
view: DataManagerSettings,
})),
),

"/submissions-log": compose(
withView(SettingsContainer),

route(async (req) => ({
title: makeTitle(
[req.params.team, req.params.flow, "submissions-log"].join("/"),
),
view: Submissions,
})),
),
"/nodes": nodeRoutes,
}),
);

Expand Down
65 changes: 65 additions & 0 deletions editor.planx.uk/src/routes/serviceSettings.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import { gql } from "@apollo/client";
import { FlowStatus } from "@opensystemslab/planx-core/types";
import { compose, mount, NaviRequest, route, withData } from "navi";
import ServiceSettings from "pages/FlowEditor/components/Settings/ServiceSettings";

import { client } from "../lib/graphql";
import { useStore } from "../pages/FlowEditor/lib/store";
import type { FlowSettings } from "../types";
import { makeTitle } from "./utils";

interface GetFlowSettings {
flows: {
id: string;
settings: FlowSettings;
status: FlowStatus;
}[];
}

export const getFlowSettings = async (req: NaviRequest) => {
const {
data: {
flows: [{ settings, status }],
},
} = await client.query<GetFlowSettings>({
query: gql`
query GetFlow($slug: String!, $team_slug: String!) {
flows(
limit: 1
where: { slug: { _eq: $slug }, team: { slug: { _eq: $team_slug } } }
) {
id
settings
status
}
}
`,
variables: {
slug: req.params.flow,
team_slug: req.params.team,
},
});

useStore.getState().setFlowSettings(settings);
useStore.getState().setFlowStatus(status);
};

const serviceSettingsRoutes = compose(
withData((req) => ({
mountpath: req.mountpath,
})),

mount({
"/": compose(
route(async (req) => ({
getData: await getFlowSettings(req),
title: makeTitle(
[req.params.team, req.params.flow, "service"].join("/"),
),
view: ServiceSettings,
})),
),
}),
);

export default serviceSettingsRoutes;
23 changes: 23 additions & 0 deletions editor.planx.uk/src/routes/submissionsLog.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { compose, mount, route, withData } from "navi";
import Submissions from "pages/FlowEditor/components/Settings/Submissions";

import { makeTitle } from "./utils";

const submissionsLogRoutes = compose(
withData((req) => ({
mountpath: req.mountpath,
})),

mount({
"/": compose(
route(async (req) => ({
title: makeTitle(
[req.params.team, req.params.flow, "submissions-log"].join("/"),
),
view: Submissions,
})),
),
}),
);

export default submissionsLogRoutes;
Loading
Loading