aboutsummaryrefslogtreecommitdiff
path: root/connection.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'connection.cpp')
-rw-r--r--connection.cpp103
1 files changed, 76 insertions, 27 deletions
diff --git a/connection.cpp b/connection.cpp
index efc40fe9..e20f843f 100644
--- a/connection.cpp
+++ b/connection.cpp
@@ -56,7 +56,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;
@@ -183,7 +187,7 @@ void Connection::onSyncSuccess(SyncData &&data) {
d->data->setLastEvent(data.nextBatch());
for( auto&& roomData: data.takeRoomData() )
{
- if ( auto* r = provideRoom(roomData.roomId) )
+ if ( auto* r = provideRoom(roomData.roomId, roomData.joinState) )
r->updateData(std::move(roomData));
}
@@ -210,20 +214,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
@@ -288,9 +284,18 @@ int Connection::millisToReconnect() const
return d->syncJob ? d->syncJob->millisToRetry() : 0;
}
-QHash< QString, Room* > Connection::roomMap() const
+QHash< QPair<QString, bool>, Room* > Connection::roomMap() const
{
- return d->roomMap;
+ // Copy-on-write-and-remove-elements is faster than copying elements one by one.
+ QHash< QPair<QString, bool>, Room* > roomMap = d->roomMap;
+ for (auto it = roomMap.begin(); it != roomMap.end(); )
+ {
+ if (it.value()->joinState() == JoinState::Leave)
+ it = roomMap.erase(it);
+ else
+ ++it;
+ }
+ return roomMap;
}
const ConnectionData* Connection::connectionData() const
@@ -298,36 +303,80 @@ 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);
+ // Room transitions:
+ // 1. none -> Invite: r=createRoom, emit invitedRoom(r,null)
+ // 2. none -> Join: r=createRoom, emit joinedRoom(r,null)
+ // 3. none -> Leave: r=createRoom, emit leftRoom(r,null)
+ // 4. inv=Invite -> Join: r=createRoom, emit joinedRoom(r,inv), delete Invite
+ // 4a. Leave, inv=Invite -> Join: change state, emit joinedRoom(r,inv), delete Invite
+ // 5. inv=Invite -> Leave: r=createRoom, emit leftRoom(r,inv), delete Invite
+ // 5a. r=Leave, inv=Invite -> Leave: emit leftRoom(r,inv), delete Invite
+ // 6. Join -> Leave: change state
+ // 7. r=Leave -> Invite: inv=createRoom, emit invitedRoom(inv,r)
+ // 8. Leave -> (changes to) Join
+ const auto roomKey = qMakePair(id, joinState == JoinState::Invite);
+ auto* room = d->roomMap.value(roomKey, nullptr);
if (room)
{
- d->roomMap.insert( id, room );
+ // Leave is a special case because in transition (5a) above
+ // joinState == room->joinState but we still have to preempt the Invite
+ // and emit a signal. For Invite and Join, there's no such problem.
+ if (room->joinState() == joinState && joinState != JoinState::Leave)
+ return room;
+ }
+ else
+ {
+ room = createRoom(this, id, joinState);
+ if (!room)
+ {
+ qCCritical(MAIN) << "Failed to create a room" << id;
+ return nullptr;
+ }
+ d->roomMap.insert(roomKey, room);
+ qCDebug(MAIN) << "Created Room" << id << ", invited:" << roomKey.second;
emit newRoom(room);
- } else {
- qCritical() << "Failed to create a room!!!" << id;
+ }
+ if (joinState == JoinState::Invite)
+ {
+ // prev is either Leave or nullptr
+ auto* prev = d->roomMap.value({id, false}, nullptr);
+ emit invitedRoom(room, prev);
+ }
+ else
+ {
+ room->setJoinState(joinState);
+ // Preempt the Invite room (if any) with a room in Join/Leave state.
+ auto* prevInvite = d->roomMap.take({id, true});
+ if (joinState == JoinState::Join)
+ emit joinedRoom(room, prevInvite);
+ else if (joinState == JoinState::Leave)
+ emit leftRoom(room, prevInvite);
+ if (prevInvite)
+ {
+ qCDebug(MAIN) << "Deleting Invite state for room" << prevInvite->id();
+ emit aboutToDeleteRoom(prevInvite);
+ delete prevInvite;
+ }
}
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();