From 7ea51059b86d8124c077ed84ad3b91770af8d610 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Sun, 2 Jun 2019 13:22:11 +0900 Subject: lib/room.*: Spelling fixes in comments [ci skip] --- lib/room.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'lib/room.h') diff --git a/lib/room.h b/lib/room.h index 055da3da..d4a1b959 100644 --- a/lib/room.h +++ b/lib/room.h @@ -322,7 +322,7 @@ namespace QMatrixClient bool hasAccountData(const QString& type) const; /** Get a generic account data event of the given type - * This returns a generic hashmap for any room account data event + * This returns a generic hash map for any room account data event * stored on the server. Tags and read markers cannot be retrieved * using this method _yet_. */ @@ -516,7 +516,7 @@ namespace QMatrixClient /** A common signal for various kinds of changes in the room * Aside from all changes in the room state - * @param changes a set of flags describing what changes occured + * @param changes a set of flags describing what changes occurred * upon the last sync * \sa StateChange */ @@ -524,7 +524,7 @@ namespace QMatrixClient /** * \brief The room name, the canonical alias or other aliases changed * - * Not triggered when displayname changes. + * Not triggered when display name changes. */ void namesChanged(Room* room); void displaynameAboutToChange(Room* room); @@ -581,7 +581,7 @@ namespace QMatrixClient /// The room's version stability may have changed void stabilityUpdated(QString recommendedDefault, QStringList stableVersions); - /// This room has been upgraded and won't receive updates anymore + /// This room has been upgraded and won't receive updates any more void upgraded(QString serverMessage, Room* successor); /// An attempted room upgrade has failed void upgradeFailed(QString errorMessage); -- cgit v1.2.3 From 6a6857b9d4dbf22402f2871494bdd06cdccdf366 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Wed, 3 Jul 2019 23:28:09 +0900 Subject: Room/Connection: make room aliases work properly Closes #301. --- lib/connection.cpp | 49 ++++++++++++++++++++++++++++++++----------------- lib/connection.h | 10 ++++++---- lib/room.cpp | 39 +++++++++++++++++++++++++++++++++++---- lib/room.h | 14 ++++++++++---- 4 files changed, 83 insertions(+), 29 deletions(-) (limited to 'lib/room.h') diff --git a/lib/connection.cpp b/lib/connection.cpp index 4c068b8f..783e12c0 100644 --- a/lib/connection.cpp +++ b/lib/connection.cpp @@ -82,8 +82,9 @@ class Connection::Private // separately; specifically, we should keep objects for Invite and // Leave state of the same room if the two happen to co-exist. QHash, Room*> roomMap; - // Mapping from aliases to room ids, as per the last sync - QHash roomAliasMap; + /// Mapping from serverparts to alias/room id mappings, + /// as of the last sync + QHash> roomAliasMap; QVector roomIdsToForget; QVector firstTimeRooms; QVector pendingStateRoomIds; @@ -158,20 +159,31 @@ Connection::~Connection() stopSync(); } -void Connection::resolveServer(const QString& mxidOrDomain) +static const auto ServerPartRegEx = QStringLiteral( + "(\\[[^]]+\\]|[^:@]+)" // Either IPv6 address or hostname/IPv4 address + "(?::(\\d{1,5}))?" // Optional port +); + +QString serverPart(const QString& mxId) { - // At this point we may have something as complex as - // @username:[IPv6:address]:port, or as simple as a plain domain name. + static auto re = "^[@!#$+].+?:(" // Localpart and colon + % ServerPartRegEx % ")$"; + static QRegularExpression parser(re, + QRegularExpression::UseUnicodePropertiesOption); // Because Asian digits + return parser.match(mxId).captured(1); +} - // Try to parse as an FQID; if there's no @ part, assume it's a domain name. - QRegularExpression parser( +void Connection::resolveServer(const QString& mxidOrDomain) +{ + // mxIdOrDomain may be something as complex as + // @username:[IPv6:address]:port, or as simple as a plain serverpart. + static QRegularExpression parser( "^(@.+?:)?" // Optional username (allow everything for compatibility) - "(\\[[^]]+\\]|[^:@]+)" // Either IPv6 address or hostname/IPv4 address - "(:\\d{1,5})?$", // Optional port - QRegularExpression::UseUnicodePropertiesOption); // Because asian digits + % ServerPartRegEx % '$', + QRegularExpression::UseUnicodePropertiesOption); // Because Asian digits auto match = parser.match(mxidOrDomain); - QUrl maybeBaseUrl = QUrl::fromUserInput(match.captured(2)); + auto maybeBaseUrl = QUrl::fromUserInput(match.captured(2)); maybeBaseUrl.setScheme("https"); // Instead of the Qt-default "http" if (!match.hasMatch() || !maybeBaseUrl.isValid()) { @@ -883,33 +895,36 @@ Room* Connection::room(const QString& roomId, JoinStates states) const Room* Connection::roomByAlias(const QString& roomAlias, JoinStates states) const { - const auto id = d->roomAliasMap.value(roomAlias); + const auto id = + d->roomAliasMap.value(serverPart(roomAlias)).value(roomAlias); if (!id.isEmpty()) return room(id, states); + qCWarning(MAIN) << "Room for alias" << roomAlias << "is not found under account" << userId(); return nullptr; } void Connection::updateRoomAliases(const QString& roomId, + const QString& aliasServer, const QStringList& previousRoomAliases, const QStringList& roomAliases) { + auto& aliasMap = d->roomAliasMap[aliasServer]; // Allocate if necessary for (const auto& a: previousRoomAliases) - if (d->roomAliasMap.remove(a) == 0) + if (aliasMap.remove(a) == 0) qCWarning(MAIN) << "Alias" << a << "is not found (already deleted?)"; for (const auto& a: roomAliases) { - auto& mappedId = d->roomAliasMap[a]; + auto& mappedId = aliasMap[a]; if (!mappedId.isEmpty()) { if (mappedId == roomId) - qCDebug(MAIN) << "Alias" << a << "is already mapped to room" + qCDebug(MAIN) << "Alias" << a << "is already mapped to" << roomId; else - qCWarning(MAIN) << "Alias" << a - << "will be force-remapped from room" + qCWarning(MAIN) << "Alias" << a << "will be force-remapped from" << mappedId << "to" << roomId; } mappedId = roomId; diff --git a/lib/connection.h b/lib/connection.h index cc2feed8..f688c10b 100644 --- a/lib/connection.h +++ b/lib/connection.h @@ -250,11 +250,13 @@ namespace QMatrixClient Q_INVOKABLE Room* roomByAlias(const QString& roomAlias, JoinStates states = JoinState::Invite|JoinState::Join) const; /** Update the internal map of room aliases to IDs */ - /// This is used for internal bookkeeping of rooms. Do NOT use - /// it to try change aliases, use Room::setAliases instead + /// This is used to maintain the internal index of room aliases. + /// It does NOT change aliases on the server, + /// \sa Room::setLocalAliases void updateRoomAliases(const QString& roomId, - const QStringList& previousRoomAliases, - const QStringList& roomAliases); + const QString& aliasServer, + const QStringList& previousRoomAliases, + const QStringList& roomAliases); Q_INVOKABLE Room* invitation(const QString& roomId) const; Q_INVOKABLE User* user(const QString& userId); const User* user() const; diff --git a/lib/room.cpp b/lib/room.cpp index 9042130a..06f3490c 100644 --- a/lib/room.cpp +++ b/lib/room.cpp @@ -96,9 +96,14 @@ class Room::Private /// The state of the room at timeline position after-maxTimelineIndex() /// \sa Room::syncEdge QHash currentState; + /// Servers with aliases for this room except the one of the local user + /// \sa Room::remoteAliases + QSet aliasServers; + Timeline timeline; PendingEvents unsyncedEvents; QHash eventsIndex; + QString displayname; Avatar avatar; int highlightCount = 0; @@ -381,9 +386,18 @@ QString Room::name() const return d->getCurrentState()->name(); } -QStringList Room::aliases() const +QStringList Room::localAliases() const { - return d->getCurrentState()->aliases(); + return d->getCurrentState( + connection()->homeserver().authority())->aliases(); +} + +QStringList Room::remoteAliases() const +{ + QStringList result; + for (const auto& s: d->aliasServers) + result += d->getCurrentState(s)->aliases(); + return result; } QString Room::canonicalAlias() const @@ -1624,7 +1638,7 @@ void Room::setCanonicalAlias(const QString& newAlias) d->requestSetState(RoomCanonicalAliasEvent(newAlias)); } -void Room::setAliases(const QStringList& aliases) +void Room::setLocalAliases(const QStringList& aliases) { d->requestSetState(RoomAliasesEvent(aliases)); } @@ -2192,16 +2206,30 @@ Room::Changes Room::processStateEvent(const RoomEvent& e) if (!is(e)) // Room member events are too numerous qCDebug(EVENTS) << "Room state event:" << e; + // clang-format off return visit(e , [] (const RoomNameEvent&) { return NameChange; } , [this,oldStateEvent] (const RoomAliasesEvent& ae) { + // clang-format on + if (ae.aliases().isEmpty()) { + qDebug(MAIN).noquote() << ae.stateKey() + << "no more has aliases for room" << objectName(); + d->aliasServers.remove(ae.stateKey()); + } else { + d->aliasServers.insert(ae.stateKey()); + qDebug(MAIN).nospace().noquote() + << "New server with aliases for room " << objectName() + << ": " << ae.stateKey(); + } const auto previousAliases = oldStateEvent ? static_cast(oldStateEvent)->aliases() : QStringList(); - connection()->updateRoomAliases(id(), previousAliases, ae.aliases()); + connection()->updateRoomAliases(id(), ae.stateKey(), + previousAliases, ae.aliases()); return OtherChange; + // clang-format off } , [this] (const RoomCanonicalAliasEvent& evt) { setObjectName(evt.alias().isEmpty() ? d->id : evt.alias()); @@ -2216,6 +2244,7 @@ Room::Changes Room::processStateEvent(const RoomEvent& e) return AvatarChange; } , [this,oldStateEvent] (const RoomMemberEvent& evt) { + // clang-format on auto* u = user(evt.userId()); const auto* oldMemberEvent = static_cast(oldStateEvent); @@ -2288,6 +2317,7 @@ Room::Changes Room::processStateEvent(const RoomEvent& e) d->membersLeft.append(u); } return MembersChange; + // clang-format off } , [this] (const EncryptionEvent&) { emit encryption(); // It can only be done once, so emit it here. @@ -2310,6 +2340,7 @@ Room::Changes Room::processStateEvent(const RoomEvent& e) return OtherChange; } ); + // clang-format on } Room::Changes Room::processEphemeralEvent(EventPtr&& event) diff --git a/lib/room.h b/lib/room.h index d4a1b959..7c85e4ed 100644 --- a/lib/room.h +++ b/lib/room.h @@ -86,7 +86,8 @@ namespace QMatrixClient Q_PROPERTY(QString predecessorId READ predecessorId NOTIFY baseStateLoaded) Q_PROPERTY(QString successorId READ successorId NOTIFY upgraded) Q_PROPERTY(QString name READ name NOTIFY namesChanged) - Q_PROPERTY(QStringList aliases READ aliases NOTIFY namesChanged) + Q_PROPERTY(QStringList localAliases READ localAliases NOTIFY namesChanged) + Q_PROPERTY(QStringList remoteAliases READ remoteAliases NOTIFY namesChanged) Q_PROPERTY(QString canonicalAlias READ canonicalAlias NOTIFY namesChanged) Q_PROPERTY(QString displayName READ displayName NOTIFY displaynameChanged) Q_PROPERTY(QString topic READ topic NOTIFY topicChanged) @@ -156,7 +157,12 @@ namespace QMatrixClient QString predecessorId() const; QString successorId() const; QString name() const; - QStringList aliases() const; + /// Room aliases defined on the current user's server + /// \sa remoteAliases, setLocalAliases + QStringList localAliases() const; + /// Room aliases defined on other servers + /// \sa localAliases + QStringList remoteAliases() const; QString canonicalAlias() const; QString displayName() const; QString topic() const; @@ -436,7 +442,8 @@ namespace QMatrixClient void discardMessage(const QString& txnId); void setName(const QString& newName); void setCanonicalAlias(const QString& newAlias); - void setAliases(const QStringList& aliases); + /// Set room aliases on the user's current server + void setLocalAliases(const QStringList& aliases); void setTopic(const QString& newTopic); /// You shouldn't normally call this method; it's here for debugging @@ -590,7 +597,6 @@ namespace QMatrixClient void beforeDestruction(Room*); protected: - /// Returns true if any of room names/aliases has changed virtual Changes processStateEvent(const RoomEvent& e); virtual Changes processEphemeralEvent(EventPtr&& event); virtual Changes processAccountDataEvent(EventPtr&& event); -- cgit v1.2.3 From 025e5ab7d90ce8cf474567457301de32d5a3e34a Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Sat, 6 Jul 2019 20:13:38 +0900 Subject: Be stricter on usage of stateKey A few places in the library dealt with state events without any notion of state_key inside events, including StateEvent[Base] and relevant functions in Room. A number of workarounds have been made; e.g., Room::setMemberState() accepted userId as a separate parameter, ignoring the state key inside the RoomMemberEvent already passed to it, and Room::setLocalAliases() had a bug in the initial version where the function still tried to pass aliases in an event with an empty state key. This commit fixes this shortcoming: StateEventBase now gets stateKey as one more parameter, Room::Private::getCurrentState() respects stateKey and returns properly constructed stub events, and Room::setMemberState() gives way to a more generic Room::setState() that works uniformly with whatever state event you pass to it. --- lib/events/roommemberevent.h | 8 +++++- lib/events/simplestateevents.h | 24 ++++++++++++----- lib/events/stateevent.cpp | 6 +++++ lib/events/stateevent.h | 10 +++++-- lib/room.cpp | 60 ++++++++++++++++++++++++++++-------------- lib/room.h | 4 +++ lib/user.cpp | 2 +- 7 files changed, 84 insertions(+), 30 deletions(-) (limited to 'lib/room.h') diff --git a/lib/events/roommemberevent.h b/lib/events/roommemberevent.h index 4490fe65..39aa280c 100644 --- a/lib/events/roommemberevent.h +++ b/lib/events/roommemberevent.h @@ -56,8 +56,14 @@ namespace QMatrixClient explicit RoomMemberEvent(const QJsonObject& obj) : StateEvent(typeId(), obj) { } + [[deprecated("Use RoomMemberEvent(userId, contentArgs) instead")]] RoomMemberEvent(MemberEventContent&& c) - : StateEvent(typeId(), matrixTypeId(), c) + : StateEvent(typeId(), matrixTypeId(), QString(), c) + { } + template + RoomMemberEvent(const QString& userId, ArgTs&&... contentArgs) + : StateEvent(typeId(), matrixTypeId(), userId, + std::forward(contentArgs)...) { } /// A special constructor to create unknown RoomMemberEvents diff --git a/lib/events/simplestateevents.h b/lib/events/simplestateevents.h index 2c23d9ca..dc6a0868 100644 --- a/lib/events/simplestateevents.h +++ b/lib/events/simplestateevents.h @@ -20,8 +20,6 @@ #include "stateevent.h" -#include "converters.h" - namespace QMatrixClient { namespace EventContent @@ -63,7 +61,7 @@ namespace QMatrixClient explicit _Name() : _Name(value_type()) { } \ template \ explicit _Name(T&& value) \ - : StateEvent(typeId(), matrixTypeId(), \ + : StateEvent(typeId(), matrixTypeId(), QString(), \ QStringLiteral(#_ContentKey), \ std::forward(value)) \ { } \ @@ -78,9 +76,6 @@ namespace QMatrixClient DEFINE_SIMPLE_STATE_EVENT(RoomNameEvent, "m.room.name", QString, name) DEFINE_EVENTTYPE_ALIAS(RoomName, RoomNameEvent) - DEFINE_SIMPLE_STATE_EVENT(RoomAliasesEvent, "m.room.aliases", - QStringList, aliases) - DEFINE_EVENTTYPE_ALIAS(RoomAliases, RoomAliasesEvent) DEFINE_SIMPLE_STATE_EVENT(RoomCanonicalAliasEvent, "m.room.canonical_alias", QString, alias) DEFINE_EVENTTYPE_ALIAS(RoomCanonicalAlias, RoomCanonicalAliasEvent) @@ -89,4 +84,21 @@ namespace QMatrixClient DEFINE_SIMPLE_STATE_EVENT(EncryptionEvent, "m.room.encryption", QString, algorithm) DEFINE_EVENTTYPE_ALIAS(RoomEncryption, EncryptionEvent) + + class RoomAliasesEvent + : public StateEvent> + { + public: + DEFINE_EVENT_TYPEID("m.room.aliases", RoomAliasesEvent) + explicit RoomAliasesEvent(const QJsonObject& obj) + : StateEvent(typeId(), obj, QStringLiteral("aliases")) + { } + RoomAliasesEvent(const QString& server, const QStringList& aliases) + : StateEvent(typeId(), matrixTypeId(), server, + QStringLiteral("aliases"), aliases) + { } + QString server() const { return stateKey(); } + QStringList aliases() const { return content().value; } + }; + REGISTER_EVENT_TYPE(RoomAliasesEvent) } // namespace QMatrixClient diff --git a/lib/events/stateevent.cpp b/lib/events/stateevent.cpp index 476e0fd6..6a6e7782 100644 --- a/lib/events/stateevent.cpp +++ b/lib/events/stateevent.cpp @@ -36,6 +36,12 @@ using namespace QMatrixClient; return makeEvent(unknownEventTypeId(), json); }); +StateEventBase::StateEventBase(Event::Type type, event_mtype_t matrixType, + const QString &stateKey, + const QJsonObject &contentJson) + : RoomEvent(type, basicStateEventJson(matrixType, contentJson, stateKey)) +{ } + bool StateEventBase::repeatsState() const { const auto prevContentJson = unsignedJson().value(PrevContentKeyL); diff --git a/lib/events/stateevent.h b/lib/events/stateevent.h index 5dadac7f..692f2685 100644 --- a/lib/events/stateevent.h +++ b/lib/events/stateevent.h @@ -37,7 +37,12 @@ namespace QMatrixClient { public: using factory_t = EventFactory; - using RoomEvent::RoomEvent; + StateEventBase(Type type, const QJsonObject& json) + : RoomEvent(type, json) + { } + StateEventBase(Type type, event_mtype_t matrixType, + const QString& stateKey = {}, + const QJsonObject& contentJson = {}); ~StateEventBase() override = default; bool isStateEvent() const override { return true; } @@ -94,8 +99,9 @@ namespace QMatrixClient { } template explicit StateEvent(Type type, event_mtype_t matrixType, + const QString& stateKey = {}, ContentParamTs&&... contentParams) - : StateEventBase(type, matrixType) + : StateEventBase(type, matrixType, stateKey) , _content(std::forward(contentParams)...) { editJson().insert(ContentKey, _content.toJson()); diff --git a/lib/room.cpp b/lib/room.cpp index 8c9f8760..44cd0d06 100644 --- a/lib/room.cpp +++ b/lib/room.cpp @@ -93,6 +93,8 @@ class Room::Private /// The state of the room at timeline position before-0 /// \sa timelineBase std::unordered_map baseState; + /// State event stubs - events without content, just type and state key + static decltype(baseState) stubbedState; /// The state of the room at timeline position after-maxTimelineIndex() /// \sa Room::syncEdge QHash currentState; @@ -193,9 +195,22 @@ class Room::Private template const EventT* getCurrentState(const QString& stateKey = {}) const { - static const EventT empty; - const auto* evt = - currentState.value({EventT::matrixTypeId(), stateKey}, &empty); + const StateEventKey evtKey { EventT::matrixTypeId(), stateKey }; + const auto* evt = currentState.value(evtKey, nullptr); + if (!evt) { + if (stubbedState.find(evtKey) == stubbedState.end()) { + // In the absence of a real event, make a stub as-if an event + // with empty content has been received. Event classes should be + // prepared for empty/invalid/malicious content anyway. + stubbedState.emplace(evtKey, + loadStateEvent(EventT::matrixTypeId(), + {}, stateKey)); + qCDebug(MAIN) << "A new stub event created for key {" + << evtKey.first << evtKey.second << "}"; + } + evt = stubbedState[evtKey].get(); + Q_ASSERT(evt); + } Q_ASSERT(evt->type() == EventT::typeId() && evt->matrixType() == EventT::matrixTypeId()); return static_cast(evt); @@ -272,28 +287,26 @@ class Room::Private QString doSendEvent(const RoomEvent* pEvent); void onEventSendingFailure(const QString& txnId, BaseJob* call = nullptr); - template - SetRoomStateWithKeyJob* requestSetState(const QString& stateKey, - const EvT& event) + SetRoomStateWithKeyJob* requestSetState(const StateEventBase& event) { if (q->successorId().isEmpty()) { // TODO: Queue up state events sending (see #133). return connection->callApi( - id, EvT::matrixTypeId(), stateKey, event.contentJson()); + id, event.matrixType(), + event.stateKey(), event.contentJson()); } qCWarning(MAIN) << q << "has been upgraded, state won't be set"; return nullptr; } - template - auto requestSetState(const EvT& event) + template + auto requestSetState(ArgTs&&... args) { - return connection->callApi( - id, EvT::matrixTypeId(), event.contentJson()); + return requestSetState(EvT(std::forward(args)...)); } - /** + /** * @brief Apply redaction to the timeline * * Tries to find an event in the timeline and redact it; deletes the @@ -317,6 +330,8 @@ class Room::Private } }; +decltype(Room::Private::baseState) Room::Private::stubbedState { }; + Room::Room(Connection* connection, QString id, JoinState initialJoinState) : QObject(connection), d(new Private(connection, id, initialJoinState)) { @@ -1625,28 +1640,32 @@ QString Room::postEvent(RoomEvent* event) QString Room::postJson(const QString& matrixType, const QJsonObject& eventContent) { - return d->sendEvent(loadEvent(basicEventJson(matrixType, eventContent))); + return d->sendEvent(loadEvent(matrixType, eventContent)); +} + +SetRoomStateWithKeyJob* Room::setState(const StateEventBase& evt) const { + return d->requestSetState(evt); } void Room::setName(const QString& newName) { - d->requestSetState(RoomNameEvent(newName)); + d->requestSetState(newName); } void Room::setCanonicalAlias(const QString& newAlias) { - d->requestSetState(RoomCanonicalAliasEvent(newAlias)); + d->requestSetState(newAlias); } void Room::setLocalAliases(const QStringList& aliases) { - d->requestSetState(connection()->homeserver().authority(), - RoomAliasesEvent(aliases)); + d->requestSetState( + connection()->homeserver().authority(), aliases); } void Room::setTopic(const QString& newTopic) { - d->requestSetState(RoomTopicEvent(newTopic)); + d->requestSetState(newTopic); } bool isEchoEvent(const RoomEventPtr& le, const PendingEventItem& re) @@ -1756,9 +1775,10 @@ LeaveRoomJob* Room::leaveRoom() return connection()->leaveRoom(this); } -SetRoomStateWithKeyJob*Room::setMemberState(const QString& memberId, const RoomMemberEvent& event) const +SetRoomStateWithKeyJob* Room::setMemberState( + const QString& memberId, const RoomMemberEvent& event) const { - return d->requestSetState(memberId, event); + return d->requestSetState(memberId, event.content()); } void Room::kickMember(const QString& memberId, const QString& reason) diff --git a/lib/room.h b/lib/room.h index 7c85e4ed..3abf262d 100644 --- a/lib/room.h +++ b/lib/room.h @@ -440,6 +440,9 @@ namespace QMatrixClient const QJsonObject& eventContent); QString retryMessage(const QString& txnId); void discardMessage(const QString& txnId); + + /// Send a request to update the room state with the given event + SetRoomStateWithKeyJob* setState(const StateEventBase& evt) const; void setName(const QString& newName); void setCanonicalAlias(const QString& newAlias); /// Set room aliases on the user's current server @@ -453,6 +456,7 @@ namespace QMatrixClient void inviteToRoom(const QString& memberId); LeaveRoomJob* leaveRoom(); + /// \deprecated - use setState() instead") SetRoomStateWithKeyJob* setMemberState( const QString& memberId, const RoomMemberEvent& event) const; void kickMember(const QString& memberId, const QString& reason = {}); diff --git a/lib/user.cpp b/lib/user.cpp index 7b695618..8bdcbe97 100644 --- a/lib/user.cpp +++ b/lib/user.cpp @@ -292,7 +292,7 @@ void User::rename(const QString& newName, const Room* r) const auto actualNewName = sanitized(newName); MemberEventContent evtC; evtC.displayName = actualNewName; - connect(r->setMemberState(id(), RoomMemberEvent(move(evtC))), + connect(r->setState(RoomMemberEvent(id(), move(evtC))), &BaseJob::success, this, [=] { updateName(actualNewName, r); }); } -- cgit v1.2.3