Reworked access to tags as public viewer
This commit is contained in:
parent
e8d0cce58a
commit
abb73f80bd
|
@ -18,7 +18,7 @@ export default function useLinks(
|
||||||
searchByTextContent,
|
searchByTextContent,
|
||||||
}: LinkRequestQuery = { sort: 0 }
|
}: LinkRequestQuery = { sort: 0 }
|
||||||
) {
|
) {
|
||||||
const { links, setLinks, resetLinks, selectedLinks, setSelectedLinks, setAllLinksOfCollection } =
|
const { links, setLinks, resetLinks, selectedLinks, setSelectedLinks } =
|
||||||
useLinkStore();
|
useLinkStore();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
|
@ -80,14 +80,6 @@ export default function useLinks(
|
||||||
if (response.ok) setLinks(data.response, isInitialCall);
|
if (response.ok) setLinks(data.response, isInitialCall);
|
||||||
};
|
};
|
||||||
|
|
||||||
const getAllLinks = async () => {
|
|
||||||
const response = await fetch(getPath());
|
|
||||||
|
|
||||||
const data = await response.json();
|
|
||||||
|
|
||||||
if (response.ok) setAllLinksOfCollection(data.response);
|
|
||||||
};
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// Save the selected links before resetting the links
|
// Save the selected links before resetting the links
|
||||||
// and then restore the selected links after resetting the links
|
// and then restore the selected links after resetting the links
|
||||||
|
@ -95,7 +87,6 @@ export default function useLinks(
|
||||||
resetLinks();
|
resetLinks();
|
||||||
|
|
||||||
setSelectedLinks(previouslySelected);
|
setSelectedLinks(previouslySelected);
|
||||||
getAllLinks();
|
|
||||||
getLinks(true);
|
getLinks(true);
|
||||||
}, [
|
}, [
|
||||||
router,
|
router,
|
||||||
|
|
|
@ -2,7 +2,7 @@ import { prisma } from "@/lib/api/db";
|
||||||
import { LinkRequestQuery, Sort } from "@/types/global";
|
import { LinkRequestQuery, Sort } from "@/types/global";
|
||||||
|
|
||||||
export default async function getLink(
|
export default async function getLink(
|
||||||
query: Omit<LinkRequestQuery, "tagId" | "pinnedOnly">
|
query: Omit<LinkRequestQuery, "tagId" | "pinnedOnly">, takeAll = false
|
||||||
) {
|
) {
|
||||||
const POSTGRES_IS_ENABLED = process.env.DATABASE_URL.startsWith("postgresql");
|
const POSTGRES_IS_ENABLED = process.env.DATABASE_URL.startsWith("postgresql");
|
||||||
|
|
||||||
|
@ -68,7 +68,7 @@ export default async function getLink(
|
||||||
}
|
}
|
||||||
|
|
||||||
const links = await prisma.link.findMany({
|
const links = await prisma.link.findMany({
|
||||||
take: Number(process.env.PAGINATION_TAKE_COUNT) || 20,
|
take: !takeAll ? Number(process.env.PAGINATION_TAKE_COUNT) || 20 : undefined,
|
||||||
skip: query.cursor ? 1 : undefined,
|
skip: query.cursor ? 1 : undefined,
|
||||||
cursor: query.cursor ? { id: query.cursor } : undefined,
|
cursor: query.cursor ? { id: query.cursor } : undefined,
|
||||||
where: {
|
where: {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { prisma } from "@/lib/api/db";
|
import { prisma } from "@/lib/api/db";
|
||||||
|
|
||||||
export default async function getTags(userId: number) {
|
export default async function getTags(userId?: number) {
|
||||||
// Remove empty tags
|
// Remove empty tags
|
||||||
await prisma.tag.deleteMany({
|
await prisma.tag.deleteMany({
|
||||||
where: {
|
where: {
|
||||||
|
|
|
@ -0,0 +1,35 @@
|
||||||
|
import getPublicLinksUnderCollection from "@/lib/api/controllers/public/links/getPublicLinksUnderCollection";
|
||||||
|
import getTags from "@/lib/api/controllers/tags/getTags";
|
||||||
|
import { LinkRequestQuery } from "@/types/global";
|
||||||
|
import type { NextApiRequest, NextApiResponse } from "next";
|
||||||
|
|
||||||
|
export default async function collections(
|
||||||
|
req: NextApiRequest,
|
||||||
|
res: NextApiResponse
|
||||||
|
) {
|
||||||
|
if (req.method === "GET") {
|
||||||
|
// Convert the type of the request query to "LinkRequestQuery"
|
||||||
|
const convertedData: Omit<LinkRequestQuery, "tagId"> = {
|
||||||
|
sort: Number(req.query.sort as string),
|
||||||
|
collectionId: req.query.collectionId
|
||||||
|
? Number(req.query.collectionId as string)
|
||||||
|
: undefined,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!convertedData.collectionId) {
|
||||||
|
return res
|
||||||
|
.status(400)
|
||||||
|
.json({ response: "Please choose a valid collection." });
|
||||||
|
}
|
||||||
|
|
||||||
|
const links = await getPublicLinksUnderCollection(convertedData, true);
|
||||||
|
const tags = await getTags();
|
||||||
|
const tagsInLinks = links.response.map(l => l.tags).flat().filter((value, index, self) =>
|
||||||
|
index === self.findIndex((t) => (
|
||||||
|
t.name === value.name
|
||||||
|
))).map(t => t.id);
|
||||||
|
const tagsWithCount = tags.response.filter(tag => tagsInLinks.includes(tag.id));
|
||||||
|
|
||||||
|
return res.status(links.status).json({ response: tagsWithCount });
|
||||||
|
}
|
||||||
|
}
|
|
@ -44,11 +44,7 @@ const cardVariants: Variants = {
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function PublicCollections() {
|
export default function PublicCollections() {
|
||||||
const { links, allLinksOfCollection } = useLinkStore();
|
const { links } = useLinkStore();
|
||||||
const tagsInCollection = allLinksOfCollection.map(l => l.tags).flat().filter((value, index, self) =>
|
|
||||||
index === self.findIndex((t) => (
|
|
||||||
t.name === value.name
|
|
||||||
)));
|
|
||||||
const { settings } = useLocalSettingsStore();
|
const { settings } = useLocalSettingsStore();
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
@ -62,7 +58,7 @@ export default function PublicCollections() {
|
||||||
archiveAsPDF: undefined as unknown as boolean,
|
archiveAsPDF: undefined as unknown as boolean,
|
||||||
});
|
});
|
||||||
|
|
||||||
const { tags } = useTagStore();
|
const { tags, setTags } = useTagStore();
|
||||||
const handleTagSelection = (tag: TagIncludingLinkCount | undefined) => {
|
const handleTagSelection = (tag: TagIncludingLinkCount | undefined) => {
|
||||||
if (tag) {
|
if (tag) {
|
||||||
Object.keys(searchFilter).forEach((v) => searchFilter[(v as keyof {name: boolean, url: boolean, description: boolean, tags: boolean, textContent: boolean})] = false)
|
Object.keys(searchFilter).forEach((v) => searchFilter[(v as keyof {name: boolean, url: boolean, description: boolean, tags: boolean, textContent: boolean})] = false)
|
||||||
|
@ -107,6 +103,7 @@ export default function PublicCollections() {
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (router.query.id) {
|
if (router.query.id) {
|
||||||
getPublicCollectionData(Number(router.query.id), setCollection);
|
getPublicCollectionData(Number(router.query.id), setCollection);
|
||||||
|
setTags(Number(router.query.id))
|
||||||
}
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
@ -252,7 +249,7 @@ export default function PublicCollections() {
|
||||||
<ViewDropdown viewMode={viewMode} setViewMode={setViewMode} />
|
<ViewDropdown viewMode={viewMode} setViewMode={setViewMode} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{collection.tagsArePublic && tagsInCollection[0] && (
|
{collection.tagsArePublic && tags[0] && (
|
||||||
<Disclosure defaultOpen={tagDisclosure}>
|
<Disclosure defaultOpen={tagDisclosure}>
|
||||||
<Disclosure.Button
|
<Disclosure.Button
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
|
@ -289,7 +286,7 @@ export default function PublicCollections() {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</button>
|
</button>
|
||||||
{tagsInCollection
|
{tags
|
||||||
.sort((a, b) => a.name.localeCompare(b.name))
|
.sort((a, b) => a.name.localeCompare(b.name))
|
||||||
.map((e, i) => {
|
.map((e, i) => {
|
||||||
const active = router.query.q === e.name;
|
const active = router.query.q === e.name;
|
||||||
|
@ -306,7 +303,7 @@ export default function PublicCollections() {
|
||||||
<i className="bi-hash text-2xl text-primary drop-shadow"></i>
|
<i className="bi-hash text-2xl text-primary drop-shadow"></i>
|
||||||
<p className="truncate pr-7">{e.name}</p>
|
<p className="truncate pr-7">{e.name}</p>
|
||||||
<div className="drop-shadow text-neutral text-xs">
|
<div className="drop-shadow text-neutral text-xs">
|
||||||
{tags.find(t => t.id === e.id)?._count?.links}
|
{e._count?.links}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</button>
|
</button>
|
||||||
|
|
|
@ -11,13 +11,11 @@ type ResponseObject = {
|
||||||
type LinkStore = {
|
type LinkStore = {
|
||||||
links: LinkIncludingShortenedCollectionAndTags[];
|
links: LinkIncludingShortenedCollectionAndTags[];
|
||||||
selectedLinks: LinkIncludingShortenedCollectionAndTags[];
|
selectedLinks: LinkIncludingShortenedCollectionAndTags[];
|
||||||
allLinksOfCollection: LinkIncludingShortenedCollectionAndTags[];
|
|
||||||
setLinks: (
|
setLinks: (
|
||||||
data: LinkIncludingShortenedCollectionAndTags[],
|
data: LinkIncludingShortenedCollectionAndTags[],
|
||||||
isInitialCall: boolean
|
isInitialCall: boolean
|
||||||
) => void;
|
) => void;
|
||||||
setSelectedLinks: (links: LinkIncludingShortenedCollectionAndTags[]) => void;
|
setSelectedLinks: (links: LinkIncludingShortenedCollectionAndTags[]) => void;
|
||||||
setAllLinksOfCollection: (links: LinkIncludingShortenedCollectionAndTags[]) => void;
|
|
||||||
addLink: (
|
addLink: (
|
||||||
body: LinkIncludingShortenedCollectionAndTags
|
body: LinkIncludingShortenedCollectionAndTags
|
||||||
) => Promise<ResponseObject>;
|
) => Promise<ResponseObject>;
|
||||||
|
@ -41,7 +39,6 @@ type LinkStore = {
|
||||||
const useLinkStore = create<LinkStore>()((set) => ({
|
const useLinkStore = create<LinkStore>()((set) => ({
|
||||||
links: [],
|
links: [],
|
||||||
selectedLinks: [],
|
selectedLinks: [],
|
||||||
allLinksOfCollection: [],
|
|
||||||
setLinks: async (data, isInitialCall) => {
|
setLinks: async (data, isInitialCall) => {
|
||||||
isInitialCall &&
|
isInitialCall &&
|
||||||
set(() => ({
|
set(() => ({
|
||||||
|
@ -61,7 +58,6 @@ const useLinkStore = create<LinkStore>()((set) => ({
|
||||||
}));
|
}));
|
||||||
},
|
},
|
||||||
setSelectedLinks: (links) => set({ selectedLinks: links }),
|
setSelectedLinks: (links) => set({ selectedLinks: links }),
|
||||||
setAllLinksOfCollection: (links) => set({allLinksOfCollection: links}),
|
|
||||||
addLink: async (body) => {
|
addLink: async (body) => {
|
||||||
const response = await fetch("/api/v1/links", {
|
const response = await fetch("/api/v1/links", {
|
||||||
body: JSON.stringify(body),
|
body: JSON.stringify(body),
|
||||||
|
|
|
@ -8,15 +8,19 @@ type ResponseObject = {
|
||||||
|
|
||||||
type TagStore = {
|
type TagStore = {
|
||||||
tags: TagIncludingLinkCount[];
|
tags: TagIncludingLinkCount[];
|
||||||
setTags: () => void;
|
setTags: (collectionId?: number) => void;
|
||||||
updateTag: (tag: TagIncludingLinkCount) => Promise<ResponseObject>;
|
updateTag: (tag: TagIncludingLinkCount) => Promise<ResponseObject>;
|
||||||
removeTag: (tagId: number) => Promise<ResponseObject>;
|
removeTag: (tagId: number) => Promise<ResponseObject>;
|
||||||
};
|
};
|
||||||
|
|
||||||
const useTagStore = create<TagStore>()((set) => ({
|
const useTagStore = create<TagStore>()((set) => ({
|
||||||
tags: [],
|
tags: [],
|
||||||
setTags: async () => {
|
setTags: async (collectionId?: number) => {
|
||||||
const response = await fetch("/api/v1/tags");
|
let path = "/api/v1/tags";
|
||||||
|
if (collectionId) {
|
||||||
|
path = "/api/v1/public/collections/tags?collectionId=" + encodeURIComponent(collectionId);
|
||||||
|
}
|
||||||
|
const response = await fetch(path);
|
||||||
|
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
|
|
||||||
|
|
Ŝarĝante…
Reference in New Issue