Add a button to cycle through unread highlights

BUG: 465095
This commit is contained in:
Azhar Momin
2025-12-28 23:02:15 +05:30
committed by James Graham
parent fd44ff972a
commit 58ea229b67
3 changed files with 121 additions and 20 deletions

View File

@@ -169,6 +169,7 @@ NeoChatRoom::NeoChatRoom(Connection *connection, QString roomId, JoinState joinS
const auto neochatconnection = static_cast<NeoChatConnection *>(connection);
Q_ASSERT(neochatconnection);
connect(neochatconnection, &NeoChatConnection::globalUrlPreviewEnabledChanged, this, &NeoChatRoom::urlPreviewEnabledChanged);
connect(this, &Room::fullyReadMarkerMoved, this, &NeoChatRoom::invalidateLastUnreadHighlightId);
}
bool NeoChatRoom::visible() const
@@ -1843,4 +1844,52 @@ void NeoChatRoom::report(const QString &reason)
connection()->callApi<NeochatReportRoomJob>(id(), reason);
}
QString NeoChatRoom::findNextUnreadHighlightId()
{
const QString startEventId = !m_lastUnreadHighlightId.isEmpty() ? m_lastUnreadHighlightId : lastFullyReadEventId();
const auto startIt = findInTimeline(startEventId);
if (startIt == historyEdge()) {
return {};
}
for (auto it = startIt.base(); it != messageEvents().cend(); ++it) {
const RoomEvent *ev = it->event();
if (highlights.contains(ev)) {
m_lastUnreadHighlightId = ev->id();
Q_EMIT highlightCycleStartedChanged();
return m_lastUnreadHighlightId;
}
}
if (!m_lastUnreadHighlightId.isEmpty()) {
m_lastUnreadHighlightId.clear();
Q_EMIT highlightCycleStartedChanged();
return findNextUnreadHighlightId();
}
return {};
}
bool NeoChatRoom::highlightCycleStarted() const
{
return !m_lastUnreadHighlightId.isEmpty();
}
void NeoChatRoom::invalidateLastUnreadHighlightId(const QString &fromEventId, const QString &toEventId)
{
Q_UNUSED(fromEventId);
if (m_lastUnreadHighlightId.isEmpty()) {
return;
}
const auto lastIt = findInTimeline(m_lastUnreadHighlightId);
const auto newReadIt = findInTimeline(toEventId);
// opposite comparision because both are reverse iterators :p
if (newReadIt <= lastIt) {
m_lastUnreadHighlightId.clear();
Q_EMIT highlightCycleStartedChanged();
}
}
#include "moc_neochatroom.cpp"

View File

