diff --git a/src/components/Event/Event.tsx b/src/components/Event/Event.tsx index 98703ad..38e4553 100644 --- a/src/components/Event/Event.tsx +++ b/src/components/Event/Event.tsx @@ -1,3 +1,4 @@ +import * as immutable from 'immutable'; import { useCallback, useEffect, useRef, useState } from 'react'; import { green, lightgrey } from '../../lib/colors'; import { @@ -35,6 +36,7 @@ export default function Event({ const [interested, setInterested] = useState(false); const [updating, setUpdating] = useState(false); const [signups, setSignups] = useState(null); + const [hasCarpool, setHasCarpool] = useState(false); const toggleInterested = useCallback(() => setInterested((i) => !i), []); const toggleInterestedThrottled = useThrottle(toggleInterested, 500); const existingSignup = useRef({ @@ -44,6 +46,18 @@ export default function Event({ }); const me = useMe(); + const [tentativeInvites, setTentativeInvites] = useState( + immutable.Set() + ); + + const addTentativeInvite = useCallback((userId: number) => { + setTentativeInvites((t) => t.add(userId)); + }, []); + + const removeTentativeInvite = useCallback((userId: number) => { + setTentativeInvites((t) => t.delete(userId)); + }, []); + const refresh = useCallback(() => { getEvent(id).then(setEvent); }, [id]); @@ -116,7 +130,19 @@ export default function Event({ const { name, group, formattedAddress, startTime, endTime } = event; return ( - +
{name} @@ -146,11 +172,7 @@ export default function Event({
{signups !== null && ( - + )} )} diff --git a/src/components/Event/EventCarpools.tsx b/src/components/Event/EventCarpools.tsx index 4702251..0dd2a1f 100644 --- a/src/components/Event/EventCarpools.tsx +++ b/src/components/Event/EventCarpools.tsx @@ -1,6 +1,7 @@ import CancelIcon from '@material-ui/icons/Cancel'; import CheckIcon from '@material-ui/icons/Check'; import EmojiPeopleIcon from '@material-ui/icons/EmojiPeople'; +import { useEffect } from 'react'; import { useCallback, useContext, useMemo, useState } from 'react'; import { lightgrey } from '../../lib/colors'; import { @@ -91,7 +92,8 @@ function CarpoolRow({ type CreationStatus = null | 'pending' | 'completed' | 'errored'; export default function Carpools() { - const { event } = useContext(EventContext); + const { event, tentativeInvites, signups, setHasCarpool } = + useContext(EventContext); const [creationStatus, setCreationStatus] = useState(null); const [createdCarpoolId, setCreatedCarpoolId] = useState(null); @@ -106,6 +108,10 @@ export default function Carpools() { const alreadyInCarpool = myCarpool !== undefined || creationStatus === 'completed'; + useEffect(() => { + setHasCarpool(alreadyInCarpool); + }, [alreadyInCarpool, setHasCarpool]); + const createEmptyCarpool = useCallback(() => { setCreationStatus('pending'); @@ -119,6 +125,55 @@ export default function Carpools() { }); }, [event.id, me.name]); + const tentativeInviteNames = useMemo(() => { + if (!signups) return []; + const names = tentativeInvites.map((id) => { + const signup = signups.find((s) => s.user.id === id); + return signup?.user.name; + }); + const nonNull = names.filter((n) => n != null); + return nonNull.toArray() as string[]; + }, [tentativeInvites, signups]); + + let createCarpoolSection; + + if (tentativeInviteNames.length > 0) { + const inviteeCount = tentativeInviteNames.length; + const peoplePlural = inviteeCount > 1 ? 'People' : 'Person'; + createCarpoolSection = ( + <> +
+ You have invited these people to carpool with you: + {tentativeInviteNames.join(',')} + + {creationStatus === null + ? `Create Carpool With ${inviteeCount} ${peoplePlural}` + : creationStatus === 'pending' + ? 'Creating...' + : 'Errored'} + + + ); + } else + createCarpoolSection = ( + <> + Available to drive? + + {creationStatus === null + ? 'Create Empty Carpool' + : creationStatus === 'pending' + ? 'Creating...' + : 'Errored'} + + + ); + return (

Carpools

@@ -133,19 +188,7 @@ export default function Carpools() { {myCarpool.name} ) : ( - <> - Available to drive? - - {creationStatus === null - ? 'Create Empty Carpool' - : creationStatus === 'pending' - ? 'Creating...' - : 'Errored'} - - + createCarpoolSection )} {event.carpools.map((carpool) => ( { @@ -7,6 +8,18 @@ const EventContext = createContext({ }, event: null! as IEvent, default: true, + signups: null as IEventSignup[] | null, + addTentativeInvite: (id: number) => { + console.error('not implemented: addTentativeInvite'); + }, + removeTentativeInvite: (id: number) => { + console.error('not implemented: removeTentativeInvite'); + }, + tentativeInvites: immutable.Set(), + hasCarpool: false, + setHasCarpool: (has: boolean) => { + console.error('not implemented: setHasCarpool'); + }, }); export default EventContext; diff --git a/src/components/Event/EventSignups.tsx b/src/components/Event/EventSignups.tsx index 643ebcb..f847ada 100644 --- a/src/components/Event/EventSignups.tsx +++ b/src/components/Event/EventSignups.tsx @@ -1,24 +1,124 @@ +import CancelIcon from '@material-ui/icons/Cancel'; import PersonAddIcon from '@material-ui/icons/PersonAdd'; -import { useMe } from '../hooks'; +import { useContext, useMemo } from 'react'; +import { PlaceDetails } from '../../lib/getPlaceDetails'; import latlongdist, { R_miles } from '../../lib/latlongdist'; -import { IEventSignup, IEvent } from '../types'; +import { useMe } from '../hooks'; +import { IEventSignup } from '../types'; import usePlace from '../usePlace'; -import { useMemo } from 'react'; +import EventContext from './EventContext'; + +function EventSignup({ + signup, + locationLatitude, + locationLongitude, + myPlaceDetails, +}: { + signup: IEventSignup; + locationLatitude: number; + locationLongitude: number; + myPlaceDetails: PlaceDetails | null; +}) { + const { user, latitude, longitude } = signup; + const me = useMe(); + const { + addTentativeInvite, + removeTentativeInvite, + tentativeInvites, + hasCarpool, + } = useContext(EventContext); + + let extraDistance = useMemo(() => { + if (myPlaceDetails != null && !(latitude === null || longitude === null)) { + const myLatitude = myPlaceDetails.latitude; + const myLongitude = myPlaceDetails.longitude; + const meToThem = latlongdist( + latitude, + longitude, + locationLongitude, + locationLatitude, + R_miles + ); + const themToLocation = latlongdist( + latitude, + longitude, + myLatitude, + myLongitude, + R_miles + ); + const totalWithThem = meToThem + themToLocation; + const totalWithoutThem = latlongdist( + locationLongitude, + locationLatitude, + myLatitude, + myLongitude, + R_miles + ); + return totalWithThem - totalWithoutThem; + } else { + return null; + } + }, [ + latitude, + longitude, + locationLatitude, + locationLongitude, + myPlaceDetails, + ]); + + const isTentativelyInvited = useMemo( + () => tentativeInvites.has(signup.user.id), + [signup.user.id, tentativeInvites] + ); + + if (user.id === me?.id) { + return null; + } + + return ( +
+ {user.name} + {extraDistance ? `: +${extraDistance.toFixed(1)} miles` : ''} + + {!hasCarpool && + (isTentativelyInvited ? ( + removeTentativeInvite(user.id)} + style={{ cursor: 'pointer' }} + /> + ) : ( + addTentativeInvite(user.id)} + style={{ cursor: 'pointer' }} + /> + ))} +
+ ); +} export default function EventSignups({ - event, signups, myPlaceId, }: { - event: IEvent; signups: IEventSignup[]; myPlaceId: string | null; }) { + const { event } = useContext(EventContext); const carpools = event.carpools; - const placeDetails = usePlace(myPlaceId); - const locationLongitude = event.latitude; - const locationLatitude = event.longitude; - const me = useMe(); + const myPlaceDetails = usePlace(myPlaceId); const signupsWithoutCarpool = useMemo(() => { // A list of users not in any carpool @@ -31,67 +131,15 @@ export default function EventSignups({ return (

People without a carpool

- {signupsWithoutCarpool.map(({ latitude, longitude, user }) => { - if (user.id === me?.id) { - return null; - } - let extraDistance = null; - if ( - placeDetails != null && - !(latitude === null || longitude === null) - ) { - const myLatitude = placeDetails.latitude; - const myLongitude = placeDetails.longitude; - const meToThem = latlongdist( - latitude, - longitude, - locationLongitude, - locationLatitude, - R_miles - ); - const themToLocation = latlongdist( - latitude, - longitude, - myLatitude, - myLongitude, - R_miles - ); - const totalWithThem = meToThem + themToLocation; - const totalWithoutThem = latlongdist( - locationLongitude, - locationLatitude, - myLatitude, - myLongitude, - R_miles - ); - extraDistance = totalWithThem - totalWithoutThem; - } - - return ( -
- {user.name} - {extraDistance ? `: +${extraDistance.toFixed(1)} miles` : ''} - { - // Invite to carpool and create carpool - }} - /> -
- ); - })} + {signupsWithoutCarpool.map((signup) => ( + + ))}
); }