From bcfbdf3e49076d5a02b72d7d39f5b1012b6bbfce Mon Sep 17 00:00:00 2001 From: Yee Jia Wei Date: Sat, 16 Dec 2023 11:25:39 +0800 Subject: [PATCH 01/10] link compact list view --- components/LinkCard.tsx | 393 +++++------------- components/LinkViews/CompactGridView.tsx | 16 + components/LinkViews/DefaultGridView.tsx | 16 + components/LinkViews/LinkCardCompact.tsx | 102 +++++ .../LinkViews/LinkComponents/LinkActions.tsx | 165 ++++++++ .../LinkComponents/LinkCollection.tsx | 34 ++ .../LinkViews/LinkComponents/LinkDate.tsx | 25 ++ .../LinkViews/LinkComponents/LinkIcon.tsx | 45 ++ components/LinkViews/LinkRow.tsx | 172 ++++++++ components/LinkViews/ListView.tsx | 24 ++ components/SortDropdown.tsx | 11 +- components/ViewDropdown.tsx | 62 +++ pages/links/index.tsx | 24 +- types/global.ts | 6 + 14 files changed, 803 insertions(+), 292 deletions(-) create mode 100644 components/LinkViews/CompactGridView.tsx create mode 100644 components/LinkViews/DefaultGridView.tsx create mode 100644 components/LinkViews/LinkCardCompact.tsx create mode 100644 components/LinkViews/LinkComponents/LinkActions.tsx create mode 100644 components/LinkViews/LinkComponents/LinkCollection.tsx create mode 100644 components/LinkViews/LinkComponents/LinkDate.tsx create mode 100644 components/LinkViews/LinkComponents/LinkIcon.tsx create mode 100644 components/LinkViews/LinkRow.tsx create mode 100644 components/LinkViews/ListView.tsx create mode 100644 components/ViewDropdown.tsx diff --git a/components/LinkCard.tsx b/components/LinkCard.tsx index fc23253a..d530813f 100644 --- a/components/LinkCard.tsx +++ b/components/LinkCard.tsx @@ -1,307 +1,138 @@ import { - CollectionIncludingMembersAndLinkCount, - LinkIncludingShortenedCollectionAndTags, + CollectionIncludingMembersAndLinkCount, + LinkIncludingShortenedCollectionAndTags, } from "@/types/global"; -import { - faFolder, - faEllipsis, - faLink, -} from "@fortawesome/free-solid-svg-icons"; -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { useEffect, useState } from "react"; +import {faLink} from "@fortawesome/free-solid-svg-icons"; +import {FontAwesomeIcon} from "@fortawesome/react-fontawesome"; +import {useEffect, useState} from "react"; import Image from "next/image"; import useLinkStore from "@/store/links"; import useCollectionStore from "@/store/collections"; -import useAccountStore from "@/store/account"; import { - faCalendarDays, - faFileImage, - faFilePdf, + faFileImage, + faFilePdf, } from "@fortawesome/free-regular-svg-icons"; -import usePermissions from "@/hooks/usePermissions"; -import { toast } from "react-hot-toast"; import isValidUrl from "@/lib/shared/isValidUrl"; -import Link from "next/link"; import unescapeString from "@/lib/client/unescapeString"; -import { useRouter } from "next/router"; -import EditLinkModal from "./ModalContent/EditLinkModal"; -import DeleteLinkModal from "./ModalContent/DeleteLinkModal"; -import PreservedFormatsModal from "./ModalContent/PreservedFormatsModal"; +import LinkActions from "@/components/LinkViews/LinkComponents/LinkActions"; +import LinkDate from "@/components/LinkViews/LinkComponents/LinkDate"; +import LinkCollection from "@/components/LinkViews/LinkComponents/LinkCollection"; type Props = { - link: LinkIncludingShortenedCollectionAndTags; - count: number; - className?: string; + link: LinkIncludingShortenedCollectionAndTags; + count: number; + className?: string; }; -export default function LinkCard({ link, count, className }: Props) { - const router = useRouter(); +export default function LinkCard({link, count, className}: Props) { + const {links} = useLinkStore(); - const permissions = usePermissions(link.collection.id as number); + const {collections} = useCollectionStore(); - const { collections } = useCollectionStore(); + const [collection, setCollection] = + useState( + collections.find( + (e) => e.id === link.collection.id + ) as CollectionIncludingMembersAndLinkCount + ); - const { links } = useLinkStore(); + useEffect(() => { + setCollection( + collections.find( + (e) => e.id === link.collection.id + ) as CollectionIncludingMembersAndLinkCount + ); + }, [collections, links]); - const { account } = useAccountStore(); + let shortendURL; - let shortendURL; - - try { - shortendURL = new URL(link.url || "").host.toLowerCase(); - } catch (error) { - console.log(error); - } - - const [collection, setCollection] = - useState( - collections.find( - (e) => e.id === link.collection.id - ) as CollectionIncludingMembersAndLinkCount - ); - - useEffect(() => { - setCollection( - collections.find( - (e) => e.id === link.collection.id - ) as CollectionIncludingMembersAndLinkCount - ); - }, [collections, links]); - - const { removeLink, updateLink } = useLinkStore(); - - const pinLink = async () => { - const isAlreadyPinned = link?.pinnedBy && link.pinnedBy[0]; - - const load = toast.loading("Applying..."); - - const response = await updateLink({ - ...link, - pinnedBy: isAlreadyPinned ? undefined : [{ id: account.id }], - }); - - toast.dismiss(load); - - response.ok && - toast.success(`Link ${isAlreadyPinned ? "Unpinned!" : "Pinned!"}`); - }; - - const deleteLink = async () => { - const load = toast.loading("Deleting..."); - - const response = await removeLink(link.id as number); - - toast.dismiss(load); - - response.ok && toast.success(`Link Deleted.`); - }; - - const url = - isValidUrl(link.url || "") && link.url ? new URL(link.url) : undefined; - - const formattedDate = new Date(link.createdAt as string).toLocaleString( - "en-US", - { - year: "numeric", - month: "short", - day: "numeric", + try { + shortendURL = new URL(link.url || "").host.toLowerCase(); + } catch (error) { + console.log(error); } - ); - const [editLinkModal, setEditLinkModal] = useState(false); - const [deleteLinkModal, setDeleteLinkModal] = useState(false); - const [preservedFormatsModal, setPreservedFormatsModal] = useState(false); - - return ( -
- {permissions === true || - permissions?.canUpdate || - permissions?.canDelete ? ( -
-
- -
-
    - {permissions === true ? ( -
  • -
    { - (document?.activeElement as HTMLElement)?.blur(); - pinLink(); - }} - > - {link?.pinnedBy && link.pinnedBy[0] - ? "Unpin" - : "Pin to Dashboard"} -
    -
  • - ) : undefined} - {permissions === true || permissions?.canUpdate ? ( -
  • -
    { - (document?.activeElement as HTMLElement)?.blur(); - setEditLinkModal(true); - }} - > - Edit -
    -
  • - ) : undefined} - {permissions === true ? ( -
  • -
    { - (document?.activeElement as HTMLElement)?.blur(); - setPreservedFormatsModal(true); - // updateArchive(); - }} - > - Preserved Formats -
    -
  • - ) : undefined} - {permissions === true || permissions?.canDelete ? ( -
  • -
    { - (document?.activeElement as HTMLElement)?.blur(); - e.shiftKey ? deleteLink() : setDeleteLinkModal(true); - }} - > - Delete -
    -
  • - ) : undefined} -
-
- ) : undefined} - -
link.url && window.open(link.url || "", "_blank")} - className="flex flex-col justify-between cursor-pointer h-full w-full gap-1 p-3" - > - {link.url && url ? ( - { - const target = e.target as HTMLElement; - target.style.display = "none"; - }} - /> - ) : link.type === "pdf" ? ( - - ) : link.type === "image" ? ( - - ) : undefined} - -
-

{count + 1}

-

- {unescapeString(link.name || link.description) || link.url} -

-
- - {link.url ? ( -
- -

{shortendURL}

-
- ) : ( -
{link.type}
- )} + const url = + isValidUrl(link.url || "") && link.url ? new URL(link.url) : undefined; + return (
{ - e.stopPropagation(); - router.push(`/collections/${link.collection.id}`); - }} - className="flex items-center gap-1 max-w-full w-fit hover:opacity-70 duration-100" + className={`h-fit hover:bg-base-300 hover:border-base-300 border border-solid border-neutral-content bg-base-200 shadow-md hover:shadow-none duration-200 rounded-2xl relative ${ + className || "" + }`} > - -

{collection?.name}

-
+
link.url && window.open(link.url || "", "_blank")} + className="flex flex-col justify-between cursor-pointer h-full w-full gap-1 p-3" + > + {link.url && url ? ( + { + const target = e.target as HTMLElement; + target.style.display = "none"; + }} + /> + ) : link.type === "pdf" ? ( + + ) : link.type === "image" ? ( + + ) : undefined} -
- -

{formattedDate}

