Skip to content

Commit cb0d46b

Browse files
committed
fix ssr errors
1 parent 6cd79d9 commit cb0d46b

File tree

5 files changed

+62
-68
lines changed

5 files changed

+62
-68
lines changed

app/profile/[login]/layout.tsx

Lines changed: 4 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,15 @@
11
'use cache';
2+
23
import { cacheLife, cacheTag } from 'next/cache';
34
import { Suspense } from 'react';
45

6+
import Loading from './loading';
57
import { Header } from '@/components/header/header';
68
import { Tab } from '@/components/tabs/tabs';
79
import { TabsBar } from '@/components/tabs/tabs-bar';
810
import { graphqlDirect } from '@/lib/graphql/graphql-direct';
911
import { TopGlobalRankingsDocument } from '@/types/generated/graphql';
1012

11-
import Loading from './loading';
12-
13-
type ProfileLayoutProps = Readonly<{ children: React.ReactNode; params: Promise<{ login: string }> }>;
14-
1513
export async function generateStaticParams() {
1614
const { byStars, byContribution, byFollowers } = (await graphqlDirect(TopGlobalRankingsDocument)) ?? {};
1715
const mergedRanks = [...byStars, ...byContribution, ...byFollowers];
@@ -36,13 +34,13 @@ function LayoutLoading() {
3634
);
3735
}
3836

