diff options
author | Kitsune Ral <Kitsune-Ral@users.sf.net> | 2018-03-31 20:29:02 +0900 |
---|---|---|
committer | Kitsune Ral <Kitsune-Ral@users.sf.net> | 2018-03-31 20:29:56 +0900 |
commit | a62bc225b8b4c714e7943aad69d84704a03b8015 (patch) | |
tree | df5ff6f4cf878ecd74c14ea41a1d27a46055b415 /lib/room.h | |
parent | 9d8900197e69e9c0ffaaff6f63a40cb80cf08fb1 (diff) | |
parent | 6a61d3a127db1e253821bfb2ebb7f433bd534c4a (diff) | |
download | libquotient-a62bc225b8b4c714e7943aad69d84704a03b8015.tar.gz libquotient-a62bc225b8b4c714e7943aad69d84704a03b8015.zip |
Merge branch 'kitsune-install-target'
Closes #113.
Diffstat (limited to 'lib/room.h')
-rw-r--r-- | lib/room.h | 424 |
1 files changed, 424 insertions, 0 deletions
diff --git a/lib/room.h b/lib/room.h new file mode 100644 index 00000000..bdef04ee --- /dev/null +++ b/lib/room.h @@ -0,0 +1,424 @@ +/****************************************************************************** + * Copyright (C) 2015 Felix Rohrbach <kde@fxrh.de> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#pragma once + +#include "jobs/syncjob.h" +#include "events/roommessageevent.h" +#include "events/accountdataevents.h" +#include "joinstate.h" + +#include <QtGui/QPixmap> + +#include <memory> +#include <deque> +#include <utility> + +namespace QMatrixClient +{ + class Event; + class Connection; + class User; + class MemberSorter; + class LeaveRoomJob; + class RedactEventJob; + + class TimelineItem + { + public: + // For compatibility with Qt containers, even though we use + // a std:: container now for the room timeline + using index_t = int; + + TimelineItem(RoomEventPtr&& e, index_t number) + : evt(move(e)), idx(number) { } + + RoomEvent* event() const { return evt.get(); } + RoomEvent* operator->() const { return evt.operator->(); } + index_t index() const { return idx; } + + // Used for event redaction + RoomEventPtr replaceEvent(RoomEventPtr&& other); + + private: + RoomEventPtr evt; + index_t idx; + }; + inline QDebug& operator<<(QDebug& d, const TimelineItem& ti) + { + QDebugStateSaver dss(d); + d.nospace() << "(" << ti.index() << "|" << ti->id() << ")"; + return d; + } + + class FileTransferInfo + { + Q_GADGET + Q_PROPERTY(bool active READ active CONSTANT) + Q_PROPERTY(bool completed READ completed CONSTANT) + Q_PROPERTY(bool failed READ failed CONSTANT) + Q_PROPERTY(int progress MEMBER progress CONSTANT) + Q_PROPERTY(int total MEMBER total CONSTANT) + Q_PROPERTY(QUrl localDir MEMBER localDir CONSTANT) + Q_PROPERTY(QUrl localPath MEMBER localPath CONSTANT) + public: + enum Status { None, Started, Completed, Failed }; + Status status = None; + int progress = 0; + int total = -1; + QUrl localDir { }; + QUrl localPath { }; + + bool active() const + { return status == Started || status == Completed; } + bool completed() const { return status == Completed; } + bool failed() const { return status == Failed; } + }; + + class Room: public QObject + { + Q_OBJECT + Q_PROPERTY(Connection* connection READ connection CONSTANT) + Q_PROPERTY(User* localUser READ localUser CONSTANT) + Q_PROPERTY(QString id READ id CONSTANT) + Q_PROPERTY(QString name READ name NOTIFY namesChanged) + Q_PROPERTY(QStringList aliases READ aliases NOTIFY namesChanged) + Q_PROPERTY(QString canonicalAlias READ canonicalAlias NOTIFY namesChanged) + Q_PROPERTY(QString displayName READ displayName NOTIFY namesChanged) + Q_PROPERTY(QString topic READ topic NOTIFY topicChanged) + Q_PROPERTY(QString avatarMediaId READ avatarMediaId NOTIFY avatarChanged STORED false) + Q_PROPERTY(QUrl avatarUrl READ avatarUrl NOTIFY avatarChanged) + Q_PROPERTY(bool usesEncryption READ usesEncryption NOTIFY encryption) + + Q_PROPERTY(int timelineSize READ timelineSize NOTIFY addedMessages) + Q_PROPERTY(QStringList memberNames READ memberNames NOTIFY memberListChanged) + Q_PROPERTY(int memberCount READ memberCount NOTIFY memberListChanged) + + Q_PROPERTY(bool displayed READ displayed WRITE setDisplayed NOTIFY displayedChanged) + Q_PROPERTY(QString firstDisplayedEventId READ firstDisplayedEventId WRITE setFirstDisplayedEventId NOTIFY firstDisplayedEventChanged) + Q_PROPERTY(QString lastDisplayedEventId READ lastDisplayedEventId WRITE setLastDisplayedEventId NOTIFY lastDisplayedEventChanged) + + Q_PROPERTY(QString readMarkerEventId READ readMarkerEventId WRITE markMessagesAsRead NOTIFY readMarkerMoved) + Q_PROPERTY(bool hasUnreadMessages READ hasUnreadMessages NOTIFY unreadMessagesChanged) + Q_PROPERTY(int unreadCount READ unreadCount NOTIFY unreadMessagesChanged) + Q_PROPERTY(QStringList tagNames READ tagNames NOTIFY tagsChanged) + + public: + using Timeline = std::deque<TimelineItem>; + using rev_iter_t = Timeline::const_reverse_iterator; + using timeline_iter_t = Timeline::const_iterator; + + Room(Connection* connection, QString id, JoinState initialJoinState); + ~Room() override; + + // Property accessors + + Connection* connection() const; + User* localUser() const; + const QString& id() const; + QString name() const; + QStringList aliases() const; + QString canonicalAlias() const; + QString displayName() const; + QString topic() const; + QString avatarMediaId() const; + QUrl avatarUrl() const; + Q_INVOKABLE JoinState joinState() const; + Q_INVOKABLE QList<User*> usersTyping() const; + QList<User*> membersLeft() const; + + Q_INVOKABLE QList<User*> users() const; + QStringList memberNames() const; + int memberCount() const; + int timelineSize() const; + bool usesEncryption() const; + + /** + * Returns a square room avatar with the given size and requests it + * from the network if needed + * @return a pixmap with the avatar or a placeholder if there's none + * available yet + */ + Q_INVOKABLE QImage avatar(int dimension); + /** + * Returns a room avatar with the given dimensions and requests it + * from the network if needed + * @return a pixmap with the avatar or a placeholder if there's none + * available yet + */ + Q_INVOKABLE QImage avatar(int width, int height); + + /** + * @brief Get a user object for a given user id + * This is the recommended way to get a user object in a room + * context. The actual object type may be changed in further + * versions to provide room-specific user information (display name, + * avatar etc.). + * \note The method will return a valid user regardless of + * the membership. + */ + Q_INVOKABLE User* user(const QString& userId) const; + + /** + * \brief Check the join state of a given user in this room + * + * \note Banned and invited users are not tracked for now (Leave + * will be returned for them). + * + * \return either of Join, Leave, depending on the given + * user's state in the room + */ + Q_INVOKABLE JoinState memberJoinState(User* user) const; + + /** + * @brief Produces a disambiguated name for a given user in + * the context of the room + */ + Q_INVOKABLE QString roomMembername(const User* u) const; + /** + * @brief Produces a disambiguated name for a user with this id in + * the context of the room + */ + Q_INVOKABLE QString roomMembername(const QString& userId) const; + + const Timeline& messageEvents() const; + /** + * A convenience method returning the read marker to the before-oldest + * message + */ + rev_iter_t timelineEdge() const; + Q_INVOKABLE TimelineItem::index_t minTimelineIndex() const; + Q_INVOKABLE TimelineItem::index_t maxTimelineIndex() const; + Q_INVOKABLE bool isValidIndex(TimelineItem::index_t timelineIndex) const; + + rev_iter_t findInTimeline(TimelineItem::index_t index) const; + rev_iter_t findInTimeline(const QString& evtId) const; + + bool displayed() const; + void setDisplayed(bool displayed = true); + QString firstDisplayedEventId() const; + rev_iter_t firstDisplayedMarker() const; + void setFirstDisplayedEventId(const QString& eventId); + void setFirstDisplayedEvent(TimelineItem::index_t index); + QString lastDisplayedEventId() const; + rev_iter_t lastDisplayedMarker() const; + void setLastDisplayedEventId(const QString& eventId); + void setLastDisplayedEvent(TimelineItem::index_t index); + + rev_iter_t readMarker(const User* user) const; + rev_iter_t readMarker() const; + QString readMarkerEventId() const; + /** + * @brief Mark the event with uptoEventId as read + * + * 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. + */ + void markMessagesAsRead(QString uptoEventId); + + /** Check whether there are unread messages in the room */ + bool hasUnreadMessages() const; + + /** Get the number of unread messages in the room + * Depending on the read marker state, this call may return either + * a precise or an estimate number of unread events. Only "notable" + * events (non-redacted message events from users other than local) + * are counted. + * + * In a case when readMarker() == timelineEdge() (the local read + * marker is beyond the local timeline) only the bottom limit of + * the unread messages number can be estimated (and even that may + * be slightly off due to, e.g., redactions of events not loaded + * to the local timeline). + * + * If all messages are read, this function will return -1 (_not_ 0, + * as zero may mean "zero or more unread messages" in a situation + * when the read marker is outside the local timeline. + */ + int unreadCount() const; + + Q_INVOKABLE int notificationCount() const; + Q_INVOKABLE void resetNotificationCount(); + Q_INVOKABLE int highlightCount() const; + Q_INVOKABLE void resetHighlightCount(); + + QStringList tagNames() const; + TagsMap tags() const; + TagRecord tag(const QString& name) const; + + /** Add a new tag to this room + * If this room already has this tag, nothing happens. If it's a new + * tag for the room, the respective tag record is added to the set + * of tags and the new set is sent to the server to update other + * clients. + */ + void addTag(const QString& name, const TagRecord& record = {}); + + /** Remove a tag from the room */ + void removeTag(const QString& name); + + /** Overwrite the room's tags + * This completely replaces the existing room's tags with a set + * of new ones and updates the new set on the server. Unlike + * most other methods in Room, this one sends a signal about changes + * immediately, not waiting for confirmation from the server + * (because tags are saved in account data rather than in shared + * room state). + */ + void setTags(const TagsMap& newTags); + + /** Check whether the list of tags has m.favourite */ + bool isFavourite() const; + /** Check whether the list of tags has m.lowpriority */ + bool isLowPriority() const; + + /** Check whether this room is a direct chat */ + bool isDirectChat() const; + + /** Get the list of users this room is a direct chat with */ + QList<const User*> directChatUsers() const; + + Q_INVOKABLE QUrl urlToThumbnail(const QString& eventId); + Q_INVOKABLE QUrl urlToDownload(const QString& eventId); + Q_INVOKABLE QString fileNameToDownload(const QString& eventId); + Q_INVOKABLE FileTransferInfo fileTransferInfo(const QString& id) const; + + /** Pretty-prints plain text into HTML + * This includes HTML escaping of <,>,",& and URLs linkification. + */ + QString prettyPrint(const QString& plainText) const; + + MemberSorter memberSorter() const; + + QJsonObject toJson() const; + void updateData(SyncRoomData&& data ); + void setJoinState( JoinState state ); + + public slots: + void postMessage(const QString& plainText, + MessageEventType type = MessageEventType::Text); + void postMessage(const RoomMessageEvent& event); + /** @deprecated If you have a custom event type, construct the event + * and pass it as a whole to postMessage() */ + void postMessage(const QString& type, const QString& plainText); + void setName(const QString& newName); + void setCanonicalAlias(const QString& newAlias); + void setTopic(const QString& newTopic); + + void getPreviousContent(int limit = 10); + + void inviteToRoom(const QString& memberId); + LeaveRoomJob* leaveRoom(); + void kickMember(const QString& memberId, const QString& reason = {}); + void ban(const QString& userId, const QString& reason = {}); + void unban(const QString& userId); + void redactEvent(const QString& eventId, + const QString& reason = {}); + + void uploadFile(const QString& id, const QUrl& localFilename, + const QString& overrideContentType = {}); + // If localFilename is empty a temporary file is created + void downloadFile(const QString& eventId, + const QUrl& localFilename = {}); + void cancelFileTransfer(const QString& id); + + /** Mark all messages in the room as read */ + void markAllMessagesAsRead(); + + signals: + void aboutToAddHistoricalMessages(RoomEventsRange events); + void aboutToAddNewMessages(RoomEventsRange events); + void addedMessages(); + + /** + * @brief The room name, the canonical alias or other aliases changed + * + * Not triggered when displayname changes. + */ + void namesChanged(Room* room); + /** @brief The room displayname changed */ + void displaynameChanged(Room* room); + void topicChanged(); + void avatarChanged(); + void userAdded(User* user); + void userRemoved(User* user); + void memberAboutToRename(User* user, QString newName); + void memberRenamed(User* user); + void memberListChanged(); + void encryption(); + + void joinStateChanged(JoinState oldState, JoinState newState); + void typingChanged(); + + void highlightCountChanged(Room* room); + void notificationCountChanged(Room* room); + + void displayedChanged(bool displayed); + void firstDisplayedEventChanged(); + void lastDisplayedEventChanged(); + void lastReadEventChanged(User* user); + void readMarkerMoved(); + void unreadMessagesChanged(Room* room); + + void tagsChanged(); + + void replacedEvent(const RoomEvent* newEvent, + const RoomEvent* oldEvent); + + void newFileTransfer(QString id, QUrl localFile); + void fileTransferProgress(QString id, qint64 progress, qint64 total); + void fileTransferCompleted(QString id, QUrl localFile, QUrl mxcUrl); + void fileTransferFailed(QString id, QString errorMessage = {}); + void fileTransferCancelled(QString id); + + protected: + virtual void processStateEvents(const RoomEvents& events); + virtual void processEphemeralEvent(EventPtr event); + virtual void processAccountDataEvent(EventPtr event); + virtual void onAddNewTimelineEvents(timeline_iter_t from) { } + virtual void onAddHistoricalTimelineEvents(rev_iter_t from) { } + virtual void onRedaction(const RoomEvent* prevEvent, + const RoomEvent* after) { } + + private: + class Private; + Private* d; + }; + + class MemberSorter + { + public: + explicit MemberSorter(const Room* r) : room(r) { } + + bool operator()(User* u1, User* u2) const; + bool operator()(User* u1, const QString& u2name) const; + + template <typename ContT, typename ValT> + typename ContT::size_type lowerBoundIndex(const ContT& c, + const ValT& v) const + { + return std::lower_bound(c.begin(), c.end(), v, *this) - c.begin(); + } + + private: + const Room* room; + }; +} // namespace QMatrixClient +Q_DECLARE_METATYPE(QMatrixClient::FileTransferInfo) |