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, isReady} = 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 (!isReady || !event) return;
43
44 let newIsCreator = eventsToBeAdded.includes(event?.id);
45 if (profile) newIsCreator = profile.events.map(e => e.id).includes(event?.id);
46
47 setTour({isCreator: newIsCreator});
48 }, [isReady, event, eventsToBeAdded, profile]);
49
50 const steps = useMemo(() => {
51 if (isCreator === null) return [];
52 return isCreator
53 ? [
54 {content: t`tour.creator.step1`, target: '.tour_event_infos'},
55 {content: t`tour.creator.step2`, target: '.tour_event_edit'},
56 {content: t`tour.creator.step3`, target: '.tour_event_share'},
57 {content: t`tour.creator.step4`, target: '.tour_waiting_list'},
58 {content: t`tour.creator.step5`, target: '.tour_travel_add'},
59 ].map(currentStep => ({...currentStep, ...STEP_SETTINGS}))
60 : [
61 {content: t`tour.user.step1`, target: '.tour_travel_add'},
62 {content: t`tour.user.step2`, target: '.tour_waiting_list'},
63 {content: t`tour.user.step3`, target: '.tour_event_infos'},
64 {content: t`tour.user.step4`, target: '.tour_event_share'},
65 ].map(currentStep => ({...currentStep, ...STEP_SETTINGS}));
66 }, [isCreator]);
67
68 // On step change : wait for the UI a little and run it
69 useEffect(() => {
70 let timer;
71 if (step >= 0 && step !== prev)
72 timer = setTimeout(() => setTour({run: true}), 250);
73 return () => clearTimeout(timer);
74 }, [step]);
75
76 const onFinish = () => {
77 if (profile) {
78 if (isCreator && !profile.onboardingCreator)
79 updateProfile({variables: {userUpdate: {onboardingCreator: true}}});
80 else if (!isCreator && !profile.onboardingUser)
81 updateProfile({variables: {userUpdate: {onboardingUser: true}}});
82 } else {
83 if (isCreator && !onboardingCreator)
84 setOnboarding({onboardingCreator: true});
85 else if (!isCreator && !onboardingUser)
86 setOnboarding({onboardingUser: true});
87 }
88 };
89
90 const onTourChange = (data: CallBackProps) => {
91 const {action, index, type, status} = data;
92
93 if (
94 ([EVENTS.STEP_AFTER, EVENTS.TARGET_NOT_FOUND] as string[]).includes(type)
95 ) {
96 if (action === ACTIONS.CLOSE) {
97 setTour({run: false, step: -1, prev: -1});
98 } else {
99 setTour({
100 run: false,
101 step: index + (action === ACTIONS.PREV ? -1 : 1),
102 prev: index,
103 });
104 }
105 } else if (
106 ([STATUS.FINISHED, STATUS.SKIPPED] as string[]).includes(status)
107 ) {
108 setTour({run: false, step: -1, prev: -1});
109 if (status === STATUS.FINISHED) onFinish();
110 }
111 };
112
113 return {
114 run,
115 steps,
116 step,
117 onTourChange,
118 };
119};
120
121export default useTour;