diff --git a/client/src/App.css b/client/src/App.css
index 2962eebb..6c035b17 100644
--- a/client/src/App.css
+++ b/client/src/App.css
@@ -15,4 +15,30 @@ a {
}
a:visited {
color: slategray;
+}
+
+.main-column {
+ width: 76ch;
+ margin-left: auto;
+ margin-right: auto;
+ padding-top: 3rem;
+ color: white;
+}
+header {
+ text-align: right;
+ width: 100%;
+}
+header hr {
+ width: 60%;
+ margin-inline-start: auto;
+ margin-inline-end: 0;
+ color: white;
+}
+
+.page-contents {
+ text-align: left;
+ background: rgba(10,66,30, 0.75);
+ margin: 1rem;
+ padding: 1rem;
+ border-radius: 1rem;
}
\ No newline at end of file
diff --git a/client/src/App.jsx b/client/src/App.jsx
index 1fc43b6d..94cd0831 100644
--- a/client/src/App.jsx
+++ b/client/src/App.jsx
@@ -5,6 +5,8 @@ import PageView from '/src/page/PageView.jsx';
import PageEdit from '/src/page/PageEdit.jsx';
import LogIn from '/src/login/LogIn.jsx';
import Register from '/src/login/Register.jsx';
+import Profile from '/src/login/Profile.jsx';
+import AuthProvider from '/src/AuthProvider.jsx';
import './App.css';
@@ -13,15 +15,18 @@ const queryClient = new QueryClient();
function App() {
return (
-
-
- }/>
- }/>
- }/>
- }/>
- }/>
-
-
+
+
+
+ }/>
+ }/>
+ }/>
+ }/>
+ }/>
+ }/>
+
+
+
);
}
diff --git a/client/src/AuthProvider.jsx b/client/src/AuthProvider.jsx
new file mode 100644
index 00000000..91ec04ee
--- /dev/null
+++ b/client/src/AuthProvider.jsx
@@ -0,0 +1,29 @@
+import { useState, createContext, useContext } from "react";
+import { postLogIn, postLogOut } from './apiTools.jsx';
+
+const LogInContext = createContext(false);
+export const LogInStatusUpdateEscapeTool = {};
+
+function AuthProvider({children}) {
+ let [loggedIn, setLoggedIn] = useState(localStorage.getItem("loggedIn") == "true");
+
+ function logOneWay (yeah) {
+ localStorage.setItem("loggedIn", yeah);
+ setLoggedIn(yeah);
+ }
+
+ LogInStatusUpdateEscapeTool.loggedIn = loggedIn;
+ LogInStatusUpdateEscapeTool.setLoggedIn = logOneWay;
+
+ return (
+
+ {children}
+
+ );
+}
+
+export function useLoggedIn() {
+ return useContext(LogInContext);
+}
+
+export default AuthProvider;
\ No newline at end of file
diff --git a/client/src/apiTools.jsx b/client/src/apiTools.jsx
index 38bd9d55..914520f1 100644
--- a/client/src/apiTools.jsx
+++ b/client/src/apiTools.jsx
@@ -1,4 +1,5 @@
import Graph from 'graphology';
+import { LogInStatusUpdateEscapeTool } from './AuthProvider.jsx';
// This is wrapper functtions to do requests to the api, from the frontend.
@@ -6,8 +7,14 @@ export const apiUrl = `${window.location.origin}/api`;
//lil helper to throw errorrs from the promise when we get not-ok results
-export const shoofetch = (url, config) => fetch(url, {...config, ...defaults})
+export const shoofetch = (url, config) => fetch(url, {...defaults, ...config})
.then(async (res) => {
+ if (res.status == 401) {
+ LogInStatusUpdateEscapeTool.setLoggedIn(false);
+ }
+
+ localStorage.setItem("session", res.session);
+
if (!res.ok) {
throw new Error(`got an error from the server: ${await res.text()}`);
}
@@ -42,7 +49,7 @@ export async function fetchPage(id) {
}
export async function postPage({id, title, description}) {
- return fetch(`${apiUrl}/page/${id}`, {
+ return shoofetch(`${apiUrl}/page/${id}`, {
method: 'POST',
body: JSON.stringify({id: id, title: title, description: description}),
...defaults
@@ -75,13 +82,25 @@ export async function createAccount({name, password, nonce}) {
})
}
-export async function logIn({name, password}) {
+export async function postLogIn({name, password}) {
return shoofetch(`${apiUrl}/login`, {
method: 'POST',
body: JSON.stringify({name: name, password: password})
+ }).then((res) => {
+ LogInStatusUpdateEscapeTool.setLoggedIn(true);
+ return res;
+ })
+}
+
+export async function postLogOut() {
+ return shoofetch(`${apiUrl}/logout`, {
+ method: 'POST'
+ }).then((res) => {
+ LogInStatusUpdateEscapeTool.setLoggedIn(false);
+ return res;
})
}
-export async function logOut() {
- return shoofetch(`${apiUrl}/logout`, { method: 'POST' });
+export async function fetchProfile() {
+ return shoofetch(`${apiUrl}/user`, {method: 'GET'});
}
\ No newline at end of file
diff --git a/client/src/clientStuff.jsx b/client/src/clientStuff.jsx
index fc54f60f..8bed4e7a 100644
--- a/client/src/clientStuff.jsx
+++ b/client/src/clientStuff.jsx
@@ -1,7 +1,7 @@
-// New hook to make links use the react-router `navigate` method instead of default browser behavior.
-
-import { useNavigate } from 'react-router-dom'
+import { useNavigate } from 'react-router-dom';
+import { postLogOut } from './apiTools.jsx';
+// New hook to make links use the react-router `navigate` method instead of default browser behavior.
export function useFixLinks() {
// spread this return value on elements in order to make them navigate
const navigate = useNavigate();
diff --git a/client/src/landing/Landing.css b/client/src/landing/Landing.css
index e63efdd9..5107a302 100644
--- a/client/src/landing/Landing.css
+++ b/client/src/landing/Landing.css
@@ -1,21 +1,12 @@
-.landing-page {
- width: 76ch;
+.landing-column {
+ width: 80%;
margin-left: auto;
margin-right: auto;
padding-top: 3rem;
color: white;
}
-.landing-page header {
- text-align: right;
- width: 100%;
-}
-.landing-page header hr {
- width: 60%;
- margin-inline-start: auto;
- margin-inline-end: 0;
- color: white;
-}
+
.landing-container {
display: grid;
width: 100%;
@@ -29,3 +20,7 @@
background: rgba(10,66,30, 0.75);
}
+ol li {
+ list-style-type: circle;
+}
+
diff --git a/client/src/landing/Landing.jsx b/client/src/landing/Landing.jsx
index fcb5fa42..0c04558d 100644
--- a/client/src/landing/Landing.jsx
+++ b/client/src/landing/Landing.jsx
@@ -1,9 +1,12 @@
-import { useNavigate, Link } from 'react-router-dom'
+import { useState } from 'react';
+import { useNavigate, Link } from 'react-router-dom';
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
-import { fetchPageList, postNewPage, logOut } from '../apiTools.jsx';
+import { fetchPageList, postNewPage, postLogOut } from '../apiTools.jsx';
import { useFixLinks } from '../clientStuff.jsx';
+import { useLoggedIn } from '../AuthProvider.jsx';
+
import PageList from './PageList.jsx';
import GraphRender from './GraphRender.jsx';
@@ -23,8 +26,10 @@ function Landing() {
},
});
+ const loggedIn = useLoggedIn();
+
return (
-
+
Welcome to the forest.
@@ -37,10 +42,20 @@ function Landing() {
-
-
-
-
+ { loggedIn
+ ?
+ <>
+
+
+
+ >
+ :
+ <>
+
+
+
+ >
+ }
diff --git a/client/src/login/LogIn.jsx b/client/src/login/LogIn.jsx
index 0c3e24c5..b7c9530d 100644
--- a/client/src/login/LogIn.jsx
+++ b/client/src/login/LogIn.jsx
@@ -1,31 +1,31 @@
import { useForm } from "react-hook-form";
import { useNavigate } from 'react-router-dom';
-import { logIn } from '../apiTools.jsx';
+import { postLogIn } from '../apiTools.jsx';
function LogIn() {
const { register, handleSubmit } = useForm();
const navigate = useNavigate();
const onSubmit = (d) => {
- logIn(d)
- .then(() => navigate('/'))
- .catch((error) => console.log('error logging in: ', error));
+ postLogIn(d).then( () => navigate('/') );
};
return (
-
+
+
+
);
}
diff --git a/client/src/login/Profile.jsx b/client/src/login/Profile.jsx
new file mode 100644
index 00000000..c6ef958b
--- /dev/null
+++ b/client/src/login/Profile.jsx
@@ -0,0 +1,52 @@
+import { useEffect } from 'react';
+import { useForm } from "react-hook-form";
+import { useNavigate } from 'react-router-dom';
+import { fetchProfile } from '../apiTools.jsx';
+import { useLoggedIn } from '../AuthProvider.jsx';
+import { useQuery } from '@tanstack/react-query';
+
+function Profile() {
+ const { register, handleSubmit } = useForm();
+ const navigate = useNavigate();
+ const loggedIn = useLoggedIn();
+
+ const { isPending, isError, error, data } = useQuery({ // fetch the currrent values
+ queryKey: ['profile'],
+ queryFn: fetchProfile,
+ retry: 1
+ });
+
+ //useEffect(() => { if (!loggedIn) navigate('/'); }, [loggedIn]);
+
+ const { name, favoriteColor, leastFavoriteColor } = (data || {});
+
+ return (
+
+
+ the page {isPending ? "is" : "isn't"} pending
+ there {isError ? "is" : "isn't"} an error
+ error is {error?.message}
+
+
+
+ );
+}
+
+export default Profile;
\ No newline at end of file
diff --git a/client/src/login/Register.jsx b/client/src/login/Register.jsx
index b977bd61..232e941b 100644
--- a/client/src/login/Register.jsx
+++ b/client/src/login/Register.jsx
@@ -13,24 +13,26 @@ function Register() {
};
return (
-
+
+
+
);
}
diff --git a/client/src/page/PageEdit.jsx b/client/src/page/PageEdit.jsx
index e7f23b58..08784587 100644
--- a/client/src/page/PageEdit.jsx
+++ b/client/src/page/PageEdit.jsx
@@ -4,7 +4,7 @@ import { apiUrl, fetchPage, postPage, deletePage } from '../apiTools.jsx';
import './Pages.css';
-function PageView() {
+function PageEdit() {
const queryClient = useQueryClient();
const navigate = useNavigate();
const { pagenumber } = useParams();
@@ -32,10 +32,9 @@ function PageView() {
const ready = !(fetchQuery.error || fetchQuery.isPending);
- let the_id, page_title, page_text, page_html;
- if (ready) [the_id, page_title, page_text, page_html] = fetchQuery.data;
- if (!page_title) page_title = " ";
- if (!page_text) page_text = " ";
+ let {id, title, description, html} = fetchQuery.data || {};
+ if (!title) title = " ";
+ if (!description) description = " ";
function submitChanges(e) {
const newTitle = document.querySelector('span').innerHTML;
@@ -45,7 +44,7 @@ function PageView() {
title: newTitle,
description: newText,
});
- navigate(`/${pagenumber}`)
+ navigate(`/${pagenumber}`, {replace: true})
}
function submitDelete(e) {
@@ -55,13 +54,14 @@ function PageView() {
}
return (
-
+
+ 🌳
{pagenumber}.
+ dangerouslySetInnerHTML={{__html: ready ? title : "..." }} />
@@ -71,7 +71,7 @@ function PageView() {
+ dangerouslySetInnerHTML={{__html: description}} />