11import type { ReactElement } from 'react' ;
2- import React from 'react' ;
2+ import React , { useEffect , useRef , useState } from 'react' ;
3+ import classNames from 'classnames' ;
34import { Button , ButtonSize , ButtonVariant } from '../buttons/Button' ;
45import { TourIcon } from '../icons' ;
56import { Tooltip } from '../tooltip/Tooltip' ;
@@ -15,6 +16,9 @@ import { featureNewD1Experience } from '../../lib/featureManagement';
1516import { useQuestDashboard } from '../../hooks/useQuestDashboard' ;
1617import { QuestStatus } from '../../graphql/quests' ;
1718
19+ const INTRO_QUEST_CTA = 'Get the most out of daily.dev' ;
20+ const INTRO_QUEST_CTA_DURATION_MS = 2000 ;
21+
1822export function IntroQuestButton ( ) : ReactElement | null {
1923 const { isAuthReady, isLoggedIn } = useAuthContext ( ) ;
2024 const { loadedSettings } = useSettingsContext ( ) ;
@@ -27,13 +31,38 @@ export function IntroQuestButton(): ReactElement | null {
2731 } ) ;
2832 const { data } = useQuestDashboard ( ) ;
2933 const introQuests = data ?. intro ?? [ ] ;
34+ const allIntroQuestsClaimed =
35+ introQuests . length > 0 &&
36+ introQuests . every ( ( { status } ) => status === QuestStatus . Claimed ) ;
3037 const hasViewedIntroQuests = checkHasCompleted ( ActionType . ViewedIntroQuests ) ;
38+ const [ isIntroCtaVisible , setIsIntroCtaVisible ] = useState ( false ) ;
39+ const hasShownIntroCta = useRef ( false ) ;
40+ const shouldRenderButton =
41+ isAuthReady &&
42+ loadedSettings &&
43+ isLoggedIn &&
44+ isNewD1Experience &&
45+ introQuests . length > 0 &&
46+ ! allIntroQuestsClaimed ;
47+ const showIntroCta =
48+ shouldRenderButton && ( ! hasShownIntroCta . current || isIntroCtaVisible ) ;
3149
32- if ( ! isAuthReady || ! loadedSettings || ! isLoggedIn || ! isNewD1Experience ) {
33- return null ;
34- }
50+ useEffect ( ( ) => {
51+ if ( ! shouldRenderButton || hasShownIntroCta . current ) {
52+ return undefined ;
53+ }
3554
36- if ( introQuests . length === 0 ) {
55+ hasShownIntroCta . current = true ;
56+ setIsIntroCtaVisible ( true ) ;
57+ const timeout = setTimeout (
58+ ( ) => setIsIntroCtaVisible ( false ) ,
59+ INTRO_QUEST_CTA_DURATION_MS ,
60+ ) ;
61+
62+ return ( ) => clearTimeout ( timeout ) ;
63+ } , [ shouldRenderButton ] ) ;
64+
65+ if ( ! shouldRenderButton ) {
3766 return null ;
3867 }
3968
@@ -44,12 +73,13 @@ export function IntroQuestButton(): ReactElement | null {
4473 const hasClaimableIntroQuest = introQuests . some ( ( { claimable } ) => claimable ) ;
4574 const showAttentionBadge = ! hasViewedIntroQuests || hasClaimableIntroQuest ;
4675 const buttonLabel = `${ completed } /${ introQuests . length } ` ;
76+ const buttonVariant = isLaptop ? ButtonVariant . Float : ButtonVariant . Tertiary ;
4777
4878 return (
4979 < Tooltip content = "Introduction" side = "bottom" >
5080 < Button
5181 type = "button"
52- variant = { isLaptop ? ButtonVariant . Float : ButtonVariant . Tertiary }
82+ variant = { buttonVariant }
5383 size = { ButtonSize . Medium }
5484 icon = { < TourIcon /> }
5585 className = "relative"
@@ -66,7 +96,24 @@ export function IntroQuestButton(): ReactElement | null {
6696 !
6797 </ Bubble >
6898 ) }
69- { buttonLabel }
99+ < span className = "flex min-w-0 items-center" >
100+ < span
101+ aria-hidden = "true"
102+ data-testid = "intro-quest-cta"
103+ data-expanded = { showIntroCta ? 'true' : 'false' }
104+ className = { classNames (
105+ 'inline-flex shrink-0 items-center overflow-hidden whitespace-nowrap rounded-10 transition-all duration-500 ease-out motion-reduce:transition-none' ,
106+ showIntroCta
107+ ? 'mr-2 max-w-[18rem] bg-background-subtle px-3 py-1 opacity-100'
108+ : 'mr-0 max-w-0 bg-background-subtle px-0 py-0 opacity-0' ,
109+ ) }
110+ >
111+ < span className = "min-w-max font-bold typo-footnote" >
112+ { INTRO_QUEST_CTA }
113+ </ span >
114+ </ span >
115+ < span className = "shrink-0" > { buttonLabel } </ span >
116+ </ span >
70117 </ Button >
71118 </ Tooltip >
72119 ) ;
0 commit comments