Skip to content

Commit 3570836

Browse files
Merge pull request #602 from shapehq/optimize-project-doc-loadtime
Enhance project caching
2 parents 608a698 + f1c4d29 commit 3570836

File tree

20 files changed

+194
-135
lines changed

20 files changed

+194
-135
lines changed

docker-compose.yaml

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
version: '3.8'
21
services:
32
cache:
43
image: redis:6.2-alpine
@@ -8,6 +7,26 @@ services:
87
command: redis-server --save 20 1 --loglevel warning
98
volumes:
109
- cache:/data
10+
11+
postgres:
12+
image: postgres:16.8
13+
restart: always
14+
ports:
15+
- '5432:5432'
16+
environment:
17+
- POSTGRES_USER=oscar
18+
- POSTGRES_PASSWORD=passw0rd
19+
- POSTGRES_DB=shape-docs
20+
21+
app:
22+
build: .
23+
ports:
24+
- '3000:3000'
25+
env_file:
26+
- .env
27+
depends_on:
28+
- cache
29+
1130
volumes:
1231
cache:
13-
driver: local
32+
driver: local

package-lock.json

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/app/(authed)/(project-doc)/[...slug]/layout.tsx

Lines changed: 7 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -4,32 +4,21 @@ import { Box, Stack } from "@mui/material"
44
import { useTheme } from "@mui/material/styles"
55
import TrailingToolbarItem from "@/features/projects/view/toolbar/TrailingToolbarItem"
66
import MobileToolbar from "@/features/projects/view/toolbar/MobileToolbar"
7-
import SecondaryHeaderPlaceholder from "@/features/sidebar/view/SecondarySplitHeaderPlaceholder"
8-
import { useContext } from "react"
9-
import { ProjectsContext } from "@/common"
10-
import LoadingIndicator from "@/common/ui/LoadingIndicator"
117
import SecondarySplitHeader from "@/features/sidebar/view/SecondarySplitHeader"
128

