diff --git a/src/app/profile/[username]/page.tsx b/src/app/profile/[username]/page.tsx index ec4b3ee9..7d6714d0 100644 --- a/src/app/profile/[username]/page.tsx +++ b/src/app/profile/[username]/page.tsx @@ -11,12 +11,22 @@ import { ProjectShowcaseSection } from '@/components/profile/ProjectShowcaseSect import { ExportBar } from '@/components/profile/ExportBar'; interface Props { - params: { username: string }; + params: Promise<{ username: string }>; } +export async function generateStaticParams() { + return [ + { username: 'dummy' }, + { username: 'kew7p1pbj7WoX66uGH2ZMcg79RB3' } + ]; +} + +export const dynamicParams = false; + // Generate OpenGraph metadata dynamically export async function generateMetadata({ params }: Props): Promise { - const profile = await getPublicProfileByUsername(params.username); + const { username } = await params; + const profile = await getPublicProfileByUsername(username); if (!profile) return { title: 'Profile not found' }; return { @@ -31,7 +41,8 @@ export async function generateMetadata({ params }: Props): Promise { } export default async function PublicProfilePage({ params }: Props) { - const profile = await getPublicProfileByUsername(params.username); + const { username } = await params; + const profile = await getPublicProfileByUsername(username); if (!profile) notFound(); diff --git a/src/components/profile/DevCard.tsx b/src/components/profile/DevCard.tsx index 448d193a..977ccb54 100644 --- a/src/components/profile/DevCard.tsx +++ b/src/components/profile/DevCard.tsx @@ -33,6 +33,8 @@ import { query, where, getCountFromServer, + doc, + onSnapshot, } from 'firebase/firestore'; import { db } from '@/lib/firebase'; import { useNotificationActions } from '@/stores/ui-store'; @@ -183,9 +185,41 @@ export default function DevCard({ user }: { user: any }) { const [avatarLoadFailed, setAvatarLoadFailed] = useState(false); const { showSuccess, showError } = useNotificationActions(); + const [realTimeUser, setRealTimeUser] = useState(user); + + useEffect(() => { + setRealTimeUser(user); + }, [user]); + + useEffect(() => { + if (!user?.uid) return; + + const collectionName = user.role === 'admin' ? 'admins' : 'members'; + const docId = user.role === 'admin' ? user.email?.toLowerCase() : user.uid; + + if (!docId) return; + + const docRef = doc(db, collectionName, docId); + const unsubscribe = onSnapshot(docRef, (docSnap) => { + if (docSnap.exists()) { + const data = docSnap.data(); + setRealTimeUser((prev: any) => ({ + ...prev, + ...data, + uid: user.uid, + role: user.role, + })); + } + }, (error) => { + console.error("Error listening to profile changes in DevCard:", error); + }); + + return () => unsubscribe(); + }, [user?.uid, user?.role, user?.email]); + useEffect(() => { const fetch = async () => { - if (!user?.points) { + if (!realTimeUser?.points) { setRankLoading(false); return; } @@ -193,7 +227,7 @@ export default function DevCard({ user }: { user: any }) { const snap = await getCountFromServer( query( collection(db, 'leaderboard'), - where('points', '>', user.points) + where('points', '>', realTimeUser.points) ) ); setRank(snap.data().count + 1); @@ -204,13 +238,13 @@ export default function DevCard({ user }: { user: any }) { } }; fetch(); - }, [user?.points]); + }, [realTimeUser?.points]); useEffect(() => { setShowSkeleton(true); const timer = setTimeout(() => setShowSkeleton(false), 650); return () => clearTimeout(timer); - }, [user?.uid]); + }, [realTimeUser?.uid]); useEffect(() => { const t = setTimeout(() => setLangMounted(true), 500); @@ -219,34 +253,34 @@ export default function DevCard({ user }: { user: any }) { useEffect(() => { setAvatarLoadFailed(false); - }, [user?.photoURL]); + }, [realTimeUser?.photoURL]); - const levelInfo = calculateLevel(user?.points ?? 0); + const levelInfo = calculateLevel(realTimeUser?.points ?? 0); const level = levelInfo.currentLevel; const levelColor = resolveLevelColor(level.color); const levelBg = resolveLevelBg(level.bg); - const earnedBadges = (user?.achievements ?? []) + const earnedBadges = (realTimeUser?.achievements ?? []) .filter((id: string) => BADGE_REGISTRY[id]) .map((id: string) => ({ id, ...BADGE_REGISTRY[id] })); const topBadges = earnedBadges.slice(0, 4); const extraCount = Math.max(0, earnedBadges.length - 4); const topLangs = ( - (user?.githubStats?.topLanguages ?? []) as { + (realTimeUser?.githubStats?.topLanguages ?? []) as { language: string; count: number; }[] ).slice(0, 4); const totalLang = topLangs.reduce((s, l) => s + l.count, 0); - const animXP = useAnimatedCount(user?.points ?? 0); - const animStreak = useAnimatedCount(user?.streak ?? 0, 900); + const animXP = useAnimatedCount(realTimeUser?.points ?? 0); + const animStreak = useAnimatedCount(realTimeUser?.streak ?? 0, 900); const profileUrl = typeof window !== 'undefined' - ? `${window.location.origin}/u/${user?.uid}` - : `devpath.in/u/${user?.uid}`; + ? `${window.location.origin}/u/${realTimeUser?.uid}` + : `devpath.in/u/${realTimeUser?.uid}`; const waitForCardImages = async (root: HTMLElement) => { const imgs = Array.from(root.querySelectorAll('img')); @@ -306,7 +340,7 @@ export default function DevCard({ user }: { user: any }) { const url = canvas.toDataURL('image/png'); const a = document.createElement('a'); a.href = url; - a.download = `devcard-${user?.name?.replace(/\s+/g, '-').toLowerCase() ?? 'devcard'}.png`; + a.download = `devcard-${realTimeUser?.name?.replace(/\s+/g, '-').toLowerCase() ?? 'devcard'}.png`; a.click(); showSuccess('DevCard downloaded successfully.'); } catch { @@ -435,10 +469,10 @@ export default function DevCard({ user }: { user: any }) { >
- {user?.photoURL && !avatarLoadFailed ? ( + {realTimeUser?.photoURL && !avatarLoadFailed ? ( {user?.name ) : (
- {user?.name?.charAt(0)?.toUpperCase() ?? '?'} + {realTimeUser?.name?.charAt(0)?.toUpperCase() ?? '?'}
)} - {user?.name ?? 'Developer'} + {realTimeUser?.name ?? 'Developer'} {level.name} - {(user?.city || user?.state) && ( + {(realTimeUser?.city || realTimeUser?.state) && ( - {[user.city, user.state].filter(Boolean).join(', ')} + {[realTimeUser.city, realTimeUser.state].filter(Boolean).join(', ')} )} - Joined {fmtDate(user?.createdAt)} + Joined {fmtDate(realTimeUser?.createdAt)} - {user?.githubStats?.username && ( + {realTimeUser?.githubStats?.username && ( - {user.githubStats.username} + {realTimeUser.githubStats.username} )} - {user?.linkedin && ( + {realTimeUser?.linkedin && ( )} - {user?.instagram && ( + {realTimeUser?.instagram && (
- {user?.completedQuizzes?.length ?? 0} + {realTimeUser?.completedQuizzes?.length ?? 0} Quizzes @@ -621,23 +655,23 @@ export default function DevCard({ user }: { user: any }) { color: '#818cf8', }} > - {user?.githubStats?.connected ? ( + {realTimeUser?.githubStats?.connected ? ( ) : ( )} - {user?.githubStats?.connected + {realTimeUser?.githubStats?.connected ? fmtPoints( - user.githubStats.totalStars ?? - user.githubStats.stars ?? + realTimeUser.githubStats.totalStars ?? + realTimeUser.githubStats.stars ?? 0 ) - : (user?.followers?.length ?? 0)} + : (realTimeUser?.followers?.length ?? 0)} - {user?.githubStats?.connected ? 'GH Stars' : 'Followers'} + {realTimeUser?.githubStats?.connected ? 'GH Stars' : 'Followers'}