Skip to content

Commit 723d1a5

Browse files
committed
fix(ui): resolve Rewards & Progress panel error
- Fixed early return placement to avoid React hooks order violation - Added error handling for video playback in RewardsSidebar - Added null safety checks for account data - Ensured all hooks are called before any early returns
1 parent 33dde51 commit 723d1a5

File tree

6 files changed

+86
-48
lines changed

6 files changed

+86
-48
lines changed

app/home/page.tsx

Lines changed: 15 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -797,7 +797,7 @@ const DemoSelector = ({
797797
isNexusMasterCompleted ? (
798798
/* Nexus Master Completed */
799799
<div className='flex flex-col items-center justify-center h-full'>
800-
<div className='text-4xl mb-4 animate-pulse'>👑</div>
800+
<div className='text-4xl mt-4 mb-4 animate-pulse'>🧑‍🚀 </div>
801801
<div className='text-xl font-bold text-green-400 mb-2'>NEXUS MASTER</div>
802802
<div className='text-sm text-green-300/80'>Achievement Unlocked!</div>
803803
</div>
@@ -1263,7 +1263,7 @@ export default function HomePageContent() {
12631263
"Stellar Network Integration",
12641264
"Web3 Wallet Connection"
12651265
],
1266-
"screenshot": "https://stellar-nexus-experience.vercel.app/images/logo/logo.png",
1266+
"screenshot": "https://stellar-nexus-experience.vercel.app/images/logo/logoicon.png",
12671267
"softwareVersion": "0.1.0",
12681268
"datePublished": "2024-01-01",
12691269
"dateModified": new Date().toISOString().split('T')[0],
@@ -1598,17 +1598,12 @@ export default function HomePageContent() {
15981598
</div>
15991599
</section>
16001600

1601-
1602-
<div className='text-center mb-12'>
1603-
<h3 className='text-4xl md:text-5xl font-bold text-transparent bg-clip-text bg-gradient-to-r from-brand-400 to-accent-400 mb-6 drop-shadow-2xl'>
1604-
🧩 ESCROW ARSENAL
1605-
</h3>
1606-
</div>
16071601
{/* Demo Cards Section - with fade-in animation */}
16081602
<div className='text-center'>
16091603
<p className=' text-white/80 max-w-3xl mx-auto mb-6'>
1610-
The <span className='text-brand-200 font-semibold'>Stellar Nexus Experience</span> turns early adoption into an adventure—earn XP, unlock badges, and co-create the future of Web3 alongside the first wave of <span className='text-brand-200 font-semibold'>Founders, Builders, and Developers</span>.
1604+
The <span className='text-brand-200 font-semibold'>Escrow Arsenal</span> turns early adoption into an adventure—earn XP, unlock badges, and co-create the future of Web3 alongside the first wave of <span className='text-brand-200 font-semibold'>Founders, Builders, and Developers</span>.
16111605
</p>
1606+
<br />
16121607
</div>
16131608

16141609

@@ -1796,26 +1791,24 @@ export default function HomePageContent() {
17961791
New to Trustless Work or Stellar? <br /> Start with our tutorial to learn how everything
17971792
works!
17981793
</p>
1794+
<div className='mt-4 text-center'>
1795+
<p className='text-brand-300 text-sm animate-pulse'>
1796+
💡 New here? Start with the tutorial to learn how everything works!
1797+
</p>
1798+
</div>
17991799
</div>
18001800

18011801
<div className='mb-8 flex justify-center'>
18021802
<button
18031803
onClick={() => setShowInteractiveTutorial(true)}
18041804
className='px-8 py-4 bg-gradient-to-r from-accent-500 to-accent-600 hover:from-accent-600 hover:to-accent-700 text-white font-bold rounded-xl transition-all duration-300 transform hover:scale-105 shadow-lg hover:shadow-xl border-2 border-white/20 hover:border-white/40'
18051805
>
1806-
<div className='flex items-center space-x-2'>
1806+
<div className='flex items-center space-x-2'>
18071807
<span>📚</span>
18081808
<span>Interactive Tutorial</span>
18091809
</div>
18101810
</button>
18111811
</div>
1812-
{!hasSeenOnboarding && (
1813-
<div className='mt-4 text-center'>
1814-
<p className='text-brand-300 text-sm animate-pulse'>
1815-
💡 New here? Start with the tutorial to learn how everything works!
1816-
</p>
1817-
</div>
1818-
)}
18191812

18201813
<div className='grid md:grid-cols-3 gap-8 text-sm'>
18211814
<div className='group p-6 bg-white/10 backdrop-blur-sm rounded-2xl border border-white/20 hover:bg-white/15 hover:border-white/30 transition-all duration-300 transform hover:scale-105 hover:shadow-2xl relative overflow-hidden'>
@@ -1885,8 +1878,11 @@ export default function HomePageContent() {
18851878
{/* Leaderboard Button - Centered between Tutorial and Footer */}
18861879
<div className='text-center mb-12'>
18871880
<h3 className='text-4xl md:text-5xl font-bold text-transparent bg-clip-text bg-gradient-to-r from-brand-400 to-accent-400 mb-6 drop-shadow-2xl'>
1888-
🏆 Nexus Global Leaderboard
1881+
Global Leaderboard
18891882
</h3>
1883+
<p className='text-white/70'>
1884+
See the top performers and compete for the Nexus Global Leaderboard!
1885+
</p>
18901886
</div>
18911887

18921888
<div className='flex justify-center items-center px-4'>
@@ -1901,7 +1897,7 @@ export default function HomePageContent() {
19011897
onClick={() => setLeaderboardSidebarOpen(true)}
19021898
className='relative px-8 py-4 bg-gradient-to-r from-purple-500 to-pink-500 hover:from-purple-600 hover:to-pink-600 text-white font-bold rounded-xl transition-all duration-300 transform hover:scale-105 shadow-lg hover:shadow-xl border-2 border-white/20 hover:border-white/40 flex items-center space-x-3 hover:animate-none'
19031899
>
1904-
<span className='text-xl'>👥&nbsp;</span>
1900+
<span className='text-xl'>🏆 &nbsp;</span>
19051901
<span>See the top performers</span>
19061902
<span className='text-xl'>&nbsp;→</span>
19071903
</button>

app/layout.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ export const metadata: Metadata = {
4646
siteName: 'Stellar Nexus Experience',
4747
images: [
4848
{
49-
url: '/images/logo/logo.png',
49+
url: '/images/logo/logoicon.png',
5050
width: 1200,
5151
height: 630,
5252
alt: 'Stellar Nexus Experience - Web3 Early Adopters Program',
@@ -59,7 +59,7 @@ export const metadata: Metadata = {
5959
card: 'summary_large_image',
6060
title: 'Stellar Nexus Experience | Web3 Early Adopters Program',
6161
description: 'Join the Stellar Nexus Experience Web3 Early Adopters Program. Master trustless work on Stellar blockchain with interactive demos, earn badges, and compete on the global leaderboard.',
62-
images: ['/images/logo/logo.png'],
62+
images: ['/images/logo/logoicon.png'],
6363
creator: '@StellarNexus',
6464
},
6565
robots: {

components/ui/RewardsSidebar.tsx

Lines changed: 46 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ export const RewardsSidebar: React.FC<RewardsDropdownProps> = ({ isOpen, onClose
4141
};
4242
}, [isOpen, onClose]);
4343

44+
4445
// Helper functions for badge styling using centralized assets
4546
const getRarityColor = (rarity: string) => {
4647
const colors = BADGE_COLORS[rarity as keyof typeof BADGE_COLORS] || BADGE_COLORS.common;
@@ -55,9 +56,7 @@ export const RewardsSidebar: React.FC<RewardsDropdownProps> = ({ isOpen, onClose
5556
const AVAILABLE_BADGES = badges.length > 0 ? badges : getAllBadges();
5657

5758
// Only show when account exists - consistent with UserProfile
58-
if (!account || !isOpen) {
59-
return null;
60-
}
59+
// Note: Early return moved to after all hooks to avoid React hooks order violation
6160

6261
// Ensure we have safe defaults for all data
6362
const safeAccount = {
@@ -147,6 +146,28 @@ export const RewardsSidebar: React.FC<RewardsDropdownProps> = ({ isOpen, onClose
147146
const availablePhases = getAvailableCharacterPhases();
148147
const currentPhase = availablePhases[selectedCharacterPhase] || availablePhases[0];
149148

149+
// Auto-select character phase based on level
150+
useEffect(() => {
151+
// Find the highest available phase based on current level
152+
const phases = [
153+
{ id: 0, name: 'Baby', video: '/videos/phases/baby.mp4', image: '/images/character/baby.png', minLevel: 1 },
154+
{ id: 1, name: 'Teen', video: '/videos/phases/teen.mp4', image: '/images/character/teen.png', minLevel: 2 },
155+
{ id: 2, name: 'Adult', video: '/videos/phases/adullt.mp4', image: '/images/character/character.png', minLevel: 3 }
156+
];
157+
158+
// Find the highest phase the user has unlocked
159+
const unlockedPhases = phases.filter(phase => level >= phase.minLevel);
160+
const highestUnlockedPhase = unlockedPhases[unlockedPhases.length - 1];
161+
162+
if (highestUnlockedPhase) {
163+
// Set the selected phase to the highest unlocked phase
164+
const phaseIndex = phases.findIndex(phase => phase.id === highestUnlockedPhase.id);
165+
if (phaseIndex !== -1) {
166+
setSelectedCharacterPhase(phaseIndex);
167+
}
168+
}
169+
}, [level]);
170+
150171
const handlePreviousPhase = () => {
151172
setSelectedCharacterPhase(prev =>
152173
prev > 0 ? prev - 1 : availablePhases.length - 1
@@ -214,17 +235,24 @@ export const RewardsSidebar: React.FC<RewardsDropdownProps> = ({ isOpen, onClose
214235
animation: '4s ease-in-out infinite alternate',
215236
}}
216237
onEnded={(e) => {
217-
const video = e.target as HTMLVideoElement;
218-
// Start reverse playback
219-
const reverseInterval = setInterval(() => {
220-
if (video.currentTime <= 0) {
221-
clearInterval(reverseInterval);
222-
video.currentTime = 0;
223-
video.play(); // Start forward playback again
224-
} else {
225-
video.currentTime -= 0.1; // Reverse playback
226-
}
227-
}, 100);
238+
try {
239+
const video = e.target as HTMLVideoElement;
240+
// Start reverse playback
241+
const reverseInterval = setInterval(() => {
242+
if (video.currentTime <= 0) {
243+
clearInterval(reverseInterval);
244+
video.currentTime = 0;
245+
video.play(); // Start forward playback again
246+
} else {
247+
video.currentTime -= 0.1; // Reverse playback
248+
}
249+
}, 100);
250+
} catch (error) {
251+
console.warn('Error in video playback:', error);
252+
}
253+
}}
254+
onError={(e) => {
255+
console.warn('Video failed to load:', currentPhase.video);
228256
}}
229257
>
230258
<source src={currentPhase.video} type='video/mp4' />
@@ -323,7 +351,7 @@ export const RewardsSidebar: React.FC<RewardsDropdownProps> = ({ isOpen, onClose
323351
{/* Points Summary */}
324352
<div className='grid grid-cols-3 gap-3'>
325353
<div className='bg-gradient-to-br from-green-500/20 to-emerald-500/20 rounded-lg p-3 border border-green-400/30'>
326-
<div className='text-xl font-bold text-green-400'>{account.totalPoints || 0}</div>
354+
<div className='text-xl font-bold text-green-400'>{account?.totalPoints || 0}</div>
327355
<div className='text-xs text-gray-300'>Total Points</div>
328356
</div>
329357
<div className='bg-gradient-to-br from-yellow-500/20 to-orange-500/20 rounded-lg p-3 border border-yellow-400/30'>
@@ -691,6 +719,9 @@ export const RewardsSidebar: React.FC<RewardsDropdownProps> = ({ isOpen, onClose
691719
}
692720
};
693721

722+
// Early return if not open or no account to prevent unnecessary rendering
723+
if (!isOpen || !account) return null;
724+
694725
return (
695726
<>
696727
{/* Dropdown Menu */}

components/ui/VideoPreloaderScreen.tsx

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ export const VideoPreloaderScreen: React.FC<VideoPreloaderScreenProps> = ({
2222
const [isVisible, setIsVisible] = useState(isLoading);
2323
const [isFadingOut, setIsFadingOut] = useState(false);
2424
const [videoLoaded, setVideoLoaded] = useState(false);
25+
const [starPositions, setStarPositions] = useState<Array<{left: string, top: string, delay: string, duration: string}>>([]);
2526

2627
useEffect(() => {
2728
if (!isLoading && isVisible) {
@@ -40,6 +41,17 @@ export const VideoPreloaderScreen: React.FC<VideoPreloaderScreenProps> = ({
4041
}
4142
}, [isLoading, isVisible]);
4243

44+
// Generate star positions on client side only to avoid hydration mismatch
45+
useEffect(() => {
46+
const positions = [...Array(20)].map(() => ({
47+
left: `${Math.random() * 100}%`,
48+
top: `${Math.random() * 100}%`,
49+
delay: `${Math.random() * 3}s`,
50+
duration: `${2 + Math.random() * 3}s`,
51+
}));
52+
setStarPositions(positions);
53+
}, []);
54+
4355
// Ensure minimum display duration
4456
useEffect(() => {
4557
if (isLoading) {
@@ -71,15 +83,15 @@ export const VideoPreloaderScreen: React.FC<VideoPreloaderScreenProps> = ({
7183

7284
{/* Animated Particles Overlay */}
7385
<div className='absolute inset-0 overflow-hidden pointer-events-none'>
74-
{[...Array(20)].map((_, i) => (
86+
{starPositions.map((position, i) => (
7587
<div
7688
key={i}
7789
className='absolute w-1 h-1 bg-white rounded-full animate-twinkle'
7890
style={{
79-
left: `${Math.random() * 100}%`,
80-
top: `${Math.random() * 100}%`,
81-
animationDelay: `${Math.random() * 3}s`,
82-
animationDuration: `${2 + Math.random() * 3}s`,
91+
left: position.left,
92+
top: position.top,
93+
animationDelay: position.delay,
94+
animationDuration: position.duration,
8395
}}
8496
/>
8597
))}

components/ui/quest/QuestSystem.tsx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -152,8 +152,7 @@ export const QuestSystem: React.FC<QuestSystemProps> = ({ account, onQuestComple
152152
<div className="space-y-6">
153153
{/* Header */}
154154
<div className="text-center">
155-
<h2 className="text-2xl font-bold text-white mb-2">🎯 Explore Nexus Quests</h2>
156-
<p className="text-white/70">Complete quests to earn XP, points, and special badges!</p>
155+
<p className="text-white/70">Complete <span className='text-brand-200 font-semibold'>Nexus Quests</span> to earn XP, points, and special badges!</p>
157156
</div>
158157

159158
{/* Quest Stats */}

public/manifest.json

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,21 +17,21 @@
1717
"type": "image/x-icon"
1818
},
1919
{
20-
"src": "/images/logo/logo.png",
20+
"src": "/images/logo/logoicon.png",
2121
"sizes": "192x192",
2222
"type": "image/png",
2323
"purpose": "any maskable"
2424
},
2525
{
26-
"src": "/images/logo/logo.png",
26+
"src": "/images/logo/logoicon.png",
2727
"sizes": "512x512",
2828
"type": "image/png",
2929
"purpose": "any maskable"
3030
}
3131
],
3232
"screenshots": [
3333
{
34-
"src": "/images/logo/logo.png",
34+
"src": "/images/logo/logoicon.png",
3535
"sizes": "1280x720",
3636
"type": "image/png",
3737
"form_factor": "wide",
@@ -46,7 +46,7 @@
4646
"url": "/?section=leaderboard",
4747
"icons": [
4848
{
49-
"src": "/images/logo/logo.png",
49+
"src": "/images/logo/logoicon.png",
5050
"sizes": "96x96"
5151
}
5252
]
@@ -58,7 +58,7 @@
5858
"url": "/?section=demos",
5959
"icons": [
6060
{
61-
"src": "/images/logo/logo.png",
61+
"src": "/images/logo/logoicon.png",
6262
"sizes": "96x96"
6363
}
6464
]

0 commit comments

Comments
 (0)