@@ -15,13 +15,15 @@ export default function DevShakeTestPage() {
1515 const [ holdTimer , setHoldTimer ] = useState < NodeJS . Timeout | null > ( null )
1616 const [ progressInterval , setProgressInterval ] = useState < NodeJS . Timeout | null > ( null )
1717 const [ showSuccess , setShowSuccess ] = useState ( false )
18+ const [ holdStartTime , setHoldStartTime ] = useState < number | null > ( null )
1819
1920 const startHold = useCallback ( ( ) => {
2021 setHoldProgress ( 0 )
2122 setIsShaking ( true )
2223 setShowSuccess ( false )
2324
2425 const startTime = Date . now ( )
26+ setHoldStartTime ( startTime )
2527 let lastIntensity : 'weak' | 'medium' | 'strong' | 'intense' = 'weak'
2628
2729 // Update progress and shake intensity
@@ -94,25 +96,65 @@ export default function DevShakeTestPage() {
9496 } , [ ] )
9597
9698 const cancelHold = useCallback ( ( ) => {
99+ const PREVIEW_DURATION_MS = 500
100+
101+ // Calculate how long the user held
102+ const elapsed = holdStartTime ? Date . now ( ) - holdStartTime : 0
103+
104+ // Clear the completion timer (we'll never complete on release)
105+ if ( holdTimer ) clearTimeout ( holdTimer )
106+ setHoldTimer ( null )
107+
108+ // If it was a quick tap, let the preview animation continue for 500ms before resetting
109+ if ( elapsed > 0 && elapsed < PREVIEW_DURATION_MS ) {
110+ const remainingPreviewTime = PREVIEW_DURATION_MS - elapsed
111+
112+ // Let animations continue for the preview duration
113+ const resetTimer = setTimeout ( ( ) => {
114+ // Clean up after preview
115+ if ( progressInterval ) clearInterval ( progressInterval )
116+ setProgressInterval ( null )
117+ setHoldProgress ( 0 )
118+ setIsShaking ( false )
119+ setShakeIntensity ( 'none' )
120+ setHoldStartTime ( null )
121+
122+ if ( 'vibrate' in navigator ) {
123+ navigator . vibrate ( 0 )
124+ }
125+ } , remainingPreviewTime )
126+
127+ setHoldTimer ( resetTimer )
128+ } else {
129+ // Released after preview duration - reset immediately
130+ if ( progressInterval ) clearInterval ( progressInterval )
131+ setProgressInterval ( null )
132+ setHoldProgress ( 0 )
133+ setIsShaking ( false )
134+ setShakeIntensity ( 'none' )
135+ setHoldStartTime ( null )
136+
137+ if ( 'vibrate' in navigator ) {
138+ navigator . vibrate ( 0 )
139+ }
140+ }
141+ } , [ holdTimer , progressInterval , holdStartTime ] )
142+
143+ const reset = useCallback ( ( ) => {
97144 if ( holdTimer ) clearTimeout ( holdTimer )
98145 if ( progressInterval ) clearInterval ( progressInterval )
99146 setHoldTimer ( null )
100147 setProgressInterval ( null )
101148 setHoldProgress ( 0 )
102149 setIsShaking ( false )
103150 setShakeIntensity ( 'none' )
104-
105- // Stop any ongoing vibration when user releases early
151+ setHoldStartTime ( null )
152+ setShowSuccess ( false )
106153 if ( 'vibrate' in navigator ) {
107154 navigator . vibrate ( 0 )
108155 }
109156 } , [ holdTimer , progressInterval ] )
110157
111- const reset = useCallback ( ( ) => {
112- cancelHold ( )
113- setShowSuccess ( false )
114- } , [ cancelHold ] )
115-
116158 return (
117159 < div className = { `flex min-h-[inherit] flex-col gap-8 ${ getShakeClass ( isShaking , shakeIntensity ) } ` } >
118160 < NavHeader title = "🧪 Dev Shake Test" />
@@ -191,7 +233,7 @@ export default function DevShakeTestPage() {
191233 </ Button >
192234
193235 < div className = "text-center text-xs text-gray-500" >
194- Press and hold the button to test the progressive shake
236+ Hold the button for the full duration (quick taps show 500ms preview)
195237 </ div >
196238 </ div >
197239 ) : (
@@ -216,7 +258,8 @@ export default function DevShakeTestPage() {
216258 < li > ✓ Button fills with black as you hold</ li >
217259 < li > ✓ Shake starts weak and gets progressively stronger</ li >
218260 < li > ✓ Haptic feedback intensifies with shake (PWA only)</ li >
219- < li > ✓ Shake stops when you release early</ li >
261+ < li > ✓ Quick tap shows preview but resets (must hold full duration)</ li >
262+ < li > ✓ Release early cancels the action</ li >
220263 < li > ✓ After full hold: shake stops, confetti appears, final haptic</ li >
221264 < li > ✓ Works on mobile touch and desktop mouse</ li >
222265 </ ul >
0 commit comments