Source identity
| Field | Recorded value |
|---|---|
| Source type | GitHub source |
| Original record | Janady13/llms-txt-hub / apps/web/hooks/use-contributions.ts |
| Date | Not specified in source |
| Repository | Janady13/llms-txt-hub |
| Revision | e0f4bf978d4df168369c7601d9c5f5ea89e84377 |
| Branch | main |
| Path or archive member | apps/web/hooks/use-contributions.ts |
| Original SHA-256 | eec467f684ec9873c6c7a57e46abcfd380d9df0fe75ab2ff3a9b6d9eefcc4100 |
| Git blob SHA | f6c3cc52fb4f1985022fbb0e021a1433a51e5c9c |
| Reading-copy SHA-256 | eec467f684ec9873c6c7a57e46abcfd380d9df0fe75ab2ff3a9b6d9eefcc4100 |
Recovered source transcript
Download complete source text ↓125 lines · 3.3 KB
'use client'
import { logger } from '@thedaviddias/logging'
import { useCallback, useEffect, useState } from 'react'
interface UserContributionStatus {
username: string
hasContributions: boolean
error?: string
}
interface ContributionsMap {
[username: string]: boolean | undefined
}
interface UseContributionsProps {
usernames: string[]
enabled?: boolean
}
interface UseContributionsReturn {
contributions: ContributionsMap
isLoading: boolean
error: string | null
refetch: () => Promise<void>
}
/**
* Fetch and cache GitHub contribution counts for the given usernames
*/
export function useContributions({
usernames,
enabled = true
}: UseContributionsProps): UseContributionsReturn {
const [contributions, setContributions] = useState<ContributionsMap>({})
const [isLoading, setIsLoading] = useState(false)
const [error, setError] = useState<string | null>(null)
// Filter valid usernames
const validUsernames = usernames.filter(
username => typeof username === 'string' && username.trim().length > 0
)
const fetchContributions = useCallback(
async (options: { force?: boolean } = {}) => {
const { force = false } = options
if (!enabled || validUsernames.length === 0) return
// Skip if we already have all contributions data
const missingUsernames = force
? validUsernames
: validUsernames.filter(username => contributions[username] === undefined)
if (missingUsernames.length === 0) return
setIsLoading(true)
setError(null)
try {
const response = await fetch('/api/members/contributions', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
usernames: missingUsernames
})
})
if (!response.ok) {
throw new Error(`Failed to fetch contributions: ${response.statusText}`)
}
const data = await response.json()
if (data.error) {
throw new Error(data.error)
}
// Update contributions map
setContributions(prev => {
const updated = { ...prev }
data.contributions.forEach((contrib: UserContributionStatus) => {
updated[contrib.username] = contrib.hasContributions
})
return updated
})
} catch (err) {
const errorMessage = err instanceof Error ? err.message : 'Failed to fetch contributions'
setError(errorMessage)
logger.error(err instanceof Error ? err : new Error(String(err)), {
data: err,
tags: { type: 'hook', hook: 'use-contributions' }
})
} finally {
setIsLoading(false)
}
},
[validUsernames, contributions, enabled]
)
const refetch = useCallback(async () => {
await fetchContributions({ force: true })
}, [fetchContributions])
// Debounced effect to fetch contributions when usernames change
useEffect(() => {
if (!enabled || validUsernames.length === 0) return
const timeoutId = setTimeout(() => {
fetchContributions()
}, 150) // Small delay to batch rapid changes
return () => clearTimeout(timeoutId)
}, [fetchContributions, validUsernames.join(','), enabled])
return {
contributions,
isLoading,
error,
refetch
}
}