File indexing completed on 2024-05-12 16:28:09
0001 // SPDX-FileCopyrightText: 2021 Carl Schwan <carlschwan@kde.org> 0002 // SPDX-License-Identifier: GPL-3.0-only 0003 0004 #include "accountmodel.h" 0005 #include "account/abstractaccount.h" 0006 #include "account/relationship.h" 0007 #include <KLocalizedString> 0008 #include <QCoreApplication> 0009 #include <QUrlQuery> 0010 0011 AccountModel::AccountModel(QObject *parent) 0012 : TimelineModel(parent) 0013 { 0014 init(); 0015 0016 connect(this, &AccountModel::identityChanged, this, &TimelineModel::nameChanged); 0017 connect(this, &AccountModel::filtersChanged, this, [this] { 0018 fillTimeline(); 0019 }); 0020 } 0021 0022 bool AccountModel::isSelf() const 0023 { 0024 if (m_identity == nullptr) { 0025 return false; 0026 } 0027 0028 return m_account->identity()->id() == m_identity->id(); 0029 } 0030 0031 QString AccountModel::displayName() const 0032 { 0033 if (!m_identity) { 0034 return i18n("Loading"); 0035 } 0036 0037 return m_identity->displayNameHtml(); 0038 } 0039 0040 void AccountModel::fillTimeline(const QString &fromId) 0041 { 0042 if (m_accountId.isEmpty() || m_accountId.isNull()) { 0043 return; 0044 } 0045 setLoading(true); 0046 0047 // Fetch pinned posts if we are starting from the top 0048 const auto fetchPinned = fromId.isNull() && !m_excludePinned; 0049 auto uriStatus = m_account->apiUrl(QStringLiteral("/api/v1/accounts/%1/statuses").arg(m_accountId)); 0050 0051 auto statusQuery = QUrlQuery(); 0052 if (m_excludeReplies) { 0053 statusQuery.addQueryItem("exclude_replies", "true"); 0054 } 0055 if (m_excludeBoosts) { 0056 statusQuery.addQueryItem("exclude_reblogs", "true"); 0057 } 0058 if (m_onlyMedia) { 0059 statusQuery.addQueryItem("only_media", "true"); 0060 } 0061 if (!m_tagged.isEmpty()) { 0062 statusQuery.addQueryItem("tagged", m_tagged); 0063 } 0064 if (!fromId.isNull()) { 0065 statusQuery.addQueryItem("max_id", fromId); 0066 } 0067 if (!statusQuery.isEmpty()) { 0068 uriStatus.setQuery(statusQuery); 0069 } 0070 0071 auto uriPinned = m_account->apiUrl(QStringLiteral("/api/v1/accounts/%1/statuses").arg(m_accountId)); 0072 uriPinned.setQuery(QUrlQuery{{ 0073 "pinned", 0074 "true", 0075 }}); 0076 0077 const auto account = m_account; 0078 const auto id = m_accountId; 0079 0080 auto handleError = [this](QNetworkReply *reply) { 0081 Q_UNUSED(reply); 0082 setLoading(false); 0083 }; 0084 0085 auto onFetchPinned = [this, id, account](QNetworkReply *reply) { 0086 if (m_account != account || m_accountId != id) { 0087 setLoading(false); 0088 return; 0089 } 0090 const auto data = reply->readAll(); 0091 const auto doc = QJsonDocument::fromJson(data); 0092 if (!doc.isArray()) { 0093 setLoading(false); 0094 return; 0095 } 0096 const auto array = doc.array(); 0097 if (array.isEmpty()) { 0098 setLoading(false); 0099 return; 0100 } 0101 0102 QList<Post *> posts; 0103 std::transform(array.cbegin(), array.cend(), std::back_inserter(posts), [this](const QJsonValue &value) { 0104 auto post = new Post(m_account, value.toObject(), this); 0105 post->setPinned(true); 0106 return post; 0107 }); 0108 beginInsertRows({}, 0, posts.size() - 1); 0109 m_timeline = posts + m_timeline; 0110 endInsertRows(); 0111 setLoading(false); 0112 }; 0113 0114 auto onFetchAccount = [account, id, fetchPinned, uriPinned, handleError, onFetchPinned, fromId, this](QNetworkReply *reply) { 0115 if (m_account != account || m_accountId != id) { 0116 setLoading(false); 0117 return; 0118 } 0119 0120 // if we just restarted the fetch (fromId is null) then we must clear the previous array 0121 // this can happen if we just entered the profile page (okay, just a no-op) or if the filters change 0122 if (fromId.isNull()) { 0123 beginResetModel(); 0124 qDeleteAll(m_timeline); 0125 m_timeline.clear(); 0126 endResetModel(); 0127 } 0128 0129 fetchedTimeline(reply->readAll()); 0130 if (fetchPinned) { 0131 m_account->get(uriPinned, true, this, onFetchPinned, handleError); 0132 } else { 0133 setLoading(false); 0134 } 0135 }; 0136 0137 m_account->get(uriStatus, true, this, onFetchAccount, handleError); 0138 } 0139 0140 Identity *AccountModel::identity() const 0141 { 0142 return m_identity.get(); 0143 } 0144 0145 QString AccountModel::accountId() const 0146 { 0147 return m_accountId; 0148 } 0149 0150 void AccountModel::setAccountId(const QString &accountId) 0151 { 0152 if (accountId == m_accountId || accountId.isEmpty()) { 0153 return; 0154 } 0155 m_accountId = accountId; 0156 Q_EMIT accountIdChanged(); 0157 0158 if (!m_account->identityCached(accountId)) { 0159 QUrl uriAccount(m_account->instanceUri()); 0160 uriAccount.setPath(QStringLiteral("/api/v1/accounts/%1").arg(accountId)); 0161 0162 m_account->get(uriAccount, true, this, [this, accountId](QNetworkReply *reply) { 0163 const auto data = reply->readAll(); 0164 const auto doc = QJsonDocument::fromJson(data); 0165 0166 m_identity = m_account->identityLookup(accountId, doc.object()); 0167 Q_EMIT identityChanged(); 0168 updateRelationships(); 0169 }); 0170 } else { 0171 m_identity = m_account->identityLookup(accountId, {}); 0172 Q_EMIT identityChanged(); 0173 updateRelationships(); 0174 } 0175 Q_EMIT accountIdChanged(); 0176 0177 fillTimeline(); 0178 } 0179 0180 AbstractAccount *AccountModel::account() const 0181 { 0182 return m_account; 0183 } 0184 0185 void AccountModel::updateRelationships() 0186 { 0187 if (m_account->identity()->id() == m_identity->id()) { 0188 return; 0189 } 0190 0191 // Fetch relationship. Don't cache this; it's lightweight. 0192 QUrl uriRelationship(m_account->instanceUri()); 0193 uriRelationship.setPath(QStringLiteral("/api/v1/accounts/relationships")); 0194 uriRelationship.setQuery(QUrlQuery{ 0195 {QStringLiteral("id[]"), m_identity->id()}, 0196 }); 0197 0198 m_account->get(uriRelationship, true, this, [this](QNetworkReply *reply) { 0199 const auto doc = QJsonDocument::fromJson(reply->readAll()); 0200 if (!doc.isArray()) { 0201 qWarning() << "Data returned from Relationship network request is not an array" 0202 << "data: " << doc; 0203 return; 0204 } 0205 0206 // We only are requesting for a single relationship, so doc should only contain one element 0207 m_identity->setRelationship(new Relationship(m_identity.get(), doc[0].toObject())); 0208 Q_EMIT identityChanged(); 0209 }); 0210 }