From 7d37d296f942ac993d041b4576ed52265170c4a8 Mon Sep 17 00:00:00 2001 From: Alexey Rusakov Date: Sun, 2 Jan 2022 06:03:26 +0100 Subject: Add ImplPtr and makeImpl The original (more complex and comprehensive) solution belongs to https://oliora.github.io/2015/12/29/pimpl-and-rule-of-zero.html - this commit only provides a small wrapper for non-copyable Private class implementations common throughout libQuotient. Unlike the original, default initialisation is made explicit - you have to pass ZeroImpl() instead (and I firmly believe it's a good thing: normally pointers to Private should not remain nullptr). The reason ZeroImpl<> is not a template variable is quite simple: unique_ptr is non-copyable and so cannot be initialised from; while a template function will initialise the value in-place thanks to copy elision. --- lib/user.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'lib/user.cpp') diff --git a/lib/user.cpp b/lib/user.cpp index 7da71dba..0dbc444a 100644 --- a/lib/user.cpp +++ b/lib/user.cpp @@ -46,7 +46,7 @@ public: decltype(User::Private::otherAvatars) User::Private::otherAvatars {}; User::User(QString userId, Connection* connection) - : QObject(connection), d(new Private(move(userId))) + : QObject(connection), d(makeImpl(move(userId))) { setObjectName(id()); if (connection->userId() == id()) { @@ -61,8 +61,6 @@ Connection* User::connection() const return static_cast(parent()); } -User::~User() = default; - void User::load() { auto* profileJob = -- cgit v1.2.3 From 8183a33c130f1284404edc61767ff6d29402d200 Mon Sep 17 00:00:00 2001 From: Alexey Rusakov Date: Sun, 23 Jan 2022 17:11:45 +0100 Subject: RoomStateView This class is called to provide an arbitrary snapshot of a room state; as the first step, Room::currentState() returns an instance of this class that stores, well, the current state. Implelementation-wise it's the same hash map of two-part state event keys to const event pointers; however, RoomStateView provides additional operations: - get(), that deprecates Room::getCurrentState(), returns a pointer to a particular event if the current state has it. Unlike the original method, the pointer returned from this one can be nullptr; this is done to get rid of stubbed state events that have to be created everytime a "state miss" occurred (i.e., when getCurrentState() does not find an existing event in the current state). - eventsOfType() - this is a new place for Room::stateEventsOfType() introduced recently. - query() - this is a way to specify a piece of the state content that you need to retrieve by passing a member function or a function object that retrieves it. That is especially convenient with member functions of the event class; just pass the pointer to this member function, and query() will parse the event type it has to retrieve out of it and call that member function on the event object. Returns an Omittable<>; if the respective piece of state doesn't exist, you'll get `Quotient::none` (the same as `std::nullopt`). - queryOr() - the same but with the fallback value; instead of an Omittable<>, the fallback value will be returned if the needed event is not found. --- CMakeLists.txt | 1 + lib/room.cpp | 150 ++++++++++++++++++++++++-------------------------- lib/room.h | 54 ++++++++---------- lib/roomstateview.cpp | 35 ++++++++++++ lib/roomstateview.h | 127 ++++++++++++++++++++++++++++++++++++++++++ lib/user.cpp | 17 ++++-- 6 files changed, 268 insertions(+), 116 deletions(-) create mode 100644 lib/roomstateview.cpp create mode 100644 lib/roomstateview.h (limited to 'lib/user.cpp') diff --git a/CMakeLists.txt b/CMakeLists.txt index 13c8f591..ddf11680 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -134,6 +134,7 @@ list(APPEND lib_SRCS lib/ssosession.h lib/ssosession.cpp lib/logging.h lib/logging.cpp lib/room.h lib/room.cpp + lib/roomstateview.h lib/roomstateview.cpp lib/user.h lib/user.cpp lib/avatar.h lib/avatar.cpp lib/uri.h lib/uri.cpp diff --git a/lib/room.cpp b/lib/room.cpp index ba63f50d..1450eb3b 100644 --- a/lib/room.cpp +++ b/lib/room.cpp @@ -16,6 +16,7 @@ #include "syncdata.h" #include "user.h" #include "eventstats.h" +#include "roomstateview.h" // NB: since Qt 6, moc_room.cpp needs User fully defined #include "moc_room.cpp" @@ -103,7 +104,7 @@ public: static decltype(baseState) stubbedState; /// The state of the room at syncEdge() /// \sa syncEdge - QHash currentState; + RoomStateView currentState; /// Servers with aliases for this room except the one of the local user /// \sa Room::remoteAliases QSet aliasServers; @@ -227,34 +228,6 @@ public: return evt; } - QVector stateEventsOfType(const QString& evtType) const - { - auto vals = QVector(); - for (auto it = currentState.cbegin(); it != currentState.cend(); ++it) - if (it.key().first == evtType) - vals.append(it.value()); - - return vals; - } - - template - const EventT* getCurrentState(const QString& stateKey = {}) const - { - const auto* evt = getCurrentState({ EventT::matrixTypeId(), stateKey }); - Q_ASSERT(evt->type() == EventT::typeId() - && evt->matrixType() == EventT::matrixTypeId()); - return static_cast(evt); - } - -// template -// const auto& getCurrentStateContent(const QString& stateKey = {}) const -// { -// if (const auto* evt = -// currentState.value({ EventT::matrixTypeId(), stateKey }, nullptr)) -// return evt->content(); -// return EventT::content_type() -// } - template Changes updateStateFrom(EventArrayT&& events) { @@ -480,8 +453,8 @@ const QString& Room::id() const { return d->id; } QString Room::version() const { - const auto v = d->getCurrentState()->version(); - return v.isEmpty() ? QStringLiteral("1") : v; + const auto v = currentState().query(&RoomCreateEvent::version); + return v && !v->isEmpty() ? *v : QStringLiteral("1"); } bool Room::isUnstable() const @@ -492,7 +465,10 @@ bool Room::isUnstable() const QString Room::predecessorId() const { - return d->getCurrentState()->predecessor().roomId; + if (const auto* evt = currentState().get()) + return evt->predecessor().roomId; + + return {}; } Room* Room::predecessor(JoinStates statesFilter) const @@ -507,7 +483,8 @@ Room* Room::predecessor(JoinStates statesFilter) const QString Room::successorId() const { - return d->getCurrentState()->successorRoomId(); + return currentState().queryOr(&RoomTombstoneEvent::successorRoomId, + QString()); } Room* Room::successor(JoinStates statesFilter) const @@ -534,39 +511,41 @@ bool Room::allHistoryLoaded() const QString Room::name() const { - return d->getCurrentState()->name(); + return currentState().queryOr(&RoomNameEvent::name, QString()); } QStringList Room::aliases() const { - const auto* evt = d->getCurrentState(); - auto result = evt->altAliases(); - if (!evt->alias().isEmpty()) - result << evt->alias(); - return result; + if (const auto* evt = currentState().get()) { + auto result = evt->altAliases(); + if (!evt->alias().isEmpty()) + result << evt->alias(); + return result; + } + return {}; } QStringList Room::altAliases() const { - return d->getCurrentState()->altAliases(); + return currentState().queryOr(&RoomCanonicalAliasEvent::altAliases, + QStringList()); } QString Room::canonicalAlias() const { - return d->getCurrentState()->alias(); + return currentState().queryOr(&RoomCanonicalAliasEvent::alias, QString()); } QString Room::displayName() const { return d->displayname; } QStringList Room::pinnedEventIds() const { - return d->getCurrentState()->pinnedEvents(); + return currentState().queryOr(&RoomPinnedEvent::pinnedEvents, QStringList()); } QVector Quotient::Room::pinnedEvents() const { - const auto& pinnedIds = d->getCurrentState()->pinnedEvents(); QVector pinnedEvents; - for (auto&& evtId: pinnedIds) + for (const auto& evtId : pinnedEventIds()) if (const auto& it = findInTimeline(evtId); it != historyEdge()) pinnedEvents.append(it->event()); @@ -582,7 +561,7 @@ void Room::refreshDisplayName() { d->updateDisplayname(); } QString Room::topic() const { - return d->getCurrentState()->topic(); + return currentState().queryOr(&RoomTopicEvent::topic, QString()); } QString Room::avatarMediaId() const { return d->avatar.mediaId(); } @@ -621,7 +600,8 @@ JoinState Room::memberJoinState(User* user) const Membership Room::memberState(const QString& userId) const { - return d->getCurrentState(userId)->membership(); + return currentState().queryOr(userId, &RoomMemberEvent::membership, + Membership::Leave); } bool Room::isMember(const QString& userId) const @@ -899,8 +879,9 @@ bool Room::canSwitchVersions() const if (!successorId().isEmpty()) return false; // No one can upgrade a room that's already upgraded - if (const auto* plEvt = d->getCurrentState()) { - const auto currentUserLevel = plEvt->powerLevelForUser(localUser()->id()); + if (const auto* plEvt = currentState().get()) { + const auto currentUserLevel = + plEvt->powerLevelForUser(localUser()->id()); const auto tombstonePowerLevel = plEvt->powerLevelForState("m.room.tombstone"_ls); return currentUserLevel >= tombstonePowerLevel; @@ -1008,6 +989,16 @@ const Room::RelatedEvents Room::relatedEvents( return relatedEvents(evt.id(), relType); } +const RoomCreateEvent* Room::creation() const +{ + return currentState().get(); +} + +const RoomTombstoneEvent *Room::tombstone() const +{ + return currentState().get(); +} + void Room::Private::getAllMembers() { // If already loaded or already loading, there's nothing to do here. @@ -1472,7 +1463,9 @@ int Room::timelineSize() const { return int(d->timeline.size()); } bool Room::usesEncryption() const { - return !d->getCurrentState()->algorithm().isEmpty(); + return !currentState() + .queryOr(&EncryptionEvent::algorithm, QString()) + .isEmpty(); } const StateEventBase* Room::getCurrentState(const QString& evtType, @@ -1481,13 +1474,7 @@ const StateEventBase* Room::getCurrentState(const QString& evtType, return d->getCurrentState({ evtType, stateKey }); } -const QVector -Room::stateEventsOfType(const QString& evtType) const -{ - return d->stateEventsOfType(evtType); -} - -const QHash& Room::currentState() const +RoomStateView Room::currentState() const { return d->currentState; } @@ -1564,7 +1551,7 @@ Room::Changes Room::Private::setSummary(RoomSummary&& newSummary) void Room::Private::insertMemberIntoMap(User* u) { const auto maybeUserName = - getCurrentState(u->id())->newDisplayName(); + currentState.query(u->id(), &RoomMemberEvent::newDisplayName); if (!maybeUserName) qCWarning(MEMBERS) << "insertMemberIntoMap():" << u->id() << "has no name (even empty)"; @@ -1594,9 +1581,9 @@ void Room::Private::insertMemberIntoMap(User* u) void Room::Private::removeMemberFromMap(User* u) { - const auto userName = - getCurrentState( - u->id())->newDisplayName().value_or(QString()); + const auto userName = currentState.queryOr(u->id(), + &RoomMemberEvent::newDisplayName, + QString()); qCDebug(MEMBERS) << "removeMemberFromMap(), username" << userName << "for user" << u->id(); @@ -1679,10 +1666,13 @@ Room::Private::moveEventsToTimeline(RoomEventsRange events, QString Room::memberName(const QString& mxId) const { // See https://github.com/matrix-org/matrix-doc/issues/1375 - const auto rme = getCurrentState(mxId); - return rme->newDisplayName() ? *rme->newDisplayName() - : rme->prevContent() ? rme->prevContent()->displayName.value_or(QString()) - : QString(); + if (const auto rme = currentState().get(mxId)) { + if (rme->newDisplayName()) + return *rme->newDisplayName(); + if (rme->prevContent() && rme->prevContent()->displayName) + return *rme->prevContent()->displayName; + } + return {}; } QString Room::roomMembername(const User* u) const @@ -1737,10 +1727,13 @@ QString Room::htmlSafeMemberName(const QString& userId) const QUrl Room::memberAvatarUrl(const QString &mxId) const { // See https://github.com/matrix-org/matrix-doc/issues/1375 - const auto rme = getCurrentState(mxId); - return rme->newAvatarUrl() ? *rme->newAvatarUrl() - : rme->prevContent() ? rme->prevContent()->avatarUrl.value_or(QUrl()) - : QUrl(); + if (const auto rme = currentState().get(mxId)) { + if (rme->newAvatarUrl()) + return *rme->newAvatarUrl(); + if (rme->prevContent() && rme->prevContent()->avatarUrl) + return *rme->prevContent()->avatarUrl; + } + return {}; } Room::Changes Room::Private::updateStatsFromSyncData(const SyncRoomData& data, @@ -2489,12 +2482,14 @@ bool Room::Private::processRedaction(const RedactionEvent& redaction) auto oldEvent = ti.replaceEvent(makeRedacted(*ti, redaction)); qCDebug(EVENTS) << "Redacted" << oldEvent->id() << "with" << redaction.id(); if (oldEvent->isStateEvent()) { - const StateEventKey evtKey { oldEvent->matrixType(), - oldEvent->stateKey() }; - Q_ASSERT(currentState.contains(evtKey)); - if (currentState.value(evtKey) == oldEvent.get()) { - Q_ASSERT(ti.index() >= 0); // Historical states can't be in - // currentState + // Check whether the old event was a part of current state; if it was, + // update the current state to the redacted event object. + const auto currentStateEvt = + currentState.get(oldEvent->matrixType(), oldEvent->stateKey()); + Q_ASSERT(currentStateEvt); + if (currentStateEvt == oldEvent.get()) { + // Historical states can't be in currentState + Q_ASSERT(ti.index() >= 0); qCDebug(STATE).nospace() << "Redacting state " << oldEvent->matrixType() << "/" << oldEvent->stateKey(); @@ -2514,6 +2509,7 @@ bool Room::Private::processRedaction(const RedactionEvent& redaction) } q->onRedaction(*oldEvent, *ti); emit q->replacedEvent(ti.event(), rawPtr(oldEvent)); + // By now, all references to oldEvent must have been updated to ti.event() return true; } @@ -2754,7 +2750,7 @@ void Room::Private::addHistoricalMessageEvents(RoomEvents&& events) for (const auto& eptr : events) { const auto& e = *eptr; if (e.isStateEvent() - && !currentState.contains({ e.matrixType(), e.stateKey() })) { + && !currentState.contains(e.matrixType(), e.stateKey())) { changes |= q->processStateEvent(e); } } @@ -2791,9 +2787,7 @@ Room::Changes Room::processStateEvent(const RoomEvent& e) return Change::None; // Find a value (create an empty one if necessary) and get a reference - // to it. Can't use getCurrentState<>() because it (creates and) returns - // a stub if a value is not found, and what's needed here is a "real" event - // or nullptr. + // to it, anticipating a change further in the function. auto& curStateEvent = d->currentState[{ e.matrixType(), e.stateKey() }]; // Prepare for the state change // clang-format off diff --git a/lib/room.h b/lib/room.h index 15bc7648..d49cfb55 100644 --- a/lib/room.h +++ b/lib/room.h @@ -10,6 +10,7 @@ #pragma once #include "connection.h" +#include "roomstateview.h" #include "eventitem.h" #include "quotient_common.h" @@ -399,14 +400,8 @@ public: const RelatedEvents relatedEvents(const RoomEvent& evt, EventRelation::reltypeid_t relType) const; - const RoomCreateEvent* creation() const - { - return getCurrentState(); - } - const RoomTombstoneEvent* tombstone() const - { - return getCurrentState(); - } + const RoomCreateEvent* creation() const; + const RoomTombstoneEvent* tombstone() const; bool displayed() const; /// Mark the room as currently displayed to the user @@ -760,45 +755,40 @@ public: /*! This method returns a (potentially empty) state event corresponding * to the pair of event type \p evtType and state key \p stateKey. */ - Q_INVOKABLE const Quotient::StateEventBase* + [[deprecated("Use currentState().get() instead; " + "make sure to check its result for nullptrs")]] // + const Quotient::StateEventBase* getCurrentState(const QString& evtType, const QString& stateKey = {}) const; - /// Get all state events in the room. - /*! This method returns all known state events that have occured in - * the room, as a mapping from the event type and state key to value. - */ - const QHash& currentState() const; - - /// Get all state events in the room of a certain type. - /*! This method returns all known state events that have occured in - * the room of the given type. - */ - Q_INVOKABLE const QVector - stateEventsOfType(const QString& evtType) const; - /// Get a state event with the given event type and state key /*! This is a typesafe overload that accepts a C++ event type instead of * its Matrix name. */ template + [[deprecated("Use currentState().get() instead; " + "make sure to check its result for nullptrs")]] // const EvT* getCurrentState(const QString& stateKey = {}) const { - const auto* evt = - eventCast(getCurrentState(EvT::matrixTypeId(), stateKey)); + QT_IGNORE_DEPRECATIONS( + const auto* evt = eventCast( + getCurrentState(EvT::matrixTypeId(), stateKey));) Q_ASSERT(evt); Q_ASSERT(evt->matrixTypeId() == EvT::matrixTypeId() && evt->stateKey() == stateKey); return evt; } - /// Set a state event of the given type with the given arguments - /*! This typesafe overload attempts to send a state event with the type - * \p EvT and the content defined by \p args. Specifically, the function - * creates a temporary object of type \p EvT passing \p args to - * the constructor, and sends a request to the homeserver using - * the Matrix event type defined by \p EvT and the event content produced - * via EvT::contentJson(). - */ + /// \brief Get the current room state + RoomStateView currentState() const; + + //! \brief Set a state event of the given type with the given arguments + //! + //! This typesafe overload attempts to send a state event with the type + //! \p EvT and the content defined by \p args. Specifically, the function + //! creates a temporary object of type \p EvT passing \p args to + //! the constructor, and sends a request to the homeserver using + //! the Matrix event type defined by \p EvT and the event content produced + //! via EvT::contentJson(). template auto setState(ArgTs&&... args) const { diff --git a/lib/roomstateview.cpp b/lib/roomstateview.cpp new file mode 100644 index 00000000..94c88eee --- /dev/null +++ b/lib/roomstateview.cpp @@ -0,0 +1,35 @@ +// SPDX-FileCopyrightText: 2021 Kitsune Ral +// SPDX-License-Identifier: LGPL-2.1-or-later + +#include "roomstateview.h" + +using namespace Quotient; + +const StateEventBase* RoomStateView::get(const QString& evtType, + const QString& stateKey) const +{ + return value({ evtType, stateKey }); +} + +bool RoomStateView::contains(const QString& evtType, + const QString& stateKey) const +{ + return contains({ evtType, stateKey }); +} + +QJsonObject RoomStateView::contentJson(const QString& evtType, + const QString& stateKey) const +{ + return queryOr(evtType, stateKey, &Event::contentJson, QJsonObject()); +} + +const QVector +RoomStateView::eventsOfType(const QString& evtType) const +{ + auto vals = QVector(); + for (auto it = cbegin(); it != cend(); ++it) + if (it.key().first == evtType) + vals.append(it.value()); + + return vals; +} diff --git a/lib/roomstateview.h b/lib/roomstateview.h new file mode 100644 index 00000000..cab69ae3 --- /dev/null +++ b/lib/roomstateview.h @@ -0,0 +1,127 @@ +// SPDX-FileCopyrightText: 2021 Kitsune Ral +// SPDX-License-Identifier: LGPL-2.1-or-later + +#pragma once + +#include "events/stateevent.h" + +#include + +namespace Quotient { + +class Room; + +class RoomStateView : private QHash { + Q_GADGET +public: + const QHash& events() const + { + return *this; + } + + //! \brief Get a state event with the given event type and state key + //! \return A state event corresponding to the pair of event type + //! \p evtType and state key \p stateKey, or nullptr if there's + //! no such \p evtType / \p stateKey combination in the current + //! state. + //! \warning In libQuotient 0.7 the return type changed to an OmittableCref + //! which is effectively a nullable const reference wrapper. You + //! have to check that it has_value() before using. Alternatively + //! you can now use queryCurrentState() to access state safely. + //! \sa getCurrentStateContentJson + const StateEventBase* get(const QString& evtType, + const QString& stateKey = {}) const; + + //! \brief Get a state event with the given event type and state key + //! + //! This is a typesafe overload that accepts a C++ event type instead of + //! its Matrix name. + //! \warning In libQuotient 0.7 the return type changed to an Omittable with + //! a reference wrapper inside - you have to check that it + //! has_value() before using. Alternatively you can now use + //! queryCurrentState() to access state safely. + template + const EvT* get(const QString& stateKey = {}) const + { + static_assert(std::is_base_of_v); + if (const auto* evt = get(EvT::matrixTypeId(), stateKey)) { + Q_ASSERT(evt->matrixType() == EvT::matrixTypeId() + && evt->stateKey() == stateKey); + return eventCast(evt); + } + return nullptr; + } + + using QHash::contains; + + bool contains(const QString& evtType, const QString& stateKey = {}) const; + + template + bool contains(const QString& stateKey = {}) const + { + return contains(EvT::matrixTypeId(), stateKey); + } + + //! \brief Get the content of the current state event with the given + //! event type and state key + //! \return An empty object if there's no event in the current state with + //! this event type and state key; the contents of the event + //! 'content' object otherwise + Q_INVOKABLE QJsonObject contentJson(const QString& evtType, + const QString& stateKey = {}) const; + + //! \brief Get all state events in the room of a certain type. + //! + //! This method returns all known state events that have occured in + //! the room of the given type. + const QVector + eventsOfType(const QString& evtType) const; + + template + auto query(const QString& evtType, const QString& stateKey, FnT&& fn) const + { + return lift(std::forward(fn), get(evtType, stateKey)); + } + + template + auto query(const QString& stateKey, FnT&& fn) const + { + using EventT = std::decay_t>; + static_assert(std::is_base_of_v); + return lift(std::forward(fn), get(stateKey)); + } + + template + auto queryOr(const QString& evtType, const QString& stateKey, FnT&& fn, + FallbackT&& fallback) const + { + return lift(std::forward(fn), get(evtType, stateKey)) + .value_or(std::forward(fallback)); + } + + template + auto query(FnT&& fn) const + { + return query({}, std::forward(fn)); + } + + template + auto queryOr(const QString& stateKey, FnT&& fn, FallbackT&& fallback) const + { + using EventT = std::decay_t>; + static_assert(std::is_base_of_v); + return lift(std::forward(fn), get(stateKey)) + .value_or(std::forward(fallback)); + } + + template + auto queryOr(FnT&& fn, FallbackT&& fallback) const + { + return queryOr({}, std::forward(fn), + std::forward(fallback)); + } + +private: + friend class Room; +}; +} // namespace Quotient diff --git a/lib/user.cpp b/lib/user.cpp index 0dbc444a..f7840c40 100644 --- a/lib/user.cpp +++ b/lib/user.cpp @@ -119,12 +119,17 @@ void User::rename(const QString& newName, const Room* r) return; } // #481: take the current state and update it with the new name - auto evtC = r->getCurrentState(id())->content(); - Q_ASSERT_X(evtC.membership == Membership::Join, __FUNCTION__, - "Attempt to rename a user that's not a room member"); - evtC.displayName = sanitized(newName); - r->setState(id(), move(evtC)); - // The state will be updated locally after it arrives with sync + if (const auto& maybeEvt = r->currentState().get(id())) { + auto content = maybeEvt->content(); + if (content.membership == Membership::Join) { + content.displayName = sanitized(newName); + r->setState(id(), move(content)); + // The state will be updated locally after it arrives with sync + return; + } + } + qCCritical(MEMBERS) + << "Attempt to rename a non-member in a room context - ignored"; } template -- cgit v1.2.3 From 9ae0d4e45befc79f621b03dc4efe869cd4277e06 Mon Sep 17 00:00:00 2001 From: Alexey Rusakov Date: Sun, 23 Jan 2022 10:32:40 +0100 Subject: Refactor Room::setState() There are two important aspects here: - Introducing Room::setState(evtType, stateKey, contentJson). These components are ultimately what is getting sent to the homeserver, so it makes sense to expose a respective `setState()` overload. Unlike setState(event) the new overload can be Q_INVOKABLE. - Room::setState() is no more const. Although it doesn't cause any changes in Room class (and only transient changes in Room::Private), it ultimately initiates a change in the room state, so calling it const has always been a bit of hypocrisy. If you relied on that, you most likely do something wrong (see the fix to User::rename() in this very commit for a simple example of such wrongness). Also: the backend is simplified by calling the original templated Room::setState() instead of calling Room::Private::requestSetState() that does exactly the same thing. --- lib/room.cpp | 36 ++++++++++++++++++++---------------- lib/room.h | 11 ++++++++--- lib/user.cpp | 2 +- lib/user.h | 2 +- 4 files changed, 30 insertions(+), 21 deletions(-) (limited to 'lib/user.cpp') diff --git a/lib/room.cpp b/lib/room.cpp index 1450eb3b..abd6110c 100644 --- a/lib/room.cpp +++ b/lib/room.cpp @@ -297,7 +297,9 @@ public: QString doSendEvent(const RoomEvent* pEvent); void onEventSendingFailure(const QString& txnId, BaseJob* call = nullptr); - SetRoomStateWithKeyJob* requestSetState(const StateEventBase& event) + SetRoomStateWithKeyJob* requestSetState(const QString& evtType, + const QString& stateKey, + const QJsonObject& contentJson) { // if (event.roomId().isEmpty()) // event.setRoomId(id); @@ -305,14 +307,8 @@ public: // event.setSender(connection->userId()); // TODO: Queue up state events sending (see #133). // TODO: Maybe addAsPending() as well, despite having no txnId - return connection->callApi( - id, event.matrixType(), event.stateKey(), event.contentJson()); - } - - template - auto requestSetState(ArgTs&&... args) - { - return requestSetState(EvT(std::forward(args)...)); + return connection->callApi(id, evtType, stateKey, + contentJson); } /*! Apply redaction to the timeline @@ -2126,33 +2122,41 @@ QString Room::postJson(const QString& matrixType, return d->sendEvent(loadEvent(matrixType, eventContent)); } -SetRoomStateWithKeyJob* Room::setState(const StateEventBase& evt) const +SetRoomStateWithKeyJob* Room::setState(const StateEventBase& evt) +{ + return d->requestSetState(evt.matrixType(), evt.stateKey(), + evt.contentJson()); +} + +SetRoomStateWithKeyJob* Room::setState(const QString& evtType, + const QString& stateKey, + const QJsonObject& contentJson) { - return d->requestSetState(evt); + return d->requestSetState(evtType, stateKey, contentJson); } void Room::setName(const QString& newName) { - d->requestSetState(newName); + setState(newName); } void Room::setCanonicalAlias(const QString& newAlias) { - d->requestSetState(newAlias, altAliases()); + setState(newAlias, altAliases()); } void Room::setPinnedEvents(const QStringList& events) { - d->requestSetState(events); + setState(events); } void Room::setLocalAliases(const QStringList& aliases) { - d->requestSetState(canonicalAlias(), aliases); + setState(canonicalAlias(), aliases); } void Room::setTopic(const QString& newTopic) { - d->requestSetState(newTopic); + setState(newTopic); } bool isEchoEvent(const RoomEventPtr& le, const PendingEventItem& re) diff --git a/lib/room.h b/lib/room.h index d49cfb55..9f70d77a 100644 --- a/lib/room.h +++ b/lib/room.h @@ -781,6 +781,9 @@ public: /// \brief Get the current room state RoomStateView currentState() const; + //! Send a request to update the room state with the given event + SetRoomStateWithKeyJob* setState(const StateEventBase& evt); + //! \brief Set a state event of the given type with the given arguments //! //! This typesafe overload attempts to send a state event with the type @@ -790,7 +793,7 @@ public: //! the Matrix event type defined by \p EvT and the event content produced //! via EvT::contentJson(). template - auto setState(ArgTs&&... args) const + auto setState(ArgTs&&... args) { return setState(EvT(std::forward(args)...)); } @@ -824,8 +827,10 @@ public Q_SLOTS: 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; + //! Send a request to update the room state based on freeform inputs + SetRoomStateWithKeyJob* setState(const QString& evtType, + const QString& stateKey, + const QJsonObject& contentJson); void setName(const QString& newName); void setCanonicalAlias(const QString& newAlias); void setPinnedEvents(const QStringList& events); diff --git a/lib/user.cpp b/lib/user.cpp index f7840c40..4c3fc9e2 100644 --- a/lib/user.cpp +++ b/lib/user.cpp @@ -110,7 +110,7 @@ void User::rename(const QString& newName) }); } -void User::rename(const QString& newName, const Room* r) +void User::rename(const QString& newName, Room* r) { if (!r) { qCWarning(MAIN) << "Passing a null room to two-argument User::rename()" diff --git a/lib/user.h b/lib/user.h index 8412b7fd..dfbff4a0 100644 --- a/lib/user.h +++ b/lib/user.h @@ -96,7 +96,7 @@ public Q_SLOTS: /// Set a new name in the global user profile void rename(const QString& newName); /// Set a new name for the user in one room - void rename(const QString& newName, const Room* r); + void rename(const QString& newName, Room* r); /// Upload the file and use it as an avatar bool setAvatar(const QString& fileName); /// Upload contents of the QIODevice and set that as an avatar -- cgit v1.2.3