add component for accepting/denying requests

This commit is contained in:
Michael Fatemi 2021-07-24 18:25:54 -04:00
parent 6f2d53270d
commit 54ed65a037
6 changed files with 170 additions and 37 deletions

View File

@ -1,6 +1,8 @@
import { createContext, useCallback, useEffect } from 'react'; import { createContext, useCallback, useEffect } from 'react';
import { import {
acceptCarpoolRequest,
cancelCarpoolInvite, cancelCarpoolInvite,
denyCarpoolRequest,
getCarpool, getCarpool,
leaveCarpool, leaveCarpool,
sendCarpoolInvite, sendCarpoolInvite,
@ -25,13 +27,19 @@ type CarpoolState = {
export const CarpoolContext = createContext({ export const CarpoolContext = createContext({
carpool: {} as CarpoolState, carpool: {} as CarpoolState,
sendInvite: (user: { id: number; name: string }) => { async sendInvite(user: { id: number; name: string }) {
console.error('not implemented: sendInvite'); console.error('not implemented: sendInvite');
}, },
cancelInvite: (user: { id: number; name: string }) => { async cancelInvite(user: { id: number; name: string }) {
console.error('not implemented: cancelInvite'); console.error('not implemented: cancelInvite');
}, },
leave: () => { async acceptRequest(userId: number) {
console.error('not implemented: acceptRequest');
},
async denyRequest(userId: number) {
console.error('not implemented: denyRequest');
},
leave() {
console.error('not implemented: leave'); console.error('not implemented: leave');
}, },
}); });
@ -55,46 +63,83 @@ export default function Carpool({ id }: { id: number }) {
}); });
}, [id, setCarpool]); }, [id, setCarpool]);
const acceptRequest = useCallback(
async (userId: number) => {
if (!carpool) {
console.error(
'Trying to accept request when carpool has not been loaded.'
);
return;
}
await acceptCarpoolRequest(id, userId);
const invite = carpool.invitations[userId];
const name = invite.user.name;
delete carpool.invitations[userId];
carpool.members.push({ id: userId, name });
},
[carpool, id]
);
const denyRequest = useCallback(
async (userId: number) => {
if (!carpool) {
console.error(
'Trying to deny request when carpool has not been loaded.'
);
return;
}
await denyCarpoolRequest(id, userId);
delete carpool.invitations[userId];
},
[carpool, id]
);
const sendInvite = useCallback( const sendInvite = useCallback(
(user: { id: number; name: string }) => { async (user: { id: number; name: string }) => {
if (carpool) { if (!carpool) {
sendCarpoolInvite(id, user.id)
.then(() => {
carpool.invitations[user.id] = { isRequest: false, user };
})
.catch(console.error);
} else {
console.error( console.error(
'Trying to send invite when carpool has not been loaded.' 'Trying to send invite when carpool has not been loaded.'
); );
return;
}
try {
await sendCarpoolInvite(id, user.id);
carpool.invitations[user.id] = { isRequest: false, user };
} catch (e) {
console.error(e);
} }
}, },
[carpool, id] [carpool, id]
); );
const cancelInvite = useCallback( const cancelInvite = useCallback(
(user: { id: number; name: string }) => { async (user: { id: number; name: string }) => {
if (!carpool) { if (!carpool) {
return null; console.error(
'Trying to cancel invite when carpool has not been loaded.'
);
return;
} }
cancelCarpoolInvite(id, user.id) try {
.then(() => { await cancelCarpoolInvite(id, user.id);
delete carpool.invitations[user.id]; } catch (e) {
}) console.error(e);
.catch(console.error); }
delete carpool.invitations[user.id];
}, },
[carpool, id] [carpool, id]
); );
const eventId = carpool?.event.id; const eventId = carpool?.event.id;
const leave = useCallback(() => { const leave = useCallback(async () => {
if (eventId) { if (eventId) {
leaveCarpool(id) try {
.then(() => { await leaveCarpool(id);
window.location.href = '/events/' + eventId; window.location.href = '/events/' + eventId;
}) } catch (e) {
.catch(console.error); console.error(e);
}
} }
}, [eventId, id]); }, [eventId, id]);
@ -108,6 +153,8 @@ export default function Carpool({ id }: { id: number }) {
carpool, carpool,
sendInvite, sendInvite,
cancelInvite, cancelInvite,
acceptRequest,
denyRequest,
leave, leave,
}} }}
> >

