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

0001 // SPDX-FileCopyrightText: 2022 Carl Schwan <carl@carlschwan.eu>
0002 // SPDX-License-Identifier: LGPL-2.0-or-later
0003 
0004 #include "maintimelinemodel.h"
0005 #include "account/abstractaccount.h"
0006 #include "timeline/timelinemodel.h"
0007 #include <KLocalizedString>
0008 #include <QUrlQuery>
0009 
0010 MainTimelineModel::MainTimelineModel(QObject *parent)
0011     : TimelineModel(parent)
0012 {
0013     init();
0014 }
0015 
0016 QString MainTimelineModel::name() const
0017 {
0018     return m_timelineName;
0019 }
0020 
0021 QString MainTimelineModel::displayName() const
0022 {
0023     if (m_timelineName == "home") {
0024         if (m_manager && m_manager->rowCount() > 1) {
0025             if (m_manager->selectedAccount() == nullptr) {
0026                 return i18n("Loading");
0027             }
0028             return i18nc("@title", "Home (%1)", m_manager->selectedAccount()->identity()->displayNameHtml());
0029         } else {
0030             return i18nc("@title", "Home");
0031         }
0032     } else if (m_timelineName == "public") {
0033         return i18nc("@title", "Local Timeline");
0034     } else if (m_timelineName == "federated") {
0035         return i18nc("@title", "Global Timeline");
0036     } else if (m_timelineName == "bookmarks") {
0037         return i18nc("@title", "Bookmarks");
0038     } else if (m_timelineName == "favourites") {
0039         return i18nc("@title", "Favourites");
0040     } else if (m_timelineName == "trending") {
0041         return i18nc("@title", "Trending");
0042     }
0043 
0044     return {};
0045 }
0046 
0047 void MainTimelineModel::setName(const QString &timelineName)
0048 {
0049     if (timelineName == m_timelineName) {
0050         return;
0051     }
0052 
0053     m_timelineName = timelineName;
0054     Q_EMIT nameChanged();
0055     setLoading(false);
0056     fillTimeline({});
0057 }
0058 
0059 void MainTimelineModel::fillTimeline(const QString &from_id)
0060 {
0061     static const QSet<QString> validTimelines = {"home", "public", "federated", "bookmarks", "favourites", "trending"};
0062     static const QSet<QString> publicTimelines = {"home", "public", "federated"};
0063 
0064     if (!m_account || m_loading || !validTimelines.contains(m_timelineName)) {
0065         return;
0066     }
0067 
0068     setLoading(true);
0069 
0070     const bool local = m_timelineName == "public";
0071 
0072     QUrlQuery q;
0073     if (local) {
0074         q.addQueryItem("local", "true");
0075     }
0076     if (!from_id.isEmpty()) {
0077         q.addQueryItem("max_id", from_id);
0078     }
0079 
0080     QUrl uri;
0081     if (publicTimelines.contains(m_timelineName)) {
0082         // federated timeline is really "public" without local set
0083         const QString apiUrl = QStringLiteral("/api/v1/timelines/%1").arg(m_timelineName == "federated" ? "public" : m_timelineName);
0084         uri = m_account->apiUrl(apiUrl);
0085         uri.setQuery(q);
0086     } else {
0087         // Fixes issues where on reaching the end the data is fetched from the start
0088         if (m_next.isEmpty() && !m_timeline.isEmpty()) {
0089             setLoading(false);
0090             return;
0091         }
0092         uri =
0093             m_next.isEmpty() ? m_account->apiUrl(QStringLiteral("/api/v1/%1").arg(m_timelineName == "trending" ? "trends/statuses" : m_timelineName)) : m_next;
0094     }
0095 
0096     auto account = m_account;
0097     auto currentTimelineName = m_timelineName;
0098     m_account->get(
0099         uri,
0100         true,
0101         this,
0102         [this, currentTimelineName, account](QNetworkReply *reply) {
0103             if (m_account != account || m_timelineName != currentTimelineName) {
0104                 setLoading(false);
0105                 return;
0106             }
0107 
0108             if (publicTimelines.contains(m_timelineName)) {
0109                 fetchedTimeline(reply->readAll());
0110             } else {
0111                 static QRegularExpression re("<(.*)>; rel=\"next\"");
0112                 const auto next = reply->rawHeader(QByteArrayLiteral("Link"));
0113                 const auto match = re.match(next);
0114                 m_next = QUrl::fromUserInput(match.captured(1));
0115                 fetchedTimeline(reply->readAll(), true);
0116             }
0117         },
0118         [this](QNetworkReply *reply) {
0119             Q_UNUSED(reply)
0120             setLoading(false);
0121         });
0122 }
0123 
0124 void MainTimelineModel::handleEvent(AbstractAccount::StreamingEventType eventType, const QByteArray &payload)
0125 {
0126     TimelineModel::handleEvent(eventType, payload);
0127     if (eventType == AbstractAccount::StreamingEventType::UpdateEvent && m_timelineName == "home") {
0128         const auto doc = QJsonDocument::fromJson(payload);
0129         const auto post = new Post(m_account, doc.object(), this);
0130         beginInsertRows({}, 0, 0);
0131         m_timeline.push_front(post);
0132         endInsertRows();
0133     }
0134 }