Skip to content

Commit 40f69ce

Browse files
changes specific to transitivebullsh.it
1 parent e2017fd commit 40f69ce

27 files changed

+602
-63
lines changed

components/HeroHeader.tsx

+135
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

+54-16
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 {
@@ -80,15 +86,6 @@ const Collection = dynamic(() =>
8086
(m) => m.Collection
8187
)
8288
)
83-
const Equation = dynamic(() =>
84-
import('react-notion-x/build/third-party/equation').then((m) => m.Equation)
85-
)
86-
const Pdf = dynamic(
87-
() => import('react-notion-x/build/third-party/pdf').then((m) => m.Pdf),
88-
{
89-
ssr: false
90-
}
91-
)
9289
const Modal = dynamic(
9390
() =>
9491
import('react-notion-x/build/third-party/modal').then((m) => {
@@ -152,11 +149,35 @@ const propertyTextValue = (
152149
return defaultFn()
153150
}
154151

152+
const propertySelectValue = (
153+
{ schema, value, key, pageHeader },
154+
defaultFn: () => React.ReactNode
155+
) => {
156+
value = normalizeTitle(value)
157+
158+
if (pageHeader && schema.type === 'multi_select' && value) {
159+
return (
160+
<Link href={`/tags/${value}`} key={key}>
161+
<a>{defaultFn()}</a>
162+
</Link>
163+
)
164+
}
165+
166+
return defaultFn()
167+
}
168+
169+
const HeroHeader = dynamic<{ className?: string }>(
170+
() => import('./HeroHeader').then((m) => m.HeroHeader),
171+
{ ssr: false }
172+
)
173+
155174
export function NotionPage({
156175
site,
157176
recordMap,
158177
error,
159-
pageId
178+
pageId,
179+
tagsPage,
180+
propertyToFilterName
160181
}: types.PageProps) {
161182
const router = useRouter()
162183
const lite = useSearchParam('lite')
@@ -167,14 +188,13 @@ export function NotionPage({
167188
nextLink: Link,
168189
Code,
169190
Collection,
170-
Equation,
171-
Pdf,
172191
Modal,
173192
Tweet,
174193
Header: NotionPageHeader,
175194
propertyLastEditedTimeValue,
176195
propertyTextValue,
177-
propertyDateValue
196+
propertyDateValue,
197+
propertySelectValue
178198
}),
179199
[]
180200
)
@@ -199,6 +219,8 @@ export function NotionPage({
199219
// parsePageId(block?.id) === parsePageId(site?.rootNotionPageId)
200220
const isBlogPost =
201221
block?.type === 'page' && block?.parent_table === 'collection'
222+
const isBioPage =
223+
parsePageId(block?.id) === parsePageId('8d0062776d0c4afca96eb1ace93a7538')
202224

203225
const showTableOfContents = !!isBlogPost
204226
const minTableOfContentsItems = 3
@@ -212,6 +234,16 @@ export function NotionPage({
212234

213235
const footer = React.useMemo(() => <Footer />, [])
214236

237+
const pageCover = React.useMemo(() => {
238+
if (isBioPage) {
239+
return (
240+
<HeroHeader className='notion-page-cover-wrapper notion-page-cover-hero' />
241+
)
242+
} else {
243+
return null
244+
}
245+
}, [isBioPage])
246+
215247
if (router.isFallback) {
216248
return <Loading />
217249
}
@@ -220,7 +252,9 @@ export function NotionPage({
220252
return <Page404 site={site} pageId={pageId} error={error} />
221253
}
222254

223-
const title = getBlockTitle(block, recordMap) || site.name
255+
const name = getBlockTitle(block, recordMap) || site.name
256+
const title =
257+
tagsPage && propertyToFilterName ? `${propertyToFilterName} ${name}` : name
224258

225259
console.log('notion page', {
226260
isDev: config.isDev,
@@ -269,7 +303,8 @@ export function NotionPage({
269303
<NotionRenderer
270304
bodyClassName={cs(
271305
styles.notion,
272-
pageId === site.rootNotionPageId && 'index-page'
306+
pageId === site.rootNotionPageId && 'index-page',
307+
tagsPage && 'tags-page'
273308
)}
274309
darkMode={isDarkMode}
275310
components={components}
@@ -284,11 +319,14 @@ export function NotionPage({
284319
defaultPageIcon={config.defaultPageIcon}
285320
defaultPageCover={config.defaultPageCover}
286321
defaultPageCoverPosition={config.defaultPageCoverPosition}
322+
linkTableTitleProperties={false}
287323
mapPageUrl={siteMapPageUrl}
288324
mapImageUrl={mapImageUrl}
289325
searchNotion={config.isSearchEnabled ? searchNotion : null}
290326
pageAside={pageAside}
291327
footer={footer}
328+
pageTitle={tagsPage && propertyToFilterName ? title : undefined}
329+
pageCover={pageCover}
292330
/>
293331

294332
<GitHubShareButton />

components/PageHead.tsx

+1
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

+2
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 ExtendedTweetRecordMap extends ExtendedRecordMap {

package.json

+6
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
"expiry-map": "^2.0.0",
3939
"fathom-client": "^3.4.1",
4040
"ky": "^1.7.2",
41+
"lodash.omit": "^4.5.0",
4142
"lqip-modern": "^2.2.1",
4243
"next": "^15.0.3",
4344
"notion-client": "^7.1.3",
@@ -47,9 +48,12 @@
4748
"p-memoize": "^7.1.1",
4849
"posthog-js": "^1.181.0",
4950
"prismjs": "^1.29.0",
51+
"raf": "^3.4.1",
52+
"random": "^5.1.1",
5053
"react": "^18.2.0",
5154
"react-body-classname": "^1.3.1",
5255
"react-dom": "^18.2.0",
56+
"react-fluid-animation": "^1.0.1",
5357
"react-notion-x": "^7.2.3",
5458
"react-tweet": "^3.2.1",
5559
"react-use": "^17.4.2",
@@ -58,7 +62,9 @@
5862
"devDependencies": {
5963
"@fisch0920/eslint-config": "^1.4.0",
6064
"@next/bundle-analyzer": "^15.0.2",
65+
"@types/lodash.omit": "^4.5.6",
6166
"@types/node": "^22.8.6",
67+
"@types/raf": "^3.4.3",
6268
"@types/react": "^18.0.21",
6369
"cross-env": "^7.0.2",
6470
"eslint": "^8.57.1",

pages/[pageId].tsx

+1-1
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

+2-1
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

+12-2
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

+1-1
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

+5-2
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)