feat: add tina CMS; layouts

This commit is contained in:
Aaron Yarborough 2025-03-28 21:24:03 +00:00
parent 09f7530d4a
commit 662a461c3c
33 changed files with 13036 additions and 1243 deletions

4
.gitignore vendored
View file

@ -35,4 +35,6 @@ yarn-error.log*
*.tsbuildinfo
next-env.d.ts
deploy-vars.sh
deploy-vars.sh
node_modules
.env

View file

@ -0,0 +1,10 @@
---
title: Sex & Punishment
author: Eric Berkowitz
stars: 3
readDate: 2024-05-31T23:00:00.000Z
url: 'https://app.thestorygraph.com/books/79ffd129-2325-45d0-826d-c6969a09e239'
thumbnailUrl: 'https://cdn.thestorygraph.com/lqtuj9d7fdj1qw1lei01yby42h9z'
tags: 'non-fiction, history'
---

View file

@ -0,0 +1,10 @@
---
tags: 'non-fiction, classics, history'
title: A Night To Remember
author: Walter Lord
stars: 3.5
readDate: 2024-06-30T23:00:00.000Z
url: 'https://app.thestorygraph.com/books/5192ee9e-ffb2-4c72-a7c3-8051d950d66f'
thumbnailUrl: 'https://cdn.thestorygraph.com/pdwshwni6jyc7ivp55j6tsxzngfl'
---

View file

@ -1,9 +1,10 @@
---
title: Alice's Adventures in Wonderland
title: Alice's Adventures in Wonderland
author: Lewis Carroll
stars: 3
readDate: 2024
url: https://app.thestorygraph.com/books/83b0e44a-06fe-4042-9d2d-e4f41244fb9c
thumbnailUrl: https://cdn.thestorygraph.com/l83t3e6wh6tq7dqxbvrba34ee2nb
tags: fiction, classics, fantasy
---
readDate: 2023-04-30T23:00:00.000Z
url: 'https://app.thestorygraph.com/books/83b0e44a-06fe-4042-9d2d-e4f41244fb9c'
thumbnailUrl: 'https://cdn.thestorygraph.com/l83t3e6wh6tq7dqxbvrba34ee2nb'
tags: 'fiction, classics, fantasy'
---

View file

@ -0,0 +1,10 @@
---
title: Animal Farm
author: George Orwell
stars: 4
readDate: 2023-01-01T00:00:00.000Z
url: 'https://app.thestorygraph.com/books/f4b706d9-4ed9-4b15-85e0-2493581de818'
thumbnailUrl: 'https://cdn.thestorygraph.com/jztibk5xvnynw7mh7hfw0orhbzuh'
tags: 'fiction, classics, dystopian'
---

View file

@ -2,8 +2,9 @@
title: Cities That Shaped The Ancient World
author: John Julius Norwich
stars: 3.5
readDate: 2024
url: https://app.thestorygraph.com/books/20bc1ff4-56bb-4e88-a403-bc150d45f9d2
thumbnailUrl: https://cdn.thestorygraph.com/i8znw2yrd6p4m76dx6rvyuyd48h2
tags: non-fiction, history
readDate: 2023-02-01T00:00:00.000Z
url: 'https://app.thestorygraph.com/books/20bc1ff4-56bb-4e88-a403-bc150d45f9d2'
thumbnailUrl: 'https://cdn.thestorygraph.com/i8znw2yrd6p4m76dx6rvyuyd48h2'
tags: 'non-fiction, history'
---

View file

