Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F116475
D41.1733013228.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Award Token
Flag For Later
Size
10 KB
Referenced Files
None
Subscribers
None
D41.1733013228.diff
View Options
diff --git a/src/contents/ui/Bubble.qml b/src/contents/ui/Bubble.qml
--- a/src/contents/ui/Bubble.qml
+++ b/src/contents/ui/Bubble.qml
@@ -25,7 +25,7 @@
property var backgroundItem: Rectangle {
anchors.fill: parent
- color: hovered ? Kirigami.Theme.activeBackgroundColor : Kirigami.Theme.backgroundColor
+ color: (hovered || isSelected) ? Kirigami.Theme.activeBackgroundColor : Kirigami.Theme.backgroundColor
}
background: compactMode ? null : backgroundItem
@@ -106,13 +106,15 @@
ColumnLayout {
Layout.fillWidth: true
- property var inReplyTo: Item {
+ property var inReplyTo: MouseArea {
Layout.fillWidth: true
Layout.margins: 0
Layout.minimumHeight: Math.min(inReplyToLayout.implicitHeight, Kirigami.Units.gridUnit * 5)
Layout.maximumHeight: Kirigami.Units.gridUnit * 5
clip: true
+ onClicked: eventListView.goToEvent(bubbleRootLayout.replyToOrAnnotatedEventId)
+
RowLayout {
id: inReplyToLayout
anchors {
diff --git a/src/contents/ui/EventView.qml b/src/contents/ui/EventView.qml
--- a/src/contents/ui/EventView.qml
+++ b/src/contents/ui/EventView.qml
@@ -21,6 +21,7 @@
property var prevEventType: prevEvent ? getMessageType(prevEvent) : 'ignore'
property var sender
property var stateKeyUser
+ property var isSelected: false
property var messageType: getMessageType(event)
@@ -45,6 +46,7 @@
Types.Text {
event: eventView.event
sender: eventView.sender
+ isSelected: eventView.isSelected
}
}
@@ -53,6 +55,7 @@
Types.Emote {
event: eventView.event
sender: eventView.sender
+ isSelected: eventView.isSelected
}
}
@@ -61,6 +64,7 @@
Types.Notice {
event: eventView.event
sender: eventView.sender
+ isSelected: eventView.isSelected
}
}
@@ -70,6 +74,7 @@
event: eventView.event
sender: eventView.sender
stateKeyUser: eventView.stateKeyUser
+ isSelected: eventView.isSelected
}
}
@@ -78,6 +83,7 @@
Types.Image {
event: eventView.event
sender: eventView.sender
+ isSelected: eventView.isSelected
}
}
@@ -86,6 +92,7 @@
Types.File {
event: eventView.event
sender: eventView.sender
+ isSelected: eventView.isSelected
}
}
@@ -94,6 +101,7 @@
Types.Video {
event: eventView.event
sender: eventView.sender
+ isSelected: eventView.isSelected
}
}
@@ -102,6 +110,7 @@
Types.Audio {
event: eventView.event
sender: eventView.sender
+ isSelected: eventView.isSelected
}
}
@@ -110,6 +119,7 @@
Types.Fallback {
event: eventView.event
sender: eventView.sender
+ isSelected: eventView.isSelected
}
}
@@ -118,6 +128,7 @@
Types.Redacted {
event: eventView.event
sender: eventView.sender
+ isSelected: eventView.isSelected
}
}
@@ -126,6 +137,7 @@
Types.Reaction {
event: eventView.event
sender: eventView.sender
+ isSelected: eventView.isSelected
}
}
diff --git a/src/contents/ui/RoomTimelineView.qml b/src/contents/ui/RoomTimelineView.qml
--- a/src/contents/ui/RoomTimelineView.qml
+++ b/src/contents/ui/RoomTimelineView.qml
@@ -27,8 +27,16 @@
verticalLayoutDirection: ListView.BottomToTop
delegate: EventViewWrapper {
+ property var eventListView: ListView.view
prevEvent: index < timeline.count - 1 ? timeline.at(index + 1) : undefined
event: timeline.at(index)
width: ListView.view.width
+ isSelected: event.eventId === roomTimelineView.selectedEventId
+ }
+
+ function goToEvent(eventId) {
+ selectedEventId = eventId;
+ const index = timeline.indexOfEvent(eventId);
+ positionViewAtIndex(index, ListView.Center);
}
}
diff --git a/src/contents/ui/event-types/Simple.qml b/src/contents/ui/event-types/Simple.qml
--- a/src/contents/ui/event-types/Simple.qml
+++ b/src/contents/ui/event-types/Simple.qml
@@ -17,6 +17,7 @@
property var sender
default property var children
+ property var isSelected
property var nameProvider: Kazv.UserNameProvider {
user: sender
diff --git a/src/matrix-room-timeline.hpp b/src/matrix-room-timeline.hpp
--- a/src/matrix-room-timeline.hpp
+++ b/src/matrix-room-timeline.hpp
@@ -45,6 +45,8 @@
Q_INVOKABLE MatrixEvent *at(int index) const;
+ Q_INVOKABLE int indexOfEvent(const QString &eventId) const;
+
private Q_SLOTS:
void updateInternalCount();
};
diff --git a/src/matrix-room-timeline.cpp b/src/matrix-room-timeline.cpp
--- a/src/matrix-room-timeline.cpp
+++ b/src/matrix-room-timeline.cpp
@@ -86,6 +86,35 @@
return QVariant();
}
+int MatrixRoomTimeline::indexOfEvent(const QString &eventId) const
+{
+ auto eventMap = m_messagesMap.get();
+ auto eventIdStd = eventId.toStdString();
+ if (eventMap.count(eventIdStd) == 0) {
+ return -1;
+ }
+ auto eventIds = m_timelineEventIds.get();
+ auto it = std::lower_bound(eventIds.begin(), eventIds.end(), eventIdStd,
+ [eventMap](const auto &idA, const auto &idB) {
+ auto a = std::make_pair(eventMap[idA].originServerTs(), idA);
+ auto b = std::make_pair(eventMap[idB].originServerTs(), idB);
+ return a < b;
+ }
+ );
+ if (it == eventIds.end()) {
+ return -1;
+ } else {
+ // suppose there are A events and B local echoes
+ // forward index:
+ // [0, 1, ..., A-1] [A, A+1, ..., A+B-1]
+ // reverse index:
+ // [A+B-1, A+B-2, ..., B] [B-1, B-2 , 0]
+ // forward = it.index()
+ // forward + reverse = A+B-1 = count() - 1
+ return count() - it.index() - 1;
+ }
+}
+
void MatrixRoomTimeline::updateInternalCount()
{
auto curCount = count();
diff --git a/src/tests/CMakeLists.txt b/src/tests/CMakeLists.txt
--- a/src/tests/CMakeLists.txt
+++ b/src/tests/CMakeLists.txt
@@ -28,4 +28,11 @@
TEST_NAME quicktest
LINK_LIBRARIES Qt5::QuickTest kazvtestlib
)
+
+if(KAZV_BUILD_BENCHMARKS)
+ ecm_add_test(
+ matrix-room-timeline-benchmark-test.cpp
+ LINK_LIBRARIES Qt5::Test kazvtestlib
+ )
+endif()
set_tests_properties(quicktest PROPERTIES WORKING_DIRECTORY ${CMAKE_SOURCE_DIR})
diff --git a/src/tests/matrix-room-timeline-benchmark-test.cpp b/src/tests/matrix-room-timeline-benchmark-test.cpp
new file mode 100644
--- /dev/null
+++ b/src/tests/matrix-room-timeline-benchmark-test.cpp
@@ -0,0 +1,102 @@
+/*
+ * This file is part of kazv.
+ * SPDX-FileCopyrightText: 2023 tusooa <tusooa@kazv.moe>
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+#include <libkazv-config.hpp>
+#include <immer/config.hpp> // https://github.com/arximboldi/immer/issues/168
+
+#include <memory>
+
+#include <QtTest>
+#include <QRandomGenerator>
+
+#include <factory.hpp>
+
+#include <matrix-room-timeline.hpp>
+#include <matrix-sdk.hpp>
+#include <matrix-room-list.hpp>
+#include <matrix-room.hpp>
+
+#include "test-model.hpp"
+#include "test-utils.hpp"
+
+using namespace Kazv;
+using namespace Kazv::Factory;
+
+Q_DECLARE_METATYPE(EventList)
+
+static int eventListMetaType{qRegisterMetaType<EventList>("EventList")};
+
+class MatrixRoomTimelineBenchmarkTest : public QObject
+{
+ Q_OBJECT
+
+public:
+ enum IndexType {
+ Random,
+ CloseToLatest,
+ Nonexistent,
+ };
+
+ Q_ENUM(IndexType)
+
+private Q_SLOTS:
+ void testIndexOfEvent_data();
+ void testIndexOfEvent();
+};
+
+void MatrixRoomTimelineBenchmarkTest::testIndexOfEvent_data()
+{
+ QTest::addColumn<EventList>("events");
+ QTest::addColumn<IndexType>("type");
+
+ auto events = EventList{};
+ for (int i = 0; i < 10000; ++i) {
+ events = std::move(events).push_back(makeEvent(withEventId("$" + std::to_string(i)) | withEventKV("/origin_server_ts"_json_pointer, i * 1000)));
+ }
+
+ QTest::addRow("random event") << events << Random;
+ QTest::addRow("event close to the latest") << events << CloseToLatest;
+ QTest::addRow("nonexistent event") << events << Nonexistent;
+}
+
+void MatrixRoomTimelineBenchmarkTest::testIndexOfEvent()
+{
+ QFETCH(EventList, events);
+ QFETCH(IndexType, type);
+
+ auto r = makeRoom(withRoomTimeline(events));
+ r.localEchoes = {
+ LocalEchoDesc{"txn1", makeEvent(withEventId("")), LocalEchoDesc::Sending},
+ LocalEchoDesc{"txn2", makeEvent(withEventId("")), LocalEchoDesc::Sending},
+ };
+
+ auto model = SdkModel{makeClient(withRoom(r))};
+ std::unique_ptr<MatrixSdk> sdk{makeTestSdk(model)};
+ auto roomList = toUniquePtr(sdk->roomList());
+ auto room = toUniquePtr(roomList->room(QString::fromStdString(r.roomId)));
+ auto timeline = toUniquePtr(room->timeline());
+
+ if (type == Random) {
+ QRandomGenerator g;
+ const int highest = events.size();
+ QBENCHMARK {
+ timeline->indexOfEvent(QString::fromStdString(
+ "$" + std::to_string(g.bounded(highest))));
+ }
+ } else if (type == CloseToLatest) {
+ QBENCHMARK {
+ timeline->indexOfEvent("$9940");
+ }
+ } else if (type == Nonexistent) {
+ QBENCHMARK {
+ timeline->indexOfEvent("$xxx");
+ }
+ }
+}
+
+QTEST_MAIN(MatrixRoomTimelineBenchmarkTest)
+
+#include "matrix-room-timeline-benchmark-test.moc"
diff --git a/src/tests/matrix-room-timeline-test.cpp b/src/tests/matrix-room-timeline-test.cpp
--- a/src/tests/matrix-room-timeline-test.cpp
+++ b/src/tests/matrix-room-timeline-test.cpp
@@ -35,6 +35,7 @@
void testLocalEcho();
void testReadReceipts();
void testEdits();
+ void testIndexOfEvent();
};
void MatrixRoomTimelineTest::testLocalEcho()
@@ -128,6 +129,34 @@
QVERIFY(event->isEdited());
}
+void MatrixRoomTimelineTest::testIndexOfEvent()
+{
+ // reverse index
+ // 4 3 2 1 0
+ // event/txnId
+ // $1 $2 $3 txn1 txn2
+ auto r = makeRoom(withRoomTimeline({
+ makeEvent(withEventId("$1") | withEventKV("/origin_server_ts"_json_pointer, 1000)),
+ makeEvent(withEventId("$2") | withEventKV("/origin_server_ts"_json_pointer, 2000)),
+ makeEvent(withEventId("$3") | withEventKV("/origin_server_ts"_json_pointer, 3000)),
+ }));
+ r.localEchoes = {
+ LocalEchoDesc{"txn1", makeEvent(withEventId("")), LocalEchoDesc::Sending},
+ LocalEchoDesc{"txn2", makeEvent(withEventId("")), LocalEchoDesc::Sending},
+ };
+
+ auto model = SdkModel{makeClient(withRoom(r))};
+ std::unique_ptr<MatrixSdk> sdk{makeTestSdk(model)};
+ auto roomList = toUniquePtr(sdk->roomList());
+ auto room = toUniquePtr(roomList->room(QString::fromStdString(r.roomId)));
+ auto timeline = toUniquePtr(room->timeline());
+
+ QCOMPARE(timeline->indexOfEvent("$1"), 4);
+ QCOMPARE(timeline->indexOfEvent("$2"), 3);
+ QCOMPARE(timeline->indexOfEvent("$3"), 2);
+ QCOMPARE(timeline->indexOfEvent("$4"), -1);
+}
+
QTEST_MAIN(MatrixRoomTimelineTest)
#include "matrix-room-timeline-test.moc"
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Sat, Nov 30, 4:33 PM (13 h, 59 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
41746
Default Alt Text
D41.1733013228.diff (10 KB)
Attached To
Mode
D41: Jump to an event when clicking on a reply
Attached
Detach File
Event Timeline
Log In to Comment