One Calendar is a privacy-first calendar web app built with Next.js. It has modern security features, including e2ee, password-protected sharing, and self-destructing share links ๐Ÿ“… calendar.xyehr.cn
5
fork

Configure Feed

Select the types of activity you want to include in your feed.

Merge pull request #144 from EvanTechDev/codex/fix-user-profile-image-loading-bug

Fix profile avatar loading and auth splash timing

authored by

Evan Huang and committed by
GitHub
30f078c5 2b3d5ab7

+37 -7
+22 -3
app/(app)/app/page.tsx
··· 3 3 import Calendar from "@/components/app/calendar" 4 4 import AuthWaitingLoading from "@/components/app/auth-waiting-loading" 5 5 import { useUser } from "@clerk/nextjs" 6 - import { useMemo, useState } from "react" 6 + import { useEffect, useMemo, useState } from "react" 7 7 8 8 function hasClerkSessionCookie() { 9 9 if (typeof document === "undefined") return false ··· 15 15 16 16 export default function Home() { 17 17 const { isLoaded } = useUser() 18 - const [hasSessionCookie] = useState(hasClerkSessionCookie) 18 + const [hasSessionCookie, setHasSessionCookie] = useState(hasClerkSessionCookie) 19 + const [minimumWaitDone, setMinimumWaitDone] = useState(false) 20 + 21 + useEffect(() => { 22 + const waitTimer = window.setTimeout(() => { 23 + setMinimumWaitDone(true) 24 + }, 500) 25 + 26 + const cookieCheckTimer = window.setInterval(() => { 27 + if (hasClerkSessionCookie()) { 28 + setHasSessionCookie(true) 29 + } 30 + }, 50) 31 + 32 + return () => { 33 + window.clearTimeout(waitTimer) 34 + window.clearInterval(cookieCheckTimer) 35 + } 36 + }, []) 19 37 20 38 const shouldShowAuthWait = useMemo(() => { 39 + if (!minimumWaitDone) return true 21 40 return hasSessionCookie && !isLoaded 22 - }, [hasSessionCookie, isLoaded]) 41 + }, [minimumWaitDone, hasSessionCookie, isLoaded]) 23 42 24 43 if (shouldShowAuthWait) { 25 44 return <AuthWaitingLoading />
+15 -4
components/app/profile/user-profile-button.tsx
··· 46 46 import { translations, useLanguage } from "@/lib/i18n" 47 47 import { useUser, SignOutButton } from "@clerk/nextjs" 48 48 import { useRouter } from "next/navigation" 49 - import Image from "next/image" 50 49 import { decryptPayload, encryptPayload, isEncryptedPayload } from "@/lib/crypto" 51 50 import { 52 51 clearEncryptionPassword, ··· 509 508 <DropdownMenuTrigger asChild> 510 509 {isSignedIn && user?.imageUrl ? ( 511 510 <Button variant="ghost" size="icon" className="rounded-full overflow-hidden h-8 w-8 p-0"> 512 - <Image src={user.imageUrl} alt="avatar" width={32} height={32} className="rounded-full object-cover" /> 511 + <img 512 + src={user.imageUrl} 513 + alt="avatar" 514 + width={32} 515 + height={32} 516 + className="rounded-full object-cover" 517 + loading="lazy" 518 + referrerPolicy="no-referrer" 519 + /> 513 520 </Button> 514 521 ) : ( 515 522 <Button variant={variant} size="icon" className={`rounded-full h-10 w-10 ${className}`}> ··· 573 580 {isSignedIn ? ( 574 581 <> 575 582 <div className="flex items-center gap-3"> 576 - <Image 583 + <img 577 584 src={user?.imageUrl || "/placeholder.svg"} 578 585 alt="avatar" 579 586 width={40} 580 587 height={40} 581 588 className="h-10 w-10 rounded-full border object-cover" 589 + loading="lazy" 590 + referrerPolicy="no-referrer" 582 591 /> 583 592 <div className="min-w-0"> 584 593 <p className="font-medium truncate">{[user?.firstName, user?.lastName].filter(Boolean).join(" ") || user?.username || "User"}</p> ··· 667 676 <div className="space-y-2"> 668 677 <Label>{t.avatar}</Label> 669 678 <div className="flex items-center gap-3"> 670 - <Image 679 + <img 671 680 src={user?.imageUrl || "/placeholder.svg"} 672 681 alt="avatar" 673 682 width={52} 674 683 height={52} 675 684 className="h-12 w-12 rounded-full border object-cover" 685 + loading="lazy" 686 + referrerPolicy="no-referrer" 676 687 /> 677 688 <Label 678 689 htmlFor="profile-avatar-input"