added cursor based pagination for links

This commit is contained in:
Daniel
2023-06-15 02:04:54 +03:30
parent 6323badbaf
commit 1b6d902c75
29 changed files with 507 additions and 182 deletions
+4 -3
View File
@@ -1,20 +1,21 @@
import getCollection from "@/lib/api/controllers/public/getCollection";
import { PublicLinkRequestQuery } from "@/types/global";
import type { NextApiRequest, NextApiResponse } from "next";
export default async function collections(
req: NextApiRequest,
res: NextApiResponse
) {
const collectionId = Number(req.query.collectionId);
const query: PublicLinkRequestQuery = req.query;
if (!collectionId) {
if (!query) {
return res
.status(401)
.json({ response: "Please choose a valid collection." });
}
if (req.method === "GET") {
const collection = await getCollection(collectionId);
const collection = await getCollection(query);
return res
.status(collection.status)
.json({ response: collection.response });
+1 -1
View File
@@ -14,7 +14,7 @@ export default async function links(req: NextApiRequest, res: NextApiResponse) {
}
if (req.method === "GET") {
const links = await getLinks(session.user.id);
const links = await getLinks(session.user.id, req.query);
return res.status(links.status).json({ response: links.response });
} else if (req.method === "POST") {
const newlink = await postLink(req.body, session.user.id);
+4 -6
View File
@@ -16,7 +16,7 @@ import { useSession } from "next-auth/react";
import ProfilePhoto from "@/components/ProfilePhoto";
import SortDropdown from "@/components/SortDropdown";
import useModalStore from "@/store/modals";
import useSort from "@/hooks/useSort";
import useLinks from "@/hooks/useLinks";
export default function Index() {
const { setModal } = useModalStore();
@@ -30,14 +30,12 @@ export default function Index() {
const [expandDropdown, setExpandDropdown] = useState(false);
const [sortDropdown, setSortDropdown] = useState(false);
const [sortBy, setSortBy] = useState<Sort>(Sort.NameAZ);
const [sortBy, setSortBy] = useState<Sort>(Sort.DateNewestFirst);
const [activeCollection, setActiveCollection] =
useState<CollectionIncludingMembers>();
const [sortedLinks, setSortedLinks] = useState(links);
useSort({ sortBy, setData: setSortedLinks, data: links });
useLinks({ collectionId: Number(router.query.id), sort: sortBy });
useEffect(() => {
setActiveCollection(
@@ -223,7 +221,7 @@ export default function Index() {
</div>
</div>
<div className="grid 2xl:grid-cols-3 xl:grid-cols-2 gap-5">
{sortedLinks.map((e, i) => {
{links.map((e, i) => {
return <LinkCard key={i} link={e} count={i} />;
})}
</div>
+1 -1
View File
@@ -20,7 +20,7 @@ export default function Collections() {
const { collections } = useCollectionStore();
const [expandDropdown, setExpandDropdown] = useState(false);
const [sortDropdown, setSortDropdown] = useState(false);
const [sortBy, setSortBy] = useState<Sort>(Sort.NameAZ);
const [sortBy, setSortBy] = useState<Sort>(Sort.DateNewestFirst);
const [sortedCollections, setSortedCollections] = useState(collections);
const session = useSession();
+6 -5
View File
@@ -12,6 +12,7 @@ import Link from "next/link";
import CollectionCard from "@/components/CollectionCard";
import { Disclosure, Transition } from "@headlessui/react";
import { useEffect, useState } from "react";
import useLinks from "@/hooks/useLinks";
export default function Dashboard() {
const { collections } = useCollectionStore();
@@ -34,6 +35,8 @@ export default function Dashboard() {
return storedValue ? storedValue === "true" : true;
});
useLinks({ pinnedOnly: true });
useEffect(() => {
localStorage.setItem(
"tagPinDisclosure",
@@ -131,11 +134,9 @@ export default function Dashboard() {
leaveTo="transform opacity-0 -translate-y-3"
>
<Disclosure.Panel className="flex flex-col gap-5 w-full">
{links
.filter((e) => e.pinnedBy && e.pinnedBy[0])
.map((e, i) => (
<LinkCard key={i} link={e} count={i} />
))}
{links.map((e, i) => (
<LinkCard key={i} link={e} count={i} />
))}
</Disclosure.Panel>
</Transition>
</div>
+4 -5
View File
@@ -1,6 +1,6 @@
import LinkCard from "@/components/LinkCard";
import SortDropdown from "@/components/SortDropdown";
import useSort from "@/hooks/useSort";
import useLinks from "@/hooks/useLinks";
import MainLayout from "@/layouts/MainLayout";
import useLinkStore from "@/store/links";
import { Sort } from "@/types/global";
@@ -12,10 +12,9 @@ export default function Links() {
const { links } = useLinkStore();
const [sortDropdown, setSortDropdown] = useState(false);
const [sortBy, setSortBy] = useState<Sort>(Sort.NameAZ);
const [sortedLinks, setSortedLinks] = useState(links);
const [sortBy, setSortBy] = useState<Sort>(Sort.DateNewestFirst);
useSort({ sortBy, setData: setSortedLinks, data: links });
useLinks({ sort: sortBy });
return (
<MainLayout>
@@ -54,7 +53,7 @@ export default function Links() {
</div>
</div>
<div className="grid 2xl:grid-cols-3 xl:grid-cols-2 gap-5">
{sortedLinks.map((e, i) => {
{links.map((e, i) => {
return <LinkCard key={i} link={e} count={i} />;
})}
</div>
+14 -5
View File
@@ -1,4 +1,5 @@
import LinkCard from "@/components/PublicPage/LinkCard";
import useDetectPageBottom from "@/hooks/useDetectPageBottom";
import getPublicCollectionData from "@/lib/client/getPublicCollectionData";
import { PublicCollectionIncludingLinks } from "@/types/global";
import { useRouter } from "next/router";
@@ -6,6 +7,7 @@ import React, { useEffect, useState } from "react";
export default function PublicCollections() {
const router = useRouter();
const hasReachedBottom = useDetectPageBottom();
const [data, setData] = useState<PublicCollectionIncludingLinks>();
@@ -13,7 +15,8 @@ export default function PublicCollections() {
if (router.query.id) {
getPublicCollectionData(
router.query.id as string,
(e: PublicCollectionIncludingLinks) => setData(e)
data as PublicCollectionIncludingLinks,
setData
);
}
@@ -27,6 +30,16 @@ export default function PublicCollections() {
// );
}, []);
useEffect(() => {
if (hasReachedBottom && router.query.id) {
getPublicCollectionData(
router.query.id as string,
data as PublicCollectionIncludingLinks,
setData
);
}
}, [hasReachedBottom]);
return data ? (
<div className="max-w-4xl mx-auto p-5 bg">
<div
@@ -36,10 +49,6 @@ export default function PublicCollections() {
{data.name}
</p>
{data.ownerName && (
<p className="text-sky-500">{"By " + data.ownerName}</p>
)}
<hr className="mt-5 max-w-[30rem] mx-auto border-1 border-slate-400" />
<p className="mt-2 text-gray-500">{data.description}</p>
+12 -43
View File
@@ -1,33 +1,21 @@
import FilterSearchDropdown from "@/components/FilterSearchDropdown";
import LinkCard from "@/components/LinkCard";
import SortDropdown from "@/components/SortDropdown";
import useSort from "@/hooks/useSort";
import useLinks from "@/hooks/useLinks";
import MainLayout from "@/layouts/MainLayout";
import useLinkStore from "@/store/links";
import { Sort } from "@/types/global";
import { LinkSearchFilter, Sort } from "@/types/global";
import { faFilter, faSearch, faSort } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { useRouter } from "next/router";
import { useEffect, useState } from "react";
type SearchFilter = {
name: boolean;
url: boolean;
description: boolean;
collection: boolean;
tags: boolean;
};
import { useState } from "react";
export default function Links() {
const { links } = useLinkStore();
const router = useRouter();
const routeQuery = decodeURIComponent(
router.query.query as string
).toLowerCase();
const [searchFilter, setSearchFilter] = useState<SearchFilter>({
const [searchFilter, setSearchFilter] = useState<LinkSearchFilter>({
name: true,
url: true,
description: true,
@@ -37,32 +25,13 @@ export default function Links() {
const [filterDropdown, setFilterDropdown] = useState(false);
const [sortDropdown, setSortDropdown] = useState(false);
const [sortBy, setSortBy] = useState<Sort>(Sort.NameAZ);
const [sortBy, setSortBy] = useState<Sort>(Sort.DateNewestFirst);
const [filteredLinks, setFilteredLinks] = useState(links);
const [sortedLinks, setSortedLinks] = useState(filteredLinks);
useSort({ sortBy, setData: setSortedLinks, data: links });
useEffect(() => {
setFilteredLinks([
...sortedLinks.filter((link) => {
if (
(searchFilter.name && link.name.toLowerCase().includes(routeQuery)) ||
(searchFilter.url && link.url.toLowerCase().includes(routeQuery)) ||
(searchFilter.description &&
link.description.toLowerCase().includes(routeQuery)) ||
(searchFilter.collection &&
link.collection.name.toLowerCase().includes(routeQuery)) ||
(searchFilter.tags &&
link.tags.some((tag) =>
tag.name.toLowerCase().includes(routeQuery)
))
)
return true;
}),
]);
}, [searchFilter, sortedLinks, router]);
useLinks({
searchFilter: searchFilter,
searchQuery: router.query.query as string,
sort: sortBy,
});
return (
<MainLayout>
@@ -126,8 +95,8 @@ export default function Links() {
</div>
</div>
</div>
{filteredLinks[0] ? (
filteredLinks.map((e, i) => {
{links[0] ? (
links.map((e, i) => {
return <LinkCard key={i} link={e} count={i} />;
})
) : (
+4 -6
View File
@@ -9,7 +9,7 @@ import { Tag } from "@prisma/client";
import useTagStore from "@/store/tags";
import SortDropdown from "@/components/SortDropdown";
import { Sort } from "@/types/global";
import useSort from "@/hooks/useSort";
import useLinks from "@/hooks/useLinks";
export default function Index() {
const router = useRouter();
@@ -18,13 +18,11 @@ export default function Index() {
const { tags } = useTagStore();
const [sortDropdown, setSortDropdown] = useState(false);
const [sortBy, setSortBy] = useState<Sort>(Sort.NameAZ);
const [sortBy, setSortBy] = useState<Sort>(Sort.DateNewestFirst);
const [activeTag, setActiveTag] = useState<Tag>();
const [sortedLinks, setSortedLinks] = useState(links);
useSort({ sortBy, setData: setSortedLinks, data: links });
useLinks({ tagId: Number(router.query.id), sort: sortBy });
useEffect(() => {
setActiveTag(tags.find((e) => e.id === Number(router.query.id)));
@@ -69,7 +67,7 @@ export default function Index() {
</div>
</div>
<div className="grid 2xl:grid-cols-3 xl:grid-cols-2 gap-5">
{sortedLinks.map((e, i) => {
{links.map((e, i) => {
return <LinkCard key={i} link={e} count={i} />;
})}
</div>