Compare commits

...

2 Commits

Author SHA1 Message Date
Supra4E8C
be3f58f0a8 fix(auth-files): cache Antigravity quota to avoid auto refresh on reopen 2025-12-29 01:18:18 +08:00
Supra4E8C
c299e403cc feat(auth-files): add Antigravity quota page size 2025-12-29 00:48:31 +08:00
4 changed files with 70 additions and 8 deletions

View File

@@ -361,6 +361,7 @@
"title": "Antigravity Quota", "title": "Antigravity Quota",
"empty_title": "No Antigravity Auth Files", "empty_title": "No Antigravity Auth Files",
"empty_desc": "Upload an Antigravity credential to view remaining quota.", "empty_desc": "Upload an Antigravity credential to view remaining quota.",
"idle": "Not loaded. Click Refresh.",
"loading": "Loading quota...", "loading": "Loading quota...",
"load_failed": "Failed to load quota: {{message}}", "load_failed": "Failed to load quota: {{message}}",
"missing_auth_index": "Auth file missing auth_index", "missing_auth_index": "Auth file missing auth_index",

View File

@@ -361,6 +361,7 @@
"title": "Antigravity 额度", "title": "Antigravity 额度",
"empty_title": "暂无 Antigravity 认证", "empty_title": "暂无 Antigravity 认证",
"empty_desc": "上传 Antigravity 认证文件后即可查看额度。", "empty_desc": "上传 Antigravity 认证文件后即可查看额度。",
"idle": "尚未加载额度,请点击刷新。",
"loading": "正在加载额度...", "loading": "正在加载额度...",
"load_failed": "额度获取失败:{{message}}", "load_failed": "额度获取失败:{{message}}",
"missing_auth_index": "认证文件缺少 auth_index", "missing_auth_index": "认证文件缺少 auth_index",

View File

