Compare commits

..

3 Commits

9 changed files with 63 additions and 32 deletions

View File

@ -7,7 +7,7 @@ import { i18nManager } from './i18n/manager';
// Page components // Page components
import Home from './pages/home'; import Home from './pages/home';
import History from './pages/history'; import History from './pages/history';
import FriendsPhoto from './pages/friends-photo'; import Create from './pages/create';
import Result from './pages/result'; import Result from './pages/result';
// Bottom navigation component // Bottom navigation component
@ -20,7 +20,7 @@ function App() {
const location = useLocation(); const location = useLocation();
// Pages that don't need to show bottom navigation // Pages that don't need to show bottom navigation
const hideBottomNavPages = ['/friends-photo', '/result']; const hideBottomNavPages = ['/create', '/result'];
useEffect(() => { useEffect(() => {
const initApp = async () => { const initApp = async () => {
@ -58,11 +58,11 @@ function App() {
<Route path="/" element={<Navigate to="/home" replace />} /> <Route path="/" element={<Navigate to="/home" replace />} />
<Route path="/home" element={<Home />} /> <Route path="/home" element={<Home />} />
<Route path="/history" element={<History />} /> <Route path="/history" element={<History />} />
<Route path="/friends-photo" element={<FriendsPhoto />} /> <Route path="/create/:templateCode" element={<Create />} />
<Route path="/result" element={<Result />} /> <Route path="/result" element={<Result />} />
</Routes> </Routes>
</div> </div>
{!hideBottomNavPages.includes(location.pathname) && <BottomNavigation />} {!hideBottomNavPages.some(path => location.pathname.startsWith(path)) && <BottomNavigation />}
</div> </div>
); );
} }

View File

@ -1,23 +1,25 @@
import { authService } from '@/services/auth';
import { useState } from 'react'; import { useState } from 'react';
import { bowongAI } from '../../../../sdk/bowongAISDK';
import { useI18n } from '../../../../hooks/useI18n'; import { useI18n } from '../../../../hooks/useI18n';
import { bowongAI } from '../../../../sdk/bowongAISDK';
import './index.css'; import './index.css';
interface UploadCardProps { interface UploadCardProps {
imageUrl: string; imageUrl: string;
title: string; title: string;
onLogin: () => void;
onUploadSuccess: (imageUrl: string) => void; onUploadSuccess: (imageUrl: string) => void;
className?: string; className?: string;
disabled?: boolean; disabled?: boolean;
} }
export default function UploadCard({ imageUrl, title, onUploadSuccess, className = '', disabled = false }: UploadCardProps) { export default function UploadCard({ imageUrl, title, onLogin, onUploadSuccess, className = '', disabled = false }: UploadCardProps) {
const [uploading, setUploading] = useState<boolean>(false); const [uploading, setUploading] = useState<boolean>(false);
const { t } = useI18n(); const { t } = useI18n();
const handleUpload = async () => { const handleUpload = async () => {
if (disabled) { if (disabled) {
alert('Please login first'); onLogin();
return; return;
} }

View File

@ -1,6 +1,6 @@
import { Template } from '@/sdk/sdk-server'; import { Template } from '@/sdk/sdk-server';
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import { useSearchParams } from 'react-router-dom'; import { useParams } from 'react-router-dom';
import { useServerSdk } from '../../hooks/index'; import { useServerSdk } from '../../hooks/index';
import { useI18n } from '../../hooks/useI18n'; import { useI18n } from '../../hooks/useI18n';
import { i18nManager } from '../../i18n/manager'; import { i18nManager } from '../../i18n/manager';
@ -11,12 +11,13 @@ import UploadCard from './components/UploadCard';
import './index.css'; import './index.css';
import { authService } from '@/services'; import { authService } from '@/services';
export default function FriendsPhoto() { export default function Create() {
const [searchParams] = useSearchParams(); const { templateCode } = useParams();
const [isLoggedIn, setIsLoggedIn] = useState<boolean>(false); const [isLoggedIn, setIsLoggedIn] = useState<boolean>(false);
const templateCode = searchParams.get('templateCode');
const [template, setTemplate] = useState<Template | null>(null); const [template, setTemplate] = useState<Template | null>(null);
const [loginRedirecting, setLoginRedirecting] = useState(false);
useEffect(() => { useEffect(() => {
const checkLogin = async () => { const checkLogin = async () => {
// Check login status, including OAuth 2.0 callback handling // Check login status, including OAuth 2.0 callback handling
@ -50,16 +51,17 @@ export default function FriendsPhoto() {
// 提交处理 // 提交处理
const handleSubmit = async () => { const handleSubmit = async () => {
if (!isLoggedIn) { if (!isLoggedIn) {
setLoginRedirecting(true);
setLoading(true); setLoading(true);
authService.login('/friends-photo'); await authService.login(window.location.pathname);
return; return;
} }
if (!templateCode) { if (!templateCode) {
alert(t('friendsPhoto.templateCodeNotSet')); alert('Template code not set');
return; return;
} }
if (!image1 || !image2) { if (!image1 || !image2) {
alert(t('friendsPhoto.uploadTwoImages')); alert('Upload two images');
return; return;
} }
@ -87,36 +89,52 @@ export default function FriendsPhoto() {
// navigate(`/result?taskId=${taskId}&templateCode=${templateCode}`); // navigate(`/result?taskId=${taskId}&templateCode=${templateCode}`);
} catch (error) { } catch (error) {
console.error('提交失败:', error); console.error('提交失败:', error);
alert(t('friendsPhoto.waitForTaskCompletion')); alert('Wait for task completion');
} finally { } finally {
setLoading(false); setLoading(false);
} }
}; };
// // If not authenticated, show login prompt // // If not authenticated, show login prompt
// if (isLoggedIn === false) {
// return ( if (loginRedirecting) {
// <div className="app-loading"> return (
// <div className="loading-container"> <div style={{ display: 'flex', justifyContent: 'center', alignItems: 'center', height: '100vh' }}>
// <div className="loading-spinner"></div> <div>Redirecting to login...</div>
// <div className="loading-text">Redirecting to login page...</div> </div>
// </div> );
// </div> }
// );
// }
return ( return (
<div className="friends-photo"> <div className="friends-photo">
{/* 上传区域 */} {/* 上传区域 */}
<div className="upload-section"> <div className="upload-section">
<UploadCard disabled={!isLoggedIn} imageUrl={image1} title={t('friendsPhoto.selectYourPhoto')} onUploadSuccess={setImage1} /> <UploadCard
<UploadCard disabled={!isLoggedIn} imageUrl={image2} title={t('friendsPhoto.selectFriendPhoto')} onUploadSuccess={setImage2} /> disabled={!isLoggedIn}
imageUrl={image1}
title={t('friendsPhoto.selectYourPhoto')}
onUploadSuccess={setImage1}
onLogin={() => {
setLoginRedirecting(true);
authService.login(window.location.pathname);
}}
/>
<UploadCard
disabled={!isLoggedIn}
imageUrl={image2}
title={t('friendsPhoto.selectFriendPhoto')}
onUploadSuccess={setImage2}
onLogin={() => {
setLoginRedirecting(true);
authService.login(window.location.pathname);
}}
/>
</div> </div>
{/* 固定底部按钮 */} {/* 固定底部按钮 */}
<div className="submit-section"> <div className="submit-section">
<button <button
className={`submit-button ${!image1 || !image2 || loading ? 'disabled' : ''}`} className={`submit-button ${(!image1 || !image2 || loading) && isLoggedIn ? 'disabled' : ''}`}
disabled={(!image1 || !image2 || loading) && isLoggedIn} disabled={(!image1 || !image2 || loading) && isLoggedIn}
onClick={handleSubmit} onClick={handleSubmit}
> >

View File

@ -15,6 +15,8 @@ export default function History() {
const [isLoggedIn, setIsLoggedIn] = useState<boolean | null>(null); const [isLoggedIn, setIsLoggedIn] = useState<boolean | null>(null);
const serverSdk = useServerSdk(); const serverSdk = useServerSdk();
const [loginRedirecting, setLoginRedirecting] = useState(false);
const checkLoginStatus = async () => { const checkLoginStatus = async () => {
try { try {
const loginStatus = await authService.checkLogin(); const loginStatus = await authService.checkLogin();
@ -105,12 +107,21 @@ export default function History() {
const handleLogin = async () => { const handleLogin = async () => {
try { try {
setLoginRedirecting(true);
await authService.login('/history'); await authService.login('/history');
} catch (error) { } catch (error) {
console.error('Login failed:', error); console.error('Login failed:', error);
} }
}; };
if (loginRedirecting) {
return (
<div style={{ display: 'flex', justifyContent: 'center', alignItems: 'center', height: '100vh' }}>
<div>Redirecting to login...</div>
</div>
);
}
return ( return (
<div className="history"> <div className="history">
{/* Settings area */} {/* Settings area */}

View File

@ -1,6 +1,5 @@
.home { .home {
/* background: linear-gradient(180deg, #f8f9fa 0%, #e9ecef 100%); */ /* background: linear-gradient(180deg, #f8f9fa 0%, #e9ecef 100%); */
background: #f5f6f8;
height: 100vh; height: 100vh;
display: flex; display: flex;
flex-direction: column; flex-direction: column;

View File

@ -38,7 +38,7 @@ export default function Home() {
}, [dispatch]); }, [dispatch]);
const handleTemplateClick = async (template: Template) => { const handleTemplateClick = async (template: Template) => {
navigate(`/friends-photo?templateCode=${template.code}`); navigate(`/create/${template.code}`);
return; return;
}; };

View File

@ -46,13 +46,14 @@ export class AuthService {
/** /**
* Google OAuth登录页面 * Google OAuth登录页面
*/ */
async login(redirectUrl?: string): Promise<void> { async login(redirectPath?: string): Promise<void> {
const { hostname, protocol, port } = window.location; const { hostname, protocol, port } = window.location;
let baseUrl = `${protocol}//${hostname}`; let baseUrl = `${protocol}//${hostname}`;
if (hostname === 'localhost') { if (hostname === 'localhost') {
baseUrl = `${protocol}//${hostname}:${port}`; baseUrl = `${protocol}//${hostname}:${port}`;
} }
window.location.href = `https://mixvideo-workflow.bowong.cc/auth/google/authorize?redirect_url=${baseUrl}${redirectUrl || ''}`; const redirectUrl = `https://mixvideo-workflow.bowong.cc/auth/google/authorize?redirect_url=${baseUrl}${redirectPath || ''}`;
window.location.href = redirectUrl;
} }
/** /**