From c8134bf1a063d05866c2e8b57b8d97a9d5c997f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=B8vring?= Date: Fri, 27 Sep 2024 21:31:45 +0200 Subject: [PATCH 1/5] Delays error messages until data is refreshed --- .../(project-doc)/[...slug]/layout.tsx | 40 ++++++------------- .../(authed)/(project-doc)/[...slug]/page.tsx | 10 +++-- src/common/context/ProjectsContext.ts | 2 + .../projects/view/ProjectsContextProvider.tsx | 11 ++++- 4 files changed, 32 insertions(+), 31 deletions(-) diff --git a/src/app/(authed)/(project-doc)/[...slug]/layout.tsx b/src/app/(authed)/(project-doc)/[...slug]/layout.tsx index 62f1ab7e..3d0122bc 100644 --- a/src/app/(authed)/(project-doc)/[...slug]/layout.tsx +++ b/src/app/(authed)/(project-doc)/[...slug]/layout.tsx @@ -1,47 +1,33 @@ "use client" +import { useContext } from "react" import { Box, Stack } from "@mui/material" import { useTheme } from "@mui/material/styles" import TrailingToolbarItem from "@/features/projects/view/toolbar/TrailingToolbarItem" import MobileToolbar from "@/features/projects/view/toolbar/MobileToolbar" import { useProjectSelection } from "@/features/projects/data" -import NotFound from "@/features/projects/view/NotFound" -import dynamic from "next/dynamic" +import { ProjectsContext } from "@/common" import SecondaryHeaderPlaceholder from "@/features/sidebar/view/SecondarySplitHeaderPlaceholder" - -const SecondarySplitHeader = dynamic(() => import("@/features/sidebar/view/SecondarySplitHeader"), - { - loading: () => , - ssr: false, - } -) +import SecondarySplitHeader from "@/features/sidebar/view/SecondarySplitHeader" export default function Page({ children }: { children: React.ReactNode }) { + const { cached } = useContext(ProjectsContext) const { project } = useProjectSelection() const theme = useTheme() return ( - {!project && - <> - - - -
- -
- - } - {project && - <> + <> + {project && }> - -
- {children} -
- - } + } + {!project && cached && } + +
+ {children} +
+
) } \ No newline at end of file diff --git a/src/app/(authed)/(project-doc)/[...slug]/page.tsx b/src/app/(authed)/(project-doc)/[...slug]/page.tsx index 7e2635e4..bb50392c 100644 --- a/src/app/(authed)/(project-doc)/[...slug]/page.tsx +++ b/src/app/(authed)/(project-doc)/[...slug]/page.tsx @@ -1,12 +1,15 @@ "use client" -import { useEffect } from "react" +import { useEffect, useContext } from "react" import ErrorMessage from "@/common/ui/ErrorMessage" import { updateWindowTitle } from "@/features/projects/domain" import { useProjectSelection } from "@/features/projects/data" import Documentation from "@/features/projects/view/Documentation" +import NotFound from "@/features/projects/view/NotFound" +import { ProjectsContext } from "@/common" export default function Page() { + const { cached } = useContext(ProjectsContext) const { project, version, specification, navigateToSelectionIfNeeded } = useProjectSelection() // Ensure the URL reflects the current selection of project, version, and specification. useEffect(() => { @@ -28,12 +31,13 @@ export default function Page() { {project && version && specification && } - {project && !version && + {project && !version && !cached && } - {project && !specification && + {project && !specification && !cached && } + {!project && !cached && } ) } diff --git a/src/common/context/ProjectsContext.ts b/src/common/context/ProjectsContext.ts index c5484722..ccd71812 100644 --- a/src/common/context/ProjectsContext.ts +++ b/src/common/context/ProjectsContext.ts @@ -6,11 +6,13 @@ import { Project } from "@/features/projects/domain" export const SidebarTogglableContext = createContext(true) type ProjectsContextValue = { + cached: boolean, projects: Project[], setProjects: (projects: Project[]) => void } export const ProjectsContext = createContext({ + cached: true, projects: [], setProjects: () => {} }) diff --git a/src/features/projects/view/ProjectsContextProvider.tsx b/src/features/projects/view/ProjectsContextProvider.tsx index cd3725e9..7eab84ae 100644 --- a/src/features/projects/view/ProjectsContextProvider.tsx +++ b/src/features/projects/view/ProjectsContextProvider.tsx @@ -11,9 +11,18 @@ const ProjectsContextProvider = ({ initialProjects?: Project[], children?: React.ReactNode }) => { + const [cached, setCached] = useState(true) const [projects, setProjects] = useState(initialProjects || []) + const setProjectsAndCached = (projects: Project[]) => { + setProjects(projects) + setCached(false) + } return ( - + {children} ) From ed7fa81f94f5db6552f7b4c4dd89877eaf2f6521 Mon Sep 17 00:00:00 2001 From: mpabarca Date: Fri, 4 Oct 2024 16:29:07 +0200 Subject: [PATCH 2/5] Add refreshed state to ProjectsContext to show updated data of projects and branches --- .../(project-doc)/[...slug]/layout.tsx | 36 +++++++++++-------- .../(authed)/(project-doc)/[...slug]/page.tsx | 11 +++--- src/common/context/ProjectsContext.ts | 4 +-- .../projects/view/ProjectsContextProvider.tsx | 14 ++++---- 4 files changed, 36 insertions(+), 29 deletions(-) diff --git a/src/app/(authed)/(project-doc)/[...slug]/layout.tsx b/src/app/(authed)/(project-doc)/[...slug]/layout.tsx index 3d0122bc..34796836 100644 --- a/src/app/(authed)/(project-doc)/[...slug]/layout.tsx +++ b/src/app/(authed)/(project-doc)/[...slug]/layout.tsx @@ -1,33 +1,39 @@ "use client" -import { useContext } from "react" import { Box, Stack } from "@mui/material" import { useTheme } from "@mui/material/styles" import TrailingToolbarItem from "@/features/projects/view/toolbar/TrailingToolbarItem" import MobileToolbar from "@/features/projects/view/toolbar/MobileToolbar" import { useProjectSelection } from "@/features/projects/data" -import { ProjectsContext } from "@/common" import SecondaryHeaderPlaceholder from "@/features/sidebar/view/SecondarySplitHeaderPlaceholder" import SecondarySplitHeader from "@/features/sidebar/view/SecondarySplitHeader" +import { useContext } from "react" +import { ProjectsContext } from "@/common" +import LoadingIndicator from "@/common/ui/LoadingIndicator" export default function Page({ children }: { children: React.ReactNode }) { - const { cached } = useContext(ProjectsContext) const { project } = useProjectSelection() + const { refreshed } = useContext(ProjectsContext) + const theme = useTheme() + return ( - <> - {project && - }> - - - } - {!project && cached && } - -
- {children} -
- + {refreshed ? + <> + {project && + }> + + + } + {!project && } + +
+ {children} +
+ + : + }
) } \ No newline at end of file diff --git a/src/app/(authed)/(project-doc)/[...slug]/page.tsx b/src/app/(authed)/(project-doc)/[...slug]/page.tsx index bb50392c..c9d0666b 100644 --- a/src/app/(authed)/(project-doc)/[...slug]/page.tsx +++ b/src/app/(authed)/(project-doc)/[...slug]/page.tsx @@ -1,15 +1,13 @@ "use client" -import { useEffect, useContext } from "react" +import { useEffect } from "react" import ErrorMessage from "@/common/ui/ErrorMessage" import { updateWindowTitle } from "@/features/projects/domain" import { useProjectSelection } from "@/features/projects/data" import Documentation from "@/features/projects/view/Documentation" import NotFound from "@/features/projects/view/NotFound" -import { ProjectsContext } from "@/common" export default function Page() { - const { cached } = useContext(ProjectsContext) const { project, version, specification, navigateToSelectionIfNeeded } = useProjectSelection() // Ensure the URL reflects the current selection of project, version, and specification. useEffect(() => { @@ -26,18 +24,19 @@ export default function Page() { specification }) }, [project, version, specification]) + return ( <> {project && version && specification && } - {project && !version && !cached && + {project && !version && } - {project && !specification && !cached && + {project && !specification && } - {!project && !cached && } + {!project && } ) } diff --git a/src/common/context/ProjectsContext.ts b/src/common/context/ProjectsContext.ts index ccd71812..cc0fe966 100644 --- a/src/common/context/ProjectsContext.ts +++ b/src/common/context/ProjectsContext.ts @@ -6,13 +6,13 @@ import { Project } from "@/features/projects/domain" export const SidebarTogglableContext = createContext(true) type ProjectsContextValue = { - cached: boolean, + refreshed: boolean, projects: Project[], setProjects: (projects: Project[]) => void } export const ProjectsContext = createContext({ - cached: true, + refreshed: false, projects: [], setProjects: () => {} }) diff --git a/src/features/projects/view/ProjectsContextProvider.tsx b/src/features/projects/view/ProjectsContextProvider.tsx index 7eab84ae..baaae4d8 100644 --- a/src/features/projects/view/ProjectsContextProvider.tsx +++ b/src/features/projects/view/ProjectsContextProvider.tsx @@ -11,17 +11,19 @@ const ProjectsContextProvider = ({ initialProjects?: Project[], children?: React.ReactNode }) => { - const [cached, setCached] = useState(true) + const [refreshed, setRefreshed] = useState(false) const [projects, setProjects] = useState(initialProjects || []) - const setProjectsAndCached = (projects: Project[]) => { - setProjects(projects) - setCached(false) + + const setProjectsAndRefreshed = (value: Project[]) => { + setProjects(value) + // Only set refreshed to true after projects are updated + if (JSON.stringify(projects) !== JSON.stringify(value)) setRefreshed(true) } return ( {children} From 110da0309c61f06d86f711e24f4aac09daf14b5f Mon Sep 17 00:00:00 2001 From: mpabarca Date: Mon, 7 Oct 2024 11:04:01 +0200 Subject: [PATCH 3/5] Fix duplication Not Found Page for wrong version or specification --- src/app/(authed)/(project-doc)/[...slug]/page.tsx | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/app/(authed)/(project-doc)/[...slug]/page.tsx b/src/app/(authed)/(project-doc)/[...slug]/page.tsx index c9d0666b..9cb06eb9 100644 --- a/src/app/(authed)/(project-doc)/[...slug]/page.tsx +++ b/src/app/(authed)/(project-doc)/[...slug]/page.tsx @@ -30,11 +30,8 @@ export default function Page() { {project && version && specification && } - {project && !version && - - } - {project && !specification && - + {project && (!version || !specification) && + } {!project && } From 686a1abba830df8b6c84383bb35f637aa33a013e Mon Sep 17 00:00:00 2001 From: mpabarca Date: Mon, 7 Oct 2024 15:16:34 +0200 Subject: [PATCH 4/5] Fix validation of update only new projects in Context using deep equality --- src/features/projects/view/ProjectsContextProvider.tsx | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/features/projects/view/ProjectsContextProvider.tsx b/src/features/projects/view/ProjectsContextProvider.tsx index baaae4d8..93213d75 100644 --- a/src/features/projects/view/ProjectsContextProvider.tsx +++ b/src/features/projects/view/ProjectsContextProvider.tsx @@ -14,10 +14,16 @@ const ProjectsContextProvider = ({ const [refreshed, setRefreshed] = useState(false) const [projects, setProjects] = useState(initialProjects || []) + const hasProjectChanged = (value: Project[]) => value.some((project, index) => { + // Compare by project id and version (or any other key fields) + return project.id !== projects[index]?.id || project.versions !== projects[index]?.versions + }) + const setProjectsAndRefreshed = (value: Project[]) => { setProjects(value) - // Only set refreshed to true after projects are updated - if (JSON.stringify(projects) !== JSON.stringify(value)) setRefreshed(true) + // If any project has changed, update the state and mark as refreshed + if (hasProjectChanged(value)) setRefreshed(true) + } return ( Date: Mon, 7 Oct 2024 15:27:12 +0200 Subject: [PATCH 5/5] Fix show SecondaryHeaderPlaceholder in Not Found page --- .../(project-doc)/[...slug]/layout.tsx | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/src/app/(authed)/(project-doc)/[...slug]/layout.tsx b/src/app/(authed)/(project-doc)/[...slug]/layout.tsx index 34796836..98306516 100644 --- a/src/app/(authed)/(project-doc)/[...slug]/layout.tsx +++ b/src/app/(authed)/(project-doc)/[...slug]/layout.tsx @@ -4,36 +4,33 @@ import { Box, Stack } from "@mui/material" import { useTheme } from "@mui/material/styles" import TrailingToolbarItem from "@/features/projects/view/toolbar/TrailingToolbarItem" import MobileToolbar from "@/features/projects/view/toolbar/MobileToolbar" -import { useProjectSelection } from "@/features/projects/data" import SecondaryHeaderPlaceholder from "@/features/sidebar/view/SecondarySplitHeaderPlaceholder" -import SecondarySplitHeader from "@/features/sidebar/view/SecondarySplitHeader" import { useContext } from "react" import { ProjectsContext } from "@/common" import LoadingIndicator from "@/common/ui/LoadingIndicator" +import SecondarySplitHeader from "@/features/sidebar/view/SecondarySplitHeader" export default function Page({ children }: { children: React.ReactNode }) { - const { project } = useProjectSelection() const { refreshed } = useContext(ProjectsContext) const theme = useTheme() return ( - {refreshed ? <> - {project && + {!refreshed ? : }> } - {!project && } -
- {children} -
+ {refreshed ? +
+ {children} +
: + + } - : - }
) } \ No newline at end of file