@@ -208,6 +208,11 @@ class NeoChatRoom : public Quotient::Room
*/
Q_PROPERTY(QString pinnedMessage READ pinnedMessage NOTIFY pinnedMessageChanged)
/**
* @brief Whether the highlight finding cycle has started.
*/
Q_PROPERTY(bool highlightCycleStarted READ highlightCycleStarted NOTIFY highlightCycleStartedChanged)
public:
explicit NeoChatRoom(Quotient::Connection *connection, QString roomId, Quotient::JoinState joinState = {});
@@ -627,6 +632,19 @@ public:
*/
Q_INVOKABLE void report(const QString &reason);
/**
* @brief Returns the ID of the next unread highlight in the room.
*
* Each call advances the internal highlight cursor. Once the last unread highlight
* is reached, the cycle is reset.
*/
Q_INVOKABLE QString findNextUnreadHighlightId();
/**
* @brief Whether the highlight finding cycle has started.
*/
bool highlightCycleStarted() const;
private:
bool m_visible = false;
@@ -662,11 +680,15 @@ private:
QString m_pinnedMessage;
void loadPinnedMessage();
QString m_lastUnreadHighlightId;
private Q_SLOTS:
void updatePushNotificationState(QString type);
void cacheLastEvent();
void invalidateLastUnreadHighlightId(const QString &fromEventId, const QString &toEventId);
Q_SIGNALS:
void cachedInputChanged();
void busyChanged();
@@ -692,6 +714,7 @@ Q_SIGNALS:
void extraEventNotFound(const QString &eventId);
void inviteTimestampChanged();
void pinnedMessageChanged();
void highlightCycleStartedChanged();
/**
* @brief Request a message be shown to the user of the given type.

View File

@@ -193,9 +193,7 @@ QQC2.ScrollView {
room: _private.room
}
KirigamiComponents.FloatingButton {
id: goReadMarkerFab
ColumnLayout {
anchors {
right: parent.right
top: parent.top
@@ -203,28 +201,59 @@ QQC2.ScrollView {
rightMargin: Kirigami.Units.largeSpacing
}
implicitWidth: Kirigami.Settings.hasTransientTouchInput ? Kirigami.Units.gridUnit * 3 : Kirigami.Units.gridUnit * 2
implicitHeight: Kirigami.Settings.hasTransientTouchInput ? Kirigami.Units.gridUnit * 3 : Kirigami.Units.gridUnit * 2
spacing: Kirigami.Units.largeSpacing
padding: Kirigami.Units.largeSpacing
KirigamiComponents.FloatingButton {
id: goReadMarkerFab
z: 2
visible: !_private.room?.partiallyReadStats.empty()
implicitWidth: Kirigami.Settings.hasTransientTouchInput ? Kirigami.Units.gridUnit * 3 : Kirigami.Units.gridUnit * 2
implicitHeight: Kirigami.Settings.hasTransientTouchInput ? Kirigami.Units.gridUnit * 3 : Kirigami.Units.gridUnit * 2
text: _private.room.readMarkerLoaded ? i18nc("@action:button", "Jump to first unread message") : i18nc("@action:button", "Jump to oldest loaded message")
icon.name: "go-up"
onClicked: {
goReadMarkerFab.textChanged()
root.goToEvent(_private.room.lastFullyReadEventId);
}
Kirigami.Action {
shortcut: "Shift+PgUp"
onTriggered: goReadMarkerFab.clicked()
padding: Kirigami.Units.largeSpacing
z: 2
visible: !_private.room?.partiallyReadStats.empty()
text: _private.room.readMarkerLoaded ? i18nc("@action:button", "Jump to first unread message") : i18nc("@action:button", "Jump to oldest loaded message")
icon.name: "go-up"
onClicked: {
goReadMarkerFab.textChanged()
root.goToEvent(_private.room.lastFullyReadEventId);
}
Kirigami.Action {
shortcut: "Shift+PgUp"
onTriggered: goReadMarkerFab.clicked()
}
QQC2.ToolTip.text: goReadMarkerFab.text
QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay
QQC2.ToolTip.visible: goReadMarkerFab.hovered
}
KirigamiComponents.FloatingButton {
id: goUnreadHighlightFab
QQC2.ToolTip.text: goReadMarkerFab.text
QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay
QQC2.ToolTip.visible: goReadMarkerFab.hovered
implicitWidth: Kirigami.Settings.hasTransientTouchInput ? Kirigami.Units.gridUnit * 3 : Kirigami.Units.gridUnit * 2
implicitHeight: Kirigami.Settings.hasTransientTouchInput ? Kirigami.Units.gridUnit * 3 : Kirigami.Units.gridUnit * 2
padding: Kirigami.Units.largeSpacing
z: 2
visible: _private.room?.highlightCount > 0
text: _private.room?.highlightCycleStarted ? i18nc("@action:button", "Jump to next unread highlight") : i18nc("@action:button", "Jump to first unread message")
icon.name: "mail-unread-symbolic"
onClicked: {
const eventId = _private.room.findNextUnreadHighlightId();
if (eventId !== "") {
goUnreadHighlightFab.textChanged();
root.goToEvent(eventId);
}
}
QQC2.ToolTip.text: goUnreadHighlightFab.text
QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay
QQC2.ToolTip.visible: goUnreadHighlightFab.hovered
}
}
KirigamiComponents.FloatingButton {
id: goMarkAsReadFab