@stanlemon/app-template 0.2.3 → 0.2.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/app.js +18 -4
- package/package.json +11 -2
- package/src/App.test.tsx +50 -23
- package/src/App.tsx +96 -82
- package/src/Input.tsx +16 -10
- package/src/Login.tsx +6 -6
- package/src/Register.tsx +6 -6
- package/src/Session.tsx +73 -0
- package/src/index.tsx +6 -1
package/app.js
CHANGED
|
@@ -3,16 +3,30 @@ import {
|
|
|
3
3
|
asyncJsonHandler as handler,
|
|
4
4
|
SimpleUsersDao,
|
|
5
5
|
} from "@stanlemon/server-with-auth";
|
|
6
|
+
import { Low, JSONFile } from "lowdb";
|
|
6
7
|
|
|
7
|
-
const
|
|
8
|
+
const adapter = new JSONFile("./db.json");
|
|
8
9
|
|
|
9
10
|
const app = createAppServer({
|
|
10
11
|
webpack: "http://localhost:8080",
|
|
11
12
|
secure: ["/api/"],
|
|
12
|
-
...
|
|
13
|
+
...new SimpleUsersDao([], adapter),
|
|
13
14
|
});
|
|
14
15
|
|
|
16
|
+
const db = new Low(adapter);
|
|
17
|
+
await db.read();
|
|
18
|
+
db.data.items ||= [];
|
|
19
|
+
|
|
15
20
|
app.get(
|
|
16
|
-
"/api/
|
|
17
|
-
handler(() =>
|
|
21
|
+
"/api/items",
|
|
22
|
+
handler(() => db.data.items)
|
|
23
|
+
);
|
|
24
|
+
|
|
25
|
+
app.post(
|
|
26
|
+
"/api/items",
|
|
27
|
+
handler(async (item) => {
|
|
28
|
+
db.data.items.push(item);
|
|
29
|
+
await db.write();
|
|
30
|
+
return db.data.items;
|
|
31
|
+
})
|
|
18
32
|
);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stanlemon/app-template",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.7",
|
|
4
4
|
"description": "A template for creating apps using the webdev package.",
|
|
5
5
|
"author": "Stan Lemon <stanlemon@users.noreply.github.com>",
|
|
6
6
|
"license": "MIT",
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
"scripts": {
|
|
9
9
|
"start": "node app.js",
|
|
10
10
|
"build": "npm run webpack:build",
|
|
11
|
+
"tsc": "tsc",
|
|
11
12
|
"webpack:serve": "webpack serve",
|
|
12
13
|
"webpack:build": "NODE_ENV=production webpack",
|
|
13
14
|
"test": "jest",
|
|
@@ -17,7 +18,15 @@
|
|
|
17
18
|
"lint:format": "eslint --fix --ext js,jsx,ts,tsx ./src/"
|
|
18
19
|
},
|
|
19
20
|
"dependencies": {
|
|
21
|
+
"@stanlemon/server-with-auth": "0.1.4",
|
|
20
22
|
"@stanlemon/webdev": "*",
|
|
21
|
-
"
|
|
23
|
+
"react": "^18.0.0",
|
|
24
|
+
"react-dom": "^18.0.0"
|
|
25
|
+
},
|
|
26
|
+
"devDependencies": {
|
|
27
|
+
"@testing-library/react": "^13.1.1",
|
|
28
|
+
"@testing-library/user-event": "^14.1.1",
|
|
29
|
+
"@types/react": "^18.0.5",
|
|
30
|
+
"@types/react-dom": "^18.0.1"
|
|
22
31
|
}
|
|
23
32
|
}
|
package/src/App.test.tsx
CHANGED
|
@@ -7,40 +7,67 @@ import {
|
|
|
7
7
|
} from "@testing-library/react";
|
|
8
8
|
import userEvent from "@testing-library/user-event";
|
|
9
9
|
import App from "./App";
|
|
10
|
+
import { SessionContext } from "./Session";
|
|
10
11
|
|
|
11
|
-
|
|
12
|
-
|
|
12
|
+
const output = ["item one", "item two"];
|
|
13
|
+
global.fetch = jest.fn((url, opts: { method: string; body: string }) => {
|
|
14
|
+
if (opts.method === "post") {
|
|
15
|
+
output.push(JSON.parse(opts.body) as string);
|
|
16
|
+
}
|
|
17
|
+
return Promise.resolve({
|
|
13
18
|
ok: true,
|
|
14
|
-
json: () =>
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
user: {
|
|
18
|
-
name: "Test Tester",
|
|
19
|
-
email: "test@test.com",
|
|
20
|
-
username: "test",
|
|
21
|
-
password: "password",
|
|
22
|
-
},
|
|
23
|
-
}),
|
|
24
|
-
})
|
|
25
|
-
) as jest.Mock;
|
|
19
|
+
json: () => Promise.resolve(output),
|
|
20
|
+
});
|
|
21
|
+
}) as jest.Mock;
|
|
26
22
|
|
|
27
23
|
test("<App/>", async () => {
|
|
28
24
|
act(() => {
|
|
29
|
-
render(
|
|
25
|
+
render(
|
|
26
|
+
<SessionContext.Provider
|
|
27
|
+
value={{
|
|
28
|
+
session: {
|
|
29
|
+
token: "abcd",
|
|
30
|
+
user: {
|
|
31
|
+
username: "user",
|
|
32
|
+
password: "password",
|
|
33
|
+
name: "user",
|
|
34
|
+
email: "user@example.com",
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
setSession: () => {},
|
|
38
|
+
}}
|
|
39
|
+
>
|
|
40
|
+
<App />
|
|
41
|
+
</SessionContext.Provider>
|
|
42
|
+
);
|
|
30
43
|
});
|
|
31
44
|
|
|
32
|
-
//
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
45
|
+
// The auth text is present
|
|
46
|
+
expect(screen.getByText("You logged in as user")).toBeInTheDocument();
|
|
47
|
+
|
|
48
|
+
// The header is present
|
|
49
|
+
expect(
|
|
50
|
+
screen.getByRole("heading", { name: "Hello World!" })
|
|
51
|
+
).toBeInTheDocument();
|
|
52
|
+
|
|
53
|
+
expect(
|
|
54
|
+
await screen.findByText("item one", { selector: "li" })
|
|
55
|
+
).toBeInTheDocument();
|
|
56
|
+
|
|
57
|
+
expect(
|
|
58
|
+
await screen.findByText("item two", { selector: "li" })
|
|
59
|
+
).toBeInTheDocument();
|
|
37
60
|
|
|
38
61
|
// Type some data into the input
|
|
39
|
-
await userEvent.type(screen.getByLabelText("Item"), "
|
|
62
|
+
await userEvent.type(screen.getByLabelText("Item"), "item three");
|
|
40
63
|
|
|
41
64
|
// Click the add button
|
|
42
|
-
|
|
65
|
+
act(() => {
|
|
66
|
+
fireEvent.click(screen.getByText("Add", { selector: "button" }));
|
|
67
|
+
});
|
|
43
68
|
|
|
44
69
|
// Now we should have a list item with the text we entered
|
|
45
|
-
expect(
|
|
70
|
+
expect(
|
|
71
|
+
await screen.findByText("item three", { selector: "li" })
|
|
72
|
+
).toBeInTheDocument();
|
|
46
73
|
});
|
package/src/App.tsx
CHANGED
|
@@ -1,15 +1,11 @@
|
|
|
1
|
-
import { useState,
|
|
1
|
+
import { useState, useContext, useEffect } from "react";
|
|
2
2
|
import "./App.less";
|
|
3
|
+
import { SessionContext } from "./Session";
|
|
3
4
|
import Header from "./Header";
|
|
4
5
|
import Input from "./Input";
|
|
5
6
|
import Login from "./Login";
|
|
6
7
|
import Register from "./Register";
|
|
7
8
|
|
|
8
|
-
export const SessionContext = createContext<{
|
|
9
|
-
session: Session | null;
|
|
10
|
-
setSession: React.Dispatch<React.SetStateAction<Session | null>>;
|
|
11
|
-
} | null>(null);
|
|
12
|
-
|
|
13
9
|
export type ErrorMessage = {
|
|
14
10
|
message: string;
|
|
15
11
|
};
|
|
@@ -18,102 +14,120 @@ export type FormErrors = {
|
|
|
18
14
|
errors: Record<string, string>;
|
|
19
15
|
};
|
|
20
16
|
|
|
21
|
-
export type Session = {
|
|
22
|
-
token: string | null;
|
|
23
|
-
user: User | null;
|
|
24
|
-
};
|
|
25
|
-
export type User = {
|
|
26
|
-
name: string | null;
|
|
27
|
-
email: string | null;
|
|
28
|
-
username: string;
|
|
29
|
-
password: string;
|
|
30
|
-
};
|
|
31
|
-
|
|
32
17
|
export default function App() {
|
|
33
|
-
const [initialized, setInitialized] = useState<boolean>(false);
|
|
34
|
-
const [session, setSession] = useState<Session | null>(null);
|
|
35
18
|
const [value, setValue] = useState<string>("");
|
|
36
19
|
const [items, setItems] = useState<string[]>([]);
|
|
37
20
|
|
|
38
|
-
const
|
|
21
|
+
const { session } = useContext(SessionContext);
|
|
39
22
|
|
|
23
|
+
const itemsJson = JSON.stringify(items);
|
|
40
24
|
useEffect(() => {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
Accept: "application/json",
|
|
45
|
-
"Content-Type": "application/json",
|
|
46
|
-
},
|
|
47
|
-
})
|
|
48
|
-
.then((response) => {
|
|
49
|
-
setInitialized(true);
|
|
50
|
-
|
|
51
|
-
if (!response.ok) {
|
|
52
|
-
throw new Error(response.statusText);
|
|
53
|
-
}
|
|
54
|
-
return response;
|
|
55
|
-
})
|
|
56
|
-
.then((response) => response.json())
|
|
57
|
-
.then((session: Session) => {
|
|
58
|
-
setSession(session);
|
|
25
|
+
fetchApi("/api/items", session?.token || "")
|
|
26
|
+
.then((items: string[]) => {
|
|
27
|
+
setItems(items);
|
|
59
28
|
})
|
|
60
|
-
.catch((err) =>
|
|
61
|
-
|
|
62
|
-
});
|
|
63
|
-
}, [session?.token, initialized]);
|
|
29
|
+
.catch((err) => console.error(err));
|
|
30
|
+
}, [itemsJson, session?.token]);
|
|
64
31
|
|
|
65
32
|
const addItem = () => {
|
|
66
|
-
setItems([...items, value]);
|
|
67
33
|
setValue("");
|
|
68
|
-
};
|
|
69
34
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
}
|
|
35
|
+
fetchApi("/api/items", session?.token || "", "post", value)
|
|
36
|
+
.then((items: string[]) => {
|
|
37
|
+
setItems(items);
|
|
38
|
+
})
|
|
39
|
+
.catch((err) => console.error(err));
|
|
40
|
+
};
|
|
77
41
|
|
|
78
42
|
return (
|
|
79
|
-
|
|
43
|
+
<>
|
|
80
44
|
<Header />
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
<
|
|
85
|
-
<em>You are not currently logged in.</em>
|
|
86
|
-
</p>
|
|
45
|
+
{!session.user && (
|
|
46
|
+
<Row>
|
|
47
|
+
<Column>
|
|
48
|
+
<h2>Login</h2>
|
|
87
49
|
<Login />
|
|
88
|
-
|
|
50
|
+
</Column>
|
|
51
|
+
<Column>
|
|
52
|
+
<h2>Register</h2>
|
|
89
53
|
<Register />
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
54
|
+
</Column>
|
|
55
|
+
</Row>
|
|
56
|
+
)}
|
|
57
|
+
{session.user && (
|
|
58
|
+
<>
|
|
94
59
|
<p>
|
|
95
|
-
<em>You logged in as {session.user
|
|
60
|
+
<em>You logged in as {session.user?.username}</em>
|
|
96
61
|
</p>
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
62
|
+
<Input
|
|
63
|
+
label="Item"
|
|
64
|
+
name="item"
|
|
65
|
+
value={value}
|
|
66
|
+
onChange={(value) => setValue(value)}
|
|
67
|
+
onEnter={addItem}
|
|
68
|
+
/>
|
|
69
|
+
<button onClick={addItem}>Add</button>
|
|
70
|
+
<ul>
|
|
71
|
+
{items.map((item, i) => (
|
|
72
|
+
<li key={i}>{item}</li>
|
|
73
|
+
))}
|
|
74
|
+
</ul>
|
|
75
|
+
</>
|
|
76
|
+
)}
|
|
77
|
+
</>
|
|
78
|
+
);
|
|
79
|
+
}
|
|
107
80
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
81
|
+
function Row({ children }: { children: React.ReactNode }) {
|
|
82
|
+
return (
|
|
83
|
+
<div
|
|
84
|
+
style={{
|
|
85
|
+
display: "flex",
|
|
86
|
+
flexDirection: "row",
|
|
87
|
+
flexWrap: "wrap",
|
|
88
|
+
width: "100%",
|
|
89
|
+
}}
|
|
90
|
+
>
|
|
91
|
+
{children}
|
|
92
|
+
</div>
|
|
114
93
|
);
|
|
115
94
|
}
|
|
116
95
|
|
|
117
|
-
function
|
|
118
|
-
return
|
|
96
|
+
function Column({ children }: { children: React.ReactNode }) {
|
|
97
|
+
return (
|
|
98
|
+
<div
|
|
99
|
+
style={{
|
|
100
|
+
display: "flex",
|
|
101
|
+
flexDirection: "column",
|
|
102
|
+
flexBasis: "100%",
|
|
103
|
+
flex: 1,
|
|
104
|
+
}}
|
|
105
|
+
>
|
|
106
|
+
{children}
|
|
107
|
+
</div>
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function fetchApi(
|
|
112
|
+
url: string,
|
|
113
|
+
token: string,
|
|
114
|
+
method = "get",
|
|
115
|
+
data?: any
|
|
116
|
+
): Promise<any> {
|
|
117
|
+
return fetch(url, {
|
|
118
|
+
method: method,
|
|
119
|
+
headers: {
|
|
120
|
+
Authorization: `Bearer ${token}`,
|
|
121
|
+
Accept: "application/json",
|
|
122
|
+
"Content-Type": "application/json",
|
|
123
|
+
},
|
|
124
|
+
body: JSON.stringify(data),
|
|
125
|
+
})
|
|
126
|
+
.then((response) => {
|
|
127
|
+
if (!response.ok) {
|
|
128
|
+
throw new Error(response.statusText);
|
|
129
|
+
}
|
|
130
|
+
return response;
|
|
131
|
+
})
|
|
132
|
+
.then((response) => response.json());
|
|
119
133
|
}
|
package/src/Input.tsx
CHANGED
|
@@ -1,3 +1,16 @@
|
|
|
1
|
+
export interface Props
|
|
2
|
+
// Inherit everything from input except onChange which we simplify here
|
|
3
|
+
extends Omit<React.ComponentPropsWithRef<"input">, "onChange"> {
|
|
4
|
+
type?: string;
|
|
5
|
+
name: string;
|
|
6
|
+
value?: string;
|
|
7
|
+
label?: string;
|
|
8
|
+
placeholder?: string;
|
|
9
|
+
onChange?: (value: string) => void;
|
|
10
|
+
onEnter?: () => void;
|
|
11
|
+
error?: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
1
14
|
export default function Input({
|
|
2
15
|
type = "text",
|
|
3
16
|
name,
|
|
@@ -11,16 +24,8 @@ export default function Input({
|
|
|
11
24
|
/* noop */
|
|
12
25
|
},
|
|
13
26
|
error,
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
name: string;
|
|
17
|
-
value?: string;
|
|
18
|
-
label?: string;
|
|
19
|
-
placeholder?: string;
|
|
20
|
-
onChange?: (value: string) => void;
|
|
21
|
-
onEnter?: () => void;
|
|
22
|
-
error?: string;
|
|
23
|
-
}) {
|
|
27
|
+
...attrs
|
|
28
|
+
}: Props) {
|
|
24
29
|
const handleChange = (e: React.ChangeEvent<HTMLInputElement>): void => {
|
|
25
30
|
onChange(e.currentTarget.value);
|
|
26
31
|
};
|
|
@@ -41,6 +46,7 @@ export default function Input({
|
|
|
41
46
|
onKeyPress={handleKeyPress}
|
|
42
47
|
placeholder={placeholder}
|
|
43
48
|
value={value}
|
|
49
|
+
{...attrs}
|
|
44
50
|
/>
|
|
45
51
|
{error && <div>{error}</div>}
|
|
46
52
|
</>
|
package/src/Login.tsx
CHANGED
|
@@ -1,19 +1,18 @@
|
|
|
1
1
|
import { useState, useContext } from "react";
|
|
2
|
-
import {
|
|
2
|
+
import { ErrorMessage } from "./App";
|
|
3
|
+
import { SessionContext, SessionData, UserData } from "./Session";
|
|
3
4
|
import Input from "./Input";
|
|
4
5
|
|
|
5
6
|
export default function Login() {
|
|
6
7
|
const [error, setError] = useState<string | null>(null);
|
|
7
|
-
const [values, setValues] = useState<
|
|
8
|
+
const [values, setValues] = useState<UserData>({
|
|
8
9
|
name: "",
|
|
9
10
|
email: "",
|
|
10
11
|
username: "",
|
|
11
12
|
password: "",
|
|
12
13
|
});
|
|
13
14
|
|
|
14
|
-
const { setSession } = useContext(SessionContext)
|
|
15
|
-
setSession: () => {},
|
|
16
|
-
};
|
|
15
|
+
const { setSession } = useContext(SessionContext);
|
|
17
16
|
|
|
18
17
|
const onSubmit = () => {
|
|
19
18
|
setError(null);
|
|
@@ -43,7 +42,7 @@ export default function Login() {
|
|
|
43
42
|
data: Record<string, unknown>;
|
|
44
43
|
}) => {
|
|
45
44
|
if (ok) {
|
|
46
|
-
setSession(data as
|
|
45
|
+
setSession(data as SessionData);
|
|
47
46
|
} else {
|
|
48
47
|
setError((data as ErrorMessage).message);
|
|
49
48
|
}
|
|
@@ -66,6 +65,7 @@ export default function Login() {
|
|
|
66
65
|
label="Username"
|
|
67
66
|
value={values.username}
|
|
68
67
|
onChange={(value) => setValues({ ...values, username: value })}
|
|
68
|
+
autoCapitalize="off"
|
|
69
69
|
/>
|
|
70
70
|
<Input
|
|
71
71
|
name="password"
|
package/src/Register.tsx
CHANGED
|
@@ -1,20 +1,19 @@
|
|
|
1
1
|
import { useState, useContext } from "react";
|
|
2
|
-
import {
|
|
2
|
+
import { FormErrors } from "./App";
|
|
3
|
+
import { SessionData, UserData, SessionContext } from "./Session";
|
|
3
4
|
import Input from "./Input";
|
|
4
5
|
|
|
5
6
|
// eslint-disable-next-line max-lines-per-function
|
|
6
7
|
export default function Register() {
|
|
7
8
|
const [errors, setErrors] = useState<Record<string, string>>({});
|
|
8
|
-
const [values, setValues] = useState<
|
|
9
|
+
const [values, setValues] = useState<UserData>({
|
|
9
10
|
name: "",
|
|
10
11
|
email: "",
|
|
11
12
|
username: "",
|
|
12
13
|
password: "",
|
|
13
14
|
});
|
|
14
15
|
|
|
15
|
-
const { setSession } = useContext(SessionContext)
|
|
16
|
-
setSession: () => {},
|
|
17
|
-
};
|
|
16
|
+
const { setSession } = useContext(SessionContext);
|
|
18
17
|
|
|
19
18
|
const onSubmit = () => {
|
|
20
19
|
setErrors({});
|
|
@@ -44,7 +43,7 @@ export default function Register() {
|
|
|
44
43
|
data: Record<string, unknown>;
|
|
45
44
|
}) => {
|
|
46
45
|
if (ok) {
|
|
47
|
-
setSession(data as
|
|
46
|
+
setSession(data as SessionData);
|
|
48
47
|
} else {
|
|
49
48
|
setErrors((data as FormErrors).errors);
|
|
50
49
|
}
|
|
@@ -63,6 +62,7 @@ export default function Register() {
|
|
|
63
62
|
value={values.username}
|
|
64
63
|
onChange={(value) => setValues({ ...values, username: value })}
|
|
65
64
|
error={errors.username}
|
|
65
|
+
autoCapitalize="off"
|
|
66
66
|
/>
|
|
67
67
|
<Input
|
|
68
68
|
name="password"
|
package/src/Session.tsx
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import React, { useState, useEffect, createContext } from "react";
|
|
2
|
+
|
|
3
|
+
export const SessionContext = createContext<{
|
|
4
|
+
session: SessionData;
|
|
5
|
+
setSession: React.Dispatch<React.SetStateAction<SessionData>>;
|
|
6
|
+
}>({
|
|
7
|
+
session: {
|
|
8
|
+
token: null,
|
|
9
|
+
user: null,
|
|
10
|
+
},
|
|
11
|
+
setSession: () => {},
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
export type SessionData = {
|
|
15
|
+
token: string | null;
|
|
16
|
+
user: UserData | null;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export type UserData = {
|
|
20
|
+
name: string | null;
|
|
21
|
+
email: string | null;
|
|
22
|
+
username: string;
|
|
23
|
+
password: string;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export default function Session({ children }: { children: React.ReactChild }) {
|
|
27
|
+
const [initialized, setInitialized] = useState<boolean>(false);
|
|
28
|
+
const [session, setSession] = useState<SessionData>({
|
|
29
|
+
token: null,
|
|
30
|
+
user: null,
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
useEffect(() => {
|
|
34
|
+
fetch("/auth/session", {
|
|
35
|
+
headers: {
|
|
36
|
+
Authorization: `Bearer ${session.token || ""}`,
|
|
37
|
+
Accept: "application/json",
|
|
38
|
+
"Content-Type": "application/json",
|
|
39
|
+
},
|
|
40
|
+
})
|
|
41
|
+
.then((response) => {
|
|
42
|
+
setInitialized(true);
|
|
43
|
+
|
|
44
|
+
if (!response.ok) {
|
|
45
|
+
throw new Error(response.statusText);
|
|
46
|
+
}
|
|
47
|
+
return response;
|
|
48
|
+
})
|
|
49
|
+
.then((response) => response.json())
|
|
50
|
+
.then((session: SessionData) => {
|
|
51
|
+
setSession(session);
|
|
52
|
+
})
|
|
53
|
+
.catch((err) => {
|
|
54
|
+
console.error(err);
|
|
55
|
+
});
|
|
56
|
+
}, [session?.token, initialized]);
|
|
57
|
+
|
|
58
|
+
if (!initialized) {
|
|
59
|
+
return (
|
|
60
|
+
<div>
|
|
61
|
+
<em>Loading...</em>
|
|
62
|
+
</div>
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const contextValue = { session, setSession };
|
|
67
|
+
|
|
68
|
+
return (
|
|
69
|
+
<SessionContext.Provider value={contextValue}>
|
|
70
|
+
{children}
|
|
71
|
+
</SessionContext.Provider>
|
|
72
|
+
);
|
|
73
|
+
}
|
package/src/index.tsx
CHANGED
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
import { createRoot } from "react-dom/client";
|
|
2
2
|
import App from "./App";
|
|
3
|
+
import Session from "./Session";
|
|
3
4
|
|
|
4
5
|
const root = createRoot(
|
|
5
6
|
document.body.appendChild(document.createElement("div"))
|
|
6
7
|
);
|
|
7
|
-
root.render(
|
|
8
|
+
root.render(
|
|
9
|
+
<Session>
|
|
10
|
+
<App />
|
|
11
|
+
</Session>
|
|
12
|
+
);
|
|
8
13
|
|
|
9
14
|
const link = document.createElement("link");
|
|
10
15
|
link.setAttribute("rel", "stylesheet");
|