File indexing completed on 2024-04-28 04:59:45

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