Commit 4d3d32c5 authored by Volker Krause's avatar Volker Krause
Browse files

Change the timeline model to be based on reservations rather than passes

parent 803fce6c
......@@ -16,6 +16,7 @@
*/
#include <pkpassmanager.h>
#include <reservationmanager.h>
#include <timelinemodel.h>
#include <QUrl>
......@@ -33,6 +34,13 @@ private:
mgr->removePass(id);
}
void clearReservations(ReservationManager *mgr)
{
for (const auto id : mgr->reservations()) {
mgr->removeReservation(id);
}
}
private slots:
void initTestCase()
{
......@@ -43,8 +51,13 @@ private slots:
{
PkPassManager mgr;
clearPasses(&mgr);
ReservationManager resMgr;
clearReservations(&resMgr);
resMgr.setPkPassManager(&mgr);
TimelineModel model;
model.setPkPassManager(&mgr);
model.setReservationManager(&resMgr);
QSignalSpy insertSpy(&model, &TimelineModel::rowsInserted);
QVERIFY(insertSpy.isValid());
......@@ -62,8 +75,9 @@ private slots:
mgr.importPass(QUrl::fromLocalFile(QLatin1String(SOURCE_DIR "/data/boardingpass-v2.pkpass")));
QCOMPARE(insertSpy.size(), 1);
QCOMPARE(updateSpy.size(), 1);
QCOMPARE(model.rowCount(), 1);
clearPasses(&mgr);
clearReservations(&resMgr);
QCOMPARE(insertSpy.size(), 1);
QCOMPARE(updateSpy.size(), 1);
QCOMPARE(rmSpy.size(), 1);
......
......@@ -104,6 +104,8 @@ int main(int argc, char **argv)
qmlRegisterUncreatableType<KPkPass::Barcode>("org.kde.pkpass", 1, 0, "Barcode", {});
qmlRegisterUncreatableType<KPkPass::Field>("org.kde.pkpass", 1, 0, "Field", {});
qmlRegisterUncreatableType<TimelineModel>("org.kde.itinerary", 1, 0, "TimelineModel", {});
QQmlApplicationEngine engine;
engine.addImageProvider(QStringLiteral("org.kde.pkpass"), new PkPassImageProvider(&passMgr));
engine.rootContext()->setContextProperty(QStringLiteral("_pkpassManager"), &passMgr);
......@@ -111,8 +113,12 @@ int main(int argc, char **argv)
engine.rootContext()->setContextProperty(QStringLiteral("_timelineModel"), &timelineModel);
engine.load(QStringLiteral(":/main.qml"));
for (const auto &file : parser.positionalArguments())
passMgr.importPass(QUrl::fromLocalFile(file));
for (const auto &file : parser.positionalArguments()) {
if (file.endsWith(QLatin1String(".pkpass")))
passMgr.importPass(QUrl::fromLocalFile(file));
else
resMgr.importReservation(QUrl::fromLocalFile(file));
}
handleViewIntent(&passMgr);
return app.exec();
......
......@@ -163,8 +163,8 @@ void ReservationManager::removeReservation(const QString& id)
{
const auto basePath = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + QStringLiteral("/reservations/");
QFile::remove(basePath + QLatin1Char('/') + id + QLatin1String(".jsonld"));
m_reservations.remove(id);
emit reservationRemoved(id);
m_reservations.remove(id);
}
void ReservationManager::passAdded(const QString& passId)
......
......@@ -17,6 +17,11 @@
#include "timelinemodel.h"
#include "pkpassmanager.h"
#include "reservationmanager.h"
#include <KItinerary/Flight>
#include <KItinerary/JsonLdDocument>
#include <KItinerary/Reservation>
#include <KPkPass/Pass>
......@@ -24,6 +29,63 @@
#include <QDebug>
#include <QLocale>
using namespace KItinerary;
// ### the below functions probably should move to KItinerary itself
static QDate relevantDate(const QVariant &res)
{
if (res.userType() == qMetaTypeId<FlightReservation>()) {
return res.value<FlightReservation>().reservationFor().value<Flight>().departureDay();
} else if (res.userType() == qMetaTypeId<LodgingReservation>()) {
return res.value<LodgingReservation>().checkinDate().date();
}
// TODO: train, bus
return {};
}
static QDateTime relevantDateTime(const QVariant &res)
{
if (res.userType() == qMetaTypeId<FlightReservation>()) {
const auto flight = res.value<FlightReservation>().reservationFor().value<Flight>();
if (flight.boardingTime().isValid())
return flight.boardingTime();
return flight.departureTime();
}
// TODO: train, bus
return {};
}
static bool isBeforeReservation(const QVariant &lhs, const QVariant &rhs)
{
if (lhs.isNull() && rhs.isNull())
return false;
if (lhs.isNull())
return false;
if (rhs.isNull())
return true;
auto lhsDt = relevantDateTime(lhs);
auto rhsDt = relevantDateTime(rhs);
if (!lhsDt.isValid())
lhsDt = QDateTime(relevantDate(lhs), QTime(23, 59, 59));
if (!rhsDt.isValid())
rhsDt = QDateTime(relevantDate(rhs), QTime(23, 59, 59));
return lhsDt < rhsDt;
}
static QString passId(const QVariant &res)
{
const auto passTypeId = JsonLdDocument::readProperty(res, "pkpassPassTypeIdentifier").toString();
const auto serialNum = JsonLdDocument::readProperty(res, "pkpassSerialNumber").toString();
if (passTypeId.isEmpty() || serialNum.isEmpty())
return {};
return passTypeId + QLatin1Char('/') + QString::fromUtf8(serialNum.toUtf8().toBase64(QByteArray::Base64UrlEncoding));
}
TimelineModel::TimelineModel(QObject *parent)
: QAbstractListModel(parent)
{
......@@ -33,43 +95,52 @@ TimelineModel::~TimelineModel() = default;
void TimelineModel::setPkPassManager(PkPassManager* mgr)
{
beginResetModel();
m_passMgr = mgr;
m_passes = mgr->passes();
std::sort(m_passes.begin(), m_passes.end(), [this](const QString &lhs, const QString &rhs) {
return PkPassManager::relevantDate(m_passMgr->pass(lhs)) > PkPassManager::relevantDate(m_passMgr->pass(rhs));
});
connect(mgr, &PkPassManager::passAdded, this, &TimelineModel::passAdded);
connect(mgr, &PkPassManager::passUpdated, this, &TimelineModel::passUpdated);
connect(mgr, &PkPassManager::passRemoved, this, &TimelineModel::passRemoved);
endResetModel();
}
void TimelineModel::setReservationManager(ReservationManager* mgr)
{
// TODO move the above logic here
beginResetModel();
m_resMgr = mgr;
m_reservationIds = mgr->reservations();
std::sort(m_reservationIds.begin(), m_reservationIds.end(), [this](const QString &lhs, const QString &rhs) {
return isBeforeReservation(m_resMgr->reservation(lhs), m_resMgr->reservation(rhs));
});
connect(mgr, &ReservationManager::reservationAdded, this, &TimelineModel::reservationAdded);
connect(mgr, &ReservationManager::reservationUpdated, this, &TimelineModel::reservationUpdated);
connect(mgr, &ReservationManager::reservationRemoved, this, &TimelineModel::reservationRemoved);
endResetModel();
}
int TimelineModel::rowCount(const QModelIndex& parent) const
{
if (parent.isValid() || !m_passMgr)
if (parent.isValid() || !m_resMgr)
return 0;
return m_passes.size();
return m_reservationIds.size();
}
QVariant TimelineModel::data(const QModelIndex& index, int role) const
{
if (!index.isValid() || !m_passMgr)
if (!index.isValid() || !m_resMgr)
return {};
const auto res = m_resMgr->reservation(m_reservationIds.at(index.row()));
switch (role) {
case PassRole:
return QVariant::fromValue(m_passMgr->pass(m_passes.at(index.row())));
return QVariant::fromValue(m_passMgr->pass(passId(res)));
case PassIdRole:
return m_passes.at(index.row());
return passId(res);
case SectionHeader:
return QLocale().toString(PkPassManager::relevantDate(m_passMgr->pass(m_passes.at(index.row()))).date(), QLocale::ShortFormat);
return QLocale().toString(relevantDate(res), QLocale::ShortFormat);
case ReservationRole:
return res;
case ReservationTypeRole:
if (res.userType() == qMetaTypeId<FlightReservation>())
return Flight;
else if (res.userType() == qMetaTypeId<LodgingReservation>())
return Hotel;
// TODO
return QVariant();
}
return {};
}
......@@ -80,36 +151,38 @@ QHash<int, QByteArray> TimelineModel::roleNames() const
names.insert(PassRole, "pass");
names.insert(PassIdRole, "passId");
names.insert(SectionHeader, "sectionHeader");
names.insert(ReservationRole, "reservation");
names.insert(ReservationTypeRole, "type");
return names;
}
void TimelineModel::passAdded(const QString &passId)
void TimelineModel::reservationAdded(const QString &resId)
{
auto it = std::lower_bound(m_passes.begin(), m_passes.end(), passId, [this](const QString &lhs, const QString &rhs) {
return PkPassManager::relevantDate(m_passMgr->pass(lhs)) > PkPassManager::relevantDate(m_passMgr->pass(rhs));
auto it = std::lower_bound(m_reservationIds.begin(), m_reservationIds.end(), resId, [this](const QString &lhs, const QString &rhs) {
return isBeforeReservation(m_resMgr->reservation(lhs), m_resMgr->reservation(rhs));
});
auto index = std::distance(m_passes.begin(), it);
auto index = std::distance(m_reservationIds.begin(), it);
beginInsertRows({}, index, index);
m_passes.insert(it, passId);
m_reservationIds.insert(it, resId);
endInsertRows();
}
void TimelineModel::passUpdated(const QString &passId)
void TimelineModel::reservationUpdated(const QString &resId)
{
auto it = std::lower_bound(m_passes.begin(), m_passes.end(), passId, [this](const QString &lhs, const QString &rhs) {
return PkPassManager::relevantDate(m_passMgr->pass(lhs)) > PkPassManager::relevantDate(m_passMgr->pass(rhs));
auto it = std::lower_bound(m_reservationIds.begin(), m_reservationIds.end(), resId, [this](const QString &lhs, const QString &rhs) {
return isBeforeReservation(m_resMgr->reservation(lhs), m_resMgr->reservation(rhs));
});
auto row = std::distance(m_passes.begin(), it);
auto row = std::distance(m_reservationIds.begin(), it);
emit dataChanged(index(row, 0), index(row, 0));
}
void TimelineModel::passRemoved(const QString &passId)
void TimelineModel::reservationRemoved(const QString &resId)
{
auto it = std::lower_bound(m_passes.begin(), m_passes.end(), passId, [this](const QString &lhs, const QString &rhs) {
return PkPassManager::relevantDate(m_passMgr->pass(lhs)) > PkPassManager::relevantDate(m_passMgr->pass(rhs));
});
auto index = std::distance(m_passes.begin(), it);
const auto index = m_reservationIds.indexOf(resId);
if (index < 0) {
return;
}
beginRemoveRows({}, index, index);
m_passes.erase(it);
m_reservationIds.remove(index);
endRemoveRows();
}
......@@ -30,8 +30,19 @@ public:
enum Role {
PassRole = Qt::UserRole + 1,
PassIdRole,
SectionHeader
SectionHeader,
ReservationRole,
ReservationTypeRole
};
enum ReservationType {
Flight,
TrainTrip,
BusTrip,
Hotel
};
Q_ENUM(ReservationType)
explicit TimelineModel(QObject *parent = nullptr);
~TimelineModel();
......@@ -43,13 +54,13 @@ public:
QHash<int, QByteArray> roleNames() const override;
private:
void passAdded(const QString &passId);
void passUpdated(const QString &passId);
void passRemoved(const QString &passId);
void reservationAdded(const QString &resId);
void reservationUpdated(const QString &resId);
void reservationRemoved(const QString &resId);
PkPassManager *m_passMgr = nullptr;
ReservationManager *m_resMgr = nullptr;
QVector<QString> m_passes;
QVector<QString> m_reservationIds;
};
#endif // TIMELINEMODEL_H
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment