all repos — caroster @ 9e700046837e1356f38ebf38303313c053368149

[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        ].map(step => ({...step, ...STEP_SETTINGS}))
 61      : [
 62          {content: t`tour.user.step1`, target: '.tour_car_add1'},
 63          {content: t`tour.user.step2`, target: '.tour_waiting_list'},
 64          {content: t`tour.user.step3`, target: '.tour_event_infos'},
 65          {content: t`tour.user.step4`, target: '.tour_event_share'},
 66        ].map(step => ({...step, ...STEP_SETTINGS}));
 67  }, [isCreator]);
 68
 69  // Init tour
 70  useEffect(() => {
 71    const hasOnboarded = () => {
 72      if (isCreator) return profile?.onboardingCreator || onboardingCreator;
 73      else return profile?.onboardingUser || onboardingUser;
 74    };
 75    if (!hasOnboarded() && steps.length > 0) setTour({showWelcome: true});
 76    else setTour({showWelcome: false});
 77  }, [steps, isCreator, onboardingCreator, onboardingUser, profile]);
 78
 79  // On step change : wait for the UI a little and run it
 80  useEffect(() => {
 81    let timer;
 82    if (step >= 0 && step !== prev)
 83      timer = setTimeout(() => setTour({run: true}), 250);
 84    return () => clearTimeout(timer);
 85  }, [step]);
 86
 87  const onFinish = () => {
 88    if (profile) {
 89      if (isCreator && !profile.onboardingCreator)
 90        updateProfile({variables: {userUpdate: {onboardingCreator: true}}});
 91      else if (!isCreator && !profile.onboardingUser)
 92        updateProfile({variables: {userUpdate: {onboardingUser: true}}});
 93    } else {
 94      if (isCreator && !onboardingCreator)
 95        setOnboarding({onboardingCreator: true});
 96      else if (!isCreator && !onboardingUser)
 97        setOnboarding({onboardingUser: true});
 98    }
 99  };
100
101  const onTourChange = (data: CallBackProps) => {
102    const {action, index, type, status} = data;
103
104    if (
105      ([EVENTS.STEP_AFTER, EVENTS.TARGET_NOT_FOUND] as string[]).includes(type)
106    ) {
107      if (action === ACTIONS.CLOSE) {
108        setTour({run: false, step: -1, prev: -1});
109      } else {
110        setTour({
111          run: false,
112          step: index + (action === ACTIONS.PREV ? -1 : 1),
113          prev: index,
114        });
115      }
116    } else if (
117      ([STATUS.FINISHED, STATUS.SKIPPED] as string[]).includes(status)
118    ) {
119      setTour({run: false, step: -1, prev: -1});
120      if (status === STATUS.FINISHED) onFinish();
121    }
122  };
123
124  return {
125    run,
126    steps,
127    step,
128    onTourChange,
129  };
130};
131
132export default useTour;