Skip to content

Commit 62b9066

Browse files
authored
add recently added and update libraries RSS feeds (#2312)
1 parent d5a828d commit 62b9066

File tree

8 files changed

+181
-5
lines changed

8 files changed

+181
-5
lines changed

common/styleguide.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,10 @@ export function A({
108108
const linkStyles = tw`font-sans text-black underline decoration-pewter dark:text-white dark:decoration-palette-gray5`;
109109
const linkHoverStyles = tw`decoration-primary-dark`;
110110

111-
if ((target === '_self' && !href.startsWith('#')) || href.startsWith('/')) {
111+
if (
112+
(target === '_self' && !href.startsWith('#')) ||
113+
(target !== '_blank' && href.startsWith('/'))
114+
) {
112115
const passedStyle = StyleSheet.flatten(style);
113116
return (
114117
<Link

components/Home/HomeSection.tsx

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,10 @@ import dynamic from 'next/dynamic';
22
import { type ComponentType, createElement } from 'react';
33
import { View } from 'react-native';
44

5-
import { A, H4, P } from '~/common/styleguide';
6-
import { type IconProps } from '~/components/Icons';
5+
import { A, H4, HoverEffect, P } from '~/common/styleguide';
6+
import { type IconProps, RSS } from '~/components/Icons';
77
import LoadingContent from '~/components/Library/LoadingContent';
8+
import Tooltip from '~/components/Tooltip';
89
import { type LibraryType, type Query } from '~/types';
910
import tw from '~/util/tailwind';
1011
import urlWithQuery from '~/util/urlWithQuery';
@@ -19,9 +20,17 @@ type Props = {
1920
Icon?: ComponentType<IconProps>;
2021
count?: number;
2122
queryParams?: Query;
23+
rss?: string;
2224
};
2325

24-
export default function HomeSection({ data, title, Icon, count = 8, queryParams = {} }: Props) {
26+
export default function HomeSection({
27+
data,
28+
title,
29+
Icon,
30+
rss,
31+
count = 8,
32+
queryParams = {},
33+
}: Props) {
2534
const hashLink = title.replace(/\s/g, '').toLowerCase();
2635

2736
return (
@@ -35,6 +44,18 @@ export default function HomeSection({ data, title, Icon, count = 8, queryParams
3544
hoverStyle={tw`text-palette-gray4 dark:text-palette-gray5`}>
3645
{title}
3746
</A>
47+
{rss && (
48+
<Tooltip
49+
trigger={
50+
<HoverEffect style={tw`ml-auto`}>
51+
<A href={rss} target="_blank" style={tw`h-5.5`}>
52+
<RSS style={tw`text-icon`} />
53+
</A>
54+
</HoverEffect>
55+
}>
56+
RSS feed
57+
</Tooltip>
58+
)}
3859
</H4>
3960
<View style={tw`flex-1 flex-row flex-wrap pt-3`}>{renderLibs(data, count)}</View>
4061
<P style={tw`px-6 pb-6 pt-2 text-sm font-light text-secondary`}>

components/Icons/index.tsx

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1247,3 +1247,27 @@ export function FundingGitHub({ width = 24, height = 24, style }: IconProps) {
12471247
</Svg>
12481248
);
12491249
}
1250+
1251+
export function RSS({ width = 24, height = 24, style }: IconProps) {
1252+
return (
1253+
<Svg width={width} height={height} viewBox="0 0 256 256" style={style}>
1254+
<path
1255+
d="M64,40A152,152,0,0,1,216,192"
1256+
fill="none"
1257+
stroke="currentColor"
1258+
strokeLinecap="round"
1259+
strokeLinejoin="round"
1260+
strokeWidth="24"
1261+
/>
1262+
<path
1263+
d="M64,112a80,80,0,0,1,80,80"
1264+
fill="none"
1265+
stroke="currentColor"
1266+
strokeLinecap="round"
1267+
strokeLinejoin="round"
1268+
strokeWidth="20"
1269+
/>
1270+
<circle cx="68" cy="188" r="12" fill="currentColor" />
1271+
</Svg>
1272+
);
1273+
}

pages/rss/added.xml.ts

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import { type NextPageContext } from 'next';
2+
3+
import { type APIResponseType } from '~/types';
4+
import { generateLibrariesRss } from '~/util/rss';
5+
import { ssrFetch } from '~/util/SSRFetch';
6+
7+
const RSS_LIMIT = 20;
8+
9+
export async function getServerSideProps(ctx: NextPageContext) {
10+
const { res } = ctx;
11+
12+
if (!res) {
13+
return { notFound: true };
14+
}
15+
16+
try {
17+
const response = await ssrFetch(
18+
'/libraries',
19+
{ order: 'added', limit: RSS_LIMIT.toString() },
20+
ctx
21+
);
22+
23+
const { libraries } = (await response.json()) as APIResponseType;
24+
25+
res.statusCode = 200;
26+
res.write(
27+
generateLibrariesRss(
28+
'Recently Added',
29+
'The recently added packages to the directory',
30+
libraries
31+
)
32+
);
33+
} catch {
34+
res.statusCode = 500;
35+
res.write('Error: Cannot generate RSS feed');
36+
}
37+
38+
res.setHeader('Content-Type', 'text/xml; charset=utf-8');
39+
res.setHeader('Cache-Control', 'public, s-maxage=600, stale-while-revalidate=300');
40+
res.end();
41+
42+
return { props: {} };
43+
}
44+
45+
export default function RSSFeedAdded() {
46+
return null;
47+
}

pages/rss/updated.xml.ts

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import { type NextPageContext } from 'next';
2+
3+
import { type APIResponseType } from '~/types';
4+
import { generateLibrariesRss } from '~/util/rss';
5+
import { ssrFetch } from '~/util/SSRFetch';
6+
7+
const RSS_LIMIT = 20;
8+
9+
export async function getServerSideProps(ctx: NextPageContext) {
10+
const { res } = ctx;
11+
12+
if (!res) {
13+
return { notFound: true };
14+
}
15+
16+
try {
17+
const response = await ssrFetch(
18+
'/libraries',
19+
{ order: 'updated', limit: RSS_LIMIT.toString() },
20+
ctx
21+
);
22+
23+
const { libraries } = (await response.json()) as APIResponseType;
24+
25+
res.statusCode = 200;
26+
res.write(
27+
generateLibrariesRss(
28+
'Just Updated',
29+
'The recently updated packages in the directory',
30+
libraries
31+
)
32+
);
33+
} catch {
34+
res.statusCode = 500;
35+
res.write('Error: Cannot generate RSS feed');
36+
}
37+
38+
res.setHeader('Content-Type', 'text/xml; charset=utf-8');
39+
res.setHeader('Cache-Control', 'public, s-maxage=600, stale-while-revalidate=300');
40+
res.end();
41+
42+
return { props: {} };
43+
}
44+
45+
export default function RSSFeedAdded() {
46+
return null;
47+
}

scenes/HomeScene.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,12 +167,14 @@ export default function HomeScene({
167167
title="Just updated"
168168
Icon={Calendar}
169169
queryParams={{ order: 'updated' }}
170+
rss="/rss/updated.xml"
170171
/>
171172
<HomeSection
172173
data={recentlyAdded.libraries}
173174
title="Recently added"
174175
Icon={Plus}
175176
queryParams={{ order: 'added' }}
177+
rss="/rss/added.xml"
176178
/>
177179
</ContentContainer>
178180
</>

tsconfig.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@
1919
"paths": {
2020
"~/*": ["./*"]
2121
},
22-
"typeRoots": ["types", "node_modules/@types"]
22+
"typeRoots": ["types", "node_modules/@types"],
23+
"esModuleInterop": true
2324
},
2425
"exclude": ["node_modules"],
2526
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"]

util/rss.ts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import { type LibraryType } from '~/types';
2+
3+
export function generateLibrariesRss(title: string, description: string, libraries: LibraryType[]) {
4+
return `<?xml version="1.0" encoding="UTF-8" ?>
5+
<rss version="2.0">
6+
<channel>
7+
<title>React Native Directory - ${title}</title>
8+
<link>https://reactnative.directory/</link>
9+
<description>${description}</description>
10+
${libraries
11+
.map(
12+
(lib, i) => `
13+
<item>
14+
<title>${lib.npmPkg}</title>
15+
<description><![CDATA[${lib.github.description}]]></description>
16+
<link>https://reactnative.directory/package/${lib.npmPkg}</link>
17+
${
18+
lib.github.topics?.length
19+
? lib.github.topics
20+
.slice(0, 5)
21+
.map(topic => `<category>${topic}</category>`)
22+
.join('')
23+
: ''
24+
}
25+
${title === 'Just Updated' ? `<pubDate>${new Date(lib.github.stats.updatedAt).toUTCString()}</pubDate>` : `<guid>${`${i}-${lib.npmPkg}`}</guid>`}
26+
</item>`
27+
)
28+
.join('')}
29+
</channel>
30+
</rss>`;
31+
}

0 commit comments

Comments
 (0)