Skip to content

Commit 2d1ff99

Browse files
changes specific to transitivebullsh.it
1 parent 163fda4 commit 2d1ff99

27 files changed

+593
-54
lines changed

components/HeroHeader.tsx

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
import raf from 'raf'
2+
import random from 'random'
3+
import React, { Component } from 'react'
4+
import FluidAnimation from 'react-fluid-animation'
5+
6+
const exp = random.exponential()
7+
const numSplatsPerEpoch = 1
8+
const minSplatRadius = 0.01
9+
const maxSplatRadius = 0.03
10+
11+
export class HeroHeader extends Component<{
12+
className?: string
13+
}> {
14+
_time: number = Date.now()
15+
_direction: number
16+
_tickRaf: any
17+
_timeout: any
18+
_animation: any
19+
20+
componentDidMount() {
21+
this._time = Date.now()
22+
this._direction = 1
23+
this._reset()
24+
this._tick()
25+
}
26+
27+
componentWillUnmount() {
28+
if (this._tickRaf) {
29+
raf.cancel(this._tickRaf)
30+
this._tickRaf = null
31+
}
32+
33+
if (this._timeout) {
34+
clearTimeout(this._timeout)
35+
this._timeout = null
36+
}
37+
}
38+
39+
render() {
40+
return (
41+
<FluidAnimation
42+
className={this.props.className}
43+
animationRef={this._animationRef}
44+
/>
45+
)
46+
}
47+
48+
_animationRef = (ref) => {
49+
this._animation = ref
50+
this._reset()
51+
}
52+
53+
_reset() {
54+
if (this._animation) {
55+
this._animation.config.splatRadius = random.float(
56+
minSplatRadius,
57+
maxSplatRadius
58+
)
59+
this._animation.addRandomSplats(random.int(100, 180))
60+
}
61+
}
62+
63+
_tick = () => {
64+
this._tickRaf = null
65+
this._timeout = null
66+
67+
let scale = 1.0
68+
69+
if (this._animation) {
70+
const w = this._animation.width
71+
const h = this._animation.height
72+
73+
// adjust the intensity scale depending on the canvas width, so it's less
74+
// intense on smaller screens
75+
const s = Math.max(0.1, Math.min(1, w / 1200))
76+
scale = Math.pow(s, 1.2)
77+
78+
this._animation.config.splatRadius = random.float(
79+
minSplatRadius * scale,
80+
maxSplatRadius * scale
81+
)
82+
83+
const splats = []
84+
for (let i = 0; i < numSplatsPerEpoch; ++i) {
85+
const color = [random.float(10), random.float(10), random.float(10)]
86+
87+
const w0 = w / 3.0
88+
const w1 = (w * 2.0) / 3.0
89+
90+
const h0 = h / 3.0
91+
const h1 = (h * 2.0) / 3.0
92+
93+
// eslint-disable-next-line no-constant-condition
94+
while (true) {
95+
const x = random.float(w)
96+
const y = random.float(h)
97+
98+
// favor uniformly distributed samples within the center-ish of the canvas
99+
if (x > w0 && x < w1 && y > h0 && y < h1) {
100+
continue
101+
}
102+
103+
const dx = random.float(-1, 1) * random.float(200, 3000) * scale
104+
const dy = random.float(-1, 1) * random.float(200, 3000) * scale
105+
const splat = { x, y, dx, dy, color }
106+
splats.push(splat)
107+
break
108+
}
109+
110+
// old version which generated samples along a circle
111+
// const t = random.float(2 * Math.PI)
112+
// const cos = Math.cos(t)
113+
// const sin = Math.sin(t)
114+
// const x = w / 2 + r * cos
115+
// const y = h / 2 + r * sin + yOffset
116+
// const k = random.float() > 0.98 ? random.float(3, 10) : 1
117+
// const dx = k * random.float(-1, 1) * random.float(50, 300) * cos
118+
// const dy = k * random.float(-1, 1) * random.float(50, 300) * sin
119+
// const splat = { x, y, dx, dy, color }
120+
// splats.push(splat)
121+
}
122+
123+
this._animation.addSplats(splats)
124+
}
125+
126+
// using an exponential distribution here allows us to favor bursts of activity
127+
// but also allow for more occasional pauses
128+
const dampenedScale = Math.pow(scale, 0.2)
129+
const timeout = (exp() * 100) / dampenedScale
130+
131+
this._timeout = setTimeout(() => {
132+
this._tickRaf = raf(this._tick)
133+
}, timeout)
134+
}
135+
}

components/NotionPage.tsx

