From 3392e66fd015e191b01f6e3fc6839edc3948e31f Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Sat, 8 Dec 2018 15:36:04 +0900 Subject: [PATCH 01/27] Refactor toJson/fillJson Both now use through a common JsonConverter<> template class with its base definition tuned for structs/QJsonObjects and specialisations for non-object types. This new implementation doesn't work with virtual fillJson functions yet (so EventContent classes still use toJson as a member function) and does not cope quite well with non-constructible objects (you have to specialise JsonConverter<> rather than, more intuitively, JsonObjectConverter<>), but overall is more streamlined compared to the previous implementation. It also fixes one important issue that pushed for a rewrite: the previous implementation was not working with structure hierarchies at all so (in particular) the Filter part of CS API was totally disfunctional. --- .../definitions/location.cpp | 20 +- .../definitions/location.h | 8 +- .../definitions/protocol.cpp | 66 ++-- .../definitions/protocol.h | 24 +- lib/application-service/definitions/user.cpp | 20 +- lib/application-service/definitions/user.h | 8 +- lib/converters.cpp | 38 ++- lib/converters.h | 288 +++++++++--------- lib/csapi/admin.cpp | 40 +-- lib/csapi/administrative_contact.cpp | 40 ++- lib/csapi/content-repo.cpp | 8 +- lib/csapi/create_room.cpp | 32 +- lib/csapi/definitions/auth_data.cpp | 19 +- lib/csapi/definitions/auth_data.h | 8 +- lib/csapi/definitions/client_device.cpp | 23 +- lib/csapi/definitions/client_device.h | 8 +- lib/csapi/definitions/device_keys.cpp | 26 +- lib/csapi/definitions/device_keys.h | 8 +- lib/csapi/definitions/event_filter.cpp | 26 +- lib/csapi/definitions/event_filter.h | 8 +- .../definitions/public_rooms_response.cpp | 61 ++-- lib/csapi/definitions/public_rooms_response.h | 16 +- lib/csapi/definitions/push_condition.cpp | 23 +- lib/csapi/definitions/push_condition.h | 8 +- lib/csapi/definitions/push_rule.cpp | 29 +- lib/csapi/definitions/push_rule.h | 8 +- lib/csapi/definitions/push_ruleset.cpp | 26 +- lib/csapi/definitions/push_ruleset.h | 8 +- lib/csapi/definitions/room_event_filter.cpp | 28 +- lib/csapi/definitions/room_event_filter.h | 12 +- lib/csapi/definitions/sync_filter.cpp | 74 +++-- lib/csapi/definitions/sync_filter.h | 49 ++- lib/csapi/definitions/user_identifier.cpp | 16 +- lib/csapi/definitions/user_identifier.h | 8 +- .../definitions/wellknown/homeserver.cpp | 14 +- lib/csapi/definitions/wellknown/homeserver.h | 8 +- .../definitions/wellknown/identity_server.cpp | 14 +- .../definitions/wellknown/identity_server.h | 8 +- lib/csapi/device_management.cpp | 4 +- lib/csapi/directory.cpp | 4 +- lib/csapi/event_context.cpp | 12 +- lib/csapi/filter.cpp | 4 +- lib/csapi/joining.cpp | 51 ++-- lib/csapi/keys.cpp | 35 +-- lib/csapi/list_joined_rooms.cpp | 2 +- lib/csapi/list_public_rooms.cpp | 17 +- lib/csapi/login.cpp | 20 +- lib/csapi/message_pagination.cpp | 6 +- lib/csapi/notifications.cpp | 29 +- lib/csapi/openid.cpp | 8 +- lib/csapi/peeking_events.cpp | 6 +- lib/csapi/presence.cpp | 10 +- lib/csapi/profile.cpp | 8 +- lib/csapi/pusher.cpp | 59 ++-- lib/csapi/pushrules.cpp | 8 +- lib/csapi/redaction.cpp | 2 +- lib/csapi/registration.cpp | 18 +- lib/csapi/room_send.cpp | 2 +- lib/csapi/room_state.cpp | 4 +- lib/csapi/rooms.cpp | 40 +-- lib/csapi/rooms.h | 13 +- lib/csapi/search.cpp | 179 +++++------ lib/csapi/tags.cpp | 14 +- lib/csapi/third_party_lookup.cpp | 12 +- lib/csapi/users.cpp | 20 +- lib/csapi/versions.cpp | 2 +- lib/csapi/voip.cpp | 2 +- lib/csapi/wellknown.cpp | 4 +- lib/csapi/whoami.cpp | 2 +- lib/csapi/{{base}}.cpp.mustache | 69 +++-- lib/csapi/{{base}}.h.mustache | 13 +- lib/events/accountdataevents.h | 35 +-- lib/events/eventloader.h | 10 +- lib/events/roommemberevent.cpp | 12 +- .../definitions/request_email_validation.cpp | 23 +- .../definitions/request_email_validation.h | 8 +- .../definitions/request_msisdn_validation.cpp | 26 +- .../definitions/request_msisdn_validation.h | 8 +- lib/identity/definitions/sid.cpp | 14 +- lib/identity/definitions/sid.h | 8 +- 80 files changed, 851 insertions(+), 1100 deletions(-) diff --git a/lib/application-service/definitions/location.cpp b/lib/application-service/definitions/location.cpp index 958a55bf8..a53db8d79 100644 --- a/lib/application-service/definitions/location.cpp +++ b/lib/application-service/definitions/location.cpp @@ -6,25 +6,19 @@ using namespace QMatrixClient; -QJsonObject QMatrixClient::toJson(const ThirdPartyLocation& pod) +void JsonObjectConverter::dumpTo( + QJsonObject& jo, const ThirdPartyLocation& pod) { - QJsonObject jo; addParam<>(jo, QStringLiteral("alias"), pod.alias); addParam<>(jo, QStringLiteral("protocol"), pod.protocol); addParam<>(jo, QStringLiteral("fields"), pod.fields); - return jo; } -ThirdPartyLocation FromJsonObject::operator()(const QJsonObject& jo) const +void JsonObjectConverter::fillFrom( + const QJsonObject& jo, ThirdPartyLocation& result) { - ThirdPartyLocation result; - result.alias = - fromJson(jo.value("alias"_ls)); - result.protocol = - fromJson(jo.value("protocol"_ls)); - result.fields = - fromJson(jo.value("fields"_ls)); - - return result; + fromJson(jo.value("alias"_ls), result.alias); + fromJson(jo.value("protocol"_ls), result.protocol); + fromJson(jo.value("fields"_ls), result.fields); } diff --git a/lib/application-service/definitions/location.h b/lib/application-service/definitions/location.h index 89b48a43a..5586cfc6b 100644 --- a/lib/application-service/definitions/location.h +++ b/lib/application-service/definitions/location.h @@ -21,12 +21,10 @@ namespace QMatrixClient /// Information used to identify this third party location. QJsonObject fields; }; - - QJsonObject toJson(const ThirdPartyLocation& pod); - - template <> struct FromJsonObject + template <> struct JsonObjectConverter { - ThirdPartyLocation operator()(const QJsonObject& jo) const; + static void dumpTo(QJsonObject& jo, const ThirdPartyLocation& pod); + static void fillFrom(const QJsonObject& jo, ThirdPartyLocation& pod); }; } // namespace QMatrixClient diff --git a/lib/application-service/definitions/protocol.cpp b/lib/application-service/definitions/protocol.cpp index 04bb7dfc7..2a62b15db 100644 --- a/lib/application-service/definitions/protocol.cpp +++ b/lib/application-service/definitions/protocol.cpp @@ -6,75 +6,55 @@ using namespace QMatrixClient; -QJsonObject QMatrixClient::toJson(const FieldType& pod) +void JsonObjectConverter::dumpTo( + QJsonObject& jo, const FieldType& pod) { - QJsonObject jo; addParam<>(jo, QStringLiteral("regexp"), pod.regexp); addParam<>(jo, QStringLiteral("placeholder"), pod.placeholder); - return jo; } -FieldType FromJsonObject::operator()(const QJsonObject& jo) const +void JsonObjectConverter::fillFrom( + const QJsonObject& jo, FieldType& result) { - FieldType result; - result.regexp = - fromJson(jo.value("regexp"_ls)); - result.placeholder = - fromJson(jo.value("placeholder"_ls)); - - return result; + fromJson(jo.value("regexp"_ls), result.regexp); + fromJson(jo.value("placeholder"_ls), result.placeholder); } -QJsonObject QMatrixClient::toJson(const ProtocolInstance& pod) +void JsonObjectConverter::dumpTo( + QJsonObject& jo, const ProtocolInstance& pod) { - QJsonObject jo; addParam<>(jo, QStringLiteral("desc"), pod.desc); addParam(jo, QStringLiteral("icon"), pod.icon); addParam<>(jo, QStringLiteral("fields"), pod.fields); addParam<>(jo, QStringLiteral("network_id"), pod.networkId); - return jo; } -ProtocolInstance FromJsonObject::operator()(const QJsonObject& jo) const +void JsonObjectConverter::fillFrom( + const QJsonObject& jo, ProtocolInstance& result) { - ProtocolInstance result; - result.desc = - fromJson(jo.value("desc"_ls)); - result.icon = - fromJson(jo.value("icon"_ls)); - result.fields = - fromJson(jo.value("fields"_ls)); - result.networkId = - fromJson(jo.value("network_id"_ls)); - - return result; + fromJson(jo.value("desc"_ls), result.desc); + fromJson(jo.value("icon"_ls), result.icon); + fromJson(jo.value("fields"_ls), result.fields); + fromJson(jo.value("network_id"_ls), result.networkId); } -QJsonObject QMatrixClient::toJson(const ThirdPartyProtocol& pod) +void JsonObjectConverter::dumpTo( + QJsonObject& jo, const ThirdPartyProtocol& pod) { - QJsonObject jo; addParam<>(jo, QStringLiteral("user_fields"), pod.userFields); addParam<>(jo, QStringLiteral("location_fields"), pod.locationFields); addParam<>(jo, QStringLiteral("icon"), pod.icon); addParam<>(jo, QStringLiteral("field_types"), pod.fieldTypes); addParam<>(jo, QStringLiteral("instances"), pod.instances); - return jo; } -ThirdPartyProtocol FromJsonObject::operator()(const QJsonObject& jo) const +void JsonObjectConverter::fillFrom( + const QJsonObject& jo, ThirdPartyProtocol& result) { - ThirdPartyProtocol result; - result.userFields = - fromJson(jo.value("user_fields"_ls)); - result.locationFields = - fromJson(jo.value("location_fields"_ls)); - result.icon = - fromJson(jo.value("icon"_ls)); - result.fieldTypes = - fromJson>(jo.value("field_types"_ls)); - result.instances = - fromJson>(jo.value("instances"_ls)); - - return result; + fromJson(jo.value("user_fields"_ls), result.userFields); + fromJson(jo.value("location_fields"_ls), result.locationFields); + fromJson(jo.value("icon"_ls), result.icon); + fromJson(jo.value("field_types"_ls), result.fieldTypes); + fromJson(jo.value("instances"_ls), result.instances); } diff --git a/lib/application-service/definitions/protocol.h b/lib/application-service/definitions/protocol.h index 2aca7d669..0a1f9a210 100644 --- a/lib/application-service/definitions/protocol.h +++ b/lib/application-service/definitions/protocol.h @@ -25,12 +25,10 @@ namespace QMatrixClient /// An placeholder serving as a valid example of the field value. QString placeholder; }; - - QJsonObject toJson(const FieldType& pod); - - template <> struct FromJsonObject + template <> struct JsonObjectConverter { - FieldType operator()(const QJsonObject& jo) const; + static void dumpTo(QJsonObject& jo, const FieldType& pod); + static void fillFrom(const QJsonObject& jo, FieldType& pod); }; struct ProtocolInstance @@ -45,12 +43,10 @@ namespace QMatrixClient /// A unique identifier across all instances. QString networkId; }; - - QJsonObject toJson(const ProtocolInstance& pod); - - template <> struct FromJsonObject + template <> struct JsonObjectConverter { - ProtocolInstance operator()(const QJsonObject& jo) const; + static void dumpTo(QJsonObject& jo, const ProtocolInstance& pod); + static void fillFrom(const QJsonObject& jo, ProtocolInstance& pod); }; struct ThirdPartyProtocol @@ -78,12 +74,10 @@ namespace QMatrixClient /// same application service. QVector instances; }; - - QJsonObject toJson(const ThirdPartyProtocol& pod); - - template <> struct FromJsonObject + template <> struct JsonObjectConverter { - ThirdPartyProtocol operator()(const QJsonObject& jo) const; + static void dumpTo(QJsonObject& jo, const ThirdPartyProtocol& pod); + static void fillFrom(const QJsonObject& jo, ThirdPartyProtocol& pod); }; } // namespace QMatrixClient diff --git a/lib/application-service/definitions/user.cpp b/lib/application-service/definitions/user.cpp index ca3342364..8ba923216 100644 --- a/lib/application-service/definitions/user.cpp +++ b/lib/application-service/definitions/user.cpp @@ -6,25 +6,19 @@ using namespace QMatrixClient; -QJsonObject QMatrixClient::toJson(const ThirdPartyUser& pod) +void JsonObjectConverter::dumpTo( + QJsonObject& jo, const ThirdPartyUser& pod) { - QJsonObject jo; addParam<>(jo, QStringLiteral("userid"), pod.userid); addParam<>(jo, QStringLiteral("protocol"), pod.protocol); addParam<>(jo, QStringLiteral("fields"), pod.fields); - return jo; } -ThirdPartyUser FromJsonObject::operator()(const QJsonObject& jo) const +void JsonObjectConverter::fillFrom( + const QJsonObject& jo, ThirdPartyUser& result) { - ThirdPartyUser result; - result.userid = - fromJson(jo.value("userid"_ls)); - result.protocol = - fromJson(jo.value("protocol"_ls)); - result.fields = - fromJson(jo.value("fields"_ls)); - - return result; + fromJson(jo.value("userid"_ls), result.userid); + fromJson(jo.value("protocol"_ls), result.protocol); + fromJson(jo.value("fields"_ls), result.fields); } diff --git a/lib/application-service/definitions/user.h b/lib/application-service/definitions/user.h index 79ca7789b..062d2cacc 100644 --- a/lib/application-service/definitions/user.h +++ b/lib/application-service/definitions/user.h @@ -21,12 +21,10 @@ namespace QMatrixClient /// Information used to identify this third party location. QJsonObject fields; }; - - QJsonObject toJson(const ThirdPartyUser& pod); - - template <> struct FromJsonObject + template <> struct JsonObjectConverter { - ThirdPartyUser operator()(const QJsonObject& jo) const; + static void dumpTo(QJsonObject& jo, const ThirdPartyUser& pod); + static void fillFrom(const QJsonObject& jo, ThirdPartyUser& pod); }; } // namespace QMatrixClient diff --git a/lib/converters.cpp b/lib/converters.cpp index 41a9a65eb..88f5267e8 100644 --- a/lib/converters.cpp +++ b/lib/converters.cpp @@ -22,38 +22,34 @@ using namespace QMatrixClient; -QJsonValue QMatrixClient::variantToJson(const QVariant& v) +QJsonValue JsonConverter::dump(const QVariant& v) { return QJsonValue::fromVariant(v); } -QJsonObject QMatrixClient::toJson(const QVariantMap& map) +QVariant JsonConverter::load(const QJsonValue& jv) { - return QJsonObject::fromVariantMap(map); + return jv.toVariant(); } -#if (QT_VERSION >= QT_VERSION_CHECK(5, 5, 0)) -QJsonObject QMatrixClient::toJson(const QVariantHash& hMap) +QJsonObject JsonConverter::dump(const variant_map_t& map) { - return QJsonObject::fromVariantHash(hMap); -} + return +#if (QT_VERSION >= QT_VERSION_CHECK(5, 5, 0)) + QJsonObject::fromVariantHash +#else + QJsonObject::fromVariantMap #endif - -QVariant FromJson::operator()(const QJsonValue& jv) const -{ - return jv.toVariant(); + (map); } -QMap -FromJson>::operator()(const QJsonValue& jv) const +variant_map_t JsonConverter::load(const QJsonValue& jv) { - return jv.toObject().toVariantMap(); -} - + return jv.toObject(). #if (QT_VERSION >= QT_VERSION_CHECK(5, 5, 0)) -QHash -FromJson>::operator()(const QJsonValue& jv) const -{ - return jv.toObject().toVariantHash(); -} + toVariantHash +#else + toVariantMap #endif + (); +} diff --git a/lib/converters.h b/lib/converters.h index 53855a1fb..6227902dd 100644 --- a/lib/converters.h +++ b/lib/converters.h @@ -57,238 +57,221 @@ class QVariant; namespace QMatrixClient { - // This catches anything implicitly convertible to QJsonValue/Object/Array - inline auto toJson(const QJsonValue& val) { return val; } - inline auto toJson(const QJsonObject& o) { return o; } - inline auto toJson(const QJsonArray& arr) { return arr; } - // Special-case QString to avoid ambiguity between QJsonValue - // and QVariant (also, QString.isEmpty() is used in _impl::AddNode<> below) - inline auto toJson(const QString& s) { return s; } - - inline QJsonArray toJson(const QStringList& strings) - { - return QJsonArray::fromStringList(strings); - } - - inline QString toJson(const QByteArray& bytes) + template + struct JsonObjectConverter { - return bytes.constData(); - } + static void dumpTo(QJsonObject& jo, const T& pod) { jo = pod; } + static void fillFrom(const QJsonObject& jo, T& pod) { pod = jo; } + }; - // QVariant is outrageously omnivorous - it consumes whatever is not - // exactly matching the signature of other toJson overloads. The trick - // below disables implicit conversion to QVariant through its numerous - // non-explicit constructors. - QJsonValue variantToJson(const QVariant& v); template - inline auto toJson(T&& /* const QVariant& or QVariant&& */ var) - -> std::enable_if_t, QVariant>::value, - QJsonValue> + struct JsonConverter { - return variantToJson(var); - } - QJsonObject toJson(const QMap& map); -#if (QT_VERSION >= QT_VERSION_CHECK(5, 5, 0)) - QJsonObject toJson(const QHash& hMap); -#endif + static QJsonObject dump(const T& pod) + { + QJsonObject jo; + JsonObjectConverter::dumpTo(jo, pod); + return jo; + } + static T doLoad(const QJsonObject& jo) + { + T pod; + JsonObjectConverter::fillFrom(jo, pod); + return pod; + } + static T load(const QJsonValue& jv) { return doLoad(jv.toObject()); } + static T load(const QJsonDocument& jd) { return doLoad(jd.object()); } + }; template - inline QJsonArray toJson(const std::vector& vals) + inline auto toJson(const T& pod) { - QJsonArray ar; - for (const auto& v: vals) - ar.push_back(toJson(v)); - return ar; + return JsonConverter::dump(pod); } template - inline QJsonArray toJson(const QVector& vals) + inline auto fillJson(QJsonObject& json, const T& data) { - QJsonArray ar; - for (const auto& v: vals) - ar.push_back(toJson(v)); - return ar; + JsonObjectConverter::dumpTo(json, data); } template - inline QJsonObject toJson(const QSet& set) + inline auto fromJson(const QJsonValue& jv) { - QJsonObject json; - for (auto e: set) - json.insert(toJson(e), QJsonObject{}); - return json; + return JsonConverter::load(jv); } template - inline QJsonObject toJson(const QHash& hashMap) + inline T fromJson(const QJsonDocument& jd) { - QJsonObject json; - for (auto it = hashMap.begin(); it != hashMap.end(); ++it) - json.insert(it.key(), toJson(it.value())); - return json; + return JsonConverter::load(jd); } template - inline QJsonObject toJson(const std::unordered_map& hashMap) + inline void fromJson(const QJsonValue& jv, T& pod) { - QJsonObject json; - for (auto it = hashMap.begin(); it != hashMap.end(); ++it) - json.insert(it.key(), toJson(it.value())); - return json; + pod = fromJson(jv); } template - struct FromJsonObject + inline void fromJson(const QJsonDocument& jd, T& pod) { - T operator()(const QJsonObject& jo) const { return T(jo); } - }; + pod = fromJson(jd); + } + // Unfolds Omittable<> template - struct FromJson + inline void fromJson(const QJsonValue& jv, Omittable& pod) { - T operator()(const QJsonValue& jv) const - { - return FromJsonObject()(jv.toObject()); - } - T operator()(const QJsonDocument& jd) const - { - return FromJsonObject()(jd.object()); - } - }; + pod = fromJson(jv); + } template - inline auto fromJson(const QJsonValue& jv) + inline void fillFromJson(const QJsonValue& jv, T& pod) { - return FromJson()(jv); + JsonObjectConverter::fillFrom(jv.toObject(), pod); } + // JsonConverter<> specialisations + template - inline auto fromJson(const QJsonDocument& jd) + struct TrivialJsonDumper { - return FromJson()(jd); - } + // Works for: QJsonValue (and all things it can consume), + // QJsonObject, QJsonArray + static auto dump(const T& val) { return val; } + }; - template <> struct FromJson + template <> struct JsonConverter : public TrivialJsonDumper { - auto operator()(const QJsonValue& jv) const { return jv.toBool(); } + static auto load(const QJsonValue& jv) { return jv.toBool(); } }; - template <> struct FromJson + template <> struct JsonConverter : public TrivialJsonDumper { - auto operator()(const QJsonValue& jv) const { return jv.toInt(); } + static auto load(const QJsonValue& jv) { return jv.toInt(); } }; - template <> struct FromJson + template <> struct JsonConverter + : public TrivialJsonDumper { - auto operator()(const QJsonValue& jv) const { return jv.toDouble(); } + static auto load(const QJsonValue& jv) { return jv.toDouble(); } }; - template <> struct FromJson + template <> struct JsonConverter : public TrivialJsonDumper { - auto operator()(const QJsonValue& jv) const { return float(jv.toDouble()); } + static auto load(const QJsonValue& jv) { return float(jv.toDouble()); } }; - template <> struct FromJson + template <> struct JsonConverter + : public TrivialJsonDumper { - auto operator()(const QJsonValue& jv) const { return qint64(jv.toDouble()); } + static auto load(const QJsonValue& jv) { return qint64(jv.toDouble()); } }; - template <> struct FromJson + template <> struct JsonConverter + : public TrivialJsonDumper { - auto operator()(const QJsonValue& jv) const { return jv.toString(); } + static auto load(const QJsonValue& jv) { return jv.toString(); } }; - template <> struct FromJson + template <> struct JsonConverter { - auto operator()(const QJsonValue& jv) const + static auto dump(const QDateTime& val) = delete; // not provided yet + static auto load(const QJsonValue& jv) { - return QDateTime::fromMSecsSinceEpoch(fromJson(jv), Qt::UTC); + return QDateTime::fromMSecsSinceEpoch( + fromJson(jv), Qt::UTC); } }; - template <> struct FromJson + template <> struct JsonConverter { - auto operator()(const QJsonValue& jv) const + static auto dump(const QDate& val) = delete; // not provided yet + static auto load(const QJsonValue& jv) { return fromJson(jv).date(); } }; - template <> struct FromJson + template <> struct JsonConverter + : public TrivialJsonDumper { - auto operator()(const QJsonValue& jv) const - { - return jv.toArray(); - } + static auto load(const QJsonValue& jv) { return jv.toArray(); } }; - template <> struct FromJson + template <> struct JsonConverter { - auto operator()(const QJsonValue& jv) const + static QString dump(const QByteArray& ba) { return ba.constData(); } + static auto load(const QJsonValue& jv) { return fromJson(jv).toLatin1(); } }; - template <> struct FromJson + template <> struct JsonConverter { - QVariant operator()(const QJsonValue& jv) const; + static QJsonValue dump(const QVariant& v); + static QVariant load(const QJsonValue& jv); }; - template - struct ArrayFromJson + template + struct JsonArrayConverter { - auto operator()(const QJsonArray& ja) const + static void dumpTo(QJsonArray& ar, const VectorT& vals) { - using size_type = typename VectorT::size_type; - VectorT vect; vect.resize(size_type(ja.size())); - std::transform(ja.begin(), ja.end(), - vect.begin(), FromJson()); - return vect; + for (const auto& v: vals) + ar.push_back(toJson(v)); } - auto operator()(const QJsonValue& jv) const + static auto dump(const VectorT& vals) { - return operator()(jv.toArray()); + QJsonArray ja; + dumpTo(ja, vals); + return ja; } - auto operator()(const QJsonDocument& jd) const + static auto load(const QJsonArray& ja) { - return operator()(jd.array()); + VectorT vect; vect.reserve(typename VectorT::size_type(ja.size())); + for (const auto& i: ja) + vect.push_back(fromJson(i)); + return vect; } + static auto load(const QJsonValue& jv) { return load(jv.toArray()); } + static auto load(const QJsonDocument& jd) { return load(jd.array()); } }; - template - struct FromJson> : ArrayFromJson> + template struct JsonConverter> + : public JsonArrayConverter> { }; - template - struct FromJson> : ArrayFromJson> + template struct JsonConverter> + : public JsonArrayConverter> + { }; + + template struct JsonConverter> + : public JsonArrayConverter> { }; - template struct FromJson> + template <> struct JsonConverter + : public JsonConverter> { - auto operator()(const QJsonValue& jv) const + static auto dump(const QStringList& sl) { - const auto jsonArray = jv.toArray(); - QList sl; sl.reserve(jsonArray.size()); - std::transform(jsonArray.begin(), jsonArray.end(), - std::back_inserter(sl), FromJson()); - return sl; + return QJsonArray::fromStringList(sl); } }; - template <> struct FromJson : FromJson> { }; - - template <> struct FromJson> + template <> struct JsonObjectConverter> { - QMap operator()(const QJsonValue& jv) const; - }; - - template struct FromJson> - { - auto operator()(const QJsonValue& jv) const + static void dumpTo(QJsonObject& json, const QSet& s) + { + for (const auto& e: s) + json.insert(toJson(e), QJsonObject{}); + } + static auto fillFrom(const QJsonObject& json, QSet& s) { - const auto json = jv.toObject(); - QSet s; s.reserve(json.size()); + s.reserve(s.size() + json.size()); for (auto it = json.begin(); it != json.end(); ++it) s.insert(it.key()); return s; @@ -298,39 +281,44 @@ namespace QMatrixClient template struct HashMapFromJson { - auto operator()(const QJsonObject& jo) const + static void dumpTo(QJsonObject& json, const HashMapT& hashMap) + { + for (auto it = hashMap.begin(); it != hashMap.end(); ++it) + json.insert(it.key(), toJson(it.value())); + } + static void fillFrom(const QJsonObject& jo, HashMapT& h) { - HashMapT h; h.reserve(jo.size()); + h.reserve(jo.size()); for (auto it = jo.begin(); it != jo.end(); ++it) h[it.key()] = fromJson(it.value()); - return h; - } - auto operator()(const QJsonValue& jv) const - { - return operator()(jv.toObject()); - } - auto operator()(const QJsonDocument& jd) const - { - return operator()(jd.object()); } }; template - struct FromJson> - : HashMapFromJson> + struct JsonObjectConverter> + : public HashMapFromJson> { }; template - struct FromJson> : HashMapFromJson> + struct JsonObjectConverter> + : public HashMapFromJson> { }; + // We could use std::conditional<> below but QT_VERSION* macros in C++ code + // cause (kinda valid but useless and noisy) compiler warnings about + // bitwise operations on signed integers; so use the preprocessor for now. + using variant_map_t = #if (QT_VERSION >= QT_VERSION_CHECK(5, 5, 0)) - template <> struct FromJson> + QVariantHash; +#else + QVariantMap; +#endif + template <> struct JsonConverter { - QHash operator()(const QJsonValue& jv) const; + static QJsonObject dump(const variant_map_t& vh); + static QVariantHash load(const QJsonValue& jv); }; -#endif // Conditional insertion into a QJsonObject diff --git a/lib/csapi/admin.cpp b/lib/csapi/admin.cpp index 6066d4d95..ce06a56d8 100644 --- a/lib/csapi/admin.cpp +++ b/lib/csapi/admin.cpp @@ -16,43 +16,29 @@ namespace QMatrixClient { // Converters - template <> struct FromJsonObject + template <> struct JsonObjectConverter { - GetWhoIsJob::ConnectionInfo operator()(const QJsonObject& jo) const + static void fillFrom(const QJsonObject& jo, GetWhoIsJob::ConnectionInfo& result) { - GetWhoIsJob::ConnectionInfo result; - result.ip = - fromJson(jo.value("ip"_ls)); - result.lastSeen = - fromJson(jo.value("last_seen"_ls)); - result.userAgent = - fromJson(jo.value("user_agent"_ls)); - - return result; + fromJson(jo.value("ip"_ls), result.ip); + fromJson(jo.value("last_seen"_ls), result.lastSeen); + fromJson(jo.value("user_agent"_ls), result.userAgent); } }; - template <> struct FromJsonObject + template <> struct JsonObjectConverter { - GetWhoIsJob::SessionInfo operator()(const QJsonObject& jo) const + static void fillFrom(const QJsonObject& jo, GetWhoIsJob::SessionInfo& result) { - GetWhoIsJob::SessionInfo result; - result.connections = - fromJson>(jo.value("connections"_ls)); - - return result; + fromJson(jo.value("connections"_ls), result.connections); } }; - template <> struct FromJsonObject + template <> struct JsonObjectConverter { - GetWhoIsJob::DeviceInfo operator()(const QJsonObject& jo) const + static void fillFrom(const QJsonObject& jo, GetWhoIsJob::DeviceInfo& result) { - GetWhoIsJob::DeviceInfo result; - result.sessions = - fromJson>(jo.value("sessions"_ls)); - - return result; + fromJson(jo.value("sessions"_ls), result.sessions); } }; } // namespace QMatrixClient @@ -94,8 +80,8 @@ const QHash& GetWhoIsJob::devices() const BaseJob::Status GetWhoIsJob::parseJson(const QJsonDocument& data) { auto json = data.object(); - d->userId = fromJson(json.value("user_id"_ls)); - d->devices = fromJson>(json.value("devices"_ls)); + fromJson(json.value("user_id"_ls), d->userId); + fromJson(json.value("devices"_ls), d->devices); return Success; } diff --git a/lib/csapi/administrative_contact.cpp b/lib/csapi/administrative_contact.cpp index f62002a6c..9b021e173 100644 --- a/lib/csapi/administrative_contact.cpp +++ b/lib/csapi/administrative_contact.cpp @@ -16,21 +16,14 @@ namespace QMatrixClient { // Converters - template <> struct FromJsonObject + template <> struct JsonObjectConverter { - GetAccount3PIDsJob::ThirdPartyIdentifier operator()(const QJsonObject& jo) const + static void fillFrom(const QJsonObject& jo, GetAccount3PIDsJob::ThirdPartyIdentifier& result) { - GetAccount3PIDsJob::ThirdPartyIdentifier result; - result.medium = - fromJson(jo.value("medium"_ls)); - result.address = - fromJson(jo.value("address"_ls)); - result.validatedAt = - fromJson(jo.value("validated_at"_ls)); - result.addedAt = - fromJson(jo.value("added_at"_ls)); - - return result; + fromJson(jo.value("medium"_ls), result.medium); + fromJson(jo.value("address"_ls), result.address); + fromJson(jo.value("validated_at"_ls), result.validatedAt); + fromJson(jo.value("added_at"_ls), result.addedAt); } }; } // namespace QMatrixClient @@ -66,7 +59,7 @@ const QVector& GetAccount3PIDsJob::thr BaseJob::Status GetAccount3PIDsJob::parseJson(const QJsonDocument& data) { auto json = data.object(); - d->threepids = fromJson>(json.value("threepids"_ls)); + fromJson(json.value("threepids"_ls), d->threepids); return Success; } @@ -74,14 +67,15 @@ namespace QMatrixClient { // Converters - QJsonObject toJson(const Post3PIDsJob::ThreePidCredentials& pod) + template <> struct JsonObjectConverter { - QJsonObject jo; - addParam<>(jo, QStringLiteral("client_secret"), pod.clientSecret); - addParam<>(jo, QStringLiteral("id_server"), pod.idServer); - addParam<>(jo, QStringLiteral("sid"), pod.sid); - return jo; - } + static void dumpTo(QJsonObject& jo, const Post3PIDsJob::ThreePidCredentials& pod) + { + addParam<>(jo, QStringLiteral("client_secret"), pod.clientSecret); + addParam<>(jo, QStringLiteral("id_server"), pod.idServer); + addParam<>(jo, QStringLiteral("sid"), pod.sid); + } + }; } // namespace QMatrixClient static const auto Post3PIDsJobName = QStringLiteral("Post3PIDsJob"); @@ -139,7 +133,7 @@ const Sid& RequestTokenTo3PIDEmailJob::data() const BaseJob::Status RequestTokenTo3PIDEmailJob::parseJson(const QJsonDocument& data) { - d->data = fromJson(data); + fromJson(data, d->data); return Success; } @@ -175,7 +169,7 @@ const Sid& RequestTokenTo3PIDMSISDNJob::data() const BaseJob::Status RequestTokenTo3PIDMSISDNJob::parseJson(const QJsonDocument& data) { - d->data = fromJson(data); + fromJson(data, d->data); return Success; } diff --git a/lib/csapi/content-repo.cpp b/lib/csapi/content-repo.cpp index 9b590e423..222239856 100644 --- a/lib/csapi/content-repo.cpp +++ b/lib/csapi/content-repo.cpp @@ -52,7 +52,7 @@ BaseJob::Status UploadContentJob::parseJson(const QJsonDocument& data) if (!json.contains("content_uri"_ls)) return { JsonParseError, "The key 'content_uri' not found in the response" }; - d->contentUri = fromJson(json.value("content_uri"_ls)); + fromJson(json.value("content_uri"_ls), d->contentUri); return Success; } @@ -276,8 +276,8 @@ const QString& GetUrlPreviewJob::ogImage() const BaseJob::Status GetUrlPreviewJob::parseJson(const QJsonDocument& data) { auto json = data.object(); - d->matrixImageSize = fromJson(json.value("matrix:image:size"_ls)); - d->ogImage = fromJson(json.value("og:image"_ls)); + fromJson(json.value("matrix:image:size"_ls), d->matrixImageSize); + fromJson(json.value("og:image"_ls), d->ogImage); return Success; } @@ -312,7 +312,7 @@ Omittable GetConfigJob::uploadSize() const BaseJob::Status GetConfigJob::parseJson(const QJsonDocument& data) { auto json = data.object(); - d->uploadSize = fromJson(json.value("m.upload.size"_ls)); + fromJson(json.value("m.upload.size"_ls), d->uploadSize); return Success; } diff --git a/lib/csapi/create_room.cpp b/lib/csapi/create_room.cpp index 36f837273..c44b73ac4 100644 --- a/lib/csapi/create_room.cpp +++ b/lib/csapi/create_room.cpp @@ -16,23 +16,25 @@ namespace QMatrixClient { // Converters - QJsonObject toJson(const CreateRoomJob::Invite3pid& pod) + template <> struct JsonObjectConverter { - QJsonObject jo; - addParam<>(jo, QStringLiteral("id_server"), pod.idServer); - addParam<>(jo, QStringLiteral("medium"), pod.medium); - addParam<>(jo, QStringLiteral("address"), pod.address); - return jo; - } + static void dumpTo(QJsonObject& jo, const CreateRoomJob::Invite3pid& pod) + { + addParam<>(jo, QStringLiteral("id_server"), pod.idServer); + addParam<>(jo, QStringLiteral("medium"), pod.medium); + addParam<>(jo, QStringLiteral("address"), pod.address); + } + }; - QJsonObject toJson(const CreateRoomJob::StateEvent& pod) + template <> struct JsonObjectConverter { - QJsonObject jo; - addParam<>(jo, QStringLiteral("type"), pod.type); - addParam(jo, QStringLiteral("state_key"), pod.stateKey); - addParam<>(jo, QStringLiteral("content"), pod.content); - return jo; - } + static void dumpTo(QJsonObject& jo, const CreateRoomJob::StateEvent& pod) + { + addParam<>(jo, QStringLiteral("type"), pod.type); + addParam(jo, QStringLiteral("state_key"), pod.stateKey); + addParam<>(jo, QStringLiteral("content"), pod.content); + } + }; } // namespace QMatrixClient class CreateRoomJob::Private @@ -77,7 +79,7 @@ BaseJob::Status CreateRoomJob::parseJson(const QJsonDocument& data) if (!json.contains("room_id"_ls)) return { JsonParseError, "The key 'room_id' not found in the response" }; - d->roomId = fromJson(json.value("room_id"_ls)); + fromJson(json.value("room_id"_ls), d->roomId); return Success; } diff --git a/lib/csapi/definitions/auth_data.cpp b/lib/csapi/definitions/auth_data.cpp index f8639432f..006b8c7e4 100644 --- a/lib/csapi/definitions/auth_data.cpp +++ b/lib/csapi/definitions/auth_data.cpp @@ -6,23 +6,20 @@ using namespace QMatrixClient; -QJsonObject QMatrixClient::toJson(const AuthenticationData& pod) +void JsonObjectConverter::dumpTo( + QJsonObject& jo, const AuthenticationData& pod) { - QJsonObject jo = toJson(pod.authInfo); + fillJson(jo, pod.authInfo); addParam<>(jo, QStringLiteral("type"), pod.type); addParam(jo, QStringLiteral("session"), pod.session); - return jo; } -AuthenticationData FromJsonObject::operator()(QJsonObject jo) const +void JsonObjectConverter::fillFrom( + QJsonObject jo, AuthenticationData& result) { - AuthenticationData result; - result.type = - fromJson(jo.take("type"_ls)); - result.session = - fromJson(jo.take("session"_ls)); + fromJson(jo.take("type"_ls), result.type); + fromJson(jo.take("session"_ls), result.session); - result.authInfo = fromJson>(jo); - return result; + fromJson(jo, result.authInfo); } diff --git a/lib/csapi/definitions/auth_data.h b/lib/csapi/definitions/auth_data.h index 661d3e5fd..26eb205c5 100644 --- a/lib/csapi/definitions/auth_data.h +++ b/lib/csapi/definitions/auth_data.h @@ -23,12 +23,10 @@ namespace QMatrixClient /// Keys dependent on the login type QHash authInfo; }; - - QJsonObject toJson(const AuthenticationData& pod); - - template <> struct FromJsonObject + template <> struct JsonObjectConverter { - AuthenticationData operator()(QJsonObject jo) const; + static void dumpTo(QJsonObject& jo, const AuthenticationData& pod); + static void fillFrom(QJsonObject jo, AuthenticationData& pod); }; } // namespace QMatrixClient diff --git a/lib/csapi/definitions/client_device.cpp b/lib/csapi/definitions/client_device.cpp index 4a192f858..752b806a8 100644 --- a/lib/csapi/definitions/client_device.cpp +++ b/lib/csapi/definitions/client_device.cpp @@ -6,28 +6,21 @@ using namespace QMatrixClient; -QJsonObject QMatrixClient::toJson(const Device& pod) +void JsonObjectConverter::dumpTo( + QJsonObject& jo, const Device& pod) { - QJsonObject jo; addParam<>(jo, QStringLiteral("device_id"), pod.deviceId); addParam(jo, QStringLiteral("display_name"), pod.displayName); addParam(jo, QStringLiteral("last_seen_ip"), pod.lastSeenIp); addParam(jo, QStringLiteral("last_seen_ts"), pod.lastSeenTs); - return jo; } -Device FromJsonObject::operator()(const QJsonObject& jo) const +void JsonObjectConverter::fillFrom( + const QJsonObject& jo, Device& result) { - Device result; - result.deviceId = - fromJson(jo.value("device_id"_ls)); - result.displayName = - fromJson(jo.value("display_name"_ls)); - result.lastSeenIp = - fromJson(jo.value("last_seen_ip"_ls)); - result.lastSeenTs = - fromJson(jo.value("last_seen_ts"_ls)); - - return result; + fromJson(jo.value("device_id"_ls), result.deviceId); + fromJson(jo.value("display_name"_ls), result.displayName); + fromJson(jo.value("last_seen_ip"_ls), result.lastSeenIp); + fromJson(jo.value("last_seen_ts"_ls), result.lastSeenTs); } diff --git a/lib/csapi/definitions/client_device.h b/lib/csapi/definitions/client_device.h index 9f10888ae..a6224f71e 100644 --- a/lib/csapi/definitions/client_device.h +++ b/lib/csapi/definitions/client_device.h @@ -28,12 +28,10 @@ namespace QMatrixClient /// reasons). Omittable lastSeenTs; }; - - QJsonObject toJson(const Device& pod); - - template <> struct FromJsonObject + template <> struct JsonObjectConverter { - Device operator()(const QJsonObject& jo) const; + static void dumpTo(QJsonObject& jo, const Device& pod); + static void fillFrom(const QJsonObject& jo, Device& pod); }; } // namespace QMatrixClient diff --git a/lib/csapi/definitions/device_keys.cpp b/lib/csapi/definitions/device_keys.cpp index a0e0ca428..1e79499fa 100644 --- a/lib/csapi/definitions/device_keys.cpp +++ b/lib/csapi/definitions/device_keys.cpp @@ -6,31 +6,23 @@ using namespace QMatrixClient; -QJsonObject QMatrixClient::toJson(const DeviceKeys& pod) +void JsonObjectConverter::dumpTo( + QJsonObject& jo, const DeviceKeys& pod) { - QJsonObject jo; addParam<>(jo, QStringLiteral("user_id"), pod.userId); addParam<>(jo, QStringLiteral("device_id"), pod.deviceId); addParam<>(jo, QStringLiteral("algorithms"), pod.algorithms); addParam<>(jo, QStringLiteral("keys"), pod.keys); addParam<>(jo, QStringLiteral("signatures"), pod.signatures); - return jo; } -DeviceKeys FromJsonObject::operator()(const QJsonObject& jo) const +void JsonObjectConverter::fillFrom( + const QJsonObject& jo, DeviceKeys& result) { - DeviceKeys result; - result.userId = - fromJson(jo.value("user_id"_ls)); - result.deviceId = - fromJson(jo.value("device_id"_ls)); - result.algorithms = - fromJson(jo.value("algorithms"_ls)); - result.keys = - fromJson>(jo.value("keys"_ls)); - result.signatures = - fromJson>>(jo.value("signatures"_ls)); - - return result; + fromJson(jo.value("user_id"_ls), result.userId); + fromJson(jo.value("device_id"_ls), result.deviceId); + fromJson(jo.value("algorithms"_ls), result.algorithms); + fromJson(jo.value("keys"_ls), result.keys); + fromJson(jo.value("signatures"_ls), result.signatures); } diff --git a/lib/csapi/definitions/device_keys.h b/lib/csapi/definitions/device_keys.h index 6023e7e8c..8ebe11255 100644 --- a/lib/csapi/definitions/device_keys.h +++ b/lib/csapi/definitions/device_keys.h @@ -34,12 +34,10 @@ namespace QMatrixClient /// JSON`_. QHash> signatures; }; - - QJsonObject toJson(const DeviceKeys& pod); - - template <> struct FromJsonObject + template <> struct JsonObjectConverter { - DeviceKeys operator()(const QJsonObject& jo) const; + static void dumpTo(QJsonObject& jo, const DeviceKeys& pod); + static void fillFrom(const QJsonObject& jo, DeviceKeys& pod); }; } // namespace QMatrixClient diff --git a/lib/csapi/definitions/event_filter.cpp b/lib/csapi/definitions/event_filter.cpp index cc444db0d..b20d7807a 100644 --- a/lib/csapi/definitions/event_filter.cpp +++ b/lib/csapi/definitions/event_filter.cpp @@ -6,31 +6,23 @@ using namespace QMatrixClient; -QJsonObject QMatrixClient::toJson(const EventFilter& pod) +void JsonObjectConverter::dumpTo( + QJsonObject& jo, const EventFilter& pod) { - QJsonObject jo; addParam(jo, QStringLiteral("limit"), pod.limit); addParam(jo, QStringLiteral("not_senders"), pod.notSenders); addParam(jo, QStringLiteral("not_types"), pod.notTypes); addParam(jo, QStringLiteral("senders"), pod.senders); addParam(jo, QStringLiteral("types"), pod.types); - return jo; } -EventFilter FromJsonObject::operator()(const QJsonObject& jo) const +void JsonObjectConverter::fillFrom( + const QJsonObject& jo, EventFilter& result) { - EventFilter result; - result.limit = - fromJson(jo.value("limit"_ls)); - result.notSenders = - fromJson(jo.value("not_senders"_ls)); - result.notTypes = - fromJson(jo.value("not_types"_ls)); - result.senders = - fromJson(jo.value("senders"_ls)); - result.types = - fromJson(jo.value("types"_ls)); - - return result; + fromJson(jo.value("limit"_ls), result.limit); + fromJson(jo.value("not_senders"_ls), result.notSenders); + fromJson(jo.value("not_types"_ls), result.notTypes); + fromJson(jo.value("senders"_ls), result.senders); + fromJson(jo.value("types"_ls), result.types); } diff --git a/lib/csapi/definitions/event_filter.h b/lib/csapi/definitions/event_filter.h index 5c6a5b277..6de1fe79b 100644 --- a/lib/csapi/definitions/event_filter.h +++ b/lib/csapi/definitions/event_filter.h @@ -25,12 +25,10 @@ namespace QMatrixClient /// A list of event types to include. If this list is absent then all event types are included. A ``'*'`` can be used as a wildcard to match any sequence of characters. QStringList types; }; - - QJsonObject toJson(const EventFilter& pod); - - template <> struct FromJsonObject + template <> struct JsonObjectConverter { - EventFilter operator()(const QJsonObject& jo) const; + static void dumpTo(QJsonObject& jo, const EventFilter& pod); + static void fillFrom(const QJsonObject& jo, EventFilter& pod); }; } // namespace QMatrixClient diff --git a/lib/csapi/definitions/public_rooms_response.cpp b/lib/csapi/definitions/public_rooms_response.cpp index 2f52501d8..0d26662c4 100644 --- a/lib/csapi/definitions/public_rooms_response.cpp +++ b/lib/csapi/definitions/public_rooms_response.cpp @@ -6,9 +6,9 @@ using namespace QMatrixClient; -QJsonObject QMatrixClient::toJson(const PublicRoomsChunk& pod) +void JsonObjectConverter::dumpTo( + QJsonObject& jo, const PublicRoomsChunk& pod) { - QJsonObject jo; addParam(jo, QStringLiteral("aliases"), pod.aliases); addParam(jo, QStringLiteral("canonical_alias"), pod.canonicalAlias); addParam(jo, QStringLiteral("name"), pod.name); @@ -18,56 +18,37 @@ QJsonObject QMatrixClient::toJson(const PublicRoomsChunk& pod) addParam<>(jo, QStringLiteral("world_readable"), pod.worldReadable); addParam<>(jo, QStringLiteral("guest_can_join"), pod.guestCanJoin); addParam(jo, QStringLiteral("avatar_url"), pod.avatarUrl); - return jo; } -PublicRoomsChunk FromJsonObject::operator()(const QJsonObject& jo) const +void JsonObjectConverter::fillFrom( + const QJsonObject& jo, PublicRoomsChunk& result) { - PublicRoomsChunk result; - result.aliases = - fromJson(jo.value("aliases"_ls)); - result.canonicalAlias = - fromJson(jo.value("canonical_alias"_ls)); - result.name = - fromJson(jo.value("name"_ls)); - result.numJoinedMembers = - fromJson(jo.value("num_joined_members"_ls)); - result.roomId = - fromJson(jo.value("room_id"_ls)); - result.topic = - fromJson(jo.value("topic"_ls)); - result.worldReadable = - fromJson(jo.value("world_readable"_ls)); - result.guestCanJoin = - fromJson(jo.value("guest_can_join"_ls)); - result.avatarUrl = - fromJson(jo.value("avatar_url"_ls)); - - return result; + fromJson(jo.value("aliases"_ls), result.aliases); + fromJson(jo.value("canonical_alias"_ls), result.canonicalAlias); + fromJson(jo.value("name"_ls), result.name); + fromJson(jo.value("num_joined_members"_ls), result.numJoinedMembers); + fromJson(jo.value("room_id"_ls), result.roomId); + fromJson(jo.value("topic"_ls), result.topic); + fromJson(jo.value("world_readable"_ls), result.worldReadable); + fromJson(jo.value("guest_can_join"_ls), result.guestCanJoin); + fromJson(jo.value("avatar_url"_ls), result.avatarUrl); } -QJsonObject QMatrixClient::toJson(const PublicRoomsResponse& pod) +void JsonObjectConverter::dumpTo( + QJsonObject& jo, const PublicRoomsResponse& pod) { - QJsonObject jo; addParam<>(jo, QStringLiteral("chunk"), pod.chunk); addParam(jo, QStringLiteral("next_batch"), pod.nextBatch); addParam(jo, QStringLiteral("prev_batch"), pod.prevBatch); addParam(jo, QStringLiteral("total_room_count_estimate"), pod.totalRoomCountEstimate); - return jo; } -PublicRoomsResponse FromJsonObject::operator()(const QJsonObject& jo) const +void JsonObjectConverter::fillFrom( + const QJsonObject& jo, PublicRoomsResponse& result) { - PublicRoomsResponse result; - result.chunk = - fromJson>(jo.value("chunk"_ls)); - result.nextBatch = - fromJson(jo.value("next_batch"_ls)); - result.prevBatch = - fromJson(jo.value("prev_batch"_ls)); - result.totalRoomCountEstimate = - fromJson(jo.value("total_room_count_estimate"_ls)); - - return result; + fromJson(jo.value("chunk"_ls), result.chunk); + fromJson(jo.value("next_batch"_ls), result.nextBatch); + fromJson(jo.value("prev_batch"_ls), result.prevBatch); + fromJson(jo.value("total_room_count_estimate"_ls), result.totalRoomCountEstimate); } diff --git a/lib/csapi/definitions/public_rooms_response.h b/lib/csapi/definitions/public_rooms_response.h index 88c805baa..4c54ac259 100644 --- a/lib/csapi/definitions/public_rooms_response.h +++ b/lib/csapi/definitions/public_rooms_response.h @@ -36,12 +36,10 @@ namespace QMatrixClient /// The URL for the room's avatar, if one is set. QString avatarUrl; }; - - QJsonObject toJson(const PublicRoomsChunk& pod); - - template <> struct FromJsonObject + template <> struct JsonObjectConverter { - PublicRoomsChunk operator()(const QJsonObject& jo) const; + static void dumpTo(QJsonObject& jo, const PublicRoomsChunk& pod); + static void fillFrom(const QJsonObject& jo, PublicRoomsChunk& pod); }; /// A list of the rooms on the server. @@ -61,12 +59,10 @@ namespace QMatrixClient /// server has an estimate. Omittable totalRoomCountEstimate; }; - - QJsonObject toJson(const PublicRoomsResponse& pod); - - template <> struct FromJsonObject + template <> struct JsonObjectConverter { - PublicRoomsResponse operator()(const QJsonObject& jo) const; + static void dumpTo(QJsonObject& jo, const PublicRoomsResponse& pod); + static void fillFrom(const QJsonObject& jo, PublicRoomsResponse& pod); }; } // namespace QMatrixClient diff --git a/lib/csapi/definitions/push_condition.cpp b/lib/csapi/definitions/push_condition.cpp index 045094bc4..ace02755c 100644 --- a/lib/csapi/definitions/push_condition.cpp +++ b/lib/csapi/definitions/push_condition.cpp @@ -6,28 +6,21 @@ using namespace QMatrixClient; -QJsonObject QMatrixClient::toJson(const PushCondition& pod) +void JsonObjectConverter::dumpTo( + QJsonObject& jo, const PushCondition& pod) { - QJsonObject jo; addParam<>(jo, QStringLiteral("kind"), pod.kind); addParam(jo, QStringLiteral("key"), pod.key); addParam(jo, QStringLiteral("pattern"), pod.pattern); addParam(jo, QStringLiteral("is"), pod.is); - return jo; } -PushCondition FromJsonObject::operator()(const QJsonObject& jo) const +void JsonObjectConverter::fillFrom( + const QJsonObject& jo, PushCondition& result) { - PushCondition result; - result.kind = - fromJson(jo.value("kind"_ls)); - result.key = - fromJson(jo.value("key"_ls)); - result.pattern = - fromJson(jo.value("pattern"_ls)); - result.is = - fromJson(jo.value("is"_ls)); - - return result; + fromJson(jo.value("kind"_ls), result.kind); + fromJson(jo.value("key"_ls), result.key); + fromJson(jo.value("pattern"_ls), result.pattern); + fromJson(jo.value("is"_ls), result.is); } diff --git a/lib/csapi/definitions/push_condition.h b/lib/csapi/definitions/push_condition.h index defcebb39..e45526d2e 100644 --- a/lib/csapi/definitions/push_condition.h +++ b/lib/csapi/definitions/push_condition.h @@ -28,12 +28,10 @@ namespace QMatrixClient /// so forth. If no prefix is present, this parameter defaults to ==. QString is; }; - - QJsonObject toJson(const PushCondition& pod); - - template <> struct FromJsonObject + template <> struct JsonObjectConverter { - PushCondition operator()(const QJsonObject& jo) const; + static void dumpTo(QJsonObject& jo, const PushCondition& pod); + static void fillFrom(const QJsonObject& jo, PushCondition& pod); }; } // namespace QMatrixClient diff --git a/lib/csapi/definitions/push_rule.cpp b/lib/csapi/definitions/push_rule.cpp index baddd1873..abbb04b59 100644 --- a/lib/csapi/definitions/push_rule.cpp +++ b/lib/csapi/definitions/push_rule.cpp @@ -6,34 +6,25 @@ using namespace QMatrixClient; -QJsonObject QMatrixClient::toJson(const PushRule& pod) +void JsonObjectConverter::dumpTo( + QJsonObject& jo, const PushRule& pod) { - QJsonObject jo; addParam<>(jo, QStringLiteral("actions"), pod.actions); addParam<>(jo, QStringLiteral("default"), pod.isDefault); addParam<>(jo, QStringLiteral("enabled"), pod.enabled); addParam<>(jo, QStringLiteral("rule_id"), pod.ruleId); addParam(jo, QStringLiteral("conditions"), pod.conditions); addParam(jo, QStringLiteral("pattern"), pod.pattern); - return jo; } -PushRule FromJsonObject::operator()(const QJsonObject& jo) const +void JsonObjectConverter::fillFrom( + const QJsonObject& jo, PushRule& result) { - PushRule result; - result.actions = - fromJson>(jo.value("actions"_ls)); - result.isDefault = - fromJson(jo.value("default"_ls)); - result.enabled = - fromJson(jo.value("enabled"_ls)); - result.ruleId = - fromJson(jo.value("rule_id"_ls)); - result.conditions = - fromJson>(jo.value("conditions"_ls)); - result.pattern = - fromJson(jo.value("pattern"_ls)); - - return result; + fromJson(jo.value("actions"_ls), result.actions); + fromJson(jo.value("default"_ls), result.isDefault); + fromJson(jo.value("enabled"_ls), result.enabled); + fromJson(jo.value("rule_id"_ls), result.ruleId); + fromJson(jo.value("conditions"_ls), result.conditions); + fromJson(jo.value("pattern"_ls), result.pattern); } diff --git a/lib/csapi/definitions/push_rule.h b/lib/csapi/definitions/push_rule.h index 5f52876df..05328b8b3 100644 --- a/lib/csapi/definitions/push_rule.h +++ b/lib/csapi/definitions/push_rule.h @@ -34,12 +34,10 @@ namespace QMatrixClient /// rules. QString pattern; }; - - QJsonObject toJson(const PushRule& pod); - - template <> struct FromJsonObject + template <> struct JsonObjectConverter { - PushRule operator()(const QJsonObject& jo) const; + static void dumpTo(QJsonObject& jo, const PushRule& pod); + static void fillFrom(const QJsonObject& jo, PushRule& pod); }; } // namespace QMatrixClient diff --git a/lib/csapi/definitions/push_ruleset.cpp b/lib/csapi/definitions/push_ruleset.cpp index 14b7a4b67..f1bad882e 100644 --- a/lib/csapi/definitions/push_ruleset.cpp +++ b/lib/csapi/definitions/push_ruleset.cpp @@ -6,31 +6,23 @@ using namespace QMatrixClient; -QJsonObject QMatrixClient::toJson(const PushRuleset& pod) +void JsonObjectConverter::dumpTo( + QJsonObject& jo, const PushRuleset& pod) { - QJsonObject jo; addParam(jo, QStringLiteral("content"), pod.content); addParam(jo, QStringLiteral("override"), pod.override); addParam(jo, QStringLiteral("room"), pod.room); addParam(jo, QStringLiteral("sender"), pod.sender); addParam(jo, QStringLiteral("underride"), pod.underride); - return jo; } -PushRuleset FromJsonObject::operator()(const QJsonObject& jo) const +void JsonObjectConverter::fillFrom( + const QJsonObject& jo, PushRuleset& result) { - PushRuleset result; - result.content = - fromJson>(jo.value("content"_ls)); - result.override = - fromJson>(jo.value("override"_ls)); - result.room = - fromJson>(jo.value("room"_ls)); - result.sender = - fromJson>(jo.value("sender"_ls)); - result.underride = - fromJson>(jo.value("underride"_ls)); - - return result; + fromJson(jo.value("content"_ls), result.content); + fromJson(jo.value("override"_ls), result.override); + fromJson(jo.value("room"_ls), result.room); + fromJson(jo.value("sender"_ls), result.sender); + fromJson(jo.value("underride"_ls), result.underride); } diff --git a/lib/csapi/definitions/push_ruleset.h b/lib/csapi/definitions/push_ruleset.h index a274b72ac..f2d937c03 100644 --- a/lib/csapi/definitions/push_ruleset.h +++ b/lib/csapi/definitions/push_ruleset.h @@ -22,12 +22,10 @@ namespace QMatrixClient QVector sender; QVector underride; }; - - QJsonObject toJson(const PushRuleset& pod); - - template <> struct FromJsonObject + template <> struct JsonObjectConverter { - PushRuleset operator()(const QJsonObject& jo) const; + static void dumpTo(QJsonObject& jo, const PushRuleset& pod); + static void fillFrom(const QJsonObject& jo, PushRuleset& pod); }; } // namespace QMatrixClient diff --git a/lib/csapi/definitions/room_event_filter.cpp b/lib/csapi/definitions/room_event_filter.cpp index 8cd2ded7e..df92e684a 100644 --- a/lib/csapi/definitions/room_event_filter.cpp +++ b/lib/csapi/definitions/room_event_filter.cpp @@ -6,31 +6,21 @@ using namespace QMatrixClient; -QJsonObject QMatrixClient::toJson(const RoomEventFilter& pod) +void JsonObjectConverter::dumpTo( + QJsonObject& jo, const RoomEventFilter& pod) { - QJsonObject jo; + fillJson(jo, pod); addParam(jo, QStringLiteral("not_rooms"), pod.notRooms); addParam(jo, QStringLiteral("rooms"), pod.rooms); addParam(jo, QStringLiteral("contains_url"), pod.containsUrl); - addParam(jo, QStringLiteral("lazy_load_members"), pod.lazyLoadMembers); - addParam(jo, QStringLiteral("include_redundant_members"), pod.includeRedundantMembers); - return jo; } -RoomEventFilter FromJsonObject::operator()(const QJsonObject& jo) const +void JsonObjectConverter::fillFrom( + const QJsonObject& jo, RoomEventFilter& result) { - RoomEventFilter result; - result.notRooms = - fromJson(jo.value("not_rooms"_ls)); - result.rooms = - fromJson(jo.value("rooms"_ls)); - result.containsUrl = - fromJson(jo.value("contains_url"_ls)); - result.lazyLoadMembers = - fromJson(jo.value("lazy_load_members"_ls)); - result.includeRedundantMembers = - fromJson(jo.value("include_redundant_members"_ls)); - - return result; + fillFromJson(jo, result); + fromJson(jo.value("not_rooms"_ls), result.notRooms); + fromJson(jo.value("rooms"_ls), result.rooms); + fromJson(jo.value("contains_url"_ls), result.containsUrl); } diff --git a/lib/csapi/definitions/room_event_filter.h b/lib/csapi/definitions/room_event_filter.h index 87f01189f..3908b8ece 100644 --- a/lib/csapi/definitions/room_event_filter.h +++ b/lib/csapi/definitions/room_event_filter.h @@ -21,17 +21,11 @@ namespace QMatrixClient QStringList rooms; /// If ``true``, includes only events with a ``url`` key in their content. If ``false``, excludes those events. Defaults to ``false``. bool containsUrl; - /// If ``true``, the only ``m.room.member`` events returned in the ``state`` section of the ``/sync`` response are those which are definitely necessary for a client to display the ``sender`` of the timeline events in that response. If ``false``, ``m.room.member`` events are not filtered. By default, servers should suppress duplicate redundant lazy-loaded ``m.room.member`` events from being sent to a given client across multiple calls to ``/sync``, given that most clients cache membership events (see include_redundant_members to change this behaviour). - bool lazyLoadMembers; - /// If ``true``, the ``state`` section of the ``/sync`` response will always contain the ``m.room.member`` events required to display the ``sender`` of the timeline events in that response, assuming ``lazy_load_members`` is enabled. This means that redundant duplicate member events may be returned across multiple calls to ``/sync``. This is useful for naive clients who never track membership data. If ``false``, duplicate ``m.room.member`` events may be suppressed by the server across multiple calls to ``/sync``. If ``lazy_load_members`` is ``false`` this field is ignored. - bool includeRedundantMembers; }; - - QJsonObject toJson(const RoomEventFilter& pod); - - template <> struct FromJsonObject + template <> struct JsonObjectConverter { - RoomEventFilter operator()(const QJsonObject& jo) const; + static void dumpTo(QJsonObject& jo, const RoomEventFilter& pod); + static void fillFrom(const QJsonObject& jo, RoomEventFilter& pod); }; } // namespace QMatrixClient diff --git a/lib/csapi/definitions/sync_filter.cpp b/lib/csapi/definitions/sync_filter.cpp index bd87804c5..32752d1f2 100644 --- a/lib/csapi/definitions/sync_filter.cpp +++ b/lib/csapi/definitions/sync_filter.cpp @@ -6,9 +6,25 @@ using namespace QMatrixClient; -QJsonObject QMatrixClient::toJson(const RoomFilter& pod) +void JsonObjectConverter::dumpTo( + QJsonObject& jo, const StateFilter& pod) +{ + fillJson(jo, pod); + addParam(jo, QStringLiteral("lazy_load_members"), pod.lazyLoadMembers); + addParam(jo, QStringLiteral("include_redundant_members"), pod.includeRedundantMembers); +} + +void JsonObjectConverter::fillFrom( + const QJsonObject& jo, StateFilter& result) +{ + fillFromJson(jo, result); + fromJson(jo.value("lazy_load_members"_ls), result.lazyLoadMembers); + fromJson(jo.value("include_redundant_members"_ls), result.includeRedundantMembers); +} + +void JsonObjectConverter::dumpTo( + QJsonObject& jo, const RoomFilter& pod) { - QJsonObject jo; addParam(jo, QStringLiteral("not_rooms"), pod.notRooms); addParam(jo, QStringLiteral("rooms"), pod.rooms); addParam(jo, QStringLiteral("ephemeral"), pod.ephemeral); @@ -16,55 +32,37 @@ QJsonObject QMatrixClient::toJson(const RoomFilter& pod) addParam(jo, QStringLiteral("state"), pod.state); addParam(jo, QStringLiteral("timeline"), pod.timeline); addParam(jo, QStringLiteral("account_data"), pod.accountData); - return jo; } -RoomFilter FromJsonObject::operator()(const QJsonObject& jo) const +void JsonObjectConverter::fillFrom( + const QJsonObject& jo, RoomFilter& result) { - RoomFilter result; - result.notRooms = - fromJson(jo.value("not_rooms"_ls)); - result.rooms = - fromJson(jo.value("rooms"_ls)); - result.ephemeral = - fromJson(jo.value("ephemeral"_ls)); - result.includeLeave = - fromJson(jo.value("include_leave"_ls)); - result.state = - fromJson(jo.value("state"_ls)); - result.timeline = - fromJson(jo.value("timeline"_ls)); - result.accountData = - fromJson(jo.value("account_data"_ls)); - - return result; + fromJson(jo.value("not_rooms"_ls), result.notRooms); + fromJson(jo.value("rooms"_ls), result.rooms); + fromJson(jo.value("ephemeral"_ls), result.ephemeral); + fromJson(jo.value("include_leave"_ls), result.includeLeave); + fromJson(jo.value("state"_ls), result.state); + fromJson(jo.value("timeline"_ls), result.timeline); + fromJson(jo.value("account_data"_ls), result.accountData); } -QJsonObject QMatrixClient::toJson(const Filter& pod) +void JsonObjectConverter::dumpTo( + QJsonObject& jo, const Filter& pod) { - QJsonObject jo; addParam(jo, QStringLiteral("event_fields"), pod.eventFields); addParam(jo, QStringLiteral("event_format"), pod.eventFormat); addParam(jo, QStringLiteral("presence"), pod.presence); addParam(jo, QStringLiteral("account_data"), pod.accountData); addParam(jo, QStringLiteral("room"), pod.room); - return jo; } -Filter FromJsonObject::operator()(const QJsonObject& jo) const +void JsonObjectConverter::fillFrom( + const QJsonObject& jo, Filter& result) { - Filter result; - result.eventFields = - fromJson(jo.value("event_fields"_ls)); - result.eventFormat = - fromJson(jo.value("event_format"_ls)); - result.presence = - fromJson(jo.value("presence"_ls)); - result.accountData = - fromJson(jo.value("account_data"_ls)); - result.room = - fromJson(jo.value("room"_ls)); - - return result; + fromJson(jo.value("event_fields"_ls), result.eventFields); + fromJson(jo.value("event_format"_ls), result.eventFormat); + fromJson(jo.value("presence"_ls), result.presence); + fromJson(jo.value("account_data"_ls), result.accountData); + fromJson(jo.value("room"_ls), result.room); } diff --git a/lib/csapi/definitions/sync_filter.h b/lib/csapi/definitions/sync_filter.h index ca275a9a7..ccc3061bc 100644 --- a/lib/csapi/definitions/sync_filter.h +++ b/lib/csapi/definitions/sync_filter.h @@ -14,6 +14,37 @@ namespace QMatrixClient { // Data structures + /// The state events to include for rooms. + struct StateFilter : RoomEventFilter + { + /// If ``true``, the only ``m.room.member`` events returned in + /// the ``state`` section of the ``/sync`` response are those + /// which are definitely necessary for a client to display + /// the ``sender`` of the timeline events in that response. + /// If ``false``, ``m.room.member`` events are not filtered. + /// By default, servers should suppress duplicate redundant + /// lazy-loaded ``m.room.member`` events from being sent to a given + /// client across multiple calls to ``/sync``, given that most clients + /// cache membership events (see ``include_redundant_members`` + /// to change this behaviour). + bool lazyLoadMembers; + /// If ``true``, the ``state`` section of the ``/sync`` response will + /// always contain the ``m.room.member`` events required to display + /// the ``sender`` of the timeline events in that response, assuming + /// ``lazy_load_members`` is enabled. This means that redundant + /// duplicate member events may be returned across multiple calls to + /// ``/sync``. This is useful for naive clients who never track + /// membership data. If ``false``, duplicate ``m.room.member`` events + /// may be suppressed by the server across multiple calls to ``/sync``. + /// If ``lazy_load_members`` is ``false`` this field is ignored. + bool includeRedundantMembers; + }; + template <> struct JsonObjectConverter + { + static void dumpTo(QJsonObject& jo, const StateFilter& pod); + static void fillFrom(const QJsonObject& jo, StateFilter& pod); + }; + /// Filters to be applied to room data. struct RoomFilter { @@ -26,18 +57,16 @@ namespace QMatrixClient /// Include rooms that the user has left in the sync, default false bool includeLeave; /// The state events to include for rooms. - Omittable state; + Omittable state; /// The message and state update events to include for rooms. Omittable timeline; /// The per user account data to include for rooms. Omittable accountData; }; - - QJsonObject toJson(const RoomFilter& pod); - - template <> struct FromJsonObject + template <> struct JsonObjectConverter { - RoomFilter operator()(const QJsonObject& jo) const; + static void dumpTo(QJsonObject& jo, const RoomFilter& pod); + static void fillFrom(const QJsonObject& jo, RoomFilter& pod); }; struct Filter @@ -53,12 +82,10 @@ namespace QMatrixClient /// Filters to be applied to room data. Omittable room; }; - - QJsonObject toJson(const Filter& pod); - - template <> struct FromJsonObject + template <> struct JsonObjectConverter { - Filter operator()(const QJsonObject& jo) const; + static void dumpTo(QJsonObject& jo, const Filter& pod); + static void fillFrom(const QJsonObject& jo, Filter& pod); }; } // namespace QMatrixClient diff --git a/lib/csapi/definitions/user_identifier.cpp b/lib/csapi/definitions/user_identifier.cpp index 80a6d4506..05a27c1c0 100644 --- a/lib/csapi/definitions/user_identifier.cpp +++ b/lib/csapi/definitions/user_identifier.cpp @@ -6,20 +6,18 @@ using namespace QMatrixClient; -QJsonObject QMatrixClient::toJson(const UserIdentifier& pod) +void JsonObjectConverter::dumpTo( + QJsonObject& jo, const UserIdentifier& pod) { - QJsonObject jo = toJson(pod.additionalProperties); + fillJson(jo, pod.additionalProperties); addParam<>(jo, QStringLiteral("type"), pod.type); - return jo; } -UserIdentifier FromJsonObject::operator()(QJsonObject jo) const +void JsonObjectConverter::fillFrom( + QJsonObject jo, UserIdentifier& result) { - UserIdentifier result; - result.type = - fromJson(jo.take("type"_ls)); + fromJson(jo.take("type"_ls), result.type); - result.additionalProperties = fromJson(jo); - return result; + fromJson(jo, result.additionalProperties); } diff --git a/lib/csapi/definitions/user_identifier.h b/lib/csapi/definitions/user_identifier.h index 426144361..cbb1550f3 100644 --- a/lib/csapi/definitions/user_identifier.h +++ b/lib/csapi/definitions/user_identifier.h @@ -20,12 +20,10 @@ namespace QMatrixClient /// Identification information for a user QVariantHash additionalProperties; }; - - QJsonObject toJson(const UserIdentifier& pod); - - template <> struct FromJsonObject + template <> struct JsonObjectConverter { - UserIdentifier operator()(QJsonObject jo) const; + static void dumpTo(QJsonObject& jo, const UserIdentifier& pod); + static void fillFrom(QJsonObject jo, UserIdentifier& pod); }; } // namespace QMatrixClient diff --git a/lib/csapi/definitions/wellknown/homeserver.cpp b/lib/csapi/definitions/wellknown/homeserver.cpp index f1482ee46..0783f11b4 100644 --- a/lib/csapi/definitions/wellknown/homeserver.cpp +++ b/lib/csapi/definitions/wellknown/homeserver.cpp @@ -6,19 +6,15 @@ using namespace QMatrixClient; -QJsonObject QMatrixClient::toJson(const HomeserverInformation& pod) +void JsonObjectConverter::dumpTo( + QJsonObject& jo, const HomeserverInformation& pod) { - QJsonObject jo; addParam<>(jo, QStringLiteral("base_url"), pod.baseUrl); - return jo; } -HomeserverInformation FromJsonObject::operator()(const QJsonObject& jo) const +void JsonObjectConverter::fillFrom( + const QJsonObject& jo, HomeserverInformation& result) { - HomeserverInformation result; - result.baseUrl = - fromJson(jo.value("base_url"_ls)); - - return result; + fromJson(jo.value("base_url"_ls), result.baseUrl); } diff --git a/lib/csapi/definitions/wellknown/homeserver.h b/lib/csapi/definitions/wellknown/homeserver.h index 09d6ba63b..f6761c30b 100644 --- a/lib/csapi/definitions/wellknown/homeserver.h +++ b/lib/csapi/definitions/wellknown/homeserver.h @@ -17,12 +17,10 @@ namespace QMatrixClient /// The base URL for the homeserver for client-server connections. QString baseUrl; }; - - QJsonObject toJson(const HomeserverInformation& pod); - - template <> struct FromJsonObject + template <> struct JsonObjectConverter { - HomeserverInformation operator()(const QJsonObject& jo) const; + static void dumpTo(QJsonObject& jo, const HomeserverInformation& pod); + static void fillFrom(const QJsonObject& jo, HomeserverInformation& pod); }; } // namespace QMatrixClient diff --git a/lib/csapi/definitions/wellknown/identity_server.cpp b/lib/csapi/definitions/wellknown/identity_server.cpp index f9d7bc375..99f366413 100644 --- a/lib/csapi/definitions/wellknown/identity_server.cpp +++ b/lib/csapi/definitions/wellknown/identity_server.cpp @@ -6,19 +6,15 @@ using namespace QMatrixClient; -QJsonObject QMatrixClient::toJson(const IdentityServerInformation& pod) +void JsonObjectConverter::dumpTo( + QJsonObject& jo, const IdentityServerInformation& pod) { - QJsonObject jo; addParam<>(jo, QStringLiteral("base_url"), pod.baseUrl); - return jo; } -IdentityServerInformation FromJsonObject::operator()(const QJsonObject& jo) const +void JsonObjectConverter::fillFrom( + const QJsonObject& jo, IdentityServerInformation& result) { - IdentityServerInformation result; - result.baseUrl = - fromJson(jo.value("base_url"_ls)); - - return result; + fromJson(jo.value("base_url"_ls), result.baseUrl); } diff --git a/lib/csapi/definitions/wellknown/identity_server.h b/lib/csapi/definitions/wellknown/identity_server.h index cb8ffcee5..67d8b08d4 100644 --- a/lib/csapi/definitions/wellknown/identity_server.h +++ b/lib/csapi/definitions/wellknown/identity_server.h @@ -17,12 +17,10 @@ namespace QMatrixClient /// The base URL for the identity server for client-server connections. QString baseUrl; }; - - QJsonObject toJson(const IdentityServerInformation& pod); - - template <> struct FromJsonObject + template <> struct JsonObjectConverter { - IdentityServerInformation operator()(const QJsonObject& jo) const; + static void dumpTo(QJsonObject& jo, const IdentityServerInformation& pod); + static void fillFrom(const QJsonObject& jo, IdentityServerInformation& pod); }; } // namespace QMatrixClient diff --git a/lib/csapi/device_management.cpp b/lib/csapi/device_management.cpp index 861e19949..9c31db5de 100644 --- a/lib/csapi/device_management.cpp +++ b/lib/csapi/device_management.cpp @@ -43,7 +43,7 @@ const QVector& GetDevicesJob::devices() const BaseJob::Status GetDevicesJob::parseJson(const QJsonDocument& data) { auto json = data.object(); - d->devices = fromJson>(json.value("devices"_ls)); + fromJson(json.value("devices"_ls), d->devices); return Success; } @@ -77,7 +77,7 @@ const Device& GetDeviceJob::data() const BaseJob::Status GetDeviceJob::parseJson(const QJsonDocument& data) { - d->data = fromJson(data); + fromJson(data, d->data); return Success; } diff --git a/lib/csapi/directory.cpp b/lib/csapi/directory.cpp index 5353f3bca..4af86f7be 100644 --- a/lib/csapi/directory.cpp +++ b/lib/csapi/directory.cpp @@ -60,8 +60,8 @@ const QStringList& GetRoomIdByAliasJob::servers() const BaseJob::Status GetRoomIdByAliasJob::parseJson(const QJsonDocument& data) { auto json = data.object(); - d->roomId = fromJson(json.value("room_id"_ls)); - d->servers = fromJson(json.value("servers"_ls)); + fromJson(json.value("room_id"_ls), d->roomId); + fromJson(json.value("servers"_ls), d->servers); return Success; } diff --git a/lib/csapi/event_context.cpp b/lib/csapi/event_context.cpp index 806c1613a..bb1f5301c 100644 --- a/lib/csapi/event_context.cpp +++ b/lib/csapi/event_context.cpp @@ -82,12 +82,12 @@ StateEvents&& GetEventContextJob::state() BaseJob::Status GetEventContextJob::parseJson(const QJsonDocument& data) { auto json = data.object(); - d->begin = fromJson(json.value("start"_ls)); - d->end = fromJson(json.value("end"_ls)); - d->eventsBefore = fromJson(json.value("events_before"_ls)); - d->event = fromJson(json.value("event"_ls)); - d->eventsAfter = fromJson(json.value("events_after"_ls)); - d->state = fromJson(json.value("state"_ls)); + fromJson(json.value("start"_ls), d->begin); + fromJson(json.value("end"_ls), d->end); + fromJson(json.value("events_before"_ls), d->eventsBefore); + fromJson(json.value("event"_ls), d->event); + fromJson(json.value("events_after"_ls), d->eventsAfter); + fromJson(json.value("state"_ls), d->state); return Success; } diff --git a/lib/csapi/filter.cpp b/lib/csapi/filter.cpp index 77dc9b92d..982e60b57 100644 --- a/lib/csapi/filter.cpp +++ b/lib/csapi/filter.cpp @@ -41,7 +41,7 @@ BaseJob::Status DefineFilterJob::parseJson(const QJsonDocument& data) if (!json.contains("filter_id"_ls)) return { JsonParseError, "The key 'filter_id' not found in the response" }; - d->filterId = fromJson(json.value("filter_id"_ls)); + fromJson(json.value("filter_id"_ls), d->filterId); return Success; } @@ -75,7 +75,7 @@ const Filter& GetFilterJob::data() const BaseJob::Status GetFilterJob::parseJson(const QJsonDocument& data) { - d->data = fromJson(data); + fromJson(data, d->data); return Success; } diff --git a/lib/csapi/joining.cpp b/lib/csapi/joining.cpp index 717811542..00d930fa7 100644 --- a/lib/csapi/joining.cpp +++ b/lib/csapi/joining.cpp @@ -16,15 +16,16 @@ namespace QMatrixClient { // Converters - QJsonObject toJson(const JoinRoomByIdJob::ThirdPartySigned& pod) + template <> struct JsonObjectConverter { - QJsonObject jo; - addParam<>(jo, QStringLiteral("sender"), pod.sender); - addParam<>(jo, QStringLiteral("mxid"), pod.mxid); - addParam<>(jo, QStringLiteral("token"), pod.token); - addParam<>(jo, QStringLiteral("signatures"), pod.signatures); - return jo; - } + static void dumpTo(QJsonObject& jo, const JoinRoomByIdJob::ThirdPartySigned& pod) + { + addParam<>(jo, QStringLiteral("sender"), pod.sender); + addParam<>(jo, QStringLiteral("mxid"), pod.mxid); + addParam<>(jo, QStringLiteral("token"), pod.token); + addParam<>(jo, QStringLiteral("signatures"), pod.signatures); + } + }; } // namespace QMatrixClient class JoinRoomByIdJob::Private @@ -58,7 +59,7 @@ BaseJob::Status JoinRoomByIdJob::parseJson(const QJsonDocument& data) if (!json.contains("room_id"_ls)) return { JsonParseError, "The key 'room_id' not found in the response" }; - d->roomId = fromJson(json.value("room_id"_ls)); + fromJson(json.value("room_id"_ls), d->roomId); return Success; } @@ -66,22 +67,24 @@ namespace QMatrixClient { // Converters - QJsonObject toJson(const JoinRoomJob::Signed& pod) + template <> struct JsonObjectConverter { - QJsonObject jo; - addParam<>(jo, QStringLiteral("sender"), pod.sender); - addParam<>(jo, QStringLiteral("mxid"), pod.mxid); - addParam<>(jo, QStringLiteral("token"), pod.token); - addParam<>(jo, QStringLiteral("signatures"), pod.signatures); - return jo; - } - - QJsonObject toJson(const JoinRoomJob::ThirdPartySigned& pod) + static void dumpTo(QJsonObject& jo, const JoinRoomJob::Signed& pod) + { + addParam<>(jo, QStringLiteral("sender"), pod.sender); + addParam<>(jo, QStringLiteral("mxid"), pod.mxid); + addParam<>(jo, QStringLiteral("token"), pod.token); + addParam<>(jo, QStringLiteral("signatures"), pod.signatures); + } + }; + + template <> struct JsonObjectConverter { - QJsonObject jo; - addParam<>(jo, QStringLiteral("signed"), pod.signedData); - return jo; - } + static void dumpTo(QJsonObject& jo, const JoinRoomJob::ThirdPartySigned& pod) + { + addParam<>(jo, QStringLiteral("signed"), pod.signedData); + } + }; } // namespace QMatrixClient class JoinRoomJob::Private @@ -123,7 +126,7 @@ BaseJob::Status JoinRoomJob::parseJson(const QJsonDocument& data) if (!json.contains("room_id"_ls)) return { JsonParseError, "The key 'room_id' not found in the response" }; - d->roomId = fromJson(json.value("room_id"_ls)); + fromJson(json.value("room_id"_ls), d->roomId); return Success; } diff --git a/lib/csapi/keys.cpp b/lib/csapi/keys.cpp index c74924116..6c16a8a3f 100644 --- a/lib/csapi/keys.cpp +++ b/lib/csapi/keys.cpp @@ -44,7 +44,7 @@ BaseJob::Status UploadKeysJob::parseJson(const QJsonDocument& data) if (!json.contains("one_time_key_counts"_ls)) return { JsonParseError, "The key 'one_time_key_counts' not found in the response" }; - d->oneTimeKeyCounts = fromJson>(json.value("one_time_key_counts"_ls)); + fromJson(json.value("one_time_key_counts"_ls), d->oneTimeKeyCounts); return Success; } @@ -52,27 +52,20 @@ namespace QMatrixClient { // Converters - template <> struct FromJsonObject + template <> struct JsonObjectConverter { - QueryKeysJob::UnsignedDeviceInfo operator()(const QJsonObject& jo) const + static void fillFrom(const QJsonObject& jo, QueryKeysJob::UnsignedDeviceInfo& result) { - QueryKeysJob::UnsignedDeviceInfo result; - result.deviceDisplayName = - fromJson(jo.value("device_display_name"_ls)); - - return result; + fromJson(jo.value("device_display_name"_ls), result.deviceDisplayName); } }; - template <> struct FromJsonObject + template <> struct JsonObjectConverter { - QueryKeysJob::DeviceInformation operator()(const QJsonObject& jo) const + static void fillFrom(const QJsonObject& jo, QueryKeysJob::DeviceInformation& result) { - QueryKeysJob::DeviceInformation result; - result.unsignedData = - fromJson(jo.value("unsigned"_ls)); - - return result; + fillFromJson(jo, result); + fromJson(jo.value("unsigned"_ls), result.unsignedData); } }; } // namespace QMatrixClient @@ -113,8 +106,8 @@ const QHash>& QueryKeys BaseJob::Status QueryKeysJob::parseJson(const QJsonDocument& data) { auto json = data.object(); - d->failures = fromJson>(json.value("failures"_ls)); - d->deviceKeys = fromJson>>(json.value("device_keys"_ls)); + fromJson(json.value("failures"_ls), d->failures); + fromJson(json.value("device_keys"_ls), d->deviceKeys); return Success; } @@ -153,8 +146,8 @@ const QHash>& ClaimKeysJob::oneTimeKeys() cons BaseJob::Status ClaimKeysJob::parseJson(const QJsonDocument& data) { auto json = data.object(); - d->failures = fromJson>(json.value("failures"_ls)); - d->oneTimeKeys = fromJson>>(json.value("one_time_keys"_ls)); + fromJson(json.value("failures"_ls), d->failures); + fromJson(json.value("one_time_keys"_ls), d->oneTimeKeys); return Success; } @@ -205,8 +198,8 @@ const QStringList& GetKeysChangesJob::left() const BaseJob::Status GetKeysChangesJob::parseJson(const QJsonDocument& data) { auto json = data.object(); - d->changed = fromJson(json.value("changed"_ls)); - d->left = fromJson(json.value("left"_ls)); + fromJson(json.value("changed"_ls), d->changed); + fromJson(json.value("left"_ls), d->left); return Success; } diff --git a/lib/csapi/list_joined_rooms.cpp b/lib/csapi/list_joined_rooms.cpp index a745dba10..85a9cae42 100644 --- a/lib/csapi/list_joined_rooms.cpp +++ b/lib/csapi/list_joined_rooms.cpp @@ -46,7 +46,7 @@ BaseJob::Status GetJoinedRoomsJob::parseJson(const QJsonDocument& data) if (!json.contains("joined_rooms"_ls)) return { JsonParseError, "The key 'joined_rooms' not found in the response" }; - d->joinedRooms = fromJson(json.value("joined_rooms"_ls)); + fromJson(json.value("joined_rooms"_ls), d->joinedRooms); return Success; } diff --git a/lib/csapi/list_public_rooms.cpp b/lib/csapi/list_public_rooms.cpp index 2fdb2005d..2a0cb0ffc 100644 --- a/lib/csapi/list_public_rooms.cpp +++ b/lib/csapi/list_public_rooms.cpp @@ -43,7 +43,7 @@ const QString& GetRoomVisibilityOnDirectoryJob::visibility() const BaseJob::Status GetRoomVisibilityOnDirectoryJob::parseJson(const QJsonDocument& data) { auto json = data.object(); - d->visibility = fromJson(json.value("visibility"_ls)); + fromJson(json.value("visibility"_ls), d->visibility); return Success; } @@ -100,7 +100,7 @@ const PublicRoomsResponse& GetPublicRoomsJob::data() const BaseJob::Status GetPublicRoomsJob::parseJson(const QJsonDocument& data) { - d->data = fromJson(data); + fromJson(data, d->data); return Success; } @@ -108,12 +108,13 @@ namespace QMatrixClient { // Converters - QJsonObject toJson(const QueryPublicRoomsJob::Filter& pod) + template <> struct JsonObjectConverter { - QJsonObject jo; - addParam(jo, QStringLiteral("generic_search_term"), pod.genericSearchTerm); - return jo; - } + static void dumpTo(QJsonObject& jo, const QueryPublicRoomsJob::Filter& pod) + { + addParam(jo, QStringLiteral("generic_search_term"), pod.genericSearchTerm); + } + }; } // namespace QMatrixClient class QueryPublicRoomsJob::Private @@ -155,7 +156,7 @@ const PublicRoomsResponse& QueryPublicRoomsJob::data() const BaseJob::Status QueryPublicRoomsJob::parseJson(const QJsonDocument& data) { - d->data = fromJson(data); + fromJson(data, d->data); return Success; } diff --git a/lib/csapi/login.cpp b/lib/csapi/login.cpp index 4d15a30b4..ee33dac28 100644 --- a/lib/csapi/login.cpp +++ b/lib/csapi/login.cpp @@ -16,15 +16,11 @@ namespace QMatrixClient { // Converters - template <> struct FromJsonObject + template <> struct JsonObjectConverter { - GetLoginFlowsJob::LoginFlow operator()(const QJsonObject& jo) const + static void fillFrom(const QJsonObject& jo, GetLoginFlowsJob::LoginFlow& result) { - GetLoginFlowsJob::LoginFlow result; - result.type = - fromJson(jo.value("type"_ls)); - - return result; + fromJson(jo.value("type"_ls), result.type); } }; } // namespace QMatrixClient @@ -60,7 +56,7 @@ const QVector& GetLoginFlowsJob::flows() const BaseJob::Status GetLoginFlowsJob::parseJson(const QJsonDocument& data) { auto json = data.object(); - d->flows = fromJson>(json.value("flows"_ls)); + fromJson(json.value("flows"_ls), d->flows); return Success; } @@ -118,10 +114,10 @@ const QString& LoginJob::deviceId() const BaseJob::Status LoginJob::parseJson(const QJsonDocument& data) { auto json = data.object(); - d->userId = fromJson(json.value("user_id"_ls)); - d->accessToken = fromJson(json.value("access_token"_ls)); - d->homeServer = fromJson(json.value("home_server"_ls)); - d->deviceId = fromJson(json.value("device_id"_ls)); + fromJson(json.value("user_id"_ls), d->userId); + fromJson(json.value("access_token"_ls), d->accessToken); + fromJson(json.value("home_server"_ls), d->homeServer); + fromJson(json.value("device_id"_ls), d->deviceId); return Success; } diff --git a/lib/csapi/message_pagination.cpp b/lib/csapi/message_pagination.cpp index c59a51ab7..9aca7ec91 100644 --- a/lib/csapi/message_pagination.cpp +++ b/lib/csapi/message_pagination.cpp @@ -68,9 +68,9 @@ RoomEvents&& GetRoomEventsJob::chunk() BaseJob::Status GetRoomEventsJob::parseJson(const QJsonDocument& data) { auto json = data.object(); - d->begin = fromJson(json.value("start"_ls)); - d->end = fromJson(json.value("end"_ls)); - d->chunk = fromJson(json.value("chunk"_ls)); + fromJson(json.value("start"_ls), d->begin); + fromJson(json.value("end"_ls), d->end); + fromJson(json.value("chunk"_ls), d->chunk); return Success; } diff --git a/lib/csapi/notifications.cpp b/lib/csapi/notifications.cpp index 785a0a8a3..c00b7cb05 100644 --- a/lib/csapi/notifications.cpp +++ b/lib/csapi/notifications.cpp @@ -16,25 +16,16 @@ namespace QMatrixClient { // Converters - template <> struct FromJsonObject + template <> struct JsonObjectConverter { - GetNotificationsJob::Notification operator()(const QJsonObject& jo) const + static void fillFrom(const QJsonObject& jo, GetNotificationsJob::Notification& result) { - GetNotificationsJob::Notification result; - result.actions = - fromJson>(jo.value("actions"_ls)); - result.event = - fromJson(jo.value("event"_ls)); - result.profileTag = - fromJson(jo.value("profile_tag"_ls)); - result.read = - fromJson(jo.value("read"_ls)); - result.roomId = - fromJson(jo.value("room_id"_ls)); - result.ts = - fromJson(jo.value("ts"_ls)); - - return result; + fromJson(jo.value("actions"_ls), result.actions); + fromJson(jo.value("event"_ls), result.event); + fromJson(jo.value("profile_tag"_ls), result.profileTag); + fromJson(jo.value("read"_ls), result.read); + fromJson(jo.value("room_id"_ls), result.roomId); + fromJson(jo.value("ts"_ls), result.ts); } }; } // namespace QMatrixClient @@ -87,11 +78,11 @@ std::vector&& GetNotificationsJob::notificati BaseJob::Status GetNotificationsJob::parseJson(const QJsonDocument& data) { auto json = data.object(); - d->nextToken = fromJson(json.value("next_token"_ls)); + fromJson(json.value("next_token"_ls), d->nextToken); if (!json.contains("notifications"_ls)) return { JsonParseError, "The key 'notifications' not found in the response" }; - d->notifications = fromJson>(json.value("notifications"_ls)); + fromJson(json.value("notifications"_ls), d->notifications); return Success; } diff --git a/lib/csapi/openid.cpp b/lib/csapi/openid.cpp index 2547f0c87..b27fe0b8b 100644 --- a/lib/csapi/openid.cpp +++ b/lib/csapi/openid.cpp @@ -59,19 +59,19 @@ BaseJob::Status RequestOpenIdTokenJob::parseJson(const QJsonDocument& data) if (!json.contains("access_token"_ls)) return { JsonParseError, "The key 'access_token' not found in the response" }; - d->accessToken = fromJson(json.value("access_token"_ls)); + fromJson(json.value("access_token"_ls), d->accessToken); if (!json.contains("token_type"_ls)) return { JsonParseError, "The key 'token_type' not found in the response" }; - d->tokenType = fromJson(json.value("token_type"_ls)); + fromJson(json.value("token_type"_ls), d->tokenType); if (!json.contains("matrix_server_name"_ls)) return { JsonParseError, "The key 'matrix_server_name' not found in the response" }; - d->matrixServerName = fromJson(json.value("matrix_server_name"_ls)); + fromJson(json.value("matrix_server_name"_ls), d->matrixServerName); if (!json.contains("expires_in"_ls)) return { JsonParseError, "The key 'expires_in' not found in the response" }; - d->expiresIn = fromJson(json.value("expires_in"_ls)); + fromJson(json.value("expires_in"_ls), d->expiresIn); return Success; } diff --git a/lib/csapi/peeking_events.cpp b/lib/csapi/peeking_events.cpp index e046a62ed..3208d48d1 100644 --- a/lib/csapi/peeking_events.cpp +++ b/lib/csapi/peeking_events.cpp @@ -66,9 +66,9 @@ RoomEvents&& PeekEventsJob::chunk() BaseJob::Status PeekEventsJob::parseJson(const QJsonDocument& data) { auto json = data.object(); - d->begin = fromJson(json.value("start"_ls)); - d->end = fromJson(json.value("end"_ls)); - d->chunk = fromJson(json.value("chunk"_ls)); + fromJson(json.value("start"_ls), d->begin); + fromJson(json.value("end"_ls), d->end); + fromJson(json.value("chunk"_ls), d->chunk); return Success; } diff --git a/lib/csapi/presence.cpp b/lib/csapi/presence.cpp index 7aba8b61d..94a3e6c55 100644 --- a/lib/csapi/presence.cpp +++ b/lib/csapi/presence.cpp @@ -76,10 +76,10 @@ BaseJob::Status GetPresenceJob::parseJson(const QJsonDocument& data) if (!json.contains("presence"_ls)) return { JsonParseError, "The key 'presence' not found in the response" }; - d->presence = fromJson(json.value("presence"_ls)); - d->lastActiveAgo = fromJson(json.value("last_active_ago"_ls)); - d->statusMsg = fromJson(json.value("status_msg"_ls)); - d->currentlyActive = fromJson(json.value("currently_active"_ls)); + fromJson(json.value("presence"_ls), d->presence); + fromJson(json.value("last_active_ago"_ls), d->lastActiveAgo); + fromJson(json.value("status_msg"_ls), d->statusMsg); + fromJson(json.value("currently_active"_ls), d->currentlyActive); return Success; } @@ -125,7 +125,7 @@ Events&& GetPresenceForListJob::data() BaseJob::Status GetPresenceForListJob::parseJson(const QJsonDocument& data) { - d->data = fromJson(data); + fromJson(data, d->data); return Success; } diff --git a/lib/csapi/profile.cpp b/lib/csapi/profile.cpp index bb0530625..4ed3ad9bc 100644 --- a/lib/csapi/profile.cpp +++ b/lib/csapi/profile.cpp @@ -54,7 +54,7 @@ const QString& GetDisplayNameJob::displayname() const BaseJob::Status GetDisplayNameJob::parseJson(const QJsonDocument& data) { auto json = data.object(); - d->displayname = fromJson(json.value("displayname"_ls)); + fromJson(json.value("displayname"_ls), d->displayname); return Success; } @@ -100,7 +100,7 @@ const QString& GetAvatarUrlJob::avatarUrl() const BaseJob::Status GetAvatarUrlJob::parseJson(const QJsonDocument& data) { auto json = data.object(); - d->avatarUrl = fromJson(json.value("avatar_url"_ls)); + fromJson(json.value("avatar_url"_ls), d->avatarUrl); return Success; } @@ -141,8 +141,8 @@ const QString& GetUserProfileJob::displayname() const BaseJob::Status GetUserProfileJob::parseJson(const QJsonDocument& data) { auto json = data.object(); - d->avatarUrl = fromJson(json.value("avatar_url"_ls)); - d->displayname = fromJson(json.value("displayname"_ls)); + fromJson(json.value("avatar_url"_ls), d->avatarUrl); + fromJson(json.value("displayname"_ls), d->displayname); return Success; } diff --git a/lib/csapi/pusher.cpp b/lib/csapi/pusher.cpp index d20db88ad..3ad0dcbed 100644 --- a/lib/csapi/pusher.cpp +++ b/lib/csapi/pusher.cpp @@ -16,43 +16,27 @@ namespace QMatrixClient { // Converters - template <> struct FromJsonObject + template <> struct JsonObjectConverter { - GetPushersJob::PusherData operator()(const QJsonObject& jo) const + static void fillFrom(const QJsonObject& jo, GetPushersJob::PusherData& result) { - GetPushersJob::PusherData result; - result.url = - fromJson(jo.value("url"_ls)); - result.format = - fromJson(jo.value("format"_ls)); - - return result; + fromJson(jo.value("url"_ls), result.url); + fromJson(jo.value("format"_ls), result.format); } }; - template <> struct FromJsonObject + template <> struct JsonObjectConverter { - GetPushersJob::Pusher operator()(const QJsonObject& jo) const + static void fillFrom(const QJsonObject& jo, GetPushersJob::Pusher& result) { - GetPushersJob::Pusher result; - result.pushkey = - fromJson(jo.value("pushkey"_ls)); - result.kind = - fromJson(jo.value("kind"_ls)); - result.appId = - fromJson(jo.value("app_id"_ls)); - result.appDisplayName = - fromJson(jo.value("app_display_name"_ls)); - result.deviceDisplayName = - fromJson(jo.value("device_display_name"_ls)); - result.profileTag = - fromJson(jo.value("profile_tag"_ls)); - result.lang = - fromJson(jo.value("lang"_ls)); - result.data = - fromJson(jo.value("data"_ls)); - - return result; + fromJson(jo.value("pushkey"_ls), result.pushkey); + fromJson(jo.value("kind"_ls), result.kind); + fromJson(jo.value("app_id"_ls), result.appId); + fromJson(jo.value("app_display_name"_ls), result.appDisplayName); + fromJson(jo.value("device_display_name"_ls), result.deviceDisplayName); + fromJson(jo.value("profile_tag"_ls), result.profileTag); + fromJson(jo.value("lang"_ls), result.lang); + fromJson(jo.value("data"_ls), result.data); } }; } // namespace QMatrixClient @@ -88,7 +72,7 @@ const QVector& GetPushersJob::pushers() const BaseJob::Status GetPushersJob::parseJson(const QJsonDocument& data) { auto json = data.object(); - d->pushers = fromJson>(json.value("pushers"_ls)); + fromJson(json.value("pushers"_ls), d->pushers); return Success; } @@ -96,13 +80,14 @@ namespace QMatrixClient { // Converters - QJsonObject toJson(const PostPusherJob::PusherData& pod) + template <> struct JsonObjectConverter { - QJsonObject jo; - addParam(jo, QStringLiteral("url"), pod.url); - addParam(jo, QStringLiteral("format"), pod.format); - return jo; - } + static void dumpTo(QJsonObject& jo, const PostPusherJob::PusherData& pod) + { + addParam(jo, QStringLiteral("url"), pod.url); + addParam(jo, QStringLiteral("format"), pod.format); + } + }; } // namespace QMatrixClient static const auto PostPusherJobName = QStringLiteral("PostPusherJob"); diff --git a/lib/csapi/pushrules.cpp b/lib/csapi/pushrules.cpp index ea8ad02a1..b91d18f76 100644 --- a/lib/csapi/pushrules.cpp +++ b/lib/csapi/pushrules.cpp @@ -46,7 +46,7 @@ BaseJob::Status GetPushRulesJob::parseJson(const QJsonDocument& data) if (!json.contains("global"_ls)) return { JsonParseError, "The key 'global' not found in the response" }; - d->global = fromJson(json.value("global"_ls)); + fromJson(json.value("global"_ls), d->global); return Success; } @@ -80,7 +80,7 @@ const PushRule& GetPushRuleJob::data() const BaseJob::Status GetPushRuleJob::parseJson(const QJsonDocument& data) { - d->data = fromJson(data); + fromJson(data, d->data); return Success; } @@ -154,7 +154,7 @@ BaseJob::Status IsPushRuleEnabledJob::parseJson(const QJsonDocument& data) if (!json.contains("enabled"_ls)) return { JsonParseError, "The key 'enabled' not found in the response" }; - d->enabled = fromJson(json.value("enabled"_ls)); + fromJson(json.value("enabled"_ls), d->enabled); return Success; } @@ -203,7 +203,7 @@ BaseJob::Status GetPushRuleActionsJob::parseJson(const QJsonDocument& data) if (!json.contains("actions"_ls)) return { JsonParseError, "The key 'actions' not found in the response" }; - d->actions = fromJson(json.value("actions"_ls)); + fromJson(json.value("actions"_ls), d->actions); return Success; } diff --git a/lib/csapi/redaction.cpp b/lib/csapi/redaction.cpp index 640986706..1d54e36d3 100644 --- a/lib/csapi/redaction.cpp +++ b/lib/csapi/redaction.cpp @@ -40,7 +40,7 @@ const QString& RedactEventJob::eventId() const BaseJob::Status RedactEventJob::parseJson(const QJsonDocument& data) { auto json = data.object(); - d->eventId = fromJson(json.value("event_id"_ls)); + fromJson(json.value("event_id"_ls), d->eventId); return Success; } diff --git a/lib/csapi/registration.cpp b/lib/csapi/registration.cpp index 320ec7969..2772cd57e 100644 --- a/lib/csapi/registration.cpp +++ b/lib/csapi/registration.cpp @@ -76,10 +76,10 @@ BaseJob::Status RegisterJob::parseJson(const QJsonDocument& data) if (!json.contains("user_id"_ls)) return { JsonParseError, "The key 'user_id' not found in the response" }; - d->userId = fromJson(json.value("user_id"_ls)); - d->accessToken = fromJson(json.value("access_token"_ls)); - d->homeServer = fromJson(json.value("home_server"_ls)); - d->deviceId = fromJson(json.value("device_id"_ls)); + fromJson(json.value("user_id"_ls), d->userId); + fromJson(json.value("access_token"_ls), d->accessToken); + fromJson(json.value("home_server"_ls), d->homeServer); + fromJson(json.value("device_id"_ls), d->deviceId); return Success; } @@ -114,7 +114,7 @@ const Sid& RequestTokenToRegisterEmailJob::data() const BaseJob::Status RequestTokenToRegisterEmailJob::parseJson(const QJsonDocument& data) { - d->data = fromJson(data); + fromJson(data, d->data); return Success; } @@ -150,7 +150,7 @@ const Sid& RequestTokenToRegisterMSISDNJob::data() const BaseJob::Status RequestTokenToRegisterMSISDNJob::parseJson(const QJsonDocument& data) { - d->data = fromJson(data); + fromJson(data, d->data); return Success; } @@ -197,7 +197,7 @@ const Sid& RequestTokenToResetPasswordEmailJob::data() const BaseJob::Status RequestTokenToResetPasswordEmailJob::parseJson(const QJsonDocument& data) { - d->data = fromJson(data); + fromJson(data, d->data); return Success; } @@ -233,7 +233,7 @@ const Sid& RequestTokenToResetPasswordMSISDNJob::data() const BaseJob::Status RequestTokenToResetPasswordMSISDNJob::parseJson(const QJsonDocument& data) { - d->data = fromJson(data); + fromJson(data, d->data); return Success; } @@ -289,7 +289,7 @@ bool CheckUsernameAvailabilityJob::available() const BaseJob::Status CheckUsernameAvailabilityJob::parseJson(const QJsonDocument& data) { auto json = data.object(); - d->available = fromJson(json.value("available"_ls)); + fromJson(json.value("available"_ls), d->available); return Success; } diff --git a/lib/csapi/room_send.cpp b/lib/csapi/room_send.cpp index 2b39ede21..0d25eb69c 100644 --- a/lib/csapi/room_send.cpp +++ b/lib/csapi/room_send.cpp @@ -38,7 +38,7 @@ const QString& SendMessageJob::eventId() const BaseJob::Status SendMessageJob::parseJson(const QJsonDocument& data) { auto json = data.object(); - d->eventId = fromJson(json.value("event_id"_ls)); + fromJson(json.value("event_id"_ls), d->eventId); return Success; } diff --git a/lib/csapi/room_state.cpp b/lib/csapi/room_state.cpp index 8f87979d9..3aa7d7361 100644 --- a/lib/csapi/room_state.cpp +++ b/lib/csapi/room_state.cpp @@ -38,7 +38,7 @@ const QString& SetRoomStateWithKeyJob::eventId() const BaseJob::Status SetRoomStateWithKeyJob::parseJson(const QJsonDocument& data) { auto json = data.object(); - d->eventId = fromJson(json.value("event_id"_ls)); + fromJson(json.value("event_id"_ls), d->eventId); return Success; } @@ -68,7 +68,7 @@ const QString& SetRoomStateJob::eventId() const BaseJob::Status SetRoomStateJob::parseJson(const QJsonDocument& data) { auto json = data.object(); - d->eventId = fromJson(json.value("event_id"_ls)); + fromJson(json.value("event_id"_ls), d->eventId); return Success; } diff --git a/lib/csapi/rooms.cpp b/lib/csapi/rooms.cpp index cebb295a4..0b08ccecb 100644 --- a/lib/csapi/rooms.cpp +++ b/lib/csapi/rooms.cpp @@ -42,7 +42,7 @@ EventPtr&& GetOneRoomEventJob::data() BaseJob::Status GetOneRoomEventJob::parseJson(const QJsonDocument& data) { - d->data = fromJson(data); + fromJson(data, d->data); return Success; } @@ -104,7 +104,7 @@ StateEvents&& GetRoomStateJob::data() BaseJob::Status GetRoomStateJob::parseJson(const QJsonDocument& data) { - d->data = fromJson(data); + fromJson(data, d->data); return Success; } @@ -114,17 +114,28 @@ class GetMembersByRoomJob::Private EventsArray chunk; }; -QUrl GetMembersByRoomJob::makeRequestUrl(QUrl baseUrl, const QString& roomId) +BaseJob::Query queryToGetMembersByRoom(const QString& at, const QString& membership, const QString& notMembership) +{ + BaseJob::Query _q; + addParam(_q, QStringLiteral("at"), at); + addParam(_q, QStringLiteral("membership"), membership); + addParam(_q, QStringLiteral("not_membership"), notMembership); + return _q; +} + +QUrl GetMembersByRoomJob::makeRequestUrl(QUrl baseUrl, const QString& roomId, const QString& at, const QString& membership, const QString& notMembership) { return BaseJob::makeRequestUrl(std::move(baseUrl), - basePath % "/rooms/" % roomId % "/members"); + basePath % "/rooms/" % roomId % "/members", + queryToGetMembersByRoom(at, membership, notMembership)); } static const auto GetMembersByRoomJobName = QStringLiteral("GetMembersByRoomJob"); -GetMembersByRoomJob::GetMembersByRoomJob(const QString& roomId) +GetMembersByRoomJob::GetMembersByRoomJob(const QString& roomId, const QString& at, const QString& membership, const QString& notMembership) : BaseJob(HttpVerb::Get, GetMembersByRoomJobName, - basePath % "/rooms/" % roomId % "/members") + basePath % "/rooms/" % roomId % "/members", + queryToGetMembersByRoom(at, membership, notMembership)) , d(new Private) { } @@ -139,7 +150,7 @@ EventsArray&& GetMembersByRoomJob::chunk() BaseJob::Status GetMembersByRoomJob::parseJson(const QJsonDocument& data) { auto json = data.object(); - d->chunk = fromJson>(json.value("chunk"_ls)); + fromJson(json.value("chunk"_ls), d->chunk); return Success; } @@ -147,17 +158,12 @@ namespace QMatrixClient { // Converters - template <> struct FromJsonObject + template <> struct JsonObjectConverter { - GetJoinedMembersByRoomJob::RoomMember operator()(const QJsonObject& jo) const + static void fillFrom(const QJsonObject& jo, GetJoinedMembersByRoomJob::RoomMember& result) { - GetJoinedMembersByRoomJob::RoomMember result; - result.displayName = - fromJson(jo.value("display_name"_ls)); - result.avatarUrl = - fromJson(jo.value("avatar_url"_ls)); - - return result; + fromJson(jo.value("display_name"_ls), result.displayName); + fromJson(jo.value("avatar_url"_ls), result.avatarUrl); } }; } // namespace QMatrixClient @@ -193,7 +199,7 @@ const QHash& GetJoinedMembersByR BaseJob::Status GetJoinedMembersByRoomJob::parseJson(const QJsonDocument& data) { auto json = data.object(); - d->joined = fromJson>(json.value("joined"_ls)); + fromJson(json.value("joined"_ls), d->joined); return Success; } diff --git a/lib/csapi/rooms.h b/lib/csapi/rooms.h index 80895b4ec..415aa4ed8 100644 --- a/lib/csapi/rooms.h +++ b/lib/csapi/rooms.h @@ -158,8 +158,17 @@ namespace QMatrixClient /*! Get the m.room.member events for the room. * \param roomId * The room to get the member events for. + * \param at + * The token defining the timeline position as-of which to return + * the list of members. This token can be obtained from + * a ``prev_batch`` token returned for each room by the sync API, or + * from a ``start`` or ``end`` token returned by a /messages request. + * \param membership + * Only return users with the specified membership + * \param notMembership + * Only return users with membership state other than specified */ - explicit GetMembersByRoomJob(const QString& roomId); + explicit GetMembersByRoomJob(const QString& roomId, const QString& at = {}, const QString& membership = {}, const QString& notMembership = {}); /*! Construct a URL without creating a full-fledged job object * @@ -167,7 +176,7 @@ namespace QMatrixClient * GetMembersByRoomJob is necessary but the job * itself isn't. */ - static QUrl makeRequestUrl(QUrl baseUrl, const QString& roomId); + static QUrl makeRequestUrl(QUrl baseUrl, const QString& roomId, const QString& at = {}, const QString& membership = {}, const QString& notMembership = {}); ~GetMembersByRoomJob() override; diff --git a/lib/csapi/search.cpp b/lib/csapi/search.cpp index 9436eb473..a5f83c791 100644 --- a/lib/csapi/search.cpp +++ b/lib/csapi/search.cpp @@ -16,146 +16,113 @@ namespace QMatrixClient { // Converters - QJsonObject toJson(const SearchJob::IncludeEventContext& pod) + template <> struct JsonObjectConverter { - QJsonObject jo; - addParam(jo, QStringLiteral("before_limit"), pod.beforeLimit); - addParam(jo, QStringLiteral("after_limit"), pod.afterLimit); - addParam(jo, QStringLiteral("include_profile"), pod.includeProfile); - return jo; - } - - QJsonObject toJson(const SearchJob::Group& pod) - { - QJsonObject jo; - addParam(jo, QStringLiteral("key"), pod.key); - return jo; - } + static void dumpTo(QJsonObject& jo, const SearchJob::IncludeEventContext& pod) + { + addParam(jo, QStringLiteral("before_limit"), pod.beforeLimit); + addParam(jo, QStringLiteral("after_limit"), pod.afterLimit); + addParam(jo, QStringLiteral("include_profile"), pod.includeProfile); + } + }; - QJsonObject toJson(const SearchJob::Groupings& pod) + template <> struct JsonObjectConverter { - QJsonObject jo; - addParam(jo, QStringLiteral("group_by"), pod.groupBy); - return jo; - } + static void dumpTo(QJsonObject& jo, const SearchJob::Group& pod) + { + addParam(jo, QStringLiteral("key"), pod.key); + } + }; - QJsonObject toJson(const SearchJob::RoomEventsCriteria& pod) - { - QJsonObject jo; - addParam<>(jo, QStringLiteral("search_term"), pod.searchTerm); - addParam(jo, QStringLiteral("keys"), pod.keys); - addParam(jo, QStringLiteral("filter"), pod.filter); - addParam(jo, QStringLiteral("order_by"), pod.orderBy); - addParam(jo, QStringLiteral("event_context"), pod.eventContext); - addParam(jo, QStringLiteral("include_state"), pod.includeState); - addParam(jo, QStringLiteral("groupings"), pod.groupings); - return jo; - } - - QJsonObject toJson(const SearchJob::Categories& pod) + template <> struct JsonObjectConverter { - QJsonObject jo; - addParam(jo, QStringLiteral("room_events"), pod.roomEvents); - return jo; - } + static void dumpTo(QJsonObject& jo, const SearchJob::Groupings& pod) + { + addParam(jo, QStringLiteral("group_by"), pod.groupBy); + } + }; - template <> struct FromJsonObject + template <> struct JsonObjectConverter { - SearchJob::UserProfile operator()(const QJsonObject& jo) const + static void dumpTo(QJsonObject& jo, const SearchJob::RoomEventsCriteria& pod) { - SearchJob::UserProfile result; - result.displayname = - fromJson(jo.value("displayname"_ls)); - result.avatarUrl = - fromJson(jo.value("avatar_url"_ls)); + addParam<>(jo, QStringLiteral("search_term"), pod.searchTerm); + addParam(jo, QStringLiteral("keys"), pod.keys); + addParam(jo, QStringLiteral("filter"), pod.filter); + addParam(jo, QStringLiteral("order_by"), pod.orderBy); + addParam(jo, QStringLiteral("event_context"), pod.eventContext); + addParam(jo, QStringLiteral("include_state"), pod.includeState); + addParam(jo, QStringLiteral("groupings"), pod.groupings); + } + }; - return result; + template <> struct JsonObjectConverter + { + static void dumpTo(QJsonObject& jo, const SearchJob::Categories& pod) + { + addParam(jo, QStringLiteral("room_events"), pod.roomEvents); } }; - template <> struct FromJsonObject + template <> struct JsonObjectConverter { - SearchJob::EventContext operator()(const QJsonObject& jo) const + static void fillFrom(const QJsonObject& jo, SearchJob::UserProfile& result) { - SearchJob::EventContext result; - result.begin = - fromJson(jo.value("start"_ls)); - result.end = - fromJson(jo.value("end"_ls)); - result.profileInfo = - fromJson>(jo.value("profile_info"_ls)); - result.eventsBefore = - fromJson(jo.value("events_before"_ls)); - result.eventsAfter = - fromJson(jo.value("events_after"_ls)); - - return result; + fromJson(jo.value("displayname"_ls), result.displayname); + fromJson(jo.value("avatar_url"_ls), result.avatarUrl); } }; - template <> struct FromJsonObject + template <> struct JsonObjectConverter { - SearchJob::Result operator()(const QJsonObject& jo) const + static void fillFrom(const QJsonObject& jo, SearchJob::EventContext& result) { - SearchJob::Result result; - result.rank = - fromJson(jo.value("rank"_ls)); - result.result = - fromJson(jo.value("result"_ls)); - result.context = - fromJson(jo.value("context"_ls)); - - return result; + fromJson(jo.value("start"_ls), result.begin); + fromJson(jo.value("end"_ls), result.end); + fromJson(jo.value("profile_info"_ls), result.profileInfo); + fromJson(jo.value("events_before"_ls), result.eventsBefore); + fromJson(jo.value("events_after"_ls), result.eventsAfter); } }; - template <> struct FromJsonObject + template <> struct JsonObjectConverter { - SearchJob::GroupValue operator()(const QJsonObject& jo) const + static void fillFrom(const QJsonObject& jo, SearchJob::Result& result) { - SearchJob::GroupValue result; - result.nextBatch = - fromJson(jo.value("next_batch"_ls)); - result.order = - fromJson(jo.value("order"_ls)); - result.results = - fromJson(jo.value("results"_ls)); - - return result; + fromJson(jo.value("rank"_ls), result.rank); + fromJson(jo.value("result"_ls), result.result); + fromJson(jo.value("context"_ls), result.context); } }; - template <> struct FromJsonObject + template <> struct JsonObjectConverter { - SearchJob::ResultRoomEvents operator()(const QJsonObject& jo) const + static void fillFrom(const QJsonObject& jo, SearchJob::GroupValue& result) { - SearchJob::ResultRoomEvents result; - result.count = - fromJson(jo.value("count"_ls)); - result.highlights = - fromJson(jo.value("highlights"_ls)); - result.results = - fromJson>(jo.value("results"_ls)); - result.state = - fromJson>(jo.value("state"_ls)); - result.groups = - fromJson>>(jo.value("groups"_ls)); - result.nextBatch = - fromJson(jo.value("next_batch"_ls)); - - return result; + fromJson(jo.value("next_batch"_ls), result.nextBatch); + fromJson(jo.value("order"_ls), result.order); + fromJson(jo.value("results"_ls), result.results); } }; - template <> struct FromJsonObject + template <> struct JsonObjectConverter { - SearchJob::ResultCategories operator()(const QJsonObject& jo) const + static void fillFrom(const QJsonObject& jo, SearchJob::ResultRoomEvents& result) { - SearchJob::ResultCategories result; - result.roomEvents = - fromJson(jo.value("room_events"_ls)); + fromJson(jo.value("count"_ls), result.count); + fromJson(jo.value("highlights"_ls), result.highlights); + fromJson(jo.value("results"_ls), result.results); + fromJson(jo.value("state"_ls), result.state); + fromJson(jo.value("groups"_ls), result.groups); + fromJson(jo.value("next_batch"_ls), result.nextBatch); + } + }; - return result; + template <> struct JsonObjectConverter + { + static void fillFrom(const QJsonObject& jo, SearchJob::ResultCategories& result) + { + fromJson(jo.value("room_events"_ls), result.roomEvents); } }; } // namespace QMatrixClient @@ -199,7 +166,7 @@ BaseJob::Status SearchJob::parseJson(const QJsonDocument& data) if (!json.contains("search_categories"_ls)) return { JsonParseError, "The key 'search_categories' not found in the response" }; - d->searchCategories = fromJson(json.value("search_categories"_ls)); + fromJson(json.value("search_categories"_ls), d->searchCategories); return Success; } diff --git a/lib/csapi/tags.cpp b/lib/csapi/tags.cpp index 808915acf..94026bb9e 100644 --- a/lib/csapi/tags.cpp +++ b/lib/csapi/tags.cpp @@ -16,16 +16,12 @@ namespace QMatrixClient { // Converters - template <> struct FromJsonObject + template <> struct JsonObjectConverter { - GetRoomTagsJob::Tag operator()(QJsonObject jo) const + static void fillFrom(QJsonObject jo, GetRoomTagsJob::Tag& result) { - GetRoomTagsJob::Tag result; - result.order = - fromJson(jo.take("order"_ls)); - - result.additionalProperties = fromJson(jo); - return result; + fromJson(jo.take("order"_ls), result.order); + fromJson(jo, result.additionalProperties); } }; } // namespace QMatrixClient @@ -61,7 +57,7 @@ const QHash& GetRoomTagsJob::tags() const BaseJob::Status GetRoomTagsJob::parseJson(const QJsonDocument& data) { auto json = data.object(); - d->tags = fromJson>(json.value("tags"_ls)); + fromJson(json.value("tags"_ls), d->tags); return Success; } diff --git a/lib/csapi/third_party_lookup.cpp b/lib/csapi/third_party_lookup.cpp index 3ba1a5ad9..12cb7c591 100644 --- a/lib/csapi/third_party_lookup.cpp +++ b/lib/csapi/third_party_lookup.cpp @@ -42,7 +42,7 @@ const QHash& GetProtocolsJob::data() const BaseJob::Status GetProtocolsJob::parseJson(const QJsonDocument& data) { - d->data = fromJson>(data); + fromJson(data, d->data); return Success; } @@ -76,7 +76,7 @@ const ThirdPartyProtocol& GetProtocolMetadataJob::data() const BaseJob::Status GetProtocolMetadataJob::parseJson(const QJsonDocument& data) { - d->data = fromJson(data); + fromJson(data, d->data); return Success; } @@ -119,7 +119,7 @@ const QVector& QueryLocationByProtocolJob::data() const BaseJob::Status QueryLocationByProtocolJob::parseJson(const QJsonDocument& data) { - d->data = fromJson>(data); + fromJson(data, d->data); return Success; } @@ -162,7 +162,7 @@ const QVector& QueryUserByProtocolJob::data() const BaseJob::Status QueryUserByProtocolJob::parseJson(const QJsonDocument& data) { - d->data = fromJson>(data); + fromJson(data, d->data); return Success; } @@ -205,7 +205,7 @@ const QVector& QueryLocationByAliasJob::data() const BaseJob::Status QueryLocationByAliasJob::parseJson(const QJsonDocument& data) { - d->data = fromJson>(data); + fromJson(data, d->data); return Success; } @@ -248,7 +248,7 @@ const QVector& QueryUserByIDJob::data() const BaseJob::Status QueryUserByIDJob::parseJson(const QJsonDocument& data) { - d->data = fromJson>(data); + fromJson(data, d->data); return Success; } diff --git a/lib/csapi/users.cpp b/lib/csapi/users.cpp index deb9cb8a5..97d8962dd 100644 --- a/lib/csapi/users.cpp +++ b/lib/csapi/users.cpp @@ -16,19 +16,13 @@ namespace QMatrixClient { // Converters - template <> struct FromJsonObject + template <> struct JsonObjectConverter { - SearchUserDirectoryJob::User operator()(const QJsonObject& jo) const + static void fillFrom(const QJsonObject& jo, SearchUserDirectoryJob::User& result) { - SearchUserDirectoryJob::User result; - result.userId = - fromJson(jo.value("user_id"_ls)); - result.displayName = - fromJson(jo.value("display_name"_ls)); - result.avatarUrl = - fromJson(jo.value("avatar_url"_ls)); - - return result; + fromJson(jo.value("user_id"_ls), result.userId); + fromJson(jo.value("display_name"_ls), result.displayName); + fromJson(jo.value("avatar_url"_ls), result.avatarUrl); } }; } // namespace QMatrixClient @@ -71,11 +65,11 @@ BaseJob::Status SearchUserDirectoryJob::parseJson(const QJsonDocument& data) if (!json.contains("results"_ls)) return { JsonParseError, "The key 'results' not found in the response" }; - d->results = fromJson>(json.value("results"_ls)); + fromJson(json.value("results"_ls), d->results); if (!json.contains("limited"_ls)) return { JsonParseError, "The key 'limited' not found in the response" }; - d->limited = fromJson(json.value("limited"_ls)); + fromJson(json.value("limited"_ls), d->limited); return Success; } diff --git a/lib/csapi/versions.cpp b/lib/csapi/versions.cpp index 128902e2c..c853ec06b 100644 --- a/lib/csapi/versions.cpp +++ b/lib/csapi/versions.cpp @@ -43,7 +43,7 @@ const QStringList& GetVersionsJob::versions() const BaseJob::Status GetVersionsJob::parseJson(const QJsonDocument& data) { auto json = data.object(); - d->versions = fromJson(json.value("versions"_ls)); + fromJson(json.value("versions"_ls), d->versions); return Success; } diff --git a/lib/csapi/voip.cpp b/lib/csapi/voip.cpp index 0479b645c..e81587237 100644 --- a/lib/csapi/voip.cpp +++ b/lib/csapi/voip.cpp @@ -42,7 +42,7 @@ const QJsonObject& GetTurnServerJob::data() const BaseJob::Status GetTurnServerJob::parseJson(const QJsonDocument& data) { - d->data = fromJson(data); + fromJson(data, d->data); return Success; } diff --git a/lib/csapi/wellknown.cpp b/lib/csapi/wellknown.cpp index d42534a03..975058300 100644 --- a/lib/csapi/wellknown.cpp +++ b/lib/csapi/wellknown.cpp @@ -52,8 +52,8 @@ BaseJob::Status GetWellknownJob::parseJson(const QJsonDocument& data) if (!json.contains("m.homeserver"_ls)) return { JsonParseError, "The key 'm.homeserver' not found in the response" }; - d->homeserver = fromJson(json.value("m.homeserver"_ls)); - d->identityServer = fromJson(json.value("m.identity_server"_ls)); + fromJson(json.value("m.homeserver"_ls), d->homeserver); + fromJson(json.value("m.identity_server"_ls), d->identityServer); return Success; } diff --git a/lib/csapi/whoami.cpp b/lib/csapi/whoami.cpp index cb6439efe..aebdf5d3c 100644 --- a/lib/csapi/whoami.cpp +++ b/lib/csapi/whoami.cpp @@ -46,7 +46,7 @@ BaseJob::Status GetTokenOwnerJob::parseJson(const QJsonDocument& data) if (!json.contains("user_id"_ls)) return { JsonParseError, "The key 'user_id' not found in the response" }; - d->userId = fromJson(json.value("user_id"_ls)); + fromJson(json.value("user_id"_ls), d->userId); return Success; } diff --git a/lib/csapi/{{base}}.cpp.mustache b/lib/csapi/{{base}}.cpp.mustache index 64fd8bf3d..ff888d769 100644 --- a/lib/csapi/{{base}}.cpp.mustache +++ b/lib/csapi/{{base}}.cpp.mustache @@ -8,49 +8,52 @@ {{/operations}} using namespace QMatrixClient; {{#models.model}}{{#in?}} -QJsonObject QMatrixClient::toJson(const {{qualifiedName}}& pod) +void JsonObjectConverter<{{qualifiedName}}>::dumpTo( + QJsonObject& jo, const {{qualifiedName}}& pod) { - QJsonObject jo{{#propertyMap}} = toJson(pod.{{nameCamelCase}}){{/propertyMap}};{{#vars}} - addParam<{{^required?}}IfNotEmpty{{/required?}}>(jo, QStringLiteral("{{baseName}}"), pod.{{nameCamelCase}});{{/vars}} - return jo; -} +{{#propertyMap}} fillJson(jo, pod.{{nameCamelCase}}); +{{/propertyMap}}{{#parents}} fillJson<{{name}}>(jo, pod); +{{/parents}}{{#vars}} addParam<{{^required?}}IfNotEmpty{{/required?}}>(jo, QStringLiteral("{{baseName}}"), pod.{{nameCamelCase}}); +{{/vars}}}{{!<- dumpTo() ends here}} {{/in?}}{{#out?}} -{{qualifiedName}} FromJsonObject<{{qualifiedName}}>::operator()({{^propertyMap}}const QJsonObject&{{/propertyMap}}{{#propertyMap}}QJsonObject{{/propertyMap}} jo) const +void JsonObjectConverter<{{qualifiedName}}>::fillFrom( + {{^propertyMap}}const QJsonObject&{{/propertyMap + }}{{#propertyMap}}QJsonObject{{/propertyMap}} jo, {{qualifiedName}}& result) { - {{qualifiedName}} result; -{{#vars}} result.{{nameCamelCase}} = - fromJson<{{dataType.qualifiedName}}>(jo.{{#propertyMap}}take{{/propertyMap}}{{^propertyMap}}value{{/propertyMap}}("{{baseName}}"_ls)); +{{#parents}} fillFromJson<{{qualifiedName}}>(jo, result); +{{/parents}}{{#vars}} fromJson(jo.{{#propertyMap}}take{{/propertyMap + }}{{^propertyMap}}value{{/propertyMap}}("{{baseName}}"_ls), result.{{nameCamelCase}}); {{/vars}}{{#propertyMap}} - result.{{nameCamelCase}} = fromJson<{{dataType.qualifiedName}}>(jo);{{/propertyMap}} - return result; -} + fromJson(jo, result.{{nameCamelCase}}); +{{/propertyMap}}} {{/out?}}{{/models.model}}{{#operations}} static const auto basePath = QStringLiteral("{{basePathWithoutHost}}"); {{# operation}}{{#models}} namespace QMatrixClient { // Converters -{{#model}}{{#in?}} - QJsonObject toJson(const {{qualifiedName}}& pod) - { - QJsonObject jo{{#propertyMap}} = toJson(pod.{{nameCamelCase}}){{/propertyMap}};{{#vars}} - addParam<{{^required?}}IfNotEmpty{{/required?}}>(jo, QStringLiteral("{{baseName}}"), pod.{{nameCamelCase}});{{/vars}} - return jo; - } -{{/in?}}{{#out?}} - template <> struct FromJsonObject<{{qualifiedName}}> +{{#model}} + template <> struct JsonObjectConverter<{{qualifiedName}}> { - {{qualifiedName}} operator()({{^propertyMap}}const QJsonObject&{{/propertyMap}}{{#propertyMap}}QJsonObject{{/propertyMap}} jo) const +{{#in?}} static void dumpTo(QJsonObject& jo, const {{qualifiedName}}& pod) { - {{qualifiedName}} result; -{{#vars}} result.{{nameCamelCase}} = - fromJson<{{dataType.qualifiedName}}>(jo.{{#propertyMap}}take{{/propertyMap}}{{^propertyMap}}value{{/propertyMap}}("{{baseName}}"_ls)); -{{/vars}}{{#propertyMap}} - result.{{nameCamelCase}} = fromJson<{{dataType.qualifiedName}}>(jo);{{/propertyMap}} - return result; - } - }; -{{/out?}}{{/model}}} // namespace QMatrixClient +{{#propertyMap}} fillJson(jo, pod.{{nameCamelCase}}); + {{/propertyMap}}{{#parents}}fillJson<{{name}}>(jo, pod); + {{/parents}}{{#vars +}} addParam<{{^required?}}IfNotEmpty{{/required?}}>(jo, QStringLiteral("{{baseName}}"), pod.{{nameCamelCase}}); +{{/vars}} } +{{/in?}}{{#out? +}} static void fillFrom({{^propertyMap}}const QJsonObject&{{/propertyMap + }}{{#propertyMap}}QJsonObject{{/propertyMap}} jo, {{qualifiedName}}& result) + { +{{#parents}} fillFromJson<{{qualifiedName}}{{!of the parent!}}>(jo, result); + {{/parents}}{{#vars +}} fromJson(jo.{{#propertyMap}}take{{/propertyMap + }}{{^propertyMap}}value{{/propertyMap}}("{{baseName}}"_ls), result.{{nameCamelCase}}); +{{/vars}}{{#propertyMap}} fromJson(jo, result.{{nameCamelCase}}); +{{/propertyMap}} } +{{/out?}} }; +{{/model}}} // namespace QMatrixClient {{/ models}}{{#responses}}{{#normalResponse?}}{{#allProperties?}} class {{camelCaseOperationId}}Job::Private { @@ -109,12 +112,12 @@ BaseJob::Status {{camelCaseOperationId}}Job::parseReply(QNetworkReply* reply) }{{/ producesNonJson?}}{{^producesNonJson?}} BaseJob::Status {{camelCaseOperationId}}Job::parseJson(const QJsonDocument& data) { -{{#inlineResponse}} d->{{paramName}} = fromJson<{{dataType.name}}>(data); +{{#inlineResponse}} fromJson(data, d->{{paramName}}); {{/inlineResponse}}{{^inlineResponse}} auto json = data.object(); {{#properties}}{{#required?}} if (!json.contains("{{baseName}}"_ls)) return { JsonParseError, "The key '{{baseName}}' not found in the response" }; -{{/required?}} d->{{paramName}} = fromJson<{{dataType.name}}>(json.value("{{baseName}}"_ls)); +{{/required?}} fromJson(json.value("{{baseName}}"_ls), d->{{paramName}}); {{/properties}}{{/inlineResponse}} return Success; }{{/ producesNonJson?}} {{/allProperties?}}{{/normalResponse?}}{{/responses}}{{/operation}}{{/operations}} diff --git a/lib/csapi/{{base}}.h.mustache b/lib/csapi/{{base}}.h.mustache index 147c86075..a9c3a63a4 100644 --- a/lib/csapi/{{base}}.h.mustache +++ b/lib/csapi/{{base}}.h.mustache @@ -18,14 +18,13 @@ namespace QMatrixClient {{/vars}}{{#propertyMap}}{{#description}} /// {{_}} {{/description}} {{>maybeOmittableType}} {{nameCamelCase}}; {{/propertyMap}} }; -{{#in?}} - QJsonObject toJson(const {{name}}& pod); -{{/in?}}{{#out?}} - template <> struct FromJsonObject<{{name}}> + template <> struct JsonObjectConverter<{{name}}> { - {{name}} operator()({{^propertyMap}}const QJsonObject&{{/propertyMap}}{{#propertyMap}}QJsonObject{{/propertyMap}} jo) const; - }; -{{/ out?}}{{/model}} + {{#in?}}static void dumpTo(QJsonObject& jo, const {{name}}& pod); + {{/in?}}{{#out?}}static void fillFrom({{^propertyMap}}const QJsonObject&{{/propertyMap + }}{{#propertyMap}}QJsonObject{{/propertyMap}} jo, {{name}}& pod); +{{/out?}} }; +{{/model}} {{/models}}{{#operations}} // Operations {{# operation}}{{#summary}} /// {{summary}}{{#description?}}{{!add a linebreak between summary and description if both exist}} diff --git a/lib/events/accountdataevents.h b/lib/events/accountdataevents.h index d1c1abc86..a99d85ac1 100644 --- a/lib/events/accountdataevents.h +++ b/lib/events/accountdataevents.h @@ -36,37 +36,38 @@ namespace QMatrixClient order_type order; TagRecord (order_type order = none) : order(order) { } - explicit TagRecord(const QJsonObject& jo) + + 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()); + } + }; + + template <> struct JsonObjectConverter + { + static void fillFrom(const QJsonObject& jo, TagRecord& rec) { // Parse a float both from JSON double and JSON string because // libqmatrixclient previously used to use strings to store order. const auto orderJv = jo.value("order"_ls); if (orderJv.isDouble()) - order = fromJson(orderJv); - else if (orderJv.isString()) + rec.order = fromJson(orderJv); + if (orderJv.isString()) { bool ok; - order = orderJv.toString().toFloat(&ok); + rec.order = orderJv.toString().toFloat(&ok); if (!ok) - order = none; + rec.order = none; } } - - bool operator<(const TagRecord& other) const + static void dumpTo(QJsonObject& jo, const TagRecord& rec) { - // Per The Spec, rooms with no order should be after those with order - return !order.omitted() && - (other.order.omitted() || order.value() < other.order.value()); + addParam(jo, QStringLiteral("order"), rec.order); } }; - inline QJsonValue toJson(const TagRecord& rec) - { - QJsonObject o; - addParam(o, QStringLiteral("order"), rec.order); - return o; - } - using TagsMap = QHash; #define DEFINE_SIMPLE_EVENT(_Name, _TypeId, _ContentType, _ContentKey) \ diff --git a/lib/events/eventloader.h b/lib/events/eventloader.h index cd2f91492..da6633927 100644 --- a/lib/events/eventloader.h +++ b/lib/events/eventloader.h @@ -57,11 +57,15 @@ namespace QMatrixClient { matrixType); } - template struct FromJsonObject> + template struct JsonConverter> { - auto operator()(const QJsonObject& jo) const + static auto load(const QJsonValue& jv) { - return loadEvent(jo); + return loadEvent(jv.toObject()); + } + static auto load(const QJsonDocument& jd) + { + return loadEvent(jd.object()); } }; } // namespace QMatrixClient diff --git a/lib/events/roommemberevent.cpp b/lib/events/roommemberevent.cpp index eaa3302c1..a5ac3c5f3 100644 --- a/lib/events/roommemberevent.cpp +++ b/lib/events/roommemberevent.cpp @@ -23,20 +23,17 @@ #include -using namespace QMatrixClient; - static const std::array membershipStrings = { { QStringLiteral("invite"), QStringLiteral("join"), QStringLiteral("knock"), QStringLiteral("leave"), QStringLiteral("ban") } }; -namespace QMatrixClient -{ +namespace QMatrixClient { template <> - struct FromJson + struct JsonConverter { - MembershipType operator()(const QJsonValue& jv) const + static MembershipType load(const QJsonValue& jv) { const auto& membershipString = jv.toString(); for (auto it = membershipStrings.begin(); @@ -48,9 +45,10 @@ namespace QMatrixClient return MembershipType::Undefined; } }; - } +using namespace QMatrixClient; + MemberEventContent::MemberEventContent(const QJsonObject& json) : membership(fromJson(json["membership"_ls])) , isDirect(json["is_direct"_ls].toBool()) diff --git a/lib/identity/definitions/request_email_validation.cpp b/lib/identity/definitions/request_email_validation.cpp index 95088bcb6..47463a8b5 100644 --- a/lib/identity/definitions/request_email_validation.cpp +++ b/lib/identity/definitions/request_email_validation.cpp @@ -6,28 +6,21 @@ using namespace QMatrixClient; -QJsonObject QMatrixClient::toJson(const RequestEmailValidation& pod) +void JsonObjectConverter::dumpTo( + QJsonObject& jo, const RequestEmailValidation& pod) { - QJsonObject jo; addParam<>(jo, QStringLiteral("client_secret"), pod.clientSecret); addParam<>(jo, QStringLiteral("email"), pod.email); addParam<>(jo, QStringLiteral("send_attempt"), pod.sendAttempt); addParam(jo, QStringLiteral("next_link"), pod.nextLink); - return jo; } -RequestEmailValidation FromJsonObject::operator()(const QJsonObject& jo) const +void JsonObjectConverter::fillFrom( + const QJsonObject& jo, RequestEmailValidation& result) { - RequestEmailValidation result; - result.clientSecret = - fromJson(jo.value("client_secret"_ls)); - result.email = - fromJson(jo.value("email"_ls)); - result.sendAttempt = - fromJson(jo.value("send_attempt"_ls)); - result.nextLink = - fromJson(jo.value("next_link"_ls)); - - return result; + fromJson(jo.value("client_secret"_ls), result.clientSecret); + fromJson(jo.value("email"_ls), result.email); + fromJson(jo.value("send_attempt"_ls), result.sendAttempt); + fromJson(jo.value("next_link"_ls), result.nextLink); } diff --git a/lib/identity/definitions/request_email_validation.h b/lib/identity/definitions/request_email_validation.h index 3e72275f3..eb7d8ed62 100644 --- a/lib/identity/definitions/request_email_validation.h +++ b/lib/identity/definitions/request_email_validation.h @@ -33,12 +33,10 @@ namespace QMatrixClient /// server will redirect the user to this URL. QString nextLink; }; - - QJsonObject toJson(const RequestEmailValidation& pod); - - template <> struct FromJsonObject + template <> struct JsonObjectConverter { - RequestEmailValidation operator()(const QJsonObject& jo) const; + static void dumpTo(QJsonObject& jo, const RequestEmailValidation& pod); + static void fillFrom(const QJsonObject& jo, RequestEmailValidation& pod); }; } // namespace QMatrixClient diff --git a/lib/identity/definitions/request_msisdn_validation.cpp b/lib/identity/definitions/request_msisdn_validation.cpp index 125baa9c0..a123d3261 100644 --- a/lib/identity/definitions/request_msisdn_validation.cpp +++ b/lib/identity/definitions/request_msisdn_validation.cpp @@ -6,31 +6,23 @@ using namespace QMatrixClient; -QJsonObject QMatrixClient::toJson(const RequestMsisdnValidation& pod) +void JsonObjectConverter::dumpTo( + QJsonObject& jo, const RequestMsisdnValidation& pod) { - QJsonObject jo; addParam<>(jo, QStringLiteral("client_secret"), pod.clientSecret); addParam<>(jo, QStringLiteral("country"), pod.country); addParam<>(jo, QStringLiteral("phone_number"), pod.phoneNumber); addParam<>(jo, QStringLiteral("send_attempt"), pod.sendAttempt); addParam(jo, QStringLiteral("next_link"), pod.nextLink); - return jo; } -RequestMsisdnValidation FromJsonObject::operator()(const QJsonObject& jo) const +void JsonObjectConverter::fillFrom( + const QJsonObject& jo, RequestMsisdnValidation& result) { - RequestMsisdnValidation result; - result.clientSecret = - fromJson(jo.value("client_secret"_ls)); - result.country = - fromJson(jo.value("country"_ls)); - result.phoneNumber = - fromJson(jo.value("phone_number"_ls)); - result.sendAttempt = - fromJson(jo.value("send_attempt"_ls)); - result.nextLink = - fromJson(jo.value("next_link"_ls)); - - return result; + fromJson(jo.value("client_secret"_ls), result.clientSecret); + fromJson(jo.value("country"_ls), result.country); + fromJson(jo.value("phone_number"_ls), result.phoneNumber); + fromJson(jo.value("send_attempt"_ls), result.sendAttempt); + fromJson(jo.value("next_link"_ls), result.nextLink); } diff --git a/lib/identity/definitions/request_msisdn_validation.h b/lib/identity/definitions/request_msisdn_validation.h index 77bea2bc5..b48ed6d53 100644 --- a/lib/identity/definitions/request_msisdn_validation.h +++ b/lib/identity/definitions/request_msisdn_validation.h @@ -36,12 +36,10 @@ namespace QMatrixClient /// server will redirect the user to this URL. QString nextLink; }; - - QJsonObject toJson(const RequestMsisdnValidation& pod); - - template <> struct FromJsonObject + template <> struct JsonObjectConverter { - RequestMsisdnValidation operator()(const QJsonObject& jo) const; + static void dumpTo(QJsonObject& jo, const RequestMsisdnValidation& pod); + static void fillFrom(const QJsonObject& jo, RequestMsisdnValidation& pod); }; } // namespace QMatrixClient diff --git a/lib/identity/definitions/sid.cpp b/lib/identity/definitions/sid.cpp index 443dbedf4..1ba4b3b59 100644 --- a/lib/identity/definitions/sid.cpp +++ b/lib/identity/definitions/sid.cpp @@ -6,19 +6,15 @@ using namespace QMatrixClient; -QJsonObject QMatrixClient::toJson(const Sid& pod) +void JsonObjectConverter::dumpTo( + QJsonObject& jo, const Sid& pod) { - QJsonObject jo; addParam<>(jo, QStringLiteral("sid"), pod.sid); - return jo; } -Sid FromJsonObject::operator()(const QJsonObject& jo) const +void JsonObjectConverter::fillFrom( + const QJsonObject& jo, Sid& result) { - Sid result; - result.sid = - fromJson(jo.value("sid"_ls)); - - return result; + fromJson(jo.value("sid"_ls), result.sid); } diff --git a/lib/identity/definitions/sid.h b/lib/identity/definitions/sid.h index eae60c470..ac8c4130a 100644 --- a/lib/identity/definitions/sid.h +++ b/lib/identity/definitions/sid.h @@ -19,12 +19,10 @@ namespace QMatrixClient /// must not be empty. QString sid; }; - - QJsonObject toJson(const Sid& pod); - - template <> struct FromJsonObject + template <> struct JsonObjectConverter { - Sid operator()(const QJsonObject& jo) const; + static void dumpTo(QJsonObject& jo, const Sid& pod); + static void fillFrom(const QJsonObject& jo, Sid& pod); }; } // namespace QMatrixClient From 9628594881346c8e06594e65d3befafc310e12d5 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Sat, 8 Dec 2018 15:36:28 +0900 Subject: [PATCH 02/27] EventContent: minor cleanup --- lib/events/eventcontent.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/events/eventcontent.h b/lib/events/eventcontent.h index 91d7a8c85..bedf0078e 100644 --- a/lib/events/eventcontent.h +++ b/lib/events/eventcontent.h @@ -43,9 +43,10 @@ namespace QMatrixClient class Base { public: - explicit Base (const QJsonObject& o = {}) : originalJson(o) { } + explicit Base (QJsonObject o = {}) : originalJson(std::move(o)) { } virtual ~Base() = default; + // FIXME: make toJson() from converters.* work on base classes QJsonObject toJson() const; public: From a0053484024a85ae47dcd2b464cb15c0f85109e5 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Fri, 23 Nov 2018 19:20:45 +0900 Subject: [PATCH 03/27] SyncJob: accept Filter instead of QString for the filter --- lib/jobs/syncjob.cpp | 7 +++++++ lib/jobs/syncjob.h | 3 +++ 2 files changed, 10 insertions(+) diff --git a/lib/jobs/syncjob.cpp b/lib/jobs/syncjob.cpp index ac0f66851..84385b556 100644 --- a/lib/jobs/syncjob.cpp +++ b/lib/jobs/syncjob.cpp @@ -42,6 +42,13 @@ SyncJob::SyncJob(const QString& since, const QString& filter, int timeout, setMaxRetries(std::numeric_limits::max()); } +SyncJob::SyncJob(const QString& since, const Filter& filter, + int timeout, const QString& presence) + : SyncJob(since, + QJsonDocument(toJson(filter)).toJson(QJsonDocument::Compact), + timeout, presence) +{ } + BaseJob::Status SyncJob::parseJson(const QJsonDocument& data) { d.parseJson(data.object()); diff --git a/lib/jobs/syncjob.h b/lib/jobs/syncjob.h index a0a3c0268..036b25d00 100644 --- a/lib/jobs/syncjob.h +++ b/lib/jobs/syncjob.h @@ -21,6 +21,7 @@ #include "basejob.h" #include "../syncdata.h" +#include "../csapi/definitions/sync_filter.h" namespace QMatrixClient { @@ -30,6 +31,8 @@ namespace QMatrixClient explicit SyncJob(const QString& since = {}, const QString& filter = {}, int timeout = -1, const QString& presence = {}); + explicit SyncJob(const QString& since, const Filter& filter, + int timeout = -1, const QString& presence = {}); SyncData &&takeData() { return std::move(d); } From 1ff8a0c26fc2738a085ca0302f0471ffa95a567e Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Wed, 14 Nov 2018 07:16:05 +0900 Subject: [PATCH 04/27] Connection: support members lazy-loading This should cover the Connection-related part of #253. Connection gained lazyLoading/setLazyLoading accessors and the respective Q_PROPERTY. When lazy loading is on, sync() adds lazy_load_members: true to its filter. --- lib/connection.cpp | 23 +++++++++++++++++++---- lib/connection.h | 6 ++++++ 2 files changed, 25 insertions(+), 4 deletions(-) diff --git a/lib/connection.cpp b/lib/connection.cpp index 26c337678..526093703 100644 --- a/lib/connection.cpp +++ b/lib/connection.cpp @@ -94,6 +94,7 @@ class Connection::Private bool cacheState = true; bool cacheToBinary = SettingsGroup("libqmatrixclient") .value("cache_type").toString() != "json"; + bool lazyLoading; void connectWithToken(const QString& user, const QString& accessToken, const QString& deviceId); @@ -287,11 +288,11 @@ void Connection::sync(int timeout) if (d->syncJob) return; - // Raw string: http://en.cppreference.com/w/cpp/language/string_literal - const auto filter = - QStringLiteral(R"({"room": { "timeline": { "limit": 100 } } })"); + Filter filter; + filter.room->timeline->limit = 100; + filter.room->state->lazyLoadMembers = d->lazyLoading; auto job = d->syncJob = callApi(BackgroundRequest, - d->data->lastEvent(), filter, timeout); + d->data->lastEvent(), filter, timeout); connect( job, &SyncJob::success, this, [this, job] { onSyncSuccess(job->takeData()); d->syncJob = nullptr; @@ -1181,6 +1182,20 @@ void Connection::setCacheState(bool newValue) } } +bool QMatrixClient::Connection::lazyLoading() const +{ + return d->lazyLoading; +} + +void QMatrixClient::Connection::setLazyLoading(bool newValue) +{ + if (d->lazyLoading != newValue) + { + d->lazyLoading = newValue; + emit lazyLoadingChanged(); + } +} + void Connection::getTurnServers() { auto job = callApi(); diff --git a/lib/connection.h b/lib/connection.h index 32533b6e7..220f6c8f1 100644 --- a/lib/connection.h +++ b/lib/connection.h @@ -122,6 +122,8 @@ namespace QMatrixClient Q_PROPERTY(QByteArray accessToken READ accessToken NOTIFY stateChanged) Q_PROPERTY(QUrl homeserver READ homeserver WRITE setHomeserver NOTIFY homeserverChanged) Q_PROPERTY(bool cacheState READ cacheState WRITE setCacheState NOTIFY cacheStateChanged) + Q_PROPERTY(bool lazyLoading READ lazyLoading WRITE setLazyLoading NOTIFY lazyLoadingChanged) + public: // Room ids, rather than room pointers, are used in the direct chat // map types because the library keeps Invite rooms separate from @@ -308,6 +310,9 @@ namespace QMatrixClient bool cacheState() const; void setCacheState(bool newValue); + bool lazyLoading() const; + void setLazyLoading(bool newValue); + /** Start a job of a specified type with specified arguments and policy * * This is a universal method to start a job of a type passed @@ -655,6 +660,7 @@ namespace QMatrixClient IgnoredUsersList removals); void cacheStateChanged(); + void lazyLoadingChanged(); void turnServersChanged(const QJsonObject& servers); protected: From 9272d21ce6e5439444794e6da58e08421e8973db Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Sat, 8 Dec 2018 15:37:16 +0900 Subject: [PATCH 05/27] Room summaries --- lib/connection.cpp | 2 +- lib/room.cpp | 48 +++++++++++++++++++++++++++++++++++++++++++++- lib/room.h | 8 ++++++++ lib/syncdata.cpp | 29 ++++++++++++++++++++++++++++ lib/syncdata.h | 19 ++++++++++++++++++ 5 files changed, 104 insertions(+), 2 deletions(-) diff --git a/lib/connection.cpp b/lib/connection.cpp index 526093703..28156d114 100644 --- a/lib/connection.cpp +++ b/lib/connection.cpp @@ -562,7 +562,7 @@ void Connection::doInDirectChat(User* u, { Q_ASSERT(r->id() == roomId); // A direct chat with yourself should only involve yourself :) - if (userId == d->userId && r->memberCount() > 1) + if (userId == d->userId && r->totalMemberCount() > 1) continue; qCDebug(MAIN) << "Requested direct chat with" << userId << "is already available as" << r->id(); diff --git a/lib/room.cpp b/lib/room.cpp index 8b81bfb2e..439baeb5d 100644 --- a/lib/room.cpp +++ b/lib/room.cpp @@ -93,6 +93,7 @@ class Room::Private Connection* connection; QString id; JoinState joinState; + RoomSummary summary; /// The state of the room at timeline position before-0 /// \sa timelineBase std::unordered_map baseState; @@ -164,6 +165,8 @@ class Room::Private const RoomMessageEvent* getEventWithFile(const QString& eventId) const; QString fileNameToDownload(const RoomMessageEvent* event) const; + Changes setSummary(RoomSummary&& newSummary); + //void inviteUser(User* u); // We might get it at some point in time. void insertMemberIntoMap(User* u); void renameMember(User* u, QString oldName); @@ -596,6 +599,10 @@ void Room::setDisplayed(bool displayed) { resetHighlightCount(); resetNotificationCount(); +// if (d->lazyLoaded) +// { +// // TODO: Get all members +// } } } @@ -976,11 +983,41 @@ bool Room::usesEncryption() const return !d->getCurrentState()->algorithm().isEmpty(); } +int Room::joinedCount() const +{ + return d->summary.joinedMemberCount > 0 + ? d->summary.joinedMemberCount + : d->membersMap.size(); +} + +int Room::invitedCount() const +{ + // TODO: Store invited users in Room too + return d->summary.invitedMemberCount; +} + +int Room::totalMemberCount() const +{ + return joinedCount() + invitedCount(); +} + GetRoomEventsJob* Room::eventsHistoryJob() const { return d->eventsHistoryJob; } +Room::Changes Room::Private::setSummary(RoomSummary&& newSummary) +{ + if (summary == newSummary) + return Change::NoChange; + summary = move(newSummary); + qCDebug(MAIN).nospace() + << "Updated room summary: joined " << summary.joinedMemberCount + << ", invited " << summary.invitedMemberCount + << ", heroes: " << summary.heroes.join(','); + return Change::SummaryChange; +} + void Room::Private::insertMemberIntoMap(User *u) { const auto userName = u->name(q); @@ -1148,6 +1185,7 @@ void Room::updateData(SyncRoomData&& data, bool fromCache) if (roomChanges&NameChange) emit namesChanged(this); + d->setSummary(move(data.summary)); d->updateDisplayname(); for( auto&& ephemeralEvent: data.ephemeral ) @@ -2073,7 +2111,14 @@ QString Room::Private::calculateDisplayname() const // return q->aliases().at(0); // 3. Room members - dispName = roomNameFromMemberNames(membersMap.values()); + if (!summary.heroes.empty()) + { + QList users; users.reserve(summary.heroes.size()); + for (const auto& h: summary.heroes) + users.push_back(q->user(h)); + dispName = roomNameFromMemberNames(users); + } else + dispName = roomNameFromMemberNames(membersMap.values()); if (!dispName.isEmpty()) return dispName; @@ -2103,6 +2148,7 @@ QJsonObject Room::Private::toJson() const { QElapsedTimer et; et.start(); QJsonObject result; + addParam(result, QStringLiteral("summary"), summary); { QJsonArray stateEvents; diff --git a/lib/room.h b/lib/room.h index 9d4561e54..97d8454ad 100644 --- a/lib/room.h +++ b/lib/room.h @@ -84,6 +84,9 @@ namespace QMatrixClient Q_PROPERTY(int timelineSize READ timelineSize NOTIFY addedMessages) Q_PROPERTY(QStringList memberNames READ memberNames NOTIFY memberListChanged) Q_PROPERTY(int memberCount READ memberCount NOTIFY memberListChanged) + Q_PROPERTY(int joinedCount READ joinedCount NOTIFY memberListChanged) + Q_PROPERTY(int invitedCount READ invitedCount NOTIFY memberListChanged) + Q_PROPERTY(int totalMemberCount READ totalMemberCount NOTIFY memberListChanged) Q_PROPERTY(bool displayed READ displayed WRITE setDisplayed NOTIFY displayedChanged) Q_PROPERTY(QString firstDisplayedEventId READ firstDisplayedEventId WRITE setFirstDisplayedEventId NOTIFY firstDisplayedEventChanged) @@ -116,6 +119,7 @@ namespace QMatrixClient MembersChange = 0x80, EncryptionOn = 0x100, AccountDataChange = 0x200, + SummaryChange = 0x400, OtherChange = 0x1000, AnyChange = 0x1FFF }; @@ -143,9 +147,13 @@ namespace QMatrixClient Q_INVOKABLE QList users() const; QStringList memberNames() const; + [[deprecated("Use joinedCount(), invitedCount(), totalMemberCount()")]] int memberCount() const; int timelineSize() const; bool usesEncryption() const; + int joinedCount() const; + int invitedCount() const; + int totalMemberCount() const; GetRoomEventsJob* eventsHistoryJob() const; diff --git a/lib/syncdata.cpp b/lib/syncdata.cpp index 1023ed6af..a5f849b34 100644 --- a/lib/syncdata.cpp +++ b/lib/syncdata.cpp @@ -34,10 +34,39 @@ inline EventsArrayT load(const QJsonObject& batches, StrT keyName) return fromJson(batches[keyName].toObject().value("events"_ls)); } +void JsonObjectConverter::dumpTo(QJsonObject& jo, + const RoomSummary& rs) +{ + if (rs.joinedMemberCount != 0) + jo.insert(QStringLiteral("m.joined_member_count"), + rs.joinedMemberCount); + if (rs.invitedMemberCount != 0) + jo.insert(QStringLiteral("m.invited_member_count"), + rs.invitedMemberCount); + if (!rs.heroes.empty()) + jo.insert(QStringLiteral("m.heroes"), toJson(rs.heroes)); +} + +void JsonObjectConverter::fillFrom(const QJsonObject& jo, + RoomSummary& rs) +{ + rs.joinedMemberCount = fromJson(jo["m.joined_member_count"_ls]); + rs.joinedMemberCount = fromJson(jo["m.invited_member_count"_ls]); + rs.heroes = fromJson(jo["m.heroes"]); +} + +bool RoomSummary::operator==(const RoomSummary& other) const +{ + return joinedMemberCount == other.joinedMemberCount && + invitedMemberCount == other.invitedMemberCount && + heroes == other.heroes; +} + SyncRoomData::SyncRoomData(const QString& roomId_, JoinState joinState_, const QJsonObject& room_) : roomId(roomId_) , joinState(joinState_) + , summary(fromJson(room_["summary"].toObject())) , state(load(room_, joinState == JoinState::Invite ? "invite_state"_ls : "state"_ls)) { diff --git a/lib/syncdata.h b/lib/syncdata.h index aa8948bcc..81a91ffcc 100644 --- a/lib/syncdata.h +++ b/lib/syncdata.h @@ -22,11 +22,30 @@ #include "events/stateevent.h" namespace QMatrixClient { + struct RoomSummary + { + int joinedMemberCount = 0; + int invitedMemberCount = 0; + QStringList heroes; //< mxids of users to take part in the room name + + bool operator==(const RoomSummary& other) const; + bool operator!=(const RoomSummary& other) const + { return !(*this == other); } + }; + + template <> + struct JsonObjectConverter + { + static void dumpTo(QJsonObject& jo, const RoomSummary& rs); + static void fillFrom(const QJsonObject& jo, RoomSummary& rs); + }; + class SyncRoomData { public: QString roomId; JoinState joinState; + RoomSummary summary; StateEvents state; RoomEvents timeline; Events ephemeral; From 4a252aa465c7a36268e8014674800e6d98a449e9 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Sat, 8 Dec 2018 22:39:38 +0900 Subject: [PATCH 06/27] Omittable<>::merge<> --- lib/util.h | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/lib/util.h b/lib/util.h index 0066c03dd..9c9a37ba4 100644 --- a/lib/util.h +++ b/lib/util.h @@ -118,6 +118,22 @@ namespace QMatrixClient _omitted = false; return _value; } + /// Merge the value from another Omittable + /// \return true if \p other is not omitted and the value of + /// the current Omittable was different (or omitted); + /// in other words, if the current Omittable has changed; + /// false otherwise + template + auto merge(const Omittable& other) + -> std::enable_if_t::value, bool> + { + if (other.omitted() || + (!_omitted && _value == other.value())) + return false; + _omitted = false; + _value = other.value(); + return true; + } value_type&& release() { _omitted = true; return std::move(_value); } operator value_type&() & { return editValue(); } From d51684b759f686b2c9895c5013dce88fead9661b Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Sat, 8 Dec 2018 22:42:25 +0900 Subject: [PATCH 07/27] MSC 688: MSC-compliant RoomSummary; update Room::calculateDisplayname() The members of the summary can be omitted in the payload; this change fixes calculation of the roomname from hero names passed in room summary. Also: RoomSummary can be dumped to QDebug now. --- lib/room.cpp | 126 +++++++++++++++++++++++++---------------------- lib/syncdata.cpp | 57 ++++++++++++++------- lib/syncdata.h | 23 ++++++--- 3 files changed, 123 insertions(+), 83 deletions(-) diff --git a/lib/room.cpp b/lib/room.cpp index 439baeb5d..fec2dc189 100644 --- a/lib/room.cpp +++ b/lib/room.cpp @@ -263,8 +263,11 @@ class Room::Private QJsonObject toJson() const; private: + using users_shortlist_t = std::array; + template + users_shortlist_t buildShortlist(const ContT& users) const; + users_shortlist_t buildShortlist(const QStringList& userIds) const; QString calculateDisplayname() const; - QString roomNameFromMemberNames(const QList& userlist) const; bool isLocalUser(const User* u) const { @@ -985,9 +988,9 @@ bool Room::usesEncryption() const int Room::joinedCount() const { - return d->summary.joinedMemberCount > 0 - ? d->summary.joinedMemberCount - : d->membersMap.size(); + return d->summary.joinedMemberCount.omitted() + ? d->membersMap.size() + : d->summary.joinedMemberCount.value(); } int Room::invitedCount() const @@ -1008,13 +1011,14 @@ GetRoomEventsJob* Room::eventsHistoryJob() const Room::Changes Room::Private::setSummary(RoomSummary&& newSummary) { - if (summary == newSummary) + if (!summary.merge(newSummary)) return Change::NoChange; summary = move(newSummary); qCDebug(MAIN).nospace() - << "Updated room summary: joined " << summary.joinedMemberCount + << "Updated room summary for" << q->objectName() + << ": joined " << summary.joinedMemberCount << ", invited " << summary.invitedMemberCount - << ", heroes: " << summary.heroes.join(','); + << ", heroes: " << summary.heroes.value().join(','); return Change::SummaryChange; } @@ -1391,7 +1395,7 @@ bool isEchoEvent(const RoomEventPtr& le, const PendingEventItem& re) bool Room::supportsCalls() const { - return d->membersMap.size() == 2; + return joinedCount() == 2; } void Room::inviteCall(const QString& callId, const int lifetime, @@ -2045,49 +2049,35 @@ Room::Changes Room::processAccountDataEvent(EventPtr&& event) return Change::NoChange; } -QString Room::Private::roomNameFromMemberNames(const QList &userlist) const +template +Room::Private::users_shortlist_t +Room::Private::buildShortlist(const ContT& users) const { - // This is part 3(i,ii,iii) in the room displayname algorithm described - // in the CS spec (see also Room::Private::updateDisplayname() ). - // The spec requires to sort users lexicographically by state_key (user id) - // and use disambiguated display names of two topmost users excluding - // the current one to render the name of the room. - - // std::array is the leanest C++ container - std::array first_two = { {nullptr, nullptr} }; + // To calculate room display name the spec requires to sort users + // lexicographically by state_key (user id) and use disambiguated + // display names of two topmost users excluding the current one to render + // the name of the room. The below code selects 3 topmost users, + // slightly extending the spec. + users_shortlist_t shortlist { }; // Prefill with nullptrs std::partial_sort_copy( - userlist.begin(), userlist.end(), - first_two.begin(), first_two.end(), - [this](const User* u1, const User* u2) { - // Filter out the "me" user so that it never hits the room name + users.begin(), users.end(), + shortlist.begin(), shortlist.end(), + [this] (const User* u1, const User* u2) { + // localUser(), if it's in the list, is sorted below all others return isLocalUser(u2) || (!isLocalUser(u1) && u1->id() < u2->id()); } ); + return shortlist; +} - // Spec extension. A single person in the chat but not the local user - // (the local user is invited). - if (userlist.size() == 1 && !isLocalUser(first_two.front()) && - joinState == JoinState::Invite) - return tr("Invitation from %1") - .arg(q->roomMembername(first_two.front())); - - // i. One-on-one chat. first_two[1] == localUser() in this case. - if (userlist.size() == 2) - return q->roomMembername(first_two[0]); - - // ii. Two users besides the current one. - if (userlist.size() == 3) - return tr("%1 and %2") - .arg(q->roomMembername(first_two[0]), - q->roomMembername(first_two[1])); - - // iii. More users. - if (userlist.size() > 3) - return tr("%1 and %Ln other(s)", "", userlist.size() - 3) - .arg(q->roomMembername(first_two[0])); - - // userlist.size() < 2 - apparently, there's only current user in the room - return QString(); +Room::Private::users_shortlist_t +Room::Private::buildShortlist(const QStringList& userIds) const +{ + QList users; + users.reserve(userIds.size()); + for (const auto& h: userIds) + users.push_back(q->user(h)); + return buildShortlist(users); } QString Room::Private::calculateDisplayname() const @@ -2110,22 +2100,42 @@ QString Room::Private::calculateDisplayname() const //if (!q->aliases().empty() && !q->aliases().at(0).isEmpty()) // return q->aliases().at(0); - // 3. Room members - if (!summary.heroes.empty()) + // Supplementary code for 3 and 4: build the shortlist of users whose names + // will be used to construct the room name. Takes into account MSC688's + // "heroes" if available. + + const bool emptyRoom = membersMap.isEmpty() || + (membersMap.size() == 1 && isLocalUser(*membersMap.begin())); + const auto shortlist = + !summary.heroes.omitted() ? buildShortlist(summary.heroes.value()) : + !emptyRoom ? buildShortlist(membersMap) : + buildShortlist(membersLeft); + + QStringList names; + for (auto u: shortlist) { - QList users; users.reserve(summary.heroes.size()); - for (const auto& h: summary.heroes) - users.push_back(q->user(h)); - dispName = roomNameFromMemberNames(users); - } else - dispName = roomNameFromMemberNames(membersMap.values()); - if (!dispName.isEmpty()) - return dispName; + if (u == nullptr || isLocalUser(u)) + break; + names.push_back(q->roomMembername(u)); + } + + auto usersCountExceptLocal = emptyRoom + ? membersLeft.size() - int(joinState == JoinState::Leave) + : q->joinedCount() - int(joinState == JoinState::Join); + if (usersCountExceptLocal > int(shortlist.size())) + names << + tr("%Ln other(s)", + "Used to make a room name from user names: A, B and _N others_", + usersCountExceptLocal); + auto namesList = QLocale().createSeparatedList(names); + + // 3. Room members + if (!emptyRoom) + return namesList; // 4. Users that previously left the room - dispName = roomNameFromMemberNames(membersLeft); - if (!dispName.isEmpty()) - return tr("Empty room (was: %1)").arg(dispName); + if (membersLeft.size() > 0) + return tr("Empty room (was: %1)").arg(namesList); // 5. Fail miserably return tr("Empty room (%1)").arg(id); diff --git a/lib/syncdata.cpp b/lib/syncdata.cpp index a5f849b34..f55d43960 100644 --- a/lib/syncdata.cpp +++ b/lib/syncdata.cpp @@ -28,45 +28,64 @@ using namespace QMatrixClient; const QString SyncRoomData::UnreadCountKey = QStringLiteral("x-qmatrixclient.unread_count"); -template -inline EventsArrayT load(const QJsonObject& batches, StrT keyName) +bool RoomSummary::isEmpty() const { - return fromJson(batches[keyName].toObject().value("events"_ls)); + return joinedMemberCount.omitted() && invitedMemberCount.omitted() && + heroes.omitted(); +} + +bool RoomSummary::merge(const RoomSummary& other) +{ + // Using bitwise OR to prevent computation shortcut. + return + joinedMemberCount.merge(other.joinedMemberCount) | + invitedMemberCount.merge(other.invitedMemberCount) | + heroes.merge(other.heroes); +} + +QDebug QMatrixClient::operator<<(QDebug dbg, const RoomSummary& rs) +{ + QDebugStateSaver _(dbg); + QStringList sl; + if (!rs.joinedMemberCount.omitted()) + sl << QStringLiteral("joined: %1").arg(rs.joinedMemberCount.value()); + if (!rs.invitedMemberCount.omitted()) + sl << QStringLiteral("invited: %1").arg(rs.invitedMemberCount.value()); + if (!rs.heroes.omitted()) + sl << QStringLiteral("heroes: [%1]").arg(rs.heroes.value().join(',')); + dbg.nospace().noquote() << sl.join(QStringLiteral("; ")); + return dbg; } void JsonObjectConverter::dumpTo(QJsonObject& jo, const RoomSummary& rs) { - if (rs.joinedMemberCount != 0) - jo.insert(QStringLiteral("m.joined_member_count"), - rs.joinedMemberCount); - if (rs.invitedMemberCount != 0) - jo.insert(QStringLiteral("m.invited_member_count"), - rs.invitedMemberCount); - if (!rs.heroes.empty()) - jo.insert(QStringLiteral("m.heroes"), toJson(rs.heroes)); + addParam(jo, QStringLiteral("m.joined_member_count"), + rs.joinedMemberCount); + addParam(jo, QStringLiteral("m.invited_member_count"), + rs.invitedMemberCount); + addParam(jo, QStringLiteral("m.heroes"), rs.heroes); } void JsonObjectConverter::fillFrom(const QJsonObject& jo, RoomSummary& rs) { - rs.joinedMemberCount = fromJson(jo["m.joined_member_count"_ls]); - rs.joinedMemberCount = fromJson(jo["m.invited_member_count"_ls]); - rs.heroes = fromJson(jo["m.heroes"]); + fromJson(jo["m.joined_member_count"_ls], rs.joinedMemberCount); + fromJson(jo["m.invited_member_count"_ls], rs.invitedMemberCount); + fromJson(jo["m.heroes"], rs.heroes); } -bool RoomSummary::operator==(const RoomSummary& other) const +template +inline EventsArrayT load(const QJsonObject& batches, StrT keyName) { - return joinedMemberCount == other.joinedMemberCount && - invitedMemberCount == other.invitedMemberCount && - heroes == other.heroes; + return fromJson(batches[keyName].toObject().value("events"_ls)); } SyncRoomData::SyncRoomData(const QString& roomId_, JoinState joinState_, const QJsonObject& room_) : roomId(roomId_) , joinState(joinState_) - , summary(fromJson(room_["summary"].toObject())) + , summary(fromJson(room_["summary"])) , state(load(room_, joinState == JoinState::Invite ? "invite_state"_ls : "state"_ls)) { diff --git a/lib/syncdata.h b/lib/syncdata.h index 81a91ffcc..663553bcb 100644 --- a/lib/syncdata.h +++ b/lib/syncdata.h @@ -22,15 +22,26 @@ #include "events/stateevent.h" namespace QMatrixClient { + /// Room summary, as defined in MSC688 + /** + * Every member of this structure is an Omittable; as per the MSC, only + * changed values are sent from the server so if nothing is in the payload + * the respective member will be omitted. In particular, `heroes.omitted()` + * means that nothing has come from the server; heroes.value().isEmpty() + * means a peculiar case of a room with the only member - the current user. + */ struct RoomSummary { - int joinedMemberCount = 0; - int invitedMemberCount = 0; - QStringList heroes; //< mxids of users to take part in the room name + Omittable joinedMemberCount; + Omittable invitedMemberCount; + Omittable heroes; //< mxids of users to take part in the room name - bool operator==(const RoomSummary& other) const; - bool operator!=(const RoomSummary& other) const - { return !(*this == other); } + bool isEmpty() const; + /// Merge the contents of another RoomSummary object into this one + /// \return true, if the current object has changed; false otherwise + bool merge(const RoomSummary& other); + + friend QDebug operator<<(QDebug dbg, const RoomSummary& rs); }; template <> From 1678296d5b6190679a9ef950f9421945fa159f8f Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Sun, 9 Dec 2018 17:42:06 +0900 Subject: [PATCH 08/27] fromJson, fillFromJson: avoid overwriting pods if the JSON value is undefined --- lib/converters.h | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/lib/converters.h b/lib/converters.h index 6227902dd..af2be6450 100644 --- a/lib/converters.h +++ b/lib/converters.h @@ -110,7 +110,8 @@ namespace QMatrixClient template inline void fromJson(const QJsonValue& jv, T& pod) { - pod = fromJson(jv); + if (!jv.isUndefined()) + pod = fromJson(jv); } template @@ -123,13 +124,17 @@ namespace QMatrixClient template inline void fromJson(const QJsonValue& jv, Omittable& pod) { - pod = fromJson(jv); + if (jv.isUndefined()) + pod = none; + else + pod = fromJson(jv); } template inline void fillFromJson(const QJsonValue& jv, T& pod) { - JsonObjectConverter::fillFrom(jv.toObject(), pod); + if (jv.isObject()) + JsonObjectConverter::fillFrom(jv.toObject(), pod); } // JsonConverter<> specialisations From 9225eaec426ecd44a1c203e11e1aafe7772c46d7 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Sun, 9 Dec 2018 19:55:14 +0900 Subject: [PATCH 09/27] Room: track more changes; fix cache smashing upon restart Commit fd52459 introduced a regression rendering the cache unusable after a client restart (an empty state overwrites whatever state was in the cache). This commit contains the fix, along with more room change tracking. --- lib/room.cpp | 59 +++++++++++++++++++++++++++++++--------------------- lib/room.h | 7 ++++--- 2 files changed, 39 insertions(+), 27 deletions(-) diff --git a/lib/room.cpp b/lib/room.cpp index fec2dc189..ca5495ea6 100644 --- a/lib/room.cpp +++ b/lib/room.cpp @@ -215,12 +215,12 @@ class Room::Private */ void dropDuplicateEvents(RoomEvents& events) const; - void setLastReadEvent(User* u, QString eventId); + Changes setLastReadEvent(User* u, QString eventId); void updateUnreadCount(rev_iter_t from, rev_iter_t to); - void promoteReadMarker(User* u, rev_iter_t newMarker, - bool force = false); + Changes promoteReadMarker(User* u, rev_iter_t newMarker, + bool force = false); - void markMessagesAsRead(rev_iter_t upToMarker); + Changes markMessagesAsRead(rev_iter_t upToMarker); QString sendEvent(RoomEventPtr&& event); @@ -392,11 +392,11 @@ void Room::setJoinState(JoinState state) emit joinStateChanged(oldState, state); } -void Room::Private::setLastReadEvent(User* u, QString eventId) +Room::Changes Room::Private::setLastReadEvent(User* u, QString eventId) { auto& storedId = lastReadEventIds[u]; if (storedId == eventId) - return; + return Change::NoChange; eventIdReadUsers.remove(storedId, u); eventIdReadUsers.insert(eventId, u); swap(storedId, eventId); @@ -407,8 +407,9 @@ void Room::Private::setLastReadEvent(User* u, QString eventId) if (storedId != serverReadMarker) connection->callApi(id, storedId); emit q->readMarkerMoved(eventId, storedId); - connection->saveRoomState(q); + return Change::ReadMarkerChange; } + return Change::NoChange; } void Room::Private::updateUnreadCount(rev_iter_t from, rev_iter_t to) @@ -451,14 +452,15 @@ void Room::Private::updateUnreadCount(rev_iter_t from, rev_iter_t to) } } -void Room::Private::promoteReadMarker(User* u, rev_iter_t newMarker, bool force) +Room::Changes Room::Private::promoteReadMarker(User* u, rev_iter_t newMarker, + bool force) { Q_ASSERT_X(u, __FUNCTION__, "User* should not be nullptr"); Q_ASSERT(newMarker >= timeline.crbegin() && newMarker <= timeline.crend()); const auto prevMarker = q->readMarker(u); if (!force && prevMarker <= newMarker) // Remember, we deal with reverse iterators - return; + return Change::NoChange; Q_ASSERT(newMarker < timeline.crend()); @@ -467,7 +469,7 @@ void Room::Private::promoteReadMarker(User* u, rev_iter_t newMarker, bool force) auto eagerMarker = find_if(newMarker.base(), timeline.cend(), [=](const TimelineItem& ti) { return ti->senderId() != u->id(); }); - setLastReadEvent(u, (*(eagerMarker - 1))->id()); + auto changes = setLastReadEvent(u, (*(eagerMarker - 1))->id()); if (isLocalUser(u)) { const auto oldUnreadCount = unreadMessages; @@ -491,14 +493,16 @@ void Room::Private::promoteReadMarker(User* u, rev_iter_t newMarker, bool force) qCDebug(MAIN) << "Room" << displayname << "still has" << unreadMessages << "unread message(s)"; emit q->unreadMessagesChanged(q); + changes |= Change::UnreadNotifsChange; } } + return changes; } -void Room::Private::markMessagesAsRead(rev_iter_t upToMarker) +Room::Changes Room::Private::markMessagesAsRead(rev_iter_t upToMarker) { const auto prevMarker = q->readMarker(); - promoteReadMarker(q->localUser(), upToMarker); + auto changes = promoteReadMarker(q->localUser(), upToMarker); if (prevMarker != upToMarker) qCDebug(MAIN) << "Marked messages as read until" << *q->readMarker(); @@ -514,6 +518,7 @@ void Room::Private::markMessagesAsRead(rev_iter_t upToMarker) break; } } + return changes; } void Room::markMessagesAsRead(QString uptoEventId) @@ -1193,7 +1198,7 @@ void Room::updateData(SyncRoomData&& data, bool fromCache) d->updateDisplayname(); for( auto&& ephemeralEvent: data.ephemeral ) - processEphemeralEvent(move(ephemeralEvent)); + roomChanges |= processEphemeralEvent(move(ephemeralEvent)); // See https://github.com/QMatrixClient/libqmatrixclient/wiki/unread_count if (data.unreadCount != -2 && data.unreadCount != d->unreadMessages) @@ -1758,9 +1763,9 @@ Room::Changes Room::Private::addNewMessageEvents(RoomEvents&& events) // clients historically expect. This may eventually change though if we // postulate that the current state is only current between syncs but not // within a sync. - Changes stateChanges = Change::NoChange; + Changes roomChanges = Change::NoChange; for (const auto& eptr: events) - stateChanges |= q->processStateEvent(*eptr); + roomChanges |= q->processStateEvent(*eptr); auto timelineSize = timeline.size(); auto totalInserted = 0; @@ -1820,16 +1825,17 @@ Room::Changes Room::Private::addNewMessageEvents(RoomEvents&& events) auto firstWriter = q->user((*from)->senderId()); if (q->readMarker(firstWriter) != timeline.crend()) { - promoteReadMarker(firstWriter, rev_iter_t(from) - 1); + roomChanges |= promoteReadMarker(firstWriter, rev_iter_t(from) - 1); qCDebug(MAIN) << "Auto-promoted read marker for" << firstWriter->id() << "to" << *q->readMarker(firstWriter); } updateUnreadCount(timeline.crbegin(), rev_iter_t(from)); + roomChanges |= Change::UnreadNotifsChange; } Q_ASSERT(timeline.size() == timelineSize + totalInserted); - return stateChanges; + return roomChanges; } void Room::Private::addHistoricalMessageEvents(RoomEvents&& events) @@ -1948,8 +1954,9 @@ Room::Changes Room::processStateEvent(const RoomEvent& e) ); } -void Room::processEphemeralEvent(EventPtr&& event) +Room::Changes Room::processEphemeralEvent(EventPtr&& event) { + Changes changes = NoChange; QElapsedTimer et; et.start(); if (auto* evt = eventCast(event)) { @@ -1988,7 +1995,7 @@ void Room::processEphemeralEvent(EventPtr&& event) continue; // FIXME, #185 auto u = user(r.userId); if (memberJoinState(u) == JoinState::Join) - d->promoteReadMarker(u, newMarker); + changes |= d->promoteReadMarker(u, newMarker); } } else { @@ -2005,7 +2012,7 @@ void Room::processEphemeralEvent(EventPtr&& event) auto u = user(r.userId); if (memberJoinState(u) == JoinState::Join && readMarker(u) == timelineEdge()) - d->setLastReadEvent(u, p.evtId); + changes |= d->setLastReadEvent(u, p.evtId); } } } @@ -2015,12 +2022,17 @@ void Room::processEphemeralEvent(EventPtr&& event) << evt->eventsWithReceipts().size() << "event(s) with" << totalReceipts << "receipt(s)," << et; } + return changes; } Room::Changes Room::processAccountDataEvent(EventPtr&& event) { + Changes changes = NoChange; if (auto* evt = eventCast(event)) + { d->setTags(evt->tags()); + changes |= Change::TagsChange; + } if (auto* evt = eventCast(event)) { @@ -2028,10 +2040,9 @@ Room::Changes Room::processAccountDataEvent(EventPtr&& event) qCDebug(MAIN) << "Server-side read marker at" << readEventId; d->serverReadMarker = readEventId; const auto newMarker = findInTimeline(readEventId); - if (newMarker != timelineEdge()) - d->markMessagesAsRead(newMarker); - else - d->setLastReadEvent(localUser(), readEventId); + changes |= newMarker != timelineEdge() + ? d->markMessagesAsRead(newMarker) + : d->setLastReadEvent(localUser(), readEventId); } // For all account data events auto& currentData = d->accountData[event->matrixType()]; diff --git a/lib/room.h b/lib/room.h index 97d8454ad..7b5be3317 100644 --- a/lib/room.h +++ b/lib/room.h @@ -120,8 +120,9 @@ namespace QMatrixClient EncryptionOn = 0x100, AccountDataChange = 0x200, SummaryChange = 0x400, - OtherChange = 0x1000, - AnyChange = 0x1FFF + ReadMarkerChange = 0x800, + OtherChange = 0x8000, + AnyChange = 0xFFFF }; Q_DECLARE_FLAGS(Changes, Change) Q_FLAG(Changes) @@ -467,7 +468,7 @@ namespace QMatrixClient protected: /// Returns true if any of room names/aliases has changed virtual Changes processStateEvent(const RoomEvent& e); - virtual void processEphemeralEvent(EventPtr&& event); + virtual Changes processEphemeralEvent(EventPtr&& event); virtual Changes processAccountDataEvent(EventPtr&& event); virtual void onAddNewTimelineEvents(timeline_iter_t /*from*/) { } virtual void onAddHistoricalTimelineEvents(rev_iter_t /*from*/) { } From 9b3e437f3268e251f1950000b210cf849d49c24e Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Sun, 9 Dec 2018 20:04:45 +0900 Subject: [PATCH 10/27] Room: defer memberListChanged(); track room summary changes This concludes beta-version of lazy-loading support in libQMatrixClient (#253). --- lib/room.cpp | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/lib/room.cpp b/lib/room.cpp index ca5495ea6..84072d3ef 100644 --- a/lib/room.cpp +++ b/lib/room.cpp @@ -282,9 +282,6 @@ Room::Room(Connection* connection, QString id, JoinState initialJoinState) // See "Accessing the Public Class" section in // https://marcmutz.wordpress.com/translated-articles/pimp-my-pimpl-%E2%80%94-reloaded/ d->q = this; - connect(this, &Room::userAdded, this, &Room::memberListChanged); - connect(this, &Room::userRemoved, this, &Room::memberListChanged); - connect(this, &Room::memberRenamed, this, &Room::memberListChanged); qCDebug(MAIN) << "New" << toCString(initialJoinState) << "Room:" << id; } @@ -1018,12 +1015,9 @@ Room::Changes Room::Private::setSummary(RoomSummary&& newSummary) { if (!summary.merge(newSummary)) return Change::NoChange; - summary = move(newSummary); - qCDebug(MAIN).nospace() - << "Updated room summary for" << q->objectName() - << ": joined " << summary.joinedMemberCount - << ", invited " << summary.invitedMemberCount - << ", heroes: " << summary.heroes.value().join(','); + qCDebug(MAIN).nospace().noquote() + << "Updated room summary for " << q->objectName() << ": " << summary; + emit q->memberListChanged(); return Change::SummaryChange; } @@ -1194,7 +1188,10 @@ void Room::updateData(SyncRoomData&& data, bool fromCache) if (roomChanges&NameChange) emit namesChanged(this); - d->setSummary(move(data.summary)); + if (roomChanges&MembersChange) + emit memberListChanged(); + + roomChanges |= d->setSummary(move(data.summary)); d->updateDisplayname(); for( auto&& ephemeralEvent: data.ephemeral ) From 501c79f55b5f6cb5df80993330d0b1ae1764024a Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Mon, 10 Dec 2018 16:32:33 +0900 Subject: [PATCH 11/27] Room::getPreviousContent: use early return ...instead of the entire function body wrapped in an if block. --- lib/room.cpp | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/lib/room.cpp b/lib/room.cpp index 84072d3ef..3cbd22713 100644 --- a/lib/room.cpp +++ b/lib/room.cpp @@ -1440,18 +1440,18 @@ void Room::getPreviousContent(int limit) void Room::Private::getPreviousContent(int limit) { - if( !isJobRunning(eventsHistoryJob) ) - { - eventsHistoryJob = - connection->callApi(id, prevBatch, "b", "", limit); - emit q->eventsHistoryJobChanged(); - connect( eventsHistoryJob, &BaseJob::success, q, [=] { - prevBatch = eventsHistoryJob->end(); - addHistoricalMessageEvents(eventsHistoryJob->chunk()); - }); - connect( eventsHistoryJob, &QObject::destroyed, - q, &Room::eventsHistoryJobChanged); - } + if (isJobRunning(eventsHistoryJob)) + return; + + eventsHistoryJob = + connection->callApi(id, prevBatch, "b", "", limit); + emit q->eventsHistoryJobChanged(); + connect( eventsHistoryJob, &BaseJob::success, q, [=] { + prevBatch = eventsHistoryJob->end(); + addHistoricalMessageEvents(eventsHistoryJob->chunk()); + }); + connect( eventsHistoryJob, &QObject::destroyed, + q, &Room::eventsHistoryJobChanged); } void Room::inviteToRoom(const QString& memberId) From c6720cc8bb8d45ab4d2b7390f076d50cb59cb8d3 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Tue, 11 Dec 2018 12:23:33 +0900 Subject: [PATCH 12/27] Expose Connection::nextBatchToken() --- lib/connection.cpp | 5 +++++ lib/connection.h | 1 + 2 files changed, 6 insertions(+) diff --git a/lib/connection.cpp b/lib/connection.cpp index 28156d114..76e61ed16 100644 --- a/lib/connection.cpp +++ b/lib/connection.cpp @@ -412,6 +412,11 @@ void Connection::stopSync() } } +QString Connection::nextBatchToken() const +{ + return d->data->lastEvent(); +} + PostReceiptJob* Connection::postReceipt(Room* room, RoomEvent* event) const { return callApi(room->id(), "m.read", event->id()); diff --git a/lib/connection.h b/lib/connection.h index 220f6c8f1..9a94aad6f 100644 --- a/lib/connection.h +++ b/lib/connection.h @@ -390,6 +390,7 @@ namespace QMatrixClient void sync(int timeout = -1); void stopSync(); + QString nextBatchToken() const; virtual MediaThumbnailJob* getThumbnail(const QString& mediaId, QSize requestedSize, RunningPolicy policy = BackgroundRequest) const; From 0e67d1e92285e0c03e4e34ad747490ae4dc5482d Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Tue, 11 Dec 2018 12:26:05 +0900 Subject: [PATCH 13/27] RoomMemberEvent: properly integrate with GetMembersByRoomJob GetMembersByRoomJob was dysfunctional so far, creating "unknown RoomMemberEvents" instead of proper ones. Now that we need it for lazy- loading, it's fixed! --- lib/events/roommemberevent.h | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/lib/events/roommemberevent.h b/lib/events/roommemberevent.h index 149d74f8d..b82240330 100644 --- a/lib/events/roommemberevent.h +++ b/lib/events/roommemberevent.h @@ -60,8 +60,16 @@ namespace QMatrixClient : StateEvent(typeId(), matrixTypeId(), c) { } - // This is a special constructor enabling RoomMemberEvent to be - // a base class for more specific member events. + /// A special constructor to create unknown RoomMemberEvents + /** + * This is needed in order to use RoomMemberEvent as a "base event + * class" in cases like GetMembersByRoomJob when RoomMemberEvents + * (rather than RoomEvents or StateEvents) are resolved from JSON. + * For such cases loadEvent<> requires an underlying class to be + * constructible with unknownTypeId() instead of its genuine id. + * Don't use it directly. + * \sa GetMembersByRoomJob, loadEvent, unknownTypeId + */ RoomMemberEvent(Type type, const QJsonObject& fullJson) : StateEvent(type, fullJson) { } @@ -81,6 +89,18 @@ namespace QMatrixClient private: REGISTER_ENUM(MembershipType) }; + + template <> + class EventFactory + { + public: + static event_ptr_tt make(const QJsonObject& json, + const QString&) + { + return makeEvent(json); + } + }; + REGISTER_EVENT_TYPE(RoomMemberEvent) DEFINE_EVENTTYPE_ALIAS(RoomMember, RoomMemberEvent) } // namespace QMatrixClient From f0bd24a830aef3405994849ce413e2d488f75429 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Tue, 11 Dec 2018 12:29:57 +0900 Subject: [PATCH 14/27] Make Room::setDisplayed() trigger loading all members Closes #253. --- lib/room.cpp | 39 +++++++++++++++++++++++++++++++++++---- 1 file changed, 35 insertions(+), 4 deletions(-) diff --git a/lib/room.cpp b/lib/room.cpp index 3cbd22713..439bec0f1 100644 --- a/lib/room.cpp +++ b/lib/room.cpp @@ -27,6 +27,7 @@ #include "csapi/account-data.h" #include "csapi/room_state.h" #include "csapi/room_send.h" +#include "csapi/rooms.h" #include "csapi/tags.h" #include "events/simplestateevents.h" #include "events/roomavatarevent.h" @@ -121,6 +122,7 @@ class Room::Private std::unordered_map accountData; QString prevBatch; QPointer eventsHistoryJob; + QPointer allMembersJob; struct FileTransferPrivateInfo { @@ -222,6 +224,8 @@ class Room::Private Changes markMessagesAsRead(rev_iter_t upToMarker); + void getAllMembers(); + QString sendEvent(RoomEventPtr&& event); template @@ -588,6 +592,36 @@ Room::rev_iter_t Room::findInTimeline(const QString& evtId) const return timelineEdge(); } +void Room::Private::getAllMembers() +{ + // If already loaded or already loading, there's nothing to do here. + if (q->joinedCount() <= membersMap.size() || isJobRunning(allMembersJob)) + return; + + allMembersJob = connection->callApi( + id, connection->nextBatchToken(), "join"); + auto nextIndex = timeline.empty() ? 0 : timeline.back().index() + 1; + connect( allMembersJob, &BaseJob::success, q, [=] { + Q_ASSERT(timeline.empty() || nextIndex <= q->maxTimelineIndex() + 1); + Changes roomChanges = NoChange; + for (auto&& e: allMembersJob->chunk()) + { + const auto& evt = *e; + baseState[{evt.matrixType(),evt.stateKey()}] = move(e); + roomChanges |= q->processStateEvent(evt); + } + // Replay member events that arrived after the point for which + // the full members list was requested. + if (!timeline.empty() ) + for (auto it = q->findInTimeline(nextIndex).base(); + it != timeline.cend(); ++it) + if (is(**it)) + roomChanges |= q->processStateEvent(**it); + if (roomChanges&MembersChange) + emit q->memberListChanged(); + }); +} + bool Room::displayed() const { return d->displayed; @@ -604,10 +638,7 @@ void Room::setDisplayed(bool displayed) { resetHighlightCount(); resetNotificationCount(); -// if (d->lazyLoaded) -// { -// // TODO: Get all members -// } + d->getAllMembers(); } } From 095444aff98ac56663bb205837a57e746d950f3b Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Wed, 12 Dec 2018 13:20:05 +0900 Subject: [PATCH 15/27] Room::allMembersLoaded(); more doc-comments --- lib/room.cpp | 1 + lib/room.h | 10 ++++++++++ 2 files changed, 11 insertions(+) diff --git a/lib/room.cpp b/lib/room.cpp index 439bec0f1..7232741a7 100644 --- a/lib/room.cpp +++ b/lib/room.cpp @@ -619,6 +619,7 @@ void Room::Private::getAllMembers() roomChanges |= q->processStateEvent(**it); if (roomChanges&MembersChange) emit q->memberListChanged(); + emit q->allMembersLoaded(); }); } diff --git a/lib/room.h b/lib/room.h index 7b5be3317..ba1eaa481 100644 --- a/lib/room.h +++ b/lib/room.h @@ -228,6 +228,13 @@ namespace QMatrixClient rev_iter_t findInTimeline(const QString& evtId) const; bool displayed() const; + /// Mark the room as currently displayed to the user + /** + * Marking the room displayed causes the room to obtain the full + * list of members if it's been lazy-loaded before; in the future + * it may do more things bound to "screen time" of the room, e.g. + * measure that "screen time". + */ void setDisplayed(bool displayed = true); QString firstDisplayedEventId() const; rev_iter_t firstDisplayedMarker() const; @@ -431,6 +438,9 @@ namespace QMatrixClient void memberAboutToRename(User* user, QString newName); void memberRenamed(User* user); void memberListChanged(); + /// The previously lazy-loaded members list is now loaded entirely + /// \sa setDisplayed + void allMembersLoaded(); void encryption(); void joinStateChanged(JoinState oldState, JoinState newState); From c46663aece5e001543b07d3ed901e64c38be4172 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Wed, 12 Dec 2018 13:20:46 +0900 Subject: [PATCH 16/27] qmc-example: Use lazy-loading; check full-loading upon setDisplayed --- examples/qmc-example.cpp | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/examples/qmc-example.cpp b/examples/qmc-example.cpp index 9c86d4a9b..bdb9ef3f6 100644 --- a/examples/qmc-example.cpp +++ b/examples/qmc-example.cpp @@ -26,6 +26,7 @@ class QMCTest : public QObject void setup(const QString& testRoomName); void onNewRoom(Room* r); void startTests(); + void loadMembers(); void sendMessage(); void addAndRemoveTag(); void sendAndRedact(); @@ -80,6 +81,7 @@ void QMCTest::setup(const QString& testRoomName) cout << "Access token: " << c->accessToken().toStdString() << endl; // Setting up sync loop + c->setLazyLoading(true); c->sync(); connect(c.data(), &Connection::syncDone, c.data(), [this,testRoomName] { cout << "Sync complete, " @@ -142,12 +144,41 @@ void QMCTest::onNewRoom(Room* r) void QMCTest::startTests() { cout << "Starting tests" << endl; + loadMembers(); sendMessage(); addAndRemoveTag(); sendAndRedact(); markDirectChat(); } +void QMCTest::loadMembers() +{ + running.push_back("Loading members"); + // The dedicated qmc-test room is too small to test + // lazy-loading-then-full-loading; use #test:matrix.org instead. + // TODO: #264 + auto* r = c->room(QStringLiteral("!vfFxDRtZSSdspfTSEr:matrix.org")); + if (!r) + { + cout << "#test:matrix.org is not found in the test user's rooms" << endl; + QMC_CHECK("Loading members", false); + return; + } + // It's not exactly correct because an arbitrary server might not support + // lazy loading; but in the absence of capabilities framework we assume + // it does. + if (r->memberNames().size() < r->joinedCount()) + { + cout << "Lazy loading doesn't seem to be enabled" << endl; + QMC_CHECK("Loading members", false); + return; + } + r->setDisplayed(); + connect(r, &Room::allMembersLoaded, [this] { + QMC_CHECK("Loading members", true); + }); +} + void QMCTest::sendMessage() { running.push_back("Message sending"); From 393485594b2bb7ab3a7ddc7e49c8cae1105bf77e Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Wed, 12 Dec 2018 13:37:50 +0900 Subject: [PATCH 17/27] Room: more doc-comments --- lib/room.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/lib/room.h b/lib/room.h index ba1eaa481..f7eb224e9 100644 --- a/lib/room.h +++ b/lib/room.h @@ -437,6 +437,13 @@ namespace QMatrixClient void userRemoved(User* user); void memberAboutToRename(User* user, QString newName); void memberRenamed(User* user); + /// The list of members has changed + /** Emitted no more than once per sync, this is a good signal to + * for cases when some action should be done upon any change in + * the member list. If you need per-item granularity you should use + * userAdded, userRemoved and memberAboutToRename / memberRenamed + * instead. + */ void memberListChanged(); /// The previously lazy-loaded members list is now loaded entirely /// \sa setDisplayed From c33680b62d968e1e0e2abcdc084eaecf5dd94d2f Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Wed, 12 Dec 2018 16:50:00 +0900 Subject: [PATCH 18/27] csapi/rooms.h: regenerate to update doc-comments --- lib/csapi/rooms.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/csapi/rooms.h b/lib/csapi/rooms.h index 415aa4ed8..b4d3d9b69 100644 --- a/lib/csapi/rooms.h +++ b/lib/csapi/rooms.h @@ -160,9 +160,9 @@ namespace QMatrixClient * The room to get the member events for. * \param at * The token defining the timeline position as-of which to return - * the list of members. This token can be obtained from - * a ``prev_batch`` token returned for each room by the sync API, or - * from a ``start`` or ``end`` token returned by a /messages request. + * the list of members. This token can be obtained from a batch token + * returned for each room by the sync API, or from + * a ``start``/``end`` token returned by a ``/messages`` request. * \param membership * Only return users with the specified membership * \param notMembership From cda9a0f02cc3e779da378d0328f9d24c708b2600 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Wed, 12 Dec 2018 17:05:26 +0900 Subject: [PATCH 19/27] gtad.yaml: use more compact definitions where possible --- lib/csapi/gtad.yaml | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/lib/csapi/gtad.yaml b/lib/csapi/gtad.yaml index cb5e553ce..ca4a0fb9a 100644 --- a/lib/csapi/gtad.yaml +++ b/lib/csapi/gtad.yaml @@ -86,12 +86,9 @@ analyzer: - /m\.room\.member$/: type: "EventsArray" imports: '"events/roommemberevent.h"' - - /state_event.yaml$/: - type: StateEvents - - /room_event.yaml$/: - type: RoomEvents - - /event.yaml$/: - type: Events + - /state_event.yaml$/: StateEvents + - /room_event.yaml$/: RoomEvents + - /event.yaml$/: Events - //: { type: "QVector<{{1}}>", imports: } - map: # `additionalProperties` in OpenAPI - RoomState: From 91b20cae3f60bf8c3b2b66c28911feca2d1d575d Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Wed, 12 Dec 2018 17:05:26 +0900 Subject: [PATCH 20/27] gtad.yaml: use more compact definitions where possible --- lib/csapi/gtad.yaml | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/lib/csapi/gtad.yaml b/lib/csapi/gtad.yaml index cb5e553ce..ca4a0fb9a 100644 --- a/lib/csapi/gtad.yaml +++ b/lib/csapi/gtad.yaml @@ -86,12 +86,9 @@ analyzer: - /m\.room\.member$/: type: "EventsArray" imports: '"events/roommemberevent.h"' - - /state_event.yaml$/: - type: StateEvents - - /room_event.yaml$/: - type: RoomEvents - - /event.yaml$/: - type: Events + - /state_event.yaml$/: StateEvents + - /room_event.yaml$/: RoomEvents + - /event.yaml$/: Events - //: { type: "QVector<{{1}}>", imports: } - map: # `additionalProperties` in OpenAPI - RoomState: From 5b06b165ba2adec50099452bcf4c5f20009423ad Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Thu, 13 Dec 2018 07:23:50 +0900 Subject: [PATCH 21/27] gtad.yaml: wrap bool in Omittable<> Case in point: https://github.com/matrix-org/matrix-doc/issues/1750 --- lib/csapi/gtad.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/csapi/gtad.yaml b/lib/csapi/gtad.yaml index ca4a0fb9a..c6ea8a136 100644 --- a/lib/csapi/gtad.yaml +++ b/lib/csapi/gtad.yaml @@ -38,7 +38,7 @@ analyzer: - number: - float: float - //: double - - boolean: { type: bool, omittedValue: 'false' } + - boolean: bool - string: - byte: &ByteStream type: QIODevice* From 8dcda23ed210151904c9137067626eddae683822 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Thu, 13 Dec 2018 07:47:58 +0900 Subject: [PATCH 22/27] Regenerate csapi/ --- lib/csapi/administrative_contact.cpp | 2 +- lib/csapi/administrative_contact.h | 2 +- lib/csapi/create_room.cpp | 2 +- lib/csapi/create_room.h | 2 +- lib/csapi/definitions/push_rule.h | 4 ++-- lib/csapi/definitions/room_event_filter.cpp | 6 ------ lib/csapi/definitions/room_event_filter.h | 8 ++------ lib/csapi/definitions/sync_filter.h | 2 +- lib/csapi/joining.h | 4 ++-- lib/csapi/list_public_rooms.cpp | 2 +- lib/csapi/list_public_rooms.h | 2 +- lib/csapi/presence.cpp | 4 ++-- lib/csapi/presence.h | 2 +- lib/csapi/pusher.cpp | 2 +- lib/csapi/pusher.h | 2 +- lib/csapi/registration.cpp | 6 +++--- lib/csapi/registration.h | 4 ++-- lib/csapi/search.h | 4 ++-- 18 files changed, 25 insertions(+), 35 deletions(-) diff --git a/lib/csapi/administrative_contact.cpp b/lib/csapi/administrative_contact.cpp index f62002a6c..20a1de422 100644 --- a/lib/csapi/administrative_contact.cpp +++ b/lib/csapi/administrative_contact.cpp @@ -86,7 +86,7 @@ namespace QMatrixClient static const auto Post3PIDsJobName = QStringLiteral("Post3PIDsJob"); -Post3PIDsJob::Post3PIDsJob(const ThreePidCredentials& threePidCreds, bool bind) +Post3PIDsJob::Post3PIDsJob(const ThreePidCredentials& threePidCreds, Omittable bind) : BaseJob(HttpVerb::Post, Post3PIDsJobName, basePath % "/account/3pid") { diff --git a/lib/csapi/administrative_contact.h b/lib/csapi/administrative_contact.h index 3fb3d44c6..02aeee4d3 100644 --- a/lib/csapi/administrative_contact.h +++ b/lib/csapi/administrative_contact.h @@ -113,7 +113,7 @@ namespace QMatrixClient * identifier to the account's Matrix ID with the passed identity * server. Default: ``false``. */ - explicit Post3PIDsJob(const ThreePidCredentials& threePidCreds, bool bind = false); + explicit Post3PIDsJob(const ThreePidCredentials& threePidCreds, Omittable bind = none); }; /// Deletes a third party identifier from the user's account diff --git a/lib/csapi/create_room.cpp b/lib/csapi/create_room.cpp index 36f837273..236daf182 100644 --- a/lib/csapi/create_room.cpp +++ b/lib/csapi/create_room.cpp @@ -43,7 +43,7 @@ class CreateRoomJob::Private static const auto CreateRoomJobName = QStringLiteral("CreateRoomJob"); -CreateRoomJob::CreateRoomJob(const QString& visibility, const QString& roomAliasName, const QString& name, const QString& topic, const QStringList& invite, const QVector& invite3pid, const QString& roomVersion, const QJsonObject& creationContent, const QVector& initialState, const QString& preset, bool isDirect, const QJsonObject& powerLevelContentOverride) +CreateRoomJob::CreateRoomJob(const QString& visibility, const QString& roomAliasName, const QString& name, const QString& topic, const QStringList& invite, const QVector& invite3pid, const QString& roomVersion, const QJsonObject& creationContent, const QVector& initialState, const QString& preset, Omittable isDirect, const QJsonObject& powerLevelContentOverride) : BaseJob(HttpVerb::Post, CreateRoomJobName, basePath % "/createRoom") , d(new Private) diff --git a/lib/csapi/create_room.h b/lib/csapi/create_room.h index a0a64df0e..d7c01d003 100644 --- a/lib/csapi/create_room.h +++ b/lib/csapi/create_room.h @@ -216,7 +216,7 @@ namespace QMatrixClient * event content prior to it being sent to the room. Defaults to * overriding nothing. */ - explicit CreateRoomJob(const QString& visibility = {}, const QString& roomAliasName = {}, const QString& name = {}, const QString& topic = {}, const QStringList& invite = {}, const QVector& invite3pid = {}, const QString& roomVersion = {}, const QJsonObject& creationContent = {}, const QVector& initialState = {}, const QString& preset = {}, bool isDirect = false, const QJsonObject& powerLevelContentOverride = {}); + explicit CreateRoomJob(const QString& visibility = {}, const QString& roomAliasName = {}, const QString& name = {}, const QString& topic = {}, const QStringList& invite = {}, const QVector& invite3pid = {}, const QString& roomVersion = {}, const QJsonObject& creationContent = {}, const QVector& initialState = {}, const QString& preset = {}, Omittable isDirect = none, const QJsonObject& powerLevelContentOverride = {}); ~CreateRoomJob() override; // Result properties diff --git a/lib/csapi/definitions/push_rule.h b/lib/csapi/definitions/push_rule.h index 5f52876df..2ab68cb13 100644 --- a/lib/csapi/definitions/push_rule.h +++ b/lib/csapi/definitions/push_rule.h @@ -7,10 +7,10 @@ #include "converters.h" #include "csapi/definitions/push_condition.h" -#include "converters.h" +#include #include #include -#include +#include "converters.h" namespace QMatrixClient { diff --git a/lib/csapi/definitions/room_event_filter.cpp b/lib/csapi/definitions/room_event_filter.cpp index 8cd2ded7e..f6f1e5cb3 100644 --- a/lib/csapi/definitions/room_event_filter.cpp +++ b/lib/csapi/definitions/room_event_filter.cpp @@ -12,8 +12,6 @@ QJsonObject QMatrixClient::toJson(const RoomEventFilter& pod) addParam(jo, QStringLiteral("not_rooms"), pod.notRooms); addParam(jo, QStringLiteral("rooms"), pod.rooms); addParam(jo, QStringLiteral("contains_url"), pod.containsUrl); - addParam(jo, QStringLiteral("lazy_load_members"), pod.lazyLoadMembers); - addParam(jo, QStringLiteral("include_redundant_members"), pod.includeRedundantMembers); return jo; } @@ -26,10 +24,6 @@ RoomEventFilter FromJsonObject::operator()(const QJsonObject& j fromJson(jo.value("rooms"_ls)); result.containsUrl = fromJson(jo.value("contains_url"_ls)); - result.lazyLoadMembers = - fromJson(jo.value("lazy_load_members"_ls)); - result.includeRedundantMembers = - fromJson(jo.value("include_redundant_members"_ls)); return result; } diff --git a/lib/csapi/definitions/room_event_filter.h b/lib/csapi/definitions/room_event_filter.h index 87f01189f..00f1e1fbb 100644 --- a/lib/csapi/definitions/room_event_filter.h +++ b/lib/csapi/definitions/room_event_filter.h @@ -19,12 +19,8 @@ namespace QMatrixClient QStringList notRooms; /// A list of room IDs to include. If this list is absent then all rooms are included. QStringList rooms; - /// If ``true``, includes only events with a ``url`` key in their content. If ``false``, excludes those events. Defaults to ``false``. - bool containsUrl; - /// If ``true``, the only ``m.room.member`` events returned in the ``state`` section of the ``/sync`` response are those which are definitely necessary for a client to display the ``sender`` of the timeline events in that response. If ``false``, ``m.room.member`` events are not filtered. By default, servers should suppress duplicate redundant lazy-loaded ``m.room.member`` events from being sent to a given client across multiple calls to ``/sync``, given that most clients cache membership events (see include_redundant_members to change this behaviour). - bool lazyLoadMembers; - /// If ``true``, the ``state`` section of the ``/sync`` response will always contain the ``m.room.member`` events required to display the ``sender`` of the timeline events in that response, assuming ``lazy_load_members`` is enabled. This means that redundant duplicate member events may be returned across multiple calls to ``/sync``. This is useful for naive clients who never track membership data. If ``false``, duplicate ``m.room.member`` events may be suppressed by the server across multiple calls to ``/sync``. If ``lazy_load_members`` is ``false`` this field is ignored. - bool includeRedundantMembers; + /// If ``true``, includes only events with a ``url`` key in their content. If ``false``, excludes those events. If omitted, ``url`` key is not considered for filtering. + Omittable containsUrl; }; QJsonObject toJson(const RoomEventFilter& pod); diff --git a/lib/csapi/definitions/sync_filter.h b/lib/csapi/definitions/sync_filter.h index ca275a9a7..592038dc5 100644 --- a/lib/csapi/definitions/sync_filter.h +++ b/lib/csapi/definitions/sync_filter.h @@ -24,7 +24,7 @@ namespace QMatrixClient /// The events that aren't recorded in the room history, e.g. typing and receipts, to include for rooms. Omittable ephemeral; /// Include rooms that the user has left in the sync, default false - bool includeLeave; + Omittable includeLeave; /// The state events to include for rooms. Omittable state; /// The message and state update events to include for rooms. diff --git a/lib/csapi/joining.h b/lib/csapi/joining.h index 137afbfcd..52c8ea426 100644 --- a/lib/csapi/joining.h +++ b/lib/csapi/joining.h @@ -59,7 +59,7 @@ namespace QMatrixClient // Result properties - /// The joined room id + /// The joined room ID. const QString& roomId() const; protected: @@ -138,7 +138,7 @@ namespace QMatrixClient // Result properties - /// The joined room id + /// The joined room ID. const QString& roomId() const; protected: diff --git a/lib/csapi/list_public_rooms.cpp b/lib/csapi/list_public_rooms.cpp index 2fdb2005d..4e3661e16 100644 --- a/lib/csapi/list_public_rooms.cpp +++ b/lib/csapi/list_public_rooms.cpp @@ -131,7 +131,7 @@ BaseJob::Query queryToQueryPublicRooms(const QString& server) static const auto QueryPublicRoomsJobName = QStringLiteral("QueryPublicRoomsJob"); -QueryPublicRoomsJob::QueryPublicRoomsJob(const QString& server, Omittable limit, const QString& since, const Omittable& filter, bool includeAllNetworks, const QString& thirdPartyInstanceId) +QueryPublicRoomsJob::QueryPublicRoomsJob(const QString& server, Omittable limit, const QString& since, const Omittable& filter, Omittable includeAllNetworks, const QString& thirdPartyInstanceId) : BaseJob(HttpVerb::Post, QueryPublicRoomsJobName, basePath % "/publicRooms", queryToQueryPublicRooms(server)) diff --git a/lib/csapi/list_public_rooms.h b/lib/csapi/list_public_rooms.h index 8401c1346..a64987456 100644 --- a/lib/csapi/list_public_rooms.h +++ b/lib/csapi/list_public_rooms.h @@ -156,7 +156,7 @@ namespace QMatrixClient * The specific third party network/protocol to request from the * homeserver. Can only be used if ``include_all_networks`` is false. */ - explicit QueryPublicRoomsJob(const QString& server = {}, Omittable limit = none, const QString& since = {}, const Omittable& filter = none, bool includeAllNetworks = false, const QString& thirdPartyInstanceId = {}); + explicit QueryPublicRoomsJob(const QString& server = {}, Omittable limit = none, const QString& since = {}, const Omittable& filter = none, Omittable includeAllNetworks = none, const QString& thirdPartyInstanceId = {}); ~QueryPublicRoomsJob() override; // Result properties diff --git a/lib/csapi/presence.cpp b/lib/csapi/presence.cpp index 7aba8b61d..460e2a764 100644 --- a/lib/csapi/presence.cpp +++ b/lib/csapi/presence.cpp @@ -30,7 +30,7 @@ class GetPresenceJob::Private QString presence; Omittable lastActiveAgo; QString statusMsg; - bool currentlyActive; + Omittable currentlyActive; }; QUrl GetPresenceJob::makeRequestUrl(QUrl baseUrl, const QString& userId) @@ -65,7 +65,7 @@ const QString& GetPresenceJob::statusMsg() const return d->statusMsg; } -bool GetPresenceJob::currentlyActive() const +Omittable GetPresenceJob::currentlyActive() const { return d->currentlyActive; } diff --git a/lib/csapi/presence.h b/lib/csapi/presence.h index 86b9d395b..c8f80357f 100644 --- a/lib/csapi/presence.h +++ b/lib/csapi/presence.h @@ -65,7 +65,7 @@ namespace QMatrixClient /// The state message for this user if one was set. const QString& statusMsg() const; /// Whether the user is currently active - bool currentlyActive() const; + Omittable currentlyActive() const; protected: Status parseJson(const QJsonDocument& data) override; diff --git a/lib/csapi/pusher.cpp b/lib/csapi/pusher.cpp index d20db88ad..0ca133683 100644 --- a/lib/csapi/pusher.cpp +++ b/lib/csapi/pusher.cpp @@ -107,7 +107,7 @@ namespace QMatrixClient static const auto PostPusherJobName = QStringLiteral("PostPusherJob"); -PostPusherJob::PostPusherJob(const QString& pushkey, const QString& kind, const QString& appId, const QString& appDisplayName, const QString& deviceDisplayName, const QString& lang, const PusherData& data, const QString& profileTag, bool append) +PostPusherJob::PostPusherJob(const QString& pushkey, const QString& kind, const QString& appId, const QString& appDisplayName, const QString& deviceDisplayName, const QString& lang, const PusherData& data, const QString& profileTag, Omittable append) : BaseJob(HttpVerb::Post, PostPusherJobName, basePath % "/pushers/set") { diff --git a/lib/csapi/pusher.h b/lib/csapi/pusher.h index 2b5061833..da3303fee 100644 --- a/lib/csapi/pusher.h +++ b/lib/csapi/pusher.h @@ -164,6 +164,6 @@ namespace QMatrixClient * other pushers with the same App ID and pushkey for different * users. The default is ``false``. */ - explicit PostPusherJob(const QString& pushkey, const QString& kind, const QString& appId, const QString& appDisplayName, const QString& deviceDisplayName, const QString& lang, const PusherData& data, const QString& profileTag = {}, bool append = false); + explicit PostPusherJob(const QString& pushkey, const QString& kind, const QString& appId, const QString& appDisplayName, const QString& deviceDisplayName, const QString& lang, const PusherData& data, const QString& profileTag = {}, Omittable append = none); }; } // namespace QMatrixClient diff --git a/lib/csapi/registration.cpp b/lib/csapi/registration.cpp index 320ec7969..34c348610 100644 --- a/lib/csapi/registration.cpp +++ b/lib/csapi/registration.cpp @@ -30,7 +30,7 @@ BaseJob::Query queryToRegister(const QString& kind) static const auto RegisterJobName = QStringLiteral("RegisterJob"); -RegisterJob::RegisterJob(const QString& kind, const Omittable& auth, bool bindEmail, const QString& username, const QString& password, const QString& deviceId, const QString& initialDeviceDisplayName, bool inhibitLogin) +RegisterJob::RegisterJob(const QString& kind, const Omittable& auth, Omittable bindEmail, const QString& username, const QString& password, const QString& deviceId, const QString& initialDeviceDisplayName, Omittable inhibitLogin) : BaseJob(HttpVerb::Post, RegisterJobName, basePath % "/register", queryToRegister(kind), @@ -251,7 +251,7 @@ DeactivateAccountJob::DeactivateAccountJob(const Omittable& class CheckUsernameAvailabilityJob::Private { public: - bool available; + Omittable available; }; BaseJob::Query queryToCheckUsernameAvailability(const QString& username) @@ -281,7 +281,7 @@ CheckUsernameAvailabilityJob::CheckUsernameAvailabilityJob(const QString& userna CheckUsernameAvailabilityJob::~CheckUsernameAvailabilityJob() = default; -bool CheckUsernameAvailabilityJob::available() const +Omittable CheckUsernameAvailabilityJob::available() const { return d->available; } diff --git a/lib/csapi/registration.h b/lib/csapi/registration.h index 9002b5c8d..ca1a1c215 100644 --- a/lib/csapi/registration.h +++ b/lib/csapi/registration.h @@ -80,7 +80,7 @@ namespace QMatrixClient * returned from this call, therefore preventing an automatic * login. Defaults to false. */ - explicit RegisterJob(const QString& kind = QStringLiteral("user"), const Omittable& auth = none, bool bindEmail = false, const QString& username = {}, const QString& password = {}, const QString& deviceId = {}, const QString& initialDeviceDisplayName = {}, bool inhibitLogin = false); + explicit RegisterJob(const QString& kind = QStringLiteral("user"), const Omittable& auth = none, Omittable bindEmail = none, const QString& username = {}, const QString& password = {}, const QString& deviceId = {}, const QString& initialDeviceDisplayName = {}, Omittable inhibitLogin = none); ~RegisterJob() override; // Result properties @@ -418,7 +418,7 @@ namespace QMatrixClient /// A flag to indicate that the username is available. This should always /// be ``true`` when the server replies with 200 OK. - bool available() const; + Omittable available() const; protected: Status parseJson(const QJsonDocument& data) override; diff --git a/lib/csapi/search.h b/lib/csapi/search.h index 85b0886b8..86a0ee923 100644 --- a/lib/csapi/search.h +++ b/lib/csapi/search.h @@ -39,7 +39,7 @@ namespace QMatrixClient /// historic profile information for the users /// that sent the events that were returned. /// By default, this is ``false``. - bool includeProfile; + Omittable includeProfile; }; /// Configuration for group. @@ -74,7 +74,7 @@ namespace QMatrixClient Omittable eventContext; /// Requests the server return the current state for /// each room returned. - bool includeState; + Omittable includeState; /// Requests that the server partitions the result set /// based on the provided list of keys. Omittable groupings; From 07c9eacc0d0009040e359fd822674a48ef8edeac Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Thu, 13 Dec 2018 07:59:45 +0900 Subject: [PATCH 23/27] Bump room state cache version to reset the cache --- lib/syncdata.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/syncdata.h b/lib/syncdata.h index 663553bcb..8694626ea 100644 --- a/lib/syncdata.h +++ b/lib/syncdata.h @@ -100,7 +100,7 @@ namespace QMatrixClient { QStringList unresolvedRooms() const { return unresolvedRoomIds; } - static std::pair cacheVersion() { return { 9, 0 }; } + static std::pair cacheVersion() { return { 10, 0 }; } static QString fileNameForRoom(QString roomId); private: From cb5f0f61e74cdc4ee64530cd73af0d080538bc1e Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Thu, 13 Dec 2018 10:17:02 +0900 Subject: [PATCH 24/27] Connection: initialize lazyLoading member variable --- lib/connection.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/connection.cpp b/lib/connection.cpp index 76e61ed16..a16bc753a 100644 --- a/lib/connection.cpp +++ b/lib/connection.cpp @@ -94,7 +94,7 @@ class Connection::Private bool cacheState = true; bool cacheToBinary = SettingsGroup("libqmatrixclient") .value("cache_type").toString() != "json"; - bool lazyLoading; + bool lazyLoading = false; void connectWithToken(const QString& user, const QString& accessToken, const QString& deviceId); From cf4759edba82baf51dd40285d2e13b200ca7fd29 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Thu, 13 Dec 2018 15:54:02 +0900 Subject: [PATCH 25/27] qmc-example: Fix the lazy-loading test --- examples/qmc-example.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/examples/qmc-example.cpp b/examples/qmc-example.cpp index bdb9ef3f6..e66687da8 100644 --- a/examples/qmc-example.cpp +++ b/examples/qmc-example.cpp @@ -83,6 +83,8 @@ void QMCTest::setup(const QString& testRoomName) // Setting up sync loop c->setLazyLoading(true); c->sync(); + connectSingleShot(c.data(), &Connection::syncDone, + this, &QMCTest::startTests); connect(c.data(), &Connection::syncDone, c.data(), [this,testRoomName] { cout << "Sync complete, " << running.size() << " tests in the air" << endl; @@ -116,7 +118,6 @@ void QMCTest::setup(const QString& testRoomName) targetRoom = room; QMC_CHECK("Join room", true); - startTests(); }); } } @@ -167,15 +168,16 @@ void QMCTest::loadMembers() // It's not exactly correct because an arbitrary server might not support // lazy loading; but in the absence of capabilities framework we assume // it does. - if (r->memberNames().size() < r->joinedCount()) + if (r->memberNames().size() >= r->joinedCount()) { cout << "Lazy loading doesn't seem to be enabled" << endl; QMC_CHECK("Loading members", false); return; } r->setDisplayed(); - connect(r, &Room::allMembersLoaded, [this] { - QMC_CHECK("Loading members", true); + connect(r, &Room::allMembersLoaded, [this,r] { + QMC_CHECK("Loading members", + r->memberNames().size() + 1 >= r->joinedCount()); }); } From 2cbb053faeae1f23606c56ef9fd9d13ca4a2dd21 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Thu, 13 Dec 2018 19:56:18 +0900 Subject: [PATCH 26/27] Room::getAllMembers: fix off-by-one error --- lib/room.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/room.cpp b/lib/room.cpp index 7232741a7..8f9095dd0 100644 --- a/lib/room.cpp +++ b/lib/room.cpp @@ -595,7 +595,7 @@ Room::rev_iter_t Room::findInTimeline(const QString& evtId) const void Room::Private::getAllMembers() { // If already loaded or already loading, there's nothing to do here. - if (q->joinedCount() <= membersMap.size() || isJobRunning(allMembersJob)) + if (q->joinedCount() - 1 <= membersMap.size() || isJobRunning(allMembersJob)) return; allMembersJob = connection->callApi( From 12a0b95fdcfea15cd0ef313aec8868656629b986 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Fri, 14 Dec 2018 12:33:04 +0900 Subject: [PATCH 27/27] qmc-example: clearer QMC_CHECK; start tests only after the first sync is done Because lazy-loading test is executed on a room different from the test room. --- examples/qmc-example.cpp | 93 +++++++++++++++++++++------------------- 1 file changed, 50 insertions(+), 43 deletions(-) diff --git a/examples/qmc-example.cpp b/examples/qmc-example.cpp index e66687da8..48787e44a 100644 --- a/examples/qmc-example.cpp +++ b/examples/qmc-example.cpp @@ -20,10 +20,10 @@ using namespace std::placeholders; class QMCTest : public QObject { public: - QMCTest(Connection* conn, const QString& testRoomName, QString source); + QMCTest(Connection* conn, QString testRoomName, QString source); private slots: - void setup(const QString& testRoomName); + void setup(); void onNewRoom(Room* r); void startTests(); void loadMembers(); @@ -44,37 +44,45 @@ class QMCTest : public QObject QStringList succeeded; QStringList failed; QString origin; + QString testRoomName; Room* targetRoom = nullptr; }; #define QMC_CHECK(description, condition) \ { \ - const bool result = !!(condition); \ Q_ASSERT(running.removeOne(description)); \ - (result ? succeeded : failed).push_back(description); \ - cout << (description) << (result ? " successul" : " FAILED") << endl; \ - if (targetRoom) \ - targetRoom->postMessage(origin % ": " % QStringLiteral(description) % \ - (result ? QStringLiteral(" successful") : QStringLiteral(" FAILED")), \ - result ? MessageEventType::Notice : MessageEventType::Text); \ + if (!!(condition)) \ + { \ + succeeded.push_back(description); \ + cout << (description) << " successful" << endl; \ + if (targetRoom) \ + targetRoom->postMessage( \ + origin % ": " % (description) % " successful", \ + MessageEventType::Notice); \ + } else { \ + failed.push_back(description); \ + cout << (description) << " FAILED" << endl; \ + if (targetRoom) \ + targetRoom->postPlainText( \ + origin % ": " % (description) % " FAILED"); \ + } \ } -QMCTest::QMCTest(Connection* conn, const QString& testRoomName, QString source) - : c(conn), origin(std::move(source)) +QMCTest::QMCTest(Connection* conn, QString testRoomName, QString source) + : c(conn), origin(std::move(source)), testRoomName(std::move(testRoomName)) { if (!origin.isEmpty()) cout << "Origin for the test message: " << origin.toStdString() << endl; if (!testRoomName.isEmpty()) cout << "Test room name: " << testRoomName.toStdString() << endl; - connect(c.data(), &Connection::connected, - this, std::bind(&QMCTest::setup, this, testRoomName)); + connect(c.data(), &Connection::connected, this, &QMCTest::setup); connect(c.data(), &Connection::loadedRoomState, this, &QMCTest::onNewRoom); // Big countdown watchdog QTimer::singleShot(180000, this, &QMCTest::leave); } -void QMCTest::setup(const QString& testRoomName) +void QMCTest::setup() { cout << "Connected, server: " << c->homeserver().toDisplayString().toStdString() << endl; @@ -85,7 +93,7 @@ void QMCTest::setup(const QString& testRoomName) c->sync(); connectSingleShot(c.data(), &Connection::syncDone, this, &QMCTest::startTests); - connect(c.data(), &Connection::syncDone, c.data(), [this,testRoomName] { + connect(c.data(), &Connection::syncDone, c.data(), [this] { cout << "Sync complete, " << running.size() << " tests in the air" << endl; if (!running.isEmpty()) @@ -98,28 +106,6 @@ void QMCTest::setup(const QString& testRoomName) else finalize(); }); - - // Join a testroom, if provided - if (!targetRoom && !testRoomName.isEmpty()) - { - cout << "Joining " << testRoomName.toStdString() << endl; - running.push_back("Join room"); - auto joinJob = c->joinRoom(testRoomName); - connect(joinJob, &BaseJob::failure, this, - [this] { QMC_CHECK("Join room", false); finalize(); }); - // As of BaseJob::success, a Room object is not guaranteed to even - // exist; it's a mere confirmation that the server processed - // the request. - connect(c.data(), &Connection::loadedRoomState, this, - [this,testRoomName] (Room* room) { - Q_ASSERT(room); // It's a grave failure if room is nullptr here - if (room->canonicalAlias() != testRoomName) - return; // Not our room - - targetRoom = room; - QMC_CHECK("Join room", true); - }); - } } void QMCTest::onNewRoom(Room* r) @@ -144,12 +130,33 @@ void QMCTest::onNewRoom(Room* r) void QMCTest::startTests() { - cout << "Starting tests" << endl; - loadMembers(); - sendMessage(); - addAndRemoveTag(); - sendAndRedact(); - markDirectChat(); + if (testRoomName.isEmpty()) + return; + + cout << "Joining " << testRoomName.toStdString() << endl; + running.push_back("Join room"); + auto joinJob = c->joinRoom(testRoomName); + connect(joinJob, &BaseJob::failure, this, + [this] { QMC_CHECK("Join room", false); finalize(); }); + // As of BaseJob::success, a Room object is not guaranteed to even + // exist; it's a mere confirmation that the server processed + // the request. + connect(c.data(), &Connection::loadedRoomState, this, + [this] (Room* room) { + Q_ASSERT(room); // It's a grave failure if room is nullptr here + if (room->canonicalAlias() != testRoomName) + return; // Not our room + + targetRoom = room; + QMC_CHECK("Join room", true); + cout << "Starting tests" << endl; + + loadMembers(); + sendMessage(); + addAndRemoveTag(); + sendAndRedact(); + markDirectChat(); + }); } void QMCTest::loadMembers()