mirror of
https://github.com/Nheko-Reborn/nheko.git
synced 2024-11-22 19:08:58 +03:00
Add a slow way to search a room
This commit is contained in:
parent
857d9cf2b6
commit
f1c1f18f81
8 changed files with 120 additions and 48 deletions
|
@ -22,6 +22,8 @@ Item {
|
||||||
|
|
||||||
property int availableWidth: width
|
property int availableWidth: width
|
||||||
|
|
||||||
|
property string searchString: ""
|
||||||
|
|
||||||
ScrollBar {
|
ScrollBar {
|
||||||
id: scrollbar
|
id: scrollbar
|
||||||
parent: chat.parent
|
parent: chat.parent
|
||||||
|
@ -43,9 +45,10 @@ Item {
|
||||||
id: filteredTimeline
|
id: filteredTimeline
|
||||||
source: room
|
source: room
|
||||||
filterByThread: room ? room.thread : ""
|
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
|
// 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()
|
//onModelChanged: if (room) room.sendReset()
|
||||||
//reuseItems: true
|
//reuseItems: true
|
||||||
|
@ -403,7 +406,7 @@ Item {
|
||||||
required property bool isEditable
|
required property bool isEditable
|
||||||
required property bool isEdited
|
required property bool isEdited
|
||||||
required property bool isStateEvent
|
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 replyTo
|
||||||
required property string threadId
|
required property string threadId
|
||||||
required property string userId
|
required property string userId
|
||||||
|
@ -417,9 +420,9 @@ Item {
|
||||||
required property int status
|
required property int status
|
||||||
required property int index
|
required property int index
|
||||||
required property int relatedEventCacheBuster
|
required property int relatedEventCacheBuster
|
||||||
required property string previousMessageUserId
|
|
||||||
required property string day
|
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
|
required property string userName
|
||||||
property bool scrolledToThis: eventId === room.scrollTarget && (y + height > chat.y + chat.contentY && y < chat.y + chat.height + chat.contentY)
|
property bool scrolledToThis: eventId === room.scrollTarget && (y + height > chat.y + chat.contentY && y < chat.y + chat.height + chat.contentY)
|
||||||
|
|
||||||
|
|
|
@ -66,6 +66,8 @@ Item {
|
||||||
spacing: 0
|
spacing: 0
|
||||||
|
|
||||||
TopBar {
|
TopBar {
|
||||||
|
id: topBar
|
||||||
|
|
||||||
showBackButton: timelineView.showBackButton
|
showBackButton: timelineView.showBackButton
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -102,6 +104,7 @@ Item {
|
||||||
|
|
||||||
MessageView {
|
MessageView {
|
||||||
implicitHeight: msgView.height - typingIndicator.height
|
implicitHeight: msgView.height - typingIndicator.height
|
||||||
|
searchString: topBar.searchString
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -25,6 +25,14 @@ Pane {
|
||||||
property bool isDirect: room ? room.isDirect : false
|
property bool isDirect: room ? room.isDirect : false
|
||||||
property string directChatOtherUserId: room ? room.directChatOtherUserId : ""
|
property string directChatOtherUserId: room ? room.directChatOtherUserId : ""
|
||||||
|
|
||||||
|
property string searchString: ""
|
||||||
|
|
||||||
|
onRoomIdChanged: {
|
||||||
|
searchString = "";
|
||||||
|
searchButton.searchActive = false;
|
||||||
|
searchField.text = ""
|
||||||
|
}
|
||||||
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
implicitHeight: topLayout.height + Nheko.paddingMedium * 2
|
implicitHeight: topLayout.height + Nheko.paddingMedium * 2
|
||||||
z: 3
|
z: 3
|
||||||
|
@ -177,12 +185,42 @@ Pane {
|
||||||
text: roomTopic
|
text: roomTopic
|
||||||
}
|
}
|
||||||
|
|
||||||
AbstractButton {
|
ImageButton {
|
||||||
|
id: pinButton
|
||||||
|
|
||||||
|
property bool pinsShown: !Settings.hiddenPins.includes(roomId)
|
||||||
|
|
||||||
|
visible: !!room && room.pinnedMessages.length > 0
|
||||||
Layout.column: 3
|
Layout.column: 3
|
||||||
Layout.row: 1
|
Layout.row: 1
|
||||||
Layout.rowSpan: 2
|
Layout.rowSpan: 2
|
||||||
|
Layout.alignment: Qt.AlignVCenter
|
||||||
Layout.preferredHeight: Nheko.avatarSize - Nheko.paddingMedium
|
Layout.preferredHeight: Nheko.avatarSize - Nheko.paddingMedium
|
||||||
Layout.preferredWidth: 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 {
|
contentItem: EncryptionIndicator {
|
||||||
sourceSize.height: parent.Layout.preferredHeight * Screen.devicePixelRatio
|
sourceSize.height: parent.Layout.preferredHeight * Screen.devicePixelRatio
|
||||||
|
@ -216,40 +254,37 @@ Pane {
|
||||||
}
|
}
|
||||||
|
|
||||||
ImageButton {
|
ImageButton {
|
||||||
id: pinButton
|
id: searchButton
|
||||||
|
|
||||||
property bool pinsShown: !Settings.hiddenPins.includes(roomId)
|
property bool searchActive: false
|
||||||
|
|
||||||
visible: !!room && room.pinnedMessages.length > 0
|
visible: !!room
|
||||||
Layout.column: 4
|
Layout.column: 5
|
||||||
Layout.row: 1
|
Layout.row: 1
|
||||||
Layout.rowSpan: 2
|
Layout.rowSpan: 2
|
||||||
Layout.alignment: Qt.AlignVCenter
|
Layout.alignment: Qt.AlignVCenter
|
||||||
Layout.preferredHeight: Nheko.avatarSize - Nheko.paddingMedium
|
Layout.preferredHeight: Nheko.avatarSize - Nheko.paddingMedium
|
||||||
Layout.preferredWidth: 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.visible: hovered
|
||||||
ToolTip.text: qsTr("Show or hide pinned messages")
|
ToolTip.text: qsTr("Search this room")
|
||||||
onClicked: {
|
onClicked: {
|
||||||
var ps = Settings.hiddenPins;
|
searchActive = !searchActive
|
||||||
if (pinsShown) {
|
if (searchActive) {
|
||||||
ps.push(roomId);
|
searchField.forceActiveFocus();
|
||||||
} else {
|
}
|
||||||
const index = ps.indexOf(roomId);
|
else {
|
||||||
if (index > -1) {
|
searchField.clear();
|
||||||
ps.splice(index, 1);
|
topBar.searchString = "";
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Settings.hiddenPins = ps;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ImageButton {
|
ImageButton {
|
||||||
id: roomOptionsButton
|
id: roomOptionsButton
|
||||||
|
|
||||||
visible: !!room
|
visible: !!room
|
||||||
Layout.column: 5
|
Layout.column: 6
|
||||||
Layout.row: 1
|
Layout.row: 1
|
||||||
Layout.rowSpan: 2
|
Layout.rowSpan: 2
|
||||||
Layout.alignment: Qt.AlignVCenter
|
Layout.alignment: Qt.AlignVCenter
|
||||||
|
@ -293,7 +328,7 @@ Pane {
|
||||||
|
|
||||||
Layout.row: 3
|
Layout.row: 3
|
||||||
Layout.column: 2
|
Layout.column: 2
|
||||||
Layout.columnSpan: 3
|
Layout.columnSpan: 4
|
||||||
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.preferredHeight: Math.min(contentHeight, Nheko.avatarSize * 4)
|
Layout.preferredHeight: Math.min(contentHeight, Nheko.avatarSize * 4)
|
||||||
|
@ -374,7 +409,7 @@ Pane {
|
||||||
|
|
||||||
Layout.row: 4
|
Layout.row: 4
|
||||||
Layout.column: 2
|
Layout.column: 2
|
||||||
Layout.columnSpan: 1
|
Layout.columnSpan: 4
|
||||||
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.preferredHeight: Math.min(contentHeight, Nheko.avatarSize * 1.5)
|
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 {
|
CursorShape {
|
||||||
|
|
|
@ -898,6 +898,7 @@ EventStore::fetchMore()
|
||||||
mtx::http::MessagesOpts opts;
|
mtx::http::MessagesOpts opts;
|
||||||
opts.room_id = room_id_;
|
opts.room_id = room_id_;
|
||||||
opts.from = cache::client()->previousBatchToken(room_id_);
|
opts.from = cache::client()->previousBatchToken(room_id_);
|
||||||
|
opts.limit = 80;
|
||||||
|
|
||||||
nhlog::ui()->debug("Paginating room {}, token {}", opts.room_id, opts.from);
|
nhlog::ui()->debug("Paginating room {}, token {}", opts.room_id, opts.from);
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
#include "TimelineFilter.h"
|
#include "TimelineFilter.h"
|
||||||
|
|
||||||
#include "Logging.h"
|
#include "Logging.h"
|
||||||
|
@ -19,6 +23,17 @@ TimelineFilter::setThreadId(const QString &t)
|
||||||
emit threadIdChanged();
|
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
|
void
|
||||||
TimelineFilter::setSource(TimelineModel *s)
|
TimelineFilter::setSource(TimelineModel *s)
|
||||||
{
|
{
|
||||||
|
@ -62,11 +77,20 @@ TimelineFilter::currentIndex() const
|
||||||
bool
|
bool
|
||||||
TimelineFilter::filterAcceptsRow(int source_row, const QModelIndex &) const
|
TimelineFilter::filterAcceptsRow(int source_row, const QModelIndex &) const
|
||||||
{
|
{
|
||||||
if (threadId.isEmpty())
|
if (threadId.isEmpty() && contentFilter.isEmpty())
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
if (auto s = sourceModel()) {
|
if (auto s = sourceModel()) {
|
||||||
auto idx = s->index(source_row, 0);
|
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 ||
|
return s->data(idx, TimelineModel::EventId) == threadId ||
|
||||||
s->data(idx, TimelineModel::ThreadId) == threadId;
|
s->data(idx, TimelineModel::ThreadId) == threadId;
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -16,6 +16,8 @@ class TimelineFilter : public QSortFilterProxyModel
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
Q_PROPERTY(QString filterByThread READ filterByThread WRITE setThreadId NOTIFY threadIdChanged)
|
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(TimelineModel *source READ source WRITE setSource NOTIFY sourceChanged)
|
||||||
Q_PROPERTY(int currentIndex READ currentIndex WRITE setCurrentIndex NOTIFY currentIndexChanged)
|
Q_PROPERTY(int currentIndex READ currentIndex WRITE setCurrentIndex NOTIFY currentIndexChanged)
|
||||||
|
|
||||||
|
@ -23,15 +25,23 @@ public:
|
||||||
explicit TimelineFilter(QObject *parent = nullptr);
|
explicit TimelineFilter(QObject *parent = nullptr);
|
||||||
|
|
||||||
QString filterByThread() const { return threadId; }
|
QString filterByThread() const { return threadId; }
|
||||||
|
QString filterByContent() const { return contentFilter; }
|
||||||
TimelineModel *source() const;
|
TimelineModel *source() const;
|
||||||
int currentIndex() const;
|
int currentIndex() const;
|
||||||
|
|
||||||
void setThreadId(const QString &t);
|
void setThreadId(const QString &t);
|
||||||
|
void setContentFilter(const QString &t);
|
||||||
void setSource(TimelineModel *t);
|
void setSource(TimelineModel *t);
|
||||||
void setCurrentIndex(int idx);
|
void setCurrentIndex(int idx);
|
||||||
|
|
||||||
|
Q_INVOKABLE QVariant dataByIndex(int i, int role = Qt::DisplayRole) const
|
||||||
|
{
|
||||||
|
return data(index(i, 0), role);
|
||||||
|
}
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void threadIdChanged();
|
void threadIdChanged();
|
||||||
|
void contentFilterChanged();
|
||||||
void sourceChanged();
|
void sourceChanged();
|
||||||
void currentIndexChanged();
|
void currentIndexChanged();
|
||||||
|
|
||||||
|
@ -39,5 +49,5 @@ protected:
|
||||||
bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const override;
|
bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QString threadId;
|
QString threadId, contentFilter;
|
||||||
};
|
};
|
||||||
|
|
|
@ -481,12 +481,9 @@ TimelineModel::roleNames() const
|
||||||
{IsOnlyEmoji, "isOnlyEmoji"},
|
{IsOnlyEmoji, "isOnlyEmoji"},
|
||||||
{Body, "body"},
|
{Body, "body"},
|
||||||
{FormattedBody, "formattedBody"},
|
{FormattedBody, "formattedBody"},
|
||||||
{PreviousMessageUserId, "previousMessageUserId"},
|
|
||||||
{IsSender, "isSender"},
|
{IsSender, "isSender"},
|
||||||
{UserId, "userId"},
|
{UserId, "userId"},
|
||||||
{UserName, "userName"},
|
{UserName, "userName"},
|
||||||
{PreviousMessageDay, "previousMessageDay"},
|
|
||||||
{PreviousMessageIsStateEvent, "previousMessageIsStateEvent"},
|
|
||||||
{Day, "day"},
|
{Day, "day"},
|
||||||
{Timestamp, "timestamp"},
|
{Timestamp, "timestamp"},
|
||||||
{Url, "url"},
|
{Url, "url"},
|
||||||
|
@ -804,22 +801,6 @@ TimelineModel::data(const QModelIndex &index, int role) const
|
||||||
if (!event)
|
if (!event)
|
||||||
return "";
|
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);
|
return data(*event, role);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -214,12 +214,9 @@ public:
|
||||||
IsOnlyEmoji,
|
IsOnlyEmoji,
|
||||||
Body,
|
Body,
|
||||||
FormattedBody,
|
FormattedBody,
|
||||||
PreviousMessageUserId,
|
|
||||||
IsSender,
|
IsSender,
|
||||||
UserId,
|
UserId,
|
||||||
UserName,
|
UserName,
|
||||||
PreviousMessageDay,
|
|
||||||
PreviousMessageIsStateEvent,
|
|
||||||
Day,
|
Day,
|
||||||
Timestamp,
|
Timestamp,
|
||||||
Url,
|
Url,
|
||||||
|
@ -257,6 +254,10 @@ public:
|
||||||
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
|
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
|
||||||
QVariant data(const mtx::events::collections::TimelineEvents &event, int role) const;
|
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 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;
|
bool canFetchMore(const QModelIndex &) const override;
|
||||||
void fetchMore(const QModelIndex &) override;
|
void fetchMore(const QModelIndex &) override;
|
||||||
|
|
Loading…
Reference in a new issue