aboutsummaryrefslogtreecommitdiff
path: root/lib/uriresolver.h
diff options
context:
space:
mode:
Diffstat (limited to 'lib/uriresolver.h')
-rw-r--r--lib/uriresolver.h180
1 files changed, 180 insertions, 0 deletions
diff --git a/lib/uriresolver.h b/lib/uriresolver.h
new file mode 100644
index 00000000..9140046c
--- /dev/null
+++ b/lib/uriresolver.h
@@ -0,0 +1,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 QUOTIENT_API 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
+ */
+QUOTIENT_API 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 QUOTIENT_API 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
+
+