@ -1,10 +1,11 @@
---
title: "The Song of Achilles"
title: The Song of Achilles
author: Madline Miller
stars: 2.5
readDate: 2025/03/22
url: https://app.thestorygraph.com/books/9202845d-26cd-4a85-b15f-408116117028
thumbnailUrl: https://cdn.thestorygraph.com/m8cw3kb3qx4h2jl8kg0u4m3txhie
tags: fiction, fantasy, romance
readDate: 2025-03-22T00:00:00.000Z
url: 'https://app.thestorygraph.com/books/9202845d-26cd-4a85-b15f-408116117028'
thumbnailUrl: 'https://cdn.thestorygraph.com/m8cw3kb3qx4h2jl8kg0u4m3txhie'
tags: 'fiction, fantasy, romance'
---
This one really tailed off half-way through. I found the characters very one-dimensional (Patrcolus pines after Achilles, Achilles has muscles and can swing a sword fast), but the story took me at least up until the half-way mark. Weirdly I wasn't interested too much when the actual Iliad story line started to get going - maybe because I've heard it a million times before - and their essentially non-existent romance (more accurately an inch-deep obsession, I'd argue) didn't exactly inspire me to carry on reading.

View file

@ -0,0 +1,10 @@
---
title: Star Maker
author: Olaf Stapledon
stars: 4.5
readDate: 2023-03-01T00:00:00.000Z
url: 'https://app.thestorygraph.com/books/c1d60727-8e24-4a1f-9f0e-974d68a934d2'
thumbnailUrl: 'https://cdn.thestorygraph.com/bln6q5k1v7ealnwss4msv0jixlhk'
tags: 'fiction, classics, science fiction'
---

View file

@ -1,9 +1,10 @@
---
title: "Stasiland: Stories from Behind the Berlin Wall"
title: 'Stasiland: Stories from Behind the Berlin Wall'
author: Anna Funder
stars: 4
readDate: 2025/03/22
url: https://app.thestorygraph.com/books/9202845d-26cd-4a85-b15f-408116117028
thumbnailUrl: https://cdn.thestorygraph.com/gphzjvwbhr8d5agrieobx0yy2xhb
tags: non-fiction, history, politics
readDate: 2023-02-01T00:00:00.000Z
url: 'https://app.thestorygraph.com/books/9202845d-26cd-4a85-b15f-408116117028'
thumbnailUrl: 'https://cdn.thestorygraph.com/gphzjvwbhr8d5agrieobx0yy2xhb'
tags: 'non-fiction, history, politics'
---

View file

@ -2,8 +2,9 @@
title: Stray Reflections
author: Muhammad Iqbal
stars: 5
readDate: 2025/03/22
url:
thumbnailUrl:
tags: non-fiction, politics, philosophy
readDate: 2023-03-01T00:00:00.000Z
url: null
thumbnailUrl: null
tags: 'non-fiction, politics, philosophy'
---

View file

@ -0,0 +1,10 @@
---
title: The Marmalade Diaries
author: Ben Aitken
stars: 4.5
readDate: 2023-01-01T00:00:00.000Z
url: 'https://app.thestorygraph.com/books/406fe719-07de-44ba-b4e7-86714f638cf9'
thumbnailUrl: 'https://cdn.thestorygraph.com/ou589mae0cxxup4cqq1mlnqdaj62'
tags: non-fiction
---

View file

@ -0,0 +1,10 @@
---
title: The Midnight Library
author: Matt Haig
stars: 4
readDate: 2024-06-30T23:00:00.000Z
url: 'https://app.thestorygraph.com/books/d9c7ed04-6148-4e01-a118-d96cba16f507'
thumbnailUrl: 'https://cdn.thestorygraph.com/b9g1h8bhrqz7qs2fagzhsixbp6xy'
tags: 'fiction, literary, science fiction'
---

View file

@ -1,10 +1,11 @@
---
title: To Be Taught, If Fortunate
title: 'To Be Taught, If Fortunate'
author: Becky Chambers
stars: 4.5
readDate: 2025/03/22
url: https://app.thestorygraph.com/books/fca2631f-b3e2-4b9d-a2fd-4c1ec5e4d3f7
thumbnailUrl: https://cdn.thestorygraph.com/8ep9zjc581zefkzfyhtizqjnz8u5
tags: fiction, science fiction
readDate: 2025-03-22T00:00:00.000Z
url: 'https://app.thestorygraph.com/books/fca2631f-b3e2-4b9d-a2fd-4c1ec5e4d3f7'
thumbnailUrl: 'https://cdn.thestorygraph.com/8ep9zjc581zefkzfyhtizqjnz8u5'
tags: 'fiction, science fiction'
---
Really enjoyed this! It was reflective yet lighthearted. I also love this specific flavour of sci-fi, where vastly different species of aliens and worlds are thought up and articulated beautifully by the author.
Really enjoyed this! It was reflective yet lighthearted. I also love this specific flavour of sci-fi, where vastly different species of aliens and worlds are thought up and articulated beautifully by the author.

View file

@ -0,0 +1,11 @@
---
title: When the Moon Hits Your Eye
author: John Scalzi
stars: 2.5
readDate: 2025-03-26T00:00:00.000Z
url: 'https://app.thestorygraph.com/books/4f3c3b02-3d2b-4765-93ac-4656133bbec5'
thumbnailUrl: 'https://cdn.thestorygraph.com/718cb49yqfu02zekeq22yl8lgm8v'
tags: 'fiction, science fiction'
---
Some interesting chapters in here exploring how governments and various public/private institutions could deal with the moon randomly turning to choose, but a lot of fluff in between them. Can't really recommend, unless you want to skip to the good bits like I did.

View file

@ -2,7 +2,9 @@
title: Attitudes to reading, and how mine have changed
pubdate: 2025-03-18T00:00:00.000Z
desc: I was discussing reading habits with my good friend Beth, specifically around reading multiple books at once vs. reading a single book at a time...
tags: reading, books
tags:
- reading
- books
---
I was discussing reading habits with my good friend Beth, specifically around reading multiple books at once vs. reading a single book at a time (we're both very members of the former group), and it got me thinking as to why there seems to me to be this split in reading habits, and where it might come from.

View file

@ -2,7 +2,9 @@
title: Migrating from GitHub to Forgejo
pubdate: 2025-03-16T00:00:00.000Z
desc: I recently moved all of my reposfrom GitHub to a self-hosted Forgejo instance running on my own servers.
tags: tech, hosting
tags:
- tech
- hosting
---
I recently moved all of my repos (public and private) from GitHub to a self-hosted [Forgejo](https://forgejo.org/) instance running on my own servers.

View file

@ -3,7 +3,10 @@ title: Performance considerations when writing a TCP game server in dotnet
pubdate: 2025-02-23T21:12:37.864Z
moddate: 2025-03-21T21:12:47.864Z
desc: While writing a TCP game server in dotnet for a hobby project, I learned a few ways to improve the efficiency and scalability of the server while running into some performance issues. Here's what I learned!
tags: tech, programming, dotnet
tags:
- tech
- programming
- dotnet
---
While writing a TCP game server in dotnet for a hobby project (check it out [here](https://github.com/AaronJY/GServer)), I learned a few ways to improve the efficiency and scalability of the server while running into some performance issues.

View file

@ -2,7 +2,9 @@
title: "Quickwrite: A reflection on Wintering by Katherine May"
pubdate: 2025-03-09T00:00:00.000Z
desc: "Katherine May draws a unique parallel in her book Wintering between the coming of and living through the winter months, and those liminal times in your life when a gap opens up underfoot and swallows us whole...."
tags: books, reading
tags:
- books
- reading
---
Katherine May draws a unique parallel in her book Wintering between the coming of and living through the winter months, and those liminal times in your life when a gap opens up underfoot and swallows us whole. Its never clear when such a gap appears to steal us away, and how long exactly well spend in the liminality we fall into, and most of us claw at the walls in an attempt to scale up and out, back into the warm light of day how things used to be. Katherines book is an account of how she learned to see it more as a wave to ride than a locked room to break free from. Her ultimate message is an argument to reframe it, or to see it for what it really is; not as a bleak, timeless realm devoid of hope and light, but of a necessary state we all find ourselves in at various points in our lives, and that this realm offers its own medicines for those who care to look. These seasonal and spiritual changes Katherine refers to as Wintering aptly a verb to articulate its ephemeral nature serve much in the same way a good nights sleep does: while a deep and peaceful sleep purges metabolic waste and toxins from our brains, a wintering can offer a more spiritual cleanse; a hibernation after a hot and unrelenting summer; a mirror held up in front to break our blind and frenzied sprint towards a goal weve long forgotten; a beloved teacher from our school days with a soft voice, reassuring smile and placations to calm our nerves.

View file

@ -2,7 +2,9 @@
title: Deploying aaronjy.me on a Google Storage bucket
pubdate: 2024-05-01T00:00:00.000Z
desc: "Google Cloud Storage is an effective solution for hosting static sites, offering a simple and scalable way to manage web assets. A manual deployment strategy involves four key steps: backing up existing files to a backup bucket, removing sensitive files for security, uploading the latest site files from the build directory, and invalidating Googles global cache to ensure users access updated content."
tags: tech, hosting
tags:
- tech
- hosting
---
Google actually has [documentation](https://cloud.google.com/storage/docs/hosting-static-website) on how to deploy a static site to a storage bucket, but I wanted to talk about how I handle deployments, as Google doesn't covert that!

View file

@ -2,7 +2,8 @@
title: Supporting content file structure changes on a static site
pubdate: 2024-03-18T16:47:32.150Z
desc: Static site generators (SSGs) convert complex site sources into HTML, CSS, and JS, allowing flexible hosting options. While they offer benefits like speed and low costs, updating content can be challenging for non-technical users. A solution involves assigning unique identifiers to articles and creating a URL mapping file to simplify restructuring and managing content links.
tags: tech
tags:
- tech
---
Static site generators (SSGs) are great. They take your complex site source and distil it down to the web's native language: HTML, CSS and JS. You can host your files anywhere: in cloud-native storage buckets; on low-cost CPanel hosting; on global CDNs; your old Lenovo ThinkPad in your cupboard running an Apache server that hasn't been patched since 2008; the list goes on. Wanna go further and throw away your CMS? Cool, you can use markdown files and a text editor as your CMS.

13833
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -1,10 +1,10 @@
{
"name": "www-aaronjy-2024",
"version": "1.6.3.0",
"name": "www-aaronjy-me",
"version": "1.7.0.0",
"private": true,
"type": "module",
"scripts": {
"dev": "next dev",
"dev": "TINA_PUBLIC_IS_LOCAL=true tinacms dev -c \"next dev\"",
"build": "next build",
"postbuild": "next-sitemap --config next-sitemap.config.cjs",
"start": "next start",
@ -17,13 +17,13 @@
"dependencies": {
"@highlightjs/cdn-assets": "^11.11.1",
"date-fns": "^4.1.0",
"feather-icons": "^4.29.2",
"highlight.js": "^11.11.0",
"next": "^14.2.6",
"next-seo": "^6.5.0",
"react": "^18",
"react-dom": "^18"
},
"react-dom": "^18",
"tinacms": "^2.7.3"
},
"devDependencies": {
"@babel/core": "^7.24.0",
"@babel/preset-env": "^7.25.4",
@ -32,6 +32,7 @@
"@commitlint/config-conventional": "^19.1.0",
"@testing-library/jest-dom": "^6.4.2",
"@testing-library/react": "^16.0.0",
"@tinacms/cli": "^1.9.3",
"@types/jest": "^29.5.12",
"babel-jest": "^29.7.0",
"eslint": "^9.9.0",

2
public/admin/.gitignore vendored Normal file
View file

@ -0,0 +1,2 @@
index.html
assets/

View file

@ -0,0 +1,34 @@
import { formatDate } from '@/lib/helpers'
import { NextSeo } from 'next-seo'
import Link from 'next/link'
import React from 'react'
function Book({ attributes, html }) {
return (
<>
<h1>{attributes.title}<br /><small>by {attributes.author}</small></h1>
<article>
<NextSeo
title={attributes.title} description={attributes.desc} openGraph={
{
title: attributes.title,
description: attributes.desc,
type: 'article',
article: {
publishedTime: attributes.pubdate ?? null
}
}
}
/>
<div>
<Link href='./'>Back...</Link>
{attributes.pubdate && <p>{formatDate(attributes.pubdate)}</p>}
<div data-test='content' dangerouslySetInnerHTML={{ __html: html || "<p>(no review)</p>" }} />
</div>
</article>
</>
)
}
export default Book

View file

@ -1,16 +1,14 @@
import Image from 'next/image'
import style from './BookListItem.module.css'
import Link from 'next/link'
export default function BookListItem ({ href, title, author, stars, readDate, url, thumbnailUrl, tags, review }) {
export default function BookListItem({ href, title, author, stars, readDate, url, thumbnailUrl, tags, review }) {
return (
<div className={style.item}>
<div className={style['image-container']}>
<Link href={href}>
<Image src={thumbnailUrl ?? '/img/book-placeholder.jpg'} alt={`Book cover for ${title}`} width={300} height={464} />
</Link>
</div>
<Link href={href}>
<div className={style['thumb']} style={{
backgroundImage: `url(${thumbnailUrl ?? '/img/book-placeholder.jpg'})`
}}></div>
</Link>
<div>
<h2 className={style.heading}>
<Link href={href}>{title}</Link>

View file

@ -3,7 +3,6 @@
display: flex;
flex-direction: column;
flex-grow: 1;
justify-content: flex-end;
}
.heading {
@ -16,3 +15,12 @@
/* font-weight: bold; */
}
.thumb {
width: auto;
height: 200px;
background-position: center;
background-repeat: no-repeat;
background-size: cover;
border-radius: 6px;
}

View file

@ -56,9 +56,7 @@ export function getStaticEntries (contentPath) {
const directoryItems = fs.readdirSync(contentPath, { withFileTypes: true })
return directoryItems.map((dirent) =>
getMarkdownEntry(`${dirent.path}/${dirent.name}`)
).sort((a, b) =>
new Date(b.attributes.pubdate).getTime() - new Date(a.attributes.pubdate).getTime()
)
);
}
export function getContentTags (contentPath) {

View file

@ -1,7 +1,7 @@
import React from 'react'
import DefaultLayout from '@/layouts/DefaultLayout/DefaultLayout'
import { getStaticEntryPaths, getStaticEntryProps } from '@/lib/content'
import Article from '@/components/Article/Article'
import Book from '@/components/Book/Book'
export const getStaticPaths = () => getStaticEntryPaths('./content/books')
export const getStaticProps = (ctx) => getStaticEntryProps('./content/books', ctx)
@ -9,7 +9,7 @@ export const getStaticProps = (ctx) => getStaticEntryProps('./content/books', ct
export default function LibrarySingle ({ attributes, html }) {
return (
<DefaultLayout>
<Article attributes={attributes} html={html} />
<Book attributes={attributes} html={html} />
</DefaultLayout>
)
}

View file

@ -6,22 +6,22 @@ import { NextSeo } from 'next-seo'
export const Title = 'Library'
export const getStaticProps = () => ({
props: {
bookEntries: getStaticEntries('./content/books')
export const getStaticProps = () => {
const bookEntries = getStaticEntries('./content/books')
.sort((a, b) => {
return b.attributes.readDate - a.attributes.readDate
});
return {
props: {
// Silliness to make sure dates don't get passed to the
// page function below as [object Object]
bookEntries: JSON.parse(JSON.stringify(bookEntries))
}
}
})
const SORT_TITLE = 'title'
const SORT_RATING = 'rating'
export default function Library ({ bookEntries }) {
// const tags = getTags(bookEntries)
// const [sorter, setSorter] = useState(SORT_TITLE)
// console.log(bookEntries)
}
export default function Library({ bookEntries }) {
return (
<DefaultLayout>
<NextSeo
@ -36,37 +36,12 @@ export default function Library ({ bookEntries }) {
<h1>{Title}</h1>
<section>
<div className='form-group'>
<label htmlFor='sortBy'>Sort by:&nbsp;</label>
<select id='sortBy' name='sort-by'>
<option value={SORT_TITLE}>Title (A-Z)</option>
<option value={SORT_RATING}>Rating (5-0)</option>
{/* <optgroup label="Genre">
{Object.keys(tags).sort().map(tag => (
<option>{tag}</option>
))}
</optgroup> */}
</select>
</div>
<Grid columns={4}>
{bookEntries.map((entry, i) => (
<Grid columns={5}>
{bookEntries.map((entry, _) => (
<BookListItem key={entry.attributes.title} {...entry.attributes} href={`/library/${entry.slug}`} />
))}
</Grid>
</section>
</DefaultLayout>
)
}
// function getTags (bookEntries) {
// const tags = {}
// for (const entry of bookEntries) {
// const entryTags = entry.attributes.tags.split(', ')
// for (const entryTag of entryTags) {
// tags[entryTag] = tags[entryTag] ? tags[entryTag] + 1 : 1
// }
// }
// return tags
// }
}

View file

@ -7,6 +7,9 @@ import StaticContentList from '@/components/StaticContentList/StaticContentList'
export const getStaticProps = () => ({
props: {
postEntries: getStaticEntries('./content/writing')
.sort((a, b) =>
new Date(b.attributes.pubdate).getTime() - new Date(a.attributes.pubdate).getTime()
)
}
})

1
tina/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
__generated__

136
tina/config.js Normal file
View file

@ -0,0 +1,136 @@
import { defineConfig } from "tinacms";
// Your hosting provider likely exposes this as an environment variable
const branch =
process.env.GITHUB_BRANCH ||
process.env.VERCEL_GIT_COMMIT_REF ||
process.env.HEAD ||
"main";
export default defineConfig({
branch,
// Get this from tina.io
clientId: process.env.NEXT_PUBLIC_TINA_CLIENT_ID,
// Get this from tina.io
token: process.env.TINA_TOKEN,
build: {
outputFolder: "admin",
publicFolder: "public",
},
media: {
tina: {
mediaRoot: "",
publicFolder: "public",
},
},
// See docs on content modeling for more info on how to setup new content models: https://tina.io/docs/schema/
schema: {
collections: [
{
name: "books",
label: "Library",
path: 'content/books',
match: {
include: "*"
},
format: "md",
fields: [
{
type: "string",
name: "title",
label: "Title",
isTitle: true,
required: true,
},
{
type: 'string',
name: "author",
label: "Author",
required: true
},
{
type: 'number',
name: "stars",
label: "Stars",
required: true
},
{
type: 'datetime',
name: "readDate",
label: "Read date",
required: false
},
{
type: 'string',
name: "url",
label: "URL",
required: false
},
{
type: 'string',
name: "thumbnailUrl",
label: "Thumbnail URL",
required: false
},
{
type: 'string',
name: "tags",
label: "Tags",
required: true
},
{
type: "rich-text",
name: "body",
label: "Body",
isBody: true,
},
]
},
{
name: "writing",
label: "Writing",
path: "content/writing",
match: {
include: "*"
},
format: "md",
fields: [
{
type: "string",
name: "title",
label: "Title",
isTitle: true,
required: true,
},
{
type: "datetime",
name: "pubdate",
label: "Publish Date",
},
{
type: "string",
ui: {
component: "textarea"
},
name: "desc",
label: "Description",
},
{
type: "string",
name: "tags",
label: "Tags",
list: true
},
{
type: "rich-text",
name: "body",
label: "Body",
isBody: true,
},
],
},
],
},
});

1
tina/tina-lock.json Normal file

File diff suppressed because one or more lines are too long