diff --git a/.env b/.env
index 57a33ce..c2a4a2c 100644
--- a/.env
+++ b/.env
@@ -1,3 +1,5 @@
-REACT_APP_API_DOMAIN_=http://localhost:5000/
-REACT_APP_API_DOMAIN__=https://wheelshare-altbackend-2efyw.ondigitalocean.app/
+REACT_APP_API_DOMAIN_LOCAL=http://localhost:5000/
+REACT_APP_API_DOMAIN_DOCN=https://wheelshare-altbackend-2efyw.ondigitalocean.app/
+REACT_APP_API_DOMAIN_WWW=https://api.wheelshare.app/
+
REACT_APP_API_DOMAIN=https://api.wheelshare.app/
diff --git a/src/components/Carpool/Carpool.tsx b/src/components/Carpool/Carpool.tsx
index 7f09c02..c471ba4 100644
--- a/src/components/Carpool/Carpool.tsx
+++ b/src/components/Carpool/Carpool.tsx
@@ -1,20 +1,27 @@
-import EventIcon from '@material-ui/icons/Event';
-import LocationOnIcon from '@material-ui/icons/LocationOn';
import MailOutlineIcon from '@material-ui/icons/MailOutline';
import PersonAddIcon from '@material-ui/icons/PersonAdd';
-
-import { useEffect, useState } from 'react';
+import { createContext } from 'react';
+import { useCallback, useEffect, useState } from 'react';
import { useParams } from 'react-router-dom';
-
-import { ICarpool } from '../types';
-
-import UISecondaryBox from '../UI/UISecondaryBox';
-import MemberList from './MemberList';
-import InvitationList from './InvitationList';
-import UIButton from '../UI/UIButton';
+import { cancelCarpoolInvite, getCarpool, sendCarpoolInvite } from '../api';
import { lightgrey } from '../colors';
-import { getCarpool } from '../api';
+import { ICarpool } from '../types';
+import UIButton from '../UI/UIButton';
+import UISecondaryBox from '../UI/UISecondaryBox';
import useToggle from '../useToggle';
+import CarpoolDetails from './CarpoolDetails';
+import InvitationList from './InvitationList';
+import MemberList from './MemberList';
+
+export const CarpoolContext = createContext({
+ carpool: null! as ICarpool,
+ sendInvite: (user: { id: number; name: string }) => {
+ console.error('not implemented: sendInvite');
+ },
+ cancelInvite: (user: { id: number; name: string }) => {
+ console.error('not implemented: cancelInvite');
+ },
+});
export default function Carpool() {
const id = +useParams<{ id: string }>().id;
@@ -26,72 +33,102 @@ export default function Carpool() {
const [invitationsOpen, toggleInvitationsOpen] = useToggle(false);
+ const sendInvite = useCallback(
+ (user: { id: number; name: string }) => {
+ if (carpool) {
+ sendCarpoolInvite(id, user.id)
+ .then(() => {
+ setCarpool(
+ (carpool) =>
+ carpool && {
+ ...carpool,
+ invitations: [
+ ...carpool.invitations,
+ { isRequest: false, user },
+ ],
+ }
+ );
+ })
+ .catch(console.error);
+ } else {
+ console.error(
+ 'Trying to send invite when carpool has not been loaded.'
+ );
+ }
+ },
+ [carpool, id]
+ );
+
+ const cancelInvite = useCallback(
+ (user: { id: number; name: string }) => {
+ cancelCarpoolInvite(id, user.id)
+ .then(() => {
+ setCarpool(
+ (carpool) =>
+ carpool && {
+ ...carpool,
+ invitations: carpool.invitations.filter(
+ (invite) => invite.user.id !== user.id
+ ),
+ }
+ );
+ })
+ .catch(console.error);
+ },
+ [id]
+ );
+
+ if (!carpool) {
+ return <>Loading...>;
+ }
+
return (
-
- {carpool ? (
- <>
- {carpool.name}
- {carpool.event.name}
-
- {/* Requests */}
-
- 1 request
-
- {/* Invitations */}
-
- Invite
-
-
- {invitationsOpen && }
-
+
+
+ {carpool ? (
+ <>
+ {carpool.name}
+ {carpool.event.name}
-
- {carpool.event.formattedAddress}
+ {/* Requests */}
+
+ 1 request
+
+ {/* Invitations */}
+
+ Invite
+
-
-
- DAWN - DUSK
-
-
-
- >
- ) : (
- Loading
- )}
-
+ {invitationsOpen && }
+
+
+ >
+ ) : (
+
Loading
+ )}
+
+
);
}
diff --git a/src/components/Carpool/CarpoolDetails.tsx b/src/components/Carpool/CarpoolDetails.tsx
new file mode 100644
index 0000000..f1fe129
--- /dev/null
+++ b/src/components/Carpool/CarpoolDetails.tsx
@@ -0,0 +1,31 @@
+import EventIcon from '@material-ui/icons/Event';
+import LocationOnIcon from '@material-ui/icons/LocationOn';
+
+import { ICarpool } from '../types';
+
+export default function CarpoolDetails({ carpool }: { carpool: ICarpool }) {
+ return (
+
+
+
+ {carpool.event.formattedAddress}
+
+
+
+ DAWN - DUSK
+
+
+ );
+}
diff --git a/src/components/Carpool/CarpoolProvider.tsx b/src/components/Carpool/CarpoolProvider.tsx
deleted file mode 100644
index 8e11b4f..0000000
--- a/src/components/Carpool/CarpoolProvider.tsx
+++ /dev/null
@@ -1,72 +0,0 @@
-import { createContext, ReactNode, useMemo, useState } from 'react';
-import * as immutable from 'immutable';
-import { useEffect } from 'react';
-import { getCarpool } from '../api';
-
-class Member extends immutable.Record({
- id: 0,
- name: '',
-}) {}
-
-class Invitation extends immutable.Record({
- user: new Member(),
- isRequest: false,
-}) {}
-
-class CarpoolState extends immutable.Record({
- id: 0,
- name: '',
- members: immutable.List(),
- invitations: immutable.List(),
-}) {}
-
-type Subscriber = (state: CarpoolState) => void;
-
-class CarpoolSDK {
- private _state = new CarpoolState();
- get state() {
- return this._state;
- }
- set state(state: CarpoolState) {
- this._state = state;
- }
- private subscribers: Subscriber[] = [];
- subscribe(subscriber: Subscriber) {
- this.subscribers.push(subscriber);
- return () => {
- this.subscribers = this.subscribers.filter((s) => s !== subscriber);
- };
- }
-}
-
-export const CarpoolContext = createContext({
- sdk: new CarpoolSDK(),
- carpool: new CarpoolState(),
-});
-
-export default function CarpoolProvider({
- id,
- children,
-}: {
- id: number;
- children: ReactNode;
-}) {
- const [carpool, setCarpool] = useState(new CarpoolState());
- const sdk = useMemo(() => new CarpoolSDK(), []);
-
- useEffect(() => {
- getCarpool(id).then((carpool) => {});
- }, [id]);
-
- useEffect(() => {
- const remove = sdk.subscribe(setCarpool);
-
- return () => remove();
- }, [sdk]);
-
- return (
-
- {children}
-
- );
-}
diff --git a/src/components/Carpool/InvitationList.tsx b/src/components/Carpool/InvitationList.tsx
index 34042d1..b5d3a7e 100644
--- a/src/components/Carpool/InvitationList.tsx
+++ b/src/components/Carpool/InvitationList.tsx
@@ -1,19 +1,20 @@
+import CancelIcon from '@material-ui/icons/Cancel';
+import PersonAddIcon from '@material-ui/icons/PersonAdd';
import { useMemo } from 'react';
-import { useEffect, useState } from 'react';
+import { useContext, useEffect, useState } from 'react';
import { getEventSignups } from '../api';
-import { ICarpool, IEventSignup } from '../types';
+import { useMe } from '../hooks';
+import { IEventSignup } from '../types';
+import { CarpoolContext } from './Carpool';
function InvitationRow({
- carpoolId,
- userId,
- userName,
+ user,
isInvited,
}: {
- carpoolId: number;
- userId: number;
- userName: string;
+ user: { id: number; name: string };
isInvited: boolean;
}) {
+ const { sendInvite, cancelInvite } = useContext(CarpoolContext);
return (
-
{userName}
+
{user.name}
+ {isInvited ? (
+
cancelInvite(user)}
+ style={{ cursor: 'pointer' }}
+ />
+ ) : (
+ sendInvite(user)}
+ style={{ cursor: 'pointer' }}
+ />
+ )}
);
}
-export default function InvitationList({ carpool }: { carpool: ICarpool }) {
+export default function InvitationList() {
+ const { carpool } = useContext(CarpoolContext);
+ const me = useMe()!;
+
const eventId = carpool.event.id;
const [availableSignups, setAvailableSignups] =
useState(null);
useEffect(() => {
- getEventSignups(eventId).then(setAvailableSignups);
- }, [eventId]);
+ getEventSignups(eventId).then((signups) =>
+ setAvailableSignups(signups.filter((signup) => signup.user.id !== me.id))
+ );
+ }, [eventId, me.id]);
- const existingSignups = useMemo(
+ const invitedUserIDs = useMemo(
() =>
new Set(
carpool.invitations
.filter((invitation) => !invitation.isRequest)
.map((invitation) => invitation.user.id)
),
- [carpool]
+ [carpool.invitations]
);
const availableSignupsAlreadyInvited = useMemo(
() =>
availableSignups
? availableSignups.filter((signup) =>
- existingSignups.has(signup.userId)
+ invitedUserIDs.has(signup.user.id)
)
: null,
- [availableSignups, existingSignups]
+ [availableSignups, invitedUserIDs]
);
const availableSignupsNotInvited = useMemo(
() =>
availableSignups
? availableSignups.filter(
- (signup) => !existingSignups.has(signup.userId)
+ (signup) => !invitedUserIDs.has(signup.user.id)
)
: null,
- [availableSignups, existingSignups]
+ [availableSignups, invitedUserIDs]
);
return (
@@ -80,24 +97,20 @@ export default function InvitationList({ carpool }: { carpool: ICarpool }) {
>
Invite Somebody
{availableSignups === null && 'Loading'}
+ {availableSignupsAlreadyInvited?.map((signup) => (
+
+ ))}
{availableSignupsNotInvited?.map((signup) => (
))}
- {availableSignupsAlreadyInvited?.map((signup) => (
-
- ))}
);
}
diff --git a/src/components/api.ts b/src/components/api.ts
index 0878126..0b1b643 100644
--- a/src/components/api.ts
+++ b/src/components/api.ts
@@ -15,13 +15,14 @@ async function post(path: string, data: any) {
return await res.json();
}
-async function delete$(path: string) {
+async function delete$(path: string, body?: any) {
const res = await fetch(base + path, {
method: 'delete',
headers: {
Authorization: 'Bearer ' + localStorage.getItem('session_token'),
'Content-Type': 'application/json',
},
+ body: body ? JSON.stringify(body) : undefined,
});
return await res.json();
}
@@ -176,3 +177,11 @@ export async function createCarpool({
}) {
return await post('/carpools/', { eventId, name });
}
+
+export async function sendCarpoolInvite(carpoolId: number, userId: number) {
+ return await post('/carpools/' + carpoolId + '/invite', { userId });
+}
+
+export async function cancelCarpoolInvite(carpoolId: number, userId: number) {
+ return await delete$('/carpools/' + carpoolId + '/invite', { userId });
+}
diff --git a/src/components/types.ts b/src/components/types.ts
index fc80a5f..0fa7130 100644
--- a/src/components/types.ts
+++ b/src/components/types.ts
@@ -33,7 +33,13 @@ export type ICarpool = {
id: number;
name: string;
}[];
- invitations: IInvitation[];
+ invitations: {
+ user: {
+ id: number;
+ name: string;
+ };
+ isRequest: boolean;
+ }[];
};
/**
@@ -81,7 +87,7 @@ export type IEvent = {
export type IEventSignup = {
eventId: number;
- userId: number;
+ // userId: number;
user: {
id: number;
name: string;