Skip to content

Commit 1408e7d

Browse files
committed
local changes
1 parent 18fe538 commit 1408e7d

File tree

3 files changed

+162
-24
lines changed

3 files changed

+162
-24
lines changed

components/FortuneTigerBetCard.tsx

Lines changed: 94 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
'use client';
22

3-
import { useState, useEffect } from 'react';
3+
import { useState, useEffect, useRef } from 'react';
44
import { motion } from 'framer-motion';
55
import { useWallet } from '@/contexts/WalletContext';
66
import { useHathor } from '@/contexts/HathorContext';
@@ -44,6 +44,7 @@ export default function FortuneTigerBetCard({ selectedToken }: FortuneTigerBetCa
4444
const [potentialPayout, setPotentialPayout] = useState(20);
4545
const [isPlacingBet, setIsPlacingBet] = useState(false);
4646
const [isSpinning, setIsSpinning] = useState(false);
47+
const [isIdleSpinning, setIsIdleSpinning] = useState(false);
4748
const [luckyNumber, setLuckyNumber] = useState(0);
4849
const [pendingBetTxId, setPendingBetTxId] = useState<string | null>(null);
4950
const [betResult, setBetResult] = useState<'win' | 'lose' | null>(null);
@@ -52,6 +53,39 @@ export default function FortuneTigerBetCard({ selectedToken }: FortuneTigerBetCa
5253
const [showWalletModal, setShowWalletModal] = useState(false);
5354
const [showAnimationSelector, setShowAnimationSelector] = useState(false);
5455
const [debugMode, setDebugMode] = useState(false);
56+
const [preloadedVideos, setPreloadedVideos] = useState<{ win: string | null; lose: string | null }>({ win: null, lose: null });
57+
58+
const idleTimerRef = useRef<NodeJS.Timeout | null>(null);
59+
const IDLE_TIMEOUT = 15000; // 15 seconds of inactivity
60+
61+
// Video animation lists for preloading
62+
const WIN_VIDEO_PATHS = [
63+
'/videos/win/b0386440-e311-447f-9ad9-66fcae177139.mp4',
64+
'/videos/win/victory-is-yours.mp4',
65+
];
66+
67+
const LOSE_VIDEO_PATHS = [
68+
'/videos/lose/4a51eae1-9047-41d8-8a99-2d03cb543766.mp4',
69+
'/videos/lose/51b786d4-3f76-4881-9aa6-95f6f106c4cc.mp4',
70+
'/videos/lose/e8029c41-709d-4dd6-85ef-fa15357ce592.mp4',
71+
'/videos/lose/fracasso-baiano-1.mp4',
72+
'/videos/lose/fracasso-baiano-2.mp4',
73+
'/videos/lose/fracasso-baiano-3.mp4',
74+
];
75+
76+
// Preload videos while spinning
77+
const preloadVideos = () => {
78+
const randomWinVideo = WIN_VIDEO_PATHS[Math.floor(Math.random() * WIN_VIDEO_PATHS.length)];
79+
const randomLoseVideo = LOSE_VIDEO_PATHS[Math.floor(Math.random() * LOSE_VIDEO_PATHS.length)];
80+
setPreloadedVideos({ win: randomWinVideo, lose: randomLoseVideo });
81+
82+
// Fetch videos to cache them
83+
[randomWinVideo, randomLoseVideo].forEach(path => {
84+
fetch(path).catch(() => {
85+
// Silently fail - we just want to cache if possible
86+
});
87+
});
88+
};
5589

5690
const contractState = getContractStateForToken(selectedToken);
5791
const randomBitLength = contractState?.random_bit_length || 16;
@@ -106,10 +140,8 @@ export default function FortuneTigerBetCard({ selectedToken }: FortuneTigerBetCa
106140
selectedAnimation = loseAnimations[Math.floor(Math.random() * loseAnimations.length)];
107141
}
108142

109-
// Show animation after a delay
110-
setTimeout(() => {
111-
setActiveAnimation(selectedAnimation);
112-
}, 500);
143+
// Show animation immediately - no delay
144+
setActiveAnimation(selectedAnimation);
113145
}
114146
}, [allBets, pendingBetTxId]);
115147

@@ -136,6 +168,53 @@ export default function FortuneTigerBetCard({ selectedToken }: FortuneTigerBetCa
136168
return () => window.removeEventListener('storage', handleStorageChange);
137169
}, []);
138170

