2023-05-16 12:08:28 -05:00
// Copyright (C) 2022-present Daniel31x13 <daniel31x13@gmail.com>
// This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3.
// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
// You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
2023-05-22 07:20:48 -05:00
import { useEffect , useState } from "react" ;
2023-05-16 12:08:28 -05:00
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome" ;
2023-05-22 07:20:48 -05:00
import { faUser , faClose } from "@fortawesome/free-solid-svg-icons" ;
2023-05-18 13:02:05 -05:00
import Checkbox from "../Checkbox" ;
import useAccountStore from "@/store/account" ;
2023-05-20 14:25:00 -05:00
import { AccountSettings } from "@/types/global" ;
2023-05-21 04:54:42 -05:00
import { useSession } from "next-auth/react" ;
2023-05-22 07:20:48 -05:00
import { resizeImage } from "@/lib/client/resizeImage" ;
2023-05-22 15:29:24 -05:00
import Modal from "." ;
import ChangePassword from "./ChangePassword" ;
import { faPenToSquare } from "@fortawesome/free-regular-svg-icons" ;
2023-05-16 12:08:28 -05:00
type Props = {
toggleSettingsModal : Function ;
} ;
export default function UserSettings ( { toggleSettingsModal } : Props ) {
2023-05-21 04:54:42 -05:00
const { update } = useSession ( ) ;
2023-05-20 14:25:00 -05:00
const { account , updateAccount } = useAccountStore ( ) ;
2023-05-22 15:29:24 -05:00
const [ user , setUser ] = useState < AccountSettings > ( {
. . . account ,
2023-05-23 18:07:26 -05:00
// profilePic: null,
2023-05-22 15:29:24 -05:00
} ) ;
2023-05-22 23:08:16 -05:00
const [ whitelistedUsersTextbox , setWhiteListedUsersTextbox ] = useState (
user . whitelistedUsers . join ( ", " )
) ;
2023-05-22 15:29:24 -05:00
const [ passwordFormModal , setPasswordFormModal ] = useState ( false ) ;
const togglePasswordFormModal = ( ) = > {
setPasswordFormModal ( ! passwordFormModal ) ;
} ;
const setPasswordForm = ( oldPassword? : string , newPassword? : string ) = > {
setUser ( { . . . user , oldPassword , newPassword } ) ;
} ;
2023-05-23 18:07:26 -05:00
// useEffect(() => {
// const determineProfilePicSource = async () => {
// const path = `/api/avatar/${account.id}`;
// const imageExists = await avatarExists(path).catch((e) => console.log(e));
// if (imageExists) setUser({ ...user, profilePic: path });
// };
2023-05-20 14:25:00 -05:00
2023-05-23 18:07:26 -05:00
// determineProfilePicSource();
// }, []);
2023-05-16 12:08:28 -05:00
2023-05-22 23:08:16 -05:00
useEffect ( ( ) = > {
setUser ( {
. . . user ,
whitelistedUsers : stringToArray ( whitelistedUsersTextbox ) ,
} ) ;
} , [ whitelistedUsersTextbox ] ) ;
const stringToArray = ( str : string ) = > {
const stringWithoutSpaces = str . replace ( /\s+/g , "" ) ;
const wordsArray = stringWithoutSpaces . split ( "," ) ;
return wordsArray ;
} ;
2023-05-22 07:20:48 -05:00
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 ) ;
2023-05-20 14:25:00 -05:00
2023-05-22 07:20:48 -05:00
if (
resizedFile . size < 1048576 // 1048576 Bytes == 1MB
) {
const reader = new FileReader ( ) ;
reader . onload = ( ) = > {
setUser ( { . . . user , profilePic : reader.result as string } ) ;
} ;
reader . readAsDataURL ( resizedFile ) ;
} else {
console . log ( "Please select a PNG or JPEG file thats less than 1MB." ) ;
}
} else {
console . log ( "Invalid file format." ) ;
}
2023-05-18 13:02:05 -05:00
} ;
2023-05-16 12:08:28 -05:00
2023-05-18 13:02:05 -05:00
const submit = async ( ) = > {
2023-05-22 23:08:16 -05:00
console . log ( user ) ;
2023-05-22 15:29:24 -05:00
await updateAccount ( {
. . . user ,
} ) ;
2023-05-23 18:07:26 -05:00
console . log ( account ) ;
2023-05-22 15:29:24 -05:00
setPasswordForm ( undefined , undefined ) ;
2023-05-20 14:25:00 -05:00
2023-05-22 07:20:48 -05:00
if ( user . email !== account . email || user . name !== account . name )
2023-05-21 04:54:42 -05:00
update ( { email : user.email , name : user.name } ) ;
2023-05-16 12:08:28 -05:00
} ;
return (
< div className = "flex flex-col gap-3 sm:w-[35rem] w-80" >
2023-05-18 13:02:05 -05:00
< p className = "text-xl text-sky-500 mb-2 text-center" > Settings < / p >
< p className = "text-sky-600" > Profile Settings < / p >
2023-05-22 07:20:48 -05:00
{ user . email !== account . email || user . name !== account . name ? (
2023-05-22 15:29:24 -05:00
< p className = "text-gray-500 text-sm sm:w-1/2" >
2023-05-21 04:54:42 -05:00
Note : The page will be refreshed to apply the changes of "Email" or
"Display Name" .
< / p >
) : null }
2023-05-18 13:02:05 -05:00
< div className = "grid sm:grid-cols-2 gap-3 auto-rows-auto" >
< div className = "flex flex-col gap-3" >
< div >
< p className = "text-sm font-bold text-sky-300 mb-2" > Display Name < / p >
< input
type = "text"
2023-05-20 14:25:00 -05:00
value = { user . name }
onChange = { ( e ) = > setUser ( { . . . user , name : e.target.value } ) }
2023-05-18 13:02:05 -05:00
className = "w-full rounded-md p-2 border-sky-100 border-solid border outline-none focus:border-sky-500 duration-100"
/ >
< / div >
< div >
< p className = "text-sm font-bold text-sky-300 mb-2" > Email < / p >
< input
type = "text"
2023-05-20 14:25:00 -05:00
value = { user . email }
onChange = { ( e ) = > setUser ( { . . . user , email : e.target.value } ) }
2023-05-18 13:02:05 -05:00
className = "w-full rounded-md p-2 border-sky-100 border-solid border outline-none focus:border-sky-500 duration-100"
/ >
< / div >
< div >
< p className = "text-sm font-bold text-sky-300 mb-2" > Password < / p >
2023-05-22 15:29:24 -05:00
< div className = "w-fit" onClick = { togglePasswordFormModal } >
2023-05-18 13:02:05 -05:00
< div className = "border border-sky-100 rounded-md bg-white px-2 py-1 text-center select-none cursor-pointer text-sky-900 duration-100 hover:border-sky-500" >
Change Password
< / div >
< / div >
2023-05-22 23:08:16 -05:00
{ user . newPassword && user . oldPassword ? (
< p className = "text-gray-500 text-sm mt-2" >
Password modified . Please click { " " }
< span className = " whitespace-nowrap" > "Apply Settings" < / span > to
apply the changes . .
< / p >
) : null }
2023-05-18 13:02:05 -05:00
< / div >
< / div >
2023-05-22 07:20:48 -05:00
< div className = "sm:row-span-2 sm:justify-self-center mb-3" >
2023-05-18 13:02:05 -05:00
< p className = "text-sm font-bold text-sky-300 mb-2 sm:text-center" >
Profile Photo
< / p >
< div className = "w-28 h-28 flex items-center justify-center border border-sky-100 rounded-full relative" >
2023-05-22 07:20:48 -05:00
{ user . profilePic && user . profilePic !== "DELETE" ? (
< div >
< img
alt = "Profile Photo"
className = "rounded-full object-cover h-28 w-28 border border-sky-100 border-opacity-0"
src = { user . profilePic }
/ >
< div
onClick = { ( ) = >
setUser ( {
. . . user ,
profilePic : "DELETE" ,
} )
}
className = "absolute top-1 left-1 w-5 h-5 flex items-center justify-center border p-1 bg-white border-sky-100 rounded-full text-gray-500 hover:text-red-500 duration-100 cursor-pointer"
>
< FontAwesomeIcon icon = { faClose } className = "w-3 h-3" / >
< / div >
< / div >
) : (
< FontAwesomeIcon
icon = { faUser }
className = "w-10 h-10 text-sky-400"
/ >
) }
2023-05-18 13:02:05 -05:00
< div className = "absolute -bottom-2 left-0 right-0 mx-auto w-fit text-center" >
< label
htmlFor = "upload-photo"
title = "PNG or JPG (Max: 3MB)"
className = "border border-sky-100 rounded-md bg-white px-2 text-center select-none cursor-pointer text-sky-900 duration-100 hover:border-sky-500"
>
Browse . . .
< input
type = "file"
name = "photo"
id = "upload-photo"
2023-05-22 07:20:48 -05:00
accept = ".png, .jpeg, .jpg"
2023-05-18 13:02:05 -05:00
className = "hidden"
2023-05-22 07:20:48 -05:00
onChange = { handleImageUpload }
2023-05-18 13:02:05 -05:00
/ >
< / label >
< / div >
< / div >
2023-05-22 07:20:48 -05:00
< / div >
2023-05-18 13:02:05 -05:00
< / div >
2023-05-22 15:29:24 -05:00
{ /* <hr / > TODO : Export functionality
2023-05-18 13:02:05 -05:00
< p className = "text-sky-600" > Data Settings < / p >
< div className = "w-fit" >
< div className = "border border-sky-100 rounded-md bg-white px-2 py-1 text-center select-none cursor-pointer text-sky-900 duration-100 hover:border-sky-500" >
Export Data
< / div >
2023-05-22 15:29:24 -05:00
< / div > * / }
2023-05-18 13:02:05 -05:00
< hr / >
< p className = "text-sky-600" > Privacy Settings < / p >
< Checkbox
2023-05-22 23:08:16 -05:00
label = "Make profile private"
state = { user . isPrivate }
2023-05-18 13:02:05 -05:00
className = "text-sm sm:text-base"
2023-05-22 23:08:16 -05:00
onClick = { ( ) = > setUser ( { . . . user , isPrivate : ! user . isPrivate } ) }
2023-05-18 13:02:05 -05:00
/ >
2023-05-22 23:08:16 -05:00
< p className = "text-gray-500 text-sm mb-3" >
This will limit who can find and add you to other Collections .
< / p >
{ user . isPrivate ? (
2023-05-20 14:25:00 -05:00
< div >
2023-05-22 23:08:16 -05:00
< p className = "text-sm font-bold text-sky-300 mb-2" >
Whitelisted Users
< / p >
2023-05-20 14:25:00 -05:00
< p className = "text-gray-500 text-sm mb-3" >
2023-05-22 23:08:16 -05:00
Please provide the Email addresses of the users you wish to grant
visibility to your profile . Separate the addresses with a comma .
Users not included will be unable to view your profile .
2023-05-20 14:25:00 -05:00
< / p >
< textarea
className = "w-full resize-none border rounded-md duration-100 bg-white p-2 outline-none border-sky-100 focus:border-sky-500"
2023-05-22 23:08:16 -05:00
placeholder = "Your profile is hidden from everyone right now..."
value = { whitelistedUsersTextbox }
onChange = { ( e ) = > {
setWhiteListedUsersTextbox ( e . target . value ) ;
} }
2023-05-20 14:25:00 -05:00
> < / textarea >
< / div >
) : null }
2023-05-22 07:20:48 -05:00
< div
className = "mx-auto mt-2 bg-sky-500 text-white flex items-center gap-2 py-2 px-5 rounded-md select-none font-bold cursor-pointer duration-100 hover:bg-sky-400"
onClick = { submit }
>
2023-05-22 15:29:24 -05:00
< FontAwesomeIcon icon = { faPenToSquare } className = "h-5" / >
2023-05-22 07:20:48 -05:00
Apply Settings
< / div >
2023-05-22 15:29:24 -05:00
{ passwordFormModal ? (
< Modal toggleModal = { togglePasswordFormModal } >
< ChangePassword
user = { user }
togglePasswordFormModal = { togglePasswordFormModal }
setPasswordForm = { setPasswordForm }
/ >
< / Modal >
) : null }
2023-05-16 12:08:28 -05:00
< / div >
) ;
}