-
- {/* {link.tags[0] ? ( -
-
- {link.tags.map((e, i) => ( - { - e.stopPropagation(); - }} - className="btn btn-xs btn-ghost truncate max-w-[19rem]" - > - #{e.name} - - ))} +
+

{count + 1}

+

+ {unescapeString(link.name || link.description) || link.url} +

-
-
- ) : ( -

No Tags

- )} */} -
- {editLinkModal ? ( - setEditLinkModal(false)} - activeLink={link} - /> - ) : undefined} - {deleteLinkModal ? ( - setDeleteLinkModal(false)} - activeLink={link} - /> - ) : undefined} - {preservedFormatsModal ? ( - setPreservedFormatsModal(false)} - activeLink={link} - /> - ) : undefined} -
- ); + + {link.url ? ( +
+ +

{shortendURL}

+
+ ) : ( +
{link.type}
+ )} + + + + + {/* {link.tags[0] ? ( +
+
+{link.tags.map((e, i) => ( + { + e.stopPropagation(); + }} + className="btn btn-xs btn-ghost truncate max-w-[19rem]" +> + #{e.name} + +))} +
+
+
+) : ( +

No Tags

+)} */} +
+ + +
+ ); } diff --git a/components/LinkViews/CompactGridView.tsx b/components/LinkViews/CompactGridView.tsx new file mode 100644 index 00000000..55cec7bc --- /dev/null +++ b/components/LinkViews/CompactGridView.tsx @@ -0,0 +1,16 @@ +import LinkCardCompact from "@/components/LinkViews/LinkCardCompact"; +import { LinkIncludingShortenedCollectionAndTags } from "@/types/global"; + +export default function CompactGridView({ + links, +}: { + links: LinkIncludingShortenedCollectionAndTags[]; +}) { + return ( +
+ {links.map((e, i) => { + return ; + })} +
+ ); +} diff --git a/components/LinkViews/DefaultGridView.tsx b/components/LinkViews/DefaultGridView.tsx new file mode 100644 index 00000000..7dbbc793 --- /dev/null +++ b/components/LinkViews/DefaultGridView.tsx @@ -0,0 +1,16 @@ +import LinkCard from "@/components/LinkCard"; +import { LinkIncludingShortenedCollectionAndTags } from "@/types/global"; + +export default function DefaultGridView({ + links, +}: { + links: LinkIncludingShortenedCollectionAndTags[]; +}) { + return ( +
+ {links.map((e, i) => { + return ; + })} +
+ ); +} diff --git a/components/LinkViews/LinkCardCompact.tsx b/components/LinkViews/LinkCardCompact.tsx new file mode 100644 index 00000000..841e19a0 --- /dev/null +++ b/components/LinkViews/LinkCardCompact.tsx @@ -0,0 +1,102 @@ +import { + CollectionIncludingMembersAndLinkCount, + LinkIncludingShortenedCollectionAndTags, +} from "@/types/global"; +import { useEffect, useState } from "react"; +import useLinkStore from "@/store/links"; +import useCollectionStore from "@/store/collections"; +import Link from "next/link"; +import unescapeString from "@/lib/client/unescapeString"; +import LinkActions from "@/components/LinkViews/LinkComponents/LinkActions"; +import LinkDate from "@/components/LinkViews/LinkComponents/LinkDate"; +import LinkCollection from "@/components/LinkViews/LinkComponents/LinkCollection"; +import LinkIcon from "@/components/LinkViews/LinkComponents/LinkIcon"; + +type Props = { + link: LinkIncludingShortenedCollectionAndTags; + count: number; + className?: string; +}; + +export default function LinkCardCompact({ link, count, className }: Props) { + const { collections } = useCollectionStore(); + + const { links } = useLinkStore(); + + let shortendURL; + + try { + shortendURL = new URL(link.url || "").host.toLowerCase(); + } catch (error) { + console.log(error); + } + + const [collection, setCollection] = + useState( + collections.find( + (e) => e.id === link.collection.id, + ) as CollectionIncludingMembersAndLinkCount, + ); + + useEffect(() => { + setCollection( + collections.find( + (e) => e.id === link.collection.id, + ) as CollectionIncludingMembersAndLinkCount, + ); + }, [collections, links]); + + return ( +
+
+
+
+ +
+ + +
+
+

{count + 1}

+

+ {unescapeString(link.name || link.description) || link.url} +

+
+ + {link.url ? ( +
{ + e.preventDefault(); + window.open(link.url || "", "_blank"); + }} + className="flex items-center gap-1 max-w-full w-fit text-xs text-neutral hover:opacity-60 duration-100" + > +

{shortendURL}

+
+ ) : ( +
+ {link.type} +
+ )} +
+ +
+ + · + +
+ +
+
+ + +
+ ); +} diff --git a/components/LinkViews/LinkComponents/LinkActions.tsx b/components/LinkViews/LinkComponents/LinkActions.tsx new file mode 100644 index 00000000..ef4e6c74 --- /dev/null +++ b/components/LinkViews/LinkComponents/LinkActions.tsx @@ -0,0 +1,165 @@ +import { useState } from "react"; +import { + CollectionIncludingMembersAndLinkCount, + LinkIncludingShortenedCollectionAndTags, +} from "@/types/global"; +import usePermissions from "@/hooks/usePermissions"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { faEllipsis } from "@fortawesome/free-solid-svg-icons"; +import EditLinkModal from "@/components/ModalContent/EditLinkModal"; +import DeleteLinkModal from "@/components/ModalContent/DeleteLinkModal"; +import PreservedFormatsModal from "@/components/ModalContent/PreservedFormatsModal"; +import useLinkStore from "@/store/links"; +import { toast } from "react-hot-toast"; +import useAccountStore from "@/store/account"; + +export default function LinkActions({ + link, + collection, +}: { + link: LinkIncludingShortenedCollectionAndTags; + collection: CollectionIncludingMembersAndLinkCount; +}) { + const permissions = usePermissions(link.collection.id as number); + + const [editLinkModal, setEditLinkModal] = useState(false); + const [deleteLinkModal, setDeleteLinkModal] = useState(false); + const [preservedFormatsModal, setPreservedFormatsModal] = useState(false); + const [expandedLink, setExpandedLink] = useState(false); + + const { account } = useAccountStore(); + + const { removeLink, updateLink } = useLinkStore(); + + const pinLink = async () => { + const isAlreadyPinned = link?.pinnedBy && link.pinnedBy[0]; + + const load = toast.loading("Applying..."); + + const response = await updateLink({ + ...link, + pinnedBy: isAlreadyPinned ? undefined : [{ id: account.id }], + }); + + toast.dismiss(load); + + response.ok && + toast.success(`Link ${isAlreadyPinned ? "Unpinned!" : "Pinned!"}`); + }; + + const deleteLink = async () => { + const load = toast.loading("Deleting..."); + + const response = await removeLink(link.id as number); + + toast.dismiss(load); + + response.ok && toast.success(`Link Deleted.`); + }; + + return ( +
+ {permissions === true || + permissions?.canUpdate || + permissions?.canDelete ? ( +
+
+ +
+
    + {permissions === true ? ( +
  • +
    { + (document?.activeElement as HTMLElement)?.blur(); + pinLink(); + }} + > + {link?.pinnedBy && link.pinnedBy[0] + ? "Unpin" + : "Pin to Dashboard"} +
    +
  • + ) : undefined} + {permissions === true || permissions?.canUpdate ? ( +
  • +
    { + (document?.activeElement as HTMLElement)?.blur(); + setEditLinkModal(true); + }} + > + Edit +
    +
  • + ) : undefined} + {permissions === true ? ( +
  • +
    { + (document?.activeElement as HTMLElement)?.blur(); + setPreservedFormatsModal(true); + // updateArchive(); + }} + > + Preserved Formats +
    +
  • + ) : undefined} + {permissions === true || permissions?.canDelete ? ( +
  • +
    { + (document?.activeElement as HTMLElement)?.blur(); + e.shiftKey ? deleteLink() : setDeleteLinkModal(true); + }} + > + Delete +
    +
  • + ) : undefined} +
+
+ ) : undefined} + + {editLinkModal ? ( + setEditLinkModal(false)} + activeLink={link} + /> + ) : undefined} + {deleteLinkModal ? ( + setDeleteLinkModal(false)} + activeLink={link} + /> + ) : undefined} + {preservedFormatsModal ? ( + setPreservedFormatsModal(false)} + activeLink={link} + /> + ) : undefined} + {/* {expandedLink ? ( + setExpandedLink(false)} link={link} /> + ) : undefined} */} +
+ ); +} diff --git a/components/LinkViews/LinkComponents/LinkCollection.tsx b/components/LinkViews/LinkComponents/LinkCollection.tsx new file mode 100644 index 00000000..606352a0 --- /dev/null +++ b/components/LinkViews/LinkComponents/LinkCollection.tsx @@ -0,0 +1,34 @@ +import { + CollectionIncludingMembersAndLinkCount, + LinkIncludingShortenedCollectionAndTags, +} from "@/types/global"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { faFolder } from "@fortawesome/free-solid-svg-icons"; +import { useRouter } from "next/router"; + +export default function LinkCollection({ + link, + collection, +}: { + link: LinkIncludingShortenedCollectionAndTags; + collection: CollectionIncludingMembersAndLinkCount; +}) { + const router = useRouter(); + + return ( +
{ + e.preventDefault(); + router.push(`/collections/${link.collection.id}`); + }} + className="flex items-center gap-1 max-w-full w-fit hover:opacity-70 duration-100" + > + +

{collection?.name}

+
+ ); +} diff --git a/components/LinkViews/LinkComponents/LinkDate.tsx b/components/LinkViews/LinkComponents/LinkDate.tsx new file mode 100644 index 00000000..282ab700 --- /dev/null +++ b/components/LinkViews/LinkComponents/LinkDate.tsx @@ -0,0 +1,25 @@ +import { LinkIncludingShortenedCollectionAndTags } from "@/types/global"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { faCalendarDays } from "@fortawesome/free-regular-svg-icons"; + +export default function LinkDate({ + link, +}: { + link: LinkIncludingShortenedCollectionAndTags; +}) { + const formattedDate = new Date(link.createdAt as string).toLocaleString( + "en-US", + { + year: "numeric", + month: "short", + day: "numeric", + }, + ); + + return ( +
+ +

{formattedDate}

+
+ ); +} diff --git a/components/LinkViews/LinkComponents/LinkIcon.tsx b/components/LinkViews/LinkComponents/LinkIcon.tsx new file mode 100644 index 00000000..0e19d53a --- /dev/null +++ b/components/LinkViews/LinkComponents/LinkIcon.tsx @@ -0,0 +1,45 @@ +import { LinkIncludingShortenedCollectionAndTags } from "@/types/global"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { faFileImage, faFilePdf } from "@fortawesome/free-regular-svg-icons"; +import Image from "next/image"; +import isValidUrl from "@/lib/shared/isValidUrl"; + +export default function LinkIcon({ + link, +}: { + link: LinkIncludingShortenedCollectionAndTags; +}) { + const url = + isValidUrl(link.url || "") && link.url ? new URL(link.url) : undefined; + + const iconClasses: string = "w-20 bg-primary/20 text-primary shadow rounded-md p-2 select-none z-10"; + + return ( +
+ {link.url && url ? ( + { + const target = e.target as HTMLElement; + target.style.display = "none"; + }} + /> + ) : link.type === "pdf" ? ( + + ) : link.type === "image" ? ( + + ) : undefined} +
+ ); +} diff --git a/components/LinkViews/LinkRow.tsx b/components/LinkViews/LinkRow.tsx new file mode 100644 index 00000000..9dcbe226 --- /dev/null +++ b/components/LinkViews/LinkRow.tsx @@ -0,0 +1,172 @@ +import { + CollectionIncludingMembersAndLinkCount, + LinkIncludingShortenedCollectionAndTags, +} from "@/types/global"; +import { + faFolder, + faEllipsis, + faLink, +} from "@fortawesome/free-solid-svg-icons"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { useEffect, useState } from "react"; +import Image from "next/image"; +import useLinkStore from "@/store/links"; +import useCollectionStore from "@/store/collections"; +import useAccountStore from "@/store/account"; +import { + faCalendarDays, + faFileImage, + faFilePdf, +} from "@fortawesome/free-regular-svg-icons"; +import usePermissions from "@/hooks/usePermissions"; +import { toast } from "react-hot-toast"; +import isValidUrl from "@/lib/shared/isValidUrl"; +import Link from "next/link"; +import unescapeString from "@/lib/client/unescapeString"; +import { useRouter } from "next/router"; +import EditLinkModal from "../ModalContent/EditLinkModal"; +import DeleteLinkModal from "../ModalContent/DeleteLinkModal"; +import ExpandedLink from "../ModalContent/ExpandedLink"; +import PreservedFormatsModal from "../ModalContent/PreservedFormatsModal"; +import LinkActions from "@/components/LinkViews/LinkComponents/LinkActions"; +import LinkDate from "@/components/LinkViews/LinkComponents/LinkDate"; +import LinkCollection from "@/components/LinkViews/LinkComponents/LinkCollection"; +import LinkIcon from "@/components/LinkViews/LinkComponents/LinkIcon"; + +type Props = { + link: LinkIncludingShortenedCollectionAndTags; + count: number; + className?: string; +}; + +export default function LinkRow({ link, count, className }: Props) { + const router = useRouter(); + + const permissions = usePermissions(link.collection.id as number); + + const { collections } = useCollectionStore(); + + const { links } = useLinkStore(); + + const { account } = useAccountStore(); + + let shortendURL; + + try { + shortendURL = new URL(link.url || "").host.toLowerCase(); + } catch (error) { + console.log(error); + } + + const [collection, setCollection] = + useState( + collections.find( + (e) => e.id === link.collection.id, + ) as CollectionIncludingMembersAndLinkCount, + ); + + useEffect(() => { + setCollection( + collections.find( + (e) => e.id === link.collection.id, + ) as CollectionIncludingMembersAndLinkCount, + ); + }, [collections, links]); + + const { removeLink, updateLink } = useLinkStore(); + + const pinLink = async () => { + const isAlreadyPinned = link?.pinnedBy && link.pinnedBy[0]; + + const load = toast.loading("Applying..."); + + const response = await updateLink({ + ...link, + pinnedBy: isAlreadyPinned ? undefined : [{ id: account.id }], + }); + + toast.dismiss(load); + + response.ok && + toast.success(`Link ${isAlreadyPinned ? "Unpinned!" : "Pinned!"}`); + }; + + const deleteLink = async () => { + const load = toast.loading("Deleting..."); + + const response = await removeLink(link.id as number); + + toast.dismiss(load); + + response.ok && toast.success(`Link Deleted.`); + }; + + const url = + isValidUrl(link.url || "") && link.url ? new URL(link.url) : undefined; + + const formattedDate = new Date(link.createdAt as string).toLocaleString( + "en-US", + { + year: "numeric", + month: "short", + day: "numeric", + }, + ); + + const iconClasses: string = + "w-20 bg-white shadow p-1 bottom-3 right-3 select-none z-10"; + + return ( +
+
+
+ + + + +
+ +

+ {unescapeString(link.name || link.description) || link.url} +

+ + +
+ + · + {link.url ? ( +
{ + e.preventDefault(); + window.open(link.url || "", "_blank"); + }} + className="flex items-center hover:opacity-60 cursor-pointer duration-100" + > +

{shortendURL}

+
+ ) : ( +
+ {link.type} +
+ )} + · + +
+
+
+
+ + +
+ ); +} diff --git a/components/LinkViews/ListView.tsx b/components/LinkViews/ListView.tsx new file mode 100644 index 00000000..06a9703c --- /dev/null +++ b/components/LinkViews/ListView.tsx @@ -0,0 +1,24 @@ +import LinkRow from "@/components/LinkViews/LinkRow"; +import { LinkIncludingShortenedCollectionAndTags } from "@/types/global"; + +export default function ListView({ + links, +}: { + links: LinkIncludingShortenedCollectionAndTags[]; +}) { + return ( +
+
+ {links.map((e, i) => { + return ; + })} + {links.map((e, i) => { + return ; + })} + {links.map((e, i) => { + return ; + })} +
+
+ ); +} diff --git a/components/SortDropdown.tsx b/components/SortDropdown.tsx index 7aebd568..e125e6b9 100644 --- a/components/SortDropdown.tsx +++ b/components/SortDropdown.tsx @@ -16,11 +16,14 @@ export default function SortDropdown({ sortBy, setSort }: Props) { role="button" className="btn btn-sm btn-square btn-ghost" > - + viewBox="0 0 24 24" + fill="none" + stroke="currentColor" + > + +
  • diff --git a/components/ViewDropdown.tsx b/components/ViewDropdown.tsx new file mode 100644 index 00000000..df2ffcd9 --- /dev/null +++ b/components/ViewDropdown.tsx @@ -0,0 +1,62 @@ +import React, { Dispatch, SetStateAction } from "react"; +import { ViewMode } from "@/types/global"; + +type Props = { + viewMode: ViewMode; + setViewMode: Dispatch>; +}; + +export default function ViewDropdown({ viewMode, setViewMode }: Props) { + let viewModes: Array<{ mode: ViewMode; name: string }> = [ + { mode: ViewMode.Default, name: "Default" }, + { mode: ViewMode.Compact, name: "Grid" }, + { mode: ViewMode.List, name: "List" }, + ]; + + return ( +
    + + + +
    + ); +} diff --git a/pages/links/index.tsx b/pages/links/index.tsx index 6364e40f..c7f31ba7 100644 --- a/pages/links/index.tsx +++ b/pages/links/index.tsx @@ -4,18 +4,31 @@ import SortDropdown from "@/components/SortDropdown"; import useLinks from "@/hooks/useLinks"; import MainLayout from "@/layouts/MainLayout"; import useLinkStore from "@/store/links"; -import { Sort } from "@/types/global"; +import { Sort, ViewMode } from "@/types/global"; import { faLink } from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { useState } from "react"; +import ViewDropdown from "@/components/ViewDropdown"; +import DefaultGridView from "@/components/LinkViews/DefaultGridView"; +import CompactGridView from "@/components/LinkViews/CompactGridView"; +import ListView from "@/components/LinkViews/ListView"; export default function Links() { const { links } = useLinkStore(); + const [viewMode, setViewMode] = useState(ViewMode.Default); const [sortBy, setSortBy] = useState(Sort.DateNewestFirst); useLinks({ sort: sortBy }); + const components = { + [ViewMode.Default]: DefaultGridView, + [ViewMode.Compact]: CompactGridView, + [ViewMode.List]: ListView, + }; + + const Component = components[viewMode]; + return (
    @@ -32,16 +45,13 @@ export default function Links() {
    -
    +
    +
    {links[0] ? ( -
    - {links.map((e, i) => { - return ; - })} -
    + ) : ( )} diff --git a/types/global.ts b/types/global.ts index c1d1893b..e1331f36 100644 --- a/types/global.ts +++ b/types/global.ts @@ -57,6 +57,12 @@ export interface PublicCollectionIncludingLinks extends Collection { links: LinksIncludingTags[]; } +export enum ViewMode { + Default, + Compact, + List, +} + export enum Sort { DateNewestFirst, DateOldestFirst, From a2b1513dbc43561e8290d272bea43efe23b09eb5 Mon Sep 17 00:00:00 2001 From: Yee Jia Wei Date: Sat, 16 Dec 2023 11:52:50 +0800 Subject: [PATCH 02/10] remove duplicated link --- components/LinkViews/ListView.tsx | 6 ------ 1 file changed, 6 deletions(-) diff --git a/components/LinkViews/ListView.tsx b/components/LinkViews/ListView.tsx index 06a9703c..8dabb72a 100644 --- a/components/LinkViews/ListView.tsx +++ b/components/LinkViews/ListView.tsx @@ -12,12 +12,6 @@ export default function ListView({ {links.map((e, i) => { return ; })} - {links.map((e, i) => { - return ; - })} - {links.map((e, i) => { - return ; - })} ); From b246cdbc44c09bd2a1519861924cb0d63e71d39b Mon Sep 17 00:00:00 2001 From: Yee Jia Wei Date: Sat, 16 Dec 2023 12:05:19 +0800 Subject: [PATCH 03/10] change link icon to component --- components/LinkCard.tsx | 28 ++++------------------------ 1 file changed, 4 insertions(+), 24 deletions(-) diff --git a/components/LinkCard.tsx b/components/LinkCard.tsx index d530813f..8591f228 100644 --- a/components/LinkCard.tsx +++ b/components/LinkCard.tsx @@ -17,6 +17,7 @@ import unescapeString from "@/lib/client/unescapeString"; import LinkActions from "@/components/LinkViews/LinkComponents/LinkActions"; import LinkDate from "@/components/LinkViews/LinkComponents/LinkDate"; import LinkCollection from "@/components/LinkViews/LinkComponents/LinkCollection"; +import LinkIcon from "@/components/LinkViews/LinkComponents/LinkIcon"; type Props = { link: LinkIncludingShortenedCollectionAndTags; @@ -65,30 +66,9 @@ export default function LinkCard({link, count, className}: Props) { onClick={() => link.url && window.open(link.url || "", "_blank")} className="flex flex-col justify-between cursor-pointer h-full w-full gap-1 p-3" > - {link.url && url ? ( - { - const target = e.target as HTMLElement; - target.style.display = "none"; - }} - /> - ) : link.type === "pdf" ? ( - - ) : link.type === "image" ? ( - - ) : undefined} +
    + +

    {count + 1}

    From ce9b4b05d4d0ca297c4872ba56e637b366bd70be Mon Sep 17 00:00:00 2001 From: Yee Jia Wei Date: Sat, 16 Dec 2023 12:05:35 +0800 Subject: [PATCH 04/10] smaller link icon size --- components/LinkViews/LinkComponents/LinkIcon.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/LinkViews/LinkComponents/LinkIcon.tsx b/components/LinkViews/LinkComponents/LinkIcon.tsx index 0e19d53a..49c9e1f8 100644 --- a/components/LinkViews/LinkComponents/LinkIcon.tsx +++ b/components/LinkViews/LinkComponents/LinkIcon.tsx @@ -12,7 +12,7 @@ export default function LinkIcon({ const url = isValidUrl(link.url || "") && link.url ? new URL(link.url) : undefined; - const iconClasses: string = "w-20 bg-primary/20 text-primary shadow rounded-md p-2 select-none z-10"; + const iconClasses: string = "w-12 bg-primary/20 text-primary shadow rounded-md p-2 select-none z-10"; return (
    From 7c35fe409fa638b7361dd541ec9b4e6c6c36ec9e Mon Sep 17 00:00:00 2001 From: Yee Jia Wei Date: Sat, 16 Dec 2023 12:08:28 +0800 Subject: [PATCH 05/10] Update LinkCard.tsx --- components/LinkCard.tsx | 137 +++++++++++++++++++--------------------- 1 file changed, 66 insertions(+), 71 deletions(-) diff --git a/components/LinkCard.tsx b/components/LinkCard.tsx index 8591f228..1a9c80e9 100644 --- a/components/LinkCard.tsx +++ b/components/LinkCard.tsx @@ -1,17 +1,12 @@ import { - CollectionIncludingMembersAndLinkCount, - LinkIncludingShortenedCollectionAndTags, + CollectionIncludingMembersAndLinkCount, + LinkIncludingShortenedCollectionAndTags, } from "@/types/global"; -import {faLink} from "@fortawesome/free-solid-svg-icons"; -import {FontAwesomeIcon} from "@fortawesome/react-fontawesome"; -import {useEffect, useState} from "react"; -import Image from "next/image"; +import { faLink } from "@fortawesome/free-solid-svg-icons"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { useEffect, useState } from "react"; import useLinkStore from "@/store/links"; import useCollectionStore from "@/store/collections"; -import { - faFileImage, - faFilePdf, -} from "@fortawesome/free-regular-svg-icons"; import isValidUrl from "@/lib/shared/isValidUrl"; import unescapeString from "@/lib/client/unescapeString"; import LinkActions from "@/components/LinkViews/LinkComponents/LinkActions"; @@ -20,76 +15,76 @@ import LinkCollection from "@/components/LinkViews/LinkComponents/LinkCollection import LinkIcon from "@/components/LinkViews/LinkComponents/LinkIcon"; type Props = { - link: LinkIncludingShortenedCollectionAndTags; - count: number; - className?: string; + link: LinkIncludingShortenedCollectionAndTags; + count: number; + className?: string; }; -export default function LinkCard({link, count, className}: Props) { - const {links} = useLinkStore(); +export default function LinkCard({ link, count, className }: Props) { + const { links } = useLinkStore(); - const {collections} = useCollectionStore(); + const { collections } = useCollectionStore(); - const [collection, setCollection] = - useState( - collections.find( - (e) => e.id === link.collection.id - ) as CollectionIncludingMembersAndLinkCount - ); + const [collection, setCollection] = + useState( + collections.find( + (e) => e.id === link.collection.id, + ) as CollectionIncludingMembersAndLinkCount, + ); - useEffect(() => { - setCollection( - collections.find( - (e) => e.id === link.collection.id - ) as CollectionIncludingMembersAndLinkCount - ); - }, [collections, links]); + useEffect(() => { + setCollection( + collections.find( + (e) => e.id === link.collection.id, + ) as CollectionIncludingMembersAndLinkCount, + ); + }, [collections, links]); - let shortendURL; + let shortendURL; - try { - shortendURL = new URL(link.url || "").host.toLowerCase(); - } catch (error) { - console.log(error); - } + try { + shortendURL = new URL(link.url || "").host.toLowerCase(); + } catch (error) { + console.log(error); + } - const url = - isValidUrl(link.url || "") && link.url ? new URL(link.url) : undefined; + const url = + isValidUrl(link.url || "") && link.url ? new URL(link.url) : undefined; - return ( -
    -
    link.url && window.open(link.url || "", "_blank")} - className="flex flex-col justify-between cursor-pointer h-full w-full gap-1 p-3" - > -
    - -
    + return ( +
    +
    link.url && window.open(link.url || "", "_blank")} + className="flex flex-col justify-between cursor-pointer h-full w-full gap-1 p-3" + > +
    + +
    -
    -

    {count + 1}

    -

    - {unescapeString(link.name || link.description) || link.url} -

    -
    +
    +

    {count + 1}

    +

    + {unescapeString(link.name || link.description) || link.url} +

    +
    - {link.url ? ( -
    - -

    {shortendURL}

    -
    - ) : ( -
    {link.type}
    - )} + {link.url ? ( +
    + +

    {shortendURL}

    +
    + ) : ( +
    {link.type}
    + )} - + - - {/* {link.tags[0] ? ( + + {/* {link.tags[0] ? (
    {link.tags.map((e, i) => ( @@ -110,9 +105,9 @@ export default function LinkCard({link, count, className}: Props) { ) : (

    No Tags

    )} */} -
    +
    - -
    - ); + +
    + ); } From e0bb7ffa080e38d72c20ddc60b4a0ebafe5d3e8c Mon Sep 17 00:00:00 2001 From: Yee Jia Wei Date: Sat, 16 Dec 2023 12:16:56 +0800 Subject: [PATCH 06/10] update responsive --- components/LinkViews/CompactGridView.tsx | 2 +- .../{ => LinkComponents}/LinkCardCompact.tsx | 50 ++--- .../LinkViews/LinkComponents/LinkIcon.tsx | 45 ++--- .../LinkViews/LinkComponents/LinkRow.tsx | 92 ++++++++++ components/LinkViews/LinkRow.tsx | 172 ------------------ components/LinkViews/ListView.tsx | 2 +- 6 files changed, 134 insertions(+), 229 deletions(-) rename components/LinkViews/{ => LinkComponents}/LinkCardCompact.tsx (64%) create mode 100644 components/LinkViews/LinkComponents/LinkRow.tsx delete mode 100644 components/LinkViews/LinkRow.tsx diff --git a/components/LinkViews/CompactGridView.tsx b/components/LinkViews/CompactGridView.tsx index 55cec7bc..f71ac5ca 100644 --- a/components/LinkViews/CompactGridView.tsx +++ b/components/LinkViews/CompactGridView.tsx @@ -1,4 +1,4 @@ -import LinkCardCompact from "@/components/LinkViews/LinkCardCompact"; +import LinkCardCompact from "@/components/LinkViews/LinkComponents/LinkCardCompact"; import { LinkIncludingShortenedCollectionAndTags } from "@/types/global"; export default function CompactGridView({ diff --git a/components/LinkViews/LinkCardCompact.tsx b/components/LinkViews/LinkComponents/LinkCardCompact.tsx similarity index 64% rename from components/LinkViews/LinkCardCompact.tsx rename to components/LinkViews/LinkComponents/LinkCardCompact.tsx index 841e19a0..2e6f60ef 100644 --- a/components/LinkViews/LinkCardCompact.tsx +++ b/components/LinkViews/LinkComponents/LinkCardCompact.tsx @@ -5,7 +5,6 @@ import { import { useEffect, useState } from "react"; import useLinkStore from "@/store/links"; import useCollectionStore from "@/store/collections"; -import Link from "next/link"; import unescapeString from "@/lib/client/unescapeString"; import LinkActions from "@/components/LinkViews/LinkComponents/LinkActions"; import LinkDate from "@/components/LinkViews/LinkComponents/LinkDate"; @@ -47,36 +46,31 @@ export default function LinkCardCompact({ link, count, className }: Props) { }, [collections, links]); return ( -
    -
    -
    -
    - -
    +
    +
    link.url && window.open(link.url || "", "_blank")} + className="flex items-center cursor-pointer p-3" + > +
    + +
    - -
    -
    -

    {count + 1}

    -

    - {unescapeString(link.name || link.description) || link.url} -

    -
    +
    +

    + {unescapeString(link.name || link.description) || link.url} +

    +
    +
    + + · {link.url ? (
    { e.preventDefault(); window.open(link.url || "", "_blank"); }} - className="flex items-center gap-1 max-w-full w-fit text-xs text-neutral hover:opacity-60 duration-100" + className="flex items-center hover:opacity-60 cursor-pointer duration-100" >

    {shortendURL}

    @@ -86,13 +80,9 @@ export default function LinkCardCompact({ link, count, className }: Props) {
    )}
    - -
    - - · - -
    - + · + +
    diff --git a/components/LinkViews/LinkComponents/LinkIcon.tsx b/components/LinkViews/LinkComponents/LinkIcon.tsx index 49c9e1f8..e9d08cbd 100644 --- a/components/LinkViews/LinkComponents/LinkIcon.tsx +++ b/components/LinkViews/LinkComponents/LinkIcon.tsx @@ -12,34 +12,29 @@ export default function LinkIcon({ const url = isValidUrl(link.url || "") && link.url ? new URL(link.url) : undefined; - const iconClasses: string = "w-12 bg-primary/20 text-primary shadow rounded-md p-2 select-none z-10"; + const iconClasses: string = + "w-12 bg-primary/20 text-primary shadow rounded-md p-2 select-none z-10"; return (
    - {link.url && url ? ( - { - const target = e.target as HTMLElement; - target.style.display = "none"; - }} - /> - ) : link.type === "pdf" ? ( - - ) : link.type === "image" ? ( - - ) : undefined} + {link.url && url ? ( + { + const target = e.target as HTMLElement; + target.style.display = "none"; + }} + /> + ) : link.type === "pdf" ? ( + + ) : link.type === "image" ? ( + + ) : undefined}
    ); } diff --git a/components/LinkViews/LinkComponents/LinkRow.tsx b/components/LinkViews/LinkComponents/LinkRow.tsx new file mode 100644 index 00000000..2c2f5c93 --- /dev/null +++ b/components/LinkViews/LinkComponents/LinkRow.tsx @@ -0,0 +1,92 @@ +import { + CollectionIncludingMembersAndLinkCount, + LinkIncludingShortenedCollectionAndTags, +} from "@/types/global"; +import { useEffect, useState } from "react"; +import useLinkStore from "@/store/links"; +import useCollectionStore from "@/store/collections"; +import unescapeString from "@/lib/client/unescapeString"; +import LinkActions from "@/components/LinkViews/LinkComponents/LinkActions"; +import LinkDate from "@/components/LinkViews/LinkComponents/LinkDate"; +import LinkCollection from "@/components/LinkViews/LinkComponents/LinkCollection"; +import LinkIcon from "@/components/LinkViews/LinkComponents/LinkIcon"; + +type Props = { + link: LinkIncludingShortenedCollectionAndTags; + count: number; + className?: string; +}; + +export default function LinkCardCompact({ link, count, className }: Props) { + const { collections } = useCollectionStore(); + + const { links } = useLinkStore(); + + let shortendURL; + + try { + shortendURL = new URL(link.url || "").host.toLowerCase(); + } catch (error) { + console.log(error); + } + + const [collection, setCollection] = + useState( + collections.find( + (e) => e.id === link.collection.id, + ) as CollectionIncludingMembersAndLinkCount, + ); + + useEffect(() => { + setCollection( + collections.find( + (e) => e.id === link.collection.id, + ) as CollectionIncludingMembersAndLinkCount, + ); + }, [collections, links]); + + return ( +
    +
    link.url && window.open(link.url || "", "_blank")} + className="flex items-center cursor-pointer p-3" + > +
    + +
    + +
    +

    + {unescapeString(link.name || link.description) || link.url} +

    + +
    +
    + + · + {link.url ? ( +
    { + e.preventDefault(); + window.open(link.url || "", "_blank"); + }} + className="flex items-center hover:opacity-60 cursor-pointer duration-100" + > +

    {shortendURL}

    +
    + ) : ( +
    + {link.type} +
    + )} +
    + · + +
    +
    +
    + + +
    + ); +} diff --git a/components/LinkViews/LinkRow.tsx b/components/LinkViews/LinkRow.tsx deleted file mode 100644 index 9dcbe226..00000000 --- a/components/LinkViews/LinkRow.tsx +++ /dev/null @@ -1,172 +0,0 @@ -import { - CollectionIncludingMembersAndLinkCount, - LinkIncludingShortenedCollectionAndTags, -} from "@/types/global"; -import { - faFolder, - faEllipsis, - faLink, -} from "@fortawesome/free-solid-svg-icons"; -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { useEffect, useState } from "react"; -import Image from "next/image"; -import useLinkStore from "@/store/links"; -import useCollectionStore from "@/store/collections"; -import useAccountStore from "@/store/account"; -import { - faCalendarDays, - faFileImage, - faFilePdf, -} from "@fortawesome/free-regular-svg-icons"; -import usePermissions from "@/hooks/usePermissions"; -import { toast } from "react-hot-toast"; -import isValidUrl from "@/lib/shared/isValidUrl"; -import Link from "next/link"; -import unescapeString from "@/lib/client/unescapeString"; -import { useRouter } from "next/router"; -import EditLinkModal from "../ModalContent/EditLinkModal"; -import DeleteLinkModal from "../ModalContent/DeleteLinkModal"; -import ExpandedLink from "../ModalContent/ExpandedLink"; -import PreservedFormatsModal from "../ModalContent/PreservedFormatsModal"; -import LinkActions from "@/components/LinkViews/LinkComponents/LinkActions"; -import LinkDate from "@/components/LinkViews/LinkComponents/LinkDate"; -import LinkCollection from "@/components/LinkViews/LinkComponents/LinkCollection"; -import LinkIcon from "@/components/LinkViews/LinkComponents/LinkIcon"; - -type Props = { - link: LinkIncludingShortenedCollectionAndTags; - count: number; - className?: string; -}; - -export default function LinkRow({ link, count, className }: Props) { - const router = useRouter(); - - const permissions = usePermissions(link.collection.id as number); - - const { collections } = useCollectionStore(); - - const { links } = useLinkStore(); - - const { account } = useAccountStore(); - - let shortendURL; - - try { - shortendURL = new URL(link.url || "").host.toLowerCase(); - } catch (error) { - console.log(error); - } - - const [collection, setCollection] = - useState( - collections.find( - (e) => e.id === link.collection.id, - ) as CollectionIncludingMembersAndLinkCount, - ); - - useEffect(() => { - setCollection( - collections.find( - (e) => e.id === link.collection.id, - ) as CollectionIncludingMembersAndLinkCount, - ); - }, [collections, links]); - - const { removeLink, updateLink } = useLinkStore(); - - const pinLink = async () => { - const isAlreadyPinned = link?.pinnedBy && link.pinnedBy[0]; - - const load = toast.loading("Applying..."); - - const response = await updateLink({ - ...link, - pinnedBy: isAlreadyPinned ? undefined : [{ id: account.id }], - }); - - toast.dismiss(load); - - response.ok && - toast.success(`Link ${isAlreadyPinned ? "Unpinned!" : "Pinned!"}`); - }; - - const deleteLink = async () => { - const load = toast.loading("Deleting..."); - - const response = await removeLink(link.id as number); - - toast.dismiss(load); - - response.ok && toast.success(`Link Deleted.`); - }; - - const url = - isValidUrl(link.url || "") && link.url ? new URL(link.url) : undefined; - - const formattedDate = new Date(link.createdAt as string).toLocaleString( - "en-US", - { - year: "numeric", - month: "short", - day: "numeric", - }, - ); - - const iconClasses: string = - "w-20 bg-white shadow p-1 bottom-3 right-3 select-none z-10"; - - return ( -
    -
    -
    - - - - -
    - -

    - {unescapeString(link.name || link.description) || link.url} -

    - - -
    - - · - {link.url ? ( -
    { - e.preventDefault(); - window.open(link.url || "", "_blank"); - }} - className="flex items-center hover:opacity-60 cursor-pointer duration-100" - > -

    {shortendURL}

    -
    - ) : ( -
    - {link.type} -
    - )} - · - -
    -
    -
    -
    - - -
    - ); -} diff --git a/components/LinkViews/ListView.tsx b/components/LinkViews/ListView.tsx index 8dabb72a..bb06327e 100644 --- a/components/LinkViews/ListView.tsx +++ b/components/LinkViews/ListView.tsx @@ -1,4 +1,4 @@ -import LinkRow from "@/components/LinkViews/LinkRow"; +import LinkRow from "@/components/LinkViews/LinkComponents/LinkRow"; import { LinkIncludingShortenedCollectionAndTags } from "@/types/global"; export default function ListView({ From 8df0eab2a22ba4030ac31dca711100244b629a61 Mon Sep 17 00:00:00 2001 From: Yee Jia Wei Date: Sat, 16 Dec 2023 12:57:50 +0800 Subject: [PATCH 07/10] add store to localstorage --- components/ViewDropdown.tsx | 121 +++++++++++++++++++----------------- pages/links/index.tsx | 3 +- store/localSettings.ts | 14 ++++- types/global.ts | 6 +- 4 files changed, 83 insertions(+), 61 deletions(-) diff --git a/components/ViewDropdown.tsx b/components/ViewDropdown.tsx index df2ffcd9..2d2ea4ad 100644 --- a/components/ViewDropdown.tsx +++ b/components/ViewDropdown.tsx @@ -1,62 +1,71 @@ -import React, { Dispatch, SetStateAction } from "react"; -import { ViewMode } from "@/types/global"; +import React, {Dispatch, SetStateAction, useEffect, useState} from "react"; +import useLocalSettingsStore from "@/store/localSettings"; + +import {ViewMode} from "@/types/global"; type Props = { - viewMode: ViewMode; - setViewMode: Dispatch>; + viewMode: string; + setViewMode: Dispatch>; }; -export default function ViewDropdown({ viewMode, setViewMode }: Props) { - let viewModes: Array<{ mode: ViewMode; name: string }> = [ - { mode: ViewMode.Default, name: "Default" }, - { mode: ViewMode.Compact, name: "Grid" }, - { mode: ViewMode.List, name: "List" }, - ]; +export default function ViewDropdown({viewMode, setViewMode}: Props) { + const {updateSettings} = useLocalSettingsStore(); - return ( -
    - - - -
    - ); + const onChangeViewMode = (e: React.MouseEvent, viewMode: ViewMode) => { + setViewMode(viewMode); + } + + useEffect(() => { + updateSettings({viewMode: viewMode as ViewMode}); + }, [viewMode]); + + return ( +
    + + + +
    + ); } diff --git a/pages/links/index.tsx b/pages/links/index.tsx index c7f31ba7..0c4670dd 100644 --- a/pages/links/index.tsx +++ b/pages/links/index.tsx @@ -16,7 +16,7 @@ import ListView from "@/components/LinkViews/ListView"; export default function Links() { const { links } = useLinkStore(); - const [viewMode, setViewMode] = useState(ViewMode.Default); + const [viewMode, setViewMode] = useState(localStorage.getItem('viewMode') || ViewMode.Default); const [sortBy, setSortBy] = useState(Sort.DateNewestFirst); useLinks({ sort: sortBy }); @@ -27,6 +27,7 @@ export default function Links() { [ViewMode.List]: ListView, }; + // @ts-ignore const Component = components[viewMode]; return ( diff --git a/store/localSettings.ts b/store/localSettings.ts index 1ddcdcc6..9ef126d7 100644 --- a/store/localSettings.ts +++ b/store/localSettings.ts @@ -1,7 +1,9 @@ import { create } from "zustand"; +import {ViewMode} from "@/types/global"; type LocalSettings = { - theme: string; + theme?: string; + viewMode?: string }; type LocalSettingsStore = { @@ -13,6 +15,7 @@ type LocalSettingsStore = { const useLocalSettingsStore = create((set) => ({ settings: { theme: "", + viewMode: "", }, updateSettings: async (newSettings) => { if ( @@ -26,6 +29,15 @@ const useLocalSettingsStore = create((set) => ({ document.querySelector("html")?.setAttribute("data-theme", localTheme); } + if ( + newSettings.viewMode && + newSettings.viewMode !== localStorage.getItem("viewMode") + ) { + localStorage.setItem("viewMode", newSettings.viewMode); + + // const localTheme = localStorage.getItem("viewMode") || ""; + } + set((state) => ({ settings: { ...state.settings, ...newSettings } })); }, setSettings: async () => { diff --git a/types/global.ts b/types/global.ts index e1331f36..c2356b59 100644 --- a/types/global.ts +++ b/types/global.ts @@ -58,9 +58,9 @@ export interface PublicCollectionIncludingLinks extends Collection { } export enum ViewMode { - Default, - Compact, - List, + Default = "default", + Compact = "compact", + List = "list", } export enum Sort { From f5e7e373a82351c7649b0c57c22f8ad41b2e6db0 Mon Sep 17 00:00:00 2001 From: daniel31x13 Date: Sat, 16 Dec 2023 15:06:26 -0500 Subject: [PATCH 08/10] improvements --- components/LinkViews/CompactGridView.tsx | 16 --- .../{DefaultGridView.tsx => DefaultView.tsx} | 2 +- components/LinkViews/GridView.tsx | 20 +++ .../LinkComponents}/LinkCard.tsx | 29 +---- .../{LinkCardCompact.tsx => LinkCardGrid.tsx} | 32 ++++- .../LinkComponents/LinkCollection.tsx | 4 +- .../LinkViews/LinkComponents/LinkIcon.tsx | 2 +- .../LinkViews/LinkComponents/LinkRow.tsx | 72 +++++------ components/LinkViews/ListView.tsx | 10 +- components/ViewDropdown.tsx | 122 +++++++++--------- pages/collections/[id].tsx | 2 +- pages/dashboard.tsx | 2 +- pages/links/index.tsx | 14 +- pages/links/pinned.tsx | 2 +- pages/search.tsx | 2 +- pages/tags/[id].tsx | 2 +- types/global.ts | 2 +- 17 files changed, 167 insertions(+), 168 deletions(-) delete mode 100644 components/LinkViews/CompactGridView.tsx rename components/LinkViews/{DefaultGridView.tsx => DefaultView.tsx} (84%) create mode 100644 components/LinkViews/GridView.tsx rename components/{ => LinkViews/LinkComponents}/LinkCard.tsx (79%) rename components/LinkViews/LinkComponents/{LinkCardCompact.tsx => LinkCardGrid.tsx} (69%) diff --git a/components/LinkViews/CompactGridView.tsx b/components/LinkViews/CompactGridView.tsx deleted file mode 100644 index f71ac5ca..00000000 --- a/components/LinkViews/CompactGridView.tsx +++ /dev/null @@ -1,16 +0,0 @@ -import LinkCardCompact from "@/components/LinkViews/LinkComponents/LinkCardCompact"; -import { LinkIncludingShortenedCollectionAndTags } from "@/types/global"; - -export default function CompactGridView({ - links, -}: { - links: LinkIncludingShortenedCollectionAndTags[]; -}) { - return ( -
    - {links.map((e, i) => { - return ; - })} -
    - ); -} diff --git a/components/LinkViews/DefaultGridView.tsx b/components/LinkViews/DefaultView.tsx similarity index 84% rename from components/LinkViews/DefaultGridView.tsx rename to components/LinkViews/DefaultView.tsx index 7dbbc793..714ee732 100644 --- a/components/LinkViews/DefaultGridView.tsx +++ b/components/LinkViews/DefaultView.tsx @@ -1,4 +1,4 @@ -import LinkCard from "@/components/LinkCard"; +import LinkCard from "@/components/LinkViews/LinkComponents/LinkCard"; import { LinkIncludingShortenedCollectionAndTags } from "@/types/global"; export default function DefaultGridView({ diff --git a/components/LinkViews/GridView.tsx b/components/LinkViews/GridView.tsx new file mode 100644 index 00000000..1b55f26a --- /dev/null +++ b/components/LinkViews/GridView.tsx @@ -0,0 +1,20 @@ +import LinkCardGrid from "@/components/LinkViews/LinkComponents/LinkCardGrid"; +import { LinkIncludingShortenedCollectionAndTags } from "@/types/global"; + +export default function CompactGridView({ + links, +}: { + links: LinkIncludingShortenedCollectionAndTags[]; +}) { + return ( +
    + {links.map((e, i) => { + return ( +
    + +
    + ); + })} +
    + ); +} diff --git a/components/LinkCard.tsx b/components/LinkViews/LinkComponents/LinkCard.tsx similarity index 79% rename from components/LinkCard.tsx rename to components/LinkViews/LinkComponents/LinkCard.tsx index 1a9c80e9..a1787d13 100644 --- a/components/LinkCard.tsx +++ b/components/LinkViews/LinkComponents/LinkCard.tsx @@ -28,15 +28,15 @@ export default function LinkCard({ link, count, className }: Props) { const [collection, setCollection] = useState( collections.find( - (e) => e.id === link.collection.id, - ) as CollectionIncludingMembersAndLinkCount, + (e) => e.id === link.collection.id + ) as CollectionIncludingMembersAndLinkCount ); useEffect(() => { setCollection( collections.find( - (e) => e.id === link.collection.id, - ) as CollectionIncludingMembersAndLinkCount, + (e) => e.id === link.collection.id + ) as CollectionIncludingMembersAndLinkCount ); }, [collections, links]); @@ -84,27 +84,6 @@ export default function LinkCard({ link, count, className }: Props) { - {/* {link.tags[0] ? ( -
    -
    -{link.tags.map((e, i) => ( - { - e.stopPropagation(); - }} - className="btn btn-xs btn-ghost truncate max-w-[19rem]" -> - #{e.name} - -))} -
    -
    -
    -) : ( -

    No Tags

    -)} */}
    diff --git a/components/LinkViews/LinkComponents/LinkCardCompact.tsx b/components/LinkViews/LinkComponents/LinkCardGrid.tsx similarity index 69% rename from components/LinkViews/LinkComponents/LinkCardCompact.tsx rename to components/LinkViews/LinkComponents/LinkCardGrid.tsx index 2e6f60ef..ae4c77a1 100644 --- a/components/LinkViews/LinkComponents/LinkCardCompact.tsx +++ b/components/LinkViews/LinkComponents/LinkCardGrid.tsx @@ -10,6 +10,7 @@ import LinkActions from "@/components/LinkViews/LinkComponents/LinkActions"; import LinkDate from "@/components/LinkViews/LinkComponents/LinkDate"; import LinkCollection from "@/components/LinkViews/LinkComponents/LinkCollection"; import LinkIcon from "@/components/LinkViews/LinkComponents/LinkIcon"; +import Link from "next/link"; type Props = { link: LinkIncludingShortenedCollectionAndTags; @@ -17,7 +18,7 @@ type Props = { className?: string; }; -export default function LinkCardCompact({ link, count, className }: Props) { +export default function LinkCardGrid({ link, count, className }: Props) { const { collections } = useCollectionStore(); const { links } = useLinkStore(); @@ -33,20 +34,20 @@ export default function LinkCardCompact({ link, count, className }: Props) { const [collection, setCollection] = useState( collections.find( - (e) => e.id === link.collection.id, - ) as CollectionIncludingMembersAndLinkCount, + (e) => e.id === link.collection.id + ) as CollectionIncludingMembersAndLinkCount ); useEffect(() => { setCollection( collections.find( - (e) => e.id === link.collection.id, - ) as CollectionIncludingMembersAndLinkCount, + (e) => e.id === link.collection.id + ) as CollectionIncludingMembersAndLinkCount ); }, [collections, links]); return ( -
    +
    link.url && window.open(link.url || "", "_blank")} className="flex items-center cursor-pointer p-3" @@ -83,6 +84,25 @@ export default function LinkCardCompact({ link, count, className }: Props) { ·
    +

    {unescapeString(link.description)}

    + {link.tags[0] ? ( +
    +
    + {link.tags.map((e, i) => ( + { + e.stopPropagation(); + }} + className="btn btn-xs btn-ghost truncate max-w-[19rem]" + > + #{e.name} + + ))} +
    +
    + ) : undefined}
    diff --git a/components/LinkViews/LinkComponents/LinkCollection.tsx b/components/LinkViews/LinkComponents/LinkCollection.tsx index 606352a0..16c9bb5f 100644 --- a/components/LinkViews/LinkComponents/LinkCollection.tsx +++ b/components/LinkViews/LinkComponents/LinkCollection.tsx @@ -18,14 +18,14 @@ export default function LinkCollection({ return (
    { - e.preventDefault(); + e.stopPropagation(); router.push(`/collections/${link.collection.id}`); }} className="flex items-center gap-1 max-w-full w-fit hover:opacity-70 duration-100" >

    {collection?.name}

    diff --git a/components/LinkViews/LinkComponents/LinkIcon.tsx b/components/LinkViews/LinkComponents/LinkIcon.tsx index e9d08cbd..cc5f3856 100644 --- a/components/LinkViews/LinkComponents/LinkIcon.tsx +++ b/components/LinkViews/LinkComponents/LinkIcon.tsx @@ -13,7 +13,7 @@ export default function LinkIcon({ isValidUrl(link.url || "") && link.url ? new URL(link.url) : undefined; const iconClasses: string = - "w-12 bg-primary/20 text-primary shadow rounded-md p-2 select-none z-10"; + "w-12 bg-white text-primary shadow rounded-md p-1 select-none z-10"; return (
    diff --git a/components/LinkViews/LinkComponents/LinkRow.tsx b/components/LinkViews/LinkComponents/LinkRow.tsx index 2c2f5c93..6b9fda86 100644 --- a/components/LinkViews/LinkComponents/LinkRow.tsx +++ b/components/LinkViews/LinkComponents/LinkRow.tsx @@ -33,60 +33,56 @@ export default function LinkCardCompact({ link, count, className }: Props) { const [collection, setCollection] = useState( collections.find( - (e) => e.id === link.collection.id, - ) as CollectionIncludingMembersAndLinkCount, + (e) => e.id === link.collection.id + ) as CollectionIncludingMembersAndLinkCount ); useEffect(() => { setCollection( collections.find( - (e) => e.id === link.collection.id, - ) as CollectionIncludingMembersAndLinkCount, + (e) => e.id === link.collection.id + ) as CollectionIncludingMembersAndLinkCount ); }, [collections, links]); return ( -
    -
    link.url && window.open(link.url || "", "_blank")} - className="flex items-center cursor-pointer p-3" - > -
    - -
    + <> +
    +
    link.url && window.open(link.url || "", "_blank")} + className="flex items-center cursor-pointer p-3" + > +
    + +
    -
    -

    - {unescapeString(link.name || link.description) || link.url} -

    +
    +

    + {unescapeString(link.name || link.description) || link.url} +

    -
    -
    - - · - {link.url ? ( -
    { - e.preventDefault(); - window.open(link.url || "", "_blank"); - }} - className="flex items-center hover:opacity-60 cursor-pointer duration-100" - > +
    +
    + + · + {link.url ? (

    {shortendURL}

    -
    - ) : ( -
    - {link.type} -
    - )} + ) : ( +
    + {link.type} +
    + )} +
    + · +
    - · -
    + +
    - -
    +
    + ); } diff --git a/components/LinkViews/ListView.tsx b/components/LinkViews/ListView.tsx index bb06327e..8b08add7 100644 --- a/components/LinkViews/ListView.tsx +++ b/components/LinkViews/ListView.tsx @@ -7,12 +7,10 @@ export default function ListView({ links: LinkIncludingShortenedCollectionAndTags[]; }) { return ( -
    -
    - {links.map((e, i) => { - return ; - })} -
    +
    + {links.map((e, i) => { + return ; + })}
    ); } diff --git a/components/ViewDropdown.tsx b/components/ViewDropdown.tsx index 2d2ea4ad..ac547e60 100644 --- a/components/ViewDropdown.tsx +++ b/components/ViewDropdown.tsx @@ -1,71 +1,71 @@ -import React, {Dispatch, SetStateAction, useEffect, useState} from "react"; +import React, { Dispatch, SetStateAction, useEffect, useState } from "react"; import useLocalSettingsStore from "@/store/localSettings"; -import {ViewMode} from "@/types/global"; +import { ViewMode } from "@/types/global"; type Props = { - viewMode: string; - setViewMode: Dispatch>; + viewMode: string; + setViewMode: Dispatch>; }; -export default function ViewDropdown({viewMode, setViewMode}: Props) { - const {updateSettings} = useLocalSettingsStore(); +export default function ViewDropdown({ viewMode, setViewMode }: Props) { + const { updateSettings } = useLocalSettingsStore(); - const onChangeViewMode = (e: React.MouseEvent, viewMode: ViewMode) => { - setViewMode(viewMode); - } + const onChangeViewMode = ( + e: React.MouseEvent, + viewMode: ViewMode + ) => { + setViewMode(viewMode); + }; - useEffect(() => { - updateSettings({viewMode: viewMode as ViewMode}); - }, [viewMode]); + useEffect(() => { + updateSettings({ viewMode: viewMode as ViewMode }); + }, [viewMode]); - return ( -
    - - - -
    - ); + return ( +
    + + {/* */} + +
    + ); } diff --git a/pages/collections/[id].tsx b/pages/collections/[id].tsx index 08ff8dc5..8e1cf1d6 100644 --- a/pages/collections/[id].tsx +++ b/pages/collections/[id].tsx @@ -1,4 +1,4 @@ -import LinkCard from "@/components/LinkCard"; +import LinkCard from "@/components/LinkViews/LinkComponents/LinkCard"; import useCollectionStore from "@/store/collections"; import useLinkStore from "@/store/links"; import { CollectionIncludingMembersAndLinkCount, Sort } from "@/types/global"; diff --git a/pages/dashboard.tsx b/pages/dashboard.tsx index 0cd4bde0..5788b73b 100644 --- a/pages/dashboard.tsx +++ b/pages/dashboard.tsx @@ -13,7 +13,7 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import MainLayout from "@/layouts/MainLayout"; import useLinkStore from "@/store/links"; import useTagStore from "@/store/tags"; -import LinkCard from "@/components/LinkCard"; +import LinkCard from "@/components/LinkViews/LinkComponents/LinkCard"; import { useEffect, useState } from "react"; import useLinks from "@/hooks/useLinks"; import Link from "next/link"; diff --git a/pages/links/index.tsx b/pages/links/index.tsx index 0c4670dd..6eddd6ec 100644 --- a/pages/links/index.tsx +++ b/pages/links/index.tsx @@ -1,4 +1,4 @@ -import LinkCard from "@/components/LinkCard"; +import LinkCard from "@/components/LinkViews/LinkComponents/LinkCard"; import NoLinksFound from "@/components/NoLinksFound"; import SortDropdown from "@/components/SortDropdown"; import useLinks from "@/hooks/useLinks"; @@ -9,21 +9,23 @@ import { faLink } from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { useState } from "react"; import ViewDropdown from "@/components/ViewDropdown"; -import DefaultGridView from "@/components/LinkViews/DefaultGridView"; -import CompactGridView from "@/components/LinkViews/CompactGridView"; +import DefaultView from "@/components/LinkViews/DefaultView"; +import GridView from "@/components/LinkViews/GridView"; import ListView from "@/components/LinkViews/ListView"; export default function Links() { const { links } = useLinkStore(); - const [viewMode, setViewMode] = useState(localStorage.getItem('viewMode') || ViewMode.Default); + const [viewMode, setViewMode] = useState( + localStorage.getItem("viewMode") || ViewMode.Default + ); const [sortBy, setSortBy] = useState(Sort.DateNewestFirst); useLinks({ sort: sortBy }); const components = { - [ViewMode.Default]: DefaultGridView, - [ViewMode.Compact]: CompactGridView, + [ViewMode.Default]: DefaultView, + // [ViewMode.Grid]: GridView, [ViewMode.List]: ListView, }; diff --git a/pages/links/pinned.tsx b/pages/links/pinned.tsx index ac52af2b..d6ba326e 100644 --- a/pages/links/pinned.tsx +++ b/pages/links/pinned.tsx @@ -1,4 +1,4 @@ -import LinkCard from "@/components/LinkCard"; +import LinkCard from "@/components/LinkViews/LinkComponents/LinkCard"; import SortDropdown from "@/components/SortDropdown"; import useLinks from "@/hooks/useLinks"; import MainLayout from "@/layouts/MainLayout"; diff --git a/pages/search.tsx b/pages/search.tsx index d05a20ad..ac54ca7f 100644 --- a/pages/search.tsx +++ b/pages/search.tsx @@ -1,5 +1,5 @@ import FilterSearchDropdown from "@/components/FilterSearchDropdown"; -import LinkCard from "@/components/LinkCard"; +import LinkCard from "@/components/LinkViews/LinkComponents/LinkCard"; import SortDropdown from "@/components/SortDropdown"; import useLinks from "@/hooks/useLinks"; import MainLayout from "@/layouts/MainLayout"; diff --git a/pages/tags/[id].tsx b/pages/tags/[id].tsx index 29c417f9..8adfdc47 100644 --- a/pages/tags/[id].tsx +++ b/pages/tags/[id].tsx @@ -1,4 +1,4 @@ -import LinkCard from "@/components/LinkCard"; +import LinkCard from "@/components/LinkViews/LinkComponents/LinkCard"; import useLinkStore from "@/store/links"; import { faCheck, diff --git a/types/global.ts b/types/global.ts index c2356b59..49505bda 100644 --- a/types/global.ts +++ b/types/global.ts @@ -59,7 +59,7 @@ export interface PublicCollectionIncludingLinks extends Collection { export enum ViewMode { Default = "default", - Compact = "compact", + Grid = "grid", List = "list", } From 4d691e0cce25bb94414532a79105207a825a5272 Mon Sep 17 00:00:00 2001 From: daniel31x13 Date: Sun, 17 Dec 2023 00:11:15 -0500 Subject: [PATCH 09/10] minor fix --- components/LinkViews/LinkComponents/LinkCard.tsx | 2 +- components/LinkViews/LinkComponents/LinkCardGrid.tsx | 2 +- components/LinkViews/LinkComponents/LinkIcon.tsx | 6 +++++- components/LinkViews/LinkComponents/LinkRow.tsx | 2 +- 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/components/LinkViews/LinkComponents/LinkCard.tsx b/components/LinkViews/LinkComponents/LinkCard.tsx index a1787d13..0c55add6 100644 --- a/components/LinkViews/LinkComponents/LinkCard.tsx +++ b/components/LinkViews/LinkComponents/LinkCard.tsx @@ -62,7 +62,7 @@ export default function LinkCard({ link, count, className }: Props) { className="flex flex-col justify-between cursor-pointer h-full w-full gap-1 p-3" >
    - +
    diff --git a/components/LinkViews/LinkComponents/LinkCardGrid.tsx b/components/LinkViews/LinkComponents/LinkCardGrid.tsx index ae4c77a1..a57ef954 100644 --- a/components/LinkViews/LinkComponents/LinkCardGrid.tsx +++ b/components/LinkViews/LinkComponents/LinkCardGrid.tsx @@ -53,7 +53,7 @@ export default function LinkCardGrid({ link, count, className }: Props) { className="flex items-center cursor-pointer p-3" >
    - +
    diff --git a/components/LinkViews/LinkComponents/LinkIcon.tsx b/components/LinkViews/LinkComponents/LinkIcon.tsx index cc5f3856..f89979aa 100644 --- a/components/LinkViews/LinkComponents/LinkIcon.tsx +++ b/components/LinkViews/LinkComponents/LinkIcon.tsx @@ -6,14 +6,18 @@ import isValidUrl from "@/lib/shared/isValidUrl"; export default function LinkIcon({ link, + className, }: { link: LinkIncludingShortenedCollectionAndTags; + className?: string; }) { const url = isValidUrl(link.url || "") && link.url ? new URL(link.url) : undefined; const iconClasses: string = - "w-12 bg-white text-primary shadow rounded-md p-1 select-none z-10"; + "bg-white text-primary shadow rounded-md border-[2px] border-white select-none z-10" + + " " + + className || ""; return (
    diff --git a/components/LinkViews/LinkComponents/LinkRow.tsx b/components/LinkViews/LinkComponents/LinkRow.tsx index 6b9fda86..79ececa5 100644 --- a/components/LinkViews/LinkComponents/LinkRow.tsx +++ b/components/LinkViews/LinkComponents/LinkRow.tsx @@ -53,7 +53,7 @@ export default function LinkCardCompact({ link, count, className }: Props) { className="flex items-center cursor-pointer p-3" >
    - +
    From c5b083e80265cbe623026aaea54d2fde7492cb2b Mon Sep 17 00:00:00 2001 From: daniel31x13 Date: Sun, 17 Dec 2023 01:25:46 -0500 Subject: [PATCH 10/10] implemented list view in other components as well --- .../LinkViews/LinkComponents/LinkActions.tsx | 8 +- .../LinkViews/LinkComponents/LinkCard.tsx | 2 +- .../LinkViews/LinkComponents/LinkCardGrid.tsx | 2 +- .../LinkViews/LinkComponents/LinkIcon.tsx | 8 +- .../LinkViews/LinkComponents/LinkRow.tsx | 12 +- components/ViewDropdown.tsx | 32 ++-- pages/collections/[id].tsx | 181 ++++++++++-------- pages/collections/index.tsx | 10 +- pages/dashboard.tsx | 4 +- pages/links/index.tsx | 12 +- pages/links/pinned.tsx | 33 +++- pages/search.tsx | 49 +++-- pages/tags/[id].tsx | 74 ++++--- 13 files changed, 237 insertions(+), 190 deletions(-) diff --git a/components/LinkViews/LinkComponents/LinkActions.tsx b/components/LinkViews/LinkComponents/LinkActions.tsx index ef4e6c74..9f53a058 100644 --- a/components/LinkViews/LinkComponents/LinkActions.tsx +++ b/components/LinkViews/LinkComponents/LinkActions.tsx @@ -16,9 +16,11 @@ import useAccountStore from "@/store/account"; export default function LinkActions({ link, collection, + position, }: { link: LinkIncludingShortenedCollectionAndTags; collection: CollectionIncludingMembersAndLinkCount; + position?: string; }) { const permissions = usePermissions(link.collection.id as number); @@ -62,7 +64,11 @@ export default function LinkActions({ {permissions === true || permissions?.canUpdate || permissions?.canDelete ? ( -
    +
    - +
    diff --git a/components/LinkViews/LinkComponents/LinkCardGrid.tsx b/components/LinkViews/LinkComponents/LinkCardGrid.tsx index a57ef954..8198884b 100644 --- a/components/LinkViews/LinkComponents/LinkCardGrid.tsx +++ b/components/LinkViews/LinkComponents/LinkCardGrid.tsx @@ -53,7 +53,7 @@ export default function LinkCardGrid({ link, count, className }: Props) { className="flex items-center cursor-pointer p-3" >
    - +
    diff --git a/components/LinkViews/LinkComponents/LinkIcon.tsx b/components/LinkViews/LinkComponents/LinkIcon.tsx index f89979aa..f09149c7 100644 --- a/components/LinkViews/LinkComponents/LinkIcon.tsx +++ b/components/LinkViews/LinkComponents/LinkIcon.tsx @@ -6,18 +6,18 @@ import isValidUrl from "@/lib/shared/isValidUrl"; export default function LinkIcon({ link, - className, + width, }: { link: LinkIncludingShortenedCollectionAndTags; - className?: string; + width?: string; }) { const url = isValidUrl(link.url || "") && link.url ? new URL(link.url) : undefined; const iconClasses: string = "bg-white text-primary shadow rounded-md border-[2px] border-white select-none z-10" + - " " + - className || ""; + " " + + (width || "w-12"); return (
    diff --git a/components/LinkViews/LinkComponents/LinkRow.tsx b/components/LinkViews/LinkComponents/LinkRow.tsx index 79ececa5..ea01c579 100644 --- a/components/LinkViews/LinkComponents/LinkRow.tsx +++ b/components/LinkViews/LinkComponents/LinkRow.tsx @@ -47,13 +47,13 @@ export default function LinkCardCompact({ link, count, className }: Props) { return ( <> -
    +
    link.url && window.open(link.url || "", "_blank")} - className="flex items-center cursor-pointer p-3" + className="flex items-center cursor-pointer py-3 sm:px-3" >
    - +
    @@ -79,7 +79,11 @@ export default function LinkCardCompact({ link, count, className }: Props) {
    - +
    diff --git a/components/ViewDropdown.tsx b/components/ViewDropdown.tsx index ac547e60..2be399a7 100644 --- a/components/ViewDropdown.tsx +++ b/components/ViewDropdown.tsx @@ -23,7 +23,7 @@ export default function ViewDropdown({ viewMode, setViewMode }: Props) { }, [viewMode]); return ( -
    +
    - {/* */} + + + {/* */}
    ); } diff --git a/pages/collections/[id].tsx b/pages/collections/[id].tsx index 8e1cf1d6..1ac2a9aa 100644 --- a/pages/collections/[id].tsx +++ b/pages/collections/[id].tsx @@ -1,7 +1,11 @@ import LinkCard from "@/components/LinkViews/LinkComponents/LinkCard"; import useCollectionStore from "@/store/collections"; import useLinkStore from "@/store/links"; -import { CollectionIncludingMembersAndLinkCount, Sort } from "@/types/global"; +import { + CollectionIncludingMembersAndLinkCount, + Sort, + ViewMode, +} from "@/types/global"; import { faEllipsis, faFolder } from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { useRouter } from "next/router"; @@ -18,6 +22,10 @@ import getPublicUserData from "@/lib/client/getPublicUserData"; import EditCollectionModal from "@/components/ModalContent/EditCollectionModal"; import EditCollectionSharingModal from "@/components/ModalContent/EditCollectionSharingModal"; import DeleteCollectionModal from "@/components/ModalContent/DeleteCollectionModal"; +import ViewDropdown from "@/components/ViewDropdown"; +import DefaultView from "@/components/LinkViews/DefaultView"; +import GridView from "@/components/LinkViews/GridView"; +import ListView from "@/components/LinkViews/ListView"; export default function Index() { const { settings } = useLocalSettingsStore(); @@ -76,6 +84,19 @@ export default function Index() { useState(false); const [deleteCollectionModal, setDeleteCollectionModal] = useState(false); + const [viewMode, setViewMode] = useState( + localStorage.getItem("viewMode") || ViewMode.Default + ); + + const linkView = { + [ViewMode.Default]: DefaultView, + // [ViewMode.Grid]: GridView, + [ViewMode.List]: ListView, + }; + + // @ts-ignore + const LinkComponent = linkView[viewMode]; + return (
    -
    - {activeCollection && ( -
    -
    - -

    - {activeCollection?.name} -

    -
    + {activeCollection && ( +
    +
    + +

    + {activeCollection?.name} +

    - )} -
    + +
    +
    + +
    +
      + {permissions === true ? ( +
    • +
      { + (document?.activeElement as HTMLElement)?.blur(); + setEditCollectionModal(true); + }} + > + Edit Collection Info +
      +
    • + ) : undefined} +
    • +
      { + (document?.activeElement as HTMLElement)?.blur(); + setEditCollectionSharingModal(true); + }} + > + {permissions === true + ? "Share and Collaborate" + : "View Team"} +
      +
    • +
    • +
      { + (document?.activeElement as HTMLElement)?.blur(); + setDeleteCollectionModal(true); + }} + > + {permissions === true + ? "Delete Collection" + : "Leave Collection"} +
      +
    • +
    +
    +
    + )} {activeCollection ? (
    @@ -158,76 +235,16 @@ export default function Index() {

    Showing {activeCollection?._count?.links} results

    -
    -
    -
    - -
    -
      - {permissions === true ? ( -
    • -
      { - (document?.activeElement as HTMLElement)?.blur(); - setEditCollectionModal(true); - }} - > - Edit Collection Info -
      -
    • - ) : undefined} -
    • -
      { - (document?.activeElement as HTMLElement)?.blur(); - setEditCollectionSharingModal(true); - }} - > - {permissions === true - ? "Share and Collaborate" - : "View Team"} -
      -
    • -
    • -
      { - (document?.activeElement as HTMLElement)?.blur(); - setDeleteCollectionModal(true); - }} - > - {permissions === true - ? "Delete Collection" - : "Leave Collection"} -
      -
    • -
    -
    -
    +
    {links.some((e) => e.collectionId === Number(router.query.id)) ? ( -
    - {links - .filter((e) => e.collection.id === activeCollection?.id) - .map((e, i) => { - return ; - })} -
    + e.collection.id === activeCollection?.id + )} + /> ) : ( )} diff --git a/pages/collections/index.tsx b/pages/collections/index.tsx index 477d92d8..a51e8f20 100644 --- a/pages/collections/index.tsx +++ b/pages/collections/index.tsx @@ -34,14 +34,14 @@ export default function Collections() {

    Your Collections

    -

    Collections you own

    +

    Collections you own

    @@ -75,14 +75,16 @@ export default function Collections() {

    Other Collections

    -

    Shared collections you're a member of

    +

    + Shared collections you're a member of +

    diff --git a/pages/dashboard.tsx b/pages/dashboard.tsx index 5788b73b..62e4d5be 100644 --- a/pages/dashboard.tsx +++ b/pages/dashboard.tsx @@ -108,12 +108,12 @@ export default function Dashboard() {

    Dashboard

    -

    A brief overview of your data

    +

    A brief overview of your data

    diff --git a/pages/links/index.tsx b/pages/links/index.tsx index 6eddd6ec..29bf548c 100644 --- a/pages/links/index.tsx +++ b/pages/links/index.tsx @@ -23,14 +23,14 @@ export default function Links() { useLinks({ sort: sortBy }); - const components = { + const linkView = { [ViewMode.Default]: DefaultView, // [ViewMode.Grid]: GridView, [ViewMode.List]: ListView, }; // @ts-ignore - const Component = components[viewMode]; + const LinkComponent = linkView[viewMode]; return ( @@ -39,22 +39,22 @@ export default function Links() {

    All Links

    -

    Links from every Collections

    +

    Links from every Collections

    -
    +
    {links[0] ? ( - + ) : ( )} diff --git a/pages/links/pinned.tsx b/pages/links/pinned.tsx index d6ba326e..b7db42df 100644 --- a/pages/links/pinned.tsx +++ b/pages/links/pinned.tsx @@ -3,18 +3,34 @@ import SortDropdown from "@/components/SortDropdown"; import useLinks from "@/hooks/useLinks"; import MainLayout from "@/layouts/MainLayout"; import useLinkStore from "@/store/links"; -import { Sort } from "@/types/global"; +import { Sort, ViewMode } from "@/types/global"; import { faThumbTack } from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { useState } from "react"; +import ViewDropdown from "@/components/ViewDropdown"; +import DefaultView from "@/components/LinkViews/DefaultView"; +import GridView from "@/components/LinkViews/GridView"; +import ListView from "@/components/LinkViews/ListView"; export default function PinnedLinks() { const { links } = useLinkStore(); + const [viewMode, setViewMode] = useState( + localStorage.getItem("viewMode") || ViewMode.Default + ); const [sortBy, setSortBy] = useState(Sort.DateNewestFirst); useLinks({ sort: sortBy, pinnedOnly: true }); + const linkView = { + [ViewMode.Default]: DefaultView, + // [ViewMode.Grid]: GridView, + [ViewMode.List]: ListView, + }; + + // @ts-ignore + const LinkComponent = linkView[viewMode]; + return (
    @@ -22,25 +38,24 @@ export default function PinnedLinks() {

    Pinned Links

    -

    Pinned Links from your Collections

    +

    + Pinned Links from your Collections +

    -
    +
    +
    {links.some((e) => e.pinnedBy && e.pinnedBy[0]) ? ( -
    - {links.map((e, i) => { - return ; - })} -
    + ) : (
    ( + localStorage.getItem("viewMode") || ViewMode.Default + ); const [sortBy, setSortBy] = useState(Sort.DateNewestFirst); useLinks({ @@ -36,15 +44,24 @@ export default function Search() { searchByTags: searchFilter.tags, }); + const linkView = { + [ViewMode.Default]: DefaultView, + // [ViewMode.Grid]: GridView, + [ViewMode.List]: ListView, + }; + + // @ts-ignore + const LinkComponent = linkView[viewMode]; + return (
    -
    -
    +
    +

    Search Results @@ -52,25 +69,17 @@ export default function Search() {

    -
    -
    - -
    - -
    - -
    +
    + + +
    {links[0] ? ( -
    - {links.map((e, i) => { - return ; - })} -
    + ) : (

    Nothing found.{" "} diff --git a/pages/tags/[id].tsx b/pages/tags/[id].tsx index 8adfdc47..e1d92da7 100644 --- a/pages/tags/[id].tsx +++ b/pages/tags/[id].tsx @@ -12,10 +12,13 @@ import { FormEvent, useEffect, useState } from "react"; import MainLayout from "@/layouts/MainLayout"; import useTagStore from "@/store/tags"; import SortDropdown from "@/components/SortDropdown"; -import { Sort, TagIncludingLinkCount } from "@/types/global"; +import { Sort, TagIncludingLinkCount, ViewMode } from "@/types/global"; import useLinks from "@/hooks/useLinks"; -import Dropdown from "@/components/Dropdown"; import { toast } from "react-hot-toast"; +import ViewDropdown from "@/components/ViewDropdown"; +import DefaultView from "@/components/LinkViews/DefaultView"; +import GridView from "@/components/LinkViews/GridView"; +import ListView from "@/components/LinkViews/ListView"; export default function Index() { const router = useRouter(); @@ -25,8 +28,6 @@ export default function Index() { const [sortBy, setSortBy] = useState(Sort.DateNewestFirst); - const [expandDropdown, setExpandDropdown] = useState(false); - const [renameTag, setRenameTag] = useState(false); const [newTagName, setNewTagName] = useState(); @@ -97,6 +98,19 @@ export default function Index() { setRenameTag(false); }; + const [viewMode, setViewMode] = useState( + localStorage.getItem("viewMode") || ViewMode.Default + ); + + const linkView = { + [ViewMode.Default]: DefaultView, + // [ViewMode.Grid]: GridView, + [ViewMode.List]: ListView, + }; + + // @ts-ignore + const LinkComponent = linkView[viewMode]; + return (

    @@ -105,7 +119,7 @@ export default function Index() {
    {renameTag ? ( <> @@ -147,7 +161,13 @@ export default function Index() { {activeTag?.name}

    -
    +
    8 + ? "dropdown-end" + : "" + }`} + >
- - {expandDropdown ? ( - { - setRenameTag(true); - setExpandDropdown(false); - }, - }, - { - name: "Remove Tag", - onClick: () => { - remove(); - setExpandDropdown(false); - }, - }, - ]} - onClickOutside={(e: Event) => { - const target = e.target as HTMLInputElement; - if (target.id !== "expand-dropdown") - setExpandDropdown(false); - }} - className="absolute top-8 left-0 w-36 font-normal" - /> - ) : null} )} -
+
+
-
- {links - .filter((e) => e.tags.some((e) => e.id === Number(router.query.id))) - .map((e, i) => { - return ; - })} -
+ + e.tags.some((e) => e.id === Number(router.query.id)) + )} + /> );