libQuotient
A Qt library for building matrix clients
uriresolver.h
Go to the documentation of this file.
1 // SPDX-FileCopyrightText: 2020 Kitsune Ral <kitsune-ral@users.sf.net>
2 // SPDX-License-Identifier: LGPL-2.1-or-later
3 
4 #pragma once
5 
6 #include "uri.h"
7 
8 #include <QtCore/QObject>
9 
10 #include <functional>
11 
12 namespace Quotient {
13 class Connection;
14 class Room;
15 class User;
16 
17 /*! \brief Abstract class to resolve the resource and act on it
18  *
19  * This class encapsulates the logic of resolving a Matrix identifier or URI
20  * into a Quotient object (or objects) and calling an appropriate handler on it.
21  * It is a type-safe way of handling a URI with no prior context on its type
22  * in cases like, e.g., when a user clicks on a URI in the application.
23  *
24  * This class provides empty "handlers" for each type of URI to facilitate
25  * gradual implementation. Derived classes are encouraged to override as many
26  * of them as possible.
27  */
28 class QUOTIENT_API UriResolverBase {
29 public:
30  /*! \brief Resolve the resource and dispatch an action depending on its type
31  *
32  * This method:
33  * 1. Resolves \p uri into an actual object (e.g., Room or User),
34  * with possible additional data such as event id, in the context of
35  * \p account.
36  * 2. If the resolving is successful, depending on the type of the object,
37  * calls the appropriate virtual function (defined in a derived
38  * concrete class) to perform an action on the resource (open a room,
39  * mention a user etc.).
40  * 3. Returns the result of resolving the resource.
41  */
42  UriResolveResult visitResource(Connection* account, const Uri& uri);
43 
44 protected:
45  virtual ~UriResolverBase() = 0;
46 
47  /// Called by visitResource() when the passed URI identifies a Matrix user
48  /*!
49  * \return IncorrectAction if the action is not correct or not supported;
50  * UriResolved if it is accepted; other values are disallowed
51  */
52  virtual UriResolveResult visitUser(User* user [[maybe_unused]],
53  const QString& action [[maybe_unused]])
54  {
55  return IncorrectAction;
56  }
57  /// Called by visitResource() when the passed URI identifies a room or
58  /// an event in a room
59  virtual void visitRoom(Room* room [[maybe_unused]],
60  const QString& eventId [[maybe_unused]])
61  {}
62  /// Called by visitResource() when the passed URI has `action() == "join"`
63  /// and identifies a room that the user defined by the Connection argument
64  /// is not a member of
65  virtual void joinRoom(Connection* account [[maybe_unused]],
66  const QString& roomAliasOrId [[maybe_unused]],
67  const QStringList& viaServers [[maybe_unused]] = {})
68  {}
69  /// Called by visitResource() when the passed URI has `type() == NonMatrix`
70  /*!
71  * Should return true if the URI is considered resolved, false otherwise.
72  * A basic implementation in a graphical client can look like
73  * `return QDesktopServices::openUrl(url);` but it's strongly advised to
74  * ask for a user confirmation beforehand.
75  */
76  virtual bool visitNonMatrix(const QUrl& url [[maybe_unused]])
77  {
78  return false;
79  }
80 };
81 
82 /*! \brief Resolve the resource and invoke an action on it, via function objects
83  *
84  * This function encapsulates the logic of resolving a Matrix identifier or URI
85  * into a Quotient object (or objects) and calling an appropriate handler on it.
86  * Unlike UriResolverBase it accepts the list of handlers from
87  * the caller; internally it's uses a minimal UriResolverBase class
88  *
89  * \param account The connection used as a context to resolve the identifier
90  *
91  * \param uri A URI that can represent a Matrix entity
92  *
93  * \param userHandler Called when the passed URI identifies a Matrix user
94  *
95  * \param roomEventHandler Called when the passed URI identifies a room or
96  * an event in a room
97  *
98  * \param joinHandler Called when the passed URI has `action() == "join"` and
99  * identifies a room that the user defined by
100  * the Connection argument is not a member of
101  *
102  * \param nonMatrixHandler Called when the passed URI has `type() == NonMatrix`;
103  * should return true if the URI is considered resolved,
104  * false otherwise
105  *
106  * \sa UriResolverBase, UriDispatcher
107  */
108 QUOTIENT_API UriResolveResult
109 visitResource(Connection* account, const Uri& uri,
110  std::function<UriResolveResult(User*, QString)> userHandler,
111  std::function<void(Room*, QString)> roomEventHandler,
112  std::function<void(Connection*, QString, QStringList)> joinHandler,
113  std::function<bool(const QUrl&)> nonMatrixHandler);
114 
115 /*! \brief Check that the resource is resolvable with no action on it */
116 inline UriResolveResult checkResource(Connection* account, const Uri& uri)
117 {
118  return visitResource(
119  account, uri, [](auto, auto) { return UriResolved; }, [](auto, auto) {},
120  [](auto, auto, auto) {}, [](auto) { return false; });
121 }
122 
123 /*! \brief Resolve the resource and invoke an action on it, via Qt signals
124  *
125  * This is an implementation of UriResolverBase that is based on
126  * QObject and uses Qt signals instead of virtual functions to provide an
127  * open-ended interface for visitors.
128  *
129  * This class is aimed primarily at clients where invoking the resolving/action
130  * and handling the action are happening in decoupled parts of the code; it's
131  * also useful to operate on Matrix identifiers and URIs from QML/JS code
132  * that cannot call resolveResource() due to QML/C++ interface limitations.
133  *
134  * This class does not restrain the client code to a certain type of
135  * connections: both direct and queued (or a mix) will work fine. One limitation
136  * caused by that is there's no way to indicate if a non-Matrix URI has been
137  * successfully resolved - a signal always returns void.
138  *
139  * Note that in case of using (non-blocking) queued connections the code that
140  * calls resolveResource() should not expect the action to be performed
141  * synchronously - the returned value is the result of resolving the URI,
142  * not acting on it.
143  */
144 class QUOTIENT_API UriDispatcher : public QObject, public UriResolverBase {
145  Q_OBJECT
146 public:
147  explicit UriDispatcher(QObject* parent = nullptr) : QObject(parent) {}
148 
149  // It's actually UriResolverBase::visitResource() but with Q_INVOKABLE
150  Q_INVOKABLE UriResolveResult resolveResource(Connection* account,
151  const Uri& uri)
152  {
153  return UriResolverBase::visitResource(account, uri);
154  }
155 
156 Q_SIGNALS:
157  /// An action on a user has been requested
158  void userAction(Quotient::User* user, QString action);
159 
160  /// An action on a room has been requested, with optional event id
161  void roomAction(Quotient::Room* room, QString eventId);
162 
163  /// A join action has been requested, with optional 'via' servers
164  void joinAction(Quotient::Connection* account, QString roomAliasOrId,
165  QStringList viaServers);
166 
167  /// An action on a non-Matrix URL has been requested
168  void nonMatrixAction(QUrl url);
169 
170 private:
171  UriResolveResult visitUser(User* user, const QString& action) override;
172  void visitRoom(Room* room, const QString& eventId) override;
173  void joinRoom(Connection* account, const QString& roomAliasOrId,
174  const QStringList& viaServers = {}) override;
175  bool visitNonMatrix(const QUrl& url) override;
176 };
177 
178 } // namespace Quotient