File indexing completed on 2024-12-15 03:45:01

0001 /*
0002     SPDX-FileCopyrightText: 2016 Volker Krause <vkrause@kde.org>
0003 
0004     SPDX-License-Identifier: MIT
0005 */
0006 
0007 #include "productmodel.h"
0008 
0009 #include <rest/restapi.h>
0010 #include <rest/restclient.h>
0011 
0012 #include <QNetworkReply>
0013 
0014 using namespace KUserFeedback::Console;
0015 
0016 ProductModel::ProductModel(QObject *parent) :
0017     QAbstractListModel(parent)
0018 {
0019 }
0020 
0021 ProductModel::~ProductModel() = default;
0022 
0023 void ProductModel::setRESTClient(RESTClient* client)
0024 {
0025     if (client != m_restClient)
0026         clear();
0027     Q_ASSERT(client);
0028     m_restClient = client;
0029     connect(m_restClient, &RESTClient::clientConnected, this, &ProductModel::reload);
0030     reload();
0031 }
0032 
0033 void ProductModel::clear()
0034 {
0035     if (m_products.isEmpty())
0036         return;
0037     beginRemoveRows({}, 0, m_products.size() - 1);
0038     m_products.clear();
0039     endRemoveRows();
0040 }
0041 
0042 void ProductModel::reload()
0043 {
0044     if (!m_restClient || !m_restClient->isConnected())
0045         return;
0046 
0047     auto reply = RESTApi::listProducts(m_restClient);
0048     connect(reply, &QNetworkReply::finished, this, [this, reply]() {
0049         if (reply->error() == QNetworkReply::NoError) {
0050             auto json = reply->readAll();
0051             mergeProducts(Product::fromJson(json));
0052         }
0053     reply->deleteLater();
0054     });
0055 }
0056 
0057 int ProductModel::rowCount(const QModelIndex& parent) const
0058 {
0059     if (parent.isValid())
0060         return 0;
0061     return m_products.size();
0062 }
0063 
0064 QVariant ProductModel::data(const QModelIndex& index, int role) const
0065 {
0066     if (!index.isValid())
0067         return {};
0068 
0069     switch (role) {
0070         case Qt::DisplayRole:
0071             return m_products.at(index.row()).name();
0072         case ProductRole:
0073             return QVariant::fromValue(m_products.at(index.row()));
0074     }
0075     return {};
0076 }
0077 
0078 QVariant ProductModel::headerData(int section, Qt::Orientation orientation, int role) const
0079 {
0080     if (orientation == Qt::Horizontal && role == Qt::DisplayRole) {
0081         switch (section) {
0082             case 0: return tr("Products");
0083         }
0084     }
0085     return QAbstractListModel::headerData(section, orientation, role);
0086 }
0087 
0088 void ProductModel::mergeProducts(QVector<Product> &&products)
0089 {
0090     std::sort(products.begin(), products.end(), [](const Product &lhs, const Product &rhs) {
0091         Q_ASSERT(lhs.isValid());
0092         Q_ASSERT(rhs.isValid());
0093         return lhs.name() < rhs.name();
0094     });
0095 
0096     auto newIt = products.cbegin();
0097     auto it = m_products.begin();
0098 
0099     while (it != m_products.end() && newIt != products.cend()) {
0100         const auto row = std::distance(m_products.begin(), it);
0101         if ((*newIt).name() < (*it).name()) {
0102             beginInsertRows({}, row, row);
0103             it = m_products.insert(it, (*newIt));
0104             endInsertRows();
0105             ++it;
0106             ++newIt;
0107         } else if ((*it).name() < (*newIt).name()) {
0108             beginRemoveRows({}, row, row);
0109             it = m_products.erase(it);
0110             endRemoveRows();
0111         } else {
0112             *it = *newIt;
0113             Q_EMIT dataChanged(index(row, 0), index(row, 0));
0114             ++it;
0115             ++newIt;
0116         }
0117     }
0118 
0119     if (it == m_products.end() && newIt != products.cend()) { // trailing insert
0120         const auto count = std::distance(newIt, products.cend());
0121         beginInsertRows({}, m_products.size(), m_products.size() + count - 1);
0122         while (newIt != products.cend())
0123             m_products.push_back(*newIt++);
0124         endInsertRows();
0125     } else if (newIt == products.cend() && it != m_products.end()) { // trailing remove
0126         const auto start = std::distance(m_products.begin(), it);
0127         const auto end = m_products.size() - 1;
0128         beginRemoveRows({}, start, end);
0129         m_products.resize(start);
0130         endResetModel();
0131     }
0132 }
0133 
0134 #include "moc_productmodel.cpp"