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 (
- +

- Profile + Account

@@ -94,7 +94,7 @@ export default function SettingsSidebar({ className }: { className?: string }) {
- + {/*
- + */} - + {/*
- + */}
+ +
+ + +

+ Help +

+
+ + + +
+ + +

+ GitHub +

+
+ +
- - -
- - -

- GitHub -

-
- - - -
- - -

- Help -

-
-
); 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 & Export +

+
+ +
+ +
+
+

+ Import your data from other platforms. +

+
setImportDropdown(true)} + className="w-fit relative" + id="import-dropdown" + > +
+ Import From +
+ {importDropdown ? ( + { + const target = e.target as HTMLInputElement; + if (target.id !== "import-dropdown") + setImportDropdown(false); + }} + className={`absolute top-7 left-0 w-48 py-1 shadow-md border border-sky-100 dark:border-neutral-700 bg-gray-50 dark:bg-neutral-800 rounded-md flex flex-col z-20`} + > +
+ + +
+
+ ) : null} +
+
+ +
+

+ 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. +

+