import QtQuick import QtQuick.Controls import QtQuick.Layouts import PageEnum 1.0 import ProtocolEnum 1.0 import Style 1.0 import "./" import "../Controls2" import "../Controls2/TextTypes" import "../Config" import "../Components" PageType { id: root BackButtonType { id: backButton anchors.top: parent.top anchors.left: parent.left anchors.right: parent.right anchors.topMargin: 20 + PageController.safeAreaTopMargin } ListViewType { id: listView anchors.top: backButton.bottom anchors.bottom: saveButton.visible ? saveButton.top : parent.bottom anchors.left: parent.left anchors.right: parent.right enabled: ServersUiController.isProcessedServerHasWriteAccess() model: XrayConfigModel delegate: ColumnLayout { width: listView.width spacing: 0 BaseHeaderType { Layout.fillWidth: true Layout.leftMargin: 16 Layout.rightMargin: 16 Layout.topMargin: 0 Layout.bottomMargin: 24 headerText: qsTr("Security") } VerticalRadioButton { Layout.fillWidth: true Layout.leftMargin: 16 Layout.rightMargin: 16 text: qsTr("None") checked: security === "none" onClicked: security = "none" } DividerType { } VerticalRadioButton { Layout.fillWidth: true Layout.leftMargin: 16 Layout.rightMargin: 16 text: qsTr("TLS") checked: security === "tls" onClicked: security = "tls" } DividerType { } VerticalRadioButton { Layout.fillWidth: true Layout.leftMargin: 16 Layout.rightMargin: 16 text: qsTr("Reality") checked: security === "reality" onClicked: security = "reality" } DividerType { } // ── TLS fields ──────────────────────────────────────────── ColumnLayout { visible: security === "tls" Layout.fillWidth: true spacing: 0 DropDownType { id: tlsAlpnDropDown Layout.fillWidth: true Layout.topMargin: 16 Layout.leftMargin: 16 Layout.rightMargin: 16 text: alpn descriptionText: qsTr("ALPN") headerText: qsTr("ALPN") drawerParent: root listView: ListViewWithRadioButtonType { rootWidth: root.width model: ListModel { Component.onCompleted: { var opts = XrayConfigModel.alpnOptions() for (var i = 0; i < opts.length; i++) { append({name: opts[i]}) } } } clickedFunction: function () { alpn = selectedText tlsAlpnDropDown.text = selectedText tlsAlpnDropDown.closeTriggered() } Component.onCompleted: { for (var i = 0; i < model.count; i++) { if (model.get(i).name === alpn) { selectedIndex = i; break } } } } Connections { target: XrayConfigModel function onDataChanged() { tlsAlpnDropDown.text = alpn } } } DropDownType { id: tlsFingerprintDropDown Layout.fillWidth: true Layout.topMargin: 8 Layout.leftMargin: 16 Layout.rightMargin: 16 text: fingerprint descriptionText: qsTr("Fingerprint") headerText: qsTr("Fingerprint") drawerParent: root listView: ListViewWithRadioButtonType { rootWidth: root.width model: ListModel { Component.onCompleted: { var opts = XrayConfigModel.fingerprintOptions() for (var i = 0; i < opts.length; i++) { append({name: opts[i]}) } } } clickedFunction: function () { fingerprint = selectedText tlsFingerprintDropDown.text = selectedText tlsFingerprintDropDown.closeTriggered() } Component.onCompleted: { for (var i = 0; i < model.count; i++) { if (model.get(i).name === fingerprint) { selectedIndex = i; break } } } } Connections { target: XrayConfigModel function onDataChanged() { tlsFingerprintDropDown.text = fingerprint } } } TextFieldWithHeaderType { Layout.fillWidth: true Layout.leftMargin: 16 Layout.rightMargin: 16 Layout.topMargin: 8 headerText: qsTr("Server Name (SNI)") textField.text: sni textField.onEditingFinished: { if (textField.text !== sni) sni = textField.text } } } // ── Reality fields ──────────────────────────────────────── ColumnLayout { visible: security === "reality" Layout.fillWidth: true spacing: 0 DropDownType { id: realityFingerprintDropDown Layout.fillWidth: true Layout.topMargin: 16 Layout.leftMargin: 16 Layout.rightMargin: 16 text: fingerprint descriptionText: qsTr("Fingerprint") headerText: qsTr("Fingerprint") drawerParent: root listView: ListViewWithRadioButtonType { rootWidth: root.width model: ListModel { Component.onCompleted: { var opts = XrayConfigModel.fingerprintOptions() for (var i = 0; i < opts.length; i++) { append({name: opts[i]}) } } } clickedFunction: function () { fingerprint = selectedText realityFingerprintDropDown.text = selectedText realityFingerprintDropDown.closeTriggered() } Component.onCompleted: { for (var i = 0; i < model.count; i++) { if (model.get(i).name === fingerprint) { selectedIndex = i; break } } } } Connections { target: XrayConfigModel function onDataChanged() { realityFingerprintDropDown.text = fingerprint } } } TextFieldWithHeaderType { Layout.fillWidth: true Layout.leftMargin: 16 Layout.rightMargin: 16 Layout.topMargin: 8 headerText: qsTr("Server Name (SNI)") textField.text: sni textField.onEditingFinished: { if (textField.text !== sni) sni = textField.text } } } Item { Layout.preferredHeight: 16 } } } BasicButtonType { id: saveButton anchors.left: root.left anchors.right: root.right anchors.bottom: root.bottom anchors.leftMargin: 16 anchors.rightMargin: 16 anchors.bottomMargin: 16 + PageController.safeAreaBottomMargin visible: listView.enabled && XrayConfigModel.hasUnsavedChanges enabled: visible text: qsTr("Save") clickedFunc: function () { var headerText = qsTr("Save settings?") var descriptionText = qsTr("All users with whom you shared a connection with will no longer be able to connect to it.") var yesButtonText = qsTr("Continue") var noButtonText = qsTr("Cancel") var yesButtonFunction = function () { if (ConnectionController.isConnected && ServersUiController.serverDefaultContainer(ServersUiController.defaultServerId) === ServersUiController.processedContainerIndex) { PageController.showNotificationMessage(qsTr("Unable change settings while there is an active connection")) return } PageController.goToPage(PageEnum.PageSetupWizardInstalling) InstallController.updateContainer(ServersUiController.processedIndex, ServersUiController.processedContainerIndex, ProtocolEnum.Xray) } var noButtonFunction = function () { if (typeof GC !== "undefined" && !GC.isMobile()) { saveButton.forceActiveFocus() } } showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction) } } }