File indexing completed on 2024-05-12 16:28:09
0001 // SPDX-FileCopyrightText: 2021 Carl Schwan <carlschwan@kde.org> 0002 // SPDX-License-Identifier: GPL-3.0-or-later 0003 0004 #include "abstracttimelinemodel.h" 0005 #include "account/abstractaccount.h" 0006 #include "account/identity.h" 0007 #include "editor/attachmenteditormodel.h" 0008 #include "editor/posteditorbackend.h" 0009 #include "poll.h" 0010 #include "post.h" 0011 #include <KLocalizedString> 0012 #include <QtMath> 0013 #include <qvariant.h> 0014 0015 AbstractTimelineModel::AbstractTimelineModel(QObject *parent) 0016 : QAbstractListModel(parent) 0017 { 0018 } 0019 0020 bool AbstractTimelineModel::loading() const 0021 { 0022 return m_loading; 0023 } 0024 0025 void AbstractTimelineModel::setLoading(bool loading) 0026 { 0027 if (m_loading == loading) { 0028 return; 0029 } 0030 m_loading = loading; 0031 Q_EMIT loadingChanged(); 0032 } 0033 0034 QHash<int, QByteArray> AbstractTimelineModel::roleNames() const 0035 { 0036 return { 0037 {IdRole, QByteArrayLiteral("id")}, 0038 {OriginalIdRole, QByteArrayLiteral("originalId")}, 0039 {UrlRole, QByteArrayLiteral("url")}, 0040 {ContentRole, QByteArrayLiteral("content")}, 0041 {SpoilerTextRole, QByteArrayLiteral("spoilerText")}, 0042 {AuthorIdentityRole, QByteArrayLiteral("authorIdentity")}, 0043 {PublishedAtRole, QByteArrayLiteral("publishedAt")}, 0044 {VisibilityRole, QByteArrayLiteral("visibility")}, 0045 {SelectedRole, QByteArrayLiteral("selected")}, 0046 {FiltersRole, QByteArrayLiteral("filters")}, 0047 {RelativeTimeRole, QByteArrayLiteral("relativeTime")}, 0048 {AbsoluteTimeRole, QByteArrayLiteral("absoluteTime")}, 0049 {SensitiveRole, QByteArrayLiteral("sensitive")}, 0050 0051 // Additional content 0052 {CardRole, QByteArrayLiteral("card")}, 0053 {ApplicationRole, QByteArrayLiteral("application")}, 0054 {PollRole, QByteArrayLiteral("poll")}, 0055 {MentionsRole, QByteArrayLiteral("mentions")}, 0056 {AttachmentsRole, QByteArrayLiteral("attachments")}, 0057 0058 // Reblog 0059 {IsBoostedRole, "isBoosted"}, 0060 {BoostAuthorIdentityRole, "boostAuthorIdentity"}, 0061 0062 // Reply 0063 {IsReplyRole, "isReply"}, 0064 {ReplyAuthorIdentityRole, "replyAuthorIdentity"}, 0065 0066 // Interaction count 0067 {ReblogsCountRole, QByteArrayLiteral("reblogsCount")}, 0068 {RepliesCountRole, QByteArrayLiteral("repliesCount")}, 0069 {FavouritesCountRole, QByteArrayLiteral("favouritesCount")}, 0070 0071 // User self interaction 0072 {FavouritedRole, QByteArrayLiteral("favourited")}, 0073 {RebloggedRole, QByteArrayLiteral("reblogged")}, 0074 {MutedRole, QByteArrayLiteral("muted")}, 0075 {BookmarkedRole, QByteArrayLiteral("bookmarked")}, 0076 {PinnedRole, QByteArrayLiteral("pinned")}, 0077 0078 // Notification 0079 {NotificationActorIdentityRole, "notificationActorIdentity"}, 0080 {TypeRole, "type"}, 0081 0082 {PostRole, "post"}, 0083 }; 0084 } 0085 0086 QVariant AbstractTimelineModel::postData(Post *post, int role) const 0087 { 0088 switch (role) { 0089 case IdRole: 0090 return post->postId(); 0091 case OriginalIdRole: 0092 return post->originalPostId(); 0093 case MentionsRole: 0094 return post->mentions(); 0095 case ContentRole: 0096 return post->content(); 0097 case AuthorIdentityRole: 0098 return QVariant::fromValue<Identity *>(post->authorIdentity().get()); 0099 case IsBoostedRole: 0100 return post->boosted(); 0101 case BoostAuthorIdentityRole: 0102 if (post->boostIdentity()) { 0103 return QVariant::fromValue<Identity *>(post->boostIdentity().get()); 0104 } 0105 return false; 0106 case IsReplyRole: 0107 return !post->inReplyTo().isEmpty(); 0108 case ReplyAuthorIdentityRole: 0109 if (!post->inReplyTo().isEmpty()) { 0110 return QVariant::fromValue<Identity *>(post->replyIdentity().get()); 0111 } 0112 return false; 0113 case PublishedAtRole: 0114 return post->publishedAt(); 0115 case RebloggedRole: 0116 return post->reblogged(); 0117 case FavouritedRole: 0118 return post->favourited(); 0119 case BookmarkedRole: 0120 return post->bookmarked(); 0121 case PinnedRole: 0122 return post->pinned(); 0123 0124 case FavouritesCountRole: 0125 return post->favouritesCount(); 0126 case RepliesCountRole: 0127 return post->repliesCount(); 0128 case ReblogsCountRole: 0129 return post->reblogsCount(); 0130 case SensitiveRole: 0131 return post->sensitive(); 0132 case SpoilerTextRole: 0133 return post->spoilerText(); 0134 case VisibilityRole: 0135 return post->visibility(); 0136 case FiltersRole: 0137 return post->filters(); 0138 case AttachmentsRole: 0139 return QVariant::fromValue<QList<Attachment *>>(post->attachments()); 0140 case CardRole: 0141 if (post->card().has_value()) { 0142 return QVariant::fromValue<Card>(*post->card()); 0143 } 0144 return false; 0145 case ApplicationRole: 0146 if (post->application().has_value()) { 0147 return QVariant::fromValue<Application>(*post->application()); 0148 } 0149 return false; 0150 case UrlRole: 0151 return QVariant::fromValue<QUrl>(post->url()); 0152 case RelativeTimeRole: { 0153 return post->relativeTime(); 0154 } 0155 case AbsoluteTimeRole: { 0156 return post->absoluteTime(); 0157 } 0158 case PollRole: 0159 if (post->poll()) { 0160 return QVariant::fromValue<Poll>(*post->poll()); 0161 } 0162 return {}; 0163 case TypeRole: 0164 case NotificationActorIdentityRole: 0165 return false; 0166 case PostRole: 0167 return QVariant::fromValue<Post *>(post); 0168 } 0169 0170 return {}; 0171 } 0172 0173 void AbstractTimelineModel::actionFavorite(const QModelIndex &index, Post *post) 0174 { 0175 if (!post->favourited()) { 0176 m_account->favorite(post); 0177 post->setFavourited(true); 0178 } else { 0179 m_account->unfavorite(post); 0180 post->setFavourited(false); 0181 } 0182 0183 Q_EMIT dataChanged(index, index, {FavouritedRole}); 0184 } 0185 0186 void AbstractTimelineModel::actionRepeat(const QModelIndex &index, Post *post) 0187 { 0188 if (!post->reblogged()) { 0189 m_account->repeat(post); 0190 post->setReblogged(true); 0191 } else { 0192 m_account->unrepeat(post); 0193 post->setReblogged(false); 0194 } 0195 0196 Q_EMIT dataChanged(index, index, {RebloggedRole}); 0197 } 0198 0199 void AbstractTimelineModel::actionRedraft(const QModelIndex &index, Post *post, bool isEdit) 0200 { 0201 m_account->get(m_account->apiUrl(QString("/api/v1/statuses/%1/source").arg(post->postId())), true, this, [this, post, index, isEdit](QNetworkReply *reply) { 0202 const auto postSource = QJsonDocument::fromJson(reply->readAll()).object(); 0203 0204 auto backend = new PostEditorBackend(); 0205 backend->setId(post->postId()); 0206 backend->setStatus(postSource["text"].toString()); 0207 backend->setSpoilerText(postSource["spoiler_text"].toString()); 0208 backend->setInReplyTo(post->inReplyTo()); 0209 backend->setVisibility(post->visibility()); 0210 backend->setLanguage(post->language()); 0211 backend->setMentions(post->mentions()); // TODO: needed? 0212 backend->setSensitive(post->sensitive()); 0213 0214 Q_EMIT postSourceReady(backend, isEdit); 0215 0216 auto attachmentBackend = backend->attachmentEditorModel(); 0217 for (const auto &attachment : post->attachments()) { 0218 attachmentBackend->appendExisting(attachment); 0219 } 0220 0221 if (isEdit) { 0222 connect(backend, &PostEditorBackend::editComplete, this, [this, post, index](QJsonObject object) { 0223 post->fromJson(object); 0224 Q_EMIT dataChanged(index, index); 0225 }); 0226 } 0227 }); 0228 } 0229 0230 void AbstractTimelineModel::actionBookmark(const QModelIndex &index, Post *post) 0231 { 0232 if (!post->bookmarked()) { 0233 m_account->bookmark(post); 0234 post->setBookmarked(true); 0235 } else { 0236 m_account->unbookmark(post); 0237 post->setBookmarked(false); 0238 } 0239 0240 Q_EMIT dataChanged(index, index, {BookmarkedRole}); 0241 } 0242 0243 void AbstractTimelineModel::actionPin(const QModelIndex &index, Post *post) 0244 { 0245 if (!post->pinned()) { 0246 m_account->pin(post); 0247 post->setPinned(true); 0248 } else { 0249 m_account->unpin(post); 0250 post->setPinned(false); 0251 } 0252 0253 Q_EMIT dataChanged(index, index, {PinnedRole}); 0254 } 0255 0256 void AbstractTimelineModel::actionDelete(const QModelIndex &index, Post *post) 0257 { 0258 Q_UNUSED(index); 0259 m_account->deleteResource(m_account->apiUrl(QString("/api/v1/statuses/%1").arg(post->postId())), true, this, [](QNetworkReply *reply) { 0260 const auto postSource = QJsonDocument::fromJson(reply->readAll()).object(); 0261 qDebug() << "DELETED: " << postSource; 0262 }); 0263 }