diff --git a/static/app/views/issueDetails/actions/newIssueExperienceButton.tsx b/static/app/views/issueDetails/actions/newIssueExperienceButton.tsx index af2f9c23793999..3cd81035c164a3 100644 --- a/static/app/views/issueDetails/actions/newIssueExperienceButton.tsx +++ b/static/app/views/issueDetails/actions/newIssueExperienceButton.tsx @@ -12,6 +12,7 @@ import {t} from 'sentry/locale'; import {trackAnalytics} from 'sentry/utils/analytics'; import {isActiveSuperuser} from 'sentry/utils/isActiveSuperuser'; import {useFeedbackForm} from 'sentry/utils/useFeedbackForm'; +import {useLocalStorageState} from 'sentry/utils/useLocalStorageState'; import useMutateUserOptions from 'sentry/utils/useMutateUserOptions'; import useOrganization from 'sentry/utils/useOrganization'; import {useUser} from 'sentry/utils/useUser'; @@ -25,17 +26,104 @@ import { } from 'sentry/views/issueDetails/issueDetailsTourModal'; import {useHasStreamlinedUI} from 'sentry/views/issueDetails/utils'; +/** + * This hook will cause the promotional modal to appear if: + * - All the steps have been registered + * - The tour has not been completed + * - The tour is not currently active + * - The streamline UI is enabled + * - The user's browser has not stored that they've seen the promo + * + * Returns a function that can be used to reset the modal. + */ +export function useIssueDetailsPromoModal() { + const organization = useOrganization(); + const hasStreamlinedUI = useHasStreamlinedUI(); + const {mutate: mutateAssistant} = useMutateAssistant(); + const { + startTour, + endTour, + currentStepId, + isRegistered: isTourRegistered, + isCompleted: isTourCompleted, + } = useIssueDetailsTour(); + + const [localTourState, setLocalTourState] = useLocalStorageState( + ISSUE_DETAILS_TOUR_GUIDE_KEY, + {hasSeen: false} + ); + + const isPromoVisible = + isTourRegistered && + !isTourCompleted && + currentStepId === null && + hasStreamlinedUI && + !localTourState.hasSeen; + + const handleEndTour = useCallback(() => { + setLocalTourState({hasSeen: true}); + mutateAssistant({guide: ISSUE_DETAILS_TOUR_GUIDE_KEY, status: 'dismissed'}); + endTour(); + trackAnalytics('issue_details.tour.skipped', {organization}); + }, [mutateAssistant, organization, endTour, setLocalTourState]); + + useEffect(() => { + if (isPromoVisible) { + openModal( + props => ( + { + handleEndTour(); + props.closeModal(); + }} + handleStartTour={() => { + props.closeModal(); + setLocalTourState({hasSeen: true}); + startTour(); + trackAnalytics('issue_details.tour.started', { + organization, + method: 'modal', + }); + }} + /> + ), + { + modalCss: IssueDetailsTourModalCss, + onClose: reason => { + if (reason) { + handleEndTour(); + } + }, + } + ); + } + }, [ + isPromoVisible, + mutateAssistant, + organization, + endTour, + startTour, + setLocalTourState, + handleEndTour, + ]); + + const resetModal = useCallback(() => { + setLocalTourState({hasSeen: false}); + mutateAssistant({guide: ISSUE_DETAILS_TOUR_GUIDE_KEY, status: 'restart'}); + }, [mutateAssistant, setLocalTourState]); + + return {resetModal}; +} + export function NewIssueExperienceButton() { const organization = useOrganization(); const isSuperUser = isActiveSuperuser(); const { - endTour, startTour, - currentStepId, isRegistered: isTourRegistered, isCompleted: isTourCompleted, } = useIssueDetailsTour(); - const {mutate: mutateAssistant} = useMutateAssistant(); + const {resetModal} = useIssueDetailsPromoModal(); // XXX: We use a ref to track the previous state of tour completion // since we only show the banner when the tour goes from incomplete to complete @@ -75,51 +163,6 @@ export function NewIssueExperienceButton() { }); }, [mutateUserOptions, organization, hasStreamlinedUI, userStreamlinePreference]); - // The promotional modal should only appear if: - // - All the steps have been registered - // - The tour has not been completed - // - The tour is not currently active - // - The streamline UI is enabled - const isPromoVisible = - isTourRegistered && !isTourCompleted && currentStepId === null && hasStreamlinedUI; - - useEffect(() => { - if (isPromoVisible) { - openModal( - props => ( - { - mutateAssistant({guide: ISSUE_DETAILS_TOUR_GUIDE_KEY, status: 'dismissed'}); - endTour(); - trackAnalytics('issue_details.tour.skipped', {organization}); - props.closeModal(); - }} - handleStartTour={() => { - props.closeModal(); - startTour(); - trackAnalytics('issue_details.tour.started', { - organization, - method: 'modal', - }); - }} - /> - ), - { - modalCss: IssueDetailsTourModalCss, - onClose: reason => { - if (reason) { - mutateAssistant({ - guide: ISSUE_DETAILS_TOUR_GUIDE_KEY, - status: 'dismissed', - }); - endTour(); - } - }, - } - ); - } - }, [isPromoVisible, mutateAssistant, organization, endTour, startTour]); - if (!hasStreamlinedUI) { return ( { - mutateAssistant({guide: ISSUE_DETAILS_TOUR_GUIDE_KEY, status: 'restart'}); - }, + onAction: resetModal, }, ];