// SPDX-FileCopyrightText: 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: 340 minimumHeight: 450 width: 450 height: 680 color: palette.window modality: Qt.NonModal flags: Qt.Dialog | Qt.WindowCloseButtonHint | Qt.WindowTitleHint title: qsTr("Room Settings") Shortcut { sequence: StandardKey.Cancel onActivated: roomSettingsDialog.close() } Flickable { id: flickable boundsBehavior: Flickable.StopAtBounds anchors.fill: parent clip: true flickableDirection: Flickable.VerticalFlick contentWidth: roomSettingsDialog.width contentHeight: contentLayout1.height ColumnLayout { id: contentLayout1 width: parent.width spacing: Nheko.paddingMedium Avatar { id: displayAvatar Layout.topMargin: Nheko.paddingMedium url: roomSettings.roomAvatarUrl.replace("mxc://", "image://MxcImage/") roomid: roomSettings.roomId displayName: roomSettings.roomName height: 130 width: 130 Layout.alignment: Qt.AlignHCenter onClicked: TimelineManager.openImageOverlay(null, roomSettings.roomAvatarUrl, "", 0, 0) ImageButton { hoverEnabled: true ToolTip.visible: hovered ToolTip.text: qsTr("Change room avatar.") anchors.left: displayAvatar.left anchors.top: displayAvatar.top anchors.leftMargin: Nheko.paddingMedium anchors.topMargin: Nheko.paddingMedium visible: roomSettings.canChangeAvatar image: ":/icons/icons/ui/edit.svg" onClicked: { roomSettings.updateAvatar(); } } } Spinner { Layout.alignment: Qt.AlignHCenter visible: roomSettings.isLoading foreground: palette.mid running: roomSettings.isLoading } Text { id: errorText color: "red" visible: opacity > 0 opacity: 0 Layout.alignment: Qt.AlignHCenter wrapMode: Text.Wrap // somehow still doesn't wrap Layout.fillWidth: true } SequentialAnimation { id: hideErrorAnimation running: false PauseAnimation { duration: 4000 } NumberAnimation { target: errorText property: 'opacity' to: 0 duration: 1000 } } Connections { target: roomSettings function onDisplayError(errorMessage) { errorText.text = errorMessage; errorText.opacity = 1; hideErrorAnimation.restart(); } } TextEdit { id: roomName property bool isNameEditingAllowed: false readOnly: !isNameEditingAllowed textFormat: isNameEditingAllowed ? TextEdit.PlainText : TextEdit.RichText text: isNameEditingAllowed ? roomSettings.plainRoomName : roomSettings.roomName font.pixelSize: fontMetrics.font.pixelSize * 2 color: palette.text Layout.alignment: Qt.AlignHCenter Layout.maximumWidth: parent.width - (Nheko.paddingSmall * 2) - nameChangeButton.anchors.leftMargin - (nameChangeButton.width * 2) horizontalAlignment: TextEdit.AlignHCenter wrapMode: TextEdit.Wrap selectByMouse: true Keys.onShortcutOverride: event.key === Qt.Key_Enter Keys.onPressed: { if (event.matches(StandardKey.InsertLineSeparator) || event.matches(StandardKey.InsertParagraphSeparator)) { roomSettings.changeName(roomName.text); roomName.isNameEditingAllowed = false; event.accepted = true; } } ImageButton { id: nameChangeButton visible: roomSettings.canChangeName anchors.leftMargin: Nheko.paddingSmall anchors.left: roomName.right anchors.verticalCenter: roomName.verticalCenter hoverEnabled: true ToolTip.visible: hovered ToolTip.text: qsTr("Change name of this room") ToolTip.delay: Nheko.tooltipDelay image: roomName.isNameEditingAllowed ? ":/icons/icons/ui/checkmark.svg" : ":/icons/icons/ui/edit.svg" onClicked: { if (roomName.isNameEditingAllowed) { roomSettings.changeName(roomName.text); roomName.isNameEditingAllowed = false; } else { roomName.isNameEditingAllowed = true; roomName.focus = true; roomName.selectAll(); } } } } RowLayout { spacing: Nheko.paddingMedium Layout.alignment: Qt.AlignHCenter Label { text: qsTr("%n member(s)", "", roomSettings.memberCount) color: palette.text } ImageButton { image: ":/icons/icons/ui/people.svg" hoverEnabled: true ToolTip.visible: hovered ToolTip.text: qsTr("View members of %1").arg(roomSettings.roomName) onClicked: TimelineManager.openRoomMembers(Rooms.getRoomById(roomSettings.roomId)) } } TextArea { id: roomTopic property bool cut: implicitHeight > 100 property bool showMore: false clip: true Layout.maximumHeight: showMore? Number.POSITIVE_INFINITY : 100 Layout.preferredHeight: implicitHeight Layout.alignment: Qt.AlignHCenter Layout.fillWidth: true Layout.leftMargin: Nheko.paddingLarge Layout.rightMargin: Nheko.paddingLarge property bool isTopicEditingAllowed: false readOnly: !isTopicEditingAllowed textFormat: isTopicEditingAllowed ? TextEdit.PlainText : TextEdit.RichText text: isTopicEditingAllowed ? roomSettings.plainRoomTopic : (roomSettings.plainRoomTopic === "" ? ("" + qsTr("No topic set") + "") : roomSettings.roomTopic) wrapMode: TextEdit.WordWrap background: null selectByMouse: !Settings.mobileMode color: palette.text horizontalAlignment: TextEdit.AlignHCenter onLinkActivated: Nheko.openLink(link) NhekoCursorShape { anchors.fill: parent cursorShape: parent.hoveredLink ? Qt.PointingHandCursor : Qt.ArrowCursor } } ImageButton { id: topicChangeButton Layout.alignment: Qt.AlignHCenter visible: roomSettings.canChangeTopic hoverEnabled: true ToolTip.visible: hovered ToolTip.text: qsTr("Change topic of this room") ToolTip.delay: Nheko.tooltipDelay image: roomTopic.isTopicEditingAllowed ? ":/icons/icons/ui/checkmark.svg" : ":/icons/icons/ui/edit.svg" onClicked: { if (roomTopic.isTopicEditingAllowed) { roomSettings.changeTopic(roomTopic.text); roomTopic.isTopicEditingAllowed = false; } else { roomTopic.isTopicEditingAllowed = true; roomTopic.showMore = true; roomTopic.focus = true; //roomTopic.selectAll(); } } } Item { Layout.alignment: Qt.AlignHCenter id: showMorePlaceholder Layout.preferredHeight: showMoreButton.height Layout.preferredWidth: showMoreButton.width visible: roomTopic.cut } GridLayout { columns: 2 rowSpacing: Nheko.paddingMedium Layout.margins: Nheko.paddingMedium Layout.fillWidth: true Label { text: qsTr("SETTINGS") font.bold: true color: palette.text } Item { Layout.fillWidth: true } Label { text: qsTr("Notifications") Layout.fillWidth: true color: palette.text } ComboBox { model: [qsTr("Muted"), qsTr("Mentions only"), qsTr("All messages")] currentIndex: roomSettings.notifications onActivated: { roomSettings.changeNotifications(index); } Layout.fillWidth: true WheelHandler{} // suppress scrolling changing values } Label { text: qsTr("Anyone can join") Layout.fillWidth: true color: palette.text } ToggleButton { id: publicRoomButton enabled: roomSettings.canChangeJoinRules checked: !roomSettings.privateAccess Layout.alignment: Qt.AlignRight } Label { text: qsTr("Allow knocking") Layout.fillWidth: true color: palette.text visible: knockingButton.visible } ToggleButton { id: knockingButton visible: !publicRoomButton.checked enabled: roomSettings.canChangeJoinRules && roomSettings.supportsKnocking checked: roomSettings.knockingEnabled onCheckedChanged: { if (checked && !roomSettings.supportsKnockRestricted) restrictedButton.checked = false; } Layout.alignment: Qt.AlignRight } Label { text: qsTr("Allow joining via other rooms") Layout.fillWidth: true color: palette.text visible: restrictedButton.visible } ToggleButton { id: restrictedButton visible: !publicRoomButton.checked enabled: roomSettings.canChangeJoinRules && roomSettings.supportsRestricted checked: roomSettings.restrictedEnabled onCheckedChanged: { if (checked && !roomSettings.supportsKnockRestricted) knockingButton.checked = false; } Layout.alignment: Qt.AlignRight } Label { text: qsTr("Rooms to join via") Layout.fillWidth: true color: palette.text visible: allowedRoomsButton.visible } Button { id: allowedRoomsButton visible: restrictedButton.checked && restrictedButton.visible enabled: roomSettings.canChangeJoinRules && roomSettings.supportsRestricted text: qsTr("Change") ToolTip.text: qsTr("Change the list of rooms users can join this room via. Usually this is the official community of this room.") onClicked: timelineRoot.showAllowedRoomsEditor(roomSettings) Layout.alignment: Qt.AlignRight } Label { text: qsTr("Allow guests to join") Layout.fillWidth: true color: palette.text } ToggleButton { id: guestAccessButton enabled: roomSettings.canChangeJoinRules checked: roomSettings.guestAccess Layout.alignment: Qt.AlignRight } Button { visible: publicRoomButton.checked == roomSettings.privateAccess || knockingButton.checked != roomSettings.knockingEnabled || restrictedButton.checked != roomSettings.restrictedEnabled || guestAccessButton.checked != roomSettings.guestAccess || roomSettings.allowedRoomsModified enabled: roomSettings.canChangeJoinRules text: qsTr("Apply access rules") onClicked: roomSettings.changeAccessRules(!publicRoomButton.checked, guestAccessButton.checked, knockingButton.checked, restrictedButton.checked) Layout.columnSpan: 2 Layout.fillWidth: true } Label { text: qsTr("History visibility") Layout.fillWidth: true color: palette.text } ComboBox { id: visComboBox model: [qsTr("Readable to anyone without joining the room"), qsTr("Past messages visible to all current members"), qsTr("Only visible to members who were invited or joined when the message was sent"), qsTr("Only visible to members who were joined when the message was sent")] currentIndex: roomSettings.historyVisibility onActivated: { roomSettings.changeHistoryVisibility(index); } Layout.fillWidth: true WheelHandler{} // suppress scrolling changing values enabled: roomSettings.canChangeHistoryVisibility delegate: ItemDelegate { text: modelData width: implicitWidth highlighted: visComboBox.highlightedIndex === index ToolTip.text: modelData ToolTip.visible: hovered ToolTip.delay: Nheko.tooltipDelay } ToolTip.text: displayText ToolTip.visible: hovered ToolTip.delay: Nheko.tooltipDelay } Label { text: qsTr("Encryption") color: palette.text } ToggleButton { id: encryptionToggle checked: roomSettings.isEncryptionEnabled onCheckedChanged: { if (roomSettings.isEncryptionEnabled) { checked = true; return ; } if (checked === true) 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.NonModal onAccepted: { if (roomSettings.isEncryptionEnabled) return ; roomSettings.enableEncryption(); } onRejected: { encryptionToggle.checked = false; } buttons: Platform.MessageDialog.Ok | Platform.MessageDialog.Cancel } Label { text: qsTr("Permission") color: palette.text } Button { text: qsTr("Configure") ToolTip.text: qsTr("View and change the permissions in this room") onClicked: timelineRoot.showPLEditor(roomSettings) Layout.alignment: Qt.AlignRight } Label { text: qsTr("Aliases") color: palette.text } Button { text: qsTr("Configure") ToolTip.text: qsTr("View and change the addresses/aliases of this room") onClicked: timelineRoot.showAliasEditor(roomSettings) Layout.alignment: Qt.AlignRight } Label { text: qsTr("Sticker & Emote Settings") color: palette.text } 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 } Label { text: qsTr("Hidden events") color: palette.text } HiddenEventsDialog { id: hiddenEventsDialog roomid: roomSettings.roomId roomName: roomSettings.roomName } Button { text: qsTr("Configure") ToolTip.text: qsTr("Select events to hide in this room") onClicked: hiddenEventsDialog.show() Layout.alignment: Qt.AlignRight } Item { // for adding extra space between sections Layout.fillWidth: true } Item { // for adding extra space between sections Layout.fillWidth: true } Label { text: qsTr("INFO") font.bold: true color: palette.text } Item { Layout.fillWidth: true } Label { text: qsTr("Internal ID") color: palette.text } AbstractButton { // AbstractButton does not allow setting text color Layout.alignment: Qt.AlignRight Layout.fillWidth: true Layout.preferredHeight: idLabel.height Label { // TextEdit does not trigger onClicked id: idLabel text: roomSettings.roomId font.pixelSize: Math.floor(fontMetrics.font.pixelSize * 0.8) color: palette.text width: parent.width horizontalAlignment: Text.AlignRight wrapMode: Text.WrapAnywhere ToolTip.text: qsTr("Copied to clipboard") ToolTip.visible: toolTipTimer.running } TextEdit{ // label does not allow selection id: textEdit visible: false text: roomSettings.roomId } onClicked: { textEdit.selectAll() textEdit.copy() toolTipTimer.start() } Timer { id: toolTipTimer } } Label { text: qsTr("Room Version") color: palette.text } Label { text: roomSettings.roomVersion font.pixelSize: fontMetrics.font.pixelSize Layout.alignment: Qt.AlignRight color: palette.text } } } } Button { id: showMoreButton anchors.horizontalCenter: flickable.horizontalCenter y: Math.min(showMorePlaceholder.y+contentLayout1.y-flickable.contentY,flickable.height-height) visible: roomTopic.cut text: roomTopic.showMore? qsTr("show less") : qsTr("show more") onClicked: {roomTopic.showMore = !roomTopic.showMore console.log(flickable.visibleArea) } } footer: DialogButtonBox { standardButtons: DialogButtonBox.Ok onAccepted: close() } }