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"