diff --git a/resources/qml/MessageInput.qml b/resources/qml/MessageInput.qml
index e1bf3f06..c95929ce 100644
--- a/resources/qml/MessageInput.qml
+++ b/resources/qml/MessageInput.qml
@@ -268,7 +268,7 @@ Rectangle {
function onRoomChanged() {
messageInput.clear();
if (room)
- messageInput.append(room.input.text());
+ messageInput.append(room.input.text);
popup.completerName = "";
messageInput.forceActiveFocus();
diff --git a/resources/qml/NotificationWarning.qml b/resources/qml/NotificationWarning.qml
new file mode 100644
index 00000000..b606581b
--- /dev/null
+++ b/resources/qml/NotificationWarning.qml
@@ -0,0 +1,38 @@
+// SPDX-FileCopyrightText: 2021 Nheko Contributors
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+import QtQuick 2.9
+import QtQuick.Controls 2.3
+import QtQuick.Layouts 1.2
+import im.nheko 1.0
+
+Item {
+ implicitHeight: warningRect.visible ? warningDisplay.implicitHeight : 0
+ height: implicitHeight
+ Layout.fillWidth: true
+
+ Rectangle {
+ id: warningRect
+
+ visible: (room && room.permissions.canPingRoom && room.input.containsAtRoom)
+ color: Nheko.colors.base
+ anchors.fill: parent
+ z: 3
+
+ Label {
+ id: warningDisplay
+
+ anchors.left: parent.left
+ anchors.leftMargin: 10
+ anchors.right: parent.right
+ anchors.rightMargin: 10
+ anchors.bottom: parent.bottom
+ color: Nheko.theme.red
+ text: qsTr("You will be pinging the whole room")
+ textFormat: Text.PlainText
+ }
+
+ }
+
+}
diff --git a/resources/qml/TimelineView.qml b/resources/qml/TimelineView.qml
index 9bc4bef0..f12060f2 100644
--- a/resources/qml/TimelineView.qml
+++ b/resources/qml/TimelineView.qml
@@ -123,6 +123,9 @@ Item {
color: Nheko.theme.separator
}
+ NotificationWarning {
+ }
+
ReplyPopup {
}
diff --git a/resources/res.qrc b/resources/res.qrc
index b46b726c..3514ebca 100644
--- a/resources/res.qrc
+++ b/resources/res.qrc
@@ -138,6 +138,7 @@
qml/QuickSwitcher.qml
qml/ForwardCompleter.qml
qml/TypingIndicator.qml
+ qml/NotificationWarning.qml
qml/RoomSettings.qml
qml/emoji/EmojiPicker.qml
qml/emoji/StickerPicker.qml
diff --git a/src/timeline/InputBar.cpp b/src/timeline/InputBar.cpp
index c82099f0..ece9db62 100644
--- a/src/timeline/InputBar.cpp
+++ b/src/timeline/InputBar.cpp
@@ -11,6 +11,7 @@
#include
#include
#include
+#include
#include
#include
@@ -129,6 +130,34 @@ InputBar::insertMimeData(const QMimeData *md)
}
}
+void
+InputBar::updateAtRoom(const QString &t)
+{
+ bool roomMention = false;
+
+ if (t.size() > 4) {
+ QTextBoundaryFinder finder(QTextBoundaryFinder::BoundaryType::Word, t);
+
+ finder.toStart();
+ do {
+ auto start = finder.position();
+ finder.toNextBoundary();
+ auto end = finder.position();
+ if (start > 0 && end - start >= 4 &&
+ t.midRef(start, end - start) == "room" &&
+ t.at(start - 1) == QChar('@')) {
+ roomMention = true;
+ break;
+ }
+ } while (finder.position() < t.size());
+ }
+
+ if (roomMention != this->containsAtRoom_) {
+ this->containsAtRoom_ = roomMention;
+ emit containsAtRoomChanged();
+ }
+}
+
void
InputBar::setText(QString newText)
{
@@ -157,6 +186,8 @@ InputBar::updateState(int selectionStart_, int selectionEnd_, int cursorPosition
else
history_.front() = text_;
history_index_ = 0;
+
+ updateAtRoom(text_);
}
selectionStart = selectionStart_;
@@ -182,6 +213,7 @@ InputBar::previousText()
else if (text().isEmpty())
history_index_--;
+ updateAtRoom(text());
return text();
}
@@ -192,6 +224,7 @@ InputBar::nextText()
if (history_index_ >= INPUT_HISTORY_SIZE)
history_index_ = 0;
+ updateAtRoom(text());
return text();
}
diff --git a/src/timeline/InputBar.h b/src/timeline/InputBar.h
index 2e6fb5c0..cdc66a06 100644
--- a/src/timeline/InputBar.h
+++ b/src/timeline/InputBar.h
@@ -28,6 +28,8 @@ class InputBar : public QObject
{
Q_OBJECT
Q_PROPERTY(bool uploading READ uploading NOTIFY uploadingChanged)
+ Q_PROPERTY(bool containsAtRoom READ containsAtRoom NOTIFY containsAtRoomChanged)
+ Q_PROPERTY(QString text READ text NOTIFY textChanged)
public:
InputBar(TimelineModel *parent)
@@ -48,6 +50,8 @@ public slots:
QString nextText();
void setText(QString newText);
+ bool containsAtRoom() const { return containsAtRoom_; }
+
void send();
void paste(bool fromMouse);
void insertMimeData(const QMimeData *data);
@@ -68,6 +72,7 @@ signals:
void insertText(QString text);
void textChanged(QString newText);
void uploadingChanged(bool value);
+ void containsAtRoomChanged();
private:
void emote(QString body, bool rainbowify);
@@ -105,11 +110,14 @@ private:
}
}
+ void updateAtRoom(const QString &t);
+
QTimer typingRefresh_;
QTimer typingTimeout_;
TimelineModel *room;
std::deque history_;
std::size_t history_index_ = 0;
int selectionStart = 0, selectionEnd = 0, cursorPosition = 0;
- bool uploading_ = false;
+ bool uploading_ = false;
+ bool containsAtRoom_ = false;
};
diff --git a/src/timeline/Permissions.cpp b/src/timeline/Permissions.cpp
index e4957045..5dafc325 100644
--- a/src/timeline/Permissions.cpp
+++ b/src/timeline/Permissions.cpp
@@ -61,3 +61,10 @@ Permissions::canSend(int eventType)
pl.event_level(to_string(qml_mtx_events::fromRoomEventType(
static_cast(eventType))));
}
+
+bool
+Permissions::canPingRoom()
+{
+ return pl.user_level(http::client()->user_id().to_string()) >=
+ pl.notification_level(mtx::events::state::notification_keys::room);
+}
diff --git a/src/timeline/Permissions.h b/src/timeline/Permissions.h
index 7aab1ddb..349520d5 100644
--- a/src/timeline/Permissions.h
+++ b/src/timeline/Permissions.h
@@ -25,6 +25,8 @@ public:
Q_INVOKABLE bool canChange(int eventType);
Q_INVOKABLE bool canSend(int eventType);
+ Q_INVOKABLE bool canPingRoom();
+
void invalidate();
private: