Skip to content

Commit 6fc0a54

Browse files
committed
Initial shared loading logic
1 parent 8da382b commit 6fc0a54

File tree

9 files changed

+141
-65
lines changed

9 files changed

+141
-65
lines changed

site/api.ts

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { SvgIcon } from "@material-ui/core"
22
import { Logo } from "./components/Icons"
3+
import { wait } from "./util"
34

45
export interface Project {
56
id: string
@@ -24,11 +25,14 @@ export namespace Project {
2425

2526
const allProjects = [testProject1, testProject2]
2627

27-
export const getAllProjectsInOrg = (_org: string): Promise<Project[]> => {
28-
return Promise.resolve(allProjects)
28+
export const getAllProjectsInOrg = async (_org: string): Promise<Project[]> => {
29+
await wait(250)
30+
return allProjects
2931
}
3032

3133
export const getProject = async (_org: string, projectId: string): Promise<Project> => {
34+
await wait(250)
35+
3236
const matchingProjects = allProjects.filter((p) => p.id === projectId)
3337

3438
if (matchingProjects.length === 0) {
@@ -38,8 +42,9 @@ export namespace Project {
3842
return matchingProjects[0]
3943
}
4044

41-
export const createWorkspace = (name: string): Promise<string> => {
42-
return Promise.resolve("test-workspace")
45+
export const createWorkspace = async (name: string): Promise<string> => {
46+
await wait(250)
47+
return "test-workspace"
4348
}
4449
}
4550

site/components/Form/Title.tsx

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import React from "react"
44

55
export interface TitleProps {
66
title: string
7-
organization: string
7+
detail: React.ReactNode
88
}
99

1010
const useStyles = makeStyles((theme) => ({
@@ -19,15 +19,13 @@ const useStyles = makeStyles((theme) => ({
1919
},
2020
}))
2121

22-
export const Title: React.FC<TitleProps> = ({ title, organization }) => {
22+
export const Title: React.FC<TitleProps> = ({ title, detail }) => {
2323
const styles = useStyles()
2424

2525
return (
2626
<div className={styles.title}>
2727
<Typography variant="h3">{title}</Typography>
28-
<Typography variant="caption">
29-
In <strong>{organization}</strong> organization
30-
</Typography>
28+
<Typography variant="caption">{detail}</Typography>
3129
</div>
3230
)
3331
}

site/components/PageTemplates/FormPage.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,19 +44,19 @@ export interface FormButton {
4444

4545
export interface FormPageProps {
4646
title: string
47-
organization: string
47+
detail?: React.ReactNode
4848
buttons?: FormButton[]
4949
}
5050

51-
export const FormPage: React.FC<FormPageProps> = ({ title, organization, children, buttons }) => {
51+
export const FormPage: React.FC<FormPageProps> = ({ title, detail, children, buttons }) => {
5252
const styles = useStyles()
5353

5454
const actualButtons = buttons || []
5555

5656
return (
5757
<div className={styles.form}>
5858
<div className={styles.header}>
59-
<Title title={title} organization={organization} />
59+
<Title title={title} detail={detail} />
6060
</div>
6161
<div className={styles.body}>{children}</div>
6262
<div className={styles.footer}>
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import { Box, CircularProgress, makeStyles } from "@material-ui/core"
2+
import React from "react"
3+
import { RequestState } from "../../hooks/useRequest"
4+
5+
export interface LoadingPageProps<T> {
6+
request: RequestState<T>
7+
children: (state: T) => React.ReactElement<any, any>
8+
}
9+
10+
const useStyles = makeStyles(() => ({
11+
fullScreenLoader: {
12+
position: "absolute",
13+
top: "0",
14+
left: "0",
15+
right: "0",
16+
bottom: "0",
17+
display: "flex",
18+
justifyContent: "center",
19+
alignItems: "center",
20+
},
21+
}))
22+
23+
export const LoadingPage: React.FC<LoadingPageProps<T>> = <T,>(props: LoadingPageProps<T>) => {
24+
const styles = useStyles()
25+
26+
const { request, children } = props
27+
const { state } = request
28+
switch (state) {
29+
case "error":
30+
return <div>{request.error.toString()}</div>
31+
case "loading":
32+
return (
33+
<div className={styles.fullScreenLoader}>
34+
{" "}
35+
<CircularProgress />
36+
</div>
37+
)
38+
case "success":
39+
return children(request.payload)
40+
}
41+
}

site/hooks/useRequest.ts

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { useState, useEffect } from "react"
2+
import isReady from "next/router"
23

34
export type RequestState<TPayload> =
45
| {
@@ -13,20 +14,35 @@ export type RequestState<TPayload> =
1314
payload: TPayload
1415
}
1516

16-
export const useRequestor = <TPayload>(fn: () => Promise<TPayload>) => {
17+
export const useRequestor = <TPayload>(fn: () => Promise<TPayload>, deps: any[] = []) => {
1718
const [requestState, setRequestState] = useState<RequestState<TPayload>>({ state: "loading" })
1819

1920
useEffect(() => {
21+
// Initially, some parameters might not be available - make sure all query parameters are set
22+
// as a courtesy to users of this hook.
23+
if (!isReady) {
24+
return
25+
}
26+
27+
let cancelled = false
2028
const f = async () => {
2129
try {
2230
const response = await fn()
23-
setRequestState({ state: "success", payload: response })
31+
if (!cancelled) {
32+
setRequestState({ state: "success", payload: response })
33+
}
2434
} catch (err) {
25-
setRequestState({ state: "error", error: err })
35+
if (!cancelled) {
36+
setRequestState({ state: "error", error: err })
37+
}
2638
}
2739
}
2840
f()
29-
})
41+
42+
return () => {
43+
cancelled = true
44+
}
45+
}, [isReady, ...deps])
3046

3147
return requestState
3248
}

site/pages/workspaces/create/[projectId].tsx

Lines changed: 27 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { FormPage, FormButton } from "../../../components/PageTemplates"
99
import { useRequestor } from "../../../hooks/useRequest"
1010
import { FormSection } from "../../../components/Form"
1111
import { formTextFieldFactory } from "../../../components/Form/FormTextField"
12+
import { LoadingPage } from "../../../components/PageTemplates/LoadingPage"
1213

1314
namespace CreateProjectForm {
1415
export interface Schema {
@@ -27,8 +28,9 @@ const FormTextField = formTextFieldFactory<CreateProjectForm.Schema>()
2728
const CreateProjectPage: React.FC = () => {
2829
const router = useRouter()
2930
const { projectId: routeProjectId } = router.query
31+
console.log(routeProjectId)
3032
const projectId = firstOrOnly(routeProjectId)
31-
// const projectToLoad = useRequestor(() => API.Project.getProject("test-org", projectId))
33+
const projectToLoad = useRequestor(() => API.Project.getProject("test-org", projectId), [projectId])
3234

3335
const form = useFormik({
3436
enableReinitialize: true,
@@ -68,19 +70,30 @@ const CreateProjectPage: React.FC = () => {
6870
]
6971

7072
return (
71-
<FormPage title={"Create Project"} organization={"test-org"} buttons={buttons}>
72-
<FormSection title="General">
73-
<FormTextField
74-
form={form}
75-
formFieldName="name"
76-
fullWidth
77-
helperText="A unique name describing your workspace."
78-
label="Workspace Name"
79-
placeholder="my-dev"
80-
required
81-
/>
82-
</FormSection>
83-
</FormPage>
73+
<LoadingPage request={projectToLoad}>
74+
{(project) => {
75+
const detail = (
76+
<>
77+
<strong>{project.name}</strong> in <strong> {"test-org"}</strong> organization
78+
</>
79+
)
80+
return (
81+
<FormPage title={"Create Project"} detail={detail} buttons={buttons}>
82+
<FormSection title="General">
83+
<FormTextField
84+
form={form}
85+
formFieldName="name"
86+
fullWidth
87+
helperText="A unique name describing your workspace."
88+
label="Workspace Name"
89+
placeholder={project.id}
90+
required
91+
/>
92+
</FormSection>
93+
</FormPage>
94+
)
95+
}}
96+
</LoadingPage>
8497
)
8598
}
8699

site/pages/workspaces/create/index.tsx

Lines changed: 28 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import * as Api from "./../../../api"
77
import CircularProgress from "@material-ui/core/CircularProgress"
88
import { ProjectIcon } from "../../../components/Project/ProjectIcon"
99
import Box from "@material-ui/core/Box"
10+
import { LoadingPage } from "../../../components/PageTemplates/LoadingPage"
1011

1112
const CreateSelectProjectPage: React.FC = () => {
1213
const router = useRouter()
@@ -20,42 +21,34 @@ const CreateSelectProjectPage: React.FC = () => {
2021
router.push(`/workspaces/create/${projectId}`)
2122
}
2223

23-
let body
24-
25-
switch (requestState.state) {
26-
case "loading":
27-
body = <CircularProgress />
28-
break
29-
case "error":
30-
body = <>{requestState.error.toString()}</>
31-
break
32-
case "success":
33-
body = (
34-
<>
35-
{requestState.payload.map((project) => {
36-
return <ProjectIcon title={project.name} icon={project.icon} onClick={select(project.id)} />
37-
})}
38-
</>
39-
)
40-
break
41-
}
42-
43-
const buttons: FormButton[] = [
44-
{
45-
title: "Cancel",
46-
props: {
47-
variant: "outlined",
48-
onClick: cancel,
49-
},
50-
},
51-
]
52-
5324
return (
54-
<FormPage title={"Select Project"} organization={"test-org"} buttons={buttons}>
55-
<Box style={{ display: "flex", flexDirection: "row", justifyContent: "center", alignItems: "center" }}>
56-
{body}
57-
</Box>
58-
</FormPage>
25+
<LoadingPage request={requestState}>
26+
{(projects) => {
27+
const buttons: FormButton[] = [
28+
{
29+
title: "Cancel",
30+
props: {
31+
variant: "outlined",
32+
onClick: cancel,
33+
},
34+
},
35+
]
36+
const detail = (
37+
<>
38+
In <strong> {"test-org"}</strong> organization
39+
</>
40+
)
41+
return (
42+
<FormPage title={"Select Project"} detail={detail} buttons={buttons}>
43+
<Box style={{ display: "flex", flexDirection: "row", justifyContent: "center", alignItems: "center" }}>
44+
{projects.map((project: Api.Project) => {
45+
return <ProjectIcon title={project.name} icon={project.icon} onClick={select(project.id)} />
46+
})}
47+
</Box>
48+
</FormPage>
49+
)
50+
}}
51+
</LoadingPage>
5952
)
6053
}
6154

site/util/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,3 @@
11
export * from "./firstOrOnly"
2+
export * from "./formik"
3+
export * from "./promise"

site/util/promise.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
/**
2+
* Returns a promise that resolves after a set time
3+
*
4+
* @param time Time to wait in milliseconds
5+
*/
6+
export async function wait(milliseconds: number): Promise<void> {
7+
await new Promise((resolve) => setTimeout(resolve, milliseconds))
8+
}

0 commit comments

Comments
 (0)