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