libQuotient
A Qt library for building matrix clients
Loading...
Searching...
No Matches
room.h
Go to the documentation of this file.
1// SPDX-FileCopyrightText: 2016 Kitsune Ral <Kitsune-Ral@users.sf.net>
2// SPDX-FileCopyrightText: 2017 Roman Plášil <me@rplasil.name>
3// SPDX-FileCopyrightText: 2017 Marius Gripsgard <marius@ubports.com>
4// SPDX-FileCopyrightText: 2018 Josip Delic <delijati@googlemail.com>
5// SPDX-FileCopyrightText: 2018 Black Hat <bhat@encom.eu.org>
6// SPDX-FileCopyrightText: 2019 Alexey Andreyev <aa13q@ya.ru>
7// SPDX-FileCopyrightText: 2020 Ram Nad <ramnad1999@gmail.com>
8// SPDX-License-Identifier: LGPL-2.1-or-later
9
10#pragma once
11
12#include "connection.h"
13#include "roommember.h"
14#include "roomstateview.h"
15#include "eventitem.h"
17
18#include "csapi/message_pagination.h"
19
20#include "events/accountdataevents.h"
21#include "events/encryptedevent.h"
22#include "events/eventrelation.h"
23#include "events/roomcreateevent.h"
24#include "events/roomkeyevent.h"
25#include "events/roommessageevent.h"
26#include "events/roompowerlevelsevent.h"
27#include "events/roomtombstoneevent.h"
28#include "events/roomjoinrulesevent.h"
29
30#include <QtCore/QJsonObject>
31#include <QtGui/QImage>
32
33#include <deque>
34#include <utility>
35
36namespace Quotient {
37class Event;
38class Avatar;
39class SyncRoomData;
40class RoomMemberEvent;
41class User;
42class RoomMember;
43struct MemberSorter;
44class LeaveRoomJob;
45class SetRoomStateWithKeyJob;
46class RedactEventJob;
47class Thread;
48
49/** The data structure used to expose file transfer information to views
50 *
51 * This is specifically tuned to work with QML exposing all traits as
52 * Q_PROPERTY values.
53 */
65public:
68 bool isUpload = false;
69 int progress = 0;
70 int total = -1;
71 QUrl localDir {};
72 QUrl localPath {};
73
74 bool started() const { return status == Started; }
75 bool completed() const { return status == Completed; }
76 bool active() const { return started() || completed(); }
77 bool failed() const { return status == Failed; }
78};
79
80//! \brief Data structure for a room member's read receipt
81//! \sa Room::lastReadReceipt
86public:
89
90 bool operator==(const ReadReceipt& other) const
91 {
93 }
94 bool operator!=(const ReadReceipt& other) const
95 {
96 return !operator==(other);
97 }
98};
100{
103}
104
105struct EventStats;
106
108{
109 enum Type { None = 0, Basic, Highlight };
110 Q_ENUM(Type)
111
113
114private:
115 Q_GADGET
116 Q_PROPERTY(Type type MEMBER type CONSTANT)
117};
118
119class QUOTIENT_API Room : public QObject {
138 STORED false)
141
149
165 STORED false)
171
174
176
177public:
184
185 //! \brief Room changes that can be tracked using Room::changed() signal
186 //!
187 //! This enumeration lists kinds of changes that can be tracked with
188 //! a "cumulative" changed() signal instead of using individual signals for
189 //! each change. Specific enumerators mention these individual signals.
190 //! \sa changed
191 enum class Change : quint32 { // QFlags can't go more than 32-bit
192 None = 0x0, //!< No changes occurred in the room
193 RoomNames = 0x1, //!< \sa namesChanged, displaynameChanged
194 // NotInUse = 0x2,
195 Topic = 0x4, //!< \sa topicChanged
196 PartiallyReadStats = 0x8, //!< \sa partiallyReadStatsChanged
197 Avatar = 0x10, //!< \sa avatarChanged
198 JoinState = 0x20, //!< \sa joinStateChanged
199 Tags = 0x40, //!< \sa tagsChanged
200 //! \sa userAdded, userRemoved, memberRenamed, memberListChanged,
201 //! displaynameChanged
202 Members = 0x80,
203 UnreadStats = 0x100, //!< \sa unreadStatsChanged
204 // AccountData pre-0.9 = 0x200,
205 Summary = 0x400, //!< \sa summaryChanged, displaynameChanged
206 // ReadMarker pre-0.9 = 0x800,
207 Highlights = 0x1000, //!< \sa highlightCountChanged
208 //! A catch-all value that covers changes not listed above (such as
209 //! encryption turned on or the room having been upgraded), as well as
210 //! changes in the room state that the library is not aware of (e.g.,
211 //! custom state events) and m.read/m.fully_read position changes.
212 //! \sa encryptionChanged, upgraded, accountDataChanged
213 Other = 0x8000,
214 //! This is intended to test a Change/Changes value for non-emptiness;
215 //! adding <tt>& Change::Any</tt> has the same meaning as
216 //! !testFlag(Change::None) or adding <tt>!= Change::None</tt>
217 //! \note testFlag(Change::Any) tests that _all_ bits are on and
218 //! will always return false.
219 Any = 0xFFFF
220 };
222
226
227 // Property accessors
228
230
231 //! Get a RoomMember object for the local user.
233 const QString& id() const;
235 bool isUnstable() const;
237 /// Room predecessor
238 /** This function validates that the predecessor has a tombstone and
239 * the tombstone refers to the current room. If that's not the case,
240 * or if the predecessor is in a join state not matching \p stateFilter,
241 * the function returns nullptr.
242 */
244 | JoinState::Join) const;
246 /// Room successor
247 /** This function validates that the successor room's creation event
248 * refers to the current room. If that's not the case, or if the successor
249 * is in a join state not matching \p stateFilter, it returns nullptr.
250 */
252 | JoinState::Join) const;
253 QString name() const;
256 //! Get a list of both canonical and alternative aliases
260 // Returns events available locally, use pinnedEventIds() for full list
263 QString topic() const;
266 const Avatar& avatarObject() const;
268
269 int timelineSize() const;
270 bool usesEncryption() const;
273 const QString& senderId,
275 const QByteArray& senderKey,
276 const QByteArray& senderEdKey);
277 int joinedCount() const;
278 int invitedCount() const;
279 int totalMemberCount() const;
280
282
283 /**
284 * Returns a square room avatar with the given size and requests it
285 * from the network if needed
286 * \return a pixmap with the avatar or a placeholder if there's none
287 * available yet
288 */
290 /**
291 * Returns a room avatar with the given dimensions and requests it
292 * from the network if needed
293 * \return a pixmap with the avatar or a placeholder if there's none
294 * available yet
295 */
297
298 //! \brief Get a RoomMember object for the given user Matrix ID
299 //!
300 //! Will return a nullptr if there is no m.room.member event for the user in
301 //! the room so needs to be null checked.
302 //!
303 //! \note This can return a member in any state that is known to the room so
304 //! check the state (using RoomMember::membershipState()) before use.
306
307 //! Get a list of room members who have joined the room.
309
310 //! Get a list of all members known to the room.
312
313 //! Get a list of all members known to have left the room.
315
316 //! Get a list of room members who are currently sending a typing indicator.
318
319 //! \brief Get a list of room members who are currently sending a typing indicator.
320 //!
321 //! The local member is excluded from this list.
323
324 //! Get a list of room member Matrix IDs who have joined the room.
326
327 //! Get a list of all member Matrix IDs known to the room.
329
330 //! \brief Get Matrix IDs for room creator(s)
331 //!
332 //! As long as the create event for the room is known, the returned list will start with
333 //! MXID of the room creation event sender. For room versions 12 and newer, the returned list
334 //! will further include additional creators if there are any.
336
337 //! Whether the name for the given member should be disambiguated
339
340 //! \brief Check the join state of a given user in this room
341 //!
342 //! \return the given user's state with respect to the room
344
345 //! Check whether a user with the given id is a member of the room
346 Q_INVOKABLE bool isMember(const QString& userId) const;
347
349
350 //! \brief Get a avatar of the specified dimensions
351 //!
352 //! This always returns immediately; if there's no avatar cached yet, the call triggers
353 //! a network request, that will emit Room::memberAvatarUpdated() once completed.
354 //! \return a pixmap with the avatar or a placeholder if there's none available yet
356
357 //! \brief Get a square avatar of the specified size
358 //!
359 //! This is an overload for the case when the needed width and height are equal.
361
362 const Timeline& messageEvents() const;
364
365 //! \brief Get the number of requested historical events
366 //! \return The number of requested events if there's a pending request; 0 otherwise
368
369 //! Check whether all historical messages are already loaded
370 //! \return true if the "oldest" event in the timeline is a room creation event and there's
371 //! no further history to load; false otherwise
372 bool allHistoryLoaded() const;
373
374 //! \brief Get a reverse iterator at the position before the "oldest" event
375 //!
376 //! Same as messageEvents().crend()
378
379 const ThreadView& threads() const;
380
381 //! \brief Get an iterator for the position beyond the latest arrived event
382 //!
383 //! Same as messageEvents().cend()
388
393
398
399 const RoomCreateEvent* creation() const;
401
402 bool displayed() const;
403 /// Mark the room as currently displayed to the user
404 /**
405 * Marking the room displayed causes the room to obtain the full
406 * list of members if it's been lazy-loaded before; in the future
407 * it may do more things bound to "screen time" of the room, e.g.
408 * measure that "screen time".
409 */
410 void setDisplayed(bool displayed = true);
419
420 //! \brief Get the latest read receipt from a user
421 //!
422 //! The user id must be valid. A read receipt with an empty event id
423 //! is returned if the user id is valid but there was no read receipt
424 //! from them.
425 //! \sa usersAtEventId
427
428 //! \brief Get the latest read receipt from the local user
429 //!
430 //! This is a shortcut for <tt>lastReadReceipt(localUserId)</tt>.
431 //! \sa lastReadReceipt
433
434 //! \brief Find the timeline item the local read receipt is at
435 //!
436 //! This is a shortcut for \code
437 //! room->findInTimeline(room->lastLocalReadReceipt().eventId);
438 //! \endcode
440
441 //! \brief Get the latest event id marked as fully read
442 //!
443 //! This can be either the event id pointed to by the actual latest
444 //! m.fully_read event, or the latest event id marked locally as fully read
445 //! if markMessagesAsRead or markAllMessagesAsRead has been called and
446 //! the homeserver didn't return an updated m.fully_read event yet.
447 //! \sa markMessagesAsRead, markAllMessagesAsRead, fullyReadMarker
449
450 //! \brief Get the iterator to the latest timeline item marked as fully read
451 //!
452 //! This method calls findInTimeline on the result of lastFullyReadEventId.
453 //! If the fully read marker turns out to be outside the timeline (because
454 //! the event marked as fully read is too far back in the history) the
455 //! returned value will be equal to historyEdge.
456 //!
457 //! Be sure to read the caveats on iterators returned by findInTimeline.
458 //! \sa lastFullyReadEventId, findInTimeline
460
461 //! \brief Get users whose latest read receipts point to the event
462 //!
463 //! This method is for cases when you need to show users who have read
464 //! an event. Calling it on inexistent or empty event id will return
465 //! an empty set.
466 //! \note The returned list may contain ids resolving to users that are
467 //! not loaded as room members yet (in particular, if members are not
468 //! yet lazy-loaded). For now this merely means that the user's
469 //! room-specific name and avatar will not be there; but generally
470 //! it's recommended to ensure that all room members are loaded
471 //! before operating on the result of this function.
472 //! \sa lastReadReceipt, allMembersLoaded
474
475 //! \brief Mark the event with uptoEventId as fully read
476 //!
477 //! Marks the event with the specified id as fully read locally and also
478 //! sends an update to m.fully_read account data to the server either
479 //! for this message or, if it's from the local user, for
480 //! the nearest non-local message before. uptoEventId must point to a known
481 //! event in the timeline; the method will do nothing if the event is behind
482 //! the current m.fully_read marker or is not loaded, to prevent
483 //! accidentally trying to move the marker back in the timeline.
484 //! \sa markAllMessagesAsRead, fullyReadMarker
486
487 //! \brief Determine whether an event should be counted as unread
488 //!
489 //! The criteria of including an event in unread counters are described in
490 //! [MSC2654](https://github.com/matrix-org/matrix-doc/pull/2654); according
491 //! to these, the event should be counted as unread (or, in libQuotient
492 //! parlance, is "notable") if it is:
493 //! - either
494 //! - a message event that is not m.notice, or
495 //! - a state event with type being one of:
496 //! `m.room.topic`, `m.room.name`, `m.room.avatar`, `m.room.tombstone`;
497 //! - neither redacted, nor an edit (redactions cause the redacted event
498 //! to stop being notable, while edits are not notable themselves while
499 //! the original event usually is);
500 //! - from a non-local user (events from other devices of the local
501 //! user are not notable).
502 //! \sa partiallyReadStats, unreadStats
503 virtual bool isEventNotable(const TimelineItem& ti) const;
504
505 //! \brief Get notification details for an event
506 //!
507 //! This allows to get details on the kind of notification that should
508 //! generated for \p evt.
510
511 //! \brief Get event statistics since the fully read marker
512 //!
513 //! This call returns a structure containing:
514 //! - the number of notable unread events since the fully read marker;
515 //! depending on the fully read marker state with respect to the local
516 //! timeline, this number may be either exact or estimated
517 //! (see EventStats::isEstimate);
518 //! - the number of highlights (TODO).
519 //!
520 //! Note that this is different from the unread count defined by MSC2654
521 //! and from the notification/highlight numbers defined by the spec in that
522 //! it counts events since the fully read marker, not since the last
523 //! read receipt position.
524 //!
525 //! As E2EE is not supported in the library, the returned result will always
526 //! be an estimate (<tt>isEstimate == true</tt>) for encrypted rooms;
527 //! moreover, since the library doesn't know how to tackle push rules yet
528 //! the number of highlights returned here will always be zero (there's no
529 //! good substitute for that now).
530 //!
531 //! \sa isEventNotable, fullyReadMarker, unreadStats, EventStats
533
534 //! \brief Get event statistics since the last read receipt
535 //!
536 //! This call returns a structure that contains the following three numbers,
537 //! all counted on the timeline segment between the event pointed to by
538 //! the m.fully_read marker and the sync edge:
539 //! - the number of unread events - depending on the read receipt state
540 //! with respect to the local timeline, this number may be either precise
541 //! or estimated (see EventStats::isEstimate);
542 //! - the number of highlights (TODO).
543 //!
544 //! As E2EE is not supported in the library, the returned result will always
545 //! be an estimate (<tt>isEstimate == true</tt>) for encrypted rooms;
546 //! moreover, since the library doesn't know how to tackle push rules yet
547 //! the number of highlights returned here will always be zero - use
548 //! highlightCount() for now.
549 //!
550 //! \sa isEventNotable, lastLocalReadReceipt, partiallyReadStats,
551 //! highlightCount
553
554 //! \brief Get the number of notifications since the last read receipt
555 //!
556 //! This is the same as <tt>unreadStats().notableCount</tt>.
557 //!
558 //! \sa unreadStats, lastLocalReadReceipt
560
561 //! \brief Get the number of highlights since the last read receipt
562 //!
563 //! As of 0.7, this is defined by the homeserver as Quotient doesn't process
564 //! push rules.
565 //!
566 //! \sa unreadStats, lastLocalReadReceipt
568
569 /** Check whether the room has account data of the given type
570 * Tags and read markers are not supported by this method _yet_.
571 */
572 bool hasAccountData(const QString& type) const;
573
574 /** Get a generic account data event of the given type
575 * This returns a generic hash map for any room account data event
576 * stored on the server. Tags and read markers cannot be retrieved
577 * using this method _yet_.
578 */
579 const EventPtr& accountData(const QString& type) const;
580
581 //! Get a list of all room account data events
582 //! \return A list of event types that exist in the room
584
586 TagsMap tags() const;
587 Tag tag(const QString& name) const;
588
589 /** Add a new tag to this room
590 * If this room already has this tag, nothing happens. If it's a new
591 * tag for the room, the respective tag record is added to the set
592 * of tags and the new set is sent to the server to update other
593 * clients.
594 */
595 void addTag(const QString& name, const Tag& tagData = {});
596 Q_INVOKABLE void addTag(const QString& name, float order);
597
598 /// Remove a tag from the room
600
601 /// The scope to apply an action on
602 /*! This enumeration is used to pick a strategy to propagate certain
603 * actions on the room to its predecessors and successors.
604 */
606 ThisRoomOnly, ///< Do not apply to predecessors and successors
607 WithinSameState, ///< Apply to predecessors and successors in the same
608 ///< state as the current one
609 OmitLeftState, ///< Apply to all reachable predecessors and successors
610 ///< except those in Leave state
611 WholeSequence ///< Apply to all reachable predecessors and successors
612 };
613
614 /** Overwrite the room's tags
615 * This completely replaces the existing room's tags with a set
616 * of new ones and updates the new set on the server. Unlike
617 * most other methods in Room, this one sends a signal about changes
618 * immediately, not waiting for confirmation from the server
619 * (because tags are saved in account data rather than in shared
620 * room state).
621 * \param applyOn setting this to Room::OnAllConversations will set tags
622 * on this and all _known_ predecessors and successors;
623 * by default only the current room is changed
624 */
626
627 /// Check whether the list of tags has m.favourite
628 bool isFavourite() const;
629 /// Check whether the list of tags has m.lowpriority
630 bool isLowPriority() const;
631 /// Check whether this room is for server notices (MSC1452)
632 bool isServerNoticeRoom() const;
633
634 /// Check whether this room is a direct chat
636
637 /// Get the list of members this room is a direct chat with
639
641 const QUrl &mxcUrl) const;
642
645
646 /// Get a file name for downloading for a given event id
647 /*!
648 * The event MUST be RoomMessageEvent and have content
649 * for downloading. \sa RoomMessageEvent::hasContent
650 */
652
653 /// Get information on file upload/download
654 /*!
655 * \param id uploads are identified by the corresponding event's
656 * transactionId (because uploads are done before
657 * the event is even sent), while downloads are using
658 * the normal event id for identifier.
659 */
662
663 /// Get the URL to the actual file source in a unified way
664 /*!
665 * For uploads it will return a URL to a local file; for downloads
666 * the URL will be taken from the corresponding room event.
667 */
669
670 /** Pretty-prints plain text into HTML
671 * As of now, it's exactly the same as Quotient::prettyPrint();
672 * in the future, it will also linkify room aliases, mxids etc.
673 * using the room context.
674 */
676
678
679 /// Whether the current user is allowed to upgrade the room
681
682 /// \brief Get the current room state
684
685 //! \brief The current Join Rule for the room
686 //!
687 //! \sa https://spec.matrix.org/latest/client-server-api/#mroomjoin_rules
689
690 //! \brief Set the Join Rule for the room
691 //!
692 //! If the local user does not have a high enough power level the request is rejected.
693 //!
694 //! \param newRule the new JoinRule to apply to the room
695 //! \param allowedRooms only required when the join rule is restricted. This is a
696 //! list of room IDs that members of can join without an invite.
697 //! If the rule is restricted and this list is empty it is treated as a join
698 //! rule of invite instead.
699 //!
700 //! \note While any room ID is permitted it is designed to be only spaces that are
701 //! input. I.e. only memebers of space `x` can join this room.
702 //!
703 //! \sa https://spec.matrix.org/latest/client-server-api/#mroomjoin_rules
705
706 //! \brief The list of Room IDs for when the join rule is Restricted
707 //!
708 //! This value will be empty when the Join Rule is not Restricted or
709 //! Knock-Restricted.
710 //!
711 //! \sa https://spec.matrix.org/latest/client-server-api/#mroomjoin_rules
713
714 //! \brief The effective power level of the given member in the room
715 //!
716 //! This is normally the same as calling `RoomPowerLevelEvent::powerLevelForUser(userId)` but
717 //! takes into account the room context and works even if the room state has no power levels
718 //! event. It is THE recommended way to get a room member's power level to display in the UI.
719 //! \param memberId The room member ID to check; if empty, the local user will be checked
720 //! \sa RoomPowerLevelsEvent, https://spec.matrix.org/v1.11/client-server-api/#mroompower_levels
722
723 //! \brief Get the power level required to send events of the given type
724 //!
725 //! \note This is a generic method that only gets the power level to send events with a given
726 //! type. Some operations have additional restrictions or enablers though: e.g.,
727 //! room member changes (kicks, invites) have special power levels; on the other hand,
728 //! redactions of one's own messages are allowed regardless of the power level.
729 //! The library has no method to check effective ability to perform an operation as yet;
730 //! you have to either blindly make a call to the homeserver or implement the logic
731 //! described in the Federation API and respective room versions, in the client code.
732 //! \note Unlike the template version below, this method determines at runtime whether an event
733 //! type is that of a state event, assuming unknown event types to be non-state; pass
734 //! `true` as the second parameter to override that.
735 //! \sa canSwitchVersions
737
738 //! \brief Get the power level required to send events of the given type
739 //!
740 //! This is an optimised version of non-template powerLevelFor() (with the same caveat about
741 //! operations based on some event types) for cases when the event type is known at build time.
742 //! \tparam EvT the event type to get the power level for
743 template <EventClass EvT>
744 int powerLevelFor() const
745 {
747 }
748
749 //! \brief Post a pre-created room message event
750 //!
751 //! Takes ownership of the event, deleting it once the matching one arrives with the sync.
752 //! \note Do not assume that the event is already on the road to the homeserver when this (or
753 //! any other `post*`) method returns; it can be queued internally.
754 //! \sa PendingEventItem::deliveryStatus()
755 //! \return a reference to the pending event item
757
758 template <typename EvT, typename... ArgTs>
760 {
761 return post(makeEvent<EvT>(std::forward<ArgTs>(args)...));
762 }
763
764 //! \brief Send a text type message
765 //!
766 //! This means MessageEventType Text, Emote or Notice.
769 const std::optional<QString>& html = std::nullopt,
771 {
772 static_assert(type == MessageEventType::Text ||
775 "MessageEvent type is not a text message"
776 );
777
779 if (html) {
780 content = std::make_unique<EventContent::TextContent>(*html, u"text/html"_s);
781 }
783 }
784
785 //! Send a file with the given content
789
790 //! Send the given Json as a message
792
793 //! Send a reaction on a given event with a given key
795
797
798 //! Send a request to update the room state with the given event
800
801 //! \brief Set a state event of the given type with the given arguments
802 //!
803 //! This type-safe overload attempts to send a state event of the type \p EvT constructed from
804 //! \p args.
805 template <typename EvT, typename... ArgTs>
806 auto setState(ArgTs&&... args)
807 {
808 return setState(EvT(std::forward<ArgTs>(args)...));
809 }
810
812
814
816
817 //! \brief Upgrade the room to \p newVersion
818 //!
819 //! Triggers an upgrade process that puts the tombstone event on the current room and creates
820 //! a new room of the specified version. It is possible to specify \p additionalCreators for
821 //! room versions that support those (unfortunately it is only possible to find out whether
822 //! a given room version supports additional creators by attempting to upgrade a room).
823 //! \return a future eventually holding a new room once it arrives via sync
825 const QStringList& additionalCreators = {});
826
827public Q_SLOTS:
828 /** Check whether the room should be upgraded */
830
833
834 //! Send a request to update the room state based on freeform inputs
836 const QString& stateKey,
837 const QJsonObject& contentJson);
838 void setName(const QString& newName);
841 /// Set room aliases on the user's current server
844
845 /// You shouldn't normally call this method; it's here for debugging
847
849
852 void kickMember(const QString& memberId, const QString& reason = {});
853 void ban(const QString& userId, const QString& reason = {});
854 void unban(const QString& userId);
855 void redactEvent(const QString& eventId, const QString& reason = {});
856
858 const QString& overrideContentType = {});
859 // If localFilename is empty a temporary file is created
860 void downloadFile(const QString& eventId, const QUrl& localFilename = {});
862
863 //! \brief Set a given event as last read and post a read receipt on it
864 //!
865 //! Does nothing if the event is behind the current read receipt.
866 //! \sa lastReadReceipt, markMessagesAsRead, markAllMessagesAsRead
868 //! Put the fully-read marker at the latest message in the room
870
871 //! Switch the room's version (aka upgrade)
872 [[deprecated("Use upgrade() instead")]]
874
875 void inviteCall(const QString& callId, const int lifetime,
876 const QString& sdp);
878 void answerCall(const QString& callId, const QString& sdp);
880
881 /**
882 * Activates encryption for this room.
883 * Warning: Cannot be undone
884 */
886
888 /// Initial set of state events has been loaded
889 /**
890 * The initial set is what comes from the initial sync for the room.
891 * This includes all basic things like RoomCreateEvent,
892 * RoomNameEvent, a (lazy-loaded, not full) set of RoomMemberEvents
893 * etc. This is a per-room reflection of Connection::loadedRoomState
894 * \sa Connection::loadedRoomState
895 */
901 /// The event is about to be appended to the list of pending events
903 /// An event has been appended to the list of pending events
905 /// The remote echo has arrived with the sync and will be merged
906 /// with its local counterpart
907 /** NB: Requires a sync loop to be emitted */
910 /// The remote and local copies of the event have been merged
911 /** NB: Requires a sync loop to be emitted */
913 /// An event will be removed from the list of pending events
915 /// An event has just been removed from the list of pending events
917 /// The status of a pending event has changed
918 /** \sa PendingEventItem::deliveryStatus */
920 /// The server accepted the message
921 /** This is emitted when an event sending request has successfully
922 * completed. This does not mean that the event is already in the
923 * local timeline, only that the server has accepted it.
924 * \param txnId transaction id assigned by the client during sending
925 * \param eventId event id assigned by the server upon acceptance
926 * \sa postEvent, postPlainText, postMessage, postHtmlMessage
927 * \sa pendingEventMerged, aboutToAddNewMessages
928 */
930
931 //! A new thread has been created/added in the room
933
934 /** A common signal for various kinds of changes in the room
935 * Aside from all changes in the room state
936 * @param changes a set of flags describing what changes occurred
937 * upon the last sync
938 * \sa Changes
939 */
941 /**
942 * \brief The room name, the canonical alias or other aliases changed
943 *
944 * Not triggered when display name changes.
945 */
952
953 //! \brief The join rule for the room has changed
955
956 //! \brief A new member has joined the room
957 //!
958 //! This can be from any previous state or a member previously unknown to
959 //! the room.
961
962 //! \brief A member who previously joined has left
963 //!
964 //! The member will still be known to the room their membership state has changed
965 //! from Membership::Join to anything else.
967
968 //! A known joined member is about to update their display name
970
971 //! A known joined member has updated their display name
973
974 //! A known joined member has updated their avatar
976
977 /// The list of members has changed
978 /** Emitted no more than once per sync, this is a good signal to
979 * for cases when some action should be done upon any change in
980 * the member list. If you need per-item granularity you should use
981 * userAdded, userRemoved and memberAboutToRename / memberRenamed
982 * instead.
983 */
985
986 /// The previously lazy-loaded members list is now loaded entirely
987 /// \sa setDisplayed
990
993
994 //! The list of members sending typing indicators has changed.
996
997 void highlightCountChanged(); ///< \sa highlightCount
998 void notificationCountChanged(); ///< \sa notificationCount
999
1003 //! The event the m.read receipt points to has changed for the listed users
1004 //! \sa lastReadReceipt
1010
1015
1018 const Quotient::RoomEvent* oldEvent);
1019
1025 // fileTransferCancelled() is no more here; use fileTransferFailed() and
1026 // check the transfer status instead
1027
1029
1030 /// The room's version stability may have changed
1033 /// This room has been upgraded and won't receive updates any more
1035 /// An attempted room upgrade has failed
1037
1038 /// The room is about to be deleted
1040
1041protected:
1045 virtual void onAddNewTimelineEvents(timeline_iter_t /*from*/) {}
1047 virtual void onRedaction(const RoomEvent& /*prevEvent*/,
1048 const RoomEvent& /*after*/)
1049 {}
1050 virtual QJsonObject toJson() const;
1051 virtual void updateData(SyncRoomData&& data, bool fromCache = false);
1053
1054private:
1055 friend class Connection;
1056
1057 class Private;
1058 Private* d;
1059
1060 // This is called from Connection, reflecting a state change that
1061 // arrived from the server. Clients should use
1062 // Connection::joinRoom() and Room::leaveRoom() to change the state.
1064};
1065
1066template <template <class> class ContT>
1067inline typename ContT<RoomMember>::size_type lowerBoundMemberIndex(const ContT<RoomMember>& c,
1068 const auto& v,
1069 MemberSorter ms = {})
1070{
1071 return std::ranges::lower_bound(c, v, ms) - c.begin();
1072}
1073
1074template <template <class> class ContT>
1075inline typename ContT<QString>::size_type lowerBoundMemberIndex(const ContT<QString>& c,
1076 const auto& v, const Room* r,
1077 MemberSorter ms = {})
1078{
1079 return std::ranges::lower_bound(c, v, ms, std::bind_front(&Room::member, r)) - c.begin();
1080}
1081
1082} // namespace Quotient
1084Q_DECLARE_METATYPE(Quotient::ReadReceipt)
1085Q_DECLARE_OPERATORS_FOR_FLAGS(Quotient::Room::Changes)
Data structure for a room member's read receipt.
Definition room.h:82
void swap(ReadReceipt &lhs, ReadReceipt &rhs)
Definition room.h:99
ContT< QString >::size_type lowerBoundMemberIndex(const ContT< QString > &c, const auto &v, const Room *r, MemberSorter ms={})
Definition room.h:1075
ContT< RoomMember >::size_type lowerBoundMemberIndex(const ContT< RoomMember > &c, const auto &v, MemberSorter ms={})
Definition room.h:1067
#define QUO_DECLARE_FLAGS(Flags, Enum)
Quotient replacement for the Q_FLAG/Q_DECLARE_FLAGS combination.
#define QUOTIENT_API