From 6d1416fb6ec7e64a765af652ea3b21722ec12be3 Mon Sep 17 00:00:00 2001 From: Nicolas Werner Date: Tue, 29 Mar 2022 04:50:25 +0200 Subject: [PATCH] Add backend for new room creation dialogs --- CMakeLists.txt | 4 +- io.github.NhekoReborn.Nheko.yaml | 4 +- resources/qml/dialogs/CreateDirect.qml | 56 +++++---- resources/qml/dialogs/CreateRoom.qml | 49 ++++++-- src/ChatPage.cpp | 10 +- src/ChatPage.h | 3 +- src/MainWindow.cpp | 15 --- src/MainWindow.h | 2 - src/dialogs/CreateRoom.cpp | 159 ------------------------- src/dialogs/CreateRoom.h | 50 -------- src/ui/NhekoGlobalObject.cpp | 31 ++++- src/ui/NhekoGlobalObject.h | 3 +- src/ui/UserProfile.cpp | 13 +- src/ui/UserProfile.h | 1 + 14 files changed, 125 insertions(+), 275 deletions(-) delete mode 100644 src/dialogs/CreateRoom.cpp delete mode 100644 src/dialogs/CreateRoom.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 7594d22b..5f822ac1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -309,7 +309,6 @@ configure_file(cmake/nheko.h config/nheko.h) # set(SRC_FILES # Dialogs - src/dialogs/CreateRoom.cpp src/dialogs/FallbackAuth.cpp src/dialogs/ReCaptcha.cpp @@ -404,7 +403,7 @@ if(USE_BUNDLED_MTXCLIENT) FetchContent_Declare( MatrixClient GIT_REPOSITORY https://github.com/Nheko-Reborn/mtxclient.git - GIT_TAG v0.7.0 + GIT_TAG 817ae6e110829cfec39cd367a133a628ab923bb4 ) set(BUILD_LIB_EXAMPLES OFF CACHE INTERNAL "") set(BUILD_LIB_TESTS OFF CACHE INTERNAL "") @@ -506,7 +505,6 @@ feature_summary(WHAT ALL INCLUDE_QUIET_PACKAGES FATAL_ON_MISSING_REQUIRED_PACKAG qt5_wrap_cpp(MOC_HEADERS # Dialogs - src/dialogs/CreateRoom.h src/dialogs/FallbackAuth.h src/dialogs/ReCaptcha.h diff --git a/io.github.NhekoReborn.Nheko.yaml b/io.github.NhekoReborn.Nheko.yaml index 9aefc04c..4832ea53 100644 --- a/io.github.NhekoReborn.Nheko.yaml +++ b/io.github.NhekoReborn.Nheko.yaml @@ -176,8 +176,8 @@ modules: buildsystem: cmake-ninja name: mtxclient sources: - - commit: 9eb9c152faf3461237d4b97ffe12503e367c8809 - tag: v0.7.0 + - commit: 817ae6e110829cfec39cd367a133a628ab923bb4 + #tag: v0.7.0 type: git url: https://github.com/Nheko-Reborn/mtxclient.git - config-opts: diff --git a/resources/qml/dialogs/CreateDirect.qml b/resources/qml/dialogs/CreateDirect.qml index 3ae0b72d..85768cad 100644 --- a/resources/qml/dialogs/CreateDirect.qml +++ b/resources/qml/dialogs/CreateDirect.qml @@ -15,36 +15,26 @@ ApplicationWindow { id: createDirectRoot title: qsTr("Create Direct Chat") property var profile - property bool otherUserHasE2ee: profile? dMod.count > 0 : true - minimumHeight: layout.implicitHeight+2*layout.anchors.margins+footer.height - minimumWidth: footer.width + property bool otherUserHasE2ee: profile? profile.deviceList.rowCount() > 0 : true + minimumHeight: layout.implicitHeight + footer.implicitHeight + Nheko.paddingLarge*2 + minimumWidth: Math.max(footer.implicitWidth, layout.implicitWidth) + modality: Qt.NonModal + flags: Qt.Dialog | Qt.WindowCloseButtonHint | Qt.WindowTitleHint + + onVisibilityChanged: { + userID.forceActiveFocus(); + } Shortcut { sequence: StandardKey.Cancel - onActivated: roomDirectoryWindow.close() - } - DelegateModel { - id: dMod - model: profile? profile.deviceList : undefined + onActivated: createDirectRoot.close() } ColumnLayout { id: layout anchors.fill: parent - anchors.margins: Nheko.paddingSmall - MatrixTextField { - id: userID - property bool isValidMxid: text.match("@.+?:.{3,}") - Layout.fillWidth: true - focus: true - placeholderText: qsTr("Name") - onTextChanged: { - if(isValidMxid) { - profile = TimelineManager.getGlobalUserProfile(text); - } else - profile = null; - } - } + anchors.margins: Nheko.paddingLarge + spacing: userID.height/4 GridLayout { Layout.fillWidth: true @@ -58,7 +48,7 @@ ApplicationWindow { Layout.preferredWidth: Nheko.avatarSize Layout.preferredHeight: Nheko.avatarSize Layout.alignment: Qt.AlignLeft - userid: profile? profile.mxid : "" + userid: profile? profile.userid : "" url: profile? profile.avatarUrl.replace("mxc://", "image://MxcImage/") : null displayName: profile? profile.displayName : "" enabled: false @@ -77,6 +67,22 @@ ApplicationWindow { font.pointSize: fontMetrics.font.pointSize * 0.9 } } + + MatrixTextField { + id: userID + property bool isValidMxid: text.match("@.+?:.{3,}") + Layout.fillWidth: true + focus: true + label: qsTr("User to invite") + placeholderText: qsTr("@user:server.tld") + onTextChanged: { + if(isValidMxid) { + profile = TimelineManager.getGlobalUserProfile(text); + } else + profile = null; + } + } + RowLayout { Layout.fillWidth: true Label { @@ -91,6 +97,8 @@ ApplicationWindow { checked: otherUserHasE2ee } } + + Item {Layout.fillHeight: true} } footer: DialogButtonBox { standardButtons: DialogButtonBox.Cancel @@ -101,7 +109,7 @@ ApplicationWindow { } onRejected: createDirectRoot.close(); onAccepted: { - profile.startChat() + profile.startChat(encryption.checked) createDirectRoot.close() } } diff --git a/resources/qml/dialogs/CreateRoom.qml b/resources/qml/dialogs/CreateRoom.qml index c000642d..5d224885 100644 --- a/resources/qml/dialogs/CreateRoom.qml +++ b/resources/qml/dialogs/CreateRoom.qml @@ -13,8 +13,15 @@ import im.nheko 1.0 ApplicationWindow { id: createRoomRoot title: qsTr("Create Room") - minimumWidth: rootLayout.implicitWidth+2*rootLayout.anchors.margins + minimumWidth: Math.max(rootLayout.implicitWidth+2*rootLayout.anchors.margins, footer.implicitWidth + Nheko.paddingLarge) minimumHeight: rootLayout.implicitHeight+footer.implicitHeight+2*rootLayout.anchors.margins + modality: Qt.NonModal + flags: Qt.Dialog | Qt.WindowCloseButtonHint | Qt.WindowTitleHint + + onVisibilityChanged: { + newRoomName.forceActiveFocus(); + } + Shortcut { sequence: StandardKey.Cancel onActivated: createRoomRoot.close() @@ -22,15 +29,18 @@ ApplicationWindow { GridLayout { id: rootLayout anchors.fill: parent - anchors.margins: Nheko.paddingSmall + anchors.margins: Nheko.paddingLarge columns: 2 + rowSpacing: Nheko.paddingMedium + MatrixTextField { id: newRoomName Layout.columnSpan: 2 Layout.fillWidth: true focus: true - placeholderText: qsTr("Name") + label: qsTr("Name") + placeholderText: qsTr("No name") } MatrixTextField { id: newRoomTopic @@ -38,8 +48,14 @@ ApplicationWindow { Layout.fillWidth: true focus: true - placeholderText: qsTr("Topic") + label: qsTr("Topic") + placeholderText: qsTr("No topic") } + + Item { + Layout.preferredHeight: newRoomName.height / 2 + } + RowLayout { Layout.columnSpan: 2 Layout.fillWidth: true @@ -63,20 +79,20 @@ ApplicationWindow { Label { Layout.preferredWidth: implicitWidth Layout.alignment: Qt.AlignLeft - text: qsTr("Private") + text: qsTr("Public") color: Nheko.colors.text HoverHandler { id: privateHover } ToolTip.visible: privateHover.hovered - ToolTip.text: qsTr("Only invited users can join the room") + ToolTip.text: qsTr("Public rooms can be joined by anyone, private rooms need explicit invites.") ToolTip.delay: Nheko.tooltipDelay } ToggleButton { Layout.alignment: Qt.AlignRight Layout.preferredWidth: implicitWidth - id: isPrivate - checked: true + id: isPublic + checked: false } Label { Layout.preferredWidth: implicitWidth @@ -95,7 +111,7 @@ ApplicationWindow { Layout.preferredWidth: implicitWidth id: isTrusted checked: false - enabled: isPrivate.checked + enabled: !isPublic.checked } Label { Layout.preferredWidth: implicitWidth @@ -115,6 +131,8 @@ ApplicationWindow { id: isEncrypted checked: false } + + Item {Layout.fillHeight: true} } footer: DialogButtonBox { standardButtons: DialogButtonBox.Cancel @@ -123,6 +141,17 @@ ApplicationWindow { DialogButtonBox.buttonRole: DialogButtonBox.AcceptRole } onRejected: createRoomRoot.close(); - //onAccepted: createRoom(newRoomName.text, newRoomTopic.text, newRoomAlias.text, newRoomVisibility.index, newRoomPreset.index) + onAccepted: { + var preset = 0; + + if (isPublic.checked) { + preset = 1; + } + else { + preset = isTrusted.checked ? 2 : 0; + } + Nheko.createRoom(newRoomName.text, newRoomTopic.text, newRoomAlias.text, isEncrypted.checked, preset) + createRoomRoot.close(); + } } } diff --git a/src/ChatPage.cpp b/src/ChatPage.cpp index 3743eae0..a355a5b2 100644 --- a/src/ChatPage.cpp +++ b/src/ChatPage.cpp @@ -1187,7 +1187,7 @@ ChatPage::decryptDownloadedSecrets(mtx::secret_storage::AesHmacSha2KeyDescriptio } void -ChatPage::startChat(QString userid) +ChatPage::startChat(QString userid, std::optional encryptionEnabled) { auto joined_rooms = cache::joinedRooms(); auto room_infos = cache::getRoomInfo(joined_rooms); @@ -1213,6 +1213,14 @@ ChatPage::startChat(QString userid) mtx::requests::CreateRoom req; req.preset = mtx::requests::Preset::PrivateChat; req.visibility = mtx::common::RoomVisibility::Private; + + if (encryptionEnabled.value_or(false)) { + mtx::events::StrippedEvent enc; + enc.type = mtx::events::EventType::RoomEncryption; + enc.content.algorithm = mtx::crypto::MEGOLM_ALGO; + req.initial_state.emplace_back(std::move(enc)); + } + if (utils::localUser() != userid) { req.invite = {userid.toStdString()}; req.is_direct = true; diff --git a/src/ChatPage.h b/src/ChatPage.h index e4b9e4e8..f43a008d 100644 --- a/src/ChatPage.h +++ b/src/ChatPage.h @@ -74,12 +74,13 @@ public: // TODO(Nico): Get rid of this! QString currentRoom() const; + void startChat(QString userid, std::optional encryptionEnabled); public slots: bool handleMatrixUri(QString uri); bool handleMatrixUri(const QUrl &uri); - void startChat(QString userid); + void startChat(QString userid) { startChat(userid, std::nullopt); } void leaveRoom(const QString &room_id); void createRoom(const mtx::requests::CreateRoom &req); void joinRoom(const QString &room); diff --git a/src/MainWindow.cpp b/src/MainWindow.cpp index c4af7f0c..7235f93d 100644 --- a/src/MainWindow.cpp +++ b/src/MainWindow.cpp @@ -54,8 +54,6 @@ #include "ui/UIA.h" #include "voip/WebRTCSession.h" -#include "dialogs/CreateRoom.h" - Q_DECLARE_METATYPE(mtx::events::collections::TimelineEvents) Q_DECLARE_METATYPE(std::vector) Q_DECLARE_METATYPE(std::vector) @@ -404,19 +402,6 @@ MainWindow::hasActiveUser() settings->contains(prefix + "auth/user_id"); } -void -MainWindow::openCreateRoomDialog( - std::function callback) -{ - auto dialog = new dialogs::CreateRoom(nullptr); - connect(dialog, - &dialogs::CreateRoom::createRoom, - this, - [callback](const mtx::requests::CreateRoom &request) { callback(request); }); - - showDialog(dialog); -} - bool MainWindow::pageSupportsTray() const { diff --git a/src/MainWindow.h b/src/MainWindow.h index 7bc94328..e8c6fafd 100644 --- a/src/MainWindow.h +++ b/src/MainWindow.h @@ -47,8 +47,6 @@ public: static MainWindow *instance() { return instance_; } void saveCurrentWindowSize(); - void - openCreateRoomDialog(std::function callback); void openJoinRoomDialog(std::function callback); MxcImageProvider *imageProvider() { return imgProvider; } diff --git a/src/dialogs/CreateRoom.cpp b/src/dialogs/CreateRoom.cpp deleted file mode 100644 index e828ae7c..00000000 --- a/src/dialogs/CreateRoom.cpp +++ /dev/null @@ -1,159 +0,0 @@ -// SPDX-FileCopyrightText: 2021 Nheko Contributors -// SPDX-FileCopyrightText: 2022 Nheko Contributors -// -// SPDX-License-Identifier: GPL-3.0-or-later - -#include -#include -#include -#include - -#include "dialogs/CreateRoom.h" - -#include "Config.h" -#include "ui/TextField.h" -#include "ui/ToggleButton.h" - -using namespace dialogs; - -CreateRoom::CreateRoom(QWidget *parent) - : QFrame(parent) -{ - setAutoFillBackground(true); - setWindowFlags(Qt::Tool | Qt::WindowStaysOnTopHint); - setWindowModality(Qt::WindowModal); - setAttribute(Qt::WA_DeleteOnClose, true); - - QFont largeFont; - largeFont.setPointSizeF(largeFont.pointSizeF() * 1.5); - - setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum); - setMinimumHeight(conf::window::minHeight); - setMinimumWidth(conf::window::minModalWidth); - - auto layout = new QVBoxLayout(this); - layout->setSpacing(conf::modals::WIDGET_SPACING); - layout->setContentsMargins(conf::modals::WIDGET_MARGIN, - conf::modals::WIDGET_MARGIN, - conf::modals::WIDGET_MARGIN, - conf::modals::WIDGET_MARGIN); - - buttonBox_ = new QDialogButtonBox(QDialogButtonBox::Cancel); - confirmBtn_ = new QPushButton(tr("Create room"), this); - confirmBtn_->setDefault(true); - buttonBox_->addButton(confirmBtn_, QDialogButtonBox::AcceptRole); - - QFont font; - font.setPointSizeF(font.pointSizeF() * 1.3); - - nameInput_ = new TextField(this); - nameInput_->setLabel(tr("Name")); - - topicInput_ = new TextField(this); - topicInput_->setLabel(tr("Topic")); - - aliasInput_ = new TextField(this); - aliasInput_->setLabel(tr("Alias")); - - auto visibilityLayout = new QHBoxLayout; - visibilityLayout->setContentsMargins(0, 10, 0, 10); - - auto presetLayout = new QHBoxLayout; - presetLayout->setContentsMargins(0, 10, 0, 10); - - auto visibilityLabel = new QLabel(tr("Room Visibility"), this); - visibilityCombo_ = new QComboBox(this); - visibilityCombo_->addItem(tr("Private")); - visibilityCombo_->addItem(tr("Public")); - - visibilityLayout->addWidget(visibilityLabel); - visibilityLayout->addWidget(visibilityCombo_, 0, Qt::AlignBottom | Qt::AlignRight); - - auto presetLabel = new QLabel(tr("Room Preset"), this); - presetCombo_ = new QComboBox(this); - presetCombo_->addItem(tr("Private Chat")); - presetCombo_->addItem(tr("Public Chat")); - presetCombo_->addItem(tr("Trusted Private Chat")); - - presetLayout->addWidget(presetLabel); - presetLayout->addWidget(presetCombo_, 0, Qt::AlignBottom | Qt::AlignRight); - - auto directLabel_ = new QLabel(tr("Direct Chat"), this); - directToggle_ = new Toggle(this); - directToggle_->setActiveColor(QColor(0x38, 0xA3, 0xD8)); - directToggle_->setInactiveColor(QColor("gray")); - directToggle_->setState(false); - - auto directLayout = new QHBoxLayout; - directLayout->setContentsMargins(0, 10, 0, 10); - directLayout->addWidget(directLabel_); - directLayout->addWidget(directToggle_, 0, Qt::AlignBottom | Qt::AlignRight); - - layout->addWidget(nameInput_); - layout->addWidget(topicInput_); - layout->addWidget(aliasInput_); - layout->addLayout(visibilityLayout); - layout->addLayout(presetLayout); - layout->addLayout(directLayout); - layout->addWidget(buttonBox_); - - connect(buttonBox_, &QDialogButtonBox::accepted, this, [this]() { - request_.name = nameInput_->text().toStdString(); - request_.topic = topicInput_->text().toStdString(); - request_.room_alias_name = aliasInput_->text().toStdString(); - - emit createRoom(request_); - - clearFields(); - emit close(); - }); - - connect(buttonBox_, &QDialogButtonBox::rejected, this, [this]() { - clearFields(); - emit close(); - }); - - connect(visibilityCombo_, - static_cast(&QComboBox::currentIndexChanged), - this, - [this](int idx) { - if (idx == 0) { - request_.visibility = mtx::common::RoomVisibility::Private; - } else { - request_.visibility = mtx::common::RoomVisibility::Public; - } - }); - - connect(presetCombo_, - static_cast(&QComboBox::currentIndexChanged), - this, - [this](int idx) { - if (idx == 0) { - request_.preset = mtx::requests::Preset::PrivateChat; - } else if (idx == 1) { - request_.preset = mtx::requests::Preset::PublicChat; - } else { - request_.preset = mtx::requests::Preset::TrustedPrivateChat; - } - }); - - connect(directToggle_, &Toggle::toggled, this, [this](bool isEnabled) { - request_.is_direct = isEnabled; - }); -} - -void -CreateRoom::clearFields() -{ - nameInput_->clear(); - topicInput_->clear(); - aliasInput_->clear(); -} - -void -CreateRoom::showEvent(QShowEvent *event) -{ - nameInput_->setFocus(); - - QFrame::showEvent(event); -} diff --git a/src/dialogs/CreateRoom.h b/src/dialogs/CreateRoom.h deleted file mode 100644 index c395941d..00000000 --- a/src/dialogs/CreateRoom.h +++ /dev/null @@ -1,50 +0,0 @@ -// SPDX-FileCopyrightText: 2021 Nheko Contributors -// SPDX-FileCopyrightText: 2022 Nheko Contributors -// -// SPDX-License-Identifier: GPL-3.0-or-later - -#pragma once - -#include -#include - -#include - -class QPushButton; -class TextField; -class QComboBox; -class Toggle; - -namespace dialogs { - -class CreateRoom : public QFrame -{ - Q_OBJECT -public: - CreateRoom(QWidget *parent = nullptr); - -signals: - void createRoom(const mtx::requests::CreateRoom &request); - -protected: - void showEvent(QShowEvent *event) override; - -private: - void clearFields(); - - QComboBox *visibilityCombo_; - QComboBox *presetCombo_; - - Toggle *directToggle_; - - QPushButton *confirmBtn_; - QDialogButtonBox *buttonBox_; - - TextField *nameInput_; - TextField *topicInput_; - TextField *aliasInput_; - - mtx::requests::CreateRoom request_; -}; - -} // dialogs diff --git a/src/ui/NhekoGlobalObject.cpp b/src/ui/NhekoGlobalObject.cpp index 3abcdf08..2e1aadf0 100644 --- a/src/ui/NhekoGlobalObject.cpp +++ b/src/ui/NhekoGlobalObject.cpp @@ -13,7 +13,6 @@ #include "Cache_p.h" #include "ChatPage.h" #include "Logging.h" -#include "MainWindow.h" #include "UserSettingsPage.h" #include "Utils.h" #include "voip/WebRTCSession.h" @@ -129,8 +128,32 @@ Nheko::logout() const } void -Nheko::openCreateRoomDialog() const +Nheko::createRoom(QString name, QString topic, QString aliasLocalpart, bool isEncrypted, int preset) { - MainWindow::instance()->openCreateRoomDialog( - [](const mtx::requests::CreateRoom &req) { ChatPage::instance()->createRoom(req); }); + mtx::requests::CreateRoom req; + + switch (preset) { + case 1: + req.preset = mtx::requests::Preset::PublicChat; + break; + case 2: + req.preset = mtx::requests::Preset::TrustedPrivateChat; + break; + case 0: + default: + req.preset = mtx::requests::Preset::PrivateChat; + } + + req.name = name.toStdString(); + req.topic = topic.toStdString(); + req.room_alias_name = aliasLocalpart.toStdString(); + + if (isEncrypted) { + mtx::events::StrippedEvent enc; + enc.type = mtx::events::EventType::RoomEncryption; + enc.content.algorithm = mtx::crypto::MEGOLM_ALGO; + req.initial_state.emplace_back(std::move(enc)); + } + + emit ChatPage::instance()->createRoom(req); } diff --git a/src/ui/NhekoGlobalObject.h b/src/ui/NhekoGlobalObject.h index 24493873..1139cf31 100644 --- a/src/ui/NhekoGlobalObject.h +++ b/src/ui/NhekoGlobalObject.h @@ -52,7 +52,8 @@ public: Q_INVOKABLE void setStatusMessage(QString msg) const; Q_INVOKABLE void showUserSettingsPage() const; Q_INVOKABLE void logout() const; - Q_INVOKABLE void openCreateRoomDialog() const; + Q_INVOKABLE void + createRoom(QString name, QString topic, QString aliasLocalpart, bool isEncrypted, int preset); public slots: void updateUserProfile(); diff --git a/src/ui/UserProfile.cpp b/src/ui/UserProfile.cpp index 898b56fd..db50b050 100644 --- a/src/ui/UserProfile.cpp +++ b/src/ui/UserProfile.cpp @@ -53,6 +53,9 @@ UserProfile::UserProfile(QString roomid, emit verificationStatiChanged(); }); + connect(this, &UserProfile::devicesChanged, [this]() { + nhlog::net()->critical("Device list: {}", deviceList_.rowCount()); + }); fetchDeviceList(this->userid_); } @@ -187,7 +190,6 @@ UserProfile::fetchDeviceList(const QString &userID) nhlog::net()->warn("failed to query device keys: {},{}", mtx::errors::to_string(err->matrix_error.errcode), static_cast(err->status_code)); - return; } // Ensure local key cache is up to date @@ -201,7 +203,6 @@ UserProfile::fetchDeviceList(const QString &userID) nhlog::net()->warn("failed to query device keys: {},{}", mtx::errors::to_string(err->matrix_error.errcode), static_cast(err->status_code)); - return; } emit verificationStatiChanged(); @@ -312,10 +313,16 @@ UserProfile::kickUser() ChatPage::instance()->kickUser(this->userid_, QLatin1String("")); } +void +UserProfile::startChat(bool encryption) +{ + ChatPage::instance()->startChat(this->userid_, encryption); +} + void UserProfile::startChat() { - ChatPage::instance()->startChat(this->userid_); + ChatPage::instance()->startChat(this->userid_, std::nullopt); } void diff --git a/src/ui/UserProfile.h b/src/ui/UserProfile.h index 0f03e537..4652a72e 100644 --- a/src/ui/UserProfile.h +++ b/src/ui/UserProfile.h @@ -143,6 +143,7 @@ public: // Q_INVOKABLE void ignoreUser(); Q_INVOKABLE void kickUser(); Q_INVOKABLE void startChat(); + Q_INVOKABLE void startChat(bool encryptionEnabled); Q_INVOKABLE void changeUsername(QString username); Q_INVOKABLE void changeDeviceName(QString deviceID, QString deviceName); Q_INVOKABLE void changeAvatar();