bit of work on friends
This commit is contained in:
@@ -22,6 +22,7 @@ import GroceryListCollection from './components/pages/GroceryListCollection';
|
||||
import { TokenType } from './util/types';
|
||||
import './sass/App.scss';
|
||||
import handleToken from './util/handleToken';
|
||||
import AddFriends from './components/pages/AddFriends';
|
||||
|
||||
function App() {
|
||||
const { setUser, token, setToken } = useAuthContext();
|
||||
@@ -54,6 +55,7 @@ function App() {
|
||||
<Route path="/profile" element={<Profile />} />
|
||||
<Route path="/collections" element={<CollectionBrowser />} />
|
||||
<Route path="/collections/:id" element={<Collection />} />
|
||||
<Route path="/add-friends" element={<AddFriends />} />
|
||||
<Route path="/explore" element={<Browser header="" searchFunction={() => {}} />} />
|
||||
<Route path="/recipe/:id" element={<Recipe />} />
|
||||
<Route path="/subscriptions" element={<Subscriptions />} />
|
||||
|
||||
22
client/src/components/pages/AddFriends.tsx
Normal file
22
client/src/components/pages/AddFriends.tsx
Normal file
@@ -0,0 +1,22 @@
|
||||
import Protect from "../../util/Protect"
|
||||
import { Divider, Panel } from "../ui"
|
||||
import FriendSearchWidget from "../ui/Widgets/NewFriendWidget"
|
||||
|
||||
const AddFriends = () => {
|
||||
return (
|
||||
<Protect>
|
||||
<h1>Search for New Friends</h1>
|
||||
|
||||
<Divider />
|
||||
|
||||
<Panel>
|
||||
<h2>Use the widget below to search for new friends!</h2>
|
||||
<Divider />
|
||||
|
||||
<FriendSearchWidget />
|
||||
</Panel>
|
||||
</Protect>
|
||||
)
|
||||
}
|
||||
|
||||
export default AddFriends
|
||||
@@ -55,6 +55,7 @@ const LoggedIn = () => {
|
||||
dropdownActive && (
|
||||
<Dropdown extraStyles="top-menu-bar actions-bar">
|
||||
<Button onClick={() => handleOptionSelect('/add-recipe')}>Add a Recipe</Button>
|
||||
<Button onClick={() => handleOptionSelect("/add-friends")}>Add Friends</Button>
|
||||
<Button onClick={() => handleOptionSelect('/collections')}>My Collections</Button>
|
||||
<Button onClick={() => handleOptionSelect('/subscriptions')}>Subscriptions</Button>
|
||||
<Button onClick={() => handleOptionSelect('/profile')}>Profile</Button>
|
||||
|
||||
@@ -1,31 +1,38 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import { addFriend, getPendingFriendRequests } from "../../util/apiUtils";
|
||||
import { useAuthContext } from "../../context/AuthContext";
|
||||
import API from "../../util/API";
|
||||
// import { addFriend, getPendingFriendRequests } from "../../util/apiUtils";
|
||||
import { UserCardType } from "../../util/types";
|
||||
import Button from "./Button";
|
||||
import Card from "./Card";
|
||||
|
||||
const UserCard: UserCardType = ({ extraStyles, user, canAdd = false, liftData }) => {
|
||||
const [shouldDisable, setShouldDisable] = useState<boolean>(canAdd);
|
||||
|
||||
const UserCard: UserCardType = ({ extraStyles, user }) => {
|
||||
const { token } = useAuthContext();
|
||||
|
||||
useEffect(() => {
|
||||
if (!token) return;
|
||||
|
||||
(async function() {
|
||||
const requestsOpen = await getPendingFriendRequests();
|
||||
const friends = new API.Friendship(token);
|
||||
const requestsOpen = await friends.getPendingFriendRequests();
|
||||
console.log(requestsOpen);
|
||||
if (!requestsOpen) return;
|
||||
|
||||
for (let req of requestsOpen) {
|
||||
if (req.targetid == user.id) {
|
||||
setShouldDisable(true);
|
||||
console.log('should disable');
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
setShouldDisable(false);
|
||||
})();
|
||||
console.log('should not disable');
|
||||
});
|
||||
}, [])
|
||||
|
||||
const handleClick = async () => {
|
||||
const { id } = user;
|
||||
const request = await addFriend(id!.toString());
|
||||
if (!token) return;
|
||||
const friends = new API.Friendship(token);
|
||||
const request = await friends.addFriend(user.id!.toString());
|
||||
if (request) console.log("Friend request sent to " + user.firstname);
|
||||
}
|
||||
|
||||
@@ -34,7 +41,7 @@ const UserCard: UserCardType = ({ extraStyles, user, canAdd = false, liftData })
|
||||
<div className="avatar"></div>
|
||||
<h3>{user.firstname} {user.lastname.substring(0,1)}.</h3>
|
||||
<h4>@{user.handle}</h4>
|
||||
{ canAdd && <Button disabledText={"Request Sent"} disabled={shouldDisable} onClick={handleClick}>Add Me</Button> }
|
||||
<Button disabledText={"Request Sent"} onClick={handleClick}>Add Me</Button>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
|
||||
60
client/src/components/ui/Widgets/NewFriendWidget.tsx
Normal file
60
client/src/components/ui/Widgets/NewFriendWidget.tsx
Normal file
@@ -0,0 +1,60 @@
|
||||
import { ChangeEvent, FC, useCallback, useEffect, useState } from "react";
|
||||
import { IUser } from "../../../schemas";
|
||||
import { TextField, UserCard } from "..";
|
||||
import { v4 } from "uuid";
|
||||
import API from "../../../util/API";
|
||||
import { useAuthContext } from "../../../context/AuthContext";
|
||||
|
||||
const FriendSearchWidget: FC<{}> = () => {
|
||||
const { token } = useAuthContext();
|
||||
|
||||
const [searchTerm, setSearchTerm] = useState<string>();
|
||||
const [userPool, setUserPool] = useState<IUser[]>([]);
|
||||
const [pendingRequests, setPendingRequests] = useState();
|
||||
const [friendResults, setFriendResults] = useState<IUser[]>([]);
|
||||
|
||||
// load available user pool on mount
|
||||
useEffect(() => {
|
||||
if (!token) return;
|
||||
(async function() {
|
||||
const users = new API.User(token);
|
||||
const result = await users.getAll();
|
||||
if (result) setUserPool(result);
|
||||
})();
|
||||
|
||||
(async function() {
|
||||
const friends = new API.Friendship(token);
|
||||
const result = await friends.getAll();
|
||||
setFriendResults(result);
|
||||
})();
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
console.log(searchTerm);
|
||||
searchTerm && setUserPool((prev) => {
|
||||
const newPool = prev.filter(person => {
|
||||
if (person.firstname.toLowerCase().includes(searchTerm) || person.lastname.toLowerCase().includes(searchTerm) || person.handle.toLowerCase().includes(searchTerm)) return person;
|
||||
})
|
||||
|
||||
return newPool;
|
||||
})
|
||||
}, [searchTerm])
|
||||
|
||||
useEffect(() => {
|
||||
console.log(userPool);
|
||||
}, [userPool])
|
||||
|
||||
return (
|
||||
<div id="friend-search-widget">
|
||||
<TextField onChange={(e: ChangeEvent<HTMLInputElement>) => setSearchTerm(e.target.value.toLowerCase())} placeholder={'Search'} />
|
||||
|
||||
{
|
||||
userPool.map((friend: IUser) => {
|
||||
return <UserCard key={v4()} user={friend} canAdd liftData={() => {}} />
|
||||
})
|
||||
}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default FriendSearchWidget;
|
||||
@@ -1,4 +1,4 @@
|
||||
import { AxiosHeaders, AxiosRequestHeaders, AxiosResponse } from "axios";
|
||||
import { AxiosError, AxiosHeaders, AxiosRequestHeaders, AxiosResponse } from "axios";
|
||||
import { IUser, IUserAuth, IFriendship, IRecipe, IIngredient, ICollection, IGroceryList } from "../schemas";
|
||||
import { default as _instance } from "./axiosInstance";
|
||||
|
||||
@@ -138,13 +138,31 @@ module API {
|
||||
|
||||
export class Friendship extends RestController<IFriendship> {
|
||||
constructor(token: string) {
|
||||
super(Settings.getAPISTRING() + "/app/friends", token);
|
||||
super(Settings.getAPISTRING() + "/app/friend", token);
|
||||
}
|
||||
|
||||
override async getAll() {
|
||||
try {
|
||||
const response = await this.instance.get(this.endpoint, 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() {
|
||||
const response = await this.instance.get(this.endpoint + "?pending=true", this.headers);
|
||||
return Promise.resolve(response.data);
|
||||
}
|
||||
|
||||
async addFriend(id: string | number) {
|
||||
const response = await this.instance.post(this.endpoint + `/${id}`, this.headers);
|
||||
return Promise.resolve(response.data);
|
||||
}
|
||||
}
|
||||
|
||||
export class Recipe extends RestController<IRecipe> {
|
||||
|
||||
@@ -9,19 +9,8 @@ const router = Router();
|
||||
export const friendRouter = (app: Express) => {
|
||||
app.use('/app/friend', router);
|
||||
|
||||
router.use((req, res, next) => {
|
||||
let test = req.session.user;
|
||||
|
||||
if (req.session.user == undefined) {
|
||||
throw new Error("No session found");
|
||||
} else {
|
||||
const narrowed = req.session.user;
|
||||
next();
|
||||
}
|
||||
})
|
||||
|
||||
router.post('/:targetid', restrictAccess, async (req, res, next) => {
|
||||
const user = req.session.user as IUser;
|
||||
const user = req.user as IUser;
|
||||
const { targetid } = req.params;
|
||||
|
||||
try {
|
||||
@@ -34,7 +23,7 @@ export const friendRouter = (app: Express) => {
|
||||
|
||||
// get all friendships for a user
|
||||
router.get('/', async (req, res, next) => {
|
||||
const user = req.session.user as IUser;
|
||||
const user = req.user as IUser;
|
||||
const { pending } = req.query;
|
||||
|
||||
try {
|
||||
@@ -53,7 +42,7 @@ export const friendRouter = (app: Express) => {
|
||||
// get one friendship by its id
|
||||
router.get('/:id', async (req, res, next) => {
|
||||
const { id } = req.params;
|
||||
const user = req.session.user as IUser;
|
||||
const user = req.user as IUser;
|
||||
|
||||
try {
|
||||
const { code, data } = await UserInstance.getFriendshipByID(id, user.id as number);
|
||||
@@ -76,7 +65,7 @@ export const friendRouter = (app: Express) => {
|
||||
router.put('/:id', async (req, res, next) => {
|
||||
const data = req.body;
|
||||
const { id } = req.params;
|
||||
const user = req.session.user as IUser;
|
||||
const user = req.user as IUser;
|
||||
|
||||
try {
|
||||
const response = await UserInstance.updateFriendship(id, user.id as number, data);
|
||||
@@ -85,4 +74,6 @@ export const friendRouter = (app: Express) => {
|
||||
next(e);
|
||||
}
|
||||
})
|
||||
|
||||
return router;
|
||||
}
|
||||
Reference in New Issue
Block a user