* add focusController class * add more key handlers * add focus navigation to qml * fixed language selector * add reverse focus change to FocusController * add default focus item * update transitions * update pages * add ListViewFocusController * fix ListView navigation * update CardType for using with focus navigation * remove useless key navigation * remove useless slots, logs, Drawer open and close * fix reverse focus move on listView * fix drawer radio buttons selection * fix drawer layout and focus move * fix PageSetupWizardProtocolSettings focus move * fix back navigation on default focus item * fix crashes after ListView navigation * fix protocol settings focus move * fix focus on users on page share * clean up page share * fix server rename * fix page share default server selection * refactor about page for correct focus move * fix focus move on list views with header and-or footer * minor fixes * fix server list back button handler * fix spawn signals on switch * fix share details drawer * fix drawer open close usage * refactor listViewFocusController * refactor focusController to make the logic more straightforward * fix focus on notification * update config page for scrolling with tab * fix crash on return with esc key * fix focus navigation in dynamic delegate of list view * fix focus move on qr code on share page * refactor page logging settings for focus navigation * update popup * Bump version * Add mandatory requirement for android.software.leanback. * Fix importing files on TVs * fix: add separate method for reading files to fix file reading on Android TV * fix(android): add CHANGE_NETWORK_STATE permission for all Android versions * Fix connection check for AWG/WG * chore: minor fixes (#1235) * fix: add a workaround to open files on Android TV due to lack of SAF * fix: change the banner format for TV * refactor: make TvFilePicker activity more sustainable * fix: add the touch emulation method for Android TV * fix: null uri processing * fix: add the touch emulation method for Android TV * fix: hide UI elements that use file saving * chore: bump version code * add `ScrollBarType` * update initial config page * refactor credentials setup page to handle the focus navigation * add `setDelegateIndex` method to `listViewFocusController` * fix focus behavior on new page/popup * make minor fixes and clean up * fix: get rid of the assign function call * Scrollbar is on if the content is larger than a screen * Fix selection in language change list * Update select language list * update logging settings page * fix checked item in lists * fix split tunneling settings * make unchangable properties readonly * refactor SwitcherType * fix hide/unhide password * `PageShare` readonly properties * Fix list view focus moving on `PageShare` * remove manual focus control on `PageShare` * format `ListViewFocusController` * format `FocusController` * add `focusControl` with utility functions for focus control * refactor `listViewFocusController` acoording to `focusControl` * refactor `focusConroller` according to `focusControl` * add `printSectionName` method to `listViewController` * remove arrow from `Close application` item * fix focus movement in `ServersListView` * `Restore from backup` is visible only on start screen * `I have nothing` is visible only on start screen * fix back button on `SelectLanguageDrawer` * rename `focusControl` to `qmlUtils` * fix `CMakeLists.txt` * fix `ScrollBarType` * fix `PageSetupWizardApiServicesList` * fix focus movement on dynamic delegates in listView * refactor `PageSetupWizardProtocols` * remove comments and clean up * fix `ListViewWithLabelsType` * fix `PageProtocolCloakSettings` * fix `PageSettingsAppSplitTunneling` * fix `PageDevMenu` * remove debug output from `FocusController` * remove debug output from `ListViewFocusController` * remove debug output from `focusControl` * `focusControl` => `FocusControl` --------- Co-authored-by: albexk <albexk@proton.me> Co-authored-by: Nethius <nethiuswork@gmail.com>
823 lines
32 KiB
QML
823 lines
32 KiB
QML
import QtQuick
|
|
import QtQuick.Controls
|
|
import QtQuick.Layouts
|
|
import QtQuick.Dialogs
|
|
|
|
import SortFilterProxyModel 0.2
|
|
|
|
import PageEnum 1.0
|
|
import ContainerProps 1.0
|
|
import Style 1.0
|
|
|
|
import "./"
|
|
import "../Controls2"
|
|
import "../Controls2/TextTypes"
|
|
import "../Components"
|
|
import "../Config"
|
|
|
|
PageType {
|
|
id: root
|
|
|
|
enum ConfigType {
|
|
AmneziaConnection,
|
|
OpenVpn,
|
|
WireGuard,
|
|
Awg,
|
|
ShadowSocks,
|
|
Cloak,
|
|
Xray
|
|
}
|
|
|
|
signal revokeConfig(int index)
|
|
onRevokeConfig: function(index) {
|
|
PageController.showBusyIndicator(true)
|
|
ExportController.revokeConfig(index,
|
|
ContainersModel.getProcessedContainerIndex(),
|
|
ServersModel.getProcessedServerCredentials())
|
|
PageController.showBusyIndicator(false)
|
|
PageController.showNotificationMessage(qsTr("Config revoked"))
|
|
}
|
|
|
|
Connections {
|
|
target: ExportController
|
|
|
|
function onGenerateConfig(type) {
|
|
shareConnectionDrawer.headerText = qsTr("Connection to ") + serverSelector.text
|
|
shareConnectionDrawer.configContentHeaderText = qsTr("File with connection settings to ") + serverSelector.text
|
|
|
|
shareConnectionDrawer.openTriggered()
|
|
shareConnectionDrawer.contentVisible = false
|
|
PageController.showBusyIndicator(true)
|
|
|
|
switch (type) {
|
|
case PageShare.ConfigType.AmneziaConnection: {
|
|
ExportController.generateConnectionConfig(clientNameTextField.textFieldText);
|
|
break;
|
|
}
|
|
case PageShare.ConfigType.OpenVpn: {
|
|
ExportController.generateOpenVpnConfig(clientNameTextField.textFieldText)
|
|
shareConnectionDrawer.configCaption = qsTr("Save OpenVPN config")
|
|
shareConnectionDrawer.configExtension = ".ovpn"
|
|
shareConnectionDrawer.configFileName = "amnezia_for_openvpn"
|
|
break
|
|
}
|
|
case PageShare.ConfigType.WireGuard: {
|
|
ExportController.generateWireGuardConfig(clientNameTextField.textFieldText)
|
|
shareConnectionDrawer.configCaption = qsTr("Save WireGuard config")
|
|
shareConnectionDrawer.configExtension = ".conf"
|
|
shareConnectionDrawer.configFileName = "amnezia_for_wireguard"
|
|
break
|
|
}
|
|
case PageShare.ConfigType.Awg: {
|
|
ExportController.generateAwgConfig(clientNameTextField.textFieldText)
|
|
shareConnectionDrawer.configCaption = qsTr("Save AmneziaWG config")
|
|
shareConnectionDrawer.configExtension = ".conf"
|
|
shareConnectionDrawer.configFileName = "amnezia_for_awg"
|
|
break
|
|
}
|
|
case PageShare.ConfigType.ShadowSocks: {
|
|
ExportController.generateShadowSocksConfig()
|
|
shareConnectionDrawer.configCaption = qsTr("Save Shadowsocks config")
|
|
shareConnectionDrawer.configExtension = ".json"
|
|
shareConnectionDrawer.configFileName = "amnezia_for_shadowsocks"
|
|
break
|
|
}
|
|
case PageShare.ConfigType.Cloak: {
|
|
ExportController.generateCloakConfig()
|
|
shareConnectionDrawer.configCaption = qsTr("Save Cloak config")
|
|
shareConnectionDrawer.configExtension = ".json"
|
|
shareConnectionDrawer.configFileName = "amnezia_for_cloak"
|
|
break
|
|
}
|
|
case PageShare.ConfigType.Xray: {
|
|
ExportController.generateXrayConfig(clientNameTextField.textFieldText)
|
|
shareConnectionDrawer.configCaption = qsTr("Save XRay config")
|
|
shareConnectionDrawer.configExtension = ".json"
|
|
shareConnectionDrawer.configFileName = "amnezia_for_xray"
|
|
break
|
|
}
|
|
}
|
|
|
|
PageController.showBusyIndicator(false)
|
|
}
|
|
|
|
function onExportErrorOccurred(error) {
|
|
shareConnectionDrawer.closeTriggered()
|
|
|
|
PageController.showErrorMessage(error)
|
|
}
|
|
}
|
|
|
|
property bool isSearchBarVisible: false
|
|
property bool showContent: false
|
|
property bool shareButtonEnabled: true
|
|
property list<QtObject> connectionTypesModel: [
|
|
amneziaConnectionFormat
|
|
]
|
|
|
|
QtObject {
|
|
id: amneziaConnectionFormat
|
|
readonly property string name: qsTr("For the AmneziaVPN app")
|
|
readonly property int type: PageShare.ConfigType.AmneziaConnection
|
|
}
|
|
QtObject {
|
|
id: openVpnConnectionFormat
|
|
readonly property string name: qsTr("OpenVPN native format")
|
|
readonly property int type: PageShare.ConfigType.OpenVpn
|
|
}
|
|
QtObject {
|
|
id: wireGuardConnectionFormat
|
|
readonly property string name: qsTr("WireGuard native format")
|
|
readonly property int type: PageShare.ConfigType.WireGuard
|
|
}
|
|
QtObject {
|
|
id: awgConnectionFormat
|
|
readonly property string name: qsTr("AmneziaWG native format")
|
|
readonly property int type: PageShare.ConfigType.Awg
|
|
}
|
|
QtObject {
|
|
id: shadowSocksConnectionFormat
|
|
readonly property string name: qsTr("Shadowsocks native format")
|
|
readonly property int type: PageShare.ConfigType.ShadowSocks
|
|
}
|
|
QtObject {
|
|
id: cloakConnectionFormat
|
|
readonly property string name: qsTr("Cloak native format")
|
|
readonly property int type: PageShare.ConfigType.Cloak
|
|
}
|
|
QtObject {
|
|
id: xrayConnectionFormat
|
|
readonly property string name: qsTr("XRay native format")
|
|
readonly property int type: PageShare.ConfigType.Xray
|
|
}
|
|
|
|
FlickableType {
|
|
id: a
|
|
|
|
anchors.top: parent.top
|
|
anchors.bottom: parent.bottom
|
|
contentHeight: content.height + 10
|
|
|
|
ColumnLayout {
|
|
id: content
|
|
|
|
anchors.top: parent.top
|
|
anchors.left: parent.left
|
|
anchors.right: parent.right
|
|
|
|
anchors.rightMargin: 16
|
|
anchors.leftMargin: 16
|
|
|
|
spacing: 0
|
|
|
|
HeaderType {
|
|
id: header
|
|
Layout.fillWidth: true
|
|
Layout.topMargin: 24
|
|
|
|
headerText: qsTr("Share VPN Access")
|
|
|
|
actionButtonImage: "qrc:/images/controls/more-vertical.svg"
|
|
actionButtonFunction: function() {
|
|
shareFullAccessDrawer.openTriggered()
|
|
}
|
|
|
|
DrawerType2 {
|
|
id: shareFullAccessDrawer
|
|
|
|
parent: root
|
|
|
|
anchors.fill: parent
|
|
expandedHeight: root.height
|
|
|
|
expandedStateContent: ColumnLayout {
|
|
id: shareFullAccessDrawerContent
|
|
anchors.top: parent.top
|
|
anchors.left: parent.left
|
|
anchors.right: parent.right
|
|
anchors.topMargin: 16
|
|
|
|
spacing: 0
|
|
|
|
onImplicitHeightChanged: {
|
|
shareFullAccessDrawer.expandedHeight = shareFullAccessDrawerContent.implicitHeight + 32
|
|
}
|
|
|
|
Header2Type {
|
|
Layout.fillWidth: true
|
|
Layout.bottomMargin: 16
|
|
Layout.leftMargin: 16
|
|
Layout.rightMargin: 16
|
|
|
|
headerText: qsTr("Share full access to the server and VPN")
|
|
descriptionText: qsTr("Use for your own devices, or share with those you trust to manage the server.")
|
|
}
|
|
|
|
LabelWithButtonType {
|
|
id: shareFullAccessButton
|
|
Layout.fillWidth: true
|
|
|
|
text: qsTr("Share")
|
|
rightImageSource: "qrc:/images/controls/chevron-right.svg"
|
|
|
|
clickedFunction: function() {
|
|
PageController.goToPage(PageEnum.PageShareFullAccess)
|
|
shareFullAccessDrawer.closeTriggered()
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Rectangle {
|
|
id: accessTypeSelector
|
|
|
|
property int currentIndex
|
|
|
|
Layout.topMargin: 32
|
|
|
|
implicitWidth: accessTypeSelectorContent.implicitWidth
|
|
implicitHeight: accessTypeSelectorContent.implicitHeight
|
|
|
|
color: AmneziaStyle.color.onyxBlack
|
|
radius: 16
|
|
|
|
RowLayout {
|
|
id: accessTypeSelectorContent
|
|
|
|
spacing: 0
|
|
|
|
HorizontalRadioButton {
|
|
id: connectionRadioButton
|
|
checked: accessTypeSelector.currentIndex === 0
|
|
|
|
implicitWidth: (root.width - 32) / 2
|
|
text: qsTr("Connection")
|
|
|
|
onClicked: {
|
|
accessTypeSelector.currentIndex = 0
|
|
}
|
|
}
|
|
|
|
HorizontalRadioButton {
|
|
id: usersRadioButton
|
|
checked: accessTypeSelector.currentIndex === 1
|
|
|
|
implicitWidth: (root.width - 32) / 2
|
|
text: qsTr("Users")
|
|
|
|
onClicked: {
|
|
accessTypeSelector.currentIndex = 1
|
|
PageController.showBusyIndicator(true)
|
|
ExportController.updateClientManagementModel(ContainersModel.getProcessedContainerIndex(),
|
|
ServersModel.getProcessedServerCredentials())
|
|
PageController.showBusyIndicator(false)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
ParagraphTextType {
|
|
Layout.fillWidth: true
|
|
Layout.topMargin: 24
|
|
Layout.bottomMargin: 24
|
|
|
|
visible: accessTypeSelector.currentIndex === 0
|
|
|
|
text: qsTr("Share VPN access without the ability to manage the server")
|
|
color: AmneziaStyle.color.mutedGray
|
|
}
|
|
|
|
TextFieldWithHeaderType {
|
|
id: clientNameTextField
|
|
Layout.fillWidth: true
|
|
Layout.topMargin: 16
|
|
|
|
visible: accessTypeSelector.currentIndex === 0
|
|
|
|
headerText: qsTr("User name")
|
|
textFieldText: "New client"
|
|
textField.maximumLength: 20
|
|
|
|
checkEmptyText: true
|
|
}
|
|
|
|
DropDownType {
|
|
id: serverSelector
|
|
|
|
signal severSelectorIndexChanged
|
|
property int currentIndex: -1
|
|
|
|
Layout.fillWidth: true
|
|
Layout.topMargin: 16
|
|
|
|
drawerHeight: 0.4375
|
|
drawerParent: root
|
|
|
|
descriptionText: qsTr("Server")
|
|
headerText: qsTr("Server")
|
|
|
|
listView: ListViewWithRadioButtonType {
|
|
id: serverSelectorListView
|
|
rootWidth: root.width
|
|
imageSource: "qrc:/images/controls/check.svg"
|
|
|
|
model: SortFilterProxyModel {
|
|
id: proxyServersModel
|
|
sourceModel: ServersModel
|
|
filters: [
|
|
ValueFilter {
|
|
roleName: "hasWriteAccess"
|
|
value: true
|
|
},
|
|
ValueFilter {
|
|
roleName: "hasInstalledContainers"
|
|
value: true
|
|
}
|
|
]
|
|
}
|
|
|
|
clickedFunction: function() {
|
|
handler()
|
|
|
|
if (serverSelector.currentIndex !== serverSelectorListView.selectedIndex) {
|
|
serverSelector.currentIndex = serverSelectorListView.selectedIndex
|
|
serverSelector.severSelectorIndexChanged()
|
|
}
|
|
|
|
serverSelector.closeTriggered()
|
|
}
|
|
|
|
Component.onCompleted: {
|
|
if (ServersModel.isDefaultServerHasWriteAccess() && ServersModel.getDefaultServerData("hasInstalledContainers")) {
|
|
serverSelectorListView.selectedIndex = proxyServersModel.mapFromSource(ServersModel.defaultIndex)
|
|
} else {
|
|
serverSelectorListView.selectedIndex = 0
|
|
}
|
|
|
|
serverSelectorListView.triggerCurrentItem()
|
|
}
|
|
|
|
function handler() {
|
|
serverSelector.text = selectedText
|
|
ServersModel.processedIndex = proxyServersModel.mapToSource(selectedIndex)
|
|
}
|
|
}
|
|
}
|
|
|
|
DropDownType {
|
|
id: protocolSelector
|
|
|
|
Layout.fillWidth: true
|
|
Layout.topMargin: 16
|
|
|
|
drawerHeight: 0.5
|
|
drawerParent: root
|
|
|
|
descriptionText: qsTr("Protocol")
|
|
headerText: qsTr("Protocol")
|
|
|
|
listView: ListViewWithRadioButtonType {
|
|
id: protocolSelectorListView
|
|
|
|
rootWidth: root.width
|
|
imageSource: "qrc:/images/controls/check.svg"
|
|
|
|
model: SortFilterProxyModel {
|
|
id: proxyContainersModel
|
|
sourceModel: ContainersModel
|
|
filters: [
|
|
ValueFilter {
|
|
roleName: "isInstalled"
|
|
value: true
|
|
},
|
|
ValueFilter {
|
|
roleName: "isShareable"
|
|
value: true
|
|
}
|
|
]
|
|
}
|
|
|
|
clickedFunction: function() {
|
|
handler()
|
|
|
|
protocolSelector.closeTriggered()
|
|
}
|
|
|
|
Connections {
|
|
target: serverSelector
|
|
|
|
function onSeverSelectorIndexChanged() {
|
|
var defaultContainer = proxyContainersModel.mapFromSource(ServersModel.getProcessedServerData("defaultContainer"))
|
|
protocolSelectorListView.selectedIndex = defaultContainer
|
|
protocolSelectorListView.triggerCurrentItem()
|
|
}
|
|
}
|
|
|
|
function handler() {
|
|
if (!proxyContainersModel.count) {
|
|
root.shareButtonEnabled = false
|
|
return
|
|
} else {
|
|
root.shareButtonEnabled = true
|
|
}
|
|
|
|
protocolSelector.text = selectedText
|
|
|
|
ContainersModel.setProcessedContainerIndex(proxyContainersModel.mapToSource(selectedIndex))
|
|
|
|
fillConnectionTypeModel()
|
|
|
|
if (accessTypeSelector.currentIndex === 1) {
|
|
PageController.showBusyIndicator(true)
|
|
ExportController.updateClientManagementModel(ContainersModel.getProcessedContainerIndex(),
|
|
ServersModel.getProcessedServerCredentials())
|
|
PageController.showBusyIndicator(false)
|
|
}
|
|
}
|
|
|
|
function fillConnectionTypeModel() {
|
|
root.connectionTypesModel = [amneziaConnectionFormat]
|
|
|
|
var index = proxyContainersModel.mapToSource(selectedIndex)
|
|
|
|
if (index === ContainerProps.containerFromString("amnezia-openvpn")) {
|
|
root.connectionTypesModel.push(openVpnConnectionFormat)
|
|
} else if (index === ContainerProps.containerFromString("amnezia-wireguard")) {
|
|
root.connectionTypesModel.push(wireGuardConnectionFormat)
|
|
} else if (index === ContainerProps.containerFromString("amnezia-awg")) {
|
|
root.connectionTypesModel.push(awgConnectionFormat)
|
|
} else if (index === ContainerProps.containerFromString("amnezia-shadowsocks")) {
|
|
root.connectionTypesModel.push(openVpnConnectionFormat)
|
|
root.connectionTypesModel.push(shadowSocksConnectionFormat)
|
|
} else if (index === ContainerProps.containerFromString("amnezia-openvpn-cloak")) {
|
|
root.connectionTypesModel.push(openVpnConnectionFormat)
|
|
root.connectionTypesModel.push(shadowSocksConnectionFormat)
|
|
root.connectionTypesModel.push(cloakConnectionFormat)
|
|
} else if (index === ContainerProps.containerFromString("amnezia-xray")) {
|
|
root.connectionTypesModel.push(xrayConnectionFormat)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
DropDownType {
|
|
id: exportTypeSelector
|
|
|
|
property int currentIndex: 0
|
|
|
|
Layout.fillWidth: true
|
|
Layout.topMargin: 16
|
|
|
|
drawerHeight: 0.4375
|
|
drawerParent: root
|
|
|
|
visible: accessTypeSelector.currentIndex === 0
|
|
enabled: root.connectionTypesModel.length > 1
|
|
|
|
descriptionText: qsTr("Connection format")
|
|
headerText: qsTr("Connection format")
|
|
|
|
listView: ListViewWithRadioButtonType {
|
|
onCurrentIndexChanged: {
|
|
exportTypeSelector.currentIndex = currentIndex
|
|
exportTypeSelector.text = selectedText
|
|
}
|
|
|
|
rootWidth: root.width
|
|
|
|
imageSource: "qrc:/images/controls/check.svg"
|
|
|
|
model: root.connectionTypesModel
|
|
currentIndex: 0
|
|
|
|
clickedFunction: function() {
|
|
exportTypeSelector.text = selectedText
|
|
exportTypeSelector.currentIndex = currentIndex
|
|
exportTypeSelector.closeTriggered()
|
|
}
|
|
|
|
Component.onCompleted: {
|
|
exportTypeSelector.text = selectedText
|
|
exportTypeSelector.currentIndex = currentIndex
|
|
}
|
|
}
|
|
}
|
|
|
|
BasicButtonType {
|
|
id: shareButton
|
|
|
|
Layout.fillWidth: true
|
|
Layout.topMargin: 40
|
|
Layout.bottomMargin: 32
|
|
|
|
enabled: shareButtonEnabled
|
|
visible: accessTypeSelector.currentIndex === 0
|
|
|
|
text: qsTr("Share")
|
|
leftImageSource: "qrc:/images/controls/share-2.svg"
|
|
|
|
|
|
parentFlickable: a
|
|
|
|
clickedFunc: function(){
|
|
if (clientNameTextField.textFieldText !== "") {
|
|
ExportController.generateConfig(root.connectionTypesModel[exportTypeSelector.currentIndex].type)
|
|
}
|
|
}
|
|
}
|
|
|
|
Header2Type {
|
|
id: usersHeader
|
|
Layout.fillWidth: true
|
|
Layout.topMargin: 24
|
|
Layout.bottomMargin: 16
|
|
|
|
visible: accessTypeSelector.currentIndex === 1 && !root.isSearchBarVisible
|
|
|
|
headerText: qsTr("Users")
|
|
actionButtonImage: "qrc:/images/controls/search.svg"
|
|
actionButtonFunction: function() {
|
|
root.isSearchBarVisible = true
|
|
}
|
|
}
|
|
|
|
RowLayout {
|
|
Layout.topMargin: 24
|
|
Layout.bottomMargin: 16
|
|
visible: accessTypeSelector.currentIndex === 1 && root.isSearchBarVisible
|
|
|
|
TextFieldWithHeaderType {
|
|
id: searchTextField
|
|
Layout.fillWidth: true
|
|
|
|
textFieldPlaceholderText: qsTr("Search")
|
|
|
|
Keys.onEscapePressed: {
|
|
root.isSearchBarVisible = false
|
|
}
|
|
|
|
function navigateTo() {
|
|
if (searchTextField.textFieldText === "") {
|
|
root.isSearchBarVisible = false
|
|
}
|
|
}
|
|
|
|
Keys.onTabPressed: { navigateTo() }
|
|
Keys.onEnterPressed: { navigateTo() }
|
|
Keys.onReturnPressed: { navigateTo() }
|
|
}
|
|
|
|
ImageButtonType {
|
|
id: closeSearchButton
|
|
image: "qrc:/images/controls/close.svg"
|
|
imageColor: AmneziaStyle.color.paleGray
|
|
|
|
function clickedFunc() {
|
|
root.isSearchBarVisible = false
|
|
}
|
|
|
|
onClicked: clickedFunc()
|
|
Keys.onEnterPressed: clickedFunc()
|
|
Keys.onReturnPressed: clickedFunc()
|
|
}
|
|
}
|
|
|
|
ListView {
|
|
id: clientsListView
|
|
Layout.fillWidth: true
|
|
Layout.preferredHeight: childrenRect.height
|
|
|
|
visible: accessTypeSelector.currentIndex === 1
|
|
|
|
property bool isFocusable: true
|
|
|
|
model: SortFilterProxyModel {
|
|
id: proxyClientManagementModel
|
|
sourceModel: ClientManagementModel
|
|
filters: RegExpFilter {
|
|
roleName: "clientName"
|
|
pattern: ".*" + searchTextField.textFieldText + ".*"
|
|
caseSensitivity: Qt.CaseInsensitive
|
|
}
|
|
}
|
|
|
|
clip: true
|
|
reuseItems: true
|
|
|
|
delegate: Item {
|
|
implicitWidth: clientsListView.width
|
|
implicitHeight: delegateContent.implicitHeight
|
|
|
|
ColumnLayout {
|
|
id: delegateContent
|
|
|
|
anchors.top: parent.top
|
|
anchors.left: parent.left
|
|
anchors.right: parent.right
|
|
|
|
anchors.rightMargin: -16
|
|
anchors.leftMargin: -16
|
|
|
|
LabelWithButtonType {
|
|
id: clientFocusItem
|
|
Layout.fillWidth: true
|
|
|
|
text: clientName
|
|
rightImageSource: "qrc:/images/controls/chevron-right.svg"
|
|
|
|
clickedFunction: function() {
|
|
clientInfoDrawer.openTriggered()
|
|
}
|
|
}
|
|
|
|
DividerType {}
|
|
|
|
DrawerType2 {
|
|
id: clientInfoDrawer
|
|
|
|
parent: root
|
|
|
|
width: root.width
|
|
height: root.height
|
|
|
|
expandedStateContent: ColumnLayout {
|
|
id: expandedStateContent
|
|
anchors.top: parent.top
|
|
anchors.left: parent.left
|
|
anchors.right: parent.right
|
|
anchors.topMargin: 16
|
|
anchors.leftMargin: 16
|
|
anchors.rightMargin: 16
|
|
|
|
onImplicitHeightChanged: {
|
|
clientInfoDrawer.expandedHeight = expandedStateContent.implicitHeight + 32
|
|
}
|
|
|
|
Header2TextType {
|
|
Layout.maximumWidth: parent.width
|
|
Layout.bottomMargin: 24
|
|
|
|
text: clientName
|
|
maximumLineCount: 2
|
|
wrapMode: Text.Wrap
|
|
elide: Qt.ElideRight
|
|
}
|
|
|
|
ParagraphTextType {
|
|
color: AmneziaStyle.color.mutedGray
|
|
visible: creationDate
|
|
Layout.fillWidth: true
|
|
|
|
text: qsTr("Creation date: %1").arg(creationDate)
|
|
}
|
|
|
|
ParagraphTextType {
|
|
color: AmneziaStyle.color.mutedGray
|
|
visible: latestHandshake
|
|
Layout.fillWidth: true
|
|
|
|
text: qsTr("Latest handshake: %1").arg(latestHandshake)
|
|
}
|
|
|
|
ParagraphTextType {
|
|
color: AmneziaStyle.color.mutedGray
|
|
visible: dataReceived
|
|
Layout.fillWidth: true
|
|
|
|
text: qsTr("Data received: %1").arg(dataReceived)
|
|
}
|
|
|
|
ParagraphTextType {
|
|
color: AmneziaStyle.color.mutedGray
|
|
visible: dataSent
|
|
Layout.fillWidth: true
|
|
|
|
text: qsTr("Data sent: %1").arg(dataSent)
|
|
}
|
|
|
|
ParagraphTextType {
|
|
color: AmneziaStyle.color.mutedGray
|
|
visible: allowedIps
|
|
Layout.fillWidth: true
|
|
|
|
text: qsTr("Allowed IPs: %1").arg(allowedIps)
|
|
}
|
|
|
|
BasicButtonType {
|
|
id: renameButton
|
|
Layout.fillWidth: true
|
|
Layout.topMargin: 24
|
|
|
|
defaultColor: AmneziaStyle.color.transparent
|
|
hoveredColor: AmneziaStyle.color.translucentWhite
|
|
pressedColor: AmneziaStyle.color.sheerWhite
|
|
disabledColor: AmneziaStyle.color.mutedGray
|
|
textColor: AmneziaStyle.color.paleGray
|
|
borderWidth: 1
|
|
|
|
text: qsTr("Rename")
|
|
|
|
clickedFunc: function() {
|
|
clientNameEditDrawer.openTriggered()
|
|
}
|
|
|
|
DrawerType2 {
|
|
id: clientNameEditDrawer
|
|
|
|
parent: root
|
|
|
|
anchors.fill: parent
|
|
expandedHeight: root.height * 0.35
|
|
|
|
expandedStateContent: ColumnLayout {
|
|
anchors.top: parent.top
|
|
anchors.left: parent.left
|
|
anchors.right: parent.right
|
|
anchors.topMargin: 32
|
|
anchors.leftMargin: 16
|
|
anchors.rightMargin: 16
|
|
|
|
TextFieldWithHeaderType {
|
|
id: clientNameEditor
|
|
Layout.fillWidth: true
|
|
headerText: qsTr("Client name")
|
|
textFieldText: clientName
|
|
textField.maximumLength: 20
|
|
checkEmptyText: true
|
|
}
|
|
|
|
BasicButtonType {
|
|
id: saveButton
|
|
|
|
Layout.fillWidth: true
|
|
|
|
text: qsTr("Save")
|
|
|
|
clickedFunc: function() {
|
|
if (clientNameEditor.textFieldText === "") {
|
|
return
|
|
}
|
|
|
|
if (clientNameEditor.textFieldText !== clientName) {
|
|
PageController.showBusyIndicator(true)
|
|
ExportController.renameClient(index,
|
|
clientNameEditor.textFieldText,
|
|
ContainersModel.getProcessedContainerIndex(),
|
|
ServersModel.getProcessedServerCredentials())
|
|
PageController.showBusyIndicator(false)
|
|
clientNameEditDrawer.closeTriggered()
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
BasicButtonType {
|
|
id: revokeButton
|
|
Layout.fillWidth: true
|
|
Layout.topMargin: 8
|
|
|
|
defaultColor: AmneziaStyle.color.transparent
|
|
hoveredColor: AmneziaStyle.color.translucentWhite
|
|
pressedColor: AmneziaStyle.color.sheerWhite
|
|
disabledColor: AmneziaStyle.color.mutedGray
|
|
textColor: AmneziaStyle.color.paleGray
|
|
borderWidth: 1
|
|
|
|
text: qsTr("Revoke")
|
|
|
|
clickedFunc: function() {
|
|
var headerText = qsTr("Revoke the config for a user - %1?").arg(clientName)
|
|
var descriptionText = qsTr("The user will no longer be able to connect to your server.")
|
|
var yesButtonText = qsTr("Continue")
|
|
var noButtonText = qsTr("Cancel")
|
|
|
|
var yesButtonFunction = function() {
|
|
clientInfoDrawer.closeTriggered()
|
|
root.revokeConfig(index)
|
|
}
|
|
var noButtonFunction = function() {
|
|
if (!GC.isMobile()) {
|
|
// focusItem1.forceActiveFocus()
|
|
}
|
|
}
|
|
|
|
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
ShareConnectionDrawer {
|
|
id: shareConnectionDrawer
|
|
|
|
anchors.fill: parent
|
|
}
|
|
}
|