Skip to content

Commit

Permalink
Merge pull request #17 from abdulrcs/feat/github-blog-integration
Browse files Browse the repository at this point in the history
Version 2.0.0 - use github issues as posts & add comments feature!
  • Loading branch information
abdulrcs committed Apr 2, 2024
2 parents 6153ee3 + 307bb15 commit 542a621
Show file tree
Hide file tree
Showing 7 changed files with 297 additions and 48 deletions.
3 changes: 3 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,8 @@ FIREBASE_STORAGE_BUCKET=
FIREBASE_MESSAGING_SENDER_ID=
FIREBASE_APP_ID=

# Github API Keys
GITHUB_TOKEN=

# Google Analytics UA CODE
NEXT_PUBLIC_UA_CODE=
83 changes: 83 additions & 0 deletions hook/useUtterances.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import { useEffect, useRef, useState } from 'react'

export default function useUtterances(commentNodeId, title) {
const commentNodeRef = useRef()
const [isCommentsLoading, setIsCommentsLoading] = useState(true)
let intersectionObserver
let mutationObserver
let hasLoaded = false

useEffect(() => {
const injectScript = () => {
if (hasLoaded) return

const scriptParentNode = document.getElementById(commentNodeId)
if (!scriptParentNode) return

const script = document.createElement('script')
script.src = 'https://utteranc.es/client.js'
script.async = true
script.setAttribute('repo', 'abdulrcs/abdulrahman.id')
script.setAttribute('issue-term', title)
script.setAttribute('theme', 'icy-dark')
script.crossOrigin = 'anonymous'

script.onload = () => setInterval(() => setIsCommentsLoading(false), 1000)

scriptParentNode.appendChild(script)
commentNodeRef.current = script

hasLoaded = true
}

const observerCallback = (entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
injectScript()
}
})
}

const mutationCallback = (mutationsList, observer) => {
for (let mutation of mutationsList) {
if (mutation.type === 'childList') {
const scriptParentNode = document.getElementById(commentNodeId)
if (scriptParentNode) {
if (!intersectionObserver) {
intersectionObserver = new IntersectionObserver(observerCallback)
}
intersectionObserver.observe(scriptParentNode)
observer.disconnect()
}
}
}
}

if (!mutationObserver) {
mutationObserver = new MutationObserver(mutationCallback)
}

mutationObserver.observe(document.body, { childList: true, subtree: true })

// Cleanup on unmount
return () => {
if (intersectionObserver) {
intersectionObserver.disconnect()
}
if (mutationObserver) {
mutationObserver.disconnect()
}
const scriptParentNode = document.getElementById(commentNodeId)
if (
scriptParentNode &&
commentNodeRef.current &&
commentNodeRef.current.parentNode === scriptParentNode
) {
scriptParentNode.removeChild(commentNodeRef.current)
}
hasLoaded = false
}
}, [commentNodeId, title])

