From f0dab1801a026ffef8f8a4b907f371ece0c1436a Mon Sep 17 00:00:00 2001
From: miruka <miruka@disroot.org>
Date: Wed, 17 Apr 2019 16:43:18 -0400
Subject: [PATCH] Order the roomEvents models from newest to oldest

Qt somehow handles scrolling on new messages on its own when the
ListView direction is bottom to top.
In normal top to bottom, manual scrolling is completly buggy.
---
 harmonyqml/backend/signal_manager.py          | 20 +++++-----
 harmonyqml/components/chat/Daybreak.qml       |  4 +-
 harmonyqml/components/chat/MessageContent.qml |  1 +
 .../components/chat/MessageDelegate.qml       | 40 +++++++++++--------
 harmonyqml/components/chat/MessageList.qml    | 13 +++++-
 harmonyqml/components/side_pane/utils.js      |  2 +-
 6 files changed, 48 insertions(+), 32 deletions(-)

diff --git a/harmonyqml/backend/signal_manager.py b/harmonyqml/backend/signal_manager.py
index 8b04645a..a463f20c 100644
--- a/harmonyqml/backend/signal_manager.py
+++ b/harmonyqml/backend/signal_manager.py
@@ -96,19 +96,17 @@ class SignalManager(QObject):
         date_time = QDateTime.fromMSecsSinceEpoch(edict["server_timestamp"])
         new_event = RoomEvent(type=etype, date_time=date_time, dict=edict)
 
-        # Insert event in model at the right position, based on timestamps
-        # to keep them sorted by date of arrival.
-        # Iterate in reverse, since a new event is more likely to be appended,
-        # but events can arrive out of order.
-        if not model or model[-1].date_time < new_event.date_time:
+        # Model is sorted from newest to oldest message
+        insert_at = None
+        for i, event in enumerate(model):
+            if new_event.date_time > event.date_time:
+                insert_at = i
+                break
+
+        if insert_at is None:
             model.append(new_event)
         else:
