aboutsummaryrefslogtreecommitdiff
path: root/lib/connection.h
diff options
context:
space:
mode:
authorBlack Hat <bhat@encom.eu.org>2019-09-26 22:22:36 -0700
committerBlack Hat <bhat@encom.eu.org>2019-09-26 22:22:36 -0700
commit363cf452bcdbaf6ff1cf94a83ca66cbb31122346 (patch)
treec64c8fda885e4e1785130e8ee3e7c47fd18cbf67 /lib/connection.h
parent412e2cf19449e73aa7da729e9c5de6502687aade (diff)
parent944653463fe4134c82d85e2d01e2bc0fa43fd727 (diff)
downloadlibquotient-363cf452bcdbaf6ff1cf94a83ca66cbb31122346.tar.gz
libquotient-363cf452bcdbaf6ff1cf94a83ca66cbb31122346.zip
Merge branch 'master' of https://github.com/quotient-im/libQuotient into
bhat-libqtolm-update
Diffstat (limited to 'lib/connection.h')
-rw-r--r--lib/connection.h1478
1 files changed, 746 insertions, 732 deletions
diff --git a/lib/connection.h b/lib/connection.h
index 199803d7..b4b16679 100644
--- a/lib/connection.h
+++ b/lib/connection.h
@@ -13,762 +13,776 @@
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#pragma once
-#include "csapi/create_room.h"
#include "joinstate.h"
-#include "events/accountdataevents.h"
#include "qt_connection_util.h"
+#include "csapi/create_room.h"
+
+#include "events/accountdataevents.h"
+
+#include <QtCore/QDir>
#include <QtCore/QObject>
-#include <QtCore/QUrl>
#include <QtCore/QSize>
-#include <QtCore/QDir>
+#include <QtCore/QUrl>
#include <functional>
namespace QtOlm {
- class Account;
+class Account;
}
-namespace QMatrixClient
+namespace Quotient {
+Q_NAMESPACE
+
+class Room;
+class User;
+class ConnectionData;
+class RoomEvent;
+
+class SyncJob;
+class SyncData;
+class RoomMessagesJob;
+class PostReceiptJob;
+class ForgetRoomJob;
+class MediaThumbnailJob;
+class JoinRoomJob;
+class UploadContentJob;
+class GetContentJob;
+class DownloadFileJob;
+class SendToDeviceJob;
+class SendMessageJob;
+class LeaveRoomJob;
+
+class Connection;
+
+using room_factory_t =
+ std::function<Room*(Connection*, const QString&, JoinState)>;
+using user_factory_t = std::function<User*(Connection*, const QString&)>;
+
+/** The default factory to create room objects
+ *
+ * Just a wrapper around operator new.
+ * \sa Connection::setRoomFactory, Connection::setRoomType
+ */
+template <typename T = Room>
+static inline room_factory_t defaultRoomFactory()
{
- class Room;
- class User;
- class ConnectionData;
- class RoomEvent;
-
- class SyncJob;
- class SyncData;
- class RoomMessagesJob;
- class PostReceiptJob;
- class ForgetRoomJob;
- class MediaThumbnailJob;
- class JoinRoomJob;
- class UploadContentJob;
- class GetContentJob;
- class DownloadFileJob;
- class SendToDeviceJob;
- class SendMessageJob;
- class LeaveRoomJob;
-
- class Connection;
-
- using room_factory_t = std::function<Room*(Connection*, const QString&,
- JoinState)>;
- using user_factory_t = std::function<User*(Connection*, const QString&)>;
-
- /** The default factory to create room objects
+ return [](Connection* c, const QString& id, JoinState js) {
+ return new T(c, id, js);
+ };
+}
+
+/** The default factory to create user objects
+ *
+ * Just a wrapper around operator new.
+ * \sa Connection::setUserFactory, Connection::setUserType
+ */
+template <typename T = User>
+static inline user_factory_t defaultUserFactory()
+{
+ return [](Connection* c, const QString& id) { return new T(id, c); };
+}
+
+/** Enumeration with flags defining the network job running policy
+ * So far only background/foreground flags are available.
+ *
+ * \sa Connection::callApi, Connection::run
+ */
+enum RunningPolicy { ForegroundRequest = 0x0, BackgroundRequest = 0x1 };
+
+Q_ENUM_NS(RunningPolicy)
+
+class Connection : public QObject {
+ Q_OBJECT
+
+ Q_PROPERTY(User* localUser READ user NOTIFY stateChanged)
+ Q_PROPERTY(QString localUserId READ userId NOTIFY stateChanged)
+ Q_PROPERTY(QString deviceId READ deviceId NOTIFY stateChanged)
+ Q_PROPERTY(QByteArray accessToken READ accessToken NOTIFY stateChanged)
+ Q_PROPERTY(QString defaultRoomVersion READ defaultRoomVersion NOTIFY
+ capabilitiesLoaded)
+ Q_PROPERTY(QUrl homeserver READ homeserver WRITE setHomeserver NOTIFY
+ homeserverChanged)
+ Q_PROPERTY(QString domain READ domain NOTIFY homeserverChanged)
+ Q_PROPERTY(bool cacheState READ cacheState WRITE setCacheState NOTIFY
+ cacheStateChanged)
+ Q_PROPERTY(bool lazyLoading READ lazyLoading WRITE setLazyLoading NOTIFY
+ lazyLoadingChanged)
+
+public:
+ // Room ids, rather than room pointers, are used in the direct chat
+ // map types because the library keeps Invite rooms separate from
+ // rooms in Join and Leave state; and direct chats in account data
+ // are stored with no regard to their state.
+ using DirectChatsMap = QMultiHash<const User*, QString>;
+ using DirectChatUsersMap = QMultiHash<QString, User*>;
+ using IgnoredUsersList = IgnoredUsersEvent::content_type;
+
+ using UsersToDevicesToEvents =
+ UnorderedMap<QString, UnorderedMap<QString, const Event&>>;
+
+ enum RoomVisibility {
+ PublishRoom,
+ UnpublishRoom
+ }; // FIXME: Should go inside CreateRoomJob
+
+ explicit Connection(QObject* parent = nullptr);
+ explicit Connection(const QUrl& server, QObject* parent = nullptr);
+ ~Connection() override;
+
+ /** Get all Invited and Joined rooms
+ * \return a hashmap from a composite key - room name and whether
+ * it's an Invite rather than Join - to room pointers
+ */
+ QHash<QPair<QString, bool>, Room*> roomMap() const;
+
+ /** Check whether the account has data of the given type
+ * Direct chats map is not supported by this method _yet_.
+ */
+ bool hasAccountData(const QString& type) const;
+
+ /** Get a generic account data event of the given type
+ * This returns an account data event of the given type
+ * stored on the server. Direct chats map cannot be retrieved
+ * using this method _yet_; use directChats() instead.
+ */
+ const EventPtr& accountData(const QString& type) const;
+
+ /** Get a generic account data event of the given type
+ * This returns an account data event of the given type
+ * stored on the server. Direct chats map cannot be retrieved
+ * using this method _yet_; use directChats() instead.
+ */
+ template <typename EventT>
+ const typename EventT::content_type accountData() const
+ {
+ if (const auto& eventPtr = accountData(EventT::matrixTypeId()))
+ return eventPtr->content();
+ return {};
+ }
+
+ /** Get account data as a JSON object
+ * This returns the content part of the account data event
+ * of the given type. Direct chats map cannot be retrieved using
+ * this method _yet_; use directChats() instead.
+ */
+ Q_INVOKABLE QJsonObject accountDataJson(const QString& type) const;
+
+ /** Set a generic account data event of the given type */
+ void setAccountData(EventPtr&& event);
+
+ Q_INVOKABLE void setAccountData(const QString& type,
+ const QJsonObject& content);
+
+ /** Get all Invited and Joined rooms grouped by tag
+ * \return a hashmap from tag name to a vector of room pointers,
+ * sorted by their order in the tag - details are at
+ * https://matrix.org/speculator/spec/drafts%2Fe2e/client_server/unstable.html#id95
+ */
+ QHash<QString, QVector<Room*>> tagsToRooms() const;
+
+ /** Get all room tags known on this connection */
+ QStringList tagNames() const;
+
+ /** Get the list of rooms with the specified tag */
+ QVector<Room*> roomsWithTag(const QString& tagName) const;
+
+ /** Mark the room as a direct chat with the user
+ * This function marks \p room as a direct chat with \p user.
+ * Emits the signal synchronously, without waiting to complete
+ * synchronisation with the server.
+ *
+ * \sa directChatsListChanged
+ */
+ void addToDirectChats(const Room* room, User* user);
+
+ /** Unmark the room from direct chats
+ * This function removes the room id from direct chats either for
+ * a specific \p user or for all users if \p user in nullptr.
+ * The room id is used to allow removal of, e.g., ids of forgotten
+ * rooms; a Room object need not exist. Emits the signal
+ * immediately, without waiting to complete synchronisation with
+ * the server.
+ *
+ * \sa directChatsListChanged
+ */
+ void removeFromDirectChats(const QString& roomId, User* user = nullptr);
+
+ /** Check whether the room id corresponds to a direct chat */
+ bool isDirectChat(const QString& roomId) const;
+
+ /** Get the whole map from users to direct chat rooms */
+ DirectChatsMap directChats() const;
+
+ /** Retrieve the list of users the room is a direct chat with
+ * @return The list of users for which this room is marked as
+ * a direct chat; an empty list if the room is not a direct chat
+ */
+ QList<User*> directChatUsers(const Room* room) const;
+
+ /** Check whether a particular user is in the ignore list */
+ Q_INVOKABLE bool isIgnored(const User* user) const;
+
+ /** Get the whole list of ignored users */
+ Q_INVOKABLE IgnoredUsersList ignoredUsers() const;
+
+ /** Add the user to the ignore list
+ * The change signal is emitted synchronously, without waiting
+ * to complete synchronisation with the server.
+ *
+ * \sa ignoredUsersListChanged
+ */
+ Q_INVOKABLE void addToIgnoredUsers(const User* user);
+
+ /** Remove the user from the ignore list */
+ /** Similar to adding, the change signal is emitted synchronously.
+ *
+ * \sa ignoredUsersListChanged
+ */
+ Q_INVOKABLE void removeFromIgnoredUsers(const User* user);
+
+ /** Get the full list of users known to this account */
+ QMap<QString, User*> users() const;
+
+ /** Get the base URL of the homeserver to connect to */
+ QUrl homeserver() const;
+ /** Get the domain name used for ids/aliases on the server */
+ QString domain() const;
+ /** Find a room by its id and a mask of applicable states */
+ Q_INVOKABLE Room* room(const QString& roomId,
+ JoinStates states = JoinState::Invite
+ | JoinState::Join) const;
+ /** Find a room by its alias and a mask of applicable states */
+ Q_INVOKABLE Room* roomByAlias(const QString& roomAlias,
+ JoinStates states = JoinState::Invite
+ | JoinState::Join) const;
+ /** Update the internal map of room aliases to IDs */
+ /// This is used to maintain the internal index of room aliases.
+ /// It does NOT change aliases on the server,
+ /// \sa Room::setLocalAliases
+ void updateRoomAliases(const QString& roomId, const QString& aliasServer,
+ const QStringList& previousRoomAliases,
+ const QStringList& roomAliases);
+ Q_INVOKABLE Room* invitation(const QString& roomId) const;
+ Q_INVOKABLE User* user(const QString& userId);
+ const User* user() const;
+ User* user();
+ QString userId() const;
+ QString deviceId() const;
+ QByteArray accessToken() const;
+ QtOlm::Account* olmAccount() const;
+ Q_INVOKABLE SyncJob* syncJob() const;
+ Q_INVOKABLE int millisToReconnect() const;
+
+ Q_INVOKABLE void getTurnServers();
+
+ struct SupportedRoomVersion {
+ QString id;
+ QString status;
+
+ static const QString StableTag; // "stable", as of CS API 0.5
+ bool isStable() const { return status == StableTag; }
+
+ friend QDebug operator<<(QDebug dbg, const SupportedRoomVersion& v)
+ {
+ QDebugStateSaver _(dbg);
+ return dbg.nospace() << v.id << '/' << v.status;
+ }
+ };
+
+ /// Get the room version recommended by the server
+ /** Only works after server capabilities have been loaded.
+ * \sa loadingCapabilities */
+ QString defaultRoomVersion() const;
+ /// Get the room version considered stable by the server
+ /** Only works after server capabilities have been loaded.
+ * \sa loadingCapabilities */
+ QStringList stableRoomVersions() const;
+ /// Get all room versions supported by the server
+ /** Only works after server capabilities have been loaded.
+ * \sa loadingCapabilities */
+ QVector<SupportedRoomVersion> availableRoomVersions() const;
+
+ /**
+ * Call this before first sync to load from previously saved file.
+ *
+ * \param fromFile A local path to read the state from. Uses QUrl
+ * to be QML-friendly. Empty parameter means saving to the directory
+ * defined by stateCachePath() / stateCacheDir().
+ */
+ Q_INVOKABLE void loadState();
+ /**
+ * This method saves the current state of rooms (but not messages
+ * in them) to a local cache file, so that it could be loaded by
+ * loadState() on a next run of the client.
*
- * Just a wrapper around operator new.
- * \sa Connection::setRoomFactory, Connection::setRoomType
+ * \param toFile A local path to save the state to. Uses QUrl to be
+ * QML-friendly. Empty parameter means saving to the directory
+ * defined by stateCachePath() / stateCacheDir().
+ */
+ Q_INVOKABLE void saveState() const;
+
+ /// This method saves the current state of a single room.
+ void saveRoomState(Room* r) const;
+
+ /// Get the default directory path to save the room state to
+ /** \sa stateCacheDir */
+ Q_INVOKABLE QString stateCachePath() const;
+
+ /// Get the default directory to save the room state to
+ /**
+ * This function returns the default directory to store the cached
+ * room state, defined as follows:
+ * \code
+ * QStandardPaths::writeableLocation(QStandardPaths::CacheLocation) +
+ * _safeUserId + "_state.json" \endcode where `_safeUserId` is userId() with
+ * `:` (colon) replaced by
+ * `_` (underscore), as colons are reserved characters on Windows.
+ * \sa loadState, saveState, stateCachePath
+ */
+ QDir stateCacheDir() const;
+
+ /** Whether or not the rooms state should be cached locally
+ * \sa loadState(), saveState()
*/
- template <typename T = Room>
- static inline room_factory_t defaultRoomFactory()
+ bool cacheState() const;
+ void setCacheState(bool newValue);
+
+ bool lazyLoading() const;
+ void setLazyLoading(bool newValue);
+
+ /*! Start a pre-created job object on this connection */
+ void run(BaseJob* job, RunningPolicy runningPolicy = ForegroundRequest) const;
+
+ /*! Start a job of a specified type with specified arguments and policy
+ *
+ * This is a universal method to create and start a job of a type passed
+ * as a template parameter. The policy allows to fine-tune the way
+ * the job is executed - as of this writing it means a choice
+ * between "foreground" and "background".
+ *
+ * \param runningPolicy controls how the job is executed
+ * \param jobArgs arguments to the job constructor
+ *
+ * \sa BaseJob::isBackground. QNetworkRequest::BackgroundRequestAttribute
+ */
+ template <typename JobT, typename... JobArgTs>
+ JobT* callApi(RunningPolicy runningPolicy, JobArgTs&&... jobArgs) const
{
- return [](Connection* c, const QString& id, JoinState js)
- {
- return new T(c, id, js);
- };
+ auto job = new JobT(std::forward<JobArgTs>(jobArgs)...);
+ run(job, runningPolicy);
+ return job;
}
- /** The default factory to create user objects
+ /*! Start a job of a specified type with specified arguments
*
- * Just a wrapper around operator new.
- * \sa Connection::setUserFactory, Connection::setUserType
+ * This is an overload that runs the job with "foreground" policy.
*/
- template <typename T = User>
- static inline user_factory_t defaultUserFactory()
+ template <typename JobT, typename... JobArgTs>
+ JobT* callApi(JobArgTs&&... jobArgs) const
{
- return [](Connection* c, const QString& id)
- {
- return new T(id, c);
- };
+ return callApi<JobT>(ForegroundRequest,
+ std::forward<JobArgTs>(jobArgs)...);
}
- /** Enumeration with flags defining the network job running policy
- * So far only background/foreground flags are available.
+ /** Generate a new transaction id. Transaction id's are unique within
+ * a single Connection object
+ */
+ Q_INVOKABLE QByteArray generateTxnId() const;
+
+ /// Set a room factory function
+ static void setRoomFactory(room_factory_t f);
+
+ /// Set a user factory function
+ static void setUserFactory(user_factory_t f);
+
+ /// Get a room factory function
+ static room_factory_t roomFactory();
+
+ /// Get a user factory function
+ static user_factory_t userFactory();
+
+ /// Set the room factory to default with the overriden room type
+ template <typename T>
+ static void setRoomType()
+ {
+ setRoomFactory(defaultRoomFactory<T>());
+ }
+
+ /// Set the user factory to default with the overriden user type
+ template <typename T>
+ static void setUserType()
+ {
+ setUserFactory(defaultUserFactory<T>());
+ }
+
+public slots:
+ /** Set the homeserver base URL */
+ void setHomeserver(const QUrl& baseUrl);
+
+ /** Determine and set the homeserver from MXID */
+ void resolveServer(const QString& mxid);
+
+ void connectToServer(const QString& user, const QString& password,
+ const QString& initialDeviceName,
+ const QString& deviceId = {});
+ void connectWithToken(const QString& userId, const QString& accessToken,
+ const QString& deviceId);
+ /// Explicitly request capabilities from the server
+ void reloadCapabilities();
+
+ /// Find out if capabilites are still loading from the server
+ bool loadingCapabilities() const;
+
+ /** @deprecated Use stopSync() instead */
+ void disconnectFromServer() { stopSync(); }
+ void logout();
+
+ void sync(int timeout = -1);
+ void syncLoop(int timeout = -1);
+
+ void stopSync();
+ QString nextBatchToken() const;
+
+ virtual MediaThumbnailJob*
+ getThumbnail(const QString& mediaId, QSize requestedSize,
+ RunningPolicy policy = BackgroundRequest) const;
+ MediaThumbnailJob*
+ getThumbnail(const QUrl& url, QSize requestedSize,
+ RunningPolicy policy = BackgroundRequest) const;
+ MediaThumbnailJob*
+ getThumbnail(const QUrl& url, int requestedWidth, int requestedHeight,
+ RunningPolicy policy = BackgroundRequest) const;
+
+ // QIODevice* should already be open
+ UploadContentJob*
+ uploadContent(QIODevice* contentSource, const QString& filename = {},
+ const QString& overrideContentType = {}) const;
+ UploadContentJob* uploadFile(const QString& fileName,
+ const QString& overrideContentType = {});
+ GetContentJob* getContent(const QString& mediaId) const;
+ GetContentJob* getContent(const QUrl& url) const;
+ // If localFilename is empty, a temporary file will be created
+ DownloadFileJob* downloadFile(const QUrl& url,
+ const QString& localFilename = {}) const;
+
+ /**
+ * \brief Create a room (generic method)
+ * This method allows to customize room entirely to your liking,
+ * providing all the attributes the original CS API provides.
+ */
+ CreateRoomJob*
+ createRoom(RoomVisibility visibility, const QString& alias,
+ const QString& name, const QString& topic, QStringList invites,
+ const QString& presetName = {}, const QString& roomVersion = {},
+ bool isDirect = false,
+ const QVector<CreateRoomJob::StateEvent>& initialState = {},
+ const QVector<CreateRoomJob::Invite3pid>& invite3pids = {},
+ const QJsonObject& creationContent = {});
+
+ /** Get a direct chat with a single user
+ * This method may return synchronously or asynchoronously depending
+ * on whether a direct chat room with the respective person exists
+ * already.
*
- * \sa Connection::callApi
- */
- enum RunningPolicy { ForegroundRequest = 0x0, BackgroundRequest = 0x1 };
-
- class Connection: public QObject {
- Q_OBJECT
-
- Q_PROPERTY(User* localUser READ user NOTIFY stateChanged)
- Q_PROPERTY(QString localUserId READ userId NOTIFY stateChanged)
- Q_PROPERTY(QString deviceId READ deviceId NOTIFY stateChanged)
- Q_PROPERTY(QByteArray accessToken READ accessToken NOTIFY stateChanged)
- Q_PROPERTY(QString defaultRoomVersion READ defaultRoomVersion NOTIFY capabilitiesLoaded)
- Q_PROPERTY(QUrl homeserver READ homeserver WRITE setHomeserver NOTIFY homeserverChanged)
- Q_PROPERTY(QString domain READ domain NOTIFY homeserverChanged)
- Q_PROPERTY(bool cacheState READ cacheState WRITE setCacheState NOTIFY cacheStateChanged)
- Q_PROPERTY(bool lazyLoading READ lazyLoading WRITE setLazyLoading NOTIFY lazyLoadingChanged)
-
- public:
- // Room ids, rather than room pointers, are used in the direct chat
- // map types because the library keeps Invite rooms separate from
- // rooms in Join and Leave state; and direct chats in account data
- // are stored with no regard to their state.
- using DirectChatsMap = QMultiHash<const User*, QString>;
- using DirectChatUsersMap = QMultiHash<QString, User*>;
- using IgnoredUsersList = IgnoredUsersEvent::content_type;
-
- using UsersToDevicesToEvents =
- std::unordered_map<QString,
- std::unordered_map<QString, const Event&>>;
-
- enum RoomVisibility { PublishRoom, UnpublishRoom }; // FIXME: Should go inside CreateRoomJob
-
- explicit Connection(QObject* parent = nullptr);
- explicit Connection(const QUrl& server, QObject* parent = nullptr);
- ~Connection() override;
-
- /** Get all Invited and Joined rooms
- * \return a hashmap from a composite key - room name and whether
- * it's an Invite rather than Join - to room pointers
- */
- QHash<QPair<QString, bool>, Room*> roomMap() const;
-
- /** Check whether the account has data of the given type
- * Direct chats map is not supported by this method _yet_.
- */
- bool hasAccountData(const QString& type) const;
-
- /** Get a generic account data event of the given type
- * This returns an account data event of the given type
- * stored on the server. Direct chats map cannot be retrieved
- * using this method _yet_; use directChats() instead.
- */
- const EventPtr& accountData(const QString& type) const;
-
- /** Get a generic account data event of the given type
- * This returns an account data event of the given type
- * stored on the server. Direct chats map cannot be retrieved
- * using this method _yet_; use directChats() instead.
- */
- template <typename EventT>
- const typename EventT::content_type accountData() const
- {
- if (const auto& eventPtr = accountData(EventT::matrixTypeId()))
- return eventPtr->content();
- return {};
- }
-
- /** Get account data as a JSON object
- * This returns the content part of the account data event
- * of the given type. Direct chats map cannot be retrieved using
- * this method _yet_; use directChats() instead.
- */
- Q_INVOKABLE QJsonObject accountDataJson(const QString& type) const;
-
- /** Set a generic account data event of the given type */
- void setAccountData(EventPtr&& event);
-
- Q_INVOKABLE void setAccountData(const QString& type,
- const QJsonObject& content);
-
- /** Get all Invited and Joined rooms grouped by tag
- * \return a hashmap from tag name to a vector of room pointers,
- * sorted by their order in the tag - details are at
- * https://matrix.org/speculator/spec/drafts%2Fe2e/client_server/unstable.html#id95
- */
- QHash<QString, QVector<Room*>> tagsToRooms() const;
-
- /** Get all room tags known on this connection */
- QStringList tagNames() const;
-
- /** Get the list of rooms with the specified tag */
- QVector<Room*> roomsWithTag(const QString& tagName) const;
-
- /** Mark the room as a direct chat with the user
- * This function marks \p room as a direct chat with \p user.
- * Emits the signal synchronously, without waiting to complete
- * synchronisation with the server.
- *
- * \sa directChatsListChanged
- */
- void addToDirectChats(const Room* room, User* user);
-
- /** Unmark the room from direct chats
- * This function removes the room id from direct chats either for
- * a specific \p user or for all users if \p user in nullptr.
- * The room id is used to allow removal of, e.g., ids of forgotten
- * rooms; a Room object need not exist. Emits the signal
- * immediately, without waiting to complete synchronisation with
- * the server.
- *
- * \sa directChatsListChanged
- */
- void removeFromDirectChats(const QString& roomId,
- User* user = nullptr);
-
- /** Check whether the room id corresponds to a direct chat */
- bool isDirectChat(const QString& roomId) const;
-
- /** Get the whole map from users to direct chat rooms */
- DirectChatsMap directChats() const;
-
- /** Retrieve the list of users the room is a direct chat with
- * @return The list of users for which this room is marked as
- * a direct chat; an empty list if the room is not a direct chat
- */
- QList<User*> directChatUsers(const Room* room) const;
-
- /** Check whether a particular user is in the ignore list */
- Q_INVOKABLE bool isIgnored(const User* user) const;
-
- /** Get the whole list of ignored users */
- Q_INVOKABLE IgnoredUsersList ignoredUsers() const;
-
- /** Add the user to the ignore list
- * The change signal is emitted synchronously, without waiting
- * to complete synchronisation with the server.
- *
- * \sa ignoredUsersListChanged
- */
- Q_INVOKABLE void addToIgnoredUsers(const User* user);
-
- /** Remove the user from the ignore list */
- /** Similar to adding, the change signal is emitted synchronously.
- *
- * \sa ignoredUsersListChanged
- */
- Q_INVOKABLE void removeFromIgnoredUsers(const User* user);
-
- /** Get the full list of users known to this account */
- QMap<QString, User*> users() const;
-
- /** Get the base URL of the homeserver to connect to */
- QUrl homeserver() const;
- /** Get the domain name used for ids/aliases on the server */
- QString domain() const;
- /** Find a room by its id and a mask of applicable states */
- Q_INVOKABLE Room* room(const QString& roomId,
- JoinStates states = JoinState::Invite|JoinState::Join) const;
- /** Find a room by its alias and a mask of applicable states */
- Q_INVOKABLE Room* roomByAlias(const QString& roomAlias,
- JoinStates states = JoinState::Invite|JoinState::Join) const;
- /** Update the internal map of room aliases to IDs */
- /// This is used to maintain the internal index of room aliases.
- /// It does NOT change aliases on the server,
- /// \sa Room::setLocalAliases
- void updateRoomAliases(const QString& roomId,
- const QString& aliasServer,
- const QStringList& previousRoomAliases,
- const QStringList& roomAliases);
- Q_INVOKABLE Room* invitation(const QString& roomId) const;
- Q_INVOKABLE User* user(const QString& userId);
- const User* user() const;
- User* user();
- QString userId() const;
- QString deviceId() const;
- QByteArray accessToken() const;
- QtOlm::Account* olmAccount() const;
- Q_INVOKABLE SyncJob* syncJob() const;
- Q_INVOKABLE int millisToReconnect() const;
-
- Q_INVOKABLE void getTurnServers();
-
- struct SupportedRoomVersion
- {
- QString id;
- QString status;
-
- static const QString StableTag; // "stable", as of CS API 0.5
- bool isStable() const { return status == StableTag; }
-
- friend QDebug operator<<(QDebug dbg,
- const SupportedRoomVersion& v)
- {
- QDebugStateSaver _(dbg);
- return dbg.nospace() << v.id << '/' << v.status;
- }
- };
-
- /// Get the room version recommended by the server
- /** Only works after server capabilities have been loaded.
- * \sa loadingCapabilities */
- QString defaultRoomVersion() const;
- /// Get the room version considered stable by the server
- /** Only works after server capabilities have been loaded.
- * \sa loadingCapabilities */
- QStringList stableRoomVersions() const;
- /// Get all room versions supported by the server
- /** Only works after server capabilities have been loaded.
- * \sa loadingCapabilities */
- QVector<SupportedRoomVersion> availableRoomVersions() const;
-
- /**
- * Call this before first sync to load from previously saved file.
- *
- * \param fromFile A local path to read the state from. Uses QUrl
- * to be QML-friendly. Empty parameter means saving to the directory
- * defined by stateCachePath() / stateCacheDir().
- */
- Q_INVOKABLE void loadState();
- /**
- * This method saves the current state of rooms (but not messages
- * in them) to a local cache file, so that it could be loaded by
- * loadState() on a next run of the client.
- *
- * \param toFile A local path to save the state to. Uses QUrl to be
- * QML-friendly. Empty parameter means saving to the directory
- * defined by stateCachePath() / stateCacheDir().
- */
- Q_INVOKABLE void saveState() const;
-
- /// This method saves the current state of a single room.
- void saveRoomState(Room* r) const;
-
- /// Get the default directory path to save the room state to
- /** \sa stateCacheDir */
- Q_INVOKABLE QString stateCachePath() const;
-
- /// Get the default directory to save the room state to
- /**
- * This function returns the default directory to store the cached
- * room state, defined as follows:
- * \code
- * QStandardPaths::writeableLocation(QStandardPaths::CacheLocation) + _safeUserId + "_state.json"
- * \endcode
- * where `_safeUserId` is userId() with `:` (colon) replaced by
- * `_` (underscore), as colons are reserved characters on Windows.
- * \sa loadState, saveState, stateCachePath
- */
- QDir stateCacheDir() const;
-
- /** Whether or not the rooms state should be cached locally
- * \sa loadState(), saveState()
- */
- bool cacheState() const;
- void setCacheState(bool newValue);
-
- bool lazyLoading() const;
- void setLazyLoading(bool newValue);
-
- /** Start a job of a specified type with specified arguments and policy
- *
- * This is a universal method to start a job of a type passed
- * as a template parameter. The policy allows to fine-tune the way
- * the job is executed - as of this writing it means a choice
- * between "foreground" and "background".
- *
- * \param runningPolicy controls how the job is executed
- * \param jobArgs arguments to the job constructor
- *
- * \sa BaseJob::isBackground. QNetworkRequest::BackgroundRequestAttribute
- */
- template <typename JobT, typename... JobArgTs>
- JobT* callApi(RunningPolicy runningPolicy,
- JobArgTs&&... jobArgs) const
- {
- auto job = new JobT(std::forward<JobArgTs>(jobArgs)...);
- connect(job, &BaseJob::failure, this, &Connection::requestFailed);
- job->start(connectionData(), runningPolicy&BackgroundRequest);
- return job;
- }
-
- /** Start a job of a specified type with specified arguments
- *
- * This is an overload that calls the job with "foreground" policy.
- */
- template <typename JobT, typename... JobArgTs>
- JobT* callApi(JobArgTs&&... jobArgs) const
- {
- return callApi<JobT>(ForegroundRequest,
- std::forward<JobArgTs>(jobArgs)...);
- }
-
- /** Generate a new transaction id. Transaction id's are unique within
- * a single Connection object
- */
- Q_INVOKABLE QByteArray generateTxnId() const;
-
- /// Set a room factory function
- static void setRoomFactory(room_factory_t f);
-
- /// Set a user factory function
- static void setUserFactory(user_factory_t f);
-
- /// Get a room factory function
- static room_factory_t roomFactory();
-
- /// Get a user factory function
- static user_factory_t userFactory();
-
- /// Set the room factory to default with the overriden room type
- template <typename T>
- static void setRoomType() { setRoomFactory(defaultRoomFactory<T>()); }
-
- /// Set the user factory to default with the overriden user type
- template <typename T>
- static void setUserType() { setUserFactory(defaultUserFactory<T>()); }
-
- public slots:
- /** Set the homeserver base URL */
- void setHomeserver(const QUrl& baseUrl);
-
- /** Determine and set the homeserver from MXID */
- void resolveServer(const QString& mxid);
-
- void connectToServer(const QString& user, const QString& password,
- const QString& initialDeviceName,
- const QString& deviceId = {});
- void connectWithToken(const QString& userId, const QString& accessToken,
- const QString& deviceId);
- /// Explicitly request capabilities from the server
- void reloadCapabilities();
-
- /// Find out if capabilites are still loading from the server
- bool loadingCapabilities() const;
-
- /** @deprecated Use stopSync() instead */
- void disconnectFromServer() { stopSync(); }
- void logout();
-
- void sync(int timeout = -1);
- void syncLoop(int timeout = -1);
-
- void stopSync();
- QString nextBatchToken() const;
-
- virtual MediaThumbnailJob* getThumbnail(const QString& mediaId,
- QSize requestedSize, RunningPolicy policy = BackgroundRequest) const;
- MediaThumbnailJob* getThumbnail(const QUrl& url,
- QSize requestedSize, RunningPolicy policy = BackgroundRequest) const;
- MediaThumbnailJob* getThumbnail(const QUrl& url,
- int requestedWidth, int requestedHeight,
- RunningPolicy policy = BackgroundRequest) const;
-
- // QIODevice* should already be open
- UploadContentJob* uploadContent(QIODevice* contentSource,
- const QString& filename = {},
- const QString& overrideContentType = {}) const;
- UploadContentJob* uploadFile(const QString& fileName,
- const QString& overrideContentType = {});
- GetContentJob* getContent(const QString& mediaId) const;
- GetContentJob* getContent(const QUrl& url) const;
- // If localFilename is empty, a temporary file will be created
- DownloadFileJob* downloadFile(const QUrl& url,
- const QString& localFilename = {}) const;
-
- /**
- * \brief Create a room (generic method)
- * This method allows to customize room entirely to your liking,
- * providing all the attributes the original CS API provides.
- */
- CreateRoomJob* createRoom(RoomVisibility visibility,
- const QString& alias, const QString& name, const QString& topic,
- QStringList invites, const QString& presetName = {},
- const QString& roomVersion = {}, bool isDirect = false,
- const QVector<CreateRoomJob::StateEvent>& initialState = {},
- const QVector<CreateRoomJob::Invite3pid>& invite3pids = {},
- const QJsonObject& creationContent = {});
-
- /** Get a direct chat with a single user
- * This method may return synchronously or asynchoronously depending
- * on whether a direct chat room with the respective person exists
- * already.
- *
- * \sa directChatAvailable
- */
- void requestDirectChat(const QString& userId);
-
- /** Get a direct chat with a single user
- * This method may return synchronously or asynchoronously depending
- * on whether a direct chat room with the respective person exists
- * already.
- *
- * \sa directChatAvailable
- */
- void requestDirectChat(User* u);
-
- /** Run an operation in a direct chat with the user
- * This method may return synchronously or asynchoronously depending
- * on whether a direct chat room with the respective person exists
- * already. Instead of emitting a signal it executes the passed
- * function object with the direct chat room as its parameter.
- */
- void doInDirectChat(const QString& userId,
- const std::function<void(Room*)>& operation);
-
- /** Run an operation in a direct chat with the user
- * This method may return synchronously or asynchoronously depending
- * on whether a direct chat room with the respective person exists
- * already. Instead of emitting a signal it executes the passed
- * function object with the direct chat room as its parameter.
- */
- void doInDirectChat(User* u,
- const std::function<void(Room*)>& operation);
-
- /** Create a direct chat with a single user, optional name and topic
- * A room will always be created, unlike in requestDirectChat.
- * It is advised to use requestDirectChat as a default way of getting
- * one-on-one with a person, and only use createDirectChat when
- * a new creation is explicitly desired.
- */
- CreateRoomJob* createDirectChat(const QString& userId,
- const QString& topic = {}, const QString& name = {});
-
- virtual JoinRoomJob* joinRoom(const QString& roomAlias,
- const QStringList& serverNames = {});
-
- /** Sends /forget to the server and also deletes room locally.
- * This method is in Connection, not in Room, since it's a
- * room lifecycle operation, and Connection is an acting room manager.
- * It ensures that the local user is not a member of a room (running /leave,
- * if necessary) then issues a /forget request and if that one doesn't fail
- * deletion of the local Room object is ensured.
- * \param id - the room id to forget
- * \return - the ongoing /forget request to the server; note that the
- * success() signal of this request is connected to deleteLater()
- * of a respective room so by the moment this finishes, there might be no
- * Room object anymore.
- */
- ForgetRoomJob* forgetRoom(const QString& id);
-
- SendToDeviceJob* sendToDevices(const QString& eventType,
- const UsersToDevicesToEvents& eventsMap) const;
-
- /** \deprecated This method is experimental and may be removed any time */
- SendMessageJob* sendMessage(const QString& roomId,
- const RoomEvent& event) const;
-
- /** \deprecated Do not use this directly, use Room::leaveRoom() instead */
- virtual LeaveRoomJob* leaveRoom( Room* room );
-
- // Old API that will be abolished any time soon. DO NOT USE.
-
- /** @deprecated Use callApi<PostReceiptJob>() or Room::postReceipt() instead */
- virtual PostReceiptJob* postReceipt(Room* room,
- RoomEvent* event) const;
- signals:
- /**
- * @deprecated
- * This was a signal resulting from a successful resolveServer().
- * Since Connection now provides setHomeserver(), the HS URL
- * may change even without resolveServer() invocation. Use
- * homeserverChanged() instead of resolved(). You can also use
- * connectToServer and connectWithToken without the HS URL set in
- * advance (i.e. without calling resolveServer), as they now trigger
- * server name resolution from MXID if the server URL is not valid.
- */
- void resolved();
- void resolveError(QString error);
-
- void homeserverChanged(QUrl baseUrl);
- void capabilitiesLoaded();
-
- void connected();
- void reconnected(); //< \deprecated Use connected() instead
- void loggedOut();
- /** Login data or state have changed
- *
- * This is a common change signal for userId, deviceId and
- * accessToken - these properties normally only change at
- * a successful login and logout and are constant at other times.
- */
- void stateChanged();
- void loginError(QString message, QString details);
-
- /** A network request (job) failed
- *
- * @param request - the pointer to the failed job
- */
- void requestFailed(BaseJob* request);
-
- /** A network request (job) failed due to network problems
- *
- * This is _only_ emitted when the job will retry on its own;
- * once it gives up, requestFailed() will be emitted.
- *
- * @param message - message about the network problem
- * @param details - raw error details, if any available
- * @param retriesTaken - how many retries have already been taken
- * @param nextRetryInMilliseconds - when the job will retry again
- */
- void networkError(QString message, QString details,
- int retriesTaken, int nextRetryInMilliseconds);
-
- void syncDone();
- void syncError(QString message, QString details);
-
- void newUser(User* user);
-
- /**
- * \group Signals emitted on room transitions
- *
- * Note: Rooms in Invite state are always stored separately from
- * rooms in Join/Leave state, because of special treatment of
- * invite_state in Matrix CS API (see The Spec on /sync for details).
- * Therefore, objects below are: r - room in Join/Leave state;
- * i - room in Invite state
- *
- * 1. none -> Invite: newRoom(r), invitedRoom(r,nullptr)
- * 2. none -> Join: newRoom(r), joinedRoom(r,nullptr)
- * 3. none -> Leave: newRoom(r), leftRoom(r,nullptr)
- * 4. Invite -> Join:
- * newRoom(r), joinedRoom(r,i), aboutToDeleteRoom(i)
- * 4a. Leave and Invite -> Join:
- * joinedRoom(r,i), aboutToDeleteRoom(i)
- * 5. Invite -> Leave:
- * newRoom(r), leftRoom(r,i), aboutToDeleteRoom(i)
- * 5a. Leave and Invite -> Leave:
- * leftRoom(r,i), aboutToDeleteRoom(i)
- * 6. Join -> Leave: leftRoom(r)
- * 7. Leave -> Invite: newRoom(i), invitedRoom(i,r)
- * 8. Leave -> Join: joinedRoom(r)
- * The following transitions are only possible via forgetRoom()
- * so far; if a room gets forgotten externally, sync won't tell
- * about it:
- * 9. any -> none: as any -> Leave, then aboutToDeleteRoom(r)
- */
-
- /** A new room object has been created */
- void newRoom(Room* room);
-
- /** A room invitation is seen for the first time
- *
- * If the same room is in Left state, it's passed in prev. Beware
- * that initial sync will trigger this signal for all rooms in
- * Invite state.
- */
- void invitedRoom(Room* room, Room* prev);
-
- /** A joined room is seen for the first time
- *
- * It's not the same as receiving a room in "join" section of sync
- * response (rooms will be there even after joining); it's also
- * not (exactly) the same as actual joining action of a user (all
- * rooms coming in initial sync will trigger this signal too). If
- * this room was in Invite state before, the respective object is
- * passed in prev (and it will be deleted shortly afterwards).
- */
- void joinedRoom(Room* room, Room* prev);
-
- /** A room has just been left
- *
- * If this room has been in Invite state (as in case of rejecting
- * an invitation), the respective object will be passed in prev
- * (and will be deleted shortly afterwards). Note that, similar
- * to invitedRoom and joinedRoom, this signal is triggered for all
- * Left rooms upon initial sync (not only those that were left
- * right before the sync).
- */
- void leftRoom(Room* room, Room* prev);
-
- /** The room object is about to be deleted */
- void aboutToDeleteRoom(Room* room);
-
- /** The room has just been created by createRoom or requestDirectChat
- *
- * This signal is not emitted in usual room state transitions,
- * only as an outcome of room creation operations invoked by
- * the client.
- * \note requestDirectChat doesn't necessarily create a new chat;
- * use directChatAvailable signal if you just need to obtain
- * a direct chat room.
- */
- void createdRoom(Room* room);
-
- /** The first sync for the room has been completed
- *
- * This signal is emitted after the room has been synced the first
- * time. This is the right signal to connect to if you need to
- * access the room state (name, aliases, members); state transition
- * signals (newRoom, joinedRoom etc.) come earlier, when the room
- * has just been created.
- */
- void loadedRoomState(Room* room);
-
- /** Account data (except direct chats) have changed */
- void accountDataChanged(QString type);
-
- /** The direct chat room is ready for using
- * This signal is emitted upon any successful outcome from
- * requestDirectChat.
- */
- void directChatAvailable(Room* directChat);
-
- /** The list of direct chats has changed
- * This signal is emitted every time when the mapping of users
- * to direct chat rooms is changed (because of either local updates
- * or a different list arrived from the server).
- */
- void directChatsListChanged(DirectChatsMap additions,
- DirectChatsMap removals);
-
- void ignoredUsersListChanged(IgnoredUsersList additions,
- IgnoredUsersList removals);
-
- void cacheStateChanged();
- void lazyLoadingChanged();
- void turnServersChanged(const QJsonObject& servers);
-
- protected:
- /**
- * @brief Access the underlying ConnectionData class
- */
- const ConnectionData* connectionData() const;
-
- /** Get a Room object for the given id in the given state
- *
- * Use this method when you need a Room object in the local list
- * of rooms, with the given state. Note that this does not interact
- * with the server; in particular, does not automatically create
- * rooms on the server. This call performs necessary join state
- * transitions; e.g., if it finds a room in Invite but
- * `joinState == JoinState::Join` then the Invite room object
- * will be deleted and a new room object with Join state created.
- * In contrast, switching between Join and Leave happens within
- * the same object.
- * \param roomId room id (not alias!)
- * \param joinState desired (target) join state of the room; if
- * omitted, any state will be found and return unchanged, or a
- * new Join room created.
- * @return a pointer to a Room object with the specified id and the
- * specified state; nullptr if roomId is empty or if roomFactory()
- * failed to create a Room object.
- */
- Room* provideRoom(const QString& roomId,
- Omittable<JoinState> joinState = none);
-
- /**
- * Completes loading sync data.
- */
- void onSyncSuccess(SyncData &&data, bool fromCache = false);
-
- protected slots:
- void syncLoopIteration();
-
- private:
- class Private;
- QScopedPointer<Private> d;
-
- /**
- * A single entry for functions that need to check whether the
- * homeserver is valid before running. May either execute connectFn
- * synchronously or asynchronously (if tryResolve is true and
- * a DNS lookup is initiated); in case of errors, emits resolveError
- * if the homeserver URL is not valid and cannot be resolved from
- * userId.
- *
- * @param userId - fully-qualified MXID to resolve HS from
- * @param connectFn - a function to execute once the HS URL is good
- */
- void checkAndConnect(const QString& userId,
- std::function<void()> connectFn);
- void doConnectToServer(const QString& user, const QString& password,
- const QString& initialDeviceName,
- const QString& deviceId = {});
-
- static room_factory_t _roomFactory;
- static user_factory_t _userFactory;
- };
-} // namespace QMatrixClient
-Q_DECLARE_METATYPE(QMatrixClient::Connection*)
+ * \sa directChatAvailable
+ */
+ void requestDirectChat(const QString& userId);
+
+ /** Get a direct chat with a single user
+ * This method may return synchronously or asynchoronously depending
+ * on whether a direct chat room with the respective person exists
+ * already.
+ *
+ * \sa directChatAvailable
+ */
+ void requestDirectChat(User* u);
+
+ /** Run an operation in a direct chat with the user
+ * This method may return synchronously or asynchoronously depending
+ * on whether a direct chat room with the respective person exists
+ * already. Instead of emitting a signal it executes the passed
+ * function object with the direct chat room as its parameter.
+ */
+ void doInDirectChat(const QString& userId,
+ const std::function<void(Room*)>& operation);
+
+ /** Run an operation in a direct chat with the user
+ * This method may return synchronously or asynchoronously depending
+ * on whether a direct chat room with the respective person exists
+ * already. Instead of emitting a signal it executes the passed
+ * function object with the direct chat room as its parameter.
+ */
+ void doInDirectChat(User* u, const std::function<void(Room*)>& operation);
+
+ /** Create a direct chat with a single user, optional name and topic
+ * A room will always be created, unlike in requestDirectChat.
+ * It is advised to use requestDirectChat as a default way of getting
+ * one-on-one with a person, and only use createDirectChat when
+ * a new creation is explicitly desired.
+ */
+ CreateRoomJob* createDirectChat(const QString& userId,
+ const QString& topic = {},
+ const QString& name = {});
+
+ virtual JoinRoomJob* joinRoom(const QString& roomAlias,
+ const QStringList& serverNames = {});
+
+ /** Sends /forget to the server and also deletes room locally.
+ * This method is in Connection, not in Room, since it's a
+ * room lifecycle operation, and Connection is an acting room manager.
+ * It ensures that the local user is not a member of a room (running /leave,
+ * if necessary) then issues a /forget request and if that one doesn't fail
+ * deletion of the local Room object is ensured.
+ * \param id - the room id to forget
+ * \return - the ongoing /forget request to the server; note that the
+ * success() signal of this request is connected to deleteLater()
+ * of a respective room so by the moment this finishes, there might be no
+ * Room object anymore.
+ */
+ ForgetRoomJob* forgetRoom(const QString& id);
+
+ SendToDeviceJob* sendToDevices(const QString& eventType,
+ const UsersToDevicesToEvents& eventsMap) const;
+
+ /** \deprecated This method is experimental and may be removed any time */
+ SendMessageJob* sendMessage(const QString& roomId,
+ const RoomEvent& event) const;
+
+ /** \deprecated Do not use this directly, use Room::leaveRoom() instead */
+ virtual LeaveRoomJob* leaveRoom(Room* room);
+
+ // Old API that will be abolished any time soon. DO NOT USE.
+
+ /** @deprecated Use callApi<PostReceiptJob>() or Room::postReceipt() instead
+ */
+ virtual PostReceiptJob* postReceipt(Room* room, RoomEvent* event) const;
+signals:
+ /**
+ * @deprecated
+ * This was a signal resulting from a successful resolveServer().
+ * Since Connection now provides setHomeserver(), the HS URL
+ * may change even without resolveServer() invocation. Use
+ * homeserverChanged() instead of resolved(). You can also use
+ * connectToServer and connectWithToken without the HS URL set in
+ * advance (i.e. without calling resolveServer), as they now trigger
+ * server name resolution from MXID if the server URL is not valid.
+ */
+ void resolved();
+ void resolveError(QString error);
+
+ void homeserverChanged(QUrl baseUrl);
+ void capabilitiesLoaded();
+
+ void connected();
+ void reconnected(); //< \deprecated Use connected() instead
+ void loggedOut();
+ /** Login data or state have changed
+ *
+ * This is a common change signal for userId, deviceId and
+ * accessToken - these properties normally only change at
+ * a successful login and logout and are constant at other times.
+ */
+ void stateChanged();
+ void loginError(QString message, QString details);
+
+ /** A network request (job) failed
+ *
+ * @param request - the pointer to the failed job
+ */
+ void requestFailed(BaseJob* request);
+
+ /** A network request (job) failed due to network problems
+ *
+ * This is _only_ emitted when the job will retry on its own;
+ * once it gives up, requestFailed() will be emitted.
+ *
+ * @param message - message about the network problem
+ * @param details - raw error details, if any available
+ * @param retriesTaken - how many retries have already been taken
+ * @param nextRetryInMilliseconds - when the job will retry again
+ */
+ void networkError(QString message, QString details, int retriesTaken,
+ int nextRetryInMilliseconds);
+
+ void syncDone();
+ void syncError(QString message, QString details);
+
+ void newUser(User* user);
+
+ /**
+ * \group Signals emitted on room transitions
+ *
+ * Note: Rooms in Invite state are always stored separately from
+ * rooms in Join/Leave state, because of special treatment of
+ * invite_state in Matrix CS API (see The Spec on /sync for details).
+ * Therefore, objects below are: r - room in Join/Leave state;
+ * i - room in Invite state
+ *
+ * 1. none -> Invite: newRoom(r), invitedRoom(r,nullptr)
+ * 2. none -> Join: newRoom(r), joinedRoom(r,nullptr)
+ * 3. none -> Leave: newRoom(r), leftRoom(r,nullptr)
+ * 4. Invite -> Join:
+ * newRoom(r), joinedRoom(r,i), aboutToDeleteRoom(i)
+ * 4a. Leave and Invite -> Join:
+ * joinedRoom(r,i), aboutToDeleteRoom(i)
+ * 5. Invite -> Leave:
+ * newRoom(r), leftRoom(r,i), aboutToDeleteRoom(i)
+ * 5a. Leave and Invite -> Leave:
+ * leftRoom(r,i), aboutToDeleteRoom(i)
+ * 6. Join -> Leave: leftRoom(r)
+ * 7. Leave -> Invite: newRoom(i), invitedRoom(i,r)
+ * 8. Leave -> Join: joinedRoom(r)
+ * The following transitions are only possible via forgetRoom()
+ * so far; if a room gets forgotten externally, sync won't tell
+ * about it:
+ * 9. any -> none: as any -> Leave, then aboutToDeleteRoom(r)
+ */
+
+ /** A new room object has been created */
+ void newRoom(Room* room);
+
+ /** A room invitation is seen for the first time
+ *
+ * If the same room is in Left state, it's passed in prev. Beware
+ * that initial sync will trigger this signal for all rooms in
+ * Invite state.
+ */
+ void invitedRoom(Room* room, Room* prev);
+
+ /** A joined room is seen for the first time
+ *
+ * It's not the same as receiving a room in "join" section of sync
+ * response (rooms will be there even after joining); it's also
+ * not (exactly) the same as actual joining action of a user (all
+ * rooms coming in initial sync will trigger this signal too). If
+ * this room was in Invite state before, the respective object is
+ * passed in prev (and it will be deleted shortly afterwards).
+ */
+ void joinedRoom(Room* room, Room* prev);
+
+ /** A room has just been left
+ *
+ * If this room has been in Invite state (as in case of rejecting
+ * an invitation), the respective object will be passed in prev
+ * (and will be deleted shortly afterwards). Note that, similar
+ * to invitedRoom and joinedRoom, this signal is triggered for all
+ * Left rooms upon initial sync (not only those that were left
+ * right before the sync).
+ */
+ void leftRoom(Room* room, Room* prev);
+
+ /** The room object is about to be deleted */
+ void aboutToDeleteRoom(Room* room);
+
+ /** The room has just been created by createRoom or requestDirectChat
+ *
+ * This signal is not emitted in usual room state transitions,
+ * only as an outcome of room creation operations invoked by
+ * the client.
+ * \note requestDirectChat doesn't necessarily create a new chat;
+ * use directChatAvailable signal if you just need to obtain
+ * a direct chat room.
+ */
+ void createdRoom(Room* room);
+
+ /** The first sync for the room has been completed
+ *
+ * This signal is emitted after the room has been synced the first
+ * time. This is the right signal to connect to if you need to
+ * access the room state (name, aliases, members); state transition
+ * signals (newRoom, joinedRoom etc.) come earlier, when the room
+ * has just been created.
+ */
+ void loadedRoomState(Room* room);
+
+ /** Account data (except direct chats) have changed */
+ void accountDataChanged(QString type);
+
+ /** The direct chat room is ready for using
+ * This signal is emitted upon any successful outcome from
+ * requestDirectChat.
+ */
+ void directChatAvailable(Room* directChat);
+
+ /** The list of direct chats has changed
+ * This signal is emitted every time when the mapping of users
+ * to direct chat rooms is changed (because of either local updates
+ * or a different list arrived from the server).
+ */
+ void directChatsListChanged(DirectChatsMap additions,
+ DirectChatsMap removals);
+
+ void ignoredUsersListChanged(IgnoredUsersList additions,
+ IgnoredUsersList removals);
+
+ void cacheStateChanged();
+ void lazyLoadingChanged();
+ void turnServersChanged(const QJsonObject& servers);
+
+protected:
+ /**
+ * @brief Access the underlying ConnectionData class
+ */
+ const ConnectionData* connectionData() const;
+
+ /** Get a Room object for the given id in the given state
+ *
+ * Use this method when you need a Room object in the local list
+ * of rooms, with the given state. Note that this does not interact
+ * with the server; in particular, does not automatically create
+ * rooms on the server. This call performs necessary join state
+ * transitions; e.g., if it finds a room in Invite but
+ * `joinState == JoinState::Join` then the Invite room object
+ * will be deleted and a new room object with Join state created.
+ * In contrast, switching between Join and Leave happens within
+ * the same object.
+ * \param roomId room id (not alias!)
+ * \param joinState desired (target) join state of the room; if
+ * omitted, any state will be found and return unchanged, or a
+ * new Join room created.
+ * @return a pointer to a Room object with the specified id and the
+ * specified state; nullptr if roomId is empty or if roomFactory()
+ * failed to create a Room object.
+ */
+ Room* provideRoom(const QString& roomId,
+ Omittable<JoinState> joinState = none);
+
+ /**
+ * Completes loading sync data.
+ */
+ void onSyncSuccess(SyncData&& data, bool fromCache = false);
+
+protected slots:
+ void syncLoopIteration();
+
+private:
+ class Private;
+ QScopedPointer<Private> d;
+
+ /**
+ * A single entry for functions that need to check whether the
+ * homeserver is valid before running. May either execute connectFn
+ * synchronously or asynchronously (if tryResolve is true and
+ * a DNS lookup is initiated); in case of errors, emits resolveError
+ * if the homeserver URL is not valid and cannot be resolved from
+ * userId.
+ *
+ * @param userId - fully-qualified MXID to resolve HS from
+ * @param connectFn - a function to execute once the HS URL is good
+ */
+ void checkAndConnect(const QString& userId, std::function<void()> connectFn);
+ void doConnectToServer(const QString& user, const QString& password,
+ const QString& initialDeviceName,
+ const QString& deviceId = {});
+
+ static room_factory_t _roomFactory;
+ static user_factory_t _userFactory;
+};
+} // namespace Quotient
+Q_DECLARE_METATYPE(Quotient::Connection*)