mirror of
https://github.com/bookorbit/bookorbit.git
synced 2026-11-03 00:11:37 -06:00
fix(reader): discard unmatched lifecycle entries
Treat an unmatched lifecycle response as terminal so built-in help documents do not retry until parked. Record the skipped title in sync activity and clear the stale parked warning. Closes #1886
This commit is contained in:
@@ -20,6 +20,7 @@ local SUCCESS_TEXT = {
|
||||
progress_push = function() return _("Sent reading progress") end,
|
||||
progress_pull = function() return _("Pulled reading progress") end,
|
||||
connection_test = function() return _("Connection test passed") end,
|
||||
lifecycle_unmatched = function() return _("Skipped a book not in BookOrbit") end,
|
||||
}
|
||||
|
||||
local ERROR_TEXT = {
|
||||
|
||||
@@ -474,9 +474,20 @@ function BookOrbit:requestLifecycleOutboxDrain(source, interactive)
|
||||
if finished then return end
|
||||
finished = true
|
||||
local latest = outbox:readLatest(started.id)
|
||||
local complete = outbox:isComplete(latest)
|
||||
local unmatched = err == "unmatched"
|
||||
local complete = unmatched or outbox:isComplete(latest)
|
||||
if complete then
|
||||
outbox:removeEntry(started.id)
|
||||
if unmatched then
|
||||
local last_error = self.settings.last_error
|
||||
if last_error and last_error.event == "lifecycle_outbox"
|
||||
and last_error.code == "outbox_blocked" then
|
||||
self.settings.last_error = nil
|
||||
end
|
||||
BookOrbitActivity.recordSuccess(
|
||||
self.settings, "lifecycle_unmatched", snap.title)
|
||||
G_reader_settings:flush()
|
||||
end
|
||||
else
|
||||
outbox:markFinished(started.id)
|
||||
self:scheduleLifecycleOutboxRetry()
|
||||
@@ -494,7 +505,7 @@ function BookOrbit:requestLifecycleOutboxDrain(source, interactive)
|
||||
end
|
||||
end
|
||||
done()
|
||||
if complete and not err then
|
||||
if complete and (not err or unmatched) then
|
||||
self:requestLifecycleOutboxDrain("recovery")
|
||||
end
|
||||
end
|
||||
|
||||
@@ -0,0 +1,134 @@
|
||||
package.path = "koreader-plugin/bookorbit.koplugin/?.lua;" .. package.path
|
||||
|
||||
local removed = 0
|
||||
local finished = 0
|
||||
local recorded_errors = 0
|
||||
local flushes = 0
|
||||
local entry = {
|
||||
id = "entry_f57f1d89ea1f37a9c43b68371cb7899d_1000_1",
|
||||
generation = 33,
|
||||
attempts = 32,
|
||||
acknowledged = {
|
||||
match = false,
|
||||
stats = false,
|
||||
annotations = false,
|
||||
state = false,
|
||||
progress = false,
|
||||
},
|
||||
snapshot = {
|
||||
digest = "f57f1d89ea1f37a9c43b68371cb7899d",
|
||||
file = "./help/quickstart-C-v2026.07.2.html",
|
||||
title = "KOReader Quickstart Guide",
|
||||
stats_ids = {},
|
||||
annotations = {},
|
||||
},
|
||||
}
|
||||
|
||||
for _, name in ipairs({ "device", "dispatcher", "ui/widget/infomessage", "pluginshare",
|
||||
"ui/trapper", "logger", "ffi/sha2", "bookorbit_api", "bookorbit_catalog",
|
||||
"bookorbit_lifecycle_outbox", "bookorbit_open_annotation_scheduler", "bookorbit_state", "bookorbit_stats_reader",
|
||||
"bookorbit_sync_coordinator", "bookorbit_sync_job_runner", "bookorbit_menu_pin" }) do
|
||||
package.loaded[name] = {}
|
||||
end
|
||||
package.loaded["gettext"] = function(text) return text end
|
||||
package.loaded["ffi/util"] = {
|
||||
template = function(text, ...)
|
||||
local values = { ... }
|
||||
return (text:gsub("%%(%d+)", function(index)
|
||||
return tostring(values[tonumber(index)])
|
||||
end))
|
||||
end,
|
||||
}
|
||||
package.loaded["ui/widget/container/widgetcontainer"] = {
|
||||
extend = function(_, value) return value end,
|
||||
}
|
||||
package.loaded["ui/widget/notification"] = { notify = function() end }
|
||||
package.loaded["ui/network/manager"] = {
|
||||
isConnected = function() return true end,
|
||||
}
|
||||
package.loaded["ui/uimanager"] = {
|
||||
nextTick = function(_, callback) callback() end,
|
||||
}
|
||||
package.loaded["libs/libkoreader-lfs"] = {
|
||||
attributes = function() return nil end,
|
||||
}
|
||||
for _, name in ipairs({ "bookorbit_main_menu", "bookorbit_progress_sync", "bookorbit_updater" }) do
|
||||
package.loaded[name] = { install = function() end }
|
||||
end
|
||||
package.loaded["bookorbit_annotations"] = {}
|
||||
package.loaded["bookorbit_bookmarks"] = {}
|
||||
package.loaded["bookorbit_highlight_summary"] = {
|
||||
add = function(summary) return summary end,
|
||||
addBookmarks = function(summary) return summary end,
|
||||
hasRemoteChanges = function() return false end,
|
||||
hasRemoteBookmarkChanges = function() return false end,
|
||||
}
|
||||
package.loaded["bookorbit_sidecar"] = {}
|
||||
package.loaded["bookorbit_sweep"] = { isRunning = function() return false end }
|
||||
package.loaded["bookorbit_sync_coordinator"] = { PRIORITY = { lifecycle = 300 } }
|
||||
package.loaded["bookorbit_state_manager"] = {
|
||||
setAccount = function() end,
|
||||
getBook = function() return nil end,
|
||||
}
|
||||
package.loaded["bookorbit_book_sync"] = {
|
||||
isRunning = function() return false end,
|
||||
run = function(opts)
|
||||
opts.on_finish("unmatched")
|
||||
return true
|
||||
end,
|
||||
}
|
||||
|
||||
local outbox = {
|
||||
nextEntry = function()
|
||||
if removed > 0 then return nil end
|
||||
return entry
|
||||
end,
|
||||
readLatest = function()
|
||||
if removed > 0 then return nil end
|
||||
return entry
|
||||
end,
|
||||
markStarted = function() return entry end,
|
||||
isComplete = function() return false end,
|
||||
isBlocked = function() return true end,
|
||||
removeEntry = function()
|
||||
removed = removed + 1
|
||||
return true
|
||||
end,
|
||||
markFinished = function() finished = finished + 1 end,
|
||||
}
|
||||
|
||||
G_reader_settings = { flush = function() flushes = flushes + 1 end }
|
||||
package.loaded["device"].model = "Kindle"
|
||||
|
||||
local Plugin = dofile("koreader-plugin/bookorbit.koplugin/main.lua")
|
||||
local plugin = setmetatable({
|
||||
settings = {
|
||||
server_url = "https://example.invalid",
|
||||
username = "reader",
|
||||
userkey = "secret",
|
||||
last_error = {
|
||||
event = "lifecycle_outbox",
|
||||
code = "outbox_blocked",
|
||||
message = "A pending lifecycle sync keeps failing and was parked.",
|
||||
},
|
||||
},
|
||||
}, { __index = Plugin })
|
||||
|
||||
plugin.getLifecycleOutbox = function() return outbox end
|
||||
plugin.submitSyncJob = function(_, job) job.run(function() end) end
|
||||
plugin.scheduleLifecycleOutboxRetry = function() end
|
||||
plugin.recordSyncError = function()
|
||||
recorded_errors = recorded_errors + 1
|
||||
end
|
||||
|
||||
plugin:requestLifecycleOutboxDrain("manual", true)
|
||||
|
||||
assert(removed == 1, "an unmatched lifecycle entry is removed")
|
||||
assert(finished == 0, "an unmatched lifecycle entry is not parked again")
|
||||
assert(recorded_errors == 0, "an unmatched document does not produce a lifecycle error")
|
||||
assert(plugin.settings.last_error == nil, "the stale parked warning is cleared")
|
||||
assert(plugin.settings.activity_log[1].kind == "lifecycle_unmatched", "the skip is visible in Sync status")
|
||||
assert(plugin.settings.activity_log[1].sub == "KOReader Quickstart Guide", "Sync status identifies the document")
|
||||
assert(flushes == 1, "the resolved lifecycle state is persisted")
|
||||
|
||||
print("bookorbit_lifecycle_unmatched_test.lua: ok")
|
||||
Reference in New Issue
Block a user