Skip to content

Commit e6b9c09

Browse files
authored
implement sever and client side caching, few other tweaks (#2103)
1 parent 6417c98 commit e6b9c09

File tree

18 files changed

+98
-81
lines changed

18 files changed

+98
-81
lines changed

bun.lock

Lines changed: 5 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

components/NavigationTab.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,10 @@ function NavigationTab({ title, counter, path = `/${title.toLowerCase()}` }: Pro
2525
},
2626
isActive && tw`text-primary bg-primary-hover`,
2727
]}
28-
hoverStyle={tw`text-tertiary bg-palette-gray6 dark:bg-default`}
28+
hoverStyle={[
29+
tw`text-tertiary bg-palette-gray6 dark:bg-default`,
30+
isActive && tw`bg-primary-active-hover`,
31+
]}
2932
target="_self">
3033
<View style={tw`flex-row items-center gap-2 px-4 pt-1.5 pb-2`}>
3134
<P style={[tw`text-white`, isActive && tw`text-primary`]}>{title}</P>

components/Package/DetailsNavigation.tsx

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,13 @@ export default function DetailsNavigation({ library }: Props) {
1818
style={[tw`pt-9 pb-3 gap-1`, isSmallScreen && tw`pt-5 gap-1.5`]}>
1919
<ContentContainer style={tw`gap-2 flex-row px-5`}>
2020
<NavigationTab title="Overview" path={`/package/${library.npmPkg}`} />
21-
<NavigationTab
22-
title="Versions"
23-
counter={library.npm?.versionsCount}
24-
path={`/package/${library.npmPkg}/versions`}
25-
/>
21+
{!library.template && (
22+
<NavigationTab
23+
title="Versions"
24+
counter={library.npm?.versionsCount}
25+
path={`/package/${library.npmPkg}/versions`}
26+
/>
27+
)}
2628
<NavigationTab title="Score" path={`/package/${library.npmPkg}/score`} />
2729
</ContentContainer>
2830
</Navigation>

components/Package/ReadmeBox.tsx

Lines changed: 30 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
import { Md } from '@m2d/react-markdown/client';
22
import { capitalize } from 'es-toolkit';
3-
import { useEffect, useState } from 'react';
43
import { View } from 'react-native';
54
import { type Theme } from 'react-shiki';
65
import rehypeRaw from 'rehype-raw';
76
import rehypeSanitize from 'rehype-sanitize';
87
import remarkEmoji from 'remark-emoji';
98
import remarkGfm from 'remark-gfm';
9+
import useSWR from 'swr';
1010

1111
import { A, P } from '~/common/styleguide';
1212
import { ReadmeFile } from '~/components/Icons';
@@ -26,62 +26,30 @@ type Props = {
2626
};
2727

2828
export default function ReadmeBox({ packageName, githubUrl, isTemplate, loader = false }: Props) {
29-
const [readmeContent, setReadmeContent] = useState<string | null | undefined>(undefined);
30-
31-
useEffect(() => {
32-
if (loader) {
33-
return;
34-
}
35-
36-
let cancelled = false;
37-
38-
void (async () => {
39-
if (isTemplate) {
40-
const templateRawUrl = githubUrl?.replace('github.com/', 'raw.githubusercontent.com/');
41-
let readmeResponse = await fetch(`${templateRawUrl}/main/README.md`);
42-
43-
if (readmeResponse.status === 404) {
44-
readmeResponse = await fetch(`${templateRawUrl}/master/README.md`);
29+
const { data, error, isLoading } = useSWR(
30+
isTemplate
31+
? `${githubUrl?.replace('github.com/', 'raw.githubusercontent.com/')}/HEAD/README.md`
32+
: `https://unpkg.com/${packageName}/README.md`,
33+
(url: string) =>
34+
fetch(url).then(res => {
35+
if (res.status === 404) {
36+
return '';
37+
} else if (res.status === 200) {
38+
return res.text();
4539
}
46-
47-
if (readmeResponse.status === 200) {
48-
const readmeContent = await readmeResponse.text();
49-
if (!cancelled) {
50-
setReadmeContent(readmeContent);
51-
}
52-
} else {
53-
setReadmeContent('');
54-
}
55-
} else {
56-
try {
57-
const readmeResponse = await fetch(`https://unpkg.com/${packageName}/README.md`);
58-
const readmeContent = await readmeResponse.text();
59-
if (!cancelled) {
60-
setReadmeContent(readmeContent);
61-
}
62-
} catch (error: any) {
63-
if (error instanceof Error) {
64-
if (error.message === 'Failed to fetch') {
65-
setReadmeContent('');
66-
return;
67-
}
68-
}
69-
if (!cancelled) {
70-
setReadmeContent(null);
71-
}
72-
}
73-
}
74-
})();
75-
return () => {
76-
cancelled = true;
77-
};
78-
}, []);
40+
return null;
41+
}),
42+
{
43+
dedupingInterval: 60_000 * 10,
44+
revalidateOnFocus: false,
45+
}
46+
);
7947

8048
if (!githubUrl || !packageName) {
8149
return null;
8250
}
8351

84-
const readmeFallbackContent = getReadmeFallbackContent(readmeContent);
52+
const readmeFallbackContent = getReadmeFallbackContent(data, isLoading, error);
8553

8654
return (
8755
<View
@@ -92,9 +60,9 @@ export default function ReadmeBox({ packageName, githubUrl, isTemplate, loader =
9260
<P>Readme</P>
9361
</View>
9462
<View style={tw`p-4 pt-3 font-light`}>
95-
{!readmeContent && readmeFallbackContent ? (
63+
{!data && readmeFallbackContent ? (
9664
<View style={tw`gap-4 py-6`}>
97-
{readmeContent === undefined && <ThreeDotsLoader />}
65+
{isLoading && <ThreeDotsLoader />}
9866
<P style={tw`text-center`}>{readmeFallbackContent}</P>
9967
</View>
10068
) : (
@@ -201,21 +169,25 @@ export default function ReadmeBox({ packageName, githubUrl, isTemplate, loader =
201169
}}
202170
rehypePlugins={[rehypeRaw, rehypeSanitize]}
203171
remarkPlugins={[remarkGfm, remarkEmoji]}>
204-
{readmeContent ?? undefined}
172+
{data ?? undefined}
205173
</Md>
206174
)}
207175
</View>
208176
</View>
209177
);
210178
}
211179

212-
function getReadmeFallbackContent(readmeContent?: string | null): string | null {
213-
if (readmeContent === undefined) {
180+
function getReadmeFallbackContent(
181+
readmeContent?: string | null,
182+
isLoading?: boolean,
183+
error?: string
184+
): string | null {
185+
if (isLoading) {
214186
return 'Loading README…';
215-
} else if (readmeContent === null) {
216-
return 'Cannot fetch README file content.';
217187
} else if (readmeContent === '') {
218188
return 'This package does not have a README file.';
189+
} else if (readmeContent === null || error) {
190+
return `Cannot fetch README file content.`;
219191
}
220192
return null;
221193
}

components/Search.tsx

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,13 @@ export default function Search({ query, total }: Props) {
3030
const { replace } = useRouter();
3131
const { isSmallScreen } = useLayout();
3232

33+
useEffect(() => {
34+
// @ts-expect-error using native input value to clear on same-page navigation
35+
if (!isInputFocused && inputRef?.current?.value && !search) {
36+
inputRef.current.clear();
37+
}
38+
}, [search]);
39+
3340
useEffect(() => {
3441
if (isApple !== null) {
3542
function keyDownListener(event: KeyboardEvent) {

components/TopBar.tsx

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
import { Header as HtmlHeader } from '@expo/html-elements';
2-
import Link from 'next/link';
32
import { useContext } from 'react';
43
import { View } from 'react-native';
54

6-
import { H5, P, useLayout } from '~/common/styleguide';
5+
import { A, H5, P, useLayout } from '~/common/styleguide';
76
import ContentContainer from '~/components/ContentContainer';
87
import NavigationTab from '~/components/NavigationTab';
98
import CustomAppearanceContext from '~/context/CustomAppearanceContext';
@@ -24,9 +23,9 @@ export default function TopBar() {
2423
<View style={[tw`flex-row items-center`, !isBelowMaxWidth && tw`min-w-[255px]`]}>
2524
<Logo style={tw`text-primary`} width={29} height={26} />
2625
<H5 style={[tw`-mt-0.5`, isBelowMaxWidth && tw`text-lg`]}>
27-
<Link href="/" style={tw`text-primary ml-2 font-bold no-underline`}>
26+
<A href="/" style={tw`text-primary ml-2 font-bold no-underline`}>
2827
{isBelowMaxWidth ? 'Directory' : 'React Native Directory'}
29-
</Link>
28+
</A>
3029
</H5>
3130
</View>
3231
<View style={[isBelowMaxWidth && tw`mr-auto`, isSmallScreen && tw`hidden`]}>

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
"rehype-sanitize": "^6.0.0",
4747
"remark-emoji": "^5.0.2",
4848
"remark-gfm": "^4.0.1",
49+
"swr": "^2.3.8",
4950
"tailwindcss": "^3.4.19",
5051
"twrnc": "^4.16.0",
5152
"use-debounce": "^10.0.6"

pages/_app.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { SafeAreaProvider } from 'react-native-safe-area-context';
55

66
import Footer from '~/components/Footer';
77
import CustomAppearanceProvider from '~/context/CustomAppearanceProvider';
8+
import tw from '~/util/tailwind';
89

910
import '~/styles/styles.css';
1011

@@ -29,7 +30,7 @@ function App({ pageProps, Component }: AppProps) {
2930
content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=2,viewport-fit=cover"
3031
/>
3132
</Head>
32-
<main>
33+
<main style={tw`flex flex-col flex-1`}>
3334
<Component {...pageProps} />
3435
</main>
3536
<Footer />

pages/api/libraries/check.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ export default function handler(req: NextApiRequest, res: NextApiResponse) {
2222
}
2323

2424
res.statusCode = 200;
25+
res.setHeader('Cache-Control', 'public, s-maxage=60, stale-while-revalidate=300');
2526
const result: CheckResultsType = {};
2627
packages.forEach(pkgName => {
2728
result[pkgName] = DATASET[pkgName];

pages/api/libraries/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,8 @@ export default function handler(req: NextApiRequest, res: NextApiResponse) {
110110
: filteredLibraries;
111111
const filteredAndPaginatedLibraries = take(drop(relevanceSortedLibraries, offset), limit);
112112

113+
res.setHeader('Cache-Control', 'public, s-maxage=60, stale-while-revalidate=300');
114+
113115
return res.json({
114116
libraries: filteredAndPaginatedLibraries,
115117
total: filteredLibraries.length,

0 commit comments

Comments
 (0)