1- import { formatDistanceToNow } from 'date-fns' ;
2- import { BriefcaseBusiness , ExternalLink , Hourglass , Link2 , Mail , MapPin , Timer , UsersRound } from 'lucide-react' ;
3- import Image from 'next/image' ;
4- import Link from 'next/link' ;
5-
61import { Header } from '@/components/header/header' ;
7- import { Page } from '@/components/page/page' ;
8- import { AspectRatio } from '@/components/ui/aspect-ratio' ;
9- import { Avatar , AvatarImage } from '@/components/ui/avatar' ;
10- import { Button } from '@/components/ui/button' ;
11- import { Tooltip , TooltipContent , TooltipProvider , TooltipTrigger } from '@/components/ui/tooltip' ;
122import { graphqlDirect } from '@/lib/graphql/graphql-direct' ;
133import { TopRanksDocument } from '@/types/generated/graphql' ;
144
15- import { ProfileListItem } from './components/profile-list-item' ;
16- import { RefreshButton } from './components/refresh-button' ;
17- import { fetchProfileData } from './utils/fetch-profile-data' ;
18- import { getSocialIcon } from './utils/get-social-icon' ;
19-
205type ProfileLayoutProps = Readonly < { children : React . ReactNode ; params : Promise < { login : string } > } > ;
216
227// Next.js will invalidate the cache when a
23- // request comes in, at most once every 3600 seconds.
24- export const revalidate = 3600 ;
8+ // request comes in, at most once every 10800 seconds.
9+ export const revalidate = 10800 ;
2510
2611// We'll prerender only the params from `generateStaticParams` at build time.
2712// If a request comes in for a path that hasn't been generated,
@@ -43,110 +28,11 @@ export async function generateStaticParams() {
4328 return [ ...uniqueLogins ] . map ( ( login ) => ( { login } ) ) ;
4429}
4530
46- export default async function ProfileLayout ( { params, children } : ProfileLayoutProps ) {
47- const { login } = await params ;
48- const { user } = await fetchProfileData ( login ) ;
49-
50- if ( ! user ) {
51- return (
52- < Page className = "gap-6 flex-col md:flex-row" >
53- < div className = "flex-grow flex flex-col gap-6" >
54- < h1 className = "text-2xl font-semibold" > User not found</ h1 >
55- < p className = "text-muted-foreground" > The user you are looking for does not exist.</ p >
56- </ div >
57- </ Page >
58- ) ;
59- }
60-
61- const showContact = ! ! user ?. email || ! ! user ?. websiteUrl || ! ! user ?. socialAccounts ?. nodes ?. length ;
62-
31+ export default async function ProfileLayout ( { children } : ProfileLayoutProps ) {
6332 return (
6433 < >
6534 < Header />
66- < Page className = "gap-6 flex-col md:flex-row" >
67- < div className = "w-full md:w-3xs xl:w-2xs flex flex-col shrink-0 gap-4" >
68- < div className = "flex flex-row md:flex-col items-center md:items-start gap-4" >
69- < div className = "w-[64] sm:w-[128] md:w-full" >
70- < AspectRatio ratio = { 1 } >
71- < Avatar className = "w-full h-full rounded-full" asChild >
72- < AvatarImage src = { user . avatarUrl ! } />
73- </ Avatar >
74- </ AspectRatio >
75- </ div >
76- < div >
77- < h1 className = "font-semibold text-2xl" > { user . name } </ h1 >
78- < h2 className = "text-muted-foreground" > @{ user . login } </ h2 >
79- </ div >
80- </ div >
81- < div className = "flex flex-row md:flex-col gap-4" >
82- < RefreshButton />
83- < Button size = "sm" variant = "secondary" className = "flex-grow" asChild >
84- < Link href = { `https://github.com/${ user . login } ` } target = "_blank" rel = "noopener noreferrer" >
85- Open GitHub
86- < ExternalLink className = "size-4" />
87- </ Link >
88- </ Button >
89- </ div >
90- < div className = "flex flex-col gap-6" >
91- < div className = "flex flex-col gap-1.5" >
92- < ProfileListItem value = { user . location } Icon = { MapPin } />
93- < ProfileListItem value = { user . company } Icon = { BriefcaseBusiness } />
94- < ProfileListItem value = { 'Profile age: ' + formatDistanceToNow ( user . githubCreatedAt ) } Icon = { Hourglass } />
95- < ProfileListItem
96- value = { 'Updated ' + formatDistanceToNow ( user . githubFetchedAt , { addSuffix : true } ) }
97- Icon = { Timer }
98- />
99- < ProfileListItem
100- value = { `${ user . followersCount ?. toLocaleString (
101- 'en-US' ,
102- ) } followers • ${ user . followingCount ?. toLocaleString ( 'en-US' ) } following`}
103- Icon = { UsersRound }
104- />
105- </ div >
106- { showContact && (
107- < div className = "flex flex-col gap-1.5" >
108- < h4 className = "text-lg font-semibold" > Contacts</ h4 >
109- < ProfileListItem value = { user . email } url = { `mailto:${ user . email } ` } Icon = { Mail } />
110- < ProfileListItem value = { user . websiteUrl } url = { user . websiteUrl ! } Icon = { Link2 } />
111- { user . socialAccounts ?. nodes ?. map ( ( account ) => (
112- < ProfileListItem
113- key = { account . displayName }
114- value = { account . displayName }
115- url = { account . url }
116- Icon = { getSocialIcon ( account . provider , account . url ) }
117- />
118- ) ) }
119- </ div >
120- ) }
121- { ! ! user . organizations ?. length && (
122- < div className = "flex flex-col gap-1.5" >
123- < h4 className = "text-lg font-semibold" > Organizations</ h4 >
124- < div className = "flex gap-1 flex-wrap" >
125- { user . organizations ?. map ( ( org ) => (
126- < TooltipProvider key = { org . login } >
127- < Tooltip >
128- < TooltipTrigger asChild >
129- < Link href = { `https://github.com/${ org . login } ` } target = "_blank" rel = "noopener noreferrer" >
130- < Image
131- src = { org . avatarUrl ! }
132- alt = { org . login }
133- width = { 32 }
134- height = { 32 }
135- className = "rounded-sm bg-secondary"
136- />
137- </ Link >
138- </ TooltipTrigger >
139- < TooltipContent > { org . login } </ TooltipContent >
140- </ Tooltip >
141- </ TooltipProvider >
142- ) ) }
143- </ div >
144- </ div >
145- ) }
146- </ div >
147- </ div >
148- { children }
149- </ Page >
35+ { children }
15036 </ >
15137 ) ;
15238}
0 commit comments