-            for i, event in enumerate(reversed(model)):
-                if event.date_time < new_event.date_time:
-                    model.insert(-i, new_event)
-                    break
-            else:
-                model.insert(0, new_event)
+            model.insert(insert_at, new_event)
 
 
     def onRoomTypingUsersUpdated(
diff --git a/harmonyqml/components/chat/Daybreak.qml b/harmonyqml/components/chat/Daybreak.qml
index bfc9172e..8652ce0d 100644
--- a/harmonyqml/components/chat/Daybreak.qml
+++ b/harmonyqml/components/chat/Daybreak.qml
@@ -5,8 +5,8 @@ Base.HLabel {
     text: date_time.toLocaleDateString()
     width: rootCol.width
     horizontalAlignment: Text.AlignHCenter
-    topPadding: rootCol.isFirstMessage ? 0 : rootCol.verticalPadding * 4
-    bottomPadding: rootCol.verticalPadding * 2
+    topPadding: rootCol.isFirstMessage ? 0 : rootCol.standardSpacing
+    bottomPadding: rootCol.standardSpacing
     font.pixelSize: normalSize * 1.1
     color: "darkolivegreen"
 }
diff --git a/harmonyqml/components/chat/MessageContent.qml b/harmonyqml/components/chat/MessageContent.qml
index cdcd2203..305f9f12 100644
--- a/harmonyqml/components/chat/MessageContent.qml
+++ b/harmonyqml/components/chat/MessageContent.qml
@@ -50,6 +50,7 @@ Row {
 
             leftPadding: horizontalPadding
             rightPadding: horizontalPadding
+            topPadding: nameLabel.visible ? 0 : verticalPadding
             bottomPadding: verticalPadding
 
             Layout.minimumWidth: nameLabel.implicitWidth
diff --git a/harmonyqml/components/chat/MessageDelegate.qml b/harmonyqml/components/chat/MessageDelegate.qml
index 6272e98a..61ea8536 100644
--- a/harmonyqml/components/chat/MessageDelegate.qml
+++ b/harmonyqml/components/chat/MessageDelegate.qml
@@ -4,13 +4,20 @@ import QtQuick.Layouts 1.4
 import "../base" as Base
 
 Column {
-    id: rootCol
+    id: "rootCol"
 
     function mins_between(date1, date2) {
+        console.log(Math.round((((date2 - date1) % 86400000) % 3600000) / 60000))
         return Math.round((((date2 - date1) % 86400000) % 3600000) / 60000)
     }
 
-    readonly property bool isMessage: type.startsWith("RoomMessage")
+    function is_message(type_) { return type_.startsWith("RoomMessage") }
+
+    readonly property var previousItem:
+        index < messageListView.model.count - 1 ?
+        messageListView.model.get(index + 1) : null
+
+    readonly property bool isMessage: is_message(type)
 
     readonly property bool isUndecryptableEvent:
         type === "OlmEvent" || type === "MegolmEvent"
@@ -21,37 +28,38 @@ Column {
     readonly property bool isOwn:
         chatPage.user_id === dict.sender
 
-    readonly property var previousData:
-        index > 0 ? messageListView.model.get(index - 1) : null
-
-    readonly property bool isFirstMessage: ! previousData
+    readonly property bool isFirstMessage: ! previousItem
 
     readonly property bool combine:
         ! isFirstMessage &&
-        previousData.isMessage === isMessage &&
-        previousData.dict.sender === dict.sender &&
-        mins_between(previousData.date_time, date_time) <= 5
+        ! talkBreak &&
+        ! dayBreak &&
+        is_message(previousItem.type) === isMessage &&
+        previousItem.dict.sender === dict.sender &&
+        mins_between(previousItem.date_time, date_time) <= 5
 
     readonly property bool dayBreak:
         isFirstMessage ||
-        previousData.date_time.getDay() != date_time.getDay()
+        date_time.getDay() != previousItem.date_time.getDay()
 
     readonly property bool talkBreak:
         ! isFirstMessage &&
         ! dayBreak &&
-        mins_between(previousData.date_time, date_time) >= 20
+        mins_between(previousItem.date_time, date_time) >= 20
 
 
-    property int standardSpacing: 8
+    property int standardSpacing: 16
     property int horizontalPadding: 7
     property int verticalPadding: 5
 
     width: parent.width
     topPadding:
-        previousData === null ? 0 :
-        talkBreak ? standardSpacing * 6 :
-        combine ? standardSpacing / 2 :
-        standardSpacing * 1.2
+        ! previousItem ? 0 :
+        talkBreak ? standardSpacing * 3 :
+        combine ? standardSpacing / 4 :
+        standardSpacing
+
+    //Text { text: rootCol.topPadding }
 
     Daybreak { visible: dayBreak }
 
diff --git a/harmonyqml/components/chat/MessageList.qml b/harmonyqml/components/chat/MessageList.qml
index 145373d0..ba7aa5b5 100644
--- a/harmonyqml/components/chat/MessageList.qml
+++ b/harmonyqml/components/chat/MessageList.qml
@@ -13,18 +13,27 @@ Rectangle {
     ListView {
         id: messageListView
         anchors.fill: parent
-        model: Backend.models.roomEvents.get(chatPage.room.room_id)
         delegate: MessageDelegate {}
+        model: Backend.models.roomEvents.get(chatPage.room.room_id)
         //highlight: Rectangle {color: "lightsteelblue"; radius: 5}
 
         clip: true
         topMargin: space
         bottomMargin: space
+        verticalLayoutDirection: ListView.BottomToTop
 
         // Keep x scroll pages cached, to limit images having to be
         // reloaded from network.
         cacheBuffer: height * 6
 
-        //Component.onCompleted: positionViewAtEnd()
+        function goToEnd() {
+            messageListView.positionViewAtEnd()
+            //messageListView.flick(0, -messageListView.bottomMargin * 100)
+        }
+
+        //Connections {
+            //target: messageListView.model
+            //onChanged: goToEnd()
+        //}
     }
 }
diff --git a/harmonyqml/components/side_pane/utils.js b/harmonyqml/components/side_pane/utils.js
index 097bb764..c4f32d9a 100644
--- a/harmonyqml/components/side_pane/utils.js
+++ b/harmonyqml/components/side_pane/utils.js
@@ -4,7 +4,7 @@
 function get_last_room_event_text(room_id) {
     var eventsModel = Backend.models.roomEvents.get(room_id)
 
-    for (var i = -1; i >= -eventsModel.count; i--) {
+    for (var i = 0; i < eventsModel.count; i++) {
         var ev = eventsModel.get(i)
 
         if (ev.type !== "RoomMemberEvent") {