File indexing completed on 2024-05-12 05:04:10
0001 // SPDX-FileCopyrightText: 2023 Joshua Goins <josh@redstrate.com> 0002 // SPDX-License-Identifier: GPL-3.0-or-later 0003 0004 #include "socialgraphmodel.h" 0005 0006 #include "abstractaccount.h" 0007 #include "accountmanager.h" 0008 #include "relationship.h" 0009 0010 #include <KLocalizedString> 0011 0012 using namespace Qt::Literals::StringLiterals; 0013 0014 SocialGraphModel::SocialGraphModel(QObject *parent) 0015 : QAbstractListModel(parent) 0016 { 0017 } 0018 0019 QString SocialGraphModel::name() const 0020 { 0021 return m_followListName; 0022 } 0023 0024 QString SocialGraphModel::displayName() const 0025 { 0026 if (m_followListName == QStringLiteral("request")) { 0027 return i18nc("@title", "Follow Requests"); 0028 } else if (m_followListName == QStringLiteral("followers")) { 0029 return i18nc("@title", "Followers"); 0030 } else if (m_followListName == QStringLiteral("following")) { 0031 return i18nc("@title", "Following"); 0032 } else if (m_followListName == QStringLiteral("mutes")) { 0033 return i18nc("@title", "Muted Users"); 0034 } else if (m_followListName == QStringLiteral("blocks")) { 0035 return i18nc("@title", "Blocked Users"); 0036 } else if (m_followListName == QStringLiteral("featured")) { 0037 return i18nc("@title", "Featured Users"); 0038 } else if (m_followListName == QStringLiteral("favourited_by")) { 0039 return i18ncp("@title", "%1 favourite", "%1 favourites", m_count); 0040 } else if (m_followListName == QStringLiteral("reblogged_by")) { 0041 return i18ncp("@title", "%1 boost", "%1 boosts", m_count); 0042 } 0043 return {}; 0044 } 0045 0046 QString SocialGraphModel::placeholderText() const 0047 { 0048 if (m_followListName == QStringLiteral("request")) { 0049 return i18n("No follow requests"); 0050 } else if (m_followListName == QStringLiteral("followers")) { 0051 return i18n("No followers"); 0052 } else if (m_followListName == QStringLiteral("following")) { 0053 return i18n("No followed users"); 0054 } else if (m_followListName == QStringLiteral("mutes")) { 0055 return i18n("No muted users"); 0056 } else if (m_followListName == QStringLiteral("blocks")) { 0057 return i18n("No blocked users"); 0058 } else if (m_followListName == QStringLiteral("featured")) { 0059 return i18n("No featured users"); 0060 } else if (m_followListName == QStringLiteral("favourited_by")) { 0061 return i18n("No users favourited this post"); 0062 } else if (m_followListName == QStringLiteral("reblogged_by")) { 0063 return i18n("No users boosted this post"); 0064 } 0065 return {}; 0066 } 0067 0068 void SocialGraphModel::setName(const QString &followlistname) 0069 { 0070 if (followlistname == m_followListName) { 0071 return; 0072 } 0073 0074 m_followListName = followlistname; 0075 Q_EMIT nameChanged(); 0076 fillTimeline(); 0077 } 0078 0079 QString SocialGraphModel::accountId() const 0080 { 0081 return m_accountId; 0082 } 0083 0084 void SocialGraphModel::setAccountId(const QString &accountId) 0085 { 0086 m_accountId = accountId; 0087 Q_EMIT accountIdChanged(); 0088 fillTimeline(); 0089 } 0090 0091 QString SocialGraphModel::statusId() const 0092 { 0093 return m_statusId; 0094 } 0095 0096 void SocialGraphModel::setStatusId(const QString &statusId) 0097 { 0098 m_statusId = statusId; 0099 Q_EMIT statusIdChanged(); 0100 fillTimeline(); 0101 } 0102 0103 int SocialGraphModel::count() const 0104 { 0105 return m_count; 0106 } 0107 0108 void SocialGraphModel::setCount(int count) 0109 { 0110 m_count = count; 0111 } 0112 0113 QVariant SocialGraphModel::data(const QModelIndex &index, int role) const 0114 { 0115 Q_ASSERT(checkIndex(index, QAbstractItemModel::CheckIndexOption::IndexIsValid)); 0116 0117 const auto identity = m_accounts[index.row()].get(); 0118 switch (role) { 0119 case CustomRoles::IdentityRole: 0120 return QVariant::fromValue<Identity *>(identity); 0121 default: 0122 Q_UNREACHABLE(); 0123 } 0124 } 0125 0126 int SocialGraphModel::rowCount(const QModelIndex &) const 0127 { 0128 return m_accounts.count(); 0129 } 0130 0131 QHash<int, QByteArray> SocialGraphModel::roleNames() const 0132 { 0133 return { 0134 {CustomRoles::IdentityRole, "identity"}, 0135 }; 0136 } 0137 0138 bool SocialGraphModel::loading() const 0139 { 0140 return m_loading; 0141 } 0142 0143 void SocialGraphModel::setLoading(bool loading) 0144 { 0145 if (m_loading == loading) { 0146 return; 0147 } 0148 m_loading = loading; 0149 Q_EMIT loadingChanged(); 0150 } 0151 0152 bool SocialGraphModel::isFollowRequest() const 0153 { 0154 return m_followListName == QStringLiteral("request"); 0155 } 0156 0157 bool SocialGraphModel::isFollowing() const 0158 { 0159 return m_followListName == QStringLiteral("following"); 0160 } 0161 0162 bool SocialGraphModel::isFollower() const 0163 { 0164 return m_followListName == QStringLiteral("followers"); 0165 } 0166 0167 void SocialGraphModel::actionAllow(const QModelIndex &index) 0168 { 0169 auto account = AccountManager::instance().selectedAccount(); 0170 0171 if (!checkIndex(index, QAbstractItemModel::CheckIndexOption::IndexIsValid)) 0172 return; 0173 0174 auto requestIdentity = m_accounts[index.row()].get(); 0175 const auto requestIdentityId = requestIdentity->id(); 0176 0177 account->post(account->apiUrl(QStringLiteral("/api/v1/follow_requests/%1/authorize").arg(requestIdentityId)), 0178 QJsonDocument{}, 0179 true, 0180 this, 0181 [this, requestIdentity, index](QNetworkReply *reply) { 0182 const auto newRelation = QJsonDocument::fromJson(reply->readAll()).object(); 0183 0184 m_accounts[index.row()]->setRelationship(new Relationship(requestIdentity, newRelation)); 0185 0186 beginRemoveRows(QModelIndex(), index.row(), index.row()); 0187 m_accounts.removeAt(index.row()); 0188 endRemoveRows(); 0189 }); 0190 0191 account->checkForFollowRequests(); 0192 } 0193 0194 void SocialGraphModel::actionDeny(const QModelIndex &index) 0195 { 0196 auto account = AccountManager::instance().selectedAccount(); 0197 0198 if (!checkIndex(index, QAbstractItemModel::CheckIndexOption::IndexIsValid)) 0199 return; 0200 0201 auto requestIdentity = m_accounts[index.row()].get(); 0202 const auto requestIdentityId = requestIdentity->id(); 0203 0204 account->post(account->apiUrl(QStringLiteral("/api/v1/follow_requests/%1/reject").arg(requestIdentityId)), 0205 QJsonDocument{}, 0206 true, 0207 this, 0208 [this, requestIdentity, index](QNetworkReply *reply) { 0209 const auto newRelation = QJsonDocument::fromJson(reply->readAll()).object(); 0210 0211 m_accounts[index.row()]->setRelationship(new Relationship(requestIdentity, newRelation)); 0212 0213 beginRemoveRows(QModelIndex(), index.row(), index.row()); 0214 m_accounts.removeAt(index.row()); 0215 endRemoveRows(); 0216 }); 0217 0218 account->checkForFollowRequests(); 0219 } 0220 0221 bool SocialGraphModel::canFetchMore(const QModelIndex &parent) const 0222 { 0223 Q_UNUSED(parent); 0224 return !m_next.isEmpty(); 0225 } 0226 0227 void SocialGraphModel::fetchMore(const QModelIndex &parent) 0228 { 0229 Q_UNUSED(parent); 0230 0231 fillTimeline(); 0232 } 0233 0234 void SocialGraphModel::fillTimeline() 0235 { 0236 auto account = AccountManager::instance().selectedAccount(); 0237 0238 if (m_followListName.isEmpty() || m_followListName.isNull()) { 0239 return; 0240 } 0241 0242 if ((m_followListName == QStringLiteral("followers") || m_followListName == QStringLiteral("following")) 0243 && (m_accountId.isEmpty() || m_accountId.isNull())) { 0244 return; 0245 } 0246 0247 if ((m_followListName == QStringLiteral("favourited_by") || m_followListName == QStringLiteral("reblogged_by")) 0248 && (m_statusId.isEmpty() || m_statusId.isNull())) { 0249 return; 0250 } 0251 0252 if (m_loading) { 0253 return; 0254 } 0255 setLoading(true); 0256 0257 QString uri; 0258 if (m_followListName == QStringLiteral("request")) { 0259 uri = QStringLiteral("/api/v1/follow_requests"); 0260 } else if (m_followListName == QStringLiteral("followers")) { 0261 uri = QStringLiteral("/api/v1/accounts/%1/followers").arg(m_accountId); 0262 } else if (m_followListName == QStringLiteral("following")) { 0263 uri = QStringLiteral("/api/v1/accounts/%1/following").arg(m_accountId); 0264 } else if (m_followListName == QStringLiteral("mutes")) { 0265 uri = QStringLiteral("/api/v1/mutes"); 0266 } else if (m_followListName == QStringLiteral("blocks")) { 0267 uri = QStringLiteral("/api/v1/blocks"); 0268 } else if (m_followListName == QStringLiteral("featured")) { 0269 uri = QStringLiteral("/api/v1/endorsements"); 0270 } else if (m_followListName == QStringLiteral("favourited_by")) { 0271 uri = QStringLiteral("/api/v1/statuses/%1/favourited_by").arg(m_statusId); 0272 } else if (m_followListName == QStringLiteral("reblogged_by")) { 0273 uri = QStringLiteral("/api/v1/statuses/%1/reblogged_by").arg(m_statusId); 0274 } 0275 0276 QUrl url; 0277 if (m_next.isEmpty()) { 0278 url = account->apiUrl(uri); 0279 } else { 0280 url = m_next; 0281 } 0282 0283 account->get(url, true, this, [this, account](QNetworkReply *reply) { 0284 const auto followRequestResult = QJsonDocument::fromJson(reply->readAll()); 0285 const auto accounts = followRequestResult.array(); 0286 0287 if (!accounts.isEmpty()) { 0288 static QRegularExpression re(QStringLiteral("<(.*)>; rel=\"next\"")); 0289 const auto next = reply->rawHeader(QByteArrayLiteral("Link")); 0290 const auto match = re.match(QString::fromUtf8(next)); 0291 if (re.isValid()) { 0292 m_next = QUrl::fromUserInput(match.captured(1)); 0293 } 0294 0295 QList<std::shared_ptr<Identity>> fetchedAccounts; 0296 0297 std::transform( 0298 accounts.cbegin(), 0299 accounts.cend(), 0300 std::back_inserter(fetchedAccounts), 0301 [account](const QJsonValue &value) -> auto{ 0302 const auto identityJson = value.toObject(); 0303 return account->identityLookup(identityJson["id"_L1].toString(), identityJson); 0304 }); 0305 0306 beginInsertRows({}, m_accounts.size(), m_accounts.size() + fetchedAccounts.size() - 1); 0307 m_accounts += fetchedAccounts; 0308 endInsertRows(); 0309 } 0310 0311 setLoading(false); 0312 }); 0313 } 0314 0315 #include "moc_socialgraphmodel.cpp"