View File

@ -5,16 +5,19 @@ import { useContext } from 'react';
import useToggle from '../useToggle'; import useToggle from '../useToggle';
import { CarpoolContext } from './Carpool'; import { CarpoolContext } from './Carpool';
import InvitationList from './InvitationList'; import InvitationList from './InvitationList';
import RequestList from './RequestList';
const spanStyle = { const spanStyle = {
padding: '0.5rem', padding: '0.5rem',
display: 'flex', display: 'flex',
alignItems: 'center', alignItems: 'center',
cursor: 'pointer', cursor: 'pointer',
}; userSelect: 'none',
} as const;
export default function CarpoolTopButtonsMembersOnly() { export default function CarpoolTopButtonsMembersOnly() {
const [invitationsOpen, toggleInvitationsOpen] = useToggle(false); const [invitationsOpen, toggleInvitationsOpen] = useToggle(false);
const [requestsOpen, toggleRequestsOpen] = useToggle(false);
const { leave } = useContext(CarpoolContext); const { leave } = useContext(CarpoolContext);
return ( return (
@ -26,8 +29,8 @@ export default function CarpoolTopButtonsMembersOnly() {
margin: '0.5rem 0', margin: '0.5rem 0',
}} }}
> >
<span style={spanStyle} onClick={console.log}> <span style={spanStyle} onClick={toggleRequestsOpen}>
<MailOutlineIcon style={{ marginRight: '0.5rem' }} /> 1 request <MailOutlineIcon style={{ marginRight: '0.5rem' }} /> View requests
</span> </span>
<span style={spanStyle} onClick={toggleInvitationsOpen}> <span style={spanStyle} onClick={toggleInvitationsOpen}>
<PersonAddIcon style={{ marginRight: '0.5rem' }} /> Invite <PersonAddIcon style={{ marginRight: '0.5rem' }} /> Invite
@ -38,6 +41,7 @@ export default function CarpoolTopButtonsMembersOnly() {
</div> </div>
{invitationsOpen && <InvitationList />} {invitationsOpen && <InvitationList />}
{requestsOpen && <RequestList />}
</> </>
); );
} }

View File

@ -0,0 +1,77 @@
import { useMemo, useState } from 'react';
import { useCallback } from 'react';
import { useContext } from 'react';
import { lightgrey } from '../../lib/colors';
import UIButton from '../UI/UIButton';
import { CarpoolContext } from './Carpool';
function RequestRow({ user }: { user: { id: number; name: string } }) {
const { acceptRequest, denyRequest } = useContext(CarpoolContext);
const [pending, setPending] = useState(false);
const acceptCallback = useCallback(() => {
if (pending) {
return;
}
setPending(true);
acceptRequest(user.id)
.catch(console.error)
.finally(() => setPending(false));
}, [acceptRequest, pending, user.id]);
const denyCallback = useCallback(() => {
if (pending) {
return;
}
setPending(true);
denyRequest(user.id)
.catch(console.error)
.finally(() => setPending(false));
}, [denyRequest, pending, user.id]);
return (
<div style={{ display: 'flex', alignItems: 'center' }}>
<div style={{ flex: 2 }}>{user.name}</div>
<div style={{ flex: 1 }}>
<UIButton
onClick={acceptCallback}
style={{ backgroundColor: lightgrey, padding: '0.5rem' }}
>
Accept
</UIButton>
</div>
<div style={{ flex: 1 }}>
<UIButton
onClick={denyCallback}
style={{ backgroundColor: lightgrey, padding: '0.5rem' }}
>
Decline
</UIButton>
</div>
</div>
);
}
export default function RequestList() {
const { carpool } = useContext(CarpoolContext);
const requestingUsers = useMemo(() => {
return Object.keys(carpool.invitations)
.filter((key) => carpool.invitations[key as unknown as number].isRequest)
.map((key) => carpool.invitations[key as unknown as number].user);
}, [carpool.invitations]);
return (
<>
<h1>Requests</h1>
{requestingUsers.length > 0 ? (
requestingUsers.map((user) => <RequestRow key={user.id} user={user} />)
) : (
<>Nobody's requested to join yet</>
)}
<br />
<br />
</>
);
}

