el.xwx.moe/pages/public/collections/[id].tsx

320 lines
11 KiB
TypeScript
Raw Normal View History

2023-10-05 11:19:31 -05:00
"use client";
2023-05-29 14:40:23 -05:00
import getPublicCollectionData from "@/lib/client/getPublicCollectionData";
2023-12-24 06:30:45 -06:00
import {
CollectionIncludingMembersAndLinkCount,
Sort,
2024-07-05 03:40:40 -05:00
TagIncludingLinkCount,
2023-12-24 06:30:45 -06:00
ViewMode,
} from "@/types/global";
2023-05-29 14:40:23 -05:00
import { useRouter } from "next/router";
import React, { useEffect, useState } from "react";
import { motion, Variants } from "framer-motion";
2023-10-11 11:18:45 -05:00
import Head from "next/head";
2023-11-15 12:12:06 -06:00
import useLinks from "@/hooks/useLinks";
import useLinkStore from "@/store/links";
2023-11-15 22:31:13 -06:00
import ProfilePhoto from "@/components/ProfilePhoto";
2023-11-16 02:22:16 -06:00
import ToggleDarkMode from "@/components/ToggleDarkMode";
import getPublicUserData from "@/lib/client/getPublicUserData";
import Image from "next/image";
import Link from "next/link";
import FilterSearchDropdown from "@/components/FilterSearchDropdown";
import SortDropdown from "@/components/SortDropdown";
2023-11-24 06:50:16 -06:00
import useLocalSettingsStore from "@/store/localSettings";
2023-11-30 05:13:42 -06:00
import SearchBar from "@/components/SearchBar";
2023-12-01 16:44:34 -06:00
import EditCollectionSharingModal from "@/components/ModalContent/EditCollectionSharingModal";
2023-12-24 06:30:45 -06:00
import ViewDropdown from "@/components/ViewDropdown";
import CardView from "@/components/LinkViews/Layouts/CardView";
import ListView from "@/components/LinkViews/Layouts/ListView";
2024-07-05 03:40:40 -05:00
import useTagStore from "@/store/tags";
2023-12-24 06:30:45 -06:00
// import GridView from "@/components/LinkViews/Layouts/GridView";
const cardVariants: Variants = {
offscreen: {
y: 50,
opacity: 0,
},
onscreen: {
y: 0,
opacity: 1,
transition: {
duration: 0.4,
},
},
};
2023-05-28 00:55:49 -05:00
export default function PublicCollections() {
2023-11-15 12:12:06 -06:00
const { links } = useLinkStore();
2024-07-05 03:50:32 -05:00
const tagsInCollection = links.map(l => l.tags).flat();
2023-11-16 02:22:16 -06:00
2023-11-24 06:50:16 -06:00
const { settings } = useLocalSettingsStore();
2023-05-29 14:40:23 -05:00
const router = useRouter();
2023-11-16 02:22:16 -06:00
const [collectionOwner, setCollectionOwner] = useState({
id: null as unknown as number,
2023-11-16 02:22:16 -06:00
name: "",
username: "",
image: "",
archiveAsScreenshot: undefined as unknown as boolean,
archiveAsPDF: undefined as unknown as boolean,
2023-11-16 02:22:16 -06:00
});
2024-07-05 03:40:40 -05:00
const { tags } = useTagStore();
const handleTagSelection = (tag: TagIncludingLinkCount | undefined) => {
if (tag) {
Object.keys(searchFilter).forEach((v) => searchFilter[(v as keyof {name: boolean, url: boolean, description: boolean, tags: boolean, textContent: boolean})] = false)
searchFilter.tags = true;
return router.push(
"/public/collections/" +
router.query.id +
"?q=" +
encodeURIComponent(tag.name || "")
);
} else {
return router.push(
"/public/collections/" +
router.query.id)
}
}
2023-11-15 12:12:06 -06:00
const [searchFilter, setSearchFilter] = useState({
name: true,
url: true,
description: true,
tags: true,
2024-03-10 05:08:28 -05:00
textContent: false,
2023-11-15 12:12:06 -06:00
});
const [sortBy, setSortBy] = useState<Sort>(Sort.DateNewestFirst);
useLinks({
sort: sortBy,
searchQueryString: router.query.q
? decodeURIComponent(router.query.q as string)
: undefined,
searchByName: searchFilter.name,
searchByUrl: searchFilter.url,
searchByDescription: searchFilter.description,
searchByTextContent: searchFilter.textContent,
searchByTags: searchFilter.tags,
});
const [collection, setCollection] =
useState<CollectionIncludingMembersAndLinkCount>();
2023-05-29 14:40:23 -05:00
useEffect(() => {
2023-06-02 22:50:16 -05:00
if (router.query.id) {
2023-11-15 12:12:06 -06:00
getPublicCollectionData(Number(router.query.id), setCollection);
2023-06-02 22:50:16 -05:00
}
2023-05-29 14:40:23 -05:00
}, []);
2023-11-16 02:22:16 -06:00
useEffect(() => {
const fetchOwner = async () => {
if (collection) {
const owner = await getPublicUserData(collection.ownerId as number);
setCollectionOwner(owner);
}
};
fetchOwner();
}, [collection]);
2023-12-01 11:17:00 -06:00
const [editCollectionSharingModal, setEditCollectionSharingModal] =
useState(false);
2023-12-24 06:30:45 -06:00
const [viewMode, setViewMode] = useState<string>(
localStorage.getItem("viewMode") || ViewMode.Card
);
const linkView = {
[ViewMode.Card]: CardView,
// [ViewMode.Grid]: GridView,
[ViewMode.List]: ListView,
};
// @ts-ignore
const LinkComponent = linkView[viewMode];
2023-11-15 12:12:06 -06:00
return collection ? (
2023-11-15 22:31:13 -06:00
<div
2023-12-19 16:20:09 -06:00
className="h-96"
2023-11-15 22:31:13 -06:00
style={{
2023-11-16 02:22:16 -06:00
backgroundImage: `linear-gradient(${collection?.color}30 10%, ${
2023-11-24 06:50:16 -06:00
settings.theme === "dark" ? "#262626" : "#f3f4f6"
2023-12-19 16:20:09 -06:00
} 13rem, ${settings.theme === "dark" ? "#171717" : "#ffffff"} 100%)`,
2023-11-15 22:31:13 -06:00
}}
>
2023-11-15 12:12:06 -06:00
{collection ? (
2023-10-11 11:18:45 -05:00
<Head>
2023-11-15 12:12:06 -06:00
<title>{collection.name} | Linkwarden</title>
2023-10-11 11:18:45 -05:00
<meta
property="og:title"
2023-11-15 12:12:06 -06:00
content={`${collection.name} | Linkwarden`}
2023-10-11 11:18:45 -05:00
key="title"
/>
</Head>
) : undefined}
2023-12-24 09:45:48 -06:00
<div className="lg:w-3/4 w-full mx-auto p-5 bg">
2023-11-15 22:31:13 -06:00
<div className="flex items-center justify-between">
2023-11-16 02:22:16 -06:00
<p className="text-4xl font-thin mb-2 capitalize mt-10">
2023-11-15 22:31:13 -06:00
{collection.name}
</p>
2023-11-16 05:51:28 -06:00
<div className="flex gap-2 items-center mt-8 min-w-fit">
2023-11-27 15:38:38 -06:00
<ToggleDarkMode />
2023-11-29 14:17:51 -06:00
2023-11-16 02:22:16 -06:00
<Link href="https://linkwarden.app/" target="_blank">
<Image
src={`/icon.png`}
width={551}
height={551}
alt="Linkwarden"
2023-11-29 14:17:51 -06:00
title="Created with Linkwarden"
2023-11-27 15:38:38 -06:00
className="h-8 w-fit mx-auto rounded"
2023-11-16 02:22:16 -06:00
/>
</Link>
</div>
2023-11-15 22:31:13 -06:00
</div>
2023-11-27 15:38:38 -06:00
<div className="mt-3">
2023-11-16 02:22:16 -06:00
<div className={`min-w-[15rem]`}>
<div className="flex gap-1 justify-center sm:justify-end items-center w-fit">
<div
className="flex items-center btn px-2 btn-ghost rounded-full"
2023-12-01 11:17:00 -06:00
onClick={() => setEditCollectionSharingModal(true)}
>
{collectionOwner.id ? (
<ProfilePhoto
src={collectionOwner.image || undefined}
2023-12-05 03:39:01 -06:00
name={collectionOwner.name}
/>
) : undefined}
{collection.members
.sort((a, b) => (a.userId as number) - (b.userId as number))
.map((e, i) => {
return (
<ProfilePhoto
key={i}
src={e.user.image ? e.user.image : undefined}
className="-ml-3"
2023-12-05 03:39:01 -06:00
name={e.user.name}
/>
);
})
.slice(0, 3)}
{collection.members.length - 3 > 0 ? (
2023-12-05 03:39:01 -06:00
<div className={`avatar drop-shadow-md placeholder -ml-3`}>
<div className="bg-base-100 text-neutral rounded-full w-8 h-8 ring-2 ring-neutral-content">
<span>+{collection.members.length - 3}</span>
</div>
2023-11-27 15:38:38 -06:00
</div>
) : null}
</div>
2023-12-04 09:24:45 -06:00
<p className="text-neutral text-sm font-semibold">
By {collectionOwner.name}
{collection.members.length > 0
? ` and ${collection.members.length} others`
: undefined}
.
2023-11-16 02:22:16 -06:00
</p>
2023-11-15 22:31:13 -06:00
</div>
</div>
</div>
2023-11-16 02:22:16 -06:00
<p className="mt-5">{collection.description}</p>
2023-05-31 09:30:45 -05:00
<div className="divider mt-5 mb-0"></div>
2023-11-15 22:31:13 -06:00
2023-11-16 02:22:16 -06:00
<div className="flex mb-5 mt-10 flex-col gap-5">
2023-12-24 06:30:45 -06:00
<div className="flex justify-between gap-3">
2023-11-30 05:13:42 -06:00
<SearchBar
placeholder={`Search ${collection._count?.links} Links`}
2023-11-16 02:22:16 -06:00
/>
2023-12-24 06:30:45 -06:00
<div className="flex gap-2 items-center w-fit">
<FilterSearchDropdown
searchFilter={searchFilter}
setSearchFilter={setSearchFilter}
/>
2023-11-16 02:22:16 -06:00
2023-12-24 06:30:45 -06:00
<SortDropdown sortBy={sortBy} setSort={setSortBy} />
<ViewDropdown viewMode={viewMode} setViewMode={setViewMode} />
2023-11-16 02:22:16 -06:00
</div>
</div>
2024-07-05 03:50:32 -05:00
{collection.tagsArePublic && tagsInCollection[0] && (
2024-07-05 03:40:40 -05:00
<div>
<p className="text-sm">Browse by topic</p>
<div className="flex gap-2 mt-2 mb-6">
<button onClick={() => handleTagSelection(undefined)}>
<div
className="
bg-neutral-content/20 hover:bg-neutral/20 duration-100 py-1 px-2 cursor-pointer flex items-center gap-2 rounded-md h-8"
>
<i className="text-primary bi-hash text-2xl text-primary drop-shadow"></i>
<p className="truncate pr-7">All</p>
<div className="text-neutral drop-shadow text-neutral text-xs">
{collection._count?.links}
</div>
</div>
</button>
2024-07-05 03:50:32 -05:00
{tagsInCollection
2024-07-05 03:40:40 -05:00
.sort((a, b) => a.name.localeCompare(b.name))
.map((e, i) => {
const active = router.query.q === e.name;
return (
<button key={i} onClick={() => handleTagSelection(e)}>
<div
className={`
${
active
? "bg-primary/20"
: "bg-neutral-content/20 hover:bg-neutral/20"
} duration-100 py-1 px-2 cursor-pointer flex items-center gap-2 rounded-md h-8`}
>
<i className="bi-hash text-2xl text-primary drop-shadow"></i>
<p className="truncate pr-7">{e.name}</p>
<div className="drop-shadow text-neutral text-xs">
2024-07-05 03:50:32 -05:00
{tags.find(t => t.id === e.id)?._count?.links}
2024-07-05 03:40:40 -05:00
</div>
</div>
</button>
);
})
}
</div>
</div>
)}
2023-12-24 06:30:45 -06:00
{links[0] ? (
<LinkComponent
links={links
.filter((e) => e.collectionId === Number(router.query.id))
.map((e, i) => {
const linkWithCollectionData = {
...e,
collection: collection, // Append collection data
};
return linkWithCollectionData;
})}
/>
) : (
<p>This collection is empty...</p>
)}
2023-05-31 09:30:45 -05:00
2023-11-26 04:17:08 -06:00
{/* <p className="text-center text-neutral">
2023-11-16 02:22:16 -06:00
List created with <span className="text-black">Linkwarden.</span>
</p> */}
</div>
2023-11-15 22:31:13 -06:00
</div>
2023-12-01 16:42:45 -06:00
{editCollectionSharingModal ? (
<EditCollectionSharingModal
onClose={() => setEditCollectionSharingModal(false)}
activeCollection={collection}
/>
) : undefined}
2023-05-29 14:40:23 -05:00
</div>
2023-05-31 09:30:45 -05:00
) : (
<></>
2023-05-29 14:40:23 -05:00
);
2023-05-28 00:55:49 -05:00
}