diff --git a/matrique.pro b/matrique.pro index 228330d..01f9a44 100644 --- a/matrique.pro +++ b/matrique.pro @@ -18,7 +18,8 @@ SOURCES += main.cpp \ matrix/controller.cpp \ matrix/roomlistmodel.cpp \ matrix/imageprovider.cpp \ - matrix/messageeventmodel.cpp + matrix/messageeventmodel.cpp \ + matrix/matriqueroom.cpp RESOURCES += \ res.qrc @@ -52,4 +53,5 @@ HEADERS += \ matrix/controller.h \ matrix/roomlistmodel.h \ matrix/imageprovider.h \ - matrix/messageeventmodel.h + matrix/messageeventmodel.h \ + matrix/matriqueroom.h diff --git a/matrix/matriqueroom.cpp b/matrix/matriqueroom.cpp new file mode 100644 index 0000000..d68c258 --- /dev/null +++ b/matrix/matriqueroom.cpp @@ -0,0 +1,92 @@ +#include "matriqueroom.h" + +#include "user.h" +#include "events/roommessageevent.h" + +using namespace QMatrixClient; + +MatriqueRoom::MatriqueRoom(Connection* connection, QString roomId, + JoinState joinState) + : Room(connection, roomId, joinState) +{ + connect( this, &MatriqueRoom::notificationCountChanged, this, &MatriqueRoom::countChanged ); + connect( this, &MatriqueRoom::highlightCountChanged, this, &MatriqueRoom::countChanged ); +} + +const QString& MatriqueRoom::cachedInput() const +{ + return m_cachedInput; +} + +void MatriqueRoom::setCachedInput(const QString& input) +{ + m_cachedInput = input; +} + +bool MatriqueRoom::isEventHighlighted(RoomEvent* e) const +{ + return highlights.contains(e); +} + +int MatriqueRoom::savedTopVisibleIndex() const +{ + return firstDisplayedMarker() == timelineEdge() ? 0 : + firstDisplayedMarker() - messageEvents().rbegin(); +} + +int MatriqueRoom::savedBottomVisibleIndex() const +{ + return lastDisplayedMarker() == timelineEdge() ? 0 : + lastDisplayedMarker() - messageEvents().rbegin(); +} + +void MatriqueRoom::saveViewport(int topIndex, int bottomIndex) +{ + if (bottomIndex == savedBottomVisibleIndex() && + (bottomIndex == 0 || topIndex == savedTopVisibleIndex())) + return; + if (bottomIndex == 0) + { + qDebug() << "Saving viewport as the latest available"; + setFirstDisplayedEventId({}); setLastDisplayedEventId({}); + return; + } + qDebug() << "Saving viewport:" << topIndex << "thru" << bottomIndex; + setFirstDisplayedEvent(maxTimelineIndex() - topIndex); + setLastDisplayedEvent(maxTimelineIndex() - bottomIndex); +} + +void MatriqueRoom::countChanged() +{ + if( displayed() && !hasUnreadMessages() ) + { + resetNotificationCount(); + resetHighlightCount(); + } +} + +void MatriqueRoom::onAddNewTimelineEvents(timeline_iter_t from) +{ + std::for_each(from, messageEvents().cend(), + [this] (const TimelineItem& ti) { checkForHighlights(ti); }); +} + +void MatriqueRoom::onAddHistoricalTimelineEvents(rev_iter_t from) +{ + std::for_each(from, messageEvents().crend(), + [this] (const TimelineItem& ti) { checkForHighlights(ti); }); +} + +void MatriqueRoom::checkForHighlights(const QMatrixClient::TimelineItem& ti) +{ + auto localUserId = localUser()->id(); + if (ti->senderId() == localUserId) + return; + if (ti->type() == EventType::RoomMessage) + { + auto* rme = static_cast(ti.event()); + if (rme->plainBody().contains(localUserId) || + rme->plainBody().contains(roomMembername(localUserId))) + highlights.insert(ti.event()); + } +} diff --git a/matrix/matriqueroom.h b/matrix/matriqueroom.h new file mode 100644 index 0000000..c402159 --- /dev/null +++ b/matrix/matriqueroom.h @@ -0,0 +1,35 @@ +#ifndef MATRIQUEROOM_H +#define MATRIQUEROOM_H + +#include "room.h" + +class MatriqueRoom: public QMatrixClient::Room +{ + Q_OBJECT + public: + MatriqueRoom(QMatrixClient::Connection* connection, + QString roomId, QMatrixClient::JoinState joinState); + + const QString& cachedInput() const; + void setCachedInput(const QString& input); + + bool isEventHighlighted(QMatrixClient::RoomEvent* e) const; + + Q_INVOKABLE int savedTopVisibleIndex() const; + Q_INVOKABLE int savedBottomVisibleIndex() const; + Q_INVOKABLE void saveViewport(int topIndex, int bottomIndex); + + private slots: + void countChanged(); + + private: + QSet highlights; + QString m_cachedInput; + + void onAddNewTimelineEvents(timeline_iter_t from) override; + void onAddHistoricalTimelineEvents(rev_iter_t from) override; + + void checkForHighlights(const QMatrixClient::TimelineItem& ti); +}; + +#endif // MATRIQUEROOM_H diff --git a/matrix/messageeventmodel.cpp b/matrix/messageeventmodel.cpp index 11191aa..62eeafb 100644 --- a/matrix/messageeventmodel.cpp +++ b/matrix/messageeventmodel.cpp @@ -11,21 +11,6 @@ #include "events/simplestateevents.h" #include "events/redactionevent.h" -enum EventRoles { - EventTypeRole = Qt::UserRole + 1, - EventIdRole, - TimeRole, - SectionRole, - AboveSectionRole, - AuthorRole, - ContentRole, - ContentTypeRole, - HighlightRole, - ReadMarkerRole, - SpecialMarksRole, - LongOperationRole, -}; - QHash MessageEventModel::roleNames() const { QHash roles = QAbstractItemModel::roleNames(); @@ -52,7 +37,7 @@ MessageEventModel::MessageEventModel(QObject* parent) qRegisterMetaType(); } -void MessageEventModel::changeRoom(QuaternionRoom* room) +void MessageEventModel::changeRoom(MatriqueRoom* room) { if (room == m_currentRoom) return; @@ -128,7 +113,7 @@ inline bool hasValidTimestamp(const QMatrixClient::TimelineItem& ti) return ti->timestamp().isValid(); } -QDateTime MessageEventModel::makeMessageTimestamp(QuaternionRoom::rev_iter_t baseIt) const +QDateTime MessageEventModel::makeMessageTimestamp(MatriqueRoom::rev_iter_t baseIt) const { const auto& timeline = m_currentRoom->messageEvents(); auto ts = baseIt->event()->timestamp(); @@ -151,7 +136,7 @@ QDateTime MessageEventModel::makeMessageTimestamp(QuaternionRoom::rev_iter_t bas return {}; } -QString MessageEventModel::makeDateString(QuaternionRoom::rev_iter_t baseIt) const +QString MessageEventModel::makeDateString(MatriqueRoom::rev_iter_t baseIt) const { auto date = makeMessageTimestamp(baseIt).toLocalTime().date(); if (QMatrixClient::SettingsGroup("UI") @@ -280,8 +265,8 @@ QVariant MessageEventModel::data(const QModelIndex& index, int role) const return tr("self-banned from the room"); case MembershipType::Knock: return tr("knocked"); - case MembershipType::Undefined: - return tr("made something unknown"); +// case MembershipType::Unknown; +// return tr("made something unknown"); } } if( event->type() == EventType::RoomAliases ) diff --git a/matrix/messageeventmodel.h b/matrix/messageeventmodel.h index 67d2146..07b1da3 100644 --- a/matrix/messageeventmodel.h +++ b/matrix/messageeventmodel.h @@ -1,10 +1,10 @@ #ifndef MESSAGEEVENTMODEL_H #define MESSAGEEVENTMODEL_H -#pragma once - #include +#include "matriqueroom.h" + class MessageEventModel: public QAbstractListModel { Q_OBJECT @@ -13,11 +13,26 @@ class MessageEventModel: public QAbstractListModel // has to be re-calculated anyway). // XXX: A better way would be to make [Room::]Timeline a list model // itself, leaving only representation of the model to a client. - Q_PROPERTY(QuaternionRoom* room MEMBER m_currentRoom CONSTANT) + Q_PROPERTY(MatriqueRoom* room MEMBER m_currentRoom CONSTANT) public: + enum EventRoles { + EventTypeRole = Qt::UserRole + 1, + EventIdRole, + TimeRole, + SectionRole, + AboveSectionRole, + AuthorRole, + ContentRole, + ContentTypeRole, + HighlightRole, + ReadMarkerRole, + SpecialMarksRole, + LongOperationRole, + }; + explicit MessageEventModel(QObject* parent = nullptr); - void changeRoom(QuaternionRoom* room); + void changeRoom(MatriqueRoom* room); int rowCount(const QModelIndex& parent = QModelIndex()) const override; QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; @@ -27,11 +42,11 @@ class MessageEventModel: public QAbstractListModel void refreshEvent(const QString& eventId); private: - QuaternionRoom* m_currentRoom; + MatriqueRoom* m_currentRoom; QString lastReadEventId; - QDateTime makeMessageTimestamp(QuaternionRoom::rev_iter_t baseIt) const; - QString makeDateString(QuaternionRoom::rev_iter_t baseIt) const; + QDateTime makeMessageTimestamp(MatriqueRoom::rev_iter_t baseIt) const; + QString makeDateString(MatriqueRoom::rev_iter_t baseIt) const; void refreshEventRoles(const QString& eventId, const QVector roles); };