aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--connection.cpp69
-rw-r--r--connection.h18
-rw-r--r--jobs/syncjob.cpp5
-rw-r--r--jobs/syncjob.h1
-rw-r--r--room.cpp23
-rw-r--r--room.h5
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;
diff --git a/room.cpp b/room.cpp
index 547b74c4..78e5b80d 100644
--- a/room.cpp
+++ b/room.cpp
@@ -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
diff --git a/room.h b/room.h
index 23a1412d..9465a960 100644
--- a/room.h
+++ b/room.h
@@ -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 */