Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion apps/readest-sync/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ if(READEST_DESKTOP OR READEST_HOST_TESTS)
add_subdirectory(tests)
endif()
set(PRESENTATION_SOURCES src/controller.cpp src/controller.h src/library_model.cpp src/library_model.h
src/operation_runner.cpp src/device_adapter.cpp src/cover_loader.cpp src/thumbnail_cache.cpp)
src/book_status.cpp src/operation_runner.cpp src/device_adapter.cpp src/cover_loader.cpp src/thumbnail_cache.cpp)
if(NOT READEST_HOST_TESTS OR READEST_DESKTOP)
add_library(readest-presentation STATIC ${PRESENTATION_SOURCES})
set_target_properties(readest-presentation PROPERTIES AUTOMOC ON)
Expand Down
4 changes: 4 additions & 0 deletions apps/readest-sync/CONTEXT.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ Readest library.
A book represented in both the PocketBook and Readest libraries, shown as one
entry in Readest Sync.

**Book status**:
The displayed summary of a book's availability and relationship to Readest,
including removal from Readest or a pending upload.

**PocketBook reading position**:
The reading location saved by the PocketBook native reader for a device book.

Expand Down
21 changes: 21 additions & 0 deletions apps/readest-sync/src/book_status.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#include "book_status.h"
#include "application.h"

QString bookStatusLabel(const readest::LibraryEntry& entry) {
using readest::Availability;
if(entry.book.book.deleted) return "Removed from Readest";
if(entry.upload_pending) return "Upload pending";
if(entry.book.local_only) return "PocketBook only";
if(entry.availability==Availability::OnDevice && entry.book.epubs==0)
return "On device · Readest progress only";
switch(entry.availability) {
case Availability::OnDevice: return "On device";
case Availability::Downloadable: return "Available to download";
case Availability::ProgressOnly: return "Progress only";
case Availability::Unknown: return "Not checked";
case Availability::Unavailable: return "EPUB unavailable";
case Availability::Multiple: return "Multiple EPUBs";
case Availability::Removed: return "Removed from cloud";
}
return {};
}
6 changes: 6 additions & 0 deletions apps/readest-sync/src/book_status.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#pragma once
#include <QString>

namespace readest { struct LibraryEntry; }

