all repos — caroster @ 535c63f081f0a6a98429277c875b9f475802e4af

[Octree] Group carpool to your event https://caroster.io

frontend/containers/TravelColumns/index.tsx (view raw)

  1import {useEffect, useMemo, useRef, useState} from 'react';
  2import {makeStyles} from '@material-ui/core/styles';
  3import Container from '@material-ui/core/Container';
  4import Slider from 'react-slick';
  5import {useTranslation} from 'react-i18next';
  6import {
  7  Travel as TravelType,
  8  useUpdateTravelMutation,
  9} from '../../generated/graphql';
 10import useEventStore from '../../stores/useEventStore';
 11import useTourStore from '../../stores/useTourStore';
 12import useToastStore from '../../stores/useToastStore';
 13import useProfile from '../../hooks/useProfile';
 14import useAddToEvents from '../../hooks/useAddToEvents';
 15import {
 16  AddPassengerToTravel,
 17  AddPassengerToWaitingList,
 18} from '../NewPassengerDialog';
 19import WaitingList from '../WaitingList';
 20import Travel from '../Travel';
 21import AddTravel from './AddTravel';
 22import sliderSettings from './_SliderSettings';
 23import usePassengersActions from '../../hooks/usePassengersActions';
 24
 25interface NewPassengerDialogContext {
 26  addSelf: boolean;
 27}
 28
 29interface Props {
 30  toggle: () => void;
 31}
 32
 33const TravelColumns = (props: Props) => {
 34  const event = useEventStore(s => s.event);
 35  const {travels = []} = event || {};
 36  const slider = useRef(null);
 37  const {t} = useTranslation();
 38  const tourStep = useTourStore(s => s.step);
 39  const addToast = useToastStore(s => s.addToast);
 40  const {addToEvent} = useAddToEvents();
 41  const {user} = useProfile();
 42  const classes = useStyles();
 43  const [newPassengerTravelContext, toggleNewPassengerToTravel] = useState<{
 44    travel: TravelType;
 45  } | null>(null);
 46  const [
 47    addPassengerToWaitingListContext,
 48    toggleNewPassengerToWaitingList,
 49  ] = useState<NewPassengerDialogContext | null>(null);
 50  const {addPassengerToTravel} = usePassengersActions();
 51  const sortedTravels = travels?.slice().sort(sortTravels);
 52
 53  const canAddSelf = useMemo(() => {
 54    if (!user) return false;
 55    const isInWaitingList = event?.waitingList?.some(
 56      passenger => passenger.user?.id === `${user.id}`
 57    );
 58    const isInTravel = event?.travels.some(travel =>
 59      travel.passengers.some(passenger => passenger.user?.id === `${user.id}`)
 60    );
 61    return !(isInWaitingList || isInTravel);
 62  }, [event, user]);
 63
 64  const addSelfToTravel = async (travel: TravelType) => {
 65    const passenger = {
 66      user: user,
 67      email: user.email,
 68      name: user.username,
 69    };
 70
 71    return addPassengerToTravel({
 72      passenger,
 73      travel,
 74      onSucceed: () => {
 75        addToEvent(event.id);
 76        addToast(t('passenger.success.added_self_to_car'));
 77      },
 78    });
 79  };
 80
 81  const slideToTravel = (travelId: string) => {
 82    const travelIndex = sortedTravels.findIndex(
 83      travel => travel.id === travelId
 84    );
 85    const slideIndex = travelIndex + 1;
 86    slider.current.slickGoTo(slideIndex);
 87  };
 88
 89  // On tour step changes : component update
 90  useEffect(() => {
 91    onTourChange(slider.current);
 92  }, [tourStep]);
 93
 94  return (
 95    <div className={classes.container}>
 96      <div className={classes.dots} id="slider-dots" />
 97      <div className={classes.slider}>
 98        <Slider ref={slider} {...sliderSettings}>
 99          <Container maxWidth="sm" className={classes.slide}>
100            <WaitingList
101              slideToTravel={slideToTravel}
102              canAddSelf={canAddSelf}
103              getToggleNewPassengerDialogFunction={(addSelf: boolean) => () =>
104                toggleNewPassengerToWaitingList({addSelf})}
105            />
106          </Container>
107          {sortedTravels?.map(travel => (
108            <Container key={travel.id} maxWidth="sm" className={classes.slide}>
109              <Travel
110                travel={travel}
111                {...props}
112                canAddSelf={canAddSelf}
113                getAddPassengerFunction={(addSelf: boolean) => () => {
114                  if (addSelf) {
115                    return addSelfToTravel(travel);
116                  } else {
117                    return toggleNewPassengerToTravel({travel});
118                  }
119                }}
120              />
121            </Container>
122          ))}
123          <Container maxWidth="sm" className={classes.slide}>
124            <AddTravel {...props} />
125          </Container>
126        </Slider>
127      </div>
128      {!!newPassengerTravelContext && (
129        <AddPassengerToTravel
130          open={!!newPassengerTravelContext}
131          toggle={() => toggleNewPassengerToTravel(null)}
132          travel={newPassengerTravelContext.travel}
133        />
134      )}
135      {!!addPassengerToWaitingListContext && (
136        <AddPassengerToWaitingList
137          open={!!addPassengerToWaitingListContext}
138          toggle={() => toggleNewPassengerToWaitingList(null)}
139          addSelf={addPassengerToWaitingListContext.addSelf}
140        />
141      )}
142    </div>
143  );
144};
145
146const onTourChange = slider => {
147  const {prev, step, isCreator} = useTourStore.getState();
148  const fromTo = (step1: number, step2: number) =>
149    prev === step1 && step === step2;
150
151  if (isCreator) {
152    if (fromTo(2, 3) || fromTo(4, 3)) slider?.slickGoTo(0, true);
153  } else if (fromTo(0, 1)) slider?.slickGoTo(0, true);
154};
155
156const sortTravels = (a: TravelType, b: TravelType) => {
157  if (!b) return 1;
158  const dateA = new Date(a.departure).getTime();
159  const dateB = new Date(b.departure).getTime();
160  if (dateA === dateB)
161    return new Date(a.created_at).getTime() - new Date(b.created_at).getTime();
162  else return dateA - dateB;
163};
164
165const useStyles = makeStyles(theme => ({
166  container: {
167    minHeight: '100vh',
168    paddingTop: theme.mixins.toolbar.minHeight,
169    paddingLeft: theme.spacing(6),
170    paddingRight: theme.spacing(6),
171    [theme.breakpoints.down('sm')]: {
172      paddingLeft: theme.spacing(),
173      paddingRight: theme.spacing(),
174    },
175    display: 'flex',
176    flexDirection: 'column',
177    overflowX: 'hidden',
178    overflowY: 'auto',
179  },
180  dots: {
181    height: 32,
182    overflow: 'auto',
183    '& overflow': '-moz-scrollbars-none',
184    '-ms-overflow-style': 'none',
185    '&::-webkit-scrollbar': {
186      height: '0 !important',
187    },
188    '& .slick-dots': {
189      position: 'static',
190      '& li': {
191        display: 'block',
192      },
193    },
194    '& .slick-dots li:first-child button:before, & .slick-dots li:last-child button:before': {
195      color: theme.palette.primary.main,
196    },
197  },
198  slider: {
199    flexGrow: 1,
200    height: 1,
201    '& .slick-slider': {
202      height: '100%',
203      '& .slick-list': {
204        overflow: 'visible',
205      },
206      cursor: 'grab',
207    },
208  },
209  slide: {
210    padding: theme.spacing(1),
211    marginBottom: theme.spacing(12),
212    outline: 'none',
213    '& > *': {
214      cursor: 'default',
215    },
216  },
217}));
218
219export default TravelColumns;