Skip to content

Commit

Permalink
Merge pull request #19 from kudos-ink/feat/infinite-scroll
Browse files Browse the repository at this point in the history
feat: add infinite scroll + small type refactor
  • Loading branch information
CJ13th authored Dec 30, 2023
2 parents 5fabbb5 + 8a8461f commit c00e389
Show file tree
Hide file tree
Showing 7 changed files with 175 additions and 32 deletions.
58 changes: 47 additions & 11 deletions actions/notion.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ const notion = new Client({
});

export async function baseQueryDatabase(
queryParams: QueryDatabaseParameters
queryParams: QueryDatabaseParameters,
): Promise<QueryDatabaseResponse> {
try {
const response = await notion.databases.query(queryParams);
Expand All @@ -33,33 +33,37 @@ export async function baseQueryDatabase(
}

export async function queryDatabase(
queryOverrides: Partial<KudosQueryParameters> = {}
queryOverrides: Partial<KudosQueryParameters> = {},
): Promise<QueryDatabaseResponse> {
const defaultQuery: KudosQueryParameters = {
database_id: databaseId,
filter_properties: defaultFilterProperties,
sorts: defaultSort,
page_size: 100,
start_cursor: undefined,
filter: undefined,
};
const query = {
...defaultQuery,
...queryOverrides,
};
} as QueryDatabaseParameters;
return await baseQueryDatabase(query);
}

export async function getGoodFirstIssues({
database_id = databaseId,
page_size = 100,
filter_properties = defaultFilterProperties,
sorts = defaultSort,
}): Promise<QueryDatabaseResponse> {
start_cursor,
}: KudosQueryParameters = {}): Promise<QueryDatabaseResponse> {
return await queryDatabase({
filter: {
or: [
{
property: "Issue Labels",
multi_select: {
contains: "good-first-issue",
contains: "C-good-first-issue",
},
},
{
Expand All @@ -68,19 +72,29 @@ export async function getGoodFirstIssues({
contains: "good first issue",
},
},
{
property: "Issue Labels",
multi_select: {
contains: "good first issue :baby:",
},
},
],
},
page_size,
filter_properties,
sorts,
start_cursor,
database_id,
});
}

export async function getBugIssues({
database_id = databaseId,
page_size = 100,
filter_properties = defaultFilterProperties,
sorts = defaultSort,
}): Promise<QueryDatabaseResponse> {
start_cursor,
}: KudosQueryParameters = {}): Promise<QueryDatabaseResponse> {
return await queryDatabase({
filter: {
property: "Issue Labels",
Expand All @@ -91,13 +105,17 @@ export async function getBugIssues({
page_size,
filter_properties,
sorts,
start_cursor,
database_id,
});
}
export async function getUnassignedIssues({
database_id = databaseId,
page_size = 100,
filter_properties = defaultFilterProperties,
sorts = defaultSort,
}): Promise<QueryDatabaseResponse> {
start_cursor,
}: KudosQueryParameters = {}): Promise<QueryDatabaseResponse> {
return await queryDatabase({
filter: {
property: "Assignee",
Expand All @@ -108,14 +126,18 @@ export async function getUnassignedIssues({
page_size,
filter_properties,
sorts,
start_cursor,
database_id,
});
}

export async function getIssuesOpenedWithin3Months({
database_id = databaseId,
page_size = 100,
filter_properties = defaultFilterProperties,
sorts = defaultSort,
}): Promise<QueryDatabaseResponse> {
start_cursor,
}: KudosQueryParameters = {}): Promise<QueryDatabaseResponse> {
return await queryDatabase({
filter: {
property: "Opened Date",
Expand All @@ -126,14 +148,18 @@ export async function getIssuesOpenedWithin3Months({
page_size,
filter_properties,
sorts,
start_cursor,
database_id,
});
}

export async function getIssuesOpenedWithin1Month({
database_id = databaseId,
page_size = 100,
filter_properties = defaultFilterProperties,
sorts = defaultSort,
}): Promise<QueryDatabaseResponse> {
start_cursor,
}: KudosQueryParameters = {}): Promise<QueryDatabaseResponse> {
return await queryDatabase({
filter: {
property: "Opened Date",
Expand All @@ -144,6 +170,8 @@ export async function getIssuesOpenedWithin1Month({
page_size,
filter_properties,
sorts,
start_cursor,
database_id,
});
}

Expand All @@ -152,12 +180,15 @@ export async function getIssuesOpenedWithin1Month({
export async function getIssuesByProject(
projectName: string,
{
database_id = databaseId,
page_size = 100,
filter_properties = defaultFilterProperties,
sorts = defaultSort,
}
start_cursor,
}: KudosQueryParameters = {},
): Promise<QueryDatabaseResponse> {
return await queryDatabase({
database_id,
filter: {
property: "Project Name",
rollup: {
Expand All @@ -171,16 +202,19 @@ export async function getIssuesByProject(
page_size,
filter_properties,
sorts,
start_cursor,
});
}

export async function getIssuesByRepo(
repoLink: ValidRepositoryLink,
{
database_id = databaseId,
page_size = 100,
filter_properties = defaultFilterProperties,
sorts = defaultSort,
}
start_cursor,
}: KudosQueryParameters = {},
): Promise<QueryDatabaseResponse> {
return await queryDatabase({
filter: {
Expand All @@ -192,6 +226,8 @@ export async function getIssuesByRepo(
page_size,
filter_properties,
sorts,
start_cursor,
database_id,
});
}

Expand Down
16 changes: 8 additions & 8 deletions app/test/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,16 @@ import {
PageObjectResponse,
} from "@notionhq/client/build/src/api-endpoints";
import { ValidNotionResponse, Properties } from "@/types";
import {
daysSince,
isValidNotionResponse,
getImagePath,
} from "@/utils/helpers";
import { daysSince, isValidNotionPage, getImagePath } from "@/utils/helpers";
import projectLogosJson from "@/public/images/imageMap.json";
import { LoadMore } from "@/components/loadMore";

export default async function Page() {
const data = await getGoodFirstIssues({ page_size: 10 });
return (
<div>
{data.results.map((row, index) => {
if (isValidNotionResponse(row)) {
if (isValidNotionPage(row)) {
return (
<Row
key={index}
Expand All @@ -33,16 +30,19 @@ export default async function Page() {
"d"
}
labels={row.properties["Issue Labels"].multi_select.map(
(label) => label.name
(label) => label.name,
)}
image={getImagePath(
row.properties["Issue Link"].url.split("/issues")[0],
projectLogosJson
projectLogosJson,
)}
/>
);
}
})}
{data.has_more && data.next_cursor && (
<LoadMore cursor={data.next_cursor} />
)}
</div>
);
}
90 changes: 90 additions & 0 deletions components/loadMore.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
"use client";

import { isValidNotionPage, daysSince, getImagePath } from "@/utils/helpers";
import { Spinner } from "@nextui-org/spinner";
import { useEffect, useState } from "react";
import { QueryDatabaseResponse } from "@notionhq/client/build/src/api-endpoints";
import projectLogosJson from "@/public/images/imageMap.json";
import { useInView } from "react-intersection-observer";
import Row from "./row";
import { getGoodFirstIssues } from "@/actions/notion";
import { LoadMoreState } from "@/types";

export function LoadMore({ cursor }: { cursor: string }) {
const { ref, inView } = useInView();
const [awaitingResponse, setAwaitingResponse] = useState(false);
const [queryData, setQueryData] = useState<LoadMoreState>({
data: [],
nextCursor: cursor,
});

useEffect(() => {
const fetchData = async () => {
if (inView && !awaitingResponse) {
setAwaitingResponse(true);
try {
const res = await getGoodFirstIssues({
page_size: 10,
start_cursor: queryData.nextCursor,
});
setQueryData((prevData) => ({
data: prevData.data.concat(res),
nextCursor: res.next_cursor || undefined,
}));
} catch (error) {
console.log("There was an error fetching the data", error);
}
}
};
fetchData();
}, [inView, queryData, awaitingResponse]);

useEffect(() => {
if (!inView && awaitingResponse) {
setAwaitingResponse(false);
}
}, [inView, awaitingResponse]);

return (
<>
{queryData.data &&
queryData.data.map((query) => {
return query.results.map((row, index) => {
if (isValidNotionPage(row)) {
return (
<Row
key={index}
projectName={
row.properties["Project Name"].rollup.array[0].rich_text[0]
.plain_text
}
repo={row.properties["Issue Link"].url.split("/issues")[0]}
issueTitle={row.properties["Issue Title"].title[0].plain_text}
issueLink={row.properties["Issue Link"].url}
openedDate={
daysSince(
row.properties["Opened Date"].date.start,
).toString() + "d"
}
labels={row.properties["Issue Labels"].multi_select.map(
(label) => label.name,
)}
image={getImagePath(
row.properties["Issue Link"].url.split("/issues")[0],
projectLogosJson,
)}
/>
);
}
});
})}
{queryData.nextCursor && (
<section className="flex justify-center">
<div ref={ref}>
<Spinner />
</div>
</section>
)}
</>
);
}
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,16 @@
"@nextui-org/card": "^2.0.24",
"@nextui-org/chip": "^2.0.25",
"@nextui-org/code": "2.0.24",
"@nextui-org/image": "^2.0.24",
"@nextui-org/dropdown": "^2.1.16",
"@nextui-org/image": "^2.0.24",
"@nextui-org/input": "2.1.16",
"@nextui-org/kbd": "2.0.25",
"@nextui-org/link": "2.0.26",
"@nextui-org/navbar": "2.0.27",
"@nextui-org/select": "^2.1.20",
"@nextui-org/slider": "^2.2.5",
"@nextui-org/snippet": "2.0.30",
"@nextui-org/spinner": "^2.0.24",
"@nextui-org/switch": "2.0.25",
"@nextui-org/system": "2.0.15",
"@nextui-org/table": "^2.0.28",
Expand All @@ -46,6 +47,7 @@
"postcss": "8.4.32",
"react": "18.2.0",
"react-dom": "18.2.0",
"react-intersection-observer": "^9.5.3",
"tailwind-variants": "^0.1.19",
"tailwindcss": "3.4.0",
"typescript": "5.3.3"
Expand Down
14 changes: 14 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit c00e389

Please sign in to comment.