QString bookStatusLabel(const readest::LibraryEntry& entry);
10 changes: 3 additions & 7 deletions apps/readest-sync/src/controller.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#include "controller.h"
#include "book_status.h"
#include <QCoreApplication>
#include <QTimer>
using namespace readest;
Expand Down Expand Up @@ -106,22 +107,17 @@ QString AppController::blockingMessage() const {
}
QString AppController::hint() const {
const auto* entry=library_.find(selected_); if(!entry) return {};
auto text=(entry->book.book.deleted?QStringLiteral("Removed from Readest"):entry->book.local_only?QStringLiteral("PocketBook only"):availabilityLabel(entry->availability))+". "+QString::fromStdString(entry->book.book.author);
auto text=bookStatusLabel(*entry)+". "+QString::fromStdString(entry->book.book.author);
if(!entry->book.local_only && !entry->book.book.deleted && entry->book.epubs==0)
text+=entry->availability==Availability::OnDevice?"\nReadest has progress only. You can upload this local EPUB.":"\nReadest has progress only. No local EPUB is available to upload.";
text+="\nPocketBook: "+percentageLabel(entry->local_percentage)+" · Readest: "+percentageLabel(entry->remote_percentage);
return text+"\nPercentages use each reader’s page counts. — means unavailable.";
}
QVariantMap AppController::book() const {
const auto* entry=library_.find(selected_); if(!entry) return {};
auto availability=entry->book.book.deleted?QStringLiteral("Removed from Readest"):
entry->upload_pending?QStringLiteral("Upload pending"):
entry->book.local_only?QStringLiteral("PocketBook only"):availabilityLabel(entry->availability);
if(entry->availability==Availability::OnDevice && entry->book.epubs==0)
availability=QStringLiteral("On device · Readest progress only");
return {{"title",QString::fromStdString(entry->book.book.title.empty()?"Untitled":entry->book.book.title)},
{"author",QString::fromStdString(entry->book.book.author)},
{"availability",availability},{"coverPath",QString::fromStdString(entry->cover)},
{"availability",bookStatusLabel(*entry)},{"coverPath",QString::fromStdString(entry->cover)},
{"pocketBookProgress",percentageLabel(entry->local_percentage)},
{"readestProgress",percentageLabel(entry->remote_percentage)}};
}
Expand Down
17 changes: 2 additions & 15 deletions apps/readest-sync/src/library_model.cpp
Original file line number Diff line number Diff line change
@@ -1,19 +1,7 @@
#include "library_model.h"
#include "book_status.h"
#include <algorithm>
QString percentageLabel(double value) { return value<0?QStringLiteral("—"):QString::number(value,'f',1)+"%"; }
QString availabilityLabel(readest::Availability value) {
using readest::Availability;
switch(value) {
case Availability::OnDevice: return "On device";
case Availability::Downloadable: return "Available to download";
case Availability::ProgressOnly: return "Progress only";
case Availability::Unknown: return "Not checked";
case Availability::Unavailable: return "EPUB unavailable";
case Availability::Multiple: return "Multiple EPUBs";
case Availability::Removed: return "Removed from cloud";
}
return {};
}
int LibraryModel::rowCount(const QModelIndex& parent) const {
return parent.isValid()?0:std::max(0,std::min(capacity_,count()-page_*capacity_));
}
Expand All @@ -27,8 +15,7 @@ QVariant LibraryModel::data(const QModelIndex& index,int role) const {
case BookHash: return QString::fromStdString(entry.id.hash);
case Title: return QString::fromStdString(book.title.empty()?"Untitled":book.title);
case Author: return QString::fromStdString(book.author);
case Availability: return book.deleted?QStringLiteral("Removed from Readest"):entry.upload_pending?QStringLiteral("Upload pending"):entry.book.local_only?QStringLiteral("PocketBook only"):
entry.availability==readest::Availability::OnDevice && entry.book.epubs==0?QStringLiteral("On device · Readest progress only"):availabilityLabel(entry.availability);
case Availability: return bookStatusLabel(entry);
case Cover: return QString::fromStdString(entry.cover);
case LocalPath: return QString::fromStdString(entry.book.path);
case LocalProgress: return percentageLabel(entry.local_percentage);
Expand Down
1 change: 0 additions & 1 deletion apps/readest-sync/src/library_model.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
#include "application.h"
#include <QAbstractListModel>

QString availabilityLabel(readest::Availability value);
QString percentageLabel(double value);
class LibraryModel : public QAbstractListModel {
Q_OBJECT
Expand Down
46 changes: 45 additions & 1 deletion apps/readest-sync/tests/qt_ui_test.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// Link the production controller/model/runner normally; test public contracts.
#include "controller.h"
#include "book_status.h"
#include "cover_provider.h"
#include "network_route.h"
#include <QTemporaryDir>
Expand Down Expand Up @@ -55,6 +56,49 @@ static ApplicationConfig configAt(const QString& base) {
assert(QDir().mkpath(QString::fromStdString(config.root)));
return config;
}
static void bookStatusChecks() {
struct Scenario {
Availability availability;
int epubs;
bool deleted,upload_pending,local_only;
const char* expected;
};
const Scenario scenarios[]={
{Availability::OnDevice,0,true,true,false,"Removed from Readest"},
{Availability::OnDevice,0,true,false,false,"Removed from Readest"},
{Availability::OnDevice,0,false,true,false,"Upload pending"},
{Availability::OnDevice,0,false,true,true,"Upload pending"},
{Availability::OnDevice,0,false,false,true,"PocketBook only"},
{Availability::OnDevice,0,false,false,false,"On device · Readest progress only"},
{Availability::OnDevice,1,false,false,false,"On device"},
{Availability::Downloadable,1,false,false,false,"Available to download"},
{Availability::ProgressOnly,0,false,false,false,"Progress only"},
{Availability::Unknown,0,false,false,false,"Not checked"},
{Availability::Unavailable,0,false,false,false,"EPUB unavailable"},
{Availability::Multiple,2,false,false,false,"Multiple EPUBs"},
{Availability::Removed,0,false,false,false,"Removed from cloud"},
{Availability::Removed,0,true,false,false,"Removed from Readest"}
};
QTemporaryDir temp; assert(temp.isValid());
AppController control(configAt(temp.path()),DeviceAccess{});
assert(control.book().isEmpty() && control.hint().isEmpty());
for(const auto& scenario:scenarios) {
LibraryEntry entry;
entry.id={"fixture-user",std::string(32,'a')};
entry.book.book.author="Fixture author";
entry.availability=scenario.availability;entry.book.epubs=scenario.epubs;
entry.book.book.deleted=scenario.deleted;entry.upload_pending=scenario.upload_pending;
entry.book.local_only=scenario.local_only;
const auto expected=QString::fromUtf8(scenario.expected);
assert(bookStatusLabel(entry)==expected);
auto* model=control.library();
model->replace({entry});
control.selectBook(QString::fromStdString(entry.id.account),QString::fromStdString(entry.id.hash));
assert(model->data(model->index(0),LibraryModel::Availability).toString()==expected);
assert(control.book().value("availability").toString()==expected);
assert(control.hint().startsWith(expected+". Fixture author\n"));
}
}
static void runnerChecks() {
{
// An online operation must hold standby off before waking Wi-Fi,
Expand Down Expand Up @@ -219,7 +263,7 @@ static void runnerChecks() {
}
int main(int argc,char** argv) {
QQuickWindow::setGraphicsApi(QSGRendererInterface::Software);
QGuiApplication app(argc,argv); runnerChecks();
QGuiApplication app(argc,argv); bookStatusChecks(); runnerChecks();
QTemporaryDir temp; assert(temp.isValid());
auto config=configAt(temp.path());
std::atomic<int> cover_requests{0};
Expand Down