Skip to content

Commit ab414b4

Browse files
committed
dont show this again
1 parent 3bd7da5 commit ab414b4

4 files changed

Lines changed: 143 additions & 28 deletions

File tree

packages/shared/src/components/filters/IntroQuestButton.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,9 @@ export function IntroQuestButton(): ReactElement | null {
3535
introQuests.length > 0 &&
3636
introQuests.every(({ status }) => status === QuestStatus.Claimed);
3737
const hasViewedIntroQuests = checkHasCompleted(ActionType.ViewedIntroQuests);
38+
const hasCompletedIntroQuests = checkHasCompleted(
39+
ActionType.IntroQuestsCompleted,
40+
);
3841
const [isIntroCtaVisible, setIsIntroCtaVisible] = useState(false);
3942
const hasShownIntroCta = useRef(false);
4043
const shouldRenderButton =
@@ -43,7 +46,8 @@ export function IntroQuestButton(): ReactElement | null {
4346
isLoggedIn &&
4447
isNewD1Experience &&
4548
introQuests.length > 0 &&
46-
!allIntroQuestsClaimed;
49+
!allIntroQuestsClaimed &&
50+
!hasCompletedIntroQuests;
4751
const showIntroCta =
4852
shouldRenderButton && (!hasShownIntroCta.current || isIntroCtaVisible);
4953

packages/shared/src/components/modals/IntroQuestModal.spec.tsx

Lines changed: 78 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import React from 'react';
2-
import { act, render, screen } from '@testing-library/react';
2+
import { act, render, screen, waitFor } from '@testing-library/react';
3+
import userEvent from '@testing-library/user-event';
34
import ReactModal from 'react-modal';
45
import { IntroQuestModal } from './IntroQuestModal';
56
import { QUEST_CLAIMED_STAMP_REVEAL_DELAY_MS } from '../quest/QuestCard';
@@ -8,6 +9,7 @@ import { ActionType } from '../../graphql/actions';
89
import { useActions, useViewSize } from '../../hooks';
910
import { useQuestDashboard } from '../../hooks/useQuestDashboard';
1011
import { useClaimQuestReward } from '../../hooks/useClaimQuestReward';
12+
import { usePrompt } from '../../hooks/usePrompt';
1113
import {
1214
QuestRewardType,
1315
QuestStatus,
@@ -36,6 +38,10 @@ jest.mock('../../hooks/useClaimQuestReward', () => ({
3638
useClaimQuestReward: jest.fn(),
3739
}));
3840

41+
jest.mock('../../hooks/usePrompt', () => ({
42+
usePrompt: jest.fn(),
43+
}));
44+
3945
jest.mock('../../contexts/LogContext', () => ({
4046
useLogContext: jest.fn(),
4147
}));
@@ -61,10 +67,12 @@ const mockUseActions = useActions as jest.Mock;
6167
const mockUseViewSize = useViewSize as jest.Mock;
6268
const mockUseQuestDashboard = useQuestDashboard as jest.Mock;
6369
const mockUseClaimQuestReward = useClaimQuestReward as jest.Mock;
70+
const mockUsePrompt = usePrompt as jest.Mock;
6471
const mockUseLogContext = useLogContext as jest.Mock;
6572
const mockGetCurrentBrowserName = getCurrentBrowserName as jest.Mock;
6673
const completeAction = jest.fn();
6774
const logEvent = jest.fn();
75+
const showPrompt = jest.fn();
6876

6977
const buildIntroQuest = (overrides: Partial<UserQuest> = {}): UserQuest => ({
7078
userQuestId: 'uq-1',
@@ -95,9 +103,13 @@ describe('IntroQuestModal', () => {
95103
completeAction.mockReset();
96104
logEvent.mockReset();
97105
mockPush.mockReset();
106+
showPrompt.mockReset();
98107
mockUseActions.mockReturnValue({
99108
completeAction,
100109
});
110+
mockUsePrompt.mockReturnValue({
111+
showPrompt,
112+
});
101113
mockUseLogContext.mockReturnValue({
102114
logEvent,
103115
});
@@ -170,6 +182,71 @@ describe('IntroQuestModal', () => {
170182
hidden: true,
171183
}),
172184
).toBeInTheDocument();
185+
expect(
186+
screen.getByRole('button', {
187+
name: "Don't show me this again",
188+
hidden: true,
189+
}),
190+
).toBeInTheDocument();
191+
});
192+
193+
it('confirms before hiding intro quests from the modal', async () => {
194+
const onRequestClose = jest.fn();
195+
showPrompt.mockResolvedValue(true);
196+
mockUseQuestDashboard.mockReturnValue({
197+
data: {
198+
intro: [buildIntroQuest()],
199+
},
200+
isPending: false,
201+
isError: false,
202+
});
203+
204+
render(<IntroQuestModal isOpen onRequestClose={onRequestClose} />);
205+
206+
await userEvent.click(
207+
screen.getByRole('button', {
208+
name: "Don't show me this again",
209+
hidden: true,
210+
}),
211+
);
212+
213+
expect(showPrompt).toHaveBeenCalledWith({
214+
title: 'Hide intro quests?',
215+
description:
216+
'Are you sure you want to permanently hide the intro quests button?',
217+
okButton: {
218+
title: 'Yes, hide it',
219+
},
220+
});
221+
expect(completeAction).toHaveBeenCalledWith(
222+
ActionType.IntroQuestsCompleted,
223+
);
224+
await waitFor(() => expect(onRequestClose).toHaveBeenCalled());
225+
});
226+
227+
it('keeps the modal open when hiding intro quests is cancelled', async () => {
228+
const onRequestClose = jest.fn();
229+
showPrompt.mockResolvedValue(false);
230+
mockUseQuestDashboard.mockReturnValue({
231+
data: {
232+
intro: [buildIntroQuest()],
233+
},
234+
isPending: false,
235+
isError: false,
236+
});
237+
238+
render(<IntroQuestModal isOpen onRequestClose={onRequestClose} />);
239+
240+
await userEvent.click(
241+
screen.getByRole('button', {
242+
name: "Don't show me this again",
243+
hidden: true,
244+
}),
245+
);
246+
247+
expect(completeAction).toHaveBeenCalledTimes(1);
248+
expect(completeAction).toHaveBeenCalledWith(ActionType.ViewedIntroQuests);
249+
expect(onRequestClose).not.toHaveBeenCalled();
173250
});
174251

175252
it('does not render destination button for completed quests', () => {

packages/shared/src/components/modals/IntroQuestModal.tsx

Lines changed: 59 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import { useRouter } from 'next/router';
1010
import type { ModalProps } from './common/Modal';
1111
import { Modal } from './common/Modal';
1212
import { ModalClose } from './common/ModalClose';
13+
import { Button, ButtonSize, ButtonVariant } from '../buttons/Button';
1314
import { TourIcon } from '../icons';
1415
import {
1516
QuestCard,
@@ -28,6 +29,7 @@ import type { QuestType } from '../../graphql/quests';
2829
import { useLogContext } from '../../contexts/LogContext';
2930
import { useActions } from '../../hooks';
3031
import { useClaimQuestReward } from '../../hooks/useClaimQuestReward';
32+
import { usePrompt } from '../../hooks/usePrompt';
3133
import { useQuestDashboard } from '../../hooks/useQuestDashboard';
3234
import { downloadBrowserExtension, webappUrl } from '../../lib/constants';
3335
import { BrowserName, getCurrentBrowserName } from '../../lib/func';
@@ -86,6 +88,7 @@ export const IntroQuestModal = ({
8688
const browserName = getCurrentBrowserName();
8789
const { logEvent } = useLogContext();
8890
const { completeAction } = useActions();
91+
const { showPrompt } = usePrompt();
8992
const { data, isPending, isError } = useQuestDashboard();
9093
const {
9194
mutate: claimQuestReward,
@@ -305,6 +308,24 @@ export const IntroQuestModal = ({
305308
[onRequestClose, router],
306309
);
307310

311+
const handleHideIntroQuests = useCallback(async (): Promise<void> => {
312+
const shouldHide = await showPrompt({
313+
title: 'Hide intro quests?',
314+
description:
315+
'Are you sure you want to permanently hide the intro quests button?',
316+
okButton: {
317+
title: 'Yes, hide it',
318+
},
319+
});
320+
321+
if (!shouldHide) {
322+
return;
323+
}
324+
325+
await completeAction(ActionType.IntroQuestsCompleted);
326+
onRequestClose?.(undefined as never);
327+
}, [completeAction, onRequestClose, showPrompt]);
328+
308329
useEffect(() => {
309330
return () => {
310331
clearClaimedStampTimers();
@@ -350,32 +371,44 @@ export const IntroQuestModal = ({
350371
)}
351372

352373
{introQuests.length > 0 && (
353-
<div className="flex flex-col gap-2">
354-
{introQuests.map((userQuest, index) => (
355-
<QuestCard
356-
key={userQuest.rotationId}
357-
quest={userQuest}
358-
onClaim={handleClaim}
359-
destination={introDestinations[userQuest.quest.eventType]}
360-
onDestinationClick={handleDestinationClick}
361-
showLevelSystem
362-
isClaiming={claimingQuestId === userQuest.userQuestId}
363-
isClaimAnimating={animatingClaimRotationIdSet.has(
364-
userQuest.rotationId,
365-
)}
366-
showClaimedStamp={claimedStampRotationIdSet.has(
367-
userQuest.rotationId,
368-
)}
369-
animateClaimedStamp={animatingClaimedStampRotationIdSet.has(
370-
userQuest.rotationId,
371-
)}
372-
suppressPersistedClaimedStamp={deferredClaimedStampRotationIdSet.has(
373-
userQuest.rotationId,
374-
)}
375-
eyebrow={padStep(index)}
376-
showLockIcon={false}
377-
/>
378-
))}
374+
<div className="flex flex-col gap-4">
375+
<div className="flex flex-col gap-2">
376+
{introQuests.map((userQuest, index) => (
377+
<QuestCard
378+
key={userQuest.rotationId}
379+
quest={userQuest}
380+
onClaim={handleClaim}
381+
destination={introDestinations[userQuest.quest.eventType]}
382+
onDestinationClick={handleDestinationClick}
383+
showLevelSystem
384+
isClaiming={claimingQuestId === userQuest.userQuestId}
385+
isClaimAnimating={animatingClaimRotationIdSet.has(
386+
userQuest.rotationId,
387+
)}
388+
showClaimedStamp={claimedStampRotationIdSet.has(
389+
userQuest.rotationId,
390+
)}
391+
animateClaimedStamp={animatingClaimedStampRotationIdSet.has(
392+
userQuest.rotationId,
393+
)}
394+
suppressPersistedClaimedStamp={deferredClaimedStampRotationIdSet.has(
395+
userQuest.rotationId,
396+
)}
397+
eyebrow={padStep(index)}
398+
showLockIcon={false}
399+
/>
400+
))}
401+
</div>
402+
<div className="flex justify-center border-t border-border-subtlest-tertiary pt-2">
403+
<Button
404+
type="button"
405+
variant={ButtonVariant.Tertiary}
406+
size={ButtonSize.Small}
407+
onClick={handleHideIntroQuests}
408+
>
409+
Don&apos;t show me this again
410+
</Button>
411+
</div>
379412
</div>
380413
)}
381414
</Modal.Body>

packages/shared/src/graphql/actions.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ export enum ActionType {
4545
CompletedOnboarding = 'completed_onboarding',
4646
GeneratedBrief = 'generated_brief',
4747
ViewedIntroQuests = 'viewed_intro_quests',
48+
IntroQuestsCompleted = 'intro_quests_completed',
4849
ClosedProfileBanner = 'closed_profile_banner',
4950
UploadedCV = 'uploaded_cv',
5051
DisableBriefCardCta = 'disable_brief_card_cta',

0 commit comments

Comments
 (0)