diff --git a/src/backend/html_markdown.py b/src/backend/html_markdown.py
index cbcbea2e..37978ac4 100644
--- a/src/backend/html_markdown.py
+++ b/src/backend/html_markdown.py
@@ -7,6 +7,7 @@ from typing import DefaultDict, Dict
from urllib.parse import unquote
import html_sanitizer.sanitizer as sanitizer
+import lxml.html # nosec
import mistune
from html_sanitizer.sanitizer import Sanitizer
from lxml.html import HtmlElement, etree # nosec
@@ -173,6 +174,19 @@ class HTMLProcessor:
]
+ def user_id_link_in_html(self, html: str, user_id: str) -> bool:
+ if not html.strip():
+ return False
+
+ regex = re.compile(rf"https?://matrix.to/#/{user_id}", re.IGNORECASE)
+
+ for _, _, href, _ in lxml.html.iterlinks(html):
+ if regex.match(unquote(href.strip())):
+ return True
+
+ return False
+
+
def from_markdown(
self,
text: str,
diff --git a/src/backend/matrix_client.py b/src/backend/matrix_client.py
index 09dcd024..b360003a 100644
--- a/src/backend/matrix_client.py
+++ b/src/backend/matrix_client.py
@@ -1073,9 +1073,11 @@ class MatrixClient(nio.AsyncClient):
registered = self.models[self.user_id, "rooms"][room.room_id]
last_event_date = registered.last_event_date
typing_members = registered.typing_members
+ mentions = registered.mentions
except KeyError:
last_event_date = datetime.fromtimestamp(0)
typing_members = []
+ mentions = 0
self.models[self.user_id, "rooms"][room.room_id] = Room(
id = room.room_id,
@@ -1108,6 +1110,7 @@ class MatrixClient(nio.AsyncClient):
can_set_guest_access = can_send_state("m.room.guest_access"),
last_event_date = last_event_date,
+ mentions = mentions,
)
diff --git a/src/backend/models/items.py b/src/backend/models/items.py
index b5ecb34a..7732b267 100644
--- a/src/backend/models/items.py
+++ b/src/backend/models/items.py
@@ -80,6 +80,8 @@ class Room(ModelItem):
last_event_date: datetime = ZeroDate
+ mentions: int = 0
+
def __lt__(self, other: "Room") -> bool:
"""Sort by join state, then descending last event date, then name.
diff --git a/src/backend/nio_callbacks.py b/src/backend/nio_callbacks.py
index ade38076..c9c7007f 100644
--- a/src/backend/nio_callbacks.py
+++ b/src/backend/nio_callbacks.py
@@ -100,6 +100,11 @@ class NioCallbacks:
room_id = room.room_id,
)
+
+ if HTML_PROCESSOR.user_id_link_in_html(co, self.client.user_id):
+ rooms = self.client.models[self.client.user_id, "rooms"]
+ rooms[room.room_id].mentions += 1
+
await self.client.register_nio_event(room, ev, content=co)
diff --git a/src/gui/Base/HTile.qml b/src/gui/Base/HTile.qml
index df2ad60f..bb6bcccd 100644
--- a/src/gui/Base/HTile.qml
+++ b/src/gui/Base/HTile.qml
@@ -6,6 +6,7 @@ import QtQuick.Layouts 1.12
HButton {
id: tile
+
signal leftClicked()
signal rightClicked()
@@ -24,6 +25,7 @@ HButton {
property Component image
+
contentItem: HRowLayout {
id: contentItem
spacing: tile.spacing
@@ -50,34 +52,37 @@ HButton {
Layout.fillHeight: true
}
+ HLabel {
+ id: rightInfo
+ font.pixelSize: theme.fontSize.small
+ verticalAlignment: Qt.AlignVCenter
+ color: theme.colors.halfDimText
+ visible: Layout.maximumWidth > 0
+
+ Layout.fillHeight: true
+ Layout.maximumWidth:
+ text && tile.width >= 200 * theme.uiScale ?
+ implicitWidth : 0
+
+ Behavior on Layout.maximumWidth { HNumberAnimation {} }
+ }
+
HRowLayout {
id: additionalInfo
visible: visibleChildren.length > 0
}
- HLabel {
- id: rightInfo
- font.pixelSize: theme.fontSize.small
- color: theme.colors.halfDimText
-
- visible: Layout.maximumWidth > 0
- Layout.fillHeight: true
- Layout.maximumWidth:
- text && tile.width >= 160 * theme.uiScale ?
- implicitWidth : 0
-
- Behavior on Layout.maximumWidth { HNumberAnimation {} }
- }
}
HRichLabel {
id: subtitle
textFormat: Text.StyledText
font.pixelSize: theme.fontSize.small
+ verticalAlignment: Qt.AlignVCenter
elide: Text.ElideRight
color: theme.colors.dimText
-
visible: Layout.maximumHeight > 0
+
Layout.maximumHeight: ! compact && text ? implicitHeight : 0
Layout.fillWidth: true
Layout.fillHeight: true
diff --git a/src/gui/MainPane/Room.qml b/src/gui/MainPane/Room.qml
index 4dc5d8d4..72e98d9c 100644
--- a/src/gui/MainPane/Room.qml
+++ b/src/gui/MainPane/Room.qml
@@ -33,14 +33,36 @@ HTileDelegate {
title.color: theme.mainPane.listView.room.name
title.text: model.display_name || qsTr("Empty room")
- additionalInfo.children: HIcon {
- svgName: "invite-received"
- colorize: theme.colors.alertBackground
+ additionalInfo.children: [
+ HLabel {
+ text: model.mentions
+ font.pixelSize: theme.fontSize.small
+ verticalAlignment: Qt.AlignVCenter
+ leftPadding: theme.spacing / 4
+ rightPadding: leftPadding
- Layout.maximumWidth: invited ? implicitWidth : 0
+ scale: model.mentions === 0 ? 0 : 1
+ visible: scale > 0
- Behavior on Layout.maximumWidth { HNumberAnimation {} }
- }
+ background: Rectangle {
+ color: theme.colors.alertBackground
+ radius: theme.radius / 4
+ }
+
+ Behavior on scale { HNumberAnimation {} }
+ },
+
+ HIcon {
+ svgName: "invite-received"
+ colorize: theme.colors.alertBackground
+ small: room.compact
+ visible: invited
+
+ Layout.maximumWidth: invited ? implicitWidth : 0
+
+ Behavior on Layout.maximumWidth { HNumberAnimation {} }
+ }
+ ]
subtitle.color: theme.mainPane.listView.room.subtitle
subtitle.textFormat: Text.StyledText