mirror of
https://github.com/myfatemi04/wheelshare-frontend.git
synced 2025-04-21 11:20:17 -04:00
Make it possible to request a carpool directly from the event page
This commit is contained in:
parent
bafc26fb06
commit
66ec0019c1
|
@ -1,10 +1,11 @@
|
||||||
import { useCallback, useEffect, useRef, useState } from 'react';
|
import { useCallback, useEffect, useRef, useState } from 'react';
|
||||||
|
import { green, lightgrey } from '../../lib/colors';
|
||||||
import {
|
import {
|
||||||
addOrUpdateEventSignup,
|
addOrUpdateEventSignup,
|
||||||
|
getEvent,
|
||||||
getEventSignups,
|
getEventSignups,
|
||||||
removeEventSignup,
|
removeEventSignup,
|
||||||
} from '../api';
|
} from '../api';
|
||||||
import { green, lightgrey } from '../../lib/colors';
|
|
||||||
import { useMe } from '../hooks';
|
import { useMe } from '../hooks';
|
||||||
import { IEvent, IEventSignup } from '../types';
|
import { IEvent, IEventSignup } from '../types';
|
||||||
import UIButton from '../UI/UIButton';
|
import UIButton from '../UI/UIButton';
|
||||||
|
@ -14,6 +15,7 @@ import UISecondaryBox from '../UI/UISecondaryBox';
|
||||||
import UISecondaryHeader from '../UI/UISecondaryHeader';
|
import UISecondaryHeader from '../UI/UISecondaryHeader';
|
||||||
import useThrottle from '../useThrottle';
|
import useThrottle from '../useThrottle';
|
||||||
import EventCarpools from './EventCarpools';
|
import EventCarpools from './EventCarpools';
|
||||||
|
import EventContext from './EventContext';
|
||||||
import EventDetails from './EventDetails';
|
import EventDetails from './EventDetails';
|
||||||
import EventSignups from './EventSignups';
|
import EventSignups from './EventSignups';
|
||||||
|
|
||||||
|
@ -21,8 +23,14 @@ function GroupName({ group }: { group: IEvent['group'] }) {
|
||||||
return <UILink href={`/groups/${group.id}`}>{group.name}</UILink>;
|
return <UILink href={`/groups/${group.id}`}>{group.name}</UILink>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function Event({ event }: { event: IEvent }) {
|
export default function Event({
|
||||||
const { name, group, formattedAddress, startTime, endTime } = event;
|
id,
|
||||||
|
initial,
|
||||||
|
}: {
|
||||||
|
id: number;
|
||||||
|
initial?: IEvent;
|
||||||
|
}) {
|
||||||
|
const [event, setEvent] = useState<IEvent | null>(initial || null);
|
||||||
const [placeId, setPlaceId] = useState<string | null>(null);
|
const [placeId, setPlaceId] = useState<string | null>(null);
|
||||||
const [interested, setInterested] = useState(false);
|
const [interested, setInterested] = useState(false);
|
||||||
const [updating, setUpdating] = useState(false);
|
const [updating, setUpdating] = useState(false);
|
||||||
|
@ -36,6 +44,12 @@ export default function Event({ event }: { event: IEvent }) {
|
||||||
});
|
});
|
||||||
const me = useMe();
|
const me = useMe();
|
||||||
|
|
||||||
|
const refresh = useCallback(() => {
|
||||||
|
getEvent(id).then(setEvent);
|
||||||
|
}, [id]);
|
||||||
|
|
||||||
|
useEffect(refresh, [refresh]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (signups === null) {
|
if (signups === null) {
|
||||||
return;
|
return;
|
||||||
|
@ -43,7 +57,7 @@ export default function Event({ event }: { event: IEvent }) {
|
||||||
|
|
||||||
const removeSignup = () => {
|
const removeSignup = () => {
|
||||||
if (prev.interested) {
|
if (prev.interested) {
|
||||||
removeEventSignup(event.id)
|
removeEventSignup(id)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
prev.interested = false;
|
prev.interested = false;
|
||||||
})
|
})
|
||||||
|
@ -56,13 +70,13 @@ export default function Event({ event }: { event: IEvent }) {
|
||||||
console.log('Adding or updating signup.', prev, {
|
console.log('Adding or updating signup.', prev, {
|
||||||
interested,
|
interested,
|
||||||
placeId,
|
placeId,
|
||||||
eventId: event.id,
|
eventId: id,
|
||||||
signups,
|
signups,
|
||||||
});
|
});
|
||||||
addOrUpdateEventSignup(event.id, placeId)
|
addOrUpdateEventSignup(id, placeId)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
prev.placeId = placeId;
|
prev.placeId = placeId;
|
||||||
prev.eventId = event.id;
|
prev.eventId = id;
|
||||||
prev.interested = true;
|
prev.interested = true;
|
||||||
})
|
})
|
||||||
.finally(() => setUpdating(false));
|
.finally(() => setUpdating(false));
|
||||||
|
@ -76,16 +90,16 @@ export default function Event({ event }: { event: IEvent }) {
|
||||||
} else {
|
} else {
|
||||||
addOrUpdateSignup();
|
addOrUpdateSignup();
|
||||||
}
|
}
|
||||||
}, [event.id, interested, placeId, signups, updating]);
|
}, [id, interested, placeId, signups, updating]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
getEventSignups(event.id)
|
getEventSignups(id)
|
||||||
.then((signups) => {
|
.then((signups) => {
|
||||||
for (let signup of signups) {
|
for (let signup of signups) {
|
||||||
if (signup.user.id === me?.id) {
|
if (signup.user.id === me?.id) {
|
||||||
setInterested(true);
|
setInterested(true);
|
||||||
setPlaceId(signup.placeId);
|
setPlaceId(signup.placeId);
|
||||||
existingSignup.current.eventId = event.id;
|
existingSignup.current.eventId = id;
|
||||||
existingSignup.current.placeId = signup.placeId;
|
existingSignup.current.placeId = signup.placeId;
|
||||||
existingSignup.current.interested = true;
|
existingSignup.current.interested = true;
|
||||||
}
|
}
|
||||||
|
@ -93,42 +107,54 @@ export default function Event({ event }: { event: IEvent }) {
|
||||||
setSignups(signups);
|
setSignups(signups);
|
||||||
})
|
})
|
||||||
.catch(console.error);
|
.catch(console.error);
|
||||||
}, [event.id, me?.id]);
|
}, [id, me?.id]);
|
||||||
|
|
||||||
|
if (!event) {
|
||||||
|
return <UISecondaryBox>Loading...</UISecondaryBox>;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { name, group, formattedAddress, startTime, endTime } = event;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<UISecondaryBox>
|
<EventContext.Provider value={{ event, refresh, default: false }}>
|
||||||
<div style={{ textAlign: 'center' }}>
|
<UISecondaryBox>
|
||||||
<UISecondaryHeader>{name}</UISecondaryHeader>
|
<div style={{ textAlign: 'center' }}>
|
||||||
<GroupName group={group} />
|
<UISecondaryHeader>{name}</UISecondaryHeader>
|
||||||
</div>
|
<GroupName group={group} />
|
||||||
<EventDetails {...{ startTime, endTime, formattedAddress }} />
|
</div>
|
||||||
<UIButton
|
<EventDetails {...{ startTime, endTime, formattedAddress }} />
|
||||||
onClick={toggleInterestedThrottled}
|
<UIButton
|
||||||
style={{
|
onClick={toggleInterestedThrottled}
|
||||||
backgroundColor: interested ? green : lightgrey,
|
style={{
|
||||||
color: interested ? 'white' : 'black',
|
backgroundColor: interested ? green : lightgrey,
|
||||||
transition: 'color 0.2s, background-color 0.2s',
|
color: interested ? 'white' : 'black',
|
||||||
}}
|
transition: 'color 0.2s, background-color 0.2s',
|
||||||
>
|
}}
|
||||||
{interested ? 'Interested' : 'Not interested'}
|
>
|
||||||
</UIButton>
|
{interested ? 'Interested' : 'Not interested'}
|
||||||
{interested && (
|
</UIButton>
|
||||||
<>
|
{interested && (
|
||||||
<UIPlacesAutocomplete
|
<>
|
||||||
placeholder="Pickup and dropoff location"
|
<UIPlacesAutocomplete
|
||||||
onSelected={(_address, placeID) => {
|
placeholder="Pickup and dropoff location"
|
||||||
setPlaceId(placeID);
|
onSelected={(_address, placeID) => {
|
||||||
}}
|
setPlaceId(placeID);
|
||||||
style={placeId != null ? { border: '2px solid ' + green } : {}}
|
}}
|
||||||
placeId={placeId}
|
style={placeId != null ? { border: '2px solid ' + green } : {}}
|
||||||
/>
|
placeId={placeId}
|
||||||
<br />
|
/>
|
||||||
<EventCarpools event={event} />
|
<br />
|
||||||
{signups !== null && (
|
<EventCarpools />
|
||||||
<EventSignups event={event} myPlaceId={placeId} signups={signups} />
|
{signups !== null && (
|
||||||
)}
|
<EventSignups
|
||||||
</>
|
event={event}
|
||||||
)}
|
myPlaceId={placeId}
|
||||||
</UISecondaryBox>
|
signups={signups}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</UISecondaryBox>
|
||||||
|
</EventContext.Provider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,15 +1,41 @@
|
||||||
// import CallMergeIcon from '@material-ui/icons/CallMerge';
|
import CancelIcon from '@material-ui/icons/Cancel';
|
||||||
|
import CheckIcon from '@material-ui/icons/Check';
|
||||||
import EmojiPeopleIcon from '@material-ui/icons/EmojiPeople';
|
import EmojiPeopleIcon from '@material-ui/icons/EmojiPeople';
|
||||||
import { useCallback, useMemo, useState } from 'react';
|
import { useCallback, useContext, useMemo, useState } from 'react';
|
||||||
import { createCarpool } from '../api';
|
import { createCarpool } from '../api';
|
||||||
import { lightgrey } from '../../lib/colors';
|
import { lightgrey } from '../../lib/colors';
|
||||||
import { useMe } from '../hooks';
|
import { useMe } from '../hooks';
|
||||||
import { IEvent } from '../types';
|
import { IEvent } from '../types';
|
||||||
import UIButton from '../UI/UIButton';
|
import UIButton from '../UI/UIButton';
|
||||||
import UILink from '../UI/UILink';
|
import UILink from '../UI/UILink';
|
||||||
|
import EventContext from './EventContext';
|
||||||
|
import {
|
||||||
|
useCancelCarpoolRequest,
|
||||||
|
useInvitationState,
|
||||||
|
useSendCarpoolRequest,
|
||||||
|
} from '../../state/Notifications/NotificationsHooks';
|
||||||
|
|
||||||
function CarpoolRow({ carpool }: { carpool: IEvent['carpools'][0] }) {
|
function CarpoolRow({
|
||||||
|
carpool,
|
||||||
|
inCarpoolAlready,
|
||||||
|
}: {
|
||||||
|
carpool: IEvent['carpools'][0];
|
||||||
|
inCarpoolAlready: boolean;
|
||||||
|
}) {
|
||||||
const PADDING = '1rem';
|
const PADDING = '1rem';
|
||||||
|
const inviteState = useInvitationState(carpool.id);
|
||||||
|
|
||||||
|
const cancelCarpoolRequest = useCancelCarpoolRequest();
|
||||||
|
const sendCarpoolRequest = useSendCarpoolRequest();
|
||||||
|
|
||||||
|
const sendButton = useCallback(() => {
|
||||||
|
sendCarpoolRequest(carpool.id);
|
||||||
|
}, [sendCarpoolRequest, carpool.id]);
|
||||||
|
|
||||||
|
const cancelButton = useCallback(() => {
|
||||||
|
cancelCarpoolRequest(carpool.id);
|
||||||
|
}, [cancelCarpoolRequest, carpool.id]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
|
@ -40,14 +66,32 @@ function CarpoolRow({ carpool }: { carpool: IEvent['carpools'][0] }) {
|
||||||
<b>{carpool.extraDistance} miles</b>
|
<b>{carpool.extraDistance} miles</b>
|
||||||
</div> */}
|
</div> */}
|
||||||
{/* </div> */}
|
{/* </div> */}
|
||||||
<EmojiPeopleIcon style={{ fontSize: '2em' }} />
|
{!inCarpoolAlready && (
|
||||||
|
<>
|
||||||
|
{inviteState === 'none' ? (
|
||||||
|
<EmojiPeopleIcon
|
||||||
|
style={{ fontSize: '2em', cursor: 'pointer' }}
|
||||||
|
onClick={sendButton}
|
||||||
|
/>
|
||||||
|
) : inviteState === 'requested' ? (
|
||||||
|
<CancelIcon
|
||||||
|
style={{ fontSize: '2em', cursor: 'pointer' }}
|
||||||
|
onClick={cancelButton}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
// inviteState === 'invited
|
||||||
|
<CheckIcon style={{ fontSize: '2em', cursor: 'pointer' }} />
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
type CreationStatus = null | 'pending' | 'completed' | 'errored';
|
type CreationStatus = null | 'pending' | 'completed' | 'errored';
|
||||||
|
|
||||||
export default function Carpools({ event }: { event: IEvent }) {
|
export default function Carpools() {
|
||||||
|
const { event } = useContext(EventContext);
|
||||||
const [creationStatus, setCreationStatus] = useState<CreationStatus>(null);
|
const [creationStatus, setCreationStatus] = useState<CreationStatus>(null);
|
||||||
const [createdCarpoolId, setCreatedCarpoolId] = useState<null | number>(null);
|
const [createdCarpoolId, setCreatedCarpoolId] = useState<null | number>(null);
|
||||||
|
|
||||||
|
@ -59,7 +103,8 @@ export default function Carpools({ event }: { event: IEvent }) {
|
||||||
),
|
),
|
||||||
[event.carpools, me.id]
|
[event.carpools, me.id]
|
||||||
);
|
);
|
||||||
const alreadyInCarpool = myCarpool !== undefined;
|
const alreadyInCarpool =
|
||||||
|
myCarpool !== undefined || creationStatus === 'completed';
|
||||||
|
|
||||||
const createEmptyCarpool = useCallback(() => {
|
const createEmptyCarpool = useCallback(() => {
|
||||||
setCreationStatus('pending');
|
setCreationStatus('pending');
|
||||||
|
@ -107,7 +152,11 @@ export default function Carpools({ event }: { event: IEvent }) {
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
{event.carpools.map((carpool) => (
|
{event.carpools.map((carpool) => (
|
||||||
<CarpoolRow carpool={carpool} key={carpool.id} />
|
<CarpoolRow
|
||||||
|
carpool={carpool}
|
||||||
|
key={carpool.id}
|
||||||
|
inCarpoolAlready={alreadyInCarpool}
|
||||||
|
/>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
12
src/components/Event/EventContext.tsx
Normal file
12
src/components/Event/EventContext.tsx
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
import { createContext } from 'react';
|
||||||
|
import { IEvent } from '../types';
|
||||||
|
|
||||||
|
const EventContext = createContext({
|
||||||
|
refresh: () => {
|
||||||
|
console.error('not implemented: refresh');
|
||||||
|
},
|
||||||
|
event: null! as IEvent,
|
||||||
|
default: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
export default EventContext;
|
|
@ -16,7 +16,7 @@ export default function EventPage() {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Header />
|
<Header />
|
||||||
{event ? <Event event={event} /> : <span>Loading...</span>}
|
{event ? <Event id={id} /> : <span>Loading...</span>}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,7 @@ export default function EventStream({ events }: { events: IEvent[] }) {
|
||||||
return (
|
return (
|
||||||
<div style={{ display: 'flex', flexDirection: 'column', width: '100%' }}>
|
<div style={{ display: 'flex', flexDirection: 'column', width: '100%' }}>
|
||||||
{events.map((event) => (
|
{events.map((event) => (
|
||||||
<Event event={event} key={event.name} />
|
<Event id={event.id} initial={event} key={event.name} />
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -2,6 +2,14 @@ import { useMemo } from 'react';
|
||||||
import { useContext } from 'react';
|
import { useContext } from 'react';
|
||||||
import { NotificationsContext } from './NotificationsProvider';
|
import { NotificationsContext } from './NotificationsProvider';
|
||||||
|
|
||||||
|
export function useSendCarpoolRequest() {
|
||||||
|
return useContext(NotificationsContext).sendCarpoolRequest;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useCancelCarpoolRequest() {
|
||||||
|
return useContext(NotificationsContext).cancelCarpoolRequest;
|
||||||
|
}
|
||||||
|
|
||||||
export function useInvitationState(
|
export function useInvitationState(
|
||||||
carpoolId: number
|
carpoolId: number
|
||||||
): 'invited' | 'requested' | 'none' {
|
): 'invited' | 'requested' | 'none' {
|
||||||
|
|
Loading…
Reference in New Issue
Block a user