aboutsummaryrefslogtreecommitdiff
path: root/lib/room.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'lib/room.cpp')
-rw-r--r--lib/room.cpp118
1 files changed, 111 insertions, 7 deletions
diff --git a/lib/room.cpp b/lib/room.cpp
index d806183f..f6956d82 100644
--- a/lib/room.cpp
+++ b/lib/room.cpp
@@ -29,7 +29,10 @@
#include "csapi/room_send.h"
#include "csapi/rooms.h"
#include "csapi/tags.h"
+#include "csapi/room_upgrades.h"
#include "events/simplestateevents.h"
+#include "events/roomcreateevent.h"
+#include "events/roomtombstoneevent.h"
#include "events/roomavatarevent.h"
#include "events/roommemberevent.h"
#include "events/typingevent.h"
@@ -250,11 +253,17 @@ class Room::Private
const QString& txnId, BaseJob* call = nullptr);
template <typename EvT>
- auto requestSetState(const QString& stateKey, const EvT& event)
+ SetRoomStateWithKeyJob* requestSetState(const QString& stateKey,
+ const EvT& event)
{
- // TODO: Queue up state events sending (see #133).
- return connection->callApi<SetRoomStateWithKeyJob>(
+ if (q->successorId().isEmpty())
+ {
+ // TODO: Queue up state events sending (see #133).
+ return connection->callApi<SetRoomStateWithKeyJob>(
id, EvT::matrixTypeId(), stateKey, event.contentJson());
+ }
+ qCWarning(MAIN) << q << "has been upgraded, state won't be set";
+ return nullptr;
}
template <typename EvT>
@@ -296,6 +305,12 @@ Room::Room(Connection* connection, QString id, JoinState initialJoinState)
// https://marcmutz.wordpress.com/translated-articles/pimp-my-pimpl-%E2%80%94-reloaded/
d->q = this;
d->displayname = d->calculateDisplayname(); // Set initial "Empty room" name
+ connectUntil(connection, &Connection::loadedRoomState, this,
+ [this] (Room* r) {
+ if (this == r)
+ emit baseStateLoaded();
+ return this == r; // loadedRoomState fires only once per room
+ });
qCDebug(MAIN) << "New" << toCString(initialJoinState) << "Room:" << id;
}
@@ -309,6 +324,28 @@ const QString& Room::id() const
return d->id;
}
+QString Room::version() const
+{
+ const auto v = d->getCurrentState<RoomCreateEvent>()->version();
+ return v.isEmpty() ? "1" : v;
+}
+
+bool Room::isUnstable() const
+{
+ return !connection()->loadingCapabilities() &&
+ !connection()->stableRoomVersions().contains(version());
+}
+
+QString Room::predecessorId() const
+{
+ return d->getCurrentState<RoomCreateEvent>()->predecessor().roomId;
+}
+
+QString Room::successorId() const
+{
+ return d->getCurrentState<RoomTombstoneEvent>()->successorRoomId();
+}
+
const Room::Timeline& Room::messageEvents() const
{
return d->timeline;
@@ -543,6 +580,26 @@ void Room::markAllMessagesAsRead()
d->markMessagesAsRead(d->timeline.crbegin());
}
+bool Room::canSwitchVersions() const
+{
+ // TODO, #276: m.room.power_levels
+ const auto* plEvt =
+ d->currentState.value({"m.room.power_levels", ""});
+ if (!plEvt)
+ return true;
+
+ const auto plJson = plEvt->contentJson();
+ const auto currentUserLevel =
+ plJson.value("users"_ls).toObject()
+ .value(localUser()->id()).toInt(
+ plJson.value("users_default"_ls).toInt());
+ const auto tombstonePowerLevel =
+ plJson.value("events").toObject()
+ .value("m.room.tombstone"_ls).toInt(
+ plJson.value("state_default"_ls).toInt());
+ return currentUserLevel >= tombstonePowerLevel;
+}
+
bool Room::hasUnreadMessages() const
{
return unreadCount() >= 0;
@@ -762,6 +819,14 @@ void Room::resetHighlightCount()
emit highlightCountChanged(this);
}
+void Room::switchVersion(QString newVersion)
+{
+ auto* job = connection()->callApi<UpgradeRoomJob>(id(), newVersion);
+ connect(job, &BaseJob::failure, this, [this,job] {
+ emit upgradeFailed(job->errorString());
+ });
+}
+
bool Room::hasAccountData(const QString& type) const
{
return d->accountData.find(type) != d->accountData.end();
@@ -1300,6 +1365,8 @@ RoomEvent* Room::Private::addAsPending(RoomEventPtr&& event)
event->setTransactionId(connection->generateTxnId());
auto* pEvent = rawPtr(event);
emit q->pendingEventAboutToAdd(pEvent);
+ // FIXME: This sometimes causes a bad read:
+ // https://travis-ci.org/QMatrixClient/libqmatrixclient/jobs/492156899#L2596
unsyncedEvents.emplace_back(move(event));
emit q->pendingEventAdded();
return pEvent;
@@ -1307,7 +1374,11 @@ RoomEvent* Room::Private::addAsPending(RoomEventPtr&& event)
QString Room::Private::sendEvent(RoomEventPtr&& event)
{
- return doSendEvent(addAsPending(std::move(event)));
+ if (q->successorId().isEmpty())
+ return doSendEvent(addAsPending(std::move(event)));
+
+ qCWarning(MAIN) << q << "has been upgraded, event won't be sent";
+ return {};
}
QString Room::Private::doSendEvent(const RoomEvent* pEvent)
@@ -1569,7 +1640,24 @@ bool isEchoEvent(const RoomEventPtr& le, const PendingEventItem& re)
bool Room::supportsCalls() const
{
- return joinedCount() == 2;
+ return joinedCount() == 2;
+}
+
+void Room::checkVersion()
+{
+ const auto defaultVersion = connection()->defaultRoomVersion();
+ const auto stableVersions = connection()->stableRoomVersions();
+ Q_ASSERT(!defaultVersion.isEmpty() && successorId().isEmpty());
+ // This method is only called after the base state has been loaded
+ // or the server capabilities have been loaded.
+ emit stabilityUpdated(defaultVersion, stableVersions);
+ if (!stableVersions.contains(version()))
+ {
+ qCDebug(MAIN) << this << "version is" << version()
+ << "which the server doesn't count as stable";
+ if (canSwitchVersions())
+ qCDebug(MAIN) << "The current user has enough privileges to fix it";
+ }
}
void Room::inviteCall(const QString& callId, const int lifetime,
@@ -1799,7 +1887,7 @@ RoomEventPtr makeRedacted(const RoomEvent& target,
std::vector<std::pair<Event::Type, QStringList>> keepContentKeysMap
{ { RoomMemberEvent::typeId(), { QStringLiteral("membership") } }
-// , { RoomCreateEvent::typeId(), { QStringLiteral("creator") } }
+ , { RoomCreateEvent::typeId(), { QStringLiteral("creator") } }
// , { RoomJoinRules::typeId(), { QStringLiteral("join_rule") } }
// , { RoomPowerLevels::typeId(),
// { QStringLiteral("ban"), QStringLiteral("events"),
@@ -2123,7 +2211,23 @@ Room::Changes Room::processStateEvent(const RoomEvent& e)
}
, [this] (const EncryptionEvent&) {
emit encryption(); // It can only be done once, so emit it here.
- return EncryptionOn;
+ return OtherChange;
+ }
+ , [this] (const RoomTombstoneEvent& evt) {
+ const auto successorId = evt.successorRoomId();
+ if (auto* successor = connection()->room(successorId))
+ emit upgraded(evt.serverMessage(), successor);
+ else
+ connectUntil(connection(), &Connection::loadedRoomState, this,
+ [this,successorId,serverMsg=evt.serverMessage()]
+ (Room* newRoom) {
+ if (newRoom->id() != successorId)
+ return false;
+ emit upgraded(serverMsg, newRoom);
+ return true;
+ });
+
+ return OtherChange;
}
);
}