Skip to content

Commit

Permalink
Add reading progress bar and GitHub contributions visualization
Browse files Browse the repository at this point in the history
- Implement ProgressBar component to display reading progress
- Create API route for fetching GitHub contributions SVG
- Integrate ProgressBar in PostLayout and Hero components
- Add Github component to display contributions with modified SVG
- Update Main component layout for improved structure
  • Loading branch information
geekskai committed Oct 20, 2024
1 parent 6736ef9 commit 8b70523
Show file tree
Hide file tree
Showing 7 changed files with 376 additions and 9 deletions.
12 changes: 3 additions & 9 deletions app/Main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,15 @@ import Tag from '@/components/Tag'
import siteMetadata from '@/data/siteMetadata'
import { formatDate } from 'pliny/utils/formatDate'
import NewsletterForm from 'pliny/ui/NewsletterForm'
import Hero from '@/components/Hero'

const MAX_DISPLAY = 5

export default function Home({ posts }) {
return (
<>
<Hero />
<div className="divide-y divide-stone-200 dark:divide-stone-700">
<div className="space-y-2 pb-8 pt-6 md:space-y-5">
<h1 className="text-3xl font-extrabold leading-9 tracking-tight text-stone-900 dark:text-stone-100 sm:text-4xl sm:leading-10 md:text-6xl md:leading-14">
{siteMetadata.title}
</h1>
<p className="text-lg leading-7 text-stone-500 dark:text-stone-400">
{siteMetadata.description}
</p>
</div>
<ul className="divide-y divide-stone-200 dark:divide-stone-700">
{!posts.length && 'No posts found.'}
{posts.slice(0, MAX_DISPLAY).map((post) => {
Expand Down Expand Up @@ -66,7 +60,7 @@ export default function Home({ posts }) {
</ul>
</div>
{posts.length > MAX_DISPLAY && (
<div className="flex justify-end text-base font-medium leading-6">
<div className="flex justify-center text-base font-medium leading-6">
<Link
href="/blog"
className="text-primary-500 hover:text-primary-600 dark:hover:text-primary-400"
Expand Down
16 changes: 16 additions & 0 deletions app/api/contributions/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
const handler = async () => {
const res = await fetch('https://ghchart.rshah.org/178942/geekskai', {
headers: {
'Content-Type': 'image/svg+xml',
'Cache-Control': 'public, max-age=86400',
},
})

const svgText = await res.text()

return new Response(svgText, {
status: 200,
})
}

export { handler as GET }
56 changes: 56 additions & 0 deletions components/Github.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
'use client'
import { useState, useEffect } from 'react'
import Image from './Image'
import Link from '@/components/Link'

const Github = () => {
const [svgContent, setSvgContent] = useState('')
const [loading, setLoading] = useState(false)

useEffect(() => {
const fetchSVGContent = async () => {
try {
setLoading(true)
const response = await fetch(`/api/contributions`, { next: { revalidate: 86400 } })
const svgText = await response.text()
const parser = new DOMParser()
const xmlDoc = parser.parseFromString(svgText, 'image/svg+xml')

const rectElements = xmlDoc.querySelectorAll('rect[style*="fill:#EEEEEE;"]')

rectElements.forEach((rectElement) => {
rectElement.setAttribute('style', 'fill:#161b22;shape-rendering:crispedges;')
})

const modifiedSvgText = new XMLSerializer().serializeToString(xmlDoc)

setSvgContent(`data:image/svg+xml;base64,${btoa(modifiedSvgText)}`)
} catch (error) {
console.error('Error fetching SVG:', error)
}
setLoading(false)
}

fetchSVGContent()
}, [])

if (loading || svgContent === '') {
return null
}

return (
<div className="dark:text-grey text-gray flex flex-col items-center justify-center pb-12">
<p className="text-gray dark:text-gray text-xs leading-7 md:mt-5">
<Link
href="https://github.com/geekskai"
className="text-primary-500 hover:text-primary-600 dark:hover:text-primary-400"
>
My Github Contributions
</Link>
</p>
<Image width={900} height={504} src={svgContent} alt="My Github Contributions" />
</div>
)
}

export default Github
94 changes: 94 additions & 0 deletions components/Hero.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
'use client'
import Image from '@/components/Image'

// import { motion } from 'framer-motion'
// import Link from 'next/link'
import Link from './Link'
import siteMetadata from '@/data/siteMetadata'

import { ReactElement, useEffect, useRef } from 'react'
// import { HiOutlineArrowNarrowDown } from 'react-icons/hi'
// import { ScrollContext } from './Providers/ScrollProvider'
import { renderCanvas } from './renderCanvas'
import Github from './Github'
import { allAuthors, Authors } from 'contentlayer/generated'
import { coreContent } from 'pliny/utils/contentlayer'

export default function Hero(): ReactElement {
const author = allAuthors.find((p) => p.slug === 'default') as Authors
const { name, occupation } = coreContent(author)

useEffect(() => {
renderCanvas()
}, [])

return (
<div>
<div className="space-y-2 pb-8 pt-6 md:space-y-5">
<h1 className="text-2xl font-extrabold leading-9 tracking-tight text-gray-900 dark:text-gray-100 sm:text-4xl sm:leading-10 md:text-5xl md:leading-14">
I'm{' '}
<span className="dark:from-secondary-700 dark:to-secondary-400 mt-10 bg-gradient-to-r from-primary-700 to-primary-400 bg-clip-text text-center text-4xl font-extrabold tracking-tight text-transparent sm:text-5xl lg:text-6xl">
{name}
</span>{' '}
👋
</h1>
<div className="dark:text-grey text-gray mb-8 mt-4 text-base">
<p className="text-gray dark:text-gray text-lg leading-7">{siteMetadata.description}</p>
<p>{occupation}</p>
<Github />
<p>
<span className="mr-3 inline-block whitespace-nowrap pt-3">🛠️ JavaScript</span>
<span className="mr-3 inline-block whitespace-nowrap pt-3">🥇 TypeScript</span>
<span className="mr-3 inline-block whitespace-nowrap pt-3">❤️ React.js</span>
<span className="mr-3 inline-block whitespace-nowrap pt-3">❤️‍🩹 Vue.js</span>
<span className="mr-3 inline-block whitespace-nowrap pt-3">🥇 Next.js</span>
<span className="mr-3 inline-block whitespace-nowrap pt-3">📦 Node.js</span>
<span className="mr-3 inline-block whitespace-nowrap pt-3">🎨 Tailwind CSS</span>
<span className="mr-3 inline-block whitespace-nowrap pt-3">📦 Webpack</span>
<span className="mr-3 inline-block whitespace-nowrap pt-3">⏳ Vite</span>
<span className="mr-3 inline-block whitespace-nowrap pt-3">🧊 HTML</span>
<span className="mr-3 inline-block whitespace-nowrap pt-3">🍡 CSS</span>
<span className="mr-3 inline-block whitespace-nowrap pt-3">🕰️ Git</span>
<span className="mr-3 inline-block whitespace-nowrap pt-3">🚃 Npm</span>
<span className="mr-3 inline-block whitespace-nowrap pt-3">🧶 Yarn</span>
<span className="mr-3 inline-block whitespace-nowrap pt-3">📜 PNpm</span>
<span className="mr-3 inline-block whitespace-nowrap pt-3">🎢 Redux</span>
<span className="mr-3 inline-block whitespace-nowrap pt-3">🍤 Zustand</span>
<span className="mr-3 inline-block whitespace-nowrap pt-3">🤖 OpenAI API</span>
<span className="mr-3 inline-block whitespace-nowrap pt-3">🧱 Material UI</span>
<span className="mr-3 inline-block whitespace-nowrap pt-3">🐜 Ant Design</span>
<span className="mr-3 inline-block whitespace-nowrap pt-3">⏳ Esbuild</span>
<span className="mr-3 inline-block whitespace-nowrap pt-3">🍽️ APIs</span>
</p>
</div>
</div>
<Link
className="inline-block w-full text-right text-base font-medium leading-6 text-primary-500 hover:text-primary-600 dark:hover:text-primary-400"
aria-label={`Read more about me`}
href="/about"
// className="underline-magical text-md w-max cursor-pointer sm:text-lg md:text-xl xl:text-2xl"
>
Read more about me &rarr;
</Link>
{/* <div className="relative z-10 flex h-[calc(100vh-400px)] md:h-[calc(100vh-340px)]">
<div className=" flex cursor-default flex-col gap-5 space-y-2">
<h1 className="text-5xl font-semibold sm:text-7xl md:text-8xl xl:text-9xl">
{siteMetadata.title}
</h1>
<h2 className="text-xl font-medium opacity-80 sm:text-2xl md:text-3xl xl:text-4xl">
{siteMetadata.description}
</h2>
<Link
className="inline-block w-full text-right text-base font-medium leading-6 text-primary-500 hover:text-primary-600 dark:hover:text-primary-400"
aria-label={`Read more about me`}
href="/about"
// className="underline-magical text-md w-max cursor-pointer sm:text-lg md:text-xl xl:text-2xl"
>
Read more about me &rarr;
</Link>
</div>
</div> */}
<canvas className="bg-skin-base pointer-events-none absolute inset-0" id="canvas"></canvas>
</div>
)
}
39 changes: 39 additions & 0 deletions components/ProgressBar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
'use client'

import React from 'react'
import { useEffect, useState } from 'react'

export function useReadingProgress() {
const [progress, setProgress] = useState(0)
useEffect(() => {
function updateScroll() {
// the height that has been scrolled
const currentScrollY = window.scrollY

// the height that can be scrolled
const scrollHeight = document.body.scrollHeight - window.innerHeight
if (scrollHeight) {
setProgress(Number((currentScrollY / scrollHeight).toFixed(2)) * 100)
}
}
// add a global scroll event listener
window.addEventListener('scroll', updateScroll)

return () => {
window.removeEventListener('scroll', updateScroll)
}
}, [])
return progress
}

export default function ProgressBar() {
const progress = useReadingProgress()
return (
<div
style={{
transform: `translateX(${progress - 100}%)`,
}}
className="fixed left-0 top-0 z-80 h-1 w-full bg-primary-500 backdrop-blur-3xl transition-transform duration-75"
/>
)
}
Loading

0 comments on commit 8b70523

Please sign in to comment.