Skip to content

Commit

Permalink
Merge pull request #49 from timlrx/sync-2.3.0
Browse files Browse the repository at this point in the history
Sync 2.3.0
  • Loading branch information
timlrx authored Nov 3, 2024
2 parents 8d95f64 + c048e99 commit 3f0c092
Show file tree
Hide file tree
Showing 20 changed files with 1,815 additions and 1,110 deletions.
2 changes: 2 additions & 0 deletions app/api/newsletter/route.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { NewsletterAPI } from 'pliny/newsletter'
import siteMetadata from '@/data/siteMetadata'

export const dynamic = 'force-static'

const handler = NewsletterAPI({
// @ts-ignore
provider: siteMetadata.newsletter.provider,
Expand Down
10 changes: 5 additions & 5 deletions app/blog/[...slug]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,10 @@ const layouts = {
PostBanner,
}

export async function generateMetadata({
params,
}: {
params: { slug: string[] }
export async function generateMetadata(props: {
params: Promise<{ slug: string[] }>
}): Promise<Metadata | undefined> {
const params = await props.params
const slug = decodeURI(params.slug.join('/'))
const post = allBlogs.find((p) => p.slug === slug)
const authorList = post?.authors || ['default']
Expand Down Expand Up @@ -80,7 +79,8 @@ export const generateStaticParams = async () => {
return paths
}

export default async function Page({ params }: { params: { slug: string[] } }) {
export default async function Page(props: { params: Promise<{ slug: string[] }> }) {
const params = await props.params
const slug = decodeURI(params.slug.join('/'))
const sortedPosts = sortPosts(allBlogs) as Blog[]
const postIndex = sortedPosts.findIndex((p) => p.slug === slug)
Expand Down
3 changes: 2 additions & 1 deletion app/blog/page/[page]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ export const generateStaticParams = async () => {
return paths
}

export default function Page({ params }: { params: { page: string } }) {
export default async function Page(props: { params: Promise<{ page: string }> }) {
const params = await props.params
const posts = allCoreContent(sortPosts(allBlogs))
const pageNumber = parseInt(params.page as string)
const initialDisplayPosts = posts.slice(
Expand Down
2 changes: 2 additions & 0 deletions app/robots.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { MetadataRoute } from 'next'
import siteMetadata from '@/data/siteMetadata'

export const dynamic = 'force-static'

export default function robots(): MetadataRoute.Robots {
return {
rules: {
Expand Down
1 change: 1 addition & 0 deletions app/seo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ interface PageSEOProps {
export function genPageMetadata({ title, description, image, ...rest }: PageSEOProps): Metadata {
return {
title,
description: description || siteMetadata.description,
openGraph: {
title: `${title} | ${siteMetadata.title}`,
description: description || siteMetadata.description,
Expand Down
2 changes: 2 additions & 0 deletions app/sitemap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import { MetadataRoute } from 'next'
import { allBlogs } from 'contentlayer/generated'
import siteMetadata from '@/data/siteMetadata'

export const dynamic = 'force-static'

export default function sitemap(): MetadataRoute.Sitemap {
const siteUrl = siteMetadata.siteUrl
const blogRoutes = allBlogs.map((post) => ({
Expand Down
2 changes: 1 addition & 1 deletion app/tag-data.json
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"data-science":3,"llm":1,"generative-ai":1,"notes":26,"mdx":2,"javascript":4,"markdown":2,"citations":1,"sqlite":2,"wasm":2,"duckdb":1,"benchmarks":3,"open-source":1,"musings":7,"fairness":2,"ethics":2,"veritas":2,"responsible-ai":2,"singapore":10,"philosophy":1,"defi":1,"amm":1,"crypto":3,"marketing":1,"graph-theory":4,"networks":7,"privacy":1,"next-js":1,"tailwind":1,"guide":1,"julia":3,"learning-julia":1,"agent-based-models":1,"python":7,"r":19,"gcp":2,"visualization":1,"serverless":1,"visualisation":10,"dashboard":3,"react":1,"spatial":5,"sg-economy":3,"web-scraping":3,"dags":1,"causal-inference":1,"spark":2,"big-data":2,"personal":4,"blogdown":1,"regression":7,"ols":6,"simulation":1,"metrics":1,"sg-social":2,"thesis-thursday":7,"stata":1,"binscatter":1}
{"personal":4,"thesis-thursday":7,"binscatter":1,"r":19,"web-scraping":3,"singapore":10,"sg-economy":3,"dashboard":3,"regression":7,"ols":6,"notes":27,"stata":1,"spatial":5,"sg-social":2,"visualisation":10,"musings":8,"simulation":1,"metrics":1,"graph-theory":4,"python":7,"blogdown":1,"spark":2,"big-data":2,"dags":1,"causal-inference":1,"networks":7,"javascript":4,"react":1,"crypto":3,"benchmarks":3,"julia":3,"visualization":1,"gcp":2,"serverless":1,"learning-julia":1,"agent-based-models":1,"next-js":2,"tailwind":1,"guide":1,"privacy":1,"marketing":1,"defi":1,"amm":1,"mdx":2,"markdown":2,"fairness":2,"ethics":2,"veritas":2,"philosophy":1,"responsible-ai":2,"data-science":4,"open-source":1,"sqlite":2,"wasm":2,"duckdb":1,"citations":1,"llm":2,"generative-ai":2}
12 changes: 10 additions & 2 deletions app/tags/[tag]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,12 @@ import { allBlogs } from 'contentlayer/generated'
import tagData from 'app/tag-data.json'
import { genPageMetadata } from 'app/seo'
import { Metadata } from 'next'
import { notFound } from 'next/navigation'

export async function generateMetadata({ params }: { params: { tag: string } }): Promise<Metadata> {
export async function generateMetadata(props: {
params: Promise<{ tag: string }>
}): Promise<Metadata> {
const params = await props.params
const tag = decodeURI(params.tag)
return genPageMetadata({
title: tag,
Expand All @@ -30,12 +34,16 @@ export const generateStaticParams = async () => {
return paths
}

export default function TagPage({ params }: { params: { tag: string } }) {
export default async function TagPage(props: { params: Promise<{ tag: string }> }) {
const params = await props.params
const tag = decodeURI(params.tag)
// Capitalize first letter and convert space to dash
const title = tag[0].toUpperCase() + tag.split(' ').join('-').slice(1)
const filteredPosts = allCoreContent(
sortPosts(allBlogs.filter((post) => post.tags && post.tags.map((t) => slug(t)).includes(tag)))
)
if (filteredPosts.length === 0) {
return notFound()
}
return <ListLayout posts={filteredPosts} title={title} />
}
59 changes: 32 additions & 27 deletions components/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,36 +7,41 @@ import ThemeSwitch from './ThemeSwitch'
import SearchButton from './SearchButton'

const Header = () => {
let headerClass = 'flex items-center w-full bg-white dark:bg-gray-950 justify-between py-10'
if (siteMetadata.stickyNav) {
headerClass += ' sticky top-0 z-50'
}

return (
<header className="flex items-center justify-between py-10">
<div>
<Link href="/" aria-label={siteMetadata.headerTitle}>
<div className="flex items-center justify-between">
<div className="mr-3">
<Logo />
</div>
{typeof siteMetadata.headerTitle === 'string' ? (
<div className="hidden h-6 text-2xl font-semibold sm:block">
{siteMetadata.headerTitle}
</div>
) : (
siteMetadata.headerTitle
)}
<header className={headerClass}>
<Link href="/" aria-label={siteMetadata.headerTitle}>
<div className="flex items-center justify-between">
<div className="mr-3">
<Logo />
</div>
</Link>
</div>
{typeof siteMetadata.headerTitle === 'string' ? (
<div className="hidden h-6 text-2xl font-semibold sm:block">
{siteMetadata.headerTitle}
</div>
) : (
siteMetadata.headerTitle
)}
</div>
</Link>
<div className="flex items-center space-x-4 leading-5 sm:space-x-6">
{headerNavLinks
.filter((link) => link.href !== '/')
.map((link) => (
<Link
key={link.title}
href={link.href}
className="hidden font-medium text-gray-900 dark:text-gray-100 sm:block"
>
{link.title}
</Link>
))}
<div className="no-scrollbar hidden max-w-40 items-center space-x-4 overflow-x-auto sm:flex sm:space-x-6 md:max-w-72 lg:max-w-96">
{headerNavLinks
.filter((link) => link.href !== '/')
.map((link) => (
<Link
key={link.title}
href={link.href}
className="block font-medium text-gray-900 hover:text-primary-500 dark:text-gray-100 dark:hover:text-primary-400"
>
{link.title}
</Link>
))}
</div>
<SearchButton />
<ThemeSwitch />
<MobileNav />
Expand Down
8 changes: 5 additions & 3 deletions components/Link.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,16 @@ const CustomLink = ({ href, ...rest }: LinkProps & AnchorHTMLAttributes<HTMLAnch
const isAnchorLink = href && href.startsWith('#')

if (isInternalLink) {
return <Link href={href} {...rest} />
return <Link className="break-words" href={href} {...rest} />
}

if (isAnchorLink) {
return <a href={href} {...rest} />
return <a className="break-words" href={href} {...rest} />
}

return <a target="_blank" rel="noopener noreferrer" href={href} {...rest} />
return (
<a className="break-words" target="_blank" rel="noopener noreferrer" href={href} {...rest} />
)
}

export default CustomLink
105 changes: 68 additions & 37 deletions components/MobileNav.tsx
Original file line number Diff line number Diff line change
@@ -1,32 +1,39 @@
'use client'

import { useState } from 'react'
import { Dialog, Transition } from '@headlessui/react'
import { disableBodyScroll, enableBodyScroll, clearAllBodyScrollLocks } from 'body-scroll-lock'
import { Fragment, useState, useEffect, useRef } from 'react'
import Link from './Link'
import headerNavLinks from '@/data/headerNavLinks'

const MobileNav = () => {
const [navShow, setNavShow] = useState(false)
const navRef = useRef(null)

const onToggleNav = () => {
setNavShow((status) => {
if (status) {
document.body.style.overflow = 'auto'
enableBodyScroll(navRef.current)
} else {
// Prevent scrolling
document.body.style.overflow = 'hidden'
disableBodyScroll(navRef.current)
}
return !status
})
}

useEffect(() => {
return clearAllBodyScrollLocks
})

return (
<>
<button aria-label="Toggle Menu" onClick={onToggleNav} className="sm:hidden">
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 20 20"
fill="currentColor"
className="h-8 w-8 text-gray-900 dark:text-gray-100"
className="h-8 w-8 text-gray-900 hover:text-primary-500 dark:text-gray-100 dark:hover:text-primary-400"
>
<path
fillRule="evenodd"
Expand All @@ -35,41 +42,65 @@ const MobileNav = () => {
/>
</svg>
</button>
<div
className={`fixed left-0 top-0 z-10 h-full w-full transform bg-white opacity-95 duration-300 ease-in-out dark:bg-gray-950 dark:opacity-[0.98] ${
navShow ? 'translate-x-0' : 'translate-x-full'
}`}
>
<div className="flex justify-end">
<button className="mr-8 mt-11 h-8 w-8" aria-label="Toggle Menu" onClick={onToggleNav}>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 20 20"
fill="currentColor"
className="text-gray-900 dark:text-gray-100"
>
<path
fillRule="evenodd"
d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z"
clipRule="evenodd"
/>
</svg>
</button>
</div>
<nav className="fixed mt-8 h-full">
{headerNavLinks.map((link) => (
<div key={link.title} className="px-12 py-4">
<Link
href={link.href}
className="text-2xl font-bold tracking-widest text-gray-900 dark:text-gray-100"
<Transition appear show={navShow} as={Fragment} unmount={false}>
<Dialog as="div" onClose={onToggleNav} unmount={false}>
<Transition.Child
as={Fragment}
enter="ease-out duration-300"
enterFrom="opacity-0"
enterTo="opacity-100"
leave="ease-in duration-200"
leaveFrom="opacity-100"
leaveTo="opacity-0"
unmount={false}
>
<div className="fixed inset-0 z-60 bg-black/25" />
</Transition.Child>

<Transition.Child
as={Fragment}
enter="transition ease-in-out duration-300 transform"
enterFrom="translate-x-full opacity-0"
enterTo="translate-x-0 opacity-95"
leave="transition ease-in duration-200 transform"
leaveFrom="translate-x-0 opacity-95"
leaveTo="translate-x-full opacity-0"
unmount={false}
>
<Dialog.Panel className="fixed left-0 top-0 z-70 h-full w-full bg-white opacity-95 duration-300 dark:bg-gray-950 dark:opacity-[0.98]">
<nav
ref={navRef}
className="mt-8 flex h-full basis-0 flex-col items-start overflow-y-auto pl-12 pt-2 text-left"
>
{headerNavLinks.map((link) => (
<Link
key={link.title}
href={link.href}
className="mb-4 py-2 pr-4 text-2xl font-bold tracking-widest text-gray-900 outline outline-0 hover:text-primary-500 dark:text-gray-100 dark:hover:text-primary-400"
onClick={onToggleNav}
>
{link.title}
</Link>
))}
</nav>

<button
className="fixed right-4 top-7 z-80 h-16 w-16 p-4 text-gray-900 hover:text-primary-500 dark:text-gray-100 dark:hover:text-primary-400"
aria-label="Toggle Menu"
onClick={onToggleNav}
>
{link.title}
</Link>
</div>
))}
</nav>
</div>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor">
<path
fillRule="evenodd"
d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z"
clipRule="evenodd"
/>
</svg>
</button>
</Dialog.Panel>
</Transition.Child>
</Dialog>
</Transition>
</>
)
}
Expand Down
2 changes: 1 addition & 1 deletion components/SearchButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ const SearchButton = () => {
viewBox="0 0 24 24"
strokeWidth={1.5}
stroke="currentColor"
className="h-6 w-6 text-gray-900 dark:text-gray-100"
className="h-6 w-6 text-gray-900 hover:text-primary-500 dark:text-gray-100 dark:hover:text-primary-400"
>
<path
strokeLinecap="round"
Expand Down
Loading

0 comments on commit 3f0c092

Please sign in to comment.