-
-
Notifications
You must be signed in to change notification settings - Fork 19
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add reading progress bar and GitHub contributions visualization
- 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
Showing
7 changed files
with
376 additions
and
9 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 → | ||
</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 → | ||
</Link> | ||
</div> | ||
</div> */} | ||
<canvas className="bg-skin-base pointer-events-none absolute inset-0" id="canvas"></canvas> | ||
</div> | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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" | ||
/> | ||
) | ||
} |
Oops, something went wrong.