all repos — caroster @ 3624031597790e3743c218dcf0222a43e8965e49

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

frontend/hooks/useTour.ts (view raw)

  1import {useEffect, useMemo} from 'react';
  2import {useTranslation} from 'react-i18next';
  3import {CallBackProps, STATUS, EVENTS, ACTIONS} from 'react-joyride';
  4import {useUpdateMeMutation} from '../generated/graphql';
  5import useOnboardingStore from '../stores/useOnboardingStore';
  6import useTourStore from '../stores/useTourStore';
  7import useEventStore from '../stores/useEventStore';
  8import useAddToEvents from '../hooks/useAddToEvents';
  9import useProfile from './useProfile';
 10
 11const STEP_SETTINGS = {
 12  disableBeacon: true,
 13  disableOverlayClose: true,
 14  hideCloseButton: true,
 15  hideFooter: false,
 16  spotlightClicks: false,
 17  showSkipButton: true,
 18  styles: {
 19    options: {
 20      zIndex: 10000,
 21    },
 22  },
 23};
 24
 25const useTour = () => {
 26  const {t} = useTranslation();
 27  const isCreator = useTourStore(s => s.isCreator);
 28  const run = useTourStore(s => s.run);
 29  const step = useTourStore(s => s.step);
 30  const prev = useTourStore(s => s.prev);
 31  const setTour = useTourStore(s => s.setTour);
 32  const onboardingUser = useOnboardingStore(s => s.onboardingUser);
 33  const onboardingCreator = useOnboardingStore(s => s.onboardingCreator);
 34  const setOnboarding = useOnboardingStore(s => s.setOnboarding);
 35  const {profile, notReady} = useProfile();
 36  const event = useEventStore(s => s.event);
 37  const {eventsToBeAdded} = useAddToEvents();
 38  const [updateProfile] = useUpdateMeMutation();
 39
 40  // Check if user is the event creator
 41  useEffect(() => {
 42    if (notReady || !event) return;
 43
 44    if (profile) {
 45      setTour({isCreator: profile.events.map(e => e.id).includes(event?.id)});
 46    } else {
 47      setTour({isCreator: eventsToBeAdded.includes(event?.id)});
 48    }
 49  }, [notReady, event, eventsToBeAdded, profile]);
 50
 51  const steps = useMemo(() => {
 52    if (isCreator === null) return [];
 53    return isCreator
 54      ? [
 55          {content: t`tour.creator.step1`, target: '.tour_event_infos'},
 56          {content: t`tour.creator.step2`, target: '.tour_event_edit'},
 57          {content: t`tour.creator.step3`, target: '.tour_event_share'},
 58          {content: t`tour.creator.step4`, target: '.tour_waiting_list'},
 59          {content: t`tour.creator.step5`, target: '.tour_car_add1'},
 60          {content: t`tour.creator.step6`, target: '.tour_car_add2'},
 61        ].map(step => ({...step, ...STEP_SETTINGS}))
 62      : [
 63          {content: t`tour.user.step1`, target: '.tour_car_add2'},
 64          {content: t`tour.user.step2`, target: '.tour_car_add1'},
 65          {content: t`tour.user.step3`, target: '.tour_waiting_list'},
 66          {content: t`tour.user.step4`, target: '.tour_event_infos'},
 67          {content: t`tour.user.step5`, target: '.tour_event_share'},
 68        ].map(step => ({...step, ...STEP_SETTINGS}));
 69  }, [isCreator]);
 70
 71  // Init tour
 72  useEffect(() => {
 73    const hasOnboarded = () => {
 74      if (isCreator) return profile?.onboardingCreator || onboardingCreator;
 75      else return profile?.onboardingUser || onboardingUser;
 76    };
 77    if (!hasOnboarded() && steps.length > 0) setTour({showWelcome: true});
 78    else setTour({showWelcome: false});
 79  }, [steps, isCreator, onboardingCreator, onboardingUser, profile]);
 80
 81  // On step change : wait for the UI a little and run it
 82  useEffect(() => {
 83    let timer;
 84    if (step >= 0 && step !== prev)
 85      timer = setTimeout(() => setTour({run: true}), 250);
 86    return () => clearTimeout(timer);
 87  }, [step]);
 88
 89  const onFinish = () => {
 90    if (profile) {
 91      if (isCreator && !profile.onboardingCreator)
 92        updateProfile({variables: {userUpdate: {onboardingCreator: true}}});
 93      else if (!isCreator && !profile.onboardingUser)
 94        updateProfile({variables: {userUpdate: {onboardingUser: true}}});
 95    } else {
 96      if (isCreator && !onboardingCreator)
 97        setOnboarding({onboardingCreator: true});
 98      else if (!isCreator && !onboardingUser)
 99        setOnboarding({onboardingUser: true});
100    }
101  };
102
103  const onTourRestart = () => {
104    setTour({showWelcome: true});
105  };
106
107  const onTourChange = (data: CallBackProps) => {
108    const {action, index, type, status} = data;
109
110    if (
111      ([EVENTS.STEP_AFTER, EVENTS.TARGET_NOT_FOUND] as string[]).includes(type)
112    ) {
113      if (action === ACTIONS.CLOSE) {
114        setTour({run: false, step: -1, prev: -1});
115      } else {
116        setTour({
117          run: false,
118          step: index + (action === ACTIONS.PREV ? -1 : 1),
119          prev: index,
120        });
121      }
122    } else if (
123      ([STATUS.FINISHED, STATUS.SKIPPED] as string[]).includes(status)
124    ) {
125      setTour({run: false, step: -1, prev: -1});
126      if (status === STATUS.FINISHED) onFinish();
127    }
128  };
129
130  return {
131    run,
132    steps,
133    step,
134    onTourChange,
135    onTourRestart,
136  };
137};
138
139export default useTour;