diff options
-rw-r--r-- | CMakeLists.txt | 5 | ||||
-rw-r--r-- | QMatrixClient.pc.in | 10 | ||||
-rw-r--r-- | lib/avatar.cpp | 17 | ||||
-rw-r--r-- | lib/connection.cpp | 42 | ||||
-rw-r--r-- | lib/connection.h | 78 | ||||
-rw-r--r-- | lib/converters.h | 5 | ||||
-rw-r--r-- | lib/events/accountdataevents.h | 38 | ||||
-rw-r--r-- | lib/jobs/basejob.cpp | 2 | ||||
-rw-r--r-- | lib/jobs/syncjob.cpp | 2 | ||||
-rw-r--r-- | lib/room.cpp | 95 | ||||
-rw-r--r-- | lib/room.h | 56 | ||||
-rw-r--r-- | lib/util.h | 18 |
12 files changed, 232 insertions, 136 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index e9b847f4..d08b13f0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -152,6 +152,7 @@ target_link_libraries(QMatrixClient Qt5::Core Qt5::Network Qt5::Gui) add_executable(qmc-example ${example_SRCS}) target_link_libraries(qmc-example Qt5::Core QMatrixClient) +configure_file(QMatrixClient.pc.in ${CMAKE_CURRENT_BINARY_DIR}/QMatrixClient.pc @ONLY NEWLINE_STYLE UNIX) # Installation @@ -194,3 +195,7 @@ if (WIN32) endif (WIN32) install(TARGETS qmc-example RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) + +if (UNIX AND NOT APPLE) + install(FILES ${CMAKE_CURRENT_BINARY_DIR}/QMatrixClient.pc DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig) +endif() diff --git a/QMatrixClient.pc.in b/QMatrixClient.pc.in new file mode 100644 index 00000000..d2938ab7 --- /dev/null +++ b/QMatrixClient.pc.in @@ -0,0 +1,10 @@ +prefix=@CMAKE_INSTALL_PREFIX@ +exec_prefix=${prefix} +includedir=${prefix}/include +libdir=${prefix}/lib + +Name: QMatrixClient +Description: A Qt5 library to write cross-platfrom clients for Matrix +Version: @API_VERSION@ +Cflags: -I${includedir} +Libs: -L${libdir} -lQMatrixClient diff --git a/lib/avatar.cpp b/lib/avatar.cpp index 7e6dc50b..be9b6a78 100644 --- a/lib/avatar.cpp +++ b/lib/avatar.cpp @@ -126,6 +126,14 @@ QImage Avatar::Private::get(Connection* connection, QSize size, if (callback) callbacks.emplace_back(move(callback)); _thumbnailRequest = connection->getThumbnail(_url, size); + if (_originalImage.isNull() && !_defaultIcon.isNull()) + { + _originalImage = QImage(_defaultIcon.actualSize(size), + QImage::Format_ARGB32_Premultiplied); + _originalImage.fill(Qt::transparent); + QPainter p { &_originalImage }; + _defaultIcon.paint(&p, { QPoint(), _defaultIcon.actualSize(size) }); + } QObject::connect( _thumbnailRequest, &MediaThumbnailJob::success, _thumbnailRequest, [this] { _fetched = true; @@ -138,15 +146,6 @@ QImage Avatar::Private::get(Connection* connection, QSize size, }); } - if( _originalImage.isNull() ) - { - if (_defaultIcon.isNull()) - return _originalImage; - - QPainter p { &_originalImage }; - _defaultIcon.paint(&p, { QPoint(), _defaultIcon.actualSize(size) }); - } - for (const auto& p: _scaledImages) if (p.first == size) return p.second; diff --git a/lib/connection.cpp b/lib/connection.cpp index e8c9a2dc..3d635a7e 100644 --- a/lib/connection.cpp +++ b/lib/connection.cpp @@ -650,14 +650,14 @@ ForgetRoomJob* Connection::forgetRoom(const QString& id) forgetJob->start(connectionData()); connect(forgetJob, &BaseJob::success, this, [this, id] { - // If the room is in the map (possibly in both forms), delete all forms. + // Delete whatever instances of the room are still in the map. for (auto f: {false, true}) if (auto r = d->roomMap.take({ id, f })) { - emit aboutToDeleteRoom(r); - qCDebug(MAIN) << "Room" << id - << "in join state" << toCString(r->joinState()) + qCDebug(MAIN) << "Room" << r->objectName() + << "in state" << toCString(r->joinState()) << "will be deleted"; + emit r->beforeDestruction(r); r->deleteLater(); } }); @@ -731,7 +731,7 @@ User* Connection::user(const QString& userId) } if( d->userMap.contains(userId) ) return d->userMap.value(userId); - auto* user = userFactory(this, userId); + auto* user = userFactory()(this, userId); d->userMap.insert(userId, user); emit newUser(user); return user; @@ -988,7 +988,7 @@ Room* Connection::provideRoom(const QString& id, JoinState joinState) } else { - room = roomFactory(this, id, joinState); + room = roomFactory()(this, id, joinState); if (!room) { qCCritical(MAIN) << "Failed to create a room" << id; @@ -996,6 +996,8 @@ Room* Connection::provideRoom(const QString& id, JoinState joinState) } d->roomMap.insert(roomKey, room); d->firstTimeRooms.push_back(room); + connect(room, &Room::beforeDestruction, + this, &Connection::aboutToDeleteRoom); emit newRoom(room); } if (joinState == JoinState::Invite) @@ -1016,7 +1018,7 @@ Room* Connection::provideRoom(const QString& id, JoinState joinState) if (prevInvite) { qCDebug(MAIN) << "Deleting Invite state for room" << prevInvite->id(); - emit aboutToDeleteRoom(prevInvite); + emit prevInvite->beforeDestruction(prevInvite); prevInvite->deleteLater(); } } @@ -1024,12 +1026,28 @@ Room* Connection::provideRoom(const QString& id, JoinState joinState) return room; } -Connection::room_factory_t Connection::roomFactory = - [](Connection* c, const QString& id, JoinState joinState) - { return new Room(c, id, joinState); }; +void Connection::setRoomFactory(room_factory_t f) +{ + _roomFactory = std::move(f); +} + +void Connection::setUserFactory(user_factory_t f) +{ + _userFactory = std::move(f); +} + +room_factory_t Connection::roomFactory() +{ + return _roomFactory; +} + +user_factory_t Connection::userFactory() +{ + return _userFactory; +} -Connection::user_factory_t Connection::userFactory = - [](Connection* c, const QString& id) { return new User(id, c); }; +room_factory_t Connection::_roomFactory = defaultRoomFactory<>(); +user_factory_t Connection::_userFactory = defaultUserFactory<>(); QByteArray Connection::generateTxnId() const { diff --git a/lib/connection.h b/lib/connection.h index 0823ff7e..cfad8774 100644 --- a/lib/connection.h +++ b/lib/connection.h @@ -69,6 +69,40 @@ namespace QMatrixClient return connection; } + class Connection; + + using room_factory_t = std::function<Room*(Connection*, const QString&, + JoinState)>; + using user_factory_t = std::function<User*(Connection*, const QString&)>; + + /** The default factory to create room objects + * + * Just a wrapper around operator new. + * \sa Connection::setRoomFactory, Connection::setRoomType + */ + template <typename T = Room> + static inline room_factory_t defaultRoomFactory() + { + return [](Connection* c, const QString& id, JoinState js) + { + return new T(c, id, js); + }; + } + + /** The default factory to create user objects + * + * Just a wrapper around operator new. + * \sa Connection::setUserFactory, Connection::setUserType + */ + template <typename T = User> + static inline user_factory_t defaultUserFactory() + { + return [](Connection* c, const QString& id) + { + return new T(id, c); + }; + } + /** Enumeration with flags defining the network job running policy * So far only background/foreground flags are available. * @@ -89,11 +123,6 @@ namespace QMatrixClient Q_PROPERTY(QUrl homeserver READ homeserver WRITE setHomeserver NOTIFY homeserverChanged) Q_PROPERTY(bool cacheState READ cacheState WRITE setCacheState NOTIFY cacheStateChanged) public: - using room_factory_t = - std::function<Room*(Connection*, const QString&, JoinState joinState)>; - using user_factory_t = - std::function<User*(Connection*, const QString&)>; - // Room ids, rather than room pointers, are used in the direct chat // map types because the library keeps Invite rooms separate from // rooms in Join and Leave state; and direct chats in account data @@ -309,25 +338,30 @@ namespace QMatrixClient std::forward<JobArgTs>(jobArgs)...); } - /** Generates a new transaction id. Transaction id's are unique within + /** Generate a new transaction id. Transaction id's are unique within * a single Connection object */ Q_INVOKABLE QByteArray generateTxnId() const; - template <typename T = Room> - static void setRoomType() - { - roomFactory = - [](Connection* c, const QString& id, JoinState joinState) - { return new T(c, id, joinState); }; - } + /// Set a room factory function + static void setRoomFactory(room_factory_t f); - template <typename T = User> - static void setUserType() - { - userFactory = - [](Connection* c, const QString& id) { return new T(id, c); }; - } + /// Set a user factory function + static void setUserFactory(user_factory_t f); + + /// Get a room factory function + static room_factory_t roomFactory(); + + /// Get a user factory function + static user_factory_t userFactory(); + + /// Set the room factory to default with the overriden room type + template <typename T> + static void setRoomType() { setRoomFactory(defaultRoomFactory<T>()); } + + /// Set the user factory to default with the overriden user type + template <typename T> + static void setUserType() { setUserFactory(defaultUserFactory<T>()); } public slots: /** Set the homeserver base URL */ @@ -632,7 +666,7 @@ namespace QMatrixClient * the server; in particular, does not automatically create rooms * on the server. * @return a pointer to a Room object with the specified id; nullptr - * if roomId is empty if roomFactory() failed to create a Room object. + * if roomId is empty or roomFactory() failed to create a Room object. */ Room* provideRoom(const QString& roomId, JoinState joinState); @@ -662,8 +696,8 @@ namespace QMatrixClient const QString& initialDeviceName, const QString& deviceId = {}); - static room_factory_t roomFactory; - static user_factory_t userFactory; + static room_factory_t _roomFactory; + static user_factory_t _userFactory; }; } // namespace QMatrixClient Q_DECLARE_METATYPE(QMatrixClient::Connection*) diff --git a/lib/converters.h b/lib/converters.h index 1e828393..7f78effe 100644 --- a/lib/converters.h +++ b/lib/converters.h @@ -163,6 +163,11 @@ namespace QMatrixClient auto operator()(const QJsonValue& jv) const { return jv.toDouble(); } }; + template <> struct FromJson<float> + { + auto operator()(const QJsonValue& jv) const { return float(jv.toDouble()); } + }; + template <> struct FromJson<qint64> { auto operator()(const QJsonValue& jv) const { return qint64(jv.toDouble()); } diff --git a/lib/events/accountdataevents.h b/lib/events/accountdataevents.h index 94fc510a..27f6c77c 100644 --- a/lib/events/accountdataevents.h +++ b/lib/events/accountdataevents.h @@ -31,22 +31,40 @@ namespace QMatrixClient struct TagRecord { - TagRecord (QString order = {}) : order(std::move(order)) { } - explicit TagRecord(const QJsonValue& jv) - : order(jv.toObject().value("order"_ls).toString()) - { } + using order_type = Omittable<float>; + + order_type order; - QString order; + TagRecord (order_type order = none) : order(order) { } + explicit TagRecord(const QJsonValue& jv) + { + // Parse a float both from JSON double and JSON string because + // libqmatrixclient previously used to use strings to store order. + const auto orderJv = jv.toObject().value("order"_ls); + if (orderJv.isDouble()) + order = fromJson<float>(orderJv); + else if (orderJv.isString()) + { + bool ok; + order = orderJv.toString().toFloat(&ok); + if (!ok) + order = none; + } + } - bool operator==(const TagRecord& other) const - { return order == other.order; } - bool operator!=(const TagRecord& other) const - { return !operator==(other); } + bool operator<(const TagRecord& other) const + { + // Per The Spec, rooms with no order should be after those with order + return !order.omitted() && + (other.order.omitted() || order.value() < other.order.value()); + } }; inline QJsonValue toJson(const TagRecord& rec) { - return QJsonObject {{ QStringLiteral("order"), rec.order }}; + QJsonObject o; + addParam(o, QStringLiteral("order"), rec.order); + return o; } using TagsMap = QHash<QString, TagRecord>; diff --git a/lib/jobs/basejob.cpp b/lib/jobs/basejob.cpp index f9628c19..979fa431 100644 --- a/lib/jobs/basejob.cpp +++ b/lib/jobs/basejob.cpp @@ -514,7 +514,7 @@ BaseJob::Status BaseJob::status() const QByteArray BaseJob::rawData(int bytesAtMost) const { - return bytesAtMost > 0 ? + return bytesAtMost > 0 && d->rawResponse.size() > bytesAtMost ? d->rawResponse.left(bytesAtMost) + "...(truncated)" : d->rawResponse; } diff --git a/lib/jobs/syncjob.cpp b/lib/jobs/syncjob.cpp index 02690e6d..9cbac71b 100644 --- a/lib/jobs/syncjob.cpp +++ b/lib/jobs/syncjob.cpp @@ -130,10 +130,10 @@ SyncRoomData::SyncRoomData(const QString& roomId_, JoinState joinState_, switch (joinState) { case JoinState::Join: ephemeral = load<Events>(room_, "ephemeral"_ls); - accountData = load<Events>(room_, "account_data"_ls); FALLTHROUGH; case JoinState::Leave: { + accountData = load<Events>(room_, "account_data"_ls); timeline = load<RoomEvents>(room_, "timeline"_ls); const auto timelineJson = room_.value("timeline"_ls).toObject(); timelineLimited = timelineJson.value("limited"_ls).toBool(); diff --git a/lib/room.cpp b/lib/room.cpp index ea1386ad..2b81d47d 100644 --- a/lib/room.cpp +++ b/lib/room.cpp @@ -107,6 +107,7 @@ class Room::Private int notificationCount = 0; members_map_t membersMap; QList<User*> usersTyping; + QMultiHash<QString, User*> eventIdReadUsers; QList<User*> membersLeft; int unreadMessages = 0; bool displayed = false; @@ -237,14 +238,13 @@ class Room::Private */ bool processRedaction(const RedactionEvent& redaction); - std::pair<TagsMap, QStringList> setTags(TagsMap newTags); - void broadcastTagUpdates(const TagsMap& additions, - const QStringList& removals) + void setTags(TagsMap newTags); + void sendTagUpdates() { connection->callApi<SetAccountDataPerRoomJob>( connection->userId(), id, TagEvent::matrixTypeId(), TagEvent(tags).contentJson()); - emit q->tagsChanged(additions, removals); + emit q->tagsChanged(); } QJsonObject toJson() const; @@ -380,8 +380,11 @@ void Room::Private::setLastReadEvent(User* u, QString eventId) auto& storedId = lastReadEventIds[u]; if (storedId == eventId) return; + eventIdReadUsers.remove(storedId, u); + eventIdReadUsers.insert(eventId, u); swap(storedId, eventId); emit q->lastReadEventChanged(u); + emit q->readMarkerForUserMoved(u, eventId, storedId); if (isLocalUser(u)) { if (storedId != serverReadMarker) @@ -640,6 +643,10 @@ QString Room::readMarkerEventId() const return d->lastReadEventIds.value(localUser()); } +QList<User*> Room::usersAtEventId(const QString& eventId) { + return d->eventIdReadUsers.values(eventId); +} + int Room::notificationCount() const { return d->notificationCount; @@ -699,7 +706,7 @@ std::pair<bool, QString> validatedTag(QString name) return { false, name }; qWarning(MAIN) << "The tag" << name - << "doesn't follow the CS API conventions, check your client code"; + << "doesn't follow the CS API conventions"; name.prepend("u."); qWarning(MAIN) << "Using " << name << "instead"; @@ -713,8 +720,10 @@ void Room::addTag(const QString& name, const TagRecord& record) (checkRes.first && d->tags.contains(checkRes.second))) return; + emit tagsAboutToChange(); d->tags.insert(checkRes.second, record); - d->broadcastTagUpdates({{ checkRes.second, record }}, {}); + emit tagsChanged(); + d->sendTagUpdates(); } void Room::addTag(const QString& name, const QString& order) @@ -724,43 +733,32 @@ void Room::addTag(const QString& name, const QString& order) void Room::removeTag(const QString& name) { - if (!d->tags.contains(name)) - return; - - d->tags.remove(name); - d->broadcastTagUpdates({}, {{ name }}); + if (d->tags.contains(name)) + { + emit tagsAboutToChange(); + d->tags.remove(name); + emit tagsChanged(); + d->sendTagUpdates(); + } else if (!name.startsWith("u.")) + removeTag("u." + name); + else + qWarning(MAIN) << "Tag" << name << "on room" << objectName() + << "not found, nothing to remove"; } void Room::setTags(TagsMap newTags) { - const auto& changes = d->setTags(move(newTags)); - d->broadcastTagUpdates(changes.first, changes.second); + d->setTags(move(newTags)); + d->sendTagUpdates(); } -std::pair<TagsMap, QStringList> Room::Private::setTags(TagsMap newTags) +void Room::Private::setTags(TagsMap newTags) { - if (newTags == tags) - return {}; - - TagsMap additions; - const auto& tagNames = newTags.keys(); - for (const auto& t: tagNames) - { - const auto& checkRes = validatedTag(t); - const auto& value = checkRes.first ? - newTags.insert(checkRes.second, newTags.take(t)).value() : - newTags.value(checkRes.second); - if (!tags.contains(checkRes.second)) - additions.insert(checkRes.second, value); - } - - QStringList removals; - for (const auto& tag: tags.keys()) - if (!newTags.contains(tag)) - removals.push_back(tag); - - tags = newTags; - return { additions, removals }; + emit q->tagsAboutToChange(); + tags = move(newTags); + qCDebug(MAIN) << "Room" << q->objectName() << "is tagged with" + << q->tagNames().join(", "); + emit q->tagsChanged(); } bool Room::isFavourite() const @@ -1913,15 +1911,8 @@ void Room::processEphemeralEvent(EventPtr&& event) void Room::processAccountDataEvent(EventPtr&& event) { if (auto* evt = eventCast<TagEvent>(event)) - { - const auto& changes = d->setTags(evt->tags()); - if (!(changes.first.empty() && changes.second.empty())) - { - qCDebug(MAIN) << "Room" << id() << "is tagged with:" - << tagNames().join(", "); - emit tagsChanged(changes.first, changes.second); - } - } + d->setTags(evt->tags()); + if (auto* evt = eventCast<ReadMarkerEvent>(event)) { auto readEventId = evt->event_id(); @@ -1939,6 +1930,7 @@ void Room::processAccountDataEvent(EventPtr&& event) // efficient; maaybe do it another day if (!currentData || currentData->contentJson() != event->contentJson()) { + emit accountDataAboutToChange(event->matrixType()); currentData = move(event); qCDebug(MAIN) << "Updated account data of type" << currentData->matrixType(); @@ -2026,10 +2018,15 @@ QString Room::Private::calculateDisplayname() const void Room::Private::updateDisplayname() { - const QString oldName = displayname; - displayname = calculateDisplayname(); - if (oldName != displayname) - emit q->displaynameChanged(q, oldName); + auto swappedName = calculateDisplayname(); + if (swappedName != displayname) + { + emit q->displaynameAboutToChange(q); + swap(displayname, swappedName); + qDebug(MAIN) << q->objectName() << "has changed display name from" + << swappedName << "to" << displayname; + emit q->displaynameChanged(q, swappedName); + } } void appendStateEvent(QJsonArray& events, const QString& type, @@ -24,7 +24,7 @@ #include "eventitem.h" #include "joinstate.h" -#include <QtGui/QPixmap> +#include <QtGui/QImage> #include <memory> #include <deque> @@ -129,20 +129,20 @@ namespace QMatrixClient /** * Returns a square room avatar with the given size and requests it * from the network if needed - * @return a pixmap with the avatar or a placeholder if there's none + * \return a pixmap with the avatar or a placeholder if there's none * available yet */ Q_INVOKABLE QImage avatar(int dimension); /** * Returns a room avatar with the given dimensions and requests it * from the network if needed - * @return a pixmap with the avatar or a placeholder if there's none + * \return a pixmap with the avatar or a placeholder if there's none * available yet */ Q_INVOKABLE QImage avatar(int width, int height); /** - * @brief Get a user object for a given user id + * \brief Get a user object for a given user id * This is the recommended way to get a user object in a room * context. The actual object type may be changed in further * versions to provide room-specific user information (display name, @@ -164,12 +164,12 @@ namespace QMatrixClient Q_INVOKABLE JoinState memberJoinState(User* user) const; /** - * @brief Produces a disambiguated name for a given user in + * Get a disambiguated name for a given user in * the context of the room */ Q_INVOKABLE QString roomMembername(const User* u) const; /** - * @brief Produces a disambiguated name for a user with this id in + * Get a disambiguated name for a user with this id in * the context of the room */ Q_INVOKABLE QString roomMembername(const QString& userId) const; @@ -177,8 +177,8 @@ namespace QMatrixClient const Timeline& messageEvents() const; const PendingEvents& pendingEvents() const; /** - * A convenience method returning the read marker to the before-oldest - * message + * A convenience method returning the read marker to + * the before-oldest message */ rev_iter_t timelineEdge() const; Q_INVOKABLE TimelineItem::index_t minTimelineIndex() const; @@ -202,8 +202,9 @@ namespace QMatrixClient rev_iter_t readMarker(const User* user) const; rev_iter_t readMarker() const; QString readMarkerEventId() const; + QList<User*> usersAtEventId(const QString& eventId); /** - * @brief Mark the event with uptoEventId as read + * \brief Mark the event with uptoEventId as read * * Finds in the timeline and marks as read the event with * the specified id; also posts a read receipt to the server either @@ -212,7 +213,7 @@ namespace QMatrixClient */ void markMessagesAsRead(QString uptoEventId); - /** Check whether there are unread messages in the room */ + /// Check whether there are unread messages in the room bool hasUnreadMessages() const; /** Get the number of unread messages in the room @@ -263,7 +264,7 @@ namespace QMatrixClient void addTag(const QString& name, const TagRecord& record = {}); Q_INVOKABLE void addTag(const QString& name, const QString& order); - /** Remove a tag from the room */ + /// Remove a tag from the room Q_INVOKABLE void removeTag(const QString& name); /** Overwrite the room's tags @@ -276,15 +277,15 @@ namespace QMatrixClient */ void setTags(TagsMap newTags); - /** Check whether the list of tags has m.favourite */ + /// Check whether the list of tags has m.favourite bool isFavourite() const; - /** Check whether the list of tags has m.lowpriority */ + /// Check whether the list of tags has m.lowpriority bool isLowPriority() const; - /** Check whether this room is a direct chat */ + /// Check whether this room is a direct chat Q_INVOKABLE bool isDirectChat() const; - /** Get the list of users this room is a direct chat with */ + /// Get the list of users this room is a direct chat with QList<User*> directChatUsers() const; Q_INVOKABLE QUrl urlToThumbnail(const QString& eventId); @@ -301,9 +302,6 @@ namespace QMatrixClient MemberSorter memberSorter() const; - QJsonObject toJson() const; - void updateData(SyncRoomData&& data ); - void setJoinState( JoinState state ); bool processCall(Room* room, const RoomEvent* event); Q_INVOKABLE void inviteCall(const QString& callId, @@ -356,7 +354,7 @@ namespace QMatrixClient const QUrl& localFilename = {}); void cancelFileTransfer(const QString& id); - /** Mark all messages in the room as read */ + /// Mark all messages in the room as read void markAllMessagesAsRead(); signals: @@ -373,12 +371,12 @@ namespace QMatrixClient void pendingEventChanged(int pendingEventIndex); /** - * @brief The room name, the canonical alias or other aliases changed + * \brief The room name, the canonical alias or other aliases changed * * Not triggered when displayname changes. */ void namesChanged(Room* room); - /** @brief The room displayname changed */ + void displaynameAboutToChange(Room* room); void displaynameChanged(Room* room, QString oldName); void topicChanged(); void avatarChanged(); @@ -400,11 +398,13 @@ namespace QMatrixClient void lastDisplayedEventChanged(); void lastReadEventChanged(User* user); void readMarkerMoved(QString fromEventId, QString toEventId); + void readMarkerForUserMoved(User* user, QString fromEventId, QString toEventId); void unreadMessagesChanged(Room* room); + void accountDataAboutToChange(QString type); void accountDataChanged(QString type); - void tagsChanged(const TagsMap& additions, - const QStringList& removals); + void tagsAboutToChange(); + void tagsChanged(); void replacedEvent(const RoomEvent* newEvent, const RoomEvent* oldEvent); @@ -416,6 +416,16 @@ namespace QMatrixClient void fileTransferCancelled(QString id); void callEvent(Room* room, const RoomEvent* event); + /// The room is about to be deleted + void beforeDestruction(Room*); + + public: // Used by Connection - not a part of the client API + QJsonObject toJson() const; + void updateData(SyncRoomData&& data ); + + // Clients should use Connection::joinRoom() and Room::leaveRoom() + // to change the room state + void setJoinState( JoinState state ); protected: /// Returns true if any of room names/aliases has changed @@ -40,6 +40,15 @@ _ClassName(_ClassName&&) Q_DECL_EQ_DELETE; \ _ClassName& operator=(_ClassName&&) Q_DECL_EQ_DELETE; +#if QT_VERSION < QT_VERSION_CHECK(5, 7, 0) +// Copy-pasted from Qt 5.10 +template <typename T> +Q_DECL_CONSTEXPR typename std::add_const<T>::type &qAsConst(T &t) Q_DECL_NOTHROW { return t; } +// prevent rvalue arguments: +template <typename T> +static void qAsConst(const T &&) Q_DECL_EQ_DELETE; +#endif + namespace QMatrixClient { // The below enables pretty-printing of enums in logs @@ -146,15 +155,6 @@ namespace QMatrixClient template <typename FnT> using fn_arg_t = typename function_traits<FnT>::arg_type; -#if QT_VERSION < QT_VERSION_CHECK(5, 7, 0) - // Copy-pasted from Qt 5.10 - template <typename T> - Q_DECL_CONSTEXPR typename std::add_const<T>::type &qAsConst(T &t) Q_DECL_NOTHROW { return t; } - // prevent rvalue arguments: - template <typename T> - static void qAsConst(const T &&) Q_DECL_EQ_DELETE; -#endif - inline auto operator"" _ls(const char* s, std::size_t size) { return QLatin1String(s, int(size)); |