mirror of
https://github.com/supabase/supabase.git
synced 2026-10-05 17:35:10 +03:00
* Rough contribute ui * Clean up filter area * Tweak query with new threads schema * Unify table and apply some ui nits * Fix types issues * Setup types * FilterS * Fix filters * Make search global * Add icons for table rows * Add single thread view * Loading state for threads * Fix icon colors * Add leaderboard * Fix conversation text overflow and add op badge * Rename leaderboard * Turn of leaderboard * Formatting * Remove other from product areas * And from stacks * Prettier types * Start contribute/about * About cards * Add avatar component * Polish up about page * Nudges * Add application form * Add squad form * Change to last 30 days instead of 24hrs * Responsive nudges * Format components for conversation view * Re use ui components on about page * Fix prettier errors * Filter nudges * chore(www): improve contribute UI (#41481) * hero tweaks * better table * wrap post time * nip and tuck * use table component * use table component * Add menu item * Alan/polish contribute filtering UI (#41504) * polishing tabs * nits in tables * nit in filtering * nits * ship it * fix selection style * nit search bar * Add view for authors * Format author page * Add contribute menu item * Tidy up about page * More about page edits * Update * basics * copy cleanup * improvements to thread table * threads improvements * remove extraneous div * thread improvements * chore(www): improve contribute UI (#41605) * basics * improvements to thread table * threads improvements * remove extraneous div * thread improvements * Add feature flag for contribute page * Add feature flag for contribute page * Fix ts error * Thread view nudges * Undo env change * Cleanup * Cleanup * Typos --------- Co-authored-by: Tomas Pozo <tomaspozogarzon@gmail.com> Co-authored-by: Danny White <3104761+dnywh@users.noreply.github.com> Co-authored-by: Alan Daniel <stylesshjs@gmail.com>
56 lines
1.7 KiB
TypeScript
56 lines
1.7 KiB
TypeScript
'use client'
|
|
|
|
import Image from 'next/image'
|
|
import Link from 'next/link'
|
|
import { useState } from 'react'
|
|
|
|
interface GithubAvatarProps {
|
|
username: string
|
|
size?: number
|
|
className?: string
|
|
}
|
|
|
|
// Basic username validation - GitHub usernames must be alphanumeric with hyphens/underscores
|
|
function isValidGithubUsername(username: string): boolean {
|
|
if (!username || username.trim().length === 0) return false
|
|
// GitHub username rules: alphanumeric, hyphens, underscores, no consecutive hyphens/underscores, max 39 chars
|
|
const githubUsernameRegex = /^[a-zA-Z0-9]([a-zA-Z0-9]|-(?!-)|_(?!_))*[a-zA-Z0-9]?$/
|
|
return username.length <= 39 && githubUsernameRegex.test(username)
|
|
}
|
|
|
|
export function GithubAvatar({ username, size = 80, className }: GithubAvatarProps) {
|
|
const [imageError, setImageError] = useState(false)
|
|
const [imageSrc, setImageSrc] = useState(`https://github.com/${username}.png`)
|
|
|
|
// Validate username and use fallback if invalid
|
|
const isValid = isValidGithubUsername(username)
|
|
const fallbackSrc = isValid
|
|
? `https://github.com/identicons/${username}.png`
|
|
: '/images/blog/blog-placeholder.png'
|
|
|
|
const handleImageError = () => {
|
|
if (!imageError) {
|
|
setImageError(true)
|
|
setImageSrc(fallbackSrc)
|
|
}
|
|
}
|
|
|
|
return (
|
|
<Link
|
|
href={`https://github.com/${username}`}
|
|
target="_blank"
|
|
rel="noreferrer"
|
|
className="group relative"
|
|
>
|
|
<Image
|
|
src={imageSrc}
|
|
alt={`${username}'s GitHub avatar`}
|
|
width={size}
|
|
height={size}
|
|
className={`${className} transition-all duration-200 group-hover:scale-110 group-hover:ring-2 group-hover:ring-brand-500 group-hover:ring-offset-2 group-hover:ring-offset-background`}
|
|
onError={handleImageError}
|
|
/>
|
|
</Link>
|
|
)
|
|
}
|