mirror of
https://github.com/myfatemi04/wheelshare-frontend.git
synced 2025-04-16 00:50:18 -04:00
add pickLatLong, other hooks
This commit is contained in:
parent
ac6717d527
commit
a37323fc07
|
@ -1,10 +1,9 @@
|
||||||
import CancelIcon from '@material-ui/icons/Cancel';
|
import CancelIcon from '@material-ui/icons/Cancel';
|
||||||
import PersonAddIcon from '@material-ui/icons/PersonAdd';
|
import PersonAddIcon from '@material-ui/icons/PersonAdd';
|
||||||
import { useMemo } from 'react';
|
import { useContext, useEffect, useMemo } from 'react';
|
||||||
import { useContext, useEffect, useState } from 'react';
|
|
||||||
import { getEventSignups } from '../api';
|
import { getEventSignups } from '../api';
|
||||||
import { useMe } from '../hooks';
|
|
||||||
import { IEventSignup } from '../types';
|
import { IEventSignup } from '../types';
|
||||||
|
import useImmutable from '../useImmutable';
|
||||||
import { CarpoolContext } from './Carpool';
|
import { CarpoolContext } from './Carpool';
|
||||||
|
|
||||||
function InvitationRow({
|
function InvitationRow({
|
||||||
|
@ -43,18 +42,15 @@ function InvitationRow({
|
||||||
|
|
||||||
export default function InvitationList() {
|
export default function InvitationList() {
|
||||||
const { carpool } = useContext(CarpoolContext);
|
const { carpool } = useContext(CarpoolContext);
|
||||||
const me = useMe()!;
|
|
||||||
|
|
||||||
const eventId = carpool.event.id;
|
const eventId = carpool.event.id;
|
||||||
|
|
||||||
const [availableSignups, setAvailableSignups] =
|
const [availableSignups, setAvailableSignups] =
|
||||||
useState<IEventSignup[] | null>(null);
|
useImmutable<IEventSignup[] | null>(null);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
getEventSignups(eventId).then((signups) =>
|
getEventSignups(eventId).then(setAvailableSignups);
|
||||||
setAvailableSignups(signups.filter((signup) => signup.user.id !== me.id))
|
}, [eventId, setAvailableSignups]);
|
||||||
);
|
|
||||||
}, [eventId, me.id]);
|
|
||||||
|
|
||||||
const invitedUserIDs = useMemo(
|
const invitedUserIDs = useMemo(
|
||||||
() =>
|
() =>
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import { useCallback, useEffect, useRef, useState } from 'react';
|
import { useCallback, useEffect } from 'react';
|
||||||
import { green, lightgrey } from '../../lib/colors';
|
import { green, lightgrey } from '../../lib/colors';
|
||||||
|
import getPlaceDetails from '../../lib/getPlaceDetails';
|
||||||
import {
|
import {
|
||||||
addOrUpdateEventSignup,
|
addOrUpdateEventSignup,
|
||||||
getEvent,
|
getEvent,
|
||||||
|
@ -14,7 +15,6 @@ import UIPlacesAutocomplete from '../UI/UIPlacesAutocomplete';
|
||||||
import UISecondaryBox from '../UI/UISecondaryBox';
|
import UISecondaryBox from '../UI/UISecondaryBox';
|
||||||
import UISecondaryHeader from '../UI/UISecondaryHeader';
|
import UISecondaryHeader from '../UI/UISecondaryHeader';
|
||||||
import useImmutable from '../useImmutable';
|
import useImmutable from '../useImmutable';
|
||||||
import useThrottle from '../useThrottle';
|
|
||||||
import EventCarpools from './EventCarpools';
|
import EventCarpools from './EventCarpools';
|
||||||
import EventContext from './EventContext';
|
import EventContext from './EventContext';
|
||||||
import EventDetails from './EventDetails';
|
import EventDetails from './EventDetails';
|
||||||
|
@ -51,20 +51,10 @@ export default function Event({
|
||||||
duration: 0,
|
duration: 0,
|
||||||
...(initial || {}),
|
...(initial || {}),
|
||||||
});
|
});
|
||||||
const [myPlaceId, setPlaceId] = useState<string | null>(null);
|
|
||||||
const [interested, setInterested] = useState(false);
|
|
||||||
const [updating, setUpdating] = useState(false);
|
|
||||||
const [signups, setSignups] =
|
const [signups, setSignups] =
|
||||||
useState<Record<string, IEventSignup>>(NOT_LOADED);
|
useImmutable<Record<string, IEventSignup>>(NOT_LOADED);
|
||||||
const [hasCarpool, setHasCarpool] = useState(false);
|
|
||||||
const toggleInterested = useCallback(() => setInterested((i) => !i), []);
|
const me = useMe()!;
|
||||||
const toggleInterestedThrottled = useThrottle(toggleInterested, 500);
|
|
||||||
const existingSignup = useRef({
|
|
||||||
interested: false,
|
|
||||||
placeId: null as string | null,
|
|
||||||
eventId: null as number | null,
|
|
||||||
});
|
|
||||||
const me = useMe();
|
|
||||||
|
|
||||||
const [tentativeInvites] = useImmutable<Record<number, boolean>>({});
|
const [tentativeInvites] = useImmutable<Record<number, boolean>>({});
|
||||||
|
|
||||||
|
@ -74,60 +64,44 @@ export default function Event({
|
||||||
|
|
||||||
useEffect(refresh, [refresh]);
|
useEffect(refresh, [refresh]);
|
||||||
|
|
||||||
useEffect(() => {
|
const updateSignup = useCallback(
|
||||||
if (signups === NOT_LOADED) {
|
async (placeId: string | null) => {
|
||||||
return;
|
await addOrUpdateEventSignup(id, placeId);
|
||||||
}
|
|
||||||
|
|
||||||
const removeSignup = () => {
|
if (placeId) {
|
||||||
if (prev.interested) {
|
const details = await getPlaceDetails(placeId);
|
||||||
removeEventSignup(id)
|
|
||||||
.then(() => {
|
signups[me.id] = {
|
||||||
prev.interested = false;
|
user: { id: me.id, name: me.name },
|
||||||
})
|
placeId,
|
||||||
.finally(() => setUpdating(false));
|
...details,
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
signups[me.id] = {
|
||||||
|
user: { id: me.id, name: me.name },
|
||||||
|
placeId: null,
|
||||||
|
latitude: null,
|
||||||
|
longitude: null,
|
||||||
|
formattedAddress: null,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
};
|
},
|
||||||
|
[id, me.id, me.name, signups]
|
||||||
|
);
|
||||||
|
|
||||||
const addOrUpdateSignup = () => {
|
const removeSignup = useCallback(async () => {
|
||||||
if (!prev.interested || prev.placeId !== myPlaceId) {
|
await removeEventSignup(id);
|
||||||
console.log('Adding or updating signup.', prev, {
|
|
||||||
interested,
|
|
||||||
placeId: myPlaceId,
|
|
||||||
eventId: id,
|
|
||||||
signups,
|
|
||||||
});
|
|
||||||
addOrUpdateEventSignup(id, myPlaceId)
|
|
||||||
.then(() => {
|
|
||||||
prev.placeId = myPlaceId;
|
|
||||||
prev.eventId = id;
|
|
||||||
prev.interested = true;
|
|
||||||
})
|
|
||||||
.finally(() => setUpdating(false));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const prev = existingSignup.current;
|
if (signups[me.id]) {
|
||||||
|
delete signups[me.id];
|
||||||
if (!interested) {
|
|
||||||
removeSignup();
|
|
||||||
} else {
|
|
||||||
addOrUpdateSignup();
|
|
||||||
}
|
}
|
||||||
}, [id, interested, myPlaceId, signups, updating]);
|
}, [id, me.id, signups]);
|
||||||
|
|
||||||
|
const interested = !!signups[me.id];
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
getEventSignups(id)
|
getEventSignups(id)
|
||||||
.then((signups) => {
|
.then((signups) => {
|
||||||
for (let signup of signups) {
|
|
||||||
if (signup.user.id === me?.id) {
|
|
||||||
setInterested(true);
|
|
||||||
setPlaceId(signup.placeId);
|
|
||||||
existingSignup.current.eventId = id;
|
|
||||||
existingSignup.current.placeId = signup.placeId;
|
|
||||||
existingSignup.current.interested = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const signupMap: Record<string, IEventSignup> = {};
|
const signupMap: Record<string, IEventSignup> = {};
|
||||||
for (let signup of signups) {
|
for (let signup of signups) {
|
||||||
signupMap[signup.user.id] = signup;
|
signupMap[signup.user.id] = signup;
|
||||||
|
@ -135,7 +109,7 @@ export default function Event({
|
||||||
setSignups(signupMap);
|
setSignups(signupMap);
|
||||||
})
|
})
|
||||||
.catch(console.error);
|
.catch(console.error);
|
||||||
}, [id, me?.id]);
|
}, [id, setSignups]);
|
||||||
|
|
||||||
if (!event) {
|
if (!event) {
|
||||||
return <UISecondaryBox>Loading...</UISecondaryBox>;
|
return <UISecondaryBox>Loading...</UISecondaryBox>;
|
||||||
|
@ -151,9 +125,6 @@ export default function Event({
|
||||||
default: false,
|
default: false,
|
||||||
tentativeInvites,
|
tentativeInvites,
|
||||||
signups,
|
signups,
|
||||||
hasCarpool,
|
|
||||||
setHasCarpool,
|
|
||||||
myPlaceId,
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<UISecondaryBox>
|
<UISecondaryBox>
|
||||||
|
@ -163,7 +134,7 @@ export default function Event({
|
||||||
</div>
|
</div>
|
||||||
<EventDetails {...{ startTime, endTime, formattedAddress }} />
|
<EventDetails {...{ startTime, endTime, formattedAddress }} />
|
||||||
<UIButton
|
<UIButton
|
||||||
onClick={toggleInterestedThrottled}
|
onClick={interested ? () => removeSignup() : () => updateSignup(null)}
|
||||||
style={{
|
style={{
|
||||||
backgroundColor: interested ? green : lightgrey,
|
backgroundColor: interested ? green : lightgrey,
|
||||||
color: interested ? 'white' : 'black',
|
color: interested ? 'white' : 'black',
|
||||||
|
@ -176,15 +147,19 @@ export default function Event({
|
||||||
<>
|
<>
|
||||||
<UIPlacesAutocomplete
|
<UIPlacesAutocomplete
|
||||||
placeholder="Pickup and dropoff location"
|
placeholder="Pickup and dropoff location"
|
||||||
onSelected={(_address, placeID) => {
|
onSelected={(_address, placeId) => {
|
||||||
setPlaceId(placeID);
|
updateSignup(placeId);
|
||||||
}}
|
}}
|
||||||
style={myPlaceId != null ? { border: '2px solid ' + green } : {}}
|
style={
|
||||||
placeId={myPlaceId}
|
signups[me.id]?.placeId != null
|
||||||
|
? { border: '2px solid ' + green }
|
||||||
|
: {}
|
||||||
|
}
|
||||||
|
placeId={signups[me.id]?.placeId}
|
||||||
/>
|
/>
|
||||||
<br />
|
<br />
|
||||||
<EventCarpools />
|
<EventCarpools />
|
||||||
{signups !== null && <EventSignups myPlaceId={myPlaceId} />}
|
{signups !== null && <EventSignups />}
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</UISecondaryBox>
|
</UISecondaryBox>
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { useCallback, useContext, useEffect, useMemo, useState } from 'react';
|
import { useCallback, useContext, useMemo, useState } from 'react';
|
||||||
import { lightgrey } from '../../lib/colors';
|
import { lightgrey } from '../../lib/colors';
|
||||||
import { createCarpool } from '../api';
|
import { createCarpool } from '../api';
|
||||||
import { useMe } from '../hooks';
|
import { useMe } from '../hooks';
|
||||||
|
@ -9,8 +9,7 @@ import EventContext from './EventContext';
|
||||||
type CreationStatus = null | 'pending' | 'completed' | 'errored';
|
type CreationStatus = null | 'pending' | 'completed' | 'errored';
|
||||||
|
|
||||||
export default function EventCarpoolCreateButton() {
|
export default function EventCarpoolCreateButton() {
|
||||||
const { event, setHasCarpool, tentativeInvites, signups } =
|
const { event, tentativeInvites } = useContext(EventContext);
|
||||||
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);
|
||||||
|
@ -23,83 +22,29 @@ export default function EventCarpoolCreateButton() {
|
||||||
),
|
),
|
||||||
[event.carpools, me.id]
|
[event.carpools, me.id]
|
||||||
);
|
);
|
||||||
const alreadyInCarpool =
|
|
||||||
myCarpool !== undefined || creationStatus === 'completed';
|
|
||||||
|
|
||||||
useEffect(() => {
|
const createCarpoolCallback = useCallback(async () => {
|
||||||
setHasCarpool(alreadyInCarpool);
|
|
||||||
}, [alreadyInCarpool, setHasCarpool]);
|
|
||||||
|
|
||||||
const createCarpoolCallback = useCallback(() => {
|
|
||||||
setCreationStatus('pending');
|
setCreationStatus('pending');
|
||||||
|
|
||||||
createCarpool({
|
const { id } = await createCarpool({
|
||||||
name: me.name + "'s Carpool",
|
name: me.name + "'s Carpool",
|
||||||
eventId: event.id,
|
eventId: event.id,
|
||||||
invitedUserIds: Object.keys(tentativeInvites).map(Number),
|
invitedUserIds: Object.keys(tentativeInvites).map(Number),
|
||||||
})
|
});
|
||||||
.then(({ id }) => {
|
try {
|
||||||
setCreatedCarpoolId(id);
|
event.carpools.push({
|
||||||
event.carpools.push({
|
id,
|
||||||
id,
|
name: me.name + "'s Carpool",
|
||||||
name: me.name + "'s Carpool",
|
members: [{ id: me.id, name: me.name }],
|
||||||
members: [{ id: me.id, name: me.name }],
|
|
||||||
});
|
|
||||||
setCreationStatus('completed');
|
|
||||||
})
|
|
||||||
.catch(() => {
|
|
||||||
setCreationStatus('errored');
|
|
||||||
});
|
});
|
||||||
|
setCreatedCarpoolId(id);
|
||||||
|
setCreationStatus('completed');
|
||||||
|
} catch (e) {
|
||||||
|
setCreationStatus('errored');
|
||||||
|
}
|
||||||
}, [event.carpools, event.id, me.id, me.name, tentativeInvites]);
|
}, [event.carpools, event.id, me.id, me.name, tentativeInvites]);
|
||||||
|
|
||||||
const tentativeInviteNames = useMemo(() => {
|
const inviteCount = Object.keys(tentativeInvites).length;
|
||||||
if (!signups) return [];
|
|
||||||
const names = Object.keys(tentativeInvites).map((id) => {
|
|
||||||
const signup = signups[id];
|
|
||||||
return signup?.user.name;
|
|
||||||
});
|
|
||||||
return names.filter((n) => n != null);
|
|
||||||
}, [tentativeInvites, signups]);
|
|
||||||
|
|
||||||
let createCarpoolSection;
|
|
||||||
|
|
||||||
if (tentativeInviteNames.length > 0) {
|
|
||||||
const inviteeCount = tentativeInviteNames.length;
|
|
||||||
const peoplePlural = inviteeCount > 1 ? 'People' : 'Person';
|
|
||||||
createCarpoolSection = (
|
|
||||||
<>
|
|
||||||
<br />
|
|
||||||
<b>List:</b>
|
|
||||||
<br />
|
|
||||||
{tentativeInviteNames.join(',')}
|
|
||||||
<UIButton
|
|
||||||
onClick={createCarpoolCallback}
|
|
||||||
style={{ backgroundColor: lightgrey }}
|
|
||||||
>
|
|
||||||
{creationStatus === null
|
|
||||||
? `Create Carpool With ${inviteeCount} ${peoplePlural}`
|
|
||||||
: creationStatus === 'pending'
|
|
||||||
? 'Creating...'
|
|
||||||
: 'Errored'}
|
|
||||||
</UIButton>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
} else
|
|
||||||
createCarpoolSection = (
|
|
||||||
<>
|
|
||||||
<span>Available to drive?</span>
|
|
||||||
<UIButton
|
|
||||||
onClick={createCarpoolCallback}
|
|
||||||
style={{ backgroundColor: lightgrey }}
|
|
||||||
>
|
|
||||||
{creationStatus === null
|
|
||||||
? 'Create Empty Carpool'
|
|
||||||
: creationStatus === 'pending'
|
|
||||||
? 'Creating...'
|
|
||||||
: 'Errored'}
|
|
||||||
</UIButton>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
|
@ -114,7 +59,21 @@ export default function EventCarpoolCreateButton() {
|
||||||
<UILink href={`/carpools/${myCarpool.id}`}>{myCarpool.name}</UILink>
|
<UILink href={`/carpools/${myCarpool.id}`}>{myCarpool.name}</UILink>
|
||||||
</span>
|
</span>
|
||||||
) : (
|
) : (
|
||||||
createCarpoolSection
|
<>
|
||||||
|
<span>Available to drive?</span>
|
||||||
|
<UIButton
|
||||||
|
onClick={createCarpoolCallback}
|
||||||
|
style={{ backgroundColor: lightgrey }}
|
||||||
|
>
|
||||||
|
{creationStatus === null
|
||||||
|
? inviteCount === 0
|
||||||
|
? 'Create Empty Carpool'
|
||||||
|
: 'Create With ' + inviteCount
|
||||||
|
: creationStatus === 'pending'
|
||||||
|
? 'Creating...'
|
||||||
|
: 'Errored'}
|
||||||
|
</UIButton>
|
||||||
|
</>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -11,8 +11,8 @@ import {
|
||||||
import { useMe } from '../hooks';
|
import { useMe } from '../hooks';
|
||||||
import { IEvent } from '../types';
|
import { IEvent } from '../types';
|
||||||
import useOptimalPath from '../useOptimalPath';
|
import useOptimalPath from '../useOptimalPath';
|
||||||
import usePlace from '../usePlace';
|
|
||||||
import EventContext from './EventContext';
|
import EventContext from './EventContext';
|
||||||
|
import useMySignup from './useMySignup';
|
||||||
|
|
||||||
function useMemberLocations(members: IEvent['carpools'][0]['members']) {
|
function useMemberLocations(members: IEvent['carpools'][0]['members']) {
|
||||||
const { signups } = useContext(EventContext);
|
const { signups } = useContext(EventContext);
|
||||||
|
@ -61,10 +61,17 @@ function CarpoolRow({
|
||||||
|
|
||||||
const {
|
const {
|
||||||
event: { latitude, longitude },
|
event: { latitude, longitude },
|
||||||
myPlaceId,
|
|
||||||
} = useContext(EventContext);
|
} = useContext(EventContext);
|
||||||
|
|
||||||
const myLocation = usePlace(myPlaceId);
|
const mySignup = useMySignup();
|
||||||
|
|
||||||
|
const myLocation =
|
||||||
|
mySignup && mySignup.latitude !== null
|
||||||
|
? {
|
||||||
|
latitude: mySignup.latitude,
|
||||||
|
longitude: mySignup.longitude,
|
||||||
|
}
|
||||||
|
: null;
|
||||||
|
|
||||||
const memberLocations = useMemberLocations(carpool.members);
|
const memberLocations = useMemberLocations(carpool.members);
|
||||||
|
|
||||||
|
|
|
@ -9,11 +9,6 @@ const EventContext = createContext({
|
||||||
default: true,
|
default: true,
|
||||||
signups: {} as Record<string, IEventSignup>,
|
signups: {} as Record<string, IEventSignup>,
|
||||||
tentativeInvites: {} as Record<number, boolean>,
|
tentativeInvites: {} as Record<number, boolean>,
|
||||||
hasCarpool: false,
|
|
||||||
setHasCarpool: (has: boolean) => {
|
|
||||||
console.error('not implemented: setHasCarpool');
|
|
||||||
},
|
|
||||||
myPlaceId: null as string | null,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
export default EventContext;
|
export default EventContext;
|
||||||
|
|
|
@ -1,68 +1,42 @@
|
||||||
import CancelIcon from '@material-ui/icons/Cancel';
|
import CancelIcon from '@material-ui/icons/Cancel';
|
||||||
import PersonAddIcon from '@material-ui/icons/PersonAdd';
|
import PersonAddIcon from '@material-ui/icons/PersonAdd';
|
||||||
import { useContext, useMemo } from 'react';
|
import { useContext, useMemo } from 'react';
|
||||||
import { PlaceDetails } from '../../lib/getPlaceDetails';
|
|
||||||
import latlongdist, { R_miles } from '../../lib/latlongdist';
|
import latlongdist, { R_miles } from '../../lib/latlongdist';
|
||||||
import { useMe } from '../hooks';
|
import { useMe } from '../hooks';
|
||||||
import { IEventSignup } from '../types';
|
import { IEventSignup } from '../types';
|
||||||
import usePlace from '../usePlace';
|
|
||||||
import EventCarpoolCreateButton from './EventCarpoolCreateButton';
|
import EventCarpoolCreateButton from './EventCarpoolCreateButton';
|
||||||
import EventContext from './EventContext';
|
import EventContext from './EventContext';
|
||||||
|
import pickLatLong from './pickLatLong';
|
||||||
|
import useMySignup from './useMySignup';
|
||||||
|
|
||||||
function EventSignup({
|
function EventSignup({ signup }: { signup: IEventSignup }) {
|
||||||
signup,
|
const { user } = signup;
|
||||||
locationLatitude,
|
|
||||||
locationLongitude,
|
|
||||||
myPlaceDetails,
|
|
||||||
}: {
|
|
||||||
signup: IEventSignup;
|
|
||||||
locationLatitude: number;
|
|
||||||
locationLongitude: number;
|
|
||||||
myPlaceDetails: PlaceDetails | null;
|
|
||||||
}) {
|
|
||||||
const { user, latitude, longitude } = signup;
|
|
||||||
const me = useMe();
|
const me = useMe();
|
||||||
const { tentativeInvites, hasCarpool } = useContext(EventContext);
|
const { tentativeInvites, event } = useContext(EventContext);
|
||||||
|
const mySignup = useMySignup();
|
||||||
|
const myLocation = pickLatLong(mySignup);
|
||||||
|
const theirLocation = pickLatLong(signup);
|
||||||
|
const eventLocation = pickLatLong(event)!;
|
||||||
|
|
||||||
const extraDistance = useMemo(() => {
|
const extraDistance = useMemo(() => {
|
||||||
if (myPlaceDetails != null && !(latitude === null || longitude === null)) {
|
if (myLocation != null && theirLocation != null) {
|
||||||
const myLatitude = myPlaceDetails.latitude;
|
const meToThem = latlongdist(myLocation, theirLocation, R_miles);
|
||||||
const myLongitude = myPlaceDetails.longitude;
|
const themToLocation = latlongdist(theirLocation, eventLocation, R_miles);
|
||||||
const meToThem = latlongdist(
|
const meToLocation = latlongdist(myLocation, eventLocation, R_miles);
|
||||||
latitude,
|
return meToThem + themToLocation - meToLocation;
|
||||||
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 {
|
} else {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}, [
|
}, [eventLocation, myLocation, theirLocation]);
|
||||||
latitude,
|
|
||||||
longitude,
|
|
||||||
locationLatitude,
|
|
||||||
locationLongitude,
|
|
||||||
myPlaceDetails,
|
|
||||||
]);
|
|
||||||
|
|
||||||
const isTentativelyInvited = signup.user.id in tentativeInvites;
|
const isTentativelyInvited = signup.user.id in tentativeInvites;
|
||||||
|
const hasCarpool = useMemo(
|
||||||
|
() =>
|
||||||
|
event.carpools.some((carpool) =>
|
||||||
|
carpool.members.some((member) => member.id === me?.id)
|
||||||
|
),
|
||||||
|
[event.carpools, me?.id]
|
||||||
|
);
|
||||||
|
|
||||||
if (user.id === me?.id) {
|
if (user.id === me?.id) {
|
||||||
return null;
|
return null;
|
||||||
|
@ -104,14 +78,9 @@ function EventSignup({
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function EventSignups({
|
export default function EventSignups() {
|
||||||
myPlaceId,
|
|
||||||
}: {
|
|
||||||
myPlaceId: string | null;
|
|
||||||
}) {
|
|
||||||
const { event, signups } = useContext(EventContext);
|
const { event, signups } = useContext(EventContext);
|
||||||
const carpools = event.carpools;
|
const carpools = event.carpools;
|
||||||
const myPlaceDetails = usePlace(myPlaceId);
|
|
||||||
|
|
||||||
const signupsWithoutCarpool = useMemo(() => {
|
const signupsWithoutCarpool = useMemo(() => {
|
||||||
// A list of users not in any carpool
|
// A list of users not in any carpool
|
||||||
|
@ -128,13 +97,7 @@ export default function EventSignups({
|
||||||
<h3 style={{ marginBlockEnd: '0' }}>People without a carpool</h3>
|
<h3 style={{ marginBlockEnd: '0' }}>People without a carpool</h3>
|
||||||
<EventCarpoolCreateButton />
|
<EventCarpoolCreateButton />
|
||||||
{signupsWithoutCarpool.map((signup) => (
|
{signupsWithoutCarpool.map((signup) => (
|
||||||
<EventSignup
|
<EventSignup key={signup.user.id} signup={signup} />
|
||||||
key={signup.user.id}
|
|
||||||
signup={signup}
|
|
||||||
myPlaceDetails={myPlaceDetails}
|
|
||||||
locationLatitude={event.latitude}
|
|
||||||
locationLongitude={event.longitude}
|
|
||||||
/>
|
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
11
src/components/Event/pickLatLong.ts
Normal file
11
src/components/Event/pickLatLong.ts
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
export default function pickLatLong<
|
||||||
|
T extends { latitude: number | null; longitude: number | null } | null
|
||||||
|
>(e: T): { latitude: number; longitude: number } | null {
|
||||||
|
if (e === null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (e.latitude === null || e.longitude === null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return { latitude: e.latitude, longitude: e.longitude };
|
||||||
|
}
|
10
src/components/Event/useMySignup.tsx
Normal file
10
src/components/Event/useMySignup.tsx
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
import { useContext, useMemo } from 'react';
|
||||||
|
import { useMe } from '../hooks';
|
||||||
|
import EventContext from './EventContext';
|
||||||
|
|
||||||
|
export default function useMySignup() {
|
||||||
|
const { signups } = useContext(EventContext);
|
||||||
|
const me = useMe()!;
|
||||||
|
|
||||||
|
return useMemo(() => signups[me.id] ?? null, [signups, me.id]);
|
||||||
|
}
|
|
@ -1,14 +1,19 @@
|
||||||
import { useEffect, useState } from 'react';
|
import { useEffect } from 'react';
|
||||||
import { getEvents } from './api';
|
import { getEvents } from './api';
|
||||||
import { IEvent } from './types';
|
import { IEvent } from './types';
|
||||||
import EventStream from './EventStream';
|
import EventStream from './EventStream';
|
||||||
|
import useImmutable from './useImmutable';
|
||||||
|
|
||||||
export default function Events() {
|
export default function Events() {
|
||||||
const [events, setEvents] = useState<IEvent[]>([]);
|
const [events, setEvents] = useImmutable<IEvent[]>([]);
|
||||||
|
|
||||||
|
const hasEvents = events.length > 0;
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
getEvents().then(setEvents);
|
if (!hasEvents) {
|
||||||
}, []);
|
getEvents().then(setEvents);
|
||||||
|
}
|
||||||
|
}, [hasEvents, setEvents]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
|
|
@ -86,17 +86,19 @@ export type IEvent = {
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export type IEventSignup = {
|
export type IEventSignup = {
|
||||||
eventId: number;
|
|
||||||
// userId: number;
|
|
||||||
user: {
|
user: {
|
||||||
id: number;
|
id: number;
|
||||||
name: string;
|
name: string;
|
||||||
};
|
};
|
||||||
placeId: string | null;
|
} & (
|
||||||
formattedAddress: string | null;
|
| { placeId: null; formattedAddress: null; latitude: null; longitude: null }
|
||||||
latitude: number | null;
|
| {
|
||||||
longitude: number | null;
|
placeId: string;
|
||||||
};
|
formattedAddress: string;
|
||||||
|
latitude: number;
|
||||||
|
longitude: number;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
export type IInvitation = {
|
export type IInvitation = {
|
||||||
user: {
|
user: {
|
||||||
|
|
|
@ -6,13 +6,7 @@ export default function getDistance(...locations: Location[]): number {
|
||||||
for (let i = 0; i < locations.length - 1; i++) {
|
for (let i = 0; i < locations.length - 1; i++) {
|
||||||
const from = locations[i];
|
const from = locations[i];
|
||||||
const to = locations[i + 1];
|
const to = locations[i + 1];
|
||||||
distance += latlongdist(
|
distance += latlongdist(from, to, R_miles);
|
||||||
from.latitude,
|
|
||||||
from.longitude,
|
|
||||||
to.latitude,
|
|
||||||
to.longitude,
|
|
||||||
R_miles
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
return distance;
|
return distance;
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,12 +13,12 @@ export const R_miles = 3958.8;
|
||||||
* @returns The distance in meters between point 1 and point 2
|
* @returns The distance in meters between point 1 and point 2
|
||||||
*/
|
*/
|
||||||
export default function latlongdist(
|
export default function latlongdist(
|
||||||
lat1: number,
|
firstLocation: { latitude: number; longitude: number },
|
||||||
lon1: number,
|
secondLocation: { latitude: number; longitude: number },
|
||||||
lat2: number,
|
|
||||||
lon2: number,
|
|
||||||
R = R_meters
|
R = R_meters
|
||||||
) {
|
) {
|
||||||
|
const { latitude: lat1, longitude: lon1 } = firstLocation;
|
||||||
|
const { latitude: lat2, longitude: lon2 } = secondLocation;
|
||||||
const φ1 = (lat1 * Math.PI) / 180; // φ, λ in radians
|
const φ1 = (lat1 * Math.PI) / 180; // φ, λ in radians
|
||||||
const φ2 = (lat2 * Math.PI) / 180;
|
const φ2 = (lat2 * Math.PI) / 180;
|
||||||
const Δφ = ((lat2 - lat1) * Math.PI) / 180;
|
const Δφ = ((lat2 - lat1) * Math.PI) / 180;
|
||||||
|
|
Loading…
Reference in New Issue
Block a user