File indexing completed on 2024-03-24 15:40:41

0001 /*
0002     This file is part of the KDE libraries
0003     SPDX-FileCopyrightText: 2010 Teo Mrnjavac <teo@kde.org>
0004 
0005     SPDX-License-Identifier: LGPL-2.1-or-later
0006 */
0007 
0008 #include "kaboutapplicationpersonmodel_p.h"
0009 #include "debug.h"
0010 
0011 #include <QNetworkAccessManager>
0012 #include <QNetworkRequest>
0013 
0014 namespace KDEPrivate
0015 {
0016 KAboutApplicationPersonModel::KAboutApplicationPersonModel(const QList<KAboutPerson> &personList, QObject *parent)
0017     : QAbstractListModel(parent)
0018     , m_personList(personList)
0019 {
0020     m_profileList.reserve(m_personList.size());
0021     bool hasAnyAvatars{false};
0022     for (const auto &person : std::as_const(m_personList)) {
0023         KAboutApplicationPersonProfile profile = KAboutApplicationPersonProfile(person.name(), person.task(), person.emailAddress(), person.ocsUsername());
0024         profile.setHomepage(QUrl(person.webAddress()));
0025         if (!profile.ocsUsername().isEmpty()) {
0026             hasAnyAvatars = true;
0027             profile.setOcsProfileUrl(QLatin1String{"https://store.kde.org/u/%1"}.arg(profile.ocsUsername()));
0028         }
0029         m_profileList.append(profile);
0030     }
0031     m_hasAnyAvatars = hasAnyAvatars;
0032 
0033     QNetworkAccessManager *manager = new QNetworkAccessManager(this);
0034     manager->setRedirectPolicy(QNetworkRequest::NoLessSafeRedirectPolicy);
0035     connect(this, &KAboutApplicationPersonModel::showRemoteAvatarsChanged, [this, manager]() {
0036         if (showRemoteAvatars()) {
0037             int i = 0;
0038             for (const auto &profile : std::as_const(m_profileList)) {
0039                 if (!profile.ocsUsername().isEmpty()) {
0040                     if (profile.avatar().isNull()) {
0041                         QNetworkRequest request(QUrl(QLatin1String{"https://store.kde.org/avatar/%1?s=%2"}.arg(profile.ocsUsername()).arg(AVATAR_HEIGHT)));
0042                         request.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferCache);
0043                         QNetworkReply *reply = manager->get(request);
0044                         reply->setProperty("personProfile", i);
0045                         connect(reply, &QNetworkReply::finished, this, &KAboutApplicationPersonModel::onAvatarJobFinished);
0046                         m_ongoingAvatarFetches << reply;
0047                     } else {
0048                         Q_EMIT dataChanged(index(i), index(i));
0049                     }
0050                 }
0051                 ++i;
0052             }
0053         } else {
0054             // We keep the avatars around, no use deleting them - just hide them in the UI
0055             // This way we don't cause unnecessary crunching on the user's connection if
0056             // they do a bunch of toggling. Just stop the current fetches, and trust the
0057             // consumer to understand it should not show avatars if this property is false.
0058             for (QNetworkReply *reply : m_ongoingAvatarFetches) {
0059                 reply->abort();
0060             }
0061             m_ongoingAvatarFetches.clear();
0062             Q_EMIT dataChanged(index(0), index(m_profileList.count() - 1));
0063         }
0064     });
0065 }
0066 
0067 int KAboutApplicationPersonModel::rowCount(const QModelIndex &parent) const
0068 {
0069     Q_UNUSED(parent)
0070     return m_personList.count();
0071 }
0072 
0073 QVariant KAboutApplicationPersonModel::data(const QModelIndex &index, int role) const
0074 {
0075     if (!index.isValid()) {
0076         qCWarning(DEBUG_KXMLGUI) << "ERROR: invalid index";
0077         return QVariant();
0078     }
0079     if (index.row() >= rowCount()) {
0080         qCWarning(DEBUG_KXMLGUI) << "ERROR: index out of bounds";
0081         return QVariant();
0082     }
0083     if (role == Qt::DisplayRole) {
0084         //        qCDebug(DEBUG_KXMLGUI) << "Spitting data for name " << m_profileList.at( index.row() ).name();
0085         QVariant var;
0086         var.setValue(m_profileList.at(index.row()));
0087         return var;
0088     } else {
0089         return QVariant();
0090     }
0091 }
0092 
0093 Qt::ItemFlags KAboutApplicationPersonModel::flags(const QModelIndex &index) const
0094 {
0095     if (index.isValid()) {
0096         return Qt::ItemIsEnabled;
0097     }
0098     return QAbstractListModel::flags(index) | Qt::ItemIsEditable;
0099 }
0100 
0101 void KAboutApplicationPersonModel::onAvatarJobFinished() // SLOT
0102 {
0103     QNetworkReply *reply = qobject_cast<QNetworkReply *>(sender());
0104     if (reply) {
0105         m_ongoingAvatarFetches.removeAll(reply);
0106         if (reply->error() == QNetworkReply::NoError) {
0107             int personProfileListIndex = reply->property("personProfile").toInt();
0108             QNetworkAccessManager *manager = reply->manager();
0109             if (manager) {
0110                 if (reply->error() != QNetworkReply::NoError) {
0111                     Q_EMIT dataChanged(index(personProfileListIndex), index(personProfileListIndex));
0112                     return;
0113                 }
0114                 reply->waitForReadyRead(1000);
0115                 QByteArray data = reply->readAll();
0116                 QPixmap pixmap;
0117                 pixmap.loadFromData(data);
0118 
0119                 KAboutApplicationPersonProfile profile = m_profileList.value(personProfileListIndex);
0120                 if (!pixmap.isNull()) {
0121                     profile.setAvatar(pixmap);
0122                     if (!m_hasAvatarPixmaps) {
0123                         m_hasAvatarPixmaps = true;
0124                         // Data has changed for all the elements now... otherwise layouts will be all wonky in our delegates
0125                         Q_EMIT dataChanged(index(0), index(m_profileList.count() - 1));
0126                     }
0127                 } else {
0128                     // Failed to read pixmap data, so... let's load something useful maybe?
0129                 }
0130 
0131                 m_profileList.replace(personProfileListIndex, profile);
0132                 Q_EMIT dataChanged(index(personProfileListIndex), index(personProfileListIndex));
0133             }
0134         }
0135         reply->deleteLater();
0136     }
0137 }
0138 
0139 } // namespace KDEPrivate
0140 
0141 #include "moc_kaboutapplicationpersonmodel_p.cpp"