add requesting/cancelling request (notifications provider)

This commit is contained in:
Michael Fatemi 2021-07-13 18:50:37 -04:00
parent 9ecf21dab3
commit 41a8a57854
5 changed files with 142 additions and 23 deletions

View File

@ -1,5 +1,6 @@
import { CSSProperties, lazy, Suspense } from 'react'; import { CSSProperties, lazy, Suspense } from 'react';
import { BrowserRouter, Route, Switch } from 'react-router-dom'; import { BrowserRouter, Route, Switch } from 'react-router-dom';
import NotificationsProvider from '../state/Notifications/NotificationsProvider';
import { useMe } from './hooks'; import { useMe } from './hooks';
import WheelShare from './WheelShare'; import WheelShare from './WheelShare';
import WheelShareLoggedOut from './WheelShareLoggedOut'; import WheelShareLoggedOut from './WheelShareLoggedOut';
@ -22,27 +23,29 @@ const style: CSSProperties = {
export default function App() { export default function App() {
const user = useMe(); const user = useMe();
return ( return (
<div style={{ padding: '1rem', maxWidth: '100vw' }}> <NotificationsProvider>
<div style={style}> <div style={{ padding: '1rem', maxWidth: '100vw' }}>
<BrowserRouter> <div style={style}>
<Switch> <BrowserRouter>
<Route <Switch>
path="/"
exact
component={user ? WheelShare : WheelShareLoggedOut}
/>
<Suspense fallback={null}>
<Route path="/groups/:id" component={Group} />
<Route <Route
component={Authenticator} path="/"
path="/auth/:provider/callback" exact
component={user ? WheelShare : WheelShareLoggedOut}
/> />
<Route path="/carpools/:id" component={CarpoolPage} /> <Suspense fallback={null}>
<Route path="/events/:id" component={EventPage} /> <Route path="/groups/:id" component={Group} />
</Suspense> <Route
</Switch> component={Authenticator}
</BrowserRouter> path="/auth/:provider/callback"
/>
<Route path="/carpools/:id" component={CarpoolPage} />
<Route path="/events/:id" component={EventPage} />
</Suspense>
</Switch>
</BrowserRouter>
</div>
</div> </div>
</div> </NotificationsProvider>
); );
} }

View File

@ -1,6 +1,11 @@
import AccountCircleIcon from '@material-ui/icons/AccountCircle'; import AccountCircleIcon from '@material-ui/icons/AccountCircle';
import { useCallback } from 'react';
import { useMemo } from 'react';
import { useContext } from 'react'; import { useContext } from 'react';
import { useInvitationState } from '../../state/Notifications/NotificationsHooks';
import { NotificationsContext } from '../../state/Notifications/NotificationsProvider';
import { lightgrey } from '../colors'; import { lightgrey } from '../colors';
import { useMe } from '../hooks';
import UIButton from '../UI/UIButton'; import UIButton from '../UI/UIButton';
import { CarpoolContext } from './Carpool'; import { CarpoolContext } from './Carpool';
@ -21,9 +26,26 @@ export default function MemberList({
name: string; name: string;
}[]; }[];
}) { }) {
const { leave } = useContext(CarpoolContext); const { leave, carpool } = useContext(CarpoolContext);
const membersToShow = members.slice(0, 2); const membersToShow = members.slice(0, 2);
const hiddenMemberCount = members.length - membersToShow.length; const hiddenMemberCount = members.length - membersToShow.length;
const me = useMe()!;
const isMember = useMemo(() => {
return members.some(({ id }) => id === me.id);
}, [me.id, members]);
const { sendCarpoolRequest, cancelCarpoolRequest } =
useContext(NotificationsContext);
const invitationState = useInvitationState(carpool.id);
const sendRequest = useCallback(() => {
sendCarpoolRequest(carpool.id);
}, [carpool.id, sendCarpoolRequest]);
const cancelRequest = useCallback(() => {
cancelCarpoolRequest(carpool.id);
}, [carpool.id, cancelCarpoolRequest]);
return ( return (
<div <div
@ -48,9 +70,20 @@ export default function MemberList({
) : ( ) : (
'This carpool has no members.' 'This carpool has no members.'
)} )}
<UIButton onClick={leave} style={{ backgroundColor: lightgrey }}>
Leave {isMember ? (
</UIButton> <UIButton onClick={leave} style={{ backgroundColor: lightgrey }}>
Leave
</UIButton>
) : invitationState === 'requested' ? (
<UIButton onClick={cancelRequest}>Cancel request to join</UIButton>
) : invitationState === 'none' ? (
<UIButton onClick={sendRequest}>Request to join</UIButton>
) : (
<span>
You've been invited, we need to make it so you can accept the invite
</span>
)}
</div> </div>
); );
} }

View File

@ -193,3 +193,11 @@ export async function cancelCarpoolInvite(carpoolId: number, userId: number) {
export async function leaveCarpool(carpoolId: number) { export async function leaveCarpool(carpoolId: number) {
return await post(`/carpools/${carpoolId}/leave`, {}); return await post(`/carpools/${carpoolId}/leave`, {});
} }
export async function sendCarpoolRequest(carpoolId: number) {
return await post('/carpools/' + carpoolId + '/request', {});
}
export async function cancelCarpoolRequest(carpoolId: number) {
return await delete$('/carpools/' + carpoolId + '/request');
}

View File

@ -0,0 +1,21 @@
import { useMemo } from 'react';
import { useContext } from 'react';
import { NotificationsContext } from './NotificationsProvider';
export function useInvitationState(
carpoolId: number
): 'invited' | 'requested' | 'none' {
const notifications = useContext(NotificationsContext);
const invited = useMemo(
() => notifications.invitedCarpoolIds.has(carpoolId),
[carpoolId, notifications.invitedCarpoolIds]
);
const requested = useMemo(
() => notifications.requestedCarpoolIds.has(carpoolId),
[carpoolId, notifications.requestedCarpoolIds]
);
return invited ? 'invited' : requested ? 'requested' : 'none';
}

View File

@ -0,0 +1,54 @@
import { createContext, ReactNode, useCallback, useState } from 'react';
import * as immutable from 'immutable';
import * as api from '../../components/api';
export const NotificationsContext = createContext({
invitedCarpoolIds: immutable.Set<number>(),
requestedCarpoolIds: immutable.Set<number>(),
sendCarpoolRequest: (carpoolId: number) =>
console.error('not implemented: sendCarpoolRequest'),
cancelCarpoolRequest: (carpoolId: number) =>
console.error('not implemented: cancelCarpoolRequest'),
});
export default function NotificationsProvider({
children,
}: {
children: ReactNode;
}) {
// eslint-disable-next-line
const [invitedCarpoolIds, _setInvitedCarpoolIds] = useState(
immutable.Set<number>()
);
const [requestedCarpoolIds, setRequestedCarpoolIds] = useState(
immutable.Set<number>()
);
const sendCarpoolRequest = useCallback((carpoolId: number) => {
api
.sendCarpoolRequest(carpoolId)
.then(() => setRequestedCarpoolIds((ids) => ids.add(carpoolId)));
}, []);
const cancelCarpoolRequest = useCallback((carpoolId: number) => {
api
.cancelCarpoolRequest(carpoolId)
.then(() => setRequestedCarpoolIds((ids) => ids.delete(carpoolId)));
}, []);
return (
<NotificationsContext.Provider
value={{
invitedCarpoolIds,
requestedCarpoolIds,
sendCarpoolRequest,
cancelCarpoolRequest,
}}
>
{children}
</NotificationsContext.Provider>
);
}