aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKitsune Ral <Kitsune-Ral@users.sf.net>2017-09-09 20:17:42 +0900
committerKitsune Ral <Kitsune-Ral@users.sf.net>2017-09-09 20:17:42 +0900
commit442470d4fcc565d54086caecdb07e9a046b26333 (patch)
tree395419ea42c3f06351fb81ab94f124afef0ef3fd
parent1c86ff91b5db9bd6757e8149df462d4247fef90c (diff)
downloadlibquotient-442470d4fcc565d54086caecdb07e9a046b26333.tar.gz
libquotient-442470d4fcc565d54086caecdb07e9a046b26333.zip
Kicking, inviting, exposing rooms in Invite state
Kicking and inviting use generated job classes. Rooms in Invite state are stored separately in the hash from those in Join/Leave state because The Spec says so. For clients, this means that the same room may appear twice in the rooms map if it's been left and then the user was again invited to it. The code in Quaternion that properly processes this will arrive shortly.
-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 */