Merge pull request #678 from IsaacWise06/fixes

General Fixes
This commit is contained in:
Daniel
2024-08-18 16:40:48 -04:00
committed by GitHub
53 changed files with 420 additions and 445 deletions
+1 -3
View File
@@ -100,9 +100,7 @@ export default function Admin() {
<p>{t("no_users_found")}</p>
)}
{newUserModal ? (
<NewUserModal onClose={() => setNewUserModal(false)} />
) : null}
{newUserModal && <NewUserModal onClose={() => setNewUserModal(false)} />}
</div>
);
}
+9 -7
View File
@@ -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(),
+9 -14
View File
@@ -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<AccountSettings>
>({});
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 && (
<ProfilePhoto
src={collectionOwner.image || undefined}
name={collectionOwner.name}
/>
) : 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 (
<ProfilePhoto
@@ -226,13 +221,13 @@ export default function Index() {
);
})
.slice(0, 3)}
{activeCollection.members.length - 3 > 0 ? (
{activeCollection.members.length - 3 > 0 && (
<div className={`avatar drop-shadow-md placeholder -ml-3`}>
<div className="bg-base-100 text-neutral rounded-full w-8 h-8 ring-2 ring-neutral-content">
<span>+{activeCollection.members.length - 3}</span>
</div>
</div>
) : null}
)}
</div>
<p className="text-neutral text-sm">
+4 -4
View File
@@ -60,7 +60,7 @@ export default function Collections() {
</div>
</div>
{sortedCollections.filter((e) => e.ownerId !== data?.user.id)[0] ? (
{sortedCollections.filter((e) => e.ownerId !== data?.user.id)[0] && (
<>
<PageHeader
icon={"bi-folder"}
@@ -76,11 +76,11 @@ export default function Collections() {
})}
</div>
</>
) : undefined}
)}
</div>
{newCollectionModal ? (
{newCollectionModal && (
<NewCollectionModal onClose={() => setNewCollectionModal(false)} />
) : undefined}
)}
</MainLayout>
);
}
+8 -7
View File
@@ -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<HTMLInputElement>,
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() {
<DashboardItem
name={tags.length === 1 ? t("tag") : t("tags")}
value={tags.length * numberOfLinks}
value={tags.length}
icon={"bi-hash"}
/>
</div>
@@ -339,9 +342,7 @@ export default function Dashboard() {
)}
</div>
</div>
{newLinkModal ? (
<NewLinkModal onClose={() => setNewLinkModal(false)} />
) : undefined}
{newLinkModal && <NewLinkModal onClose={() => setNewLinkModal(false)} />}
</MainLayout>
);
}
+5 -5
View File
@@ -203,9 +203,9 @@ export default function Login({
{t("login")}
</Button>
{availableLogins.buttonAuths.length > 0 ? (
{availableLogins.buttonAuths.length > 0 && (
<div className="divider my-1">{t("or_continue_with")}</div>
) : undefined}
)}
</>
);
}
@@ -224,9 +224,9 @@ export default function Login({
loading={submitLoader}
>
{value.name.toLowerCase() === "google" ||
value.name.toLowerCase() === "apple" ? (
<i className={"bi-" + value.name.toLowerCase()}></i>
) : undefined}
(value.name.toLowerCase() === "apple" && (
<i className={"bi-" + value.name.toLowerCase()}></i>
))}
{value.name}
</Button>
</React.Fragment>
+141 -144
View File
@@ -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<AccountSettings>
>({});
const [searchFilter, setSearchFilter] = useState({
name: true,
@@ -93,160 +88,162 @@ export default function PublicCollections() {
(localStorage.getItem("viewMode") as ViewMode) || ViewMode.Card
);
return collection ? (
<div
className="h-96"
style={{
backgroundImage: `linear-gradient(${collection?.color}30 10%, ${
settings.theme === "dark" ? "#262626" : "#f3f4f6"
} 13rem, ${settings.theme === "dark" ? "#171717" : "#ffffff"} 100%)`,
}}
>
{collection ? (
<Head>
<title>{collection.name} | Linkwarden</title>
<meta
property="og:title"
content={`${collection.name} | Linkwarden`}
key="title"
/>
</Head>
) : undefined}
<div className="lg:w-3/4 w-full mx-auto p-5 bg">
<div className="flex items-center justify-between">
<p className="text-4xl font-thin mb-2 capitalize mt-10">
{collection.name}
</p>
<div className="flex gap-2 items-center mt-8 min-w-fit">
<ToggleDarkMode />
if (!collection) return <></>;
else
return (
<div
className="h-96"
style={{
backgroundImage: `linear-gradient(${collection?.color}30 10%, ${
settings.theme === "dark" ? "#262626" : "#f3f4f6"
} 13rem, ${settings.theme === "dark" ? "#171717" : "#ffffff"} 100%)`,
}}
>
{collection && (
<Head>
<title>{collection.name} | Linkwarden</title>
<meta
property="og:title"
content={`${collection.name} | Linkwarden`}
key="title"
/>
</Head>
)}
<div className="lg:w-3/4 w-full mx-auto p-5 bg">
<div className="flex items-center justify-between">
<p className="text-4xl font-thin mb-2 capitalize mt-10">
{collection.name}
</p>
<div className="flex gap-2 items-center mt-8 min-w-fit">
<ToggleDarkMode />
<Link href="https://linkwarden.app/" target="_blank">
<Image
src={`/icon.png`}
width={551}
height={551}
alt="Linkwarden"
title={t("list_created_with_linkwarden")}
className="h-8 w-fit mx-auto rounded"
/>
</Link>
<Link href="https://linkwarden.app/" target="_blank">
<Image
src={`/icon.png`}
width={551}
height={551}
alt="Linkwarden"
title={t("list_created_with_linkwarden")}
className="h-8 w-fit mx-auto rounded"
/>
</Link>
</div>
</div>
</div>
<div className="mt-3">
<div className={`min-w-[15rem]`}>
<div className="flex gap-1 justify-center sm:justify-end items-center w-fit">
<div
className="flex items-center btn px-2 btn-ghost rounded-full"
onClick={() => setEditCollectionSharingModal(true)}
>
{collectionOwner.id ? (
<ProfilePhoto
src={collectionOwner.image || undefined}
name={collectionOwner.name}
/>
) : undefined}
{collection.members
.sort((a, b) => (a.userId as number) - (b.userId as number))
.map((e, i) => {
return (
<ProfilePhoto
key={i}
src={e.user.image ? e.user.image : undefined}
className="-ml-3"
name={e.user.name}
/>
);
})
.slice(0, 3)}
{collection.members.length - 3 > 0 ? (
<div className={`avatar drop-shadow-md placeholder -ml-3`}>
<div className="bg-base-100 text-neutral rounded-full w-8 h-8 ring-2 ring-neutral-content">
<span>+{collection.members.length - 3}</span>
</div>
</div>
) : null}
</div>
<p className="text-neutral text-sm">
{collection.members.length > 0 &&
collection.members.length === 1
? t("by_author_and_other", {
author: collectionOwner.name,
count: collection.members.length,
<div className="mt-3">
<div className={`min-w-[15rem]`}>
<div className="flex gap-1 justify-center sm:justify-end items-center w-fit">
<div
className="flex items-center btn px-2 btn-ghost rounded-full"
onClick={() => setEditCollectionSharingModal(true)}
>
{collectionOwner.id && (
<ProfilePhoto
src={collectionOwner.image || undefined}
name={collectionOwner.name}
/>
)}
{collection.members
.sort((a, b) => (a.userId as number) - (b.userId as number))
.map((e, i) => {
return (
<ProfilePhoto
key={i}
src={e.user.image ? e.user.image : undefined}
className="-ml-3"
name={e.user.name}
/>
);
})
: collection.members.length > 0 &&
collection.members.length !== 1
? t("by_author_and_others", {
.slice(0, 3)}
{collection.members.length - 3 > 0 && (
<div className={`avatar drop-shadow-md placeholder -ml-3`}>
<div className="bg-base-100 text-neutral rounded-full w-8 h-8 ring-2 ring-neutral-content">
<span>+{collection.members.length - 3}</span>
</div>
</div>
)}
</div>
<p className="text-neutral text-sm">
{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,
})}
</p>
: 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,
})}
</p>
</div>
</div>
</div>
</div>
<p className="mt-5">{collection.description}</p>
<p className="mt-5">{collection.description}</p>
<div className="divider mt-5 mb-0"></div>
<div className="divider mt-5 mb-0"></div>
<div className="flex mb-5 mt-10 flex-col gap-5">
<LinkListOptions
t={t}
viewMode={viewMode}
setViewMode={setViewMode}
sortBy={sortBy}
setSortBy={setSortBy}
searchFilter={searchFilter}
setSearchFilter={setSearchFilter}
>
<SearchBar
placeholder={
collection._count?.links === 1
? t("search_count_link", {
count: collection._count?.links,
})
: t("search_count_links", {
count: collection._count?.links,
})
<div className="flex mb-5 mt-10 flex-col gap-5">
<LinkListOptions
t={t}
viewMode={viewMode}
setViewMode={setViewMode}
sortBy={sortBy}
setSortBy={setSortBy}
searchFilter={searchFilter}
setSearchFilter={setSearchFilter}
>
<SearchBar
placeholder={
collection._count?.links === 1
? t("search_count_link", {
count: collection._count?.links,
})
: t("search_count_links", {
count: collection._count?.links,
})
}
/>
</LinkListOptions>
<Links
links={
links?.map((e, i) => {
const linkWithCollectionData = {
...e,
collection: collection, // Append collection data
};
return linkWithCollectionData;
}) as any
}
layout={viewMode}
placeholderCount={1}
useData={data}
/>
</LinkListOptions>
{!data.isLoading && links && !links[0] && (
<p>{t("nothing_found")}</p>
)}
<Links
links={
links?.map((e, i) => {
const linkWithCollectionData = {
...e,
collection: collection, // Append collection data
};
return linkWithCollectionData;
}) as any
}
layout={viewMode}
placeholderCount={1}
useData={data}
/>
{!data.isLoading && links && !links[0] && <p>{t("nothing_found")}</p>}
{/* <p className="text-center text-neutral">
{/* <p className="text-center text-neutral">
List created with <span className="text-black">Linkwarden.</span>
</p> */}
</div>
</div>
{editCollectionSharingModal && (
<EditCollectionSharingModal
onClose={() => setEditCollectionSharingModal(false)}
activeCollection={collection}
/>
)}
</div>
{editCollectionSharingModal ? (
<EditCollectionSharingModal
onClose={() => setEditCollectionSharingModal(false)}
activeCollection={collection}
/>
) : undefined}
</div>
) : (
<></>
);
);
}
export { getServerSideProps };
+11 -11
View File
@@ -133,9 +133,9 @@ export default function Register({
loading={submitLoader}
>
{value.name.toLowerCase() === "google" ||
value.name.toLowerCase() === "apple" ? (
<i className={"bi-" + value.name.toLowerCase()}></i>
) : undefined}
(value.name.toLowerCase() === "apple" && (
<i className={"bi-" + value.name.toLowerCase()}></i>
))}
{value.name}
</Button>
</React.Fragment>
@@ -201,7 +201,7 @@ export default function Register({
</div>
)}
{emailEnabled ? (
{emailEnabled && (
<div>
<p className="text-sm w-fit font-semibold mb-1">{t("email")}</p>
@@ -214,7 +214,7 @@ export default function Register({
onChange={(e) => setForm({ ...form, email: e.target.value })}
/>
</div>
) : undefined}
)}
<div className="w-full">
<p className="text-sm w-fit font-semibold mb-1">
@@ -248,7 +248,7 @@ export default function Register({
/>
</div>
{process.env.NEXT_PUBLIC_STRIPE ? (
{process.env.NEXT_PUBLIC_STRIPE && (
<div className="text-xs text-neutral mb-3">
<p>
<Trans
@@ -270,7 +270,7 @@ export default function Register({
/>
</p>
</div>
) : undefined}
)}
<Button
type="submit"
@@ -282,9 +282,9 @@ export default function Register({
{t("sign_up")}
</Button>
{availableLogins.buttonAuths.length > 0 ? (
{availableLogins.buttonAuths.length > 0 && (
<div className="divider my-1">{t("or_continue_with")}</div>
) : undefined}
)}
{displayLoginExternalButton()}
<div>
@@ -298,7 +298,7 @@ export default function Register({
{t("login")}
</Link>
</div>
{process.env.NEXT_PUBLIC_STRIPE ? (
{process.env.NEXT_PUBLIC_STRIPE && (
<div className="text-neutral text-center flex items-baseline gap-1 justify-center">
<p>{t("need_help")}</p>
<Link
@@ -309,7 +309,7 @@ export default function Register({
{t("get_in_touch")}
</Link>
</div>
) : undefined}
)}
</div>
</div>
</form>
+4 -4
View File
@@ -40,7 +40,7 @@ export default function AccessTokens() {
{t("new_token")}
</button>
{tokens.length > 0 ? (
{tokens.length > 0 && (
<table className="table mt-2 overflow-x-auto">
<thead>
<tr>
@@ -85,12 +85,12 @@ export default function AccessTokens() {
))}
</tbody>
</table>
) : undefined}
)}
</div>
{newTokenModal ? (
{newTokenModal && (
<NewTokenModal onClose={() => setNewTokenModal(false)} />
) : undefined}
)}
{revokeTokenModal && selectedToken && (
<RevokeTokenModal
onClose={() => {
+17 -12
View File
@@ -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<HTMLInputElement>) => {
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<HTMLInputElement>,
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 })}
/>
</div>
{emailEnabled ? (
{emailEnabled && (
<div>
<p className="mb-2">{t("email")}</p>
<TextInput
@@ -199,7 +205,7 @@ export default function Account() {
onChange={(e) => setUser({ ...user, email: e.target.value })}
/>
</div>
) : undefined}
)}
<div>
<p className="mb-2">{t("language")}</p>
<select
@@ -437,9 +443,8 @@ export default function Account() {
<p>
{t("delete_account_warning")}
{process.env.NEXT_PUBLIC_STRIPE
? " " + t("cancel_subscription_notice")
: undefined}
{process.env.NEXT_PUBLIC_STRIPE &&
" " + t("cancel_subscription_notice")}
</p>
</div>
@@ -448,14 +453,14 @@ export default function Account() {
</Link>
</div>
{emailChangeVerificationModal ? (
{emailChangeVerificationModal && (
<EmailChangeVerificationModal
onClose={() => setEmailChangeVerificationModal(false)}
onSubmit={submit}
oldEmail={account.email || ""}
newEmail={user.email || ""}
/>
) : undefined}
)}
</SettingsLayout>
);
}
+2 -2
View File
@@ -83,7 +83,7 @@ export default function Delete() {
/>
</div>
{process.env.NEXT_PUBLIC_STRIPE ? (
{process.env.NEXT_PUBLIC_STRIPE && (
<fieldset className="border rounded-md p-2 border-primary">
<legend className="px-3 py-1 text-sm sm:text-base border rounded-md border-primary">
<b>{t("optional")}</b> <i>{t("feedback_help")}</i>
@@ -123,7 +123,7 @@ export default function Delete() {
/>
</div>
</fieldset>
) : undefined}
)}
<Button
className="mx-auto"