File indexing completed on 2024-05-12 16:28:09

0001 // SPDX-FileCopyrightText: 2022 Carl Schwan <carlschwan@kde.org>
0002 // SPDX-License-Identifier: LGPL-2.0-or-later
0003 
0004 #include "searchmodel.h"
0005 #include "account/abstractaccount.h"
0006 #include "account/account.h"
0007 #include "timeline/accountmodel.h"
0008 #include <KLocalizedString>
0009 #include <QNetworkReply>
0010 #include <QUrlQuery>
0011 #include <algorithm>
0012 
0013 SearchModel::SearchModel(QObject *parent)
0014     : AbstractTimelineModel(parent)
0015 {
0016     m_account = AccountManager::instance().selectedAccount();
0017 
0018     connect(&AccountManager::instance(), &AccountManager::invalidated, this, [=](AbstractAccount *account) {
0019         if (m_account == account) {
0020             beginResetModel();
0021             clear();
0022             endResetModel();
0023         }
0024     });
0025 
0026     connect(&AccountManager::instance(), &AccountManager::accountSelected, this, [=](AbstractAccount *account) {
0027         if (m_account != account) {
0028             m_account = account;
0029             beginResetModel();
0030             clear();
0031             endResetModel();
0032         }
0033     });
0034 }
0035 
0036 SearchModel::~SearchModel() = default;
0037 
0038 void SearchModel::search(const QString &queryString)
0039 {
0040     auto url = m_account->apiUrl("/api/v2/search");
0041     url.setQuery({{"q", queryString}});
0042     setLoading(true);
0043     setLoaded(false);
0044     m_account->get(url, true, this, [this](QNetworkReply *reply) {
0045         const auto searchResult = QJsonDocument::fromJson(reply->readAll()).object();
0046         const auto statuses = searchResult[QStringLiteral("statuses")].toArray();
0047 
0048         beginResetModel();
0049         clear();
0050 
0051         std::transform(
0052             statuses.cbegin(),
0053             statuses.cend(),
0054             std::back_inserter(m_statuses),
0055             [this](const QJsonValue &value) -> auto{ return new Post(m_account, value.toObject(), this); });
0056         const auto accounts = searchResult[QStringLiteral("accounts")].toArray();
0057         std::transform(
0058             accounts.cbegin(),
0059             accounts.cend(),
0060             std::back_inserter(m_accounts),
0061             [this](const QJsonValue &value) -> auto{
0062                 const auto account = value.toObject();
0063                 return m_account->identityLookup(account["id"].toString(), account);
0064             });
0065         const auto hashtags = searchResult[QStringLiteral("hashtags")].toArray();
0066         std::transform(
0067             hashtags.cbegin(),
0068             hashtags.cend(),
0069             std::back_inserter(m_hashtags),
0070             [](const QJsonValue &value) -> auto{ return SearchHashtag(value.toObject()); });
0071         endResetModel();
0072         setLoading(false);
0073         setLoaded(true);
0074     });
0075 }
0076 
0077 int SearchModel::rowCount(const QModelIndex &parent) const
0078 {
0079     Q_UNUSED(parent);
0080 
0081     return m_accounts.count() + m_statuses.count() + m_hashtags.count();
0082 }
0083 
0084 QVariant SearchModel::data(const QModelIndex &index, int role) const
0085 {
0086     const auto row = index.row();
0087 
0088     const bool isStatus = row >= m_accounts.count() && row < m_accounts.count() + m_statuses.size();
0089     const bool isHashtag = row >= m_accounts.size() + m_statuses.count();
0090 
0091     if (role == TypeRole) {
0092         if (isHashtag) {
0093             return Hashtag;
0094         } else if (isStatus) {
0095             return Status;
0096         } else {
0097             return Account;
0098         }
0099     }
0100 
0101     if (isStatus) {
0102         const auto post = m_statuses[row - m_accounts.count()];
0103         return postData(post, role);
0104     }
0105 
0106     if (isHashtag) {
0107         const auto hashtag = m_hashtags[row - m_accounts.count() - m_statuses.count()];
0108         switch (role) {
0109         case IdRole:
0110             return hashtag.getName();
0111         }
0112     }
0113 
0114     const auto identity = m_accounts[row];
0115     switch (role) {
0116     case AuthorIdentityRole:
0117         return QVariant::fromValue<Identity *>(identity.get());
0118     }
0119 
0120     return {};
0121 }
0122 
0123 void SearchModel::clear()
0124 {
0125     m_accounts.clear();
0126     qDeleteAll(m_statuses);
0127     m_statuses.clear();
0128     m_hashtags.clear();
0129     setLoading(false);
0130     setLoaded(false);
0131 }
0132 
0133 QString SearchModel::labelForType(ResultType sectionType)
0134 {
0135     switch (sectionType) {
0136     case Account:
0137         return i18n("People");
0138     case Hashtag:
0139         return i18n("Hashtags");
0140     case Status:
0141         return i18n("Post");
0142     default:
0143         return {};
0144     }
0145 }
0146 
0147 bool SearchModel::loaded() const
0148 {
0149     return m_loaded;
0150 }
0151 
0152 void SearchModel::setLoaded(bool loaded)
0153 {
0154     if (m_loaded == loaded) {
0155         return;
0156     }
0157     m_loaded = loaded;
0158     Q_EMIT loadedChanged();
0159 }
0160 
0161 SearchHashtag::SearchHashtag(const QJsonObject &object)
0162 {
0163     m_name = object["name"].toString();
0164 }
0165 
0166 QString SearchHashtag::getName() const
0167 {
0168     return m_name;
0169 }