Skip to content

Commit

Permalink
Fixed #322 and added simple markdown publishing system.
Browse files Browse the repository at this point in the history
  • Loading branch information
jeffdc committed Feb 22, 2022
1 parent 1d6c09f commit 722026e
Show file tree
Hide file tree
Showing 15 changed files with 387 additions and 69 deletions.
10 changes: 5 additions & 5 deletions components/imageedit.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -134,13 +134,13 @@ const ImageEdit = ({ image, show, onSave, onClose }: Props): JSX.Element => {
<Modal.Title>Edit Image Details</Modal.Title>
</Modal.Header>
<Modal.Body>
<Row>
<Col xs={3} className="ms-2">
<Image src={selected.small} layout="fill" objectFit="contain" objectPosition="top" />
<Row className="">
<Col xs={4} className="">
<img src={selected.small} width="200px" />
</Col>
<Col className="my-1">
<Col xs={7} className="my-1">
<Row>
<Col xs={3}>
<Col>
Default:
<InfoTip
id="default"
Expand Down
12 changes: 12 additions & 0 deletions components/ref/dateformatter.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { parseISO, format } from 'date-fns'

type Props = {
dateString: string
}

const DateFormatter = ({ dateString }: Props) => {
const date = parseISO(dateString)
return <time dateTime={dateString}>{format(date, 'LLLL d, yyyy')}</time>
}

export default DateFormatter
18 changes: 18 additions & 0 deletions components/ref/markdown-styles.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
.markdown {
@apply text-lg leading-relaxed;
}

.markdown p,
.markdown ul,
.markdown ol,
.markdown blockquote {
@apply my-6;
}

.markdown h2 {
@apply text-3xl mt-12 mb-4 leading-snug;
}

.markdown h3 {
@apply text-2xl mt-8 mb-4 leading-snug;
}
18 changes: 18 additions & 0 deletions components/ref/postBody.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import markdownStyles from './markdown-styles.module.css'

type Props = {
content: string
}

const PostBody = ({ content }: Props) => {
return (
<div className="">
<div
className={markdownStyles['markdown']}
dangerouslySetInnerHTML={{ __html: content }}
/>
</div>
)
}

export default PostBody
1 change: 1 addition & 0 deletions layouts/header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ const Header = (): JSX.Element => {
<NavDropdown.Item href="/guide">ID Guide</NavDropdown.Item>
<NavDropdown.Item href="/filterguide">Filter Terms</NavDropdown.Item>
<NavDropdown.Item href="/glossary">Glossary</NavDropdown.Item>
<NavDropdown.Item href="/refindex">Reference</NavDropdown.Item>
</NavDropdown>
</Nav>
</Navbar.Collapse>
Expand Down
7 changes: 7 additions & 0 deletions libs/pages/mdtoHtml.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { remark } from 'remark'
import html from 'remark-html'

export default async function markdownToHtml(markdown: string) {
const result = await remark().use(html).process(markdown)
return result.toString()
}
47 changes: 47 additions & 0 deletions libs/pages/refposts.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import fs from 'fs'
import { join } from 'path'
import matter from 'gray-matter'

const postsDirectory = join(process.cwd(), 'ref')

export function getPostSlugs() {
return fs.readdirSync(postsDirectory)
}

export function getPostBySlug(slug: string, fields: string[] = []) {
const realSlug = slug.replace(/\.md$/, '')
const fullPath = join(postsDirectory, `${realSlug}.md`)
const fileContents = fs.readFileSync(fullPath, 'utf8')
const { data, content } = matter(fileContents)

type Items = {
[key: string]: string
}

const items: Items = {}

// Ensure only the minimal needed data is exposed
fields.forEach((field) => {
if (field === 'slug') {
items[field] = realSlug
}
if (field === 'content') {
items[field] = content
}

if (typeof data[field] !== 'undefined') {
items[field] = data[field]
}
})

return items
}

export function getAllPosts(fields: string[] = []) {
const slugs = getPostSlugs()
const posts = slugs
.map((slug) => getPostBySlug(slug, fields))
// sort posts by date in descending order
.sort((post1, post2) => (post1.date > post2.date ? -1 : 1))
return posts
}
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,9 @@
"axios": "^0.26.0",
"bootstrap": "^5.1.3",
"classnames": "^2.3.1",
"date-fns": "^2.28.0",
"fp-ts": "^2.11.8",
"gray-matter": "^4.0.3",
"io-ts": "^2.2.16",
"jimp": "^0.16.1",
"jquery": "^3.6.0",
Expand Down
5 changes: 4 additions & 1 deletion pages/about.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -117,12 +117,15 @@ const About = ({ stats, genTime }: Props): JSX.Element => {
<a href="https://www.inaturalist.org/people/joshuacde">Joshua C&apos;deBaca</a>
</li>
<li>
<a href="https://www.inaturalist.org/people/kemper">Yann Kemper</a>
<a href="https://www.inaturalist.org/people/calconey">Tim Frey</a>
</li>
</ul>
</Col>
<Col>
<ul>
<li>
<a href="https://www.inaturalist.org/people/kemper">Yann Kemper</a>
</li>
<li>
<a href="https://www.inaturalist.org/people/kimberlietx">Kimberlie Sasan</a>
</li>
Expand Down
80 changes: 80 additions & 0 deletions pages/ref/[slug].tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import { GetStaticPaths } from 'next';
import ErrorPage from 'next/error';
import Head from 'next/head';
import { useRouter } from 'next/router';
import DateFormatter from '../../components/ref/dateformatter';
import PostBody from '../../components/ref/postBody';
import markdownToHtml from '../../libs/pages/mdtoHtml';
import { getAllPosts, getPostBySlug } from '../../libs/pages/refposts';
import PostType from '../../types/post';

type Props = {
post: PostType;
morePosts: PostType[];
preview?: boolean;
};

const Post = ({ post, morePosts, preview }: Props) => {
const router = useRouter();
if (!router.isFallback && !post?.slug) {
return <ErrorPage statusCode={404} />;
}
return (
<>
{router.isFallback ? (
<h4>Loading…</h4>
) : (
<>
<article className="m-4">
<Head>
<title>{post.title}</title>
</Head>
<h1 className="my-2">{post.title}</h1>
<strong>
<DateFormatter dateString={post.date} /> - {post.author.name}
</strong>
<hr />
<PostBody content={post.content} />
</article>
</>
)}
</>
);
};

export default Post;

type Params = {
params: {
slug: string;
};
};

export async function getStaticProps({ params }: Params) {
const post = getPostBySlug(params.slug, ['title', 'date', 'slug', 'author', 'content']);
const content = await markdownToHtml(post.content || '');

return {
props: {
post: {
...post,
content,
},
},
};
}

export const getStaticPaths: GetStaticPaths = async () => {
const posts = getAllPosts(['slug']);

return {
paths: posts.map((post) => {
return {
params: {
slug: post.slug,
},
};
}),
fallback: false,
};
};
41 changes: 41 additions & 0 deletions pages/refindex.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import Head from 'next/head';
import Link from 'next/link';
import { Container } from 'react-bootstrap';
import { getAllPosts } from '../libs/pages/refposts';
import Post from '../types/post';

type Props = {
allPosts: Post[];
};

const Index = ({ allPosts }: Props) => {
return (
<>
<Head>
<title>Gallformers Reference Library</title>
</Head>
<Container className="mx-0 mt-4">
<h1 className="my-4">The Gallformers Reference Library</h1>
{allPosts
.sort((a, b) => a.title.localeCompare(b.title))
.map((p) => (
<div key={p.slug} className="my-2">
<Link href={`/ref/${p.slug}`}>
<a>{p.title}</a>
</Link>
</div>
))}
</Container>
</>
);
};

export default Index;

export const getStaticProps = async () => {
const allPosts = getAllPosts(['title', 'date', 'slug', 'author']);

return {
props: { allPosts },
};
};
Loading

0 comments on commit 722026e

Please sign in to comment.