From 0ca7a385d5bef3ba8228336ade59d9b1c16945f4 Mon Sep 17 00:00:00 2001 From: Jimmy Domagala-Tang Date: Tue, 30 Apr 2024 12:30:59 -0700 Subject: [PATCH] Merge pull request #18131 from overleaf/jdt-promo-hooks feat: split logic for promos out to hooks GitOrigin-RevId: 8f713cdf309f84dddb20e8da76009512bd990a8f --- .../shared/hooks/promotions/use-tutorial.tsx | 85 +++++++++++++++++++ 1 file changed, 85 insertions(+) create mode 100644 services/web/frontend/js/shared/hooks/promotions/use-tutorial.tsx diff --git a/services/web/frontend/js/shared/hooks/promotions/use-tutorial.tsx b/services/web/frontend/js/shared/hooks/promotions/use-tutorial.tsx new file mode 100644 index 0000000000..5d98de682e --- /dev/null +++ b/services/web/frontend/js/shared/hooks/promotions/use-tutorial.tsx @@ -0,0 +1,85 @@ +import { useCallback, useState } from 'react' +import * as eventTracking from '@/infrastructure/event-tracking' +import { postJSON } from '@/infrastructure/fetch-json' +import { debugConsole } from '@/utils/debugging' +import { useEditorContext } from '@/shared/context/editor-context' + +export const useTutorial = ( + tutorialKey: string, + eventData: Record = {} +) => { + const [showPopup, setShowPopup] = useState(false) + + const { deactivateTutorial, currentPopup, setCurrentPopup } = + useEditorContext() + + const completeTutorial = useCallback( + async ({ + event = 'promo-click', + action = 'complete', + ...rest + }: { + event: 'promo-click' | 'promo-dismiss' + action: 'complete' | 'postpone' + } & Record) => { + eventTracking.sendMB(event, { ...eventData, ...rest }) + try { + await postJSON(`/tutorial/${tutorialKey}/${action}`) + } catch (err) { + debugConsole.error(err) + } + setShowPopup(false) + deactivateTutorial(tutorialKey) + }, + [deactivateTutorial, eventData, tutorialKey] + ) + + const dismissTutorial = useCallback(async () => { + await completeTutorial({ + event: 'promo-dismiss', + action: 'complete', + }) + }, [completeTutorial]) + + const maybeLater = useCallback(async () => { + await completeTutorial({ + event: 'promo-click', + action: 'postpone', + button: 'maybe-later', + }) + }, [completeTutorial]) + + // try to show the popup if we don't already have one showing, returns true if it can show, false if it can't + const tryShowingPopup = useCallback(() => { + if (currentPopup === null) { + setCurrentPopup(tutorialKey) + setShowPopup(true) + eventTracking.sendMB('promo-prompt', eventData) + return true + } + return false + }, [currentPopup, setCurrentPopup, tutorialKey, eventData]) + + const clearPopup = useCallback( + (force: boolean = false) => { + // popups should only clear themselves, in cases they need to cleanup or shouldnt show anymore + // allow forcing the clear if needed, eg: higher prio alert needs to show + if (force || currentPopup === tutorialKey) { + setCurrentPopup(null) + setShowPopup(false) + } + }, + [setCurrentPopup, setShowPopup, currentPopup, tutorialKey] + ) + + return { + completeTutorial, + dismissTutorial, + maybeLater, + tryShowingPopup, + clearPopup, + showPopup, + } +} + +export default useTutorial