diff --git a/src/components/App.tsx b/src/components/App.tsx
index f098788..40b03b2 100644
--- a/src/components/App.tsx
+++ b/src/components/App.tsx
@@ -1,5 +1,6 @@
import { CSSProperties, lazy, Suspense } from 'react';
import { BrowserRouter, Route, Switch } from 'react-router-dom';
+import NotificationsProvider from '../state/Notifications/NotificationsProvider';
import { useMe } from './hooks';
import WheelShare from './WheelShare';
import WheelShareLoggedOut from './WheelShareLoggedOut';
@@ -22,27 +23,29 @@ const style: CSSProperties = {
export default function App() {
const user = useMe();
return (
-
-
-
-
-
-
-
+
+
+
+
+
-
-
-
-
-
+
+
+
+
+
+
+
+
+
-
+
);
}
diff --git a/src/components/Carpool/MemberList.tsx b/src/components/Carpool/MemberList.tsx
index aac57ad..31a2150 100644
--- a/src/components/Carpool/MemberList.tsx
+++ b/src/components/Carpool/MemberList.tsx
@@ -1,6 +1,11 @@
import AccountCircleIcon from '@material-ui/icons/AccountCircle';
+import { useCallback } from 'react';
+import { useMemo } from 'react';
import { useContext } from 'react';
+import { useInvitationState } from '../../state/Notifications/NotificationsHooks';
+import { NotificationsContext } from '../../state/Notifications/NotificationsProvider';
import { lightgrey } from '../colors';
+import { useMe } from '../hooks';
import UIButton from '../UI/UIButton';
import { CarpoolContext } from './Carpool';
@@ -21,9 +26,26 @@ export default function MemberList({
name: string;
}[];
}) {
- const { leave } = useContext(CarpoolContext);
+ const { leave, carpool } = useContext(CarpoolContext);
const membersToShow = members.slice(0, 2);
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 (
- Leave
-
+
+ {isMember ? (
+
+ Leave
+
+ ) : invitationState === 'requested' ? (
+ Cancel request to join
+ ) : invitationState === 'none' ? (
+ Request to join
+ ) : (
+
+ You've been invited, we need to make it so you can accept the invite
+
+ )}
);
}
diff --git a/src/components/api.ts b/src/components/api.ts
index 0b26b31..7e01c8c 100644
--- a/src/components/api.ts
+++ b/src/components/api.ts
@@ -193,3 +193,11 @@ export async function cancelCarpoolInvite(carpoolId: number, userId: number) {
export async function leaveCarpool(carpoolId: number) {
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');
+}
diff --git a/src/state/Notifications/NotificationsHooks.tsx b/src/state/Notifications/NotificationsHooks.tsx
new file mode 100644
index 0000000..5959238
--- /dev/null
+++ b/src/state/Notifications/NotificationsHooks.tsx
@@ -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';
+}
diff --git a/src/state/Notifications/NotificationsProvider.tsx b/src/state/Notifications/NotificationsProvider.tsx
new file mode 100644
index 0000000..be3d119
--- /dev/null
+++ b/src/state/Notifications/NotificationsProvider.tsx
@@ -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
(),
+ requestedCarpoolIds: immutable.Set(),
+
+ 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()
+ );
+
+ const [requestedCarpoolIds, setRequestedCarpoolIds] = useState(
+ immutable.Set()
+ );
+
+ 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 (
+
+ {children}
+
+ );
+}