diff --git a/components/CollectionCard.tsx b/components/CollectionCard.tsx index 432425c..4ff9677 100644 --- a/components/CollectionCard.tsx +++ b/components/CollectionCard.tsx @@ -1,5 +1,8 @@ import Link from "next/link"; -import { CollectionIncludingMembersAndLinkCount } from "@/types/global"; +import { + AccountSettings, + CollectionIncludingMembersAndLinkCount, +} from "@/types/global"; import React, { useEffect, useState } from "react"; import ProfilePhoto from "./ProfilePhoto"; import usePermissions from "@/hooks/usePermissions"; @@ -12,12 +15,11 @@ import { dropdownTriggerer } from "@/lib/client/utils"; import { useTranslation } from "next-i18next"; import { useUser } from "@/hooks/store/user"; -type Props = { +export default function CollectionCard({ + collection, +}: { collection: CollectionIncludingMembersAndLinkCount; - className?: string; -}; - -export default function CollectionCard({ collection, className }: Props) { +}) { const { t } = useTranslation(); const { settings } = useLocalSettingsStore(); const { data: user = {} } = useUser(); @@ -33,15 +35,9 @@ export default function CollectionCard({ collection, className }: Props) { const permissions = usePermissions(collection.id as number); - const [collectionOwner, setCollectionOwner] = useState({ - id: null as unknown as number, - name: "", - username: "", - image: "", - archiveAsScreenshot: undefined as unknown as boolean, - archiveAsMonolith: undefined as unknown as boolean, - archiveAsPDF: undefined as unknown as boolean, - }); + const [collectionOwner, setCollectionOwner] = useState< + Partial + >({}); useEffect(() => { const fetchOwner = async () => { @@ -132,12 +128,12 @@ export default function CollectionCard({ collection, className }: Props) { className="flex items-center absolute bottom-3 left-3 z-10 btn px-2 btn-ghost rounded-full" onClick={() => setEditCollectionSharingModal(true)} > - {collectionOwner.id ? ( + {collectionOwner.id && ( - ) : undefined} + )} {collection.members .sort((a, b) => (a.userId as number) - (b.userId as number)) .map((e, i) => { @@ -151,13 +147,13 @@ export default function CollectionCard({ collection, className }: Props) { ); }) .slice(0, 3)} - {collection.members.length - 3 > 0 ? ( + {collection.members.length - 3 > 0 && (
+{collection.members.length - 3}
- ) : null} + )}
- {collection.isPublic ? ( + {collection.isPublic && ( - ) : undefined} + )}
- {editCollectionModal ? ( + {editCollectionModal && ( setEditCollectionModal(false)} activeCollection={collection} /> - ) : undefined} - {editCollectionSharingModal ? ( + )} + {editCollectionSharingModal && ( setEditCollectionSharingModal(false)} activeCollection={collection} /> - ) : undefined} - {deleteCollectionModal ? ( + )} + {deleteCollectionModal && ( setDeleteCollectionModal(false)} activeCollection={collection} /> - ) : undefined} + )}
); } diff --git a/components/CollectionListing.tsx b/components/CollectionListing.tsx index e83d388..f3a722e 100644 --- a/components/CollectionListing.tsx +++ b/components/CollectionListing.tsx @@ -272,12 +272,12 @@ const renderItem = ( >

{collection.name}

- {collection.isPublic ? ( + {collection.isPublic && ( - ) : undefined} + )}
{collection._count?.links}
diff --git a/components/Dropdown.tsx b/components/Dropdown.tsx index 4a48ab7..90371d8 100644 --- a/components/Dropdown.tsx +++ b/components/Dropdown.tsx @@ -60,47 +60,49 @@ export default function Dropdown({ } }, [points, dropdownHeight]); - return !points || pos ? ( - { - setDropdownHeight(e.height); - setDropdownWidth(e.width); - }} - style={ - points - ? { - position: "fixed", - top: `${pos?.y}px`, - left: `${pos?.x}px`, - } - : undefined - } - onClickOutside={onClickOutside} - className={`${ - className || "" - } py-1 shadow-md border border-neutral-content bg-base-200 rounded-md flex flex-col z-20`} - > - {items.map((e, i) => { - const inner = e && ( -
-
-

{e.name}

+ return ( + (!points || pos) && ( + { + setDropdownHeight(e.height); + setDropdownWidth(e.width); + }} + style={ + points + ? { + position: "fixed", + top: `${pos?.y}px`, + left: `${pos?.x}px`, + } + : undefined + } + onClickOutside={onClickOutside} + className={`${ + className || "" + } py-1 shadow-md border border-neutral-content bg-base-200 rounded-md flex flex-col z-20`} + > + {items.map((e, i) => { + const inner = e && ( +
+
+

{e.name}

+
-
- ); + ); - return e && e.href ? ( - - {inner} - - ) : ( - e && ( -
+ return e && e.href ? ( + {inner} -
- ) - ); - })} - - ) : null; + + ) : ( + e && ( +
+ {inner} +
+ ) + ); + })} + + ) + ); } diff --git a/components/InputSelect/TagSelection.tsx b/components/InputSelect/TagSelection.tsx index efd246b..65e7499 100644 --- a/components/InputSelect/TagSelection.tsx +++ b/components/InputSelect/TagSelection.tsx @@ -34,7 +34,6 @@ export default function TagSelection({ onChange, defaultValue }: Props) { options={options} styles={styles} defaultValue={defaultValue} - // menuPosition="fixed" isMulti /> ); diff --git a/components/LinkViews/LinkComponents/LinkActions.tsx b/components/LinkViews/LinkComponents/LinkActions.tsx index 837cd6f..f93f969 100644 --- a/components/LinkViews/LinkComponents/LinkActions.tsx +++ b/components/LinkViews/LinkComponents/LinkActions.tsx @@ -136,7 +136,7 @@ export default function LinkActions({ {t("show_link_details")}
- {permissions === true || permissions?.canUpdate ? ( + {(permissions === true || permissions?.canUpdate) && (
  • - ) : undefined} + )} {link.type === "url" && (
  • )} - {permissions === true || permissions?.canDelete ? ( + {(permissions === true || permissions?.canDelete) && (
  • - ) : undefined} + )} )} - - {editLinkModal ? ( + {editLinkModal && ( setEditLinkModal(false)} activeLink={link} /> - ) : undefined} - {deleteLinkModal ? ( + )} + {deleteLinkModal && ( setDeleteLinkModal(false)} activeLink={link} /> - ) : undefined} - {preservedFormatsModal ? ( + )} + {preservedFormatsModal && ( setPreservedFormatsModal(false)} link={link} /> - ) : undefined} - {linkDetailModal ? ( + )} + {linkDetailModal && ( setLinkDetailModal(false)} onEdit={() => setEditLinkModal(true)} link={link} /> - ) : undefined} + )} ); } diff --git a/components/LinkViews/LinkComponents/LinkCard.tsx b/components/LinkViews/LinkComponents/LinkCard.tsx index c6b60c5..f1e32df 100644 --- a/components/LinkViews/LinkComponents/LinkCard.tsx +++ b/components/LinkViews/LinkComponents/LinkCard.tsx @@ -96,7 +96,7 @@ export default function LinkCard({ link, flipDropdown, editMode }: Props) { let isPublic = router.pathname.startsWith("/public") ? true : undefined; useEffect(() => { - let interval: any; + let interval: NodeJS.Timeout | null = null; if ( isVisible && diff --git a/components/LinkViews/LinkComponents/LinkList.tsx b/components/LinkViews/LinkComponents/LinkList.tsx index 59e6e2d..31a214b 100644 --- a/components/LinkViews/LinkComponents/LinkList.tsx +++ b/components/LinkViews/LinkComponents/LinkList.tsx @@ -127,9 +127,9 @@ export default function LinkCardCompact({
    - {collection ? ( + {collection && ( - ) : undefined} + )} {link.name && }
    diff --git a/components/LinkViews/LinkComponents/LinkMasonry.tsx b/components/LinkViews/LinkComponents/LinkMasonry.tsx index ee63244..4dd70f1 100644 --- a/components/LinkViews/LinkComponents/LinkMasonry.tsx +++ b/components/LinkViews/LinkComponents/LinkMasonry.tsx @@ -88,7 +88,7 @@ export default function LinkMasonry({ link, flipDropdown, editMode }: Props) { const permissions = usePermissions(collection?.id as number); useEffect(() => { - let interval: any; + let interval: NodeJS.Timeout | null = null; if ( isVisible && diff --git a/components/MobileNavigation.tsx b/components/MobileNavigation.tsx index 114e8ea..22f3321 100644 --- a/components/MobileNavigation.tsx +++ b/components/MobileNavigation.tsx @@ -87,15 +87,13 @@ export default function MobileNavigation({}: Props) {
    - {newLinkModal ? ( - setNewLinkModal(false)} /> - ) : undefined} - {newCollectionModal ? ( + {newLinkModal && setNewLinkModal(false)} />} + {newCollectionModal && ( setNewCollectionModal(false)} /> - ) : undefined} - {uploadFileModal ? ( + )} + {uploadFileModal && ( setUploadFileModal(false)} /> - ) : undefined} + )} ); } diff --git a/components/ModalContent/EditCollectionSharingModal.tsx b/components/ModalContent/EditCollectionSharingModal.tsx index 321e672..04c44b0 100644 --- a/components/ModalContent/EditCollectionSharingModal.tsx +++ b/components/ModalContent/EditCollectionSharingModal.tsx @@ -1,7 +1,11 @@ import React, { useEffect, useState } from "react"; import TextInput from "@/components/TextInput"; import toast from "react-hot-toast"; -import { CollectionIncludingMembersAndLinkCount, Member } from "@/types/global"; +import { + AccountSettings, + CollectionIncludingMembersAndLinkCount, + Member, +} from "@/types/global"; import getPublicUserData from "@/lib/client/getPublicUserData"; import usePermissions from "@/hooks/usePermissions"; import ProfilePhoto from "../ProfilePhoto"; @@ -65,15 +69,9 @@ export default function EditCollectionSharingModal({ const [memberUsername, setMemberUsername] = useState(""); - const [collectionOwner, setCollectionOwner] = useState({ - id: null as unknown as number, - name: "", - username: "", - image: "", - archiveAsScreenshot: undefined as unknown as boolean, - archiveAsMonolith: undefined as unknown as boolean, - archiveAsPDF: undefined as unknown as boolean, - }); + const [collectionOwner, setCollectionOwner] = useState< + Partial + >({}); useEffect(() => { const fetchOwner = async () => { @@ -133,7 +131,7 @@ export default function EditCollectionSharingModal({ )} - {collection.isPublic ? ( + {collection.isPublic && (

    {t("sharable_link_guide")}

    @@ -141,7 +139,7 @@ export default function EditCollectionSharingModal({
    - ) : null} + )} {permissions === true &&
    } diff --git a/components/ModalContent/EditLinkModal.tsx b/components/ModalContent/EditLinkModal.tsx index 2d9711b..07911f4 100644 --- a/components/ModalContent/EditLinkModal.tsx +++ b/components/ModalContent/EditLinkModal.tsx @@ -77,7 +77,7 @@ export default function EditLinkModal({ onClose, activeLink }: Props) {
    - {link.url ? ( + {link.url && (

    {shortenedURL}

    - ) : undefined} + )}

    {t("name")}

    @@ -103,7 +103,7 @@ export default function EditLinkModal({ onClose, activeLink }: Props) {

    {t("collection")}

    - {link.collection.name ? ( + {link.collection.name && ( - ) : null} + )}
    diff --git a/components/ModalContent/NewLinkModal.tsx b/components/ModalContent/NewLinkModal.tsx index ff628bf..61ee8bc 100644 --- a/components/ModalContent/NewLinkModal.tsx +++ b/components/ModalContent/NewLinkModal.tsx @@ -124,7 +124,7 @@ export default function NewLinkModal({ onClose }: Props) {

    {t("collection")}

    - {link.collection.name ? ( + {link.collection.name && ( - ) : null} + )}
    - {optionsExpanded ? ( + {optionsExpanded && (
    @@ -171,7 +171,7 @@ export default function NewLinkModal({ onClose }: Props) {
    - ) : undefined} + )}
    - {emailEnabled ? ( + {emailEnabled && (

    {t("email")}

    - ) : undefined} + )}

    diff --git a/components/ModalContent/PreservedFormatsModal.tsx b/components/ModalContent/PreservedFormatsModal.tsx index 309170a..d883a21 100644 --- a/components/ModalContent/PreservedFormatsModal.tsx +++ b/components/ModalContent/PreservedFormatsModal.tsx @@ -2,6 +2,7 @@ import React, { useEffect, useState } from "react"; import { LinkIncludingShortenedCollectionAndTags, ArchivedFormat, + AccountSettings, } from "@/types/global"; import toast from "react-hot-toast"; import Link from "next/link"; @@ -35,15 +36,9 @@ export default function PreservedFormatsModal({ onClose, link }: Props) { let isPublic = router.pathname.startsWith("/public") ? true : undefined; - const [collectionOwner, setCollectionOwner] = useState({ - id: null as unknown as number, - name: "", - username: "", - image: "", - archiveAsScreenshot: undefined as unknown as boolean, - archiveAsMonolith: undefined as unknown as boolean, - archiveAsPDF: undefined as unknown as boolean, - }); + const [collectionOwner, setCollectionOwner] = useState< + Partial + >({}); useEffect(() => { const fetchOwner = async () => { @@ -99,7 +94,7 @@ export default function PreservedFormatsModal({ onClose, link }: Props) { await getLink.mutateAsync({ id: link.id as number }); })(); - let interval: any; + let interval: NodeJS.Timeout | null = null; if (!isReady()) { interval = setInterval(async () => { @@ -149,7 +144,7 @@ export default function PreservedFormatsModal({ onClose, link }: Props) { )}

    - {monolithAvailable(link) ? ( + {monolithAvailable(link) && ( - ) : undefined} + )} - {screenshotAvailable(link) ? ( + {screenshotAvailable(link) && ( - ) : undefined} + )} - {pdfAvailable(link) ? ( + {pdfAvailable(link) && ( - ) : undefined} + )} - {readabilityAvailable(link) ? ( + {readabilityAvailable(link) && ( - ) : undefined} + )} {!isReady() && !atLeastOneFormatAvailable() ? (
    @@ -203,17 +198,20 @@ export default function PreservedFormatsModal({ onClose, link }: Props) {

    {t("preservation_in_queue")}

    {t("check_back_later")}

    - ) : !isReady() && atLeastOneFormatAvailable() ? ( -
    - -

    {t("there_are_more_formats")}

    -

    {t("check_back_later")}

    -
    - ) : undefined} + ) : ( + !isReady() && + atLeastOneFormatAvailable() && ( +
    + +

    {t("there_are_more_formats")}

    +

    {t("check_back_later")}

    +
    + ) + )}
    e.target.files && setFile(e.target.files[0])} /> @@ -163,7 +163,7 @@ export default function UploadFileModal({ onClose }: Props) {

    {t("collection")}

    - {link.collection.name ? ( + {link.collection.name && ( - ) : null} + )}
    - {optionsExpanded ? ( + {optionsExpanded && (
    @@ -209,7 +209,7 @@ export default function UploadFileModal({ onClose }: Props) {
    - ) : undefined} + )}
    setOptionsExpanded(!optionsExpanded)} diff --git a/components/Navbar.tsx b/components/Navbar.tsx index 5a09612..2fe2096 100644 --- a/components/Navbar.tsx +++ b/components/Navbar.tsx @@ -114,7 +114,7 @@ export default function Navbar() { - {sidebar ? ( + {sidebar && (
    @@ -122,16 +122,14 @@ export default function Navbar() {
    - ) : null} - {newLinkModal ? ( - setNewLinkModal(false)} /> - ) : undefined} - {newCollectionModal ? ( + )} + {newLinkModal && setNewLinkModal(false)} />} + {newCollectionModal && ( setNewCollectionModal(false)} /> - ) : undefined} - {uploadFileModal ? ( + )} + {uploadFileModal && ( setUploadFileModal(false)} /> - ) : undefined} + )}
    ); } diff --git a/components/NoLinksFound.tsx b/components/NoLinksFound.tsx index 516dfb6..a8ec44d 100644 --- a/components/NoLinksFound.tsx +++ b/components/NoLinksFound.tsx @@ -39,9 +39,7 @@ export default function NoLinksFound({ text }: Props) {
    - {newLinkModal ? ( - setNewLinkModal(false)} /> - ) : undefined} + {newLinkModal && setNewLinkModal(false)} />}
    ); } diff --git a/components/PreserverdFormatRow.tsx b/components/PreserverdFormatRow.tsx index 45b156f..7919807 100644 --- a/components/PreserverdFormatRow.tsx +++ b/components/PreserverdFormatRow.tsx @@ -4,7 +4,6 @@ import { } from "@/types/global"; import Link from "next/link"; import { useRouter } from "next/router"; -import { useGetLink } from "@/hooks/store/links"; type Props = { name: string; @@ -21,8 +20,6 @@ export default function PreservedFormatRow({ link, downloadable, }: Props) { - const getLink = useGetLink(); - const router = useRouter(); let isPublic = router.pathname.startsWith("/public") ? true : undefined; diff --git a/components/ProfileDropdown.tsx b/components/ProfileDropdown.tsx index 11d71a3..80d5119 100644 --- a/components/ProfileDropdown.tsx +++ b/components/ProfileDropdown.tsx @@ -60,7 +60,7 @@ export default function ProfileDropdown() { })}
    - {isAdmin ? ( + {isAdmin && (
  • - ) : null} + )}
  • { diff --git a/components/ReadableView.tsx b/components/ReadableView.tsx index def3185..18477a0 100644 --- a/components/ReadableView.tsx +++ b/components/ReadableView.tsx @@ -68,7 +68,7 @@ export default function ReadableView({ link }: Props) { useEffect(() => { if (link) getLink.mutateAsync({ id: link.id as number }); - let interval: any; + let interval: NodeJS.Timeout | null = null; if ( link && (link?.image === "pending" || @@ -182,7 +182,7 @@ export default function ReadableView({ link }: Props) { link?.name || link?.description || link?.url || "" )}

    - {link?.url ? ( + {link?.url && ( - {isValidUrl(link?.url || "") - ? new URL(link?.url as string).host - : undefined} + {isValidUrl(link?.url || "") && + new URL(link?.url as string).host} - ) : undefined} + )}
    diff --git a/components/ToggleDarkMode.tsx b/components/ToggleDarkMode.tsx index 2b9e457..28631b9 100644 --- a/components/ToggleDarkMode.tsx +++ b/components/ToggleDarkMode.tsx @@ -1,5 +1,5 @@ import useLocalSettingsStore from "@/store/localSettings"; -import { useEffect, useState } from "react"; +import { useEffect, useState, ChangeEvent } from "react"; import { useTranslation } from "next-i18next"; type Props = { @@ -10,14 +10,18 @@ export default function ToggleDarkMode({ className }: Props) { const { t } = useTranslation(); const { settings, updateSettings } = useLocalSettingsStore(); - const [theme, setTheme] = useState(localStorage.getItem("theme")); + const [theme, setTheme] = useState( + localStorage.getItem("theme") + ); - const handleToggle = (e: any) => { + const handleToggle = (e: ChangeEvent) => { setTheme(e.target.checked ? "dark" : "light"); }; useEffect(() => { - updateSettings({ theme: theme as string }); + if (theme) { + updateSettings({ theme }); + } }, [theme]); return ( @@ -34,7 +38,7 @@ export default function ToggleDarkMode({ className }: Props) { type="checkbox" onChange={handleToggle} className="theme-controller" - checked={localStorage.getItem("theme") === "light" ? false : true} + checked={theme === "dark"} /> diff --git a/components/UserListing.tsx b/components/UserListing.tsx index 378ad98..46c6e75 100644 --- a/components/UserListing.tsx +++ b/components/UserListing.tsx @@ -74,12 +74,12 @@ const UserListing = ( - {deleteUserModal.isOpen && deleteUserModal.userId ? ( + {deleteUserModal.isOpen && deleteUserModal.userId && ( setDeleteUserModal({ isOpen: false, userId: null })} userId={deleteUserModal.userId} /> - ) : null} + )} ); }; diff --git a/e2e/data/user.ts b/e2e/data/user.ts index 3ae0b73..cc155ae 100644 --- a/e2e/data/user.ts +++ b/e2e/data/user.ts @@ -2,19 +2,17 @@ import axios, { AxiosError } from "axios" axios.defaults.baseURL = "http://localhost:3000" -export async function seedUser (username?: string, password?: string, name?: string) { +export async function seedUser(username?: string, password?: string, name?: string) { try { return await axios.post("/api/v1/users", { username: username || "test", password: password || "password", name: name || "Test User", }) - } catch (e: any) { - if (e instanceof AxiosError) { - if (e.response?.status === 400) { - return - } - } - throw e + } catch (error) { + const axiosError = error as AxiosError; + if (axiosError && axiosError.response?.status === 400) return + + throw error } } \ No newline at end of file diff --git a/layouts/CenteredForm.tsx b/layouts/CenteredForm.tsx index 841036b..5367dd6 100644 --- a/layouts/CenteredForm.tsx +++ b/layouts/CenteredForm.tsx @@ -23,7 +23,7 @@ export default function CenteredForm({ data-testid={dataTestId} >
    - {settings.theme ? ( + {settings.theme && ( Linkwarden - ) : undefined} - {text ? ( + )} + {text && (

    {text}

    - ) : undefined} + )} {children}

    - {showAnnouncement ? ( + {showAnnouncement && ( - ) : undefined} + )}

    diff --git a/layouts/SettingsLayout.tsx b/layouts/SettingsLayout.tsx index cae3534..5a037b5 100644 --- a/layouts/SettingsLayout.tsx +++ b/layouts/SettingsLayout.tsx @@ -54,7 +54,7 @@ export default function SettingsLayout({ children }: Props) { {children} - {sidebar ? ( + {sidebar && (
    - ) : null} + )}
    diff --git a/lib/api/controllers/dashboard/getDashboardData.ts b/lib/api/controllers/dashboard/getDashboardData.ts index bb2f9e4..35b39d1 100644 --- a/lib/api/controllers/dashboard/getDashboardData.ts +++ b/lib/api/controllers/dashboard/getDashboardData.ts @@ -1,11 +1,11 @@ import { prisma } from "@/lib/api/db"; -import { LinkRequestQuery, Sort } from "@/types/global"; +import { LinkRequestQuery, Order, Sort } from "@/types/global"; export default async function getDashboardData( userId: number, query: LinkRequestQuery ) { - let order: any = { id: "desc" }; + let order: Order = { id: "desc" }; if (query.sort === Sort.DateNewestFirst) order = { id: "desc" }; else if (query.sort === Sort.DateOldestFirst) order = { id: "asc" }; else if (query.sort === Sort.NameAZ) order = { name: "asc" }; diff --git a/lib/api/controllers/dashboard/getDashboardDataV2.ts b/lib/api/controllers/dashboard/getDashboardDataV2.ts index 6d2fb02..fa952c5 100644 --- a/lib/api/controllers/dashboard/getDashboardDataV2.ts +++ b/lib/api/controllers/dashboard/getDashboardDataV2.ts @@ -1,5 +1,5 @@ import { prisma } from "@/lib/api/db"; -import { LinkRequestQuery, Sort } from "@/types/global"; +import { LinkRequestQuery, Order, Sort } from "@/types/global"; type Response = | { @@ -17,7 +17,7 @@ export default async function getDashboardData( userId: number, query: LinkRequestQuery ): Promise> { - let order: any; + let order: Order = { id: "desc" }; if (query.sort === Sort.DateNewestFirst) order = { id: "desc" }; else if (query.sort === Sort.DateOldestFirst) order = { id: "asc" }; else if (query.sort === Sort.NameAZ) order = { name: "asc" }; @@ -105,9 +105,8 @@ export default async function getDashboardData( }); const links = [...recentlyAddedLinks, ...pinnedLinks].sort( - (a, b) => (new Date(b.id) as any) - (new Date(a.id) as any) + (a, b) => new Date(b.id).getTime() - new Date(a.id).getTime() ); - return { data: { links, diff --git a/lib/api/controllers/links/getLinks.ts b/lib/api/controllers/links/getLinks.ts index a6b1dab..32d16d2 100644 --- a/lib/api/controllers/links/getLinks.ts +++ b/lib/api/controllers/links/getLinks.ts @@ -1,11 +1,11 @@ import { prisma } from "@/lib/api/db"; -import { LinkRequestQuery, Sort } from "@/types/global"; +import { LinkRequestQuery, Order, Sort } from "@/types/global"; export default async function getLink(userId: number, query: LinkRequestQuery) { const POSTGRES_IS_ENABLED = process.env.DATABASE_URL?.startsWith("postgresql"); - let order: any = { id: "desc" }; + let order: Order = { id: "desc" }; if (query.sort === Sort.DateNewestFirst) order = { id: "desc" }; else if (query.sort === Sort.DateOldestFirst) order = { id: "asc" }; else if (query.sort === Sort.NameAZ) order = { name: "asc" }; diff --git a/lib/api/controllers/migration/exportData.ts b/lib/api/controllers/migration/exportData.ts index 73141fd..917406c 100644 --- a/lib/api/controllers/migration/exportData.ts +++ b/lib/api/controllers/migration/exportData.ts @@ -22,18 +22,5 @@ export default async function exportData(userId: number) { const { password, id, ...userData } = user; - function redactIds(obj: any) { - if (Array.isArray(obj)) { - obj.forEach((o) => redactIds(o)); - } else if (obj !== null && typeof obj === "object") { - delete obj.id; - for (let key in obj) { - redactIds(obj[key]); - } - } - } - - redactIds(userData); - return { response: userData, status: 200 }; } diff --git a/lib/api/controllers/migration/importFromWallabag.ts b/lib/api/controllers/migration/importFromWallabag.ts index 568952a..b723d3f 100644 --- a/lib/api/controllers/migration/importFromWallabag.ts +++ b/lib/api/controllers/migration/importFromWallabag.ts @@ -1,5 +1,4 @@ import { prisma } from "@/lib/api/db"; -import { Backup } from "@/types/global"; import createFolder from "@/lib/api/storage/createFolder"; const MAX_LINKS_PER_USER = Number(process.env.MAX_LINKS_PER_USER) || 30000; diff --git a/lib/api/controllers/public/links/getPublicLinksUnderCollection.ts b/lib/api/controllers/public/links/getPublicLinksUnderCollection.ts index d93e804..b03de3a 100644 --- a/lib/api/controllers/public/links/getPublicLinksUnderCollection.ts +++ b/lib/api/controllers/public/links/getPublicLinksUnderCollection.ts @@ -1,5 +1,5 @@ import { prisma } from "@/lib/api/db"; -import { LinkRequestQuery, Sort } from "@/types/global"; +import { LinkRequestQuery, Order, Sort } from "@/types/global"; export default async function getLink( query: Omit @@ -7,7 +7,7 @@ export default async function getLink( const POSTGRES_IS_ENABLED = process.env.DATABASE_URL?.startsWith("postgresql"); - let order: any; + let order: Order = { id: "desc" }; if (query.sort === Sort.DateNewestFirst) order = { id: "desc" }; else if (query.sort === Sort.DateOldestFirst) order = { id: "asc" }; else if (query.sort === Sort.NameAZ) order = { name: "asc" }; diff --git a/lib/api/controllers/session/createSession.ts b/lib/api/controllers/session/createSession.ts index c1a6aa7..178c42d 100644 --- a/lib/api/controllers/session/createSession.ts +++ b/lib/api/controllers/session/createSession.ts @@ -29,7 +29,7 @@ export default async function createSession( secret: process.env.NEXTAUTH_SECRET as string, }); - const createToken = await prisma.accessToken.create({ + await prisma.accessToken.create({ data: { name: sessionName || "Unknown Device", userId, diff --git a/lib/api/controllers/users/userId/deleteUserById.ts b/lib/api/controllers/users/userId/deleteUserById.ts index a6e6106..a481893 100644 --- a/lib/api/controllers/users/userId/deleteUserById.ts +++ b/lib/api/controllers/users/userId/deleteUserById.ts @@ -24,10 +24,7 @@ export default async function deleteUserById( if (!isServerAdmin) { if (user.password) { - const isPasswordValid = bcrypt.compareSync( - body.password, - user.password as string - ); + const isPasswordValid = bcrypt.compareSync(body.password, user.password); if (!isPasswordValid && !isServerAdmin) { return { diff --git a/lib/api/generatePreview.ts b/lib/api/generatePreview.ts index 2dc325b..1c6ca94 100644 --- a/lib/api/generatePreview.ts +++ b/lib/api/generatePreview.ts @@ -16,7 +16,7 @@ const generatePreview = async ( return; } - image.resize(1280, Jimp.AUTO).quality(20); + image.resize(1000, Jimp.AUTO).quality(20); const processedBuffer = await image.getBufferAsync(Jimp.MIME_JPEG); if ( diff --git a/lib/api/storage/moveFile.ts b/lib/api/storage/moveFile.ts index c86728f..c5aedb6 100644 --- a/lib/api/storage/moveFile.ts +++ b/lib/api/storage/moveFile.ts @@ -14,7 +14,7 @@ export default async function moveFile(from: string, to: string) { }; try { - s3Client.copyObject(copyParams, async (err: any) => { + s3Client.copyObject(copyParams, async (err: unknown) => { if (err) { console.error("Error copying the object:", err); } else { diff --git a/lib/client/resizeImage.ts b/lib/client/resizeImage.ts index 17cbe56..f229c48 100644 --- a/lib/client/resizeImage.ts +++ b/lib/client/resizeImage.ts @@ -9,7 +9,7 @@ export const resizeImage = (file: File): Promise => "JPEG", // output format 100, // quality 0, // rotation - (uri: any) => { + (uri) => { resolve(uri as Blob); }, "blob" // output type diff --git a/lib/client/utils.ts b/lib/client/utils.ts index e067933..c1aa219 100644 --- a/lib/client/utils.ts +++ b/lib/client/utils.ts @@ -7,10 +7,15 @@ export function isPWA() { } export function isIphone() { - return /iPhone/.test(navigator.userAgent) && !(window as any).MSStream; + return ( + /iPhone/.test(navigator.userAgent) && + !(window as unknown as { MSStream?: any }).MSStream + ); } -export function dropdownTriggerer(e: any) { +export function dropdownTriggerer( + e: React.FocusEvent | React.MouseEvent +) { let targetEl = e.currentTarget; if (targetEl && targetEl.matches(":focus")) { setTimeout(function () { diff --git a/lib/shared/getArchiveValidity.ts b/lib/shared/getArchiveValidity.ts index 8ec8c44..fc553f8 100644 --- a/lib/shared/getArchiveValidity.ts +++ b/lib/shared/getArchiveValidity.ts @@ -39,7 +39,9 @@ export function monolithAvailable( ); } -export function previewAvailable(link: any) { +export function previewAvailable( + link: LinkIncludingShortenedCollectionAndTags +) { return ( link && link.preview && diff --git a/pages/admin.tsx b/pages/admin.tsx index df62ffe..31978b6 100644 --- a/pages/admin.tsx +++ b/pages/admin.tsx @@ -100,9 +100,7 @@ export default function Admin() {

    {t("no_users_found")}

    )} - {newUserModal ? ( - setNewUserModal(false)} /> - ) : null} + {newUserModal && setNewUserModal(false)} />} ); } diff --git a/pages/api/v1/archives/[linkId].ts b/pages/api/v1/archives/[linkId].ts index aed8288..e2a08d1 100644 --- a/pages/api/v1/archives/[linkId].ts +++ b/pages/api/v1/archives/[linkId].ts @@ -166,8 +166,12 @@ export default async function Index(req: NextApiRequest, res: NextApiResponse) { where: { id: linkId }, }); - if (linkStillExists && files.file[0].mimetype?.includes("image")) { - const collectionId = collectionPermissions.id as number; + const { mimetype } = files.file[0]; + const isPDF = mimetype?.includes("pdf"); + const isImage = mimetype?.includes("image"); + + if (linkStillExists && isImage) { + const collectionId = collectionPermissions.id; createFolder({ filePath: `archives/preview/${collectionId}`, }); @@ -184,13 +188,11 @@ export default async function Index(req: NextApiRequest, res: NextApiResponse) { await prisma.link.update({ where: { id: linkId }, data: { - preview: files.file[0].mimetype?.includes("pdf") - ? "unavailable" - : undefined, - image: files.file[0].mimetype?.includes("image") + preview: isPDF ? "unavailable" : undefined, + image: isImage ? `archives/${collectionPermissions.id}/${linkId + suffix}` : null, - pdf: files.file[0].mimetype?.includes("pdf") + pdf: isPDF ? `archives/${collectionPermissions.id}/${linkId + suffix}` : null, lastPreserved: new Date().toISOString(), diff --git a/pages/collections/[id].tsx b/pages/collections/[id].tsx index f8e24b3..e3fe442 100644 --- a/pages/collections/[id].tsx +++ b/pages/collections/[id].tsx @@ -1,4 +1,5 @@ import { + AccountSettings, CollectionIncludingMembersAndLinkCount, Sort, ViewMode, @@ -54,15 +55,9 @@ export default function Index() { const { data: user = {} } = useUser(); - const [collectionOwner, setCollectionOwner] = useState({ - id: null as unknown as number, - name: "", - username: "", - image: "", - archiveAsScreenshot: undefined as unknown as boolean, - archiveAsMonolith: undefined as unknown as boolean, - archiveAsPDF: undefined as unknown as boolean, - }); + const [collectionOwner, setCollectionOwner] = useState< + Partial + >({}); useEffect(() => { const fetchOwner = async () => { @@ -207,14 +202,14 @@ export default function Index() { className="flex items-center btn px-2 btn-ghost rounded-full w-fit" onClick={() => setEditCollectionSharingModal(true)} > - {collectionOwner.id ? ( + {collectionOwner.id && ( - ) : undefined} + )} {activeCollection.members - .sort((a, b) => (a.userId as number) - (b.userId as number)) + .sort((a, b) => a.userId - b.userId) .map((e, i) => { return ( 0 ? ( + {activeCollection.members.length - 3 > 0 && (
    +{activeCollection.members.length - 3}
    - ) : null} + )}

    diff --git a/pages/collections/index.tsx b/pages/collections/index.tsx index 051d02a..5975923 100644 --- a/pages/collections/index.tsx +++ b/pages/collections/index.tsx @@ -60,7 +60,7 @@ export default function Collections() { - {sortedCollections.filter((e) => e.ownerId !== data?.user.id)[0] ? ( + {sortedCollections.filter((e) => e.ownerId !== data?.user.id)[0] && ( <> - ) : undefined} + )} - {newCollectionModal ? ( + {newCollectionModal && ( setNewCollectionModal(false)} /> - ) : undefined} + )} ); } diff --git a/pages/dashboard.tsx b/pages/dashboard.tsx index 7f59a3b..0bdb525 100644 --- a/pages/dashboard.tsx +++ b/pages/dashboard.tsx @@ -55,11 +55,14 @@ export default function Dashboard() { handleNumberOfLinksToShow(); }, [width]); - const importBookmarks = async (e: any, format: MigrationFormat) => { - const file: File = e.target.files[0]; + const importBookmarks = async ( + e: React.ChangeEvent, + format: MigrationFormat + ) => { + const file: File | null = e.target.files && e.target.files[0]; if (file) { - var reader = new FileReader(); + const reader = new FileReader(); reader.readAsText(file, "UTF-8"); reader.onload = async function (e) { const load = toast.loading("Importing..."); @@ -132,7 +135,7 @@ export default function Dashboard() { @@ -339,9 +342,7 @@ export default function Dashboard() { )} - {newLinkModal ? ( - setNewLinkModal(false)} /> - ) : undefined} + {newLinkModal && setNewLinkModal(false)} />} ); } diff --git a/pages/login.tsx b/pages/login.tsx index c1bc8d8..e1b530c 100644 --- a/pages/login.tsx +++ b/pages/login.tsx @@ -203,9 +203,9 @@ export default function Login({ {t("login")} - {availableLogins.buttonAuths.length > 0 ? ( + {availableLogins.buttonAuths.length > 0 && (

    {t("or_continue_with")}
    - ) : undefined} + )} ); } @@ -224,9 +224,9 @@ export default function Login({ loading={submitLoader} > {value.name.toLowerCase() === "google" || - value.name.toLowerCase() === "apple" ? ( - - ) : undefined} + (value.name.toLowerCase() === "apple" && ( + + ))} {value.name} diff --git a/pages/public/collections/[id].tsx b/pages/public/collections/[id].tsx index 53efbb3..d49660f 100644 --- a/pages/public/collections/[id].tsx +++ b/pages/public/collections/[id].tsx @@ -1,6 +1,7 @@ "use client"; import getPublicCollectionData from "@/lib/client/getPublicCollectionData"; import { + AccountSettings, CollectionIncludingMembersAndLinkCount, Sort, ViewMode, @@ -29,15 +30,9 @@ export default function PublicCollections() { const router = useRouter(); - const [collectionOwner, setCollectionOwner] = useState({ - id: null as unknown as number, - name: "", - username: "", - image: "", - archiveAsScreenshot: undefined as unknown as boolean, - archiveAsMonolith: undefined as unknown as boolean, - archiveAsPDF: undefined as unknown as boolean, - }); + const [collectionOwner, setCollectionOwner] = useState< + Partial + >({}); const [searchFilter, setSearchFilter] = useState({ name: true, @@ -93,160 +88,162 @@ export default function PublicCollections() { (localStorage.getItem("viewMode") as ViewMode) || ViewMode.Card ); - return collection ? ( -
    - {collection ? ( - - {collection.name} | Linkwarden - - - ) : undefined} -
    -
    -

    - {collection.name} -

    -
    - + if (!collection) return <>; + else + return ( +
    + {collection && ( + + {collection.name} | Linkwarden + + + )} +
    +
    +

    + {collection.name} +

    +
    + - - Linkwarden - + + Linkwarden + +
    -
    -
    -
    -
    -
    setEditCollectionSharingModal(true)} - > - {collectionOwner.id ? ( - - ) : undefined} - {collection.members - .sort((a, b) => (a.userId as number) - (b.userId as number)) - .map((e, i) => { - return ( - - ); - }) - .slice(0, 3)} - {collection.members.length - 3 > 0 ? ( -
    -
    - +{collection.members.length - 3} -
    -
    - ) : null} -
    - -

    - {collection.members.length > 0 && - collection.members.length === 1 - ? t("by_author_and_other", { - author: collectionOwner.name, - count: collection.members.length, +

    +
    +
    +
    setEditCollectionSharingModal(true)} + > + {collectionOwner.id && ( + + )} + {collection.members + .sort((a, b) => (a.userId as number) - (b.userId as number)) + .map((e, i) => { + return ( + + ); }) - : collection.members.length > 0 && - collection.members.length !== 1 - ? t("by_author_and_others", { + .slice(0, 3)} + {collection.members.length - 3 > 0 && ( +
    +
    + +{collection.members.length - 3} +
    +
    + )} +
    + +

    + {collection.members.length > 0 && + collection.members.length === 1 + ? t("by_author_and_other", { author: collectionOwner.name, count: collection.members.length, }) - : t("by_author", { - author: collectionOwner.name, - })} -

    + : collection.members.length > 0 && + collection.members.length !== 1 + ? t("by_author_and_others", { + author: collectionOwner.name, + count: collection.members.length, + }) + : t("by_author", { + author: collectionOwner.name, + })} +

    +
    -
    -

    {collection.description}

    +

    {collection.description}

    -
    +
    -
    - - + + + + + { + const linkWithCollectionData = { + ...e, + collection: collection, // Append collection data + }; + return linkWithCollectionData; + }) as any } + layout={viewMode} + placeholderCount={1} + useData={data} /> - + {!data.isLoading && links && !links[0] && ( +

    {t("nothing_found")}

    + )} - { - const linkWithCollectionData = { - ...e, - collection: collection, // Append collection data - }; - return linkWithCollectionData; - }) as any - } - layout={viewMode} - placeholderCount={1} - useData={data} - /> - {!data.isLoading && links && !links[0] &&

    {t("nothing_found")}

    } - - {/*

    + {/*

    List created with Linkwarden.

    */} +
    + {editCollectionSharingModal && ( + setEditCollectionSharingModal(false)} + activeCollection={collection} + /> + )}
    - {editCollectionSharingModal ? ( - setEditCollectionSharingModal(false)} - activeCollection={collection} - /> - ) : undefined} -
    - ) : ( - <> - ); + ); } export { getServerSideProps }; diff --git a/pages/register.tsx b/pages/register.tsx index 4eba2bc..282558b 100644 --- a/pages/register.tsx +++ b/pages/register.tsx @@ -133,9 +133,9 @@ export default function Register({ loading={submitLoader} > {value.name.toLowerCase() === "google" || - value.name.toLowerCase() === "apple" ? ( - - ) : undefined} + (value.name.toLowerCase() === "apple" && ( + + ))} {value.name} @@ -201,7 +201,7 @@ export default function Register({
    )} - {emailEnabled ? ( + {emailEnabled && (

    {t("email")}

    @@ -214,7 +214,7 @@ export default function Register({ onChange={(e) => setForm({ ...form, email: e.target.value })} />
    - ) : undefined} + )}

    @@ -248,7 +248,7 @@ export default function Register({ />

    - {process.env.NEXT_PUBLIC_STRIPE ? ( + {process.env.NEXT_PUBLIC_STRIPE && (

    - ) : undefined} + )}
    diff --git a/pages/settings/access-tokens.tsx b/pages/settings/access-tokens.tsx index 9e1cfd6..f75789d 100644 --- a/pages/settings/access-tokens.tsx +++ b/pages/settings/access-tokens.tsx @@ -40,7 +40,7 @@ export default function AccessTokens() { {t("new_token")} - {tokens.length > 0 ? ( + {tokens.length > 0 && ( @@ -85,12 +85,12 @@ export default function AccessTokens() { ))}
    - ) : undefined} + )}
    - {newTokenModal ? ( + {newTokenModal && ( setNewTokenModal(false)} /> - ) : undefined} + )} {revokeTokenModal && selectedToken && ( { diff --git a/pages/settings/account.tsx b/pages/settings/account.tsx index 2feddb3..70d01f2 100644 --- a/pages/settings/account.tsx +++ b/pages/settings/account.tsx @@ -1,4 +1,4 @@ -import { useState, useEffect } from "react"; +import { useState, useEffect, ChangeEvent } from "react"; import { AccountSettings } from "@/types/global"; import { toast } from "react-hot-toast"; import SettingsLayout from "@/layouts/SettingsLayout"; @@ -55,8 +55,10 @@ export default function Account() { if (!objectIsEmpty(account)) setUser({ ...account }); }, [account]); - const handleImageUpload = async (e: any) => { - const file: File = e.target.files[0]; + const handleImageUpload = async (e: ChangeEvent) => { + const file = e.target.files?.[0]; + if (!file) return toast.error(t("image_upload_no_file_error")); + const fileExtension = file.name.split(".").pop()?.toLowerCase(); const allowedExtensions = ["png", "jpeg", "jpg"]; if (allowedExtensions.includes(fileExtension as string)) { @@ -114,9 +116,13 @@ export default function Account() { setSubmitLoader(false); }; - const importBookmarks = async (e: any, format: MigrationFormat) => { + const importBookmarks = async ( + e: ChangeEvent, + format: MigrationFormat + ) => { setSubmitLoader(true); - const file: File = e.target.files[0]; + const file = e.target.files?.[0]; + if (file) { var reader = new FileReader(); reader.readAsText(file, "UTF-8"); @@ -190,7 +196,7 @@ export default function Account() { onChange={(e) => setUser({ ...user, username: e.target.value })} /> - {emailEnabled ? ( + {emailEnabled && (

    {t("email")}

    setUser({ ...user, email: e.target.value })} />
    - ) : undefined} + )}

    {t("language")}