From 754c15d2bb5f91a1fa96bb42511c2335c7cc12df Mon Sep 17 00:00:00 2001 From: daniel31x13 Date: Fri, 13 Oct 2023 02:03:38 -0400 Subject: [PATCH 1/3] added support for wayback machine --- components/Modal/Link/LinkDetails.tsx | 35 ++++++++++++++++++++++----- lib/api/archive.ts | 3 +++ lib/api/sendToWayback.ts | 23 ++++++++++++++++++ package.json | 1 + yarn.lock | 19 +++++++++++++++ 5 files changed, 75 insertions(+), 6 deletions(-) create mode 100644 lib/api/sendToWayback.ts diff --git a/components/Modal/Link/LinkDetails.tsx b/components/Modal/Link/LinkDetails.tsx index 827bd7e..232eb54 100644 --- a/components/Modal/Link/LinkDetails.tsx +++ b/components/Modal/Link/LinkDetails.tsx @@ -12,6 +12,7 @@ import { faBoxArchive, faCloudArrowDown, faFolder, + faGlobe, } from "@fortawesome/free-solid-svg-icons"; import useCollectionStore from "@/store/collections"; import { @@ -224,9 +225,9 @@ export default function LinkDetails({ link, isOwnerOrMod }: Props) {
-
+
-
+
@@ -237,7 +238,6 @@ export default function LinkDetails({ link, isOwnerOrMod }: Props) {
-
+
-
+
@@ -271,7 +271,6 @@ export default function LinkDetails({ link, isOwnerOrMod }: Props) {
+ +
+
+
+ +
+ +

Wayback Machine

+
+ + + + +
); diff --git a/lib/api/archive.ts b/lib/api/archive.ts index 1163f26..685413e 100644 --- a/lib/api/archive.ts +++ b/lib/api/archive.ts @@ -1,12 +1,15 @@ import { Page, chromium, devices } from "playwright"; import { prisma } from "@/lib/api/db"; import createFile from "@/lib/api/storage/createFile"; +import sendToWayback from "./sendToWayback"; export default async function archive(linkId: number, url: string) { const browser = await chromium.launch(); const context = await browser.newContext(devices["Desktop Chrome"]); const page = await context.newPage(); + sendToWayback(url); + try { await page.goto(url, { waitUntil: "domcontentloaded" }); diff --git a/lib/api/sendToWayback.ts b/lib/api/sendToWayback.ts new file mode 100644 index 0000000..c736bdd --- /dev/null +++ b/lib/api/sendToWayback.ts @@ -0,0 +1,23 @@ +import axios from "axios"; + +export default async function sendToWayback(url: string) { + const headers = { + Accept: "text/html,application/xhtml+xml,application/xml", + "Accept-Encoding": "gzip, deflate", + Dnt: "1", + "Upgrade-Insecure-Requests": "1", + "User-Agent": + "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.103 Safari/537.36", + }; + + await axios + .get(`https://web.archive.org/save/${url}`, { + headers: headers, + }) + .then((response) => { + console.log(response.data); + }) + .catch((error) => { + console.error(error); + }); +} diff --git a/package.json b/package.json index 6113639..22e782b 100644 --- a/package.json +++ b/package.json @@ -28,6 +28,7 @@ "@types/nodemailer": "^6.4.8", "@types/react": "18.2.14", "@types/react-dom": "18.2.7", + "axios": "^1.5.1", "bcrypt": "^5.1.0", "colorthief": "^2.4.0", "crypto-js": "^4.1.1", diff --git a/yarn.lock b/yarn.lock index 78896a4..83ac24e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1791,6 +1791,15 @@ axe-core@^4.6.2: resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.6.3.tgz#fc0db6fdb65cc7a80ccf85286d91d64ababa3ece" integrity sha512-/BQzOX780JhsxDnPpH4ZiyrJAzcd8AfzFPkv+89veFSr1rcMjuq2JDCwypKaPeB6ljHp9KjXhPpjgCvQlWYuqg== +axios@^1.5.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/axios/-/axios-1.5.1.tgz#11fbaa11fc35f431193a9564109c88c1f27b585f" + integrity sha512-Q28iYCWzNHjAm+yEAot5QaAMxhMghWLFVf7rRdwhUI+c2jix2DUXjAHXVi+s1ibs3mjPO/cCgbA++3BjD0vP/A== + dependencies: + follow-redirects "^1.15.0" + form-data "^4.0.0" + proxy-from-env "^1.1.0" + axobject-query@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/axobject-query/-/axobject-query-3.1.1.tgz#3b6e5c6d4e43ca7ba51c5babf99d22a9c68485e1" @@ -2715,6 +2724,11 @@ flatted@^3.1.0: resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.7.tgz#609f39207cb614b89d0765b477cb2d437fbf9787" integrity sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ== +follow-redirects@^1.15.0: + version "1.15.3" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.3.tgz#fe2f3ef2690afce7e82ed0b44db08165b207123a" + integrity sha512-1VzOtuEM8pC9SFU1E+8KfTjZyMztRsgEfwQl44z8A25uy13jSzTj6dyK2Df52iV0vgHCfBwLhDWevLn95w5v6Q== + for-each@^0.3.3: version "0.3.3" resolved "https://registry.yarnpkg.com/for-each/-/for-each-0.3.3.tgz#69b447e88a0a5d32c3e7084f3f1710034b21376e" @@ -4180,6 +4194,11 @@ prop-types@^15.6.0, prop-types@^15.6.2, prop-types@^15.8.1: object-assign "^4.1.1" react-is "^16.13.1" +proxy-from-env@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2" + integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg== + psl@^1.1.28, psl@^1.1.33: version "1.9.0" resolved "https://registry.yarnpkg.com/psl/-/psl-1.9.0.tgz#d0df2a137f00794565fcaf3b2c00cd09f8d5a5a7" From 36a1ed209ee75c3ecf3364c32db9a695ce7b6d18 Mon Sep 17 00:00:00 2001 From: daniel31x13 Date: Fri, 13 Oct 2023 02:14:18 -0400 Subject: [PATCH 2/3] small design improvements --- components/Modal/Link/AddOrEditLink.tsx | 3 ++- components/Modal/Link/LinkDetails.tsx | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/components/Modal/Link/AddOrEditLink.tsx b/components/Modal/Link/AddOrEditLink.tsx index 942f92a..dcdf422 100644 --- a/components/Modal/Link/AddOrEditLink.tsx +++ b/components/Modal/Link/AddOrEditLink.tsx @@ -137,7 +137,8 @@ export default function AddOrEditLink({ className="text-gray-500 dark:text-gray-300 text-center truncate w-full" title={link.url} > - + Editing:{" "} + {link.url}

diff --git a/components/Modal/Link/LinkDetails.tsx b/components/Modal/Link/LinkDetails.tsx index 232eb54..317286e 100644 --- a/components/Modal/Link/LinkDetails.tsx +++ b/components/Modal/Link/LinkDetails.tsx @@ -297,7 +297,7 @@ export default function LinkDetails({ link, isOwnerOrMod }: Props) {
-

Wayback Machine

+

Archive.org Snapshot

Date: Mon, 16 Oct 2023 13:10:52 -0400 Subject: [PATCH 3/3] fixed filter by tags + refactored search + bug fixed + settings page [WIP] --- components/Modal/Link/LinkDetails.tsx | 40 ++-- components/Modal/User/ProfileSettings.tsx | 4 +- components/Navbar.tsx | 7 +- components/ProfilePhoto.tsx | 3 + components/SettingsSidebar.tsx | 219 ++++++++++++++++++++ components/Sidebar.tsx | 1 - layouts/MainLayout.tsx | 41 ++-- layouts/SettingsLayout.tsx | 78 ++++++++ lib/api/controllers/links/getLinks.ts | 232 +++++++++++----------- lib/client/avatarExists.ts | 11 +- package.json | 1 + pages/collections/[id].tsx | 8 +- pages/index.tsx | 2 +- pages/settings/appearance.tsx | 10 + pages/settings/archive.tsx | 10 + pages/settings/billing.tsx | 18 ++ pages/settings/index.tsx | 10 + pages/settings/password.tsx | 10 + pages/settings/privacy.tsx | 10 + pages/settings/profile.tsx | 10 + pages/tags/[id].tsx | 8 +- yarn.lock | 12 ++ 22 files changed, 561 insertions(+), 184 deletions(-) create mode 100644 components/SettingsSidebar.tsx create mode 100644 layouts/SettingsLayout.tsx create mode 100644 pages/settings/appearance.tsx create mode 100644 pages/settings/archive.tsx create mode 100644 pages/settings/billing.tsx create mode 100644 pages/settings/index.tsx create mode 100644 pages/settings/password.tsx create mode 100644 pages/settings/privacy.tsx create mode 100644 pages/settings/profile.tsx diff --git a/components/Modal/Link/LinkDetails.tsx b/components/Modal/Link/LinkDetails.tsx index 317286e..7060ee8 100644 --- a/components/Modal/Link/LinkDetails.tsx +++ b/components/Modal/Link/LinkDetails.tsx @@ -79,25 +79,29 @@ export default function LinkDetails({ link, isOwnerOrMod }: Props) { const bannerInner = document.getElementById("link-banner-inner"); if (colorPalette && banner && bannerInner) { - banner.style.background = `linear-gradient(to right, ${rgbToHex( - colorPalette[0][0], - colorPalette[0][1], - colorPalette[0][2] - )}, ${rgbToHex( - colorPalette[1][0], - colorPalette[1][1], - colorPalette[1][2] - )})`; + if (colorPalette[0] && colorPalette[1]) { + banner.style.background = `linear-gradient(to right, ${rgbToHex( + colorPalette[0][0], + colorPalette[0][1], + colorPalette[0][2] + )}, ${rgbToHex( + colorPalette[1][0], + colorPalette[1][1], + colorPalette[1][2] + )})`; + } - bannerInner.style.background = `linear-gradient(to right, ${rgbToHex( - colorPalette[2][0], - colorPalette[2][1], - colorPalette[2][2] - )}, ${rgbToHex( - colorPalette[3][0], - colorPalette[3][1], - colorPalette[3][2] - )})`; + if (colorPalette[2] && colorPalette[3]) { + bannerInner.style.background = `linear-gradient(to right, ${rgbToHex( + colorPalette[2][0], + colorPalette[2][1], + colorPalette[2][2] + )}, ${rgbToHex( + colorPalette[3][0], + colorPalette[3][1], + colorPalette[3][2] + )})`; + } } }, [colorPalette, theme]); diff --git a/components/Modal/User/ProfileSettings.tsx b/components/Modal/User/ProfileSettings.tsx index 95ce4b8..0bb48eb 100644 --- a/components/Modal/User/ProfileSettings.tsx +++ b/components/Modal/User/ProfileSettings.tsx @@ -109,7 +109,7 @@ export default function ProfileSettings({
{profileStatus && ( @@ -120,7 +120,7 @@ export default function ProfileSettings({ profilePic: "", }) } - className="absolute top-1 left-1 w-5 h-5 flex items-center justify-center border p-1 bg-white border-slate-200 rounded-full text-gray-500 hover:text-red-500 duration-100 cursor-pointer" + className="absolute top-1 left-1 w-5 h-5 flex items-center justify-center border p-1 border-slate-200 dark:border-neutral-700 rounded-full bg-white dark:bg-neutral-800 text-center select-none cursor-pointer duration-100 hover:text-red-500" >
diff --git a/components/Navbar.tsx b/components/Navbar.tsx index ee300e0..f780f32 100644 --- a/components/Navbar.tsx +++ b/components/Navbar.tsx @@ -19,8 +19,6 @@ export default function Navbar() { const [profileDropdown, setProfileDropdown] = useState(false); - const [sidebar, setSidebar] = useState(false); - const router = useRouter(); const { theme, setTheme } = useTheme(); @@ -33,6 +31,8 @@ export default function Navbar() { } }; + const [sidebar, setSidebar] = useState(false); + window.addEventListener("resize", () => setSidebar(false)); useEffect(() => { @@ -79,6 +79,7 @@ export default function Navbar() { >

- +
diff --git a/components/ProfilePhoto.tsx b/components/ProfilePhoto.tsx index f9c99eb..e2f2b39 100644 --- a/components/ProfilePhoto.tsx +++ b/components/ProfilePhoto.tsx @@ -9,6 +9,7 @@ type Props = { className?: string; emptyImage?: boolean; status?: Function; + priority?: boolean; }; export default function ProfilePhoto({ @@ -16,6 +17,7 @@ export default function ProfilePhoto({ className, emptyImage, status, + priority, }: Props) { const [error, setError] = useState(emptyImage || true); @@ -43,6 +45,7 @@ export default function ProfilePhoto({ src={src} height={112} width={112} + priority={priority} className={`h-10 w-10 bg-sky-600 dark:bg-sky-600 shadow rounded-full aspect-square border border-slate-200 dark:border-neutral-700 ${className}`} /> ); diff --git a/components/SettingsSidebar.tsx b/components/SettingsSidebar.tsx new file mode 100644 index 0000000..eb26f78 --- /dev/null +++ b/components/SettingsSidebar.tsx @@ -0,0 +1,219 @@ +import useCollectionStore from "@/store/collections"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { + faUser, + faPalette, + faBoxArchive, + faLock, + faKey, +} from "@fortawesome/free-solid-svg-icons"; +import Link from "next/link"; +import { useRouter } from "next/router"; +import { useEffect, useState } from "react"; +import { + faCircleQuestion, + faCreditCard, +} from "@fortawesome/free-regular-svg-icons"; +import { + faGithub, + faMastodon, + faXTwitter, +} from "@fortawesome/free-brands-svg-icons"; + +export default function SettingsSidebar({ className }: { className?: string }) { + const { collections } = useCollectionStore(); + + const router = useRouter(); + + const [active, setActive] = useState(""); + + useEffect(() => { + setActive(router.asPath); + }, [router, collections]); + + return ( +
+
+ +
+ + +

+ Profile +

+
+ + + +
+ + +

+ Appearance +

+
+ + + +
+ + +

+ Archive +

+
+ + + +
+ + +

+ Privacy +

+
+ + + +
+ + +

+ Password +

+
+ + + {process.env.NEXT_PUBLIC_STRIPE_IS_ACTIVE ? ( + +
+ + +

+ Billing +

+
+ + ) : undefined} +
+ +
+ +
+ + +

+ Twitter +

+
+ + + +
+ + +

+ Mastodon +

+
+ + + +
+ + +

+ GitHub +

+
+ + + +
+ + +

+ Help +

+
+ +
+
+ ); +} diff --git a/components/Sidebar.tsx b/components/Sidebar.tsx index d121ae2..7254bde 100644 --- a/components/Sidebar.tsx +++ b/components/Sidebar.tsx @@ -12,7 +12,6 @@ import Link from "next/link"; import { useRouter } from "next/router"; import { useEffect, useState } from "react"; import { Disclosure, Transition } from "@headlessui/react"; -import Image from "next/image"; export default function Sidebar({ className }: { className?: string }) { const [tagDisclosure, setTagDisclosure] = useState(() => { diff --git a/layouts/MainLayout.tsx b/layouts/MainLayout.tsx index 87d15e6..48ecbb8 100644 --- a/layouts/MainLayout.tsx +++ b/layouts/MainLayout.tsx @@ -1,10 +1,6 @@ import Navbar from "@/components/Navbar"; import Sidebar from "@/components/Sidebar"; import { ReactNode, useEffect } from "react"; -import { useSession } from "next-auth/react"; -import Loader from "../components/Loader"; -import useRedirect from "@/hooks/useRedirect"; -import { useRouter } from "next/router"; import ModalManagement from "@/components/ModalManagement"; import useModalStore from "@/store/modals"; @@ -13,11 +9,6 @@ interface Props { } export default function MainLayout({ children }: Props) { - const { status, data } = useSession(); - const router = useRouter(); - const redirect = useRedirect(); - const routeExists = router.route === "/_error" ? false : true; - const { modal } = useModalStore(); useEffect(() => { @@ -26,24 +17,20 @@ export default function MainLayout({ children }: Props) { : (document.body.style.overflow = "auto"); }, [modal]); - if (status === "authenticated" && !redirect && routeExists) - return ( - <> - + return ( + <> + -
-
- -
- -
- - {children} -
+
+
+
- - ); - else if ((status === "unauthenticated" && !redirect) || !routeExists) - return <>{children}; - else return <>; + +
+ + {children} +
+
+ + ); } diff --git a/layouts/SettingsLayout.tsx b/layouts/SettingsLayout.tsx new file mode 100644 index 0000000..ae5c08f --- /dev/null +++ b/layouts/SettingsLayout.tsx @@ -0,0 +1,78 @@ +import SettingsSidebar from "@/components/SettingsSidebar"; +import { ReactNode, useEffect, useState } from "react"; +import ModalManagement from "@/components/ModalManagement"; +import useModalStore from "@/store/modals"; +import { useRouter } from "next/router"; +import ClickAwayHandler from "@/components/ClickAwayHandler"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { faBars } from "@fortawesome/free-solid-svg-icons"; + +interface Props { + children: ReactNode; +} + +export default function SettingsLayout({ children }: Props) { + const { modal } = useModalStore(); + + const router = useRouter(); + + useEffect(() => { + modal + ? (document.body.style.overflow = "hidden") + : (document.body.style.overflow = "auto"); + }, [modal]); + + const [sidebar, setSidebar] = useState(false); + + window.addEventListener("resize", () => setSidebar(false)); + + useEffect(() => { + setSidebar(false); + }, [router]); + + const toggleSidebar = () => { + setSidebar(!sidebar); + }; + + return ( + <> + + +
+
+ +
+ +
+
+
+ +
+ +

+ {router.asPath.split("/").pop()} Settings +

+
+
+ {children} + + {sidebar ? ( +
+ +
+ +
+
+
+ ) : null} +
+
+ + ); +} diff --git a/lib/api/controllers/links/getLinks.ts b/lib/api/controllers/links/getLinks.ts index be02293..ac0d057 100644 --- a/lib/api/controllers/links/getLinks.ts +++ b/lib/api/controllers/links/getLinks.ts @@ -3,138 +3,130 @@ import { LinkRequestQuery, Sort } from "@/types/global"; export default async function getLink(userId: number, body: string) { const query: LinkRequestQuery = JSON.parse(decodeURIComponent(body)); - console.log(query); const POSTGRES_IS_ENABLED = process.env.DATABASE_URL.startsWith("postgresql"); - // Sorting logic + let order: any; - if (query.sort === Sort.DateNewestFirst) - order = { - createdAt: "desc", - }; - else if (query.sort === Sort.DateOldestFirst) - order = { - createdAt: "asc", - }; - else if (query.sort === Sort.NameAZ) - order = { - name: "asc", - }; - else if (query.sort === Sort.NameZA) - order = { - name: "desc", - }; - else if (query.sort === Sort.DescriptionAZ) - order = { - description: "asc", - }; - else if (query.sort === Sort.DescriptionZA) - order = { - description: "desc", - }; + if (query.sort === Sort.DateNewestFirst) order = { createdAt: "desc" }; + else if (query.sort === Sort.DateOldestFirst) order = { createdAt: "asc" }; + else if (query.sort === Sort.NameAZ) order = { name: "asc" }; + else if (query.sort === Sort.NameZA) order = { name: "desc" }; + else if (query.sort === Sort.DescriptionAZ) order = { description: "asc" }; + else if (query.sort === Sort.DescriptionZA) order = { description: "desc" }; + + const searchConditions = []; + + if (query.searchQuery) { + if (query.searchFilter?.name) { + searchConditions.push({ + name: { + contains: query.searchQuery, + mode: POSTGRES_IS_ENABLED ? "insensitive" : undefined, + }, + }); + } + + if (query.searchFilter?.url) { + searchConditions.push({ + url: { + contains: query.searchQuery, + mode: POSTGRES_IS_ENABLED ? "insensitive" : undefined, + }, + }); + } + + if (query.searchFilter?.description) { + searchConditions.push({ + description: { + contains: query.searchQuery, + mode: POSTGRES_IS_ENABLED ? "insensitive" : undefined, + }, + }); + } + + if (query.searchFilter?.tags) { + searchConditions.push({ + tags: { + some: { + name: { + contains: query.searchQuery, + mode: POSTGRES_IS_ENABLED ? "insensitive" : undefined, + }, + OR: [ + { ownerId: userId }, + { + links: { + some: { + collection: { + members: { + some: { userId }, + }, + }, + }, + }, + }, + ], + }, + }, + }); + } + } + + const tagCondition = []; + + if (query.tagId) { + tagCondition.push({ + tags: { + some: { + id: query.tagId, + }, + }, + }); + } + + const collectionCondition = []; + + if (query.collectionId) { + collectionCondition.push({ + collection: { + id: query.collectionId, + }, + }); + } const links = await prisma.link.findMany({ take: Number(process.env.PAGINATION_TAKE_COUNT) || 20, skip: query.cursor ? 1 : undefined, - cursor: query.cursor - ? { - id: query.cursor, - } - : undefined, + cursor: query.cursor ? { id: query.cursor } : undefined, where: { - collection: { - id: query.collectionId ? query.collectionId : undefined, // If collectionId was defined, filter by collection - OR: [ - { - ownerId: userId, - }, - { - members: { - some: { - userId, + AND: [ + { + collection: { + OR: [ + { ownerId: userId }, + { + members: { + some: { userId }, + }, }, - }, - }, - ], - }, - [query.searchQuery ? "OR" : "AND"]: [ - { - pinnedBy: query.pinnedOnly ? { some: { id: userId } } : undefined, - }, - { - name: { - contains: - query.searchQuery && query.searchFilter?.name - ? query.searchQuery - : undefined, - mode: POSTGRES_IS_ENABLED ? "insensitive" : undefined, + ], }, }, + ...collectionCondition, { - url: { - contains: - query.searchQuery && query.searchFilter?.url - ? query.searchQuery - : undefined, - mode: POSTGRES_IS_ENABLED ? "insensitive" : undefined, - }, - }, - { - description: { - contains: - query.searchQuery && query.searchFilter?.description - ? query.searchQuery - : undefined, - mode: POSTGRES_IS_ENABLED ? "insensitive" : undefined, - }, - }, - { - tags: - query.searchQuery && !query.searchFilter?.tags - ? undefined - : { - some: query.tagId - ? { - // If tagId was defined, filter by tag - id: query.tagId, - name: - query.searchQuery && query.searchFilter?.tags - ? { - contains: query.searchQuery, - mode: POSTGRES_IS_ENABLED - ? "insensitive" - : undefined, - } - : undefined, - OR: [ - { ownerId: userId }, // Tags owned by the user - { - links: { - some: { - name: { - contains: - query.searchQuery && - query.searchFilter?.tags - ? query.searchQuery - : undefined, - mode: POSTGRES_IS_ENABLED - ? "insensitive" - : undefined, - }, - collection: { - members: { - some: { - userId, // Tags from collections where the user is a member - }, - }, - }, - }, - }, - }, - ], - } + OR: [ + ...tagCondition, + { + [query.searchQuery ? "OR" : "AND"]: [ + { + pinnedBy: query.pinnedOnly + ? { some: { id: userId } } : undefined, }, + ...searchConditions, + ], + }, + ], }, ], }, @@ -146,9 +138,7 @@ export default async function getLink(userId: number, body: string) { select: { id: true }, }, }, - orderBy: order || { - createdAt: "desc", - }, + orderBy: order || { createdAt: "desc" }, }); return { response: links, status: 200 }; diff --git a/lib/client/avatarExists.ts b/lib/client/avatarExists.ts index 2e82fa6..f81f1e9 100644 --- a/lib/client/avatarExists.ts +++ b/lib/client/avatarExists.ts @@ -1,4 +1,13 @@ +const avatarCache = new Map(); + export default async function avatarExists(fileUrl: string): Promise { + if (avatarCache.has(fileUrl)) { + return avatarCache.get(fileUrl); + } + const response = await fetch(fileUrl, { method: "HEAD" }); - return !(response.headers.get("content-type") === "text/html"); + const exists = !(response.headers.get("content-type") === "text/html"); + + avatarCache.set(fileUrl, exists); + return exists; } diff --git a/package.json b/package.json index 22e782b..93867e6 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,7 @@ "@auth/prisma-adapter": "^1.0.1", "@aws-sdk/client-s3": "^3.379.1", "@fortawesome/fontawesome-svg-core": "^6.4.0", + "@fortawesome/free-brands-svg-icons": "^6.4.2", "@fortawesome/free-regular-svg-icons": "^6.4.0", "@fortawesome/free-solid-svg-icons": "^6.4.0", "@fortawesome/react-fontawesome": "^0.2.0", diff --git a/pages/collections/[id].tsx b/pages/collections/[id].tsx index 34626f8..d3f6f56 100644 --- a/pages/collections/[id].tsx +++ b/pages/collections/[id].tsx @@ -229,11 +229,9 @@ export default function Index() {
{links.some((e) => e.collectionId === Number(router.query.id)) ? (
- {links - .filter((e) => e.collectionId === Number(router.query.id)) - .map((e, i) => { - return ; - })} + {links.map((e, i) => { + return ; + })}
) : ( diff --git a/pages/index.tsx b/pages/index.tsx index 7c4db0c..e4679ca 100644 --- a/pages/index.tsx +++ b/pages/index.tsx @@ -1,7 +1,7 @@ import { useRouter } from "next/router"; import { useEffect } from "react"; -export default function Home() { +export default function Index() { const router = useRouter(); useEffect(() => { diff --git a/pages/settings/appearance.tsx b/pages/settings/appearance.tsx new file mode 100644 index 0000000..5eb17f9 --- /dev/null +++ b/pages/settings/appearance.tsx @@ -0,0 +1,10 @@ +import SettingsLayout from "@/layouts/SettingsLayout"; +import React from "react"; + +export default function appearance() { + return ( + +
appearance
+
+ ); +} diff --git a/pages/settings/archive.tsx b/pages/settings/archive.tsx new file mode 100644 index 0000000..c0faa5a --- /dev/null +++ b/pages/settings/archive.tsx @@ -0,0 +1,10 @@ +import SettingsLayout from "@/layouts/SettingsLayout"; +import React from "react"; + +export default function archive() { + return ( + +
archive
+
+ ); +} diff --git a/pages/settings/billing.tsx b/pages/settings/billing.tsx new file mode 100644 index 0000000..cc9f040 --- /dev/null +++ b/pages/settings/billing.tsx @@ -0,0 +1,18 @@ +import SettingsLayout from "@/layouts/SettingsLayout"; +import { useRouter } from "next/router"; +import { useEffect } from "react"; + +export default function billing() { + const router = useRouter(); + + useEffect(() => { + if (!process.env.NEXT_PUBLIC_STRIPE_IS_ACTIVE) + router.push("/settings/profile"); + }, []); + + return ( + +
Billing
+
+ ); +} diff --git a/pages/settings/index.tsx b/pages/settings/index.tsx new file mode 100644 index 0000000..3adb156 --- /dev/null +++ b/pages/settings/index.tsx @@ -0,0 +1,10 @@ +import { useRouter } from "next/router"; +import { useEffect } from "react"; + +export default function Settings() { + const router = useRouter(); + + useEffect(() => { + router.push("/settings/profile"); + }, []); +} diff --git a/pages/settings/password.tsx b/pages/settings/password.tsx new file mode 100644 index 0000000..bffea57 --- /dev/null +++ b/pages/settings/password.tsx @@ -0,0 +1,10 @@ +import SettingsLayout from "@/layouts/SettingsLayout"; +import React from "react"; + +export default function password() { + return ( + +
password
+
+ ); +} diff --git a/pages/settings/privacy.tsx b/pages/settings/privacy.tsx new file mode 100644 index 0000000..b57f9d5 --- /dev/null +++ b/pages/settings/privacy.tsx @@ -0,0 +1,10 @@ +import SettingsLayout from "@/layouts/SettingsLayout"; +import React from "react"; + +export default function privacy() { + return ( + +
privacy
+
+ ); +} diff --git a/pages/settings/profile.tsx b/pages/settings/profile.tsx new file mode 100644 index 0000000..ae776ca --- /dev/null +++ b/pages/settings/profile.tsx @@ -0,0 +1,10 @@ +import SettingsLayout from "@/layouts/SettingsLayout"; +import React from "react"; + +export default function profile() { + return ( + +
profile
+
+ ); +} diff --git a/pages/tags/[id].tsx b/pages/tags/[id].tsx index 97a9228..4acd069 100644 --- a/pages/tags/[id].tsx +++ b/pages/tags/[id].tsx @@ -67,11 +67,9 @@ export default function Index() {
- {links - .filter((e) => e.tags.some((e) => e.id === Number(router.query.id))) - .map((e, i) => { - return ; - })} + {links.map((e, i) => { + return ; + })}
diff --git a/yarn.lock b/yarn.lock index 83ac24e..bb2f10a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -779,6 +779,11 @@ resolved "https://registry.yarnpkg.com/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-6.4.0.tgz#88da2b70d6ca18aaa6ed3687832e11f39e80624b" integrity sha512-HNii132xfomg5QVZw0HwXXpN22s7VBHQBv9CeOu9tfJnhsWQNd2lmTNi8CSrnw5B+5YOmzu1UoPAyxaXsJ6RgQ== +"@fortawesome/fontawesome-common-types@6.4.2": + version "6.4.2" + resolved "https://registry.yarnpkg.com/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-6.4.2.tgz#1766039cad33f8ad87f9467b98e0d18fbc8f01c5" + integrity sha512-1DgP7f+XQIJbLFCTX1V2QnxVmpLdKdzzo2k8EmvDOePfchaIGQ9eCHj2up3/jNEbZuBqel5OxiaOJf37TWauRA== + "@fortawesome/fontawesome-svg-core@^6.4.0": version "6.4.0" resolved "https://registry.yarnpkg.com/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-6.4.0.tgz#3727552eff9179506e9203d72feb5b1063c11a21" @@ -786,6 +791,13 @@ dependencies: "@fortawesome/fontawesome-common-types" "6.4.0" +"@fortawesome/free-brands-svg-icons@^6.4.2": + version "6.4.2" + resolved "https://registry.yarnpkg.com/@fortawesome/free-brands-svg-icons/-/free-brands-svg-icons-6.4.2.tgz#9b8e78066ea6dd563da5dfa686615791d0f7cc71" + integrity sha512-LKOwJX0I7+mR/cvvf6qIiqcERbdnY+24zgpUSouySml+5w8B4BJOx8EhDR/FTKAu06W12fmUIcv6lzPSwYKGGg== + dependencies: + "@fortawesome/fontawesome-common-types" "6.4.2" + "@fortawesome/free-regular-svg-icons@^6.4.0": version "6.4.0" resolved "https://registry.yarnpkg.com/@fortawesome/free-regular-svg-icons/-/free-regular-svg-icons-6.4.0.tgz#cacc53bd8d832d46feead412d9ea9ce80a55e13a"