return { isCommentsLoading }
}
9 changes: 8 additions & 1 deletion next.config.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
module.exports = {
images: {
domains: ['imagizer.imageshack.com', 'i.imgur.com', 'images.unsplash.com', 'media.giphy.com', 'cdn-images-1.medium.com'],
domains: [
'imagizer.imageshack.com',
'i.imgur.com',
'images.unsplash.com',
'media.giphy.com',
'cdn-images-1.medium.com',
'github.com',
],
},
}
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "abdulrahman.id",
"version": "0.1.0",
"version": "2.0.0",
"private": true,
"scripts": {
"dev": "next dev",
Expand All @@ -13,6 +13,7 @@
"@chakra-ui/react": "^1.6.1",
"@emotion/react": "^11",
"@emotion/styled": "^11",
"@rena.to/github-blog": "^0.4.2",
"contentful": "^8.3.7",
"cors": "^2.8.5",
"dateformat": "^4.5.1",
Expand Down
83 changes: 60 additions & 23 deletions pages/blog/[slug].js
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
import { useEffect, useState } from 'react'
import { Avatar, Text, Heading, Stack } from '@chakra-ui/react'
import {
Avatar,
Text,
Heading,
Stack,
Spinner,
Center,
HStack,
} from '@chakra-ui/react'
import { serialize } from 'next-mdx-remote/serialize'
import { MDXRemote } from 'next-mdx-remote'

Expand All @@ -15,6 +23,10 @@ import PostContainer from '../../components/PostContainer'
import MDXComponents from '../../components/MDXComponents'
import { useRouter } from 'next/router'

import { GithubBlog } from '@rena.to/github-blog'

import useUtterances from '../../hook/useUtterances'

export default function Post({ metadata, source }) {
const [views, setViews] = useState('...')
const router = useRouter()
Expand All @@ -24,14 +36,17 @@ export default function Post({ metadata, source }) {
.then((res) => res.json())
.then((json) => setViews(json.views))
}, [])

const { isCommentsLoading } = useUtterances('comments', metadata.title)

return (
<>
<NextSeo
title={metadata.title}
description={metadata.summary}
canonical={`https://abdulrahman.id/blog/${metadata.slug}`}
canonical={`https://abdulrahman.id/blog/${slug}`}
openGraph={{
url: `https://abdulrahman.id/blog/${metadata.slug}`,
url: `https://abdulrahman.id/blog/${slug}`,
site_name: 'Abdul Rahman',
title: metadata.title,
description: metadata.summary,
Expand All @@ -44,7 +59,7 @@ export default function Post({ metadata, source }) {
},
images: [
{
url: metadata.image,
url: metadata.frontmatter.image,
alt: metadata.title,
},
],
Expand All @@ -53,17 +68,17 @@ export default function Post({ metadata, source }) {
{ property: 'twitter:card', content: 'summary_large_image' },
{
property: 'twitter:url',
content: `https://abdulrahman.id/blog/${metadata.slug}`,
content: `https://abdulrahman.id/blog/${slug}`,
},
{ property: 'twitter:title', content: metadata.title },
{ property: 'twitter:description', content: metadata.summary },
{ property: 'twitter:image', content: metadata.image },
{ property: 'twitter:image', content: metadata.frontmatter.image },
]}
/>
<ArticleJsonLd
url={`https://abdulrahman.id/blog/${metadata.slug}`}
url={`https://abdulrahman.id/blog/${slug}`}
title={metadata.title}
images={[metadata.image]}
images={[metadata.frontmatter.image]}
datePublished={metadata.date}
dateModified={metadata.date}
authorName="Abdul Rahman"
Expand Down Expand Up @@ -116,7 +131,7 @@ export default function Post({ metadata, source }) {
borderColor={{ base: '#333', md: 'borderColor' }}
>
<Image
src={metadata.image}
src={metadata.frontmatter.image}
borderRadius="10px"
width={1366}
height={892}
Expand All @@ -129,6 +144,17 @@ export default function Post({ metadata, source }) {
</Stack>
<PostContainer>
<MDXRemote {...source} components={MDXComponents} />
{isCommentsLoading && (
<Center flexDirection="column" pt={8}>
<Spinner thickness="5px" w="56px" h="56px" color="#058d92" />
<Text color="textSecondary" fontSize="sm" pt={2}>
Loading comments...
</Text>
</Center>
)}
<Stack opacity={isCommentsLoading ? 0 : 1}>
<div id="comments" />
</Stack>
</PostContainer>
</Stack>
</Stack>
Expand All @@ -137,30 +163,41 @@ export default function Post({ metadata, source }) {
)
}

let client = require('contentful').createClient({
space: process.env.CONTENTFUL_SPACE_ID,
accessToken: process.env.CONTENTFUL_ACCESS_TOKEN,
})

export async function getStaticPaths() {
let data = await client.getEntries({
content_type: 'blogPosts',
const blog = new GithubBlog({
repo: 'abdulrcs/abdulrahman.id',
token: process.env.GITHUB_TOKEN,
})

const data = await blog.getPosts({
query: {
author: 'abdulrcs',
type: 'post',
state: 'published',
},
pager: { limit: 10, offset: 0 },
})

return {
paths: data.items.map((item) => ({
params: { slug: item.fields.slug },
paths: data.edges.map(({ post }) => ({
params: { slug: post.frontmatter.slug },
})),
fallback: false,
}
}

export async function getStaticProps({ params }) {
let data = await client.getEntries({
content_type: 'blogPosts',
'fields.slug': params.slug,
const blog = new GithubBlog({
repo: 'abdulrcs/abdulrahman.id',
token: process.env.GITHUB_TOKEN,
})

const article = data.items[0].fields
const data = await blog.getPost({
query: {
author: 'abdulrcs',
search: params.slug,
},
})
const article = data.post
const source = article.body
article.readingTime = readingTime(source).text
const mdxSource = await serialize(source, {
Expand Down
50 changes: 28 additions & 22 deletions pages/blog/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ import useMediaQuery from '../../hook/useMediaQuery'
import readingTime from 'reading-time'
import dateFormat from 'dateformat'

export default function Index({ articles }) {
import { GithubBlog } from '@rena.to/github-blog'

export default function Index({ posts }) {
const [query, setQuery] = useState('')
const handleChange = (e) => setQuery(e.target.value)
const isLargerThan1024 = useMediaQuery(1024)
Expand Down Expand Up @@ -74,13 +76,13 @@ export default function Index({ articles }) {
</InputGroup>
<Divider />
<Stack spacing={5}>
{articles
{posts
.filter((e) =>
e.fields.title.toLowerCase().includes(query.toLowerCase()),
e.post.title.toLowerCase().includes(query.toLowerCase()),
)
.map((article) => (
.map(({ post }) => (
<Stack
key={article.sys.id}
key={post.frontmatter.slug}
direction={isLargerThan1024 ? 'row' : 'column'}
alignItems="flex-start"
justifyContent="flex-start"
Expand All @@ -91,34 +93,36 @@ export default function Index({ articles }) {
width={100}
textAlign="right"
>
{dateFormat(Date.parse(article.fields.date), 'mmm d yyyy')}
{dateFormat(Date.parse(post.frontmatter.date), 'mmm d yyyy')}
<br />{' '}
<Text fontSize="sm" textAlign="right">
{readingTime(article.fields.body).text}
{post.frontmatter.readingTime}
</Text>
</Text>
<Text
color="textSecondary"
fontSize="sm"
display={isLargerThan1024 ? 'none' : 'block'}
>
{dateFormat(Date.parse(article.fields.date), 'mmm d yyyy')}{' '}
{dateFormat(Date.parse(post.frontmatter.date), 'mmm d yyyy')}{' '}
<Box as="span" fontSize="xs">
&bull;
</Box>{' '}
{readingTime(article.fields.body).text}
{post.frontmatter.readingTime}
</Text>
<Flex flexDirection="column" px={isLargerThan1024 ? 10 : 0}>
<Link href={'/blog/' + article.fields.slug}>
<Link href={'/blog/' + post.frontmatter.slug}>
<Text
color="displayColor"
fontSize="xl"
fontWeight="bold"
cursor="pointer"
>
{article.fields.title}
{post.title}
</Text>
<Text color="textSecondary">
{post.frontmatter.summary}
</Text>
<Text color="textSecondary">{article.fields.summary}</Text>

<Text color="button1" cursor="pointer">
Learn more &rarr;
Expand All @@ -133,21 +137,23 @@ export default function Index({ articles }) {
)
}

let client = require('contentful').createClient({
space: process.env.CONTENTFUL_SPACE_ID,
accessToken: process.env.CONTENTFUL_ACCESS_TOKEN,
})

export async function getStaticProps() {
let data = await client.getEntries({
content_type: 'blogPosts',
limit: 50,
order: 'sys.createdAt',
const blog = new GithubBlog({
repo: 'abdulrcs/abdulrahman.id',
token: process.env.GITHUB_TOKEN,
})
const posts = await blog.getPosts({
query: {
author: 'abdulrcs',
type: 'post',
state: 'published',
},
pager: { limit: 10, offset: 0 },
})

return {
props: {
articles: data.items.reverse(),
posts: posts.edges,
},
}
}

0 comments on commit 542a621

Please sign in to comment.