feat(popup): 重做扩展云同步中心与项目文档
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
298
popup.js
298
popup.js
@@ -1,33 +1,277 @@
|
||||
document.getElementById('copyBtn').addEventListener('click', function() {
|
||||
chrome.tabs.query({ active: true, currentWindow: true }, function(tabs) {
|
||||
var activeTab = tabs && tabs[0];
|
||||
if (!activeTab || typeof activeTab.id !== 'number') {
|
||||
alert('未找到当前标签页');
|
||||
return;
|
||||
}
|
||||
|
||||
chrome.tabs.sendMessage(activeTab.id, { action: 'getMagnets' }, function(response) {
|
||||
function sendMessageToBackground(message) {
|
||||
return new Promise(function(resolve, reject) {
|
||||
chrome.runtime.sendMessage(message, function(response) {
|
||||
if (chrome.runtime.lastError) {
|
||||
alert('当前页面不支持,请在涩花塘论坛页面使用');
|
||||
reject(new Error(chrome.runtime.lastError.message));
|
||||
return;
|
||||
}
|
||||
|
||||
var magnets = response && Array.isArray(response.magnets) ? response.magnets : [];
|
||||
magnets = Array.from(new Set(magnets));
|
||||
|
||||
if (magnets.length === 0) {
|
||||
alert('当前页面未找到磁力链接');
|
||||
return;
|
||||
}
|
||||
|
||||
navigator.clipboard.writeText(magnets.join('\n'))
|
||||
.then(function() {
|
||||
alert('已复制 ' + magnets.length + ' 个磁力链接!');
|
||||
})
|
||||
.catch(function(err) {
|
||||
var errorMsg = err && err.message ? err.message : '复制失败';
|
||||
alert('复制失败:' + errorMsg);
|
||||
});
|
||||
resolve(response || null);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function setMessage(text, isError) {
|
||||
var message = document.getElementById('message');
|
||||
if (!message) return;
|
||||
message.textContent = text || '';
|
||||
message.style.color = isError ? '#ff8585' : '#8892a4';
|
||||
}
|
||||
|
||||
var lastGoodCloudStats = null;
|
||||
var lastGoodSyncStatus = null;
|
||||
var syncProgressTimer = null;
|
||||
var lastServerHealthy = null;
|
||||
|
||||
function wait(ms) {
|
||||
return new Promise(function(resolve) {
|
||||
setTimeout(resolve, ms);
|
||||
});
|
||||
}
|
||||
|
||||
function setSyncProgress(text) {
|
||||
var el = document.getElementById('syncProgressText');
|
||||
if (!el) return;
|
||||
el.textContent = text || '';
|
||||
}
|
||||
|
||||
function startSyncProgress(prefix) {
|
||||
var startedAt = Date.now();
|
||||
var dots = 0;
|
||||
if (syncProgressTimer) {
|
||||
clearInterval(syncProgressTimer);
|
||||
}
|
||||
setSyncProgress((prefix || '正在同步') + ' 0s');
|
||||
syncProgressTimer = setInterval(function() {
|
||||
dots = (dots + 1) % 4;
|
||||
var suffix = new Array(dots + 1).join('.');
|
||||
var elapsed = Math.max(0, Math.floor((Date.now() - startedAt) / 1000));
|
||||
setSyncProgress((prefix || '正在同步') + suffix + ' ' + elapsed + 's');
|
||||
}, 500);
|
||||
}
|
||||
|
||||
function stopSyncProgress(text) {
|
||||
if (syncProgressTimer) {
|
||||
clearInterval(syncProgressTimer);
|
||||
syncProgressTimer = null;
|
||||
}
|
||||
setSyncProgress(text || '');
|
||||
}
|
||||
|
||||
function updateStatus(status) {
|
||||
if (status && status.authenticated !== undefined) {
|
||||
lastGoodSyncStatus = status;
|
||||
}
|
||||
var accountDot = document.getElementById('accountDot');
|
||||
var accountText = document.getElementById('accountText');
|
||||
var email = document.getElementById('syncEmail');
|
||||
var loggedOut = document.getElementById('authLoggedOut');
|
||||
var loggedIn = document.getElementById('authLoggedIn');
|
||||
var loggedInEmail = document.getElementById('loggedInEmail');
|
||||
var healthy = !!(status && status.color === 'green');
|
||||
var authenticated = !!(status && status.authenticated);
|
||||
if (accountDot) accountDot.classList.toggle('ok', healthy);
|
||||
if (accountText) accountText.textContent = status && (status.accountText || status.text) ? (status.accountText || status.text) : '账号状态未知';
|
||||
if (email) email.textContent = status && status.email ? ('账号:' + status.email) : '';
|
||||
if (loggedOut) loggedOut.classList.toggle('hidden', authenticated);
|
||||
if (loggedIn) loggedIn.classList.toggle('hidden', !authenticated);
|
||||
if (loggedInEmail) loggedInEmail.textContent = status && status.email ? ('当前账号:' + status.email) : '当前账号:未知';
|
||||
}
|
||||
|
||||
function updateServerStatus(ok, text) {
|
||||
var serverDot = document.getElementById('serverDot');
|
||||
var serverText = document.getElementById('serverText');
|
||||
lastServerHealthy = !!ok;
|
||||
if (serverDot) serverDot.classList.toggle('ok', !!ok);
|
||||
if (serverText) serverText.textContent = text || (ok ? '服务器状态正常' : '服务器状态异常');
|
||||
}
|
||||
|
||||
function formatDateTime(value) {
|
||||
if (!value) return '暂无';
|
||||
try {
|
||||
var date = new Date(value);
|
||||
if (Number.isNaN(date.getTime())) return String(value);
|
||||
return date.toLocaleString('zh-CN');
|
||||
} catch (error) {
|
||||
return String(value);
|
||||
}
|
||||
}
|
||||
|
||||
function renderCloudStats(stats) {
|
||||
var summary = document.getElementById('cloudStatsSummary');
|
||||
var latest = document.getElementById('cloudStatsLatest');
|
||||
var tables = document.getElementById('cloudStatsTables');
|
||||
if (!summary || !latest || !tables) return;
|
||||
if (!stats || !stats.ok) {
|
||||
if (lastGoodCloudStats && lastGoodCloudStats.ok) {
|
||||
updateServerStatus(true, '服务器状态正常(展示最近一次成功数据)');
|
||||
summary.textContent = '云端状态暂时刷新失败,显示最近一次成功数据';
|
||||
latest.textContent = '最近成功更新:线程 ' + formatDateTime(lastGoodCloudStats.latest.threads) + ' | 范围 ' + formatDateTime(lastGoodCloudStats.latest.coverages) + ' | 页缓存 ' + formatDateTime(lastGoodCloudStats.latest.pages);
|
||||
tables.textContent = '上次空间占用:' + (Array.isArray(lastGoodCloudStats.tables) ? lastGoodCloudStats.tables.map(function(table) {
|
||||
return table.tableName + ' ' + table.sizeMb + 'MB';
|
||||
}).join(' | ') : '暂无');
|
||||
return;
|
||||
}
|
||||
updateServerStatus(false, '服务器状态暂时不可读');
|
||||
summary.textContent = '暂时无法读取云端状态';
|
||||
latest.textContent = '';
|
||||
tables.textContent = '';
|
||||
return;
|
||||
}
|
||||
updateServerStatus(true, '服务器状态正常');
|
||||
lastGoodCloudStats = stats;
|
||||
summary.textContent = '帖子索引 ' + stats.counts.threads + ' 条(含磁力 ' + (stats.counts.magnetThreads || 0) + ' 条)/ 页缓存 ' + stats.counts.pages + ' 条 / 范围缓存 ' + stats.counts.coverages + ' 条 / 用户 ' + stats.counts.users + ' 个 / 保险柜 ' + stats.counts.vaultItems + ' 条';
|
||||
latest.textContent = '最近更新:线程 ' + formatDateTime(stats.latest.threads) + ' | 范围 ' + formatDateTime(stats.latest.coverages) + ' | 页缓存 ' + formatDateTime(stats.latest.pages);
|
||||
tables.textContent = '空间占用:' + (Array.isArray(stats.tables) ? stats.tables.map(function(table) {
|
||||
return table.tableName + ' ' + table.sizeMb + 'MB';
|
||||
}).join(' | ') : '暂无');
|
||||
}
|
||||
|
||||
function formatBytes(bytes) {
|
||||
var value = Number(bytes || 0);
|
||||
if (!Number.isFinite(value) || value <= 0) return '0 B';
|
||||
if (value < 1024) return value + ' B';
|
||||
if (value < 1024 * 1024) return (value / 1024).toFixed(1) + ' KB';
|
||||
if (value < 1024 * 1024 * 1024) return (value / 1024 / 1024).toFixed(2) + ' MB';
|
||||
return (value / 1024 / 1024 / 1024).toFixed(2) + ' GB';
|
||||
}
|
||||
|
||||
function renderLocalStats(stats) {
|
||||
var summary = document.getElementById('localStatsSummary');
|
||||
var storage = document.getElementById('localStatsStorage');
|
||||
var meta = document.getElementById('localStatsMeta');
|
||||
if (!summary || !storage || !meta) return;
|
||||
if (!stats || !stats.ok) {
|
||||
summary.textContent = '暂时无法读取本地状态';
|
||||
storage.textContent = '';
|
||||
meta.textContent = '';
|
||||
return;
|
||||
}
|
||||
summary.textContent = '帖子索引 ' + stats.counts.threads + ' 条(含磁力 ' + (stats.counts.magnetThreads || 0) + ' 条)/ 页缓存 ' + stats.counts.pages + ' 条 / 范围缓存 ' + stats.counts.coverages + ' 条 / 收藏 ' + stats.counts.favorites + ' 条 / 历史 ' + stats.counts.history + ' 条';
|
||||
storage.textContent = '本地存储占用:' + formatBytes(stats.storage.usage) + ' / ' + formatBytes(stats.storage.quota);
|
||||
meta.textContent = '上传抑制元数据:线程 ' + stats.counts.uploadMetaThreads + ' 条 | 范围 ' + stats.counts.uploadMetaCoverages + ' 条 | 页 ' + stats.counts.uploadMetaPages + ' 条';
|
||||
}
|
||||
|
||||
async function refreshStatus() {
|
||||
try {
|
||||
var response = await sendMessageToBackground({ action: 'cloudGetSyncStatus' });
|
||||
if (response && response.ok !== false) {
|
||||
updateStatus(response || { color: 'red', text: '状态未知', authenticated: false });
|
||||
return;
|
||||
}
|
||||
if (lastGoodSyncStatus) {
|
||||
updateStatus(lastGoodSyncStatus);
|
||||
setMessage('云同步状态暂时刷新失败,显示最近一次成功状态', true);
|
||||
return;
|
||||
}
|
||||
updateStatus({ color: 'red', text: '云同步异常', authenticated: false, email: '' });
|
||||
} catch (error) {
|
||||
if (lastGoodSyncStatus) {
|
||||
updateStatus(lastGoodSyncStatus);
|
||||
setMessage('云同步状态暂时刷新失败,显示最近一次成功状态', true);
|
||||
return;
|
||||
}
|
||||
updateStatus({ color: 'red', text: '云同步异常', authenticated: false, email: '' });
|
||||
setMessage('状态获取失败:' + error.message, true);
|
||||
}
|
||||
}
|
||||
|
||||
async function refreshCloudStats() {
|
||||
var attempts = 0;
|
||||
var response = null;
|
||||
var error = null;
|
||||
for (attempts = 0; attempts < 3; attempts++) {
|
||||
try {
|
||||
response = await sendMessageToBackground({ action: 'cloudGetCacheStats' });
|
||||
if (response && response.ok) {
|
||||
renderCloudStats(response);
|
||||
return;
|
||||
}
|
||||
} catch (err) {
|
||||
error = err;
|
||||
}
|
||||
await wait(500 * (attempts + 1));
|
||||
}
|
||||
renderCloudStats(null);
|
||||
if (error) {
|
||||
setMessage('云端状态暂时刷新失败,稍后会自动重试', true);
|
||||
}
|
||||
}
|
||||
|
||||
async function refreshLocalStats() {
|
||||
try {
|
||||
var response = await sendMessageToBackground({ action: 'localGetCacheStats' });
|
||||
renderLocalStats(response);
|
||||
} catch (error) {
|
||||
renderLocalStats(null);
|
||||
}
|
||||
}
|
||||
|
||||
async function submitAuth(action) {
|
||||
var emailInput = document.getElementById('emailInput');
|
||||
var passwordInput = document.getElementById('passwordInput');
|
||||
var email = emailInput ? emailInput.value.trim() : '';
|
||||
var password = passwordInput ? passwordInput.value : '';
|
||||
if (!email || !password) {
|
||||
setMessage('请输入邮箱和密码', true);
|
||||
return;
|
||||
}
|
||||
setMessage(action === 'cloudRegister' ? '正在注册...' : '正在登录...', false);
|
||||
try {
|
||||
var response = await sendMessageToBackground({ action: action, email: email, password: password });
|
||||
if (!response || !response.ok) {
|
||||
setMessage((action === 'cloudRegister' ? '注册失败:' : '登录失败:') + (response && response.error ? response.error : '未知错误'), true);
|
||||
return;
|
||||
}
|
||||
if (passwordInput) passwordInput.value = '';
|
||||
updateStatus(response.status || { color: 'green', text: '云同步正常', authenticated: true, email: email });
|
||||
setMessage(action === 'cloudRegister' ? '注册成功,云同步已开启' : '登录成功,云同步已开启', false);
|
||||
} catch (error) {
|
||||
setMessage((action === 'cloudRegister' ? '注册失败:' : '登录失败:') + error.message, true);
|
||||
}
|
||||
}
|
||||
|
||||
async function syncNow() {
|
||||
setMessage('', false);
|
||||
startSyncProgress('正在同步本地缓存到云端');
|
||||
try {
|
||||
await sendMessageToBackground({ action: 'cloudBackfillLocalCache', force: true });
|
||||
stopSyncProgress('已提交同步任务,正在刷新状态...');
|
||||
await wait(1200);
|
||||
startSyncProgress('正在刷新云端状态');
|
||||
await refreshStatus();
|
||||
await refreshCloudStats();
|
||||
await refreshLocalStats();
|
||||
stopSyncProgress('同步完成');
|
||||
setMessage('已触发云端回填', false);
|
||||
} catch (error) {
|
||||
stopSyncProgress('同步失败');
|
||||
setMessage('同步失败:' + error.message, true);
|
||||
}
|
||||
}
|
||||
|
||||
async function logout() {
|
||||
try {
|
||||
await sendMessageToBackground({ action: 'cloudLogout' });
|
||||
await refreshStatus();
|
||||
setMessage('已退出登录', false);
|
||||
} catch (error) {
|
||||
setMessage('退出失败:' + error.message, true);
|
||||
}
|
||||
}
|
||||
|
||||
function openExternalUrl(url) {
|
||||
chrome.tabs.create({ url: url });
|
||||
}
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
document.getElementById('loginBtn').addEventListener('click', function() { submitAuth('cloudLogin'); });
|
||||
document.getElementById('registerBtn').addEventListener('click', function() { submitAuth('cloudRegister'); });
|
||||
document.getElementById('syncNowBtn').addEventListener('click', syncNow);
|
||||
document.getElementById('logoutBtn').addEventListener('click', logout);
|
||||
document.getElementById('downloadNode2Btn').addEventListener('click', function() { openExternalUrl('http://7.haory.cn/x/x72/qBittorrent_5.1.4_x64_setup.exe'); });
|
||||
document.getElementById('downloadOfficialBtn').addEventListener('click', function() { openExternalUrl('https://sourceforge.net/projects/qbittorrent/files/qbittorrent-win32/qbittorrent-5.1.4/qbittorrent_5.1.4_x64_setup.exe/download'); });
|
||||
document.getElementById('downloadMotrixBtn').addEventListener('click', function() { openExternalUrl('http://7.haory.cn/x/x72/Motrix-Setup-1.8.19.exe'); });
|
||||
refreshStatus();
|
||||
refreshCloudStats();
|
||||
refreshLocalStats();
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user