File indexing completed on 2024-05-12 05:04:17

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 "timelinemodel.h"
0006 
0007 using namespace Qt::Literals::StringLiterals;
0008 
0009 TimelineModel::TimelineModel(QObject *parent)
0010     : AbstractTimelineModel(parent)
0011     , m_manager(&AccountManager::instance())
0012 {
0013 }
0014 
0015 void TimelineModel::init()
0016 {
0017     m_manager = &AccountManager::instance();
0018     m_account = m_manager->selectedAccount();
0019 
0020     if (m_account) {
0021         connect(m_account, &AbstractAccount::streamingEvent, this, &TimelineModel::handleEvent);
0022     }
0023 
0024     connect(m_manager, &AccountManager::invalidated, this, [=](AbstractAccount *account) {
0025         if (m_account == account) {
0026             qDebug() << "Invalidating account" << account;
0027 
0028             reset();
0029             fillTimeline();
0030         }
0031     });
0032 
0033     connect(this, &TimelineModel::showBoostsChanged, this, [this] {
0034         reset();
0035         fillTimeline();
0036     });
0037     connect(this, &TimelineModel::showRepliesChanged, this, [this] {
0038         reset();
0039         fillTimeline();
0040     });
0041 
0042     connect(m_manager, &AccountManager::accountSelected, this, [=](AbstractAccount *account) {
0043         if (m_account == account) {
0044             return;
0045         }
0046 
0047         if (m_account) {
0048             disconnect(m_account, &AbstractAccount::streamingEvent, this, &TimelineModel::handleEvent);
0049         }
0050 
0051         m_account = account;
0052 
0053         connect(m_account, &AbstractAccount::streamingEvent, this, &TimelineModel::handleEvent);
0054 
0055         reset();
0056 
0057         setLoading(false);
0058 
0059         Q_EMIT nameChanged();
0060         fillTimeline();
0061     });
0062 
0063     fillTimeline();
0064 }
0065 
0066 void TimelineModel::fetchedTimeline(const QByteArray &data, bool alwaysAppendToEnd)
0067 {
0068     QList<Post *> posts;
0069 
0070     const auto doc = QJsonDocument::fromJson(data);
0071 
0072     if (!doc.isArray()) {
0073         return;
0074     }
0075 
0076     const auto array = doc.array();
0077 
0078     if (array.isEmpty()) {
0079         return;
0080     }
0081 
0082     std::transform(array.cbegin(), array.cend(), std::back_inserter(posts), [this](const QJsonValue &value) -> Post * {
0083         auto post = new Post(m_account, value.toObject(), this);
0084         if (!post->hidden()) {
0085             return post;
0086         } else {
0087             return nullptr;
0088         }
0089     });
0090 
0091     posts.erase(std::remove_if(posts.begin(),
0092                                posts.end(),
0093                                [this](Post *post) {
0094                                    if (!m_showBoosts && post->boostIdentity()) {
0095                                        return true;
0096                                    }
0097                                    if (!m_showReplies && !post->inReplyTo().isEmpty()) {
0098                                        return true;
0099                                    }
0100                                    return post == nullptr;
0101                                }),
0102                 posts.end());
0103 
0104     if (!m_timeline.isEmpty()) {
0105         if (alwaysAppendToEnd) {
0106             beginInsertRows({}, m_timeline.size(), m_timeline.size() + posts.size() - 1);
0107             m_timeline += posts;
0108             endInsertRows();
0109         } else {
0110             const auto postOld = m_timeline.first();
0111             const auto postNew = posts.first();
0112             if (postOld->originalPostId() > postNew->originalPostId()) {
0113                 const int row = m_timeline.size();
0114                 const int last = row + posts.size() - 1;
0115                 beginInsertRows({}, row, last);
0116                 m_timeline += posts;
0117                 endInsertRows();
0118             } else {
0119                 beginInsertRows({}, 0, posts.size() - 1);
0120                 m_timeline = posts + m_timeline;
0121                 endInsertRows();
0122             }
0123         }
0124     } else {
0125         beginInsertRows({}, 0, posts.size() - 1);
0126         m_timeline = posts;
0127         endInsertRows();
0128     }
0129 }
0130 
0131 void TimelineModel::fetchMore(const QModelIndex &parent)
0132 {
0133     Q_UNUSED(parent);
0134 
0135     if (m_timeline.empty()) {
0136         return;
0137     }
0138 
0139     const auto p = m_timeline.last();
0140 
0141     if (m_shouldLoadMore) {
0142         fillTimeline(p->originalPostId());
0143     } else {
0144         m_shouldLoadMore = true;
0145     }
0146 }
0147 
0148 void TimelineModel::setShouldLoadMore(bool shouldLoadMore)
0149 {
0150     m_shouldLoadMore = shouldLoadMore;
0151 }
0152 
0153 bool TimelineModel::canFetchMore(const QModelIndex &parent) const
0154 {
0155     Q_UNUSED(parent);
0156     return true;
0157 }
0158 
0159 int TimelineModel::rowCount(const QModelIndex &parent) const
0160 {
0161     Q_UNUSED(parent)
0162     return m_timeline.size();
0163 }
0164 
0165 QVariant TimelineModel::data(const QModelIndex &index, int role) const
0166 {
0167     if (!index.isValid()) {
0168         return {};
0169     }
0170     if (role == TypeRole) {
0171         return false;
0172     }
0173     return postData(m_timeline[index.row()], role);
0174 }
0175 
0176 void TimelineModel::actionReply(const QModelIndex &index)
0177 {
0178     int row = index.row();
0179     auto p = m_timeline[row];
0180 
0181     Q_EMIT wantReply(m_account, p, index);
0182 }
0183 
0184 void TimelineModel::actionFavorite(const QModelIndex &index)
0185 {
0186     const int row = index.row();
0187     const auto post = m_timeline[row];
0188     AbstractTimelineModel::actionFavorite(index, post);
0189 }
0190 
0191 void TimelineModel::actionRepeat(const QModelIndex &index)
0192 {
0193     const int row = index.row();
0194     const auto post = m_timeline[row];
0195     AbstractTimelineModel::actionRepeat(index, post);
0196 }
0197 
0198 void TimelineModel::actionVote(const QModelIndex &index, const QList<int> &choices)
0199 {
0200     const int row = index.row();
0201     const auto post = m_timeline[row];
0202     const auto poll = post->poll();
0203     Q_ASSERT(poll);
0204 
0205     QJsonObject obj;
0206     QJsonArray array;
0207     std::transform(
0208         choices.cbegin(),
0209         choices.cend(),
0210         std::back_inserter(array),
0211         [](int choice) -> auto{ return choice; });
0212     obj["choices"_L1] = array;
0213     QJsonDocument doc(obj);
0214     const auto id = poll->id();
0215 
0216     m_account->post(m_account->apiUrl(QStringLiteral("/api/v1/polls/%1/votes").arg(id)), doc, true, this, [this, id](QNetworkReply *reply) {
0217         int i = 0;
0218         for (auto &post : m_timeline) {
0219             if (post->poll() && post->poll()->id() == id) {
0220                 const auto newPoll = QJsonDocument::fromJson(reply->readAll()).object();
0221                 post->setPollJson(newPoll);
0222                 Q_EMIT dataChanged(this->index(i, 0), this->index(i, 0), {PollRole});
0223                 break;
0224             }
0225             i++;
0226         }
0227     });
0228 }
0229 
0230 void TimelineModel::actionBookmark(const QModelIndex &index)
0231 {
0232     int row = index.row();
0233     const auto post = m_timeline[row];
0234 
0235     AbstractTimelineModel::actionBookmark(index, post);
0236 }
0237 
0238 void TimelineModel::actionPin(const QModelIndex &index)
0239 {
0240     int row = index.row();
0241     const auto post = m_timeline[row];
0242 
0243     AbstractTimelineModel::actionPin(index, post);
0244 }
0245 
0246 void TimelineModel::actionRedraft(const QModelIndex &index, bool isEdit)
0247 {
0248     int row = index.row();
0249     auto p = m_timeline[row];
0250 
0251     AbstractTimelineModel::actionRedraft(index, p, isEdit);
0252 }
0253 
0254 void TimelineModel::actionDelete(const QModelIndex &index)
0255 {
0256     int row = index.row();
0257     auto p = m_timeline[row];
0258 
0259     AbstractTimelineModel::actionDelete(index, p);
0260 
0261     beginRemoveRows({}, row, row);
0262     m_timeline.removeAt(row);
0263     endRemoveRows();
0264 }
0265 
0266 void TimelineModel::handleEvent(AbstractAccount::StreamingEventType eventType, const QByteArray &payload)
0267 {
0268     if (eventType == AbstractAccount::StreamingEventType::DeleteEvent) {
0269         int i = 0;
0270         for (const auto &post : std::as_const(m_timeline)) {
0271             if (post->originalPostId().toUtf8() == payload) {
0272                 beginRemoveRows({}, i, i);
0273                 m_timeline.removeAt(i);
0274                 endRemoveRows();
0275                 break;
0276             }
0277             i++;
0278         }
0279     }
0280 }
0281 
0282 #include "moc_timelinemodel.cpp"