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
29#include <QtCore/QJsonObject>
30#include <QtGui/QImage>
31
32#include <deque>
33#include <utility>
34
35namespace Quotient {
36class Event;
37class Avatar;
38class SyncRoomData;
39class RoomMemberEvent;
40class User;
41class RoomMember;
42struct MemberSorter;
43class LeaveRoomJob;
44class SetRoomStateWithKeyJob;
45class RedactEventJob;
46class Thread;
47
48/** The data structure used to expose file transfer information to views
49 *
50 * This is specifically tuned to work with QML exposing all traits as
51 * Q_PROPERTY values.
52 */
64public:
67 bool isUpload = false;
68 int progress = 0;
69 int total = -1;
70 QUrl localDir {};
71 QUrl localPath {};
72
73 bool started() const { return status == Started; }
74 bool completed() const { return status == Completed; }
75 bool active() const { return started() || completed(); }
76 bool failed() const { return status == Failed; }
77};
78
79//! \brief Data structure for a room member's read receipt
80//! \sa Room::lastReadReceipt
85public:
88
89 bool operator==(const ReadReceipt& other) const
90 {
92 }
93 bool operator!=(const ReadReceipt& other) const
94 {
95 return !operator==(other);
96 }
97};
99{
102}
103
104struct EventStats;
105
107{
108 enum Type { None = 0, Basic, Highlight };
109 Q_ENUM(Type)
110
112
113private:
114 Q_GADGET
115 Q_PROPERTY(Type type MEMBER type CONSTANT)
116};
117
118class QUOTIENT_API Room : public QObject {
137 STORED false)
140
148
164 STORED false)
168
171
173
174public:
181
182 //! \brief Room changes that can be tracked using Room::changed() signal
183 //!
184 //! This enumeration lists kinds of changes that can be tracked with
185 //! a "cumulative" changed() signal instead of using individual signals for
186 //! each change. Specific enumerators mention these individual signals.
187 //! \sa changed
188 enum class Change : quint32 { // QFlags can't go more than 32-bit
189 None = 0x0, //!< No changes occurred in the room
190 RoomNames = 0x1, //!< \sa namesChanged, displaynameChanged
191 // NotInUse = 0x2,
192 Topic = 0x4, //!< \sa topicChanged
193 PartiallyReadStats = 0x8, //!< \sa partiallyReadStatsChanged
194 Avatar = 0x10, //!< \sa avatarChanged
195 JoinState = 0x20, //!< \sa joinStateChanged
196 Tags = 0x40, //!< \sa tagsChanged
197 //! \sa userAdded, userRemoved, memberRenamed, memberListChanged,
198 //! displaynameChanged
199 Members = 0x80,
200 UnreadStats = 0x100, //!< \sa unreadStatsChanged
201 // AccountData pre-0.9 = 0x200,
202 Summary = 0x400, //!< \sa summaryChanged, displaynameChanged
203 // ReadMarker pre-0.9 = 0x800,
204 Highlights = 0x1000, //!< \sa highlightCountChanged
205 //! A catch-all value that covers changes not listed above (such as
206 //! encryption turned on or the room having been upgraded), as well as
207 //! changes in the room state that the library is not aware of (e.g.,
208 //! custom state events) and m.read/m.fully_read position changes.
209 //! \sa encryptionChanged, upgraded, accountDataChanged
210 Other = 0x8000,
211 //! This is intended to test a Change/Changes value for non-emptiness;
212 //! adding <tt>& Change::Any</tt> has the same meaning as
213 //! !testFlag(Change::None) or adding <tt>!= Change::None</tt>
214 //! \note testFlag(Change::Any) tests that _all_ bits are on and
215 //! will always return false.
216 Any = 0xFFFF
217 };
219
223
224 // Property accessors
225
227
228 //! Get a RoomMember object for the local user.
230 const QString& id() const;
232 bool isUnstable() const;
234 /// Room predecessor
235 /** This function validates that the predecessor has a tombstone and
236 * the tombstone refers to the current room. If that's not the case,
237 * or if the predecessor is in a join state not matching \p stateFilter,
238 * the function returns nullptr.
239 */
241 | JoinState::Join) const;
243 /// Room successor
244 /** This function validates that the successor room's creation event
245 * refers to the current room. If that's not the case, or if the successor
246 * is in a join state not matching \p stateFilter, it returns nullptr.
247 */
249 | JoinState::Join) const;
250 QString name() const;
253 //! Get a list of both canonical and alternative aliases
257 // Returns events available locally, use pinnedEventIds() for full list
260 QString topic() const;
263 const Avatar& avatarObject() const;
265
266 int timelineSize() const;
267 bool usesEncryption() const;
270 const QString& senderId,
272 const QByteArray& senderKey,
273 const QByteArray& senderEdKey);
274 int joinedCount() const;
275 int invitedCount() const;
276 int totalMemberCount() const;
277
279
280 /**
281 * Returns a square room avatar with the given size and requests it
282 * from the network if needed
283 * \return a pixmap with the avatar or a placeholder if there's none
284 * available yet
285 */
287 /**
288 * Returns a room avatar with the given dimensions and requests it
289 * from the network if needed
290 * \return a pixmap with the avatar or a placeholder if there's none
291 * available yet
292 */
294
295 //! \brief Get a RoomMember object for the given user Matrix ID
296 //!
297 //! Will return a nullptr if there is no m.room.member event for the user in
298 //! the room so needs to be null checked.
299 //!
300 //! \note This can return a member in any state that is known to the room so
301 //! check the state (using RoomMember::membershipState()) before use.
303
304 //! Get a list of room members who have joined the room.
306
307 //! Get a list of all members known to the room.
309
310 //! Get a list of all members known to have left the room.
312
313 //! Get a list of room members who are currently sending a typing indicator.
315
316 //! \brief Get a list of room members who are currently sending a typing indicator.
317 //!
318 //! The local member is excluded from this list.
320
321 //! Get a list of room member Matrix IDs who have joined the room.
323
324 //! Get a list of all member Matrix IDs known to the room.
326
327 //! Whether the name for the given member should be disambiguated
329
330 //! \brief Check the join state of a given user in this room
331 //!
332 //! \return the given user's state with respect to the room
334
335 //! Check whether a user with the given id is a member of the room
336 Q_INVOKABLE bool isMember(const QString& userId) const;
337
339
340 //! \brief Get a avatar of the specified dimensions
341 //!
342 //! This always returns immediately; if there's no avatar cached yet, the call triggers
343 //! a network request, that will emit Room::memberAvatarUpdated() once completed.
344 //! \return a pixmap with the avatar or a placeholder if there's none available yet
346
347 //! \brief Get a square avatar of the specified size
348 //!
349 //! This is an overload for the case when the needed width and height are equal.
351
352 const Timeline& messageEvents() const;
354
355 //! \brief Get the number of requested historical events
356 //! \return The number of requested events if there's a pending request; 0 otherwise
358
359 //! Check whether all historical messages are already loaded
360 //! \return true if the "oldest" event in the timeline is a room creation event and there's
361 //! no further history to load; false otherwise
362 bool allHistoryLoaded() const;
363
364 //! \brief Get a reverse iterator at the position before the "oldest" event
365 //!
366 //! Same as messageEvents().crend()
368
369 const ThreadView& threads() const;
370
371 //! \brief Get an iterator for the position beyond the latest arrived event
372 //!
373 //! Same as messageEvents().cend()
378
383
388
389 const RoomCreateEvent* creation() const;
391
392 bool displayed() const;
393 /// Mark the room as currently displayed to the user
394 /**
395 * Marking the room displayed causes the room to obtain the full
396 * list of members if it's been lazy-loaded before; in the future
397 * it may do more things bound to "screen time" of the room, e.g.
398 * measure that "screen time".
399 */
400 void setDisplayed(bool displayed = true);
409
410 //! \brief Get the latest read receipt from a user
411 //!
412 //! The user id must be valid. A read receipt with an empty event id
413 //! is returned if the user id is valid but there was no read receipt
414 //! from them.
415 //! \sa usersAtEventId
417
418 //! \brief Get the latest read receipt from the local user
419 //!
420 //! This is a shortcut for <tt>lastReadReceipt(localUserId)</tt>.
421 //! \sa lastReadReceipt
423
424 //! \brief Find the timeline item the local read receipt is at
425 //!
426 //! This is a shortcut for \code
427 //! room->findInTimeline(room->lastLocalReadReceipt().eventId);
428 //! \endcode
430
431 //! \brief Get the latest event id marked as fully read
432 //!
433 //! This can be either the event id pointed to by the actual latest
434 //! m.fully_read event, or the latest event id marked locally as fully read
435 //! if markMessagesAsRead or markAllMessagesAsRead has been called and
436 //! the homeserver didn't return an updated m.fully_read event yet.
437 //! \sa markMessagesAsRead, markAllMessagesAsRead, fullyReadMarker
439
440 //! \brief Get the iterator to the latest timeline item marked as fully read
441 //!
442 //! This method calls findInTimeline on the result of lastFullyReadEventId.
443 //! If the fully read marker turns out to be outside the timeline (because
444 //! the event marked as fully read is too far back in the history) the
445 //! returned value will be equal to historyEdge.
446 //!
447 //! Be sure to read the caveats on iterators returned by findInTimeline.
448 //! \sa lastFullyReadEventId, findInTimeline
450
451 //! \brief Get users whose latest read receipts point to the event
452 //!
453 //! This method is for cases when you need to show users who have read
454 //! an event. Calling it on inexistent or empty event id will return
455 //! an empty set.
456 //! \note The returned list may contain ids resolving to users that are
457 //! not loaded as room members yet (in particular, if members are not
458 //! yet lazy-loaded). For now this merely means that the user's
459 //! room-specific name and avatar will not be there; but generally
460 //! it's recommended to ensure that all room members are loaded
461 //! before operating on the result of this function.
462 //! \sa lastReadReceipt, allMembersLoaded
464
465 //! \brief Mark the event with uptoEventId as fully read
466 //!
467 //! Marks the event with the specified id as fully read locally and also
468 //! sends an update to m.fully_read account data to the server either
469 //! for this message or, if it's from the local user, for
470 //! the nearest non-local message before. uptoEventId must point to a known
471 //! event in the timeline; the method will do nothing if the event is behind
472 //! the current m.fully_read marker or is not loaded, to prevent
473 //! accidentally trying to move the marker back in the timeline.
474 //! \sa markAllMessagesAsRead, fullyReadMarker
476
477 //! \brief Determine whether an event should be counted as unread
478 //!
479 //! The criteria of including an event in unread counters are described in
480 //! [MSC2654](https://github.com/matrix-org/matrix-doc/pull/2654); according
481 //! to these, the event should be counted as unread (or, in libQuotient
482 //! parlance, is "notable") if it is:
483 //! - either
484 //! - a message event that is not m.notice, or
485 //! - a state event with type being one of:
486 //! `m.room.topic`, `m.room.name`, `m.room.avatar`, `m.room.tombstone`;
487 //! - neither redacted, nor an edit (redactions cause the redacted event
488 //! to stop being notable, while edits are not notable themselves while
489 //! the original event usually is);
490 //! - from a non-local user (events from other devices of the local
491 //! user are not notable).
492 //! \sa partiallyReadStats, unreadStats
493 virtual bool isEventNotable(const TimelineItem& ti) const;
494
495 //! \brief Get notification details for an event
496 //!
497 //! This allows to get details on the kind of notification that should
498 //! generated for \p evt.
500
501 //! \brief Get event statistics since the fully read marker
502 //!
503 //! This call returns a structure containing:
504 //! - the number of notable unread events since the fully read marker;
505 //! depending on the fully read marker state with respect to the local
506 //! timeline, this number may be either exact or estimated
507 //! (see EventStats::isEstimate);
508 //! - the number of highlights (TODO).
509 //!
510 //! Note that this is different from the unread count defined by MSC2654
511 //! and from the notification/highlight numbers defined by the spec in that
512 //! it counts events since the fully read marker, not since the last
513 //! read receipt position.
514 //!
515 //! As E2EE is not supported in the library, the returned result will always
516 //! be an estimate (<tt>isEstimate == true</tt>) for encrypted rooms;
517 //! moreover, since the library doesn't know how to tackle push rules yet
518 //! the number of highlights returned here will always be zero (there's no
519 //! good substitute for that now).
520 //!
521 //! \sa isEventNotable, fullyReadMarker, unreadStats, EventStats
523
524 //! \brief Get event statistics since the last read receipt
525 //!
526 //! This call returns a structure that contains the following three numbers,
527 //! all counted on the timeline segment between the event pointed to by
528 //! the m.fully_read marker and the sync edge:
529 //! - the number of unread events - depending on the read receipt state
530 //! with respect to the local timeline, this number may be either precise
531 //! or estimated (see EventStats::isEstimate);
532 //! - the number of highlights (TODO).
533 //!
534 //! As E2EE is not supported in the library, the returned result will always
535 //! be an estimate (<tt>isEstimate == true</tt>) for encrypted rooms;
536 //! moreover, since the library doesn't know how to tackle push rules yet
537 //! the number of highlights returned here will always be zero - use
538 //! highlightCount() for now.
539 //!
540 //! \sa isEventNotable, lastLocalReadReceipt, partiallyReadStats,
541 //! highlightCount
543
544 //! \brief Get the number of notifications since the last read receipt
545 //!
546 //! This is the same as <tt>unreadStats().notableCount</tt>.
547 //!
548 //! \sa unreadStats, lastLocalReadReceipt
550
551 //! \brief Get the number of highlights since the last read receipt
552 //!
553 //! As of 0.7, this is defined by the homeserver as Quotient doesn't process
554 //! push rules.
555 //!
556 //! \sa unreadStats, lastLocalReadReceipt
558
559 /** Check whether the room has account data of the given type
560 * Tags and read markers are not supported by this method _yet_.
561 */
562 bool hasAccountData(const QString& type) const;
563
564 /** Get a generic account data event of the given type
565 * This returns a generic hash map for any room account data event
566 * stored on the server. Tags and read markers cannot be retrieved
567 * using this method _yet_.
568 */
569 const EventPtr& accountData(const QString& type) const;
570
571 //! Get a list of all room account data events
572 //! \return A list of event types that exist in the room
574
576 TagsMap tags() const;
577 Tag tag(const QString& name) const;
578
579 /** Add a new tag to this room
580 * If this room already has this tag, nothing happens. If it's a new
581 * tag for the room, the respective tag record is added to the set
582 * of tags and the new set is sent to the server to update other
583 * clients.
584 */
585 void addTag(const QString& name, const Tag& tagData = {});
586 Q_INVOKABLE void addTag(const QString& name, float order);
587
588 /// Remove a tag from the room
590
591 /// The scope to apply an action on
592 /*! This enumeration is used to pick a strategy to propagate certain
593 * actions on the room to its predecessors and successors.
594 */
596 ThisRoomOnly, ///< Do not apply to predecessors and successors
597 WithinSameState, ///< Apply to predecessors and successors in the same
598 ///< state as the current one
599 OmitLeftState, ///< Apply to all reachable predecessors and successors
600 ///< except those in Leave state
601 WholeSequence ///< Apply to all reachable predecessors and successors
602 };
603
604 /** Overwrite the room's tags
605 * This completely replaces the existing room's tags with a set
606 * of new ones and updates the new set on the server. Unlike
607 * most other methods in Room, this one sends a signal about changes
608 * immediately, not waiting for confirmation from the server
609 * (because tags are saved in account data rather than in shared
610 * room state).
611 * \param applyOn setting this to Room::OnAllConversations will set tags
612 * on this and all _known_ predecessors and successors;
613 * by default only the current room is changed
614 */
616
617 /// Check whether the list of tags has m.favourite
618 bool isFavourite() const;
619 /// Check whether the list of tags has m.lowpriority
620 bool isLowPriority() const;
621 /// Check whether this room is for server notices (MSC1452)
622 bool isServerNoticeRoom() const;
623
624 /// Check whether this room is a direct chat
626
627 /// Get the list of members this room is a direct chat with
629
631 const QUrl &mxcUrl) const;
632
635
636 /// Get a file name for downloading for a given event id
637 /*!
638 * The event MUST be RoomMessageEvent and have content
639 * for downloading. \sa RoomMessageEvent::hasContent
640 */
642
643 /// Get information on file upload/download
644 /*!
645 * \param id uploads are identified by the corresponding event's
646 * transactionId (because uploads are done before
647 * the event is even sent), while downloads are using
648 * the normal event id for identifier.
649 */
652
653 /// Get the URL to the actual file source in a unified way
654 /*!
655 * For uploads it will return a URL to a local file; for downloads
656 * the URL will be taken from the corresponding room event.
657 */
659
660 /** Pretty-prints plain text into HTML
661 * As of now, it's exactly the same as Quotient::prettyPrint();
662 * in the future, it will also linkify room aliases, mxids etc.
663 * using the room context.
664 */
666
667#if Quotient_VERSION_MAJOR == 0 && Quotient_VERSION_MINOR < 10
668 [[deprecated("Create MemberSorter objects directly instead")]]
670#endif
671
673
674 /// Whether the current user is allowed to upgrade the room
676
677 /// \brief Get the current room state
679
680 //! \brief The effective power level of the given member in the room
681 //!
682 //! This is normally the same as calling `RoomPowerLevelEvent::powerLevelForUser(userId)` but
683 //! takes into account the room context and works even if the room state has no power levels
684 //! event. It is THE recommended way to get a room member's power level to display in the UI.
685 //! \param memberId The room member ID to check; if empty, the local user will be checked
686 //! \sa RoomPowerLevelsEvent, https://spec.matrix.org/v1.11/client-server-api/#mroompower_levels
688
689 //! \brief Get the power level required to send events of the given type
690 //!
691 //! \note This is a generic method that only gets the power level to send events with a given
692 //! type. Some operations have additional restrictions or enablers though: e.g.,
693 //! room member changes (kicks, invites) have special power levels; on the other hand,
694 //! redactions of one's own messages are allowed regardless of the power level. To check
695 //! effective ability to perform an operation, use Room's can*() methods instead of
696 //! comparing the power levels (those are also slightly more efficient).
697 //! \note Unlike the template version below, this method determines at runtime whether an event
698 //! type is that of a state event, assuming unknown event types to be non-state; pass
699 //! `true` as the second parameter to override that.
700 //! \sa canSend, canRedact, canSwitchVersions
702
703 //! \brief Get the power level required to send events of the given type
704 //!
705 //! This is an optimised version of non-template powerLevelFor() (with the same caveat about
706 //! operations based on some event types) for cases when the event type is known at build time.
707 //! \tparam EvT the event type to get the power level for
708 template <EventClass EvT>
709 int powerLevelFor() const
710 {
712 }
713
714 //! \brief Post a pre-created room message event
715 //!
716 //! Takes ownership of the event, deleting it once the matching one arrives with the sync.
717 //! \note Do not assume that the event is already on the road to the homeserver when this (or
718 //! any other `post*`) method returns; it can be queued internally.
719 //! \sa PendingEventItem::deliveryStatus()
720 //! \return a reference to the pending event item
722
723 template <typename EvT, typename... ArgTs>
725 {
726 return post(makeEvent<EvT>(std::forward<ArgTs>(args)...));
727 }
728
731
733
734 //! Send a request to update the room state with the given event
736
737 //! \brief Set a state event of the given type with the given arguments
738 //!
739 //! This type-safe overload attempts to send a state event of the type \p EvT constructed from
740 //! \p args.
741 template <typename EvT, typename... ArgTs>
742 auto setState(ArgTs&&... args)
743 {
744 return setState(EvT(std::forward<ArgTs>(args)...));
745 }
746
748
750
752
753public Q_SLOTS:
754 /** Check whether the room should be upgraded */
756
762 /// Send a reaction on a given event with a given key
764
765 /** Post a pre-created room message event
766 *
767 * Takes ownership of the event, deleting it once the matching one
768 * arrives with the sync
769 * \return transaction id associated with the event.
770 */
771 [[deprecated("Use post() instead")]]
776
777 //! Send a request to update the room state based on freeform inputs
779 const QString& stateKey,
780 const QJsonObject& contentJson);
781 void setName(const QString& newName);
784 /// Set room aliases on the user's current server
787
788 /// You shouldn't normally call this method; it's here for debugging
790
792
795 void kickMember(const QString& memberId, const QString& reason = {});
796 void ban(const QString& userId, const QString& reason = {});
797 void unban(const QString& userId);
798 void redactEvent(const QString& eventId, const QString& reason = {});
799
801 const QString& overrideContentType = {});
802 // If localFilename is empty a temporary file is created
803 void downloadFile(const QString& eventId, const QUrl& localFilename = {});
805
806 //! \brief Set a given event as last read and post a read receipt on it
807 //!
808 //! Does nothing if the event is behind the current read receipt.
809 //! \sa lastReadReceipt, markMessagesAsRead, markAllMessagesAsRead
811 //! Put the fully-read marker at the latest message in the room
813
814 /// Switch the room's version (aka upgrade)
816
817 void inviteCall(const QString& callId, const int lifetime,
818 const QString& sdp);
820 void answerCall(const QString& callId, const QString& sdp);
822
823 /**
824 * Activates encryption for this room.
825 * Warning: Cannot be undone
826 */
828
830 /// Initial set of state events has been loaded
831 /**
832 * The initial set is what comes from the initial sync for the room.
833 * This includes all basic things like RoomCreateEvent,
834 * RoomNameEvent, a (lazy-loaded, not full) set of RoomMemberEvents
835 * etc. This is a per-room reflection of Connection::loadedRoomState
836 * \sa Connection::loadedRoomState
837 */
843 /// The event is about to be appended to the list of pending events
845 /// An event has been appended to the list of pending events
847 /// The remote echo has arrived with the sync and will be merged
848 /// with its local counterpart
849 /** NB: Requires a sync loop to be emitted */
852 /// The remote and local copies of the event have been merged
853 /** NB: Requires a sync loop to be emitted */
855 /// An event will be removed from the list of pending events
857 /// An event has just been removed from the list of pending events
859 /// The status of a pending event has changed
860 /** \sa PendingEventItem::deliveryStatus */
862 /// The server accepted the message
863 /** This is emitted when an event sending request has successfully
864 * completed. This does not mean that the event is already in the
865 * local timeline, only that the server has accepted it.
866 * \param txnId transaction id assigned by the client during sending
867 * \param eventId event id assigned by the server upon acceptance
868 * \sa postEvent, postPlainText, postMessage, postHtmlMessage
869 * \sa pendingEventMerged, aboutToAddNewMessages
870 */
872
873 /** A common signal for various kinds of changes in the room
874 * Aside from all changes in the room state
875 * @param changes a set of flags describing what changes occurred
876 * upon the last sync
877 * \sa Changes
878 */
880 /**
881 * \brief The room name, the canonical alias or other aliases changed
882 *
883 * Not triggered when display name changes.
884 */
891
892 //! \brief A new member has joined the room
893 //!
894 //! This can be from any previous state or a member previously unknown to
895 //! the room.
897
898 //! \brief A member who previously joined has left
899 //!
900 //! The member will still be known to the room their membership state has changed
901 //! from Membership::Join to anything else.
903
904 //! A known joined member is about to update their display name
906
907 //! A known joined member has updated their display name
909
910 //! A known joined member has updated their avatar
912
913 /// The list of members has changed
914 /** Emitted no more than once per sync, this is a good signal to
915 * for cases when some action should be done upon any change in
916 * the member list. If you need per-item granularity you should use
917 * userAdded, userRemoved and memberAboutToRename / memberRenamed
918 * instead.
919 */
921
922 /// The previously lazy-loaded members list is now loaded entirely
923 /// \sa setDisplayed
926
929
930 //! The list of members sending typing indicators has changed.
932
933 void highlightCountChanged(); ///< \sa highlightCount
934 void notificationCountChanged(); ///< \sa notificationCount
935
939 //! The event the m.read receipt points to has changed for the listed users
940 //! \sa lastReadReceipt
946
951
954 const Quotient::RoomEvent* oldEvent);
955
961 // fileTransferCancelled() is no more here; use fileTransferFailed() and
962 // check the transfer status instead
963
965
966 /// The room's version stability may have changed
969 /// This room has been upgraded and won't receive updates any more
971 /// An attempted room upgrade has failed
973
974 /// The room is about to be deleted
976
977protected:
981 virtual void onAddNewTimelineEvents(timeline_iter_t /*from*/) {}
983 virtual void onRedaction(const RoomEvent& /*prevEvent*/,
984 const RoomEvent& /*after*/)
985 {}
986 virtual QJsonObject toJson() const;
987 virtual void updateData(SyncRoomData&& data, bool fromCache = false);
989
990private:
991 friend class Connection;
992
993 class Private;
994 Private* d;
995
996 // This is called from Connection, reflecting a state change that
997 // arrived from the server. Clients should use
998 // Connection::joinRoom() and Room::leaveRoom() to change the state.
1000};
1001
1002template <template <class> class ContT>
1003inline typename ContT<RoomMember>::size_type lowerBoundMemberIndex(const ContT<RoomMember>& c,
1004 const auto& v,
1005 MemberSorter ms = {})
1006{
1007 return std::ranges::lower_bound(c, v, ms) - c.begin();
1008}
1009
1010template <template <class> class ContT>
1011inline typename ContT<QString>::size_type lowerBoundMemberIndex(const ContT<QString>& c,
1012 const auto& v, const Room* r,
1013 MemberSorter ms = {})
1014{
1015 return std::ranges::lower_bound(c, v, ms, std::bind_front(&Room::member, r)) - c.begin();
1016}
1017
1018} // namespace Quotient
1020Q_DECLARE_METATYPE(Quotient::ReadReceipt)
1021Q_DECLARE_OPERATORS_FOR_FLAGS(Quotient::Room::Changes)
Data structure for a room member's read receipt.
Definition room.h:81
void swap(ReadReceipt &lhs, ReadReceipt &rhs)
Definition room.h:98
ContT< QString >::size_type lowerBoundMemberIndex(const ContT< QString > &c, const auto &v, const Room *r, MemberSorter ms={})
Definition room.h:1011
ContT< RoomMember >::size_type lowerBoundMemberIndex(const ContT< RoomMember > &c, const auto &v, MemberSorter ms={})
Definition room.h:1003
#define QUO_DECLARE_FLAGS(Flags, Enum)
Quotient replacement for the Q_FLAG/Q_DECLARE_FLAGS combination.
#define QUOTIENT_API