Source identity
| Field | Recorded value |
|---|---|
| Source type | GitHub source |
| Original record | Janady13/llms-txt-hub / apps/web/app/members/contribution-badge.tsx |
| Date | Not specified in source |
| Repository | Janady13/llms-txt-hub |
| Revision | e0f4bf978d4df168369c7601d9c5f5ea89e84377 |
| Branch | main |
| Path or archive member | apps/web/app/members/contribution-badge.tsx |
| Original SHA-256 | 9f875a7cd42f5ae087e1ef08a300ca89a906c94465c65920fc9b9d35e19f9acd |
| Git blob SHA | db4874ed11fb7c4820049034b9d128ed177a88f8 |
| Reading-copy SHA-256 | 9f875a7cd42f5ae087e1ef08a300ca89a906c94465c65920fc9b9d35e19f9acd |
Recovered source transcript
Download complete source text ↓95 lines · 2.7 KB
'use client'
import { Badge } from '@thedaviddias/design-system/badge'
import { Star } from 'lucide-react'
import { useEffect, useRef, useState } from 'react'
import { getUserContributions } from '@/lib/github-contributions'
interface ContributionBadgeProps {
username: string
}
/**
* Displays a contributor badge for users with GitHub contributions
*/
export function ContributionBadge({ username }: ContributionBadgeProps) {
const [isContributor, setIsContributor] = useState<boolean | null>(null)
const [isLoading, setIsLoading] = useState(false)
const [isVisible, setIsVisible] = useState(false)
const elementRef = useRef<HTMLDivElement>(null)
// Use Intersection Observer to lazy load contribution data
useEffect(() => {
// Check if IntersectionObserver is available (not in SSR/jsdom)
if (typeof window === 'undefined' || !('IntersectionObserver' in window)) {
// Fallback: set visibility immediately if IntersectionObserver is not available
setIsVisible(true)
return
}
const observer = new IntersectionObserver(
entries => {
const entry = entries[0]
if (entry.isIntersecting && !isLoading && isContributor === null) {
setIsVisible(true)
}
},
{
root: null,
rootMargin: '50px', // Start loading slightly before the element is visible
threshold: 0.01
}
)
if (elementRef.current) {
observer.observe(elementRef.current)
}
return () => {
// Clean up observer to prevent memory leaks
observer.disconnect()
}
}, [isLoading, isContributor])
// Fetch contribution data when visible
useEffect(() => {
if (!isVisible || isLoading || isContributor !== null) return
/**
* Fetches contribution data for the given username
*/
const fetchContributions = async () => {
setIsLoading(true)
try {
const contributions = await getUserContributions(username)
setIsContributor(contributions.total > 0)
} catch (_error) {
// On error, assume not a contributor
setIsContributor(false)
} finally {
setIsLoading(false)
}
}
fetchContributions()
}, [isVisible, username, isLoading, isContributor])
return (
<div ref={elementRef} className="inline-block">
{isLoading && (
<Badge variant="secondary" className="text-xs opacity-50">
<div className="mr-1 h-3 w-3 animate-spin rounded-full border-2 border-current border-t-transparent" />
Checking...
</Badge>
)}
{isContributor === true && (
<Badge variant="secondary" className="text-xs">
<Star className="mr-1 h-3 w-3" />
Contributor
</Badge>
)}
</div>
)
}