From d6b5cba61f69eb23ffb90675ce28fd2db90682ef Mon Sep 17 00:00:00 2001 From: Black Hat Date: Sun, 8 Jul 2018 20:54:06 +0800 Subject: [PATCH] Switch to release branch. --- matrix/libqmatrixclient | 2 +- matrix/messageeventmodel.cpp | 496 ++++++++++++++++++----------------- matrix/messageeventmodel.h | 1 + qml/Room.qml | 2 +- qml/form/ListForm.qml | 2 + qml/form/RoomForm.qml | 14 +- 6 files changed, 275 insertions(+), 242 deletions(-) diff --git a/matrix/libqmatrixclient b/matrix/libqmatrixclient index c4acd8e..fe4bede 160000 --- a/matrix/libqmatrixclient +++ b/matrix/libqmatrixclient @@ -1 +1 @@ -Subproject commit c4acd8ece12622164caf396c06bd0f22ab3589f7 +Subproject commit fe4bedeb349ed867feba7cb3c996a97f726d2083 diff --git a/matrix/messageeventmodel.cpp b/matrix/messageeventmodel.cpp index cc758c9..9243ae6 100644 --- a/matrix/messageeventmodel.cpp +++ b/matrix/messageeventmodel.cpp @@ -31,15 +31,16 @@ void MessageEventModel::setRoom(QMatrixClient::Room* room) return; beginResetModel(); - if(m_currentRoom) + if( m_currentRoom ) { - m_currentRoom->disconnect(this); + m_currentRoom->disconnect( this ); qDebug() << "Disconnected from" << m_currentRoom->id(); } m_currentRoom = room; - if(room) + if( room ) { + lastReadEventId = room->readMarkerEventId(); using namespace QMatrixClient; connect(m_currentRoom, &Room::aboutToAddNewMessages, this, [=](RoomEventsRange events) @@ -49,11 +50,21 @@ void MessageEventModel::setRoom(QMatrixClient::Room* room) connect(m_currentRoom, &Room::aboutToAddHistoricalMessages, this, [=](RoomEventsRange events) { + if (rowCount() > 0) + nextNewerRow = rowCount() - 1; beginInsertRows(QModelIndex(), rowCount(), rowCount() + int(events.size()) - 1); }); - connect(m_currentRoom, &Room::addedMessages, - this, &MessageEventModel::endInsertRows); + connect(m_currentRoom, &Room::addedMessages, this, + [=] { + if (nextNewerRow > -1) + { + const auto idx = index(nextNewerRow); + emit dataChanged(idx, idx); + nextNewerRow = -1; + } + endInsertRows(); + }); connect(m_currentRoom, &Room::readMarkerMoved, this, [this] { refreshEventRoles( std::exchange(lastReadEventId, @@ -75,8 +86,8 @@ void MessageEventModel::setRoom(QMatrixClient::Room* room) this, &MessageEventModel::refreshEvent); qDebug() << "Connected to room" << room->id() << "as" << room->localUser()->id(); - } - lastReadEventId = room ? room->readMarkerEventId() : ""; + } else + lastReadEventId.clear(); endResetModel(); emit roomChanged(); } @@ -153,255 +164,268 @@ int MessageEventModel::rowCount(const QModelIndex& parent) const QVariant MessageEventModel::data(const QModelIndex& index, int role) const { if( !m_currentRoom || - index.row() < 0 || index.row() >= m_currentRoom->timelineSize()) - return QVariant(); + index.row() < 0 || index.row() >= m_currentRoom->timelineSize()) + return QVariant(); - const auto timelineIt = m_currentRoom->messageEvents().rbegin() + index.row(); - const auto& ti = *timelineIt; + const auto timelineIt = m_currentRoom->messageEvents().rbegin() + index.row(); + const auto& ti = *timelineIt; - using namespace QMatrixClient; - if( role == Qt::DisplayRole ) - { - if (ti->isRedacted()) - { - auto reason = ti->redactedBecause()->reason(); - if (reason.isEmpty()) - return tr("Redacted"); + using namespace QMatrixClient; + if( role == Qt::DisplayRole ) + { + if (ti->isRedacted()) + { + auto reason = ti->redactedBecause()->reason(); + if (reason.isEmpty()) + return tr("Redacted"); + else + return tr("Redacted: %1") + .arg(ti->redactedBecause()->reason()); + } - return tr("Redacted: %1") - .arg(ti->redactedBecause()->reason()); - } + if( ti->type() == EventType::RoomMessage ) + { + using namespace MessageEventContent; - return visit(*ti - , [this] (const RoomMessageEvent& e) -> QVariant { - using namespace MessageEventContent; + auto* e = ti.viewAs(); + if (e->hasTextContent() && e->mimeType().name() != "text/plain") + return static_cast(e->content())->body; + if (e->hasFileContent()) + { + auto fileCaption = e->content()->fileInfo()->originalName; + if (fileCaption.isEmpty()) + fileCaption = m_currentRoom->prettyPrint(e->plainBody()); + if (fileCaption.isEmpty()) + return tr("a file"); + } + return m_currentRoom->prettyPrint(e->plainBody()); + } + if( ti->type() == EventType::RoomMember ) + { + auto* e = ti.viewAs(); + // FIXME: Rewind to the name that was at the time of this event + QString subjectName = m_currentRoom->roomMembername(e->userId()); + // The below code assumes senderName output in AuthorRole + switch( e->membership() ) + { + case MembershipType::Invite: + if (e->repeatsState()) + return tr("reinvited %1 to the room").arg(subjectName); + // [[fallthrough]] + case MembershipType::Join: + { + if (e->repeatsState()) + return tr("joined the room (repeated)"); + if (!e->prev_content() || + e->membership() != e->prev_content()->membership) + { + return e->membership() == MembershipType::Invite + ? tr("invited %1 to the room").arg(subjectName) + : tr("joined the room"); + } + QString text {}; + if (e->displayName() != e->prev_content()->displayName) + { + if (e->displayName().isEmpty()) + text = tr("cleared the display name"); + else + text = tr("changed the display name to %1") + .arg(e->displayName()); + } + if (e->avatarUrl() != e->prev_content()->avatarUrl) + { + if (!text.isEmpty()) + text += " and "; + if (e->avatarUrl().isEmpty()) + text += tr("cleared the avatar"); + else + text += tr("updated the avatar"); + } + return text; + } + case MembershipType::Leave: + if (e->prev_content() && + e->prev_content()->membership == MembershipType::Ban) + { + if (e->senderId() != e->userId()) + return tr("unbanned %1").arg(subjectName); + else + return tr("self-unbanned"); + } + if (e->senderId() != e->userId()) + return tr("has put %1 out of the room").arg(subjectName); + else + return tr("left the room"); + case MembershipType::Ban: + if (e->senderId() != e->userId()) + return tr("banned %1 from the room").arg(subjectName); + else + return tr("self-banned from the room"); + case MembershipType::Knock: + return tr("knocked"); + case MembershipType::Undefined: + return tr("made something unknown"); + } + } + if( ti->type() == EventType::RoomAliases ) + { + auto* e = ti.viewAs(); + return tr("set aliases to: %1").arg(e->aliases().join(", ")); + } + if( ti->type() == EventType::RoomCanonicalAlias ) + { + auto* e = ti.viewAs(); + if (e->alias().isEmpty()) + return tr("cleared the room main alias"); + else + return tr("set the room main alias to: %1").arg(e->alias()); + } + if( ti->type() == EventType::RoomName ) + { + auto* e = ti.viewAs(); + if (e->name().isEmpty()) + return tr("cleared the room name"); + else + return tr("set the room name to: %1").arg(e->name()); + } + if( ti->type() == EventType::RoomTopic ) + { + auto* e = ti.viewAs(); + if (e->topic().isEmpty()) + return tr("cleared the topic"); + else + return tr("set the topic to: %1").arg(e->topic()); + } + if( ti->type() == EventType::RoomAvatar ) + { + return tr("changed the room avatar"); + } + if( ti->type() == EventType::RoomEncryption ) + { + return tr("activated End-to-End Encryption"); + } + return tr("Unknown Event"); + } - if (e.hasTextContent() && e.mimeType().name() != "text/plain") - return static_cast(e.content())->body; - if (e.hasFileContent()) - { - auto fileCaption = e.content()->fileInfo()->originalName; - if (fileCaption.isEmpty()) - fileCaption = m_currentRoom->prettyPrint(e.plainBody()); - if (fileCaption.isEmpty()) - return tr("a file"); - } - return m_currentRoom->prettyPrint(e.plainBody()); - } - , [this] (const RoomMemberEvent& e) -> QVariant { - // FIXME: Rewind to the name that was at the time of this event - QString subjectName = m_currentRoom->roomMembername(e.userId()); - // The below code assumes senderName output in AuthorRole - switch( e.membership() ) - { - case MembershipType::Invite: - if (e.repeatsState()) - return tr("reinvited %1 to the room").arg(subjectName); - FALLTHROUGH; - case MembershipType::Join: - { - if (e.repeatsState()) - return tr("joined the room (repeated)"); - if (!e.prevContent() || - e.membership() != e.prevContent()->membership) - { - return e.membership() == MembershipType::Invite - ? tr("invited %1 to the room").arg(subjectName) - : tr("joined the room"); - } - QString text {}; - if (e.displayName() != e.prevContent()->displayName) - { - if (e.displayName().isEmpty()) - text = tr("cleared the display name"); - else - text = tr("changed the display name to %1") - .arg(e.displayName()); - } - if (e.avatarUrl() != e.prevContent()->avatarUrl) - { - if (!text.isEmpty()) - text += " and "; - if (e.avatarUrl().isEmpty()) - text += tr("cleared the avatar"); - else - text += tr("updated the avatar"); - } - return text; - } - case MembershipType::Leave: - if (e.prevContent() && - e.prevContent()->membership == MembershipType::Ban) - { - return (e.senderId() != e.userId()) - ? tr("unbanned %1").arg(subjectName) - : tr("self-unbanned"); - } - return (e.senderId() != e.userId()) - ? tr("has put %1 out of the room").arg(subjectName) - : tr("left the room"); - case MembershipType::Ban: - return (e.senderId() != e.userId()) - ? tr("banned %1 from the room").arg(subjectName) - : tr("self-banned from the room"); - case MembershipType::Knock: - return tr("knocked"); - default: - ; - } - return tr("made something unknown"); - } - , [] (const RoomAliasesEvent& e) -> QVariant { - return tr("set aliases to: %1").arg(e.aliases().join(", ")); - } - , [] (const RoomCanonicalAliasEvent& e) -> QVariant { - return (e.alias().isEmpty()) - ? tr("cleared the room main alias") - : tr("set the room main alias to: %1").arg(e.alias()); - } - , [] (const RoomNameEvent& e) -> QVariant { - return (e.name().isEmpty()) - ? tr("cleared the room name") - : tr("set the room name to: %1").arg(e.name()); - } - , [] (const RoomTopicEvent& e) -> QVariant { - return (e.topic().isEmpty()) - ? tr("cleared the topic") - : tr("set the topic to: %1").arg(e.topic()); - } - , [] (const RoomAvatarEvent&) -> QVariant { - return tr("changed the room avatar"); - } - , [] (const EncryptionEvent&) -> QVariant { - return tr("activated End-to-End Encryption"); - } - , tr("Unknown Event") - ); - } + if( role == Qt::ToolTipRole ) + { + return ti->originalJson(); + } - if( role == Qt::ToolTipRole ) - { - return ti->originalJson(); - } + if( role == EventTypeRole ) + { + if (ti->isStateEvent()) + return "state"; - if( role == EventTypeRole ) - { - if (ti->isStateEvent()) - return "state"; + if (ti->type() == EventType::RoomMessage) + { + switch (ti.viewAs()->msgtype()) + { + case MessageEventType::Emote: + return "emote"; + case MessageEventType::Notice: + return "notice"; + case MessageEventType::Image: + return "image"; + case MessageEventType::File: + case MessageEventType::Audio: + case MessageEventType::Video: + return "file"; + default: + return "message"; + } + } - if (auto e = ti.viewAs()) - { - switch (e->msgtype()) - { - case MessageEventType::Emote: - return "emote"; - case MessageEventType::Notice: - return "notice"; - case MessageEventType::Image: - return "image"; - case MessageEventType::File: - case MessageEventType::Audio: - case MessageEventType::Video: - return "file"; - default: - return "message"; - } - } - return "other"; - } + return "other"; + } - if (role == EventResolvedTypeRole) - return EventTypeRegistry::getMatrixType(ti->type()); + if( role == TimeRole ) + return makeMessageTimestamp(timelineIt); - if( role == TimeRole ) - return makeMessageTimestamp(timelineIt); + if( role == SectionRole ) + return makeDateString(timelineIt); // FIXME: move date rendering to QML - if( role == SectionRole ) - return makeDateString(timelineIt); // FIXME: move date rendering to QML + if( role == AuthorRole ) + { + auto userId = ti->senderId(); + // FIXME: It shouldn't be User, it should be its state "as of event" + return QVariant::fromValue(m_currentRoom->user(userId)); + } - if( role == AuthorRole ) - { - auto userId = ti->senderId(); - // FIXME: It shouldn't be User, it should be its state "as of event" - return QVariant::fromValue(m_currentRoom->user(userId)); - } + if (role == ContentTypeRole) + { + if (ti->type() == EventType::RoomMessage) + { + const auto& contentType = + ti.viewAs()->mimeType().name(); + return contentType == "text/plain" ? "text/html" : contentType; + } + return "text/plain"; + } - if (role == ContentTypeRole) - { - if (is(*ti)) - { - const auto& contentType = - ti.viewAs()->mimeType().name(); - return contentType == "text/plain" ? "text/html" : contentType; - } - return "text/plain"; - } + if (role == ContentRole) + { + if (ti->isRedacted()) + { + auto reason = ti->redactedBecause()->reason(); + if (reason.isEmpty()) + return tr("Redacted"); + else + return tr("Redacted: %1") + .arg(ti->redactedBecause()->reason()); + } - if (role == ContentRole) - { - if (ti->isRedacted()) - { - auto reason = ti->redactedBecause()->reason(); - return (reason.isEmpty()) - ? tr("Redacted") - : tr("Redacted: %1").arg(ti->redactedBecause()->reason()); - } + if( ti->type() == EventType::RoomMessage ) + { + using namespace MessageEventContent; - if (is(*ti)) - { - using namespace MessageEventContent; + auto* e = ti.viewAs(); + switch (e->msgtype()) + { + case MessageEventType::Image: + case MessageEventType::File: + case MessageEventType::Audio: + case MessageEventType::Video: + return QVariant::fromValue(e->content()->originalJson); + default: + ; + } + } + } - auto* e = ti.viewAs(); - switch (e->msgtype()) - { - case MessageEventType::Image: - case MessageEventType::File: - case MessageEventType::Audio: - case MessageEventType::Video: - return QVariant::fromValue(e->content()->originalJson); - default: - ; - } - } - } + if( role == ReadMarkerRole ) + return ti->id() == lastReadEventId; - if( role == HighlightRole ) - return QVariant(); + if( role == SpecialMarksRole ) + { + if (ti->isStateEvent() && ti.viewAs()->repeatsState()) + return "hidden"; + return ti->isRedacted() ? "redacted" : ""; + } - if( role == ReadMarkerRole ) - return ti->id() == lastReadEventId; + if( role == EventIdRole ) + return ti->id(); - if( role == SpecialMarksRole ) - { - if (auto e = ti.viewAs()) - if (e->repeatsState()) - return "hidden"; - return ti->isRedacted() ? "redacted" : ""; - } + if( role == LongOperationRole ) + { + if (ti->type() == EventType::RoomMessage && + ti.viewAs()->hasFileContent()) + { + auto info = m_currentRoom->fileTransferInfo(ti->id()); + return QVariant::fromValue(info); + } + } - if( role == EventIdRole ) - return ti->id(); + auto aboveEventIt = timelineIt + 1; // FIXME: shouldn't be here, because #312 + if (aboveEventIt != m_currentRoom->timelineEdge()) + { + if( role == AboveSectionRole ) + return makeDateString(aboveEventIt); - if( role == LongOperationRole ) - { - if (is(*ti) && - ti.viewAs()->hasFileContent()) - { - auto info = m_currentRoom->fileTransferInfo(ti->id()); - return QVariant::fromValue(info); - } - } - - auto aboveEventIt = timelineIt + 1; // FIXME: shouldn't be here, because #312 - if (aboveEventIt != m_currentRoom->timelineEdge()) - { - if( role == AboveSectionRole ) - return makeDateString(aboveEventIt); - - if( role == AboveAuthorRole ) - return QVariant::fromValue( - m_currentRoom->user((*aboveEventIt)->senderId())); - } + if( role == AboveAuthorRole ) + return QVariant::fromValue( + m_currentRoom->user((*aboveEventIt)->senderId())); + } return QVariant(); } @@ -424,5 +448,5 @@ QHash MessageEventModel::roleNames() const roles[SpecialMarksRole] = "marks"; roles[LongOperationRole] = "progressInfo"; roles[EventResolvedTypeRole] = "eventResolvedType"; -return roles; + return roles; } diff --git a/matrix/messageeventmodel.h b/matrix/messageeventmodel.h index 8fce89b..f5d57a1 100644 --- a/matrix/messageeventmodel.h +++ b/matrix/messageeventmodel.h @@ -44,6 +44,7 @@ class MessageEventModel: public QAbstractListModel private: QMatrixClient::Room* m_currentRoom = nullptr; QString lastReadEventId; + int nextNewerRow = -1; QDateTime makeMessageTimestamp(QMatrixClient::Room::rev_iter_t baseIt) const; QString makeDateString(QMatrixClient::Room::rev_iter_t baseIt) const; diff --git a/qml/Room.qml b/qml/Room.qml index 3695d17..785e8d5 100644 --- a/qml/Room.qml +++ b/qml/Room.qml @@ -1,4 +1,4 @@ -import QtQuick 2.10 +import QtQuick 2.11 import QtQuick.Controls 2.3 import QtQuick.Layouts 1.3 import Matrique 0.1 diff --git a/qml/form/ListForm.qml b/qml/form/ListForm.qml index 0ad0f0f..6b33a0c 100644 --- a/qml/form/ListForm.qml +++ b/qml/form/ListForm.qml @@ -180,6 +180,8 @@ Item { } highlightMoveDuration: 250 + currentIndex: -1 + ScrollBar.vertical: ScrollBar { id: scrollBar } model: delegateModel diff --git a/qml/form/RoomForm.qml b/qml/form/RoomForm.qml index d0f9ff9..6269d0e 100644 --- a/qml/form/RoomForm.qml +++ b/qml/form/RoomForm.qml @@ -16,7 +16,7 @@ Item { background: Item { anchors.fill: parent - visible: currentRoom === null + visible: currentRoom == null Pane { anchors.fill: parent } @@ -32,7 +32,7 @@ Item { anchors.fill: parent spacing: 0 - visible: currentRoom !== null + visible: currentRoom != null Pane { z: 10 @@ -61,7 +61,7 @@ Item { Label { Layout.fillWidth: true - text: currentRoom !== null ? currentRoom.displayName : "" + text: currentRoom != null ? currentRoom.displayName : "" font.pointSize: 16 elide: Text.ElideRight wrapMode: Text.NoWrap @@ -69,7 +69,7 @@ Item { Label { Layout.fillWidth: true - text: currentRoom !== null ? currentRoom.topic : "" + text: currentRoom != null ? currentRoom.topic : "" elide: Text.ElideRight wrapMode: Text.NoWrap } @@ -91,6 +91,8 @@ Item { model: MessageEventModel{ id: messageEventModel room: currentRoom + + onModelReset: currentRoom.getPreviousContent(50) } delegate: Row { readonly property bool sentByMe: author === currentRoom.localUser @@ -127,6 +129,10 @@ Item { } ScrollBar.vertical: ScrollBar { /*anchors.left: messageListView.right*/ } + + onAtYBeginningChanged: { + if(currentRoom && atYBeginning) currentRoom.getPreviousContent(50) + } } Pane {