From b8b31cb36d886fa50f29a2ec9789d7e4844fd5a1 Mon Sep 17 00:00:00 2001 From: Malte E Date: Fri, 25 Mar 2022 22:30:19 +0100 Subject: [PATCH 1/6] CreateRoom dialog in QML --- resources/qml/RoomList.qml | 13 ++++- resources/qml/dialogs/CreateRoom.qml | 81 ++++++++++++++++++++++++++++ resources/res.qrc | 1 + 3 files changed, 94 insertions(+), 1 deletion(-) create mode 100644 resources/qml/dialogs/CreateRoom.qml diff --git a/resources/qml/RoomList.qml b/resources/qml/RoomList.qml index 702d77e5..eb518f40 100644 --- a/resources/qml/RoomList.qml +++ b/resources/qml/RoomList.qml @@ -26,6 +26,13 @@ Page { } + Component { + id: createRoomComponent + + CreateRoom { + } + } + ListView { id: roomlist @@ -648,7 +655,11 @@ Page { Platform.MenuItem { text: qsTr("Create a new room") - onTriggered: Nheko.openCreateRoomDialog() + onTriggered: { + var createRoom = createRoomComponent.createObject(timelineRoot); + createRoom.show(); + timelineRoot.destroyOnClose(createRoom); + } } } diff --git a/resources/qml/dialogs/CreateRoom.qml b/resources/qml/dialogs/CreateRoom.qml new file mode 100644 index 00000000..843d10c8 --- /dev/null +++ b/resources/qml/dialogs/CreateRoom.qml @@ -0,0 +1,81 @@ +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// SPDX-FileCopyrightText: 2022 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later + +import ".." +import QtQuick 2.15 +import QtQuick.Window 2.13 +import QtQuick.Layouts 1.3 +import QtQuick.Controls 2.3 +import im.nheko 1.0 + +ApplicationWindow { + id: createRoomRoot + title: qsTr("Create Room") + minimumWidth: rootLayout.implicitWidth+2*rootLayout.anchors.margins + minimumHeight: rootLayout.implicitHeight+footer.implicitHeight+2*rootLayout.anchors.margins + GridLayout { + id: rootLayout + anchors.fill: parent + anchors.margins: Nheko.paddingSmall + columns: 2 + MatrixTextField { + id: newRoomName + Layout.columnSpan: 2 + Layout.fillWidth: true + + focus: true + placeholderText: qsTr("Name") + } + MatrixTextField { + id: newRoomTopic + Layout.columnSpan: 2 + Layout.fillWidth: true + + focus: true + placeholderText: qsTr("Topic") + } + MatrixTextField { + id: newRoomAlias + Layout.columnSpan: 2 + Layout.fillWidth: true + + focus: true + placeholderText: qsTr("Alias") + } + Label { + Layout.preferredWidth: implicitWidth + Layout.alignment: Qt.AlignLeft + text: qsTr("Room Visibility") + color: Nheko.colors.text + } + ComboBox { + id: newRoomVisibility + Layout.preferredWidth: implicitWidth + Layout.alignment: Qt.AlignRight + model: [qsTr("Private"), qsTr("Public")] + } + Label { + Layout.preferredWidth: implicitWidth + Layout.alignment: Qt.AlignLeft + text: qsTr("Room Preset") + color: Nheko.colors.text + } + ComboBox { + id: newRoomPreset + Layout.preferredWidth: implicitWidth + Layout.alignment: Qt.AlignRight + model: [qsTr("Private Chat"), qsTr("Public Chat"), qsTr("Trusted Private Chat")] + } + } + footer: DialogButtonBox { + standardButtons: DialogButtonBox.Cancel + Button { + text: "CreateRoom" + DialogButtonBox.buttonRole: DialogButtonBox.AcceptRole + } + onRejected: createRoomRoot.close(); + //onAccepted: createRoom(newRoomName.text, newRoomTopic.text, newRoomAlias.text, newRoomVisibility.index, newRoomPreset.index) + } +} diff --git a/resources/res.qrc b/resources/res.qrc index 3b762d20..6ed37fac 100644 --- a/resources/res.qrc +++ b/resources/res.qrc @@ -143,6 +143,7 @@ qml/device-verification/NewVerificationRequest.qml qml/device-verification/Success.qml qml/device-verification/Waiting.qml + qml/dialogs/CreateRoom.qml qml/dialogs/ImageOverlay.qml qml/dialogs/ImagePackEditorDialog.qml qml/dialogs/ImagePackSettingsDialog.qml From 5384ab377c5a6e9dc12b9b536935f22cffbff6e9 Mon Sep 17 00:00:00 2001 From: Malte E Date: Sat, 26 Mar 2022 17:28:44 +0100 Subject: [PATCH 2/6] initial direct chat creation dialog --- resources/qml/RoomList.qml | 16 +++++ resources/qml/dialogs/CreateDirect.qml | 92 ++++++++++++++++++++++++++ resources/qml/dialogs/CreateRoom.qml | 2 +- resources/res.qrc | 1 + 4 files changed, 110 insertions(+), 1 deletion(-) create mode 100644 resources/qml/dialogs/CreateDirect.qml diff --git a/resources/qml/RoomList.qml b/resources/qml/RoomList.qml index eb518f40..078baede 100644 --- a/resources/qml/RoomList.qml +++ b/resources/qml/RoomList.qml @@ -33,6 +33,13 @@ Page { } } + Component { + id: createDirectComponent + + CreateDirect { + } + } + ListView { id: roomlist @@ -662,6 +669,15 @@ Page { } } + Platform.MenuItem { + text: qsTr("Start a direct chat") + onTriggered: { + var createDirect = createDirectComponent.createObject(timelineRoot); + createDirect.show(); + timelineRoot.destroyOnClose(createDirect); + } + } + } } diff --git a/resources/qml/dialogs/CreateDirect.qml b/resources/qml/dialogs/CreateDirect.qml new file mode 100644 index 00000000..bbb758e3 --- /dev/null +++ b/resources/qml/dialogs/CreateDirect.qml @@ -0,0 +1,92 @@ +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// SPDX-FileCopyrightText: 2022 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later + +import ".." +import QtQuick 2.15 +import QtQuick.Window 2.13 +import QtQuick.Layouts 1.3 +import QtQuick.Controls 2.3 +import im.nheko 1.0 + +ApplicationWindow { + id: createDirectRoot + title: qsTr("Create Direct Chat") + property var profile: null + minimumHeight: layout.implicitHeight+2*layout.anchors.margins+footer.height + minimumWidth: footer.width + ColumnLayout { + id: layout + anchors.fill: parent + anchors.margins: Nheko.paddingSmall + MatrixTextField { + id: userID + Layout.fillWidth: true + focus: true + placeholderText: qsTr("Name") + /*onTextChanged: { + if(isValidMxid(text)) + profile = getProfile(text); + else + profile = null; + }*/ + } + + GridLayout { + Layout.fillWidth: true + rows: 2 + columns: 2 + rowSpacing: Nheko.paddingSmall + columnSpacing: Nheko.paddingMedium + anchors.centerIn: parent + + Avatar { + Layout.rowSpan: 2 + Layout.preferredWidth: Nheko.avatarSize + Layout.preferredHeight: Nheko.avatarSize + Layout.alignment: Qt.AlignLeft + userid: profile.mxid + url: profile.avatarUrl.replace("mxc://", "image://MxcImage/") + displayName: profile.displayName + enabled: false + } + Label { + Layout.fillWidth: true + text: "John Smith" //profile.displayName + color: TimelineManager.userColor(userID.text, Nheko.colors.window) + font.pointSize: fontMetrics.font.pointSize + } + + Label { + Layout.fillWidth: true + text: userID.text + color: Nheko.colors.buttonText + font.pointSize: fontMetrics.font.pointSize * 0.9 + } + } + RowLayout { + Layout.fillWidth: true + Label { + Layout.fillWidth: true + Layout.alignment: Qt.AlignLeft + text: qsTr("Encryption") + color: Nheko.colors.text + } + ToggleButton { + Layout.alignment: Qt.AlignRight + id: encryption + checked: true + } + } + } + footer: DialogButtonBox { + standardButtons: DialogButtonBox.Cancel + Button { + text: "Start Direct Chat" + DialogButtonBox.buttonRole: DialogButtonBox.AcceptRole + } + onRejected: createDirectRoot.close(); + //onAccepted: createRoom(newRoomName.text, newRoomTopic.text, newRoomAlias.text, newRoomVisibility.index, newRoomPreset.index) + } +} diff --git a/resources/qml/dialogs/CreateRoom.qml b/resources/qml/dialogs/CreateRoom.qml index 843d10c8..85db8f10 100644 --- a/resources/qml/dialogs/CreateRoom.qml +++ b/resources/qml/dialogs/CreateRoom.qml @@ -72,7 +72,7 @@ ApplicationWindow { footer: DialogButtonBox { standardButtons: DialogButtonBox.Cancel Button { - text: "CreateRoom" + text: "Create Room" DialogButtonBox.buttonRole: DialogButtonBox.AcceptRole } onRejected: createRoomRoot.close(); diff --git a/resources/res.qrc b/resources/res.qrc index 6ed37fac..3ce63f42 100644 --- a/resources/res.qrc +++ b/resources/res.qrc @@ -143,6 +143,7 @@ qml/device-verification/NewVerificationRequest.qml qml/device-verification/Success.qml qml/device-verification/Waiting.qml + qml/dialogs/CreateDirect.qml qml/dialogs/CreateRoom.qml qml/dialogs/ImageOverlay.qml qml/dialogs/ImagePackEditorDialog.qml From 838cf63578d2145e9264bd37e68993f6e6e8d132 Mon Sep 17 00:00:00 2001 From: Malte E Date: Sat, 26 Mar 2022 22:25:48 +0100 Subject: [PATCH 3/6] direct chat creator can now create direct chats --- resources/qml/dialogs/CreateDirect.qml | 38 +++++++++++++++++--------- src/timeline/TimelineViewManager.cpp | 8 ++++++ src/timeline/TimelineViewManager.h | 1 + 3 files changed, 34 insertions(+), 13 deletions(-) diff --git a/resources/qml/dialogs/CreateDirect.qml b/resources/qml/dialogs/CreateDirect.qml index bbb758e3..b76e728a 100644 --- a/resources/qml/dialogs/CreateDirect.qml +++ b/resources/qml/dialogs/CreateDirect.qml @@ -8,29 +8,38 @@ import QtQuick 2.15 import QtQuick.Window 2.13 import QtQuick.Layouts 1.3 import QtQuick.Controls 2.3 +import QtQml.Models 2.15 import im.nheko 1.0 ApplicationWindow { id: createDirectRoot title: qsTr("Create Direct Chat") - property var profile: null + property var profile + property bool otherUserHasE2ee: profile? dMod.count > 0 : true minimumHeight: layout.implicitHeight+2*layout.anchors.margins+footer.height minimumWidth: footer.width + + DelegateModel { + id: dMod + model: profile? profile.deviceList : undefined + } + 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(text)) - profile = getProfile(text); - else + onTextChanged: { + if(isValidMxid) { + profile = TimelineManager.getGlobalUserProfile(text); + } else profile = null; - }*/ + } } GridLayout { @@ -39,21 +48,20 @@ ApplicationWindow { columns: 2 rowSpacing: Nheko.paddingSmall columnSpacing: Nheko.paddingMedium - anchors.centerIn: parent Avatar { Layout.rowSpan: 2 Layout.preferredWidth: Nheko.avatarSize Layout.preferredHeight: Nheko.avatarSize Layout.alignment: Qt.AlignLeft - userid: profile.mxid - url: profile.avatarUrl.replace("mxc://", "image://MxcImage/") - displayName: profile.displayName + userid: profile? profile.mxid : "" + url: profile? profile.avatarUrl.replace("mxc://", "image://MxcImage/") : null + displayName: profile? profile.displayName : "" enabled: false } Label { Layout.fillWidth: true - text: "John Smith" //profile.displayName + text: profile? profile.displayName : "" color: TimelineManager.userColor(userID.text, Nheko.colors.window) font.pointSize: fontMetrics.font.pointSize } @@ -76,7 +84,7 @@ ApplicationWindow { ToggleButton { Layout.alignment: Qt.AlignRight id: encryption - checked: true + checked: otherUserHasE2ee } } } @@ -85,8 +93,12 @@ ApplicationWindow { Button { text: "Start Direct Chat" DialogButtonBox.buttonRole: DialogButtonBox.AcceptRole + enabled: userID.isValidMxid } onRejected: createDirectRoot.close(); - //onAccepted: createRoom(newRoomName.text, newRoomTopic.text, newRoomAlias.text, newRoomVisibility.index, newRoomPreset.index) + onAccepted: { + profile.startChat() + createDirectRoot.close() + } } } diff --git a/src/timeline/TimelineViewManager.cpp b/src/timeline/TimelineViewManager.cpp index a18f3ee2..72f89fd4 100644 --- a/src/timeline/TimelineViewManager.cpp +++ b/src/timeline/TimelineViewManager.cpp @@ -199,6 +199,14 @@ TimelineViewManager::openGlobalUserProfile(QString userId) emit openProfile(profile); } +UserProfile* +TimelineViewManager::getGlobalUserProfile(QString userId) +{ + UserProfile *profile = new UserProfile{QString{}, userId, this}; + QQmlEngine::setObjectOwnership(profile, QQmlEngine::JavaScriptOwnership); + return(profile); +} + void TimelineViewManager::setVideoCallItem() { diff --git a/src/timeline/TimelineViewManager.h b/src/timeline/TimelineViewManager.h index 393b1479..807fe76f 100644 --- a/src/timeline/TimelineViewManager.h +++ b/src/timeline/TimelineViewManager.h @@ -67,6 +67,7 @@ public: Q_INVOKABLE void openRoomSettings(QString room_id); Q_INVOKABLE void openInviteUsers(QString roomId); Q_INVOKABLE void openGlobalUserProfile(QString userId); + Q_INVOKABLE UserProfile* getGlobalUserProfile(QString userId); Q_INVOKABLE void focusMessageInput(); From af17e4f8c007d8276f98fd675cbfe818d24e7420 Mon Sep 17 00:00:00 2001 From: Malte E Date: Sat, 26 Mar 2022 22:28:31 +0100 Subject: [PATCH 4/6] run lint --- src/timeline/TimelineViewManager.cpp | 4 ++-- src/timeline/TimelineViewManager.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/timeline/TimelineViewManager.cpp b/src/timeline/TimelineViewManager.cpp index 72f89fd4..3bccd8f3 100644 --- a/src/timeline/TimelineViewManager.cpp +++ b/src/timeline/TimelineViewManager.cpp @@ -199,12 +199,12 @@ TimelineViewManager::openGlobalUserProfile(QString userId) emit openProfile(profile); } -UserProfile* +UserProfile * TimelineViewManager::getGlobalUserProfile(QString userId) { UserProfile *profile = new UserProfile{QString{}, userId, this}; QQmlEngine::setObjectOwnership(profile, QQmlEngine::JavaScriptOwnership); - return(profile); + return (profile); } void diff --git a/src/timeline/TimelineViewManager.h b/src/timeline/TimelineViewManager.h index 807fe76f..07ebfe79 100644 --- a/src/timeline/TimelineViewManager.h +++ b/src/timeline/TimelineViewManager.h @@ -67,7 +67,7 @@ public: Q_INVOKABLE void openRoomSettings(QString room_id); Q_INVOKABLE void openInviteUsers(QString roomId); Q_INVOKABLE void openGlobalUserProfile(QString userId); - Q_INVOKABLE UserProfile* getGlobalUserProfile(QString userId); + Q_INVOKABLE UserProfile *getGlobalUserProfile(QString userId); Q_INVOKABLE void focusMessageInput(); From d7cb2bd64734adb6938b85ac28075c2c8e8d25e0 Mon Sep 17 00:00:00 2001 From: Malte E Date: Sun, 27 Mar 2022 22:32:29 +0200 Subject: [PATCH 5/6] expose options better --- resources/qml/dialogs/CreateDirect.qml | 4 ++ resources/qml/dialogs/CreateRoom.qml | 79 ++++++++++++++++++++------ 2 files changed, 67 insertions(+), 16 deletions(-) diff --git a/resources/qml/dialogs/CreateDirect.qml b/resources/qml/dialogs/CreateDirect.qml index b76e728a..3ae0b72d 100644 --- a/resources/qml/dialogs/CreateDirect.qml +++ b/resources/qml/dialogs/CreateDirect.qml @@ -19,6 +19,10 @@ ApplicationWindow { minimumHeight: layout.implicitHeight+2*layout.anchors.margins+footer.height minimumWidth: footer.width + Shortcut { + sequence: StandardKey.Cancel + onActivated: roomDirectoryWindow.close() + } DelegateModel { id: dMod model: profile? profile.deviceList : undefined diff --git a/resources/qml/dialogs/CreateRoom.qml b/resources/qml/dialogs/CreateRoom.qml index 85db8f10..c000642d 100644 --- a/resources/qml/dialogs/CreateRoom.qml +++ b/resources/qml/dialogs/CreateRoom.qml @@ -15,6 +15,10 @@ ApplicationWindow { title: qsTr("Create Room") minimumWidth: rootLayout.implicitWidth+2*rootLayout.anchors.margins minimumHeight: rootLayout.implicitHeight+footer.implicitHeight+2*rootLayout.anchors.margins + Shortcut { + sequence: StandardKey.Cancel + onActivated: createRoomRoot.close() + } GridLayout { id: rootLayout anchors.fill: parent @@ -36,43 +40,86 @@ ApplicationWindow { focus: true placeholderText: qsTr("Topic") } - MatrixTextField { - id: newRoomAlias + RowLayout { Layout.columnSpan: 2 Layout.fillWidth: true - - focus: true - placeholderText: qsTr("Alias") + Label { + Layout.preferredWidth: implicitWidth + text: qsTr("#") + color: Nheko.colors.text + } + MatrixTextField { + id: newRoomAlias + focus: true + placeholderText: qsTr("Alias") + } + Label { + Layout.preferredWidth: implicitWidth + property string userName: userInfoGrid.profile.userid + text: userName.substring(userName.indexOf(":")) + color: Nheko.colors.text + } } Label { Layout.preferredWidth: implicitWidth Layout.alignment: Qt.AlignLeft - text: qsTr("Room Visibility") + text: qsTr("Private") color: Nheko.colors.text + HoverHandler { + id: privateHover + } + ToolTip.visible: privateHover.hovered + ToolTip.text: qsTr("Only invited users can join the room") + ToolTip.delay: Nheko.tooltipDelay } - ComboBox { - id: newRoomVisibility - Layout.preferredWidth: implicitWidth + ToggleButton { Layout.alignment: Qt.AlignRight - model: [qsTr("Private"), qsTr("Public")] + Layout.preferredWidth: implicitWidth + id: isPrivate + checked: true } Label { Layout.preferredWidth: implicitWidth Layout.alignment: Qt.AlignLeft - text: qsTr("Room Preset") + text: qsTr("Trusted") color: Nheko.colors.text + HoverHandler { + id: trustedHover + } + ToolTip.visible: trustedHover.hovered + ToolTip.text: qsTr("All invitees are given the same power level as the creator") + ToolTip.delay: Nheko.tooltipDelay } - ComboBox { - id: newRoomPreset - Layout.preferredWidth: implicitWidth + ToggleButton { Layout.alignment: Qt.AlignRight - model: [qsTr("Private Chat"), qsTr("Public Chat"), qsTr("Trusted Private Chat")] + Layout.preferredWidth: implicitWidth + id: isTrusted + checked: false + enabled: isPrivate.checked + } + Label { + Layout.preferredWidth: implicitWidth + Layout.alignment: Qt.AlignLeft + text: qsTr("Encryption") + color: Nheko.colors.text + HoverHandler { + id: encryptionHover + } + ToolTip.visible: encryptionHover.hovered + ToolTip.text: qsTr("Caution: Encryption cannot be disabled") + ToolTip.delay: Nheko.tooltipDelay + } + ToggleButton { + Layout.alignment: Qt.AlignRight + Layout.preferredWidth: implicitWidth + id: isEncrypted + checked: false } } footer: DialogButtonBox { standardButtons: DialogButtonBox.Cancel Button { - text: "Create Room" + text: qsTr("Create Room") DialogButtonBox.buttonRole: DialogButtonBox.AcceptRole } onRejected: createRoomRoot.close(); From 6d1416fb6ec7e64a765af652ea3b21722ec12be3 Mon Sep 17 00:00:00 2001 From: Nicolas Werner Date: Tue, 29 Mar 2022 04:50:25 +0200 Subject: [PATCH 6/6] 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();