added series home (#37)

This commit is contained in:
Kim, Jimin 2022-01-17 11:44:37 +09:00
parent 618c42d436
commit 21a48cc351
4 changed files with 117 additions and 25 deletions

View file

@ -3,6 +3,7 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"
import { import {
faBook, faBook,
faCalendar, faCalendar,
faFile,
faHourglass, faHourglass,
} from "@fortawesome/free-solid-svg-icons" } from "@fortawesome/free-solid-svg-icons"
@ -20,18 +21,28 @@ const StyledMetaContainer = styled.div`
const Meta = (props: { fetchedPage: PageData }) => { const Meta = (props: { fetchedPage: PageData }) => {
return ( return (
<StyledMetaContainer> <StyledMetaContainer>
{/* posts count */}
<FontAwesomeIcon icon={faFile} />
&nbsp;&nbsp;
{props.fetchedPage.length
? props.fetchedPage.length + " posts"
: "no posts"}
&nbsp;&nbsp;&nbsp;&nbsp;
{/* date */}
<FontAwesomeIcon icon={faCalendar} /> <FontAwesomeIcon icon={faCalendar} />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;
{props.fetchedPage.date || "Unknown date"} {props.fetchedPage.date || "Unknown date"}
&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;
{/* read time */}
<FontAwesomeIcon icon={faHourglass} /> <FontAwesomeIcon icon={faHourglass} />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;
{props.fetchedPage.readTime {props.fetchedPage.readTime
? props.fetchedPage.readTime + " read" ? props.fetchedPage.readTime + " read"
: "unknown length"} : "unknown length"}
&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;
{/* word count */}
<FontAwesomeIcon icon={faBook} /> <FontAwesomeIcon icon={faBook} />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;
{props.fetchedPage.wordCount {props.fetchedPage.wordCount
? props.fetchedPage.wordCount + " words" ? props.fetchedPage.wordCount + " words"
: "unknown words"} : "unknown words"}

View file

@ -2,14 +2,20 @@ import styled from "styled-components"
import { Link } from "react-router-dom" import { Link } from "react-router-dom"
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome" import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"
import { faArrowLeft, faArrowRight } from "@fortawesome/free-solid-svg-icons" import {
faArrowLeft,
faArrowRight,
faListUl,
} from "@fortawesome/free-solid-svg-icons"
import theming from "../../styles/theming" import theming from "../../styles/theming"
const StyledNextPrevContainer = styled.div` const StyledContainer = styled.div`
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
size: 100%; size: 100%;
line-height: 1rem;
` `
const StyledLink = styled(Link)` const StyledLink = styled(Link)`
@ -23,7 +29,6 @@ const StyledLink = styled(Link)`
height: 1rem; height: 1rem;
width: 2rem; width: 2rem;
margin-top: 2rem;
line-height: 1rem; line-height: 1rem;
text-align: center; text-align: center;
@ -45,16 +50,19 @@ const StyledDisabledLink = styled.div`
height: 1rem; height: 1rem;
width: 2rem; width: 2rem;
margin-top: 2rem;
line-height: 1rem; line-height: 1rem;
text-align: center; text-align: center;
user-select: none; user-select: none;
` `
const NextPrevButtons = (props: { prevURL?: string; nextURL?: string }) => { const SeriesControlButtons = (props: {
seriesHome: string
prevURL?: string
nextURL?: string
}) => {
return ( return (
<StyledNextPrevContainer> <StyledContainer>
{props.prevURL ? ( {props.prevURL ? (
<StyledLink to={props.prevURL}> <StyledLink to={props.prevURL}>
<FontAwesomeIcon icon={faArrowLeft} /> <FontAwesomeIcon icon={faArrowLeft} />
@ -64,6 +72,11 @@ const NextPrevButtons = (props: { prevURL?: string; nextURL?: string }) => {
<FontAwesomeIcon icon={faArrowLeft} /> <FontAwesomeIcon icon={faArrowLeft} />
</StyledDisabledLink> </StyledDisabledLink>
)} )}
<StyledLink to={props.seriesHome}>
<FontAwesomeIcon icon={faListUl} />
</StyledLink>
{props.nextURL ? ( {props.nextURL ? (
<StyledLink to={props.nextURL}> <StyledLink to={props.nextURL}>
<FontAwesomeIcon icon={faArrowRight} /> <FontAwesomeIcon icon={faArrowRight} />
@ -73,8 +86,8 @@ const NextPrevButtons = (props: { prevURL?: string; nextURL?: string }) => {
<FontAwesomeIcon icon={faArrowRight} /> <FontAwesomeIcon icon={faArrowRight} />
</StyledDisabledLink> </StyledDisabledLink>
)} )}
</StyledNextPrevContainer> </StyledContainer>
) )
} }
export default NextPrevButtons export default SeriesControlButtons

View file

@ -7,13 +7,14 @@ import { PageData, Map } from "../../../types/types"
import GithubLinkIcon from "../../components/GithubLinkIcon" import GithubLinkIcon from "../../components/GithubLinkIcon"
import MainContent from "../../components/MainContent" import MainContent from "../../components/MainContent"
import PostCard from "../../components/PostCard"
import Loading from "../../components/Loading" import Loading from "../../components/Loading"
import TagList from "../../components/TagList" import TagList from "../../components/TagList"
import Badge from "../../components/Badge" import Badge from "../../components/Badge"
import Tag from "../../components/Tag" import Tag from "../../components/Tag"
import NotFound from "../NotFound" import NotFound from "../NotFound"
import NextPrevButtons from "./NextPrevButtons" import SeriesControlButtons from "./SeriesControlButtons"
import Meta from "./Meta" import Meta from "./Meta"
import Toc from "./Toc" import Toc from "./Toc"
@ -47,6 +48,7 @@ const ProjectImage = styled.img`
enum PageType { enum PageType {
POST, POST,
SERIES, SERIES,
SERIES_HOME,
PORTFOLIO_PROJECT, PORTFOLIO_PROJECT,
UNSEARCHABLE, UNSEARCHABLE,
} }
@ -65,7 +67,15 @@ const fetchContent = async (pageType: PageType, url: string) => {
const categorizePageType = (url: string): PageType => { const categorizePageType = (url: string): PageType => {
if (url.startsWith("/post")) return PageType.POST if (url.startsWith("/post")) return PageType.POST
if (url.startsWith("/series")) return PageType.SERIES if (url.startsWith("/series")) {
if ([...(url.match(/\//g) || [])].length == 2) {
// url: /series/series-title
return PageType.SERIES_HOME
} else {
// url: /series/series-title/post-title
return PageType.SERIES
}
}
if (url.startsWith("/portfolio")) return PageType.PORTFOLIO_PROJECT if (url.startsWith("/portfolio")) return PageType.PORTFOLIO_PROJECT
return PageType.UNSEARCHABLE return PageType.UNSEARCHABLE
@ -80,22 +90,25 @@ const Page = () => {
useEffect(() => { useEffect(() => {
const url = location.pathname.replace(/\/$/, "") // remove trailing slash const url = location.pathname.replace(/\/$/, "") // remove trailing slash
const _pageType = categorizePageType(url) const pageType = categorizePageType(url)
/** /**
* Test if url is a valid one * Test if url is a valid one
*/ */
let show404 = false let show404 = false
switch (_pageType) { switch (pageType) {
case PageType.POST: { case PageType.POST: {
if (!map.posts[url]) show404 = true if (!map.posts[url]) show404 = true
break break
} }
case PageType.SERIES_HOME:
case PageType.SERIES: { case PageType.SERIES: {
if (!(url.slice(0, url.lastIndexOf("/")) in map.series)) show404 = true show404 = !Object.keys(map.series).some((seriesHomeURL) =>
url.startsWith(seriesHomeURL)
)
break break
} }
@ -131,7 +144,18 @@ const Page = () => {
toc: undefined, toc: undefined,
content: "No content", content: "No content",
// series
seriesHome: "", seriesHome: "",
prev: "",
next: "",
// series home
order: [],
length: 0,
// portfolio
image: "", image: "",
overview: "", overview: "",
@ -139,13 +163,13 @@ const Page = () => {
repo: "", repo: "",
} }
fetchContent(_pageType, url).then((fetched_content) => { fetchContent(pageType, url).then((fetched_content) => {
if (!fetched_content) { if (!fetched_content) {
setIsLoading(false) setIsLoading(false)
return return
} }
switch (_pageType) { switch (pageType) {
case PageType.POST: { case PageType.POST: {
const post = map.posts[url] const post = map.posts[url]
@ -190,6 +214,21 @@ const Page = () => {
break break
} }
case PageType.SERIES_HOME: {
const seriesData = map.series[url]
pageData.title = seriesData.title
pageData.content = fetched_content.content
pageData.date = seriesData.date
pageData.readTime = seriesData.readTime
pageData.wordCount = seriesData.wordCount
pageData.order = seriesData.order
pageData.length = seriesData.length
break
}
case PageType.PORTFOLIO_PROJECT: { case PageType.PORTFOLIO_PROJECT: {
const data = const data =
portfolio.projects[url as keyof typeof portfolio.projects] portfolio.projects[url as keyof typeof portfolio.projects]
@ -214,7 +253,11 @@ const Page = () => {
} }
} }
setPageType(_pageType) /**
* Apply result
*/
setPageType(pageType)
setPageData(pageData) setPageData(pageData)
setIsLoading(false) setIsLoading(false)
@ -241,7 +284,11 @@ const Page = () => {
<MainContent> <MainContent>
{/* next/previous series post buttons */} {/* next/previous series post buttons */}
{pageType == PageType.SERIES && ( {pageType == PageType.SERIES && (
<NextPrevButtons prevURL={pageData.prev} nextURL={pageData.next} /> <SeriesControlButtons
seriesHome={pageData.seriesHome}
prevURL={pageData.prev}
nextURL={pageData.next}
/>
)} )}
{pageType == PageType.PORTFOLIO_PROJECT && pageData.repo && ( {pageType == PageType.PORTFOLIO_PROJECT && pageData.repo && (
@ -273,9 +320,9 @@ const Page = () => {
<br /> <br />
{/* Post metadata */} {/* Post metadata */}
{[PageType.POST, PageType.SERIES].includes(pageType) && ( {[PageType.POST, PageType.SERIES, PageType.SERIES_HOME].includes(
<Meta fetchedPage={pageData} /> pageType
)} ) && <Meta fetchedPage={pageData} />}
</small> </small>
<hr /> <hr />
@ -294,6 +341,21 @@ const Page = () => {
}} }}
/> />
</MainContent> </MainContent>
{/* series post list */}
{pageType == PageType.SERIES_HOME &&
pageData.order.map((post) => {
return (
<PostCard
key={post}
postData={{
url: post,
...map.posts[post],
}}
/>
)
})}
</> </>
) )
} }

View file

@ -62,12 +62,18 @@ export interface PageData {
toc?: string toc?: string
content: string content: string
// series-specific data // series
seriesHome: string seriesHome: string
prev?: string prev?: string
next?: string next?: string
// portfolio-specific data // series home
order: string[]
length: number
// portfolio
image: string // image url image: string // image url
overview: string overview: string