Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion common/styleguide.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,10 @@ export function A({
const linkStyles = tw`font-sans text-black underline decoration-pewter dark:text-white dark:decoration-palette-gray5`;
const linkHoverStyles = tw`decoration-primary-dark`;

if ((target === '_self' && !href.startsWith('#')) || href.startsWith('/')) {
if (
(target === '_self' && !href.startsWith('#')) ||
(target !== '_blank' && href.startsWith('/'))
) {
const passedStyle = StyleSheet.flatten(style);
return (
<Link
Expand Down
27 changes: 24 additions & 3 deletions components/Home/HomeSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@ import dynamic from 'next/dynamic';
import { type ComponentType, createElement } from 'react';
import { View } from 'react-native';

import { A, H4, P } from '~/common/styleguide';
import { type IconProps } from '~/components/Icons';
import { A, H4, HoverEffect, P } from '~/common/styleguide';
import { type IconProps, RSS } from '~/components/Icons';
import LoadingContent from '~/components/Library/LoadingContent';
import Tooltip from '~/components/Tooltip';
import { type LibraryType, type Query } from '~/types';
import tw from '~/util/tailwind';
import urlWithQuery from '~/util/urlWithQuery';
Expand All @@ -19,9 +20,17 @@ type Props = {
Icon?: ComponentType<IconProps>;
count?: number;
queryParams?: Query;
rss?: string;
};

export default function HomeSection({ data, title, Icon, count = 8, queryParams = {} }: Props) {
export default function HomeSection({
data,
title,
Icon,
rss,
count = 8,
queryParams = {},
}: Props) {
const hashLink = title.replace(/\s/g, '').toLowerCase();

return (
Expand All @@ -35,6 +44,18 @@ export default function HomeSection({ data, title, Icon, count = 8, queryParams
hoverStyle={tw`text-palette-gray4 dark:text-palette-gray5`}>
{title}
</A>
{rss && (
<Tooltip
trigger={
<HoverEffect style={tw`ml-auto`}>
<A href={rss} target="_blank" style={tw`h-5.5`}>
<RSS style={tw`text-icon`} />
</A>
</HoverEffect>
}>
RSS feed
</Tooltip>
)}
</H4>
<View style={tw`flex-1 flex-row flex-wrap pt-3`}>{renderLibs(data, count)}</View>
<P style={tw`px-6 pb-6 pt-2 text-sm font-light text-secondary`}>
Expand Down
24 changes: 24 additions & 0 deletions components/Icons/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1247,3 +1247,27 @@ export function FundingGitHub({ width = 24, height = 24, style }: IconProps) {
</Svg>
);
}

export function RSS({ width = 24, height = 24, style }: IconProps) {
return (
<Svg width={width} height={height} viewBox="0 0 256 256" style={style}>
<path
d="M64,40A152,152,0,0,1,216,192"
fill="none"
stroke="currentColor"
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="24"
/>
<path
d="M64,112a80,80,0,0,1,80,80"
fill="none"
stroke="currentColor"
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="20"
/>
<circle cx="68" cy="188" r="12" fill="currentColor" />
</Svg>
);
}
47 changes: 47 additions & 0 deletions pages/rss/added.xml.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { type NextPageContext } from 'next';

import { type APIResponseType } from '~/types';
import { generateLibrariesRss } from '~/util/rss';
import { ssrFetch } from '~/util/SSRFetch';

const RSS_LIMIT = 20;

export async function getServerSideProps(ctx: NextPageContext) {
const { res } = ctx;

if (!res) {
return { notFound: true };
}

try {
const response = await ssrFetch(
'/libraries',
{ order: 'added', limit: RSS_LIMIT.toString() },
ctx
);

const { libraries } = (await response.json()) as APIResponseType;

res.statusCode = 200;
res.write(
generateLibrariesRss(
'Recently Added',
'The recently added packages to the directory',
libraries
)
);
} catch {
res.statusCode = 500;
res.write('Error: Cannot generate RSS feed');
}

res.setHeader('Content-Type', 'text/xml; charset=utf-8');
res.setHeader('Cache-Control', 'public, s-maxage=600, stale-while-revalidate=300');
res.end();

return { props: {} };
}

export default function RSSFeedAdded() {
return null;
}
47 changes: 47 additions & 0 deletions pages/rss/updated.xml.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { type NextPageContext } from 'next';

import { type APIResponseType } from '~/types';
import { generateLibrariesRss } from '~/util/rss';
import { ssrFetch } from '~/util/SSRFetch';

const RSS_LIMIT = 20;

export async function getServerSideProps(ctx: NextPageContext) {
const { res } = ctx;

if (!res) {
return { notFound: true };
}

try {
const response = await ssrFetch(
'/libraries',
{ order: 'updated', limit: RSS_LIMIT.toString() },
ctx
);

const { libraries } = (await response.json()) as APIResponseType;

res.statusCode = 200;
res.write(
generateLibrariesRss(
'Just Updated',
'The recently updated packages in the directory',
libraries
)
);
} catch {
res.statusCode = 500;
res.write('Error: Cannot generate RSS feed');
}

res.setHeader('Content-Type', 'text/xml; charset=utf-8');
res.setHeader('Cache-Control', 'public, s-maxage=600, stale-while-revalidate=300');
res.end();

return { props: {} };
}

export default function RSSFeedAdded() {
return null;
}
2 changes: 2 additions & 0 deletions scenes/HomeScene.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -167,12 +167,14 @@ export default function HomeScene({
title="Just updated"
Icon={Calendar}
queryParams={{ order: 'updated' }}
rss="/rss/updated.xml"
/>
<HomeSection
data={recentlyAdded.libraries}
title="Recently added"
Icon={Plus}
queryParams={{ order: 'added' }}
rss="/rss/added.xml"
/>
</ContentContainer>
</>
Expand Down
3 changes: 2 additions & 1 deletion tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@
"paths": {
"~/*": ["./*"]
},
"typeRoots": ["types", "node_modules/@types"]
"typeRoots": ["types", "node_modules/@types"],
"esModuleInterop": true
},
"exclude": ["node_modules"],
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"]
Expand Down
31 changes: 31 additions & 0 deletions util/rss.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { type LibraryType } from '~/types';

export function generateLibrariesRss(title: string, description: string, libraries: LibraryType[]) {
return `<?xml version="1.0" encoding="UTF-8" ?>
<rss version="2.0">
<channel>
<title>React Native Directory - ${title}</title>
<link>https://reactnative.directory/</link>
<description>${description}</description>
${libraries
.map(
(lib, i) => `
<item>
<title>${lib.npmPkg}</title>
<description><![CDATA[${lib.github.description}]]></description>
<link>https://reactnative.directory/package/${lib.npmPkg}</link>
${
lib.github.topics?.length
? lib.github.topics
.slice(0, 5)
.map(topic => `<category>${topic}</category>`)
.join('')
: ''
}
${title === 'Just Updated' ? `<pubDate>${new Date(lib.github.stats.updatedAt).toUTCString()}</pubDate>` : `<guid>${`${i}-${lib.npmPkg}`}</guid>`}
</item>`
)
.join('')}
</channel>
</rss>`;
}