View File

@ -1,5 +1,10 @@
import { useCallback } from 'react'; import { useCallback } from 'react';
import { acceptInvite, acceptRequest, denyInvite, denyRequest } from '../api'; import {
acceptInvite,
acceptCarpoolRequest,
denyInvite,
denyCarpoolRequest,
} from '../api';
import { IInvitation } from '../types'; import { IInvitation } from '../types';
import UIButton from '../UI/UIButton'; import UIButton from '../UI/UIButton';
@ -11,11 +16,11 @@ export default function Notification({
const carpoolId = notification.carpool.id; const carpoolId = notification.carpool.id;
const acceptReq = useCallback(() => { const acceptReq = useCallback(() => {
acceptRequest(carpoolId, notification.user.id); acceptCarpoolRequest(carpoolId, notification.user.id);
}, [carpoolId, notification.user.id]); }, [carpoolId, notification.user.id]);
const rejectReq = useCallback(() => { const rejectReq = useCallback(() => {
denyRequest(carpoolId, notification.user.id); denyCarpoolRequest(carpoolId, notification.user.id);
}, [carpoolId, notification.user.id]); }, [carpoolId, notification.user.id]);
const acceptInv = useCallback(() => { const acceptInv = useCallback(() => {
@ -30,12 +35,12 @@ export default function Notification({
return ( return (
<div className="notification"> <div className="notification">
{notification.user.name} {notification.user.name}{' '}
{notification.isRequest ? ( {notification.isRequest ? (
<span> request you to join </span> <span>requested to join</span>
) : ( ) : (
<span> invited you to join </span> <span>invited you to join</span>
)} )}{' '}
{notification.carpool.name + ' at ' + sentTime.toLocaleString()} {notification.carpool.name + ' at ' + sentTime.toLocaleString()}
{notification.isRequest ? ( {notification.isRequest ? (
<div className="notification-buttons" style={{ display: 'flex' }}> <div className="notification-buttons" style={{ display: 'flex' }}>

View File

@ -5,7 +5,7 @@ const baseStyle: CSSProperties = {
borderRadius: '0.5em', borderRadius: '0.5em',
textTransform: 'uppercase', textTransform: 'uppercase',
fontWeight: 500, fontWeight: 500,
marginTop: '0.5em', margin: '0.5em',
cursor: 'pointer', cursor: 'pointer',
userSelect: 'none', userSelect: 'none',
}; };

View File

@ -148,7 +148,7 @@ export async function getNotifications() {
return await get('/users/@me/received_requests_and_invites'); return await get('/users/@me/received_requests_and_invites');
} }
export async function acceptRequest(carpoolId: number, userId: number) { export async function acceptCarpoolRequest(carpoolId: number, userId: number) {
return await post(`/carpools/${carpoolId}/accept_request`, { userId }); return await post(`/carpools/${carpoolId}/accept_request`, { userId });
} }
@ -156,7 +156,7 @@ export async function acceptInvite(carpoolId: number) {
return await post(`/carpools/${carpoolId}/accept_invite`, {}); return await post(`/carpools/${carpoolId}/accept_invite`, {});
} }
export async function denyRequest(carpoolId: number, userId: number) { export async function denyCarpoolRequest(carpoolId: number, userId: number) {
return await post(`/carpools/${carpoolId}/deny_request`, { userId }); return await post(`/carpools/${carpoolId}/deny_request`, { userId });
} }