Skip to content

Commit 561ee68

Browse files
committed
Add utterances for comments
1 parent 3b09ec2 commit 561ee68

File tree

4 files changed

+129
-0
lines changed

4 files changed

+129
-0
lines changed

components/NotionPage.tsx

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import { PageHead } from './PageHead'
2929
import { PageAside } from './PageAside'
3030
import { Footer } from './Footer'
3131
import { NotionPageHeader } from './NotionPageHeader'
32+
import { ReactUtterances } from './ReactUtterances'
3233

3334
import styles from './styles.module.css'
3435

@@ -235,6 +236,20 @@ export const NotionPage: React.FC<types.PageProps> = ({
235236
const canonicalPageUrl =
236237
!config.isDev && getCanonicalPageUrl(site, recordMap)(pageId)
237238

239+
let comments: React.ReactNode = null
240+
241+
if (block.type === 'page' && block.parent_table === 'collection') {
242+
comments = (
243+
<ReactUtterances
244+
repo='transitive-bullshit/transitivebullsh.it'
245+
issueMap='issue-term'
246+
issueTerm='title'
247+
label='blog'
248+
theme='preferred-color-scheme'
249+
/>
250+
)
251+
}
252+
238253
const socialImage = mapImageUrl(
239254
getPageProperty<string>('Social Image', block, recordMap) ||
240255
(block as PageBlock).format?.page_cover ||
@@ -281,6 +296,7 @@ export const NotionPage: React.FC<types.PageProps> = ({
281296
mapPageUrl={siteMapPageUrl}
282297
mapImageUrl={mapImageUrl}
283298
searchNotion={config.isSearchEnabled ? searchNotion : null}
299+
pageFooter={comments}
284300
pageAside={pageAside}
285301
footer={footer}
286302
/>

components/ReactUtterances.tsx

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
import React from 'react'
2+
3+
import styles from './styles.module.css'
4+
5+
export type MappingType =
6+
| 'pathname'
7+
| 'url'
8+
| 'title'
9+
| 'og:title'
10+
| 'issue-number'
11+
| 'issue-term'
12+
13+
export type Theme =
14+
| 'github-light'
15+
| 'github-dark'
16+
| 'preferred-color-scheme'
17+
| 'github-dark-orange'
18+
| 'icy-dark'
19+
| 'dark-blue'
20+
| 'photon-dark'
21+
22+
interface ReactUtterancesProps {
23+
repo: string
24+
issueMap: MappingType
25+
issueTerm?: string
26+
issueNumber?: number
27+
label?: string
28+
theme: Theme
29+
}
30+
31+
interface ReactUtterancesState {
32+
pending: boolean
33+
}
34+
35+
export class ReactUtterances extends React.Component<
36+
ReactUtterancesProps,
37+
ReactUtterancesState
38+
> {
39+
reference: React.RefObject<HTMLDivElement>
40+
41+
constructor(props: ReactUtterancesProps) {
42+
super(props)
43+
44+
if (props.issueMap === 'issue-term' && props.issueTerm === undefined) {
45+
throw Error(
46+
"Property 'issueTerm' must be provided with issueMap 'issue-term'"
47+
)
48+
}
49+
50+
if (props.issueMap === 'issue-number' && props.issueNumber === undefined) {
51+
throw Error(
52+
"Property 'issueNumber' must be provided with issueMap 'issue-number'"
53+
)
54+
}
55+
56+
this.reference = React.createRef<HTMLDivElement>()
57+
this.state = { pending: true }
58+
}
59+
60+
componentDidMount(): void {
61+
const { repo, issueMap, issueTerm, issueNumber, label, theme } = this.props
62+
const scriptElement = document.createElement('script')
63+
scriptElement.src = 'https://utteranc.es/client.js'
64+
scriptElement.async = true
65+
scriptElement.defer = true
66+
scriptElement.setAttribute('repo', repo)
67+
scriptElement.setAttribute('crossorigin', 'annonymous')
68+
scriptElement.setAttribute('theme', theme)
69+
scriptElement.onload = () => this.setState({ pending: false })
70+
71+
if (label) {
72+
scriptElement.setAttribute('label', label)
73+
}
74+
75+
if (issueMap === 'issue-number') {
76+
scriptElement.setAttribute('issue-number', issueNumber!.toString())
77+
} else if (issueMap === 'issue-term') {
78+
scriptElement.setAttribute('issue-term', issueTerm!)
79+
} else {
80+
scriptElement.setAttribute('issue-term', issueMap)
81+
}
82+
83+
// TODO: Check current availability
84+
this.reference.current!.appendChild(scriptElement)
85+
}
86+
87+
render(): React.ReactElement {
88+
return (
89+
<div className={styles.comments}>
90+
<div className={styles.utterances} ref={this.reference}>
91+
{this.state.pending && <p>Loading Comments...</p>}
92+
</div>
93+
</div>
94+
)
95+
}
96+
}

components/styles.module.css

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,19 @@
111111
border-top: 1px solid var(--fg-color-0);
112112
}
113113

114+
.utterances {
115+
margin-top: 2em;
116+
width: 100%;
117+
}
118+
119+
@media only screen and (min-width: 567px) {
120+
.utterances {
121+
width: calc(100% + 60px);
122+
position: relative;
123+
left: -60px;
124+
}
125+
}
126+
114127
@media only screen and (max-width: 566px) {
115128
.footer {
116129
flex-direction: column;

styles/global.css

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,3 +33,7 @@ body {
3333
height: 1.2em !important;
3434
width: 1.2em !important;
3535
}
36+
37+
.utterances {
38+
max-width: 100% !important;
39+
}

0 commit comments

Comments
 (0)