171+
// Reset idle timer on user activity
172+
const resetIdleTimer = () => {
173+
if (idleTimerRef.current) {
174+
clearTimeout(idleTimerRef.current);
175+
}
176+
setIsIdleSpinning(false);
177+
178+
idleTimerRef.current = setTimeout(() => {
179+
// Only start idle spinning if not currently spinning or in other interaction
180+
if (!isSpinning && !isPlacingBet && !activeAnimation) {
181+
setIsIdleSpinning(true);
182+
}
183+
}, IDLE_TIMEOUT);
184+
};
185+
186+
// Set up idle timer and user interaction listeners
187+
useEffect(() => {
188+
resetIdleTimer();
189+
190+
const handleUserActivity = () => {
191+
resetIdleTimer();
192+
};
193+
194+
// Listen for various user interactions
195+
window.addEventListener('mousedown', handleUserActivity);
196+
window.addEventListener('keydown', handleUserActivity);
197+
window.addEventListener('scroll', handleUserActivity);
198+
window.addEventListener('touchstart', handleUserActivity);
199+
200+
return () => {
201+
window.removeEventListener('mousedown', handleUserActivity);
202+
window.removeEventListener('keydown', handleUserActivity);
203+
window.removeEventListener('scroll', handleUserActivity);
204+
window.removeEventListener('touchstart', handleUserActivity);
205+
if (idleTimerRef.current) {
206+
clearTimeout(idleTimerRef.current);
207+
}
208+
};
209+
}, [isSpinning, isPlacingBet, activeAnimation]);
210+
211+
// Stop idle spinning when actual spinning starts
212+
useEffect(() => {
213+
if (isSpinning || isPlacingBet) {
214+
setIsIdleSpinning(false);
215+
}
216+
}, [isSpinning, isPlacingBet]);
217+
139218
const setQuickAmount = (percentage: number) => {
140219
const amount = totalBalance * percentage;
141220
const maxAllowed = Number(maxBetAmount) / 100;
@@ -165,6 +244,10 @@ export default function FortuneTigerBetCard({ selectedToken }: FortuneTigerBetCa
165244
// Simulate wallet confirmation after 2 seconds
166245
setTimeout(() => {
167246
setIsPlacingBet(false);
247+
248+
// Preload videos while spinning
249+
preloadVideos();
250+
168251
// Start spinning AFTER wallet confirmation
169252
setIsSpinning(true);
170253
toast.success('🎰 Debug: Transaction confirmed! Spinning...');
@@ -184,10 +267,8 @@ export default function FortuneTigerBetCard({ selectedToken }: FortuneTigerBetCa
184267
setIsSpinning(false);
185268
setBetResult(isWin ? 'win' : 'lose');
186269

187-
// Show the selected animation after a delay
188-
setTimeout(() => {
189-
setActiveAnimation(animationId);
190-
}, 500);
270+
// Show the selected animation immediately
271+
setActiveAnimation(animationId);
191272
}, 7000); // 7 seconds of spinning
192273
}, 2000); // 2 seconds for wallet confirmation
193274
};
@@ -242,6 +323,9 @@ export default function FortuneTigerBetCard({ selectedToken }: FortuneTigerBetCa
242323
const result = await placeBet(betAmount, threshold, selectedToken, contractId, tokenUid, contractBalance);
243324
setPendingBetTxId(result.response.hash);
244325

326+
// Preload videos while spinning
327+
preloadVideos();
328+
245329
// Start spinning AFTER wallet confirmation
246330
setIsSpinning(true);
247331
toast.success('🎰 Transaction confirmed! Spinning...');
@@ -289,7 +373,7 @@ export default function FortuneTigerBetCard({ selectedToken }: FortuneTigerBetCa
289373
{/* Slot Machine Area */}
290374
<div className="mb-8">
291375
<SlotMachineAnimation
292-
isSpinning={isSpinning}
376+
isSpinning={isSpinning || isIdleSpinning}
293377
finalNumber={luckyNumber}
294378
result={betResult}
295379
/>

components/SlotMachineAnimation.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ export function SlotMachineAnimation({
7575
isSpinning={isSpinning}
7676
duration={1.5 + (colIndex * 0.3)}
7777
delay={colIndex * 0.15}
78-
spinSpeed={0.3 + (colIndex * 0.05)}
78+
spinSpeed={1.2 + (colIndex * 0.15)}
7979
size="full-column"
8080
/>
8181

components/VideoPlayer.tsx

Lines changed: 67 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ interface VideoPlayerProps {
1515
export function VideoPlayer({ videoPath, result, payout, token, onComplete }: VideoPlayerProps) {
1616
const videoRef = useRef<HTMLVideoElement>(null);
1717
const [hasEnded, setHasEnded] = useState(false);
18+
const [autoplayFailed, setAutoplayFailed] = useState(false);
19+
const [videoLoaded, setVideoLoaded] = useState(false);
1820

1921
// Generate falling particles (minimal count for performance)
2022
const particles = useMemo(() => Array.from({ length: 8 }, (_, i) => ({
@@ -29,24 +31,46 @@ export function VideoPlayer({ videoPath, result, payout, token, onComplete }: Vi
2931
const video = videoRef.current;
3032
if (!video) return;
3133

32-
// Auto-play the video
33-
video.play().catch(err => {
34-
console.log('Video autoplay blocked:', err);
35-
});
34+
console.log('VideoPlayer mounted with path:', videoPath);
3635

3736
const handleEnded = () => {
37+
console.log('Video ended');
3838
setHasEnded(true);
3939
setTimeout(() => {
4040
onComplete();
4141
}, 500);
4242
};
4343

44+
const handleError = (e: Event) => {
45+
console.error('Video error:', video.error, video.error?.code);
46+
setAutoplayFailed(true);
47+
};
48+
49+
const handlePlay = () => {
50+
console.log('Video started playing, unmuting');
51+
// Unmute once video starts playing
52+
video.muted = false;
53+
setVideoLoaded(true);
54+
setAutoplayFailed(false);
55+
};
56+
57+
const handleCanPlayThrough = () => {
58+
console.log('Video ready to play');
59+
setVideoLoaded(true);
60+
};
61+
4462
video.addEventListener('ended', handleEnded);
63+
video.addEventListener('error', handleError);
64+
video.addEventListener('play', handlePlay);
65+
video.addEventListener('canplaythrough', handleCanPlayThrough);
4566

4667
return () => {
4768
video.removeEventListener('ended', handleEnded);
69+
video.removeEventListener('error', handleError);
70+
video.removeEventListener('play', handlePlay);
71+
video.removeEventListener('canplaythrough', handleCanPlayThrough);
4872
};
49-
}, [onComplete]);
73+
}, [videoPath, onComplete]);
5074

5175
const isWin = result === 'win';
5276

@@ -162,14 +186,44 @@ export function VideoPlayer({ videoPath, result, payout, token, onComplete }: Vi
162186
{isWin ? '🎉 YOU WON! 🎉' : '😔 TRY AGAIN'}
163187
</motion.div>
164188

165-
{/* Video */}
166-
<video
167-
ref={videoRef}
168-
src={videoPath}
169-
className="w-full rounded-xl shadow-2xl"
170-
playsInline
171-
muted={false}
172-
/>
189+
{/* Video Container */}
190+
<div className="relative w-full bg-black rounded-xl overflow-hidden">
191+
<video
192+
ref={videoRef}
193+
src={videoPath}
194+
className="w-full h-auto rounded-xl shadow-2xl bg-black block"
195+
playsInline
196+
autoPlay
197+
muted
198+
/>
199+
200+
{/* Play Button Overlay (shows only if video fails to load) */}
201+
{autoplayFailed && (
202+
<motion.button
203+
initial={{ opacity: 0, scale: 0.8 }}
204+
animate={{ opacity: 1, scale: 1 }}
205+
whileHover={{ scale: 1.1 }}
206+
whileTap={{ scale: 0.95 }}
207+
onClick={(e) => {
208+
e.preventDefault();
209+
console.log('Play button clicked');
210+
const video = videoRef.current;
211+
if (video) {
212+
video.muted = false;
213+
video.play().catch(err => console.error('Play failed:', err));
214+
}
215+
}}
216+
className="absolute inset-0 flex items-center justify-center rounded-xl bg-black/50 hover:bg-black/60 transition-colors cursor-pointer"
217+
>
218+
<div className="flex flex-col items-center gap-4">
219+
<div className="flex items-center justify-center w-24 h-24 rounded-full bg-yellow-500 hover:bg-yellow-400 shadow-2xl transform transition-transform">
220+
<div className="text-6xl ml-1"></div>
221+
</div>
222+
<div className="text-white text-lg font-bold">Click to Play</div>
223+
</div>
224+
</motion.button>
225+
)}
226+
</div>
173227

174228
{/* Bottom Payout Banner (only for wins) */}
175229
{isWin && payout && token && (

0 commit comments

Comments
 (0)