@@ -176,6 +176,27 @@
} }
} }
.antigravityControls {
display: flex;
gap: $spacing-md;
flex-wrap: wrap;
align-items: flex-end;
margin-bottom: $spacing-md;
}
.antigravityControl {
display: flex;
flex-direction: column;
gap: 4px;
label {
font-size: 12px;
color: var(--text-secondary);
font-weight: 500;
white-space: nowrap;
}
}
.antigravityCard { .antigravityCard {
background-image: linear-gradient( background-image: linear-gradient(
180deg, 180deg,

View File

@@ -172,6 +172,9 @@ const ANTIGRAVITY_QUOTA_GROUPS: AntigravityQuotaGroupDefinition[] = [
} }
]; ];
let antigravityQuotaCache: Record<string, AntigravityQuotaState> = {};
let antigravityQuotaCacheLoaded = false;
// 标准化 auth_index 值(与 usage.ts 中的 normalizeAuthIndex 保持一致) // 标准化 auth_index 值(与 usage.ts 中的 normalizeAuthIndex 保持一致)
function normalizeAuthIndexValue(value: unknown): string | null { function normalizeAuthIndexValue(value: unknown): string | null {
if (typeof value === 'number' && Number.isFinite(value)) { if (typeof value === 'number' && Number.isFinite(value)) {
@@ -393,6 +396,7 @@ export function AuthFilesPage() {
const [page, setPage] = useState(1); const [page, setPage] = useState(1);
const [pageSize, setPageSize] = useState(9); const [pageSize, setPageSize] = useState(9);
const [antigravityPage, setAntigravityPage] = useState(1); const [antigravityPage, setAntigravityPage] = useState(1);
const [antigravityPageSize, setAntigravityPageSize] = useState(6);
const [uploading, setUploading] = useState(false); const [uploading, setUploading] = useState(false);
const [deleting, setDeleting] = useState<string | null>(null); const [deleting, setDeleting] = useState<string | null>(null);
const [deletingAll, setDeletingAll] = useState(false); const [deletingAll, setDeletingAll] = useState(false);
@@ -508,7 +512,6 @@ export function AuthFilesPage() {
[files] [files]
); );
const antigravityPageSize = 6;
const antigravityTotalPages = Math.max( const antigravityTotalPages = Math.max(
1, 1,
Math.ceil(antigravityFiles.length / antigravityPageSize) Math.ceil(antigravityFiles.length / antigravityPageSize)
@@ -627,6 +630,8 @@ export function AuthFilesPage() {
} }
}); });
setAntigravityQuota(nextState); setAntigravityQuota(nextState);
antigravityQuotaCache = nextState;
antigravityQuotaCacheLoaded = true;
} finally { } finally {
if (requestId === antigravityRequestIdRef.current) { if (requestId === antigravityRequestIdRef.current) {
setAntigravityLoading(false); setAntigravityLoading(false);
@@ -646,15 +651,20 @@ export function AuthFilesPage() {
setAntigravityQuota({}); setAntigravityQuota({});
return; return;
} }
if (antigravityQuotaCacheLoaded) {
setAntigravityQuota(antigravityQuotaCache);
return;
}
loadAntigravityQuota(); loadAntigravityQuota();
}, [antigravityFiles, loadAntigravityQuota]); }, [
antigravityFiles,
loadAntigravityQuota,
antigravityQuotaCacheLoaded,
antigravityQuotaCache
]);
// 定时刷新状态数据每240秒 // 定时刷新状态数据每240秒
useInterval(loadKeyStats, 240_000); useInterval(loadKeyStats, 240_000);
useInterval(() => {
if (antigravityFiles.length === 0) return;
loadAntigravityQuota();
}, 240_000);
// 提取所有存在的类型 // 提取所有存在的类型
const existingTypes = useMemo(() => { const existingTypes = useMemo(() => {
@@ -1191,7 +1201,9 @@ export function AuthFilesPage() {
const displayType = item.type || item.provider || 'antigravity'; const displayType = item.type || item.provider || 'antigravity';
const typeColor = getTypeColor(displayType); const typeColor = getTypeColor(displayType);
const quotaState = antigravityQuota[item.name]; const quotaState = antigravityQuota[item.name];
const quotaStatus = quotaState?.status ?? 'idle'; const quotaStatus =
quotaState?.status ??
(antigravityLoading || !antigravityQuotaCacheLoaded ? 'loading' : 'idle');
const quotaGroups = quotaState?.groups ?? []; const quotaGroups = quotaState?.groups ?? [];
return ( return (
@@ -1211,8 +1223,10 @@ export function AuthFilesPage() {
</div> </div>
<div className={styles.quotaSection}> <div className={styles.quotaSection}>
{quotaStatus === 'loading' || quotaStatus === 'idle' ? ( {quotaStatus === 'loading' ? (
<div className={styles.quotaMessage}>{t('antigravity_quota.loading')}</div> <div className={styles.quotaMessage}>{t('antigravity_quota.loading')}</div>
) : quotaStatus === 'idle' ? (
<div className={styles.quotaMessage}>{t('antigravity_quota.idle')}</div>
) : quotaStatus === 'error' ? ( ) : quotaStatus === 'error' ? (
<div className={styles.quotaError}> <div className={styles.quotaError}>
{t('antigravity_quota.load_failed', { {t('antigravity_quota.load_failed', {
@@ -1401,6 +1415,31 @@ export function AuthFilesPage() {
/> />
) : ( ) : (
<> <>
<div className={styles.antigravityControls}>
<div className={styles.antigravityControl}>
<label>{t('auth_files.page_size_label')}</label>
<select
className={styles.pageSizeSelect}
value={antigravityPageSize}
onChange={(e) => {
setAntigravityPageSize(Number(e.target.value) || 6);
setAntigravityPage(1);
}}
>
<option value={6}>6</option>
<option value={9}>9</option>
<option value={12}>12</option>
<option value={18}>18</option>
<option value={24}>24</option>
</select>
</div>
<div className={styles.antigravityControl}>
<label>{t('common.info')}</label>
<div className={styles.statsInfo}>
{antigravityFiles.length} {t('auth_files.files_count')}
</div>
</div>
</div>
<div className={styles.antigravityGrid}> <div className={styles.antigravityGrid}>
{antigravityPageItems.map(renderAntigravityCard)} {antigravityPageItems.map(renderAntigravityCard)}
</div> </div>