Allow filtering the timeline for notifications

This commit is contained in:
Nicolas Werner 2025-09-13 01:01:06 +02:00
parent 5b025fa2b0
commit 53cd31d181
No known key found for this signature in database
GPG key ID: C8D75E610773F2D9
7 changed files with 73 additions and 11 deletions

View file

@ -0,0 +1 @@
<svg width="32" height="32" fill="none" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M12 1.996a7.49 7.49 0 0 1 7.496 7.25l.004.25v4.097l1.38 3.156a1.25 1.25 0 0 1-1.145 1.75L15 18.502a3 3 0 0 1-5.995.177L9 18.499H4.275a1.251 1.251 0 0 1-1.147-1.747L4.5 13.594V9.496c0-4.155 3.352-7.5 7.5-7.5ZM13.5 18.5l-3 .002a1.5 1.5 0 0 0 2.993.145l.006-.147ZM12 3.496c-3.32 0-6 2.674-6 6v4.41L4.656 17h14.697L18 13.907V9.509l-.004-.225A5.988 5.988 0 0 0 12 3.496Z" fill="#212121"/></svg>

After

Width:  |  Height:  |  Size: 494 B

View file

@ -16,6 +16,7 @@ Item {
property int availableWidth: width
property int padding: Nheko.paddingMedium
property string searchString: ""
property bool filterByNotifications: false
property Room roommodel: room
// HACK: https://bugreports.qt.io/browse/QTBUG-83972, qtwayland cannot auto hide menu
@ -60,7 +61,7 @@ Item {
boundsBehavior: Flickable.StopAtBounds
displayMarginBeginning: height / 4
displayMarginEnd: height / 4
model: (filteredTimeline.filterByThread || filteredTimeline.filterByContent) ? filteredTimeline : room
model: (filteredTimeline.filterByThread || filteredTimeline.filterByContent || filteredTimeline.filterByNotifications) ? filteredTimeline : room
//pixelAligned: true
spacing: 2
verticalLayoutDirection: ListView.BottomToTop
@ -145,6 +146,7 @@ Item {
id: filteredTimeline
filterByContent: chatRoot.searchString
filterByNotifications: chatRoot.filterByNotifications
filterByThread: room ? room.thread : ""
source: room
}

View file

@ -119,6 +119,7 @@ Item {
Layout.fillWidth: true
implicitHeight: msgView.height - typingIndicator.height
searchString: topBar.searchString
filterByNotifications: topBar.filterNotifications
}
Loader {
source: CallManager.isOnCall && CallManager.callType != Voip.VOICE ? (Qt.platform.os != "windows" ? "voip/VideoCall.qml" : "voip/VideoCallD3D11.qml") : ""

View file

@ -22,6 +22,7 @@ Pane {
property bool searchHasFocus: searchField.focus && searchField.enabled
property string searchString: ""
property bool showBackButton: false
property bool filterNotifications: false
property int trustlevel: room ? room.trustlevel : Crypto.Unverified
Layout.fillWidth: true
@ -129,13 +130,30 @@ Pane {
selectByMouse: false
text: roomTopic
}
ImageButton {
id: notificationsButton
Layout.alignment: Qt.AlignRight
Layout.column: 3
Layout.preferredHeight: Nheko.avatarSize - Nheko.paddingMedium
Layout.preferredWidth: Nheko.avatarSize - Nheko.paddingMedium
Layout.row: 1
Layout.rowSpan: 2
ToolTip.text: qsTr("Show only notifications")
ToolTip.visible: hovered
image: ":/icons/icons/ui/alert.svg"
onClicked: {
topBar.filterNotifications = !topBar.filterNotifications
}
}
ImageButton {
id: pinButton
property bool pinsShown: !Settings.hiddenPins.includes(roomId)
Layout.alignment: Qt.AlignVCenter
Layout.column: 3
Layout.column: 4
Layout.preferredHeight: Nheko.avatarSize - Nheko.paddingMedium
Layout.preferredWidth: Nheko.avatarSize - Nheko.paddingMedium
Layout.row: 1
@ -160,7 +178,7 @@ Pane {
}
AbstractButton {
id: memberButton
Layout.column: 4
Layout.column: 5
Layout.preferredHeight: Nheko.avatarSize - Nheko.paddingMedium
Layout.preferredWidth: Nheko.avatarSize - Nheko.paddingMedium
Layout.row: 1
@ -200,7 +218,7 @@ Pane {
property bool searchActive: false
Layout.alignment: Qt.AlignVCenter
Layout.column: 5
Layout.column: 6
Layout.preferredHeight: Nheko.avatarSize - Nheko.paddingMedium
Layout.preferredWidth: Nheko.avatarSize - Nheko.paddingMedium
Layout.row: 1
@ -224,7 +242,7 @@ Pane {
id: roomOptionsButton
Layout.alignment: Qt.AlignVCenter
Layout.column: 6
Layout.column: 7
Layout.preferredHeight: Nheko.avatarSize - Nheko.paddingMedium
Layout.preferredWidth: Nheko.avatarSize - Nheko.paddingMedium
Layout.row: 1
@ -273,7 +291,7 @@ Pane {
id: pinnedMessages
Layout.column: 2
Layout.columnSpan: 4
Layout.columnSpan: 5
Layout.fillWidth: true
Layout.preferredHeight: Math.min(contentHeight, Nheko.avatarSize * 4)
Layout.row: 3
@ -330,7 +348,7 @@ Pane {
id: widgets
Layout.column: 2
Layout.columnSpan: 4
Layout.columnSpan: 5
Layout.fillWidth: true
Layout.preferredHeight: Math.min(contentHeight, Nheko.avatarSize * 1.5)
Layout.row: 4
@ -356,7 +374,7 @@ Pane {
id: searchField
Layout.column: 2
Layout.columnSpan: 4
Layout.columnSpan: 5
Layout.fillWidth: true
Layout.row: 5
enabled: visible
@ -378,6 +396,7 @@ Pane {
searchString = "";
searchButton.searchActive = false;
searchField.text = "";
filterNotifications = false;
}
// HACK: https://bugreports.qt.io/browse/QTBUG-83972, qtwayland cannot auto hide menu

View file

@ -1,6 +1,7 @@
<RCC>
<qresource prefix="/icons">
<file>icons/ui/add-square-button.svg</file>
<file>icons/ui/alert.svg</file>
<file>icons/ui/angle-arrow-left.svg</file>
<file>icons/ui/attach.svg</file>
<file>icons/ui/ban.svg</file>

View file

@ -96,6 +96,10 @@ TimelineFilter::setThreadId(const QString &t)
{
nhlog::ui()->debug("Filtering by thread '{}'", t.toStdString());
if (this->threadId != t) {
#if QT_VERSION >= QT_VERSION_CHECK(6, 9, 0)
beginFilterChange();
#endif
this->threadId = t;
emit threadIdChanged();
@ -104,11 +108,30 @@ TimelineFilter::setThreadId(const QString &t)
}
}
void
TimelineFilter::setFilterNotifications(bool filter)
{
nhlog::ui()->debug("Filtering by notifications '{}'", filter);
if (this->filterByNotifications_ != filter) {
#if QT_VERSION >= QT_VERSION_CHECK(6, 9, 0)
beginFilterChange();
#endif
this->filterByNotifications_ = filter;
emit filterNotificationsChanged();
startFiltering();
fetchMore({});
}
}
void
TimelineFilter::setContentFilter(const QString &c)
{
nhlog::ui()->debug("Filtering by content '{}'", c.toStdString());
if (this->contentFilter != c) {
#if QT_VERSION >= QT_VERSION_CHECK(6, 9, 0)
beginFilterChange();
#endif
this->contentFilter = c;
emit contentFilterChanged();
@ -145,7 +168,8 @@ TimelineFilter::sourceDataChanged(const QModelIndex &topLeft,
const QModelIndex &bottomRight,
const QVector<int> &roles)
{
if (!roles.contains(TimelineModel::Roles::Body) && !roles.contains(TimelineModel::ThreadId))
if (!roles.contains(TimelineModel::Roles::Body) && !roles.contains(TimelineModel::ThreadId) &&
!roles.contains(TimelineModel::Notificationlevel))
return;
if (auto s = source()) {
@ -233,19 +257,27 @@ TimelineFilter::filterAcceptsRow(int source_row, const QModelIndex &) const
if (source_row > incrementalSearchIndex)
return false;
if (threadId.isEmpty() && contentFilter.isEmpty())
if (threadId.isEmpty() && contentFilter.isEmpty() && !filterByNotifications_)
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())
if (filterByNotifications_ && s->data(idx, TimelineModel::Notificationlevel)
.value<qml_mtx_events::NotificationLevel>() !=
qml_mtx_events::NotificationLevel::Highlight) {
return false;
}
if (threadId.isEmpty()) {
return true;
}
return s->data(idx, TimelineModel::EventId) == threadId ||
s->data(idx, TimelineModel::ThreadId) == threadId;

View file

@ -18,6 +18,8 @@ class TimelineFilter : public QSortFilterProxyModel
QML_ELEMENT
Q_PROPERTY(QString filterByThread READ filterByThread WRITE setThreadId NOTIFY threadIdChanged)
Q_PROPERTY(bool filterByNotifications READ filterByNotifications WRITE setFilterNotifications
NOTIFY filterNotificationsChanged)
Q_PROPERTY(QString filterByContent READ filterByContent WRITE setContentFilter NOTIFY
contentFilterChanged)
Q_PROPERTY(TimelineModel *source READ source WRITE setSource NOTIFY sourceChanged)
@ -28,12 +30,14 @@ public:
explicit TimelineFilter(QObject *parent = nullptr);
QString filterByThread() const { return threadId; }
bool filterByNotifications() const { return filterByNotifications_; }
QString filterByContent() const { return contentFilter; }
TimelineModel *source() const;
int currentIndex() const;
bool isFiltering() const;
void setThreadId(const QString &t);
void setFilterNotifications(bool v);
void setContentFilter(const QString &t);
void setSource(TimelineModel *t);
void setCurrentIndex(int idx);
@ -47,6 +51,7 @@ public:
signals:
void threadIdChanged();
void filterNotificationsChanged();
void contentFilterChanged();
void sourceChanged();
void currentIndexChanged();
@ -67,4 +72,5 @@ private:
QString threadId, contentFilter;
int cachedCount = 0, incrementalSearchIndex = 0;
bool filterByNotifications_ = false;
};