Add a slow way to search a room

This commit is contained in:
Nicolas Werner 2022-10-06 21:59:59 +02:00
parent 857d9cf2b6
commit f1c1f18f81
No known key found for this signature in database
GPG key ID: C8D75E610773F2D9
8 changed files with 120 additions and 48 deletions

View file

@ -22,6 +22,8 @@ Item {
property int availableWidth: width
property string searchString: ""
ScrollBar {
id: scrollbar
parent: chat.parent
@ -43,9 +45,10 @@ Item {
id: filteredTimeline
source: room
filterByThread: room ? room.thread : ""
filterByContent: chatRoot.searchString
}
model: filteredTimeline.filterByThread ? filteredTimeline : room
model: (filteredTimeline.filterByThread || filteredTimeline.filterByContent) ? filteredTimeline : room
// reuseItems still has a few bugs, see https://bugreports.qt.io/browse/QTBUG-95105 https://bugreports.qt.io/browse/QTBUG-95107
//onModelChanged: if (room) room.sendReset()
//reuseItems: true
@ -403,7 +406,7 @@ Item {
required property bool isEditable
required property bool isEdited
required property bool isStateEvent
required property bool previousMessageIsStateEvent
property bool previousMessageIsStateEvent: chat.model.dataByIndex(index+1, Room.IsStateEvent)
required property string replyTo
required property string threadId
required property string userId
@ -417,9 +420,9 @@ Item {
required property int status
required property int index
required property int relatedEventCacheBuster
required property string previousMessageUserId
required property string day
required property string previousMessageDay
property string previousMessageUserId: chat.model.dataByIndex(index+1, Room.UserId)
property string previousMessageDay: chat.model.dataByIndex(index+1, Room.Day)
required property string userName
property bool scrolledToThis: eventId === room.scrollTarget && (y + height > chat.y + chat.contentY && y < chat.y + chat.height + chat.contentY)

View file

@ -66,6 +66,8 @@ Item {
spacing: 0
TopBar {
id: topBar
showBackButton: timelineView.showBackButton
}
@ -102,6 +104,7 @@ Item {
MessageView {
implicitHeight: msgView.height - typingIndicator.height
searchString: topBar.searchString
Layout.fillWidth: true
}

View file

@ -25,6 +25,14 @@ Pane {
property bool isDirect: room ? room.isDirect : false
property string directChatOtherUserId: room ? room.directChatOtherUserId : ""
property string searchString: ""
onRoomIdChanged: {
searchString = "";
searchButton.searchActive = false;
searchField.text = ""
}
Layout.fillWidth: true
implicitHeight: topLayout.height + Nheko.paddingMedium * 2
z: 3
@ -177,12 +185,42 @@ Pane {
text: roomTopic
}
AbstractButton {
ImageButton {
id: pinButton
property bool pinsShown: !Settings.hiddenPins.includes(roomId)
visible: !!room && room.pinnedMessages.length > 0
Layout.column: 3
Layout.row: 1
Layout.rowSpan: 2
Layout.alignment: Qt.AlignVCenter
Layout.preferredHeight: Nheko.avatarSize - Nheko.paddingMedium
Layout.preferredWidth: Nheko.avatarSize - Nheko.paddingMedium
image: pinsShown ? ":/icons/icons/ui/pin.svg" : ":/icons/icons/ui/pin-off.svg"
ToolTip.visible: hovered
ToolTip.text: qsTr("Show or hide pinned messages")
onClicked: {
var ps = Settings.hiddenPins;
if (pinsShown) {
ps.push(roomId);
} else {
const index = ps.indexOf(roomId);
if (index > -1) {
ps.splice(index, 1);
}
}
Settings.hiddenPins = ps;
}
}
AbstractButton {
Layout.column: 4
Layout.row: 1
Layout.rowSpan: 2
Layout.preferredHeight: Nheko.avatarSize - Nheko.paddingMedium
Layout.maximumWidth: Nheko.avatarSize - Nheko.paddingMedium
contentItem: EncryptionIndicator {
sourceSize.height: parent.Layout.preferredHeight * Screen.devicePixelRatio
@ -216,40 +254,37 @@ Pane {
}
ImageButton {
id: pinButton
id: searchButton
property bool pinsShown: !Settings.hiddenPins.includes(roomId)
property bool searchActive: false
visible: !!room && room.pinnedMessages.length > 0
Layout.column: 4
visible: !!room
Layout.column: 5
Layout.row: 1
Layout.rowSpan: 2
Layout.alignment: Qt.AlignVCenter
Layout.preferredHeight: Nheko.avatarSize - Nheko.paddingMedium
Layout.preferredWidth: Nheko.avatarSize - Nheko.paddingMedium
image: pinsShown ? ":/icons/icons/ui/pin.svg" : ":/icons/icons/ui/pin-off.svg"
image: ":/icons/icons/ui/search.svg"
ToolTip.visible: hovered
ToolTip.text: qsTr("Show or hide pinned messages")
ToolTip.text: qsTr("Search this room")
onClicked: {
var ps = Settings.hiddenPins;
if (pinsShown) {
ps.push(roomId);
} else {
const index = ps.indexOf(roomId);
if (index > -1) {
ps.splice(index, 1);
}
searchActive = !searchActive
if (searchActive) {
searchField.forceActiveFocus();
}
else {
searchField.clear();
topBar.searchString = "";
}
Settings.hiddenPins = ps;
}
}
ImageButton {
id: roomOptionsButton
visible: !!room
Layout.column: 5
Layout.column: 6
Layout.row: 1
Layout.rowSpan: 2
Layout.alignment: Qt.AlignVCenter
@ -293,7 +328,7 @@ Pane {
Layout.row: 3
Layout.column: 2
Layout.columnSpan: 3
Layout.columnSpan: 4
Layout.fillWidth: true
Layout.preferredHeight: Math.min(contentHeight, Nheko.avatarSize * 4)
@ -374,7 +409,7 @@ Pane {
Layout.row: 4
Layout.column: 2
Layout.columnSpan: 1
Layout.columnSpan: 4
Layout.fillWidth: true
Layout.preferredHeight: Math.min(contentHeight, Nheko.avatarSize * 1.5)
@ -404,6 +439,20 @@ Pane {
}
}
}
MatrixTextField {
id: searchField
visible: searchButton.searchActive
Layout.row: 5
Layout.column: 2
Layout.columnSpan: 4
Layout.fillWidth: true
placeholderText: qsTr("Enter search query")
onAccepted: topBar.searchString = text
}
}
CursorShape {

View file

@ -898,6 +898,7 @@ EventStore::fetchMore()
mtx::http::MessagesOpts opts;
opts.room_id = room_id_;
opts.from = cache::client()->previousBatchToken(room_id_);
opts.limit = 80;
nhlog::ui()->debug("Paginating room {}, token {}", opts.room_id, opts.from);

View file

@ -1,3 +1,7 @@
// SPDX-FileCopyrightText: 2022 Nheko Contributors
//
// SPDX-License-Identifier: GPL-3.0-or-later
#include "TimelineFilter.h"
#include "Logging.h"
@ -19,6 +23,17 @@ TimelineFilter::setThreadId(const QString &t)
emit threadIdChanged();
}
void
TimelineFilter::setContentFilter(const QString &c)
{
nhlog::ui()->debug("Filtering by content '{}'", c.toStdString());
if (this->contentFilter != c) {
this->contentFilter = c;
invalidateFilter();
}
emit contentFilterChanged();
}
void
TimelineFilter::setSource(TimelineModel *s)
{
@ -62,11 +77,20 @@ TimelineFilter::currentIndex() const
bool
TimelineFilter::filterAcceptsRow(int source_row, const QModelIndex &) const
{
if (threadId.isEmpty())
if (threadId.isEmpty() && contentFilter.isEmpty())
return true;
if (auto s = sourceModel()) {
auto idx = s->index(source_row, 0);
if (!contentFilter.isEmpty() && !s->data(idx, TimelineModel::Body)
.toString()
.contains(contentFilter, Qt::CaseInsensitive)) {
return false;
}
if (threadId.isEmpty())
return true;
return s->data(idx, TimelineModel::EventId) == threadId ||
s->data(idx, TimelineModel::ThreadId) == threadId;
} else {

View file

@ -16,6 +16,8 @@ class TimelineFilter : public QSortFilterProxyModel
Q_OBJECT
Q_PROPERTY(QString filterByThread READ filterByThread WRITE setThreadId NOTIFY threadIdChanged)
Q_PROPERTY(QString filterByContent READ filterByContent WRITE setContentFilter NOTIFY
contentFilterChanged)
Q_PROPERTY(TimelineModel *source READ source WRITE setSource NOTIFY sourceChanged)
Q_PROPERTY(int currentIndex READ currentIndex WRITE setCurrentIndex NOTIFY currentIndexChanged)
@ -23,15 +25,23 @@ public:
explicit TimelineFilter(QObject *parent = nullptr);
QString filterByThread() const { return threadId; }
QString filterByContent() const { return contentFilter; }
TimelineModel *source() const;
int currentIndex() const;
void setThreadId(const QString &t);
void setContentFilter(const QString &t);
void setSource(TimelineModel *t);
void setCurrentIndex(int idx);
Q_INVOKABLE QVariant dataByIndex(int i, int role = Qt::DisplayRole) const
{
return data(index(i, 0), role);
}
signals:
void threadIdChanged();
void contentFilterChanged();
void sourceChanged();
void currentIndexChanged();
@ -39,5 +49,5 @@ protected:
bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const override;
private:
QString threadId;
QString threadId, contentFilter;
};

View file

@ -481,12 +481,9 @@ TimelineModel::roleNames() const
{IsOnlyEmoji, "isOnlyEmoji"},
{Body, "body"},
{FormattedBody, "formattedBody"},
{PreviousMessageUserId, "previousMessageUserId"},
{IsSender, "isSender"},
{UserId, "userId"},
{UserName, "userName"},
{PreviousMessageDay, "previousMessageDay"},
{PreviousMessageIsStateEvent, "previousMessageIsStateEvent"},
{Day, "day"},
{Timestamp, "timestamp"},
{Url, "url"},
@ -804,22 +801,6 @@ TimelineModel::data(const QModelIndex &index, int role) const
if (!event)
return "";
if (role == PreviousMessageDay || role == PreviousMessageUserId ||
role == PreviousMessageIsStateEvent) {
int prevIdx = rowCount() - index.row() - 2;
if (prevIdx < 0)
return {};
auto tempEv = events.get(prevIdx);
if (!tempEv)
return {};
if (role == PreviousMessageUserId)
return data(*tempEv, UserId);
else if (role == PreviousMessageDay)
return data(*tempEv, Day);
else
return data(*tempEv, IsStateEvent);
}
return data(*event, role);
}

View file

@ -214,12 +214,9 @@ public:
IsOnlyEmoji,
Body,
FormattedBody,
PreviousMessageUserId,
IsSender,
UserId,
UserName,
PreviousMessageDay,
PreviousMessageIsStateEvent,
Day,
Timestamp,
Url,
@ -257,6 +254,10 @@ public:
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
QVariant data(const mtx::events::collections::TimelineEvents &event, int role) const;
Q_INVOKABLE QVariant dataById(const QString &id, int role, const QString &relatedTo);
Q_INVOKABLE QVariant dataByIndex(int i, int role = Qt::DisplayRole) const
{
return data(index(i), role);
}
bool canFetchMore(const QModelIndex &) const override;
void fetchMore(const QModelIndex &) override;