184 lines
6.4 KiB
TypeScript
184 lines
6.4 KiB
TypeScript
import { useEffect, useState } from 'react';
|
|
import { useNavigate } from 'react-router-dom';
|
|
import { useServerSdk } from '../../hooks/index';
|
|
import { useI18n } from '../../hooks/useI18n';
|
|
import { i18nManager } from '../../i18n/manager';
|
|
import { authService } from '../../services/auth';
|
|
// import LanguageSwitcher from '../../components/LanguageSwitcher'; // 已隐藏 - 只支持英语
|
|
import './index.css';
|
|
export default function History() {
|
|
const { t } = useI18n();
|
|
const navigate = useNavigate();
|
|
const [records, setRecords] = useState<any[]>([]);
|
|
const [refreshing, setRefreshing] = useState(false);
|
|
const [loading, setLoading] = useState(true);
|
|
const [isLoggedIn, setIsLoggedIn] = useState<boolean | null>(null);
|
|
const serverSdk = useServerSdk();
|
|
|
|
const checkLoginStatus = async () => {
|
|
try {
|
|
const loginStatus = await authService.checkLogin();
|
|
setIsLoggedIn(loginStatus);
|
|
return loginStatus;
|
|
} catch (error) {
|
|
console.error('Failed to check login status:', error);
|
|
setIsLoggedIn(false);
|
|
return false;
|
|
}
|
|
};
|
|
|
|
const loadRecords = async () => {
|
|
try {
|
|
const logs = await serverSdk.getMineLogs();
|
|
setRecords(logs || []);
|
|
} catch (error) {
|
|
console.error('Failed to load records:', error);
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
};
|
|
|
|
useEffect(() => {
|
|
const initPage = async () => {
|
|
const loginStatus = await checkLoginStatus();
|
|
if (loginStatus) {
|
|
await loadRecords();
|
|
} else {
|
|
setLoading(false);
|
|
}
|
|
};
|
|
|
|
initPage();
|
|
// Set page title
|
|
i18nManager.updateNavigationBarTitle('history');
|
|
}, []);
|
|
|
|
// Regularly update progress of generating tasks
|
|
useEffect(() => {
|
|
const hasGeneratingTasks = records.some(record => record.status === 'processing');
|
|
if (!hasGeneratingTasks) return;
|
|
|
|
const interval = setInterval(() => {
|
|
// Trigger re-render to update progress
|
|
setRecords(prevRecords => [...prevRecords]);
|
|
}, 1000);
|
|
|
|
return () => clearInterval(interval);
|
|
}, [records]);
|
|
|
|
// Pull to refresh
|
|
const handleRefresh = async () => {
|
|
setRefreshing(true);
|
|
try {
|
|
await loadRecords();
|
|
} catch (error) {
|
|
console.error('Refresh failed:', error);
|
|
} finally {
|
|
setRefreshing(false);
|
|
}
|
|
};
|
|
|
|
// Click history record item
|
|
const handleItemClick = (record: any) => {
|
|
console.log('record', record);
|
|
if (record.status === 'failed') {
|
|
console.error('Refresh failed:', record.errorMessage);
|
|
// Show error message
|
|
} else {
|
|
console.log('record.paymentId', record);
|
|
// Navigate to generation page to view progress
|
|
navigate(`/result?taskId=${record.paymentId}`);
|
|
}
|
|
};
|
|
|
|
const formatTime = (timeStr: string) => {
|
|
const date = new Date(timeStr);
|
|
const year = date.getFullYear();
|
|
const month = String(date.getMonth() + 1).padStart(2, '0');
|
|
const day = String(date.getDate()).padStart(2, '0');
|
|
const hours = String(date.getHours()).padStart(2, '0');
|
|
const minutes = String(date.getMinutes()).padStart(2, '0');
|
|
const seconds = String(date.getSeconds()).padStart(2, '0');
|
|
|
|
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
|
|
};
|
|
|
|
const handleLogin = async () => {
|
|
try {
|
|
await authService.login('/history');
|
|
} catch (error) {
|
|
console.error('Login failed:', error);
|
|
}
|
|
};
|
|
|
|
return (
|
|
<div className="history">
|
|
{/* Settings area */}
|
|
<div className="history-list">
|
|
<div className="refresh-area" onClick={handleRefresh}>
|
|
{refreshing && <div className="refresh-indicator">Refreshing...</div>}
|
|
</div>
|
|
<div className="history-grid">
|
|
{loading ? (
|
|
<div className="loading-state">
|
|
<span className="loading-text">{t('common.loading')}</span>
|
|
</div>
|
|
) : isLoggedIn === false ? (
|
|
<div className="login-required-state">
|
|
<img className="login-icon-image" src="/assets/icons/user.png" alt="login" />
|
|
<div className="login-content">
|
|
<h3 className="login-title">{t('history.notLoggedIn')}</h3>
|
|
<p className="login-description">{t('history.loginToViewHistory')}</p>
|
|
<button className="login-button" onClick={handleLogin}>
|
|
{t('history.loginButton')}
|
|
</button>
|
|
</div>
|
|
</div>
|
|
) : records.length === 0 ? (
|
|
<div className="empty-state">
|
|
<img className="empty-icon-image" src="/assets/icons/cloud.png" alt="empty" />
|
|
<span className="empty-text">{t('history.noRecords')}</span>
|
|
</div>
|
|
) : (
|
|
records.map(record => {
|
|
// const progress = calculateProgress(record); // Temporarily unused
|
|
const isGenerating = record.status === 'processing';
|
|
return (
|
|
<div key={record.id} className="history-item" onClick={() => handleItemClick(record)}>
|
|
<div className="item-thumbnail">
|
|
{record.status === 'completed' && record.outputResult ? (
|
|
<img className="thumbnail-image" src={record.inputImages?.[0] || ''} alt="thumbnail" />
|
|
) : (
|
|
<div className={`thumbnail-placeholder ${record.status}`}>
|
|
<img className="thumbnail-image" src={record.inputImages?.[0] || ''} alt="thumbnail" />
|
|
{/* Scanning effect for generating state */}
|
|
{isGenerating && (
|
|
<div className="thumbnail-overlay">
|
|
<div className="thumbnail-progress-overlay" />
|
|
<div className="thumbnail-scan-light" />
|
|
{/* Loading spinner */}
|
|
<div className="thumbnail-loader" />
|
|
</div>
|
|
)}
|
|
</div>
|
|
)}
|
|
</div>
|
|
|
|
<div className="item-content">
|
|
<div className="item-header">
|
|
<span className="item-title">{record.templateName}</span>
|
|
</div>
|
|
<div className="item-info">
|
|
<span className="item-time">{formatTime(record.createdAt)}</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
})
|
|
)}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|