From f5eaee8dc0becb846ebf886099f7c9258c7cb845 Mon Sep 17 00:00:00 2001
From: daniel31x13
Date: Tue, 17 Oct 2023 16:02:07 -0400
Subject: [PATCH 1/7] bug fixed + redesigned profile settings page
---
components/LinkCard.tsx | 3 +-
components/PublicPage/LinkCard.tsx | 5 +-
components/SettingsSidebar.tsx | 2 +-
components/SubmitButton.tsx | 4 +-
layouts/SettingsLayout.tsx | 18 ++-
pages/_app.tsx | 2 +-
pages/settings/profile.tsx | 177 ++++++++++++++++++++++++++++-
7 files changed, 197 insertions(+), 14 deletions(-)
diff --git a/components/LinkCard.tsx b/components/LinkCard.tsx
index 1e26b78..0658ea7 100644
--- a/components/LinkCard.tsx
+++ b/components/LinkCard.tsx
@@ -20,6 +20,7 @@ import usePermissions from "@/hooks/usePermissions";
import { toast } from "react-hot-toast";
import isValidUrl from "@/lib/client/isValidUrl";
import Link from "next/link";
+import unescapeString from "@/lib/client/unescapeString";
type Props = {
link: LinkIncludingShortenedCollectionAndTags;
@@ -161,7 +162,7 @@ export default function LinkCard({ link, count, className }: Props) {
{count + 1}
- {link.name || link.description}
+ {unescapeString(link.name || link.description)}
- {link.name || link.description}
+ {unescapeString(link.name || link.description)}
- {link.description}
+ {unescapeString(link.description)}
diff --git a/components/SettingsSidebar.tsx b/components/SettingsSidebar.tsx
index 3852b2e..795132a 100644
--- a/components/SettingsSidebar.tsx
+++ b/components/SettingsSidebar.tsx
@@ -34,7 +34,7 @@ export default function SettingsSidebar({ className }: { className?: string }) {
return (
diff --git a/components/SubmitButton.tsx b/components/SubmitButton.tsx
index 93d85c8..8a06eac 100644
--- a/components/SubmitButton.tsx
+++ b/components/SubmitButton.tsx
@@ -27,8 +27,8 @@ export default function SubmitButton({
if (!loading) onClick();
}}
>
- {icon &&
}
-
{label}
+ {icon &&
}
+
{label}
);
}
diff --git a/layouts/SettingsLayout.tsx b/layouts/SettingsLayout.tsx
index ae5c08f..80a7ed8 100644
--- a/layouts/SettingsLayout.tsx
+++ b/layouts/SettingsLayout.tsx
@@ -5,7 +5,8 @@ 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";
+import { faBars, faChevronLeft } from "@fortawesome/free-solid-svg-icons";
+import Link from "next/link";
interface Props {
children: ReactNode;
@@ -38,12 +39,12 @@ export default function SettingsLayout({ children }: Props) {
<>
-
+
-
+
-
+
+
+
+
+
{router.asPath.split("/").pop()} Settings
-
+
{children}
{sidebar ? (
diff --git a/pages/_app.tsx b/pages/_app.tsx
index 7e8dd7c..f5966b4 100644
--- a/pages/_app.tsx
+++ b/pages/_app.tsx
@@ -52,7 +52,7 @@ export default function App({
reverseOrder={false}
toastOptions={{
className:
- "border border-sky-100 dark:dark:border-neutral-700 dark:bg-neutral-900 dark:text-white",
+ "border border-sky-100 dark:border-neutral-700 dark:bg-neutral-900 dark:text-white",
}}
/>
diff --git a/pages/settings/profile.tsx b/pages/settings/profile.tsx
index ae776ca..7cc3f8b 100644
--- a/pages/settings/profile.tsx
+++ b/pages/settings/profile.tsx
@@ -1,10 +1,183 @@
+import { useState, useEffect } from "react";
+import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+import { faClose, faPenToSquare } from "@fortawesome/free-solid-svg-icons";
+import useAccountStore from "@/store/account";
+import { AccountSettings } from "@/types/global";
+import { toast } from "react-hot-toast";
import SettingsLayout from "@/layouts/SettingsLayout";
-import React from "react";
+import TextInput from "@/components/TextInput";
+import { resizeImage } from "@/lib/client/resizeImage";
+import ProfilePhoto from "@/components/ProfilePhoto";
+import SubmitButton from "@/components/SubmitButton";
+import { useSession, signOut } from "next-auth/react";
export default function profile() {
+ const { update, data } = useSession();
+
+ const emailEnabled = process.env.NEXT_PUBLIC_EMAIL_PROVIDER;
+
+ const [profileStatus, setProfileStatus] = useState(true);
+ const [submitLoader, setSubmitLoader] = useState(false);
+
+ const handleProfileStatus = (e: boolean) => {
+ setProfileStatus(!e);
+ };
+
+ const { account, updateAccount } = useAccountStore();
+
+ const [user, setUser] = useState
({
+ ...account,
+ });
+
+ useEffect(() => {
+ setUser({ ...account });
+ }, [account]);
+
+ const handleImageUpload = async (e: any) => {
+ const file: File = e.target.files[0];
+ const fileExtension = file.name.split(".").pop()?.toLowerCase();
+ const allowedExtensions = ["png", "jpeg", "jpg"];
+ if (allowedExtensions.includes(fileExtension as string)) {
+ const resizedFile = await resizeImage(file);
+ if (
+ resizedFile.size < 1048576 // 1048576 Bytes == 1MB
+ ) {
+ const reader = new FileReader();
+ reader.onload = () => {
+ setUser({ ...user, profilePic: reader.result as string });
+ };
+ reader.readAsDataURL(resizedFile);
+ } else {
+ toast.error("Please select a PNG or JPEG file thats less than 1MB.");
+ }
+ } else {
+ toast.error("Invalid file format.");
+ }
+ };
+
+ const submit = async () => {
+ setSubmitLoader(true);
+
+ const load = toast.loading("Applying...");
+
+ const response = await updateAccount({
+ ...user,
+ });
+
+ toast.dismiss(load);
+
+ if (response.ok) {
+ toast.success("Settings Applied!");
+
+ if (user.email !== account.email) {
+ update({
+ id: data?.user.id,
+ });
+
+ signOut();
+ } else if (
+ user.username !== account.username ||
+ user.name !== account.name
+ )
+ update({
+ id: data?.user.id,
+ });
+
+ setUser({ ...user, newPassword: undefined });
+ } else toast.error(response.data as string);
+ setSubmitLoader(false);
+ };
+
return (
- profile
+
+
+
+
+
+ Display Name
+
+
setUser({ ...user, name: e.target.value })}
+ />
+
+
+
+ Username
+
+
setUser({ ...user, username: e.target.value })}
+ />
+
+
+ {emailEnabled ? (
+
+
Email
+
setUser({ ...user, email: e.target.value })}
+ />
+
+ ) : undefined}
+
+ {user.email !== account.email ? (
+
+ You will need to log back in after you apply this Email.
+
+ ) : undefined}
+
+
+
+
+ Profile Photo
+
+
+
+ {profileStatus && (
+
+ setUser({
+ ...user,
+ profilePic: "",
+ })
+ }
+ 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"
+ >
+
+
+ )}
+
+
+
+
+
+
+
+
+
);
}
From 19467f243f290e492bea11cbdf56c104ef49c982 Mon Sep 17 00:00:00 2001
From: daniel31x13
Date: Wed, 18 Oct 2023 17:50:55 -0400
Subject: [PATCH 2/7] redesigned settings page [still WIP]
---
components/Modal/User/PrivacySettings.tsx | 1 -
components/SettingsSidebar.tsx | 76 ++---
layouts/SettingsLayout.tsx | 6 +-
pages/settings/account.tsx | 393 ++++++++++++++++++++++
pages/settings/appearance.tsx | 30 +-
pages/settings/billing.tsx | 24 +-
pages/settings/migration.tsx | 10 -
pages/settings/password.tsx | 77 ++++-
pages/settings/privacy.tsx | 10 -
pages/settings/profile.tsx | 183 ----------
10 files changed, 561 insertions(+), 249 deletions(-)
create mode 100644 pages/settings/account.tsx
delete mode 100644 pages/settings/migration.tsx
delete mode 100644 pages/settings/privacy.tsx
delete mode 100644 pages/settings/profile.tsx
diff --git a/components/Modal/User/PrivacySettings.tsx b/components/Modal/User/PrivacySettings.tsx
index df28907..ce61782 100644
--- a/components/Modal/User/PrivacySettings.tsx
+++ b/components/Modal/User/PrivacySettings.tsx
@@ -12,7 +12,6 @@ import SubmitButton from "../../SubmitButton";
import { toast } from "react-hot-toast";
import Link from "next/link";
import ClickAwayHandler from "@/components/ClickAwayHandler";
-import useInitialData from "@/hooks/useInitialData";
type Props = {
toggleSettingsModal: Function;
diff --git a/components/SettingsSidebar.tsx b/components/SettingsSidebar.tsx
index 795132a..d45b01e 100644
--- a/components/SettingsSidebar.tsx
+++ b/components/SettingsSidebar.tsx
@@ -34,13 +34,13 @@ export default function SettingsSidebar({ className }: { className?: string }) {
return (
@@ -94,7 +94,7 @@ export default function SettingsSidebar({ className }: { className?: string }) {
-
+ {/*
-
+ */}
-
+ {/*
-
+ */}
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
);
diff --git a/layouts/SettingsLayout.tsx b/layouts/SettingsLayout.tsx
index 80a7ed8..5d14240 100644
--- a/layouts/SettingsLayout.tsx
+++ b/layouts/SettingsLayout.tsx
@@ -39,7 +39,7 @@ export default function SettingsLayout({ children }: Props) {
<>
-
+
@@ -60,11 +60,13 @@ export default function SettingsLayout({ children }: Props) {
-
+
{router.asPath.split("/").pop()} Settings
+
+
{children}
{sidebar ? (
diff --git a/pages/settings/account.tsx b/pages/settings/account.tsx
new file mode 100644
index 0000000..6108aeb
--- /dev/null
+++ b/pages/settings/account.tsx
@@ -0,0 +1,393 @@
+import { useState, useEffect } from "react";
+import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+import { faClose, faPenToSquare } from "@fortawesome/free-solid-svg-icons";
+import useAccountStore from "@/store/account";
+import { AccountSettings } from "@/types/global";
+import { toast } from "react-hot-toast";
+import SettingsLayout from "@/layouts/SettingsLayout";
+import TextInput from "@/components/TextInput";
+import { resizeImage } from "@/lib/client/resizeImage";
+import ProfilePhoto from "@/components/ProfilePhoto";
+import SubmitButton from "@/components/SubmitButton";
+import { useSession, signOut } from "next-auth/react";
+import React from "react";
+import { MigrationFormat, MigrationRequest } from "@/types/global";
+import Link from "next/link";
+import ClickAwayHandler from "@/components/ClickAwayHandler";
+import Checkbox from "@/components/Checkbox";
+
+export default function account() {
+ const { update, data } = useSession();
+
+ const emailEnabled = process.env.NEXT_PUBLIC_EMAIL_PROVIDER;
+
+ const [profileStatus, setProfileStatus] = useState(true);
+ const [submitLoader, setSubmitLoader] = useState(false);
+
+ const handleProfileStatus = (e: boolean) => {
+ setProfileStatus(!e);
+ };
+
+ const { account, updateAccount } = useAccountStore();
+
+ const [user, setUser] = useState
(
+ !objectIsEmpty(account)
+ ? account
+ : ({
+ // @ts-ignore
+ id: null,
+ name: "",
+ username: "",
+ email: "",
+ emailVerified: null,
+ image: null,
+ isPrivate: true,
+ // @ts-ignore
+ createdAt: null,
+ whitelistedUsers: [],
+ profilePic: "",
+ } as unknown as AccountSettings)
+ );
+
+ function objectIsEmpty(obj: object) {
+ return Object.keys(obj).length === 0;
+ }
+
+ useEffect(() => {
+ if (!objectIsEmpty(account)) setUser({ ...account });
+ }, [account]);
+
+ const handleImageUpload = async (e: any) => {
+ const file: File = e.target.files[0];
+ const fileExtension = file.name.split(".").pop()?.toLowerCase();
+ const allowedExtensions = ["png", "jpeg", "jpg"];
+ if (allowedExtensions.includes(fileExtension as string)) {
+ const resizedFile = await resizeImage(file);
+ if (
+ resizedFile.size < 1048576 // 1048576 Bytes == 1MB
+ ) {
+ const reader = new FileReader();
+ reader.onload = () => {
+ setUser({ ...user, profilePic: reader.result as string });
+ };
+ reader.readAsDataURL(resizedFile);
+ } else {
+ toast.error("Please select a PNG or JPEG file thats less than 1MB.");
+ }
+ } else {
+ toast.error("Invalid file format.");
+ }
+ };
+
+ const submit = async () => {
+ setSubmitLoader(true);
+
+ const load = toast.loading("Applying...");
+
+ const response = await updateAccount({
+ ...user,
+ });
+
+ toast.dismiss(load);
+
+ if (response.ok) {
+ toast.success("Settings Applied!");
+
+ if (user.email !== account.email) {
+ update({
+ id: data?.user.id,
+ });
+
+ signOut();
+ } else if (
+ user.username !== account.username ||
+ user.name !== account.name
+ )
+ update({
+ id: data?.user.id,
+ });
+ } else toast.error(response.data as string);
+ setSubmitLoader(false);
+ };
+
+ const [importDropdown, setImportDropdown] = useState(false);
+
+ const importBookmarks = async (e: any, format: MigrationFormat) => {
+ const file: File = e.target.files[0];
+
+ if (file) {
+ var reader = new FileReader();
+ reader.readAsText(file, "UTF-8");
+ reader.onload = async function (e) {
+ const load = toast.loading("Importing...");
+
+ const request: string = e.target?.result as string;
+
+ const body: MigrationRequest = {
+ format,
+ data: request,
+ };
+
+ const response = await fetch("/api/migration", {
+ method: "POST",
+ body: JSON.stringify(body),
+ });
+
+ const data = await response.json();
+
+ toast.dismiss(load);
+
+ toast.success("Imported the Bookmarks! Reloading the page...");
+
+ setImportDropdown(false);
+
+ setTimeout(() => {
+ location.reload();
+ }, 2000);
+ };
+ reader.onerror = function (e) {
+ console.log("Error:", e);
+ };
+ }
+ };
+
+ const [whitelistedUsersTextbox, setWhiteListedUsersTextbox] = useState("");
+
+ useEffect(() => {
+ setWhiteListedUsersTextbox(account?.whitelistedUsers?.join(", "));
+ }, [account]);
+
+ useEffect(() => {
+ setUser({
+ ...user,
+ whitelistedUsers: stringToArray(whitelistedUsersTextbox),
+ });
+ }, [whitelistedUsersTextbox]);
+
+ const stringToArray = (str: string) => {
+ const stringWithoutSpaces = str?.replace(/\s+/g, "");
+
+ const wordsArray = stringWithoutSpaces?.split(",");
+
+ return wordsArray;
+ };
+
+ return (
+
+
+
+
+
+
+ Display Name
+
+
setUser({ ...user, name: e.target.value })}
+ />
+
+
+
+ Username
+
+
setUser({ ...user, username: e.target.value })}
+ />
+
+
+ {emailEnabled ? (
+
+
Email
+
setUser({ ...user, email: e.target.value })}
+ />
+
+ ) : undefined}
+
+ {user.email !== account.email ? (
+
+ You will need to log back in after you apply this Email.
+
+ ) : undefined}
+
+
+
+
+ Profile Photo
+
+
+
+ {profileStatus && (
+
+ setUser({
+ ...user,
+ profilePic: "",
+ })
+ }
+ 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"
+ >
+
+
+ )}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Import your data from other platforms.
+
+
+
+
+
+
+ Download your data instantly.
+
+
+
+ Export Data
+
+
+
+
+
+
+
+
+
+ Profile Visibility
+
+
+
+
+
+
setUser({ ...user, isPrivate: !user.isPrivate })}
+ />
+
+
+ This will limit who can find and add you to other Collections.
+
+
+ {user.isPrivate && (
+
+
+ Whitelisted Users
+
+
+ Please provide the Username of the users you wish to grant
+ visibility to your profile. Separated by comma.
+
+
+ )}
+
+
+
+
+
+ );
+}
diff --git a/pages/settings/appearance.tsx b/pages/settings/appearance.tsx
index 5eb17f9..ea5f0a3 100644
--- a/pages/settings/appearance.tsx
+++ b/pages/settings/appearance.tsx
@@ -1,10 +1,36 @@
import SettingsLayout from "@/layouts/SettingsLayout";
-import React from "react";
+import { useTheme } from "next-themes";
export default function appearance() {
+ const { theme, setTheme } = useTheme();
+
+ const handleToggle = () => {
+ if (theme === "dark") {
+ setTheme("light");
+ } else {
+ setTheme("dark");
+ }
+ };
+
return (
- appearance
+
+
setTheme("dark")}
+ >
+
Dark Theme
+
+ {/*
*/}
+
+
setTheme("light")}
+ >
+
Light Theme
+ {/*
*/}
+
+
);
}
diff --git a/pages/settings/billing.tsx b/pages/settings/billing.tsx
index cc9f040..c2094c0 100644
--- a/pages/settings/billing.tsx
+++ b/pages/settings/billing.tsx
@@ -12,7 +12,29 @@ export default function billing() {
return (
- Billing
+
);
}
diff --git a/pages/settings/migration.tsx b/pages/settings/migration.tsx
deleted file mode 100644
index 023c2ca..0000000
--- a/pages/settings/migration.tsx
+++ /dev/null
@@ -1,10 +0,0 @@
-import SettingsLayout from "@/layouts/SettingsLayout";
-import React from "react";
-
-export default function appearance() {
- return (
-
- Migration
-
- );
-}
diff --git a/pages/settings/password.tsx b/pages/settings/password.tsx
index bffea57..d17d23f 100644
--- a/pages/settings/password.tsx
+++ b/pages/settings/password.tsx
@@ -1,10 +1,83 @@
import SettingsLayout from "@/layouts/SettingsLayout";
-import React from "react";
+import { useState } from "react";
+import useAccountStore from "@/store/account";
+import { faPenToSquare } from "@fortawesome/free-regular-svg-icons";
+import SubmitButton from "@/components/SubmitButton";
+import { toast } from "react-hot-toast";
+import TextInput from "@/components/TextInput";
export default function password() {
+ const [newPassword, setNewPassword1] = useState("");
+ const [newPassword2, setNewPassword2] = useState("");
+
+ const [submitLoader, setSubmitLoader] = useState(false);
+
+ const { account, updateAccount } = useAccountStore();
+
+ const submit = async () => {
+ if (newPassword == "" || newPassword2 == "") {
+ toast.error("Please fill all the fields.");
+ }
+
+ if (newPassword !== newPassword2)
+ return toast.error("Passwords do not match.");
+ else if (newPassword.length < 8)
+ return toast.error("Passwords must be at least 8 characters.");
+
+ setSubmitLoader(true);
+
+ const load = toast.loading("Applying...");
+
+ const response = await updateAccount({
+ ...account,
+ newPassword,
+ });
+
+ toast.dismiss(load);
+
+ if (response.ok) {
+ toast.success("Settings Applied!");
+ setNewPassword1("");
+ setNewPassword2("");
+ } else toast.error(response.data as string);
+
+ setSubmitLoader(false);
+ };
+
return (
- password
+
+ To change your password, please fill out the following. Your password
+ should be at least 8 characters.
+
+
+
New Password
+
+
setNewPassword1(e.target.value)}
+ placeholder="••••••••••••••"
+ type="password"
+ />
+
+
+ Confirm New Password
+
+
+ setNewPassword2(e.target.value)}
+ placeholder="••••••••••••••"
+ type="password"
+ />
+
+
+
);
}
diff --git a/pages/settings/privacy.tsx b/pages/settings/privacy.tsx
deleted file mode 100644
index b57f9d5..0000000
--- a/pages/settings/privacy.tsx
+++ /dev/null
@@ -1,10 +0,0 @@
-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
deleted file mode 100644
index 7cc3f8b..0000000
--- a/pages/settings/profile.tsx
+++ /dev/null
@@ -1,183 +0,0 @@
-import { useState, useEffect } from "react";
-import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { faClose, faPenToSquare } from "@fortawesome/free-solid-svg-icons";
-import useAccountStore from "@/store/account";
-import { AccountSettings } from "@/types/global";
-import { toast } from "react-hot-toast";
-import SettingsLayout from "@/layouts/SettingsLayout";
-import TextInput from "@/components/TextInput";
-import { resizeImage } from "@/lib/client/resizeImage";
-import ProfilePhoto from "@/components/ProfilePhoto";
-import SubmitButton from "@/components/SubmitButton";
-import { useSession, signOut } from "next-auth/react";
-
-export default function profile() {
- const { update, data } = useSession();
-
- const emailEnabled = process.env.NEXT_PUBLIC_EMAIL_PROVIDER;
-
- const [profileStatus, setProfileStatus] = useState(true);
- const [submitLoader, setSubmitLoader] = useState(false);
-
- const handleProfileStatus = (e: boolean) => {
- setProfileStatus(!e);
- };
-
- const { account, updateAccount } = useAccountStore();
-
- const [user, setUser] = useState({
- ...account,
- });
-
- useEffect(() => {
- setUser({ ...account });
- }, [account]);
-
- const handleImageUpload = async (e: any) => {
- const file: File = e.target.files[0];
- const fileExtension = file.name.split(".").pop()?.toLowerCase();
- const allowedExtensions = ["png", "jpeg", "jpg"];
- if (allowedExtensions.includes(fileExtension as string)) {
- const resizedFile = await resizeImage(file);
- if (
- resizedFile.size < 1048576 // 1048576 Bytes == 1MB
- ) {
- const reader = new FileReader();
- reader.onload = () => {
- setUser({ ...user, profilePic: reader.result as string });
- };
- reader.readAsDataURL(resizedFile);
- } else {
- toast.error("Please select a PNG or JPEG file thats less than 1MB.");
- }
- } else {
- toast.error("Invalid file format.");
- }
- };
-
- const submit = async () => {
- setSubmitLoader(true);
-
- const load = toast.loading("Applying...");
-
- const response = await updateAccount({
- ...user,
- });
-
- toast.dismiss(load);
-
- if (response.ok) {
- toast.success("Settings Applied!");
-
- if (user.email !== account.email) {
- update({
- id: data?.user.id,
- });
-
- signOut();
- } else if (
- user.username !== account.username ||
- user.name !== account.name
- )
- update({
- id: data?.user.id,
- });
-
- setUser({ ...user, newPassword: undefined });
- } else toast.error(response.data as string);
- setSubmitLoader(false);
- };
-
- return (
-
-
-
-
-
-
- Display Name
-
-
setUser({ ...user, name: e.target.value })}
- />
-
-
-
- Username
-
-
setUser({ ...user, username: e.target.value })}
- />
-
-
- {emailEnabled ? (
-
-
Email
-
setUser({ ...user, email: e.target.value })}
- />
-
- ) : undefined}
-
- {user.email !== account.email ? (
-
- You will need to log back in after you apply this Email.
-
- ) : undefined}
-
-
-
-
- Profile Photo
-
-
-
- {profileStatus && (
-
- setUser({
- ...user,
- profilePic: "",
- })
- }
- 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"
- >
-
-
- )}
-
-
-
-
-
-
-
-
-
-
- );
-}
From ca3ce7e3de6b71578af1e7444ce002e5c801dfeb Mon Sep 17 00:00:00 2001
From: daniel31x13
Date: Thu, 19 Oct 2023 00:00:23 -0400
Subject: [PATCH 3/7] adjustible archive formats + finalized settings page
---
components/Modal/Link/LinkDetails.tsx | 44 +++++----
components/Navbar.tsx | 9 +-
lib/api/archive.ts | 95 +++++++++++--------
lib/api/controllers/links/postLink.ts | 2 +-
lib/api/controllers/users/updateUser.ts | 3 +
pages/settings/appearance.tsx | 31 +++---
pages/settings/archive.tsx | 82 +++++++++++++++-
.../migration.sql | 4 +
prisma/schema.prisma | 4 +
9 files changed, 189 insertions(+), 85 deletions(-)
create mode 100644 prisma/migrations/20231019032936_modify_archive_formats/migration.sql
diff --git a/components/Modal/Link/LinkDetails.tsx b/components/Modal/Link/LinkDetails.tsx
index 7060ee8..4b870d8 100644
--- a/components/Modal/Link/LinkDetails.tsx
+++ b/components/Modal/Link/LinkDetails.tsx
@@ -239,6 +239,16 @@ export default function LinkDetails({ link, isOwnerOrMod }: Props) {
+
handleDownload("png")}
+ className="cursor-pointer hover:bg-slate-200 hover:dark:bg-neutral-700 duration-100 p-2 rounded-md"
+ >
+
+
+
-
-
handleDownload("png")}
- className="cursor-pointer hover:bg-slate-200 hover:dark:bg-neutral-700 duration-100 p-2 rounded-md"
- >
-
-
@@ -272,6 +272,16 @@ export default function LinkDetails({ link, isOwnerOrMod }: Props) {
+
handleDownload("pdf")}
+ className="cursor-pointer hover:bg-slate-200 hover:dark:bg-neutral-700 duration-100 p-2 rounded-md"
+ >
+
+
+
-
-
handleDownload("pdf")}
- className="cursor-pointer hover:bg-slate-200 hover:dark:bg-neutral-700 duration-100 p-2 rounded-md"
- >
-
-
@@ -301,7 +301,9 @@ export default function LinkDetails({ link, isOwnerOrMod }: Props) {
-
Archive.org Snapshot
+
+ Latest archive.org Snapshot
+
{
- setModal({
- modal: "ACCOUNT",
- state: true,
- active: account,
- });
- setProfileDropdown(!profileDropdown);
- },
+ href: "/settings/account",
},
{
name: `Switch to ${theme === "light" ? "Dark" : "Light"}`,
diff --git a/lib/api/archive.ts b/lib/api/archive.ts
index 685413e..fce0454 100644
--- a/lib/api/archive.ts
+++ b/lib/api/archive.ts
@@ -1,55 +1,72 @@
-import { Page, chromium, devices } from "playwright";
+import { 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();
+export default async function archive(
+ linkId: number,
+ url: string,
+ userId: number
+) {
+ const user = await prisma.user.findUnique({
+ where: {
+ id: userId,
+ },
+ });
- sendToWayback(url);
+ if (user?.archiveAsWaybackMachine) sendToWayback(url);
- try {
- await page.goto(url, { waitUntil: "domcontentloaded" });
+ if (user?.archiveAsPDF || user?.archiveAsScreenshot) {
+ const browser = await chromium.launch();
+ const context = await browser.newContext(devices["Desktop Chrome"]);
+ const page = await context.newPage();
- await page.evaluate(
- autoScroll,
- Number(process.env.AUTOSCROLL_TIMEOUT) || 30
- );
+ try {
+ await page.goto(url, { waitUntil: "domcontentloaded" });
- const linkExists = await prisma.link.findUnique({
- where: {
- id: linkId,
- },
- });
+ await page.evaluate(
+ autoScroll,
+ Number(process.env.AUTOSCROLL_TIMEOUT) || 30
+ );
- if (linkExists) {
- const pdf = await page.pdf({
- width: "1366px",
- height: "1931px",
- printBackground: true,
- margin: { top: "15px", bottom: "15px" },
- });
- const screenshot = await page.screenshot({
- fullPage: true,
+ const linkExists = await prisma.link.findUnique({
+ where: {
+ id: linkId,
+ },
});
- createFile({
- data: screenshot,
- filePath: `archives/${linkExists.collectionId}/${linkId}.png`,
- });
+ if (linkExists) {
+ if (user.archiveAsScreenshot) {
+ const screenshot = await page.screenshot({
+ fullPage: true,
+ });
- createFile({
- data: pdf,
- filePath: `archives/${linkExists.collectionId}/${linkId}.pdf`,
- });
+ createFile({
+ data: screenshot,
+ filePath: `archives/${linkExists.collectionId}/${linkId}.png`,
+ });
+ }
+
+ if (user.archiveAsPDF) {
+ const pdf = await page.pdf({
+ width: "1366px",
+ height: "1931px",
+ printBackground: true,
+ margin: { top: "15px", bottom: "15px" },
+ });
+
+ createFile({
+ data: pdf,
+ filePath: `archives/${linkExists.collectionId}/${linkId}.pdf`,
+ });
+ }
+ }
+
+ await browser.close();
+ } catch (err) {
+ console.log(err);
+ await browser.close();
}
-
- await browser.close();
- } catch (err) {
- console.log(err);
- await browser.close();
}
}
diff --git a/lib/api/controllers/links/postLink.ts b/lib/api/controllers/links/postLink.ts
index 8c849bb..a9afe16 100644
--- a/lib/api/controllers/links/postLink.ts
+++ b/lib/api/controllers/links/postLink.ts
@@ -94,7 +94,7 @@ export default async function postLink(
createFolder({ filePath: `archives/${newLink.collectionId}` });
- archive(newLink.id, newLink.url);
+ archive(newLink.id, newLink.url, userId);
return { response: newLink, status: 200 };
}
diff --git a/lib/api/controllers/users/updateUser.ts b/lib/api/controllers/users/updateUser.ts
index 5e1d0da..65cbb1c 100644
--- a/lib/api/controllers/users/updateUser.ts
+++ b/lib/api/controllers/users/updateUser.ts
@@ -108,6 +108,9 @@ export default async function updateUser(
username: user.username.toLowerCase(),
email: user.email?.toLowerCase(),
isPrivate: user.isPrivate,
+ archiveAsScreenshot: user.archiveAsScreenshot,
+ archiveAsPDF: user.archiveAsPDF,
+ archiveAsWaybackMachine: user.archiveAsWaybackMachine,
password:
user.newPassword && user.newPassword !== ""
? newHashedPassword
diff --git a/pages/settings/appearance.tsx b/pages/settings/appearance.tsx
index ea5f0a3..95300f2 100644
--- a/pages/settings/appearance.tsx
+++ b/pages/settings/appearance.tsx
@@ -1,34 +1,37 @@
import SettingsLayout from "@/layouts/SettingsLayout";
import { useTheme } from "next-themes";
+import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+import { faSun, faMoon } from "@fortawesome/free-solid-svg-icons";
export default function appearance() {
const { theme, setTheme } = useTheme();
- const handleToggle = () => {
- if (theme === "dark") {
- setTheme("light");
- } else {
- setTheme("dark");
- }
- };
-
return (
+ Select Theme
setTheme("dark")}
>
-
Dark Theme
+
+
Dark Theme
- {/*
*/}
+ {/*
*/}
setTheme("light")}
>
-
Light Theme
- {/*
*/}
+
+
Light Theme
+ {/*
*/}
diff --git a/pages/settings/archive.tsx b/pages/settings/archive.tsx
index c0faa5a..fc60772 100644
--- a/pages/settings/archive.tsx
+++ b/pages/settings/archive.tsx
@@ -1,10 +1,88 @@
+import Checkbox from "@/components/Checkbox";
+import SubmitButton from "@/components/SubmitButton";
import SettingsLayout from "@/layouts/SettingsLayout";
-import React from "react";
+import React, { useEffect, useState } from "react";
+import useAccountStore from "@/store/account";
+import { toast } from "react-hot-toast";
+import { AccountSettings } from "@/types/global";
export default function archive() {
+ const [submitLoader, setSubmitLoader] = useState(false);
+ const { account, updateAccount } = useAccountStore();
+ const [user, setUser] = useState
(account);
+
+ const [archiveAsScreenshot, setArchiveAsScreenshot] =
+ useState(false);
+ const [archiveAsPDF, setArchiveAsPDF] = useState(false);
+ const [archiveAsWaybackMachine, setArchiveAsWaybackMachine] =
+ useState(false);
+
+ useEffect(() => {
+ setUser({
+ ...account,
+ archiveAsScreenshot,
+ archiveAsPDF,
+ archiveAsWaybackMachine,
+ });
+ }, [account, archiveAsScreenshot, archiveAsPDF, archiveAsWaybackMachine]);
+
+ function objectIsEmpty(obj: object) {
+ return Object.keys(obj).length === 0;
+ }
+
+ useEffect(() => {
+ if (!objectIsEmpty(account)) {
+ setArchiveAsScreenshot(account.archiveAsScreenshot);
+ setArchiveAsPDF(account.archiveAsPDF);
+ setArchiveAsWaybackMachine(account.archiveAsWaybackMachine);
+ }
+ }, [account]);
+
+ const submit = async () => {
+ setSubmitLoader(true);
+
+ const load = toast.loading("Applying...");
+
+ const response = await updateAccount({
+ ...user,
+ });
+
+ toast.dismiss(load);
+
+ if (response.ok) {
+ toast.success("Settings Applied!");
+ } else toast.error(response.data as string);
+ setSubmitLoader(false);
+ };
+
return (
- archive
+ Formats to Archive webpages:
+
+ setArchiveAsScreenshot(!archiveAsScreenshot)}
+ />
+ setArchiveAsPDF(!archiveAsPDF)}
+ />
+
+ setArchiveAsWaybackMachine(!archiveAsWaybackMachine)}
+ />
+
+
+
);
}
diff --git a/prisma/migrations/20231019032936_modify_archive_formats/migration.sql b/prisma/migrations/20231019032936_modify_archive_formats/migration.sql
new file mode 100644
index 0000000..89d866c
--- /dev/null
+++ b/prisma/migrations/20231019032936_modify_archive_formats/migration.sql
@@ -0,0 +1,4 @@
+-- AlterTable
+ALTER TABLE "User" ADD COLUMN "archiveAsPDF" BOOLEAN NOT NULL DEFAULT true,
+ADD COLUMN "archiveAsScreenshot" BOOLEAN NOT NULL DEFAULT true,
+ADD COLUMN "archiveAsWaybackMachine" BOOLEAN NOT NULL DEFAULT false;
diff --git a/prisma/schema.prisma b/prisma/schema.prisma
index eca457b..ab67eb6 100644
--- a/prisma/schema.prisma
+++ b/prisma/schema.prisma
@@ -54,6 +54,10 @@ model User {
pinnedLinks Link[]
+ archiveAsScreenshot Boolean @default(true)
+ archiveAsPDF Boolean @default(true)
+ archiveAsWaybackMachine Boolean @default(false)
+
collectionsJoined UsersAndCollections[]
isPrivate Boolean @default(false)
whitelistedUsers WhitelistedUser[]
From 4482c52fa96a43ecb105d622345917b33a28f10d Mon Sep 17 00:00:00 2001
From: daniel31x13
Date: Thu, 19 Oct 2023 00:09:28 -0400
Subject: [PATCH 4/7] fixed build error
---
pages/choose-username.tsx | 6 ++----
pages/forgot.tsx | 1 -
pages/login.tsx | 1 -
pages/settings/account.tsx | 2 +-
pages/settings/appearance.tsx | 2 +-
pages/settings/archive.tsx | 2 +-
pages/settings/billing.tsx | 2 +-
pages/settings/password.tsx | 2 +-
pages/subscribe.tsx | 2 +-
9 files changed, 8 insertions(+), 12 deletions(-)
diff --git a/pages/choose-username.tsx b/pages/choose-username.tsx
index c4f2f9e..75d6aa0 100644
--- a/pages/choose-username.tsx
+++ b/pages/choose-username.tsx
@@ -1,15 +1,13 @@
import SubmitButton from "@/components/SubmitButton";
import { signOut } from "next-auth/react";
-import Image from "next/image";
-import { useEffect, useState } from "react";
+import { useState } from "react";
import { toast } from "react-hot-toast";
import { useSession } from "next-auth/react";
-import { useRouter } from "next/router";
import useAccountStore from "@/store/account";
import CenteredForm from "@/layouts/CenteredForm";
import TextInput from "@/components/TextInput";
-export default function Subscribe() {
+export default function ChooseUsername() {
const [submitLoader, setSubmitLoader] = useState(false);
const [inputedUsername, setInputedUsername] = useState("");
diff --git a/pages/forgot.tsx b/pages/forgot.tsx
index 25389ad..eb60eb7 100644
--- a/pages/forgot.tsx
+++ b/pages/forgot.tsx
@@ -2,7 +2,6 @@ import SubmitButton from "@/components/SubmitButton";
import TextInput from "@/components/TextInput";
import CenteredForm from "@/layouts/CenteredForm";
import { signIn } from "next-auth/react";
-import Image from "next/image";
import Link from "next/link";
import { useState } from "react";
import { toast } from "react-hot-toast";
diff --git a/pages/login.tsx b/pages/login.tsx
index f06d3b4..f6968cc 100644
--- a/pages/login.tsx
+++ b/pages/login.tsx
@@ -2,7 +2,6 @@ import SubmitButton from "@/components/SubmitButton";
import TextInput from "@/components/TextInput";
import CenteredForm from "@/layouts/CenteredForm";
import { signIn } from "next-auth/react";
-import Image from "next/image";
import Link from "next/link";
import { useState } from "react";
import { toast } from "react-hot-toast";
diff --git a/pages/settings/account.tsx b/pages/settings/account.tsx
index 6108aeb..783c6f9 100644
--- a/pages/settings/account.tsx
+++ b/pages/settings/account.tsx
@@ -16,7 +16,7 @@ import Link from "next/link";
import ClickAwayHandler from "@/components/ClickAwayHandler";
import Checkbox from "@/components/Checkbox";
-export default function account() {
+export default function Account() {
const { update, data } = useSession();
const emailEnabled = process.env.NEXT_PUBLIC_EMAIL_PROVIDER;
diff --git a/pages/settings/appearance.tsx b/pages/settings/appearance.tsx
index 95300f2..4f333bc 100644
--- a/pages/settings/appearance.tsx
+++ b/pages/settings/appearance.tsx
@@ -3,7 +3,7 @@ import { useTheme } from "next-themes";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faSun, faMoon } from "@fortawesome/free-solid-svg-icons";
-export default function appearance() {
+export default function Appearance() {
const { theme, setTheme } = useTheme();
return (
diff --git a/pages/settings/archive.tsx b/pages/settings/archive.tsx
index fc60772..db1c6f1 100644
--- a/pages/settings/archive.tsx
+++ b/pages/settings/archive.tsx
@@ -6,7 +6,7 @@ import useAccountStore from "@/store/account";
import { toast } from "react-hot-toast";
import { AccountSettings } from "@/types/global";
-export default function archive() {
+export default function Archive() {
const [submitLoader, setSubmitLoader] = useState(false);
const { account, updateAccount } = useAccountStore();
const [user, setUser] = useState(account);
diff --git a/pages/settings/billing.tsx b/pages/settings/billing.tsx
index c2094c0..185f2a7 100644
--- a/pages/settings/billing.tsx
+++ b/pages/settings/billing.tsx
@@ -2,7 +2,7 @@ import SettingsLayout from "@/layouts/SettingsLayout";
import { useRouter } from "next/router";
import { useEffect } from "react";
-export default function billing() {
+export default function Billing() {
const router = useRouter();
useEffect(() => {
diff --git a/pages/settings/password.tsx b/pages/settings/password.tsx
index d17d23f..b68228c 100644
--- a/pages/settings/password.tsx
+++ b/pages/settings/password.tsx
@@ -6,7 +6,7 @@ import SubmitButton from "@/components/SubmitButton";
import { toast } from "react-hot-toast";
import TextInput from "@/components/TextInput";
-export default function password() {
+export default function Password() {
const [newPassword, setNewPassword1] = useState("");
const [newPassword2, setNewPassword2] = useState("");
diff --git a/pages/subscribe.tsx b/pages/subscribe.tsx
index a3c1279..b8b478a 100644
--- a/pages/subscribe.tsx
+++ b/pages/subscribe.tsx
@@ -1,6 +1,6 @@
import SubmitButton from "@/components/SubmitButton";
import { signOut } from "next-auth/react";
-import { useEffect, useState } from "react";
+import { useState } from "react";
import { toast } from "react-hot-toast";
import { useSession } from "next-auth/react";
import { useRouter } from "next/router";
From 93d1b00bbe579c9528ce02cc8bfcb8876f5032ff Mon Sep 17 00:00:00 2001
From: daniel31x13
Date: Thu, 19 Oct 2023 00:15:42 -0400
Subject: [PATCH 5/7] optimization
---
lib/api/controllers/users/updateUser.ts | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lib/api/controllers/users/updateUser.ts b/lib/api/controllers/users/updateUser.ts
index 65cbb1c..6bb4c22 100644
--- a/lib/api/controllers/users/updateUser.ts
+++ b/lib/api/controllers/users/updateUser.ts
@@ -164,7 +164,7 @@ export default async function updateUser(
const STRIPE_SECRET_KEY = process.env.STRIPE_SECRET_KEY;
- if (STRIPE_SECRET_KEY && emailEnabled)
+ if (STRIPE_SECRET_KEY && emailEnabled && sessionUser.email !== user.email)
await updateCustomerEmail(
STRIPE_SECRET_KEY,
sessionUser.email,
From 42e16cbf041566193c2010c96ff307ee705c5458 Mon Sep 17 00:00:00 2001
From: daniel31x13
Date: Thu, 19 Oct 2023 00:17:35 -0400
Subject: [PATCH 6/7] minor change
---
pages/settings/account.tsx | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/pages/settings/account.tsx b/pages/settings/account.tsx
index 783c6f9..9cd52b6 100644
--- a/pages/settings/account.tsx
+++ b/pages/settings/account.tsx
@@ -207,7 +207,7 @@ export default function Account() {
) : undefined}
{user.email !== account.email ? (
-
+
You will need to log back in after you apply this Email.
) : undefined}
From 146b8576f4004e7cb3689748a1a0c58372a4f936 Mon Sep 17 00:00:00 2001
From: daniel31x13
Date: Thu, 19 Oct 2023 00:20:28 -0400
Subject: [PATCH 7/7] cleared up old code
---
components/Modal/User/BillingPortal.tsx | 46 ----
components/Modal/User/ChangePassword.tsx | 115 ----------
components/Modal/User/PrivacySettings.tsx | 248 ----------------------
components/Modal/User/ProfileSettings.tsx | 195 -----------------
components/Modal/User/index.tsx | 107 ----------
components/ModalManagement.tsx | 12 --
lib/api/controllers/links/postLink.ts | 2 +-
store/modals.ts | 7 -
8 files changed, 1 insertion(+), 731 deletions(-)
delete mode 100644 components/Modal/User/BillingPortal.tsx
delete mode 100644 components/Modal/User/ChangePassword.tsx
delete mode 100644 components/Modal/User/PrivacySettings.tsx
delete mode 100644 components/Modal/User/ProfileSettings.tsx
delete mode 100644 components/Modal/User/index.tsx
diff --git a/components/Modal/User/BillingPortal.tsx b/components/Modal/User/BillingPortal.tsx
deleted file mode 100644
index 214bfca..0000000
--- a/components/Modal/User/BillingPortal.tsx
+++ /dev/null
@@ -1,46 +0,0 @@
-import { useState } from "react";
-import SubmitButton from "@/components/SubmitButton";
-import { toast } from "react-hot-toast";
-import { useRouter } from "next/router";
-import { faArrowUpRightFromSquare } from "@fortawesome/free-solid-svg-icons";
-
-export default function PaymentPortal() {
- const [submitLoader, setSubmitLoader] = useState(false);
- const router = useRouter();
-
- const submit = () => {
- setSubmitLoader(true);
- const load = toast.loading("Redirecting to billing portal...");
-
- router.push(process.env.NEXT_PUBLIC_STRIPE_BILLING_PORTAL_URL as string);
- };
-
- return (
-
-
-
- To manage/cancel your subsciption, visit the billing portal.
-
-
-
-
-
- If you still need help or encountered any issues, feel free to reach
- out to us at:{" "}
-
- support@linkwarden.app
-
-
-
-
- );
-}
diff --git a/components/Modal/User/ChangePassword.tsx b/components/Modal/User/ChangePassword.tsx
deleted file mode 100644
index 552463c..0000000
--- a/components/Modal/User/ChangePassword.tsx
+++ /dev/null
@@ -1,115 +0,0 @@
-import { Dispatch, SetStateAction, useEffect, useState } from "react";
-import { AccountSettings } from "@/types/global";
-import useAccountStore from "@/store/account";
-import { signOut, useSession } from "next-auth/react";
-import { faPenToSquare } from "@fortawesome/free-regular-svg-icons";
-import SubmitButton from "@/components/SubmitButton";
-import { toast } from "react-hot-toast";
-import TextInput from "@/components/TextInput";
-
-type Props = {
- togglePasswordFormModal: Function;
- setUser: Dispatch>;
- user: AccountSettings;
-};
-
-export default function ChangePassword({
- togglePasswordFormModal,
- setUser,
- user,
-}: Props) {
- const [newPassword, setNewPassword1] = useState("");
- const [newPassword2, setNewPassword2] = useState("");
-
- const [submitLoader, setSubmitLoader] = useState(false);
-
- const { account, updateAccount } = useAccountStore();
- const { update, data } = useSession();
-
- useEffect(() => {
- if (
- !(newPassword == "" || newPassword2 == "") &&
- newPassword === newPassword2
- ) {
- setUser({ ...user, newPassword });
- }
- }, [newPassword, newPassword2]);
-
- const submit = async () => {
- if (newPassword == "" || newPassword2 == "") {
- toast.error("Please fill all the fields.");
- }
-
- if (newPassword !== newPassword2)
- return toast.error("Passwords do not match.");
- else if (newPassword.length < 8)
- return toast.error("Passwords must be at least 8 characters.");
-
- setSubmitLoader(true);
-
- const load = toast.loading("Applying...");
-
- const response = await updateAccount({
- ...user,
- });
-
- toast.dismiss(load);
-
- if (response.ok) {
- toast.success("Settings Applied!");
-
- if (user.email !== account.email) {
- update({
- id: data?.user.id,
- });
-
- signOut();
- } else if (
- user.username !== account.username ||
- user.name !== account.name
- )
- update({
- id: data?.user.id,
- });
-
- setUser({ ...user, newPassword: undefined });
- togglePasswordFormModal();
- } else toast.error(response.data as string);
-
- setSubmitLoader(false);
- };
-
- return (
-
-
-
New Password
-
-
setNewPassword1(e.target.value)}
- placeholder="••••••••••••••"
- type="password"
- />
-
-
- Confirm New Password
-
-
- setNewPassword2(e.target.value)}
- placeholder="••••••••••••••"
- type="password"
- />
-
-
-
-
- );
-}
diff --git a/components/Modal/User/PrivacySettings.tsx b/components/Modal/User/PrivacySettings.tsx
deleted file mode 100644
index ce61782..0000000
--- a/components/Modal/User/PrivacySettings.tsx
+++ /dev/null
@@ -1,248 +0,0 @@
-import { Dispatch, SetStateAction, useEffect, useState } from "react";
-import Checkbox from "../../Checkbox";
-import useAccountStore from "@/store/account";
-import {
- AccountSettings,
- MigrationFormat,
- MigrationRequest,
-} from "@/types/global";
-import { signOut, useSession } from "next-auth/react";
-import { faPenToSquare } from "@fortawesome/free-regular-svg-icons";
-import SubmitButton from "../../SubmitButton";
-import { toast } from "react-hot-toast";
-import Link from "next/link";
-import ClickAwayHandler from "@/components/ClickAwayHandler";
-
-type Props = {
- toggleSettingsModal: Function;
- setUser: Dispatch>;
- user: AccountSettings;
-};
-
-export default function PrivacySettings({
- toggleSettingsModal,
- setUser,
- user,
-}: Props) {
- const { update, data } = useSession();
- const { account, updateAccount } = useAccountStore();
-
- const [importDropdown, setImportDropdown] = useState(false);
- const [submitLoader, setSubmitLoader] = useState(false);
-
- const [whitelistedUsersTextbox, setWhiteListedUsersTextbox] = useState(
- user.whitelistedUsers.join(", ")
- );
-
- useEffect(() => {
- setUser({
- ...user,
- whitelistedUsers: stringToArray(whitelistedUsersTextbox),
- });
- }, [whitelistedUsersTextbox]);
-
- useEffect(() => {
- setUser({ ...user, newPassword: undefined });
- }, []);
-
- const stringToArray = (str: string) => {
- const stringWithoutSpaces = str.replace(/\s+/g, "");
-
- const wordsArray = stringWithoutSpaces.split(",");
-
- return wordsArray;
- };
-
- const importBookmarks = async (e: any, format: MigrationFormat) => {
- const file: File = e.target.files[0];
-
- if (file) {
- var reader = new FileReader();
- reader.readAsText(file, "UTF-8");
- reader.onload = async function (e) {
- const load = toast.loading("Importing...");
-
- const request: string = e.target?.result as string;
-
- const body: MigrationRequest = {
- format,
- data: request,
- };
-
- const response = await fetch("/api/migration", {
- method: "POST",
- body: JSON.stringify(body),
- });
-
- const data = await response.json();
-
- toast.dismiss(load);
-
- toast.success("Imported the Bookmarks! Reloading the page...");
-
- setImportDropdown(false);
-
- setTimeout(() => {
- location.reload();
- }, 2000);
- };
- reader.onerror = function (e) {
- console.log("Error:", e);
- };
- }
- };
-
- const submit = async () => {
- setSubmitLoader(true);
-
- const load = toast.loading("Applying...");
-
- const response = await updateAccount({
- ...user,
- });
-
- toast.dismiss(load);
-
- if (response.ok) {
- toast.success("Settings Applied!");
-
- if (user.email !== account.email) {
- update({
- id: data?.user.id,
- });
-
- signOut();
- } else if (
- user.username !== account.username ||
- user.name !== account.name
- )
- update({
- id: data?.user.id,
- });
-
- setUser({ ...user, newPassword: undefined });
- toggleSettingsModal();
- } else toast.error(response.data as string);
- setSubmitLoader(false);
- };
-
- return (
-
-
-
- Profile Visibility
-
-
-
setUser({ ...user, isPrivate: !user.isPrivate })}
- />
-
-
- This will limit who can find and add you to other Collections.
-
-
- {user.isPrivate && (
-
-
- Whitelisted Users
-
-
- Please provide the Username of the users you wish to grant
- visibility to your profile. Separated by comma.
-
-
- )}
-
-
-
-
Import Data
-
-
-
-
-
-
- Export Data
-
-
-
-
-
-
-
- );
-}
diff --git a/components/Modal/User/ProfileSettings.tsx b/components/Modal/User/ProfileSettings.tsx
deleted file mode 100644
index 0bb48eb..0000000
--- a/components/Modal/User/ProfileSettings.tsx
+++ /dev/null
@@ -1,195 +0,0 @@
-import { Dispatch, SetStateAction, useEffect, useState } from "react";
-import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { faClose } from "@fortawesome/free-solid-svg-icons";
-import useAccountStore from "@/store/account";
-import { AccountSettings } from "@/types/global";
-import { signOut, useSession } from "next-auth/react";
-import { resizeImage } from "@/lib/client/resizeImage";
-import { faPenToSquare } from "@fortawesome/free-regular-svg-icons";
-import SubmitButton from "../../SubmitButton";
-import ProfilePhoto from "../../ProfilePhoto";
-import { toast } from "react-hot-toast";
-import TextInput from "@/components/TextInput";
-
-type Props = {
- toggleSettingsModal: Function;
- setUser: Dispatch>;
- user: AccountSettings;
-};
-
-const emailEnabled = process.env.NEXT_PUBLIC_EMAIL_PROVIDER;
-
-export default function ProfileSettings({
- toggleSettingsModal,
- setUser,
- user,
-}: Props) {
- const { update, data } = useSession();
- const { account, updateAccount } = useAccountStore();
- const [profileStatus, setProfileStatus] = useState(true);
-
- const [submitLoader, setSubmitLoader] = useState(false);
-
- const handleProfileStatus = (e: boolean) => {
- setProfileStatus(!e);
- };
-
- const handleImageUpload = async (e: any) => {
- const file: File = e.target.files[0];
-
- const fileExtension = file.name.split(".").pop()?.toLowerCase();
- const allowedExtensions = ["png", "jpeg", "jpg"];
-
- if (allowedExtensions.includes(fileExtension as string)) {
- const resizedFile = await resizeImage(file);
-
- if (
- resizedFile.size < 1048576 // 1048576 Bytes == 1MB
- ) {
- const reader = new FileReader();
-
- reader.onload = () => {
- setUser({ ...user, profilePic: reader.result as string });
- };
-
- reader.readAsDataURL(resizedFile);
- } else {
- toast.error("Please select a PNG or JPEG file thats less than 1MB.");
- }
- } else {
- toast.error("Invalid file format.");
- }
- };
-
- useEffect(() => {
- setUser({ ...user, newPassword: undefined });
- }, []);
-
- const submit = async () => {
- setSubmitLoader(true);
-
- const load = toast.loading("Applying...");
-
- const response = await updateAccount({
- ...user,
- });
-
- toast.dismiss(load);
-
- if (response.ok) {
- toast.success("Settings Applied!");
-
- if (user.email !== account.email) {
- update({
- id: data?.user.id,
- });
-
- signOut();
- } else if (
- user.username !== account.username ||
- user.name !== account.name
- )
- update({
- id: data?.user.id,
- });
-
- setUser({ ...user, newPassword: undefined });
- toggleSettingsModal();
- } else toast.error(response.data as string);
- setSubmitLoader(false);
- };
-
- return (
-
-
-
-
- Profile Photo
-
-
-
- {profileStatus && (
-
- setUser({
- ...user,
- profilePic: "",
- })
- }
- 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"
- >
-
-
- )}
-
-
-
-
-
-
-
-
-
-
- Display Name
-
-
setUser({ ...user, name: e.target.value })}
- />
-
-
-
-
Username
-
setUser({ ...user, username: e.target.value })}
- />
-
-
- {emailEnabled ? (
-
-
Email
-
setUser({ ...user, email: e.target.value })}
- />
-
- ) : undefined}
-
- {user.email !== account.email ? (
-
- You will need to log back in after you apply this Email.
-
- ) : undefined}
-
-
-
-
-
- );
-}
diff --git a/components/Modal/User/index.tsx b/components/Modal/User/index.tsx
deleted file mode 100644
index 54b1ba3..0000000
--- a/components/Modal/User/index.tsx
+++ /dev/null
@@ -1,107 +0,0 @@
-import { Tab } from "@headlessui/react";
-import { AccountSettings } from "@/types/global";
-import { useState } from "react";
-import ChangePassword from "./ChangePassword";
-import ProfileSettings from "./ProfileSettings";
-import PrivacySettings from "./PrivacySettings";
-import BillingPortal from "./BillingPortal";
-
-type Props = {
- toggleSettingsModal: Function;
- activeUser: AccountSettings;
- className?: string;
- defaultIndex?: number;
-};
-
-const STRIPE_BILLING_PORTAL_URL =
- process.env.NEXT_PUBLIC_STRIPE_BILLING_PORTAL_URL;
-
-export default function UserModal({
- className,
- defaultIndex,
- toggleSettingsModal,
- activeUser,
-}: Props) {
- const [user, setUser] = useState(activeUser);
-
- return (
-
-
-
-
- selected
- ? "px-2 py-1 bg-sky-200 dark:bg-sky-800 dark:text-white duration-100 rounded-md outline-none"
- : "px-2 py-1 hover:bg-slate-200 hover:dark:bg-neutral-700 rounded-md duration-100 outline-none"
- }
- >
- Profile Settings
-
-
-
- selected
- ? "px-2 py-1 bg-sky-200 dark:bg-sky-800 dark:text-white duration-100 rounded-md outline-none"
- : "px-2 py-1 hover:bg-slate-200 hover:dark:bg-neutral-700 rounded-md duration-100 outline-none"
- }
- >
- Privacy Settings
-
-
-
- selected
- ? "px-2 py-1 bg-sky-200 dark:bg-sky-800 dark:text-white duration-100 rounded-md outline-none"
- : "px-2 py-1 hover:bg-slate-200 hover:dark:bg-neutral-700 rounded-md duration-100 outline-none"
- }
- >
- Password
-
-
- {STRIPE_BILLING_PORTAL_URL ? (
-
- selected
- ? "px-2 py-1 bg-sky-200 dark:bg-sky-800 duration-100 rounded-md outline-none"
- : "px-2 py-1 hover:bg-slate-200 hover:dark:bg-neutral-700 rounded-md duration-100 outline-none"
- }
- >
- Billing
-
- ) : undefined}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {STRIPE_BILLING_PORTAL_URL ? (
-
-
-
- ) : undefined}
-
-
-
- );
-}
diff --git a/components/ModalManagement.tsx b/components/ModalManagement.tsx
index 1958fe3..aa7790d 100644
--- a/components/ModalManagement.tsx
+++ b/components/ModalManagement.tsx
@@ -2,12 +2,10 @@ import useModalStore from "@/store/modals";
import Modal from "./Modal";
import LinkModal from "./Modal/Link";
import {
- AccountSettings,
CollectionIncludingMembersAndLinkCount,
LinkIncludingShortenedCollectionAndTags,
} from "@/types/global";
import CollectionModal from "./Modal/Collection";
-import UserModal from "./Modal/User";
import { useEffect } from "react";
import { useRouter } from "next/router";
@@ -49,15 +47,5 @@ export default function ModalManagement() {
/>
);
- else if (modal && modal.modal === "ACCOUNT")
- return (
-
-
-
- );
else return <>>;
}
diff --git a/lib/api/controllers/links/postLink.ts b/lib/api/controllers/links/postLink.ts
index a9afe16..33b4ba2 100644
--- a/lib/api/controllers/links/postLink.ts
+++ b/lib/api/controllers/links/postLink.ts
@@ -2,7 +2,7 @@ import { prisma } from "@/lib/api/db";
import { LinkIncludingShortenedCollectionAndTags } from "@/types/global";
import getTitle from "@/lib/api/getTitle";
import archive from "@/lib/api/archive";
-import { Collection, Link, UsersAndCollections } from "@prisma/client";
+import { Collection, UsersAndCollections } from "@prisma/client";
import getPermission from "@/lib/api/getPermission";
import createFolder from "@/lib/api/storage/createFolder";
diff --git a/store/modals.ts b/store/modals.ts
index 047bc28..71c336f 100644
--- a/store/modals.ts
+++ b/store/modals.ts
@@ -1,17 +1,10 @@
import {
- AccountSettings,
CollectionIncludingMembersAndLinkCount,
LinkIncludingShortenedCollectionAndTags,
} from "@/types/global";
import { create } from "zustand";
type Modal =
- | {
- modal: "ACCOUNT";
- state: boolean;
- active: AccountSettings;
- defaultIndex?: number;
- }
| {
modal: "LINK";
state: boolean;