more customization on user/versus other-user profiles
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
import { useEffect, useState } from "react";
|
import { FC, useEffect, useState } from "react";
|
||||||
import { v4 } from "uuid";
|
import { v4 } from "uuid";
|
||||||
import { useAuthContext } from "../../context/AuthContext";
|
import { useAuthContext } from "../../context/AuthContext";
|
||||||
import { getAllUsers, getFriendships, getPendingFriendRequests, getUserByID } from "../../util/apiUtils";
|
import { getAllUsers, getFriendships, getPendingFriendRequests, getUserByID } from "../../util/apiUtils";
|
||||||
@@ -8,7 +8,7 @@ import { IUser, IFriendship } from "../../schemas";
|
|||||||
import { Card, Divider, Panel } from "../ui";
|
import { Card, Divider, Panel } from "../ui";
|
||||||
import FriendSearchWidget from "../ui/Widgets/FriendSearchWidget";
|
import FriendSearchWidget from "../ui/Widgets/FriendSearchWidget";
|
||||||
|
|
||||||
export default function Friends() {
|
const Friends: FC<{ targetUser?: IUser }> = ({ targetUser }) => {
|
||||||
const [friends, setFriends] = useState<IFriendship[]>();
|
const [friends, setFriends] = useState<IFriendship[]>();
|
||||||
const [userList, setUserList] = useState(new Array<IUser>());
|
const [userList, setUserList] = useState(new Array<IUser>());
|
||||||
const { user, token } = useAuthContext();
|
const { user, token } = useAuthContext();
|
||||||
@@ -35,29 +35,35 @@ export default function Friends() {
|
|||||||
if (!token || !friends) return;
|
if (!token || !friends) return;
|
||||||
|
|
||||||
friends.map(async (friend: IFriendship) => {
|
friends.map(async (friend: IFriendship) => {
|
||||||
const Friends = new API.Friendship(token);
|
const User = new API.User(token);
|
||||||
const userData = await Friends.getByID(friend.targetid as string);
|
const userData = await User.getByID(friend.targetid as string);
|
||||||
if (userData) setUserList((prev: IUser[]) => {
|
if (userData) setUserList((prev: IUser[]) => {
|
||||||
return [...prev, userData]
|
if (prev.includes(userData)) {
|
||||||
|
return prev;
|
||||||
|
} else {
|
||||||
|
return [...prev, userData]
|
||||||
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}, [friends]);
|
}, [friends]);
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
console.log(userList);
|
|
||||||
}, [setUserList])
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{ userList.length ?
|
{ userList.length ?
|
||||||
(
|
(
|
||||||
<Card extraStyles="flex-row">
|
<Card extraStyles="flex-row">
|
||||||
<h2>Your friendships:</h2>
|
<h2>Friends ({ userList?.length ?? "0" }):</h2>
|
||||||
|
|
||||||
{
|
{
|
||||||
userList.map((user: IUser) => {
|
userList.map((user: IUser) => {
|
||||||
return <UserCard key={v4()} user={user} />
|
return <UserCard key={v4()} targetUser={user} />
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
<aside>
|
||||||
|
<p>Looking for someone else?</p>
|
||||||
|
<p>You can search for more friends <a href="/add-friends">here!</a></p>
|
||||||
|
</aside>
|
||||||
</Card>
|
</Card>
|
||||||
) :
|
) :
|
||||||
(
|
(
|
||||||
@@ -73,4 +79,6 @@ export default function Friends() {
|
|||||||
}
|
}
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export default Friends
|
||||||
@@ -1,53 +1,67 @@
|
|||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { v4 } from "uuid";
|
import { v4 } from "uuid";
|
||||||
import { useAuthContext } from "../../context/AuthContext";
|
import { useAuthContext } from "../../context/AuthContext";
|
||||||
import { ICollection, IRecipe } from "../../schemas";
|
import { ICollection } from "../../schemas";
|
||||||
import API from "../../util/API";
|
import API from "../../util/API";
|
||||||
import { Card, Page } from "../ui";
|
import { Page, Panel } from "../ui";
|
||||||
|
|
||||||
interface CollectionDetails {
|
|
||||||
idx: number
|
|
||||||
collection: ICollection
|
|
||||||
recipes: IRecipe[]
|
|
||||||
}
|
|
||||||
|
|
||||||
const CollectionBrowser = () => {
|
const CollectionBrowser = () => {
|
||||||
const [list, setList] = useState<ICollection[]>();
|
const [list, setList] = useState<ICollection[]>();
|
||||||
const { token } = useAuthContext();
|
const { token } = useAuthContext();
|
||||||
|
|
||||||
|
async function getRecipeCount(collection: ICollection) {
|
||||||
|
if (!token) return [];
|
||||||
|
const collections = new API.Collection(token);
|
||||||
|
const result = await collections.getRecipesFromOne(collection.id);
|
||||||
|
if (result) return result;
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
async function mapRecipes() {
|
||||||
|
if (!list) return;
|
||||||
|
|
||||||
|
return list.map(async (each) => {
|
||||||
|
const count = await getRecipeCount(each);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Panel key={v4()}>
|
||||||
|
<h2>{each.name}</h2>
|
||||||
|
<p>{count.length} recipes</p>
|
||||||
|
<a href={`/collections/${each.id}`}>Link to details</a>
|
||||||
|
</Panel>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!token) return;
|
if (!token) return;
|
||||||
(async() => {
|
(async() => {
|
||||||
const collections = new API.Collection(token);
|
const collections = new API.Collection(token);
|
||||||
const recipes = new API.Recipe(token);
|
|
||||||
|
|
||||||
const allRecipes = await collections.getAllAuthored();
|
const allRecipes = await collections.getAllAuthored();
|
||||||
if (allRecipes) {
|
if (allRecipes) setList(allRecipes);
|
||||||
const result = new Array<CollectionDetails[]>();
|
|
||||||
|
|
||||||
let i = 0;
|
|
||||||
for (let each of allRecipes) {
|
|
||||||
}
|
|
||||||
|
|
||||||
setList(allRecipes);
|
|
||||||
}
|
|
||||||
})();
|
})();
|
||||||
}, [token])
|
}, [token])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
|
||||||
|
}, [list])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Page>
|
<Page>
|
||||||
<h1>Browsing your {2} collections:</h1>
|
{ list && (
|
||||||
|
<>
|
||||||
{
|
<h1>Browsing your {list.length} collection{ (list.length !== 1) && "s" }:</h1>
|
||||||
list && list.map(each => {
|
|
||||||
|
{ list.map(each => {
|
||||||
return (
|
return (
|
||||||
<Card key={v4()}>
|
<Panel key={v4()}>
|
||||||
<h2>{each.name}</h2>
|
<h2>{each.name}</h2>
|
||||||
<a href={`/collections/${each.id}`}>Link to details</a>
|
<a href={`/collections/${each.id}`}>Link to details</a>
|
||||||
</Card>
|
</Panel>
|
||||||
)
|
)
|
||||||
})
|
})}
|
||||||
}
|
</>
|
||||||
|
)}
|
||||||
</Page>
|
</Page>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,8 +37,6 @@ export default function Profile() {
|
|||||||
isSet: false
|
isSet: false
|
||||||
});
|
});
|
||||||
|
|
||||||
const dateFormatter = new Intl.DateTimeFormat('en-US', { timeStyle: undefined, dateStyle: "long" });
|
|
||||||
|
|
||||||
// STEP 1: FETCH METADATA (requires token)
|
// STEP 1: FETCH METADATA (requires token)
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!token || !user) return;
|
if (!token || !user) return;
|
||||||
@@ -115,10 +113,6 @@ export default function Profile() {
|
|||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
|
|
||||||
(async() => {
|
|
||||||
const Friends = new API.Friendship(token);
|
|
||||||
})
|
|
||||||
|
|
||||||
setMetadata((prev) => {
|
setMetadata((prev) => {
|
||||||
return {
|
return {
|
||||||
...prev,
|
...prev,
|
||||||
@@ -135,6 +129,8 @@ export default function Profile() {
|
|||||||
// STEP 2: set up page UI based on profile config above
|
// STEP 2: set up page UI based on profile config above
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (metadata.isSet) {
|
if (metadata.isSet) {
|
||||||
|
|
||||||
|
// if this is another user's profile
|
||||||
if (metadata.targetUser) {
|
if (metadata.targetUser) {
|
||||||
setContents(
|
setContents(
|
||||||
<Protect redirect="/">
|
<Protect redirect="/">
|
||||||
@@ -148,12 +144,12 @@ export default function Profile() {
|
|||||||
</Panel>
|
</Panel>
|
||||||
|
|
||||||
<Panel>
|
<Panel>
|
||||||
<h2>My collections:</h2>
|
<h2>{metadata.targetUser.firstname}'s collections ({ metadata.collections.length ?? "0" }):</h2>
|
||||||
<CollectionList targetID={metadata.targetUser.id} />
|
<CollectionList targetID={metadata.targetUser.id} />
|
||||||
</Panel>
|
</Panel>
|
||||||
|
|
||||||
<Panel>
|
<Panel>
|
||||||
<h2>My friends:</h2>
|
<h2>{metadata.targetUser.firstname}'s friends:</h2>
|
||||||
<Friends />
|
<Friends />
|
||||||
</Panel>
|
</Panel>
|
||||||
</div>
|
</div>
|
||||||
@@ -161,6 +157,8 @@ export default function Profile() {
|
|||||||
</Protect>
|
</Protect>
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
|
// if this is the current user's profile
|
||||||
setContents(
|
setContents(
|
||||||
<Protect redirect="profile">
|
<Protect redirect="profile">
|
||||||
<div className="profile-authenticated">
|
<div className="profile-authenticated">
|
||||||
@@ -178,12 +176,11 @@ export default function Profile() {
|
|||||||
|
|
||||||
<Panel>
|
<Panel>
|
||||||
{/* include number of collections */}
|
{/* include number of collections */}
|
||||||
<h2>My collections ({ metadata.collections.length || 0 }):</h2>
|
<h2><a href="/collections">My collections</a> ({ metadata.collections.length || 0 }):</h2>
|
||||||
<CollectionList />
|
<CollectionList />
|
||||||
</Panel>
|
</Panel>
|
||||||
|
|
||||||
<Panel>
|
<Panel>
|
||||||
<h2>My friends:</h2>
|
|
||||||
<Friends />
|
<Friends />
|
||||||
</Panel>
|
</Panel>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { useParams } from "react-router-dom";
|
import { useParams } from "react-router-dom";
|
||||||
import { Page, Panel } from "../ui";
|
import { Page, Panel } from "../ui";
|
||||||
import { IRecipe } from "../../util/types";
|
import { IRecipe } from "../../schemas";
|
||||||
import { getRecipeByID } from "../../util/apiUtils";
|
import { getRecipeByID } from "../../util/apiUtils";
|
||||||
|
|
||||||
export default function Recipe() {
|
export default function Recipe() {
|
||||||
|
|||||||
@@ -1,47 +1,55 @@
|
|||||||
|
import { AxiosError } from "axios";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { useAuthContext } from "../../context/AuthContext";
|
import { useAuthContext } from "../../context/AuthContext";
|
||||||
import API from "../../util/API";
|
|
||||||
// import { addFriend, getPendingFriendRequests } from "../../util/apiUtils";
|
|
||||||
import { UserCardType } from "../../util/types";
|
import { UserCardType } from "../../util/types";
|
||||||
|
import API from "../../util/API";
|
||||||
import Button from "./Button";
|
import Button from "./Button";
|
||||||
import Card from "./Card";
|
import Card from "./Card";
|
||||||
|
|
||||||
const UserCard: UserCardType = ({ extraStyles, user }) => {
|
const UserCard: UserCardType = ({ extraStyles, targetUser }) => {
|
||||||
|
const [buttonVariant, setButtonVariant] = useState(<></>);
|
||||||
const { token } = useAuthContext();
|
const { token } = useAuthContext();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!token) return;
|
if (!token) return;
|
||||||
|
|
||||||
(async function() {
|
(async function() {
|
||||||
const friends = new API.Friendship(token);
|
try {
|
||||||
const requestsOpen = await friends.getPendingFriendRequests();
|
const friends = new API.Friendship(token);
|
||||||
console.log(requestsOpen);
|
const requestsOpen = await friends.getPendingFriendRequests();
|
||||||
if (!requestsOpen) return;
|
if (!requestsOpen) return;
|
||||||
|
|
||||||
for (let req of requestsOpen) {
|
for (let req of requestsOpen) {
|
||||||
if (req.targetid == user.id) {
|
if (req.targetid == targetUser.id) {
|
||||||
console.log('should disable');
|
setButtonVariant(<Button disabled>Request Sent!</Button>)
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setButtonVariant(<Button onClick={handleClick}>Send Request</Button>)
|
||||||
|
} catch (error) {
|
||||||
|
if (error instanceof AxiosError) {
|
||||||
|
console.log(error.response?.statusText);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
})();
|
||||||
console.log('should not disable');
|
|
||||||
});
|
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
const handleClick = async () => {
|
const handleClick = async () => {
|
||||||
if (!token) return;
|
if (!token) return;
|
||||||
const friends = new API.Friendship(token);
|
const friends = new API.Friendship(token);
|
||||||
const request = await friends.addFriend(user.id!.toString());
|
const request = await friends.addFriend(targetUser.id!.toString());
|
||||||
if (request) console.log("Friend request sent to " + user.firstname);
|
if (request) {
|
||||||
|
setButtonVariant(<Button disabled>Request Sent!</Button>)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card extraStyles={'user-card' + extraStyles}>
|
<Card extraStyles={'user-card' + extraStyles}>
|
||||||
<div className="avatar"></div>
|
<div className="avatar"></div>
|
||||||
<h3>{user.firstname} {user.lastname.substring(0,1)}.</h3>
|
<h3><a href={`/profile?id=${targetUser.id}`}>{targetUser.firstname} {targetUser.lastname.substring(0,1)}.</a></h3>
|
||||||
<h4>@{user.handle}</h4>
|
<h4>@{targetUser.handle}</h4>
|
||||||
<Button disabledText={"Request Sent"} onClick={handleClick}>Add Me</Button>
|
{ buttonVariant }
|
||||||
</Card>
|
</Card>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -154,6 +154,19 @@ module API {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async getTargetUserFriendships(id: string | number) {
|
||||||
|
try {
|
||||||
|
const response = await this.instance.get(this.endpoint + `?targetUser=${id}`, this.headers);
|
||||||
|
return Promise.resolve(response.data);
|
||||||
|
} catch (e) {
|
||||||
|
const error = e as AxiosError;
|
||||||
|
if (error.response?.status == 404) {
|
||||||
|
console.log('no friends found');
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async getPendingFriendRequests() {
|
async getPendingFriendRequests() {
|
||||||
const response = await this.instance.get(this.endpoint + "?pending=true", this.headers);
|
const response = await this.instance.get(this.endpoint + "?pending=true", this.headers);
|
||||||
return Promise.resolve(response.data);
|
return Promise.resolve(response.data);
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ export interface ProtectParams extends PortalBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface UserCardProps extends PortalBase {
|
interface UserCardProps extends PortalBase {
|
||||||
user: IUser
|
targetUser: IUser
|
||||||
canAdd?: boolean
|
canAdd?: boolean
|
||||||
liftData?: (data: any) => void
|
liftData?: (data: any) => void
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,15 +24,20 @@ export const friendRouter = (app: Express) => {
|
|||||||
// get all friendships for a user
|
// get all friendships for a user
|
||||||
router.get('/', async (req, res, next) => {
|
router.get('/', async (req, res, next) => {
|
||||||
const user = req.user as IUser;
|
const user = req.user as IUser;
|
||||||
const { pending } = req.query;
|
const { pending, targetUser } = req.query;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (pending) {
|
if (pending) {
|
||||||
const { code, data } = await UserInstance.getPendingFriendRequests(user.id as number);
|
const { code, data } = await UserInstance.getPendingFriendRequests(user.id as number);
|
||||||
res.status(code).send(data);
|
res.status(code).send(data);
|
||||||
} else {
|
} else {
|
||||||
const { code, data } = await UserInstance.getFriends(user.id as number);
|
if (targetUser) {
|
||||||
res.status(code).send(data);
|
const { code, data } = await UserInstance.getFriends(parseInt(targetUser as string));
|
||||||
|
res.status(code).send(data);
|
||||||
|
} else {
|
||||||
|
const { code, data } = await UserInstance.getFriends(user.id as number);
|
||||||
|
res.status(code).send(data);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch(e) {
|
} catch(e) {
|
||||||
next(e);
|
next(e);
|
||||||
|
|||||||
Reference in New Issue
Block a user