Age | Commit message (Collapse) | Author |
|
Profiling revealed 3 inefficiencies in read receipts code - and given
there are a lot of them coming, these inefficiences quickly add up.
Fixing them allows to slash read receipt processing time by 60%, and
the total time of updating a room by more than a half.
1. Room::lastReadEventChanged() is emitted per receipt. This can be
taxing on initial syncs or in bigger rooms; this commit converts it
to an aggregate signal only emitted once per sync room batch and
carrying the list of all user ids (more on that below) with updated
read receipts.
For that, Room::P::setLastReadEvent() is split into
Room::P::setLocalLastReadEvent() that is called whenever the local
read receipt has to be updated, and setLastReadEvent() proper that is
very fast and only updates the internal data structures, nothing else.
setLocalLastEvent() calls it, as does processEphemeralEvents(); both
take responsibility to emit lastReadEventChanged() depending on the
outcome of setLastReadEvent() invocation(s).
2. Massively aggravating the above point, user id from each read receipt
is turned to a User object - and since most of the users are unknown
at early moments, this causes thousands of allocations. Therefore
the new aggregated lastReadEventChanged() only carries user ids, and
clients will have to resolve them to User objects if they need.
3. Despite fairly tight conditions (note we're talking about thousands
of receipts), Quotient still creates an intermediate C++ structure
(EventsWithReceipts), only for the sake of passing it to
processEphemeralEvent() that immediately disassembles it back again,
converting to a series of calls to set(Local)LastReadEvent(). To fix
this, processEphemeralEvent() now takes the event content JSON
directly and iterates over it instead.
Aside from that, a few extraneous conditions and logging has been
removed and the whole function rewritten with switchOnType() to reduce
cognitive complexity.
|
|
Besides having a misleading name (and it goes back to the spec),
EncryptedFile under `file` key preempts the `url` (or `thumbnail_url`)
string value so only one of the two should exist. This is a case for
using std::variant<> - despite its clumsy syntax, it can actually
simplify and streamline code when all the necessary bits are in place
(such as conversion to JSON and getting the common piece - the URL -
out of it). This commit replaces `FileInfo::url` and `FileInfo::file`
with a common field `source` of type `FileSourceInfo` that is an alias
for a variant type covering both underlying types; and `url()` is
reintroduced as a function instead, to allow simplified access
to whichever URL is available inside the variant.
Oh, and EncryptedFile is EncryptedFileMetadata now, to clarify that it
does not represent the file payload itself but rather the data necessary
to obtain that payload.
|
|
|
|
Co-authored-by: Alexey Rusakov <Kitsune-Ral@users.sf.net>
|
|
|
|
Mainly driven by clang-tidy and SonarCloud warnings (sadly, SonarCloud
doesn't store historical reports so no link can be provided here).
|
|
See https://github.com/matrix-org/matrix-spec/pull/1054.
# Conflicts:
# lib/events/callanswerevent.cpp
# lib/events/callanswerevent.h
|
|
|
|
|
|
There are two important aspects here:
- Introducing Room::setState(evtType, stateKey, contentJson). These
components are ultimately what is getting sent to the homeserver,
so it makes sense to expose a respective `setState()` overload.
Unlike setState(event) the new overload can be Q_INVOKABLE.
- Room::setState() is no more const. Although it doesn't cause any
changes in Room class (and only transient changes in Room::Private),
it ultimately initiates a change in the room state, so calling it
const has always been a bit of hypocrisy. If you relied on that, you
most likely do something wrong (see the fix to User::rename()
in this very commit for a simple example of such wrongness).
Also: the backend is simplified by calling the original templated
Room::setState() instead of calling Room::Private::requestSetState()
that does exactly the same thing.
|
|
This class is called to provide an arbitrary snapshot of a room state;
as the first step, Room::currentState() returns an instance of this
class that stores, well, the current state. Implelementation-wise it's
the same hash map of two-part state event keys to const event pointers;
however, RoomStateView provides additional operations:
- get(), that deprecates Room::getCurrentState(), returns a pointer to
a particular event if the current state has it. Unlike the original
method, the pointer returned from this one can be nullptr; this is
done to get rid of stubbed state events that have to be created
everytime a "state miss" occurred (i.e., when getCurrentState()
does not find an existing event in the current state).
- eventsOfType() - this is a new place for Room::stateEventsOfType()
introduced recently.
- query() - this is a way to specify a piece of the state content that
you need to retrieve by passing a member function or a function object
that retrieves it. That is especially convenient with member functions
of the event class; just pass the pointer to this member function,
and query() will parse the event type it has to retrieve out of it and
call that member function on the event object. Returns an Omittable<>;
if the respective piece of state doesn't exist, you'll get
`Quotient::none` (the same as `std::nullopt`).
- queryOr() - the same but with the fallback value; instead of an
Omittable<>, the fallback value will be returned if the needed event
is not found.
|
|
RelatesTo and EventRelation have been two means to the same end in two
different contexts. (Modernised) EventRelation is the one used now both
for ReactionEvent and EventContent::TextContent. The modernisation
mostly boils down to using inline variables instead of functions to
return relation types and switching to QLatin1String from const char*
(because we know exactly that those constants are Latin-1 and
QLatin1String is more efficient than const char* to compare/convert to
QString).
|
|
|
|
|
|
|
|
This include all (hopefully) classes/structures and functions that have
non-inline definitions, as well as namespaces with Q_NAMESPACE since
those have non-inline (as of Qt 5.15) QMetaObject - for that a new
macro, QUO_NAMESPACE, has been devised to accommodate the lack of
Q_NAMESPACE_EXPORT in Qt before 5.14.
|
|
|
|
|
|
This is useful for implementing Spaces support, where all events of
type `m.space.child` are needed, and we don't know their state keys in
advance.
|
|
Trying to test bits with Changes::testFlag(Change::Any) was a bad idea.
Along the way: made logging in setLastReadReceipt() refer to the actual
timeline item when possible.
|
|
|
|
This introduces a new API to count unread events that would allow to
obtain those unread and highlight counts since either the fully read
marker (Room::partiallyReadStats) or the last read receipt
(Room::unreadStats). Element uses the read receipt as the anchor
to count unread numbers, while Quaternion historically used the fully
read marker for that (with the pre-0.7 library sticking the two markers
to each other). From now on the meaning of "unread" in Quotient is
aligned with that of the spec and Element, and "partially read" means
events between the fully read marker and the local read receipt;
the design allows client authors to use either or both counting
strategies as they see fit. Respectively, Room::P::setFullyReadMarker()
updates partially-read statistics, while Room::P::setLastReadReceipt(),
when called on a local user, updates unread statistics.
Room::notificationCount() and Room::highlightCount() maintain their
previous meaning as the counters since the last read receipt;
Room::notificationCount() counts unread events locally, falling back
to the value from the above-mentioned key defined by MSC2654, and if
that is not there, further to `unread_notifications/notification_count`
defined in the current spec. Room::highlightCount(), however, is still
taken from the homeserver, not from Room::unreadStats().highlightCount.
|
|
This makes updating display name and emission of necessary signals
including Room::changed() more systematic when it has to occur outside
of updateData() flow - e.g. when loading all members.
|
|
Since MSC2654's unread count is counted from the m.read receipt, and
the course is to follow the spec's terminology and use "unread count"
for the number of notable events since m.read, this required to move
the existing number of notable events since m.fully_read to another
field, henceforth called partiallyReadCount. At the same time,
SyncData::notificationCount is dropped completely since MSC2654 claims
to supersede it.
Also: Room::resetNotificationCount() and Room::resetHighlightCount() are
deprecated, as these never worked properly overwriting values that can
be calculated or sourced from the server, only for these values to be
set back again the next time the room is updated from /sync.
|
|
Room::isEventNotable has been moved out from Room::Private and made
compliant with MSC2654.
The concept of Room::checkForNotifications is taken from Quaternion
where a method with the same name has been in QuaternionRoom for a long
time - however, actual body is a stub for now, always returning
{ Notification::None } (Quaternion's implementation is too crude to be
taken to the library). Now we really need a pushrules processor to fill
this method with something reasonably good. Internally the library now
calls checkForNotifications() on every event added to the timeline,
filling up the events-to-notifications map because it is anticipated
that calculation of notifications can be rather resource-intensive and
should only be done once for a given event.
Finally, Room::notificationsFor is an accessor into the mentioned map,
standing next to isEventNotable (but unlike isEventNotable, it's not
virtual; checkForNotifications is).
|
|
This enumeration sees very limited (if any) use outside Quotient; and
though this change will surely break code using it the fix is very
straightforward and quick.
|
|
To simplify retrieval of the local m.read receipt and the marker for it.
|
|
[skip ci]
|
|
This reduces the surface interacting with the User class that eventually
will be split into LocalUser (most part) and RoomMember (a tiny wrapper
around the member data in a given room, used almost everywhere in Room
where User currently is).
Also: dropped a log message when the new receipt is at or behind
the old one as it causes a lot of noise in the logs.
|
|
|
|
These usually don't affect the room outlooks in the room list in any
way; so can be merged into OtherChange instead.
Also: OtherChanges synonym has been added, implying that there might
be more than one change behind a single "Other" flag.
|
|
It is useful to be able to get all of the state events that have
occured in a room, instead of needing to use Room::getCurrentState,
which filters based on the event type and state key.
|
|
|
|
See also https://bugreports.qt.io/browse/QTBUG-82295.
|
|
|
|
|
|
Add convenience function for activating encryption
|
|
Implement the mxc protocol in the NetworkAccessManager
|
|
EncryptionEvent constructor
|
|
The query is easier to manipulate; and the original mxc URL is not used
for the real network request anyway.
|
|
There was a mess with fileTransferCancelled(); it was only emitted when
a download (but not an upload) was cancelled; besides, in case of
downloads a file transfer info structure was getting deleted whereas
uploads left a file transfer in Cancelled status. This all now converges
on:
- fileTransferFailed() for both failures and cancellations (to simplify
slot connection, and also to follow the practice in, e.g., Qt Network).
- the file transfer info structure is kept around in Cancelled status,
following the logic used for failures. There's no particular cleanup
which may become a problem if one uploads and cancels many times
(download file transfers are keyed to event ids, mitigating
the problem); this will be fixed in another commit.
Closes #503. Closes #504.
|
|
This is useful for cases when the room display name is returned to QML
that doesn't have an equivalent of QString::toHtmlEscaped().
|
|
Room::memberJoinState() was only used to check if the user has joined
the room (it couldn't be used for anything else), meaning that its best
replacement is actually not memberState() but isMember() introduced
hereby. It's also better to pass user ids instead of User objects to
memberState() and isMember() since that is enough to check membership.
# Conflicts:
# lib/room.cpp
# lib/room.h
|
|
This still works with older moc yet produces actual warnings when
compiling C++ code.
|
|
hasUnreadMessages is derived from unreadCount; isFavourite/isLowPriority
effectively depend on tagNames.
|
|
[skip ci]
|
|
|
|
|
|
It's now possible to get receipts along with their timestamps by calling
Room::lastReadReceipt(). Together this new method, fullyReadMarker(),
and lastFullyReadEventId() deprecate readMarker() overloads and
readMarkerEventId() respectively.
lastFullyReadEventId is also a Q_PROPERTY (deprecating
readMarkerEventId); readMarkerMoved() signal is deprecated by
fullyReadMarkerMoved(), while readMarkerForUserMoved() is deprecated
in favour of existing lastReadEventChanged().
|
|
Namely memberCount(), localAliases(), remoteAliases(), timelineEdge().
|