Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
25 changes: 7 additions & 18 deletions src/app/(authed)/(project-doc)/[...slug]/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,32 +4,21 @@ 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 SecondaryHeaderPlaceholder from "@/features/sidebar/view/SecondarySplitHeaderPlaceholder"
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 { refreshed } = useContext(ProjectsContext)

const theme = useTheme()

return (
<Stack sx={{ height: "100%" }}>
<>
{!refreshed ? <SecondaryHeaderPlaceholder/> :
<SecondarySplitHeader mobileToolbar={<MobileToolbar/>}>
<TrailingToolbarItem/>
</SecondarySplitHeader>
}
<Box sx={{ height: "0.5px", background: theme.palette.divider }} />
{refreshed ?
<main style={{ flexGrow: "1", overflowY: "auto" }}>
{children}
</main> :
<LoadingIndicator />
}
<SecondarySplitHeader mobileToolbar={<MobileToolbar/>}>
<TrailingToolbarItem/>
</SecondarySplitHeader>
<Box sx={{ height: "0.5px", background: theme.palette.divider }} />
<main style={{ flexGrow: "1", overflowY: "auto" }}>
{children}
</main>
</>
</Stack>
)
Expand Down
12 changes: 9 additions & 3 deletions src/app/(authed)/(project-doc)/[...slug]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
"use client"

import { useEffect } from "react"
import { useContext, 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/context/ProjectsContext"
import LoadingIndicator from "@/common/ui/LoadingIndicator"

export default function Page() {
const { project, version, specification, navigateToSelectionIfNeeded } = useProjectSelection()
const { refreshing } = useContext(ProjectsContext)
// Ensure the URL reflects the current selection of project, version, and specification.
useEffect(() => {
navigateToSelectionIfNeeded()
Expand All @@ -30,10 +33,13 @@ export default function Page() {
{project && version && specification &&
<Documentation url={specification.url} />
}
{project && (!version || !specification) &&
{project && (!version || !specification) && !refreshing &&
<ErrorMessage text={`The selected ${!version ? "branch or tag" : "specification"} was not found.`}/>
}
{!project && <NotFound/>}
{refreshing && // project data is currently being fetched - show loading indicator
<LoadingIndicator />
}
{!project && !refreshing && <NotFound/>}
</>
)
}
4 changes: 2 additions & 2 deletions src/common/context/ProjectsContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@ import { Project } from "@/features/projects/domain"
export const SidebarTogglableContext = createContext<boolean>(true)

type ProjectsContextValue = {
refreshed: boolean,
refreshing: boolean,
projects: Project[],
setProjects: (projects: Project[]) => void
}

export const ProjectsContext = createContext<ProjectsContextValue>({
refreshed: false,
refreshing: false,
projects: [],
setProjects: () => {}
})
10 changes: 5 additions & 5 deletions src/features/projects/view/ProjectsContextProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@ const ProjectsContextProvider = ({
initialProjects?: Project[];
children?: React.ReactNode;
}) => {
const [refreshed, setRefreshed] = useState<boolean>(true);
const [projects, setProjects] = useState<Project[]>(initialProjects || []);
const isLoadingRef = useRef(false);
const [refreshing, setRefreshing] = useState(false);
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Perhaps we do not need const isLoadingRef = useRef(false)?


const hasProjectChanged = (value: Project[]) =>
value.some((project, index) => {
Expand All @@ -26,16 +26,15 @@ const ProjectsContextProvider = ({

const setProjectsAndRefreshed = (value: Project[]) => {
setProjects(value);
// If any project has changed, update the state and mark as refreshed
if (hasProjectChanged(value)) setRefreshed(true);
};

// Trigger background refresh after initial mount
useEffect(() => {
const refreshProjects = () => {
if (isLoadingRef.current) return;
isLoadingRef.current = true;

setRefreshing(true);

fetch("/api/projects", { method: "POST" })
.then((res) => res.json())
.then(
Expand All @@ -47,6 +46,7 @@ const ProjectsContextProvider = ({
.catch((error) => console.error("Failed to refresh projects", error))
.finally(() => {
isLoadingRef.current = false;
setRefreshing(false);
});
};
// Initial refresh
Expand All @@ -65,8 +65,8 @@ const ProjectsContextProvider = ({
return (
<ProjectsContext.Provider
value={{
refreshed,
projects,
refreshing,
setProjects: setProjectsAndRefreshed,
}}
>
Expand Down
Loading