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

0001 // SPDX-FileCopyrightText: 2021 kaniini <https://git.pleroma.social/kaniini>
0002 // SPDX-FileCopyrightText: 2021 Carl Schwan <carlschwan@kde.org>
0003 // SPDX-License-Identifier: GPL-3.0-only
0004 
0005 #include "threadmodel.h"
0006 #include "account/abstractaccount.h"
0007 #include "timelinemodel.h"
0008 #include <KLocalizedString>
0009 #include <QJsonObject>
0010 #include <qstringliteral.h>
0011 
0012 ThreadModel::ThreadModel(QObject *parent)
0013     : TimelineModel(parent)
0014 {
0015     init();
0016 }
0017 
0018 QVariant ThreadModel::data(const QModelIndex &index, int role) const
0019 {
0020     if (role == SelectedRole) {
0021         return m_postId == TimelineModel::data(index, IdRole).toString();
0022     }
0023 
0024     return TimelineModel::data(index, role);
0025 }
0026 
0027 QString ThreadModel::displayName() const
0028 {
0029     return i18nc("@title", "Thread");
0030 }
0031 
0032 QString ThreadModel::postId() const
0033 {
0034     return m_postId;
0035 }
0036 
0037 void ThreadModel::setPostId(const QString &postId)
0038 {
0039     if (m_postId == postId) {
0040         return;
0041     }
0042     m_postId = postId;
0043     Q_EMIT postIdChanged();
0044 
0045     fillTimeline();
0046 }
0047 
0048 void ThreadModel::fillTimeline(const QString &fromId)
0049 {
0050     Q_UNUSED(fromId)
0051 
0052     if (m_postId.isNull() || m_postId.isEmpty()) {
0053         return;
0054     }
0055 
0056     setLoading(true);
0057 
0058     if (!m_account) {
0059         m_account = AccountManager::instance().selectedAccount();
0060     }
0061 
0062     const auto statusUrl = m_account->apiUrl(QString("/api/v1/statuses/%1").arg(m_postId));
0063     const auto contextUrl = m_account->apiUrl(QString("/api/v1/statuses/%1/context").arg(m_postId));
0064     auto thread = std::make_shared<QList<Post *>>();
0065 
0066     auto handleError = [this](QNetworkReply *reply) {
0067         Q_UNUSED(reply);
0068         setLoading(false);
0069     };
0070 
0071     auto onFetchContext = [=](QNetworkReply *reply) {
0072         const auto data = reply->readAll();
0073         const auto doc = QJsonDocument::fromJson(data);
0074         const auto obj = doc.object();
0075 
0076         if (!doc.isObject()) {
0077             return;
0078         }
0079 
0080         auto ancestors = obj["ancestors"].toArray().toVariantList();
0081         std::reverse(ancestors.begin(), ancestors.end());
0082 
0083         for (const auto &ancestor : ancestors) {
0084             if (ancestor.canConvert<QJsonObject>() || ancestor.canConvert<QVariantMap>()) {
0085                 thread->push_front(new Post(m_account, ancestor.toJsonObject(), this));
0086             }
0087         }
0088 
0089         const auto descendents = obj["descendants"].toArray();
0090 
0091         for (const auto &descendent : descendents) {
0092             if (!descendent.isObject()) {
0093                 continue;
0094             }
0095 
0096             thread->push_back(new Post(m_account, descendent.toObject(), this));
0097         }
0098 
0099         beginResetModel();
0100         m_timeline = *thread;
0101         endResetModel();
0102         setLoading(false);
0103     };
0104 
0105     auto onFetchStatus = [=](QNetworkReply *reply) {
0106         const auto data = reply->readAll();
0107         const auto doc = QJsonDocument::fromJson(data);
0108         const auto obj = doc.object();
0109 
0110         if (!doc.isObject()) {
0111             return;
0112         }
0113         thread->push_front(new Post(m_account, obj, this));
0114         m_account->get(contextUrl, true, this, onFetchContext, handleError);
0115     };
0116 
0117     m_account->get(statusUrl, true, this, onFetchStatus, handleError);
0118 }
0119 
0120 bool ThreadModel::canFetchMore(const QModelIndex &parent) const
0121 {
0122     Q_UNUSED(parent);
0123     return false;
0124 }