diff --git a/src/components/Carpool/Carpool.tsx b/src/components/Carpool/Carpool.tsx index b4d614f..ad0690b 100644 --- a/src/components/Carpool/Carpool.tsx +++ b/src/components/Carpool/Carpool.tsx @@ -1,6 +1,8 @@ import { createContext, useCallback, useEffect } from 'react'; import { + acceptCarpoolRequest, cancelCarpoolInvite, + denyCarpoolRequest, getCarpool, leaveCarpool, sendCarpoolInvite, @@ -25,13 +27,19 @@ type CarpoolState = { export const CarpoolContext = createContext({ carpool: {} as CarpoolState, - sendInvite: (user: { id: number; name: string }) => { + async sendInvite(user: { id: number; name: string }) { console.error('not implemented: sendInvite'); }, - cancelInvite: (user: { id: number; name: string }) => { + async cancelInvite(user: { id: number; name: string }) { 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'); }, }); @@ -55,46 +63,83 @@ export default function Carpool({ id }: { id: number }) { }); }, [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( - (user: { id: number; name: string }) => { - if (carpool) { - sendCarpoolInvite(id, user.id) - .then(() => { - carpool.invitations[user.id] = { isRequest: false, user }; - }) - .catch(console.error); - } else { + async (user: { id: number; name: string }) => { + if (!carpool) { console.error( '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] ); const cancelInvite = useCallback( - (user: { id: number; name: string }) => { + async (user: { id: number; name: string }) => { if (!carpool) { - return null; + console.error( + 'Trying to cancel invite when carpool has not been loaded.' + ); + return; } - cancelCarpoolInvite(id, user.id) - .then(() => { - delete carpool.invitations[user.id]; - }) - .catch(console.error); + try { + await cancelCarpoolInvite(id, user.id); + } catch (e) { + console.error(e); + } + delete carpool.invitations[user.id]; }, [carpool, id] ); const eventId = carpool?.event.id; - const leave = useCallback(() => { + const leave = useCallback(async () => { if (eventId) { - leaveCarpool(id) - .then(() => { - window.location.href = '/events/' + eventId; - }) - .catch(console.error); + try { + await leaveCarpool(id); + window.location.href = '/events/' + eventId; + } catch (e) { + console.error(e); + } } }, [eventId, id]); @@ -108,6 +153,8 @@ export default function Carpool({ id }: { id: number }) { carpool, sendInvite, cancelInvite, + acceptRequest, + denyRequest, leave, }} > diff --git a/src/components/Carpool/CarpoolTopButtonsMembersOnly.tsx b/src/components/Carpool/CarpoolTopButtonsMembersOnly.tsx index 12cf484..d2f0574 100644 --- a/src/components/Carpool/CarpoolTopButtonsMembersOnly.tsx +++ b/src/components/Carpool/CarpoolTopButtonsMembersOnly.tsx @@ -5,16 +5,19 @@ import { useContext } from 'react'; import useToggle from '../useToggle'; import { CarpoolContext } from './Carpool'; import InvitationList from './InvitationList'; +import RequestList from './RequestList'; const spanStyle = { padding: '0.5rem', display: 'flex', alignItems: 'center', cursor: 'pointer', -}; + userSelect: 'none', +} as const; export default function CarpoolTopButtonsMembersOnly() { const [invitationsOpen, toggleInvitationsOpen] = useToggle(false); + const [requestsOpen, toggleRequestsOpen] = useToggle(false); const { leave } = useContext(CarpoolContext); return ( @@ -26,8 +29,8 @@ export default function CarpoolTopButtonsMembersOnly() { margin: '0.5rem 0', }} > - - 1 request + + View requests Invite @@ -38,6 +41,7 @@ export default function CarpoolTopButtonsMembersOnly() { {invitationsOpen && } + {requestsOpen && } ); } diff --git a/src/components/Carpool/RequestList.tsx b/src/components/Carpool/RequestList.tsx new file mode 100644 index 0000000..b6f2cca --- /dev/null +++ b/src/components/Carpool/RequestList.tsx @@ -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 ( +
+
{user.name}
+ +
+ + Accept + +
+ +
+ + Decline + +
+
+ ); +} + +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 ( + <> +

Requests

+ {requestingUsers.length > 0 ? ( + requestingUsers.map((user) => ) + ) : ( + <>Nobody's requested to join yet + )} +
+
+ + ); +} diff --git a/src/components/Notifications/Notification.tsx b/src/components/Notifications/Notification.tsx index 58a927b..76500f8 100644 --- a/src/components/Notifications/Notification.tsx +++ b/src/components/Notifications/Notification.tsx @@ -1,5 +1,10 @@ import { useCallback } from 'react'; -import { acceptInvite, acceptRequest, denyInvite, denyRequest } from '../api'; +import { + acceptInvite, + acceptCarpoolRequest, + denyInvite, + denyCarpoolRequest, +} from '../api'; import { IInvitation } from '../types'; import UIButton from '../UI/UIButton'; @@ -11,11 +16,11 @@ export default function Notification({ const carpoolId = notification.carpool.id; const acceptReq = useCallback(() => { - acceptRequest(carpoolId, notification.user.id); + acceptCarpoolRequest(carpoolId, notification.user.id); }, [carpoolId, notification.user.id]); const rejectReq = useCallback(() => { - denyRequest(carpoolId, notification.user.id); + denyCarpoolRequest(carpoolId, notification.user.id); }, [carpoolId, notification.user.id]); const acceptInv = useCallback(() => { @@ -30,12 +35,12 @@ export default function Notification({ return (
- {notification.user.name} + {notification.user.name}{' '} {notification.isRequest ? ( - request you to join + requested to join ) : ( - invited you to join - )} + invited you to join + )}{' '} {notification.carpool.name + ' at ' + sentTime.toLocaleString()} {notification.isRequest ? (
diff --git a/src/components/UI/UIButton.tsx b/src/components/UI/UIButton.tsx index 2d145cd..88f76dc 100644 --- a/src/components/UI/UIButton.tsx +++ b/src/components/UI/UIButton.tsx @@ -5,7 +5,7 @@ const baseStyle: CSSProperties = { borderRadius: '0.5em', textTransform: 'uppercase', fontWeight: 500, - marginTop: '0.5em', + margin: '0.5em', cursor: 'pointer', userSelect: 'none', }; diff --git a/src/components/api.ts b/src/components/api.ts index f46f6ae..5133dee 100644 --- a/src/components/api.ts +++ b/src/components/api.ts @@ -148,7 +148,7 @@ export async function getNotifications() { 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 }); } @@ -156,7 +156,7 @@ export async function acceptInvite(carpoolId: number) { 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 }); }