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

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