Skip to content

Commit 7851019

Browse files
leeandherandrewshie-sentry
authored andcommitted
ref(issues): Extract modal into hook, allow local dismissal (#89741)
For whatever reason, it's possible that users do not create assistant records, which will cause annoying behaviour like repeatedly popping the tour modal. I'm guessing this has something to do with an ad blocker, or `/assistant/` being flagged on their network or something oddly specific. Either way, to make it a bit more resilient, we can add a back up check with local storage. This should prevent any further issues of repeated modals since only super users can ever reset this local storage value in-app. The component was also getting pretty confusing, so pulled it all into its own hook.
1 parent edd3c8d commit 7851019

File tree

1 file changed

+92
-51
lines changed

1 file changed

+92
-51
lines changed

static/app/views/issueDetails/actions/newIssueExperienceButton.tsx

+92-51
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import {t} from 'sentry/locale';
1212
import {trackAnalytics} from 'sentry/utils/analytics';
1313
import {isActiveSuperuser} from 'sentry/utils/isActiveSuperuser';
1414
import {useFeedbackForm} from 'sentry/utils/useFeedbackForm';
15+
import {useLocalStorageState} from 'sentry/utils/useLocalStorageState';
1516
import useMutateUserOptions from 'sentry/utils/useMutateUserOptions';
1617
import useOrganization from 'sentry/utils/useOrganization';
1718
import {useUser} from 'sentry/utils/useUser';
@@ -25,17 +26,104 @@ import {
2526
} from 'sentry/views/issueDetails/issueDetailsTourModal';
2627
import {useHasStreamlinedUI} from 'sentry/views/issueDetails/utils';
2728

29+
/**
30+
* This hook will cause the promotional modal to appear if:
31+
* - All the steps have been registered
32+
* - The tour has not been completed
33+
* - The tour is not currently active
34+
* - The streamline UI is enabled
35+
* - The user's browser has not stored that they've seen the promo
36+
*
37+
* Returns a function that can be used to reset the modal.
38+
*/
39+
export function useIssueDetailsPromoModal() {
40+
const organization = useOrganization();
41+
const hasStreamlinedUI = useHasStreamlinedUI();
42+
const {mutate: mutateAssistant} = useMutateAssistant();
43+
const {
44+
startTour,
45+
endTour,
46+
currentStepId,
47+
isRegistered: isTourRegistered,
48+
isCompleted: isTourCompleted,
49+
} = useIssueDetailsTour();
50+
51+
const [localTourState, setLocalTourState] = useLocalStorageState(
52+
ISSUE_DETAILS_TOUR_GUIDE_KEY,
53+
{hasSeen: false}
54+
);
55+
56+
const isPromoVisible =
57+
isTourRegistered &&
58+
!isTourCompleted &&
59+
currentStepId === null &&
60+
hasStreamlinedUI &&
61+
!localTourState.hasSeen;
62+
63+
const handleEndTour = useCallback(() => {
64+
setLocalTourState({hasSeen: true});
65+
mutateAssistant({guide: ISSUE_DETAILS_TOUR_GUIDE_KEY, status: 'dismissed'});
66+
endTour();
67+
trackAnalytics('issue_details.tour.skipped', {organization});
68+
}, [mutateAssistant, organization, endTour, setLocalTourState]);
69+
70+
useEffect(() => {
71+
if (isPromoVisible) {
72+
openModal(
73+
props => (
74+
<IssueDetailsTourModal
75+
handleDismissTour={() => {
76+
handleEndTour();
77+
props.closeModal();
78+
}}
79+
handleStartTour={() => {
80+
props.closeModal();
81+
setLocalTourState({hasSeen: true});
82+
startTour();
83+
trackAnalytics('issue_details.tour.started', {
84+
organization,
85+
method: 'modal',
86+
});
87+
}}
88+
/>
89+
),
90+
{
91+
modalCss: IssueDetailsTourModalCss,
92+
onClose: reason => {
93+
if (reason) {
94+
handleEndTour();
95+
}
96+
},
97+
}
98+
);
99+
}
100+
}, [
101+
isPromoVisible,
102+
mutateAssistant,
103+
organization,
104+
endTour,
105+
startTour,
106+
setLocalTourState,
107+
handleEndTour,
108+
]);
109+
110+
const resetModal = useCallback(() => {
111+
setLocalTourState({hasSeen: false});
112+
mutateAssistant({guide: ISSUE_DETAILS_TOUR_GUIDE_KEY, status: 'restart'});
113+
}, [mutateAssistant, setLocalTourState]);
114+
115+
return {resetModal};
116+
}
117+
28118
export function NewIssueExperienceButton() {
29119
const organization = useOrganization();
30120
const isSuperUser = isActiveSuperuser();
31121
const {
32-
endTour,
33122
startTour,
34-
currentStepId,
35123
isRegistered: isTourRegistered,
36124
isCompleted: isTourCompleted,
37125
} = useIssueDetailsTour();
38-
const {mutate: mutateAssistant} = useMutateAssistant();
126+
const {resetModal} = useIssueDetailsPromoModal();
39127

40128
// XXX: We use a ref to track the previous state of tour completion
41129
// since we only show the banner when the tour goes from incomplete to complete
@@ -75,51 +163,6 @@ export function NewIssueExperienceButton() {
75163
});
76164
}, [mutateUserOptions, organization, hasStreamlinedUI, userStreamlinePreference]);
77165

78-
// The promotional modal should only appear if:
79-
// - All the steps have been registered
80-
// - The tour has not been completed
81-
// - The tour is not currently active
82-
// - The streamline UI is enabled
83-
const isPromoVisible =
84-
isTourRegistered && !isTourCompleted && currentStepId === null && hasStreamlinedUI;
85-
86-
useEffect(() => {
87-
if (isPromoVisible) {
88-
openModal(
89-
props => (
90-
<IssueDetailsTourModal
91-
handleDismissTour={() => {
92-
mutateAssistant({guide: ISSUE_DETAILS_TOUR_GUIDE_KEY, status: 'dismissed'});
93-
endTour();
94-
trackAnalytics('issue_details.tour.skipped', {organization});
95-
props.closeModal();
96-
}}
97-
handleStartTour={() => {
98-
props.closeModal();
99-
startTour();
100-
trackAnalytics('issue_details.tour.started', {
101-
organization,
102-
method: 'modal',
103-
});
104-
}}
105-
/>
106-
),
107-
{
108-
modalCss: IssueDetailsTourModalCss,
109-
onClose: reason => {
110-
if (reason) {
111-
mutateAssistant({
112-
guide: ISSUE_DETAILS_TOUR_GUIDE_KEY,
113-
status: 'dismissed',
114-
});
115-
endTour();
116-
}
117-
},
118-
}
119-
);
120-
}
121-
}, [isPromoVisible, mutateAssistant, organization, endTour, startTour]);
122-
123166
if (!hasStreamlinedUI) {
124167
return (
125168
<TryNewButton
@@ -173,9 +216,7 @@ export function NewIssueExperienceButton() {
173216
key: 'reset-tour-modal',
174217
label: t('Reset tour modal (Superuser only)'),
175218
hidden: !isSuperUser || !isTourCompleted,
176-
onAction: () => {
177-
mutateAssistant({guide: ISSUE_DETAILS_TOUR_GUIDE_KEY, status: 'restart'});
178-
},
219+
onAction: resetModal,
179220
},
180221
];
181222

0 commit comments

Comments
 (0)