mirror of
https://github.com/myfatemi04/wheelshare-frontend.git
synced 2025-04-16 00:50:18 -04:00
integrate useImmutable
This commit is contained in:
parent
0735d3ed3a
commit
a48ad180e0
7
.env
7
.env
|
@ -1,5 +1,2 @@
|
|||
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/
|
||||
REACT_APP_API_LOCAL=http://localhost:5000/
|
||||
REACT_APP_API_PROD=https://api.wheelshare.app/
|
||||
|
|
|
@ -3,6 +3,7 @@ import { BrowserRouter, Route, Switch } from 'react-router-dom';
|
|||
import GroupsProvider from '../state/GroupsProvider';
|
||||
import NotificationsProvider from '../state/Notifications/NotificationsProvider';
|
||||
import { useMe } from './hooks';
|
||||
import useImmutable from './useImmutable';
|
||||
import WheelShare from './WheelShare';
|
||||
import WheelShareLoggedOut from './WheelShareLoggedOut';
|
||||
|
||||
|
@ -23,11 +24,20 @@ const style: CSSProperties = {
|
|||
|
||||
export default function App() {
|
||||
const user = useMe();
|
||||
const [imm] = useImmutable({
|
||||
x: 0,
|
||||
y: 0,
|
||||
z: { a: 1, b: 2, c: [0, 1, 2] },
|
||||
});
|
||||
return (
|
||||
<NotificationsProvider>
|
||||
<GroupsProvider>
|
||||
<div style={{ padding: '1rem', maxWidth: '100vw' }}>
|
||||
<div style={style}>
|
||||
{JSON.stringify(imm)}
|
||||
{/* Reset button */}
|
||||
<button onClick={() => imm.z.a++}>Increment</button>
|
||||
<button onClick={() => imm.z.c.push(imm.z.c.length)}>Push</button>
|
||||
<BrowserRouter>
|
||||
<Switch>
|
||||
<Route
|
||||
|
|
|
@ -1,11 +1,4 @@
|
|||
import * as immutable from 'immutable';
|
||||
import {
|
||||
createContext,
|
||||
useCallback,
|
||||
useEffect,
|
||||
useMemo,
|
||||
useState,
|
||||
} from 'react';
|
||||
import { createContext, useCallback, useEffect, useMemo } from 'react';
|
||||
import {
|
||||
cancelCarpoolInvite,
|
||||
getCarpool,
|
||||
|
@ -13,33 +6,23 @@ import {
|
|||
sendCarpoolInvite,
|
||||
} from '../api';
|
||||
import { useMe } from '../hooks';
|
||||
import { ICarpool } from '../types';
|
||||
import UISecondaryBox from '../UI/UISecondaryBox';
|
||||
import useImmutable from '../useImmutable';
|
||||
import CarpoolDetails from './CarpoolDetails';
|
||||
import InvitationsAndRequests from './InvitationsAndRequests';
|
||||
import MemberList from './MemberList';
|
||||
|
||||
class CarpoolState extends immutable.Record({
|
||||
id: 0,
|
||||
name: '',
|
||||
eventId: -1,
|
||||
event: {
|
||||
id: -1,
|
||||
name: '',
|
||||
formattedAddress: '',
|
||||
latitude: 0,
|
||||
longitude: 0,
|
||||
placeId: '',
|
||||
},
|
||||
members: immutable.List<{ id: number; name: string }>(),
|
||||
invitations:
|
||||
immutable.Map<
|
||||
number,
|
||||
{ isRequest: boolean; user: { id: number; name: string } }
|
||||
>(),
|
||||
}) {}
|
||||
type CarpoolState = {
|
||||
id: number;
|
||||
name: string;
|
||||
event: ICarpool['event'];
|
||||
members: { id: number; name: string }[];
|
||||
invitations: Record<number, ICarpool['invitations'][0]>;
|
||||
};
|
||||
|
||||
export const CarpoolContext = createContext({
|
||||
carpool: new CarpoolState(),
|
||||
carpool: {} as CarpoolState,
|
||||
sendInvite: (user: { id: number; name: string }) => {
|
||||
console.error('not implemented: sendInvite');
|
||||
},
|
||||
|
@ -52,36 +35,36 @@ export const CarpoolContext = createContext({
|
|||
});
|
||||
|
||||
export default function Carpool({ id }: { id: number }) {
|
||||
const [carpool, setCarpool] = useState(new CarpoolState());
|
||||
const [carpool, setCarpool] = useImmutable<CarpoolState>({
|
||||
id,
|
||||
name: '',
|
||||
event: {} as ICarpool['event'],
|
||||
members: [],
|
||||
invitations: {},
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
getCarpool(id).then((carpool) => {
|
||||
setCarpool(
|
||||
new CarpoolState({
|
||||
id: carpool.id,
|
||||
name: carpool.name,
|
||||
eventId: carpool.eventId || carpool.event.id,
|
||||
event: carpool.event,
|
||||
members: immutable.List(carpool.members),
|
||||
invitations: immutable.Map(
|
||||
carpool.invitations.map((invite) => [invite.user.id, invite])
|
||||
),
|
||||
})
|
||||
);
|
||||
const invitationsMap: Record<number, ICarpool['invitations'][0]> = {};
|
||||
carpool.invitations.forEach((invite) => {
|
||||
invitationsMap[invite.user.id] = invite;
|
||||
});
|
||||
setCarpool({
|
||||
id: carpool.id,
|
||||
name: carpool.name,
|
||||
event: carpool.event,
|
||||
members: carpool.members,
|
||||
invitations: invitationsMap,
|
||||
});
|
||||
});
|
||||
}, [id]);
|
||||
}, [id, setCarpool]);
|
||||
|
||||
const sendInvite = useCallback(
|
||||
(user: { id: number; name: string }) => {
|
||||
if (carpool) {
|
||||
sendCarpoolInvite(id, user.id)
|
||||
.then(() => {
|
||||
setCarpool((carpool) =>
|
||||
carpool.set(
|
||||
'invitations',
|
||||
carpool.invitations.set(user.id, { isRequest: false, user })
|
||||
)
|
||||
);
|
||||
carpool.invitations[user.id] = { isRequest: false, user };
|
||||
})
|
||||
.catch(console.error);
|
||||
} else {
|
||||
|
@ -97,22 +80,14 @@ export default function Carpool({ id }: { id: number }) {
|
|||
(user: { id: number; name: string }) => {
|
||||
cancelCarpoolInvite(id, user.id)
|
||||
.then(() => {
|
||||
setCarpool(
|
||||
(carpool) =>
|
||||
carpool && {
|
||||
...carpool,
|
||||
invitations: carpool.invitations.filter(
|
||||
(invite) => invite.user.id !== user.id
|
||||
),
|
||||
}
|
||||
);
|
||||
delete carpool.invitations[user.id];
|
||||
})
|
||||
.catch(console.error);
|
||||
},
|
||||
[id]
|
||||
[carpool.invitations, id]
|
||||
);
|
||||
|
||||
const eventId = carpool.eventId;
|
||||
const eventId = carpool.event.id;
|
||||
|
||||
const leave = useCallback(() => {
|
||||
if (eventId) {
|
||||
|
|
|
@ -59,9 +59,8 @@ export default function InvitationList() {
|
|||
const invitedUserIDs = useMemo(
|
||||
() =>
|
||||
new Set(
|
||||
carpool.invitations
|
||||
Object.values(carpool.invitations)
|
||||
.filter((invitation) => !invitation.isRequest)
|
||||
.valueSeq()
|
||||
.map((invitation) => invitation.user.id)
|
||||
),
|
||||
[carpool.invitations]
|
||||
|
|
|
@ -24,7 +24,7 @@ export default function MemberList() {
|
|||
const { leave, carpool } = useContext(CarpoolContext);
|
||||
const members = carpool.members;
|
||||
const membersToShow = members.slice(0, shownMembersCount);
|
||||
const hiddenMemberCount = members.size - membersToShow.size;
|
||||
const hiddenMemberCount = members.length - membersToShow.length;
|
||||
|
||||
const { sendCarpoolRequest, cancelCarpoolRequest } =
|
||||
useContext(NotificationsContext);
|
||||
|
@ -54,7 +54,7 @@ export default function MemberList() {
|
|||
}}
|
||||
>
|
||||
<h3 style={{ marginBlockEnd: '0' }}>Members</h3>
|
||||
{members.size > 0 ? (
|
||||
{members.length > 0 ? (
|
||||
<div style={{ display: 'flex', flexDirection: 'column' }}>
|
||||
{membersToShow.map((member) => (
|
||||
<MemberRow member={member} key={member.id} />
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import * as immutable from 'immutable';
|
||||
import { useCallback, useEffect, useRef, useState } from 'react';
|
||||
import { green, lightgrey } from '../../lib/colors';
|
||||
import {
|
||||
|
@ -14,6 +13,7 @@ import UILink from '../UI/UILink';
|
|||
import UIPlacesAutocomplete from '../UI/UIPlacesAutocomplete';
|
||||
import UISecondaryBox from '../UI/UISecondaryBox';
|
||||
import UISecondaryHeader from '../UI/UISecondaryHeader';
|
||||
import useImmutable from '../useImmutable';
|
||||
import useThrottle from '../useThrottle';
|
||||
import EventCarpools from './EventCarpools';
|
||||
import EventContext from './EventContext';
|
||||
|
@ -49,17 +49,7 @@ export default function Event({
|
|||
});
|
||||
const me = useMe();
|
||||
|
||||
const [tentativeInvites, setTentativeInvites] = useState(
|
||||
immutable.Set<number>()
|
||||
);
|
||||
|
||||
const addTentativeInvite = useCallback((userId: number) => {
|
||||
setTentativeInvites((t) => t.add(userId));
|
||||
}, []);
|
||||
|
||||
const removeTentativeInvite = useCallback((userId: number) => {
|
||||
setTentativeInvites((t) => t.delete(userId));
|
||||
}, []);
|
||||
const [tentativeInvites] = useImmutable<Record<number, boolean>>({});
|
||||
|
||||
const refresh = useCallback(() => {
|
||||
getEvent(id).then(setEvent);
|
||||
|
@ -142,8 +132,6 @@ export default function Event({
|
|||
event,
|
||||
refresh,
|
||||
default: false,
|
||||
addTentativeInvite,
|
||||
removeTentativeInvite,
|
||||
tentativeInvites,
|
||||
signups,
|
||||
hasCarpool,
|
||||
|
|
|
@ -167,7 +167,7 @@ export default function Carpools() {
|
|||
createCarpool({
|
||||
name: me.name + "'s Carpool",
|
||||
eventId: event.id,
|
||||
invitedUserIds: tentativeInvites.toArray(),
|
||||
invitedUserIds: Object.keys(tentativeInvites).map(Number),
|
||||
})
|
||||
.then(({ id }) => {
|
||||
setCreatedCarpoolId(id);
|
||||
|
@ -180,12 +180,11 @@ export default function Carpools() {
|
|||
|
||||
const tentativeInviteNames = useMemo(() => {
|
||||
if (!signups) return [];
|
||||
const names = tentativeInvites.map((id) => {
|
||||
const names = Object.keys(tentativeInvites).map((id) => {
|
||||
const signup = signups[id];
|
||||
return signup?.user.name;
|
||||
});
|
||||
const nonNull = names.filter((n) => n != null);
|
||||
return nonNull.toArray() as string[];
|
||||
return names.filter((n) => n != null);
|
||||
}, [tentativeInvites, signups]);
|
||||
|
||||
let createCarpoolSection;
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import { createContext } from 'react';
|
||||
import { IEvent, IEventSignup } from '../types';
|
||||
import * as immutable from 'immutable';
|
||||
|
||||
const EventContext = createContext({
|
||||
refresh: () => {
|
||||
|
@ -9,13 +8,7 @@ const EventContext = createContext({
|
|||
event: null! as IEvent,
|
||||
default: true,
|
||||
signups: {} as Record<string, IEventSignup>,
|
||||
addTentativeInvite: (id: number) => {
|
||||
console.error('not implemented: addTentativeInvite');
|
||||
},
|
||||
removeTentativeInvite: (id: number) => {
|
||||
console.error('not implemented: removeTentativeInvite');
|
||||
},
|
||||
tentativeInvites: immutable.Set<number>(),
|
||||
tentativeInvites: {} as Record<number, boolean>,
|
||||
hasCarpool: false,
|
||||
setHasCarpool: (has: boolean) => {
|
||||
console.error('not implemented: setHasCarpool');
|
||||
|
|
|
@ -21,12 +21,7 @@ function EventSignup({
|
|||
}) {
|
||||
const { user, latitude, longitude } = signup;
|
||||
const me = useMe();
|
||||
const {
|
||||
addTentativeInvite,
|
||||
removeTentativeInvite,
|
||||
tentativeInvites,
|
||||
hasCarpool,
|
||||
} = useContext(EventContext);
|
||||
const { tentativeInvites, hasCarpool } = useContext(EventContext);
|
||||
|
||||
const extraDistance = useMemo(() => {
|
||||
if (myPlaceDetails != null && !(latitude === null || longitude === null)) {
|
||||
|
@ -66,10 +61,7 @@ function EventSignup({
|
|||
myPlaceDetails,
|
||||
]);
|
||||
|
||||
const isTentativelyInvited = useMemo(
|
||||
() => tentativeInvites.has(signup.user.id),
|
||||
[signup.user.id, tentativeInvites]
|
||||
);
|
||||
const isTentativelyInvited = signup.user.id in tentativeInvites;
|
||||
|
||||
if (user.id === me?.id) {
|
||||
return null;
|
||||
|
@ -98,12 +90,12 @@ function EventSignup({
|
|||
{!hasCarpool &&
|
||||
(isTentativelyInvited ? (
|
||||
<CancelIcon
|
||||
onClick={() => removeTentativeInvite(user.id)}
|
||||
onClick={() => delete tentativeInvites[user.id]}
|
||||
style={{ cursor: 'pointer' }}
|
||||
/>
|
||||
) : (
|
||||
<PersonAddIcon
|
||||
onClick={() => addTentativeInvite(user.id)}
|
||||
onClick={() => (tentativeInvites[user.id] = true)}
|
||||
style={{ cursor: 'pointer' }}
|
||||
/>
|
||||
))}
|
||||
|
|
|
@ -14,24 +14,44 @@ function createEdgeForObject<T extends PlainJSObject>(
|
|||
value: T,
|
||||
setValue: Dispatch<SetStateAction<T>>
|
||||
): T {
|
||||
// @ts-expect-error
|
||||
const edge: T = {};
|
||||
for (let [key, keyValue] of Object.entries(value)) {
|
||||
const set = (next: SetStateAction<typeof keyValue>) => {
|
||||
const v = typeof next === 'function' ? next(keyValue) : next;
|
||||
setValue((value) => ({ ...value, [key]: v }));
|
||||
};
|
||||
return new Proxy(value, {
|
||||
set: (target, property, value) => {
|
||||
setValue((v) => ({ ...v, [property]: value }));
|
||||
|
||||
Object.defineProperty(edge, key, {
|
||||
enumerable: true,
|
||||
configurable: false,
|
||||
get: () => createEdge(keyValue, set),
|
||||
set: (v) => void setValue((value) => ({ ...value, [key]: v })),
|
||||
});
|
||||
}
|
||||
return edge;
|
||||
return true;
|
||||
},
|
||||
// @ts-expect-error
|
||||
get: (target, property: keyof T) => {
|
||||
const keyValue = target[property];
|
||||
const set = (next: SetStateAction<typeof keyValue>) => {
|
||||
const v = typeof next === 'function' ? next(keyValue) : next;
|
||||
setValue((value) => ({ ...value, [property]: v }));
|
||||
};
|
||||
|
||||
return createEdge(keyValue, set);
|
||||
},
|
||||
deleteProperty: (target, property) => {
|
||||
setValue((v) => {
|
||||
const newValue = { ...v };
|
||||
// @ts-ignore
|
||||
delete newValue[property];
|
||||
return newValue;
|
||||
});
|
||||
|
||||
return true;
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
const inPlaceArrayOperations = [
|
||||
'fill',
|
||||
'reverse',
|
||||
'push',
|
||||
'pop',
|
||||
'shift',
|
||||
'unshift',
|
||||
];
|
||||
|
||||
function createEdgeForArray<T extends PlainJS>(
|
||||
value: PlainJSArray<T>,
|
||||
setValue: Dispatch<SetStateAction<PlainJSArray<T>>>
|
||||
|
@ -58,7 +78,8 @@ function createEdgeForArray<T extends PlainJS>(
|
|||
if (typeof property === 'number') {
|
||||
return target[property];
|
||||
} else {
|
||||
if (typeof target[property] === 'function') {
|
||||
// @ts-ignore
|
||||
if (inPlaceArrayOperations.includes(property)) {
|
||||
return function () {
|
||||
const newValue = [...value];
|
||||
const method = newValue[property];
|
||||
|
@ -67,9 +88,15 @@ function createEdgeForArray<T extends PlainJS>(
|
|||
setValue(newValue);
|
||||
return result;
|
||||
};
|
||||
} else {
|
||||
return target[property];
|
||||
} else if (typeof target[property] === 'function') {
|
||||
return function () {
|
||||
console.log(target[property]);
|
||||
// @ts-ignore
|
||||
return target[property].apply(target, arguments);
|
||||
};
|
||||
}
|
||||
|
||||
return target[property];
|
||||
}
|
||||
},
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue
Block a user