diff --git a/resources/qml/TimelineView.qml b/resources/qml/TimelineView.qml
index 1a1900ad..6bc2eb53 100644
--- a/resources/qml/TimelineView.qml
+++ b/resources/qml/TimelineView.qml
@@ -115,30 +115,68 @@ Item {
onMovementEnded: updatePosition()
spacing: 4
- delegate: TimelineRow {
+ verticalLayoutDirection: ListView.BottomToTop
+
+ delegate: Rectangle {
+ // This would normally be previousSection, but our model's order is inverted.
+ property bool sectionBoundary: (ListView.nextSection != "" && ListView.nextSection !== ListView.section) || model.index === chat.count - 1
+
+ id: wrapper
+ property Item section
+ width: chat.width
+ height: section ? section.height + timelinerow.height : timelinerow.height
+
+ TimelineRow {
+ id: timelinerow
+ y: section ? section.y + section.height : 0
+ }
function isFullyVisible() {
return height > 1 && (y - chat.contentY - 1) + height < chat.height
}
function getIndex() {
return index;
}
+
+ onSectionBoundaryChanged: {
+ if (sectionBoundary) {
+ var properties = {
+ 'modelData': model.dump,
+ 'section': ListView.section,
+ 'nextSection': ListView.nextSection
+ }
+ section = sectionHeader.createObject(wrapper, properties)
+ } else {
+ section.destroy()
+ section = null
+ }
+ }
+
}
section {
property: "section"
- delegate: Column {
+ }
+ Component {
+ id: sectionHeader
+ Column {
+ property var modelData
+ property string section
+ property string nextSection
+
topPadding: 4
bottomPadding: 4
spacing: 8
+ visible: !!modelData
+
width: parent.width
height: (section.includes(" ") ? dateBubble.height + 8 + userName.height : userName.height) + 8
Label {
id: dateBubble
- anchors.horizontalCenter: parent.horizontalCenter
+ anchors.horizontalCenter: parent ? parent.horizontalCenter : undefined
visible: section.includes(" ")
- text: chat.model.formatDateSeparator(new Date(Number(section.split(" ")[1])))
+ text: chat.model.formatDateSeparator(modelData.timestamp)
color: colors.windowText
height: contentHeight * 1.2
@@ -155,20 +193,20 @@ Item {
Avatar {
width: avatarSize
height: avatarSize
- url: chat.model.avatarUrl(section.split(" ")[0]).replace("mxc://", "image://MxcImage/")
- displayName: chat.model.displayName(section.split(" ")[0])
+ url: chat.model.avatarUrl(modelData.userId).replace("mxc://", "image://MxcImage/")
+ displayName: modelData.userName
MouseArea {
anchors.fill: parent
- onClicked: chat.model.openUserProfile(section.split(" ")[0])
+ onClicked: chat.model.openUserProfile(modelData.userId)
cursorShape: Qt.PointingHandCursor
}
}
Text {
id: userName
- text: chat.model.escapeEmoji(chat.model.displayName(section.split(" ")[0]))
- color: chat.model.userColor(section.split(" ")[0], colors.window)
+ text: chat.model.escapeEmoji(modelData.userName)
+ color: chat.model.userColor(modelData.userId, colors.window)
textFormat: Text.RichText
MouseArea {
diff --git a/src/timeline/TimelineModel.cpp b/src/timeline/TimelineModel.cpp
index 593a21df..8746a31f 100644
--- a/src/timeline/TimelineModel.cpp
+++ b/src/timeline/TimelineModel.cpp
@@ -202,6 +202,7 @@ TimelineModel::roleNames() const
{ReplyTo, "replyTo"},
{RoomName, "roomName"},
{RoomTopic, "roomTopic"},
+ {Dump, "dump"},
};
}
int
@@ -235,7 +236,7 @@ TimelineModel::data(const QModelIndex &index, int role) const
std::string userId = acc::sender(event);
- for (int r = index.row() - 1; r > 0; r--) {
+ for (size_t r = index.row() + 1; r < eventOrder.size(); r++) {
auto tempEv = events.value(eventOrder[r]);
QDateTime prevDate = origin_server_ts(tempEv);
prevDate.setTime(QTime());
@@ -314,6 +315,35 @@ TimelineModel::data(const QModelIndex &index, int role) const
return QVariant(QString::fromStdString(room_name(event)));
case RoomTopic:
return QVariant(QString::fromStdString(room_topic(event)));
+ case Dump: {
+ QVariantMap m;
+ auto names = roleNames();
+
+ // m.insert(names[Section], data(index, static_cast<int>(Section)));
+ m.insert(names[Type], data(index, static_cast<int>(Type)));
+ m.insert(names[Body], data(index, static_cast<int>(Body)));
+ m.insert(names[FormattedBody], data(index, static_cast<int>(FormattedBody)));
+ m.insert(names[UserId], data(index, static_cast<int>(UserId)));
+ m.insert(names[UserName], data(index, static_cast<int>(UserName)));
+ m.insert(names[Timestamp], data(index, static_cast<int>(Timestamp)));
+ m.insert(names[Url], data(index, static_cast<int>(Url)));
+ m.insert(names[ThumbnailUrl], data(index, static_cast<int>(ThumbnailUrl)));
+ m.insert(names[Filename], data(index, static_cast<int>(Filename)));
+ m.insert(names[Filesize], data(index, static_cast<int>(Filesize)));
+ m.insert(names[MimeType], data(index, static_cast<int>(MimeType)));
+ m.insert(names[Height], data(index, static_cast<int>(Height)));
+ m.insert(names[Width], data(index, static_cast<int>(Width)));
+ m.insert(names[ProportionalHeight],
+ data(index, static_cast<int>(ProportionalHeight)));
+ m.insert(names[Id], data(index, static_cast<int>(Id)));
+ m.insert(names[State], data(index, static_cast<int>(State)));
+ m.insert(names[IsEncrypted], data(index, static_cast<int>(IsEncrypted)));
+ m.insert(names[ReplyTo], data(index, static_cast<int>(ReplyTo)));
+ m.insert(names[RoomName], data(index, static_cast<int>(RoomName)));
+ m.insert(names[RoomTopic], data(index, static_cast<int>(RoomTopic)));
+
+ return QVariant(m);
+ }
default:
return QVariant();
}
@@ -335,10 +365,8 @@ TimelineModel::addEvents(const mtx::responses::Timeline &timeline)
if (ids.empty())
return;
- beginInsertRows(QModelIndex(),
- static_cast<int>(this->eventOrder.size()),
- static_cast<int>(this->eventOrder.size() + ids.size() - 1));
- this->eventOrder.insert(this->eventOrder.end(), ids.begin(), ids.end());
+ beginInsertRows(QModelIndex(), 0, static_cast<int>(ids.size() - 1));
+ this->eventOrder.insert(this->eventOrder.begin(), ids.rbegin(), ids.rend());
endInsertRows();
updateLastMessage();
@@ -362,7 +390,7 @@ isMessage(const mtx::events::Event<T> &)
void
TimelineModel::updateLastMessage()
{
- for (auto it = eventOrder.rbegin(); it != eventOrder.rend(); ++it) {
+ for (auto it = eventOrder.begin(); it != eventOrder.end(); ++it) {
auto event = events.value(*it);
if (auto e = std::get_if<mtx::events::EncryptedEvent<mtx::events::msg::Encrypted>>(
&event)) {
@@ -499,8 +527,10 @@ TimelineModel::addBackwardsEvents(const mtx::responses::Messages &msgs)
std::vector<QString> ids = internalAddEvents(msgs.chunk);
if (!ids.empty()) {
- beginInsertRows(QModelIndex(), 0, static_cast<int>(ids.size() - 1));
- this->eventOrder.insert(this->eventOrder.begin(), ids.rbegin(), ids.rend());
+ beginInsertRows(QModelIndex(),
+ static_cast<int>(this->eventOrder.size()),
+ static_cast<int>(this->eventOrder.size() + ids.size() - 1));
+ this->eventOrder.insert(this->eventOrder.end(), ids.begin(), ids.end());
endInsertRows();
}
@@ -1120,11 +1150,9 @@ TimelineModel::addPendingMessage(mtx::events::collections::TimelineEvents event)
internalAddEvents({event});
QString txn_id_qstr = QString::fromStdString(mtx::accessors::event_id(event));
- beginInsertRows(QModelIndex(),
- static_cast<int>(this->eventOrder.size()),
- static_cast<int>(this->eventOrder.size()));
+ beginInsertRows(QModelIndex(), 0, 0);
pending.push_back(txn_id_qstr);
- this->eventOrder.insert(this->eventOrder.end(), txn_id_qstr);
+ this->eventOrder.insert(this->eventOrder.begin(), txn_id_qstr);
endInsertRows();
updateLastMessage();
diff --git a/src/timeline/TimelineModel.h b/src/timeline/TimelineModel.h
index 7ff80c45..4161a0fc 100644
--- a/src/timeline/TimelineModel.h
+++ b/src/timeline/TimelineModel.h
@@ -147,6 +147,7 @@ public:
ReplyTo,
RoomName,
RoomTopic,
+ Dump,
};
QHash<int, QByteArray> roleNames() const override;
|