Skip to content

Commit

Permalink
feat!: add category tree
Browse files Browse the repository at this point in the history
  • Loading branch information
theodorusclarence committed Feb 6, 2022
1 parent 984e802 commit 7b72869
Show file tree
Hide file tree
Showing 7 changed files with 149 additions and 7 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ yarn-error.log*

# local env files
.env.local
.env.clarence.local
.env.development.local
.env.test.local
.env.production.local
Expand Down
14 changes: 9 additions & 5 deletions src/components/Favicon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,11 @@ type FaviconProps = {
} & Omit<NextImageProps, 'src' | 'alt' | 'width' | 'height'>;

export default function Favicon({ className, fullUrl, ...rest }: FaviconProps) {
const FAVICON_URL = 'https://icons.duckduckgo.com/ip3/';
const { hostname } = new URL(fullUrl);

const src = FAVICON_URL + hostname + '.ico';
const { url, hostname } = getFaviconUrl(fullUrl);

return (
<NextImage
src={src}
src={url}
alt={`${hostname} favicon`}
width='20'
height='20'
Expand All @@ -25,3 +22,10 @@ export default function Favicon({ className, fullUrl, ...rest }: FaviconProps) {
/>
);
}

export const getFaviconUrl = (url: string) => {
const FAVICON_URL = 'https://icons.duckduckgo.com/ip3/';
const { hostname } = new URL(url);

return { url: FAVICON_URL + hostname + '.ico', hostname };
};
2 changes: 1 addition & 1 deletion src/components/links/TreeLink.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import clsxm from '@/lib/clsxm';
import { Tree } from '@/lib/notion';

type TreeLinkProps = {
link: Tree;
link: Pick<Tree, 'display' | 'link' | 'icon'>;
} & React.ComponentPropsWithoutRef<'div'>;

export default function TreeLink({
Expand Down
45 changes: 45 additions & 0 deletions src/lib/notion.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,51 @@ export const getUrlBySlug = async (slug: string) => {
return url;
};

export const getAllLinkCategories = async () => {
if (!NOTION_LINK_DATABASE_ID) {
throw new Error('NEXT_PUBLIC_NOTION_LINK_DATABASE_ID env is not defined');
}

const response = await notion.databases.retrieve({
database_id: NOTION_LINK_DATABASE_ID,
});

const results = response as unknown as LinkResult;

const categories = results.properties.categories.multi_select.options.map(
(option) => option.name
);

return categories;
};

/**
* Get URLs Category
*/
export const getCategoryUrls = async (category: string) => {
if (!NOTION_LINK_DATABASE_ID) {
throw new Error('NEXT_PUBLIC_NOTION_LINK_DATABASE_ID env is not defined');
}

const response = await notion.databases.query({
database_id: NOTION_LINK_DATABASE_ID,
filter: {
property: 'categories',
multi_select: { contains: category },
},
});

const results = response.results as unknown as LinkResult[];

const url: Omit<Url, 'count'>[] = results.map((result) => ({
pageId: result?.id,
slug: result?.properties.slug.title[0]?.plain_text,
link: result?.properties.link.rich_text[0]?.plain_text,
}));

return url;
};

/**
* Increment count column by 1
*/
Expand Down
11 changes: 10 additions & 1 deletion src/pages/_middleware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,16 @@ import { getUrlBySlug } from '@/lib/notion';

export default async function middleware(req: NextRequest) {
const path = req.nextUrl.pathname.split('/')[1];
const whitelist = ['favicons', 'fonts', 'images', 'svg', '', 'login', 'new'];
const whitelist = [
'favicons',
'fonts',
'images',
'svg',
'',
'login',
'new',
'c',
];
if (whitelist.includes(path)) {
return;
}
Expand Down
75 changes: 75 additions & 0 deletions src/pages/c/[category].tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import {
GetStaticPaths,
GetStaticPropsContext,
InferGetStaticPropsType,
} from 'next';
import * as React from 'react';

import { getAllLinkCategories, getCategoryUrls } from '@/lib/notion';

import Accent from '@/components/Accent';
import { getFaviconUrl } from '@/components/Favicon';
import Layout from '@/components/layout/Layout';
import PrimaryLink from '@/components/links/PrimaryLink';
import TreeLink from '@/components/links/TreeLink';
import Seo from '@/components/Seo';

export default function CPage({
links,
category,
}: InferGetStaticPropsType<typeof getStaticProps>) {
return (
<Layout>
<Seo templateTitle={category} />

<main>
<section className=''>
<div className='layout flex min-h-screen flex-col items-center justify-center py-20'>
<h1 className='text-center text-5xl md:text-7xl'>
<Accent>{category}</Accent>
</h1>
<div className='mx-auto mt-8 grid w-full max-w-sm gap-4 text-center'>
{links.map((link) => (
<TreeLink
key={link.pageId}
link={{
display: link.slug,
link: link.link!,
icon: {
type: 'external',
external: { url: getFaviconUrl(link.link!).url },
},
}}
/>
))}
</div>
{/* Thank you for not removing this as an attribution 🙏 */}
<p className='mt-10 dark:text-gray-300'>
Built using{' '}
<PrimaryLink href='https://github.com/theodorusclarence/notiolink'>
<Accent>Notiolink</Accent>
</PrimaryLink>
</p>
</div>
</section>
</main>
</Layout>
);
}

export const getStaticPaths: GetStaticPaths = async () => {
const categories = await getAllLinkCategories();

return {
paths: categories.map((category) => ({ params: { category } })),
fallback: false,
};
};

export const getStaticProps = async (context: GetStaticPropsContext) => {
const category = context.params?.category as string;
return {
props: { links: await getCategoryUrls(category), category },
revalidate: 5,
};
};
8 changes: 8 additions & 0 deletions src/types/notion.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ interface LinkProperties {
count: TextColumn;
link: TextColumn;
slug: TitleColumn;
categories: MultiSelectColumn;
}
//#endregion //*======== Links ===========

Expand Down Expand Up @@ -60,6 +61,13 @@ interface NumberColumn {
number: number;
}

interface MultiSelectColumn {
id: string;
multi_select: {
options: Array<{ name: string }>;
};
}

interface RichText {
type: string;
plain_text: string;
Expand Down

1 comment on commit 7b72869

@vercel
Copy link

@vercel vercel bot commented on 7b72869 Feb 6, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.