updated public page
This commit is contained in:
@@ -134,15 +134,16 @@ export default function LinkGrid({ link, count, className }: Props) {
|
|||||||
<p className="text-sm truncate">{shortendURL}</p>
|
<p className="text-sm truncate">{shortendURL}</p>
|
||||||
</div>
|
</div>
|
||||||
</Link>
|
</Link>
|
||||||
|
</div>
|
||||||
|
|
||||||
<hr className="divider my-2 -mx-3 last:hidden border-t border-neutral-content h-[1px]" />
|
<hr className="divider mt-2 mb-1 last:hidden border-t border-neutral-content h-[1px]" />
|
||||||
|
<div className="flex justify-between text-xs text-neutral px-3 pb-1">
|
||||||
<div className="flex justify-between text-xs text-neutral">
|
<div className="cursor-pointer w-fit">
|
||||||
<div className="cursor-pointer w-fit">
|
{collection ? (
|
||||||
<LinkCollection link={link} collection={collection} />
|
<LinkCollection link={link} collection={collection} />
|
||||||
</div>
|
) : undefined}
|
||||||
<LinkDate link={link} />
|
|
||||||
</div>
|
</div>
|
||||||
|
<LinkDate link={link} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{showInfo ? (
|
{showInfo ? (
|
||||||
@@ -153,9 +154,24 @@ export default function LinkGrid({ link, count, className }: Props) {
|
|||||||
>
|
>
|
||||||
<i className="bi-x text-neutral text-2xl"></i>
|
<i className="bi-x text-neutral text-2xl"></i>
|
||||||
</div>
|
</div>
|
||||||
<div className="pb-3 mt-1">
|
<p className="text-neutral text-lg font-semibold">Description</p>
|
||||||
<p>{unescapeString(link.description)}</p>
|
|
||||||
{link.tags[0] ? (
|
<hr className="divider my-2 last:hidden border-t border-neutral-content h-[1px]" />
|
||||||
|
<p>
|
||||||
|
{link.description ? (
|
||||||
|
unescapeString(link.description)
|
||||||
|
) : (
|
||||||
|
<span className="text-neutral text-sm">
|
||||||
|
No description provided.
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</p>
|
||||||
|
{link.tags[0] ? (
|
||||||
|
<>
|
||||||
|
<p className="text-neutral text-lg mt-3 font-semibold">Tags</p>
|
||||||
|
|
||||||
|
<hr className="divider my-2 last:hidden border-t border-neutral-content h-[1px]" />
|
||||||
|
|
||||||
<div className="flex gap-3 items-center flex-wrap mt-2 truncate relative">
|
<div className="flex gap-3 items-center flex-wrap mt-2 truncate relative">
|
||||||
<div className="flex gap-1 items-center flex-wrap">
|
<div className="flex gap-1 items-center flex-wrap">
|
||||||
{link.tags.map((e, i) => (
|
{link.tags.map((e, i) => (
|
||||||
@@ -172,8 +188,8 @@ export default function LinkGrid({ link, count, className }: Props) {
|
|||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
) : undefined}
|
</>
|
||||||
</div>
|
) : undefined}
|
||||||
</div>
|
</div>
|
||||||
) : undefined}
|
) : undefined}
|
||||||
|
|
||||||
|
|||||||
@@ -101,7 +101,7 @@ export default function LinkActions({
|
|||||||
toggleShowInfo();
|
toggleShowInfo();
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{!linkInfo ? "Show" : "Hide"} Link Info
|
{!linkInfo ? "Show" : "Hide"} Link Details
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
{permissions === true || permissions?.canUpdate ? (
|
{permissions === true || permissions?.canUpdate ? (
|
||||||
|
|||||||
@@ -50,7 +50,11 @@ export default function LinkCardCompact({ link, count, className }: Props) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="border-neutral-content relative hover:bg-base-300 duration-200 rounded-lg">
|
<div
|
||||||
|
className={`border-neutral-content relative ${
|
||||||
|
!showInfo ? "hover:bg-base-300" : ""
|
||||||
|
} duration-200 rounded-lg`}
|
||||||
|
>
|
||||||
<div
|
<div
|
||||||
onClick={() => link.url && window.open(link.url || "", "_blank")}
|
onClick={() => link.url && window.open(link.url || "", "_blank")}
|
||||||
className="flex items-center cursor-pointer py-3 px-3"
|
className="flex items-center cursor-pointer py-3 px-3"
|
||||||
@@ -66,8 +70,12 @@ export default function LinkCardCompact({ link, count, className }: Props) {
|
|||||||
|
|
||||||
<div className="mt-1 flex flex-col sm:flex-row sm:items-center gap-2 text-xs text-neutral">
|
<div className="mt-1 flex flex-col sm:flex-row sm:items-center gap-2 text-xs text-neutral">
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<LinkCollection link={link} collection={collection} />
|
{collection ? (
|
||||||
·
|
<>
|
||||||
|
<LinkCollection link={link} collection={collection} />
|
||||||
|
·
|
||||||
|
</>
|
||||||
|
) : undefined}
|
||||||
{link.url ? (
|
{link.url ? (
|
||||||
<div className="flex items-center gap-1 max-w-full w-fit text-neutral">
|
<div className="flex items-center gap-1 max-w-full w-fit text-neutral">
|
||||||
<i className="bi-link-45deg text-base" />
|
<i className="bi-link-45deg text-base" />
|
||||||
@@ -93,26 +101,45 @@ export default function LinkCardCompact({ link, count, className }: Props) {
|
|||||||
linkInfo={showInfo}
|
linkInfo={showInfo}
|
||||||
/>
|
/>
|
||||||
{showInfo ? (
|
{showInfo ? (
|
||||||
<div className="pl-10">
|
<div>
|
||||||
<div className="pb-3 mt-1">
|
<div className="pb-3 mt-1 px-3">
|
||||||
<p>{unescapeString(link.description)}</p>
|
<p className="text-neutral text-lg font-semibold">Description</p>
|
||||||
|
|
||||||
|
<hr className="divider my-2 last:hidden border-t border-neutral-content h-[1px]" />
|
||||||
|
<p>
|
||||||
|
{link.description ? (
|
||||||
|
unescapeString(link.description)
|
||||||
|
) : (
|
||||||
|
<span className="text-neutral text-sm">
|
||||||
|
No description provided.
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</p>
|
||||||
{link.tags[0] ? (
|
{link.tags[0] ? (
|
||||||
<div className="flex gap-3 items-center flex-wrap mt-2 truncate relative">
|
<>
|
||||||
<div className="flex gap-1 items-center flex-wrap">
|
<p className="text-neutral text-lg mt-3 font-semibold">
|
||||||
{link.tags.map((e, i) => (
|
Tags
|
||||||
<Link
|
</p>
|
||||||
href={"/tags/" + e.id}
|
|
||||||
key={i}
|
<hr className="divider my-2 last:hidden border-t border-neutral-content h-[1px]" />
|
||||||
onClick={(e) => {
|
|
||||||
e.stopPropagation();
|
<div className="flex gap-3 items-center flex-wrap mt-2 truncate relative">
|
||||||
}}
|
<div className="flex gap-1 items-center flex-wrap">
|
||||||
className="btn btn-xs btn-ghost truncate max-w-[19rem]"
|
{link.tags.map((e, i) => (
|
||||||
>
|
<Link
|
||||||
#{e.name}
|
href={"/tags/" + e.id}
|
||||||
</Link>
|
key={i}
|
||||||
))}
|
onClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
}}
|
||||||
|
className="btn btn-xs btn-ghost truncate max-w-[19rem]"
|
||||||
|
>
|
||||||
|
#{e.name}
|
||||||
|
</Link>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</>
|
||||||
) : undefined}
|
) : undefined}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,132 +0,0 @@
|
|||||||
import Image from "next/image";
|
|
||||||
import { Link as LinkType, Tag } from "@prisma/client";
|
|
||||||
import isValidUrl from "@/lib/shared/isValidUrl";
|
|
||||||
import unescapeString from "@/lib/client/unescapeString";
|
|
||||||
import { TagIncludingLinkCount } from "@/types/global";
|
|
||||||
import Link from "next/link";
|
|
||||||
import { useState } from "react";
|
|
||||||
import PreservedFormatsModal from "../ModalContent/PreservedFormatsModal";
|
|
||||||
|
|
||||||
interface LinksIncludingTags extends LinkType {
|
|
||||||
tags: TagIncludingLinkCount[];
|
|
||||||
}
|
|
||||||
|
|
||||||
type Props = {
|
|
||||||
link: LinksIncludingTags;
|
|
||||||
count: number;
|
|
||||||
};
|
|
||||||
|
|
||||||
export default function LinkCard({ link, count }: Props) {
|
|
||||||
const url = link.url && isValidUrl(link.url) ? new URL(link.url) : undefined;
|
|
||||||
|
|
||||||
const formattedDate = new Date(
|
|
||||||
link.createdAt as unknown as string
|
|
||||||
).toLocaleString("en-US", {
|
|
||||||
year: "numeric",
|
|
||||||
month: "short",
|
|
||||||
day: "numeric",
|
|
||||||
});
|
|
||||||
|
|
||||||
const [preservedFormatsModal, setPreservedFormatsModal] = useState(false);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="border border-solid border-neutral-content bg-base-200 shadow hover:shadow-none duration-100 rounded-lg p-3 flex items-start relative gap-3 group/item">
|
|
||||||
<div className="flex justify-between items-end gap-5 w-full h-full z-0">
|
|
||||||
<div className="flex flex-col justify-between w-full">
|
|
||||||
<div className="flex items-center gap-2">
|
|
||||||
<p className="text-2xl">
|
|
||||||
{url && (
|
|
||||||
<Image
|
|
||||||
src={`https://t2.gstatic.com/faviconV2?client=SOCIAL&type=FAVICON&fallback_opts=TYPE,SIZE,URL&url=${url.origin}&size=32`}
|
|
||||||
width={30}
|
|
||||||
height={30}
|
|
||||||
alt=""
|
|
||||||
className="select-none z-10 rounded-md shadow border-[1px] border-white bg-white float-left mr-2"
|
|
||||||
draggable="false"
|
|
||||||
onError={(e) => {
|
|
||||||
const target = e.target as HTMLElement;
|
|
||||||
target.style.display = "none";
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{unescapeString(link.name || link.description)}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="flex gap-3 items-center flex-wrap my-2">
|
|
||||||
<div className="flex gap-1 items-center flex-wrap">
|
|
||||||
{link.tags.map((e, i) => (
|
|
||||||
<Link
|
|
||||||
href={"/public/collections/20?q=" + e.name}
|
|
||||||
key={i}
|
|
||||||
className="btn btn-xs btn-ghost truncate max-w-[19rem]"
|
|
||||||
>
|
|
||||||
#{e.name}
|
|
||||||
</Link>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="flex gap-1 items-center flex-wrap text-sm text-neutral">
|
|
||||||
<p>{formattedDate}</p>
|
|
||||||
<p>·</p>
|
|
||||||
<Link
|
|
||||||
href={url ? url.href : link.url || ""}
|
|
||||||
target="_blank"
|
|
||||||
className="hover:opacity-50 duration-100 truncate w-52 sm:w-fit"
|
|
||||||
title={url ? url.href : link.url || ""}
|
|
||||||
>
|
|
||||||
{url ? url.host : link.url}
|
|
||||||
</Link>
|
|
||||||
</div>
|
|
||||||
<div className="w-full">
|
|
||||||
{unescapeString(link.description)}{" "}
|
|
||||||
<Link
|
|
||||||
href={link.url || ""}
|
|
||||||
target="_blank"
|
|
||||||
className="flex gap-1 items-center flex-wrap text-sm text-neutral hover:opacity-50 duration-100 min-w-fit float-right mt-1 ml-2"
|
|
||||||
>
|
|
||||||
<p>Visit</p>
|
|
||||||
<i className={"bi-chevron-right"}></i>
|
|
||||||
</Link>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
className={`dropdown dropdown-left absolute ${"top-3 right-3"} z-20`}
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
tabIndex={0}
|
|
||||||
role="button"
|
|
||||||
className="btn btn-ghost btn-sm btn-square text-neutral"
|
|
||||||
>
|
|
||||||
<i
|
|
||||||
id={"expand-dropdown" + link.id}
|
|
||||||
title="More"
|
|
||||||
className="bi-three-dots text-xl"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<ul className="dropdown-content z-[20] menu shadow bg-base-200 border border-neutral-content rounded-box w-44 mr-1">
|
|
||||||
<li>
|
|
||||||
<div
|
|
||||||
role="button"
|
|
||||||
tabIndex={0}
|
|
||||||
onClick={() => {
|
|
||||||
(document?.activeElement as HTMLElement)?.blur();
|
|
||||||
setPreservedFormatsModal(true);
|
|
||||||
// updateArchive();
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Preserved Formats
|
|
||||||
</div>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
{preservedFormatsModal ? (
|
|
||||||
<PreservedFormatsModal
|
|
||||||
onClose={() => setPreservedFormatsModal(false)}
|
|
||||||
activeLink={link as any}
|
|
||||||
/>
|
|
||||||
) : undefined}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,7 +1,10 @@
|
|||||||
"use client";
|
"use client";
|
||||||
import PublicLinkCard from "@/components/PublicPage/PublicLinkCard";
|
|
||||||
import getPublicCollectionData from "@/lib/client/getPublicCollectionData";
|
import getPublicCollectionData from "@/lib/client/getPublicCollectionData";
|
||||||
import { CollectionIncludingMembersAndLinkCount, Sort } from "@/types/global";
|
import {
|
||||||
|
CollectionIncludingMembersAndLinkCount,
|
||||||
|
Sort,
|
||||||
|
ViewMode,
|
||||||
|
} from "@/types/global";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import React, { useEffect, useState } from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
import { motion, Variants } from "framer-motion";
|
import { motion, Variants } from "framer-motion";
|
||||||
@@ -18,6 +21,10 @@ import SortDropdown from "@/components/SortDropdown";
|
|||||||
import useLocalSettingsStore from "@/store/localSettings";
|
import useLocalSettingsStore from "@/store/localSettings";
|
||||||
import SearchBar from "@/components/SearchBar";
|
import SearchBar from "@/components/SearchBar";
|
||||||
import EditCollectionSharingModal from "@/components/ModalContent/EditCollectionSharingModal";
|
import EditCollectionSharingModal from "@/components/ModalContent/EditCollectionSharingModal";
|
||||||
|
import ViewDropdown from "@/components/ViewDropdown";
|
||||||
|
import CardView from "@/components/LinkViews/Layouts/CardView";
|
||||||
|
import ListView from "@/components/LinkViews/Layouts/ListView";
|
||||||
|
// import GridView from "@/components/LinkViews/Layouts/GridView";
|
||||||
|
|
||||||
const cardVariants: Variants = {
|
const cardVariants: Variants = {
|
||||||
offscreen: {
|
offscreen: {
|
||||||
@@ -94,6 +101,19 @@ export default function PublicCollections() {
|
|||||||
const [editCollectionSharingModal, setEditCollectionSharingModal] =
|
const [editCollectionSharingModal, setEditCollectionSharingModal] =
|
||||||
useState(false);
|
useState(false);
|
||||||
|
|
||||||
|
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];
|
||||||
|
|
||||||
return collection ? (
|
return collection ? (
|
||||||
<div
|
<div
|
||||||
className="h-96"
|
className="h-96"
|
||||||
@@ -185,51 +205,38 @@ export default function PublicCollections() {
|
|||||||
<div className="divider mt-5 mb-0"></div>
|
<div className="divider mt-5 mb-0"></div>
|
||||||
|
|
||||||
<div className="flex mb-5 mt-10 flex-col gap-5">
|
<div className="flex mb-5 mt-10 flex-col gap-5">
|
||||||
<div className="flex justify-between">
|
<div className="flex justify-between gap-3">
|
||||||
<SearchBar
|
<SearchBar
|
||||||
placeholder={`Search ${collection._count?.links} Links`}
|
placeholder={`Search ${collection._count?.links} Links`}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div className="flex gap-2 items-center">
|
<div className="flex gap-2 items-center w-fit">
|
||||||
<div className="relative">
|
<FilterSearchDropdown
|
||||||
<FilterSearchDropdown
|
searchFilter={searchFilter}
|
||||||
searchFilter={searchFilter}
|
setSearchFilter={setSearchFilter}
|
||||||
setSearchFilter={setSearchFilter}
|
/>
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="relative">
|
<SortDropdown sortBy={sortBy} setSort={setSortBy} />
|
||||||
<SortDropdown sortBy={sortBy} setSort={setSortBy} />
|
|
||||||
</div>
|
<ViewDropdown viewMode={viewMode} setViewMode={setViewMode} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex flex-col gap-5">
|
{links[0] ? (
|
||||||
{links
|
<LinkComponent
|
||||||
?.filter((e) => e.collectionId === Number(router.query.id))
|
links={links
|
||||||
.map((e, i) => {
|
.filter((e) => e.collectionId === Number(router.query.id))
|
||||||
const linkWithCollectionData = {
|
.map((e, i) => {
|
||||||
...e,
|
const linkWithCollectionData = {
|
||||||
collection: collection, // Append collection data
|
...e,
|
||||||
};
|
collection: collection, // Append collection data
|
||||||
|
};
|
||||||
return (
|
return linkWithCollectionData;
|
||||||
<motion.div
|
})}
|
||||||
key={i}
|
/>
|
||||||
initial="offscreen"
|
) : (
|
||||||
whileInView="onscreen"
|
<p>This collection is empty...</p>
|
||||||
viewport={{ once: true, amount: 0.8 }}
|
)}
|
||||||
>
|
|
||||||
<motion.div variants={cardVariants}>
|
|
||||||
<PublicLinkCard
|
|
||||||
link={linkWithCollectionData as any}
|
|
||||||
count={i}
|
|
||||||
/>
|
|
||||||
</motion.div>
|
|
||||||
</motion.div>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* <p className="text-center text-neutral">
|
{/* <p className="text-center text-neutral">
|
||||||
List created with <span className="text-black">Linkwarden.</span>
|
List created with <span className="text-black">Linkwarden.</span>
|
||||||
|
|||||||
Reference in New Issue
Block a user