gitlink_help_center/static/js/reading-progress.js

272 lines
7.8 KiB
JavaScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

(function() {
'use strict';
const STORAGE_KEY = 'gitlink_reading_progress';
const BADGE_CLASS = 'reading-progress-badge';
function getReadDocs() {
try {
const data = localStorage.getItem(STORAGE_KEY);
return data ? JSON.parse(data) : [];
} catch (e) {
console.error('读取进度失败:', e);
return [];
}
}
function saveReadDocs(docs) {
try {
localStorage.setItem(STORAGE_KEY, JSON.stringify(docs));
} catch (e) {
console.error('保存进度失败:', e);
}
}
function normalizePath(path) {
if (!path) return '/';
// URL 解码(处理中文路径)
try {
path = decodeURIComponent(path);
} catch (e) {
// 如果解码失败,保持原样
}
path = path.replace(/^\/+/, '').replace(/\/+$/, '');
return '/' + path;
}
function markAsRead() {
const currentPath = normalizePath(window.location.pathname);
const readDocs = getReadDocs();
if (!readDocs.includes(currentPath)) {
readDocs.push(currentPath);
saveReadDocs(readDocs);
console.log('✅ 已标记为已阅读:', currentPath);
updateBadges();
} else {
console.log(' 已经标记过了:', currentPath);
}
}
function isDocRead(path) {
const readDocs = getReadDocs();
const normalizedPath = normalizePath(path);
return readDocs.some(docPath => normalizePath(docPath) === normalizedPath);
}
function createBadge() {
const badge = document.createElement('span');
badge.className = BADGE_CLASS;
badge.textContent = '✓';
badge.style.cssText = `
display: inline-flex !important;
align-items: center !important;
justify-content: center !important;
width: 18px !important;
height: 18px !important;
background-color: #22c55e !important;
color: white !important;
border-radius: 50% !important;
font-size: 11px !important;
font-weight: bold !important;
flex-shrink: 0 !important;
margin-left: 8px !important;
visibility: visible !important;
opacity: 1 !important;
position: relative !important;
`;
return badge;
}
function updateBadges() {
const readDocs = getReadDocs();
console.log('📚 已阅读的文档数:', readDocs.length, readDocs);
if (readDocs.length === 0) {
console.log('⚠️ 还没有已阅读的文档');
return;
}
// 清除所有旧标记
document.querySelectorAll('.' + BADGE_CLASS).forEach(badge => badge.remove());
// 获取所有可能的链接元素
const selectors = [
'a.menu__link',
'a.menu-link',
'.menu a[href^="/"]',
'.sidebar a[href^="/"]',
'nav a[href^="/"]',
'[class*="sidebar"] a[href^="/"]',
'[class*="menu"] a[href^="/"]'
];
let totalLinks = 0;
let matchedLinks = 0;
selectors.forEach(selector => {
const links = document.querySelectorAll(selector);
links.forEach(link => {
totalLinks++;
const href = link.getAttribute('href');
if (href && !href.startsWith('http') && !href.startsWith('#') && href !== '/') {
const linkPath = normalizePath(href);
if (isDocRead(linkPath)) {
matchedLinks++;
if (!link.querySelector('.' + BADGE_CLASS)) {
const badge = createBadge();
link.appendChild(badge);
console.log('✓ 添加标记:', linkPath, '->', link.textContent.trim().substring(0, 30));
}
}
}
});
});
console.log(`🔍 检查了 ${totalLinks} 个链接,匹配了 ${matchedLinks} 个已阅读文档`);
}
function initProgressBar() {
if (document.querySelector('[data-reading-progress-bar]')) {
return;
}
const progressBar = document.createElement('div');
progressBar.setAttribute('data-reading-progress-bar', 'true');
progressBar.style.cssText = `
position: fixed !important;
top: 0 !important;
left: 0 !important;
width: 100% !important;
height: 4px !important;
background-color: rgba(70, 106, 255, 0.1) !important;
z-index: 9999 !important;
pointer-events: none !important;
`;
const bar = document.createElement('div');
bar.style.cssText = `
height: 100% !important;
background: linear-gradient(90deg, #466aff, #6b8cff) !important;
width: 0% !important;
transition: width 0.15s ease-out !important;
`;
progressBar.appendChild(bar);
document.body.appendChild(progressBar);
let ticking = false;
window.addEventListener('scroll', () => {
if (!ticking) {
requestAnimationFrame(() => {
const windowHeight = window.innerHeight;
const documentHeight = document.documentElement.scrollHeight - windowHeight;
const scrolled = window.scrollY;
const progress = (scrolled / documentHeight) * 100;
bar.style.width = Math.min(progress, 100) + '%';
ticking = false;
});
ticking = true;
}
});
}
function initScrollDetection() {
let hasMarked = false;
function checkScroll() {
const scrollPosition = window.innerHeight + window.scrollY;
const pageHeight = document.documentElement.scrollHeight;
const threshold = 200;
const progress = Math.min((scrollPosition / pageHeight) * 100, 100);
if (!hasMarked && (progress >= 90 || (pageHeight - scrollPosition < threshold))) {
hasMarked = true;
console.log('📖 滚动进度: ' + Math.round(progress) + '%,标记为已阅读');
markAsRead();
setTimeout(() => { hasMarked = false; }, 3000);
}
}
window.addEventListener('scroll', () => checkScroll());
setTimeout(checkScroll, 1000);
}
function init() {
console.log('🚀 初始化阅读进度追踪...');
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', () => setTimeout(initAll, 500));
} else {
setTimeout(initAll, 500);
}
}
function initAll() {
console.log('⚙️ 启动功能...');
initProgressBar();
updateBadges();
initScrollDetection();
// 定期更新
setInterval(updateBadges, 3000);
}
// 启动
init();
// 监听路由变化
let lastUrl = location.href;
new MutationObserver(() => {
const url = location.href;
if (url !== lastUrl) {
lastUrl = url;
console.log('🔄 页面变化,更新标记');
setTimeout(updateBadges, 500);
}
}).observe(document, { subtree: true, childList: true });
// 暴露调试接口
window.readingProgressDebug = {
getReadDocs,
markAsRead,
updateBadges,
clearAll: () => {
localStorage.removeItem(STORAGE_KEY);
document.querySelectorAll('.' + BADGE_CLASS).forEach(b => b.remove());
console.log('🗑️ 已清除所有阅读记录');
updateBadges();
},
test: () => {
console.log('🧪 开始测试...');
const currentPath = normalizePath(window.location.pathname);
console.log('当前路径:', currentPath);
console.log('已阅读文档:', getReadDocs());
const links = document.querySelectorAll('a[href^="/"]');
console.log('找到链接数:', links.length);
links.forEach(link => {
const href = link.getAttribute('href');
if (href && !href.startsWith('http') && href !== '/') {
console.log('链接:', normalizePath(href), '文本:', link.textContent.trim());
}
});
updateBadges();
}
};
console.log('✅ 阅读进度追踪已加载');
console.log('🔧 调试命令:');
console.log(' readingProgressDebug.markAsRead() - 手动标记当前页面');
console.log(' readingProgressDebug.updateBadges() - 更新侧边栏标记');
console.log(' readingProgressDebug.test() - 运行测试');
console.log(' readingProgressDebug.clearAll() - 清除所有记录');
})();