Introduce more comprehensive helper functions

This commit is contained in:
jpirnay
2026-05-04 17:24:08 +02:00
parent 42a9c09e4c
commit ae746a8127
2 changed files with 78 additions and 73 deletions
+75 -73
View File
@@ -95,8 +95,7 @@ void KOReaderSyncActivity::onWifiSelectionComplete(const bool success) {
logSyncMemSnapshot("after_performSync"); logSyncMemSnapshot("after_performSync");
} }
void KOReaderSyncActivity::performSync() { bool KOReaderSyncActivity::calculateDocumentHash() {
// Calculate document hash based on user's preferred method
if (KOREADER_STORE.getMatchMethod() == DocumentMatchMethod::FILENAME) { if (KOREADER_STORE.getMatchMethod() == DocumentMatchMethod::FILENAME) {
documentHash = KOReaderDocumentId::calculateFromFilename(epubPath); documentHash = KOReaderDocumentId::calculateFromFilename(epubPath);
} else { } else {
@@ -109,81 +108,48 @@ void KOReaderSyncActivity::performSync() {
statusMessage = tr(STR_HASH_FAILED); statusMessage = tr(STR_HASH_FAILED);
} }
requestUpdate(true); requestUpdate(true);
return; return false;
} }
LOG_DBG("KOSync", "Document hash: %s", documentHash.c_str()); LOG_DBG("KOSync", "Document hash: %s", documentHash.c_str());
return true;
}
// Local mapping is only needed for compare/upload paths. bool KOReaderSyncActivity::handleAutoPushPreflight() {
// Pull-only modes can skip this expensive step and go straight to remote fetch. KOReaderSyncClient::beginPersistentSession();
if (syncIntent != KOReaderSyncIntentState::PULL_REMOTE && syncIntent != KOReaderSyncIntentState::AUTO_PULL) { KOReaderProgress warmupProgress;
// Precompute local mapping before first network request so the expensive const auto warmupResult = KOReaderSyncClient::getProgress(documentHash, warmupProgress);
// inflate/index work happens before TLS. This avoids a second local mapping if (warmupResult != KOReaderSyncClient::OK && warmupResult != KOReaderSyncClient::NOT_FOUND) {
// pass later and keeps the upload path lightweight. KOReaderSyncClient::endPersistentSession();
{ {
RenderLock lock(*this); RenderLock lock(*this);
statusMessage = tr(STR_MAPPING_LOCAL); state = SYNC_FAILED;
} statusMessage = KOReaderSyncClient::errorString(warmupResult);
requestUpdateAndWait(); const char* detail = KOReaderSyncClient::lastFailureDetail();
if (!computeLocalProgressAndChapter()) { if (detail && detail[0]) {
{ statusMessage += "";
RenderLock lock(*this); statusMessage += detail;
state = SYNC_FAILED;
statusMessage = tr(STR_SYNC_FAILED_MSG);
} }
requestUpdate(true);
return;
} }
requestUpdate(true);
return false;
} }
// Auto-push must not overwrite progress that is already further along on the server.
// Drop EPUB state before HTTPS to maximize contiguous heap for TLS. if (syncIntent == KOReaderSyncIntentState::AUTO_PUSH && warmupResult == KOReaderSyncClient::OK &&
releaseEpubForMapping(); warmupProgress.percentage > localProgress.percentage) {
LOG_DBG("KOSync", "AUTO_PUSH skipped: remote %.4f >= local %.4f", warmupProgress.percentage,
// Push intent skips comparison UI but still warms an HTTP/TLS session first localProgress.percentage);
// so PUT can reuse the connection instead of forcing a fresh handshake. KOReaderSyncClient::endPersistentSession();
if (syncIntent == KOReaderSyncIntentState::PUSH_LOCAL || syncIntent == KOReaderSyncIntentState::AUTO_PUSH) { HalClock::wifiOff(true);
// Direct push previously started with no reusable HTTP/TLS session, forcing APP_STATE.koReaderSyncSession.outcome = KOReaderSyncOutcomeState::UPLOAD_COMPLETE;
// a fresh handshake in updateProgress. Compare flow often succeeds because APP_STATE.saveToFile();
// upload reuses the GET session. Warm the session here so push can take the resumeReader(KOReaderSyncOutcomeState::UPLOAD_COMPLETE);
// same reuse path without showing comparison UI. return false;
KOReaderSyncClient::beginPersistentSession();
KOReaderProgress warmupProgress;
const auto warmupResult = KOReaderSyncClient::getProgress(documentHash, warmupProgress);
if (warmupResult != KOReaderSyncClient::OK && warmupResult != KOReaderSyncClient::NOT_FOUND) {
KOReaderSyncClient::endPersistentSession();
{
RenderLock lock(*this);
state = SYNC_FAILED;
statusMessage = KOReaderSyncClient::errorString(warmupResult);
const char* detail = KOReaderSyncClient::lastFailureDetail();
if (detail && detail[0]) {
statusMessage += "";
statusMessage += detail;
}
}
requestUpdate(true);
return;
}
// Auto-push must not overwrite progress that is already further along on the server.
// Compare percentages from the warmup GET; users opted into automatic sync, so a
// remote-ahead state is treated as "nothing to do" and falls straight back to home.
if (syncIntent == KOReaderSyncIntentState::AUTO_PUSH && warmupResult == KOReaderSyncClient::OK &&
warmupProgress.percentage > localProgress.percentage) {
LOG_DBG("KOSync", "AUTO_PUSH skipped: remote %.4f >= local %.4f", warmupProgress.percentage,
localProgress.percentage);
KOReaderSyncClient::endPersistentSession();
HalClock::wifiOff(true);
// Reuse UPLOAD_COMPLETE outcome so the resume path is identical to a successful push;
// there is nothing to apply to the reader and progress.bin already reflects local state.
APP_STATE.koReaderSyncSession.outcome = KOReaderSyncOutcomeState::UPLOAD_COMPLETE;
APP_STATE.saveToFile();
resumeReader(KOReaderSyncOutcomeState::UPLOAD_COMPLETE);
return;
}
performUpload();
return;
} }
return true;
}
void KOReaderSyncActivity::performFetchAndCompare() {
{ {
RenderLock lock(*this); RenderLock lock(*this);
statusMessage = tr(STR_FETCH_PROGRESS); statusMessage = tr(STR_FETCH_PROGRESS);
@@ -213,7 +179,6 @@ void KOReaderSyncActivity::performSync() {
if (syncIntent == KOReaderSyncIntentState::AUTO_PULL) { if (syncIntent == KOReaderSyncIntentState::AUTO_PULL) {
// Auto-pull at book open: nothing to apply, just open the book with local progress. // Auto-pull at book open: nothing to apply, just open the book with local progress.
// No user-visible failure since the user opted into "open with sync, if available".
KOReaderSyncClient::endPersistentSession(); KOReaderSyncClient::endPersistentSession();
HalClock::wifiOff(true); HalClock::wifiOff(true);
resumeReader(KOReaderSyncOutcomeState::CANCELLED); resumeReader(KOReaderSyncOutcomeState::CANCELLED);
@@ -236,8 +201,6 @@ void KOReaderSyncActivity::performSync() {
{ {
RenderLock lock(*this); RenderLock lock(*this);
state = SYNC_FAILED; state = SYNC_FAILED;
// Combine the short category label with the rich diagnostic so users (and bug
// reports) can tell network/TLS/server/heap failures apart at a glance.
statusMessage = KOReaderSyncClient::errorString(result); statusMessage = KOReaderSyncClient::errorString(result);
const char* detail = KOReaderSyncClient::lastFailureDetail(); const char* detail = KOReaderSyncClient::lastFailureDetail();
if (detail && detail[0]) { if (detail && detail[0]) {
@@ -326,10 +289,6 @@ void KOReaderSyncActivity::performSync() {
state = SHOWING_RESULT; state = SHOWING_RESULT;
// Default to the option that corresponds to the furthest progress. // Default to the option that corresponds to the furthest progress.
// Compare in the shared CrossPoint coordinate system (spine → page → paragraph)
// rather than percentage, since percentages are derived differently on each
// side and lose resolution. Remote has already been mapped via
// ensureRemotePositionMapped() at this point.
auto isLocalAhead = [&]() { auto isLocalAhead = [&]() {
if (remotePosition.spineIndex < 0) { if (remotePosition.spineIndex < 0) {
return localProgress.percentage > remoteProgress.percentage; // mapping unavailable; fall back return localProgress.percentage > remoteProgress.percentage; // mapping unavailable; fall back
@@ -350,6 +309,49 @@ void KOReaderSyncActivity::performSync() {
requestUpdate(true); requestUpdate(true);
} }
void KOReaderSyncActivity::performSync() {
if (!calculateDocumentHash()) {
return;
}
// Local mapping is only needed for compare/upload paths.
// Pull-only modes can skip this expensive step and go straight to remote fetch.
if (syncIntent != KOReaderSyncIntentState::PULL_REMOTE && syncIntent != KOReaderSyncIntentState::AUTO_PULL) {
// Precompute local mapping before first network request so the expensive
// inflate/index work happens before TLS. This avoids a second local mapping
// pass later and keeps the upload path lightweight.
{
RenderLock lock(*this);
statusMessage = tr(STR_MAPPING_LOCAL);
}
requestUpdateAndWait();
if (!computeLocalProgressAndChapter()) {
{
RenderLock lock(*this);
state = SYNC_FAILED;
statusMessage = tr(STR_SYNC_FAILED_MSG);
}
requestUpdate(true);
return;
}
}
// Drop EPUB state before HTTPS to maximize contiguous heap for TLS.
releaseEpubForMapping();
// Push intent skips comparison UI but still warms an HTTP/TLS session first
// so PUT can reuse the connection instead of forcing a fresh handshake.
if (syncIntent == KOReaderSyncIntentState::PUSH_LOCAL || syncIntent == KOReaderSyncIntentState::AUTO_PUSH) {
if (!handleAutoPushPreflight()) {
return;
}
performUpload();
return;
}
performFetchAndCompare();
}
void KOReaderSyncActivity::performUpload() { void KOReaderSyncActivity::performUpload() {
{ {
RenderLock lock(*this); RenderLock lock(*this);
@@ -103,6 +103,9 @@ class KOReaderSyncActivity final : public Activity {
void onWifiSelectionComplete(bool success); void onWifiSelectionComplete(bool success);
void performSync(); void performSync();
bool calculateDocumentHash();
bool handleAutoPushPreflight();
void performFetchAndCompare();
void performUpload(); void performUpload();
void closeCancelled(); void closeCancelled();
void resumeReader(KOReaderSyncOutcomeState outcome, const SyncResult* appliedResult = nullptr); void resumeReader(KOReaderSyncOutcomeState outcome, const SyncResult* appliedResult = nullptr);