fix: keep reading bookmarks bound to route

This commit is contained in:
kuyua9
2026-05-12 18:10:42 +08:00
parent 6517eeb1a2
commit e2c9e0f06d
3 changed files with 93 additions and 22 deletions
@@ -43,7 +43,7 @@
import { computed, nextTick, ref, onMounted, onUnmounted, watch } from 'vue' import { computed, nextTick, ref, onMounted, onUnmounted, watch } from 'vue'
import { useRoute } from 'vitepress' import { useRoute } from 'vitepress'
import { import {
createReadingBookmark, createReadingBookmarkSnapshot,
readReadingBookmark, readReadingBookmark,
writeReadingBookmark writeReadingBookmark
} from '../utils/readingBookmark.js' } from '../utils/readingBookmark.js'
@@ -125,24 +125,40 @@ const progressTitle = computed(() =>
: `${bookmarkTitle.value} · 阅读进度 ${progress.value}%` : `${bookmarkTitle.value} · 阅读进度 ${progress.value}%`
) )
const saveBookmark = () => { const clearBookmarkSaveTimer = () => {
if (saveTimer) {
window.clearTimeout(saveTimer)
saveTimer = null
}
}
const clearClickSaveTimer = () => {
if (clickSaveTimer) {
window.clearTimeout(clickSaveTimer)
clickSaveTimer = null
}
}
const saveBookmark = (path = currentPath()) => {
writeReadingBookmark( writeReadingBookmark(
getClientStorage(), getClientStorage(),
createReadingBookmark({ createReadingBookmarkSnapshot({
path: currentPath(), path,
title: articleTitle.value, getTitle: () => articleTitle.value,
section: activeSection.value, getSection: () => activeSection.value,
scrollY: window.scrollY, getScrollY: () => window.scrollY,
progress: progress.value getProgress: () => progress.value
}) })
) )
} }
const scheduleBookmarkSave = () => { const scheduleBookmarkSave = () => {
if (saveTimer) { const path = currentPath()
window.clearTimeout(saveTimer) clearBookmarkSaveTimer()
} saveTimer = window.setTimeout(() => {
saveTimer = window.setTimeout(saveBookmark, 180) saveTimer = null
saveBookmark(path)
}, 180)
} }
const updateProgress = () => { const updateProgress = () => {
@@ -325,12 +341,12 @@ const handleClick = () => {
behavior: 'smooth' behavior: 'smooth'
}) })
if (clickSaveTimer) { const path = currentPath()
window.clearTimeout(clickSaveTimer) clearClickSaveTimer()
}
clickSaveTimer = window.setTimeout(() => { clickSaveTimer = window.setTimeout(() => {
clickSaveTimer = null
updateProgress() updateProgress()
saveBookmark() saveBookmark(path)
}, 400) }, 400)
} }
@@ -344,15 +360,11 @@ onUnmounted(() => {
if (scrollTimer) { if (scrollTimer) {
clearTimeout(scrollTimer) clearTimeout(scrollTimer)
} }
if (saveTimer) { clearBookmarkSaveTimer()
clearTimeout(saveTimer)
}
if (restoreTimer) { if (restoreTimer) {
clearTimeout(restoreTimer) clearTimeout(restoreTimer)
} }
if (clickSaveTimer) { clearClickSaveTimer()
clearTimeout(clickSaveTimer)
}
// 清理拖拽事件 // 清理拖拽事件
document.removeEventListener('mousemove', onDrag) document.removeEventListener('mousemove', onDrag)
document.removeEventListener('mouseup', endDrag) document.removeEventListener('mouseup', endDrag)
@@ -366,6 +378,8 @@ onUnmounted(() => {
watch( watch(
() => route.path, () => route.path,
() => { () => {
clearBookmarkSaveTimer()
clearClickSaveTimer()
resetRouteState() resetRouteState()
restoreBookmark() restoreBookmark()
} }
@@ -27,6 +27,24 @@ export const createReadingBookmark = ({
updatedAt: now() updatedAt: now()
}) })
export const createReadingBookmarkSnapshot = ({
path,
getTitle = () => '',
getSection = () => '',
getScrollY = () => 0,
getProgress = () => 0,
now = () => Date.now()
}) =>
createReadingBookmark({
path,
title: getTitle(),
section: getSection(),
scrollY: getScrollY(),
progress: getProgress(),
now
})
const normalizeBookmark = ( const normalizeBookmark = (
value, value,
expectedPath, expectedPath,
@@ -3,6 +3,7 @@ import { describe, it } from 'node:test'
import { import {
createReadingBookmark, createReadingBookmark,
createReadingBookmarkSnapshot,
getReadingBookmarkKey, getReadingBookmarkKey,
readReadingBookmark, readReadingBookmark,
writeReadingBookmark writeReadingBookmark
@@ -62,6 +63,44 @@ describe('reading bookmarks', () => {
) )
}) })
it('keeps delayed saves bound to the path captured before navigation', () => {
const storage = createStorage()
let currentPath = '/easy-vibe/zh-cn/page-a/'
const scheduledPath = currentPath
currentPath = '/easy-vibe/zh-cn/page-b/'
writeReadingBookmark(
storage,
createReadingBookmarkSnapshot({
path: scheduledPath,
getTitle: () => '页面 A',
getSection: () => '小节 A',
getScrollY: () => 240,
getProgress: () => 32,
now: () => 777
})
)
assert.equal(
readReadingBookmark(storage, currentPath, 1000),
null
)
assert.deepEqual(
readReadingBookmark(storage, scheduledPath, 1000),
{
version: 1,
path: scheduledPath,
title: '页面 A',
section: '小节 A',
scrollY: 240,
progress: 32,
updatedAt: 777
}
)
})
it('normalizes invalid numeric values', () => { it('normalizes invalid numeric values', () => {
assert.deepEqual( assert.deepEqual(
createReadingBookmark({ createReadingBookmark({