aboutsummaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/application-service/definitions/protocol.h9
-rw-r--r--lib/connection.cpp16
-rw-r--r--lib/csapi/account-data.h4
-rw-r--r--lib/csapi/administrative_contact.h65
-rw-r--r--lib/csapi/appservice_room_directory.h6
-rw-r--r--lib/csapi/banning.h3
-rw-r--r--lib/csapi/content-repo.h39
-rw-r--r--lib/csapi/create_room.h205
-rw-r--r--lib/csapi/definitions/device_keys.h8
-rw-r--r--lib/csapi/definitions/event_filter.h8
-rw-r--r--lib/csapi/definitions/openid_token.h4
-rw-r--r--lib/csapi/definitions/push_condition.h17
-rw-r--r--lib/csapi/definitions/push_rule.h4
-rw-r--r--lib/csapi/definitions/request_email_validation.h5
-rw-r--r--lib/csapi/definitions/request_msisdn_validation.h5
-rw-r--r--lib/csapi/definitions/request_token_response.h6
-rw-r--r--lib/csapi/definitions/room_event_filter.h25
-rw-r--r--lib/csapi/definitions/sync_filter.h8
-rw-r--r--lib/csapi/definitions/third_party_signed.h2
-rw-r--r--lib/csapi/definitions/user_identifier.h5
-rw-r--r--lib/csapi/device_management.h6
-rw-r--r--lib/csapi/directory.h20
-rw-r--r--lib/csapi/event_context.h14
-rw-r--r--lib/csapi/filter.h2
-rw-r--r--lib/csapi/inviting.h9
-rw-r--r--lib/csapi/joining.h18
-rw-r--r--lib/csapi/keys.h35
-rw-r--r--lib/csapi/kicking.h9
-rw-r--r--lib/csapi/list_joined_rooms.h2
-rw-r--r--lib/csapi/list_public_rooms.h2
-rw-r--r--lib/csapi/login.h38
-rw-r--r--lib/csapi/logout.h11
-rw-r--r--lib/csapi/message_pagination.h34
-rw-r--r--lib/csapi/notifications.h8
-rw-r--r--lib/csapi/openid.h9
-rw-r--r--lib/csapi/peeking_events.h14
-rw-r--r--lib/csapi/presence.h2
-rw-r--r--lib/csapi/profile.h6
-rw-r--r--lib/csapi/pusher.h38
-rw-r--r--lib/csapi/pushrules.h32
-rw-r--r--lib/csapi/read_markers.h2
-rw-r--r--lib/csapi/receipts.h4
-rw-r--r--lib/csapi/redaction.h9
-rw-r--r--lib/csapi/registration.h139
-rw-r--r--lib/csapi/room_send.h5
-rw-r--r--lib/csapi/room_state.h28
-rw-r--r--lib/csapi/rooms.h16
-rw-r--r--lib/csapi/sso_login_redirect.h4
-rw-r--r--lib/csapi/tags.h4
-rw-r--r--lib/csapi/third_party_membership.h15
-rw-r--r--lib/csapi/typing.h6
-rw-r--r--lib/csapi/users.h4
-rw-r--r--lib/csapi/versions.h10
-rw-r--r--lib/csapi/wellknown.h2
-rw-r--r--lib/csapi/whoami.h6
-rw-r--r--lib/identity/definitions/request_email_validation.h6
-rw-r--r--lib/identity/definitions/request_msisdn_validation.h8
-rw-r--r--lib/room.cpp509
-rw-r--r--lib/room.h22
-rw-r--r--lib/syncdata.cpp17
-rw-r--r--lib/user.cpp12
-rw-r--r--lib/util.cpp4
62 files changed, 838 insertions, 747 deletions
diff --git a/lib/application-service/definitions/protocol.h b/lib/application-service/definitions/protocol.h
index 6aee9c57..213dbf19 100644
--- a/lib/application-service/definitions/protocol.h
+++ b/lib/application-service/definitions/protocol.h
@@ -40,7 +40,7 @@ struct ProtocolInstance {
/// provided at the higher level Protocol object.
QString icon;
- /// Preset values for ``fields`` the client may use to search by.
+ /// Preset values for `fields` the client may use to search by.
QJsonObject fields;
/// A unique identifier across all instances.
@@ -81,10 +81,9 @@ struct ThirdPartyProtocol {
/// A content URI representing an icon for the third party protocol.
QString icon;
- /// The type definitions for the fields defined in the ``user_fields`` and
- /// ``location_fields``. Each entry in those arrays MUST have an entry here.
- /// The
- /// ``string`` key for this object is field name itself.
+ /// The type definitions for the fields defined in the `user_fields` and
+ /// `location_fields`. Each entry in those arrays MUST have an entry here.
+ /// The `string` key for this object is field name itself.
///
/// May be an empty object if no fields are defined.
QHash<QString, FieldType> fieldTypes;
diff --git a/lib/connection.cpp b/lib/connection.cpp
index c4587f30..853053bd 100644
--- a/lib/connection.cpp
+++ b/lib/connection.cpp
@@ -804,11 +804,11 @@ PostReceiptJob* Connection::postReceipt(Room* room, RoomEvent* event)
JoinRoomJob* Connection::joinRoom(const QString& roomAlias,
const QStringList& serverNames)
{
- auto job = callApi<JoinRoomJob>(roomAlias, serverNames);
- // Upon completion, ensure a room object in Join state is created
- // (or it might already be there due to a sync completing earlier).
- // finished() is used here instead of success() to overtake clients
- // that may add their own slots to finished().
+ auto* const job = callApi<JoinRoomJob>(roomAlias, serverNames);
+ // Upon completion, ensure a room object is created in case it hasn't come
+ // with a sync yet. If the room object is not there, provideRoom() will
+ // create it in Join state. finished() is used here instead of success()
+ // to overtake clients that may add their own slots to finished().
connect(job, &BaseJob::finished, this, [this, job] {
if (job->status().good())
provideRoom(job->roomId());
@@ -1474,11 +1474,13 @@ Room* Connection::provideRoom(const QString& id, Omittable<JoinState> joinState)
room = d->roomMap.value({ id, true }, nullptr);
if (room)
return room;
- // No Invite either, setup a new room object below
+ // No Invite either, setup a new room object in Join state
+ joinState = JoinState::Join;
}
if (!room) {
- room = roomFactory()(this, id, joinState.value_or(JoinState::Join));
+ Q_ASSERT(joinState.has_value());
+ room = roomFactory()(this, id, *joinState);
if (!room) {
qCCritical(MAIN) << "Failed to create a room" << id;
return nullptr;
diff --git a/lib/csapi/account-data.h b/lib/csapi/account-data.h
index 9a31596f..0c760e80 100644
--- a/lib/csapi/account-data.h
+++ b/lib/csapi/account-data.h
@@ -12,7 +12,7 @@ namespace Quotient {
*
* Set some account_data for the client. This config is only visible to the user
* that set the account_data. The config will be synced to clients in the
- * top-level ``account_data``.
+ * top-level `account_data`.
*/
class SetAccountDataJob : public BaseJob {
public:
@@ -65,7 +65,7 @@ public:
*
* Set some account_data for the client on a given room. This config is only
* visible to the user that set the account_data. The config will be synced to
- * clients in the per-room ``account_data``.
+ * clients in the per-room `account_data`.
*/
class SetAccountDataPerRoomJob : public BaseJob {
public:
diff --git a/lib/csapi/administrative_contact.h b/lib/csapi/administrative_contact.h
index 1966d533..e436971d 100644
--- a/lib/csapi/administrative_contact.h
+++ b/lib/csapi/administrative_contact.h
@@ -93,14 +93,14 @@ struct JsonObjectConverter<GetAccount3PIDsJob::ThirdPartyIdentifier> {
*
* Adds contact information to the user's account.
*
- * This endpoint is deprecated in favour of the more specific ``/3pid/add``
- * and ``/3pid/bind`` endpoints.
+ * This endpoint is deprecated in favour of the more specific `/3pid/add`
+ * and `/3pid/bind` endpoints.
*
- * .. Note::
- * Previously this endpoint supported a ``bind`` parameter. This parameter
- * has been removed, making this endpoint behave as though it was ``false``.
- * This results in this endpoint being an equivalent to ``/3pid/bind`` rather
- * than dual-purpose.
+ * **Note:**
+ * Previously this endpoint supported a `bind` parameter. This parameter
+ * has been removed, making this endpoint behave as though it was `false`.
+ * This results in this endpoint being an equivalent to `/3pid/bind` rather
+ * than dual-purpose.
*/
class Post3PIDsJob : public BaseJob {
public:
@@ -144,7 +144,8 @@ struct JsonObjectConverter<Post3PIDsJob::ThreePidCredentials> {
/*! \brief Adds contact information to the user's account.
*
- * This API endpoint uses the `User-Interactive Authentication API`_.
+ * This API endpoint uses the [User-Interactive Authentication
+ * API](/client-server-api/#user-interactive-authentication-api).
*
* Adds contact information to the user's account. Homeservers should use 3PIDs
* added through this endpoint for password resets instead of relying on the
@@ -206,7 +207,7 @@ public:
* Removes a third party identifier from the user's account. This might not
* cause an unbind of the identifier from the identity server.
*
- * Unlike other endpoints, this endpoint does not take an ``id_access_token``
+ * Unlike other endpoints, this endpoint does not take an `id_access_token`
* parameter because the homeserver is expected to sign the request to the
* identity server instead.
*/
@@ -222,9 +223,9 @@ public:
*
* \param idServer
* The identity server to unbind from. If not provided, the homeserver
- * MUST use the ``id_server`` the identifier was added through. If the
- * homeserver does not know the original ``id_server``, it MUST return
- * a ``id_server_unbind_result`` of ``no-support``.
+ * MUST use the `id_server` the identifier was added through. If the
+ * homeserver does not know the original `id_server`, it MUST return
+ * a `id_server_unbind_result` of `no-support`.
*/
explicit Delete3pidFromAccountJob(const QString& medium,
const QString& address,
@@ -233,8 +234,8 @@ public:
// Result properties
/// An indicator as to whether or not the homeserver was able to unbind
- /// the 3PID from the identity server. ``success`` indicates that the
- /// indentity server has unbound the identifier whereas ``no-support``
+ /// the 3PID from the identity server. `success` indicates that the
+ /// indentity server has unbound the identifier whereas `no-support`
/// indicates that the identity server refuses to support the request
/// or the homeserver was not able to determine an identity server to
/// unbind from.
@@ -249,7 +250,7 @@ public:
* Removes a user's third party identifier from the provided identity server
* without removing it from the homeserver.
*
- * Unlike other endpoints, this endpoint does not take an ``id_access_token``
+ * Unlike other endpoints, this endpoint does not take an `id_access_token`
* parameter because the homeserver is expected to sign the request to the
* identity server instead.
*/
@@ -265,9 +266,9 @@ public:
*
* \param idServer
* The identity server to unbind from. If not provided, the homeserver
- * MUST use the ``id_server`` the identifier was added through. If the
- * homeserver does not know the original ``id_server``, it MUST return
- * a ``id_server_unbind_result`` of ``no-support``.
+ * MUST use the `id_server` the identifier was added through. If the
+ * homeserver does not know the original `id_server`, it MUST return
+ * a `id_server_unbind_result` of `no-support`.
*/
explicit Unbind3pidFromAccountJob(const QString& medium,
const QString& address,
@@ -276,8 +277,8 @@ public:
// Result properties
/// An indicator as to whether or not the identity server was able to unbind
- /// the 3PID. ``success`` indicates that the identity server has unbound the
- /// identifier whereas ``no-support`` indicates that the identity server
+ /// the 3PID. `success` indicates that the identity server has unbound the
+ /// identifier whereas `no-support` indicates that the identity server
/// refuses to support the request or the homeserver was not able to
/// determine an identity server to unbind from.
QString idServerUnbindResult() const
@@ -293,7 +294,9 @@ public:
* already associated with an account on this homeserver. This API should
* be used to request validation tokens when adding an email address to an
* account. This API's parameters and response are identical to that of
- * the |/register/email/requestToken|_ endpoint. The homeserver should validate
+ * the
+ * [`/register/email/requestToken`](/client-server-api/#post_matrixclientr0registeremailrequesttoken)
+ * endpoint. The homeserver should validate
* the email itself, either by sending a validation email itself or by using
* a service it has control over.
*/
@@ -307,9 +310,11 @@ public:
* already associated with an account on this homeserver. This API should
* be used to request validation tokens when adding an email address to an
* account. This API's parameters and response are identical to that of
- * the |/register/email/requestToken|_ endpoint. The homeserver should
- * validate the email itself, either by sending a validation email itself or
- * by using a service it has control over.
+ * the
+ * [`/register/email/requestToken`](/client-server-api/#post_matrixclientr0registeremailrequesttoken)
+ * endpoint. The homeserver should validate
+ * the email itself, either by sending a validation email itself or by
+ * using a service it has control over.
*/
explicit RequestTokenTo3PIDEmailJob(const EmailValidationData& body);
@@ -331,7 +336,9 @@ public:
* already associated with an account on this homeserver. This API should
* be used to request validation tokens when adding a phone number to an
* account. This API's parameters and response are identical to that of
- * the |/register/msisdn/requestToken|_ endpoint. The homeserver should validate
+ * the
+ * [`/register/msisdn/requestToken`](/client-server-api/#post_matrixclientr0registermsisdnrequesttoken)
+ * endpoint. The homeserver should validate
* the phone number itself, either by sending a validation message itself or by
* using a service it has control over.
*/
@@ -345,9 +352,11 @@ public:
* already associated with an account on this homeserver. This API should
* be used to request validation tokens when adding a phone number to an
* account. This API's parameters and response are identical to that of
- * the |/register/msisdn/requestToken|_ endpoint. The homeserver should
- * validate the phone number itself, either by sending a validation message
- * itself or by using a service it has control over.
+ * the
+ * [`/register/msisdn/requestToken`](/client-server-api/#post_matrixclientr0registermsisdnrequesttoken)
+ * endpoint. The homeserver should validate
+ * the phone number itself, either by sending a validation message itself
+ * or by using a service it has control over.
*/
explicit RequestTokenTo3PIDMSISDNJob(const MsisdnValidationData& body);
diff --git a/lib/csapi/appservice_room_directory.h b/lib/csapi/appservice_room_directory.h
index 3fa02a07..2631f38c 100644
--- a/lib/csapi/appservice_room_directory.h
+++ b/lib/csapi/appservice_room_directory.h
@@ -17,9 +17,9 @@ namespace Quotient {
* This API is similar to the room directory visibility API used by clients
* to update the homeserver's more general room directory.
*
- * This API requires the use of an application service access token
- * (``as_token``) instead of a typical client's access_token. This API cannot be
- * invoked by users who are not identified as application services.
+ * This API requires the use of an application service access token (`as_token`)
+ * instead of a typical client's access_token. This API cannot be invoked by
+ * users who are not identified as application services.
*/
class UpdateAppserviceRoomDirectoryVsibilityJob : public BaseJob {
public:
diff --git a/lib/csapi/banning.h b/lib/csapi/banning.h
index 37ae91ee..48c054c2 100644
--- a/lib/csapi/banning.h
+++ b/lib/csapi/banning.h
@@ -30,7 +30,8 @@ public:
*
* \param reason
* The reason the user has been banned. This will be supplied as the
- * ``reason`` on the target's updated `m.room.member`_ event.
+ * `reason` on the target's updated
+ * [`m.room.member`](/client-server-api/#mroommember) event.
*/
explicit BanJob(const QString& roomId, const QString& userId,
const QString& reason = {});
diff --git a/lib/csapi/content-repo.h b/lib/csapi/content-repo.h
index ed67485c..f3d7309a 100644
--- a/lib/csapi/content-repo.h
+++ b/lib/csapi/content-repo.h
@@ -32,7 +32,8 @@ public:
// Result properties
- /// The `MXC URI`_ to the uploaded content.
+ /// The [MXC URI](/client-server-api/#matrix-content-mxc-uris) to the
+ /// uploaded content.
QString contentUri() const
{
return loadFromJson<QString>("content_uri"_ls);
@@ -47,10 +48,10 @@ public:
/*! \brief Download content from the content repository.
*
* \param serverName
- * The server name from the ``mxc://`` URI (the authoritory component)
+ * The server name from the `mxc://` URI (the authoritory component)
*
* \param mediaId
- * The media ID from the ``mxc://`` URI (the path component)
+ * The media ID from the `mxc://` URI (the path component)
*
* \param allowRemote
* Indicates to the server that it should not attempt to fetch the media
@@ -95,13 +96,13 @@ public:
* name
*
* \param serverName
- * The server name from the ``mxc://`` URI (the authoritory component)
+ * The server name from the `mxc://` URI (the authoritory component)
*
* \param mediaId
- * The media ID from the ``mxc://`` URI (the path component)
+ * The media ID from the `mxc://` URI (the path component)
*
* \param fileName
- * A filename to give in the ``Content-Disposition`` header.
+ * A filename to give in the `Content-Disposition` header.
*
* \param allowRemote
* Indicates to the server that it should not attempt to fetch the media
@@ -127,7 +128,7 @@ public:
/// The content type of the file that was previously uploaded.
QString contentType() const { return reply()->rawHeader("Content-Type"); }
- /// The ``fileName`` requested or the name of the file that was previously
+ /// The `fileName` requested or the name of the file that was previously
/// uploaded, if set.
QString contentDisposition() const
{
@@ -141,17 +142,18 @@ public:
/*! \brief Download a thumbnail of content from the content repository
*
* Download a thumbnail of content from the content repository.
- * See the `thumbnailing <#thumbnails>`_ section for more information.
+ * See the [Thumbnails](/client-server-api/#thumbnails) section for more
+ * information.
*/
class GetContentThumbnailJob : public BaseJob {
public:
/*! \brief Download a thumbnail of content from the content repository
*
* \param serverName
- * The server name from the ``mxc://`` URI (the authoritory component)
+ * The server name from the `mxc://` URI (the authoritory component)
*
* \param mediaId
- * The media ID from the ``mxc://`` URI (the path component)
+ * The media ID from the `mxc://` URI (the path component)
*
* \param width
* The *desired* width of the thumbnail. The actual thumbnail may be
@@ -162,8 +164,8 @@ public:
* larger than the size specified.
*
* \param method
- * The desired resizing method. See the `thumbnailing <#thumbnails>`_
- * section for more information.
+ * The desired resizing method. See the
+ * [Thumbnails](/client-server-api/#thumbnails) section for more information.
*
* \param allowRemote
* Indicates to the server that it should not attempt to fetch
@@ -199,11 +201,11 @@ public:
* Get information about a URL for the client. Typically this is called when a
* client sees a URL in a message and wants to render a preview for the user.
*
- * .. Note::
- * Clients should consider avoiding this endpoint for URLs posted in encrypted
- * rooms. Encrypted rooms often contain more sensitive information the users
- * do not want to share with the homeserver, and this can mean that the URLs
- * being shared should also not be shared with the homeserver.
+ * **Note:**
+ * Clients should consider avoiding this endpoint for URLs posted in encrypted
+ * rooms. Encrypted rooms often contain more sensitive information the users
+ * do not want to share with the homeserver, and this can mean that the URLs
+ * being shared should also not be shared with the homeserver.
*/
class GetUrlPreviewJob : public BaseJob {
public:
@@ -235,7 +237,8 @@ public:
return loadFromJson<Omittable<qint64>>("matrix:image:size"_ls);
}
- /// An `MXC URI`_ to the image. Omitted if there is no image.
+ /// An [MXC URI](/client-server-api/#matrix-content-mxc-uris) to the image.
+ /// Omitted if there is no image.
QString ogImage() const { return loadFromJson<QString>("og:image"_ls); }
};
diff --git a/lib/csapi/create_room.h b/lib/csapi/create_room.h
index 6a718ff4..81dfbffc 100644
--- a/lib/csapi/create_room.h
+++ b/lib/csapi/create_room.h
@@ -16,47 +16,42 @@ namespace Quotient {
* the new room, including checking power levels for each event. It MUST
* apply the events implied by the request in the following order:
*
- * 1. The ``m.room.create`` event itself. Must be the first event in the
+ * 1. The `m.room.create` event itself. Must be the first event in the
* room.
*
- * 2. An ``m.room.member`` event for the creator to join the room. This is
+ * 2. An `m.room.member` event for the creator to join the room. This is
* needed so the remaining events can be sent.
*
- * 3. A default ``m.room.power_levels`` event, giving the room creator
+ * 3. A default `m.room.power_levels` event, giving the room creator
* (and not other members) permission to send state events. Overridden
- * by the ``power_level_content_override`` parameter.
+ * by the `power_level_content_override` parameter.
*
- * 4. Events set by the ``preset``. Currently these are the
- * ``m.room.join_rules``,
- * ``m.room.history_visibility``, and ``m.room.guest_access`` state events.
+ * 4. Events set by the `preset`. Currently these are the `m.room.join_rules`,
+ * `m.room.history_visibility`, and `m.room.guest_access` state events.
*
- * 5. Events listed in ``initial_state``, in the order that they are
+ * 5. Events listed in `initial_state`, in the order that they are
* listed.
*
- * 6. Events implied by ``name`` and ``topic`` (``m.room.name`` and
- * ``m.room.topic`` state events).
+ * 6. Events implied by `name` and `topic` (`m.room.name` and `m.room.topic`
+ * state events).
*
- * 7. Invite events implied by ``invite`` and ``invite_3pid`` (``m.room.member``
- * with
- * ``membership: invite`` and ``m.room.third_party_invite``).
+ * 7. Invite events implied by `invite` and `invite_3pid` (`m.room.member` with
+ * `membership: invite` and `m.room.third_party_invite`).
*
* The available presets do the following with respect to room state:
*
- * ======================== ============== ======================
- * ================ ========= Preset ``join_rules``
- * ``history_visibility`` ``guest_access`` Other
- * ======================== ============== ======================
- * ================ =========
- * ``private_chat`` ``invite`` ``shared`` ``can_join``
- * ``trusted_private_chat`` ``invite`` ``shared`` ``can_join`` All
- * invitees are given the same power level as the room creator.
- * ``public_chat`` ``public`` ``shared`` ``forbidden``
- * ======================== ============== ======================
- * ================ =========
+ * | Preset | `join_rules` | `history_visibility` |
+ * `guest_access` | Other |
+ * |------------------------|--------------|----------------------|----------------|-------|
+ * | `private_chat` | `invite` | `shared` | `can_join`
+ * | | | `trusted_private_chat` | `invite` | `shared` |
+ * `can_join` | All invitees are given the same power level as the room
+ * creator. | | `public_chat` | `public` | `shared` |
+ * `forbidden` | |
*
- * The server will create a ``m.room.create`` event in the room with the
+ * The server will create a `m.room.create` event in the room with the
* requesting user as the creator, alongside other keys provided in the
- * ``creation_content``.
+ * `creation_content`.
*/
class CreateRoomJob : public BaseJob {
public:
@@ -68,50 +63,44 @@ public:
/// the new room, including checking power levels for each event. It MUST
/// apply the events implied by the request in the following order:
///
- /// 1. The ``m.room.create`` event itself. Must be the first event in the
+ /// 1. The `m.room.create` event itself. Must be the first event in the
/// room.
///
- /// 2. An ``m.room.member`` event for the creator to join the room. This is
+ /// 2. An `m.room.member` event for the creator to join the room. This is
/// needed so the remaining events can be sent.
///
- /// 3. A default ``m.room.power_levels`` event, giving the room creator
+ /// 3. A default `m.room.power_levels` event, giving the room creator
/// (and not other members) permission to send state events. Overridden
- /// by the ``power_level_content_override`` parameter.
+ /// by the `power_level_content_override` parameter.
///
- /// 4. Events set by the ``preset``. Currently these are the
- /// ``m.room.join_rules``,
- /// ``m.room.history_visibility``, and ``m.room.guest_access`` state
- /// events.
+ /// 4. Events set by the `preset`. Currently these are the
+ /// `m.room.join_rules`,
+ /// `m.room.history_visibility`, and `m.room.guest_access` state events.
///
- /// 5. Events listed in ``initial_state``, in the order that they are
+ /// 5. Events listed in `initial_state`, in the order that they are
/// listed.
///
- /// 6. Events implied by ``name`` and ``topic`` (``m.room.name`` and
- /// ``m.room.topic``
+ /// 6. Events implied by `name` and `topic` (`m.room.name` and `m.room.topic`
/// state events).
///
- /// 7. Invite events implied by ``invite`` and ``invite_3pid``
- /// (``m.room.member`` with
- /// ``membership: invite`` and ``m.room.third_party_invite``).
+ /// 7. Invite events implied by `invite` and `invite_3pid` (`m.room.member`
+ /// with
+ /// `membership: invite` and `m.room.third_party_invite`).
///
/// The available presets do the following with respect to room state:
///
- /// ======================== ============== ======================
- /// ================ =========
- /// Preset ``join_rules`` ``history_visibility``
- /// ``guest_access`` Other
- /// ======================== ============== ======================
- /// ================ =========
- /// ``private_chat`` ``invite`` ``shared`` ``can_join``
- /// ``trusted_private_chat`` ``invite`` ``shared`` ``can_join`` All
- /// invitees are given the same power level as the room creator.
- /// ``public_chat`` ``public`` ``shared`` ``forbidden``
- /// ======================== ============== ======================
- /// ================ =========
+ /// | Preset | `join_rules` | `history_visibility` |
+ /// `guest_access` | Other |
+ /// |------------------------|--------------|----------------------|----------------|-------|
+ /// | `private_chat` | `invite` | `shared` |
+ /// `can_join` | | | `trusted_private_chat` | `invite` |
+ /// `shared` | `can_join` | All invitees are given the same
+ /// power level as the room creator. | | `public_chat` | `public`
+ /// | `shared` | `forbidden` | |
///
- /// The server will create a ``m.room.create`` event in the room with the
+ /// The server will create a `m.room.create` event in the room with the
/// requesting user as the creator, alongside other keys provided in the
- /// ``creation_content``.
+ /// `creation_content`.
struct Invite3pid {
/// The hostname+port of the identity server which should be used for
/// third party identifier lookups.
@@ -121,7 +110,7 @@ public:
/// r0.5-compatible clients and this specification version.
QString idAccessToken;
/// The kind of address being passed in the address field, for example
- /// ``email``.
+ /// `email`.
QString medium;
/// The invitee's third party identifier.
QString address;
@@ -133,50 +122,44 @@ public:
/// the new room, including checking power levels for each event. It MUST
/// apply the events implied by the request in the following order:
///
- /// 1. The ``m.room.create`` event itself. Must be the first event in the
+ /// 1. The `m.room.create` event itself. Must be the first event in the
/// room.
///
- /// 2. An ``m.room.member`` event for the creator to join the room. This is
+ /// 2. An `m.room.member` event for the creator to join the room. This is
/// needed so the remaining events can be sent.
///
- /// 3. A default ``m.room.power_levels`` event, giving the room creator
+ /// 3. A default `m.room.power_levels` event, giving the room creator
/// (and not other members) permission to send state events. Overridden
- /// by the ``power_level_content_override`` parameter.
+ /// by the `power_level_content_override` parameter.
///
- /// 4. Events set by the ``preset``. Currently these are the
- /// ``m.room.join_rules``,
- /// ``m.room.history_visibility``, and ``m.room.guest_access`` state
- /// events.
+ /// 4. Events set by the `preset`. Currently these are the
+ /// `m.room.join_rules`,
+ /// `m.room.history_visibility`, and `m.room.guest_access` state events.
///
- /// 5. Events listed in ``initial_state``, in the order that they are
+ /// 5. Events listed in `initial_state`, in the order that they are
/// listed.
///
- /// 6. Events implied by ``name`` and ``topic`` (``m.room.name`` and
- /// ``m.room.topic``
+ /// 6. Events implied by `name` and `topic` (`m.room.name` and `m.room.topic`
/// state events).
///
- /// 7. Invite events implied by ``invite`` and ``invite_3pid``
- /// (``m.room.member`` with
- /// ``membership: invite`` and ``m.room.third_party_invite``).
+ /// 7. Invite events implied by `invite` and `invite_3pid` (`m.room.member`
+ /// with
+ /// `membership: invite` and `m.room.third_party_invite`).
///
/// The available presets do the following with respect to room state:
///
- /// ======================== ============== ======================
- /// ================ =========
- /// Preset ``join_rules`` ``history_visibility``
- /// ``guest_access`` Other
- /// ======================== ============== ======================
- /// ================ =========
- /// ``private_chat`` ``invite`` ``shared`` ``can_join``
- /// ``trusted_private_chat`` ``invite`` ``shared`` ``can_join`` All
- /// invitees are given the same power level as the room creator.
- /// ``public_chat`` ``public`` ``shared`` ``forbidden``
- /// ======================== ============== ======================
- /// ================ =========
+ /// | Preset | `join_rules` | `history_visibility` |
+ /// `guest_access` | Other |
+ /// |------------------------|--------------|----------------------|----------------|-------|
+ /// | `private_chat` | `invite` | `shared` |
+ /// `can_join` | | | `trusted_private_chat` | `invite` |
+ /// `shared` | `can_join` | All invitees are given the same
+ /// power level as the room creator. | | `public_chat` | `public`
+ /// | `shared` | `forbidden` | |
///
- /// The server will create a ``m.room.create`` event in the room with the
+ /// The server will create a `m.room.create` event in the room with the
/// requesting user as the creator, alongside other keys provided in the
- /// ``creation_content``.
+ /// `creation_content`.
struct StateEvent {
/// The type of event to send.
QString type;
@@ -191,12 +174,12 @@ public:
/*! \brief Create a new room
*
* \param visibility
- * A ``public`` visibility indicates that the room will be shown
- * in the published room list. A ``private`` visibility will hide
+ * A `public` visibility indicates that the room will be shown
+ * in the published room list. A `private` visibility will hide
* the room from the published room list. Rooms default to
- * ``private`` visibility if this key is not included. NB: This
- * should not be confused with ``join_rules`` which also uses the
- * word ``public``.
+ * `private` visibility if this key is not included. NB: This
+ * should not be confused with `join_rules` which also uses the
+ * word `public`.
*
* \param roomAliasName
* The desired room alias **local part**. If this is included, a
@@ -204,20 +187,20 @@ public:
* room. The alias will belong on the *same* homeserver which
* created the room. For example, if this was set to "foo" and
* sent to the homeserver "example.com" the complete room alias
- * would be ``#foo:example.com``.
+ * would be `#foo:example.com`.
*
* The complete room alias will become the canonical alias for
* the room.
*
* \param name
- * If this is included, an ``m.room.name`` event will be sent
+ * If this is included, an `m.room.name` event will be sent
* into the room to indicate the name of the room. See Room
- * Events for more information on ``m.room.name``.
+ * Events for more information on `m.room.name`.
*
* \param topic
- * If this is included, an ``m.room.topic`` event will be sent
+ * If this is included, an `m.room.topic` event will be sent
* into the room to indicate the topic for the room. See Room
- * Events for more information on ``m.room.topic``.
+ * Events for more information on `m.room.topic`.
*
* \param invite
* A list of user IDs to invite to the room. This will tell the
@@ -230,14 +213,14 @@ public:
* \param roomVersion
* The room version to set for the room. If not provided, the homeserver
* is to use its configured default. If provided, the homeserver will return
- * a 400 error with the errcode ``M_UNSUPPORTED_ROOM_VERSION`` if it does
- * not support the room version.
+ * a 400 error with the errcode `M_UNSUPPORTED_ROOM_VERSION` if it does not
+ * support the room version.
*
* \param creationContent
- * Extra keys, such as ``m.federate``, to be added to the content
- * of the `m.room.create`_ event. The server will clobber the following
- * keys: ``creator``, ``room_version``. Future versions of the
- * specification may allow the server to clobber other keys.
+ * Extra keys, such as `m.federate`, to be added to the content
+ * of the [`m.room.create`](client-server-api/#mroomcreate) event. The
+ * server will clobber the following keys: `creator`, `room_version`. Future
+ * versions of the specification may allow the server to clobber other keys.
*
* \param initialState
* A list of state events to set in the new room. This allows
@@ -245,28 +228,30 @@ public:
* room. The expected format of the state events are an object
* with type, state_key and content keys set.
*
- * Takes precedence over events set by ``preset``, but gets
- * overriden by ``name`` and ``topic`` keys.
+ * Takes precedence over events set by `preset`, but gets
+ * overriden by `name` and `topic` keys.
*
* \param preset
* Convenience parameter for setting various default state events
* based on a preset.
*
- * If unspecified, the server should use the ``visibility`` to determine
- * which preset to use. A visbility of ``public`` equates to a preset of
- * ``public_chat`` and ``private`` visibility equates to a preset of
- * ``private_chat``.
+ * If unspecified, the server should use the `visibility` to determine
+ * which preset to use. A visbility of `public` equates to a preset of
+ * `public_chat` and `private` visibility equates to a preset of
+ * `private_chat`.
*
* \param isDirect
- * This flag makes the server set the ``is_direct`` flag on the
- * ``m.room.member`` events sent to the users in ``invite`` and
- * ``invite_3pid``. See `Direct Messaging`_ for more information.
+ * This flag makes the server set the `is_direct` flag on the
+ * `m.room.member` events sent to the users in `invite` and
+ * `invite_3pid`. See [Direct
+ * Messaging](/client-server-api/#direct-messaging) for more information.
*
* \param powerLevelContentOverride
* The power level content to override in the default power level
* event. This object is applied on top of the generated
- * `m.room.power_levels`_ event content prior to it being sent to the room.
- * Defaults to overriding nothing.
+ * [`m.room.power_levels`](client-server-api/#mroompower_levels)
+ * event content prior to it being sent to the room. Defaults to
+ * overriding nothing.
*/
explicit CreateRoomJob(const QString& visibility = {},
const QString& roomAliasName = {},
diff --git a/lib/csapi/definitions/device_keys.h b/lib/csapi/definitions/device_keys.h
index 3065f218..84ecefae 100644
--- a/lib/csapi/definitions/device_keys.h
+++ b/lib/csapi/definitions/device_keys.h
@@ -21,15 +21,15 @@ struct DeviceKeys {
QStringList algorithms;
/// Public identity keys. The names of the properties should be in the
- /// format ``<algorithm>:<device_id>``. The keys themselves should be
+ /// format `<algorithm>:<device_id>`. The keys themselves should be
/// encoded as specified by the key algorithm.
QHash<QString, QString> keys;
/// Signatures for the device key object. A map from user ID, to a map from
- /// ``<algorithm>:<device_id>`` to the signature.
+ /// `<algorithm>:<device_id>` to the signature.
///
- /// The signature is calculated using the process described at `Signing
- /// JSON`_.
+ /// The signature is calculated using the process described at [Signing
+ /// JSON](/appendices/#signing-json).
QHash<QString, QHash<QString, QString>> signatures;
};
diff --git a/lib/csapi/definitions/event_filter.h b/lib/csapi/definitions/event_filter.h
index 67497412..c55d4f92 100644
--- a/lib/csapi/definitions/event_filter.h
+++ b/lib/csapi/definitions/event_filter.h
@@ -14,13 +14,13 @@ struct EventFilter {
/// A list of sender IDs to exclude. If this list is absent then no senders
/// are excluded. A matching sender will be excluded even if it is listed in
- /// the ``'senders'`` filter.
+ /// the `'senders'` filter.
QStringList notSenders;
/// A list of event types to exclude. If this list is absent then no event
/// types are excluded. A matching type will be excluded even if it is
- /// listed in the ``'types'`` filter. A '*' can be used as a wildcard to
- /// match any sequence of characters.
+ /// listed in the `'types'` filter. A '*' can be used as a wildcard to match
+ /// any sequence of characters.
QStringList notTypes;
/// A list of senders IDs to include. If this list is absent then all
@@ -28,7 +28,7 @@ struct EventFilter {
QStringList senders;
/// 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
+ /// types are included. A `'*'` can be used as a wildcard to match any
/// sequence of characters.
QStringList types;
};
diff --git a/lib/csapi/definitions/openid_token.h b/lib/csapi/definitions/openid_token.h
index 5e68c376..3c447321 100644
--- a/lib/csapi/definitions/openid_token.h
+++ b/lib/csapi/definitions/openid_token.h
@@ -11,10 +11,10 @@ namespace Quotient {
struct OpenidToken {
/// An access token the consumer may use to verify the identity of
/// the person who generated the token. This is given to the federation
- /// API ``GET /openid/userinfo`` to verify the user's identity.
+ /// API `GET /openid/userinfo` to verify the user's identity.
QString accessToken;
- /// The string ``Bearer``.
+ /// The string `Bearer`.
QString tokenType;
/// The homeserver domain the consumer should use when attempting to
diff --git a/lib/csapi/definitions/push_condition.h b/lib/csapi/definitions/push_condition.h
index a6decf1b..ce66d075 100644
--- a/lib/csapi/definitions/push_condition.h
+++ b/lib/csapi/definitions/push_condition.h
@@ -9,26 +9,27 @@
namespace Quotient {
struct PushCondition {
- /// The kind of condition to apply. See `conditions <#conditions>`_ for
- /// more information on the allowed kinds and how they work.
+ /// The kind of condition to apply. See
+ /// [conditions](/client-server-api/#conditions) for more information on the
+ /// allowed kinds and how they work.
QString kind;
- /// Required for ``event_match`` conditions. The dot-separated field of the
+ /// Required for `event_match` conditions. The dot-separated field of the
/// event to match.
///
- /// Required for ``sender_notification_permission`` conditions. The field in
+ /// Required for `sender_notification_permission` conditions. The field in
/// the power level event the user needs a minimum power level for. Fields
- /// must be specified under the ``notifications`` property in the power
- /// level event's ``content``.
+ /// must be specified under the `notifications` property in the power level
+ /// event's `content`.
QString key;
- /// Required for ``event_match`` conditions. The glob-style pattern to
+ /// Required for `event_match` conditions. The glob-style pattern to
/// match against. Patterns with no special glob characters should be
/// treated as having asterisks prepended and appended when testing the
/// condition.
QString pattern;
- /// Required for ``room_member_count`` conditions. A decimal integer
+ /// Required for `room_member_count` conditions. A decimal integer
/// optionally prefixed by one of, ==, <, >, >= or <=. A prefix of < matches
/// rooms where the member count is strictly less than the given number and
/// so forth. If no prefix is present, this parameter defaults to ==.
diff --git a/lib/csapi/definitions/push_rule.h b/lib/csapi/definitions/push_rule.h
index 43749bae..135537c1 100644
--- a/lib/csapi/definitions/push_rule.h
+++ b/lib/csapi/definitions/push_rule.h
@@ -25,10 +25,10 @@ struct PushRule {
/// The conditions that must hold true for an event in order for a rule to
/// be applied to an event. A rule with no conditions always matches. Only
- /// applicable to ``underride`` and ``override`` rules.
+ /// applicable to `underride` and `override` rules.
QVector<PushCondition> conditions;
- /// The glob-style pattern to match against. Only applicable to ``content``
+ /// The glob-style pattern to match against. Only applicable to `content`
/// rules.
QString pattern;
};
diff --git a/lib/csapi/definitions/request_email_validation.h b/lib/csapi/definitions/request_email_validation.h
index ab34862e..b1781e27 100644
--- a/lib/csapi/definitions/request_email_validation.h
+++ b/lib/csapi/definitions/request_email_validation.h
@@ -16,15 +16,14 @@ struct EmailValidationData : RequestEmailValidation {
/// 3PID verification.
///
/// This parameter is deprecated with a plan to be removed in a future
- /// specification version for ``/account/password`` and ``/register``
- /// requests.
+ /// specification version for `/account/password` and `/register` requests.
QString idServer;
/// An access token previously registered with the identity server. Servers
/// can treat this as optional to distinguish between r0.5-compatible
/// clients and this specification version.
///
- /// Required if an ``id_server`` is supplied.
+ /// Required if an `id_server` is supplied.
QString idAccessToken;
};
diff --git a/lib/csapi/definitions/request_msisdn_validation.h b/lib/csapi/definitions/request_msisdn_validation.h
index 8539cd98..4600b48c 100644
--- a/lib/csapi/definitions/request_msisdn_validation.h
+++ b/lib/csapi/definitions/request_msisdn_validation.h
@@ -16,15 +16,14 @@ struct MsisdnValidationData : RequestMsisdnValidation {
/// 3PID verification.
///
/// This parameter is deprecated with a plan to be removed in a future
- /// specification version for ``/account/password`` and ``/register``
- /// requests.
+ /// specification version for `/account/password` and `/register` requests.
QString idServer;
/// An access token previously registered with the identity server. Servers
/// can treat this as optional to distinguish between r0.5-compatible
/// clients and this specification version.
///
- /// Required if an ``id_server`` is supplied.
+ /// Required if an `id_server` is supplied.
QString idAccessToken;
};
diff --git a/lib/csapi/definitions/request_token_response.h b/lib/csapi/definitions/request_token_response.h
index 00222839..f9981100 100644
--- a/lib/csapi/definitions/request_token_response.h
+++ b/lib/csapi/definitions/request_token_response.h
@@ -10,20 +10,20 @@ namespace Quotient {
struct RequestTokenResponse {
/// The session ID. Session IDs are opaque strings that must consist
- /// entirely of the characters ``[0-9a-zA-Z.=_-]``. Their length must not
+ /// entirely of the characters `[0-9a-zA-Z.=_-]`. Their length must not
/// exceed 255 characters and they must not be empty.
QString sid;
/// An optional field containing a URL where the client must submit the
/// validation token to, with identical parameters to the Identity Service
- /// API's ``POST /validate/email/submitToken`` endpoint (without the
+ /// API's `POST /validate/email/submitToken` endpoint (without the
/// requirement for an access token). The homeserver must send this token to
/// the user (if applicable), who should then be prompted to provide it to
/// the client.
///
/// If this field is not present, the client can assume that verification
/// will happen without the client's involvement provided the homeserver
- /// advertises this specification version in the ``/versions`` response
+ /// advertises this specification version in the `/versions` response
/// (ie: r0.5.0).
QString submitUrl;
};
diff --git a/lib/csapi/definitions/room_event_filter.h b/lib/csapi/definitions/room_event_filter.h
index 11e87fde..91caf667 100644
--- a/lib/csapi/definitions/room_event_filter.h
+++ b/lib/csapi/definitions/room_event_filter.h
@@ -11,30 +11,31 @@
namespace Quotient {
struct RoomEventFilter : EventFilter {
- /// If ``true``, enables lazy-loading of membership events. See
- /// `Lazy-loading room members <#lazy-loading-room-members>`_
- /// for more information. Defaults to ``false``.
+ /// If `true`, enables lazy-loading of membership events. See
+ /// [Lazy-loading room
+ /// members](/client-server-api/#lazy-loading-room-members) for more
+ /// information. Defaults to `false`.
Omittable<bool> lazyLoadMembers;
- /// If ``true``, sends all membership events for all events, even if they
- /// have already been sent to the client. Does not apply unless
- /// ``lazy_load_members`` is ``true``. See `Lazy-loading room members
- /// <#lazy-loading-room-members>`_ for more information. Defaults to
- /// ``false``.
+ /// If `true`, sends all membership events for all events, even if they have
+ /// already been sent to the client. Does not apply unless
+ /// `lazy_load_members` is `true`. See [Lazy-loading room
+ /// members](/client-server-api/#lazy-loading-room-members) for more
+ /// information. Defaults to `false`.
Omittable<bool> includeRedundantMembers;
/// A list of room IDs to exclude. If this list is absent then no rooms are
/// excluded. A matching room will be excluded even if it is listed in the
- /// ``'rooms'`` filter.
+ /// `'rooms'` filter.
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. If omitted, ``url`` key is not
- /// considered for filtering.
+ /// 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<bool> containsUrl;
};
diff --git a/lib/csapi/definitions/sync_filter.h b/lib/csapi/definitions/sync_filter.h
index 9c8f08d5..62e17962 100644
--- a/lib/csapi/definitions/sync_filter.h
+++ b/lib/csapi/definitions/sync_filter.h
@@ -14,13 +14,13 @@ namespace Quotient {
struct RoomFilter {
/// A list of room IDs to exclude. If this list is absent then no rooms are
/// excluded. A matching room will be excluded even if it is listed in the
- /// ``'rooms'`` filter. This filter is applied before the filters in
- /// ``ephemeral``, ``state``, ``timeline`` or ``account_data``
+ /// `'rooms'` filter. This filter is applied before the filters in
+ /// `ephemeral`, `state`, `timeline` or `account_data`
QStringList notRooms;
/// A list of room IDs to include. If this list is absent then all rooms are
- /// included. This filter is applied before the filters in ``ephemeral``,
- /// ``state``, ``timeline`` or ``account_data``
+ /// included. This filter is applied before the filters in `ephemeral`,
+ /// `state`, `timeline` or `account_data`
QStringList rooms;
/// The events that aren't recorded in the room history, e.g. typing and
diff --git a/lib/csapi/definitions/third_party_signed.h b/lib/csapi/definitions/third_party_signed.h
index 526545d0..7097bda4 100644
--- a/lib/csapi/definitions/third_party_signed.h
+++ b/lib/csapi/definitions/third_party_signed.h
@@ -7,7 +7,7 @@
#include "converters.h"
namespace Quotient {
-/// A signature of an ``m.third_party_invite`` token to prove that this user
+/// A signature of an `m.third_party_invite` token to prove that this user
/// owns a third party identity which has been invited to the room.
struct ThirdPartySigned {
/// The Matrix ID of the user who issued the invite.
diff --git a/lib/csapi/definitions/user_identifier.h b/lib/csapi/definitions/user_identifier.h
index dadf6f97..cb585a6a 100644
--- a/lib/csapi/definitions/user_identifier.h
+++ b/lib/csapi/definitions/user_identifier.h
@@ -9,8 +9,9 @@
namespace Quotient {
/// Identification information for a user
struct UserIdentifier {
- /// The type of identification. See `Identifier types`_ for supported
- /// values and additional property descriptions.
+ /// The type of identification. See [Identifier
+ /// types](/client-server-api/#identifier-types) for supported values and
+ /// additional property descriptions.
QString type;
/// Identification information for a user
diff --git a/lib/csapi/device_management.h b/lib/csapi/device_management.h
index 47dc7ec8..7fb69873 100644
--- a/lib/csapi/device_management.h
+++ b/lib/csapi/device_management.h
@@ -83,7 +83,8 @@ public:
/*! \brief Delete a device
*
- * This API endpoint uses the `User-Interactive Authentication API`_.
+ * This API endpoint uses the [User-Interactive Authentication
+ * API](/client-server-api/#user-interactive-authentication-api).
*
* Deletes the given device, and invalidates any access token associated with it.
*/
@@ -104,7 +105,8 @@ public:
/*! \brief Bulk deletion of devices
*
- * This API endpoint uses the `User-Interactive Authentication API`_.
+ * This API endpoint uses the [User-Interactive Authentication
+ * API](/client-server-api/#user-interactive-authentication-api).
*
* Deletes the given devices, and invalidates any access token associated with
* them.
diff --git a/lib/csapi/directory.h b/lib/csapi/directory.h
index 9b109aad..93a31595 100644
--- a/lib/csapi/directory.h
+++ b/lib/csapi/directory.h
@@ -68,13 +68,13 @@ public:
* instance that room aliases can only be deleted by their creator or a server
* administrator.
*
- * .. Note::
- * Servers may choose to update the ``alt_aliases`` for the
- * ``m.room.canonical_alias`` state event in the room when an alias is removed.
+ * **Note:**
+ * Servers may choose to update the `alt_aliases` for the
+ * `m.room.canonical_alias` state event in the room when an alias is removed.
* Servers which choose to update the canonical alias event are recommended to,
* in addition to their other relevant permission checks, delete the alias and
* return a successful response even if the user does not have permission to
- * update the ``m.room.canonical_alias`` event.
+ * update the `m.room.canonical_alias` event.
*/
class DeleteRoomAliasJob : public BaseJob {
public:
@@ -99,18 +99,18 @@ public:
* given room.
*
* This endpoint can be called by users who are in the room (external
- * users receive an ``M_FORBIDDEN`` error response). If the room's
- * ``m.room.history_visibility`` maps to ``world_readable``, any
+ * users receive an `M_FORBIDDEN` error response). If the room's
+ * `m.room.history_visibility` maps to `world_readable`, any
* user can call this endpoint.
*
* Servers may choose to implement additional access control checks here,
* such as allowing server administrators to view aliases regardless of
* membership.
*
- * .. Note::
- * Clients are recommended not to display this list of aliases prominently
- * as they are not curated, unlike those listed in the
- * ``m.room.canonical_alias`` state event.
+ * **Note:**
+ * Clients are recommended not to display this list of aliases prominently
+ * as they are not curated, unlike those listed in the `m.room.canonical_alias`
+ * state event.
*/
class GetLocalAliasesJob : public BaseJob {
public:
diff --git a/lib/csapi/event_context.h b/lib/csapi/event_context.h
index d82d16ab..4e50edf3 100644
--- a/lib/csapi/event_context.h
+++ b/lib/csapi/event_context.h
@@ -16,8 +16,8 @@ namespace Quotient {
* surrounding an event.
*
* *Note*: This endpoint supports lazy-loading of room member events. See
- * `Lazy-loading room members <#lazy-loading-room-members>`_ for more
- * information.
+ * [Lazy-loading room members](/client-server-api/#lazy-loading-room-members)
+ * for more information.
*/
class GetEventContextJob : public BaseJob {
public:
@@ -33,13 +33,13 @@ public:
* The maximum number of events to return. Default: 10.
*
* \param filter
- * A JSON ``RoomEventFilter`` to filter the returned events with. The
- * filter is only applied to ``events_before``, ``events_after``, and
- * ``state``. It is not applied to the ``event`` itself. The filter may
- * be applied before or/and after the ``limit`` parameter - whichever the
+ * A JSON `RoomEventFilter` to filter the returned events with. The
+ * filter is only applied to `events_before`, `events_after`, and
+ * `state`. It is not applied to the `event` itself. The filter may
+ * be applied before or/and after the `limit` parameter - whichever the
* homeserver prefers.
*
- * See `Filtering <#filtering>`_ for more information.
+ * See [Filtering](/client-server-api/#filtering) for more information.
*/
explicit GetEventContextJob(const QString& roomId, const QString& eventId,
Omittable<int> limit = none,
diff --git a/lib/csapi/filter.h b/lib/csapi/filter.h
index f07b489c..01bec36b 100644
--- a/lib/csapi/filter.h
+++ b/lib/csapi/filter.h
@@ -32,7 +32,7 @@ public:
// Result properties
/// The ID of the filter that was created. Cannot start
- /// with a ``{`` as this character is used to determine
+ /// with a `{` as this character is used to determine
/// if the filter provided is inline JSON or a previously
/// declared filter by homeservers on some APIs.
QString filterId() const { return loadFromJson<QString>("filter_id"_ls); }
diff --git a/lib/csapi/inviting.h b/lib/csapi/inviting.h
index 59a61b89..1e65ecff 100644
--- a/lib/csapi/inviting.h
+++ b/lib/csapi/inviting.h
@@ -10,12 +10,11 @@ namespace Quotient {
/*! \brief Invite a user to participate in a particular room.
*
- * .. _invite-by-user-id-endpoint:
- *
* *Note that there are two forms of this API, which are documented separately.
* This version of the API requires that the inviter knows the Matrix
* identifier of the invitee. The other is documented in the*
- * `third party invites section`_.
+ * [third party invites
+ * section](/client-server-api/#post_matrixclientr0roomsroomidinvite-1).
*
* This API invites a user to participate in a particular room.
* They do not start participating in the room until they actually join the
@@ -25,9 +24,7 @@ namespace Quotient {
* join that room.
*
* If the user was invited to the room, the homeserver will append a
- * ``m.room.member`` event to the room.
- *
- * .. _third party invites section: `invite-by-third-party-id-endpoint`_
+ * `m.room.member` event to the room.
*/
class InviteUserJob : public BaseJob {
public:
diff --git a/lib/csapi/joining.h b/lib/csapi/joining.h
index dd936f92..1b6f99e4 100644
--- a/lib/csapi/joining.h
+++ b/lib/csapi/joining.h
@@ -13,7 +13,7 @@ namespace Quotient {
/*! \brief Start the requesting user participating in a particular room.
*
* *Note that this API requires a room ID, not alias.*
- * ``/join/{roomIdOrAlias}`` *exists if you have a room alias.*
+ * `/join/{roomIdOrAlias}` *exists if you have a room alias.*
*
* This API starts a user participating in a particular room, if that user
* is allowed to participate in that room. After this call, the client is
@@ -21,7 +21,9 @@ namespace Quotient {
* events associated with the room until the user leaves the room.
*
* After a user has joined a room, the room will appear as an entry in the
- * response of the |/initialSync|_ and |/sync|_ APIs.
+ * response of the
+ * [`/initialSync`](/client-server-api/#get_matrixclientr0initialsync) and
+ * [`/sync`](/client-server-api/#get_matrixclientr0sync) APIs.
*/
class JoinRoomByIdJob : public BaseJob {
public:
@@ -32,7 +34,7 @@ public:
*
* \param thirdPartySigned
* If supplied, the homeserver must verify that it matches a pending
- * ``m.room.third_party_invite`` event in the room, and perform
+ * `m.room.third_party_invite` event in the room, and perform
* key validity checking if required by the event.
*/
explicit JoinRoomByIdJob(
@@ -48,7 +50,7 @@ public:
/*! \brief Start the requesting user participating in a particular room.
*
* *Note that this API takes either a room ID or alias, unlike*
- * ``/room/{roomId}/join``.
+ * `/room/{roomId}/join`.
*
* This API starts a user participating in a particular room, if that user
* is allowed to participate in that room. After this call, the client is
@@ -56,7 +58,9 @@ public:
* events associated with the room until the user leaves the room.
*
* After a user has joined a room, the room will appear as an entry in the
- * response of the |/initialSync|_ and |/sync|_ APIs.
+ * response of the
+ * [`/initialSync`](/client-server-api/#get_matrixclientr0initialsync) and
+ * [`/sync`](/client-server-api/#get_matrixclientr0sync) APIs.
*/
class JoinRoomJob : public BaseJob {
public:
@@ -70,8 +74,8 @@ public:
* must be participating in the room.
*
* \param thirdPartySigned
- * If a ``third_party_signed`` was supplied, the homeserver must verify
- * that it matches a pending ``m.room.third_party_invite`` event in the
+ * If a `third_party_signed` was supplied, the homeserver must verify
+ * that it matches a pending `m.room.third_party_invite` event in the
* room, and perform key validity checking if required by the event.
*/
explicit JoinRoomJob(
diff --git a/lib/csapi/keys.h b/lib/csapi/keys.h
index 8f6c8cc9..621945eb 100644
--- a/lib/csapi/keys.h
+++ b/lib/csapi/keys.h
@@ -25,8 +25,8 @@ public:
* \param oneTimeKeys
* One-time public keys for "pre-key" messages. The names of
* the properties should be in the format
- * ``<algorithm>:<key_id>``. The format of the key is determined
- * by the `key algorithm <#key-algorithms>`_.
+ * `<algorithm>:<key_id>`. The format of the key is determined
+ * by the [key algorithm](/client-server-api/#key-algorithms).
*
* May be absent if no new one-time keys are required.
*/
@@ -98,7 +98,7 @@ public:
///
/// If the homeserver could be reached, but the user or device
/// was unknown, no failure is recorded. Instead, the corresponding
- /// user or device is missing from the ``device_keys`` result.
+ /// user or device is missing from the `device_keys` result.
QHash<QString, QJsonObject> failures() const
{
return loadFromJson<QHash<QString, QJsonObject>>("failures"_ls);
@@ -107,7 +107,7 @@ public:
/// Information on the queried devices. A map from user ID, to a
/// map from device ID to device information. For each device,
/// the information returned will be the same as uploaded via
- /// ``/keys/upload``, with the addition of an ``unsigned``
+ /// `/keys/upload`, with the addition of an `unsigned`
/// property.
QHash<QString, QHash<QString, DeviceInformation>> deviceKeys() const
{
@@ -163,18 +163,17 @@ public:
///
/// If the homeserver could be reached, but the user or device
/// was unknown, no failure is recorded. Instead, the corresponding
- /// user or device is missing from the ``one_time_keys`` result.
+ /// user or device is missing from the `one_time_keys` result.
QHash<QString, QJsonObject> failures() const
{
return loadFromJson<QHash<QString, QJsonObject>>("failures"_ls);
}
/// One-time keys for the queried devices. A map from user ID, to a
- /// map from devices to a map from ``<algorithm>:<key_id>`` to the key
- /// object.
+ /// map from devices to a map from `<algorithm>:<key_id>` to the key object.
///
- /// See the `key algorithms <#key-algorithms>`_ section for information
- /// on the Key Object format.
+ /// See the [key algorithms](/client-server-api/#key-algorithms) section for
+ /// information on the Key Object format.
QHash<QString, QHash<QString, QVariant>> oneTimeKeys() const
{
return loadFromJson<QHash<QString, QHash<QString, QVariant>>>(
@@ -190,26 +189,28 @@ public:
* The server should include in the results any users who:
*
* * currently share a room with the calling user (ie, both users have
- * membership state ``join``); *and*
+ * membership state `join`); *and*
* * added new device identity keys or removed an existing device with
- * identity keys, between ``from`` and ``to``.
+ * identity keys, between `from` and `to`.
*/
class GetKeysChangesJob : public BaseJob {
public:
/*! \brief Query users with recent device key updates.
*
* \param from
- * The desired start point of the list. Should be the ``next_batch`` field
- * from a response to an earlier call to |/sync|. Users who have not
+ * The desired start point of the list. Should be the `next_batch` field
+ * from a response to an earlier call to
+ * [`/sync`](/client-server-api/#get_matrixclientr0sync). Users who have not
* uploaded new device identity keys since this point, nor deleted
* existing devices with identity keys since then, will be excluded
* from the results.
*
* \param to
- * The desired end point of the list. Should be the ``next_batch``
- * field from a recent call to |/sync| - typically the most recent
- * such call. This may be used by the server as a hint to check its
- * caches are up to date.
+ * The desired end point of the list. Should be the `next_batch`
+ * field from a recent call to
+ * [`/sync`](/client-server-api/#get_matrixclientr0sync) - typically the
+ * most recent such call. This may be used by the server as a hint to check
+ * its caches are up to date.
*/
explicit GetKeysChangesJob(const QString& from, const QString& to);
diff --git a/lib/csapi/kicking.h b/lib/csapi/kicking.h
index 2645a54f..11018368 100644
--- a/lib/csapi/kicking.h
+++ b/lib/csapi/kicking.h
@@ -15,10 +15,10 @@ namespace Quotient {
* The caller must have the required power level in order to perform this
* operation.
*
- * Kicking a user adjusts the target member's membership state to be ``leave``
- * with an optional ``reason``. Like with other membership changes, a user can
+ * Kicking a user adjusts the target member's membership state to be `leave`
+ * with an optional `reason`. Like with other membership changes, a user can
* directly adjust the target member's state by making a request to
- * ``/rooms/<room id>/state/m.room.member/<user id>``.
+ * `/rooms/<room id>/state/m.room.member/<user id>`.
*/
class KickJob : public BaseJob {
public:
@@ -32,7 +32,8 @@ public:
*
* \param reason
* The reason the user has been kicked. This will be supplied as the
- * ``reason`` on the target's updated `m.room.member`_ event.
+ * `reason` on the target's updated
+ * [`m.room.member`](/client-server-api/#mroommember) event.
*/
explicit KickJob(const QString& roomId, const QString& userId,
const QString& reason = {});
diff --git a/lib/csapi/list_joined_rooms.h b/lib/csapi/list_joined_rooms.h
index 1034aa7b..59a24a49 100644
--- a/lib/csapi/list_joined_rooms.h
+++ b/lib/csapi/list_joined_rooms.h
@@ -26,7 +26,7 @@ public:
// Result properties
- /// The ID of each room in which the user has ``joined`` membership.
+ /// The ID of each room in which the user has `joined` membership.
QStringList joinedRooms() const
{
return loadFromJson<QStringList>("joined_rooms"_ls);
diff --git a/lib/csapi/list_public_rooms.h b/lib/csapi/list_public_rooms.h
index b0cb79d2..963c8b56 100644
--- a/lib/csapi/list_public_rooms.h
+++ b/lib/csapi/list_public_rooms.h
@@ -170,7 +170,7 @@ public:
*
* \param thirdPartyInstanceId
* The specific third party network/protocol to request from the
- * homeserver. Can only be used if ``include_all_networks`` is false.
+ * homeserver. Can only be used if `include_all_networks` is false.
*/
explicit QueryPublicRoomsJob(const QString& server = {},
Omittable<int> limit = none,
diff --git a/lib/csapi/login.h b/lib/csapi/login.h
index a406fc79..b35db1eb 100644
--- a/lib/csapi/login.h
+++ b/lib/csapi/login.h
@@ -14,17 +14,17 @@ namespace Quotient {
/*! \brief Get the supported login types to authenticate users
*
* Gets the homeserver's supported login types to authenticate users. Clients
- * should pick one of these and supply it as the ``type`` when logging in.
+ * should pick one of these and supply it as the `type` when logging in.
*/
class GetLoginFlowsJob : public BaseJob {
public:
// Inner data structures
/// Gets the homeserver's supported login types to authenticate users.
- /// Clients should pick one of these and supply it as the ``type`` when
+ /// Clients should pick one of these and supply it as the `type` when
/// logging in.
struct LoginFlow {
- /// The login type. This is supplied as the ``type`` when
+ /// The login type. This is supplied as the `type` when
/// logging in.
QString type;
};
@@ -64,13 +64,14 @@ struct JsonObjectConverter<GetLoginFlowsJob::LoginFlow> {
* Authenticates the user, and issues an access token they can
* use to authorize themself in subsequent requests.
*
- * If the client does not supply a ``device_id``, the server must
+ * If the client does not supply a `device_id`, the server must
* auto-generate one.
*
- * The returned access token must be associated with the ``device_id``
+ * The returned access token must be associated with the `device_id`
* supplied by the client or generated by the server. The server may
* invalidate any access token previously associated with that device. See
- * `Relationship between access tokens and devices`_.
+ * [Relationship between access tokens and
+ * devices](/client-server-api/#relationship-between-access-tokens-and-devices).
*/
class LoginJob : public BaseJob {
public:
@@ -83,30 +84,33 @@ public:
* Authenticates the user, and issues an access token they can
* use to authorize themself in subsequent requests.
*
- * If the client does not supply a ``device_id``, the server must
+ * If the client does not supply a `device_id`, the server must
* auto-generate one.
*
- * The returned access token must be associated with the ``device_id``
+ * The returned access token must be associated with the `device_id`
* supplied by the client or generated by the server. The server may
* invalidate any access token previously associated with that device. See
- * `Relationship between access tokens and devices`_.
+ * [Relationship between access tokens and
+ * devices](/client-server-api/#relationship-between-access-tokens-and-devices).
*
* \param password
- * Required when ``type`` is ``m.login.password``. The user's
+ * Required when `type` is `m.login.password`. The user's
* password.
*
* \param token
- * Required when ``type`` is ``m.login.token``. Part of `Token-based`_
- * login.
+ * Required when `type` is `m.login.token`. Part of Token-based login.
*
* \param deviceId
* ID of the client device. If this does not correspond to a
- * known client device, a new device will be created. The server
- * will auto-generate a device_id if this is not specified.
+ * known client device, a new device will be created. The given
+ * device ID must not be the same as a
+ * [cross-signing](/client-server-api/#cross-signing) key ID.
+ * The server will auto-generate a device_id
+ * if this is not specified.
*
* \param initialDeviceDisplayName
* A display name to assign to the newly-created device. Ignored
- * if ``device_id`` corresponds to a known device.
+ * if `device_id` corresponds to a known device.
*/
explicit LoginJob(const QString& type,
const Omittable<UserIdentifier>& identifier = none,
@@ -130,8 +134,8 @@ public:
/// been registered.
///
/// **Deprecated**. Clients should extract the server_name from
- /// ``user_id`` (by splitting at the first colon) if they require
- /// it. Note also that ``homeserver`` is not spelt this way.
+ /// `user_id` (by splitting at the first colon) if they require
+ /// it. Note also that `homeserver` is not spelt this way.
QString homeServer() const
{
return loadFromJson<QString>("home_server"_ls);
diff --git a/lib/csapi/logout.h b/lib/csapi/logout.h
index 78f14e40..2e4c2692 100644
--- a/lib/csapi/logout.h
+++ b/lib/csapi/logout.h
@@ -12,7 +12,8 @@ namespace Quotient {
*
* Invalidates an existing access token, so that it can no longer be used for
* authorization. The device associated with the access token is also deleted.
- * `Device keys <#device-keys>`_ for the device are deleted alongside the device.
+ * [Device keys](/client-server-api/#device-keys) for the device are deleted
+ * alongside the device.
*/
class LogoutJob : public BaseJob {
public:
@@ -31,10 +32,12 @@ public:
*
* Invalidates all access tokens for a user, so that they can no longer be used
* for authorization. This includes the access token that made this request. All
- * devices for the user are also deleted. `Device keys <#device-keys>`_ for the
- * device are deleted alongside the device.
+ * devices for the user are also deleted. [Device
+ * keys](/client-server-api/#device-keys) for the device are deleted alongside
+ * the device.
*
- * This endpoint does not use the `User-Interactive Authentication API`_ because
+ * This endpoint does not use the [User-Interactive Authentication
+ * API](/client-server-api/#user-interactive-authentication-api) because
* User-Interactive Authentication is designed to protect against attacks where
* the someone gets hold of a single access token then takes over the account.
* This endpoint invalidates all access tokens for the user, including the token
diff --git a/lib/csapi/message_pagination.h b/lib/csapi/message_pagination.h
index 286d4237..363e4d99 100644
--- a/lib/csapi/message_pagination.h
+++ b/lib/csapi/message_pagination.h
@@ -15,8 +15,8 @@ namespace Quotient {
* pagination query parameters to paginate history in the room.
*
* *Note*: This endpoint supports lazy-loading of room member events. See
- * `Lazy-loading room members <#lazy-loading-room-members>`_ for more
- * information.
+ * [Lazy-loading room members](/client-server-api/#lazy-loading-room-members)
+ * for more information.
*/
class GetRoomEventsJob : public BaseJob {
public:
@@ -27,8 +27,8 @@ public:
*
* \param from
* The token to start returning events from. 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 previous request
+ * from a `prev_batch` token returned for each room by the sync API,
+ * or from a `start` or `end` token returned by a previous request
* to this endpoint.
*
* \param dir
@@ -36,8 +36,8 @@ public:
*
* \param to
* The token to stop returning events at. This token can be obtained from
- * a ``prev_batch`` token returned for each room by the sync endpoint,
- * or from a ``start`` or ``end`` token returned by a previous request to
+ * a `prev_batch` token returned for each room by the sync endpoint,
+ * or from a `start` or `end` token returned by a previous request to
* this endpoint.
*
* \param limit
@@ -64,25 +64,25 @@ public:
// Result properties
- /// The token the pagination starts from. If ``dir=b`` this will be
- /// the token supplied in ``from``.
+ /// The token the pagination starts from. If `dir=b` this will be
+ /// the token supplied in `from`.
QString begin() const { return loadFromJson<QString>("start"_ls); }
- /// The token the pagination ends at. If ``dir=b`` this token should
+ /// The token the pagination ends at. If `dir=b` this token should
/// be used again to request even earlier events.
QString end() const { return loadFromJson<QString>("end"_ls); }
- /// A list of room events. The order depends on the ``dir`` parameter.
- /// For ``dir=b`` events will be in reverse-chronological order,
- /// for ``dir=f`` in chronological order, so that events start
- /// at the ``from`` point.
+ /// A list of room events. The order depends on the `dir` parameter.
+ /// For `dir=b` events will be in reverse-chronological order,
+ /// for `dir=f` in chronological order, so that events start
+ /// at the `from` point.
RoomEvents chunk() { return takeFromJson<RoomEvents>("chunk"_ls); }
- /// A list of state events relevant to showing the ``chunk``. For example, if
- /// ``lazy_load_members`` is enabled in the filter then this may contain
- /// the membership events for the senders of events in the ``chunk``.
+ /// A list of state events relevant to showing the `chunk`. For example, if
+ /// `lazy_load_members` is enabled in the filter then this may contain
+ /// the membership events for the senders of events in the `chunk`.
///
- /// Unless ``include_redundant_members`` is ``true``, the server
+ /// Unless `include_redundant_members` is `true`, the server
/// may remove membership events which would have already been
/// sent to the client in prior calls to this endpoint, assuming
/// the membership of those members has not changed.
diff --git a/lib/csapi/notifications.h b/lib/csapi/notifications.h
index ff499c7a..0999fece 100644
--- a/lib/csapi/notifications.h
+++ b/lib/csapi/notifications.h
@@ -22,7 +22,7 @@ public:
/// user has been, or would have been notified about.
struct Notification {
/// The action(s) to perform when the conditions for this rule are met.
- /// See `Push Rules: API`_.
+ /// See [Push Rules: API](/client-server-api/#push-rules-api).
QVector<QVariant> actions;
/// The Event object for the event that triggered the notification.
EventPtr event;
@@ -49,7 +49,7 @@ public:
* Limit on the number of events to return in this request.
*
* \param only
- * Allows basic filtering of events returned. Supply ``highlight``
+ * Allows basic filtering of events returned. Supply `highlight`
* to return only events where the notification had the highlight
* tweak set.
*/
@@ -68,8 +68,8 @@ public:
// Result properties
- /// The token to supply in the ``from`` param of the next
- /// ``/notifications`` request in order to request more
+ /// The token to supply in the `from` param of the next
+ /// `/notifications` request in order to request more
/// events. If this is absent, there are no more results.
QString nextToken() const { return loadFromJson<QString>("next_token"_ls); }
diff --git a/lib/csapi/openid.h b/lib/csapi/openid.h
index efb5f623..0be39c8c 100644
--- a/lib/csapi/openid.h
+++ b/lib/csapi/openid.h
@@ -18,7 +18,7 @@ namespace Quotient {
* OpenID.
*
* The access token generated is only valid for the OpenID API. It cannot
- * be used to request another OpenID access token or call ``/sync``, for
+ * be used to request another OpenID access token or call `/sync`, for
* example.
*/
class RequestOpenIdTokenJob : public BaseJob {
@@ -38,9 +38,10 @@ public:
// Result properties
/// OpenID token information. This response is nearly compatible with the
- /// response documented in the `OpenID Connect 1.0 Specification
- /// <http://openid.net/specs/openid-connect-core-1_0.html#TokenResponse>`_
- /// with the only difference being the lack of an ``id_token``. Instead,
+ /// response documented in the
+ /// [OpenID Connect 1.0
+ /// Specification](http://openid.net/specs/openid-connect-core-1_0.html#TokenResponse)
+ /// with the only difference being the lack of an `id_token`. Instead,
/// the Matrix homeserver's name is provided.
OpenidToken tokenData() const { return fromJson<OpenidToken>(jsonData()); }
};
diff --git a/lib/csapi/peeking_events.h b/lib/csapi/peeking_events.h
index cecd9f2d..885ff340 100644
--- a/lib/csapi/peeking_events.h
+++ b/lib/csapi/peeking_events.h
@@ -13,12 +13,12 @@ namespace Quotient {
*
* This will listen for new events related to a particular room and return
* them to the caller. This will block until an event is received, or until
- * the ``timeout`` is reached.
+ * the `timeout` is reached.
*
- * This API is the same as the normal ``/events`` endpoint, but can be
+ * This API is the same as the normal `/events` endpoint, but can be
* called by users who have not joined the room.
*
- * Note that the normal ``/events`` endpoint has been deprecated. This
+ * Note that the normal `/events` endpoint has been deprecated. This
* API will also be deprecated at some point, but its replacement is not
* yet known.
*/
@@ -51,12 +51,12 @@ public:
// Result properties
- /// A token which correlates to the first value in ``chunk``. This
- /// is usually the same token supplied to ``from=``.
+ /// A token which correlates to the first value in `chunk`. This
+ /// is usually the same token supplied to `from=`.
QString begin() const { return loadFromJson<QString>("start"_ls); }
- /// A token which correlates to the last value in ``chunk``. This
- /// token should be used in the next request to ``/events``.
+ /// A token which correlates to the last value in `chunk`. This
+ /// token should be used in the next request to `/events`.
QString end() const { return loadFromJson<QString>("end"_ls); }
/// An array of events.
diff --git a/lib/csapi/presence.h b/lib/csapi/presence.h
index a885bf4f..4ab50e25 100644
--- a/lib/csapi/presence.h
+++ b/lib/csapi/presence.h
@@ -12,7 +12,7 @@ namespace Quotient {
*
* This API sets the given user's presence state. When setting the status,
* the activity time is updated to reflect that activity; the client does
- * not need to specify the ``last_active_ago`` field. You cannot set the
+ * not need to specify the `last_active_ago` field. You cannot set the
* presence state of another user.
*/
class SetPresenceJob : public BaseJob {
diff --git a/lib/csapi/profile.h b/lib/csapi/profile.h
index 3858fab2..8bbe4f8c 100644
--- a/lib/csapi/profile.h
+++ b/lib/csapi/profile.h
@@ -11,7 +11,7 @@ namespace Quotient {
/*! \brief Set the user's display name.
*
* This API sets the given user's display name. You must have permission to
- * set this user's display name, e.g. you need to have their ``access_token``.
+ * set this user's display name, e.g. you need to have their `access_token`.
*/
class SetDisplayNameJob : public BaseJob {
public:
@@ -61,7 +61,7 @@ public:
/*! \brief Set the user's avatar URL.
*
* This API sets the given user's avatar URL. You must have permission to
- * set this user's avatar URL, e.g. you need to have their ``access_token``.
+ * set this user's avatar URL, e.g. you need to have their `access_token`.
*/
class SetAvatarUrlJob : public BaseJob {
public:
@@ -109,7 +109,7 @@ public:
* Get the combined profile information for this user. This API may be used
* to fetch the user's own profile information or other users; either
* locally or on remote homeservers. This API may return keys which are not
- * limited to ``displayname`` or ``avatar_url``.
+ * limited to `displayname` or `avatar_url`.
*/
class GetUserProfileJob : public BaseJob {
public:
diff --git a/lib/csapi/pusher.h b/lib/csapi/pusher.h
index ae0050d2..13c9ec25 100644
--- a/lib/csapi/pusher.h
+++ b/lib/csapi/pusher.h
@@ -19,7 +19,7 @@ public:
/// A dictionary of information for the pusher implementation
/// itself.
struct PusherData {
- /// Required if ``kind`` is ``http``. The URL to use to send
+ /// Required if `kind` is `http`. The URL to use to send
/// notifications to.
QString url;
/// The format to use when sending notifications to the Push
@@ -29,11 +29,11 @@ public:
/// Gets all currently active pushers for the authenticated user.
struct Pusher {
- /// This is a unique identifier for this pusher. See ``/set`` for
+ /// This is a unique identifier for this pusher. See `/set` for
/// more detail.
/// Max length, 512 bytes.
QString pushkey;
- /// The kind of pusher. ``"http"`` is a pusher that
+ /// The kind of pusher. `"http"` is a pusher that
/// sends HTTP pokes.
QString kind;
/// This is a reverse-DNS style identifier for the application.
@@ -104,27 +104,27 @@ struct JsonObjectConverter<GetPushersJob::Pusher> {
/*! \brief Modify a pusher for this user on the homeserver.
*
- * This endpoint allows the creation, modification and deletion of `pushers`_
- * for this user ID. The behaviour of this endpoint varies depending on the
- * values in the JSON body.
+ * This endpoint allows the creation, modification and deletion of
+ * [pushers](/client-server-api/#push-notifications) for this user ID. The
+ * behaviour of this endpoint varies depending on the values in the JSON body.
*/
class PostPusherJob : public BaseJob {
public:
// Inner data structures
/// A dictionary of information for the pusher implementation
- /// itself. If ``kind`` is ``http``, this should contain ``url``
+ /// itself. If `kind` is `http`, this should contain `url`
/// which is the URL to use to send notifications to.
struct PusherData {
- /// Required if ``kind`` is ``http``. The URL to use to send
+ /// Required if `kind` is `http`. The URL to use to send
/// notifications to. MUST be an HTTPS URL with a path of
- /// ``/_matrix/push/v1/notify``.
+ /// `/_matrix/push/v1/notify`.
QString url;
/// The format to send notifications in to Push Gateways if the
- /// ``kind`` is ``http``. The details about what fields the
+ /// `kind` is `http`. The details about what fields the
/// homeserver should send to the push gateway are defined in the
- /// `Push Gateway Specification`_. Currently the only format
- /// available is 'event_id_only'.
+ /// [Push Gateway Specification](/push-gateway-api/). Currently the only
+ /// format available is 'event_id_only'.
QString format;
};
@@ -140,13 +140,13 @@ public:
* client has no such concept, use any unique identifier.
* Max length, 512 bytes.
*
- * If the ``kind`` is ``"email"``, this is the email address to
+ * If the `kind` is `"email"`, this is the email address to
* send notifications to.
*
* \param kind
- * The kind of pusher to configure. ``"http"`` makes a pusher that
- * sends HTTP pokes. ``"email"`` makes a pusher that emails the
- * user with unread notifications. ``null`` deletes the pusher.
+ * The kind of pusher to configure. `"http"` makes a pusher that
+ * sends HTTP pokes. `"email"` makes a pusher that emails the
+ * user with unread notifications. `null` deletes the pusher.
*
* \param appId
* This is a reverse-DNS style identifier for the application.
@@ -154,7 +154,7 @@ public:
* different platform versions get different app identifiers.
* Max length, 64 chars.
*
- * If the ``kind`` is ``"email"``, this is ``"m.email"``.
+ * If the `kind` is `"email"`, this is `"m.email"`.
*
* \param appDisplayName
* A string that will allow the user to identify what application
@@ -170,7 +170,7 @@ public:
*
* \param data
* A dictionary of information for the pusher implementation
- * itself. If ``kind`` is ``http``, this should contain ``url``
+ * itself. If `kind` is `http`, this should contain `url`
* which is the URL to use to send notifications to.
*
* \param profileTag
@@ -182,7 +182,7 @@ public:
* given pushkey and App ID in addition to any others with
* different user IDs. Otherwise, the homeserver must remove any
* other pushers with the same App ID and pushkey for different
- * users. The default is ``false``.
+ * users. The default is `false`.
*/
explicit PostPusherJob(const QString& pushkey, const QString& kind,
const QString& appId, const QString& appDisplayName,
diff --git a/lib/csapi/pushrules.h b/lib/csapi/pushrules.h
index 1c6d5c2d..a5eb48f0 100644
--- a/lib/csapi/pushrules.h
+++ b/lib/csapi/pushrules.h
@@ -15,9 +15,9 @@ namespace Quotient {
/*! \brief Retrieve all push rulesets.
*
* Retrieve all push rulesets for this user. Clients can "drill-down" on
- * the rulesets by suffixing a ``scope`` to this path e.g.
- * ``/pushrules/global/``. This will return a subset of this data under the
- * specified key e.g. the ``global`` key.
+ * the rulesets by suffixing a `scope` to this path e.g.
+ * `/pushrules/global/`. This will return a subset of this data under the
+ * specified key e.g. the `global` key.
*/
class GetPushRulesJob : public BaseJob {
public:
@@ -49,7 +49,7 @@ public:
/*! \brief Retrieve a push rule.
*
* \param scope
- * ``global`` to specify global rules.
+ * `global` to specify global rules.
*
* \param kind
* The kind of rule
@@ -71,7 +71,7 @@ public:
// Result properties
/// The specific push rule. This will also include keys specific to the
- /// rule itself such as the rule's ``actions`` and ``conditions`` if set.
+ /// rule itself such as the rule's `actions` and `conditions` if set.
PushRule pushRule() const { return fromJson<PushRule>(jsonData()); }
};
@@ -84,7 +84,7 @@ public:
/*! \brief Delete a push rule.
*
* \param scope
- * ``global`` to specify global rules.
+ * `global` to specify global rules.
*
* \param kind
* The kind of rule
@@ -117,7 +117,7 @@ public:
/*! \brief Add or change a push rule.
*
* \param scope
- * ``global`` to specify global rules.
+ * `global` to specify global rules.
*
* \param kind
* The kind of rule
@@ -129,7 +129,7 @@ public:
* The action(s) to perform when the conditions for this rule are met.
*
* \param before
- * Use 'before' with a ``rule_id`` as its value to make the new rule the
+ * Use 'before' with a `rule_id` as its value to make the new rule the
* next-most important rule with respect to the given user defined rule.
* It is not possible to add a rule relative to a predefined server rule.
*
@@ -141,10 +141,10 @@ public:
* \param conditions
* The conditions that must hold true for an event in order for a
* rule to be applied to an event. A rule with no conditions
- * always matches. Only applicable to ``underride`` and ``override`` rules.
+ * always matches. Only applicable to `underride` and `override` rules.
*
* \param pattern
- * Only applicable to ``content`` rules. The glob-style pattern to match
+ * Only applicable to `content` rules. The glob-style pattern to match
* against.
*/
explicit SetPushRuleJob(const QString& scope, const QString& kind,
@@ -165,8 +165,8 @@ public:
/*! \brief Get whether a push rule is enabled
*
* \param scope
- * Either ``global`` or ``device/<profile_tag>`` to specify global
- * rules or device rules for the given ``profile_tag``.
+ * Either `global` or `device/<profile_tag>` to specify global
+ * rules or device rules for the given `profile_tag`.
*
* \param kind
* The kind of rule
@@ -200,7 +200,7 @@ public:
/*! \brief Enable or disable a push rule.
*
* \param scope
- * ``global`` to specify global rules.
+ * `global` to specify global rules.
*
* \param kind
* The kind of rule
@@ -224,8 +224,8 @@ public:
/*! \brief The actions for a push rule
*
* \param scope
- * Either ``global`` or ``device/<profile_tag>`` to specify global
- * rules or device rules for the given ``profile_tag``.
+ * Either `global` or `device/<profile_tag>` to specify global
+ * rules or device rules for the given `profile_tag`.
*
* \param kind
* The kind of rule
@@ -263,7 +263,7 @@ public:
/*! \brief Set the actions for a push rule.
*
* \param scope
- * ``global`` to specify global rules.
+ * `global` to specify global rules.
*
* \param kind
* The kind of rule
diff --git a/lib/csapi/read_markers.h b/lib/csapi/read_markers.h
index 0e122c63..00a2aa0d 100644
--- a/lib/csapi/read_markers.h
+++ b/lib/csapi/read_markers.h
@@ -26,7 +26,7 @@ public:
*
* \param mRead
* The event ID to set the read receipt location at. This is
- * equivalent to calling ``/receipt/m.read/$elsewhere:example.org``
+ * equivalent to calling `/receipt/m.read/$elsewhere:example.org`
* and is provided here to save that extra call.
*/
explicit SetReadMarkerJob(const QString& roomId, const QString& mFullyRead,
diff --git a/lib/csapi/receipts.h b/lib/csapi/receipts.h
index 1fac0acf..7ac093cd 100644
--- a/lib/csapi/receipts.h
+++ b/lib/csapi/receipts.h
@@ -27,8 +27,8 @@ public:
* The event ID to acknowledge up to.
*
* \param receipt
- * Extra receipt information to attach to ``content`` if any. The
- * server will automatically set the ``ts`` field.
+ * Extra receipt information to attach to `content` if any. The
+ * server will automatically set the `ts` field.
*/
explicit PostReceiptJob(const QString& roomId, const QString& receiptType,
const QString& eventId,
diff --git a/lib/csapi/redaction.h b/lib/csapi/redaction.h
index 541e433a..f0db9f9f 100644
--- a/lib/csapi/redaction.h
+++ b/lib/csapi/redaction.h
@@ -15,9 +15,12 @@ namespace Quotient {
*
* This cannot be undone.
*
- * Users may redact their own events, and any user with a power level
- * greater than or equal to the ``redact`` power level of the room may
- * redact events there.
+ * Any user with a power level greater than or equal to the `m.room.redaction`
+ * event power level may send redaction events in the room. If the user's power
+ * level greater is also greater than or equal to the `redact` power level
+ * of the room, the user may redact events sent by other users.
+ *
+ * Server administrators may redact events sent by users on their server.
*/
class RedactEventJob : public BaseJob {
public:
diff --git a/lib/csapi/registration.h b/lib/csapi/registration.h
index 62bc35f1..ae8fc162 100644
--- a/lib/csapi/registration.h
+++ b/lib/csapi/registration.h
@@ -15,8 +15,9 @@ namespace Quotient {
/*! \brief Register for an account on this homeserver.
*
- * This API endpoint uses the `User-Interactive Authentication API`_, except in
- * the cases where a guest account is being registered.
+ * This API endpoint uses the [User-Interactive Authentication
+ * API](/client-server-api/#user-interactive-authentication-api), except in the
+ * cases where a guest account is being registered.
*
* Register for an account on this homeserver.
*
@@ -31,45 +32,46 @@ namespace Quotient {
* If registration is successful, this endpoint will issue an access token
* the client can use to authorize itself in subsequent requests.
*
- * If the client does not supply a ``device_id``, the server must
+ * If the client does not supply a `device_id`, the server must
* auto-generate one.
*
* The server SHOULD register an account with a User ID based on the
- * ``username`` provided, if any. Note that the grammar of Matrix User ID
+ * `username` provided, if any. Note that the grammar of Matrix User ID
* localparts is restricted, so the server MUST either map the provided
- * ``username`` onto a ``user_id`` in a logical manner, or reject
- * ``username``\s which do not comply to the grammar, with
- * ``M_INVALID_USERNAME``.
+ * `username` onto a `user_id` in a logical manner, or reject
+ * `username`\s which do not comply to the grammar, with
+ * `M_INVALID_USERNAME`.
*
* Matrix clients MUST NOT assume that localpart of the registered
- * ``user_id`` matches the provided ``username``.
+ * `user_id` matches the provided `username`.
*
- * The returned access token must be associated with the ``device_id``
+ * The returned access token must be associated with the `device_id`
* supplied by the client or generated by the server. The server may
* invalidate any access token previously associated with that device. See
- * `Relationship between access tokens and devices`_.
+ * [Relationship between access tokens and
+ * devices](/client-server-api/#relationship-between-access-tokens-and-devices).
*
* When registering a guest account, all parameters in the request body
- * with the exception of ``initial_device_display_name`` MUST BE ignored
- * by the server. The server MUST pick a ``device_id`` for the account
+ * with the exception of `initial_device_display_name` MUST BE ignored
+ * by the server. The server MUST pick a `device_id` for the account
* regardless of input.
*
* Any user ID returned by this API must conform to the grammar given in the
- * `Matrix specification <../appendices.html#user-identifiers>`_.
+ * [Matrix specification](/appendices/#user-identifiers).
*/
class RegisterJob : public BaseJob {
public:
/*! \brief Register for an account on this homeserver.
*
* \param kind
- * The kind of account to register. Defaults to ``user``.
+ * The kind of account to register. Defaults to `user`.
*
* \param auth
* Additional authentication information for the
* user-interactive authentication API. Note that this
* information is *not* used to define how the registered user
* should be authenticated, but is instead used to
- * authenticate the ``register`` call itself.
+ * authenticate the `register` call itself.
*
* \param username
* The basis for the localpart of the desired Matrix ID. If omitted,
@@ -85,10 +87,10 @@ public:
*
* \param initialDeviceDisplayName
* A display name to assign to the newly-created device. Ignored
- * if ``device_id`` corresponds to a known device.
+ * if `device_id` corresponds to a known device.
*
* \param inhibitLogin
- * If true, an ``access_token`` and ``device_id`` should not be
+ * If true, an `access_token` and `device_id` should not be
* returned from this call, therefore preventing an automatic
* login. Defaults to false.
*/
@@ -105,12 +107,12 @@ public:
/// The fully-qualified Matrix user ID (MXID) that has been registered.
///
/// Any user ID returned by this API must conform to the grammar given in
- /// the `Matrix specification <../appendices.html#user-identifiers>`_.
+ /// the [Matrix specification](/appendices/#user-identifiers).
QString userId() const { return loadFromJson<QString>("user_id"_ls); }
/// An access token for the account.
/// This access token can then be used to authorize other requests.
- /// Required if the ``inhibit_login`` option is false.
+ /// Required if the `inhibit_login` option is false.
QString accessToken() const
{
return loadFromJson<QString>("access_token"_ls);
@@ -120,8 +122,8 @@ public:
/// been registered.
///
/// **Deprecated**. Clients should extract the server_name from
- /// ``user_id`` (by splitting at the first colon) if they require
- /// it. Note also that ``homeserver`` is not spelt this way.
+ /// `user_id` (by splitting at the first colon) if they require
+ /// it. Note also that `homeserver` is not spelt this way.
QString homeServer() const
{
return loadFromJson<QString>("home_server"_ls);
@@ -129,7 +131,7 @@ public:
/// ID of the registered device. Will be the same as the
/// corresponding parameter in the request, if one was specified.
- /// Required if the ``inhibit_login`` option is false.
+ /// Required if the `inhibit_login` option is false.
QString deviceId() const { return loadFromJson<QString>("device_id"_ls); }
};
@@ -201,9 +203,9 @@ public:
*
* Changes the password for an account on this homeserver.
*
- * This API endpoint uses the `User-Interactive Authentication API`_ to
- * ensure the user changing the password is actually the owner of the
- * account.
+ * This API endpoint uses the [User-Interactive Authentication
+ * API](/client-server-api/#user-interactive-authentication-api) to ensure the
+ * user changing the password is actually the owner of the account.
*
* An access token should be submitted to this endpoint if the client has
* an active session.
@@ -222,10 +224,10 @@ public:
*
* \param logoutDevices
* Whether the user's other access tokens, and their associated devices,
- * should be revoked if the request succeeds. Defaults to true.
+ * should be revoked if the request succeeds.
*
- * When ``false``, the server can still take advantage of `the soft logout
- * method <#soft-logout>`_ for the user's remaining devices.
+ * When `false`, the server can still take advantage of the [soft logout
+ * method](/client-server-api/#soft-logout) for the user's remaining devices.
*
* \param auth
* Additional authentication information for the user-interactive
@@ -242,23 +244,18 @@ public:
* The homeserver must check that the given email address **is
* associated** with an account on this homeserver. This API should be
* used to request validation tokens when authenticating for the
- * ``/account/password`` endpoint.
+ * `/account/password` endpoint.
*
* This API's parameters and response are identical to that of the
- * |/register/email/requestToken|_ endpoint, except that
- * ``M_THREEPID_NOT_FOUND`` may be returned if no account matching the
+ * [`/register/email/requestToken`](/client-server-api/#post_matrixclientr0registeremailrequesttoken)
+ * endpoint, except that
+ * `M_THREEPID_NOT_FOUND` may be returned if no account matching the
* given email address could be found. The server may instead send an
* email to the given address prompting the user to create an account.
- * ``M_THREEPID_IN_USE`` may not be returned.
+ * `M_THREEPID_IN_USE` may not be returned.
*
* The homeserver should validate the email itself, either by sending a
* validation email itself or by using a service it has control over.
- *
- *
- * .. |/register/email/requestToken| replace:: ``/register/email/requestToken``
- *
- * .. _/register/email/requestToken:
- * #post-matrix-client-r0-register-email-requesttoken
*/
class RequestTokenToResetPasswordEmailJob : public BaseJob {
public:
@@ -269,24 +266,18 @@ public:
* The homeserver must check that the given email address **is
* associated** with an account on this homeserver. This API should be
* used to request validation tokens when authenticating for the
- * ``/account/password`` endpoint.
+ * `/account/password` endpoint.
*
* This API's parameters and response are identical to that of the
- * |/register/email/requestToken|_ endpoint, except that
- * ``M_THREEPID_NOT_FOUND`` may be returned if no account matching the
+ * [`/register/email/requestToken`](/client-server-api/#post_matrixclientr0registeremailrequesttoken)
+ * endpoint, except that
+ * `M_THREEPID_NOT_FOUND` may be returned if no account matching the
* given email address could be found. The server may instead send an
* email to the given address prompting the user to create an account.
- * ``M_THREEPID_IN_USE`` may not be returned.
+ * `M_THREEPID_IN_USE` may not be returned.
*
* The homeserver should validate the email itself, either by sending a
* validation email itself or by using a service it has control over.
- *
- *
- * .. |/register/email/requestToken| replace::
- * ``/register/email/requestToken``
- *
- * .. _/register/email/requestToken:
- * #post-matrix-client-r0-register-email-requesttoken
*/
explicit RequestTokenToResetPasswordEmailJob(const EmailValidationData& body);
@@ -305,22 +296,18 @@ public:
* The homeserver must check that the given phone number **is
* associated** with an account on this homeserver. This API should be
* used to request validation tokens when authenticating for the
- * ``/account/password`` endpoint.
+ * `/account/password` endpoint.
*
* This API's parameters and response are identical to that of the
- * |/register/msisdn/requestToken|_ endpoint, except that
- * ``M_THREEPID_NOT_FOUND`` may be returned if no account matching the
+ * [`/register/msisdn/requestToken`](/client-server-api/#post_matrixclientr0registermsisdnrequesttoken)
+ * endpoint, except that
+ * `M_THREEPID_NOT_FOUND` may be returned if no account matching the
* given phone number could be found. The server may instead send the SMS
* to the given phone number prompting the user to create an account.
- * ``M_THREEPID_IN_USE`` may not be returned.
+ * `M_THREEPID_IN_USE` may not be returned.
*
* The homeserver should validate the phone number itself, either by sending a
* validation message itself or by using a service it has control over.
- *
- * .. |/register/msisdn/requestToken| replace:: ``/register/msisdn/requestToken``
- *
- * .. _/register/msisdn/requestToken:
- * #post-matrix-client-r0-register-email-requesttoken
*/
class RequestTokenToResetPasswordMSISDNJob : public BaseJob {
public:
@@ -331,23 +318,18 @@ public:
* The homeserver must check that the given phone number **is
* associated** with an account on this homeserver. This API should be
* used to request validation tokens when authenticating for the
- * ``/account/password`` endpoint.
+ * `/account/password` endpoint.
*
* This API's parameters and response are identical to that of the
- * |/register/msisdn/requestToken|_ endpoint, except that
- * ``M_THREEPID_NOT_FOUND`` may be returned if no account matching the
+ * [`/register/msisdn/requestToken`](/client-server-api/#post_matrixclientr0registermsisdnrequesttoken)
+ * endpoint, except that
+ * `M_THREEPID_NOT_FOUND` may be returned if no account matching the
* given phone number could be found. The server may instead send the SMS
* to the given phone number prompting the user to create an account.
- * ``M_THREEPID_IN_USE`` may not be returned.
+ * `M_THREEPID_IN_USE` may not be returned.
*
* The homeserver should validate the phone number itself, either by sending
* a validation message itself or by using a service it has control over.
- *
- * .. |/register/msisdn/requestToken| replace::
- * ``/register/msisdn/requestToken``
- *
- * .. _/register/msisdn/requestToken:
- * #post-matrix-client-r0-register-email-requesttoken
*/
explicit RequestTokenToResetPasswordMSISDNJob(
const MsisdnValidationData& body);
@@ -366,7 +348,8 @@ public:
* Deactivate the user's account, removing all ability for the user to
* login again.
*
- * This API endpoint uses the `User-Interactive Authentication API`_.
+ * This API endpoint uses the [User-Interactive Authentication
+ * API](/client-server-api/#user-interactive-authentication-api).
*
* An access token should be submitted to this endpoint if the client has
* an active session.
@@ -374,7 +357,7 @@ public:
* The homeserver may change the flows available depending on whether a
* valid access token is provided.
*
- * Unlike other endpoints, this endpoint does not take an ``id_access_token``
+ * Unlike other endpoints, this endpoint does not take an `id_access_token`
* parameter because the homeserver is expected to sign the request to the
* identity server instead.
*/
@@ -388,11 +371,11 @@ public:
*
* \param idServer
* The identity server to unbind all of the user's 3PIDs from.
- * If not provided, the homeserver MUST use the ``id_server``
+ * If not provided, the homeserver MUST use the `id_server`
* that was originally use to bind each identifier. If the
- * homeserver does not know which ``id_server`` that was,
- * it must return an ``id_server_unbind_result`` of
- * ``no-support``.
+ * homeserver does not know which `id_server` that was,
+ * it must return an `id_server_unbind_result` of
+ * `no-support`.
*/
explicit DeactivateAccountJob(const Omittable<AuthenticationData>& auth = none,
const QString& idServer = {});
@@ -400,12 +383,12 @@ public:
// Result properties
/// An indicator as to whether or not the homeserver was able to unbind
- /// the user's 3PIDs from the identity server(s). ``success`` indicates
+ /// the user's 3PIDs from the identity server(s). `success` indicates
/// that all identifiers have been unbound from the identity server while
- /// ``no-support`` indicates that one or more identifiers failed to unbind
+ /// `no-support` indicates that one or more identifiers failed to unbind
/// due to the identity server refusing the request or the homeserver
/// being unable to determine an identity server to unbind from. This
- /// must be ``success`` if the homeserver has no identifiers to unbind
+ /// must be `success` if the homeserver has no identifiers to unbind
/// for the user.
QString idServerUnbindResult() const
{
@@ -447,7 +430,7 @@ public:
// Result properties
/// A flag to indicate that the username is available. This should always
- /// be ``true`` when the server replies with 200 OK.
+ /// be `true` when the server replies with 200 OK.
Omittable<bool> available() const
{
return loadFromJson<Omittable<bool>>("available"_ls);
diff --git a/lib/csapi/room_send.h b/lib/csapi/room_send.h
index 39460aca..96f5beca 100644
--- a/lib/csapi/room_send.h
+++ b/lib/csapi/room_send.h
@@ -16,7 +16,7 @@ namespace Quotient {
*
* The body of the request should be the content object of the event; the
* fields in this object will vary depending on the type of event. See
- * `Room Events`_ for the m. event specification.
+ * [Room Events](/client-server-api/#room-events) for the m. event specification.
*/
class SendMessageJob : public BaseJob {
public:
@@ -40,7 +40,8 @@ public:
*
* The body of the request should be the content object of the event; the
* fields in this object will vary depending on the type of event. See
- * `Room Events`_ for the m. event specification.
+ * [Room Events](/client-server-api/#room-events) for the m. event
+ * specification.
*/
explicit SendMessageJob(const QString& roomId, const QString& eventType,
const QString& txnId, const QJsonObject& body = {});
diff --git a/lib/csapi/room_state.h b/lib/csapi/room_state.h
index 447605ff..f95af223 100644
--- a/lib/csapi/room_state.h
+++ b/lib/csapi/room_state.h
@@ -10,22 +10,20 @@ namespace Quotient {
/*! \brief Send a state event to the given room.
*
- * .. For backwards compatibility with older links...
- * .. _`put-matrix-client-r0-rooms-roomid-state-eventtype`:
- *
* State events can be sent using this endpoint. These events will be
- * overwritten if ``<room id>``, ``<event type>`` and ``<state key>`` all
+ * overwritten if `<room id>`, `<event type>` and `<state key>` all
* match.
*
* Requests to this endpoint **cannot use transaction IDs**
- * like other ``PUT`` paths because they cannot be differentiated from the
- * ``state_key``. Furthermore, ``POST`` is unsupported on state paths.
+ * like other `PUT` paths because they cannot be differentiated from the
+ * `state_key`. Furthermore, `POST` is unsupported on state paths.
*
* The body of the request should be the content object of the event; the
* fields in this object will vary depending on the type of event. See
- * `Room Events`_ for the ``m.`` event specification.
+ * [Room Events](/client-server-api/#room-events) for the `m.` event
+ * specification.
*
- * If the event type being sent is ``m.room.canonical_alias`` servers
+ * If the event type being sent is `m.room.canonical_alias` servers
* SHOULD ensure that any new aliases being listed in the event are valid
* per their grammar/syntax and that they point to the room ID where the
* state event is to be sent. Servers do not validate aliases which are
@@ -46,22 +44,20 @@ public:
* an empty string, the trailing slash on this endpoint is optional.
*
* \param body
- * .. For backwards compatibility with older links...
- * .. _`put-matrix-client-r0-rooms-roomid-state-eventtype`:
- *
* State events can be sent using this endpoint. These events will be
- * overwritten if ``<room id>``, ``<event type>`` and ``<state key>`` all
+ * overwritten if `<room id>`, `<event type>` and `<state key>` all
* match.
*
* Requests to this endpoint **cannot use transaction IDs**
- * like other ``PUT`` paths because they cannot be differentiated from the
- * ``state_key``. Furthermore, ``POST`` is unsupported on state paths.
+ * like other `PUT` paths because they cannot be differentiated from the
+ * `state_key`. Furthermore, `POST` is unsupported on state paths.
*
* The body of the request should be the content object of the event; the
* fields in this object will vary depending on the type of event. See
- * `Room Events`_ for the ``m.`` event specification.
+ * [Room Events](/client-server-api/#room-events) for the `m.` event
+ * specification.
*
- * If the event type being sent is ``m.room.canonical_alias`` servers
+ * If the event type being sent is `m.room.canonical_alias` servers
* SHOULD ensure that any new aliases being listed in the event are valid
* per their grammar/syntax and that they point to the room ID where the
* state event is to be sent. Servers do not validate aliases which are
diff --git a/lib/csapi/rooms.h b/lib/csapi/rooms.h
index f0bfa349..51af2c65 100644
--- a/lib/csapi/rooms.h
+++ b/lib/csapi/rooms.h
@@ -12,7 +12,7 @@ namespace Quotient {
/*! \brief Get a single event by event ID.
*
- * Get a single event based on ``roomId/eventId``. You must have permission to
+ * Get a single event based on `roomId/eventId`. You must have permission to
* retrieve this event e.g. by being a member in the room for this event.
*/
class GetOneRoomEventJob : public BaseJob {
@@ -43,9 +43,6 @@ public:
/*! \brief Get the state identified by the type and key.
*
- * .. For backwards compatibility with older links...
- * .. _`get-matrix-client-r0-rooms-roomid-state-eventtype`:
- *
* Looks up the contents of a state event in a room. If the user is
* joined to the room then the state is taken from the current
* state of the room. If the user has left the room then the state is
@@ -118,16 +115,15 @@ public:
*
* \param at
* The point in time (pagination token) to return members for in the room.
- * This token can be obtained from a ``prev_batch`` token returned for
+ * This token can be obtained from a `prev_batch` token returned for
* each room by the sync API. Defaults to the current state of the room,
* as determined by the server.
*
* \param membership
* The kind of membership to filter for. Defaults to no filtering if
- * unspecified. When specified alongside ``not_membership``, the two
+ * unspecified. When specified alongside `not_membership`, the two
* parameters create an 'or' condition: either the membership *is*
- * the same as ``membership`` **or** *is not* the same as
- * ``not_membership``.
+ * the same as `membership` **or** *is not* the same as `not_membership`.
*
* \param notMembership
* The kind of membership to exclude from the results. Defaults to no
@@ -162,7 +158,7 @@ public:
* room. The current user must be in the room for it to work, unless it is an
* Application Service in which case any of the AS's users must be in the room.
* This API is primarily for Application Services and should be faster to
- * respond than ``/members`` as it can be implemented more efficiently on the
+ * respond than `/members` as it can be implemented more efficiently on the
* server.
*/
class GetJoinedMembersByRoomJob : public BaseJob {
@@ -173,7 +169,7 @@ public:
/// the room. The current user must be in the room for it to work, unless it
/// is an Application Service in which case any of the AS's users must be in
/// the room. This API is primarily for Application Services and should be
- /// faster to respond than ``/members`` as it can be implemented more
+ /// faster to respond than `/members` as it can be implemented more
/// efficiently on the server.
struct RoomMember {
/// The display name of the user this object is representing.
diff --git a/lib/csapi/sso_login_redirect.h b/lib/csapi/sso_login_redirect.h
index d6330e38..6205ca59 100644
--- a/lib/csapi/sso_login_redirect.h
+++ b/lib/csapi/sso_login_redirect.h
@@ -13,7 +13,9 @@ namespace Quotient {
* A web-based Matrix client should instruct the user's browser to
* navigate to this endpoint in order to log in via SSO.
*
- * The server MUST respond with an HTTP redirect to the SSO interface.
+ * The server MUST respond with an HTTP redirect to the SSO interface,
+ * or present a page which lets the user select an IdP to continue
+ * with in the event multiple are supported by the server.
*/
class RedirectToSSOJob : public BaseJob {
public:
diff --git a/lib/csapi/tags.h b/lib/csapi/tags.h
index a815d9b3..a854531a 100644
--- a/lib/csapi/tags.h
+++ b/lib/csapi/tags.h
@@ -18,7 +18,7 @@ public:
/// List the tags set by a user on a room.
struct Tag {
- /// A number in a range ``[0,1]`` describing a relative
+ /// A number in a range `[0,1]` describing a relative
/// position of the room under the given tag.
Omittable<float> order;
/// List the tags set by a user on a room.
@@ -83,7 +83,7 @@ public:
* The tag to add.
*
* \param order
- * A number in a range ``[0,1]`` describing a relative
+ * A number in a range `[0,1]` describing a relative
* position of the room under the given tag.
*
* \param additionalProperties
diff --git a/lib/csapi/third_party_membership.h b/lib/csapi/third_party_membership.h
index 55cab370..a424678f 100644
--- a/lib/csapi/third_party_membership.h
+++ b/lib/csapi/third_party_membership.h
@@ -10,14 +10,13 @@ namespace Quotient {
/*! \brief Invite a user to participate in a particular room.
*
- * .. _invite-by-third-party-id-endpoint:
- *
* *Note that there are two forms of this API, which are documented separately.
* This version of the API does not require that the inviter know the Matrix
* identifier of the invitee, and instead relies on third party identifiers.
* The homeserver uses an identity server to perform the mapping from
* third party identifier to a Matrix identifier. The other is documented in
- * the* `joining rooms section`_.
+ * the* [joining rooms
+ * section](/client-server-api/#post_matrixclientr0roomsroomidinvite).
*
* This API invites a user to participate in a particular room.
* They do not start participating in the room until they actually join the
@@ -27,7 +26,7 @@ namespace Quotient {
* join that room.
*
* If the identity server did know the Matrix user identifier for the
- * third party identifier, the homeserver will append a ``m.room.member``
+ * third party identifier, the homeserver will append a `m.room.member`
* event to the room.
*
* If the identity server does not know a Matrix user identifier for the
@@ -35,7 +34,7 @@ namespace Quotient {
* which can be accepted upon providing proof of ownership of the third
* party identifier. This is achieved by the identity server generating a
* token, which it gives to the inviting homeserver. The homeserver will
- * add an ``m.room.third_party_invite`` event into the graph for the room,
+ * add an `m.room.third_party_invite` event into the graph for the room,
* containing that token.
*
* When the invitee binds the invited third party identifier to a Matrix
@@ -51,9 +50,7 @@ namespace Quotient {
* - The matrix user ID who invited them to the room
*
* If a token is requested from the identity server, the homeserver will
- * append a ``m.room.third_party_invite`` event to the room.
- *
- * .. _joining rooms section: `invite-by-user-id-endpoint`_
+ * append a `m.room.third_party_invite` event to the room.
*/
class InviteBy3PIDJob : public BaseJob {
public:
@@ -73,7 +70,7 @@ public:
*
* \param medium
* The kind of address being passed in the address field, for example
- * ``email``.
+ * `email`.
*
* \param address
* The invitee's third party identifier.
diff --git a/lib/csapi/typing.h b/lib/csapi/typing.h
index 2c953949..64a310d0 100644
--- a/lib/csapi/typing.h
+++ b/lib/csapi/typing.h
@@ -11,8 +11,8 @@ namespace Quotient {
/*! \brief Informs the server that the user has started or stopped typing.
*
* This tells the server that the user is typing for the next N
- * milliseconds where N is the value specified in the ``timeout`` key.
- * Alternatively, if ``typing`` is ``false``, it tells the server that the
+ * milliseconds where N is the value specified in the `timeout` key.
+ * Alternatively, if `typing` is `false`, it tells the server that the
* user has stopped typing.
*/
class SetTypingJob : public BaseJob {
@@ -26,7 +26,7 @@ public:
* The room in which the user is typing.
*
* \param typing
- * Whether the user is typing or not. If ``false``, the ``timeout``
+ * Whether the user is typing or not. If `false`, the `timeout`
* key can be omitted.
*
* \param timeout
diff --git a/lib/csapi/users.h b/lib/csapi/users.h
index 6fc26f57..eab18f6c 100644
--- a/lib/csapi/users.h
+++ b/lib/csapi/users.h
@@ -19,7 +19,7 @@ namespace Quotient {
*
* The search is performed case-insensitively on user IDs and display
* names preferably using a collation determined based upon the
- * ``Accept-Language`` header provided in the request, if present.
+ * `Accept-Language` header provided in the request, if present.
*/
class SearchUserDirectoryJob : public BaseJob {
public:
@@ -34,7 +34,7 @@ public:
///
/// The search is performed case-insensitively on user IDs and display
/// names preferably using a collation determined based upon the
- /// ``Accept-Language`` header provided in the request, if present.
+ /// `Accept-Language` header provided in the request, if present.
struct User {
/// The user's matrix user ID.
QString userId;
diff --git a/lib/csapi/versions.h b/lib/csapi/versions.h
index 828a7eb9..896e2ea9 100644
--- a/lib/csapi/versions.h
+++ b/lib/csapi/versions.h
@@ -12,14 +12,14 @@ namespace Quotient {
*
* Gets the versions of the specification supported by the server.
*
- * Values will take the form ``rX.Y.Z``.
+ * Values will take the form `rX.Y.Z`.
*
- * Only the latest ``Z`` value will be reported for each supported ``X.Y``
- * value. i.e. if the server implements ``r0.0.0``, ``r0.0.1``, and ``r1.2.0``,
- * it will report ``r0.0.1`` and ``r1.2.0``.
+ * Only the latest `Z` value will be reported for each supported `X.Y` value.
+ * i.e. if the server implements `r0.0.0`, `r0.0.1`, and `r1.2.0`, it will
+ * report `r0.0.1` and `r1.2.0`.
*
* The server may additionally advertise experimental features it supports
- * through ``unstable_features``. These features should be namespaced and
+ * through `unstable_features`. These features should be namespaced and
* may optionally include version information within their name if desired.
* Features listed here are not for optionally toggling parts of the Matrix
* specification and should only be used to advertise support for a feature
diff --git a/lib/csapi/wellknown.h b/lib/csapi/wellknown.h
index b21d9fc7..c707d232 100644
--- a/lib/csapi/wellknown.h
+++ b/lib/csapi/wellknown.h
@@ -14,7 +14,7 @@ namespace Quotient {
*
* Gets discovery information about the domain. The file may include
* additional keys, which MUST follow the Java package naming convention,
- * e.g. ``com.example.myapp.property``. This ensures property names are
+ * e.g. `com.example.myapp.property`. This ensures property names are
* suitably namespaced for each application and reduces the risk of
* clashes.
*
diff --git a/lib/csapi/whoami.h b/lib/csapi/whoami.h
index af8f1e8a..184459ea 100644
--- a/lib/csapi/whoami.h
+++ b/lib/csapi/whoami.h
@@ -14,8 +14,8 @@ namespace Quotient {
*
* Note that, as with the rest of the Client-Server API,
* Application Services may masquerade as users within their
- * namespace by giving a ``user_id`` query parameter. In this
- * situation, the server should verify that the given ``user_id``
+ * namespace by giving a `user_id` query parameter. In this
+ * situation, the server should verify that the given `user_id`
* is registered by the appservice, and return it in the response
* body.
*/
@@ -33,7 +33,7 @@ public:
// Result properties
- /// The user id that owns the access token.
+ /// The user ID that owns the access token.
QString userId() const { return loadFromJson<QString>("user_id"_ls); }
};
diff --git a/lib/identity/definitions/request_email_validation.h b/lib/identity/definitions/request_email_validation.h
index 079da953..87549505 100644
--- a/lib/identity/definitions/request_email_validation.h
+++ b/lib/identity/definitions/request_email_validation.h
@@ -11,16 +11,16 @@ namespace Quotient {
struct RequestEmailValidation {
/// A unique string generated by the client, and used to identify the
/// validation attempt. It must be a string consisting of the characters
- /// ``[0-9a-zA-Z.=_-]``. Its length must not exceed 255 characters and it
+ /// `[0-9a-zA-Z.=_-]`. Its length must not exceed 255 characters and it
/// must not be empty.
QString clientSecret;
/// The email address to validate.
QString email;
- /// The server will only send an email if the ``send_attempt``
+ /// The server will only send an email if the `send_attempt`
/// is a number greater than the most recent one which it has seen,
- /// scoped to that ``email`` + ``client_secret`` pair. This is to
+ /// scoped to that `email` + `client_secret` pair. This is to
/// avoid repeatedly sending the same email in the case of request
/// retries between the POSTing user and the identity server.
/// The client should increment this value if they desire a new
diff --git a/lib/identity/definitions/request_msisdn_validation.h b/lib/identity/definitions/request_msisdn_validation.h
index a29fd0de..d2ea463f 100644
--- a/lib/identity/definitions/request_msisdn_validation.h
+++ b/lib/identity/definitions/request_msisdn_validation.h
@@ -11,20 +11,20 @@ namespace Quotient {
struct RequestMsisdnValidation {
/// A unique string generated by the client, and used to identify the
/// validation attempt. It must be a string consisting of the characters
- /// ``[0-9a-zA-Z.=_-]``. Its length must not exceed 255 characters and it
+ /// `[0-9a-zA-Z.=_-]`. Its length must not exceed 255 characters and it
/// must not be empty.
QString clientSecret;
/// The two-letter uppercase ISO-3166-1 alpha-2 country code that the
- /// number in ``phone_number`` should be parsed as if it were dialled from.
+ /// number in `phone_number` should be parsed as if it were dialled from.
QString country;
/// The phone number to validate.
QString phoneNumber;
- /// The server will only send an SMS if the ``send_attempt`` is a
+ /// The server will only send an SMS if the `send_attempt` is a
/// number greater than the most recent one which it has seen,
- /// scoped to that ``country`` + ``phone_number`` + ``client_secret``
+ /// scoped to that `country` + `phone_number` + `client_secret`
/// triple. This is to avoid repeatedly sending the same SMS in
/// the case of request retries between the POSTing user and the
/// identity server. The client should increment this value if
diff --git a/lib/room.cpp b/lib/room.cpp
index a5940eb2..7631abe1 100644
--- a/lib/room.cpp
+++ b/lib/room.cpp
@@ -31,6 +31,7 @@
#include "csapi/kicking.h"
#include "csapi/leaving.h"
#include "csapi/receipts.h"
+#include "csapi/read_markers.h"
#include "csapi/redaction.h"
#include "csapi/room_send.h"
#include "csapi/room_state.h"
@@ -55,7 +56,6 @@
#include "events/roompowerlevelsevent.h"
#include "jobs/downloadfilejob.h"
#include "jobs/mediathumbnailjob.h"
-#include "jobs/postreadmarkersjob.h"
#include "events/roomcanonicalaliasevent.h"
#include <QtCore/QDir>
@@ -107,8 +107,8 @@ public:
UnorderedMap<StateEventKey, StateEventPtr> baseState;
/// State event stubs - events without content, just type and state key
static decltype(baseState) stubbedState;
- /// The state of the room at timeline position after-maxTimelineIndex()
- /// \sa Room::syncEdge
+ /// The state of the room at syncEdge()
+ /// \sa syncEdge
QHash<StateEventKey, const StateEventBase*> currentState;
/// Servers with aliases for this room except the one of the local user
/// \sa Room::remoteAliases
@@ -135,7 +135,7 @@ public:
QString firstDisplayedEventId;
QString lastDisplayedEventId;
QHash<const User*, QString> lastReadEventIds;
- QString serverReadMarker;
+ QString fullyReadUntilEventId;
TagsMap tags;
UnorderedMap<QString, EventPtr> accountData;
QString prevBatch;
@@ -204,6 +204,8 @@ public:
/// A point in the timeline corresponding to baseState
rev_iter_t timelineBase() const { return q->findInTimeline(-1); }
+ rev_iter_t historyEdge() const { return timeline.crend(); }
+ Timeline::const_iterator syncEdge() const { return timeline.cend(); }
void getPreviousContent(int limit = 10);
@@ -286,8 +288,8 @@ public:
}
if (events.size() > 9 || et.nsecsElapsed() >= profilerMinNsecs())
qCDebug(PROFILER)
- << "*** Room::Private::updateStateFrom():" << events.size()
- << "event(s)," << et;
+ << "Updated" << q->objectName() << "room state from"
+ << events.size() << "event(s) in" << et;
}
return changes;
}
@@ -311,11 +313,12 @@ public:
*/
void dropDuplicateEvents(RoomEvents& events) const;
- Changes setLastReadEvent(User* u, QString eventId);
- void updateUnreadCount(const rev_iter_t& from, const rev_iter_t& to);
- Changes promoteReadMarker(User* u, const rev_iter_t& newMarker, bool force = false);
-
- Changes markMessagesAsRead(rev_iter_t upToMarker);
+ void setLastReadReceipt(User* u, rev_iter_t newMarker,
+ QString newEvtId = {});
+ Changes setFullyReadMarker(const QString &eventId);
+ Changes updateUnreadCount(const rev_iter_t& from, const rev_iter_t& to);
+ Changes recalculateUnreadCount(bool force = false);
+ void markMessagesAsRead(const rev_iter_t &upToMarker);
void getAllMembers();
@@ -618,8 +621,9 @@ User* Room::user(const QString& userId) const
JoinState Room::memberJoinState(User* user) const
{
- return d->membersMap.contains(user->name(this), user) ? JoinState::Join
- : JoinState::Leave;
+ return user != nullptr && d->membersMap.contains(user->name(this), user)
+ ? JoinState::Join
+ : JoinState::Leave;
}
JoinState Room::joinState() const { return d->joinState; }
@@ -636,139 +640,178 @@ void Room::setJoinState(JoinState state)
emit joinStateChanged(oldState, state);
}
-Room::Changes Room::Private::setLastReadEvent(User* u, QString eventId)
+void Room::Private::setLastReadReceipt(User* u, rev_iter_t newMarker,
+ QString newEvtId)
{
+ if (!u) {
+ Q_ASSERT(u != nullptr); // For Debug builds
+ qCCritical(MAIN) << "Empty user, skipping read receipt registration";
+ return; // For Release builds
+ }
+ if (q->memberJoinState(u) != JoinState::Join) {
+ qCWarning(EPHEMERAL)
+ << "Won't record read receipt for non-member" << u->id();
+ return;
+ }
+
+ if (newMarker == historyEdge() && !newEvtId.isEmpty())
+ newMarker = q->findInTimeline(newEvtId);
+ if (newMarker != historyEdge()) {
+ // NB: with reverse iterators, timeline history >= sync edge
+ if (newMarker >= q->readMarker(u)) {
+ qCDebug(EPHEMERAL) << "The new read receipt for" << u->id()
+ << "is at or behind the old one, skipping";
+ return;
+ }
+
+ // Try to auto-promote the read marker over the user's own messages
+ // (switch to direct iterators for that).
+ const auto eagerMarker = find_if(newMarker.base(), syncEdge(),
+ [=](const TimelineItem& ti) {
+ return ti->senderId() != u->id();
+ })
+ - 1;
+ newEvtId = (*eagerMarker)->id();
+ if (eagerMarker != newMarker.base() - 1) // &*(rIt.base() - 1) === &*rIt
+ qCDebug(EPHEMERAL) << "Auto-promoted read receipt for" << u->id()
+ << "to" << newEvtId;
+ }
+
auto& storedId = lastReadEventIds[u];
- if (storedId == eventId)
- return Change::NoChange;
+ if (storedId == newEvtId)
+ return;
+ // Finally make the change
eventIdReadUsers.remove(storedId, u);
- eventIdReadUsers.insert(eventId, u);
- swap(storedId, eventId);
+ eventIdReadUsers.insert(newEvtId, u);
+ swap(storedId, newEvtId); // Now newEvtId actually stores the old eventId
+ qCDebug(EPHEMERAL) << "The new read receipt for" << u->id() << "is at"
+ << storedId;
emit q->lastReadEventChanged(u);
- emit q->readMarkerForUserMoved(u, eventId, storedId);
- if (isLocalUser(u)) {
- if (storedId != serverReadMarker)
- connection->callApi<PostReadMarkersJob>(BackgroundRequest, id,
- storedId);
- emit q->readMarkerMoved(eventId, storedId);
- return Change::ReadMarkerChange;
- }
- return Change::NoChange;
+ if (!isLocalUser(u))
+ emit q->readMarkerForUserMoved(u, newEvtId, storedId);
}
-void Room::Private::updateUnreadCount(const rev_iter_t& from,
- const rev_iter_t& to)
+Room::Changes Room::Private::updateUnreadCount(const rev_iter_t& from,
+ const rev_iter_t& to)
{
Q_ASSERT(from >= timeline.crbegin() && from <= timeline.crend());
Q_ASSERT(to >= from && to <= timeline.crend());
- // Catch a special case when the last read event id refers to an event
- // that has just arrived. In this case we should recalculate
- // unreadMessages and might need to promote the read marker further
- // over local-origin messages.
- auto readMarker = q->readMarker();
- if (readMarker == timeline.crend() && q->allHistoryLoaded())
- --readMarker; // Read marker not found in the timeline, initialise it
- if (readMarker >= from && readMarker < to) {
- promoteReadMarker(q->localUser(), readMarker, true);
- return;
- }
-
- Q_ASSERT(to <= readMarker);
+ auto fullyReadMarker = q->readMarker();
+ if (fullyReadMarker < from)
+ return NoChange; // What's arrived is already fully read
+
+ // If there's no read marker in the whole room, initialise it
+ if (fullyReadMarker == historyEdge() && q->allHistoryLoaded())
+ return setFullyReadMarker(timeline.front()->id());
+
+ // Catch a special case when the last fully read event id refers to an
+ // event that has just arrived. In this case we should recalculate
+ // unreadMessages to get an exact number instead of an estimation
+ // (see https://github.com/quotient-im/libQuotient/wiki/unread_count).
+ // For the same reason (switching from the estimation to the exact
+ // number) this branch always emits unreadMessagesChanged() and returns
+ // UnreadNotifsChange, even if the estimation luckily matched the exact
+ // result.
+ if (fullyReadMarker < to)
+ return recalculateUnreadCount(true);
+
+ // At this point the fully read marker is somewhere beyond the "oldest"
+ // message from the arrived batch - add up newly arrived messages to
+ // the current counter, instead of a complete recalculation.
+ Q_ASSERT(to <= fullyReadMarker);
QElapsedTimer et;
et.start();
const auto newUnreadMessages =
- count_if(from, to, std::bind(&Room::Private::isEventNotable, this, _1));
+ count_if(from, to,
+ std::bind(&Room::Private::isEventNotable, this, _1));
if (et.nsecsElapsed() > profilerMinNsecs() / 10)
- qCDebug(PROFILER) << "Counting gained unread messages took" << et;
-
- if (newUnreadMessages > 0) {
- // See https://github.com/quotient-im/libQuotient/wiki/unread_count
- if (unreadMessages < 0)
- unreadMessages = 0;
-
- unreadMessages += newUnreadMessages;
- qCDebug(MESSAGES) << "Room" << q->objectName() << "has gained"
- << newUnreadMessages << "unread message(s),"
- << (q->readMarker() == timeline.crend()
- ? "in total at least"
- : "in total")
+ qCDebug(PROFILER) << "Counting gained unread messages in"
+ << q->objectName() << "took" << et;
+
+ if (newUnreadMessages == 0)
+ return NoChange;
+
+ // See https://github.com/quotient-im/libQuotient/wiki/unread_count
+ if (unreadMessages < 0)
+ unreadMessages = 0;
+
+ unreadMessages += newUnreadMessages;
+ qCDebug(MESSAGES) << "Room" << q->objectName() << "has gained"
+ << newUnreadMessages << "unread message(s),"
+ << (q->readMarker() == historyEdge()
+ ? "in total at least"
+ : "in total")
+ << unreadMessages << "unread message(s)";
+ emit q->unreadMessagesChanged(q);
+ return UnreadNotifsChange;
+}
+
+Room::Changes Room::Private::recalculateUnreadCount(bool force)
+{
+ // The recalculation logic assumes that the fully read marker points at
+ // a specific position in the timeline
+ Q_ASSERT(q->readMarker() != historyEdge());
+ const auto oldUnreadCount = unreadMessages;
+ QElapsedTimer et;
+ et.start();
+ unreadMessages =
+ int(count_if(timeline.crbegin(), q->readMarker(),
+ [this](const auto& ti) { return isEventNotable(ti); }));
+ if (et.nsecsElapsed() > profilerMinNsecs() / 10)
+ qCDebug(PROFILER) << "Recounting unread messages in" << q->objectName()
+ << "took" << et;
+
+ // See https://github.com/quotient-im/libQuotient/wiki/unread_count
+ if (unreadMessages == 0)
+ unreadMessages = -1;
+
+ if (!force && unreadMessages == oldUnreadCount)
+ return NoChange;
+
+ if (unreadMessages == -1)
+ qCDebug(MESSAGES)
+ << "Room" << displayname << "has no more unread messages";
+ else
+ qCDebug(MESSAGES) << "Room" << displayname << "still has"
<< unreadMessages << "unread message(s)";
- emit q->unreadMessagesChanged(q);
- }
+ emit q->unreadMessagesChanged(q);
+ return UnreadNotifsChange;
}
-Room::Changes Room::Private::promoteReadMarker(User* u,
- const rev_iter_t& newMarker,
- bool force)
+Room::Changes Room::Private::setFullyReadMarker(const QString& eventId)
{
- Q_ASSERT_X(u, __FUNCTION__, "User* should not be nullptr");
- Q_ASSERT(newMarker >= timeline.crbegin() && newMarker <= timeline.crend());
+ if (fullyReadUntilEventId == eventId)
+ return NoChange;
- const auto prevMarker = q->readMarker(u);
- if (!force && prevMarker <= newMarker) // Remember, we deal with reverse
- // iterators
- return Change::NoChange;
-
- Q_ASSERT(newMarker < timeline.crend());
+ const auto prevFullyReadId = std::exchange(fullyReadUntilEventId, eventId);
+ qCDebug(MESSAGES) << "Fully read marker in" << q->objectName() //
+ << "set to" << fullyReadUntilEventId;
+ emit q->readMarkerMoved(prevFullyReadId, fullyReadUntilEventId);
- // Try to auto-promote the read marker over the user's own messages
- // (switch to direct iterators for that).
- auto eagerMarker =
- find_if(newMarker.base(), timeline.cend(), [=](const TimelineItem& ti) {
- return ti->senderId() != u->id();
- });
+ Changes changes = ReadMarkerChange;
+ if (const auto rm = q->readMarker(); rm != historyEdge()) {
+ // Pull read receipt if it's behind
+ if (auto rr = q->readMarker(q->localUser()); rr > rm)
+ setLastReadReceipt(q->localUser(), rm);
- auto changes = setLastReadEvent(u, (*(eagerMarker - 1))->id());
- if (isLocalUser(u)) {
- const auto oldUnreadCount = unreadMessages;
- QElapsedTimer et;
- et.start();
- unreadMessages =
- int(count_if(eagerMarker, timeline.cend(),
- [this](const auto& ti) { return isEventNotable(ti); }));
- if (et.nsecsElapsed() > profilerMinNsecs() / 10)
- qCDebug(PROFILER) << "Recounting unread messages took" << et;
-
- // See https://github.com/quotient-im/libQuotient/wiki/unread_count
- if (unreadMessages == 0)
- unreadMessages = -1;
-
- if (force || unreadMessages != oldUnreadCount) {
- if (unreadMessages == -1) {
- qCDebug(MESSAGES)
- << "Room" << displayname << "has no more unread messages";
- } else
- qCDebug(MESSAGES) << "Room" << displayname << "still has"
- << unreadMessages << "unread message(s)";
- emit q->unreadMessagesChanged(q);
- changes |= Change::UnreadNotifsChange;
- }
+ changes |= recalculateUnreadCount();
}
return changes;
}
-Room::Changes Room::Private::markMessagesAsRead(rev_iter_t upToMarker)
+void Room::Private::markMessagesAsRead(const rev_iter_t &upToMarker)
{
- const auto prevMarker = q->readMarker();
- auto changes = promoteReadMarker(q->localUser(), upToMarker);
- if (prevMarker != upToMarker)
- qCDebug(MESSAGES) << "Marked messages as read until" << *q->readMarker();
-
- // We shouldn't send read receipts for the local user's own messages - so
- // search earlier messages for the latest message not from the local user
- // until the previous last-read message, whichever comes first.
- for (; upToMarker < prevMarker; ++upToMarker) {
- if ((*upToMarker)->senderId() != q->localUser()->id()) {
- connection->callApi<PostReceiptJob>(BackgroundRequest,
- id, QStringLiteral("m.read"),
- QUrl::toPercentEncoding(
- (*upToMarker)->id()));
- break;
- }
+ if (upToMarker < q->readMarker()) {
+ setFullyReadMarker((*upToMarker)->id());
+ // Assuming that if a read receipt was sent on a newer event, it will
+ // stay there instead of "un-reading" notifications/mentions from
+ // m.fully_read to m.read
+ connection->callApi<SetReadMarkerJob>(BackgroundRequest, id,
+ fullyReadUntilEventId,
+ fullyReadUntilEventId);
}
- return changes;
}
void Room::markMessagesAsRead(QString uptoEventId)
@@ -800,14 +843,11 @@ bool Room::hasUnreadMessages() const { return unreadCount() >= 0; }
int Room::unreadCount() const { return d->unreadMessages; }
-Room::rev_iter_t Room::historyEdge() const { return d->timeline.crend(); }
+Room::rev_iter_t Room::historyEdge() const { return d->historyEdge(); }
-Room::Timeline::const_iterator Room::syncEdge() const
-{
- return d->timeline.cend();
-}
+Room::Timeline::const_iterator Room::syncEdge() const { return d->syncEdge(); }
-Room::rev_iter_t Room::timelineEdge() const { return historyEdge(); }
+Room::rev_iter_t Room::timelineEdge() const { return d->historyEdge(); }
TimelineItem::index_t Room::minTimelineIndex() const
{
@@ -886,7 +926,7 @@ void Room::Private::getAllMembers()
// the full members list was requested.
if (!timeline.empty())
for (auto it = q->findInTimeline(nextIndex).base();
- it != timeline.cend(); ++it)
+ it != syncEdge(); ++it)
if (is<RoomMemberEvent>(**it))
roomChanges |= q->processStateEvent(**it);
if (roomChanges & MembersChange)
@@ -904,11 +944,8 @@ void Room::setDisplayed(bool displayed)
d->displayed = displayed;
emit displayedChanged(displayed);
- if (displayed) {
- resetHighlightCount();
- resetNotificationCount();
+ if (displayed)
d->getAllMembers();
- }
}
QString Room::firstDisplayedEventId() const { return d->firstDisplayedEventId; }
@@ -923,6 +960,11 @@ void Room::setFirstDisplayedEventId(const QString& eventId)
if (d->firstDisplayedEventId == eventId)
return;
+ if (!eventId.isEmpty() && findInTimeline(eventId) == historyEdge())
+ qCWarning(MESSAGES)
+ << eventId
+ << "is marked as first displayed but doesn't seem to be loaded";
+
d->firstDisplayedEventId = eventId;
emit firstDisplayedEventChanged();
}
@@ -945,8 +987,20 @@ void Room::setLastDisplayedEventId(const QString& eventId)
if (d->lastDisplayedEventId == eventId)
return;
+ const auto marker = findInTimeline(eventId);
+ if (!eventId.isEmpty() && marker == historyEdge())
+ qCWarning(MESSAGES)
+ << eventId
+ << "is marked as last displayed but doesn't seem to be loaded";
+
d->lastDisplayedEventId = eventId;
emit lastDisplayedEventChanged();
+ if (d->displayed && marker < readMarker(localUser())) {
+ d->setLastReadReceipt(localUser(), marker);
+ connection()->callApi<PostReceiptJob>(BackgroundRequest, id(),
+ QStringLiteral("m.read"),
+ QUrl::toPercentEncoding(eventId));
+ }
}
void Room::setLastDisplayedEvent(TimelineItem::index_t index)
@@ -961,11 +1015,14 @@ Room::rev_iter_t Room::readMarker(const User* user) const
return findInTimeline(d->lastReadEventIds.value(user));
}
-Room::rev_iter_t Room::readMarker() const { return readMarker(localUser()); }
+Room::rev_iter_t Room::readMarker() const
+{
+ return findInTimeline(d->fullyReadUntilEventId);
+}
QString Room::readMarkerEventId() const
{
- return d->lastReadEventIds.value(localUser());
+ return d->fullyReadUntilEventId;
}
QList<User*> Room::usersAtEventId(const QString& eventId)
@@ -1477,21 +1534,13 @@ void Room::updateData(SyncRoomData&& data, bool fromCache)
setJoinState(data.joinState);
Changes roomChanges = Change::NoChange;
- QElapsedTimer et;
- et.start();
for (auto&& event : data.accountData)
roomChanges |= processAccountDataEvent(move(event));
roomChanges |= d->updateStateFrom(data.state);
+ // The order of calculation is important - don't merge these lines!
+ roomChanges |= d->addNewMessageEvents(move(data.timeline));
- if (!data.timeline.empty()) {
- et.restart();
- roomChanges |= d->addNewMessageEvents(move(data.timeline));
- if (data.timeline.size() > 9 || et.nsecsElapsed() >= profilerMinNsecs())
- qCDebug(PROFILER)
- << "*** Room::addNewMessageEvents():" << data.timeline.size()
- << "event(s)," << et;
- }
if (roomChanges & TopicChange)
emit topicChanged();
@@ -1507,17 +1556,30 @@ void Room::updateData(SyncRoomData&& data, bool fromCache)
roomChanges |= processEphemeralEvent(move(ephemeralEvent));
// See https://github.com/quotient-im/libQuotient/wiki/unread_count
+ // -2 is a special value to which SyncRoomData::SyncRoomData sets
+ // unreadCount when it's missing in the payload (to distinguish from
+ // explicit 0 in the payload).
if (data.unreadCount != -2 && data.unreadCount != d->unreadMessages) {
qCDebug(MESSAGES) << "Setting unread_count to" << data.unreadCount;
d->unreadMessages = data.unreadCount;
emit unreadMessagesChanged(this);
}
- if (data.highlightCount != d->highlightCount) {
+ // Similar to unreadCount, SyncRoomData constructor assigns -1 to
+ // highlightCount/notificationCount when those are missing in the payload
+ if (data.highlightCount != -1 && data.highlightCount != d->highlightCount) {
+ qCDebug(MESSAGES).nospace()
+ << "Highlights in " << objectName() //
+ << ": " << d->highlightCount << " -> " << data.highlightCount;
d->highlightCount = data.highlightCount;
emit highlightCountChanged();
}
- if (data.notificationCount != d->notificationCount) {
+ if (data.notificationCount != -1
+ && data.notificationCount != d->notificationCount) //
+ {
+ qCDebug(MESSAGES).nospace()
+ << "Notifications in " << objectName() //
+ << ": " << d->notificationCount << " -> " << data.notificationCount;
d->notificationCount = data.notificationCount;
emit notificationCountChanged();
}
@@ -2221,6 +2283,8 @@ Room::Changes Room::Private::addNewMessageEvents(RoomEvents&& events)
if (events.empty())
return Change::NoChange;
+ QElapsedTimer et;
+ et.start();
{
// Pre-process redactions and edits so that events that get
// redacted/replaced in the same batch landed in the timeline already
@@ -2288,7 +2352,7 @@ Room::Changes Room::Private::addNewMessageEvents(RoomEvents&& events)
emit q->aboutToAddNewMessages(eventsSpan);
auto insertedSize = moveEventsToTimeline(eventsSpan, Newer);
totalInserted += insertedSize;
- auto firstInserted = timeline.cend() - insertedSize;
+ auto firstInserted = syncEdge() - insertedSize;
q->onAddNewTimelineEvents(firstInserted);
emit q->addedMessages(firstInserted->index(),
timeline.back().index());
@@ -2318,20 +2382,20 @@ Room::Changes Room::Private::addNewMessageEvents(RoomEvents&& events)
unsyncedEvents.erase(unsyncedEvents.begin() + pendingEvtIdx);
if (auto insertedSize = moveEventsToTimeline({ remoteEcho, it }, Newer)) {
totalInserted += insertedSize;
- q->onAddNewTimelineEvents(timeline.cend() - insertedSize);
+ q->onAddNewTimelineEvents(syncEdge() - insertedSize);
}
emit q->pendingEventMerged();
}
// Events merged and transferred from `events` to `timeline` now.
- const auto from = timeline.cend() - totalInserted;
+ const auto from = syncEdge() - totalInserted;
if (q->supportsCalls())
- for (auto it = from; it != timeline.cend(); ++it)
+ for (auto it = from; it != syncEdge(); ++it)
if (const auto* evt = it->viewAs<CallEventBase>())
emit q->callEvent(q, evt);
if (totalInserted > 0) {
- for (auto it = from; it != timeline.cend(); ++it) {
+ for (auto it = from; it != syncEdge(); ++it) {
if (const auto* reaction = it->viewAs<ReactionEvent>()) {
const auto& relation = reaction->relation();
relations[{ relation.eventId, relation.type }] << reaction;
@@ -2344,27 +2408,28 @@ Room::Changes Room::Private::addNewMessageEvents(RoomEvents&& events)
<< timeline.back();
// The first event in the just-added batch (referred to by `from`)
- // defines whose read marker can possibly be promoted any further over
+ // defines whose read receipt can possibly be promoted any further over
// the same author's events newly arrived. Others will need explicit
- // read receipts from the server (or, for the local user,
- // markMessagesAsRead() invocation) to promote their read markers over
+ // read receipts from the server - or, for the local user, calling
+ // setLastDisplayedEventId() - to promote their read receipts over
// the new message events.
- if (const auto senderId = (*from)->senderId(); !senderId.isEmpty()) {
- auto* const firstWriter = q->user(senderId);
- if (firstWriter && q->readMarker(firstWriter) != timeline.crend()) {
+ if (auto* const firstWriter = q->user((*from)->senderId())) {
+ setLastReadReceipt(firstWriter, rev_iter_t(from + 1));
+ if (firstWriter == q->localUser() && q->readMarker().base() == from) {
+ // If the local user's message(s) is/are first in the batch
+ // and the fully read marker was right before it, promote
+ // the fully read marker to the same event as the read receipt.
roomChanges |=
- promoteReadMarker(firstWriter, rev_iter_t(from) - 1);
- qCDebug(MESSAGES)
- << "Auto-promoted read marker for" << senderId
- << "to" << *q->readMarker(firstWriter);
+ setFullyReadMarker(lastReadEventIds.value(firstWriter));
}
}
-
- updateUnreadCount(timeline.crbegin(), rev_iter_t(from));
- roomChanges |= Change::UnreadNotifsChange;
+ roomChanges |= updateUnreadCount(timeline.crbegin(), rev_iter_t(from));
}
Q_ASSERT(timeline.size() == timelineSize + totalInserted);
+ if (totalInserted > 9 || et.nsecsElapsed() >= profilerMinNsecs())
+ qCDebug(PROFILER) << "Added" << totalInserted << "new event(s) to"
+ << q->objectName() << "in" << et;
return roomChanges;
}
@@ -2392,27 +2457,32 @@ void Room::Private::addHistoricalMessageEvents(RoomEvents&& events)
emit q->aboutToAddHistoricalMessages(events);
const auto insertedSize = moveEventsToTimeline(events, Older);
- const auto from = timeline.crend() - insertedSize;
+ const auto from = historyEdge() - insertedSize;
qCDebug(STATE) << "Room" << displayname << "received" << insertedSize
<< "past events; the oldest event is now" << timeline.front();
q->onAddHistoricalTimelineEvents(from);
emit q->addedMessages(timeline.front().index(), from->index());
- for (auto it = from; it != timeline.crend(); ++it) {
+ for (auto it = from; it != historyEdge(); ++it) {
if (const auto* reaction = it->viewAs<ReactionEvent>()) {
const auto& relation = reaction->relation();
relations[{ relation.eventId, relation.type }] << reaction;
emit q->updatedEvent(relation.eventId);
}
}
- if (from <= q->readMarker())
- updateUnreadCount(from, timeline.crend());
+ if (updateUnreadCount(from, historyEdge()) != NoChange)
+ connection->saveRoomState(q);
+
+ // When there are no unread messages and the read marker is within the
+ // known timeline, unreadMessages == -1
+ // (see https://github.com/quotient-im/libQuotient/wiki/unread_count).
+ Q_ASSERT(unreadMessages != 0 || q->readMarker() == historyEdge());
Q_ASSERT(timeline.size() == timelineSize + insertedSize);
if (insertedSize > 9 || et.nsecsElapsed() >= profilerMinNsecs())
- qCDebug(PROFILER) << "*** Room::addHistoricalMessageEvents():"
- << insertedSize << "event(s)," << et;
+ qCDebug(PROFILER) << "Added" << insertedSize << "historical event(s) to"
+ << q->objectName() << "in" << et;
}
Room::Changes Room::processStateEvent(const RoomEvent& e)
@@ -2611,12 +2681,13 @@ Room::Changes Room::processEphemeralEvent(EventPtr&& event)
d->usersTyping.clear();
for (const QString& userId : qAsConst(evt->users())) {
auto* const u = user(userId);
- if (u && memberJoinState(u) == JoinState::Join)
+ if (memberJoinState(u) == JoinState::Join)
d->usersTyping.append(u);
}
if (evt->users().size() > 3 || et.nsecsElapsed() >= profilerMinNsecs())
- qCDebug(PROFILER) << "*** Room::processEphemeralEvent(typing):"
- << evt->users().size() << "users," << et;
+ qCDebug(PROFILER)
+ << "Processing typing events from" << evt->users().size()
+ << "user(s) in" << objectName() << "took" << et;
emit typingChanged();
}
if (auto* evt = eventCast<ReceiptEvent>(event)) {
@@ -2625,44 +2696,36 @@ Room::Changes Room::processEphemeralEvent(EventPtr&& event)
totalReceipts += p.receipts.size();
{
if (p.receipts.size() == 1)
- qCDebug(EPHEMERAL) << "Marking" << p.evtId << "as read for"
- << p.receipts[0].userId;
+ qCDebug(EPHEMERAL)
+ << objectName() << "received a read receipt for"
+ << p.evtId << "from" << p.receipts[0].userId;
else
- qCDebug(EPHEMERAL) << "Marking" << p.evtId << "as read for"
- << p.receipts.size() << "users";
+ qCDebug(EPHEMERAL)
+ << objectName() << "received read receipts for"
+ << p.evtId << "from" << p.receipts.size() << "users";
}
const auto newMarker = findInTimeline(p.evtId);
- if (newMarker != timelineEdge()) {
- for (const Receipt& r : p.receipts) {
- if (r.userId == connection()->userId())
- continue; // FIXME, #185
- auto* const u = user(r.userId);
- if (u && memberJoinState(u) == JoinState::Join)
- changes |= d->promoteReadMarker(u, newMarker);
- }
- } else {
- qCDebug(EPHEMERAL) << "Event" << p.evtId
- << "not found; saving read receipts anyway";
- // If the event is not found (most likely, because it's too old
- // and hasn't been fetched from the server yet), but there is
- // a previous marker for a user, keep the previous marker.
- // Otherwise, blindly store the event id for this user.
- for (const Receipt& r : p.receipts) {
- if (r.userId == connection()->userId())
- continue; // FIXME, #185
- auto* const u = user(r.userId);
- if (u && memberJoinState(u) == JoinState::Join
- && readMarker(u) == timelineEdge())
- changes |= d->setLastReadEvent(u, p.evtId);
+ if (newMarker == historyEdge())
+ qCDebug(EPHEMERAL) << "Event of the read receipt(s) is not "
+ "found; saving them anyway";
+ for (const Receipt& r : p.receipts)
+ if (auto* const u = user(r.userId);
+ memberJoinState(u) == JoinState::Join) {
+ // If the event is not found (most likely, because it's
+ // too old and hasn't been fetched from the server yet)
+ // but there is a previous marker for a user, keep
+ // the previous marker because read receipts are not
+ // supposed to move backwards. Otherwise, blindly
+ // store the event id for this user and update the read
+ // marker when/if the event is fetched later on.
+ d->setLastReadReceipt(u, newMarker, p.evtId);
}
- }
}
if (evt->eventsWithReceipts().size() > 3 || totalReceipts > 10
|| et.nsecsElapsed() >= profilerMinNsecs())
- qCDebug(PROFILER)
- << "*** Room::processEphemeralEvent(receipts):"
- << evt->eventsWithReceipts().size() << "event(s) with"
- << totalReceipts << "receipt(s)," << et;
+ qCDebug(PROFILER) << "Processing" << totalReceipts << "receipt(s) on"
+ << evt->eventsWithReceipts().size()
+ << "event(s) in" << objectName() << "took" << et;
}
return changes;
}
@@ -2675,15 +2738,9 @@ Room::Changes Room::processAccountDataEvent(EventPtr&& event)
changes |= Change::TagsChange;
}
- if (auto* evt = eventCast<ReadMarkerEvent>(event)) {
- auto readEventId = evt->event_id();
- qCDebug(STATE) << "Server-side read marker at" << readEventId;
- d->serverReadMarker = readEventId;
- const auto newMarker = findInTimeline(readEventId);
- changes |= newMarker != timelineEdge()
- ? d->markMessagesAsRead(newMarker)
- : d->setLastReadEvent(localUser(), readEventId);
- }
+ if (auto* evt = eventCast<const ReadMarkerEvent>(event))
+ changes |= d->setFullyReadMarker(evt->event_id());
+
// For all account data events
auto& currentData = d->accountData[event->matrixType()];
// A polymorphic event-specific comparison might be a bit more
@@ -2694,9 +2751,9 @@ Room::Changes Room::processAccountDataEvent(EventPtr&& event)
qCDebug(STATE) << "Updated account data of type"
<< currentData->matrixType();
emit accountDataChanged(currentData->matrixType());
- return Change::AccountDataChange;
+ changes |= Change::AccountDataChange;
}
- return Change::NoChange;
+ return changes;
}
template <typename ContT>
@@ -2864,6 +2921,31 @@ QJsonObject Room::Private::toJson() const
{ QStringLiteral("events"), accountDataEvents } });
}
+ if (const auto& readReceiptEventId = lastReadEventIds.value(q->localUser());
+ !readReceiptEventId.isEmpty()) //
+ {
+ // Okay, that's a mouthful; but basically, it's simply placing an m.read
+ // event in the 'ephemeral' section of the cached sync payload.
+ // See also receiptevent.* and m.read example in the spec.
+ // Only the local user's read receipt is saved - others' are really
+ // considered ephemeral but this one is useful in understanding where
+ // the user is in the timeline before any history is loaded.
+ result.insert(
+ QStringLiteral("ephemeral"),
+ QJsonObject {
+ { QStringLiteral("events"),
+ QJsonArray { QJsonObject {
+ { TypeKey, ReceiptEvent::matrixTypeId() },
+ { ContentKey,
+ QJsonObject {
+ { readReceiptEventId,
+ QJsonObject {
+ { QStringLiteral("m.read"),
+ QJsonObject {
+ { connection->userId(),
+ QJsonObject {} } } } } } } } } } } });
+ }
+
QJsonObject unreadNotifObj { { SyncRoomData::UnreadCountKey,
unreadMessages } };
@@ -2876,7 +2958,8 @@ QJsonObject Room::Private::toJson() const
result.insert(QStringLiteral("unread_notifications"), unreadNotifObj);
if (et.elapsed() > 30)
- qCDebug(PROFILER) << "Room::toJson() for" << displayname << "took" << et;
+ qCDebug(PROFILER) << "Room::toJson() for" << q->objectName() << "took"
+ << et;
return result;
}
diff --git a/lib/room.h b/lib/room.h
index 1ddff517..6270a5a5 100644
--- a/lib/room.h
+++ b/lib/room.h
@@ -336,8 +336,22 @@ public:
void setLastDisplayedEventId(const QString& eventId);
void setLastDisplayedEvent(TimelineItem::index_t index);
+ /*! \brief Obtain a read receipt of any user
+ *
+ * Since 0.6.8, there's an important difference between the single-argument
+ * and the zero-argument overloads of this function: a call with an argument
+ * returns the last _read receipt_ position (for any room member) while
+ * a call without arguments returns the last _fully read_ position.
+ * This is due to API stability guarantees; 0.7 will have distinctly named
+ * methods to return read receipts and the fully read marker.
+ */
rev_iter_t readMarker(const User* user) const;
+ /*! \brief Obtain the local user's fully-read marker
+ *
+ * \sa the description for the single-argument overload of this function
+ */
rev_iter_t readMarker() const;
+ /// \brief Get the event id for the local user's fully-read marker
QString readMarkerEventId() const;
QList<User*> usersAtEventId(const QString& eventId);
/**
@@ -346,7 +360,11 @@ public:
* Finds in the timeline and marks as read the event with
* the specified id; also posts a read receipt to the server either
* for this message or, if it's from the local user, for
- * the nearest non-local message before. uptoEventId must be non-empty.
+ * the nearest non-local message before. If the fully read marker is within
+ * the displayed viewport (between firstDisplayedMarker() and
+ * lastDisplayedMarker()) then it is advanced as well.
+ *
+ * uptoEventId must be non-empty.
*/
void markMessagesAsRead(QString uptoEventId);
@@ -573,7 +591,7 @@ public slots:
void downloadFile(const QString& eventId, const QUrl& localFilename = {});
void cancelFileTransfer(const QString& id);
- /// Mark all messages in the room as read
+ /// Mark the bottommost message in the room as fully read
void markAllMessagesAsRead();
/// Switch the room's version (aka upgrade)
diff --git a/lib/syncdata.cpp b/lib/syncdata.cpp
index e6472e18..a3809469 100644
--- a/lib/syncdata.cpp
+++ b/lib/syncdata.cpp
@@ -106,12 +106,8 @@ SyncRoomData::SyncRoomData(const QString& roomId_, JoinState joinState_,
const auto unreadJson = room_.value("unread_notifications"_ls).toObject();
unreadCount = unreadJson.value(UnreadCountKey).toInt(-2);
- highlightCount = unreadJson.value("highlight_count"_ls).toInt();
- notificationCount = unreadJson.value("notification_count"_ls).toInt();
- if (highlightCount > 0 || notificationCount > 0)
- qCDebug(SYNCJOB) << "Room" << roomId_
- << "has highlights:" << highlightCount
- << "and notifications:" << notificationCount;
+ highlightCount = unreadJson.value("highlight_count"_ls).toInt(-1);
+ notificationCount = unreadJson.value("notification_count"_ls).toInt(-1);
}
SyncData::SyncData(const QString& cacheFileName)
@@ -186,13 +182,14 @@ void SyncData::parseJson(const QJsonObject& json, const QString& baseDir)
deviceOneTimeKeysCount_);
auto rooms = json.value("rooms"_ls).toObject();
- JoinStates::Int ii = 1; // ii is used to make a JoinState value
auto totalRooms = 0;
auto totalEvents = 0;
- for (size_t i = 0; i < JoinStateStrings.size(); ++i, ii <<= 1) {
+ for (size_t i = 0; i < JoinStateStrings.size(); ++i) {
+ // This assumes that JoinState values go over powers of 2: 1,2,4,...
+ const auto joinState = JoinState(1U << i);
const auto rs = rooms.value(JoinStateStrings[i]).toObject();
// We have a Qt container on the right and an STL one on the left
- roomData.reserve(static_cast<size_t>(rs.size()));
+ roomData.reserve(roomData.size() + static_cast<size_t>(rs.size()));
for (auto roomIt = rs.begin(); roomIt != rs.end(); ++roomIt) {
auto roomJson =
roomIt->isObject()
@@ -202,7 +199,7 @@ void SyncData::parseJson(const QJsonObject& json, const QString& baseDir)
unresolvedRoomIds.push_back(roomIt.key());
continue;
}
- roomData.emplace_back(roomIt.key(), JoinState(ii), roomJson);
+ roomData.emplace_back(roomIt.key(), joinState, roomJson);
const auto& r = roomData.back();
totalEvents += r.state.size() + r.ephemeral.size()
+ r.accountData.size() + r.timeline.size();
diff --git a/lib/user.cpp b/lib/user.cpp
index 85f9d9a7..4e369a4f 100644
--- a/lib/user.cpp
+++ b/lib/user.cpp
@@ -71,7 +71,9 @@ void User::Private::fetchProfile(const User* q)
{
defaultAvatar.emplace(Avatar {});
defaultName = "";
- auto* j = q->connection()->callApi<GetUserProfileJob>(BackgroundRequest, id);
+ auto* j =
+ q->connection()->callApi<GetUserProfileJob>(BackgroundRequest,
+ QUrl::toPercentEncoding(id));
// FIXME: accepting const User* and const_cast'ing it here is only
// until we get a better User API in 0.7
QObject::connect(j, &BaseJob::success, q,
@@ -171,11 +173,11 @@ void User::rename(const QString& newName, const Room* r)
rename(newName);
return;
}
- Q_ASSERT_X(r->memberJoinState(this) == JoinState::Join, __FUNCTION__,
+ // #481: take the current state and update it with the new name
+ auto evtC = r->getCurrentState<RoomMemberEvent>(id())->content();
+ Q_ASSERT_X(evtC.membership == MembershipType::Join, __FUNCTION__,
"Attempt to rename a user that's not a room member");
- const auto actualNewName = sanitized(newName);
- MemberEventContent evtC;
- evtC.displayName = actualNewName;
+ evtC.displayName = sanitized(newName);
r->setState<RoomMemberEvent>(id(), move(evtC));
// The state will be updated locally after it arrives with sync
}
diff --git a/lib/util.cpp b/lib/util.cpp
index 875d7522..cf5e81a3 100644
--- a/lib/util.cpp
+++ b/lib/util.cpp
@@ -47,7 +47,7 @@ void Quotient::linkifyUrls(QString& htmlEscapedText)
// comma or dot
static const QRegularExpression FullUrlRegExp(
QStringLiteral(
- R"(\b((www\.(?!\.)(?!(\w|\.|-)+@)|(https?|ftp|magnet|matrix):(//)?)(&(?![lg]t;)|[^&\s<>'"])+(&(?![lg]t;)|[^&!,.\s<>'"\]):])))"),
+ R"(\b((www\.(?!\.)(?!(\w|\.|-)+@)|(https?|ftp):(//)?\w|(magnet|matrix):)(&(?![lg]t;)|[^&\s<>'"])+(&(?![lg]t;)|[^&!,.\s<>'"\]):])))"),
RegExpOptions);
// email address:
// [word chars, dots or dashes]@[word chars, dots or dashes].[word chars]
@@ -58,7 +58,7 @@ void Quotient::linkifyUrls(QString& htmlEscapedText)
// https://matrix.org/docs/spec/appendices.html#identifier-grammar
static const QRegularExpression MxIdRegExp(
QStringLiteral(
- R"((^|[^<>/])([!#@][-a-z0-9_=#/.]{1,252}:(?:\w|\.|-)+\.\w+(?::\d{1,5})?))"),
+ R"((^|[][[:space:](){}`'";])([!#@][-a-z0-9_=#/.]{1,252}:\w(?:\w|\.|-)*\.\w+(?::\d{1,5})?))"),
RegExpOptions);
Q_ASSERT(FullUrlRegExp.isValid() && EmailAddressRegExp.isValid()
&& MxIdRegExp.isValid());