use-tutorial.tsx 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101
  1. import { useCallback, useState } from 'react'
  2. import * as eventTracking from '@/infrastructure/event-tracking'
  3. import { postJSON } from '@/infrastructure/fetch-json'
  4. import { debugConsole } from '@/utils/debugging'
  5. import { useEditorContext } from '@/shared/context/editor-context'
  6. export const useTutorial = (
  7. tutorialKey: string,
  8. eventData: Record<string, any> = {}
  9. ) => {
  10. const [showPopup, setShowPopup] = useState(false)
  11. const { deactivateTutorial, currentPopup, setCurrentPopup } =
  12. useEditorContext()
  13. const completeTutorial = useCallback(
  14. async ({
  15. event = 'promo-click',
  16. action = 'complete',
  17. ...rest
  18. }: {
  19. event: 'promo-click' | 'promo-dismiss'
  20. action: 'complete' | 'postpone'
  21. } & Record<string, any>) => {
  22. eventTracking.sendMB(event, { ...eventData, ...rest })
  23. try {
  24. await postJSON(`/tutorial/${tutorialKey}/${action}`)
  25. } catch (err) {
  26. debugConsole.error(err)
  27. }
  28. setShowPopup(false)
  29. deactivateTutorial(tutorialKey)
  30. },
  31. [deactivateTutorial, eventData, tutorialKey]
  32. )
  33. const dismissTutorial = useCallback(async () => {
  34. await completeTutorial({
  35. event: 'promo-dismiss',
  36. action: 'complete',
  37. })
  38. }, [completeTutorial])
  39. const maybeLater = useCallback(async () => {
  40. await completeTutorial({
  41. event: 'promo-click',
  42. action: 'postpone',
  43. button: 'maybe-later',
  44. })
  45. }, [completeTutorial])
  46. // try to show the popup if we don't already have one showing, returns true if it can show, false if it can't
  47. const tryShowingPopup = useCallback(
  48. (eventName: string = 'promo-prompt') => {
  49. if (currentPopup === null) {
  50. setCurrentPopup(tutorialKey)
  51. setShowPopup(true)
  52. eventTracking.sendMB(eventName, eventData)
  53. return true
  54. }
  55. return false
  56. },
  57. [currentPopup, setCurrentPopup, tutorialKey, eventData]
  58. )
  59. const clearPopup = useCallback(() => {
  60. // popups should only clear themselves, in cases they need to cleanup or shouldnt show anymore
  61. // allow forcing the clear if needed, eg: higher prio alert needs to show
  62. if (currentPopup === tutorialKey) {
  63. setCurrentPopup(null)
  64. setShowPopup(false)
  65. }
  66. }, [setCurrentPopup, setShowPopup, currentPopup, tutorialKey])
  67. const clearAndShow = useCallback(
  68. (eventName: string = 'promo-prompt') => {
  69. setCurrentPopup(tutorialKey)
  70. setShowPopup(true)
  71. eventTracking.sendMB(eventName, eventData)
  72. },
  73. [setCurrentPopup, setShowPopup, tutorialKey, eventData]
  74. )
  75. const hideUntilReload = useCallback(() => {
  76. clearPopup()
  77. deactivateTutorial(tutorialKey)
  78. }, [clearPopup, deactivateTutorial, tutorialKey])
  79. return {
  80. completeTutorial,
  81. dismissTutorial,
  82. maybeLater,
  83. tryShowingPopup,
  84. clearPopup,
  85. clearAndShow,
  86. showPopup,
  87. hideUntilReload,
  88. }
  89. }
  90. export default useTutorial