From 4e020645f118055b94eda5704573854e4b81cf80 Mon Sep 17 00:00:00 2001 From: Loren Burkholder Date: Fri, 24 Sep 2021 23:27:57 -0400 Subject: Reorganize all the dialogs into the dialogs folder --- resources/qml/InviteDialog.qml | 159 -------------- resources/qml/RawMessageDialog.qml | 52 ----- resources/qml/ReadReceipts.qml | 130 ------------ resources/qml/RoomDirectory.qml | 215 ------------------- resources/qml/RoomMembers.qml | 179 ---------------- resources/qml/RoomSettings.qml | 304 --------------------------- resources/qml/UserProfile.qml | 326 ---------------------------- resources/qml/dialogs/InviteDialog.qml | 160 ++++++++++++++ resources/qml/dialogs/RawMessageDialog.qml | 52 +++++ resources/qml/dialogs/ReadReceipts.qml | 131 ++++++++++++ resources/qml/dialogs/RoomDirectory.qml | 216 +++++++++++++++++++ resources/qml/dialogs/RoomMembers.qml | 180 ++++++++++++++++ resources/qml/dialogs/RoomSettings.qml | 305 +++++++++++++++++++++++++++ resources/qml/dialogs/UserProfile.qml | 327 +++++++++++++++++++++++++++++ resources/res.qrc | 14 +- 15 files changed, 1378 insertions(+), 1372 deletions(-) delete mode 100644 resources/qml/InviteDialog.qml delete mode 100644 resources/qml/RawMessageDialog.qml delete mode 100644 resources/qml/ReadReceipts.qml delete mode 100644 resources/qml/RoomDirectory.qml delete mode 100644 resources/qml/RoomMembers.qml delete mode 100644 resources/qml/RoomSettings.qml delete mode 100644 resources/qml/UserProfile.qml create mode 100644 resources/qml/dialogs/InviteDialog.qml create mode 100644 resources/qml/dialogs/RawMessageDialog.qml create mode 100644 resources/qml/dialogs/ReadReceipts.qml create mode 100644 resources/qml/dialogs/RoomDirectory.qml create mode 100644 resources/qml/dialogs/RoomMembers.qml create mode 100644 resources/qml/dialogs/RoomSettings.qml create mode 100644 resources/qml/dialogs/UserProfile.qml diff --git a/resources/qml/InviteDialog.qml b/resources/qml/InviteDialog.qml deleted file mode 100644 index 916bdd39..00000000 --- a/resources/qml/InviteDialog.qml +++ /dev/null @@ -1,159 +0,0 @@ -// SPDX-FileCopyrightText: 2021 Nheko Contributors -// -// SPDX-License-Identifier: GPL-3.0-or-later - -import QtQuick 2.12 -import QtQuick.Controls 2.12 -import QtQuick.Layouts 1.12 -import im.nheko 1.0 - -ApplicationWindow { - id: inviteDialogRoot - - property string roomId - property string plainRoomName - property InviteesModel invitees - - function addInvite() { - if (inviteeEntry.isValidMxid) { - invitees.addUser(inviteeEntry.text); - inviteeEntry.clear(); - } - } - - function cleanUpAndClose() { - if (inviteeEntry.isValidMxid) - addInvite(); - - invitees.accept(); - close(); - } - - title: qsTr("Invite users to %1").arg(plainRoomName) - height: 380 - width: 340 - palette: Nheko.colors - color: Nheko.colors.window - flags: Qt.Dialog | Qt.WindowCloseButtonHint | Qt.WindowTitleHint - Component.onCompleted: Nheko.reparent(inviteDialogRoot) - - Shortcut { - sequence: "Ctrl+Enter" - onActivated: cleanUpAndClose() - } - - Shortcut { - sequence: StandardKey.Cancel - onActivated: inviteDialogRoot.close() - } - - ColumnLayout { - anchors.fill: parent - anchors.margins: Nheko.paddingMedium - spacing: Nheko.paddingMedium - - Label { - text: qsTr("User ID to invite") - Layout.fillWidth: true - color: Nheko.colors.text - } - - RowLayout { - spacing: Nheko.paddingMedium - - MatrixTextField { - id: inviteeEntry - - property bool isValidMxid: text.match("@.+?:.{3,}") - - backgroundColor: Nheko.colors.window - placeholderText: qsTr("@joe:matrix.org", "Example user id. The name 'joe' can be localized however you want.") - Layout.fillWidth: true - onAccepted: { - if (isValidMxid) - addInvite(); - - } - Component.onCompleted: forceActiveFocus() - Keys.onShortcutOverride: event.accepted = ((event.key === Qt.Key_Return || event.key === Qt.Key_Enter) && (event.modifiers & Qt.ControlModifier)) - Keys.onPressed: { - if ((event.key === Qt.Key_Return || event.key === Qt.Key_Enter) && (event.modifiers === Qt.ControlModifier)) - cleanUpAndClose(); - - } - } - - Button { - text: qsTr("Add") - enabled: inviteeEntry.isValidMxid - onClicked: addInvite() - } - - } - - ListView { - id: inviteesList - - Layout.fillWidth: true - Layout.fillHeight: true - model: invitees - - delegate: RowLayout { - spacing: Nheko.paddingMedium - - Avatar { - width: Nheko.avatarSize - height: Nheko.avatarSize - userid: model.mxid - url: model.avatarUrl.replace("mxc://", "image://MxcImage/") - displayName: model.displayName - onClicked: TimelineManager.openGlobalUserProfile(model.mxid) - } - - ColumnLayout { - spacing: Nheko.paddingSmall - - Label { - text: model.displayName - color: TimelineManager.userColor(model ? model.mxid : "", Nheko.colors.window) - font.pointSize: fontMetrics.font.pointSize - } - - Label { - text: model.mxid - color: Nheko.colors.buttonText - font.pointSize: fontMetrics.font.pointSize * 0.9 - } - - Item { - Layout.fillHeight: true - Layout.fillWidth: true - } - - } - - } - - } - - } - - footer: DialogButtonBox { - id: buttons - - Button { - text: qsTr("Invite") - DialogButtonBox.buttonRole: DialogButtonBox.AcceptRole - enabled: invitees.count > 0 - onClicked: cleanUpAndClose() - } - - Button { - text: qsTr("Cancel") - DialogButtonBox.buttonRole: DialogButtonBox.DestructiveRole - onClicked: inviteDialogRoot.close() - } - - } - -} diff --git a/resources/qml/RawMessageDialog.qml b/resources/qml/RawMessageDialog.qml deleted file mode 100644 index c171de7e..00000000 --- a/resources/qml/RawMessageDialog.qml +++ /dev/null @@ -1,52 +0,0 @@ -// SPDX-FileCopyrightText: 2021 Nheko Contributors -// -// SPDX-License-Identifier: GPL-3.0-or-later - -import QtQuick 2.15 -import QtQuick.Controls 2.15 -import im.nheko 1.0 - -ApplicationWindow { - id: rawMessageRoot - - property alias rawMessage: rawMessageView.text - - height: 420 - width: 420 - palette: Nheko.colors - color: Nheko.colors.window - flags: Qt.Tool | Qt.WindowStaysOnTopHint | Qt.WindowCloseButtonHint | Qt.WindowTitleHint - Component.onCompleted: Nheko.reparent(rawMessageRoot) - - Shortcut { - sequence: StandardKey.Cancel - onActivated: rawMessageRoot.close() - } - - ScrollView { - anchors.margins: Nheko.paddingMedium - anchors.fill: parent - palette: Nheko.colors - padding: Nheko.paddingMedium - - TextArea { - id: rawMessageView - - font: Nheko.monospaceFont() - color: Nheko.colors.text - readOnly: true - - background: Rectangle { - color: Nheko.colors.base - } - - } - - } - - footer: DialogButtonBox { - standardButtons: DialogButtonBox.Ok - onAccepted: rawMessageRoot.close() - } - -} diff --git a/resources/qml/ReadReceipts.qml b/resources/qml/ReadReceipts.qml deleted file mode 100644 index e1dd7c00..00000000 --- a/resources/qml/ReadReceipts.qml +++ /dev/null @@ -1,130 +0,0 @@ -// SPDX-FileCopyrightText: 2021 Nheko Contributors -// -// SPDX-License-Identifier: GPL-3.0-or-later - -import QtQuick 2.15 -import QtQuick.Controls 2.15 -import QtQuick.Layouts 1.15 -import im.nheko 1.0 - -ApplicationWindow { - id: readReceiptsRoot - - property ReadReceiptsProxy readReceipts - property Room room - - height: 380 - width: 340 - minimumHeight: 380 - minimumWidth: headerTitle.width + 2 * Nheko.paddingMedium - palette: Nheko.colors - color: Nheko.colors.window - flags: Qt.Dialog | Qt.WindowCloseButtonHint | Qt.WindowTitleHint - Component.onCompleted: Nheko.reparent(readReceiptsRoot) - - Shortcut { - sequence: StandardKey.Cancel - onActivated: readReceiptsRoot.close() - } - - ColumnLayout { - anchors.fill: parent - anchors.margins: Nheko.paddingMedium - spacing: Nheko.paddingMedium - - Label { - id: headerTitle - - color: Nheko.colors.text - Layout.alignment: Qt.AlignCenter - text: qsTr("Read receipts") - font.pointSize: fontMetrics.font.pointSize * 1.5 - } - - ScrollView { - palette: Nheko.colors - padding: Nheko.paddingMedium - ScrollBar.horizontal.visible: false - Layout.fillHeight: true - Layout.minimumHeight: 200 - Layout.fillWidth: true - - ListView { - id: readReceiptsList - - clip: true - spacing: Nheko.paddingMedium - boundsBehavior: Flickable.StopAtBounds - model: readReceipts - - delegate: RowLayout { - spacing: Nheko.paddingMedium - - Avatar { - width: Nheko.avatarSize - height: Nheko.avatarSize - userid: model.mxid - url: model.avatarUrl.replace("mxc://", "image://MxcImage/") - displayName: model.displayName - onClicked: room.openUserProfile(model.mxid) - ToolTip.visible: avatarHover.hovered - ToolTip.text: model.mxid - - HoverHandler { - id: avatarHover - } - - } - - ColumnLayout { - spacing: Nheko.paddingSmall - - Label { - text: model.displayName - color: TimelineManager.userColor(model ? model.mxid : "", Nheko.colors.window) - font.pointSize: fontMetrics.font.pointSize - ToolTip.visible: displayNameHover.hovered - ToolTip.text: model.mxid - - TapHandler { - onSingleTapped: room.openUserProfile(userId) - } - - CursorShape { - anchors.fill: parent - cursorShape: Qt.PointingHandCursor - } - - HoverHandler { - id: displayNameHover - } - - } - - Label { - text: model.timestamp - color: Nheko.colors.buttonText - font.pointSize: fontMetrics.font.pointSize * 0.9 - } - - Item { - Layout.fillHeight: true - Layout.fillWidth: true - } - - } - - } - - } - - } - - } - - footer: DialogButtonBox { - standardButtons: DialogButtonBox.Ok - onAccepted: readReceiptsRoot.close() - } - -} diff --git a/resources/qml/RoomDirectory.qml b/resources/qml/RoomDirectory.qml deleted file mode 100644 index 54d405ff..00000000 --- a/resources/qml/RoomDirectory.qml +++ /dev/null @@ -1,215 +0,0 @@ -// SPDX-FileCopyrightText: 2021 Nheko Contributors -// -// SPDX-License-Identifier: GPL-3.0-or-later - -import "./ui" -import QtQuick 2.9 -import QtQuick.Controls 2.3 -import QtQuick.Layouts 1.3 -import im.nheko 1.0 - -ApplicationWindow { - id: roomDirectoryWindow - - property RoomDirectoryModel publicRooms - - visible: true - minimumWidth: 650 - minimumHeight: 420 - palette: Nheko.colors - color: Nheko.colors.window - modality: Qt.WindowModal - flags: Qt.Dialog | Qt.WindowCloseButtonHint | Qt.WindowTitleHint - Component.onCompleted: Nheko.reparent(roomDirectoryWindow) - title: qsTr("Explore Public Rooms") - - Shortcut { - sequence: StandardKey.Cancel - onActivated: roomDirectoryWindow.close() - } - - ListView { - id: roomDirView - - anchors.fill: parent - model: publicRooms - - ScrollHelper { - flickable: parent - anchors.fill: parent - enabled: !Settings.mobileMode - } - - delegate: Rectangle { - id: roomDirDelegate - - property color background: Nheko.colors.window - property color importantText: Nheko.colors.text - property color unimportantText: Nheko.colors.buttonText - property int avatarSize: fontMetrics.lineSpacing * 4 - - color: background - height: avatarSize + Nheko.paddingLarge - width: ListView.view.width - - RowLayout { - spacing: Nheko.paddingMedium - anchors.fill: parent - anchors.margins: Nheko.paddingLarge - implicitHeight: textContent.height - - Avatar { - id: roomAvatar - - Layout.alignment: Qt.AlignVCenter - width: avatarSize - height: avatarSize - url: model.avatarUrl.replace("mxc://", "image://MxcImage/") - roomid: model.roomid - displayName: model.name - } - - ColumnLayout { - id: textContent - - Layout.alignment: Qt.AlignLeft - width: parent.width - avatar.width - Layout.preferredWidth: parent.width - avatar.width - spacing: Nheko.paddingSmall - - ElidedLabel { - Layout.alignment: Qt.AlignBottom - color: roomDirDelegate.importantText - elideWidth: textContent.width - numMembersRectangle.width - buttonRectangle.width - font.pixelSize: fontMetrics.font.pixelSize * 1.1 - fullText: model.name - } - - RowLayout { - id: roomDescriptionRow - - Layout.preferredWidth: parent.width - spacing: Nheko.paddingSmall - Layout.alignment: Qt.AlignVCenter | Qt.AlignLeft - Layout.preferredHeight: fontMetrics.lineSpacing * 4 - - Label { - id: roomTopic - - color: roomDirDelegate.unimportantText - Layout.alignment: Qt.AlignVCenter | Qt.AlignLeft - font.pixelSize: fontMetrics.font.pixelSize - elide: Text.ElideRight - maximumLineCount: 2 - Layout.fillWidth: true - text: model.topic - verticalAlignment: Text.AlignVCenter - wrapMode: Text.WordWrap - } - - Item { - id: numMembersRectangle - - Layout.margins: Nheko.paddingSmall - width: roomCount.width - - Label { - id: roomCount - - color: roomDirDelegate.unimportantText - anchors.centerIn: parent - font.pixelSize: fontMetrics.font.pixelSize - text: model.numMembers.toString() - } - - } - - Item { - id: buttonRectangle - - Layout.margins: Nheko.paddingSmall - width: joinRoomButton.width - - Button { - id: joinRoomButton - - visible: model.canJoin - anchors.centerIn: parent - text: "Join" - onClicked: publicRooms.joinRoom(model.index) - } - - } - - } - - } - - } - - } - - footer: Item { - anchors.horizontalCenter: parent.horizontalCenter - width: parent.width - visible: !publicRooms.reachedEndOfPagination && publicRooms.loadingMoreRooms - // hacky but works - height: loadingSpinner.height + 2 * Nheko.paddingLarge - anchors.margins: Nheko.paddingLarge - - Spinner { - id: loadingSpinner - - anchors.centerIn: parent - anchors.margins: Nheko.paddingLarge - running: visible - foreground: Nheko.colors.mid - } - - } - - } - - publicRooms: RoomDirectoryModel { - } - - header: RowLayout { - id: searchBarLayout - - spacing: Nheko.paddingMedium - width: parent.width - implicitHeight: roomSearch.height - - MatrixTextField { - id: roomSearch - - Layout.fillWidth: true - selectByMouse: true - font.pixelSize: fontMetrics.font.pixelSize - padding: Nheko.paddingMedium - color: Nheko.colors.text - placeholderText: qsTr("Search for public rooms") - onTextChanged: searchTimer.restart() - } - - MatrixTextField { - id: chooseServer - Layout.minimumWidth: 0.3 * header.width - Layout.maximumWidth: 0.3 * header.width - - padding: Nheko.paddingMedium - color: Nheko.colors.text - placeholderText: qsTr("Choose custom homeserver") - onTextChanged: publicRooms.setMatrixServer(text) - } - - Timer { - id: searchTimer - - interval: 350 - onTriggered: roomDirView.model.setSearchTerm(roomSearch.text) - } - - } - -} diff --git a/resources/qml/RoomMembers.qml b/resources/qml/RoomMembers.qml deleted file mode 100644 index 3376a4b6..00000000 --- a/resources/qml/RoomMembers.qml +++ /dev/null @@ -1,179 +0,0 @@ -// SPDX-FileCopyrightText: 2021 Nheko Contributors -// -// SPDX-License-Identifier: GPL-3.0-or-later - -import "./ui" -import QtQuick 2.12 -import QtQuick.Controls 2.12 -import QtQuick.Layouts 1.12 -import QtQuick.Window 2.13 -import im.nheko 1.0 - -ApplicationWindow { - id: roomMembersRoot - - property MemberList members - property Room room - - title: qsTr("Members of %1").arg(members.roomName) - height: 650 - width: 420 - minimumHeight: 420 - palette: Nheko.colors - color: Nheko.colors.window - flags: Qt.Dialog | Qt.WindowCloseButtonHint | Qt.WindowTitleHint - Component.onCompleted: Nheko.reparent(roomMembersRoot) - - Shortcut { - sequence: StandardKey.Cancel - onActivated: roomMembersRoot.close() - } - - ColumnLayout { - anchors.fill: parent - anchors.margins: Nheko.paddingMedium - spacing: Nheko.paddingMedium - - Avatar { - id: roomAvatar - - width: 130 - height: width - roomid: members.roomId - displayName: members.roomName - Layout.alignment: Qt.AlignHCenter - url: members.avatarUrl.replace("mxc://", "image://MxcImage/") - onClicked: TimelineManager.openRoomSettings(members.roomId) - } - - ElidedLabel { - font.pixelSize: fontMetrics.font.pixelSize * 2 - fullText: qsTr("%n people in %1", "Summary above list of members", members.memberCount).arg(members.roomName) - Layout.alignment: Qt.AlignHCenter - elideWidth: parent.width - Nheko.paddingMedium - } - - ImageButton { - Layout.alignment: Qt.AlignHCenter - image: ":/icons/icons/ui/add-square-button.png" - hoverEnabled: true - ToolTip.visible: hovered - ToolTip.text: qsTr("Invite more people") - onClicked: TimelineManager.openInviteUsers(members.roomId) - } - - ScrollView { - palette: Nheko.colors - padding: Nheko.paddingMedium - ScrollBar.horizontal.visible: false - Layout.fillHeight: true - Layout.minimumHeight: 200 - Layout.fillWidth: true - - ListView { - id: memberList - - clip: true - spacing: Nheko.paddingMedium - boundsBehavior: Flickable.StopAtBounds - model: members - - ScrollHelper { - flickable: parent - anchors.fill: parent - enabled: !Settings.mobileMode - } - - delegate: RowLayout { - id: del - - width: ListView.view.width - spacing: Nheko.paddingMedium - - Avatar { - id: avatar - - width: Nheko.avatarSize - height: Nheko.avatarSize - userid: model.mxid - url: model.avatarUrl.replace("mxc://", "image://MxcImage/") - displayName: model.displayName - onClicked: Rooms.currentRoom.openUserProfile(model.mxid) - } - - ColumnLayout { - spacing: Nheko.paddingSmall - - ElidedLabel { - fullText: model.displayName - color: TimelineManager.userColor(model ? model.mxid : "", Nheko.colors.window) - font.pixelSize: fontMetrics.font.pixelSize - elideWidth: del.width - Nheko.paddingMedium * 2 - avatar.width - encryptInd.width - } - - ElidedLabel { - fullText: model.mxid - color: Nheko.colors.buttonText - font.pixelSize: Math.ceil(fontMetrics.font.pixelSize * 0.9) - elideWidth: del.width - Nheko.paddingMedium * 2 - avatar.width - encryptInd.width - } - - Item { - Layout.fillHeight: true - Layout.fillWidth: true - } - - } - - EncryptionIndicator { - id: encryptInd - - Layout.alignment: Qt.AlignRight - visible: room.isEncrypted - encrypted: room.isEncrypted - trust: encrypted ? model.trustlevel : Crypto.Unverified - ToolTip.text: { - if (!encrypted) - return qsTr("This room is not encrypted!"); - - switch (trust) { - case Crypto.Verified: - return qsTr("This user is verified."); - case Crypto.TOFU: - return qsTr("This user isn't verified, but is still using the same master key from the first time you met."); - default: - return qsTr("This user has unverified devices!"); - } - } - } - - } - - footer: Item { - width: parent.width - visible: (members.numUsersLoaded < members.memberCount) && members.loadingMoreMembers - // use the default height if it's visible, otherwise no height at all - height: membersLoadingSpinner.height - anchors.margins: Nheko.paddingMedium - - Spinner { - id: membersLoadingSpinner - - anchors.centerIn: parent - height: visible ? 35 : 0 - } - - } - - } - - } - - } - - footer: DialogButtonBox { - standardButtons: DialogButtonBox.Ok - onAccepted: roomMembersRoot.close() - } - -} diff --git a/resources/qml/RoomSettings.qml b/resources/qml/RoomSettings.qml deleted file mode 100644 index 6caf8790..00000000 --- a/resources/qml/RoomSettings.qml +++ /dev/null @@ -1,304 +0,0 @@ -// SPDX-FileCopyrightText: 2021 Nheko Contributors -// -// SPDX-License-Identifier: GPL-3.0-or-later - -import "./ui" -import Qt.labs.platform 1.1 as Platform -import QtQuick 2.15 -import QtQuick.Controls 2.3 -import QtQuick.Layouts 1.2 -import QtQuick.Window 2.13 -import im.nheko 1.0 - -ApplicationWindow { - id: roomSettingsDialog - - property var roomSettings - - minimumWidth: 450 - minimumHeight: 680 - palette: Nheko.colors - color: Nheko.colors.window - modality: Qt.NonModal - flags: Qt.Dialog | Qt.WindowCloseButtonHint | Qt.WindowTitleHint - Component.onCompleted: Nheko.reparent(roomSettingsDialog) - title: qsTr("Room Settings") - - Shortcut { - sequence: StandardKey.Cancel - onActivated: roomSettingsDialog.close() - } - - ColumnLayout { - id: contentLayout1 - - anchors.fill: parent - anchors.margins: 10 - spacing: 10 - - Avatar { - url: roomSettings.roomAvatarUrl.replace("mxc://", "image://MxcImage/") - roomid: roomSettings.roomId - displayName: roomSettings.roomName - height: 130 - width: 130 - Layout.alignment: Qt.AlignHCenter - onClicked: { - if (roomSettings.canChangeAvatar) - roomSettings.updateAvatar(); - - } - } - - Spinner { - Layout.alignment: Qt.AlignHCenter - visible: roomSettings.isLoading - foreground: Nheko.colors.mid - running: roomSettings.isLoading - } - - Text { - id: errorText - - color: "red" - visible: opacity > 0 - opacity: 0 - Layout.alignment: Qt.AlignHCenter - } - - SequentialAnimation { - id: hideErrorAnimation - - running: false - - PauseAnimation { - duration: 4000 - } - - NumberAnimation { - target: errorText - property: 'opacity' - to: 0 - duration: 1000 - } - - } - - Connections { - target: roomSettings - onDisplayError: { - errorText.text = errorMessage; - errorText.opacity = 1; - hideErrorAnimation.restart(); - } - } - - ColumnLayout { - Layout.alignment: Qt.AlignHCenter - - MatrixText { - text: roomSettings.roomName - font.pixelSize: fontMetrics.font.pixelSize * 2 - Layout.alignment: Qt.AlignHCenter - } - - MatrixText { - text: qsTr("%1 member(s)").arg(roomSettings.memberCount) - Layout.alignment: Qt.AlignHCenter - - TapHandler { - onTapped: TimelineManager.openRoomMembers(roomSettings.roomId) - } - - CursorShape { - cursorShape: Qt.PointingHandCursor - anchors.fill: parent - } - - } - - } - - ImageButton { - Layout.alignment: Qt.AlignHCenter - image: ":/icons/icons/ui/edit.png" - visible: roomSettings.canChangeNameAndTopic - onClicked: roomSettings.openEditModal() - } - - ScrollView { - Layout.fillHeight: true - Layout.alignment: Qt.AlignHCenter - Layout.fillWidth: true - Layout.leftMargin: Nheko.paddingLarge - Layout.rightMargin: Nheko.paddingLarge - - TextArea { - text: TimelineManager.escapeEmoji(roomSettings.roomTopic) - wrapMode: TextEdit.WordWrap - textFormat: TextEdit.RichText - readOnly: true - background: null - selectByMouse: true - color: Nheko.colors.text - horizontalAlignment: TextEdit.AlignHCenter - onLinkActivated: Nheko.openLink(link) - - CursorShape { - anchors.fill: parent - cursorShape: parent.hoveredLink ? Qt.PointingHandCursor : Qt.ArrowCursor - } - - } - - } - - GridLayout { - columns: 2 - rowSpacing: Nheko.paddingLarge - - MatrixText { - text: qsTr("SETTINGS") - font.bold: true - } - - Item { - Layout.fillWidth: true - } - - MatrixText { - text: qsTr("Notifications") - Layout.fillWidth: true - } - - ComboBox { - model: [qsTr("Muted"), qsTr("Mentions only"), qsTr("All messages")] - currentIndex: roomSettings.notifications - onActivated: { - roomSettings.changeNotifications(index); - } - Layout.fillWidth: true - } - - MatrixText { - text: qsTr("Room access") - Layout.fillWidth: true - } - - ComboBox { - enabled: roomSettings.canChangeJoinRules - model: { - let opts = [qsTr("Anyone and guests"), qsTr("Anyone"), qsTr("Invited users")]; - if (roomSettings.supportsKnocking) - opts.push(qsTr("By knocking")); - - if (roomSettings.supportsRestricted) - opts.push(qsTr("Restricted by membership in other rooms")); - - return opts; - } - currentIndex: roomSettings.accessJoinRules - onActivated: { - roomSettings.changeAccessRules(index); - } - Layout.fillWidth: true - } - - MatrixText { - text: qsTr("Encryption") - } - - ToggleButton { - id: encryptionToggle - - checked: roomSettings.isEncryptionEnabled - onClicked: { - if (roomSettings.isEncryptionEnabled) { - checked = true; - return ; - } - confirmEncryptionDialog.open(); - } - Layout.alignment: Qt.AlignRight - } - - Platform.MessageDialog { - id: confirmEncryptionDialog - - title: qsTr("End-to-End Encryption") - text: qsTr("Encryption is currently experimental and things might break unexpectedly.
- Please take note that it can't be disabled afterwards.") - modality: Qt.Modal - onAccepted: { - if (roomSettings.isEncryptionEnabled) - return ; - - roomSettings.enableEncryption(); - } - onRejected: { - encryptionToggle.checked = false; - } - buttons: Dialog.Ok | Dialog.Cancel - } - - MatrixText { - text: qsTr("Sticker & Emote Settings") - } - - Button { - text: qsTr("Change") - ToolTip.text: qsTr("Change what packs are enabled, remove packs or create new ones") - onClicked: TimelineManager.openImagePackSettings(roomSettings.roomId) - Layout.alignment: Qt.AlignRight - } - - Item { - // for adding extra space between sections - Layout.fillWidth: true - } - - Item { - // for adding extra space between sections - Layout.fillWidth: true - } - - MatrixText { - text: qsTr("INFO") - font.bold: true - } - - Item { - Layout.fillWidth: true - } - - MatrixText { - text: qsTr("Internal ID") - } - - MatrixText { - text: roomSettings.roomId - font.pixelSize: fontMetrics.font.pixelSize * 1.2 - Layout.alignment: Qt.AlignRight - } - - MatrixText { - text: qsTr("Room Version") - } - - MatrixText { - text: roomSettings.roomVersion - font.pixelSize: fontMetrics.font.pixelSize * 1.2 - Layout.alignment: Qt.AlignRight - } - - } - - DialogButtonBox { - Layout.fillWidth: true - standardButtons: DialogButtonBox.Ok - onAccepted: close() - } - - } - -} diff --git a/resources/qml/UserProfile.qml b/resources/qml/UserProfile.qml deleted file mode 100644 index f57a9441..00000000 --- a/resources/qml/UserProfile.qml +++ /dev/null @@ -1,326 +0,0 @@ -// SPDX-FileCopyrightText: 2021 Nheko Contributors -// -// SPDX-License-Identifier: GPL-3.0-or-later - -import "./device-verification" -import "./ui" -import QtQuick 2.15 -import QtQuick.Controls 2.15 -import QtQuick.Layouts 1.2 -import QtQuick.Window 2.13 -import im.nheko 1.0 - -ApplicationWindow { - // this does not work in ApplicationWindow, just in Window - //transientParent: Nheko.mainwindow() - - id: userProfileDialog - - property var profile - - height: 650 - width: 420 - minimumWidth: 150 - minimumHeight: 150 - palette: Nheko.colors - color: Nheko.colors.window - title: profile.isGlobalUserProfile ? qsTr("Global User Profile") : qsTr("Room User Profile") - modality: Qt.NonModal - flags: Qt.Dialog | Qt.WindowCloseButtonHint | Qt.WindowTitleHint - Component.onCompleted: Nheko.reparent(userProfileDialog) - - Shortcut { - sequence: StandardKey.Cancel - onActivated: userProfileDialog.close() - } - - - ListView { - - ScrollHelper { - flickable: parent - anchors.fill: parent - enabled: !Settings.mobileMode - } - - header: ColumnLayout { - id: contentL - width: devicelist.width - - spacing: 10 - - Avatar { - url: profile.avatarUrl.replace("mxc://", "image://MxcImage/") - height: 130 - width: 130 - displayName: profile.displayName - id: displayAvatar - userid: profile.userid - Layout.alignment: Qt.AlignHCenter - onClicked: TimelineManager.openImageOverlay(profile.avatarUrl, "") - - ImageButton { - hoverEnabled: true - ToolTip.visible: hovered - ToolTip.text: profile.isGlobalUserProfile ? qsTr("Change avatar globally.") : qsTr("Change avatar. Will only apply to this room.") - anchors.left: displayAvatar.left - anchors.top: displayAvatar.top - anchors.leftMargin: Nheko.paddingMedium - anchors.topMargin: Nheko.paddingMedium - visible: profile.isSelf - image: ":/icons/icons/ui/edit.png" - onClicked: profile.changeAvatar() - } - } - - Spinner { - Layout.alignment: Qt.AlignHCenter - running: profile.isLoading - visible: profile.isLoading - foreground: Nheko.colors.mid - } - - Text { - id: errorText - - color: "red" - visible: opacity > 0 - opacity: 0 - Layout.alignment: Qt.AlignHCenter - } - - SequentialAnimation { - id: hideErrorAnimation - - running: false - - PauseAnimation { - duration: 4000 - } - - NumberAnimation { - target: errorText - property: 'opacity' - to: 0 - duration: 1000 - } - - } - - Connections { - function onDisplayError(errorMessage) { - errorText.text = errorMessage; - errorText.opacity = 1; - hideErrorAnimation.restart(); - } - - target: profile - } - - TextInput { - id: displayUsername - - property bool isUsernameEditingAllowed - - readOnly: !isUsernameEditingAllowed - text: profile.displayName - font.pixelSize: 20 - color: TimelineManager.userColor(profile.userid, Nheko.colors.window) - font.bold: true - Layout.alignment: Qt.AlignHCenter - selectByMouse: true - onAccepted: { - profile.changeUsername(displayUsername.text); - displayUsername.isUsernameEditingAllowed = false; - } - - ImageButton { - visible: profile.isSelf - anchors.leftMargin: Nheko.paddingSmall - anchors.left: displayUsername.right - anchors.verticalCenter: displayUsername.verticalCenter - hoverEnabled: true - ToolTip.visible: hovered - ToolTip.text: profile.isGlobalUserProfile ? qsTr("Change display name globally.") : qsTr("Change display name. Will only apply to this room.") - image: displayUsername.isUsernameEditingAllowed ? ":/icons/icons/ui/checkmark.png" : ":/icons/icons/ui/edit.png" - onClicked: { - if (displayUsername.isUsernameEditingAllowed) { - profile.changeUsername(displayUsername.text); - displayUsername.isUsernameEditingAllowed = false; - } else { - displayUsername.isUsernameEditingAllowed = true; - displayUsername.focus = true; - displayUsername.selectAll(); - } - } - } - - } - - MatrixText { - text: profile.userid - Layout.alignment: Qt.AlignHCenter - } - - - RowLayout { - visible: !profile.isGlobalUserProfile - Layout.alignment: Qt.AlignHCenter - spacing: Nheko.paddingSmall - MatrixText { - id: displayRoomname - text: qsTr("Room: %1").arg(profile.room?profile.room.roomName:"") - ToolTip.text: qsTr("This is a room-specific profile. The user's name and avatar may be different from their global versions.") - ToolTip.visible: ma.hovered - HoverHandler { - id: ma - } - } - - ImageButton { - image: ":/icons/icons/ui/world.png" - hoverEnabled: true - ToolTip.visible: hovered - ToolTip.text: qsTr("Open the global profile for this user.") - onClicked: profile.openGlobalProfile() - } - } - - Button { - id: verifyUserButton - - text: qsTr("Verify") - Layout.alignment: Qt.AlignHCenter - enabled: profile.userVerified != Crypto.Verified - visible: profile.userVerified != Crypto.Verified && !profile.isSelf && profile.userVerificationEnabled - onClicked: profile.verify() - } - - Image { - Layout.preferredHeight: 16 - Layout.preferredWidth: 16 - source: "image://colorimage/:/icons/icons/ui/lock.png?" + ((profile.userVerified == Crypto.Verified) ? "green" : Nheko.colors.buttonText) - visible: profile.userVerified != Crypto.Unverified - Layout.alignment: Qt.AlignHCenter - } - - RowLayout { - // ImageButton{ - // image:":/icons/icons/ui/volume-off-indicator.png" - // Layout.margins: { - // left: 5 - // right: 5 - // } - // ToolTip.visible: hovered - // ToolTip.text: qsTr("Ignore messages from this user.") - // onClicked : { - // profile.ignoreUser() - // } - // } - - Layout.alignment: Qt.AlignHCenter - Layout.bottomMargin: 10 - spacing: Nheko.paddingSmall - - ImageButton { - image: ":/icons/icons/ui/black-bubble-speech.png" - hoverEnabled: true - ToolTip.visible: hovered - ToolTip.text: qsTr("Start a private chat.") - onClicked: profile.startChat() - } - - ImageButton { - image: ":/icons/icons/ui/round-remove-button.png" - hoverEnabled: true - ToolTip.visible: hovered - ToolTip.text: qsTr("Kick the user.") - onClicked: profile.kickUser() - visible: !profile.isGlobalUserProfile && profile.room.permissions.canKick() - } - - ImageButton { - image: ":/icons/icons/ui/do-not-disturb-rounded-sign.png" - hoverEnabled: true - ToolTip.visible: hovered - ToolTip.text: qsTr("Ban the user.") - onClicked: profile.banUser() - visible: !profile.isGlobalUserProfile && profile.room.permissions.canBan() - } - - } - } - - id: devicelist - Layout.fillHeight: true - Layout.fillWidth: true - clip: true - spacing: 8 - boundsBehavior: Flickable.StopAtBounds - model: profile.deviceList - anchors.fill: parent - anchors.margins: 10 - - - delegate: RowLayout { - width: devicelist.width - spacing: 4 - - ColumnLayout { - spacing: 0 - - Text { - Layout.fillWidth: true - Layout.alignment: Qt.AlignLeft - elide: Text.ElideRight - font.bold: true - color: Nheko.colors.text - text: model.deviceId - } - - Text { - Layout.fillWidth: true - Layout.alignment: Qt.AlignRight - elide: Text.ElideRight - color: Nheko.colors.text - text: model.deviceName - } - - } - - Image { - Layout.preferredHeight: 16 - Layout.preferredWidth: 16 - source: ((model.verificationStatus == VerificationStatus.VERIFIED) ? "image://colorimage/:/icons/icons/ui/lock.png?green" : ((model.verificationStatus == VerificationStatus.UNVERIFIED) ? "image://colorimage/:/icons/icons/ui/unlock.png?yellow" : "image://colorimage/:/icons/icons/ui/unlock.png?red")) - } - - Button { - id: verifyButton - - visible: (!profile.userVerificationEnabled && !profile.isSelf) || (profile.isSelf && (model.verificationStatus != VerificationStatus.VERIFIED || !profile.userVerificationEnabled)) - text: (model.verificationStatus != VerificationStatus.VERIFIED) ? qsTr("Verify") : qsTr("Unverify") - onClicked: { - if (model.verificationStatus == VerificationStatus.VERIFIED) - profile.unverify(model.deviceId); - else - profile.verify(model.deviceId); - } - } - - } - footerPositioning: ListView.OverlayFooter - footer: DialogButtonBox { - z: 2 - width: devicelist.width - alignment: Qt.AlignRight - standardButtons: DialogButtonBox.Ok - onAccepted: userProfileDialog.close() - background: Rectangle { - anchors.fill: parent - color: Nheko.colors.window - } - } - - } - -} diff --git a/resources/qml/dialogs/InviteDialog.qml b/resources/qml/dialogs/InviteDialog.qml new file mode 100644 index 00000000..86c176be --- /dev/null +++ b/resources/qml/dialogs/InviteDialog.qml @@ -0,0 +1,160 @@ +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later + +import ".." +import QtQuick 2.12 +import QtQuick.Controls 2.12 +import QtQuick.Layouts 1.12 +import im.nheko 1.0 + +ApplicationWindow { + id: inviteDialogRoot + + property string roomId + property string plainRoomName + property InviteesModel invitees + + function addInvite() { + if (inviteeEntry.isValidMxid) { + invitees.addUser(inviteeEntry.text); + inviteeEntry.clear(); + } + } + + function cleanUpAndClose() { + if (inviteeEntry.isValidMxid) + addInvite(); + + invitees.accept(); + close(); + } + + title: qsTr("Invite users to %1").arg(plainRoomName) + height: 380 + width: 340 + palette: Nheko.colors + color: Nheko.colors.window + flags: Qt.Dialog | Qt.WindowCloseButtonHint | Qt.WindowTitleHint + Component.onCompleted: Nheko.reparent(inviteDialogRoot) + + Shortcut { + sequence: "Ctrl+Enter" + onActivated: cleanUpAndClose() + } + + Shortcut { + sequence: StandardKey.Cancel + onActivated: inviteDialogRoot.close() + } + + ColumnLayout { + anchors.fill: parent + anchors.margins: Nheko.paddingMedium + spacing: Nheko.paddingMedium + + Label { + text: qsTr("User ID to invite") + Layout.fillWidth: true + color: Nheko.colors.text + } + + RowLayout { + spacing: Nheko.paddingMedium + + MatrixTextField { + id: inviteeEntry + + property bool isValidMxid: text.match("@.+?:.{3,}") + + backgroundColor: Nheko.colors.window + placeholderText: qsTr("@joe:matrix.org", "Example user id. The name 'joe' can be localized however you want.") + Layout.fillWidth: true + onAccepted: { + if (isValidMxid) + addInvite(); + + } + Component.onCompleted: forceActiveFocus() + Keys.onShortcutOverride: event.accepted = ((event.key === Qt.Key_Return || event.key === Qt.Key_Enter) && (event.modifiers & Qt.ControlModifier)) + Keys.onPressed: { + if ((event.key === Qt.Key_Return || event.key === Qt.Key_Enter) && (event.modifiers === Qt.ControlModifier)) + cleanUpAndClose(); + + } + } + + Button { + text: qsTr("Add") + enabled: inviteeEntry.isValidMxid + onClicked: addInvite() + } + + } + + ListView { + id: inviteesList + + Layout.fillWidth: true + Layout.fillHeight: true + model: invitees + + delegate: RowLayout { + spacing: Nheko.paddingMedium + + Avatar { + width: Nheko.avatarSize + height: Nheko.avatarSize + userid: model.mxid + url: model.avatarUrl.replace("mxc://", "image://MxcImage/") + displayName: model.displayName + onClicked: TimelineManager.openGlobalUserProfile(model.mxid) + } + + ColumnLayout { + spacing: Nheko.paddingSmall + + Label { + text: model.displayName + color: TimelineManager.userColor(model ? model.mxid : "", Nheko.colors.window) + font.pointSize: fontMetrics.font.pointSize + } + + Label { + text: model.mxid + color: Nheko.colors.buttonText + font.pointSize: fontMetrics.font.pointSize * 0.9 + } + + Item { + Layout.fillHeight: true + Layout.fillWidth: true + } + + } + + } + + } + + } + + footer: DialogButtonBox { + id: buttons + + Button { + text: qsTr("Invite") + DialogButtonBox.buttonRole: DialogButtonBox.AcceptRole + enabled: invitees.count > 0 + onClicked: cleanUpAndClose() + } + + Button { + text: qsTr("Cancel") + DialogButtonBox.buttonRole: DialogButtonBox.DestructiveRole + onClicked: inviteDialogRoot.close() + } + + } + +} diff --git a/resources/qml/dialogs/RawMessageDialog.qml b/resources/qml/dialogs/RawMessageDialog.qml new file mode 100644 index 00000000..c171de7e --- /dev/null +++ b/resources/qml/dialogs/RawMessageDialog.qml @@ -0,0 +1,52 @@ +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later + +import QtQuick 2.15 +import QtQuick.Controls 2.15 +import im.nheko 1.0 + +ApplicationWindow { + id: rawMessageRoot + + property alias rawMessage: rawMessageView.text + + height: 420 + width: 420 + palette: Nheko.colors + color: Nheko.colors.window + flags: Qt.Tool | Qt.WindowStaysOnTopHint | Qt.WindowCloseButtonHint | Qt.WindowTitleHint + Component.onCompleted: Nheko.reparent(rawMessageRoot) + + Shortcut { + sequence: StandardKey.Cancel + onActivated: rawMessageRoot.close() + } + + ScrollView { + anchors.margins: Nheko.paddingMedium + anchors.fill: parent + palette: Nheko.colors + padding: Nheko.paddingMedium + + TextArea { + id: rawMessageView + + font: Nheko.monospaceFont() + color: Nheko.colors.text + readOnly: true + + background: Rectangle { + color: Nheko.colors.base + } + + } + + } + + footer: DialogButtonBox { + standardButtons: DialogButtonBox.Ok + onAccepted: rawMessageRoot.close() + } + +} diff --git a/resources/qml/dialogs/ReadReceipts.qml b/resources/qml/dialogs/ReadReceipts.qml new file mode 100644 index 00000000..e825dd81 --- /dev/null +++ b/resources/qml/dialogs/ReadReceipts.qml @@ -0,0 +1,131 @@ +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later + +import ".." +import QtQuick 2.15 +import QtQuick.Controls 2.15 +import QtQuick.Layouts 1.15 +import im.nheko 1.0 + +ApplicationWindow { + id: readReceiptsRoot + + property ReadReceiptsProxy readReceipts + property Room room + + height: 380 + width: 340 + minimumHeight: 380 + minimumWidth: headerTitle.width + 2 * Nheko.paddingMedium + palette: Nheko.colors + color: Nheko.colors.window + flags: Qt.Dialog | Qt.WindowCloseButtonHint | Qt.WindowTitleHint + Component.onCompleted: Nheko.reparent(readReceiptsRoot) + + Shortcut { + sequence: StandardKey.Cancel + onActivated: readReceiptsRoot.close() + } + + ColumnLayout { + anchors.fill: parent + anchors.margins: Nheko.paddingMedium + spacing: Nheko.paddingMedium + + Label { + id: headerTitle + + color: Nheko.colors.text + Layout.alignment: Qt.AlignCenter + text: qsTr("Read receipts") + font.pointSize: fontMetrics.font.pointSize * 1.5 + } + + ScrollView { + palette: Nheko.colors + padding: Nheko.paddingMedium + ScrollBar.horizontal.visible: false + Layout.fillHeight: true + Layout.minimumHeight: 200 + Layout.fillWidth: true + + ListView { + id: readReceiptsList + + clip: true + spacing: Nheko.paddingMedium + boundsBehavior: Flickable.StopAtBounds + model: readReceipts + + delegate: RowLayout { + spacing: Nheko.paddingMedium + + Avatar { + width: Nheko.avatarSize + height: Nheko.avatarSize + userid: model.mxid + url: model.avatarUrl.replace("mxc://", "image://MxcImage/") + displayName: model.displayName + onClicked: room.openUserProfile(model.mxid) + ToolTip.visible: avatarHover.hovered + ToolTip.text: model.mxid + + HoverHandler { + id: avatarHover + } + + } + + ColumnLayout { + spacing: Nheko.paddingSmall + + Label { + text: model.displayName + color: TimelineManager.userColor(model ? model.mxid : "", Nheko.colors.window) + font.pointSize: fontMetrics.font.pointSize + ToolTip.visible: displayNameHover.hovered + ToolTip.text: model.mxid + + TapHandler { + onSingleTapped: room.openUserProfile(userId) + } + + CursorShape { + anchors.fill: parent + cursorShape: Qt.PointingHandCursor + } + + HoverHandler { + id: displayNameHover + } + + } + + Label { + text: model.timestamp + color: Nheko.colors.buttonText + font.pointSize: fontMetrics.font.pointSize * 0.9 + } + + Item { + Layout.fillHeight: true + Layout.fillWidth: true + } + + } + + } + + } + + } + + } + + footer: DialogButtonBox { + standardButtons: DialogButtonBox.Ok + onAccepted: readReceiptsRoot.close() + } + +} diff --git a/resources/qml/dialogs/RoomDirectory.qml b/resources/qml/dialogs/RoomDirectory.qml new file mode 100644 index 00000000..5c27fc26 --- /dev/null +++ b/resources/qml/dialogs/RoomDirectory.qml @@ -0,0 +1,216 @@ +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later + +import ".." +import "../ui" +import QtQuick 2.9 +import QtQuick.Controls 2.3 +import QtQuick.Layouts 1.3 +import im.nheko 1.0 + +ApplicationWindow { + id: roomDirectoryWindow + + property RoomDirectoryModel publicRooms + + visible: true + minimumWidth: 650 + minimumHeight: 420 + palette: Nheko.colors + color: Nheko.colors.window + modality: Qt.WindowModal + flags: Qt.Dialog | Qt.WindowCloseButtonHint | Qt.WindowTitleHint + Component.onCompleted: Nheko.reparent(roomDirectoryWindow) + title: qsTr("Explore Public Rooms") + + Shortcut { + sequence: StandardKey.Cancel + onActivated: roomDirectoryWindow.close() + } + + ListView { + id: roomDirView + + anchors.fill: parent + model: publicRooms + + ScrollHelper { + flickable: parent + anchors.fill: parent + enabled: !Settings.mobileMode + } + + delegate: Rectangle { + id: roomDirDelegate + + property color background: Nheko.colors.window + property color importantText: Nheko.colors.text + property color unimportantText: Nheko.colors.buttonText + property int avatarSize: fontMetrics.lineSpacing * 4 + + color: background + height: avatarSize + Nheko.paddingLarge + width: ListView.view.width + + RowLayout { + spacing: Nheko.paddingMedium + anchors.fill: parent + anchors.margins: Nheko.paddingLarge + implicitHeight: textContent.height + + Avatar { + id: roomAvatar + + Layout.alignment: Qt.AlignVCenter + width: avatarSize + height: avatarSize + url: model.avatarUrl.replace("mxc://", "image://MxcImage/") + roomid: model.roomid + displayName: model.name + } + + ColumnLayout { + id: textContent + + Layout.alignment: Qt.AlignLeft + width: parent.width - avatar.width + Layout.preferredWidth: parent.width - avatar.width + spacing: Nheko.paddingSmall + + ElidedLabel { + Layout.alignment: Qt.AlignBottom + color: roomDirDelegate.importantText + elideWidth: textContent.width - numMembersRectangle.width - buttonRectangle.width + font.pixelSize: fontMetrics.font.pixelSize * 1.1 + fullText: model.name + } + + RowLayout { + id: roomDescriptionRow + + Layout.preferredWidth: parent.width + spacing: Nheko.paddingSmall + Layout.alignment: Qt.AlignVCenter | Qt.AlignLeft + Layout.preferredHeight: fontMetrics.lineSpacing * 4 + + Label { + id: roomTopic + + color: roomDirDelegate.unimportantText + Layout.alignment: Qt.AlignVCenter | Qt.AlignLeft + font.pixelSize: fontMetrics.font.pixelSize + elide: Text.ElideRight + maximumLineCount: 2 + Layout.fillWidth: true + text: model.topic + verticalAlignment: Text.AlignVCenter + wrapMode: Text.WordWrap + } + + Item { + id: numMembersRectangle + + Layout.margins: Nheko.paddingSmall + width: roomCount.width + + Label { + id: roomCount + + color: roomDirDelegate.unimportantText + anchors.centerIn: parent + font.pixelSize: fontMetrics.font.pixelSize + text: model.numMembers.toString() + } + + } + + Item { + id: buttonRectangle + + Layout.margins: Nheko.paddingSmall + width: joinRoomButton.width + + Button { + id: joinRoomButton + + visible: model.canJoin + anchors.centerIn: parent + text: "Join" + onClicked: publicRooms.joinRoom(model.index) + } + + } + + } + + } + + } + + } + + footer: Item { + anchors.horizontalCenter: parent.horizontalCenter + width: parent.width + visible: !publicRooms.reachedEndOfPagination && publicRooms.loadingMoreRooms + // hacky but works + height: loadingSpinner.height + 2 * Nheko.paddingLarge + anchors.margins: Nheko.paddingLarge + + Spinner { + id: loadingSpinner + + anchors.centerIn: parent + anchors.margins: Nheko.paddingLarge + running: visible + foreground: Nheko.colors.mid + } + + } + + } + + publicRooms: RoomDirectoryModel { + } + + header: RowLayout { + id: searchBarLayout + + spacing: Nheko.paddingMedium + width: parent.width + implicitHeight: roomSearch.height + + MatrixTextField { + id: roomSearch + + Layout.fillWidth: true + selectByMouse: true + font.pixelSize: fontMetrics.font.pixelSize + padding: Nheko.paddingMedium + color: Nheko.colors.text + placeholderText: qsTr("Search for public rooms") + onTextChanged: searchTimer.restart() + } + + MatrixTextField { + id: chooseServer + Layout.minimumWidth: 0.3 * header.width + Layout.maximumWidth: 0.3 * header.width + + padding: Nheko.paddingMedium + color: Nheko.colors.text + placeholderText: qsTr("Choose custom homeserver") + onTextChanged: publicRooms.setMatrixServer(text) + } + + Timer { + id: searchTimer + + interval: 350 + onTriggered: roomDirView.model.setSearchTerm(roomSearch.text) + } + + } + +} diff --git a/resources/qml/dialogs/RoomMembers.qml b/resources/qml/dialogs/RoomMembers.qml new file mode 100644 index 00000000..b2806292 --- /dev/null +++ b/resources/qml/dialogs/RoomMembers.qml @@ -0,0 +1,180 @@ +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later + +import ".." +import "../ui" +import QtQuick 2.12 +import QtQuick.Controls 2.12 +import QtQuick.Layouts 1.12 +import QtQuick.Window 2.13 +import im.nheko 1.0 + +ApplicationWindow { + id: roomMembersRoot + + property MemberList members + property Room room + + title: qsTr("Members of %1").arg(members.roomName) + height: 650 + width: 420 + minimumHeight: 420 + palette: Nheko.colors + color: Nheko.colors.window + flags: Qt.Dialog | Qt.WindowCloseButtonHint | Qt.WindowTitleHint + Component.onCompleted: Nheko.reparent(roomMembersRoot) + + Shortcut { + sequence: StandardKey.Cancel + onActivated: roomMembersRoot.close() + } + + ColumnLayout { + anchors.fill: parent + anchors.margins: Nheko.paddingMedium + spacing: Nheko.paddingMedium + + Avatar { + id: roomAvatar + + width: 130 + height: width + roomid: members.roomId + displayName: members.roomName + Layout.alignment: Qt.AlignHCenter + url: members.avatarUrl.replace("mxc://", "image://MxcImage/") + onClicked: TimelineManager.openRoomSettings(members.roomId) + } + + ElidedLabel { + font.pixelSize: fontMetrics.font.pixelSize * 2 + fullText: qsTr("%n people in %1", "Summary above list of members", members.memberCount).arg(members.roomName) + Layout.alignment: Qt.AlignHCenter + elideWidth: parent.width - Nheko.paddingMedium + } + + ImageButton { + Layout.alignment: Qt.AlignHCenter + image: ":/icons/icons/ui/add-square-button.png" + hoverEnabled: true + ToolTip.visible: hovered + ToolTip.text: qsTr("Invite more people") + onClicked: TimelineManager.openInviteUsers(members.roomId) + } + + ScrollView { + palette: Nheko.colors + padding: Nheko.paddingMedium + ScrollBar.horizontal.visible: false + Layout.fillHeight: true + Layout.minimumHeight: 200 + Layout.fillWidth: true + + ListView { + id: memberList + + clip: true + spacing: Nheko.paddingMedium + boundsBehavior: Flickable.StopAtBounds + model: members + + ScrollHelper { + flickable: parent + anchors.fill: parent + enabled: !Settings.mobileMode + } + + delegate: RowLayout { + id: del + + width: ListView.view.width + spacing: Nheko.paddingMedium + + Avatar { + id: avatar + + width: Nheko.avatarSize + height: Nheko.avatarSize + userid: model.mxid + url: model.avatarUrl.replace("mxc://", "image://MxcImage/") + displayName: model.displayName + onClicked: Rooms.currentRoom.openUserProfile(model.mxid) + } + + ColumnLayout { + spacing: Nheko.paddingSmall + + ElidedLabel { + fullText: model.displayName + color: TimelineManager.userColor(model ? model.mxid : "", Nheko.colors.window) + font.pixelSize: fontMetrics.font.pixelSize + elideWidth: del.width - Nheko.paddingMedium * 2 - avatar.width - encryptInd.width + } + + ElidedLabel { + fullText: model.mxid + color: Nheko.colors.buttonText + font.pixelSize: Math.ceil(fontMetrics.font.pixelSize * 0.9) + elideWidth: del.width - Nheko.paddingMedium * 2 - avatar.width - encryptInd.width + } + + Item { + Layout.fillHeight: true + Layout.fillWidth: true + } + + } + + EncryptionIndicator { + id: encryptInd + + Layout.alignment: Qt.AlignRight + visible: room.isEncrypted + encrypted: room.isEncrypted + trust: encrypted ? model.trustlevel : Crypto.Unverified + ToolTip.text: { + if (!encrypted) + return qsTr("This room is not encrypted!"); + + switch (trust) { + case Crypto.Verified: + return qsTr("This user is verified."); + case Crypto.TOFU: + return qsTr("This user isn't verified, but is still using the same master key from the first time you met."); + default: + return qsTr("This user has unverified devices!"); + } + } + } + + } + + footer: Item { + width: parent.width + visible: (members.numUsersLoaded < members.memberCount) && members.loadingMoreMembers + // use the default height if it's visible, otherwise no height at all + height: membersLoadingSpinner.height + anchors.margins: Nheko.paddingMedium + + Spinner { + id: membersLoadingSpinner + + anchors.centerIn: parent + height: visible ? 35 : 0 + } + + } + + } + + } + + } + + footer: DialogButtonBox { + standardButtons: DialogButtonBox.Ok + onAccepted: roomMembersRoot.close() + } + +} diff --git a/resources/qml/dialogs/RoomSettings.qml b/resources/qml/dialogs/RoomSettings.qml new file mode 100644 index 00000000..0e7749ce --- /dev/null +++ b/resources/qml/dialogs/RoomSettings.qml @@ -0,0 +1,305 @@ +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later + +import ".." +import "../ui" +import Qt.labs.platform 1.1 as Platform +import QtQuick 2.15 +import QtQuick.Controls 2.3 +import QtQuick.Layouts 1.2 +import QtQuick.Window 2.13 +import im.nheko 1.0 + +ApplicationWindow { + id: roomSettingsDialog + + property var roomSettings + + minimumWidth: 450 + minimumHeight: 680 + palette: Nheko.colors + color: Nheko.colors.window + modality: Qt.NonModal + flags: Qt.Dialog | Qt.WindowCloseButtonHint | Qt.WindowTitleHint + Component.onCompleted: Nheko.reparent(roomSettingsDialog) + title: qsTr("Room Settings") + + Shortcut { + sequence: StandardKey.Cancel + onActivated: roomSettingsDialog.close() + } + + ColumnLayout { + id: contentLayout1 + + anchors.fill: parent + anchors.margins: 10 + spacing: 10 + + Avatar { + url: roomSettings.roomAvatarUrl.replace("mxc://", "image://MxcImage/") + roomid: roomSettings.roomId + displayName: roomSettings.roomName + height: 130 + width: 130 + Layout.alignment: Qt.AlignHCenter + onClicked: { + if (roomSettings.canChangeAvatar) + roomSettings.updateAvatar(); + + } + } + + Spinner { + Layout.alignment: Qt.AlignHCenter + visible: roomSettings.isLoading + foreground: Nheko.colors.mid + running: roomSettings.isLoading + } + + Text { + id: errorText + + color: "red" + visible: opacity > 0 + opacity: 0 + Layout.alignment: Qt.AlignHCenter + } + + SequentialAnimation { + id: hideErrorAnimation + + running: false + + PauseAnimation { + duration: 4000 + } + + NumberAnimation { + target: errorText + property: 'opacity' + to: 0 + duration: 1000 + } + + } + + Connections { + target: roomSettings + onDisplayError: { + errorText.text = errorMessage; + errorText.opacity = 1; + hideErrorAnimation.restart(); + } + } + + ColumnLayout { + Layout.alignment: Qt.AlignHCenter + + MatrixText { + text: roomSettings.roomName + font.pixelSize: fontMetrics.font.pixelSize * 2 + Layout.alignment: Qt.AlignHCenter + } + + MatrixText { + text: qsTr("%1 member(s)").arg(roomSettings.memberCount) + Layout.alignment: Qt.AlignHCenter + + TapHandler { + onTapped: TimelineManager.openRoomMembers(roomSettings.roomId) + } + + CursorShape { + cursorShape: Qt.PointingHandCursor + anchors.fill: parent + } + + } + + } + + ImageButton { + Layout.alignment: Qt.AlignHCenter + image: ":/icons/icons/ui/edit.png" + visible: roomSettings.canChangeNameAndTopic + onClicked: roomSettings.openEditModal() + } + + ScrollView { + Layout.fillHeight: true + Layout.alignment: Qt.AlignHCenter + Layout.fillWidth: true + Layout.leftMargin: Nheko.paddingLarge + Layout.rightMargin: Nheko.paddingLarge + + TextArea { + text: TimelineManager.escapeEmoji(roomSettings.roomTopic) + wrapMode: TextEdit.WordWrap + textFormat: TextEdit.RichText + readOnly: true + background: null + selectByMouse: true + color: Nheko.colors.text + horizontalAlignment: TextEdit.AlignHCenter + onLinkActivated: Nheko.openLink(link) + + CursorShape { + anchors.fill: parent + cursorShape: parent.hoveredLink ? Qt.PointingHandCursor : Qt.ArrowCursor + } + + } + + } + + GridLayout { + columns: 2 + rowSpacing: Nheko.paddingLarge + + MatrixText { + text: qsTr("SETTINGS") + font.bold: true + } + + Item { + Layout.fillWidth: true + } + + MatrixText { + text: qsTr("Notifications") + Layout.fillWidth: true + } + + ComboBox { + model: [qsTr("Muted"), qsTr("Mentions only"), qsTr("All messages")] + currentIndex: roomSettings.notifications + onActivated: { + roomSettings.changeNotifications(index); + } + Layout.fillWidth: true + } + + MatrixText { + text: qsTr("Room access") + Layout.fillWidth: true + } + + ComboBox { + enabled: roomSettings.canChangeJoinRules + model: { + let opts = [qsTr("Anyone and guests"), qsTr("Anyone"), qsTr("Invited users")]; + if (roomSettings.supportsKnocking) + opts.push(qsTr("By knocking")); + + if (roomSettings.supportsRestricted) + opts.push(qsTr("Restricted by membership in other rooms")); + + return opts; + } + currentIndex: roomSettings.accessJoinRules + onActivated: { + roomSettings.changeAccessRules(index); + } + Layout.fillWidth: true + } + + MatrixText { + text: qsTr("Encryption") + } + + ToggleButton { + id: encryptionToggle + + checked: roomSettings.isEncryptionEnabled + onClicked: { + if (roomSettings.isEncryptionEnabled) { + checked = true; + return ; + } + confirmEncryptionDialog.open(); + } + Layout.alignment: Qt.AlignRight + } + + Platform.MessageDialog { + id: confirmEncryptionDialog + + title: qsTr("End-to-End Encryption") + text: qsTr("Encryption is currently experimental and things might break unexpectedly.
+ Please take note that it can't be disabled afterwards.") + modality: Qt.Modal + onAccepted: { + if (roomSettings.isEncryptionEnabled) + return ; + + roomSettings.enableEncryption(); + } + onRejected: { + encryptionToggle.checked = false; + } + buttons: Dialog.Ok | Dialog.Cancel + } + + MatrixText { + text: qsTr("Sticker & Emote Settings") + } + + Button { + text: qsTr("Change") + ToolTip.text: qsTr("Change what packs are enabled, remove packs or create new ones") + onClicked: TimelineManager.openImagePackSettings(roomSettings.roomId) + Layout.alignment: Qt.AlignRight + } + + Item { + // for adding extra space between sections + Layout.fillWidth: true + } + + Item { + // for adding extra space between sections + Layout.fillWidth: true + } + + MatrixText { + text: qsTr("INFO") + font.bold: true + } + + Item { + Layout.fillWidth: true + } + + MatrixText { + text: qsTr("Internal ID") + } + + MatrixText { + text: roomSettings.roomId + font.pixelSize: fontMetrics.font.pixelSize * 1.2 + Layout.alignment: Qt.AlignRight + } + + MatrixText { + text: qsTr("Room Version") + } + + MatrixText { + text: roomSettings.roomVersion + font.pixelSize: fontMetrics.font.pixelSize * 1.2 + Layout.alignment: Qt.AlignRight + } + + } + + DialogButtonBox { + Layout.fillWidth: true + standardButtons: DialogButtonBox.Ok + onAccepted: close() + } + + } + +} diff --git a/resources/qml/dialogs/UserProfile.qml b/resources/qml/dialogs/UserProfile.qml new file mode 100644 index 00000000..9bf548e3 --- /dev/null +++ b/resources/qml/dialogs/UserProfile.qml @@ -0,0 +1,327 @@ +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later + +import ".." +import "../device-verification" +import "../ui" +import QtQuick 2.15 +import QtQuick.Controls 2.15 +import QtQuick.Layouts 1.2 +import QtQuick.Window 2.13 +import im.nheko 1.0 + +ApplicationWindow { + // this does not work in ApplicationWindow, just in Window + //transientParent: Nheko.mainwindow() + + id: userProfileDialog + + property var profile + + height: 650 + width: 420 + minimumWidth: 150 + minimumHeight: 150 + palette: Nheko.colors + color: Nheko.colors.window + title: profile.isGlobalUserProfile ? qsTr("Global User Profile") : qsTr("Room User Profile") + modality: Qt.NonModal + flags: Qt.Dialog | Qt.WindowCloseButtonHint | Qt.WindowTitleHint + Component.onCompleted: Nheko.reparent(userProfileDialog) + + Shortcut { + sequence: StandardKey.Cancel + onActivated: userProfileDialog.close() + } + + + ListView { + + ScrollHelper { + flickable: parent + anchors.fill: parent + enabled: !Settings.mobileMode + } + + header: ColumnLayout { + id: contentL + width: devicelist.width + + spacing: 10 + + Avatar { + url: profile.avatarUrl.replace("mxc://", "image://MxcImage/") + height: 130 + width: 130 + displayName: profile.displayName + id: displayAvatar + userid: profile.userid + Layout.alignment: Qt.AlignHCenter + onClicked: TimelineManager.openImageOverlay(profile.avatarUrl, "") + + ImageButton { + hoverEnabled: true + ToolTip.visible: hovered + ToolTip.text: profile.isGlobalUserProfile ? qsTr("Change avatar globally.") : qsTr("Change avatar. Will only apply to this room.") + anchors.left: displayAvatar.left + anchors.top: displayAvatar.top + anchors.leftMargin: Nheko.paddingMedium + anchors.topMargin: Nheko.paddingMedium + visible: profile.isSelf + image: ":/icons/icons/ui/edit.png" + onClicked: profile.changeAvatar() + } + } + + Spinner { + Layout.alignment: Qt.AlignHCenter + running: profile.isLoading + visible: profile.isLoading + foreground: Nheko.colors.mid + } + + Text { + id: errorText + + color: "red" + visible: opacity > 0 + opacity: 0 + Layout.alignment: Qt.AlignHCenter + } + + SequentialAnimation { + id: hideErrorAnimation + + running: false + + PauseAnimation { + duration: 4000 + } + + NumberAnimation { + target: errorText + property: 'opacity' + to: 0 + duration: 1000 + } + + } + + Connections { + function onDisplayError(errorMessage) { + errorText.text = errorMessage; + errorText.opacity = 1; + hideErrorAnimation.restart(); + } + + target: profile + } + + TextInput { + id: displayUsername + + property bool isUsernameEditingAllowed + + readOnly: !isUsernameEditingAllowed + text: profile.displayName + font.pixelSize: 20 + color: TimelineManager.userColor(profile.userid, Nheko.colors.window) + font.bold: true + Layout.alignment: Qt.AlignHCenter + selectByMouse: true + onAccepted: { + profile.changeUsername(displayUsername.text); + displayUsername.isUsernameEditingAllowed = false; + } + + ImageButton { + visible: profile.isSelf + anchors.leftMargin: Nheko.paddingSmall + anchors.left: displayUsername.right + anchors.verticalCenter: displayUsername.verticalCenter + hoverEnabled: true + ToolTip.visible: hovered + ToolTip.text: profile.isGlobalUserProfile ? qsTr("Change display name globally.") : qsTr("Change display name. Will only apply to this room.") + image: displayUsername.isUsernameEditingAllowed ? ":/icons/icons/ui/checkmark.png" : ":/icons/icons/ui/edit.png" + onClicked: { + if (displayUsername.isUsernameEditingAllowed) { + profile.changeUsername(displayUsername.text); + displayUsername.isUsernameEditingAllowed = false; + } else { + displayUsername.isUsernameEditingAllowed = true; + displayUsername.focus = true; + displayUsername.selectAll(); + } + } + } + + } + + MatrixText { + text: profile.userid + Layout.alignment: Qt.AlignHCenter + } + + + RowLayout { + visible: !profile.isGlobalUserProfile + Layout.alignment: Qt.AlignHCenter + spacing: Nheko.paddingSmall + MatrixText { + id: displayRoomname + text: qsTr("Room: %1").arg(profile.room?profile.room.roomName:"") + ToolTip.text: qsTr("This is a room-specific profile. The user's name and avatar may be different from their global versions.") + ToolTip.visible: ma.hovered + HoverHandler { + id: ma + } + } + + ImageButton { + image: ":/icons/icons/ui/world.png" + hoverEnabled: true + ToolTip.visible: hovered + ToolTip.text: qsTr("Open the global profile for this user.") + onClicked: profile.openGlobalProfile() + } + } + + Button { + id: verifyUserButton + + text: qsTr("Verify") + Layout.alignment: Qt.AlignHCenter + enabled: profile.userVerified != Crypto.Verified + visible: profile.userVerified != Crypto.Verified && !profile.isSelf && profile.userVerificationEnabled + onClicked: profile.verify() + } + + Image { + Layout.preferredHeight: 16 + Layout.preferredWidth: 16 + source: "image://colorimage/:/icons/icons/ui/lock.png?" + ((profile.userVerified == Crypto.Verified) ? "green" : Nheko.colors.buttonText) + visible: profile.userVerified != Crypto.Unverified + Layout.alignment: Qt.AlignHCenter + } + + RowLayout { + // ImageButton{ + // image:":/icons/icons/ui/volume-off-indicator.png" + // Layout.margins: { + // left: 5 + // right: 5 + // } + // ToolTip.visible: hovered + // ToolTip.text: qsTr("Ignore messages from this user.") + // onClicked : { + // profile.ignoreUser() + // } + // } + + Layout.alignment: Qt.AlignHCenter + Layout.bottomMargin: 10 + spacing: Nheko.paddingSmall + + ImageButton { + image: ":/icons/icons/ui/black-bubble-speech.png" + hoverEnabled: true + ToolTip.visible: hovered + ToolTip.text: qsTr("Start a private chat.") + onClicked: profile.startChat() + } + + ImageButton { + image: ":/icons/icons/ui/round-remove-button.png" + hoverEnabled: true + ToolTip.visible: hovered + ToolTip.text: qsTr("Kick the user.") + onClicked: profile.kickUser() + visible: !profile.isGlobalUserProfile && profile.room.permissions.canKick() + } + + ImageButton { + image: ":/icons/icons/ui/do-not-disturb-rounded-sign.png" + hoverEnabled: true + ToolTip.visible: hovered + ToolTip.text: qsTr("Ban the user.") + onClicked: profile.banUser() + visible: !profile.isGlobalUserProfile && profile.room.permissions.canBan() + } + + } + } + + id: devicelist + Layout.fillHeight: true + Layout.fillWidth: true + clip: true + spacing: 8 + boundsBehavior: Flickable.StopAtBounds + model: profile.deviceList + anchors.fill: parent + anchors.margins: 10 + + + delegate: RowLayout { + width: devicelist.width + spacing: 4 + + ColumnLayout { + spacing: 0 + + Text { + Layout.fillWidth: true + Layout.alignment: Qt.AlignLeft + elide: Text.ElideRight + font.bold: true + color: Nheko.colors.text + text: model.deviceId + } + + Text { + Layout.fillWidth: true + Layout.alignment: Qt.AlignRight + elide: Text.ElideRight + color: Nheko.colors.text + text: model.deviceName + } + + } + + Image { + Layout.preferredHeight: 16 + Layout.preferredWidth: 16 + source: ((model.verificationStatus == VerificationStatus.VERIFIED) ? "image://colorimage/:/icons/icons/ui/lock.png?green" : ((model.verificationStatus == VerificationStatus.UNVERIFIED) ? "image://colorimage/:/icons/icons/ui/unlock.png?yellow" : "image://colorimage/:/icons/icons/ui/unlock.png?red")) + } + + Button { + id: verifyButton + + visible: (!profile.userVerificationEnabled && !profile.isSelf) || (profile.isSelf && (model.verificationStatus != VerificationStatus.VERIFIED || !profile.userVerificationEnabled)) + text: (model.verificationStatus != VerificationStatus.VERIFIED) ? qsTr("Verify") : qsTr("Unverify") + onClicked: { + if (model.verificationStatus == VerificationStatus.VERIFIED) + profile.unverify(model.deviceId); + else + profile.verify(model.deviceId); + } + } + + } + footerPositioning: ListView.OverlayFooter + footer: DialogButtonBox { + z: 2 + width: devicelist.width + alignment: Qt.AlignRight + standardButtons: DialogButtonBox.Ok + onAccepted: userProfileDialog.close() + background: Rectangle { + anchors.fill: parent + color: Nheko.colors.window + } + } + + } + +} diff --git a/resources/res.qrc b/resources/res.qrc index 3514ebca..a001a929 100644 --- a/resources/res.qrc +++ b/resources/res.qrc @@ -139,11 +139,8 @@ qml/ForwardCompleter.qml qml/TypingIndicator.qml qml/NotificationWarning.qml - qml/RoomSettings.qml qml/emoji/EmojiPicker.qml qml/emoji/StickerPicker.qml - qml/UserProfile.qml - qml/RoomDirectory.qml qml/delegates/MessageDelegate.qml qml/delegates/Encrypted.qml qml/delegates/FileMessage.qml @@ -179,10 +176,13 @@ qml/components/AdaptiveLayoutElement.qml qml/components/AvatarListTile.qml qml/components/FlatButton.qml - qml/RoomMembers.qml - qml/InviteDialog.qml - qml/ReadReceipts.qml - qml/RawMessageDialog.qml + qml/dialogs/InviteDialog.qml + qml/dialogs/RawMessageDialog.qml + qml/dialogs/ReadReceipts.qml + qml/dialogs/RoomDirectory.qml + qml/dialogs/RoomMembers.qml + qml/dialogs/RoomSettings.qml + qml/dialogs/UserProfile.qml media/ring.ogg -- cgit 1.5.1