From 0d0e53218f8f2f51cfaa0a22afc8aaf3ef6e44a5 Mon Sep 17 00:00:00 2001 From: Daniel Date: Mon, 1 May 2023 13:37:01 +0330 Subject: [PATCH] many changes across the app --- components/LinkList.tsx | 25 +++++---- components/Modal/DeleteCollection.tsx | 2 +- components/Modal/EditCollection.tsx | 54 +++++++++++++++--- components/Modal/EditLink.tsx | 35 +++++++++--- components/Modal/index.tsx | 19 +++++-- components/Navbar.tsx | 11 +++- components/Sidebar/SidebarItem.tsx | 21 +++++-- components/Sidebar/index.tsx | 55 ++++++++++++++++--- layouts/AuthRedirect.tsx | 2 +- layouts/Dashboard.tsx | 2 +- .../collections/deleteCollection.ts | 8 ++- lib/api/controllers/links/updateLink.ts | 25 +++++++-- store/links.ts | 5 +- styles/globals.css | 6 +- 14 files changed, 215 insertions(+), 55 deletions(-) diff --git a/components/LinkList.tsx b/components/LinkList.tsx index 432da8f..01ed6c7 100644 --- a/components/LinkList.tsx +++ b/components/LinkList.tsx @@ -19,6 +19,7 @@ import Dropdown from "./Dropdown"; import useLinkStore from "@/store/links"; import Modal from "./Modal"; import EditLink from "./Modal/EditLink"; +import Link from "next/link"; export default function ({ link, @@ -57,7 +58,7 @@ export default function ({ width={32} height={32} alt="" - className="select-none mt-3 z-10 rounded-md" + className="select-none mt-3 z-10 rounded-md shadow" draggable="false" onError={(e) => { const target = e.target as HTMLElement; @@ -84,18 +85,20 @@ export default function ({

{link.title}

-
- -

{link.collection.name}

-
+ +
+ +

{link.collection.name}

+
+ +
{link.tags.map((e, i) => ( -

- # {e.name} -

+ +

+ # {e.name} +

+ ))}
diff --git a/components/Modal/DeleteCollection.tsx b/components/Modal/DeleteCollection.tsx index 64483b7..c643f43 100644 --- a/components/Modal/DeleteCollection.tsx +++ b/components/Modal/DeleteCollection.tsx @@ -34,7 +34,7 @@ export default function AddCollection({ }; return ( -
+

Delete Collection

diff --git a/components/Modal/EditCollection.tsx b/components/Modal/EditCollection.tsx index 444ec7e..2b8d116 100644 --- a/components/Modal/EditCollection.tsx +++ b/components/Modal/EditCollection.tsx @@ -5,11 +5,17 @@ import React, { useState } from "react"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { faClose, faPenToSquare } from "@fortawesome/free-solid-svg-icons"; +import { + faClose, + faPenToSquare, + faTrashCan, +} from "@fortawesome/free-solid-svg-icons"; import useCollectionStore from "@/store/collections"; import { ExtendedCollection } from "@/types/global"; import { useSession } from "next-auth/react"; import getPublicUserDataByEmail from "@/lib/client/getPublicUserDataByEmail"; +import Modal from "@/components/Modal"; +import DeleteCollection from "@/components/Modal/DeleteCollection"; type Props = { toggleCollectionModal: Function; @@ -27,6 +33,12 @@ export default function EditCollection({ const { updateCollection } = useCollectionStore(); + const [deleteCollectionModal, setDeleteCollectionModal] = useState(false); + + const toggleDeleteCollectionModal = () => { + setDeleteCollectionModal(!deleteCollectionModal); + }; + const session = useSession(); const submit = async () => { @@ -250,13 +262,41 @@ export default function EditCollection({ ); })} -

- - Edit Collection +
+
+ + Edit Collection +
+ +
+
+ +

OR

+ +
+
+ +
{ + toggleDeleteCollectionModal(); + }} + className="w-fit inline-flex rounded-md cursor-pointer bg-red-500 hover:bg-red-400 duration-100 p-2" + > + +
+ + {deleteCollectionModal ? ( + + + + ) : null}
); } diff --git a/components/Modal/EditLink.tsx b/components/Modal/EditLink.tsx index a59d289..8ce35ad 100644 --- a/components/Modal/EditLink.tsx +++ b/components/Modal/EditLink.tsx @@ -10,6 +10,7 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { ExtendedLink } from "@/types/global"; import { faPenToSquare } from "@fortawesome/free-regular-svg-icons"; import useLinkStore from "@/store/links"; +import { faTrashCan } from "@fortawesome/free-solid-svg-icons"; type Props = { toggleLinkModal: Function; @@ -19,7 +20,7 @@ type Props = { export default function EditLink({ toggleLinkModal, link }: Props) { const [currentLink, setCurrentLink] = useState(link); - const { updateLink } = useLinkStore(); + const { updateLink, removeLink } = useLinkStore(); const shortendURL = new URL(link.url).host.toLowerCase(); @@ -85,12 +86,32 @@ export default function EditLink({ toggleLinkModal, link }: Props) { />
-
- - Edit Link +
+
+ + Edit Link +
+ +
+
+ +

OR

+ +
+
+ +
{ + removeLink(link); + toggleLinkModal(); + }} + className="w-fit inline-flex rounded-md cursor-pointer bg-red-500 hover:bg-red-400 duration-100 p-2" + > + +
); diff --git a/components/Modal/index.tsx b/components/Modal/index.tsx index 7098d2b..5f7f01e 100644 --- a/components/Modal/index.tsx +++ b/components/Modal/index.tsx @@ -3,8 +3,10 @@ // This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. // You should have received a copy of the GNU General Public License along with this program. If not, see . -import { ReactNode } from "react"; +import { MouseEventHandler, ReactNode } from "react"; import ClickAwayHandler from "@/components/ClickAwayHandler"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { faChevronLeft } from "@fortawesome/free-solid-svg-icons"; type Props = { toggleModal: Function; @@ -13,9 +15,18 @@ type Props = { export default function ({ toggleModal, children }: Props) { return ( -
- -
+
+ +
+
} + className="absolute top-5 left-5 inline-flex rounded-md cursor-pointer hover:bg-white hover:border-sky-500 border-sky-100 border duration-100 p-1" + > + +
{children}
diff --git a/components/Navbar.tsx b/components/Navbar.tsx index f51d9ea..8fe2389 100644 --- a/components/Navbar.tsx +++ b/components/Navbar.tsx @@ -15,12 +15,13 @@ import { faChevronDown, faBars, } from "@fortawesome/free-solid-svg-icons"; -import { useState } from "react"; +import { useEffect, useState } from "react"; import Dropdown from "@/components/Dropdown"; import Modal from "./Modal"; import AddLink from "./Modal/AddLink"; import ClickAwayHandler from "./ClickAwayHandler"; import Sidebar from "./Sidebar"; +import { useRouter } from "next/router"; export default function () { const { data: session } = useSession(); @@ -32,8 +33,14 @@ export default function () { const [linkModal, setLinkModal] = useState(false); const [sidebar, setSidebar] = useState(false); + const router = useRouter(); + window.addEventListener("resize", () => setSidebar(false)); + useEffect(() => { + setSidebar(false); + }, [router]); + const toggleSidebar = () => { setSidebar(!sidebar); }; @@ -123,7 +130,7 @@ export default function () { {sidebar ? (
-
+
diff --git a/components/Sidebar/SidebarItem.tsx b/components/Sidebar/SidebarItem.tsx index 2437985..be17ecd 100644 --- a/components/Sidebar/SidebarItem.tsx +++ b/components/Sidebar/SidebarItem.tsx @@ -4,7 +4,8 @@ // You should have received a copy of the GNU General Public License along with this program. If not, see . import Link from "next/link"; -import React, { ReactElement } from "react"; +import React, { ReactElement, useEffect, useState } from "react"; +import { useRouter } from "next/router"; interface SidebarItemProps { text: string; @@ -13,13 +14,25 @@ interface SidebarItemProps { } export default function ({ text, icon, path }: SidebarItemProps) { + const router = useRouter(); + const [active, setActive] = useState(false); + + useEffect(() => { + if (router.asPath === path) setActive(true); + else setActive(false); + }, [router]); + return ( -
+
{React.cloneElement(icon, { - className: "w-4 text-sky-300", + className: `w-4 ${active ? "text-white" : "text-sky-300"}`, })} -

{text}

+

{text}

); diff --git a/components/Sidebar/index.tsx b/components/Sidebar/index.tsx index 81489d5..89fa8f3 100644 --- a/components/Sidebar/index.tsx +++ b/components/Sidebar/index.tsx @@ -14,12 +14,21 @@ import { import SidebarItem from "./SidebarItem"; import useTagStore from "@/store/tags"; import Link from "next/link"; +import { useRouter } from "next/router"; +import { useEffect, useState } from "react"; export default function () { const { collections } = useCollectionStore(); - const { tags } = useTagStore(); + const router = useRouter(); + + const [active, setActive] = useState(""); + + useEffect(() => { + setActive(router.asPath); + }, [router]); + return (

@@ -27,16 +36,48 @@ export default function () {

-
- -

All Links

+
+ +

+ All Links +

-
- -

All Collections

+
+ +

+ All Collections +

diff --git a/layouts/AuthRedirect.tsx b/layouts/AuthRedirect.tsx index 8e9bfcd..c0c1980 100644 --- a/layouts/AuthRedirect.tsx +++ b/layouts/AuthRedirect.tsx @@ -41,5 +41,5 @@ export default function ({ children }: Props) { }, [status]); if (status !== "loading" && !redirect) return <>{children}; - else return ; + // else return ; } diff --git a/layouts/Dashboard.tsx b/layouts/Dashboard.tsx index a1588e2..4bb28f8 100644 --- a/layouts/Dashboard.tsx +++ b/layouts/Dashboard.tsx @@ -36,5 +36,5 @@ export default function ({ children }: Props) { ); else if ((status === "unauthenticated" && !redirect) || !routeExists) return <>{children}; - else return ; + else return <>; } diff --git a/lib/api/controllers/collections/deleteCollection.ts b/lib/api/controllers/collections/deleteCollection.ts index 6cc27ef..c94c69a 100644 --- a/lib/api/controllers/collections/deleteCollection.ts +++ b/lib/api/controllers/collections/deleteCollection.ts @@ -33,7 +33,13 @@ export default async function (collection: { id: number }, userId: number) { }, }); - fs.rmdirSync(`data/archives/${collection.id}`, { recursive: true }); + try { + fs.rmdirSync(`data/archives/${collection.id}`, { recursive: true }); + } catch (error) { + console.log( + "Collection's archive directory wasn't deleted most likely because it didn't exist..." + ); + } return await prisma.collection.delete({ where: { diff --git a/lib/api/controllers/links/updateLink.ts b/lib/api/controllers/links/updateLink.ts index a1cfd23..f30860b 100644 --- a/lib/api/controllers/links/updateLink.ts +++ b/lib/api/controllers/links/updateLink.ts @@ -5,7 +5,7 @@ import { prisma } from "@/lib/api/db"; import { ExtendedLink } from "@/types/global"; -import { Link, UsersAndCollections } from "@prisma/client"; +import { UsersAndCollections } from "@prisma/client"; import getPermission from "@/lib/api/getPermission"; export default async function (link: ExtendedLink, userId: number) { @@ -17,10 +17,23 @@ export default async function (link: ExtendedLink, userId: number) { (e: UsersAndCollections) => e.userId === userId && e.canUpdate ); - if (!(collectionIsAccessible?.ownerId === userId || memberHasAccess)) - return { response: "Collection is not accessible.", status: 401 }; + if (link.collection.ownerId) { + const collectionIsAccessible = await getPermission( + userId, + link.collection.id + ); - const updatedLink: Link = await prisma.link.update({ + const memberHasAccess = collectionIsAccessible?.members.some( + (e: UsersAndCollections) => e.userId === userId && e.canCreate + ); + + if (!(collectionIsAccessible?.ownerId === userId || memberHasAccess)) + return { response: "Collection is not accessible.", status: 401 }; + } else { + link.collection.ownerId = userId; + } + + const updatedLink: ExtendedLink = await prisma.link.update({ where: { id: link.id, }, @@ -60,6 +73,10 @@ export default async function (link: ExtendedLink, userId: number) { })), }, }, + include: { + tags: true, + collection: true, + }, }); return { response: updatedLink, status: 200 }; diff --git a/store/links.ts b/store/links.ts index 68e9f51..819a51d 100644 --- a/store/links.ts +++ b/store/links.ts @@ -63,9 +63,12 @@ const useLinkStore = create()((set) => ({ if (response.ok) { set((state) => ({ - links: state.links.map((e) => (e.id === link.id ? link : e)), + links: state.links.map((e) => + e.id === data.response.id ? data.response : e + ), })); useTagStore.getState().setTags(); + useCollectionStore.getState().setCollections(); } }, removeLink: async (link) => { diff --git a/styles/globals.css b/styles/globals.css index 971a0b9..76cc6d6 100644 --- a/styles/globals.css +++ b/styles/globals.css @@ -63,16 +63,14 @@ } .slide-right { - animation: slide-right-animation 100ms; + animation: slide-right-animation 200ms; } @keyframes slide-right-animation { 0% { - transform: translateX(-25%); - opacity: 0; + transform: translateX(-100%); } 100% { transform: translateX(0); - opacity: 1; } }