summary refs log tree commit diff
diff options
context:
space:
mode:
authorNicolas Werner <nicolas.werner@hotmail.de>2020-01-03 23:21:33 +0100
committerNicolas Werner <nicolas.werner@hotmail.de>2020-01-06 18:29:08 +0100
commit946ab4d0f287307c24e310c6d2faef931f094ec5 (patch)
tree197f5239dbf46614b3ad37bf281806e5339eb673
parentUpload windows nightlies to S3 (diff)
downloadnheko-946ab4d0f287307c24e310c6d2faef931f094ec5.tar.xz
invert timeline
-rw-r--r--resources/qml/TimelineView.qml56
-rw-r--r--src/timeline/TimelineModel.cpp52
-rw-r--r--src/timeline/TimelineModel.h1
3 files changed, 88 insertions, 21 deletions
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;