diff options
| author | Armand Philippot <git@armandphilippot.com> | 2022-02-20 16:11:50 +0100 |
|---|---|---|
| committer | Armand Philippot <git@armandphilippot.com> | 2022-02-20 16:15:08 +0100 |
| commit | 73a5c7fae9ffbe9ada721148c8c454a643aceebe (patch) | |
| tree | c8fad013ed9b5dd589add87f8d45cf02bbfc6e91 /public/projects/react-small-apps/apps/notebook/src/App.js | |
| parent | b01239fbdcc5bbc5921f73ec0e8fee7bedd5c8e8 (diff) | |
chore!: restructure repo
I separated public files from the config/dev files. It improves repo
readability.
I also moved dotenv helper to public/inc directory and extract the
Matomo tracker in the same directory.
Diffstat (limited to 'public/projects/react-small-apps/apps/notebook/src/App.js')
| -rw-r--r-- | public/projects/react-small-apps/apps/notebook/src/App.js | 174 |
1 files changed, 174 insertions, 0 deletions
diff --git a/public/projects/react-small-apps/apps/notebook/src/App.js b/public/projects/react-small-apps/apps/notebook/src/App.js new file mode 100644 index 0000000..6057342 --- /dev/null +++ b/public/projects/react-small-apps/apps/notebook/src/App.js @@ -0,0 +1,174 @@ +import { Navigate, Route, Routes, useLocation } from "react-router-dom"; +import { Footer, Header, Main, Nav, Page } from "./components/layout"; +import { useCallback, useEffect, useState } from "react"; +import { defaultPages } from "./config/pages"; +import "./App.css"; + +let pageId = 0; + +function App() { + const storedPages = JSON.parse(localStorage.getItem("notebook-pages")); + const initialPages = storedPages || defaultPages; + const [pages, setPages] = useState(initialPages); + const [currentPage, setCurrentPage] = useState({}); + const [deletedPages, setDeletedPages] = useState([]); + const location = useLocation(); + + pageId = storedPages ? storedPages.at(storedPages.length - 1).id : pageId; + + const isCover = () => currentPage && currentPage.id === 0; + const isPageExists = useCallback( + (id) => { + const pageIndex = pages.findIndex((page) => page.id === id); + return pageIndex === -1 ? false : true; + }, + [pages] + ); + + const addNewPage = useCallback(() => { + pageId++; + const newPage = { + id: pageId, + body: "", + title: `Page ${pageId}`, + url: `/page/${pageId}`, + }; + setPages((previous) => [...previous, newPage]); + }, []); + + const removePage = useCallback(() => { + const currentPageId = currentPage.id; + const pagesCopy = pages.slice(0); + const currentPageIndex = pages.findIndex( + (page) => page.id === currentPageId + ); + setDeletedPages((prev) => [...prev, currentPage]); + pagesCopy.splice(currentPageIndex, 1); + const newPages = pagesCopy.map((page) => { + if (page.id <= currentPageId) return page; + const newId = page.id - 1; + const newURL = `/page/${newId}`; + return { ...page, id: newId, url: newURL }; + }); + setCurrentPage(...newPages.filter((page) => page.id === currentPageId)); + setPages(newPages); + pageId = pageId - 1; + }, [pages, currentPage]); + + const restorePage = useCallback(() => { + const deletedPage = deletedPages.pop(); + const pagesCopy = pages.slice(0); + const restoredPageIndex = pagesCopy.findIndex( + (page) => page.id === deletedPage.id + ); + const newPages = pagesCopy.map((page) => { + if (page.id < deletedPage.id) return page; + const newId = page.id + 1; + const newURL = `/page/${newId}`; + return { ...page, id: newId, url: newURL }; + }); + newPages.splice(restoredPageIndex, 0, deletedPage); + setCurrentPage(...newPages.filter((page) => page.id === deletedPage.id)); + setPages(newPages); + }, [pages, deletedPages]); + + useEffect(() => { + !isPageExists(1) && addNewPage(); + }, [isPageExists, addNewPage]); + + useEffect(() => { + const requestedPage = pages.find((page) => page.url === location.pathname); + if (requestedPage) { + setCurrentPage(requestedPage); + } else { + setCurrentPage(() => pages.find((page) => page.url === "/404")); + } + }, [location.pathname, pages]); + + useEffect(() => { + if (currentPage) document.title = `Notebook - ${currentPage.title}`; + }, [currentPage]); + + useEffect(() => { + setPages((prevPages) => { + return prevPages.map((page) => { + if (page.id !== currentPage.id) return page; + return { ...page, body: currentPage.body }; + }); + }); + }, [currentPage.id, currentPage.body]); + + useEffect(() => { + setPages((prevPages) => { + return prevPages.map((page) => { + if (page.id !== currentPage.id) return page; + return { ...page, title: currentPage.title }; + }); + }); + }, [currentPage.id, currentPage.title]); + + useEffect(() => { + localStorage.setItem("notebook-pages", JSON.stringify(pages)); + }, [pages]); + + return ( + <> + <Header /> + <Main> + <div className={`notebook ${isCover() ? "notebook--cover" : ""}`}> + <div className="notebook-page notebook-page--mirror"></div> + <Routes> + <Route + path="/" + element={ + <Page + page={currentPage} + setPage={setCurrentPage} + removePage={removePage} + restorePage={restorePage} + deletedPages={deletedPages} + /> + } + /> + <Route path="/page" element={<Navigate replace to="/404" />} /> + <Route path="/page/0" element={<Navigate replace to="/" />} /> + <Route + path="/page/:number" + element={ + <Page + page={currentPage} + setPage={setCurrentPage} + removePage={removePage} + restorePage={restorePage} + deletedPages={deletedPages} + /> + } + /> + <Route + element={ + <Page + page={currentPage} + setPage={setCurrentPage} + removePage={removePage} + restorePage={restorePage} + deletedPages={deletedPages} + /> + } + path="/404" + /> + <Route path="*" element={<Navigate replace to="/404" />} /> + </Routes> + </div> + <Nav + pages={pages} + currentPage={currentPage} + addNewPage={addNewPage} + isPageExists={isPageExists} + /> + </Main> + <Footer /> + </> + ); +} + +export default App; |
