File indexing completed on 2024-11-03 07:45:10

0001 // SPDX-FileCopyrightText: 2023 James Graham <james.h.graham@protonmail.com>
0002 // SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0003 
0004 #pragma once
0005 
0006 #include <QObject>
0007 
0008 #include <KFormat>
0009 
0010 #include <Quotient/eventitem.h>
0011 #include <Quotient/events/roomevent.h>
0012 #include <Quotient/events/roommessageevent.h>
0013 
0014 #include "enums/delegatetype.h"
0015 
0016 class LinkPreviewer;
0017 class NeoChatRoom;
0018 class ReactionModel;
0019 
0020 /**
0021  * @class EventHandler
0022  *
0023  * This class is designed to handle a Quotient::RoomEvent allowing data to be extracted
0024  * in a form ready for the NeoChat UI.
0025  *
0026  * To use this properly both the room and the event should be set (and the event should
0027  * be from the given room).
0028  *
0029  * @note EventHandler will always try to return something even when not properly
0030  *       initialised, this is usually the best empty value it can create with available
0031  *       information. This is to minimize warnings from QML especially during startup
0032  *       and room changes.
0033  */
0034 class EventHandler : public QObject
0035 {
0036     Q_OBJECT
0037 
0038 public:
0039     /**
0040      * @brief Return the current room the EventHandler is using.
0041      */
0042     const NeoChatRoom *getRoom() const;
0043 
0044     /**
0045      * @brief Set the current room the EventHandler to using.
0046      */
0047     void setRoom(const NeoChatRoom *room);
0048 
0049     /**
0050      * @brief Return the current event the EventHandler is using.
0051      */
0052     const Quotient::Event *getEvent() const;
0053 
0054     /**
0055      * @brief Set the current event the EventHandler to using.
0056      */
0057     void setEvent(const Quotient::RoomEvent *event);
0058 
0059     /**
0060      * @brief Return the Matrix ID of the event.
0061      */
0062     QString getId() const;
0063 
0064     /**
0065      * @brief Return the DelegateType of the event.
0066      *
0067      * @note While similar this is not the matrix event or message type. This is
0068      *       to tell a QML ListView what delegate to show for each event. So while
0069      *       similar to the spec it is not the same.
0070      */
0071     DelegateType::Type getDelegateType() const;
0072 
0073     /**
0074      * @brief Get the author of the event in context of the room.
0075      *
0076      * This is different to getting a Quotient::User object
0077      * as neither of those can provide details like the displayName or avatarMediaId
0078      * without the room context as these can vary from room to room. This function
0079      * uses the room context and outputs the result as QVariantMap.
0080      *
0081      * An empty QVariantMap will be returned if the EventHandler hasn't had the room
0082      * intialised. An empty user (i.e. a QVariantMap with all the correct keys
0083      * but empty values) will be returned if the room has been set but not an event.
0084      *
0085      * @param isPending if the event is pending, i.e. has not been confirmed by
0086      *                  the server.
0087      *
0088      * @return a QVariantMap for the user with the following properties:
0089      *  - isLocalUser - Whether the user is the local user.
0090      *  - id - The matrix ID of the user.
0091      *  - displayName - Display name in the context of this room.
0092      *  - avatarSource - The mxc URL for the user's avatar in the current room.
0093      *  - avatarMediaId - Avatar id in the context of this room.
0094      *  - color - Color for the user.
0095      *  - object - The Quotient::User object for the user.
0096      *
0097      * @sa Quotient::User
0098      */
0099     QVariantMap getAuthor(bool isPending = false) const;
0100 
0101     /**
0102      * @brief Get the display name of the event author.
0103      *
0104      * This method is separate from getAuthor() and special in that it will return
0105      * the old display name of the author if the current event is one that caused it
0106      * to change. This allows for scenarios where the UI wishes to notify that a
0107      * user's display name has changed and what it changed from.
0108      *
0109      * @param isPending whether the event is pending as this cannot be derived from
0110      *                  just the event object.
0111      */
0112     QString getAuthorDisplayName(bool isPending = false) const;
0113 
0114     /**
0115      * @brief Get the display name of the event author but with any newlines removed.
0116      *
0117      * Turns out you can put newlines in your display name so we need to handle that
0118      * primarily for the room list subtitle.
0119      *
0120      * @param isPending whether the event is pending as this cannot be derived from
0121      *                  just the event object.
0122      */
0123     QString singleLineAuthorDisplayname(bool isPending = false) const;
0124 
0125     /**
0126      * @brief Return a QDateTime object for the event timestamp.
0127      */
0128     QDateTime getTime(bool isPending = false, QDateTime lastUpdated = {}) const;
0129 
0130     /**
0131      * @brief Return a QString for the event timestamp.
0132      *
0133      * This is intended to return a string that is read for display in the UI without
0134      * any further manipulation required.
0135      *
0136      * @param relative whether the string is realtive to the current date, i.e.
0137      *                 Yesterday or Wednesday, etc.
0138      * @param format the QLocale::FormatType to use.
0139      * @param isPending whether the event is pending as this cannot be derived from
0140      *                  just the event object.
0141      * @param lastUpdated the time the event was last updated locally as this cannot be
0142      *                    obtained from the event.
0143      */
0144     QString getTimeString(bool relative, QLocale::FormatType format = QLocale::ShortFormat, bool isPending = false, QDateTime lastUpdated = {}) const;
0145 
0146     /**
0147      * @brief Whether the event should be highlighted in the timeline.
0148      *
0149      * @note Messages in direct chats are never highlighted.
0150      */
0151     bool isHighlighted();
0152 
0153     /**
0154      * @brief Whether the event should be hidden in the timeline.
0155      *
0156      * This could be for numerous reasons, e.g. if it's a replacement event, if the
0157      * user has hidden all state events or if the sender has been ignored by the local
0158      * user.
0159      */
0160     bool isHidden();
0161 
0162     /**
0163      * @brief Output a string for the message content ready for display in a rich text field.
0164      *
0165      * The output string is dependant upon the event type and the desired output format.
0166      *
0167      * For most messages this is the body content of the message. For media messages
0168      * this will be the caption and for state events it will be a string specific
0169      * to that event with some dynamic details about the event added.
0170      *
0171      * E.g. For a room topic state event the text will be:
0172      *      "set the topic to: <new topic text>"
0173      *
0174      * @param stripNewlines whether the output should have new lines in it.
0175      */
0176     QString getRichBody(bool stripNewlines = false) const;
0177 
0178     /**
0179      * @brief Output a string for the message content ready for display in a plain text field.
0180      *
0181      * The output string is dependant upon the event type and the desired output format.
0182      *
0183      * For most messages this is the body content of the message. For media messages
0184      * this will be the caption and for state events it will be a string specific
0185      * to that event with some dynamic details about the event added.
0186      *
0187      * E.g. For a room topic state event the text will be:
0188      *      "set the topic to: <new topic text>"
0189      *
0190      * @param stripNewlines whether the output should have new lines in it.
0191      */
0192     QString getPlainBody(bool stripNewlines = false) const;
0193 
0194     /**
0195      * @brief Output a generic string for the message content ready for display.
0196      *
0197      * The output string is dependant upon the event type.
0198      *
0199      * Unlike EventHandler::getRichBody or EventHandler::getPlainBody the string
0200      * is the same for all events of the same type.
0201      *
0202      * E.g. For a message the text will be:
0203      *      "sent a message"
0204      *
0205      * @sa getRichBody(), getPlainBody()
0206      */
0207     QString getGenericBody() const;
0208 
0209     /**
0210      * @brief Output a string for the event to be used as  a RoomList subtitle.
0211      *
0212      * The output includes the username followed by the plain message, all with no
0213      * line breaks.
0214      */
0215     QString subtitleText() const;
0216 
0217     /**
0218      * @brief Return the media info for the event.
0219      *
0220      * An empty QVariantMap will be returned for any event that doesn't have any
0221      * media info.
0222      *
0223      * @return This should consist of the following:
0224      *  - source - The mxc URL for the media.
0225      *  - mimeType - The MIME type of the media (should be image/xxx for this delegate).
0226      *  - mimeIcon - The MIME icon name (should be image-xxx).
0227      *  - size - The file size in bytes.
0228      *  - width - The width in pixels of the audio media.
0229      *  - height - The height in pixels of the audio media.
0230      *  - tempInfo - mediaInfo (with the same properties as this except no tempInfo) for a temporary image while the file downloads.
0231      */
0232     QVariantMap getMediaInfo() const;
0233 
0234     /**
0235      * @brief Whether the event is a reply to another in the timeline.
0236      */
0237     bool hasReply() const;
0238 
0239     /**
0240      * @brief Return the Matrix ID of the event replied to.
0241      */
0242     QString getReplyId() const;
0243 
0244     /**
0245      * @brief Return the DelegateType of the event replied to.
0246      *
0247      * @note While similar this is not the matrix event or message type. This is
0248      *       to tell a QML ListView what delegate to show for each event. So while
0249      *       similar to the spec it is not the same.
0250      */
0251     DelegateType::Type getReplyDelegateType() const;
0252 
0253     /**
0254      * @brief Get the author of the event replied to in context of the room.
0255      *
0256      * This is different to getting a Quotient::User object
0257      * as neither of those can provide details like the displayName or avatarMediaId
0258      * without the room context as these can vary from room to room. This function
0259      * uses the room context and outputs the result as QVariantMap.
0260      *
0261      * An empty QVariantMap will be returned if the EventHandler hasn't had the room
0262      * intialised. An empty user (i.e. a QVariantMap with all the correct keys
0263      * but empty values) will be returned if the room has been set but not an event.
0264      *
0265      * @return a QVariantMap for the user with the following properties:
0266      *  - isLocalUser - Whether the user is the local user.
0267      *  - id - The matrix ID of the user.
0268      *  - displayName - Display name in the context of this room.
0269      *  - avatarSource - The mxc URL for the user's avatar in the current room.
0270      *  - avatarMediaId - Avatar id in the context of this room.
0271      *  - color - Color for the user.
0272      *  - object - The Quotient::User object for the user.
0273      *
0274      * @sa Quotient::User
0275      */
0276     QVariantMap getReplyAuthor() const;
0277 
0278     /**
0279      * @brief Output a string for the message content of the event replied to ready
0280      * for display in a rich text field.
0281      *
0282      * The output string is dependant upon the event type and the desired output format.
0283      *
0284      * For most messages this is the body content of the message. For media messages
0285      * this will be the caption and for state events it will be a string specific
0286      * to that event with some dynamic details about the event added.
0287      *
0288      * E.g. For a room topic state event the text will be:
0289      *      "set the topic to: <new topic text>"
0290      *
0291      * @param stripNewlines whether the output should have new lines in it.
0292      */
0293     QString getReplyRichBody(bool stripNewlines = false) const;
0294 
0295     /**
0296      * @brief Output a string for the message content of the event replied to ready
0297      * for display in a plain text field.
0298      *
0299      * The output string is dependant upon the event type and the desired output format.
0300      *
0301      * For most messages this is the body content of the message. For media messages
0302      * this will be the caption and for state events it will be a string specific
0303      * to that event with some dynamic details about the event added.
0304      *
0305      * E.g. For a room topic state event the text will be:
0306      *      "set the topic to: <new topic text>"
0307      *
0308      * @param stripNewlines whether the output should have new lines in it.
0309      */
0310     QString getReplyPlainBody(bool stripNewlines = false) const;
0311 
0312     /**
0313      * @brief Return the media info for the event replied to.
0314      *
0315      * An empty QVariantMap will be returned for any event that doesn't have any
0316      * media info.
0317      *
0318      * @return This should consist of the following:
0319      *  - source - The mxc URL for the media.
0320      *  - mimeType - The MIME type of the media (should be image/xxx for this delegate).
0321      *  - mimeIcon - The MIME icon name (should be image-xxx).
0322      *  - size - The file size in bytes.
0323      *  - width - The width in pixels of the audio media.
0324      *  - height - The height in pixels of the audio media.
0325      *  - tempInfo - mediaInfo (with the same properties as this except no tempInfo) for a temporary image while the file downloads.
0326      */
0327     QVariantMap getReplyMediaInfo() const;
0328 
0329     /**
0330      * @brief Whether the message is part of a thread.
0331      *
0332      * i.e. There is a rel_type of m.thread.
0333      */
0334     bool isThreaded() const;
0335 
0336     /**
0337      * @brief Return the Matrix ID of the thread's root message.
0338      *
0339      * Empty if this not part of a thread.
0340      */
0341     QString threadRoot() const;
0342 
0343     /**
0344      * @brief Return the latitude for the event.
0345      *
0346      * Returns -100.0 if the event doesn't have a location (latitudes are in the
0347      * range -90deg to +90deg so -100 is out of range).
0348      */
0349     float getLatitude() const;
0350 
0351     /**
0352      * @brief Return the longitude for the event.
0353      *
0354      * Returns -200.0 if the event doesn't have a location (latitudes are in the
0355      * range -180deg to +180deg so -200 is out of range).
0356      */
0357     float getLongitude() const;
0358 
0359     /**
0360      * @brief Return the type of location marker for the event.
0361      */
0362     QString getLocationAssetType() const;
0363 
0364     /**
0365      * @brief Whether the event has any read marker for other users.
0366      */
0367     bool hasReadMarkers() const;
0368 
0369     /**
0370      * @brief Returns a list of user read marker for the event.
0371      *
0372      * @param maxMarkers the maximum number of users to return. Usually the number
0373      *                   of user read makers shown is limited to not clutter the UI.
0374      *                   This needs to be the same as used in getNumberExcessReadMarkers
0375      *                   so that the markers line up with the number displayed, i.e.
0376      *                   the number of users shown plus the excess number will be
0377      *                   the total number of other user read markers at an event.
0378      */
0379     QVariantList getReadMarkers(int maxMarkers = 5) const;
0380 
0381     /**
0382      * @brief Returns the number of excess user read markers for the event.
0383      *
0384      * This returns a string in the form "+ x" ready for use in the UI.
0385      *
0386      * @param maxMarkers the maximum number of markers shown in the UI. This needs to
0387      *                   be the same as used in getReadMarkers so that the value lines
0388      *                   up with the number displayed, i.e. the number of users shown
0389      *                   plus the excess number will be the total number of other user
0390      *                   read markers at an event.
0391      */
0392     QString getNumberExcessReadMarkers(int maxMarkers = 5) const;
0393 
0394     /**
0395      * @brief Returns a string with the names of the read markers at the event.
0396      *
0397      * This is in the form "x users: name 1, name 2, ...".
0398      */
0399     QString getReadMarkersString() const;
0400 
0401 private:
0402     const NeoChatRoom *m_room = nullptr;
0403     const Quotient::RoomEvent *m_event = nullptr;
0404 
0405     KFormat m_format;
0406 
0407     DelegateType::Type getDelegateTypeForEvent(const Quotient::RoomEvent *event) const;
0408 
0409     QString getBody(const Quotient::RoomEvent *event, Qt::TextFormat format, bool stripNewlines) const;
0410     QString getMessageBody(const Quotient::RoomMessageEvent &event, Qt::TextFormat format, bool stripNewlines) const;
0411 
0412     QVariantMap getMediaInfoForEvent(const Quotient::RoomEvent *event) const;
0413     QVariantMap getMediaInfoFromFileInfo(const Quotient::EventContent::FileInfo *fileInfo, const QString &eventId, bool isThumbnail = false) const;
0414 };