mirror of
https://github.com/NoFxAiOS/nofx.git
synced 2026-07-14 16:26:57 +08:00
fix: resolve login redirect loop issue (#422)
- Redirect to /traders instead of / after successful login/registration - Make 'Get Started Now' button redirect logged-in users to /traders - Prevent infinite loop where logged-in users are shown landing page repeatedly Fixes issue where after login success, clicking "Get Started Now" would show login modal again instead of entering the main application. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: tinkle-community <tinklefund@gmail.com>
This commit is contained in:
@@ -130,30 +130,30 @@ export function AuthProvider({ children }: { children: React.ReactNode }) {
|
|||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
},
|
},
|
||||||
body: JSON.stringify({ user_id: userID, otp_code: otpCode }),
|
body: JSON.stringify({ user_id: userID, otp_code: otpCode }),
|
||||||
});
|
})
|
||||||
|
|
||||||
const data = await response.json();
|
const data = await response.json()
|
||||||
|
|
||||||
if (response.ok) {
|
if (response.ok) {
|
||||||
// 登录成功,保存token和用户信息
|
// 登录成功,保存token和用户信息
|
||||||
const userInfo = { id: data.user_id, email: data.email };
|
const userInfo = { id: data.user_id, email: data.email }
|
||||||
setToken(data.token);
|
setToken(data.token)
|
||||||
setUser(userInfo);
|
setUser(userInfo)
|
||||||
localStorage.setItem('auth_token', data.token);
|
localStorage.setItem('auth_token', data.token)
|
||||||
localStorage.setItem('auth_user', JSON.stringify(userInfo));
|
localStorage.setItem('auth_user', JSON.stringify(userInfo))
|
||||||
|
|
||||||
// 跳转到首页
|
// 跳转到配置页面
|
||||||
window.history.pushState({}, '', '/');
|
window.history.pushState({}, '', '/traders')
|
||||||
window.dispatchEvent(new PopStateEvent('popstate'));
|
window.dispatchEvent(new PopStateEvent('popstate'))
|
||||||
|
|
||||||
return { success: true, message: data.message };
|
return { success: true, message: data.message }
|
||||||
} else {
|
} else {
|
||||||
return { success: false, message: data.error };
|
return { success: false, message: data.error }
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return { success: false, message: 'OTP验证失败,请重试' };
|
return { success: false, message: 'OTP验证失败,请重试' }
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
const completeRegistration = async (userID: string, otpCode: string) => {
|
const completeRegistration = async (userID: string, otpCode: string) => {
|
||||||
try {
|
try {
|
||||||
@@ -163,30 +163,30 @@ export function AuthProvider({ children }: { children: React.ReactNode }) {
|
|||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
},
|
},
|
||||||
body: JSON.stringify({ user_id: userID, otp_code: otpCode }),
|
body: JSON.stringify({ user_id: userID, otp_code: otpCode }),
|
||||||
});
|
})
|
||||||
|
|
||||||
const data = await response.json();
|
const data = await response.json()
|
||||||
|
|
||||||
if (response.ok) {
|
if (response.ok) {
|
||||||
// 注册完成,自动登录
|
// 注册完成,自动登录
|
||||||
const userInfo = { id: data.user_id, email: data.email };
|
const userInfo = { id: data.user_id, email: data.email }
|
||||||
setToken(data.token);
|
setToken(data.token)
|
||||||
setUser(userInfo);
|
setUser(userInfo)
|
||||||
localStorage.setItem('auth_token', data.token);
|
localStorage.setItem('auth_token', data.token)
|
||||||
localStorage.setItem('auth_user', JSON.stringify(userInfo));
|
localStorage.setItem('auth_user', JSON.stringify(userInfo))
|
||||||
|
|
||||||
// 跳转到首页
|
// 跳转到配置页面
|
||||||
window.history.pushState({}, '', '/');
|
window.history.pushState({}, '', '/traders')
|
||||||
window.dispatchEvent(new PopStateEvent('popstate'));
|
window.dispatchEvent(new PopStateEvent('popstate'))
|
||||||
|
|
||||||
return { success: true, message: data.message };
|
return { success: true, message: data.message }
|
||||||
} else {
|
} else {
|
||||||
return { success: false, message: data.error };
|
return { success: false, message: data.error }
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return { success: false, message: '注册完成失败,请重试' };
|
return { success: false, message: '注册完成失败,请重试' }
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
const logout = () => {
|
const logout = () => {
|
||||||
setUser(null);
|
setUser(null);
|
||||||
|
|||||||
@@ -32,48 +32,105 @@ export function LandingPage() {
|
|||||||
user={user}
|
user={user}
|
||||||
onLogout={logout}
|
onLogout={logout}
|
||||||
onPageChange={(page) => {
|
onPageChange={(page) => {
|
||||||
console.log('LandingPage onPageChange called with:', page);
|
console.log('LandingPage onPageChange called with:', page)
|
||||||
if (page === 'competition') {
|
if (page === 'competition') {
|
||||||
window.location.href = '/competition';
|
window.location.href = '/competition'
|
||||||
} else if (page === 'traders') {
|
} else if (page === 'traders') {
|
||||||
window.location.href = '/traders';
|
window.location.href = '/traders'
|
||||||
} else if (page === 'trader') {
|
} else if (page === 'trader') {
|
||||||
window.location.href = '/dashboard';
|
window.location.href = '/dashboard'
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<div className='min-h-screen px-4 sm:px-6 lg:px-8' style={{ background: 'var(--brand-black)', color: 'var(--brand-light-gray)' }}>
|
<div
|
||||||
<HeroSection language={language} />
|
className='min-h-screen px-4 sm:px-6 lg:px-8'
|
||||||
<AboutSection language={language} />
|
style={{
|
||||||
<FeaturesSection language={language} />
|
background: 'var(--brand-black)',
|
||||||
<HowItWorksSection language={language} />
|
color: 'var(--brand-light-gray)',
|
||||||
<CommunitySection />
|
}}
|
||||||
|
>
|
||||||
|
<HeroSection language={language} />
|
||||||
|
<AboutSection language={language} />
|
||||||
|
<FeaturesSection language={language} />
|
||||||
|
<HowItWorksSection language={language} />
|
||||||
|
<CommunitySection />
|
||||||
|
|
||||||
{/* CTA */}
|
{/* CTA */}
|
||||||
<AnimatedSection backgroundColor='var(--panel-bg)'>
|
<AnimatedSection backgroundColor='var(--panel-bg)'>
|
||||||
<div className='max-w-4xl mx-auto text-center'>
|
<div className='max-w-4xl mx-auto text-center'>
|
||||||
<motion.h2 className='text-5xl font-bold mb-6' style={{ color: 'var(--brand-light-gray)' }} initial={{ opacity: 0, y: 30 }} whileInView={{ opacity: 1, y: 0 }} viewport={{ once: true }}>
|
<motion.h2
|
||||||
{t('readyToDefine', language)}
|
className='text-5xl font-bold mb-6'
|
||||||
</motion.h2>
|
style={{ color: 'var(--brand-light-gray)' }}
|
||||||
<motion.p className='text-xl mb-12' style={{ color: 'var(--text-secondary)' }} initial={{ opacity: 0, y: 30 }} whileInView={{ opacity: 1, y: 0 }} viewport={{ once: true }} transition={{ delay: 0.1 }}>
|
initial={{ opacity: 0, y: 30 }}
|
||||||
{t('startWithCrypto', language)}
|
whileInView={{ opacity: 1, y: 0 }}
|
||||||
</motion.p>
|
viewport={{ once: true }}
|
||||||
<div className='flex flex-wrap justify-center gap-4'>
|
>
|
||||||
<motion.button onClick={() => setShowLoginModal(true)} className='flex items-center gap-2 px-10 py-4 rounded-lg font-semibold text-lg' style={{ background: 'var(--brand-yellow)', color: 'var(--brand-black)' }} whileHover={{ scale: 1.05 }} whileTap={{ scale: 0.95 }}>
|
{t('readyToDefine', language)}
|
||||||
{t('getStartedNow', language)}
|
</motion.h2>
|
||||||
<motion.div animate={{ x: [0, 5, 0] }} transition={{ duration: 1.5, repeat: Infinity }}>
|
<motion.p
|
||||||
<ArrowRight className='w-5 h-5' />
|
className='text-xl mb-12'
|
||||||
</motion.div>
|
style={{ color: 'var(--text-secondary)' }}
|
||||||
</motion.button>
|
initial={{ opacity: 0, y: 30 }}
|
||||||
<motion.a href='https://github.com/tinkle-community/nofx/tree/dev' target='_blank' rel='noopener noreferrer' className='flex items-center gap-2 px-10 py-4 rounded-lg font-semibold text-lg' style={{ background: 'transparent', color: 'var(--brand-light-gray)', border: '2px solid var(--brand-yellow)' }} whileHover={{ scale: 1.05, backgroundColor: 'rgba(240, 185, 11, 0.1)' }} whileTap={{ scale: 0.95 }}>
|
whileInView={{ opacity: 1, y: 0 }}
|
||||||
{t('viewSourceCode', language)}
|
viewport={{ once: true }}
|
||||||
</motion.a>
|
transition={{ delay: 0.1 }}
|
||||||
|
>
|
||||||
|
{t('startWithCrypto', language)}
|
||||||
|
</motion.p>
|
||||||
|
<div className='flex flex-wrap justify-center gap-4'>
|
||||||
|
<motion.button
|
||||||
|
onClick={() => {
|
||||||
|
if (isLoggedIn) {
|
||||||
|
window.location.href = '/traders'
|
||||||
|
} else {
|
||||||
|
setShowLoginModal(true)
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
className='flex items-center gap-2 px-10 py-4 rounded-lg font-semibold text-lg'
|
||||||
|
style={{
|
||||||
|
background: 'var(--brand-yellow)',
|
||||||
|
color: 'var(--brand-black)',
|
||||||
|
}}
|
||||||
|
whileHover={{ scale: 1.05 }}
|
||||||
|
whileTap={{ scale: 0.95 }}
|
||||||
|
>
|
||||||
|
{t('getStartedNow', language)}
|
||||||
|
<motion.div
|
||||||
|
animate={{ x: [0, 5, 0] }}
|
||||||
|
transition={{ duration: 1.5, repeat: Infinity }}
|
||||||
|
>
|
||||||
|
<ArrowRight className='w-5 h-5' />
|
||||||
|
</motion.div>
|
||||||
|
</motion.button>
|
||||||
|
<motion.a
|
||||||
|
href='https://github.com/tinkle-community/nofx/tree/dev'
|
||||||
|
target='_blank'
|
||||||
|
rel='noopener noreferrer'
|
||||||
|
className='flex items-center gap-2 px-10 py-4 rounded-lg font-semibold text-lg'
|
||||||
|
style={{
|
||||||
|
background: 'transparent',
|
||||||
|
color: 'var(--brand-light-gray)',
|
||||||
|
border: '2px solid var(--brand-yellow)',
|
||||||
|
}}
|
||||||
|
whileHover={{
|
||||||
|
scale: 1.05,
|
||||||
|
backgroundColor: 'rgba(240, 185, 11, 0.1)',
|
||||||
|
}}
|
||||||
|
whileTap={{ scale: 0.95 }}
|
||||||
|
>
|
||||||
|
{t('viewSourceCode', language)}
|
||||||
|
</motion.a>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</AnimatedSection>
|
||||||
</AnimatedSection>
|
|
||||||
|
|
||||||
{showLoginModal && <LoginModal onClose={() => setShowLoginModal(false)} language={language} />}
|
{showLoginModal && (
|
||||||
<FooterSection language={language} />
|
<LoginModal
|
||||||
|
onClose={() => setShowLoginModal(false)}
|
||||||
|
language={language}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
<FooterSection language={language} />
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user