// SPDX-FileCopyrightText: 2021 kaniini // SPDX-FileCopyrightText: 2021 Carl Schwan // SPDX-FileCopyrightText: 2024 Joshua Goins // SPDX-License-Identifier: GPL-3.0-only #pragma once #include "timeline/attachment.h" #include "timeline/poll.h" #include class Post; class Identity; class AbstractAccount; class Application { Q_GADGET Q_PROPERTY(QString name READ name) Q_PROPERTY(QUrl website READ website) public: Application() = default; explicit Application(QJsonObject application); [[nodiscard]] QString name() const; [[nodiscard]] QUrl website() const; private: QJsonObject m_application; }; class Card { Q_GADGET Q_PROPERTY(QString authorName READ authorName) Q_PROPERTY(QString authorUrl READ authorUrl) Q_PROPERTY(QString blurhash READ blurhash) Q_PROPERTY(QString description READ description) Q_PROPERTY(QString embedUrl READ embedUrl) Q_PROPERTY(int width READ width) Q_PROPERTY(int height READ height) Q_PROPERTY(QString html READ html) Q_PROPERTY(QString image READ image) Q_PROPERTY(QString providerName READ providerName) Q_PROPERTY(QString providerUrl READ providerUrl) Q_PROPERTY(QString title READ title) Q_PROPERTY(QUrl url READ url) public: Card() = default; explicit Card(QJsonObject card); [[nodiscard]] QString authorName() const; [[nodiscard]] QString authorUrl() const; [[nodiscard]] QString blurhash() const; [[nodiscard]] QString description() const; [[nodiscard]] QString embedUrl() const; [[nodiscard]] int width() const; [[nodiscard]] int height() const; [[nodiscard]] QString html() const; [[nodiscard]] QString image() const; [[nodiscard]] QString providerName() const; [[nodiscard]] QString providerUrl() const; [[nodiscard]] QString title() const; [[nodiscard]] QUrl url() const; private: QJsonObject m_card; }; /** * @brief Represents a post, which may have text or images attached. */ class Post : public QObject { Q_OBJECT QML_ELEMENT QML_UNCREATABLE("Posts should be accessed via models") Q_PROPERTY(QString postId READ postId CONSTANT) Q_PROPERTY(QString spoilerText READ spoilerText CONSTANT) Q_PROPERTY(QString content READ content CONSTANT) Q_PROPERTY(bool hasContent READ hasContent CONSTANT) Q_PROPERTY(bool sensitive READ sensitive CONSTANT) Q_PROPERTY(Visibility visibility READ visibility CONSTANT) Q_PROPERTY(QString language READ language CONSTANT) Q_PROPERTY(QString inReplyTo READ inReplyTo CONSTANT) Q_PROPERTY(QStringList mentions READ mentions CONSTANT) Q_PROPERTY(Poll *poll READ poll NOTIFY pollChanged) Q_PROPERTY(QStringList filters READ filters CONSTANT) Q_PROPERTY(Identity *authorIdentity READ getAuthorIdentity CONSTANT) Q_PROPERTY(QQmlListProperty attachments READ attachmentList CONSTANT) Q_PROPERTY(QString relativeTime READ relativeTime CONSTANT) Q_PROPERTY(QString absoluteTime READ absoluteTime CONSTANT) Q_PROPERTY(Card *card READ getCard CONSTANT) Q_PROPERTY(QString type READ type CONSTANT) Q_PROPERTY(QString editedAt READ editedAt CONSTANT) Q_PROPERTY(bool wasEdited READ wasEdited CONSTANT) Q_PROPERTY(QList standaloneTags READ standaloneTags CONSTANT) Q_PROPERTY(Post *quotedPost READ quotedPost NOTIFY quotedPostChanged) public: Post() = delete; Post(const Post &) = delete; /** * @brief Create an empty post for @p account. * @note The @c Post is not parented to the account automatically. */ explicit Post(AbstractAccount *account, QObject *parent = nullptr); /** * @brief Create a post for @p account from JSON @p obj. * @note The @c Post is not parented to the account automatically. */ Post(AbstractAccount *account, QJsonObject obj, QObject *parent = nullptr); /** * @brief Loads post content from JSON @p obj. */ void fromJson(QJsonObject obj); /** * @return This post's id. * @note The id may be different because it was boosted. This is the id of the parent post. * @sa originalPostId() */ [[nodiscard]] QString postId() const; /** * @return The post's original id if boosted, but identical to postId if not. * @sa postId() */ [[nodiscard]] QString originalPostId() const; /** * @return The published/creation time of this post. */ [[nodiscard]] QDateTime publishedAt() const; /** * @return A locale-aware relative time when the post was published. */ [[nodiscard]] QString relativeTime() const; /** * @return A absolute locale-aware time when the post was published. */ [[nodiscard]] QString absoluteTime() const; /** * @return The identity of this post's author. */ [[nodiscard]] std::shared_ptr authorIdentity() const; /** * @return The HTML text of this post. */ [[nodiscard]] QString content() const; /** * @return If the post has any text content. * @note Use this instead of checking the length of content() because it could contain useless HTML code. */ [[nodiscard]] bool hasContent() const; /** * @brief The possible visibility levels of a post. */ enum Visibility { Public, Unlisted, Private, Direct, Local }; Q_ENUM(Visibility) /** * @return The visibility of the post. */ [[nodiscard]] Visibility visibility() const; /** * @return Whether the post has a content warning and should be considered sensitive. */ [[nodiscard]] bool sensitive() const; /** * @return The spoiler text (subject) of the post. */ [[nodiscard]] QString spoilerText() const; /** * @return The attachments for this post. */ [[nodiscard]] QList attachments() const; /** * @return The application used to write this post, if there is one available. */ [[nodiscard]] std::optional application() const; /** * @return The list of username mentions in this post. */ [[nodiscard]] QStringList mentions() const; /** * @return The standalone tags of this post that are not part of the main text. */ [[nodiscard]] QVector standaloneTags() const; /** * @return The number of time this post has been replied. */ [[nodiscard]] int repliesCount() const; /** * @return The number of time this post has been favorited. */ [[nodiscard]] int favouritesCount() const; /** * @brief The number of time this post has been boosted. */ [[nodiscard]] int reblogsCount() const; /** * @return A web accessible URL to this post. */ [[nodiscard]] QUrl url() const; /** * @return The id that this post is replying to. Could be empty if it isn't replying to anything. */ [[nodiscard]] QString inReplyTo() const; /** * @return The identity of who replied to this post, if someone did. */ [[nodiscard]] std::shared_ptr replyIdentity() const; /** * @return The poll on this post, if there is one. */ [[nodiscard]] Poll *poll() const; /** * @brief Sets the poll on this post from JSON @p object. */ void setPollJson(const QJsonObject &object); /** * @return The link card for this post, if there is one. */ [[nodiscard]] std::optional card() const; /** * @return The ISO-639 language code that the post is sent in. * @note This doesn't reflect the actual content of the post, and is set by the author. */ [[nodiscard]] QString language() const; /** * @return If the post was edited. */ [[nodiscard]] bool wasEdited() const; /** * @return The time this post was last edited */ [[nodiscard]] QString editedAt() const; /** * @return If this post existed because it was boosted by someone else. * @sa boostIdentity() */ [[nodiscard]] bool boosted() const; /** * @return The identity of who boosted this post, if someone did. */ [[nodiscard]] std::shared_ptr boostIdentity() const; /** * @return Whether the user favorited this post or not. */ [[nodiscard]] bool favourited() const; /** * @brief Set this post as favourited as or not. */ void setFavourited(bool favourited); /** * @return Whether the user boosted this post. */ [[nodiscard]] bool reblogged() const; /** * @brief Set this post as boosted or not. */ void setReblogged(bool reblogged); /** * @return Whether the user bookmarked this post. */ [[nodiscard]] bool bookmarked() const; /** * @brief Set this post as bookmarked or not. */ void setBookmarked(bool bookmarked); /** * @return Whether the user muted this post. */ [[nodiscard]] bool muted() const; /** * @brief Set this post as muted or not. */ void setMuted(bool muted); /** * @return The filters that apply to this post. */ [[nodiscard]] QStringList filters() const; /** * @return Whether this post is supposed to be hidden by a filter. */ [[nodiscard]] bool hidden() const; /** * @return Whether the user pinned this post. */ [[nodiscard]] bool pinned() const; /** * @brief Set whether or not this post is pinned to the top of the timeline. */ void setPinned(bool pinned); /** * @return Whether the user filtered this post. */ [[nodiscard]] bool filtered() const; /** * @brief Adds @p attachments to this post. */ Q_INVOKABLE void addAttachments(const QJsonArray &attachments); /** * @return The string representation of the @p visibility enum. */ static QString visibilityToString(Post::Visibility visibility); /** * @brief The enum that matches the @p visibility string. */ static Post::Visibility stringToVisibility(const QString &visibility); [[nodiscard]] Post *quotedPost() const; Q_SIGNALS: void pollChanged(); void replyIdentityChanged(); void quotedPostChanged(); private: [[nodiscard]] QString type() const; [[nodiscard]] Identity *getAuthorIdentity() const; [[nodiscard]] QQmlListProperty attachmentList() const; [[nodiscard]] Card *getCard() const; void setCard(std::optional card); void setApplication(std::optional application); void processContent(const QJsonObject &obj); AbstractAccount *const m_parent; QDateTime m_publishedAt; QString m_postId; QString m_originalPostId; QUrl m_url; QString m_content; bool m_hasContent; QString m_spoilerText; QString m_author; QString m_reply_to_author; QString m_content_type; QStringList m_mentions; QString m_language; QDateTime m_editedAt; QVector m_standaloneTags; Post *m_quotedPost = nullptr; QString m_replyTargetId; QStringList m_filters; std::optional m_card; std::optional m_application; std::shared_ptr m_authorIdentity; QList m_attachments; QQmlListProperty m_attachmentList; std::unique_ptr m_poll; bool m_sensitive = false; Visibility m_visibility; bool m_boosted = false; std::shared_ptr m_boostIdentity; std::shared_ptr m_replyIdentity; bool m_favourited = false; bool m_reblogged = false; bool m_muted = false; bool m_bookmarked = false; bool m_filtered = false; bool m_hidden = false; bool m_pinned = false; int m_reblogsCount = 0; int m_favouritesCount = 0; int m_repliesCount = 0; };