Files
recipe-manager/client/src/components/pages/Profile.tsx
2023-02-14 14:19:19 -06:00

196 lines
7.3 KiB
TypeScript

import { useNavigate } from "react-router-dom";
import { useEffect, useState } from "react";
import { AxiosError } from "axios";
import { useAuthContext } from "../../context/AuthContext";
import API from "../../util/API";
import Protect from "../../util/Protect";
import { ICollection, IUser } from "../../schemas";
import Friends from "../derived/Friends";
import CollectionList from "../derived/CollectionList";
import { Button, Divider, Page, Panel } from "../ui";
import useDateFormat from "../../hooks/useDateFormat";
interface ProfileMetadata {
targetID?: string | number
targetUser?: IUser | undefined
formattedDate: string
collections: ICollection[]
friends: IUser[]
isSet: boolean
}
export default function Profile() {
// globals and router utils
const { user, token } = useAuthContext();
const navigate = useNavigate();
// UI state info
const [contents, setContents] = useState<JSX.Element>(<></>);
// master state for this page
const [metadata, setMetadata] = useState<ProfileMetadata>({
targetID: undefined,
targetUser: undefined,
formattedDate: "",
collections: [],
friends: [],
isSet: false
});
// STEP 1: FETCH METADATA (requires token)
useEffect(() => {
if (!token || !user) return;
const params = new URLSearchParams(window.location.search);
const targetID = params.get('id');
// if a target is specified in the url
if (targetID) {
setMetadata((prev: ProfileMetadata) => {
return { ...prev, targetID: targetID }
});
// fetch and store user data with associated user id
(async() => {
try {
const User = new API.User(token);
const result = await User.getByID(targetID);
if (result) {
setMetadata((prev) => {
return {
...prev,
targetUser: result,
formattedDate: useDateFormat(result.datecreated)
}
})
}
} catch (error) {
if (error instanceof AxiosError) {
if (error?.response?.status == 404) {
// to do: replace with customizable 404 page
setContents(
<Page>
<h1>404: Not found</h1>
<Divider />
<Panel>
<p>No user found with ID {targetID}</p>
<Button onClick={() => navigate('/')}>Home</Button>
</Panel>
</Page>
)
return;
}
} else {
console.error(error);
}
}
})();
// do the same for this user's collections and friends
(async() => {
const Collections = new API.Collection(token);
const result = await Collections.getAllAuthored(metadata.targetID);
if (result) {
setMetadata((prev: ProfileMetadata) => {
return {
...prev, collections: result
}
})
}
})();
} else {
// otherwise, this is the current user's profile and should load some slightly different info
(async() => {
const Collections = new API.Collection(token);
const result = await Collections.getAllAuthored();
if (result) {
setMetadata((prev: ProfileMetadata) => {
return {
...prev, collections: result
}
})
}
})();
setMetadata((prev) => {
return {
...prev,
formattedDate: useDateFormat(user.datecreated)
}
})
}
setMetadata((prev) => {
return { ...prev, isSet: true }
})
}, [token]);
// STEP 2: set up page UI based on profile config above
useEffect(() => {
if (metadata.isSet) {
// if this is another user's profile
if (metadata.targetUser) {
setContents(
<Protect redirect="/">
<div className="profile-authenticated">
<h1>{metadata.targetUser.firstname}'s Profile</h1>
<div className="profile-grid">
<Panel>
<h2>About {metadata.targetUser.firstname} {metadata.targetUser.lastname}:</h2>
<p>Recipin Member since: {metadata.formattedDate}</p>
</Panel>
<Panel>
<h2>{metadata.targetUser.firstname}'s collections ({ metadata.collections.length ?? "0" }):</h2>
<CollectionList targetID={metadata.targetUser.id} />
</Panel>
<Panel>
<h2>{metadata.targetUser.firstname}'s friends:</h2>
<Friends />
</Panel>
</div>
</div>
</Protect>
)
} else {
// if this is the current user's profile
setContents(
<Protect redirect="profile">
<div className="profile-authenticated">
<h1>{user!.firstname}'s Profile</h1>
<div className="profile-grid">
<Panel>
<h2>About me:</h2>
<p>{user!.firstname} {user!.lastname}</p>
<p>Recipin Member since: {metadata.formattedDate}</p>
<Divider />
<p>30 recipes</p>
<p>2 collections</p>
</Panel>
<Panel>
{/* include number of collections */}
<h2><a href="/collections">My collections</a> ({ metadata.collections.length || 0 }):</h2>
<CollectionList />
</Panel>
<Panel>
<Friends />
</Panel>
</div>
</div>
</Protect>
)
}
}
}, [metadata])
// STEP 3: mount the UI
return contents
}