💄 Améliore l'affichage et certaines actions

Fait fonctionner la suppression d'une page
Fait fonctionner la pagination
Fait fonctionner mieux le formulaire
Fait fonctionner le markdown
This commit is contained in:
Clement Desmidt 2022-03-04 13:07:27 +01:00
parent a8116aa5a2
commit 844f625298
5 changed files with 77671 additions and 245 deletions

77744
public/js/app.js vendored

File diff suppressed because one or more lines are too long

View File

@ -2,7 +2,7 @@ import PageForm from "./Form";
import Pages from "./List"; import Pages from "./List";
import Prompt from "./Prompt"; import Prompt from "./Prompt";
import * as React from 'react'; import * as React from 'react';
import {Alert, AlertTitle, Divider} from "@mui/material"; import {Alert, AlertTitle, Button, Divider} from "@mui/material";
import {EncryptStorage} from "storage-encryption"; import {EncryptStorage} from "storage-encryption";
import { import {
Switch, Switch,
@ -96,17 +96,18 @@ function App() {
return user ? ( return user ? (
<p> <p>
Welcome!{" "} Bienvenue !{" "}
<button <Button variant="outlined" size="small" sx={{align: "right"}} onClick={() => {
onClick={() => { signout(() => history.push("/"));
signout(() => history.push("/")); }}>
}} Fermer mon carnet Ă  clef
> </Button>
Sign out
</button>
</p> </p>
) : ( ) : (
<p>You are not logged in.</p> <p>
Votre carnet est fermé.{" "}
<Link to="/diary/public/pages">Voir vos pages</Link>
</p>
); );
} }
@ -149,7 +150,7 @@ function App() {
function ListPage() { function ListPage() {
return ( return (
<div className="col-md-8"> <div className="col-md-12">
<Pages <Pages
pages={listPages} pages={listPages}
url={getPageContentUrl} url={getPageContentUrl}
@ -167,12 +168,7 @@ function App() {
<div> <div>
<AuthButton /> <AuthButton />
<Error/> <Error/>
<ul> <Divider sx={{height: "20px", mb: "20px", mt:"10px"}}/>
<li>
<Link to="/diary/public/pages">Voir vos pages</Link>
</li>
</ul>
<Switch> <Switch>
<Route path="/diary/public/pass"> <Route path="/diary/public/pass">
<PromptPage /> <PromptPage />

View File

@ -6,31 +6,35 @@ let encryptStorage = new EncryptStorage('test'); // TODO la clef doit venir de l
export default function PageForm({setListPages, csrf, url, passphrase}) { export default function PageForm({setListPages, csrf, url, passphrase}) {
const isPassphraseSet = passphrase !== null; const isPassphraseSet = passphrase !== null;
const [content, setContent] = React.useState("");
const onSubmit = async (event: React.FormEvent<HTMLFormElement>) => { const onSubmit = async (event: React.FormEvent<HTMLFormElement>) => {
event.preventDefault(); event.preventDefault();
encryptStorage = new EncryptStorage(passphrase); encryptStorage = new EncryptStorage(passphrase);
let HTMLForm : HTMLFormElement = event.currentTarget; const HTMLForm : HTMLFormElement = event.currentTarget;
let decryptedFormData = new FormData(HTMLForm); const decryptedFormData = new FormData(HTMLForm);
let encryptedFormData = new FormData(); const encryptedFormData = new FormData();
for (let [key, value] of decryptedFormData.entries()) { for (const [key, value] of decryptedFormData.entries()) {
encryptStorage.encrypt('uuid'+key, value); encryptStorage.encrypt('uuid'+key, value);
let newEncryptedString = localStorage.getItem('uuid'+key); const newEncryptedString = localStorage.getItem('uuid'+key);
if (newEncryptedString) { if (newEncryptedString) {
encryptedFormData.append(key, newEncryptedString); encryptedFormData.append(key, newEncryptedString);
} }
} }
encryptStorage.encrypt('uuidtext', content);
encryptedFormData.append("text", ""+localStorage.getItem('uuidtext'))
encryptedFormData.append('_token', csrf); encryptedFormData.append('_token', csrf);
let response = await fetch(url, { const response = await fetch(url, {
method: 'POST', method: 'POST',
body: encryptedFormData body: encryptedFormData
}); });
const json = await response.json(); const json = await response.json();
const uuid = json.uuid; const uuid = json.uuid;
for (let key of decryptedFormData.keys()) { for (const key of decryptedFormData.keys()) {
let newEncryptedString = localStorage.getItem('uuid'+key); const newEncryptedString = localStorage.getItem('uuid'+key);
localStorage.setItem(uuid+key, ""+newEncryptedString); localStorage.setItem(uuid+key, ""+newEncryptedString);
localStorage.removeItem("uuid"+key); localStorage.removeItem("uuid"+key);
} }
@ -50,14 +54,10 @@ export default function PageForm({setListPages, csrf, url, passphrase}) {
if (isPassphraseSet) { if (isPassphraseSet) {
return ( return (
/* <Stack <form action={url} id="postPage" method="post" onSubmit={onSubmit}>
component="form" <Stack
sx={{ component="div"
width: '25ch',
}}
spacing={2} spacing={2}
noValidate
autoComplete="off"
> >
<TextField <TextField
label="Titre" label="Titre"
@ -67,40 +67,14 @@ export default function PageForm({setListPages, csrf, url, passphrase}) {
size="small" size="small"
/> />
<MDEditor <MDEditor
value={value} value={content}
onChange={setValue} onChange={setContent}
/> />
<MDEditor.Markdown source={value} /> <Button variant="contained" type={"submit"}>
<TextField
label="Texte"
name="content"
id="filled-hidden-label-normal"
defaultValue="Normal"
variant="outlined"
multiline
maxRows={15}
/>
<Button variant="contained" component="span" onClick={onSubmit}>
Enregistrer Enregistrer
</Button> </Button>
</Stack>*/ </Stack>
<div className="container"> </form>
<div className="row justify-content-center">
<div className="col-md-8">
<div className="card">
<form action={url} id="postPage" method="post" onSubmit={onSubmit}>
<label htmlFor="title">Titre:</label>
<input id="title" name="title"/>
<hr/>
<label htmlFor="text">Texte:</label>
<textarea id="text" name="text"/>
<hr/>
<input type="submit" value="Enregistrer"/>
</form>
</div>
</div>
</div>
</div>
); );
} }

View File

@ -1,4 +1,4 @@
import {Grid, List, Pagination} from '@mui/material'; import {Grid, Pagination} from '@mui/material';
import * as React from 'react'; import * as React from 'react';
import Page from "./Page"; import Page from "./Page";
@ -9,18 +9,53 @@ interface List {
export default function Pages({pages, url, removeUrl, csrf, passphrase, setPassphrase}) { export default function Pages({pages, url, removeUrl, csrf, passphrase, setPassphrase}) {
const isPassphraseSet = passphrase !== null; const isPassphraseSet = passphrase !== null;
const perPage = 3;
const total = pages.length;
const numberOfPage = Math.ceil(total/perPage);
const [currentPage, setPage] = React.useState(1);
const [listPages, setListPages] = React.useState(pages);
let listPages = pages.map(page => const removePage = async (id) => {
<Page page={page} url={url} setPassphrase={setPassphrase} passphrase={passphrase} csrf={csrf} removeUrl={removeUrl} key={page.id}/> const formData = new FormData();
) formData.set('_token', csrf);
formData.set('_method', 'DELETE');
const response = await fetch(removeUrl.replace("replace_me", id), {
method: 'POST', body: formData
});
const json = await response.json();
if (json.success) {
console.log(listPages.length);
const newListPages = listPages.filter(function (value) {
console.log(value.id);
return value.id !== id;
});
setListPages(newListPages);
console.log(newListPages.length);
console.log(listPages.length);
updateListPages(newListPages.slice((currentPage - 1) * perPage, ((currentPage - 1) * perPage) + perPage).map(page =>
<Page page={page} url={url} remove={removePage} setPassphrase={setPassphrase} passphrase={passphrase}
key={page.id}/>));
}
}
const [listPagesDisplayed, updateListPages] = React.useState(listPages.slice((currentPage-1) * perPage, ((currentPage-1) * perPage) + perPage ).map(page =>
<Page page={page} url={url} remove={removePage} setPassphrase={setPassphrase} passphrase={passphrase} key={page.id}/>));
const handlePageChange = (event: React.ChangeEvent<unknown>, value: number) => {
setPage(value);
updateListPages(listPages.slice((value-1) * perPage, ((value-1) * perPage) + perPage).map(page =>
<Page page={page} url={url} remove={removePage} setPassphrase={setPassphrase} passphrase={passphrase} key={page.id}/>));
};
if (isPassphraseSet) { if (isPassphraseSet) {
return ( return (
<div> <div>
<Grid container rowSpacing={1} columnSpacing={1}> <Grid container rowSpacing={1} columnSpacing={1}>
{listPages} {listPagesDisplayed}
</Grid> </Grid>
<Pagination count={10} color="primary" /> <Pagination count={numberOfPage} page={currentPage} onChange={handlePageChange} sx={{align: "center"}} color="primary" />
</div> </div>
); );
} }

View File

@ -13,7 +13,7 @@ import ExpandMoreIcon from "@mui/icons-material/ExpandMore";
import * as React from 'react'; import * as React from 'react';
import {EncryptStorage} from 'storage-encryption'; import {EncryptStorage} from 'storage-encryption';
import {Delete} from "@mui/icons-material"; import {Delete} from "@mui/icons-material";
import {unmountComponentAtNode} from "react-dom"; import MDEditor from "@uiw/react-md-editor";
interface Page { interface Page {
date: string; date: string;
@ -36,24 +36,13 @@ const ExpandMore = styled((props: ExpandMoreProps) => {
}), }),
})); }));
export default function Page({page, url, removeUrl, csrf, passphrase, setPassphrase}) { export default function Page({page, url, passphrase, setPassphrase, remove}) {
const [more, setMore] = React.useState(false); const [more, setMore] = React.useState(false);
const [expanded, setExpanded] = React.useState(false); const [expanded, setExpanded] = React.useState(false);
const handleMoreClick = () => { setMore(!more); }; const handleMoreClick = () => { setMore(!more); };
const handleExpandClick = () => { onLoad().then(r => setExpanded(!expanded)); }; const handleExpandClick = () => { onLoad().then(r => setExpanded(!expanded)); };
const remove = async () => {
const formData = new FormData();
formData.set('_token', csrf);
formData.set('_method', 'DELETE');
let response = await fetch(removeUrl.replace("replace_me", page.id), {
method: 'POST', body: formData
});
const json = await response.json(); const encryptStorage = new EncryptStorage(passphrase);
unmountComponentAtNode(document.getElementById(page.id));
};
let encryptStorage = new EncryptStorage(passphrase);
let title, content = ""; let title, content = "";
let alert_popup:JSX.Element|null = null; let alert_popup:JSX.Element|null = null;
let setTitle, setContent: React.Dispatch<any>|null = null; let setTitle, setContent: React.Dispatch<any>|null = null;
@ -63,9 +52,9 @@ export default function Page({page, url, removeUrl, csrf, passphrase, setPassphr
[title, setTitle] = React.useState(encryptStorage.decrypt(page.id + "title")); [title, setTitle] = React.useState(encryptStorage.decrypt(page.id + "title"));
onLoad = async () => { onLoad = async () => {
if (localStorage.getItem(page.id + "text") === null) { if (localStorage.getItem(page.id + "text") === null) {
let response = await fetch(url.replace("replace_me", page.id)); const response = await fetch(url.replace("replace_me", page.id));
let json = await response.json(); const json = await response.json();
localStorage.setItem(page.id + "title", json.metadata.title); localStorage.setItem(page.id + "title", json.metadata.title);
localStorage.setItem(page.id + "text", json.content); localStorage.setItem(page.id + "text", json.content);
} }
@ -83,7 +72,7 @@ export default function Page({page, url, removeUrl, csrf, passphrase, setPassphr
} }
return ( return (
<Grid item xs={12} sm={6} md={6} id={page.id}> <Grid item xs={12} sm={12} md={12} id={page.id}>
{alert_popup} {alert_popup}
<Card> <Card>
<CardHeader <CardHeader
@ -96,7 +85,7 @@ export default function Page({page, url, removeUrl, csrf, passphrase, setPassphr
subheader={page.date} subheader={page.date}
/> />
<Collapse in={more} timeout="auto" unmountOnExit> <Collapse in={more} timeout="auto" unmountOnExit>
<IconButton aria-label="remove" onClick={remove} > <IconButton aria-label="remove" onClick={() => {remove(page.id)}} >
<Delete /> <Delete />
</IconButton> </IconButton>
</Collapse> </Collapse>
@ -113,7 +102,7 @@ export default function Page({page, url, removeUrl, csrf, passphrase, setPassphr
<Collapse in={expanded} timeout="auto" unmountOnExit> <Collapse in={expanded} timeout="auto" unmountOnExit>
<CardContent> <CardContent>
<Typography paragraph> <Typography paragraph>
{content} <MDEditor.Markdown source={content} />
</Typography> </Typography>
</CardContent> </CardContent>
</Collapse> </Collapse>