diff options
-rw-r--r-- | connection.cpp | 69 | ||||
-rw-r--r-- | connection.h | 18 | ||||
-rw-r--r-- | jobs/syncjob.cpp | 5 | ||||
-rw-r--r-- | jobs/syncjob.h | 1 | ||||
-rw-r--r-- | room.cpp | 23 | ||||
-rw-r--r-- | room.h | 5 |
6 files changed, 80 insertions, 41 deletions
diff --git a/connection.cpp b/connection.cpp index 2c9ee88a..5d8a42e3 100644 --- a/connection.cpp +++ b/connection.cpp @@ -50,7 +50,11 @@ class Connection::Private Connection* q; ConnectionData* data; - QHash<QString, Room*> roomMap; + // A complex key below is a pair of room name and whether its + // state is Invited. The spec mandates to keep Invited room state + // separately so we should, e.g., keep objects for Invite and + // Leave state of the same room. + QHash<QPair<QString, bool>, Room*> roomMap; QHash<QString, User*> userMap; QString username; QString password; @@ -160,7 +164,7 @@ void Connection::sync(int timeout) d->data->setLastEvent(job->nextBatch()); for( auto&& roomData: job->takeRoomData() ) { - if ( auto* r = provideRoom(roomData.roomId) ) + if ( auto* r = provideRoom(roomData.roomId, roomData.joinState) ) r->updateData(std::move(roomData)); } d->syncJob = nullptr; @@ -197,20 +201,12 @@ PostReceiptJob* Connection::postReceipt(Room* room, RoomEvent* event) const JoinRoomJob* Connection::joinRoom(const QString& roomAlias) { - auto job = callApi<JoinRoomJob>(roomAlias); - connect( job, &BaseJob::success, [=] () { - if ( Room* r = provideRoom(job->roomId()) ) - emit joinedRoom(r); - }); - return job; + return callApi<JoinRoomJob>(roomAlias); } void Connection::leaveRoom(Room* room) { - auto job = callApi<LeaveRoomJob>(room->id()); - connect( job, &BaseJob::success, [=] () { - emit leftRoom(room); - }); + callApi<LeaveRoomJob>(room->id()); } RoomMessagesJob* Connection::getMessages(Room* room, const QString& from) const @@ -275,7 +271,7 @@ int Connection::millisToReconnect() const return d->syncJob ? d->syncJob->millisToRetry() : 0; } -QHash< QString, Room* > Connection::roomMap() const +const QHash< QPair<QString, bool>, Room* >& Connection::roomMap() const { return d->roomMap; } @@ -285,36 +281,55 @@ const ConnectionData* Connection::connectionData() const return d->data; } -Room* Connection::provideRoom(const QString& id) +Room* Connection::provideRoom(const QString& id, JoinState joinState) { + // TODO: This whole function is a strong case for a RoomManager class. if (id.isEmpty()) { qCDebug(MAIN) << "Connection::provideRoom() with empty id, doing nothing"; return nullptr; } - if (d->roomMap.contains(id)) - return d->roomMap.value(id); - - // Not yet in the map, create a new one. - auto* room = createRoom(this, id); - if (room) + const auto roomKey = qMakePair(id, joinState == JoinState::Invite); + auto* room = d->roomMap.value(roomKey, nullptr); + if (!room) { - d->roomMap.insert( id, room ); + room = createRoom(this, id, joinState); + if (!room) + { + qCritical() << "Failed to create a room!!!" << id; + return nullptr; + } + qCDebug(MAIN) << "Created Room" << id << ", invited:" << roomKey.second; + + d->roomMap.insert(roomKey, room); emit newRoom(room); - } else { - qCritical() << "Failed to create a room!!!" << id; + } + else if (room->joinState() != joinState) + { + room->setJoinState(joinState); + if (joinState == JoinState::Leave) + emit leftRoom(room); + else if (joinState == JoinState::Join) + emit joinedRoom(room); + } + + if (joinState != JoinState::Invite && d->roomMap.contains({id, true})) + { + // Preempt the Invite room after it's been acted upon (joined or left). + qCDebug(MAIN) << "Deleting invited state"; + delete d->roomMap.take({id, true}); } return room; } -std::function<Room*(Connection*, const QString&)> Connection::createRoom = - [](Connection* c, const QString& id) { return new Room(c, id); }; +Connection::room_factory_t Connection::createRoom = + [](Connection* c, const QString& id, JoinState joinState) + { return new Room(c, id, joinState); }; -std::function<User*(Connection*, const QString&)> Connection::createUser = +Connection::user_factory_t Connection::createUser = [](Connection* c, const QString& id) { return new User(id, c); }; - QByteArray Connection::generateTxnId() { return d->data->generateTxnId(); diff --git a/connection.h b/connection.h index 4b0413e3..b118ffb0 100644 --- a/connection.h +++ b/connection.h @@ -18,6 +18,8 @@ #pragma once +#include "joinstate.h" + #include <QtCore/QObject> #include <QtCore/QUrl> #include <QtCore/QSize> @@ -41,11 +43,16 @@ namespace QMatrixClient class Connection: public QObject { Q_OBJECT public: + using room_factory_t = + std::function<Room*(Connection*, const QString&, JoinState joinState)>; + using user_factory_t = + std::function<User*(Connection*, const QString&)>; + explicit Connection(const QUrl& server, QObject* parent = nullptr); Connection(); virtual ~Connection(); - QHash<QString, Room*> roomMap() const; + const QHash<QPair<QString, bool>, Room*>& roomMap() const; Q_INVOKABLE virtual void resolveServer(const QString& domain); Q_INVOKABLE virtual void connectToServer(const QString& user, @@ -102,7 +109,8 @@ namespace QMatrixClient static void setRoomType() { createRoom = - [](Connection* c, const QString& id) { return new T(c, id); }; + [](Connection* c, const QString& id, JoinState joinState) + { return new T(c, id, joinState); }; } template <typename T = User> @@ -144,13 +152,13 @@ namespace QMatrixClient * @return a pointer to a Room object with the specified id; nullptr * if roomId is empty if createRoom() failed to create a Room object. */ - Room* provideRoom(const QString& roomId); + Room* provideRoom(const QString& roomId, JoinState joinState); private: class Private; Private* d; - static std::function<Room*(Connection*, const QString&)> createRoom; - static std::function<User*(Connection*, const QString&)> createUser; + static room_factory_t createRoom; + static user_factory_t createUser; }; } // namespace QMatrixClient diff --git a/jobs/syncjob.cpp b/jobs/syncjob.cpp index 29ddc2e6..38cfcb2a 100644 --- a/jobs/syncjob.cpp +++ b/jobs/syncjob.cpp @@ -99,15 +99,14 @@ SyncRoomData::SyncRoomData(const QString& roomId_, JoinState joinState_, const QJsonObject& room_) : roomId(roomId_) , joinState(joinState_) - , state("state") + , state(joinState == JoinState::Invite ? "invite_state" : "state") , timeline("timeline") , ephemeral("ephemeral") , accountData("account_data") - , inviteState("invite_state") { switch (joinState) { case JoinState::Invite: - inviteState.fromJson(room_); + state.fromJson(room_); break; case JoinState::Join: state.fromJson(room_); diff --git a/jobs/syncjob.h b/jobs/syncjob.h index 07824e23..57a87c9f 100644 --- a/jobs/syncjob.h +++ b/jobs/syncjob.h @@ -51,7 +51,6 @@ namespace QMatrixClient Batch<RoomEvent> timeline; Batch<Event> ephemeral; Batch<Event> accountData; - Batch<Event> inviteState; bool timelineLimited; QString timelinePrevBatch; @@ -18,6 +18,9 @@ #include "room.h" +#include "jobs/generated/kicking.h" +#include "jobs/generated/inviting.h" + #include <array> #include <QtCore/QHash> @@ -48,9 +51,9 @@ class Room::Private typedef QMultiHash<QString, User*> members_map_t; typedef std::pair<rev_iter_t, rev_iter_t> rev_iter_pair_t; - Private(Connection* c, QString id_) + Private(Connection* c, QString id_, JoinState initialJoinState) : q(nullptr), connection(c), id(std::move(id_)) - , joinState(JoinState::Join), unreadMessages(false) + , joinState(initialJoinState), unreadMessages(false) , highlightCount(0), notificationCount(0), roomMessagesJob(nullptr) { } @@ -134,8 +137,8 @@ class Room::Private } }; -Room::Room(Connection* connection, QString id) - : QObject(connection), d(new Private(connection, id)) +Room::Room(Connection* connection, QString id, JoinState initialJoinState) + : QObject(connection), d(new Private(connection, id, initialJoinState)) { // See "Accessing the Public Class" section in // https://marcmutz.wordpress.com/translated-articles/pimp-my-pimpl-%E2%80%94-reloaded/ @@ -194,6 +197,8 @@ void Room::setJoinState(JoinState state) if( state == oldState ) return; d->joinState = state; + qCDebug(MAIN) << "Room" << id() << "changed state: " + << int(oldState) << "->" << int(state); emit joinStateChanged(oldState, state); } @@ -601,11 +606,21 @@ void Room::Private::getPreviousContent(int limit) } } +void Room::inviteToRoom(const QString& memberId) const +{ + connection()->callApi<InviteUserJob>(id(), memberId); +} + void Room::leaveRoom() const { connection()->callApi<LeaveRoomJob>(id()); } +void Room::kickMember(const QString& memberId, const QString& reason) const +{ + connection()->callApi<KickJob>(id(), memberId, reason); +} + void Room::Private::dropDuplicateEvents(RoomEvents* events) const { // Collect all duplicate events at the end of the container @@ -77,7 +77,7 @@ namespace QMatrixClient using Timeline = std::deque<TimelineItem>; using rev_iter_t = Timeline::const_reverse_iterator; - Room(Connection* connection, QString id); + Room(Connection* connection, QString id, JoinState initialJoinState); virtual ~Room(); Connection* connection() const; @@ -154,7 +154,10 @@ namespace QMatrixClient void getPreviousContent(int limit = 10); + void inviteToRoom(const QString& memberId) const; void leaveRoom() const; + void kickMember(const QString& memberId, const QString& reason) const; + void userRenamed(User* user, QString oldName); /** Mark all messages in the room as read */ |