File indexing completed on 2024-12-08 07:33:45

0001 // SPDX-FileCopyrightText: 2018-2019 Black Hat <bhat@encom.eu.org>
0002 // SPDX-License-Identifier: GPL-3.0-only
0003 
0004 #pragma once
0005 
0006 #include <KFormat>
0007 #include <QAbstractListModel>
0008 #include <QQmlEngine>
0009 
0010 #include "linkpreviewer.h"
0011 #include "neochatroom.h"
0012 #include "pollhandler.h"
0013 
0014 class ReactionModel;
0015 
0016 /**
0017  * @class MessageEventModel
0018  *
0019  * This class defines the model for visualising the room timeline.
0020  *
0021  * This model covers all event types in the timeline with many of the roles being
0022  * specific to a subset of events. This means the user needs to understand which
0023  * roles will return useful information for a given event type.
0024  *
0025  * @sa NeoChatRoom
0026  */
0027 class MessageEventModel : public QAbstractListModel
0028 {
0029     Q_OBJECT
0030     QML_ELEMENT
0031 
0032     /**
0033      * @brief The current room that the model is getting its messages from.
0034      */
0035     Q_PROPERTY(NeoChatRoom *room READ room WRITE setRoom NOTIFY roomChanged)
0036 
0037 public:
0038     /**
0039      * @brief Defines the model roles.
0040      */
0041     enum EventRoles {
0042         DelegateTypeRole = Qt::UserRole + 1, /**< The delegate type of the message. */
0043         PlainText, /**< Plain text representation of the message. */
0044         EventIdRole, /**< The matrix event ID of the event. */
0045         TimeRole, /**< The timestamp for when the event was sent (as a QDateTime). */
0046         TimeStringRole, /**< The timestamp for when the event was sent as a string (in QLocale::ShortFormat). */
0047         SectionRole, /**< The date of the event as a string. */
0048         AuthorRole, /**< The author of the event. */
0049         HighlightRole, /**< Whether the event should be highlighted. */
0050         SpecialMarksRole, /**< Whether the event is hidden or not. */
0051         ProgressInfoRole, /**< Progress info when downloading files. */
0052         GenericDisplayRole, /**< A generic string based upon the message type. */
0053 
0054         ShowLinkPreviewRole, /**< Whether a link preview should be shown. */
0055         LinkPreviewRole, /**< The link preview details. */
0056 
0057         MediaInfoRole, /**< The media info for the event. */
0058 
0059         IsReplyRole, /**< Is the message a reply to another event. */
0060         ReplyAuthor, /**< The author of the event that was replied to. */
0061         ReplyIdRole, /**< The matrix ID of the message that was replied to. */
0062         ReplyDelegateTypeRole, /**< The delegate type of the message that was replied to. */
0063         ReplyDisplayRole, /**< The body of the message that was replied to. */
0064         ReplyMediaInfoRole, /**< The media info of the message that was replied to. */
0065 
0066         IsThreadedRole,
0067         ThreadRootRole,
0068 
0069         ShowAuthorRole, /**< Whether the author's name should be shown. */
0070         ShowSectionRole, /**< Whether the section header should be shown. */
0071 
0072         ReadMarkersRole, /**< The first 5 other users at the event for read marker tracking. */
0073         ExcessReadMarkersRole, /**< The number of other users at the event after the first 5. */
0074         ReadMarkersStringRole, /**< String with the display name and mxID of the users at the event. */
0075         ShowReadMarkersRole, /**< Whether there are any other user read markers to be shown. */
0076         ReactionRole, /**< List model for this event. */
0077         ShowReactionsRole, /**< Whether there are any reactions to be shown. */
0078 
0079         VerifiedRole, /**< Whether an encrypted message is sent in a verified session. */
0080         AuthorDisplayNameRole, /**< The displayname for the event's sender; for name change events, the old displayname. */
0081         IsRedactedRole, /**< Whether an event has been deleted. */
0082         IsPendingRole, /**< Whether an event is waiting to be accepted by the server. */
0083         LatitudeRole, /**< Latitude for a location event. */
0084         LongitudeRole, /**< Longitude for a location event. */
0085         AssetRole, /**< Type of location event, e.g. self pin of the user location. */
0086         PollHandlerRole, /**< The PollHandler for the event, if any. */
0087         LastRole, // Keep this last
0088     };
0089     Q_ENUM(EventRoles)
0090 
0091     explicit MessageEventModel(QObject *parent = nullptr);
0092 
0093     [[nodiscard]] NeoChatRoom *room() const;
0094     void setRoom(NeoChatRoom *room);
0095 
0096     /**
0097      * @brief Get the given role value at the given index.
0098      *
0099      * @sa QAbstractItemModel::data
0100      */
0101     [[nodiscard]] QVariant data(const QModelIndex &idx, int role = Qt::DisplayRole) const override;
0102 
0103     /**
0104      * @brief Number of rows in the model.
0105      *
0106      * @sa  QAbstractItemModel::rowCount
0107      */
0108     [[nodiscard]] int rowCount(const QModelIndex &parent = QModelIndex()) const override;
0109 
0110     /**
0111      * @brief Returns a mapping from Role enum values to role names.
0112      *
0113      * @sa EventRoles, QAbstractItemModel::roleNames()
0114      */
0115     [[nodiscard]] QHash<int, QByteArray> roleNames() const override;
0116 
0117     /**
0118      * @brief Get the row number of the given event ID in the model.
0119      */
0120     Q_INVOKABLE [[nodiscard]] int eventIdToRow(const QString &eventID) const;
0121 
0122 protected:
0123     bool event(QEvent *event) override;
0124 
0125 private Q_SLOTS:
0126     int refreshEvent(const QString &eventId);
0127     void refreshRow(int row);
0128 
0129 private:
0130     NeoChatRoom *m_currentRoom = nullptr;
0131     QString lastReadEventId;
0132     QPersistentModelIndex m_lastReadEventIndex;
0133     int rowBelowInserted = -1;
0134     bool resetting = false;
0135     bool movingEvent = false;
0136     KFormat m_format;
0137 
0138     QMap<QString, QSharedPointer<LinkPreviewer>> m_linkPreviewers;
0139     QMap<QString, QSharedPointer<ReactionModel>> m_reactionModels;
0140 
0141     [[nodiscard]] int timelineBaseIndex() const;
0142     [[nodiscard]] QDateTime makeMessageTimestamp(const Quotient::Room::rev_iter_t &baseIt) const;
0143 
0144     bool canFetchMore(const QModelIndex &parent) const override;
0145     void fetchMore(const QModelIndex &parent) override;
0146 
0147     void refreshLastUserEvents(int baseTimelineRow);
0148     void refreshEventRoles(int row, const QList<int> &roles = {});
0149     int refreshEventRoles(const QString &eventId, const QList<int> &roles = {});
0150     void moveReadMarker(const QString &toEventId);
0151 
0152     void createEventObjects(const Quotient::RoomMessageEvent *event);
0153     // Hack to ensure that we don't call endInsertRows when we haven't called beginInsertRows
0154     bool m_initialized = false;
0155 
0156 Q_SIGNALS:
0157     void roomChanged();
0158     void fancyEffectsReasonFound(const QString &fancyEffect);
0159 };