39-
async function ProfileLayoutAwaitParams({ children, params }: LayoutProps<'/profile/[login]'>) {
37+
export default async function ProfileLayout({ children, params }: LayoutProps<'/profile/[login]'>) {
4038
const { login } = await params;
4139
cacheLife('hours');
4240
cacheTag(`profile:${login}`);
4341

4442
return (
45-
<>
43+
<Suspense fallback={<LayoutLoading />}>
4644
<Header login={login} />
4745
<TabsBar className="mb-4">
4846
<Tab href={`/profile/${login}`} pathnames={[`/profile/${login}`, `/profile/${login}/country`]}>
@@ -53,14 +51,6 @@ async function ProfileLayoutAwaitParams({ children, params }: LayoutProps<'/prof
5351
<Tab href={`/profile/${login}/languages`}>Languages</Tab>
5452
</TabsBar>
5553
{children}
56-
</>
57-
);
58-
}
59-
60-
export default async function ProfileLayout({ children, params }: ProfileLayoutProps) {
61-
return (
62-
<Suspense fallback={<LayoutLoading />}>
63-
<ProfileLayoutAwaitParams params={params}>{children}</ProfileLayoutAwaitParams>
6454
</Suspense>
6555
);
6656
}

app/profile/[login]/repositories/page.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,12 @@ import type { Metadata } from 'next';
44
import { cacheLife, cacheTag } from 'next/cache';
55
import { notFound } from 'next/navigation';
66

7-
import { fetchProfilePageRepositories } from '@/graphql/helpers/fetch-profile-page-repositories';
87
import { LayoutLeftColumn } from '../components/layout-left-column';
98
import NotFound from '../not-found';
109
import { buildProfileTabSEO } from '../seo';
1110
import { UserContributionsList } from './components/user-contributions-list';
1211
import { UserRepositoriesList } from './components/user-repositories-list';
12+
import { fetchProfilePageRepositories } from '@/graphql/helpers/fetch-profile-page-repositories';
1313

1414
export async function generateMetadata({ params }: PageProps<'/profile/[login]/repositories'>): Promise<Metadata> {
1515
const { login } = await params;
@@ -44,7 +44,7 @@ export default async function ProfileRepositories({ params }: PageProps<'/profil
4444
<LayoutLeftColumn user={user}>
4545
<div className="flex flex-col gap-10">
4646
<div className="flex flex-col gap-3">
47-
<h2 className="text-xl font-semibold">Repositories</h2>
47+
{!!repositories?.length && <h2 className="text-xl font-semibold">Repositories</h2>}
4848
<UserRepositoriesList
4949
repositories={repositories}
5050
login={login}
@@ -53,8 +53,8 @@ export default async function ProfileRepositories({ params }: PageProps<'/profil
5353
/>
5454
</div>
5555
<div className="flex flex-col gap-3">
56-
<h2 className="text-xl font-semibold">Contributions</h2>
57-
<UserContributionsList contributions={contributions} login={login} loadMore />
56+
{!!contributions?.length && <h2 className="text-xl font-semibold">Contributions</h2>}
57+
{!!contributions?.length && <UserContributionsList contributions={contributions} login={login} loadMore />}
5858
</div>
5959
</div>
6060
</LayoutLeftColumn>
Lines changed: 35 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
'use client';
22

3-
import { type FC, type ReactNode, useState } from 'react';
3+
import { type FC, type ReactNode, useEffect, useState } from 'react';
44
import { useMediaQuery } from 'usehooks-ts';
55

66
import { Button } from '@/components/ui/button';
@@ -31,40 +31,46 @@ type AdaptiveModalProps = {
3131

3232
export const AdaptiveModal: FC<AdaptiveModalProps> = ({ trigger, children, title, description }) => {
3333
const [open, setOpen] = useState(false);
34+
const [mounted, setMounted] = useState(false);
3435
const isDesktop = useMediaQuery('(min-width: 768px)');
3536

36-
if (isDesktop) {
37+
useEffect(() => {
38+
setMounted(true);
39+
}, []);
40+
41+
// Always render Drawer on server and initial client render to avoid hydration mismatch
42+
if (!mounted || !isDesktop) {
3743
return (
38-
<Dialog open={open} onOpenChange={setOpen}>
39-
<DialogTrigger asChild>{trigger}</DialogTrigger>
40-
<DialogContent className="max-h-3/4 overflow-y-auto">
41-
<DialogHeader>
42-
<DialogTitle>{title}</DialogTitle>
43-
<DialogDescription>{description}</DialogDescription>
44-
</DialogHeader>
45-
{children}
46-
</DialogContent>
47-
</Dialog>
44+
<Drawer open={open} onOpenChange={setOpen}>
45+
<DrawerTrigger asChild>{trigger}</DrawerTrigger>
46+
<DrawerContent>
47+
<DrawerHeader>
48+
<DrawerTitle>{title}</DrawerTitle>
49+
</DrawerHeader>
50+
<div className="overflow-y-auto px-4 pb-4 flex flex-col gap-4">
51+
<div className="text-sm text-muted-foreground">{description}</div>
52+
{children}
53+
</div>
54+
<DrawerFooter>
55+
<DrawerClose asChild>
56+
<Button variant="outline">Close</Button>
57+
</DrawerClose>
58+
</DrawerFooter>
59+
</DrawerContent>
60+
</Drawer>
4861
);
4962
}
5063

5164
return (
52-
<Drawer open={open} onOpenChange={setOpen}>
53-
<DrawerTrigger asChild>{trigger}</DrawerTrigger>
54-
<DrawerContent>
55-
<DrawerHeader>
56-
<DrawerTitle>{title}</DrawerTitle>
57-
</DrawerHeader>
58-
<div className="overflow-y-auto px-4 pb-4 flex flex-col gap-4">
59-
<div className="text-sm text-muted-foreground">{description}</div>
60-
{children}
61-
</div>
62-
<DrawerFooter>
63-
<DrawerClose asChild>
64-
<Button variant="outline">Close</Button>
65-
</DrawerClose>
66-
</DrawerFooter>
67-
</DrawerContent>
68-
</Drawer>
65+
<Dialog open={open} onOpenChange={setOpen}>
66+
<DialogTrigger asChild>{trigger}</DialogTrigger>
67+
<DialogContent className="max-h-3/4 overflow-y-auto">
68+
<DialogHeader>
69+
<DialogTitle>{title}</DialogTitle>
70+
<DialogDescription>{description}</DialogDescription>
71+
</DialogHeader>
72+
{children}
73+
</DialogContent>
74+
</Dialog>
6975
);
7076
};
Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
'use client';
22

33
import type { ReactNode } from 'react';
4+
import { useEffect, useState } from 'react';
45
import { useMediaQuery } from 'usehooks-ts';
56

6-
import { cn } from '@/lib/utils';
7-
87
import { Popover, PopoverContent, PopoverTrigger } from '../ui/popover';
98
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '../ui/tooltip';
9+
import { cn } from '@/lib/utils';
1010

1111
type AdaptiveTooltipProps = {
1212
trigger: ReactNode;
@@ -15,23 +15,29 @@ type AdaptiveTooltipProps = {
1515
};
1616

1717
export const AdaptiveTooltip = ({ trigger, children, contentClassName }: AdaptiveTooltipProps) => {
18+
const [mounted, setMounted] = useState(false);
1819
const isDesktop = useMediaQuery('(min-width: 768px)');
1920

20-
if (isDesktop) {
21+
useEffect(() => {
22+
setMounted(true);
23+
}, []);
24+
25+
// Always render Popover on server and initial client render to avoid hydration mismatch
26+
if (!mounted || !isDesktop) {
2127
return (
22-
<TooltipProvider>
23-
<Tooltip>
24-
<TooltipTrigger asChild>{trigger}</TooltipTrigger>
25-
<TooltipContent className={cn('text-sm', contentClassName)}>{children}</TooltipContent>
26-
</Tooltip>
27-
</TooltipProvider>
28+
<Popover>
29+
<PopoverTrigger asChild>{trigger}</PopoverTrigger>
30+
<PopoverContent className="text-sm w-auto">{children}</PopoverContent>
31+
</Popover>
2832
);
2933
}
3034

3135
return (
32-
<Popover>
33-
<PopoverTrigger asChild>{trigger}</PopoverTrigger>
34-
<PopoverContent className="text-sm w-auto">{children}</PopoverContent>
35-
</Popover>
36+
<TooltipProvider>
37+
<Tooltip>
38+
<TooltipTrigger asChild>{trigger}</TooltipTrigger>
39+
<TooltipContent className={cn('text-sm', contentClassName)}>{children}</TooltipContent>
40+
</Tooltip>
41+
</TooltipProvider>
3642
);
3743
};

utils/heap-snapshot.ts

Lines changed: 0 additions & 8 deletions
This file was deleted.

0 commit comments

Comments
 (0)