Skip to content

Commit 30eea04

Browse files
changes specific to transitivebullsh.it
1 parent 53b5dc4 commit 30eea04

29 files changed

+571
-55
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 {
@@ -111,15 +117,6 @@ const Collection = dynamic(() =>
111117
(m) => m.Collection
112118
)
113119
)
114-
const Equation = dynamic(() =>
115-
import('react-notion-x/build/third-party/equation').then((m) => m.Equation)
116-
)
117-
const Pdf = dynamic(
118-
() => import('react-notion-x/build/third-party/pdf').then((m) => m.Pdf),
119-
{
120-
ssr: false
121-
}
122-
)
123120
const Modal = dynamic(
124121
() =>
125122
import('react-notion-x/build/third-party/modal').then((m) => {
@@ -183,11 +180,35 @@ const propertyTextValue = (
183180
return defaultFn()
184181
}
185182

183+
const propertySelectValue = (
184+
{ schema, value, key, pageHeader }: any,
185+
defaultFn: () => React.ReactNode
186+
) => {
187+
value = normalizeTitle(value)
188+
189+
if (pageHeader && schema.type === 'multi_select' && value) {
190+
return (
191+
<Link href={`/tags/${value}`} key={key} legacyBehavior>
192+
<a>{defaultFn()}</a>
193+
</Link>
194+
)
195+
}
196+
197+
return defaultFn()
198+
}
199+
200+
const HeroHeader = dynamic<{ className?: string }>(
201+
() => import('./HeroHeader').then((m) => m.HeroHeader),
202+
{ ssr: false }
203+
)
204+
186205
export function NotionPage({
187206
site,
188207
recordMap,
189208
error,
190-
pageId
209+
pageId,
210+
tagsPage,
211+
propertyToFilterName
191212
}: types.PageProps) {
192213
const router = useRouter()
193214
const lite = useSearchParam('lite')
@@ -198,14 +219,13 @@ export function NotionPage({
198219
nextLink: Link,
199220
Code,
200221
Collection,
201-
Equation,
202-
Pdf,
203222
Modal,
204223
Tweet,
205224
Header: NotionPageHeader,
206225
propertyLastEditedTimeValue,
207226
propertyTextValue,
208-
propertyDateValue
227+
propertyDateValue,
228+
propertySelectValue
209229
}),
210230
[]
211231
)
@@ -230,6 +250,8 @@ export function NotionPage({
230250
// parsePageId(block?.id) === parsePageId(site?.rootNotionPageId)
231251
const isBlogPost =
232252
block?.type === 'page' && block?.parent_table === 'collection'
253+
const isBioPage =
254+
parsePageId(block?.id) === parsePageId('8d0062776d0c4afca96eb1ace93a7538')
233255

234256
const showTableOfContents = !!isBlogPost
235257
const minTableOfContentsItems = 3
@@ -247,6 +269,16 @@ export function NotionPage({
247269

248270
const footer = React.useMemo(() => <Footer />, [])
249271

272+
const pageCover = React.useMemo(() => {
273+
if (isBioPage) {
274+
return (
275+
<HeroHeader className='notion-page-cover-wrapper notion-page-cover-hero' />
276+
)
277+
} else {
278+
return null
279+
}
280+
}, [isBioPage])
281+
250282
if (router.isFallback) {
251283
return <Loading />
252284
}
@@ -255,7 +287,9 @@ export function NotionPage({
255287
return <Page404 site={site} pageId={pageId} error={error} />
256288
}
257289

258-
const title = getBlockTitle(block, recordMap) || site.name
290+
const name = getBlockTitle(block, recordMap) || site.name
291+
const title =
292+
tagsPage && propertyToFilterName ? `${propertyToFilterName} ${name}` : name
259293

260294
console.log('notion page', {
261295
isDev: config.isDev,
@@ -305,7 +339,8 @@ export function NotionPage({
305339
<NotionRenderer
306340
bodyClassName={cs(
307341
styles.notion,
308-
pageId === site.rootNotionPageId && 'index-page'
342+
pageId === site.rootNotionPageId && 'index-page',
343+
tagsPage && 'tags-page'
309344
)}
310345
darkMode={isDarkMode}
311346
components={components}
@@ -320,11 +355,14 @@ export function NotionPage({
320355
defaultPageIcon={config.defaultPageIcon}
321356
defaultPageCover={config.defaultPageCover}
322357
defaultPageCoverPosition={config.defaultPageCoverPosition}
358+
linkTableTitleProperties={false}
323359
mapPageUrl={siteMapPageUrl}
324360
mapImageUrl={mapImageUrl}
325361
searchNotion={config.isSearchEnabled ? searchNotion : undefined}
326362
pageAside={pageAside}
327363
footer={footer}
364+
pageTitle={tagsPage && propertyToFilterName ? title : undefined}
365+
pageCover={pageCover}
328366
/>
329367

330368
<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/get-site-map.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ const getAllPages = pMemoize(getAllPagesImpl, {
2828
const getPage = async (pageId: string, opts?: any) => {
2929
console.log('\nnotion getPage', uuidToId(pageId))
3030
return notion.getPage(pageId, {
31+
throwOnCollectionErrors: true,
3132
kyOptions: {
3233
timeout: 30_000
3334
},

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

next.config.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@ export default withBundleAnalyzer({
1717
{ protocol: 'https', hostname: 'images.unsplash.com' },
1818
{ protocol: 'https', hostname: 'abs.twimg.com' },
1919
{ protocol: 'https', hostname: 'pbs.twimg.com' },
20-
{ protocol: 'https', hostname: 's3.us-west-2.amazonaws.com' }
20+
{ protocol: 'https', hostname: 's3.us-west-2.amazonaws.com' },
21+
{ protocol: 'https', hostname: 'transitivebullsh.it' }
2122
],
2223
formats: ['image/avif', 'image/webp'],
2324
dangerouslyAllowSVG: true,

package.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,10 +48,13 @@
4848
"p-memoize": "^7.1.1",
4949
"posthog-js": "^1.249.4",
5050
"prismjs": "^1.30.0",
51+
"raf": "^3.4.1",
52+
"random": "^5.4.0",
5153
"react": "^19.1.0",
5254
"react-body-classname": "^1.3.1",
5355
"react-dom": "^19.1.0",
5456
"react-notion-x": "^7.4.1",
57+
"react-fluid-animation": "^1.0.1",
5558
"react-tweet": "^3.2.2",
5659
"react-use": "^17.6.0",
5760
"rss": "^1.2.2"
@@ -61,6 +64,7 @@
6164
"@next/bundle-analyzer": "^15.3.3",
6265
"@types/node": "^22.15.30",
6366
"@types/prismjs": "^1.26.5",
67+
"@types/raf": "^3.4.3",
6468
"@types/react": "^19.1.6",
6569
"@types/react-body-classname": "^1.1.10",
6670
"@types/rss": "^0.0.32",

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

0 commit comments

Comments
 (0)