Skip to content

Commit 14739a8

Browse files
authored
simplify SSR fetches, optimize popular page, improve search filters (#2199)
1 parent f8f6586 commit 14739a8

22 files changed

Lines changed: 239 additions & 258 deletions

components/CheckBox.tsx

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,17 @@
1+
import { type ComponentType } from 'react';
12
import { type StyleProp, View, type ViewStyle } from 'react-native';
23

34
import tw from '~/util/tailwind';
45

5-
import { Check } from './Icons';
6+
import { Check, type IconProps } from './Icons';
67

78
type Props = {
89
style?: StyleProp<ViewStyle>;
910
value?: boolean;
11+
Icon?: ComponentType<IconProps>;
1012
};
1113

12-
export default function CheckBox({ style, value }: Props) {
14+
export default function CheckBox({ style, value, Icon = Check }: Props) {
1315
return (
1416
<View
1517
style={[
@@ -20,7 +22,7 @@ export default function CheckBox({ style, value }: Props) {
2022
{ transition: 'border-color .33s, background-color .33s' },
2123
style,
2224
]}>
23-
{value ? <Check width={14} height={10} style={tw`text-white dark:text-black`} /> : null}
25+
{value ? <Icon width={14} height={10} style={tw`text-white dark:text-black`} /> : null}
2426
</View>
2527
);
2628
}

components/Explore/ExploreSection.tsx

Lines changed: 13 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ import { A, H3, P } from '~/common/styleguide';
66
import { type IconProps } from '~/components/Icons';
77
import LoadingContent from '~/components/Library/LoadingContent';
88
import { type LibraryType, type Query } from '~/types';
9+
import { POPULAR_QUERY_BASE } from '~/util/Constants';
10+
import { TimeRange } from '~/util/datetime';
911
import tw from '~/util/tailwind';
1012
import urlWithQuery from '~/util/urlWithQuery';
1113

@@ -16,27 +18,12 @@ const LibraryWithLoading = dynamic(() => import('~/components/Library'), {
1618
type Props = {
1719
data: LibraryType[];
1820
title: string;
19-
filter: (library: LibraryType) => boolean;
2021
icon?: FunctionComponent<IconProps>;
21-
count?: number;
2222
queryParams?: Query;
2323
};
2424

25-
const UPDATED_IN = 1000 * 60 * 60 * 24 * 90; // 90 days
26-
const DEFAULT_PARAMS: Query = {
27-
wasRecentlyUpdated: 'true',
28-
isMaintained: 'true',
29-
order: 'popularity',
30-
};
31-
32-
export default function ExploreSection({
33-
data,
34-
title,
35-
filter,
36-
icon,
37-
count = 4,
38-
queryParams = { [title.toLowerCase()]: true },
39-
}: Props) {
25+
const UPDATED_IN = 1000 * TimeRange.MONTH * 3;
26+
export default function ExploreSection({ data, title, icon, queryParams }: Props) {
4027
const hashLink = title.replace(/\s/g, '').toLowerCase();
4128

4229
return (
@@ -51,12 +38,16 @@ export default function ExploreSection({
5138
{title}
5239
</A>
5340
</H3>
54-
<View style={tw`flex-1 flex-row flex-wrap pt-3`}>
55-
{renderLibs(data.filter(filter), count)}
56-
</View>
41+
<View style={tw`flex-1 flex-row flex-wrap pt-3`}>{renderLibs(data)}</View>
5742
<P style={tw`px-6 pb-6 pt-2 text-sm font-light text-secondary`}>
5843
Want to see more? Check out other{' '}
59-
<A href={urlWithQuery('/packages', { ...queryParams, ...DEFAULT_PARAMS })} target="_self">
44+
<A
45+
href={urlWithQuery('/packages', {
46+
[title.toLowerCase()]: true,
47+
...queryParams,
48+
...POPULAR_QUERY_BASE,
49+
})}
50+
target="_self">
6051
{title} libraries
6152
</A>{' '}
6253
in the directory!
@@ -65,11 +56,10 @@ export default function ExploreSection({
6556
);
6657
}
6758

68-
function renderLibs(list: LibraryType[], count = 4) {
59+
function renderLibs(list: LibraryType[]) {
6960
const now = Date.now();
7061
return list
7162
.filter(({ github }) => now - new Date(github.stats.updatedAt).getTime() < UPDATED_IN)
72-
.splice(0, count)
7363
.map((item: LibraryType, index: number) => (
7464
<LibraryWithLoading
7565
key={`explore-item-${index}-${item.github.name}`}

components/Filters/ToggleLink.tsx

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { View } from 'react-native';
44

55
import { P } from '~/common/styleguide';
66
import CheckBox from '~/components/CheckBox';
7+
import { XIcon } from '~/components/Icons';
78
import { type FilterParamsType, type Query } from '~/types';
89
import tw from '~/util/tailwind';
910
import urlWithQuery from '~/util/urlWithQuery';
@@ -12,11 +13,13 @@ type Props = {
1213
query: Query;
1314
filterParam: FilterParamsType;
1415
basePath?: string;
16+
allowFalse?: boolean;
1517
};
1618

17-
export function ToggleLink({ query, filterParam, basePath = '/packages' }: Props) {
19+
export function ToggleLink({ query, filterParam, basePath = '/packages', allowFalse }: Props) {
1820
const [isHovered, setHovered] = useState<boolean>(false);
1921
const isSelected = !!query[filterParam.param];
22+
const isFalsy = allowFalse && query[filterParam.param] === 'false';
2023

2124
return (
2225
<Link
@@ -30,7 +33,15 @@ export function ToggleLink({ query, filterParam, basePath = '/packages' }: Props
3033
style={tw`cursor-pointer flex-row items-center`}
3134
onPointerEnter={() => setHovered(true)}
3235
onPointerLeave={() => setHovered(false)}>
33-
<CheckBox value={isSelected} style={isHovered && tw`border-primary-dark`} />
36+
<CheckBox
37+
value={isSelected}
38+
style={[
39+
isFalsy && tw`border-error bg-error`,
40+
isHovered && tw`border-primary-dark`,
41+
isHovered && isFalsy && tw`border-error`,
42+
]}
43+
Icon={isFalsy ? XIcon : undefined}
44+
/>
3445
<P style={[tw`text-sm font-light leading-[18px]`, isHovered && tw`text-hover`]}>
3546
{filterParam.title}
3647
</P>

components/Filters/index.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ export function Filters({ query, style, basePath = '/packages' }: FiltersProps)
3737
query={pageQuery}
3838
filterParam={platform}
3939
basePath={basePath}
40+
allowFalse
4041
/>
4142
))}
4243
</FiltersSection>
@@ -79,6 +80,7 @@ export function Filters({ query, style, basePath = '/packages' }: FiltersProps)
7980
query={pageQuery}
8081
filterParam={compatibility}
8182
basePath={basePath}
83+
allowFalse
8284
/>
8385
))}
8486
</FiltersSection>

components/Pagination.tsx

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,15 @@ type Props = {
1414
query: Query;
1515
total?: number | null;
1616
style?: ViewStyle;
17+
noTags?: boolean;
1718
basePath?: string;
1819
};
1920

2021
type ArrowButtonProps = {
2122
disabled?: boolean;
2223
};
2324

24-
export default function Pagination({ query, total, style, basePath = '/packages' }: Props) {
25+
export default function Pagination({ query, total, style, noTags, basePath = '/packages' }: Props) {
2526
const currentOffset = query.offset ? Number.parseInt(query.offset, 10) : 0;
2627
const currentPage = Math.floor(currentOffset / NUM_PER_PAGE) + 1;
2728

@@ -37,7 +38,16 @@ export default function Pagination({ query, total, style, basePath = '/packages'
3738

3839
return (
3940
<View style={tw`flex-row justify-between`}>
40-
{query.owner ? <SearchTag title="Owner" value={query.owner} /> : <View />}
41+
{!noTags ? (
42+
<View>
43+
{query.owner && <SearchTag title="Owner" value={query.owner} />}
44+
{query.minPopularity && (
45+
<SearchTag title="Minimal popularity" value={query.minPopularity} />
46+
)}
47+
</View>
48+
) : (
49+
<View />
50+
)}
4151
<View style={[tw`flex-row items-center justify-end`, style]}>
4252
{backDisabled ? (
4353
<BackArrow disabled />

components/SearchTag.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ type Props = {
1414
export default function SearchTag({ title, value }: Props) {
1515
return (
1616
<View
17-
style={tw`flex-row items-center rounded border border-default bg-palette-gray1 pr-1.5 text-[12px] no-underline dark:bg-dark`}>
17+
style={tw`h-full flex-row items-center rounded border border-default bg-palette-gray1 pr-1.5 text-[12px] no-underline dark:bg-dark`}>
1818
<View
1919
style={tw`h-full select-none justify-center border-r border-default bg-palette-gray2 pl-2.5 pr-2 text-secondary dark:bg-powder`}>
2020
<span>{title}</span>

pages/404.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
import * as Sentry from '@sentry/react';
1+
import { captureMessage } from '@sentry/react';
22

33
import ErrorScene from '~/scenes/ErrorScene';
44

55
export default function NotFound() {
6-
Sentry.captureMessage('404');
6+
captureMessage('404');
77
return <ErrorScene statusCode={404} />;
88
}

pages/index.tsx

Lines changed: 29 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,7 @@ import { type NextPageContext } from 'next';
33

44
import HomeScene from '~/scenes/HomeScene';
55
import { type HomePageProps } from '~/types/pages';
6-
import { NEXT_1H_CACHE_HEADER } from '~/util/Constants';
7-
import getApiUrl from '~/util/getApiUrl';
6+
import { ssrFetch } from '~/util/SSRFetch';
87
import urlWithQuery from '~/util/urlWithQuery';
98

109
function Index(props: HomePageProps) {
@@ -22,44 +21,39 @@ Index.getInitialProps = async (ctx: NextPageContext) => {
2221
return;
2322
}
2423

25-
const mostDownloadedResponse = await fetch(
26-
getApiUrl(
27-
urlWithQuery('/libraries', {
28-
order: 'downloads',
29-
limit: LIMIT.toString(),
30-
isMaintained: 'true',
31-
hasNativeCode: 'true',
32-
}),
33-
ctx
34-
),
35-
NEXT_1H_CACHE_HEADER
24+
const mostDownloadedResponse = await ssrFetch(
25+
'/libraries',
26+
{
27+
order: 'downloads',
28+
limit: LIMIT.toString(),
29+
isMaintained: 'true',
30+
hasNativeCode: 'true',
31+
},
32+
ctx
3633
);
37-
const recentlyAddedResponse = await fetch(
38-
getApiUrl(urlWithQuery('/libraries', { order: 'added', limit: LIMIT.toString() }), ctx),
39-
NEXT_1H_CACHE_HEADER
34+
const recentlyAddedResponse = await ssrFetch(
35+
'/libraries',
36+
{ order: 'added', limit: LIMIT.toString() },
37+
ctx
4038
);
41-
const recentlyUpdatedResponse = await fetch(
42-
getApiUrl(urlWithQuery('/libraries', { order: 'updated', limit: LIMIT.toString() }), ctx),
43-
NEXT_1H_CACHE_HEADER
39+
const recentlyUpdatedResponse = await ssrFetch(
40+
'/libraries',
41+
{ order: 'updated', limit: LIMIT.toString() },
42+
ctx
4443
);
45-
const popularResponse = await fetch(
46-
getApiUrl(
47-
urlWithQuery('/libraries', {
48-
order: 'popularity',
49-
limit: LIMIT.toString(),
50-
isMaintained: 'true',
51-
isPopular: 'true',
52-
wasRecentlyUpdated: 'true',
53-
}),
54-
ctx
55-
),
56-
NEXT_1H_CACHE_HEADER
44+
const popularResponse = await ssrFetch(
45+
'/libraries',
46+
{
47+
order: 'popularity',
48+
limit: LIMIT.toString(),
49+
isMaintained: 'true',
50+
isPopular: 'true',
51+
wasRecentlyUpdated: 'true',
52+
},
53+
ctx
5754
);
5855

59-
const statisticResponse = await fetch(
60-
getApiUrl(urlWithQuery('/libraries/statistic'), ctx),
61-
NEXT_1H_CACHE_HEADER
62-
);
56+
const statisticResponse = await ssrFetch('/libraries/statistic', {}, ctx);
6357

6458
return {
6559
mostDownloaded: await mostDownloadedResponse.json(),

pages/package/[name]/[scopedName]/index.tsx

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,10 @@ import { type NextPageContext } from 'next';
33
import ErrorScene from '~/scenes/ErrorScene';
44
import PackageOverviewScene from '~/scenes/PackageOverviewScene';
55
import { type PackageOverviewPageProps } from '~/types/pages';
6-
import { EMPTY_PACKAGE_DATA, NEXT_10M_CACHE_HEADER, NEXT_1H_CACHE_HEADER } from '~/util/Constants';
7-
import getApiUrl from '~/util/getApiUrl';
6+
import { EMPTY_PACKAGE_DATA, NEXT_10M_CACHE_HEADER } from '~/util/Constants';
87
import { getPackagePageErrorProps } from '~/util/getPackagePageErrorProps';
98
import { parseQueryParams } from '~/util/parseQueryParams';
10-
import urlWithQuery from '~/util/urlWithQuery';
9+
import { ssrFetch } from '~/util/SSRFetch';
1110

1211
export default function ScopedOverviewPage({
1312
apiData,
@@ -34,24 +33,19 @@ export async function getServerSideProps(ctx: NextPageContext) {
3433

3534
try {
3635
const [apiResponse, npmResponse] = await Promise.all([
37-
fetch(
38-
getApiUrl(urlWithQuery(`/libraries`, { search: packageName }), ctx),
39-
NEXT_1H_CACHE_HEADER
40-
),
36+
ssrFetch(`/libraries`, { search: packageName }, ctx),
4137
fetch(`https://registry.npmjs.org/${packageName}/latest`, NEXT_10M_CACHE_HEADER),
4238
]);
4339

4440
if (apiResponse.status !== 200 || (npmResponse.status !== 200 && npmResponse.status !== 404)) {
4541
return getPackagePageErrorProps(packageName, apiResponse.status, npmResponse.status);
4642
}
4743

48-
const [apiData, registryData] = await Promise.all([apiResponse.json(), npmResponse.json()]);
49-
5044
return {
5145
props: {
5246
packageName,
53-
apiData,
54-
registryData,
47+
apiData: await apiResponse.json(),
48+
registryData: await npmResponse.json(),
5549
},
5650
};
5751
} catch {

pages/package/[name]/[scopedName]/score.tsx

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,10 @@ import { type NextPageContext } from 'next';
33
import ErrorScene from '~/scenes/ErrorScene';
44
import PackageScoreScene from '~/scenes/PackageScoreScene';
55
import { type PackageScorePageProps } from '~/types/pages';
6-
import { EMPTY_PACKAGE_DATA, NEXT_1H_CACHE_HEADER } from '~/util/Constants';
7-
import getApiUrl from '~/util/getApiUrl';
6+
import { EMPTY_PACKAGE_DATA } from '~/util/Constants';
87
import { getPackagePageErrorProps } from '~/util/getPackagePageErrorProps';
98
import { parseQueryParams } from '~/util/parseQueryParams';
10-
import urlWithQuery from '~/util/urlWithQuery';
9+
import { ssrFetch } from '~/util/SSRFetch';
1110

1211
export default function ScorePage({ apiData, packageName, errorMessage }: PackageScorePageProps) {
1312
if (!packageName || !apiData) {
@@ -26,21 +25,16 @@ export async function getServerSideProps(ctx: NextPageContext) {
2625
}
2726

2827
try {
29-
const apiResponse = await fetch(
30-
getApiUrl(urlWithQuery(`/libraries`, { search: packageName }), ctx),
31-
NEXT_1H_CACHE_HEADER
32-
);
28+
const apiResponse = await ssrFetch(`/libraries`, { search: packageName }, ctx);
3329

3430
if (apiResponse.status !== 200) {
3531
return getPackagePageErrorProps(packageName, apiResponse.status);
3632
}
3733

38-
const apiData = await apiResponse.json();
39-
4034
return {
4135
props: {
4236
packageName,
43-
apiData,
37+
apiData: await apiResponse.json(),
4438
},
4539
};
4640
} catch {

0 commit comments

Comments
 (0)