Document messageeventmodel
Document the API and cleanup some unused roles.
This commit is contained in:
@@ -9,9 +9,9 @@
|
|||||||
bool CollapseStateProxyModel::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const
|
bool CollapseStateProxyModel::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const
|
||||||
{
|
{
|
||||||
Q_UNUSED(source_parent);
|
Q_UNUSED(source_parent);
|
||||||
return sourceModel()->data(sourceModel()->index(source_row, 0), MessageEventModel::EventTypeRole)
|
return sourceModel()->data(sourceModel()->index(source_row, 0), MessageEventModel::DelegateTypeRole)
|
||||||
!= MessageEventModel::DelegateType::State // If this is not a state, show it
|
!= MessageEventModel::DelegateType::State // If this is not a state, show it
|
||||||
|| sourceModel()->data(sourceModel()->index(source_row + 1, 0), MessageEventModel::EventTypeRole)
|
|| sourceModel()->data(sourceModel()->index(source_row + 1, 0), MessageEventModel::DelegateTypeRole)
|
||||||
!= MessageEventModel::DelegateType::State // If this is the first state in a block, show it. TODO hidden events?
|
!= MessageEventModel::DelegateType::State // If this is the first state in a block, show it. TODO hidden events?
|
||||||
|| sourceModel()->data(sourceModel()->index(source_row, 0), MessageEventModel::ShowSectionRole).toBool(); // If it's a new day, show it
|
|| sourceModel()->data(sourceModel()->index(source_row, 0), MessageEventModel::ShowSectionRole).toBool(); // If it's a new day, show it
|
||||||
}
|
}
|
||||||
@@ -47,7 +47,7 @@ QString CollapseStateProxyModel::aggregateEventToString(int sourceRow) const
|
|||||||
if (!uniqueAuthors.contains(nextAuthor)) {
|
if (!uniqueAuthors.contains(nextAuthor)) {
|
||||||
uniqueAuthors.append(nextAuthor);
|
uniqueAuthors.append(nextAuthor);
|
||||||
}
|
}
|
||||||
if (sourceModel()->data(sourceModel()->index(i - 1, 0), MessageEventModel::EventTypeRole)
|
if (sourceModel()->data(sourceModel()->index(i - 1, 0), MessageEventModel::DelegateTypeRole)
|
||||||
!= MessageEventModel::DelegateType::State // If it's not a state event
|
!= MessageEventModel::DelegateType::State // If it's not a state event
|
||||||
|| sourceModel()->data(sourceModel()->index(i - 1, 0), MessageEventModel::ShowSectionRole).toBool() // or the section needs to be visible
|
|| sourceModel()->data(sourceModel()->index(i - 1, 0), MessageEventModel::ShowSectionRole).toBool() // or the section needs to be visible
|
||||||
) {
|
) {
|
||||||
@@ -105,7 +105,7 @@ QVariantList CollapseStateProxyModel::stateEventsList(int sourceRow) const
|
|||||||
{"text", sourceModel()->data(sourceModel()->index(i, 0), Qt::DisplayRole).toString()},
|
{"text", sourceModel()->data(sourceModel()->index(i, 0), Qt::DisplayRole).toString()},
|
||||||
};
|
};
|
||||||
stateEvents.append(nextState);
|
stateEvents.append(nextState);
|
||||||
if (sourceModel()->data(sourceModel()->index(i - 1, 0), MessageEventModel::EventTypeRole)
|
if (sourceModel()->data(sourceModel()->index(i - 1, 0), MessageEventModel::DelegateTypeRole)
|
||||||
!= MessageEventModel::DelegateType::State // If it's not a state event
|
!= MessageEventModel::DelegateType::State // If it's not a state event
|
||||||
|| sourceModel()->data(sourceModel()->index(i - 1, 0), MessageEventModel::ShowSectionRole).toBool() // or the section needs to be visible
|
|| sourceModel()->data(sourceModel()->index(i - 1, 0), MessageEventModel::ShowSectionRole).toBool() // or the section needs to be visible
|
||||||
) {
|
) {
|
||||||
@@ -123,7 +123,7 @@ QVariantList CollapseStateProxyModel::authorList(int sourceRow) const
|
|||||||
if (!uniqueAuthors.contains(nextAvatar)) {
|
if (!uniqueAuthors.contains(nextAvatar)) {
|
||||||
uniqueAuthors.append(nextAvatar);
|
uniqueAuthors.append(nextAvatar);
|
||||||
}
|
}
|
||||||
if (sourceModel()->data(sourceModel()->index(i - 1, 0), MessageEventModel::EventTypeRole)
|
if (sourceModel()->data(sourceModel()->index(i - 1, 0), MessageEventModel::DelegateTypeRole)
|
||||||
!= MessageEventModel::DelegateType::State // If it's not a state event
|
!= MessageEventModel::DelegateType::State // If it's not a state event
|
||||||
|| sourceModel()->data(sourceModel()->index(i - 1, 0), MessageEventModel::ShowSectionRole).toBool() // or the section needs to be visible
|
|| sourceModel()->data(sourceModel()->index(i - 1, 0), MessageEventModel::ShowSectionRole).toBool() // or the section needs to be visible
|
||||||
) {
|
) {
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ using namespace Quotient;
|
|||||||
QHash<int, QByteArray> MessageEventModel::roleNames() const
|
QHash<int, QByteArray> MessageEventModel::roleNames() const
|
||||||
{
|
{
|
||||||
QHash<int, QByteArray> roles = QAbstractItemModel::roleNames();
|
QHash<int, QByteArray> roles = QAbstractItemModel::roleNames();
|
||||||
roles[EventTypeRole] = "eventType";
|
roles[DelegateTypeRole] = "delegateType";
|
||||||
roles[MessageRole] = "message";
|
roles[MessageRole] = "message";
|
||||||
roles[EventIdRole] = "eventId";
|
roles[EventIdRole] = "eventId";
|
||||||
roles[TimeRole] = "time";
|
roles[TimeRole] = "time";
|
||||||
@@ -45,12 +45,10 @@ QHash<int, QByteArray> MessageEventModel::roleNames() const
|
|||||||
roles[SpecialMarksRole] = "marks";
|
roles[SpecialMarksRole] = "marks";
|
||||||
roles[LongOperationRole] = "progressInfo";
|
roles[LongOperationRole] = "progressInfo";
|
||||||
roles[FileMimetypeIcon] = "fileMimetypeIcon";
|
roles[FileMimetypeIcon] = "fileMimetypeIcon";
|
||||||
roles[AnnotationRole] = "annotation";
|
|
||||||
roles[EventResolvedTypeRole] = "eventResolvedType";
|
roles[EventResolvedTypeRole] = "eventResolvedType";
|
||||||
roles[IsReplyRole] = "isReply";
|
roles[IsReplyRole] = "isReply";
|
||||||
roles[ReplyRole] = "reply";
|
roles[ReplyRole] = "reply";
|
||||||
roles[ReplyIdRole] = "replyId";
|
roles[ReplyIdRole] = "replyId";
|
||||||
roles[UserMarkerRole] = "userMarker";
|
|
||||||
roles[ShowAuthorRole] = "showAuthor";
|
roles[ShowAuthorRole] = "showAuthor";
|
||||||
roles[ShowSectionRole] = "showSection";
|
roles[ShowSectionRole] = "showSection";
|
||||||
roles[ReadMarkersRole] = "readMarkers";
|
roles[ReadMarkersRole] = "readMarkers";
|
||||||
@@ -76,7 +74,6 @@ QHash<int, QByteArray> MessageEventModel::roleNames() const
|
|||||||
|
|
||||||
MessageEventModel::MessageEventModel(QObject *parent)
|
MessageEventModel::MessageEventModel(QObject *parent)
|
||||||
: QAbstractListModel(parent)
|
: QAbstractListModel(parent)
|
||||||
, m_currentRoom(nullptr)
|
|
||||||
{
|
{
|
||||||
using namespace Quotient;
|
using namespace Quotient;
|
||||||
qmlRegisterAnonymousType<FileTransferInfo>("org.kde.neochat", 1);
|
qmlRegisterAnonymousType<FileTransferInfo>("org.kde.neochat", 1);
|
||||||
@@ -89,6 +86,11 @@ MessageEventModel::MessageEventModel(QObject *parent)
|
|||||||
|
|
||||||
MessageEventModel::~MessageEventModel() = default;
|
MessageEventModel::~MessageEventModel() = default;
|
||||||
|
|
||||||
|
NeoChatRoom *MessageEventModel::room() const
|
||||||
|
{
|
||||||
|
return m_currentRoom;
|
||||||
|
}
|
||||||
|
|
||||||
void MessageEventModel::setRoom(NeoChatRoom *room)
|
void MessageEventModel::setRoom(NeoChatRoom *room)
|
||||||
{
|
{
|
||||||
if (room == m_currentRoom) {
|
if (room == m_currentRoom) {
|
||||||
@@ -316,7 +318,7 @@ int MessageEventModel::refreshEventRoles(const QString &id, const QVector<int> &
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
row = int(timelineIt - m_currentRoom->messageEvents().rbegin()) + timelineBaseIndex();
|
row = int(timelineIt - m_currentRoom->messageEvents().rbegin()) + timelineBaseIndex();
|
||||||
if (data(index(row, 0), EventTypeRole).toInt() == ReadMarker || data(index(row, 0), EventTypeRole).toInt() == Other) {
|
if (data(index(row, 0), DelegateTypeRole).toInt() == ReadMarker || data(index(row, 0), DelegateTypeRole).toInt() == Other) {
|
||||||
row++;
|
row++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -447,7 +449,7 @@ QVariant MessageEventModel::data(const QModelIndex &idx, int role) const
|
|||||||
|
|
||||||
if (m_lastReadEventIndex.row() == row) {
|
if (m_lastReadEventIndex.row() == row) {
|
||||||
switch (role) {
|
switch (role) {
|
||||||
case EventTypeRole:
|
case DelegateTypeRole:
|
||||||
return DelegateType::ReadMarker;
|
return DelegateType::ReadMarker;
|
||||||
case TimeRole: {
|
case TimeRole: {
|
||||||
const QDateTime eventDate = data(index(m_lastReadEventIndex.row() + 1, 0), TimeRole).toDateTime().toLocalTime();
|
const QDateTime eventDate = data(index(m_lastReadEventIndex.row() + 1, 0), TimeRole).toDateTime().toLocalTime();
|
||||||
@@ -499,7 +501,7 @@ QVariant MessageEventModel::data(const QModelIndex &idx, int role) const
|
|||||||
return evt.originalJson();
|
return evt.originalJson();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (role == EventTypeRole) {
|
if (role == DelegateTypeRole) {
|
||||||
if (auto e = eventCast<const RoomMessageEvent>(&evt)) {
|
if (auto e = eventCast<const RoomMessageEvent>(&evt)) {
|
||||||
switch (e->msgtype()) {
|
switch (e->msgtype()) {
|
||||||
case MessageEventType::Emote:
|
case MessageEventType::Emote:
|
||||||
@@ -676,29 +678,11 @@ QVariant MessageEventModel::data(const QModelIndex &idx, int role) const
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (role == AnnotationRole) {
|
|
||||||
if (isPending) {
|
|
||||||
return pendingIt->annotation();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (role == TimeRole || role == SectionRole) {
|
if (role == TimeRole || role == SectionRole) {
|
||||||
auto ts = isPending ? pendingIt->lastUpdated() : makeMessageTimestamp(timelineIt);
|
auto ts = isPending ? pendingIt->lastUpdated() : makeMessageTimestamp(timelineIt);
|
||||||
return role == TimeRole ? QVariant(ts) : renderDate(ts);
|
return role == TimeRole ? QVariant(ts) : renderDate(ts);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (role == UserMarkerRole) {
|
|
||||||
QVariantList variantList;
|
|
||||||
const auto users = m_currentRoom->usersAtEventId(evt.id());
|
|
||||||
for (User *user : users) {
|
|
||||||
if (user == m_currentRoom->localUser()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
variantList.append(QVariant::fromValue(user));
|
|
||||||
}
|
|
||||||
return variantList;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (role == IsReplyRole) {
|
if (role == IsReplyRole) {
|
||||||
return !evt.contentJson()["m.relates_to"].toObject()["m.in_reply_to"].toObject()["event_id"].toString().isEmpty();
|
return !evt.contentJson()["m.relates_to"].toObject()["m.in_reply_to"].toObject()["event_id"].toString().isEmpty();
|
||||||
}
|
}
|
||||||
@@ -784,7 +768,7 @@ QVariant MessageEventModel::data(const QModelIndex &idx, int role) const
|
|||||||
// While the row is removed the subsequent row indexes are not changed so we need to skip over the removed index.
|
// While the row is removed the subsequent row indexes are not changed so we need to skip over the removed index.
|
||||||
// See - https://doc.qt.io/qt-5/qabstractitemmodel.html#beginRemoveRows
|
// See - https://doc.qt.io/qt-5/qabstractitemmodel.html#beginRemoveRows
|
||||||
if (data(i, SpecialMarksRole) != EventStatus::Hidden && !itemData(i).empty()) {
|
if (data(i, SpecialMarksRole) != EventStatus::Hidden && !itemData(i).empty()) {
|
||||||
return data(i, AuthorRole) != data(idx, AuthorRole) || data(i, EventTypeRole) == MessageEventModel::State
|
return data(i, AuthorRole) != data(idx, AuthorRole) || data(i, DelegateTypeRole) == MessageEventModel::State
|
||||||
|| data(i, TimeRole).toDateTime().msecsTo(data(idx, TimeRole).toDateTime()) > 600000
|
|| data(i, TimeRole).toDateTime().msecsTo(data(idx, TimeRole).toDateTime()) > 600000
|
||||||
|| data(i, TimeRole).toDateTime().toLocalTime().date().day() != data(idx, TimeRole).toDateTime().toLocalTime().date().day();
|
|| data(i, TimeRole).toDateTime().toLocalTime().date().day() != data(idx, TimeRole).toDateTime().toLocalTime().date().day();
|
||||||
}
|
}
|
||||||
@@ -1005,7 +989,7 @@ QVariant MessageEventModel::data(const QModelIndex &idx, int role) const
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
int MessageEventModel::eventIDToIndex(const QString &eventID) const
|
int MessageEventModel::eventIdToRow(const QString &eventID) const
|
||||||
{
|
{
|
||||||
const auto it = m_currentRoom->findInTimeline(eventID);
|
const auto it = m_currentRoom->findInTimeline(eventID);
|
||||||
if (it == m_currentRoom->historyEdge()) {
|
if (it == m_currentRoom->historyEdge()) {
|
||||||
@@ -1047,7 +1031,7 @@ QVariant MessageEventModel::getLastLocalUserMessageEventId()
|
|||||||
targetMessage.insert("event_id", eventId);
|
targetMessage.insert("event_id", eventId);
|
||||||
targetMessage.insert("formattedBody", content["formatted_body"].toString());
|
targetMessage.insert("formattedBody", content["formatted_body"].toString());
|
||||||
// Need to get the message from the original eventId or body will have * on the front
|
// Need to get the message from the original eventId or body will have * on the front
|
||||||
QModelIndex idx = index(eventIDToIndex(eventId), 0);
|
QModelIndex idx = index(eventIdToRow(eventId), 0);
|
||||||
targetMessage.insert("message", idx.data(Qt::UserRole + 2));
|
targetMessage.insert("message", idx.data(Qt::UserRole + 2));
|
||||||
|
|
||||||
return targetMessage;
|
return targetMessage;
|
||||||
@@ -1057,14 +1041,14 @@ QVariant MessageEventModel::getLastLocalUserMessageEventId()
|
|||||||
return targetMessage;
|
return targetMessage;
|
||||||
}
|
}
|
||||||
|
|
||||||
QVariant MessageEventModel::getLatestMessageFromIndex(const int baseline)
|
QVariant MessageEventModel::getLatestMessageFromRow(const int startRow)
|
||||||
{
|
{
|
||||||
QVariantMap replyResponse;
|
QVariantMap replyResponse;
|
||||||
const auto &timelineBottom = m_currentRoom->messageEvents().rbegin() + baseline;
|
const auto &timelineBottom = m_currentRoom->messageEvents().rbegin() + startRow;
|
||||||
|
|
||||||
// set a cap limit of baseline + 35 messages, to prevent loading a lot of messages
|
// set a cap limit of startRow + 35 messages, to prevent loading a lot of messages
|
||||||
// in rooms where the user has not sent many messages
|
// in rooms where the user has not sent many messages
|
||||||
const auto limit = timelineBottom + std::min(baseline + 35, m_currentRoom->timelineSize());
|
const auto limit = timelineBottom + std::min(startRow + 35, m_currentRoom->timelineSize());
|
||||||
|
|
||||||
for (auto it = timelineBottom; it != limit; ++it) {
|
for (auto it = timelineBottom; it != limit; ++it) {
|
||||||
auto evt = it->event();
|
auto evt = it->event();
|
||||||
@@ -1086,7 +1070,7 @@ QVariant MessageEventModel::getLatestMessageFromIndex(const int baseline)
|
|||||||
}
|
}
|
||||||
replyResponse.insert("event_id", eventId);
|
replyResponse.insert("event_id", eventId);
|
||||||
// Need to get the message from the original eventId or body will have * on the front
|
// Need to get the message from the original eventId or body will have * on the front
|
||||||
QModelIndex idx = index(eventIDToIndex(eventId), 0);
|
QModelIndex idx = index(eventIdToRow(eventId), 0);
|
||||||
replyResponse.insert("message", idx.data(Qt::UserRole + 2));
|
replyResponse.insert("message", idx.data(Qt::UserRole + 2));
|
||||||
replyResponse.insert("sender_id", QVariant::fromValue(m_currentRoom->getUser((*it)->senderId())));
|
replyResponse.insert("sender_id", QVariant::fromValue(m_currentRoom->getUser((*it)->senderId())));
|
||||||
replyResponse.insert("at", -it->index());
|
replyResponse.insert("at", -it->index());
|
||||||
|
|||||||
@@ -7,77 +7,99 @@
|
|||||||
|
|
||||||
#include "neochatroom.h"
|
#include "neochatroom.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @class MessageEventModel
|
||||||
|
*
|
||||||
|
* This class defines the model for visualising the room timeline.
|
||||||
|
*
|
||||||
|
* This model covers all event types in the timeline with many of the roles being
|
||||||
|
* specific to a subset of events. This means the user needs to understand which
|
||||||
|
* roles will return useful information for a given event type.
|
||||||
|
*
|
||||||
|
* @sa NeoChatRoom
|
||||||
|
*/
|
||||||
class MessageEventModel : public QAbstractListModel
|
class MessageEventModel : public QAbstractListModel
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief The current room that the model is getting its messages from.
|
||||||
|
*/
|
||||||
Q_PROPERTY(NeoChatRoom *room READ room WRITE setRoom NOTIFY roomChanged)
|
Q_PROPERTY(NeoChatRoom *room READ room WRITE setRoom NOTIFY roomChanged)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
/**
|
||||||
|
* @brief The type of delegate that is needed for the event.
|
||||||
|
*
|
||||||
|
* @note While similar this is not the matrix event or message type. This is
|
||||||
|
* to tell a QML ListView what delegate to show for each event. So while
|
||||||
|
* similar to the spec it is not the same.
|
||||||
|
*/
|
||||||
enum DelegateType {
|
enum DelegateType {
|
||||||
Emote,
|
Emote, /**< A message that begins with /me. */
|
||||||
Notice,
|
Notice, /**< A notice event. */
|
||||||
Image,
|
Image, /**< A message that is an image. */
|
||||||
Audio,
|
Audio, /**< A message that is an audio recording. */
|
||||||
Video,
|
Video, /**< A message that is a video. */
|
||||||
File,
|
File, /**< A message that is a file. */
|
||||||
Message,
|
Message, /**< A text message. */
|
||||||
Sticker,
|
Sticker, /**< A message that is a sticker. */
|
||||||
State,
|
State, /**< A state event in the room. */
|
||||||
Encrypted,
|
Encrypted, /**< An encrypted message that cannot be decrypted. */
|
||||||
ReadMarker,
|
ReadMarker, /**< The local user read marker. */
|
||||||
Poll,
|
Poll, /**< The initial event for a poll. */
|
||||||
Location,
|
Location, /**< A location event. */
|
||||||
Other,
|
Other, /**< Anything that cannot be classified as another type. */
|
||||||
};
|
};
|
||||||
Q_ENUM(DelegateType);
|
Q_ENUM(DelegateType);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Defines the model roles.
|
||||||
|
*/
|
||||||
enum EventRoles {
|
enum EventRoles {
|
||||||
EventTypeRole = Qt::UserRole + 1,
|
DelegateTypeRole = Qt::UserRole + 1, /**< The delegate type of the message. */
|
||||||
MessageRole,
|
MessageRole, /**< Plain text representation of the message. */
|
||||||
EventIdRole,
|
EventIdRole, /**< The matrix event ID of the event. */
|
||||||
TimeRole,
|
TimeRole, /**< The timestamp for when the event was sent. */
|
||||||
SectionRole,
|
SectionRole, /**< The date of the event as a string. */
|
||||||
AuthorRole,
|
AuthorRole, /**< The author of the event. */
|
||||||
ContentRole,
|
ContentRole, /**< The full message content. */
|
||||||
ContentTypeRole,
|
ContentTypeRole, /**< The content mime type. */
|
||||||
HighlightRole,
|
HighlightRole, /**< Whether the event should be highlighted. */
|
||||||
SpecialMarksRole,
|
SpecialMarksRole, /**< Whether the event is hidden or not. */
|
||||||
LongOperationRole,
|
LongOperationRole, /**< Progress info when downloading files. */
|
||||||
AnnotationRole,
|
FormattedBodyRole, /**< The formatted body of a rich message. */
|
||||||
UserMarkerRole,
|
GenericDisplayRole, /**< A generic string based upon the message type. */
|
||||||
FormattedBodyRole,
|
|
||||||
GenericDisplayRole,
|
|
||||||
|
|
||||||
MimeTypeRole,
|
MimeTypeRole, /**< The mime type of the message's file or media. */
|
||||||
FileMimetypeIcon,
|
FileMimetypeIcon, /**< The icon name for the mime type of a file. */
|
||||||
|
|
||||||
IsReplyRole,
|
IsReplyRole, /**< Is the message a reply to another event. */
|
||||||
ReplyRole,
|
ReplyRole, /**< The content data of the message that was replied to. */
|
||||||
ReplyIdRole,
|
ReplyIdRole, /**< The matrix ID of the message that was replied to. */
|
||||||
|
|
||||||
ShowAuthorRole,
|
ShowAuthorRole, /**< Whether the author's name should be shown. */
|
||||||
ShowSectionRole,
|
ShowSectionRole, /**< Whether the section header should be shown. */
|
||||||
|
|
||||||
ReadMarkersRole, /**< QVariantList of users at the event for read marker tracking. */
|
ReadMarkersRole, /**< Other users at the event for read marker tracking. */
|
||||||
ReadMarkersStringRole, /**< QString with the display name and mxID of the users at the event. */
|
ReadMarkersStringRole, /**< String with the display name and mxID of the users at the event. */
|
||||||
ShowReadMarkersRole, /**< bool with whether there are any other user read markers to be shown. */
|
ShowReadMarkersRole, /**< Whether there are any other user read markers to be shown. */
|
||||||
ReactionRole,
|
ReactionRole, /**< List of reactions to this event. */
|
||||||
|
SourceRole, /**< The full message source JSON. */
|
||||||
|
MediaUrlRole, /**< The source URL for any media in the message. */
|
||||||
|
|
||||||
SourceRole,
|
|
||||||
MediaUrlRole,
|
|
||||||
// For debugging
|
// For debugging
|
||||||
EventResolvedTypeRole,
|
EventResolvedTypeRole, /**< The event type the message. */
|
||||||
AuthorIdRole,
|
AuthorIdRole, /**< Matrix ID of the message author. */
|
||||||
VerifiedRole,
|
|
||||||
// Sender's displayname, always without the matrix id
|
VerifiedRole, /**< Whether an encrypted message is sent in a verified session. */
|
||||||
DisplayNameForInitialsRole,
|
DisplayNameForInitialsRole, /**< Sender's displayname, always without the matrix id. */
|
||||||
// The displayname for the event's sender; for name change events, the old displayname
|
AuthorDisplayNameRole, /**< The displayname for the event's sender; for name change events, the old displayname. */
|
||||||
AuthorDisplayNameRole,
|
IsRedactedRole, /**< Whether an event has been deleted. */
|
||||||
IsRedactedRole,
|
IsPendingRole, /**< Whether an event is waiting to be accepted by the server. */
|
||||||
IsPendingRole,
|
LatitudeRole, /**< Latitude for a location event. */
|
||||||
LatitudeRole,
|
LongitudeRole, /**< Longitude for a location event. */
|
||||||
LongitudeRole,
|
AssetRole, /**< Type of location event, e.g. self pin of the user location. */
|
||||||
AssetRole,
|
|
||||||
LastRole, // Keep this last
|
LastRole, // Keep this last
|
||||||
};
|
};
|
||||||
Q_ENUM(EventRoles)
|
Q_ENUM(EventRoles)
|
||||||
@@ -85,20 +107,67 @@ public:
|
|||||||
explicit MessageEventModel(QObject *parent = nullptr);
|
explicit MessageEventModel(QObject *parent = nullptr);
|
||||||
~MessageEventModel() override;
|
~MessageEventModel() override;
|
||||||
|
|
||||||
[[nodiscard]] NeoChatRoom *room() const
|
[[nodiscard]] NeoChatRoom *room() const;
|
||||||
{
|
|
||||||
return m_currentRoom;
|
|
||||||
}
|
|
||||||
void setRoom(NeoChatRoom *room);
|
void setRoom(NeoChatRoom *room);
|
||||||
|
|
||||||
[[nodiscard]] int rowCount(const QModelIndex &parent = QModelIndex()) const override;
|
/**
|
||||||
|
* @brief Get the given role value at the given index.
|
||||||
|
*
|
||||||
|
* @sa QAbstractItemModel::data
|
||||||
|
*/
|
||||||
[[nodiscard]] QVariant data(const QModelIndex &idx, int role = Qt::DisplayRole) const override;
|
[[nodiscard]] QVariant data(const QModelIndex &idx, int role = Qt::DisplayRole) const override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Number of rows in the model.
|
||||||
|
*
|
||||||
|
* @sa QAbstractItemModel::rowCount
|
||||||
|
*/
|
||||||
|
[[nodiscard]] int rowCount(const QModelIndex &parent = QModelIndex()) const override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns a mapping from Role enum values to role names.
|
||||||
|
*
|
||||||
|
* @sa EventRoles, QAbstractItemModel::roleNames()
|
||||||
|
*/
|
||||||
[[nodiscard]] QHash<int, QByteArray> roleNames() const override;
|
[[nodiscard]] QHash<int, QByteArray> roleNames() const override;
|
||||||
|
|
||||||
Q_INVOKABLE [[nodiscard]] int eventIDToIndex(const QString &eventID) const;
|
/**
|
||||||
|
* @brief Get the row number of the given event ID in the model.
|
||||||
|
*/
|
||||||
|
Q_INVOKABLE [[nodiscard]] int eventIdToRow(const QString &eventID) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get the last message sent by the local user.
|
||||||
|
*
|
||||||
|
* @note This checks a maximum of the previous 35 message for performance reasons.
|
||||||
|
*
|
||||||
|
* @return a QVariantMap for the event with the following parameters:
|
||||||
|
* - eventId - The event ID.
|
||||||
|
* - formattedBody - The message text formatted as Qt::RichText.
|
||||||
|
* - message - The message text formatted as Qt::PlainText.
|
||||||
|
*/
|
||||||
Q_INVOKABLE [[nodiscard]] QVariant getLastLocalUserMessageEventId();
|
Q_INVOKABLE [[nodiscard]] QVariant getLastLocalUserMessageEventId();
|
||||||
Q_INVOKABLE [[nodiscard]] QVariant getLatestMessageFromIndex(const int baseline);
|
|
||||||
Q_INVOKABLE void loadReply(const QModelIndex &row);
|
/**
|
||||||
|
* @brief Get the last message sent earlier than the given row.
|
||||||
|
*
|
||||||
|
* @note This checks a maximum of the previous 35 message for performance reasons.
|
||||||
|
*
|
||||||
|
* @return a QVariantMap for the event with the following parameters:
|
||||||
|
* - eventId - The event ID.
|
||||||
|
* - message - The message text formatted as Qt::PlainText.
|
||||||
|
* - sender_id - The matrix ID of the sender.
|
||||||
|
* - at - The QModelIndex of the message.
|
||||||
|
*/
|
||||||
|
Q_INVOKABLE [[nodiscard]] QVariant getLatestMessageFromRow(const int startRow);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Load the event that the item at the given index replied to.
|
||||||
|
*
|
||||||
|
* This is used to ensure that the reply data is available when the message that
|
||||||
|
* was replied to is outside the currently loaded timeline.
|
||||||
|
*/
|
||||||
|
Q_INVOKABLE void loadReply(const QModelIndex &index);
|
||||||
|
|
||||||
private Q_SLOTS:
|
private Q_SLOTS:
|
||||||
int refreshEvent(const QString &eventId);
|
int refreshEvent(const QString &eventId);
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ bool MessageFilterModel::filterAcceptsRow(int sourceRow, const QModelIndex &sour
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto eventType = index.data(MessageEventModel::EventTypeRole).toInt();
|
const auto eventType = index.data(MessageEventModel::DelegateTypeRole).toInt();
|
||||||
|
|
||||||
if (eventType == MessageEventModel::Other) {
|
if (eventType == MessageEventModel::Other) {
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -206,7 +206,7 @@ QQC2.Control {
|
|||||||
if (event.key === Qt.Key_V && event.modifiers & Qt.ControlModifier) {
|
if (event.key === Qt.Key_V && event.modifiers & Qt.ControlModifier) {
|
||||||
chatBar.pasteImage();
|
chatBar.pasteImage();
|
||||||
} else if (event.key === Qt.Key_Up && event.modifiers & Qt.ControlModifier) {
|
} else if (event.key === Qt.Key_Up && event.modifiers & Qt.ControlModifier) {
|
||||||
let replyEvent = messageEventModel.getLatestMessageFromIndex(0)
|
let replyEvent = messageEventModel.getLatestMessageFromRow(0)
|
||||||
if (replyEvent && replyEvent["event_id"]) {
|
if (replyEvent && replyEvent["event_id"]) {
|
||||||
currentRoom.chatBoxReplyId = replyEvent["event_id"]
|
currentRoom.chatBoxReplyId = replyEvent["event_id"]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import org.kde.kirigami 2.15 as Kirigami
|
|||||||
import org.kde.neochat 1.0
|
import org.kde.neochat 1.0
|
||||||
|
|
||||||
DelegateChooser {
|
DelegateChooser {
|
||||||
role: "eventType"
|
role: "delegateType"
|
||||||
|
|
||||||
DelegateChoice {
|
DelegateChoice {
|
||||||
roleValue: MessageEventModel.State
|
roleValue: MessageEventModel.State
|
||||||
|
|||||||
@@ -336,7 +336,7 @@ ColumnLayout {
|
|||||||
Layout.leftMargin: showUserMessageOnRight ? 0 : bubble.x + bubble.anchors.leftMargin
|
Layout.leftMargin: showUserMessageOnRight ? 0 : bubble.x + bubble.anchors.leftMargin
|
||||||
Layout.rightMargin: showUserMessageOnRight ? Kirigami.Units.largeSpacing : 0
|
Layout.rightMargin: showUserMessageOnRight ? Kirigami.Units.largeSpacing : 0
|
||||||
|
|
||||||
visible: eventType !== MessageEventModel.State && eventType !== MessageEventModel.Notice && reaction != undefined && reaction.length > 0
|
visible: delegateType !== MessageEventModel.State && delegateType !== MessageEventModel.Notice && reaction != undefined && reaction.length > 0
|
||||||
}
|
}
|
||||||
AvatarFlow {
|
AvatarFlow {
|
||||||
Layout.alignment: Qt.AlignRight
|
Layout.alignment: Qt.AlignRight
|
||||||
|
|||||||
@@ -476,7 +476,7 @@ Kirigami.ScrollablePage {
|
|||||||
id: hoverActions
|
id: hoverActions
|
||||||
property var event: null
|
property var event: null
|
||||||
property bool userMsg: event && event.author.id === Controller.activeConnection.localUserId
|
property bool userMsg: event && event.author.id === Controller.activeConnection.localUserId
|
||||||
property bool showEdit: event && (userMsg && (event.eventType === MessageEventModel.Emote || event.eventType === MessageEventModel.Message))
|
property bool showEdit: event && (userMsg && (event.delegateType === MessageEventModel.Emote || event.delegateType === MessageEventModel.Message))
|
||||||
property var delegate: null
|
property var delegate: null
|
||||||
property var bubble: null
|
property var bubble: null
|
||||||
property var hovered: bubble && bubble.hovered
|
property var hovered: bubble && bubble.hovered
|
||||||
@@ -642,7 +642,7 @@ Kirigami.ScrollablePage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function eventToIndex(eventID) {
|
function eventToIndex(eventID) {
|
||||||
const index = messageEventModel.eventIDToIndex(eventID)
|
const index = messageEventModel.eventIdToRow(eventID)
|
||||||
if (index === -1)
|
if (index === -1)
|
||||||
return -1
|
return -1
|
||||||
return sortedMessageEventModel.mapFromSource(messageEventModel.index(index, 0)).row
|
return sortedMessageEventModel.mapFromSource(messageEventModel.index(index, 0)).row
|
||||||
@@ -702,7 +702,7 @@ Kirigami.ScrollablePage {
|
|||||||
eventId: event.eventId,
|
eventId: event.eventId,
|
||||||
formattedBody: event.formattedBody,
|
formattedBody: event.formattedBody,
|
||||||
source: event.source,
|
source: event.source,
|
||||||
eventType: event.eventType,
|
eventType: event.delegateType,
|
||||||
plainMessage: plainMessage,
|
plainMessage: plainMessage,
|
||||||
});
|
});
|
||||||
contextMenu.open();
|
contextMenu.open();
|
||||||
|
|||||||
Reference in New Issue
Block a user