diff options
-rw-r--r-- | components/button.tsx | 6 | ||||
-rw-r--r-- | components/chapters.tsx | 64 | ||||
-rw-r--r-- | components/image.tsx | 14 | ||||
-rw-r--r-- | components/navbar.tsx | 40 | ||||
-rw-r--r-- | components/seperator.tsx | 16 | ||||
-rw-r--r-- | components/tag.tsx | 26 | ||||
-rw-r--r-- | dprint.json | 32 | ||||
-rw-r--r-- | package.json | 46 | ||||
-rw-r--r-- | pages/_app.tsx | 13 | ||||
-rw-r--r-- | pages/index.tsx | 72 | ||||
-rw-r--r-- | pages/post/[id].tsx | 123 | ||||
-rw-r--r-- | pages/search.tsx | 165 | ||||
-rw-r--r-- | posts/gert.md | 49 | ||||
-rw-r--r-- | posts/index.md | 49 | ||||
-rw-r--r-- | posts/po4-api.md | 105 | ||||
-rw-r--r-- | posts/po4-voerbak.md | 44 | ||||
-rw-r--r-- | posts/po4.md | 79 | ||||
-rw-r--r-- | tsconfig.json | 54 |
18 files changed, 546 insertions, 451 deletions
diff --git a/components/button.tsx b/components/button.tsx index 452d85f..23201aa 100644 --- a/components/button.tsx +++ b/components/button.tsx @@ -3,7 +3,7 @@ export default function Button(props: { href?: string; onclick?: () => void; }) { - return props.href ? - <a href={props.href} className="button">{props.text}</a> : - <button onClick={props.onclick} className="button">{props.text}</button> + return props.href + ? <a href={props.href} className='button'>{props.text}</a> + : <button onClick={props.onclick} className='button'>{props.text}</button>; } diff --git a/components/chapters.tsx b/components/chapters.tsx index facf1e1..62d8285 100644 --- a/components/chapters.tsx +++ b/components/chapters.tsx @@ -1,9 +1,9 @@ -import { ReactNode, useState, CSSProperties } from 'react'; +import { CSSProperties, ReactNode, useState } from 'react'; import { NavbarItem } from '../components/navbar'; -import RemoveRoundedIcon from '@material-ui/icons/RemoveRounded'; import KeyboardArrowDownRoundedIcon from '@material-ui/icons/KeyboardArrowDownRounded'; +import RemoveRoundedIcon from '@material-ui/icons/RemoveRounded'; export interface chapter { name: string; @@ -16,50 +16,48 @@ function NavbarChapter(props: { chapter: chapter; children?: ReactNode; }) { - var [ collapsed, setCollapsed ] = useState(true); + var [collapsed, setCollapsed] = useState(true); - var icon = <div className={ "collapseIcon" + (collapsed ? "" : " collapsed") }> - { - props.chapter.children?.length > 0 ? - <KeyboardArrowDownRoundedIcon/> : - <RemoveRoundedIcon/> - } - </div> + var icon = <div className={'collapseIcon' + (collapsed ? '' : ' collapsed')}> + {props.chapter.children?.length > 0 + ? <KeyboardArrowDownRoundedIcon /> + : <RemoveRoundedIcon />} + </div>; var classes = [ - "chapter", - `indentLevel${props.level}` - ] - !collapsed && classes.push("childrenCollapsed"); + 'chapter', + `indentLevel${props.level}`, + ]; + !collapsed && classes.push('childrenCollapsed'); var outercss = /* { "--children-height": 0 + "px" } */ {} as CSSProperties; return <NavbarItem - icon={icon} - classList={classes} - title={props.chapter.name} - onIconClick={() => props.chapter.children?.length > 0 && setCollapsed(!collapsed)} - href={props.chapter.sectionLink} - key={(() => Math.round(Math.random() * 1e12))()} - style={{ - marginLeft: 12 * props.level, - }} outerStyle={outercss}> + icon={icon} + classList={classes} + title={props.chapter.name} + onIconClick={() => props.chapter.children?.length > 0 && setCollapsed(!collapsed)} + href={props.chapter.sectionLink} + key={(() => Math.round(Math.random() * 1e12))()} + style={{ + marginLeft: 12 * props.level, + }} + outerStyle={outercss} + > {props.children} - </NavbarItem> + </NavbarItem>; } class Chapter { constructor(public chapters: Array<chapter>, public level: number) {} render() { - return <div className="chapterChildren"> - { - this.chapters?.map(chapter => { - return <NavbarChapter level={this.level} chapter={chapter}> - { new Chapter(chapter.children, this.level + 1).render() } - </NavbarChapter> - }) - } - </div> + return <div className='chapterChildren'> + {this.chapters?.map(chapter => { + return <NavbarChapter level={this.level} chapter={chapter}> + {new Chapter(chapter.children, this.level + 1).render()} + </NavbarChapter>; + })} + </div>; } } diff --git a/components/image.tsx b/components/image.tsx index ad84ff6..5a8b2cf 100644 --- a/components/image.tsx +++ b/components/image.tsx @@ -2,12 +2,10 @@ export default function Image(props: { src: string; alt?: string; }) { - return <div className="image"> - <img src={props.src} alt={props.alt}/> - { - props.alt && <div> - <p>{props.alt}</p> - </div> - } - </div> + return <div className='image'> + <img src={props.src} alt={props.alt} /> + {props.alt && <div> + <p>{props.alt}</p> + </div>} + </div>; } diff --git a/components/navbar.tsx b/components/navbar.tsx index 9a78eec..35949f4 100644 --- a/components/navbar.tsx +++ b/components/navbar.tsx @@ -1,4 +1,4 @@ -import { ReactNode, CSSProperties } from 'react'; +import { CSSProperties, ReactNode } from 'react'; import HomeRoundedIcon from '@material-ui/icons/HomeRounded'; import SearchRoundedIcon from '@material-ui/icons/SearchRounded'; @@ -16,15 +16,15 @@ export function NavbarItem(props: { onClick?: () => void; }) { var classes = props.classList || []; - classes.push("navbarItem"); - props.active && classes.push("active"); - return <a href={props.href} className={classes.join(" ")} style={props.outerStyle}> - <div className="inner" style={props.style}> - <div className="icon" onClick={props.onIconClick}>{props.icon}</div> - <span className="title" onClick={props.onClick}>{props.title}</span> + classes.push('navbarItem'); + props.active && classes.push('active'); + return <a href={props.href} className={classes.join(' ')} style={props.outerStyle}> + <div className='inner' style={props.style}> + <div className='icon' onClick={props.onIconClick}>{props.icon}</div> + <span className='title' onClick={props.onClick}>{props.title}</span> </div> {props.children} - </a> + </a>; } export default function Navbar(props: { @@ -32,16 +32,18 @@ export default function Navbar(props: { }) { return <div style={{ marginBottom: 24 }}> <NavbarItem - active={props.page == "home"} - icon={<HomeRoundedIcon/>} - title="Home" - href="/" - classList={["indentLevel0", "link"]}/> + active={props.page == 'home'} + icon={<HomeRoundedIcon />} + title='Home' + href='/' + classList={['indentLevel0', 'link']} + /> <NavbarItem - active={props.page == "search"} - icon={<SearchRoundedIcon/>} - title="Search for posts" - href="/search" - classList={["indentLevel0", "link"]}/> - </div> + active={props.page == 'search'} + icon={<SearchRoundedIcon />} + title='Search for posts' + href='/search' + classList={['indentLevel0', 'link']} + /> + </div>; } diff --git a/components/seperator.tsx b/components/seperator.tsx index 5f05aca..f1843a7 100644 --- a/components/seperator.tsx +++ b/components/seperator.tsx @@ -1,8 +1,12 @@ export default function ArticleSeperator() { - return <div className="seperator"> - <svg width="96" height="12" viewBox="0 0 96 12" fill="none" xmlns="http://www.w3.org/2000/svg"> - <path fillRule="evenodd" clipRule="evenodd" d="M8.04282 8.19986C6.51226 9.73171 4.24587 12 0 12V6C1.68094 6 2.4102 5.34716 3.87868 3.87868C3.90463 3.85273 3.9308 3.82654 3.95718 3.80014C5.48774 2.26829 7.75413 0 12 0C16.2459 0 18.5123 2.26829 20.0428 3.80014C20.0692 3.82654 20.0954 3.85273 20.1213 3.87868C21.5898 5.34716 22.3191 6 24 6C25.6809 6 26.4102 5.34716 27.8787 3.87868C27.9046 3.85273 27.9308 3.82654 27.9572 3.80014C29.4877 2.26829 31.7541 0 36 0C40.2459 0 42.5123 2.26829 44.0428 3.80014C44.0692 3.82654 44.0954 3.85273 44.1213 3.87868C45.5898 5.34716 46.3191 6 48 6C49.6809 6 50.4102 5.34716 51.8787 3.87868C51.9046 3.85273 51.9308 3.82654 51.9572 3.80014C53.4877 2.26829 55.7541 0 60 0C64.2459 0 66.5123 2.26829 68.0428 3.80014C68.0692 3.82654 68.0954 3.85273 68.1213 3.87868C69.5898 5.34716 70.3191 6 72 6C73.6809 6 74.4102 5.34716 75.8787 3.87868L75.9572 3.80014C77.4877 2.26829 79.7541 0 84 0C88.2459 0 90.5123 2.26829 92.0428 3.80014L92.1213 3.87868C93.5898 5.34716 94.3191 6 96 6V12C91.7541 12 89.4877 9.73171 87.9572 8.19986L87.8787 8.12132C86.4102 6.65284 85.6809 6 84 6C82.3191 6 81.5898 6.65284 80.1213 8.12132L80.0428 8.19986C78.5123 9.73171 76.2459 12 72 12C67.7541 12 65.4877 9.73171 63.9572 8.19986C63.9308 8.17346 63.9046 8.14727 63.8787 8.12132C62.4102 6.65284 61.6809 6 60 6C58.3191 6 57.5898 6.65284 56.1213 8.12132C56.0954 8.14727 56.0692 8.17346 56.0428 8.19986C54.5123 9.73171 52.2459 12 48 12C43.7541 12 41.4877 9.73171 39.9572 8.19986C39.9308 8.17346 39.9046 8.14727 39.8787 8.12132C38.4102 6.65284 37.6809 6 36 6C34.3191 6 33.5898 6.65284 32.1213 8.12132C32.0954 8.14727 32.0692 8.17346 32.0428 8.19986C30.5123 9.73171 28.2459 12 24 12C19.7541 12 17.4877 9.73171 15.9572 8.19986C15.9308 8.17346 15.9046 8.14727 15.8787 8.12132C14.4102 6.65284 13.6809 6 12 6C10.3191 6 9.5898 6.65284 8.12132 8.12132C8.09537 8.14727 8.0692 8.17346 8.04282 8.19986Z" fill="var(--fire-opal)"/> -</svg> - </div> + return <div className='seperator'> + <svg width='96' height='12' viewBox='0 0 96 12' fill='none' xmlns='http://www.w3.org/2000/svg'> + <path + fillRule='evenodd' + clipRule='evenodd' + d='M8.04282 8.19986C6.51226 9.73171 4.24587 12 0 12V6C1.68094 6 2.4102 5.34716 3.87868 3.87868C3.90463 3.85273 3.9308 3.82654 3.95718 3.80014C5.48774 2.26829 7.75413 0 12 0C16.2459 0 18.5123 2.26829 20.0428 3.80014C20.0692 3.82654 20.0954 3.85273 20.1213 3.87868C21.5898 5.34716 22.3191 6 24 6C25.6809 6 26.4102 5.34716 27.8787 3.87868C27.9046 3.85273 27.9308 3.82654 27.9572 3.80014C29.4877 2.26829 31.7541 0 36 0C40.2459 0 42.5123 2.26829 44.0428 3.80014C44.0692 3.82654 44.0954 3.85273 44.1213 3.87868C45.5898 5.34716 46.3191 6 48 6C49.6809 6 50.4102 5.34716 51.8787 3.87868C51.9046 3.85273 51.9308 3.82654 51.9572 3.80014C53.4877 2.26829 55.7541 0 60 0C64.2459 0 66.5123 2.26829 68.0428 3.80014C68.0692 3.82654 68.0954 3.85273 68.1213 3.87868C69.5898 5.34716 70.3191 6 72 6C73.6809 6 74.4102 5.34716 75.8787 3.87868L75.9572 3.80014C77.4877 2.26829 79.7541 0 84 0C88.2459 0 90.5123 2.26829 92.0428 3.80014L92.1213 3.87868C93.5898 5.34716 94.3191 6 96 6V12C91.7541 12 89.4877 9.73171 87.9572 8.19986L87.8787 8.12132C86.4102 6.65284 85.6809 6 84 6C82.3191 6 81.5898 6.65284 80.1213 8.12132L80.0428 8.19986C78.5123 9.73171 76.2459 12 72 12C67.7541 12 65.4877 9.73171 63.9572 8.19986C63.9308 8.17346 63.9046 8.14727 63.8787 8.12132C62.4102 6.65284 61.6809 6 60 6C58.3191 6 57.5898 6.65284 56.1213 8.12132C56.0954 8.14727 56.0692 8.17346 56.0428 8.19986C54.5123 9.73171 52.2459 12 48 12C43.7541 12 41.4877 9.73171 39.9572 8.19986C39.9308 8.17346 39.9046 8.14727 39.8787 8.12132C38.4102 6.65284 37.6809 6 36 6C34.3191 6 33.5898 6.65284 32.1213 8.12132C32.0954 8.14727 32.0692 8.17346 32.0428 8.19986C30.5123 9.73171 28.2459 12 24 12C19.7541 12 17.4877 9.73171 15.9572 8.19986C15.9308 8.17346 15.9046 8.14727 15.8787 8.12132C14.4102 6.65284 13.6809 6 12 6C10.3191 6 9.5898 6.65284 8.12132 8.12132C8.09537 8.14727 8.0692 8.17346 8.04282 8.19986Z' + fill='var(--fire-opal)' + /> + </svg> + </div>; } - diff --git a/components/tag.tsx b/components/tag.tsx index 477b8e2..048c405 100644 --- a/components/tag.tsx +++ b/components/tag.tsx @@ -3,22 +3,26 @@ import { CSSProperties } from 'react'; export default function Tags(props: { tags: Array<string>; }) { - return <div className="tags"> + return <div className='tags'> <span>Tags:</span> - {props.tags.map(tag => <Tag key={Math.random().toString()} name={tag}/>)} - </div> + {props.tags.map(tag => <Tag key={Math.random().toString()} name={tag} />)} + </div>; } export function Tag(props: { name: string; }) { - return <a className="tag" href={"/search?q=" + props.name} style={{ - "--tag-hue": props.name - .split("") - .map(char => char.charCodeAt(0)) - .reduce((a, b) => a + b) - % 360 - } as CSSProperties}> + return <a + className='tag' + href={'/search?q=' + props.name} + style={{ + '--tag-hue': props.name + .split('') + .map(char => char.charCodeAt(0)) + .reduce((a, b) => a + b) + % 360, + } as CSSProperties} + > {props.name} - </a> + </a>; } diff --git a/dprint.json b/dprint.json new file mode 100644 index 0000000..6e15825 --- /dev/null +++ b/dprint.json @@ -0,0 +1,32 @@ +{ + "$schema": "https://dprint.dev/schemas/v0.json", + "projectType": "openSource", + "incremental": true, + "useTabs": true, + "typescript": { + "semiColons": "always", + "quoteStyle": "preferSingle", + "importDeclaration.spaceSurroundingNamedImports": true + }, + "markdown": { + "textWrap": "always", + "emphasisKind": "asterisks", + "strongKind": "asterisks" + }, + "json": { "lineWidth": 80 }, + "includes": [ + "**/*.{ts,tsx}", + "**/*.{md}", + "**/*.{json}" + ], + "excludes": [ + "node_modules/**", + "**/*-lock.json", + ".next/**" + ], + "plugins": [ + "https://plugins.dprint.dev/typescript-0.44.0.wasm", + "https://plugins.dprint.dev/markdown-0.6.2.wasm", + "https://plugins.dprint.dev/json-0.10.1.wasm" + ] +} diff --git a/package.json b/package.json index 5e29438..2d9e85e 100644 --- a/package.json +++ b/package.json @@ -1,25 +1,25 @@ { - "name": "blog", - "version": "0.1.0", - "private": true, - "scripts": { - "dev": "next dev", - "build": "next build", - "start": "next start" - }, - "dependencies": { - "@material-ui/core": "^4.11.3", - "@material-ui/icons": "^4.11.2", - "fuse.js": "^6.4.6", - "next": "10.0.9", - "react": "17.0.2", - "react-dom": "17.0.2", - "react-markdown": "^5.0.3", - "remark-gfm": "^1.0.0" - }, - "devDependencies": { - "@types/node": "^14.14.36", - "@types/react": "^17.0.3", - "typescript": "^4.2.3" - } + "name": "blog", + "version": "0.1.0", + "private": true, + "scripts": { + "dev": "next dev", + "build": "next build", + "start": "next start" + }, + "dependencies": { + "@material-ui/core": "^4.11.3", + "@material-ui/icons": "^4.11.2", + "fuse.js": "^6.4.6", + "next": "10.0.9", + "react": "17.0.2", + "react-dom": "17.0.2", + "react-markdown": "^5.0.3", + "remark-gfm": "^1.0.0" + }, + "devDependencies": { + "@types/node": "^14.14.36", + "@types/react": "^17.0.3", + "typescript": "^4.2.3" + } } diff --git a/pages/_app.tsx b/pages/_app.tsx index 62c1c9b..662f841 100644 --- a/pages/_app.tsx +++ b/pages/_app.tsx @@ -1,20 +1,19 @@ import Head from 'next/head'; +import '../styles/button.css'; import '../styles/colors.css'; -import '../styles/layout.css'; import '../styles/globals.css'; -import '../styles/navbar.css'; -import '../styles/button.css'; import '../styles/image.css'; -import '../styles/tags.css'; +import '../styles/layout.css'; +import '../styles/navbar.css'; import '../styles/search.css'; +import '../styles/tags.css'; export default function Blog({ Component, pageProps }) { return <div> <Head> - <link rel="stylesheet" href="/font/font.css"/> + <link rel='stylesheet' href='/font/font.css' /> </Head> <Component {...pageProps} /> - </div> + </div>; } - diff --git a/pages/index.tsx b/pages/index.tsx index 96b8684..65b53cb 100644 --- a/pages/index.tsx +++ b/pages/index.tsx @@ -1,52 +1,52 @@ -import Navbar, { NavbarItem } from '../components/navbar'; import Chapters, { chapter } from '../components/chapters'; +import Navbar, { NavbarItem } from '../components/navbar'; import Seperator from '../components/seperator'; -import { getStaticProps as getBlogPage, ArticleMeta, RenderedArticle } from './post/[id]'; +import { ArticleMeta, getStaticProps as getBlogPage, RenderedArticle } from './post/[id]'; -var posts = ["index", "index", "index"]; +var posts = ['index', 'index', 'index']; export default function Home(props: { posts: Array<{ props: { - content: string, - meta: ArticleMeta - } - }> + content: string; + meta: ArticleMeta; + }; + }>; }) { return <div> - <div className="centeredPage"> - <div className="titleWrapper"> + <div className='centeredPage'> + <div className='titleWrapper'> <h1>{props.posts[0].props.meta.title}</h1> </div> - <div className="navAreaWrapper"> - <div className="sticky"> - <Navbar page="home"/> - <NavbarItem title="Pinned posts:" classList={["pinned"]}/> - <Chapters chapters={[ - ...props.posts.slice(1).map(post => { - return { - children: post.props.meta.chapters, - name: post.props.meta.title, - sectionLink: "/post/" + post.props.meta.id - } as chapter - }) - ]}/> + <div className='navAreaWrapper'> + <div className='sticky'> + <Navbar page='home' /> + <NavbarItem title='Pinned posts:' classList={['pinned']} /> + <Chapters + chapters={[ + ...props.posts.slice(1).map(post => { + return { + children: post.props.meta.chapters, + name: post.props.meta.title, + sectionLink: '/post/' + post.props.meta.id, + } as chapter; + }), + ]} + /> </div> </div> - <div className="contentWrapper"> - { - props.posts.map((post, index) => { - return <> - { index != 0 && <h1>{post.props.meta.title}</h1> } - <RenderedArticle content={post.props.content}/> - { index + 1 != props.posts.length && <Seperator/> } - </> - }) - } + <div className='contentWrapper'> + {props.posts.map((post, index) => { + return <> + {index != 0 && <h1>{post.props.meta.title}</h1>} + <RenderedArticle content={post.props.content} /> + {index + 1 != props.posts.length && <Seperator />} + </>; + })} </div> </div> - </div> + </div>; } export function getStaticProps() { @@ -54,9 +54,9 @@ export function getStaticProps() { posts.forEach(id => { postsContent.push(getBlogPage({ params: { id } })); - }) + }); var staticProps = { props: { posts: postsContent } }; - - return staticProps + + return staticProps; } diff --git a/pages/post/[id].tsx b/pages/post/[id].tsx index b68b790..c0d4e9d 100644 --- a/pages/post/[id].tsx +++ b/pages/post/[id].tsx @@ -1,14 +1,14 @@ -import { ReactNode } from 'react'; import ReactMarkdownWithHTML from 'react-markdown/with-html'; import { readdirSync, readFileSync } from 'fs'; import { join } from 'path'; +import { ReactNode } from 'react'; import gfm from 'remark-gfm'; -import Seperator from '../../components/seperator'; import Navbar from '../../components/navbar'; +import Seperator from '../../components/seperator'; // import Button from '../../components/button'; -import Image from '../../components/image'; import Chapters, { chapter } from '../../components/chapters'; +import Image from '../../components/image'; import Tags from '../../components/tag'; export interface ArticleMeta { @@ -21,70 +21,72 @@ export interface ArticleMeta { id?: string; } -export function RenderedArticle(props: { content: string }) { +export function RenderedArticle(props: { content: string; }) { return <ReactMarkdownWithHTML - plugins={[gfm]} - allowDangerousHtml - children={props.content} - renderers={{ - image: Image, - thematicBreak: Seperator, - heading: Heading, - }}/>; + plugins={[gfm]} + allowDangerousHtml + children={props.content} + renderers={{ + image: Image, + thematicBreak: Seperator, + heading: Heading, + }} + />; } var headingLevel = (input: string) => input?.match(/^[#]+/)[0]?.length || 0; -var sectionID = (input: string) => input - .replace(/[()\[\]{}!@#$%^&*<>?,./\;':"\\|=+]/g, "") - .replace(/\s/g, "-") - .toLowerCase(); +var sectionID = (input: string) => + input + .replace(/[()\[\]{}!@#$%^&*<>?,./\;':"\\|=+]/g, '') + .replace(/\s/g, '-') + .toLowerCase(); function Heading(props: { children?: ReactNode; level?: number; }) { - var HeadingTag = "h" + props.level as keyof JSX.IntrinsicElements; - return <HeadingTag id={sectionID(props.children[0].props.children)} children={props.children}/> + var HeadingTag = 'h' + props.level as keyof JSX.IntrinsicElements; + return <HeadingTag id={sectionID(props.children[0].props.children)} children={props.children} />; } export default function Post(props: { - content: string, - meta: ArticleMeta + content: string; + meta: ArticleMeta; }) { return <div> - <div className="centeredPage"> - <div className="titleWrapper"> + <div className='centeredPage'> + <div className='titleWrapper'> <h1>{props.meta.title}</h1> - <p className="subtile">{props.meta.subtitle}</p> - { props.meta.tags && <Tags tags={props.meta.tags}/> } + <p className='subtile'>{props.meta.subtitle}</p> + {props.meta.tags && <Tags tags={props.meta.tags} />} </div> - <div className="navAreaWrapper"> - <div className="sticky"> - <Navbar/> - <Chapters chapters={props.meta.chapters}/> + <div className='navAreaWrapper'> + <div className='sticky'> + <Navbar /> + <Chapters chapters={props.meta.chapters} /> </div> </div> - <div className="contentWrapper"> - <RenderedArticle content={props.content}/> + <div className='contentWrapper'> + <RenderedArticle content={props.content} /> </div> </div> - </div> + </div>; } var parseTag = { - "title": (val: string) => val, - "subtitle": (val: string) => val, - "author": (val: string) => val, - "tags": (val: string) => val.split(",").map(i => i.trim()), - "date": (val: string) => new Date(val).toDateString(), -} + 'title': (val: string) => val, + 'subtitle': (val: string) => val, + 'author': (val: string) => val, + 'tags': (val: string) => val.split(',').map(i => i.trim()), + 'date': (val: string) => new Date(val).toDateString(), +}; function parseMeta(file: Array<string>): ArticleMeta { var meta: ArticleMeta = {}; file.forEach(line => { - if (!line.startsWith("[meta]: ")) return; + if (!line.startsWith('[meta]: ')) return; var tags = line.match(/\[meta\]:\s+\<(.+?)\>\s+\((.+?)\)/); if (!tags || !tags[1] || !tags[2]) return; if (!parseTag.hasOwnProperty(tags[1])) return; @@ -98,7 +100,7 @@ function parseToCRecursive(headings: Array<string>): Array<chapter> { interface WIPchapter extends chapter { unparsedChildren?: Array<string>; } - var children: Array<WIPchapter> = [] + var children: Array<WIPchapter> = []; var lowestLevel = headingLevel(headings[0]); var currentChildIndex = -1; @@ -108,44 +110,44 @@ function parseToCRecursive(headings: Array<string>): Array<chapter> { var chapterName = headings[i].match(/^[#]+\s+(.+)/)[1]; children.push({ name: chapterName, - sectionLink: "#" + sectionID(chapterName), + sectionLink: '#' + sectionID(chapterName), unparsedChildren: [], }); currentChildIndex += 1; } else { - children[currentChildIndex].unparsedChildren.push(headings[i]) + children[currentChildIndex].unparsedChildren.push(headings[i]); } } children.map(child => { - child.children = parseToCRecursive(child.unparsedChildren) + child.children = parseToCRecursive(child.unparsedChildren); delete child.unparsedChildren; - return child - }) + return child; + }); return children as Array<chapter>; } function parseToC(file: Array<string>): Array<chapter> { - var fileAsStr = file.join("\n"); - fileAsStr = fileAsStr.replace(/```.*?```/gs, ""); // filter out code blocks from table of contents - var fileAsArr = fileAsStr.split("\n"); - var chapterStrings = fileAsArr.filter(line => line.startsWith("#")); + var fileAsStr = file.join('\n'); + fileAsStr = fileAsStr.replace(/```.*?```/gs, ''); // filter out code blocks from table of contents + var fileAsArr = fileAsStr.split('\n'); + var chapterStrings = fileAsArr.filter(line => line.startsWith('#')); return parseToCRecursive(chapterStrings); } function preprocessor(fileContent: string) { - var fileAsArr = fileContent.split("\n"); + var fileAsArr = fileContent.split('\n'); var meta = parseMeta(fileAsArr); meta.chapters = parseToC(fileAsArr); - var result = fileAsArr.join("\n").trim() - return { meta, result } + var result = fileAsArr.join('\n').trim(); + return { meta, result }; } -export function getStaticProps(props: {params: { id: string }}) { - var filename = join("posts/", props.params.id + ".md") - var filecontent = readFileSync(filename).toString().trim() +export function getStaticProps(props: { params: { id: string; }; }) { + var filename = join('posts/', props.params.id + '.md'); + var filecontent = readFileSync(filename).toString().trim(); var parsed = preprocessor(filecontent); parsed.meta.id = props.params.id; @@ -155,21 +157,20 @@ export function getStaticProps(props: {params: { id: string }}) { content: parsed.result, meta: parsed.meta, }, - } + }; } export function getStaticPaths() { - var files = readdirSync("posts").filter(f => f.endsWith(".md")); + var files = readdirSync('posts').filter(f => f.endsWith('.md')); return { paths: files.map((f) => { return { params: { - id: f.substr(0, f.length - 3) - } - } + id: f.substr(0, f.length - 3), + }, + }; }), fallback: false, - } + }; } - diff --git a/pages/search.tsx b/pages/search.tsx index c9b27a5..8eff8d8 100644 --- a/pages/search.tsx +++ b/pages/search.tsx @@ -1,65 +1,69 @@ -import { useState, useEffect } from 'react'; import Fuse from 'fuse.js'; +import { useEffect, useState } from 'react'; import Navbar from '../components/navbar'; -import Tags from '../components/tag' +import Tags from '../components/tag'; import SearchOutlinedIcon from '@material-ui/icons/SearchOutlined'; -function SearchBar(props: { searchFunction: () => void }) { - return <div className="searchBar"> +function SearchBar(props: { searchFunction: () => void; }) { + return <div className='searchBar'> <input - className="input" - id="searchInput" - placeholder="Search for posts..." - onChange={() => props.searchFunction()} - spellCheck="false" - autoComplete="off"/> - <button className="button" onClick={() => props.searchFunction()}><SearchOutlinedIcon/></button> - </div> + className='input' + id='searchInput' + placeholder='Search for posts...' + onChange={() => props.searchFunction()} + spellCheck='false' + autoComplete='off' + /> + <button className='button' onClick={() => props.searchFunction()}> + <SearchOutlinedIcon /> + </button> + </div>; } interface Post { - title: string - subtitle: string - author: string - date: string - urlname: string - tags: Array<string> + title: string; + subtitle: string; + author: string; + date: string; + urlname: string; + tags: Array<string>; } interface PostsInfo { - valid_tags: Array<string> - posts: Array<Post> + valid_tags: Array<string>; + posts: Array<Post>; } -function Post(props: { post: Post }) { - return <a className="post" href={"/post/" + props.post.urlname}> - <b className="title">{props.post.title}</b> - {props.post.subtitle && <p className="subtitle">{props.post.subtitle}</p>} - <p className="authordate">Written by {props.post.author} on {new Date(props.post.date).toLocaleString("en-us", { - month: "long", day: "numeric" - })}</p> - <Tags tags={props.post.tags}/> +function Post(props: { post: Post; }) { + return <a className='post' href={'/post/' + props.post.urlname}> + <b className='title'>{props.post.title}</b> + {props.post.subtitle && <p className='subtitle'>{props.post.subtitle}</p>} + <p className='authordate'> + Written by {props.post.author} on {new Date(props.post.date).toLocaleString('en-us', { + month: 'long', + day: 'numeric', + })} + </p> + <Tags tags={props.post.tags} /> </a>; } -function Posts(props: { posts: Array<Post> }) { - return <div className="searchResults"> - { - props.posts.map(post => <Post post={post} key={Math.random().toString()}/>) - } +function Posts(props: { posts: Array<Post>; }) { + return <div className='searchResults'> + {props.posts.map(post => <Post post={post} key={Math.random().toString()} />)} </div>; } function searchFilter(query: string, tags: Array<string>) { var output = { - query: "", - tags: [] - } + query: '', + tags: [], + }; // remove string literals from tag matching - var queryWithoutLiterals = query.replace(/\".+?\"/g, ""); + var queryWithoutLiterals = query.replace(/\".+?\"/g, ''); // find tags and remove them from the query tags.forEach(tag => { @@ -67,55 +71,56 @@ function searchFilter(query: string, tags: Array<string>) { if (index == -1) return; // remove tag from query - queryWithoutLiterals = - queryWithoutLiterals.substr(0, index) + - queryWithoutLiterals.substr(index + tag.length); + queryWithoutLiterals = queryWithoutLiterals.substr(0, index) + + queryWithoutLiterals.substr(index + tag.length); output.tags.push(tag); }); // add back in the string literals (janky just gets pasted on end) - output.query = - queryWithoutLiterals + " " + - (query.match(/\".+?\"/g) + output.query = queryWithoutLiterals + ' ' + + (query.match(/\".+?\"/g) ?.map(r => r.substr(1, r.length - 2)) - .join(" ") || ""); + .join(' ') || ''); return output; } export default function SearchPage() { var [posts, setPosts] = useState<PostsInfo>({ posts: [], valid_tags: [] }); - var [query, setQuery] = useState("-"); + var [query, setQuery] = useState('-'); var [visiblePosts, setVisiblePosts] = useState<Array<Post>>([]); var fuse = new Fuse(posts.posts, { keys: [ - "title", - "subtitle", - "author", - "date", - "urlname", - "tags" + 'title', + 'subtitle', + 'author', + 'date', + 'urlname', + 'tags', ], - isCaseSensitive: false - }) + isCaseSensitive: false, + }); - useEffect(() => {(async () => { - var query = new URLSearchParams(window.location.search).get("q") || ""; - if(query) - (document.getElementById("searchInput") as HTMLInputElement).value = query; + useEffect(() => { + (async () => { + var query = new URLSearchParams(window.location.search).get('q') || ''; + if (query) { + (document.getElementById('searchInput') as HTMLInputElement).value = query; + } - var posts = await fetch("/posts.json"); - var postsJson: PostsInfo = await posts.json(); - postsJson.posts = postsJson.posts.sort((a, b) => new Date(b.date).getTime() - new Date(a.date).getTime()) - setPosts(postsJson); - setQuery(query); - })()}, []); + var posts = await fetch('/posts.json'); + var postsJson: PostsInfo = await posts.json(); + postsJson.posts = postsJson.posts.sort((a, b) => new Date(b.date).getTime() - new Date(a.date).getTime()); + setPosts(postsJson); + setQuery(query); + })(); + }, []); useEffect(() => { var search = searchFilter(query, posts.valid_tags); - if(search.query.length == 0) { + if (search.query.length == 0) { var results = posts.posts; } else { var fuseSearch = fuse.search(search.query); @@ -123,32 +128,34 @@ export default function SearchPage() { } results = results.filter(result => { - for(var i in search.tags) { - if (!result.tags.includes(search.tags[i])) + for (var i in search.tags) { + if (!result.tags.includes(search.tags[i])) { return false; + } } return true; }); - setVisiblePosts(results) + setVisiblePosts(results); }, [query]); return <div> - <div className="centeredPage"> - <div className="titleWrapper"> + <div className='centeredPage'> + <div className='titleWrapper'> <h1>Search for posts</h1> </div> - <div className="navAreaWrapper"> - <div className="sticky"> - <Navbar page="search"/> + <div className='navAreaWrapper'> + <div className='sticky'> + <Navbar page='search' /> </div> </div> - <div className="contentWrapper"> - <SearchBar searchFunction={() => { - setTimeout(() => setQuery((document.getElementById("searchInput") as HTMLInputElement).value)); - }}/> - <Posts posts={visiblePosts}/> + <div className='contentWrapper'> + <SearchBar + searchFunction={() => { + setTimeout(() => setQuery((document.getElementById('searchInput') as HTMLInputElement).value)); + }} + /> + <Posts posts={visiblePosts} /> </div> </div> - </div> + </div>; } - diff --git a/posts/gert.md b/posts/gert.md index 62cc835..fb121ac 100644 --- a/posts/gert.md +++ b/posts/gert.md @@ -1,22 +1,48 @@ -[meta]: <title> (Loek’s excruciatingly interesting blog) -[meta]: <subtitle> (Loek heeft dit geschreven) -[meta]: <author> (Loek) -[meta]: <date> (March 27 2021) -[meta]: <tags> (banana's, test,gert testing, debug) - -Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Gravida dictum fusce ut placerat orci nulla pellentesque. Laoreet id donec ultrices tincidunt arcu. Tortor aliquam nulla facilisi cras fermentum odio eu feugiat. A scelerisque purus semper eget duis at tellus. A iaculis at erat pellentesque adipiscing commodo elit at imperdiet. Arcu bibendum at varius vel pharetra vel turpis nunc eget. Euismod in pellentesque massa placerat duis. Lorem ipsum dolor sit amet consectetur adipiscing elit. Ultrices in iaculis nunc sed augue lacus. Vestibulum mattis ullamcorper velit sed. Adipiscing diam donec adipiscing +Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor +incididunt ut labore et dolore magna aliqua. Gravida dictum fusce ut placerat +orci nulla pellentesque. Laoreet id donec ultrices tincidunt arcu. Tortor +aliquam nulla facilisi cras fermentum odio eu feugiat. A scelerisque purus +semper eget duis at tellus. A iaculis at erat pellentesque adipiscing commodo +elit at imperdiet. Arcu bibendum at varius vel pharetra vel turpis nunc eget. +Euismod in pellentesque massa placerat duis. Lorem ipsum dolor sit amet +consectetur adipiscing elit. Ultrices in iaculis nunc sed augue lacus. +Vestibulum mattis ullamcorper velit sed. Adipiscing diam donec adipiscing [Here's a link](https://github.com/lonkaars) ![fonny doge meme big laugh hahaha funni image big fonny me laugh because image fonne](https://external-content.duckduckgo.com/iu/?u=http%3A%2F%2Fbarkpost-assets.s3.amazonaws.com%2Fwp-content%2Fuploads%2F2013%2F11%2FplainDoge.jpg&f=1&nofb=1) -Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Gravida dictum fusce ut placerat orci nulla pellentesque. Laoreet id donec ultrices tincidunt arcu. Tortor aliquam nulla facilisi cras fermentum odio eu feugiat. A scelerisque purus semper eget duis at tellus. A iaculis at erat pellentesque adipiscing commodo elit at imperdiet. Arcu bibendum at varius vel pharetra vel turpis nunc eget. Euismod in pellentesque massa placerat duis. Lorem ipsum dolor sit amet consectetur adipiscing elit. Ultrices in iaculis nunc sed augue lacus. Vestibulum mattis ullamcorper velit sed. Adipiscing diam donec adipiscing +Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor +incididunt ut labore et dolore magna aliqua. Gravida dictum fusce ut placerat +orci nulla pellentesque. Laoreet id donec ultrices tincidunt arcu. Tortor +aliquam nulla facilisi cras fermentum odio eu feugiat. A scelerisque purus +semper eget duis at tellus. A iaculis at erat pellentesque adipiscing commodo +elit at imperdiet. Arcu bibendum at varius vel pharetra vel turpis nunc eget. +Euismod in pellentesque massa placerat duis. Lorem ipsum dolor sit amet +consectetur adipiscing elit. Ultrices in iaculis nunc sed augue lacus. +Vestibulum mattis ullamcorper velit sed. Adipiscing diam donec adipiscing --- -Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Gravida dictum fusce ut placerat orci nulla pellentesque. Laoreet id donec ultrices tincidunt arcu. Tortor aliquam nulla facilisi cras fermentum odio eu feugiat. A scelerisque purus semper eget duis at tellus. A iaculis at erat pellentesque adipiscing commodo elit at imperdiet. Arcu bibendum at varius vel pharetra vel turpis nunc eget. Euismod in pellentesque massa placerat duis. Lorem ipsum dolor sit amet consectetur adipiscing elit. Ultrices in iaculis nunc sed augue lacus. Vestibulum mattis ullamcorper velit sed. Adipiscing diam donec adipiscing - -Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Gravida dictum fusce ut placerat orci nulla pellentesque. Laoreet id donec ultrices tincidunt arcu. Tortor aliquam nulla facilisi cras fermentum odio eu feugiat. A scelerisque purus semper eget duis at tellus. A iaculis at erat pellentesque adipiscing commodo elit at imperdiet. Arcu bibendum at varius vel pharetra vel turpis nunc eget. Euismod in pellentesque massa placerat duis. Lorem ipsum dolor sit amet consectetur adipiscing elit. Ultrices in iaculis nunc sed augue lacus. Vestibulum mattis ullamcorper velit sed. Adipiscing diam donec adipiscing +Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor +incididunt ut labore et dolore magna aliqua. Gravida dictum fusce ut placerat +orci nulla pellentesque. Laoreet id donec ultrices tincidunt arcu. Tortor +aliquam nulla facilisi cras fermentum odio eu feugiat. A scelerisque purus +semper eget duis at tellus. A iaculis at erat pellentesque adipiscing commodo +elit at imperdiet. Arcu bibendum at varius vel pharetra vel turpis nunc eget. +Euismod in pellentesque massa placerat duis. Lorem ipsum dolor sit amet +consectetur adipiscing elit. Ultrices in iaculis nunc sed augue lacus. +Vestibulum mattis ullamcorper velit sed. Adipiscing diam donec adipiscing + +Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor +incididunt ut labore et dolore magna aliqua. Gravida dictum fusce ut placerat +orci nulla pellentesque. Laoreet id donec ultrices tincidunt arcu. Tortor +aliquam nulla facilisi cras fermentum odio eu feugiat. A scelerisque purus +semper eget duis at tellus. A iaculis at erat pellentesque adipiscing commodo +elit at imperdiet. Arcu bibendum at varius vel pharetra vel turpis nunc eget. +Euismod in pellentesque massa placerat duis. Lorem ipsum dolor sit amet +consectetur adipiscing elit. Ultrices in iaculis nunc sed augue lacus. +Vestibulum mattis ullamcorper velit sed. Adipiscing diam donec adipiscing # Here's some hierarchy test @@ -31,4 +57,3 @@ Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor i ### subsubsection ### subsubsection - diff --git a/posts/index.md b/posts/index.md index f61a35c..fb121ac 100644 --- a/posts/index.md +++ b/posts/index.md @@ -1,22 +1,48 @@ -[meta]: <title> (Loek’s excruciatingly interesting blog) -[meta]: <subtitle> (Loek heeft dit geschreven) -[meta]: <author> (Loek) -[meta]: <date> (March 27 2021) -[meta]: <tags> (test, debug) - -Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Gravida dictum fusce ut placerat orci nulla pellentesque. Laoreet id donec ultrices tincidunt arcu. Tortor aliquam nulla facilisi cras fermentum odio eu feugiat. A scelerisque purus semper eget duis at tellus. A iaculis at erat pellentesque adipiscing commodo elit at imperdiet. Arcu bibendum at varius vel pharetra vel turpis nunc eget. Euismod in pellentesque massa placerat duis. Lorem ipsum dolor sit amet consectetur adipiscing elit. Ultrices in iaculis nunc sed augue lacus. Vestibulum mattis ullamcorper velit sed. Adipiscing diam donec adipiscing +Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor +incididunt ut labore et dolore magna aliqua. Gravida dictum fusce ut placerat +orci nulla pellentesque. Laoreet id donec ultrices tincidunt arcu. Tortor +aliquam nulla facilisi cras fermentum odio eu feugiat. A scelerisque purus +semper eget duis at tellus. A iaculis at erat pellentesque adipiscing commodo +elit at imperdiet. Arcu bibendum at varius vel pharetra vel turpis nunc eget. +Euismod in pellentesque massa placerat duis. Lorem ipsum dolor sit amet +consectetur adipiscing elit. Ultrices in iaculis nunc sed augue lacus. +Vestibulum mattis ullamcorper velit sed. Adipiscing diam donec adipiscing [Here's a link](https://github.com/lonkaars) ![fonny doge meme big laugh hahaha funni image big fonny me laugh because image fonne](https://external-content.duckduckgo.com/iu/?u=http%3A%2F%2Fbarkpost-assets.s3.amazonaws.com%2Fwp-content%2Fuploads%2F2013%2F11%2FplainDoge.jpg&f=1&nofb=1) -Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Gravida dictum fusce ut placerat orci nulla pellentesque. Laoreet id donec ultrices tincidunt arcu. Tortor aliquam nulla facilisi cras fermentum odio eu feugiat. A scelerisque purus semper eget duis at tellus. A iaculis at erat pellentesque adipiscing commodo elit at imperdiet. Arcu bibendum at varius vel pharetra vel turpis nunc eget. Euismod in pellentesque massa placerat duis. Lorem ipsum dolor sit amet consectetur adipiscing elit. Ultrices in iaculis nunc sed augue lacus. Vestibulum mattis ullamcorper velit sed. Adipiscing diam donec adipiscing +Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor +incididunt ut labore et dolore magna aliqua. Gravida dictum fusce ut placerat +orci nulla pellentesque. Laoreet id donec ultrices tincidunt arcu. Tortor +aliquam nulla facilisi cras fermentum odio eu feugiat. A scelerisque purus +semper eget duis at tellus. A iaculis at erat pellentesque adipiscing commodo +elit at imperdiet. Arcu bibendum at varius vel pharetra vel turpis nunc eget. +Euismod in pellentesque massa placerat duis. Lorem ipsum dolor sit amet +consectetur adipiscing elit. Ultrices in iaculis nunc sed augue lacus. +Vestibulum mattis ullamcorper velit sed. Adipiscing diam donec adipiscing --- -Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Gravida dictum fusce ut placerat orci nulla pellentesque. Laoreet id donec ultrices tincidunt arcu. Tortor aliquam nulla facilisi cras fermentum odio eu feugiat. A scelerisque purus semper eget duis at tellus. A iaculis at erat pellentesque adipiscing commodo elit at imperdiet. Arcu bibendum at varius vel pharetra vel turpis nunc eget. Euismod in pellentesque massa placerat duis. Lorem ipsum dolor sit amet consectetur adipiscing elit. Ultrices in iaculis nunc sed augue lacus. Vestibulum mattis ullamcorper velit sed. Adipiscing diam donec adipiscing - -Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Gravida dictum fusce ut placerat orci nulla pellentesque. Laoreet id donec ultrices tincidunt arcu. Tortor aliquam nulla facilisi cras fermentum odio eu feugiat. A scelerisque purus semper eget duis at tellus. A iaculis at erat pellentesque adipiscing commodo elit at imperdiet. Arcu bibendum at varius vel pharetra vel turpis nunc eget. Euismod in pellentesque massa placerat duis. Lorem ipsum dolor sit amet consectetur adipiscing elit. Ultrices in iaculis nunc sed augue lacus. Vestibulum mattis ullamcorper velit sed. Adipiscing diam donec adipiscing +Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor +incididunt ut labore et dolore magna aliqua. Gravida dictum fusce ut placerat +orci nulla pellentesque. Laoreet id donec ultrices tincidunt arcu. Tortor +aliquam nulla facilisi cras fermentum odio eu feugiat. A scelerisque purus +semper eget duis at tellus. A iaculis at erat pellentesque adipiscing commodo +elit at imperdiet. Arcu bibendum at varius vel pharetra vel turpis nunc eget. +Euismod in pellentesque massa placerat duis. Lorem ipsum dolor sit amet +consectetur adipiscing elit. Ultrices in iaculis nunc sed augue lacus. +Vestibulum mattis ullamcorper velit sed. Adipiscing diam donec adipiscing + +Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor +incididunt ut labore et dolore magna aliqua. Gravida dictum fusce ut placerat +orci nulla pellentesque. Laoreet id donec ultrices tincidunt arcu. Tortor +aliquam nulla facilisi cras fermentum odio eu feugiat. A scelerisque purus +semper eget duis at tellus. A iaculis at erat pellentesque adipiscing commodo +elit at imperdiet. Arcu bibendum at varius vel pharetra vel turpis nunc eget. +Euismod in pellentesque massa placerat duis. Lorem ipsum dolor sit amet +consectetur adipiscing elit. Ultrices in iaculis nunc sed augue lacus. +Vestibulum mattis ullamcorper velit sed. Adipiscing diam donec adipiscing # Here's some hierarchy test @@ -31,4 +57,3 @@ Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor i ### subsubsection ### subsubsection - diff --git a/posts/po4-api.md b/posts/po4-api.md index 0ca148b..487b2d3 100644 --- a/posts/po4-api.md +++ b/posts/po4-api.md @@ -1,63 +1,61 @@ -[meta]: <title> (po connect-4 api readme) -[meta]: <tags> (po4, po-connect-4, api, rest) -[meta]: <date> (April 1 2021) -[meta]: <author> (Loekaars) - # API -This is the subdirectory for the API. You can find the API reference in [this](https://docs.google.com/spreadsheets/d/1mDN9IUqRIMjr_9RmLxKybjIgVuaUadalmPEFnG-XeJg/edit?usp=sharing) Google Docs document. +This is the subdirectory for the API. You can find the API reference in +[this](https://docs.google.com/spreadsheets/d/1mDN9IUqRIMjr_9RmLxKybjIgVuaUadalmPEFnG-XeJg/edit?usp=sharing) +Google Docs document. ## Endpoint reference (WIP) API return type classes are mostly defined in api/api.ts -endpoint|method|description|parameters|authorization --|-|-|-|- -/status | GET | get online user count and active game count -/auth/login | POST | log in with email or username -/auth/token | POST | log in using a token (stored as cookie) -/auth/signup | POST | sign up -/user/info | GET\|POST | get user info by username or id note: avatar is a uri to a 256x256 .png image -/user/games | GET\|POST | get games of user -/user/avatar | GET\|POST | fetch or update avatar note: avatar is a client-resized 256x256 .png base64-encoded image, request returns error when image is not .png or larger than 256x256 -/user/prefrences | GET\|POST | fetch or change user preferences -/user/password | POST | update password -/user/email | POST | update email (token used for authentication if password is undefined) -/user/username | POST | update username (token used for authentication if password is undefined) -/user/status | POST | update status -/user/searchFriends | POST | search user's friend list -/social/request | POST | send a friend request to a user by user id -/social/accept | POST | accept a friend request -/social/remove | POST | remove a friend from your friend list or delete a friend request from a user -/social/search | POST | search for users by username or status -/social/block | POST | block a user -/social/unblock | POST | unblock a user -/social/list/requests | GET | get a list of unaccepted friend requests -/social/list/blocks | GET | get a list of blocked people -/social/list/friends | GET | get a list of your friends -/game/new | POST | create a new private game -/game/random | POST | join or create a public game -/game/info | POST | -/game/accept | POST | accept game invite or rematch -/game/spectate | POST | spectate a game by id +| endpoint | method | description | parameters | authorization | +| --------------------- | --------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------- | ------------- | +| /status | GET | get online user count and active game count | | | +| /auth/login | POST | log in with email or username | | | +| /auth/token | POST | log in using a token (stored as cookie) | | | +| /auth/signup | POST | sign up | | | +| /user/info | GET\|POST | get user info by username or id note: avatar is a uri to a 256x256 .png image | | | +| /user/games | GET\|POST | get games of user | | | +| /user/avatar | GET\|POST | fetch or update avatar note: avatar is a client-resized 256x256 .png base64-encoded image, request returns error when image is not .png or larger than 256x256 | | | +| /user/prefrences | GET\|POST | fetch or change user preferences | | | +| /user/password | POST | update password | | | +| /user/email | POST | update email (token used for authentication if password is undefined) | | | +| /user/username | POST | update username (token used for authentication if password is undefined) | | | +| /user/status | POST | update status | | | +| /user/searchFriends | POST | search user's friend list | | | +| /social/request | POST | send a friend request to a user by user id | | | +| /social/accept | POST | accept a friend request | | | +| /social/remove | POST | remove a friend from your friend list or delete a friend request from a user | | | +| /social/search | POST | search for users by username or status | | | +| /social/block | POST | block a user | | | +| /social/unblock | POST | unblock a user | | | +| /social/list/requests | GET | get a list of unaccepted friend requests | | | +| /social/list/blocks | GET | get a list of blocked people | | | +| /social/list/friends | GET | get a list of your friends | | | +| /game/new | POST | create a new private game | | | +| /game/random | POST | join or create a public game | | | +| /game/info | POST | | | | +| /game/accept | POST | accept game invite or rematch | | | +| /game/spectate | POST | spectate a game by id | | | ## Events These are events that are fired by the socket.io connection -event|description|data|direction|context --|-|-|-|- -fieldUpdate|recieve new playfield from server|`{ field: string }`|`s -> c`|game -turnUpdate|recieve if it's player 1's move|`{ player1: boolean }`|`s -> c`|game -gameStart|sent when game starts|`none`|`s -> c`|game -finish|sent when game finishes|`none`|`s -> c`|game -resign|send to resign, is then forwarded to all subscribed clients|`none`|`s <-> c`|game -newMove|send a new move|`{ move: int, game_id: string }`|`s <- c`|game -registerGameListener|listen to events for a game|`{ id: string }`|`s <- c`|game -incomingFriendRequest|get notified of friend request|`none`|`s -> c`|global -changedRelation|get notified of a different relation to someone|`{ id: string }`|`s -> c`|global +| event | description | data | direction | context | +| --------------------- | ----------------------------------------------------------- | -------------------------------- | --------- | ------- | +| fieldUpdate | recieve new playfield from server | `{ field: string }` | `s -> c` | game | +| turnUpdate | recieve if it's player 1's move | `{ player1: boolean }` | `s -> c` | game | +| gameStart | sent when game starts | `none` | `s -> c` | game | +| finish | sent when game finishes | `none` | `s -> c` | game | +| resign | send to resign, is then forwarded to all subscribed clients | `none` | `s <-> c` | game | +| newMove | send a new move | `{ move: int, game_id: string }` | `s <- c` | game | +| registerGameListener | listen to events for a game | `{ id: string }` | `s <- c` | game | +| incomingFriendRequest | get notified of friend request | `none` | `s -> c` | global | +| changedRelation | get notified of a different relation to someone | `{ id: string }` | `s -> c` | global | ## How to test API endpoints + ```sh # If you're running the standalone flask server: curl http://localhost:5000/<endpoint> @@ -70,10 +68,16 @@ curl http://localhost:2080/api/<endpoint> Please follow these rules when creating new API endpoints: -1. Endpoints should always return a valid JSON object and an appropriate [http code](https://en.wikipedia.org/wiki/List_of_HTTP_status_codes) -2. Endpoints that are in a namespace should get their own directory in this folder, eg. http://localhost:5000/status is defined in api/status.py, http://localhost:5000/auth/signup is defined in api/auth/signup.py etc. -3. Endpoints that take data should verify that the data is present and valid, and return an empty JSON object with http code 400 (bad request) if the data isn't valid. -4. Endpoints that require database access should get the cursor/connection object from api/db.py +1. Endpoints should always return a valid JSON object and an appropriate + [http code](https://en.wikipedia.org/wiki/List_of_HTTP_status_codes) +2. Endpoints that are in a namespace should get their own directory in this + folder, eg. http://localhost:5000/status is defined in api/status.py, + http://localhost:5000/auth/signup is defined in api/auth/signup.py etc. +3. Endpoints that take data should verify that the data is present and valid, + and return an empty JSON object with http code 400 (bad request) if the data + isn't valid. +4. Endpoints that require database access should get the cursor/connection + object from api/db.py ## Example endpoint @@ -102,4 +106,3 @@ dynamic_route = ["/tests", status] # | # namespace (defined in dynamic_route variable) ``` - diff --git a/posts/po4-voerbak.md b/posts/po4-voerbak.md index 99759b1..74e616b 100644 --- a/posts/po4-voerbak.md +++ b/posts/po4-voerbak.md @@ -1,21 +1,19 @@ -[meta]: <title> (po connect-4 voerbak readme) -[meta]: <tags> (po4, po-connect-4, voerbak, c) -[meta]: <date> (April 1 2021) -[meta]: <author> (Loekaars) - # Voerbak -Here's the source for voerbak, this project's connect 4 engine. The name comes from an abbreviation for the Dutch word for connect 4: Vier Op Een Rij -> VOER + bak = voerbak +Here's the source for voerbak, this project's connect 4 engine. The name comes +from an abbreviation for the Dutch word for connect 4: Vier Op Een Rij -> VOER + +bak = voerbak -Voerbak uses a 1-dimensional array for storing the playfield, and it's printed after every move. The ordering is left to right, then bottom to top: +Voerbak uses a 1-dimensional array for storing the playfield, and it's printed +after every move. The ordering is left to right, then bottom to top: -35|36|37|38|39|40|41 --|-|-|-|-|-|- -28|29|30|31|32|33|34 -21|22|23|24|25|26|27 -14|15|16|17|18|19|20 -7|8|9|10|11|12|13 -0|1|2|3|4|5|6 +| 35 | 36 | 37 | 38 | 39 | 40 | 41 | +| -- | -- | -- | -- | -- | -- | -- | +| 28 | 29 | 30 | 31 | 32 | 33 | 34 | +| 21 | 22 | 23 | 24 | 25 | 26 | 27 | +| 14 | 15 | 16 | 17 | 18 | 19 | 20 | +| 7 | 8 | 9 | 10 | 11 | 12 | 13 | +| 0 | 1 | 2 | 3 | 4 | 5 | 6 | Voerbak is used in this project using api/game/voerbak_connector.py @@ -27,9 +25,10 @@ make ## Input -Voerbak takes moves seperated by newlines from stdin. An example game would look like this: +Voerbak takes moves seperated by newlines from stdin. An example game would look +like this: -``` sh +```sh echo "4,3,3,2,1,2,2,7,1,7,1,7,1" | sed "s/,/\n/g" | ./voerbak # ^ convert "," to newline ``` @@ -49,12 +48,12 @@ message type Message reference: -type|name|messages --|-|- -d|draw|full = board is full -e|errors|full = column is full -m|move|true|false = if it's player 1's move -w|win|int-int = board indices where 4 was connected +| type | name | messages | +| ---- | ------ | --------------------------------------------- | +| d | draw | full = board is full | +| e | errors | full = column is full | +| m | move | true | +| w | win | int-int = board indices where 4 was connected | ## Command-line arguments @@ -78,4 +77,3 @@ for any corresponding short options. Report bugs to https://github.com/lonkaars/po-4-op-een-rij/. ``` - diff --git a/posts/po4.md b/posts/po4.md index c9770dc..a32331a 100644 --- a/posts/po4.md +++ b/posts/po4.md @@ -1,8 +1,3 @@ -[meta]: <title> (po connect-4 readme) -[meta]: <tags> (po4, po-connect-4, readme, github) -[meta]: <date> (April 1 2021) -[meta]: <author> (Loekaars) - # PO connect-4 ![](./banner.png) @@ -14,8 +9,8 @@ </p> > Some of this project's code is in Dutch (commit messages, documents etc.), -along with the whole website. This was originally a school project, but I'm -going to keep maintaining this project during my exams and summer break +> along with the whole website. This was originally a school project, but I'm +> going to keep maintaining this project during my exams and summer break ## Planned features: @@ -41,7 +36,8 @@ going to keep maintaining this project during my exams and summer break - [NextJS](https://nextjs.org/) for static react pages and html page routing - [socket.io](https://socket.io/) for bidirecitonal communication during a game - [SQLite](https://sqlite.org/index.html) for the database -- [nginx](https://nginx.org/en/) for serving static files generated by nextjs, caching and reverse proxy +- [nginx](https://nginx.org/en/) for serving static files generated by nextjs, + caching and reverse proxy A design prototype of the website can be found on [Figma](https://www.figma.com/file/rTciVQApAe6cwrH1Prl5Wn/4-op-een-rij?node-id=0%3A1). @@ -52,8 +48,8 @@ moving and updating it to be in api/readme.md. ## setup -To set up this project you'll need to install npm and pip dependencies, pull -all git submodules and compile voerbak and the sql extensions. +To set up this project you'll need to install npm and pip dependencies, pull all +git submodules and compile voerbak and the sql extensions. > I haven't figured out how to run this project on Windows, so please install > [WSL](https://docs.microsoft.com/en-us/windows/wsl/install-win10) if you want @@ -70,8 +66,8 @@ To start the setup process you only need to run the following command: ./configure ``` -The script calls sudo and apt install so some password input/manual -confirmation is required, but most of the install should be automated. +The script calls sudo and apt install so some password input/manual confirmation +is required, but most of the install should be automated. ### manual setup (other distro's) @@ -79,40 +75,44 @@ If your disto doesn't use the `apt` package manager, you can still run this project by following these steps: 0. `git clone https://github.com/lonkaars/po-4-op-een-rij` -1. Make sure you have [python](https://python.org/downloads) (with pip and venv) installed. -2. Make sure you have [nodejs](https://nodejs.org/en/download) (with npm) installed. +1. Make sure you have [python](https://python.org/downloads) (with pip and venv) + installed. +2. Make sure you have [nodejs](https://nodejs.org/en/download) (with npm) + installed. 3. Make sure you have [nginx](https://nginx.org/en/) installed. -4. Make sure you have [make](https://www.gnu.org/software/make/) and the gnu c compilers [gcc](https://gcc.gnu.org/) installed (most distro's will have these by default). +4. Make sure you have [make](https://www.gnu.org/software/make/) and the gnu c + compilers [gcc](https://gcc.gnu.org/) installed (most distro's will have + these by default). 5. Install typescript, react-scripts and yarn: - ```sh - npm i -g typescript yarn - ``` + ```sh + npm i -g typescript yarn + ``` 6. Create a new python virtual environment and install pip modules: - ```sh - python -m venv venv - source venv/bin/activate - pip install -r requirements.txt - ``` + ```sh + python -m venv venv + source venv/bin/activate + pip install -r requirements.txt + ``` 7. Install node modules: - ```sh - yarn - ``` + ```sh + yarn + ``` 8. Build voerbak: - ```sh - cd voerbak - make - ``` + ```sh + cd voerbak + make + ``` 9. Download submodules: - ```sh - git submodule init - git submodule update - ``` + ```sh + git submodule init + git submodule update + ``` 10. Initialize database and build SQL extensions: - ```sh - cd database - ./init_db.sh - make - ``` + ```sh + cd database + ./init_db.sh + make + ``` ## How to start @@ -134,4 +134,3 @@ sudo nginx -c $PWD/nginx.conf # this command is also in ./configure sed "s/user nobody/user $(whoami)/" -i nginx.conf ``` - diff --git a/tsconfig.json b/tsconfig.json index 35d51ea..2ba1e4f 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,29 +1,29 @@ { - "compilerOptions": { - "target": "es5", - "lib": [ - "dom", - "dom.iterable", - "esnext" - ], - "allowJs": true, - "skipLibCheck": true, - "strict": false, - "forceConsistentCasingInFileNames": true, - "noEmit": true, - "esModuleInterop": true, - "module": "esnext", - "moduleResolution": "node", - "resolveJsonModule": true, - "isolatedModules": true, - "jsx": "preserve" - }, - "include": [ - "next-env.d.ts", - "**/*.ts", - "**/*.tsx" - ], - "exclude": [ - "node_modules" - ] + "compilerOptions": { + "target": "es5", + "lib": [ + "dom", + "dom.iterable", + "esnext" + ], + "allowJs": true, + "skipLibCheck": true, + "strict": false, + "forceConsistentCasingInFileNames": true, + "noEmit": true, + "esModuleInterop": true, + "module": "esnext", + "moduleResolution": "node", + "resolveJsonModule": true, + "isolatedModules": true, + "jsx": "preserve" + }, + "include": [ + "next-env.d.ts", + "**/*.ts", + "**/*.tsx" + ], + "exclude": [ + "node_modules" + ] } |