Lines changed: 54 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,13 @@ import Image from 'next/legacy/image'
44
import Link from 'next/link'
55
import { useRouter } from 'next/router'
66
import { type PageBlock } from 'notion-types'
7-
import { formatDate, getBlockTitle, getPageProperty } from 'notion-utils'
7+
import {
8+
formatDate,
9+
getBlockTitle,
10+
getPageProperty,
11+
normalizeTitle,
12+
parsePageId
13+
} from 'notion-utils'
814
import * as React from 'react'
915
import BodyClassName from 'react-body-classname'
1016
import { type NotionComponents, NotionRenderer } from 'react-notion-x'
@@ -76,15 +82,6 @@ const Collection = dynamic(() =>
7682
(m) => m.Collection
7783
)
7884
)
79-
const Equation = dynamic(() =>
80-
import('react-notion-x/build/third-party/equation').then((m) => m.Equation)
81-
)
82-
const Pdf = dynamic(
83-
() => import('react-notion-x/build/third-party/pdf').then((m) => m.Pdf),
84-
{
85-
ssr: false
86-
}
87-
)
8885
const Modal = dynamic(
8986
() =>
9087
import('react-notion-x/build/third-party/modal').then((m) => {
@@ -141,11 +138,35 @@ const propertyTextValue = (
141138
return defaultFn()
142139
}
143140

141+
const propertySelectValue = (
142+
{ schema, value, key, pageHeader },
143+
defaultFn: () => React.ReactNode
144+
) => {
145+
value = normalizeTitle(value)
146+
147+
if (pageHeader && schema.type === 'multi_select' && value) {
148+
return (
149+
<Link href={`/tags/${value}`} key={key}>
150+
<a>{defaultFn()}</a>
151+
</Link>
152+
)
153+
}
154+
155+
return defaultFn()
156+
}
157+
158+
const HeroHeader = dynamic<{ className?: string }>(
159+
() => import('./HeroHeader').then((m) => m.HeroHeader),
160+
{ ssr: false }
161+
)
162+
144163
export function NotionPage({
145164
site,
146165
recordMap,
147166
error,
148-
pageId
167+
pageId,
168+
tagsPage,
169+
propertyToFilterName
149170
}: types.PageProps) {
150171
const router = useRouter()
151172
const lite = useSearchParam('lite')
@@ -156,14 +177,13 @@ export function NotionPage({
156177
nextLink: Link,
157178
Code,
158179
Collection,
159-
Equation,
160-
Pdf,
161180
Modal,
162181
Tweet,
163182
Header: NotionPageHeader,
164183
propertyLastEditedTimeValue,
165184
propertyTextValue,
166-
propertyDateValue
185+
propertyDateValue,
186+
propertySelectValue
167187
}),
168188
[]
169189
)
@@ -188,6 +208,8 @@ export function NotionPage({
188208
// parsePageId(block?.id) === parsePageId(site?.rootNotionPageId)
189209
const isBlogPost =
190210
block?.type === 'page' && block?.parent_table === 'collection'
211+
const isBioPage =
212+
parsePageId(block?.id) === parsePageId('8d0062776d0c4afca96eb1ace93a7538')
191213

192214
const showTableOfContents = !!isBlogPost
193215
const minTableOfContentsItems = 3
@@ -201,6 +223,16 @@ export function NotionPage({
201223

202224
const footer = React.useMemo(() => <Footer />, [])
203225

226+
const pageCover = React.useMemo(() => {
227+
if (isBioPage) {
228+
return (
229+
<HeroHeader className='notion-page-cover-wrapper notion-page-cover-hero' />
230+
)
231+
} else {
232+
return null
233+
}
234+
}, [isBioPage])
235+
204236
if (router.isFallback) {
205237
return <Loading />
206238
}
@@ -209,7 +241,9 @@ export function NotionPage({
209241
return <Page404 site={site} pageId={pageId} error={error} />
210242
}
211243

212-
const title = getBlockTitle(block, recordMap) || site.name
244+
const name = getBlockTitle(block, recordMap) || site.name
245+
const title =
246+
tagsPage && propertyToFilterName ? `${propertyToFilterName} ${name}` : name
213247

214248
console.log('notion page', {
215249
isDev: config.isDev,
@@ -258,7 +292,8 @@ export function NotionPage({
258292
<NotionRenderer
259293
bodyClassName={cs(
260294
styles.notion,
261-
pageId === site.rootNotionPageId && 'index-page'
295+
pageId === site.rootNotionPageId && 'index-page',
296+
tagsPage && 'tags-page'
262297
)}
263298
darkMode={isDarkMode}
264299
components={components}
@@ -273,11 +308,14 @@ export function NotionPage({
273308
defaultPageIcon={config.defaultPageIcon}
274309
defaultPageCover={config.defaultPageCover}
275310
defaultPageCoverPosition={config.defaultPageCoverPosition}
311+
linkTableTitleProperties={false}
276312
mapPageUrl={siteMapPageUrl}
277313
mapImageUrl={mapImageUrl}
278314
searchNotion={config.isSearchEnabled ? searchNotion : null}
279315
pageAside={pageAside}
280316
footer={footer}
317+
pageTitle={tagsPage && propertyToFilterName ? title : undefined}
318+
pageCover={pageCover}
281319
/>
282320

283321
<GitHubShareButton />

components/PageHead.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ export function PageHead({
9595
href={rssFeedUrl}
9696
title={site?.name}
9797
/>
98+
<meta name='follow.it-verification-code' content='c0A1rAARM3FC2XRfMAke' />
9899

99100
<meta property='og:title' content={title} />
100101
<meta name='twitter:title' content={title} />

lib/types.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ export interface PageProps {
1616
recordMap?: ExtendedRecordMap
1717
pageId?: string
1818
error?: PageError
19+
tagsPage?: boolean
20+
propertyToFilterName?: string | string
1921
}
2022

2123
export interface Params extends ParsedUrlQuery {

package.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
"expiry-map": "^2.0.0",
4141
"fathom-client": "^3.4.1",
4242
"ky": "^1.7.2",
43+
"lodash.omit": "^4.5.0",
4344
"lqip-modern": "^2.1.0",
4445
"next": "^15.0.2",
4546
"notion-client": "^7.0.1",
@@ -49,9 +50,12 @@
4950
"p-memoize": "^7.1.1",
5051
"posthog-js": "^1.20.2",
5152
"prismjs": "^1.29.0",
53+
"raf": "^3.4.1",
54+
"random": "^5.1.1",
5255
"react": "^18.2.0",
5356
"react-body-classname": "^1.3.1",
5457
"react-dom": "^18.2.0",
58+
"react-fluid-animation": "^1.0.1",
5559
"react-notion-x": "^7.0.1",
5660
"react-tweet-embed": "^2.0.0",
5761
"react-use": "^17.4.2",
@@ -60,7 +64,9 @@
6064
"devDependencies": {
6165
"@fisch0920/eslint-config": "^1.4.0",
6266
"@next/bundle-analyzer": "^15.0.2",
67+
"@types/lodash.omit": "^4.5.6",
6368
"@types/node": "^22.8.6",
69+
"@types/raf": "^3.4.3",
6470
"@types/react": "^18.0.21",
6571
"cross-env": "^7.0.2",
6672
"eslint": "^8.57.1",

pages/[pageId].tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ export const getStaticProps: GetStaticProps<PageProps, Params> = async (
1414
try {
1515
const props = await resolveNotionPage(domain, rawPageId)
1616

17-
return { props, revalidate: 10 }
17+
return { props, revalidate: 60 }
1818
} catch (err) {
1919
console.error('page error', domain, rawPageId, err)
2020

pages/_app.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
// used for rendering equations (optional)
2-
import 'katex/dist/katex.min.css'
2+
// import 'katex/dist/katex.min.css'
3+
34
// used for code syntax highlighting (optional)
45
import 'prismjs/themes/prism-coy.css'
56
// core styles shared by all of react-notion-x (required)

pages/_document.tsx

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,19 @@ export default class MyDocument extends Document {
1212
rel='icon'
1313
type='image/png'
1414
sizes='32x32'
15-
href='favicon.png'
15+
href='favicon-32x32.png'
16+
/>
17+
<link
18+
rel='apple-touch-icon'
19+
sizes='180x180'
20+
href='/apple-touch-icon.png'
21+
/>
22+
<link
23+
rel='icon'
24+
type='image/png'
25+
sizes='96x96'
26+
href='/favicon-96x96.png'
1627
/>
17-
1828
<link rel='manifest' href='/manifest.json' />
1929
</Head>
2030

pages/index.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ export const getStaticProps = async () => {
66
try {
77
const props = await resolveNotionPage(domain)
88

9-
return { props, revalidate: 10 }
9+
return { props, revalidate: 60 }
1010
} catch (err) {
1111
console.error('page error', domain, err)
1212

pages/robots.txt.tsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,11 @@ export const getServerSideProps: GetServerSideProps = async ({ req, res }) => {
1414
}
1515
}
1616

17-
// cache for up to one day
18-
res.setHeader('Cache-Control', 'public, max-age=86400, immutable')
17+
// cache at vercel edge for up to one day
18+
res.setHeader(
19+
'Cache-Control',
20+
'max-age=0, s-maxage=86400, stale-while-revalidate=3600'
21+
)
1922
res.setHeader('Content-Type', 'text/plain')
2023

2124
// only allow the site to be crawlable on the production deployment

0 commit comments

Comments
 (0)