diff --git a/source/src/pages/Page/Meta.tsx b/source/src/pages/Page/Meta.tsx index a85251b..a358093 100644 --- a/source/src/pages/Page/Meta.tsx +++ b/source/src/pages/Page/Meta.tsx @@ -6,7 +6,7 @@ import { faHourglass, } from "@fortawesome/free-solid-svg-icons" -import { FetchedPage } from "../../../types/types" +import { PageData } from "../../../types/types" import theming from "../../styles/theming" const StyledMetaContainer = styled.div` @@ -17,7 +17,7 @@ const StyledMetaContainer = styled.div` })}; ` -const Meta = (props: { fetchedPage: FetchedPage }) => { +const Meta = (props: { fetchedPage: PageData }) => { return ( diff --git a/source/src/pages/Page/index.tsx b/source/src/pages/Page/index.tsx index 5eaad4a..3957826 100644 --- a/source/src/pages/Page/index.tsx +++ b/source/src/pages/Page/index.tsx @@ -3,7 +3,7 @@ import { Helmet } from "react-helmet-async" import { useLocation } from "react-router-dom" import styled from "styled-components" -import { FetchedPage, Map } from "../../../types/types" +import { PageData, Map } from "../../../types/types" import MainContent from "../../components/MainContent" import Loading from "../../components/Loading" @@ -15,6 +15,7 @@ import NextPrevButtons from "./NextPrevButtons" import Meta from "./Meta" import Toc from "./Toc" +import portfolio from "../../data/portfolio.json" import _map from "../../data/map.json" import { useEffect } from "react" @@ -24,105 +25,219 @@ const StyledTitle = styled.h1` margin-bottom: 1rem; ` +enum PageType { + POST, + SERIES, + PORTFOLIO_PROJECT, + UNSEARCHABLE, +} + interface SeriesData { seriesHome: string prev?: string next?: string } +const fetchContent = async (pageType: PageType, url: string) => { + try { + if (pageType == PageType.UNSEARCHABLE) { + return await import(`../../data/content/unsearchable${url}.json`) + } + + return await import(`../../data/content${url}.json`) + } catch (err) { + return + } +} + +const categorizePageType = (url: string): PageType => { + if (url.startsWith("/post")) return PageType.POST + if (url.startsWith("/series")) return PageType.SERIES + if (url.startsWith("/portfolio")) return PageType.PORTFOLIO_PROJECT + + return PageType.UNSEARCHABLE +} + const Page = () => { - const [fetchedPage, setFetchPage] = useState( - undefined - ) - const [isPageUnsearchable, setIsPageUnsearchable] = useState(false) - const [isSeries, setIsSeries] = useState(false) + const location = useLocation() + + const [pageData, setPageData] = useState(undefined) + const [pageType, setPageType] = useState(PageType.POST) + const [isLoading, setIsLoading] = useState(true) + + // only used when the page is a series post + // todo: merge with pageData const [seriesData, setSeriesData] = useState( undefined ) - const [isLoading, setIsLoading] = useState(true) - const location = useLocation() - - const fetchContent = async ( - isContentUnsearchable: boolean, - url: string - ) => { - return isContentUnsearchable - ? await import(`../../data/content/unsearchable${url}.json`) - : await import(`../../data/content${url}.json`) - } useEffect(() => { - let _isSeries = false const url = location.pathname.replace(/\/$/, "") // remove trailing slash - if (url.startsWith("/series")) _isSeries = true + const _pageType = categorizePageType(url) - if (_isSeries) { - const seriesURL = url.slice(0, url.lastIndexOf("/")) - if (seriesURL in map.series) { - const _curr: number = map.series[seriesURL].order.indexOf(url) - const _prev = _curr - 1 - const _next = _curr + 1 + /** + * Test if url is a valid one + */ - setSeriesData({ - seriesHome: seriesURL, - prev: - _prev >= 0 - ? map.series[seriesURL].order[_prev] - : undefined, - next: - _next < map.series[seriesURL].order.length - ? map.series[seriesURL].order[_next] - : undefined, - }) + let show404 = false + switch (_pageType) { + case PageType.POST: { + if (!map.posts[url]) show404 = true + + break + } + + case PageType.SERIES: { + if (!(url.slice(0, url.lastIndexOf("/")) in map.series)) + show404 = true + + break + } + + case PageType.PORTFOLIO_PROJECT: { + if (!(url in portfolio.projects)) show404 = true + + break + } + + case PageType.UNSEARCHABLE: { + if (!map.unsearchable[url]) show404 = true + + break } } - // fetch page - const MapPost = map.posts[url] - const fetchedPage: FetchedPage = { - ...MapPost, - toc: undefined, - content: "", - tags: [] as string[], + if (show404) { + setIsLoading(false) + return } - let _isUnsearchable = false - if (!MapPost) { - _isUnsearchable = true - setIsPageUnsearchable(_isUnsearchable) + /** + * Get page data + */ - if (!map.unsearchable[url]) { + const pageData: PageData = { + title: "No title", + date: "Unknown date", + readTime: "Unknown read time", + wordCount: 0, + tags: [], + toc: undefined, + content: "No content", + } + + fetchContent(_pageType, url).then((fetched_content) => { + if (!fetched_content) { setIsLoading(false) return } - } - fetchContent(_isUnsearchable, url).then((fetched_content) => { - fetchedPage.content = fetched_content.content || "No content" - fetchedPage.toc = fetched_content.toc - fetchedPage.title = _isUnsearchable - ? map.unsearchable[url].title - : fetchedPage?.title || "No title" + switch (_pageType) { + case PageType.POST: { + const post = map.posts[url] - if (!_isUnsearchable) - fetchedPage.date = fetchedPage?.date || "Unknown date" + pageData.content = fetched_content.content + pageData.toc = fetched_content.toc + + pageData.title = post.title + pageData.date = post.date + pageData.readTime = post.readTime + pageData.wordCount = post.wordCount + pageData.tags = post.tags || [] + + break + } + + case PageType.SERIES: { + const seriesURL = url.slice(0, url.lastIndexOf("/")) + + const curr = map.series[seriesURL].order.indexOf(url) + const prev = curr - 1 + const next = curr + 1 + + setSeriesData({ + seriesHome: seriesURL, + prev: + prev >= 0 + ? map.series[seriesURL].order[prev] + : undefined, + next: + next < map.series[seriesURL].order.length + ? map.series[seriesURL].order[next] + : undefined, + }) + + const post = map.posts[url] + + pageData.content = fetched_content.content + pageData.toc = fetched_content.toc + + pageData.title = post.title + pageData.date = post.date + pageData.readTime = post.readTime + pageData.wordCount = post.wordCount + pageData.tags = post.tags || [] + + break + } + + case PageType.PORTFOLIO_PROJECT: { + const data = + portfolio.projects[ + url as keyof typeof portfolio.projects + ] + + console.log(fetched_content) + + pageData.content = fetched_content.content + pageData.toc = fetched_content.toc + + pageData.title = data.name + + // todo: add portfolio data + /* + "image": "/img/portfolio/developomp.com.png", + "overview": "my website for blogging, portfolio, and resume.", + "badges": [ + "typescript", + "javascript", + "nodedotjs", + "firebase", + "react", + "html5", + "css3" + ], + "repo": "https://github.com/developomp/developomp-site" + */ + + break + } + + case PageType.UNSEARCHABLE: { + pageData.title = map.unsearchable[url].title + pageData.content = fetched_content.content + + break + } + } + + setPageType(_pageType) + setPageData(pageData) - setIsSeries(_isSeries) - setFetchPage(fetchedPage) setIsLoading(false) }) }, [location]) if (isLoading) return - if (!fetchedPage) return + if (!pageData) return return ( <> - pomp | {fetchedPage.title} + pomp | {pageData.title} - + { - {isSeries ? ( + {pageType == PageType.SERIES ? ( { ) : (
)} - {fetchedPage.title} + {pageData.title} {/* Post tags */} - {!!fetchedPage.tags.length && ( + {!!pageData.tags.length && ( - {fetchedPage.tags.map((tag) => { + {pageData.tags.map((tag) => { return ( - + ) @@ -158,18 +273,20 @@ const Page = () => {
{/* Post metadata */} - {!isPageUnsearchable && } + {pageType != PageType.UNSEARCHABLE && ( + + )}

{/* add table of contents if it exists */} - + {/* page content */}
diff --git a/source/types/types.ts b/source/types/types.ts index 7262148..4b35bc7 100644 --- a/source/types/types.ts +++ b/source/types/types.ts @@ -53,7 +53,7 @@ export interface PostData { tags?: string[] } -export interface FetchedPage { +export interface PageData { title: string date: string readTime: string