139
export default function Page({ children }: { children: React.ReactNode }) {
14-
const { refreshed } = useContext(ProjectsContext)
15-
1610
const theme = useTheme()
1711

1812
return (
1913
<Stack sx={{ height: "100%" }}>
2014
<>
21-
{!refreshed ? <SecondaryHeaderPlaceholder/> :
22-
<SecondarySplitHeader mobileToolbar={<MobileToolbar/>}>
23-
<TrailingToolbarItem/>
24-
</SecondarySplitHeader>
25-
}
26-
<Box sx={{ height: "0.5px", background: theme.palette.divider }} />
27-
{refreshed ?
28-
<main style={{ flexGrow: "1", overflowY: "auto" }}>
29-
{children}
30-
</main> :
31-
<LoadingIndicator />
32-
}
15+
<SecondarySplitHeader mobileToolbar={<MobileToolbar/>}>
16+
<TrailingToolbarItem/>
17+
</SecondarySplitHeader>
18+
<Box sx={{ height: "0.5px", background: theme.palette.divider }} />
19+
<main style={{ flexGrow: "1", overflowY: "auto" }}>
20+
{children}
21+
</main>
3322
</>
3423
</Stack>
3524
)

src/app/(authed)/(project-doc)/[...slug]/page.tsx

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,17 @@
11
"use client"
22

3-
import { useEffect } from "react"
3+
import { useContext, useEffect } from "react"
44
import ErrorMessage from "@/common/ui/ErrorMessage"
55
import { updateWindowTitle } from "@/features/projects/domain"
66
import { useProjectSelection } from "@/features/projects/data"
77
import Documentation from "@/features/projects/view/Documentation"
88
import NotFound from "@/features/projects/view/NotFound"
9+
import { ProjectsContext } from "@/common/context/ProjectsContext"
10+
import LoadingIndicator from "@/common/ui/LoadingIndicator"
911

1012
export default function Page() {
1113
const { project, version, specification, navigateToSelectionIfNeeded } = useProjectSelection()
14+
const { refreshing } = useContext(ProjectsContext)
1215
// Ensure the URL reflects the current selection of project, version, and specification.
1316
useEffect(() => {
1417
navigateToSelectionIfNeeded()
@@ -30,10 +33,13 @@ export default function Page() {
3033
{project && version && specification &&
3134
<Documentation url={specification.url} />
3235
}
33-
{project && (!version || !specification) &&
36+
{project && (!version || !specification) && !refreshing &&
3437
<ErrorMessage text={`The selected ${!version ? "branch or tag" : "specification"} was not found.`}/>
3538
}
36-
{!project && <NotFound/>}
39+
{refreshing && // project data is currently being fetched - show loading indicator
40+
<LoadingIndicator />
41+
}
42+
{!project && !refreshing && <NotFound/>}
3743
</>
3844
)
3945
}

src/app/(authed)/layout.tsx

Lines changed: 23 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,38 @@
1-
import { redirect } from "next/navigation"
2-
import { SessionProvider } from "next-auth/react"
3-
import { session, projectRepository } from "@/composition"
4-
import ErrorHandler from "@/common/ui/ErrorHandler"
5-
import SessionBarrier from "@/features/auth/view/SessionBarrier"
6-
import ProjectsContextProvider from "@/features/projects/view/ProjectsContextProvider"
7-
import { SidebarTogglableContextProvider, SplitView } from "@/features/sidebar/view"
1+
import { redirect } from "next/navigation";
2+
import { SessionProvider } from "next-auth/react";
3+
import { session, projectDataSource } from "@/composition";
4+
import ErrorHandler from "@/common/ui/ErrorHandler";
5+
import SessionBarrier from "@/features/auth/view/SessionBarrier";
6+
import ProjectsContextProvider from "@/features/projects/view/ProjectsContextProvider";
7+
import {
8+
SidebarTogglableContextProvider,
9+
SplitView,
10+
} from "@/features/sidebar/view";
811

9-
export default async function Layout({ children }: { children: React.ReactNode }) {
10-
const isAuthenticated = await session.getIsAuthenticated()
12+
export default async function Layout({
13+
children,
14+
}: {
15+
children: React.ReactNode;
16+
}) {
17+
const isAuthenticated = await session.getIsAuthenticated();
1118
if (!isAuthenticated) {
12-
return redirect("/api/auth/signin")
19+
return redirect("/api/auth/signin");
1320
}
14-
const projects = await projectRepository.get()
21+
22+
const projects = await projectDataSource.getProjects();
23+
24+
1525
return (
1626
<ErrorHandler>
1727
<SessionProvider>
1828
<SessionBarrier>
1929
<ProjectsContextProvider initialProjects={projects}>
2030
<SidebarTogglableContextProvider>
21-
<SplitView>
22-
{children}
23-
</SplitView>
31+
<SplitView>{children}</SplitView>
2432
</SidebarTogglableContextProvider>
2533
</ProjectsContextProvider>
2634
</SessionBarrier>
2735
</SessionProvider>
2836
</ErrorHandler>
29-
)
37+
);
3038
}

src/app/api/blob/[owner]/[repository]/[...path]/route.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,4 +33,4 @@ export async function GET(req: NextRequest, { params }: { params: Promise<GetBlo
3333
headers.set("Content-Type", "text/plain");
3434
}
3535
return new NextResponse(file, { status: 200, headers })
36-
}
36+
}

src/app/api/hooks/github/route.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,4 @@ import { gitHubHookHandler } from "@/composition"
44
export const POST = async (req: NextRequest): Promise<NextResponse> => {
55
await gitHubHookHandler.handle(req)
66
return NextResponse.json({ status: "OK" })
7-
}
7+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
2+
import { NextResponse } from "next/server"
3+
import { projectDataSource } from "@/composition";
4+
5+
6+
export async function POST() {
7+
const projects = await projectDataSource.refreshProjects()
8+
return NextResponse.json({ projects })
9+
}

src/common/context/ProjectsContext.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,11 @@ import { Project } from "@/features/projects/domain"
66
export const SidebarTogglableContext = createContext<boolean>(true)
77

88
type ProjectsContextValue = {
9-
refreshed: boolean,
9+
refreshing: boolean,
1010
projects: Project[],
11-
setProjects: (projects: Project[]) => void
1211
}
1312

1413
export const ProjectsContext = createContext<ProjectsContextValue>({
15-
refreshed: false,
14+
refreshing: false,
1615
projects: [],
17-
setProjects: () => {}
1816
})

src/common/utils/fetcher.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
export class FetcherError extends Error {
1+
export class FetcherError extends Error {
22
readonly status: number
33

44
constructor(status: number, message: string) {
@@ -12,9 +12,9 @@
1212
input: RequestInfo,
1313
init?: RequestInit
1414
): Promise<JSON> {
15-
const res = await fetch(input, init)
15+
const res = await fetch(input, init)
1616
if (!res.ok) {
1717
throw new FetcherError(res.status, "An error occurred while fetching the data.")
1818
}
1919
return res.json()
20-
}
20+
}

0 commit comments

Comments
 (0)