From 3528fe4e5dee1684f43e14fc9c6a50b4c1e25faf Mon Sep 17 00:00:00 2001 From: Nicolas Werner Date: Thu, 2 Sep 2021 03:15:07 +0200 Subject: [PATCH] Warn the user before they ping the whole room --- resources/qml/MessageInput.qml | 2 +- resources/qml/NotificationWarning.qml | 38 +++++++++++++++++++++++++++ resources/qml/TimelineView.qml | 3 +++ resources/res.qrc | 1 + src/timeline/InputBar.cpp | 33 +++++++++++++++++++++++ src/timeline/InputBar.h | 10 ++++++- src/timeline/Permissions.cpp | 7 +++++ src/timeline/Permissions.h | 2 ++ 8 files changed, 94 insertions(+), 2 deletions(-) create mode 100644 resources/qml/NotificationWarning.qml 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: