Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Issue 524/update profile to account #560

Closed
wants to merge 18 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions src/AppRoutes.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ import { Routes, Route, Navigate, Outlet } from 'react-router-dom';
import { useSession } from '@hooks';
import { SessionProvider } from '@contexts';
// Component Imports
import { CIVIC_FORM_LIST, FormLayout } from '@components/CivicProfileForms';
import { PROFILE_FORM_LIST, FormLayout } from '@components/ProfileForms';
import { MESSAGE_PAGES_LIST, MessagesLayout } from '@components/Messages';
// Page Imports
import { CivicProfile, Home, Contacts, Profile, Signup } from './pages';
import { Account, Profile, Contacts, Home, Signup } from './pages';

const ProtectedRoute = ({ isLoggedIn, children }) =>
isLoggedIn ? children ?? <Outlet /> : <Navigate to="/" replace />;
Expand Down Expand Up @@ -43,7 +43,7 @@ const AppRoutes = () => {
<Route element={<ProtectedRoute isLoggedIn={session.info.isLoggedIn} />}>
<Route path="/contacts">
<Route index element={<Contacts />} />
<Route path=":webId" element={<Profile />} />
<Route path=":webId" element={<Account />} />
</Route>
<Route path="/messages">
{MESSAGE_PAGES_LIST.map(({ path: routePath, element }) => (
Expand All @@ -60,9 +60,9 @@ const AppRoutes = () => {
/>
))}
</Route>
<Route path="/profile" element={<Profile />} />
<Route path="/civic-profile" element={<CivicProfile />}>
{CIVIC_FORM_LIST.map((formProps) => (
<Route path="/account" element={<Account />} />
<Route path="/profile" element={<Profile />}>
{PROFILE_FORM_LIST.map((formProps) => (
<Route
{...formProps}
element={
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,48 +3,48 @@ import React, { useContext, useEffect, useState } from 'react';
// Custom Hook Imports
import { useSession } from '@hooks';
// Material UI Imports
import Box from '@mui/material/Box';
import Stack from '@mui/material/Stack';
import useMediaQuery from '@mui/material/useMediaQuery';
import { useTheme } from '@mui/material/styles';
// Context Imports
import { SignedInUserContext } from '@contexts';
// Component Inputs
import ProfileImageField from './ProfileImageField';
import ProfileInputField from './ProfileInputField';
import ProfileEditButtonGroup from './ProfileEditButtonGroup';
import AccountImageField from './AccountImageField';
import AccountInputField from './AccountInputField';
import AccountEditButtonGroup from './AccountEditButtonGroup';

/**
* The UserProfile Component is a component that renders the user's profile on
* The UserAccount Component is a component that renders the user's account on
* PASS
*
* @memberof Profile
* @name ProfileComponent
* @name AccountComponent
* @param {object} Props - Props for ClientProfile component
* @param {object} [Props.contactProfile] - Contact object with data from profile
* or null if user profile is selected
* @returns {React.JSX.Element} The UserProfile Component
* @returns {React.JSX.Element} The UserAccount Component
*/
const ProfileComponent = ({ contactProfile }) => {
const AccountComponent = ({ contactProfile }) => {
const { session } = useSession();
const { updateProfileInfo, setProfileData, profileData, fetchProfileInfo } =
useContext(SignedInUserContext);

// Public Profile Data
const [profileName, setProfileName] = useState(profileData?.profileInfo?.profileName);
// Public Profile Data (Account Data)
const [accountName, setAccountName] = useState(profileData?.profileInfo?.profileName);
const [nickname, setNickname] = useState(profileData?.profileInfo?.nickname);

const [edit, setEdit] = useState(false);

const loadProfileData = async () => {
const loadAccountData = async () => {
const profileDataSolid = await fetchProfileInfo(session.info.webId);
setProfileData(profileDataSolid);

setProfileName(profileDataSolid.profileInfo?.profileName);
setAccountName(profileDataSolid.profileInfo?.profileName);
setNickname(profileDataSolid.profileInfo?.nickname);
};

const handleCancelEdit = () => {
loadProfileData();
loadAccountData();
setEdit(!edit);
};

Expand All @@ -56,33 +56,26 @@ const ProfileComponent = ({ contactProfile }) => {
event.preventDefault();

const inputValues = {
profileName,
accountName,
nickname
};

await updateProfileInfo(session, profileData, inputValues);

loadProfileData();
loadAccountData();
setEdit(false);
};

useEffect(() => {
loadProfileData();
loadAccountData();
}, []);

const theme = useTheme();
const isSmallScreen = useMediaQuery(theme.breakpoints.down('sm'));

return (
<Box
sx={{
display: 'flex',
flexDirection: isSmallScreen ? 'column' : 'row',
gap: '15px',
padding: '10px'
}}
>
<ProfileImageField loadProfileData={loadProfileData} contactProfile={contactProfile} />
<Stack direction={isSmallScreen ? 'column' : 'row'} spacing={2} padding="10px">
<AccountImageField loadAccountData={loadAccountData} contactProfile={contactProfile} />
<form
onSubmit={handleUpdateProfile}
style={{
Expand All @@ -93,40 +86,34 @@ const ProfileComponent = ({ contactProfile }) => {
padding: '20px'
}}
>
<Box
sx={{
display: 'flex',
flexDirection: 'column',
gap: '10px'
}}
>
<ProfileInputField
<Stack spacing={1}>
<AccountInputField
inputName="Name"
inputValue={
contactProfile
? `${contactProfile?.givenName ?? ''} ${contactProfile?.familyName ?? ''}`
: profileName
: accountName
}
setInputValue={setProfileName}
setInputValue={setAccountName}
edit={edit}
/>
<ProfileInputField
<AccountInputField
inputName="Nickname"
inputValue={contactProfile ? contactProfile?.nickname : nickname}
setInputValue={setNickname}
edit={edit}
/>
</Box>
</Stack>
{!contactProfile && (
<ProfileEditButtonGroup
<AccountEditButtonGroup
edit={edit}
handleCancelEdit={handleCancelEdit}
handleEditInput={handleEditInput}
/>
)}
</form>
</Box>
</Stack>
);
};

export default ProfileComponent;
export default AccountComponent;
Original file line number Diff line number Diff line change
Expand Up @@ -8,21 +8,21 @@ import ClearIcon from '@mui/icons-material/Clear';
import EditIcon from '@mui/icons-material/Edit';

/**
* The ProfileEditButtonGroup Component is a component that consist of the profile
* page edit buttons for the ProfileInputField component
* The AccountEditButtonGroup Component is a component that consist of the account
* page edit buttons for the AccountInputField component
*
* @memberof Profile
* @name ProfileEditButtonGroup
* @param {object} Props - Props for Profile Edit buttons
* @name AccountEditButtonGroup
* @param {object} Props - Props for Account Edit buttons
* @param {boolean} Props.edit - Boolean state for editing values in the
* ProfileInputField component
* AccountInputField component
* @param {() => void} Props.handleCancelEdit - Handler function for canceling
* edit for ProfileInputField component
* edit for AccountInputField component
* @param {() => void} Props.handleEditInput - Handler function for editing the
* ProfileInputField component
* @returns {React.JSX.Element} The ProfileEditButtonGroup
* AccountInputField component
* @returns {React.JSX.Element} The AccountEditButtonGroup
*/
const ProfileEditButtonGroup = ({ edit, handleCancelEdit, handleEditInput }) => (
const AccountEditButtonGroup = ({ edit, handleCancelEdit, handleEditInput }) => (
<Box
sx={{
display: 'flex',
Expand Down Expand Up @@ -61,4 +61,4 @@ const ProfileEditButtonGroup = ({ edit, handleCancelEdit, handleEditInput }) =>
</Box>
);

export default ProfileEditButtonGroup;
export default AccountEditButtonGroup;
Original file line number Diff line number Diff line change
Expand Up @@ -4,84 +4,80 @@ import React, { useContext, useState } from 'react';
import { useSession } from '@hooks';
// Material UI Imports
import Avatar from '@mui/material/Avatar';
import Box from '@mui/material/Box';
import Button from '@mui/material/Button';
import HideImageIcon from '@mui/icons-material/HideImage';
import ImageIcon from '@mui/icons-material/Image';
import Stack from '@mui/material/Stack';
import Typography from '@mui/material/Typography';
// Contexts Imports
import { SignedInUserContext } from '@contexts';
import useNotification from '../../hooks/useNotification';

/**
* ProfileImageField Component - Component that creates the editable inputs fields
* for the Profile page
* AccountImageField Component - Component that creates the editable
* account avatar image selector for the Account page
*
* @memberof Profile
* @name ProfileImageField
* @param {object} Props - Props used for NewMessage
* @param {() => void} Props.loadProfileData - Handler function for setting local
* state for profile card in PASS
* @name AccountImageField
* @param {object} Props - Props used for AccountImageField
* @param {() => void} Props.loadAccountData - Handler function for setting local
* state for account card in PASS
* @param {object} [Props.contactProfile] - Contact object with data from profile
* or null if user profile is selected
* @returns {React.JSX.Element} React component for NewMessage
* or null if user account is selected
* @returns {React.JSX.Element} React component for AccountImageField
*/
const ProfileImageField = ({ loadProfileData, contactProfile }) => {
const AccountImageField = ({ loadAccountData, contactProfile }) => {
const { addNotification } = useNotification();
const { session } = useSession();
const { profileData, fetchProfileInfo, removeProfileImage, uploadProfileImage } =
useContext(SignedInUserContext);
const [profileImg, setProfileImg] = useState(localStorage.getItem('profileImage'));
const [accountImg, setAccountImg] = useState(localStorage.getItem('profileImage'));

const handleProfileImage = async (event) => {
const handleAccountImage = async (event) => {
if (event.target.files[0].size > 1 * 1000 * 1024) {
addNotification('error', 'Profile images have a maximum size of 1MB.');
addNotification('error', 'Account images have a maximum size of 1MB.');
} else {
await uploadProfileImage(session, profileData, event.target.files[0]);

const updatedProfileData = await fetchProfileInfo(session.info.webId);
localStorage.setItem('profileImage', updatedProfileData.profileInfo.profileImage);
setProfileImg(updatedProfileData.profileInfo.profileImage);
const updatedAccountData = await fetchProfileInfo(session.info.webId);
localStorage.setItem('profileImage', updatedAccountData.profileInfo.profileImage);
setAccountImg(updatedAccountData.profileInfo.profileImage);

loadProfileData();
loadAccountData();
}
};

const handleRemoveProfileImg = async () => {
if (window.confirm("You're about to delete your profile image. Do you wish to continue?")) {
const handleRemoveAccountImg = async () => {
if (window.confirm("You're about to delete your account image. Do you wish to continue?")) {
await removeProfileImage(session, profileData);

loadProfileData();
loadAccountData();
localStorage.removeItem('profileImage');
setProfileImg(null);
setAccountImg(null);
}
};

return (
<Box
sx={{
display: 'flex',
flexDirection: 'column',
justifyContent: 'center',
alignItems: 'center',
boxShadow: '0 0 3px 0 black',
padding: '20px',
gap: '10px'
}}
<Stack
spacing={1}
justifyContent="center"
alignItems="center"
padding="20px"
boxShadow="0 0 3px 0 black"
>
<Typography color="black">Profile Image: </Typography>
<Typography color="black">Account Image: </Typography>
<Avatar
src={contactProfile ? contactProfile.profileImage : profileImg}
alt="PASS profile"
src={contactProfile ? contactProfile.profileImage : accountImg}
alt="PASS account"
sx={{ height: '100px', width: '100px', objectFit: 'contain' }}
/>
{!contactProfile &&
(profileImg ? (
(accountImg ? (
<Button
variant="outlined"
color="error"
sx={{ width: '150px' }}
onClick={handleRemoveProfileImg}
onClick={handleRemoveAccountImg}
endIcon={<HideImageIcon />}
>
Remove Img
Expand All @@ -91,16 +87,16 @@ const ProfileImageField = ({ loadProfileData, contactProfile }) => {
variant="outlined"
component="label"
color="primary"
onChange={handleProfileImage}
onChange={handleAccountImage}
endIcon={<ImageIcon />}
sx={{ width: '150px' }}
>
Choose Img
<input type="file" hidden accept=".gif, .png, .jpeg, .jpg, .webp" />
</Button>
))}
</Box>
</Stack>
);
};

export default ProfileImageField;
export default AccountImageField;
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,20 @@ import Input from '@mui/material/Input';
import InputLabel from '@mui/material/InputLabel';

/**
* ProfileInputField Component - Component that creates the editable inputs fields
* for the Profile page
* AccountInputField Component - Component that creates the editable input fields
* for the Account page
*
* @memberof Profile
* @name ProfileInputField
* @param {object} Props - Props used for NewMessage
* @name AccountInputField
* @param {object} Props - Props used for AccountInputField
* @param {string} Props.inputName - Name of input field
* @param {string} Props.inputValue - Value of input field used for updating profile
* @param {string} Props.inputValue - Value of input field used for updating account
* @param {(value: React.SetStateAction<string|null>) => void} Props.setInputValue
* - Set function for inputValue
* @param {boolean} Props.edit - Boolean used to toggle edit inputs
* @returns {React.JSX.Element} React component for NewMessage
* @returns {React.JSX.Element} React component for AccountInputField
*/
const ProfileInputField = ({ inputName, inputValue, setInputValue, edit }) => (
const AccountInputField = ({ inputName, inputValue, setInputValue, edit }) => (
<Box sx={{ display: 'flex', alignItems: 'center', gap: '10px' }}>
<InputLabel htmlFor={`input-${inputName}`} sx={{ color: 'black' }}>
{inputName}:
Expand All @@ -34,4 +34,4 @@ const ProfileInputField = ({ inputName, inputValue, setInputValue, edit }) => (
</Box>
);

export default ProfileInputField;
export default AccountInputField;
Loading