File indexing completed on 2024-04-21 04:59:30

0001 // SPDX-FileCopyrightText: 2021 Carl Schwan <carlschwan@kde.org>
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 <KConfigGroup>
0007 #include <KSharedConfig>
0008 #include <QObject>
0009 #include <QQmlEngine>
0010 #include <Quotient/room.h>
0011 #include <Quotient/uriresolver.h>
0012 #include <KConfigGroup>
0013 
0014 #include "chatdocumenthandler.h"
0015 #include "enums/delegatetype.h"
0016 #include "models/mediamessagefiltermodel.h"
0017 #include "models/messagefiltermodel.h"
0018 #include "models/timelinemodel.h"
0019 
0020 class NeoChatRoom;
0021 class NeoChatConnection;
0022 
0023 namespace Quotient
0024 {
0025 class Room;
0026 class User;
0027 }
0028 
0029 using namespace Quotient;
0030 
0031 /**
0032  * @class RoomManager
0033  *
0034  * A singleton class to help manage which room is open in NeoChat.
0035  *
0036  * This class also inherits UriResolverBase and overrides the relevant functions to
0037  * resolve various URIs. The base functions visitUser(), visitRoom(), etc are held
0038  * private intentionally and instead resolveResource() should be called with either
0039  * an appropriate URI or a Matrix ID and action.
0040  */
0041 class RoomManager : public QObject, public UriResolverBase
0042 {
0043     Q_OBJECT
0044     QML_ELEMENT
0045     QML_SINGLETON
0046 
0047     Q_PROPERTY(NeoChatConnection *connection READ connection WRITE setConnection NOTIFY connectionChanged)
0048 
0049     /**
0050      * @brief The current open room in NeoChat, if any.
0051      *
0052      * @sa hasOpenRoom
0053      */
0054     Q_PROPERTY(NeoChatRoom *currentRoom READ currentRoom NOTIFY currentRoomChanged)
0055 
0056     /**
0057      * @brief The TimelineModel that should be used for room message visualisation.
0058      *
0059      * The room object the model uses to get the data will be updated by this class
0060      * so there is no need to do this manually or replace the model when a room
0061      * changes.
0062      *
0063      * @note Available here so that the room page and drawer both have access to the
0064      *       same model.
0065      */
0066     Q_PROPERTY(TimelineModel *timelineModel READ timelineModel CONSTANT)
0067 
0068     /**
0069      * @brief The MessageFilterModel that should be used for room message visualisation.
0070      *
0071      * @note Available here so that the room page and drawer both have access to the
0072      *       same model.
0073      */
0074     Q_PROPERTY(MessageFilterModel *messageFilterModel READ messageFilterModel CONSTANT)
0075 
0076     /**
0077      * @brief The MediaMessageFilterModel that should be used for room media message visualisation.
0078      *
0079      * @note Available here so that the room page and drawer both have access to the
0080      *       same model.
0081      */
0082     Q_PROPERTY(MediaMessageFilterModel *mediaMessageFilterModel READ mediaMessageFilterModel CONSTANT)
0083 
0084     /**
0085      * @brief Whether a room is currently open in NeoChat.
0086      *
0087      * @sa room
0088      */
0089     Q_PROPERTY(bool hasOpenRoom READ hasOpenRoom NOTIFY currentRoomChanged)
0090 
0091     /**
0092      * @brief The room ID of the last space entered.
0093      */
0094     Q_PROPERTY(QString lastSpaceId READ lastSpaceId CONSTANT)
0095 
0096     /**
0097      * @brief The ChatDocumentHandler for the open room.
0098      *
0099      * @sa ChatDocumentHandler
0100      */
0101     Q_PROPERTY(ChatDocumentHandler *chatDocumentHandler READ chatDocumentHandler WRITE setChatDocumentHandler NOTIFY chatDocumentHandlerChanged)
0102 
0103 public:
0104     virtual ~RoomManager();
0105     static RoomManager &instance();
0106     static RoomManager *create(QQmlEngine *engine, QJSEngine *)
0107     {
0108         engine->setObjectOwnership(&instance(), QQmlEngine::CppOwnership);
0109         return &instance();
0110     }
0111 
0112     NeoChatRoom *currentRoom() const;
0113 
0114     TimelineModel *timelineModel() const;
0115     MessageFilterModel *messageFilterModel() const;
0116     MediaMessageFilterModel *mediaMessageFilterModel() const;
0117 
0118     /**
0119      * @brief Resolve the given URI resource.
0120      *
0121      * @note It's actually Quotient::UriResolverBase::visitResource() but with Q_INVOKABLE
0122      *       and the connection grabbed from RoomManager.
0123      *
0124      * @sa Quotient::UriResolverBase::visitResource()
0125      */
0126     Q_INVOKABLE UriResolveResult resolveResource(const Uri &uri);
0127 
0128     /**
0129      * @brief Resolve the given resource.
0130      *
0131      * @note It's actually Quotient::UriResolverBase::visitResource() but with Q_INVOKABLE
0132      *       and the connection grabbed from RoomManager.
0133      *
0134      * @sa Quotient::UriResolverBase::visitResource()
0135      */
0136     Q_INVOKABLE void resolveResource(const QString &idOrUri, const QString &action = {});
0137 
0138     bool hasOpenRoom() const;
0139 
0140     /**
0141      * @brief Load the last opened room or the welcome page.
0142      */
0143     Q_INVOKABLE void loadInitialRoom();
0144 
0145     /**
0146      * @brief Open a new window with the given room.
0147      *
0148      * The open window will have its own message list for the given room.
0149      */
0150     Q_INVOKABLE void openWindow(NeoChatRoom *room);
0151 
0152     /**
0153      * @brief Leave the room and close it if it is open.
0154      */
0155     Q_INVOKABLE void leaveRoom(NeoChatRoom *room);
0156 
0157     /**
0158      * @brief Knock a room.
0159      *
0160      * See https://spec.matrix.org/latest/client-server-api/#knocking-on-rooms for
0161      * knocking on rooms.
0162      */
0163     void knockRoom(Quotient::Connection *account, const QString &roomAliasOrId, const QString &reason, const QStringList &viaServers);
0164 
0165     /**
0166      * @brief Show a media item maximized.
0167      *
0168      * @param index the index to open the maximize delegate model at. This is the
0169      *        index in the MediaMessageFilterModel owned by this RoomManager. A value
0170      *        of -1 opens a the default item.
0171      */
0172     Q_INVOKABLE void maximizeMedia(int index);
0173 
0174     /**
0175      * @brief Request that any full screen overlay currently open closes.
0176      */
0177     Q_INVOKABLE void requestFullScreenClose();
0178 
0179     /**
0180      * @brief Show the JSON source for the given event Matrix ID
0181      */
0182     Q_INVOKABLE void viewEventSource(const QString &eventId);
0183 
0184     /**
0185      * @brief Show a conterxt menu for the given event.
0186      */
0187     Q_INVOKABLE void viewEventMenu(const QString &eventId,
0188                                    const QVariantMap &author,
0189                                    DelegateType::Type delegateType,
0190                                    const QString &plainText,
0191                                    const QString &htmlText = {},
0192                                    const QString &selectedText = {},
0193                                    const QString &mimeType = {},
0194                                    const FileTransferInfo &progressInfo = {});
0195 
0196     /**
0197      * @brief Call this when the current used connection is dropped.
0198      */
0199     Q_INVOKABLE void reset();
0200 
0201     ChatDocumentHandler *chatDocumentHandler() const;
0202     void setChatDocumentHandler(ChatDocumentHandler *handler);
0203 
0204     /**
0205      * @brief Set a URL to be loaded as the initial room.
0206      */
0207     void setUrlArgument(const QString &arg);
0208 
0209     QString lastSpaceId();
0210 
0211     NeoChatConnection *connection() const;
0212     void setConnection(NeoChatConnection *connection);
0213 
0214 Q_SIGNALS:
0215     void currentRoomChanged();
0216 
0217     /**
0218      * @brief Push a new room page.
0219      *
0220      * Signal triggered when the main window pageStack should push a new page with
0221      * the message list for the given room.
0222      *
0223      * @param room the room to be shown on the new page.
0224      * @param event the event to got to if available.
0225      */
0226     void pushRoom(NeoChatRoom *room, const QString &event);
0227 
0228     /**
0229      * @brief Replace the existing room.
0230      *
0231      * Signal triggered when the room displayed by the message list should be changed.
0232      *
0233      * @param room the room to be shown on the new page.
0234      * @param event the event to got to if available.
0235      */
0236     void replaceRoom(NeoChatRoom *room, const QString &event);
0237 
0238     /**
0239      * @brief Push a new space home page.
0240      *
0241      * Signal triggered when the main window pageStack should push a new page with
0242      * the space home for the given space room.
0243      *
0244      * @param spaceRoom the space room to be shown on the new page.
0245      */
0246     void pushSpaceHome(NeoChatRoom *spaceRoom);
0247 
0248     /**
0249      * @brief Replace the existing space home.
0250      *
0251      * Signal triggered when the currently displayed room page should be changed
0252      * to the space home for the given space room.
0253      *
0254      * @param spaceRoom the space room to be shown on the new page.
0255      */
0256     void replaceSpaceHome(NeoChatRoom *spaceRoom);
0257 
0258     /**
0259      * @brief Go to the specified event in the current room.
0260      */
0261     void goToEvent(const QString &event);
0262 
0263     /**
0264      * @brief Open room in a new window.
0265      *
0266      * Signal triggered when a room needs to be opened in a new window.
0267      */
0268     void openRoomInNewWindow(NeoChatRoom *room);
0269 
0270     /**
0271      * @brief Show details for the given user.
0272      *
0273      * Ask current room to open the user's details for the give user.
0274      * This assumes the user is loaded.
0275      */
0276     void showUserDetail(const Quotient::User *user);
0277 
0278     /**
0279      * @brief Request a media item is shown maximized.
0280      *
0281      * @param index the index to open the maximize delegate model at. This is the
0282      *        index in the MediaMessageFilterModel owned by this RoomManager. A value
0283      *        of -1 opens a the default item.
0284      */
0285     void showMaximizedMedia(int index);
0286 
0287     /**
0288      * @brief Request that any full screen overlay closes.
0289      */
0290     void closeFullScreen();
0291 
0292     /**
0293      * @brief Request the JSON source for the given event ID is shown.
0294      */
0295     void showEventSource(const QString &eventId);
0296 
0297     /**
0298      * @brief Request to show a menu for the given event.
0299      */
0300     void showMessageMenu(const QString &eventId,
0301                          const QVariantMap &author,
0302                          DelegateType::Type delegateType,
0303                          const QString &plainText,
0304                          const QString &htmlText,
0305                          const QString &selectedText);
0306 
0307     /**
0308      * @brief Request to show a menu for the given media event.
0309      */
0310     void showFileMenu(const QString &eventId,
0311                       const QVariantMap &author,
0312                       DelegateType::Type delegateType,
0313                       const QString &plainText,
0314                       const QString &mimeType,
0315                       const FileTransferInfo &progressInfo);
0316 
0317     /**
0318      * @brief Show the direct chat confirmation dialog.
0319      *
0320      * Ask current room to show confirmation dialog to open direct chat.
0321      * This assumes the user is loaded.
0322      */
0323     void askDirectChatConfirmation(const Quotient::User *user);
0324 
0325     /**
0326      * @brief Displays warning to the user.
0327      */
0328     void warning(const QString &title, const QString &message);
0329 
0330     void chatDocumentHandlerChanged();
0331 
0332     void connectionChanged();
0333 
0334 private:
0335     void openRoomForActiveConnection();
0336 
0337     NeoChatRoom *m_currentRoom;
0338     NeoChatRoom *m_lastCurrentRoom;
0339     QString m_arg;
0340     KSharedConfig::Ptr m_config;
0341     KConfigGroup m_lastRoomConfig;
0342     KConfigGroup m_lastSpaceConfig;
0343     QPointer<ChatDocumentHandler> m_chatDocumentHandler;
0344 
0345     TimelineModel *m_timelineModel;
0346     MessageFilterModel *m_messageFilterModel;
0347     MediaMessageFilterModel *m_mediaMessageFilterModel;
0348     NeoChatConnection *m_connection;
0349 
0350     /**
0351      * @brief Resolve a user URI.
0352      *
0353      * This overloads Quotient::UriResolverBase::visitUser().
0354      *
0355      * Called by Quotient::UriResolverBase::visitResource() when the passed URI
0356      * identifies a Matrix user.
0357      *
0358      * @note This is private as resolveResource() should always be called, which
0359      *       will in turn call Quotient::UriResolverBase::visitResource() and this
0360      *       function if appropriate for the URI.
0361      *
0362      * @sa resolveResource(), Quotient::UriResolverBase::visitUser(), Quotient::UriResolverBase::visitResource()
0363      */
0364     UriResolveResult visitUser(User *user, const QString &action) override;
0365 
0366     /**
0367      * @brief Visit a room.
0368      *
0369      * This overloads Quotient::UriResolverBase::visitRoom().
0370      *
0371      * Called by Quotient::UriResolverBase::visitResource() when the passed URI
0372      * identifies a room or an event in a room.
0373      *
0374      * @note This is private as resolveResource() should always be called, which
0375      *       will in turn call Quotient::UriResolverBase::visitResource() and this
0376      *       function if appropriate for the URI.
0377      *
0378      * @sa resolveResource(), Quotient::UriResolverBase::visitUser(), Quotient::UriResolverBase::visitResource()
0379      */
0380     Q_INVOKABLE void visitRoom(Quotient::Room *room, const QString &eventId) override;
0381 
0382     /**
0383      * @brief Join a room.
0384      *
0385      * This overloads Quotient::UriResolverBase::joinRoom().
0386      *
0387      * Called by Quotient::UriResolverBase::visitResource() when the passed URI has
0388      * `action() == "join"` and identifies a room that the user defined by the
0389      * Connection argument is not a member of.
0390      *
0391      * @note This is private as resolveResource() should always be called, which
0392      *       will in turn call Quotient::UriResolverBase::visitResource() and this
0393      *       function if appropriate for the URI.
0394      *
0395      * @sa resolveResource(), Quotient::UriResolverBase::visitUser(), Quotient::UriResolverBase::visitResource()
0396      */
0397     void joinRoom(Quotient::Connection *account, const QString &roomAliasOrId, const QStringList &viaServers) override;
0398 
0399     /**
0400      * @brief Visit a non-matrix resource.
0401      *
0402      * This overloads Quotient::UriResolverBase::visitNonMatrix().
0403      *
0404      * Called by Quotient::UriResolverBase::visitResource() when the passed URI
0405      * has `type() == NonMatrix`
0406      *
0407      * @note This is private as resolveResource() should always be called, which
0408      *       will in turn call Quotient::UriResolverBase::visitResource() and this
0409      *       function if appropriate for the URI.
0410      *
0411      * @sa resolveResource(), Quotient::UriResolverBase::visitUser(), Quotient::UriResolverBase::visitResource()
0412      */
0413     Q_INVOKABLE bool visitNonMatrix(const QUrl &url) override;
0414 
0415 private:
0416     explicit RoomManager(QObject *parent = nullptr);
0417 };