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