Allow filtering the timeline for notifications
This commit is contained in:
parent
5b025fa2b0
commit
53cd31d181
7 changed files with 73 additions and 11 deletions
1
resources/icons/ui/alert.svg
Normal file
1
resources/icons/ui/alert.svg
Normal 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 |
|
|
@ -16,6 +16,7 @@ Item {
|
||||||
property int availableWidth: width
|
property int availableWidth: width
|
||||||
property int padding: Nheko.paddingMedium
|
property int padding: Nheko.paddingMedium
|
||||||
property string searchString: ""
|
property string searchString: ""
|
||||||
|
property bool filterByNotifications: false
|
||||||
property Room roommodel: room
|
property Room roommodel: room
|
||||||
|
|
||||||
// HACK: https://bugreports.qt.io/browse/QTBUG-83972, qtwayland cannot auto hide menu
|
// HACK: https://bugreports.qt.io/browse/QTBUG-83972, qtwayland cannot auto hide menu
|
||||||
|
|
@ -60,7 +61,7 @@ Item {
|
||||||
boundsBehavior: Flickable.StopAtBounds
|
boundsBehavior: Flickable.StopAtBounds
|
||||||
displayMarginBeginning: height / 4
|
displayMarginBeginning: height / 4
|
||||||
displayMarginEnd: height / 4
|
displayMarginEnd: height / 4
|
||||||
model: (filteredTimeline.filterByThread || filteredTimeline.filterByContent) ? filteredTimeline : room
|
model: (filteredTimeline.filterByThread || filteredTimeline.filterByContent || filteredTimeline.filterByNotifications) ? filteredTimeline : room
|
||||||
//pixelAligned: true
|
//pixelAligned: true
|
||||||
spacing: 2
|
spacing: 2
|
||||||
verticalLayoutDirection: ListView.BottomToTop
|
verticalLayoutDirection: ListView.BottomToTop
|
||||||
|
|
@ -145,6 +146,7 @@ Item {
|
||||||
id: filteredTimeline
|
id: filteredTimeline
|
||||||
|
|
||||||
filterByContent: chatRoot.searchString
|
filterByContent: chatRoot.searchString
|
||||||
|
filterByNotifications: chatRoot.filterByNotifications
|
||||||
filterByThread: room ? room.thread : ""
|
filterByThread: room ? room.thread : ""
|
||||||
source: room
|
source: room
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -119,6 +119,7 @@ Item {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
implicitHeight: msgView.height - typingIndicator.height
|
implicitHeight: msgView.height - typingIndicator.height
|
||||||
searchString: topBar.searchString
|
searchString: topBar.searchString
|
||||||
|
filterByNotifications: topBar.filterNotifications
|
||||||
}
|
}
|
||||||
Loader {
|
Loader {
|
||||||
source: CallManager.isOnCall && CallManager.callType != Voip.VOICE ? (Qt.platform.os != "windows" ? "voip/VideoCall.qml" : "voip/VideoCallD3D11.qml") : ""
|
source: CallManager.isOnCall && CallManager.callType != Voip.VOICE ? (Qt.platform.os != "windows" ? "voip/VideoCall.qml" : "voip/VideoCallD3D11.qml") : ""
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@ Pane {
|
||||||
property bool searchHasFocus: searchField.focus && searchField.enabled
|
property bool searchHasFocus: searchField.focus && searchField.enabled
|
||||||
property string searchString: ""
|
property string searchString: ""
|
||||||
property bool showBackButton: false
|
property bool showBackButton: false
|
||||||
|
property bool filterNotifications: false
|
||||||
property int trustlevel: room ? room.trustlevel : Crypto.Unverified
|
property int trustlevel: room ? room.trustlevel : Crypto.Unverified
|
||||||
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
|
@ -129,13 +130,30 @@ Pane {
|
||||||
selectByMouse: false
|
selectByMouse: false
|
||||||
text: roomTopic
|
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 {
|
ImageButton {
|
||||||
id: pinButton
|
id: pinButton
|
||||||
|
|
||||||
property bool pinsShown: !Settings.hiddenPins.includes(roomId)
|
property bool pinsShown: !Settings.hiddenPins.includes(roomId)
|
||||||
|
|
||||||
Layout.alignment: Qt.AlignVCenter
|
Layout.alignment: Qt.AlignVCenter
|
||||||
Layout.column: 3
|
Layout.column: 4
|
||||||
Layout.preferredHeight: Nheko.avatarSize - Nheko.paddingMedium
|
Layout.preferredHeight: Nheko.avatarSize - Nheko.paddingMedium
|
||||||
Layout.preferredWidth: Nheko.avatarSize - Nheko.paddingMedium
|
Layout.preferredWidth: Nheko.avatarSize - Nheko.paddingMedium
|
||||||
Layout.row: 1
|
Layout.row: 1
|
||||||
|
|
@ -160,7 +178,7 @@ Pane {
|
||||||
}
|
}
|
||||||
AbstractButton {
|
AbstractButton {
|
||||||
id: memberButton
|
id: memberButton
|
||||||
Layout.column: 4
|
Layout.column: 5
|
||||||
Layout.preferredHeight: Nheko.avatarSize - Nheko.paddingMedium
|
Layout.preferredHeight: Nheko.avatarSize - Nheko.paddingMedium
|
||||||
Layout.preferredWidth: Nheko.avatarSize - Nheko.paddingMedium
|
Layout.preferredWidth: Nheko.avatarSize - Nheko.paddingMedium
|
||||||
Layout.row: 1
|
Layout.row: 1
|
||||||
|
|
@ -200,7 +218,7 @@ Pane {
|
||||||
property bool searchActive: false
|
property bool searchActive: false
|
||||||
|
|
||||||
Layout.alignment: Qt.AlignVCenter
|
Layout.alignment: Qt.AlignVCenter
|
||||||
Layout.column: 5
|
Layout.column: 6
|
||||||
Layout.preferredHeight: Nheko.avatarSize - Nheko.paddingMedium
|
Layout.preferredHeight: Nheko.avatarSize - Nheko.paddingMedium
|
||||||
Layout.preferredWidth: Nheko.avatarSize - Nheko.paddingMedium
|
Layout.preferredWidth: Nheko.avatarSize - Nheko.paddingMedium
|
||||||
Layout.row: 1
|
Layout.row: 1
|
||||||
|
|
@ -224,7 +242,7 @@ Pane {
|
||||||
id: roomOptionsButton
|
id: roomOptionsButton
|
||||||
|
|
||||||
Layout.alignment: Qt.AlignVCenter
|
Layout.alignment: Qt.AlignVCenter
|
||||||
Layout.column: 6
|
Layout.column: 7
|
||||||
Layout.preferredHeight: Nheko.avatarSize - Nheko.paddingMedium
|
Layout.preferredHeight: Nheko.avatarSize - Nheko.paddingMedium
|
||||||
Layout.preferredWidth: Nheko.avatarSize - Nheko.paddingMedium
|
Layout.preferredWidth: Nheko.avatarSize - Nheko.paddingMedium
|
||||||
Layout.row: 1
|
Layout.row: 1
|
||||||
|
|
@ -273,7 +291,7 @@ Pane {
|
||||||
id: pinnedMessages
|
id: pinnedMessages
|
||||||
|
|
||||||
Layout.column: 2
|
Layout.column: 2
|
||||||
Layout.columnSpan: 4
|
Layout.columnSpan: 5
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.preferredHeight: Math.min(contentHeight, Nheko.avatarSize * 4)
|
Layout.preferredHeight: Math.min(contentHeight, Nheko.avatarSize * 4)
|
||||||
Layout.row: 3
|
Layout.row: 3
|
||||||
|
|
@ -330,7 +348,7 @@ Pane {
|
||||||
id: widgets
|
id: widgets
|
||||||
|
|
||||||
Layout.column: 2
|
Layout.column: 2
|
||||||
Layout.columnSpan: 4
|
Layout.columnSpan: 5
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.preferredHeight: Math.min(contentHeight, Nheko.avatarSize * 1.5)
|
Layout.preferredHeight: Math.min(contentHeight, Nheko.avatarSize * 1.5)
|
||||||
Layout.row: 4
|
Layout.row: 4
|
||||||
|
|
@ -356,7 +374,7 @@ Pane {
|
||||||
id: searchField
|
id: searchField
|
||||||
|
|
||||||
Layout.column: 2
|
Layout.column: 2
|
||||||
Layout.columnSpan: 4
|
Layout.columnSpan: 5
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.row: 5
|
Layout.row: 5
|
||||||
enabled: visible
|
enabled: visible
|
||||||
|
|
@ -378,6 +396,7 @@ Pane {
|
||||||
searchString = "";
|
searchString = "";
|
||||||
searchButton.searchActive = false;
|
searchButton.searchActive = false;
|
||||||
searchField.text = "";
|
searchField.text = "";
|
||||||
|
filterNotifications = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// HACK: https://bugreports.qt.io/browse/QTBUG-83972, qtwayland cannot auto hide menu
|
// HACK: https://bugreports.qt.io/browse/QTBUG-83972, qtwayland cannot auto hide menu
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
<RCC>
|
<RCC>
|
||||||
<qresource prefix="/icons">
|
<qresource prefix="/icons">
|
||||||
<file>icons/ui/add-square-button.svg</file>
|
<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/angle-arrow-left.svg</file>
|
||||||
<file>icons/ui/attach.svg</file>
|
<file>icons/ui/attach.svg</file>
|
||||||
<file>icons/ui/ban.svg</file>
|
<file>icons/ui/ban.svg</file>
|
||||||
|
|
|
||||||
|
|
@ -96,6 +96,10 @@ TimelineFilter::setThreadId(const QString &t)
|
||||||
{
|
{
|
||||||
nhlog::ui()->debug("Filtering by thread '{}'", t.toStdString());
|
nhlog::ui()->debug("Filtering by thread '{}'", t.toStdString());
|
||||||
if (this->threadId != t) {
|
if (this->threadId != t) {
|
||||||
|
#if QT_VERSION >= QT_VERSION_CHECK(6, 9, 0)
|
||||||
|
beginFilterChange();
|
||||||
|
#endif
|
||||||
|
|
||||||
this->threadId = t;
|
this->threadId = t;
|
||||||
|
|
||||||
emit threadIdChanged();
|
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
|
void
|
||||||
TimelineFilter::setContentFilter(const QString &c)
|
TimelineFilter::setContentFilter(const QString &c)
|
||||||
{
|
{
|
||||||
nhlog::ui()->debug("Filtering by content '{}'", c.toStdString());
|
nhlog::ui()->debug("Filtering by content '{}'", c.toStdString());
|
||||||
if (this->contentFilter != c) {
|
if (this->contentFilter != c) {
|
||||||
|
#if QT_VERSION >= QT_VERSION_CHECK(6, 9, 0)
|
||||||
|
beginFilterChange();
|
||||||
|
#endif
|
||||||
this->contentFilter = c;
|
this->contentFilter = c;
|
||||||
|
|
||||||
emit contentFilterChanged();
|
emit contentFilterChanged();
|
||||||
|
|
@ -145,7 +168,8 @@ TimelineFilter::sourceDataChanged(const QModelIndex &topLeft,
|
||||||
const QModelIndex &bottomRight,
|
const QModelIndex &bottomRight,
|
||||||
const QVector<int> &roles)
|
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;
|
return;
|
||||||
|
|
||||||
if (auto s = source()) {
|
if (auto s = source()) {
|
||||||
|
|
@ -233,19 +257,27 @@ TimelineFilter::filterAcceptsRow(int source_row, const QModelIndex &) const
|
||||||
if (source_row > incrementalSearchIndex)
|
if (source_row > incrementalSearchIndex)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (threadId.isEmpty() && contentFilter.isEmpty())
|
if (threadId.isEmpty() && contentFilter.isEmpty() && !filterByNotifications_)
|
||||||
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)
|
if (!contentFilter.isEmpty() && !s->data(idx, TimelineModel::Body)
|
||||||
.toString()
|
.toString()
|
||||||
.contains(contentFilter, Qt::CaseInsensitive)) {
|
.contains(contentFilter, Qt::CaseInsensitive)) {
|
||||||
return false;
|
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 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;
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,8 @@ class TimelineFilter : public QSortFilterProxyModel
|
||||||
QML_ELEMENT
|
QML_ELEMENT
|
||||||
|
|
||||||
Q_PROPERTY(QString filterByThread READ filterByThread WRITE setThreadId NOTIFY threadIdChanged)
|
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
|
Q_PROPERTY(QString filterByContent READ filterByContent WRITE setContentFilter NOTIFY
|
||||||
contentFilterChanged)
|
contentFilterChanged)
|
||||||
Q_PROPERTY(TimelineModel *source READ source WRITE setSource NOTIFY sourceChanged)
|
Q_PROPERTY(TimelineModel *source READ source WRITE setSource NOTIFY sourceChanged)
|
||||||
|
|
@ -28,12 +30,14 @@ public:
|
||||||
explicit TimelineFilter(QObject *parent = nullptr);
|
explicit TimelineFilter(QObject *parent = nullptr);
|
||||||
|
|
||||||
QString filterByThread() const { return threadId; }
|
QString filterByThread() const { return threadId; }
|
||||||
|
bool filterByNotifications() const { return filterByNotifications_; }
|
||||||
QString filterByContent() const { return contentFilter; }
|
QString filterByContent() const { return contentFilter; }
|
||||||
TimelineModel *source() const;
|
TimelineModel *source() const;
|
||||||
int currentIndex() const;
|
int currentIndex() const;
|
||||||
bool isFiltering() const;
|
bool isFiltering() const;
|
||||||
|
|
||||||
void setThreadId(const QString &t);
|
void setThreadId(const QString &t);
|
||||||
|
void setFilterNotifications(bool v);
|
||||||
void setContentFilter(const QString &t);
|
void setContentFilter(const QString &t);
|
||||||
void setSource(TimelineModel *t);
|
void setSource(TimelineModel *t);
|
||||||
void setCurrentIndex(int idx);
|
void setCurrentIndex(int idx);
|
||||||
|
|
@ -47,6 +51,7 @@ public:
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void threadIdChanged();
|
void threadIdChanged();
|
||||||
|
void filterNotificationsChanged();
|
||||||
void contentFilterChanged();
|
void contentFilterChanged();
|
||||||
void sourceChanged();
|
void sourceChanged();
|
||||||
void currentIndexChanged();
|
void currentIndexChanged();
|
||||||
|
|
@ -67,4 +72,5 @@ private:
|
||||||
|
|
||||||
QString threadId, contentFilter;
|
QString threadId, contentFilter;
|
||||||
int cachedCount = 0, incrementalSearchIndex = 0;
|
int cachedCount = 0, incrementalSearchIndex = 0;
|
||||||
|
bool filterByNotifications_ = false;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue