Compare commits

..

21 commits

Author SHA1 Message Date
Cyril Anisimov
4a8877c2ed refactor OpenVpnSettings page for correct focus 2025-06-17 09:28:56 +02:00
Cyril Anisimov
09e3f39122 make TextAreaType focusable 2025-06-17 09:28:56 +02:00
Cyril Anisimov
3393b97392 refactor OpenVpnConfigModel 2025-06-17 09:28:56 +02:00
Cyril Anisimov
0bc762ccbf update positioning in ListViewFocusController 2025-06-17 09:28:56 +02:00
Cyril Anisimov
727526b39a add focus to CheckBoxType 2025-06-17 09:28:56 +02:00
Cyril Anisimov
00f49dbc82 fix xray settings layout 2025-06-17 09:28:56 +02:00
Cyril Anisimov
b1627887eb fix selection on EasyWizardSetupPage 2025-06-17 09:28:56 +02:00
Cyril Anisimov
ad32b68d84 fix layout 2025-06-17 09:28:56 +02:00
Cyril Anisimov
117c66cd7d fix margins in server info managment tab 2025-06-17 09:28:56 +02:00
Cyril Anisimov
b8d49abdd1 fix: update server access property assignment and import sites method calls 2025-06-17 09:28:56 +02:00
Cyril Anisimov
0298371a52 fix config string view 2025-06-17 09:28:56 +02:00
Cyril Anisimov
5d133aa1a6 fix config load on Mac 2025-06-17 09:28:56 +02:00
Cyril Anisimov
4b20250051 remove deprecated parentFlickable field 2025-06-17 09:28:56 +02:00
Cyril Anisimov
06544660a4 fix full access share 2025-06-17 09:28:56 +02:00
Cyril Anisimov
671d762a20 fix SettingsContainersListView 2025-06-17 09:28:56 +02:00
Cyril Anisimov
d110c96b5c fix key import 2025-06-17 09:28:56 +02:00
Cyril Anisimov
cf5ecf1f1a fix copy on share config 2025-06-17 09:28:56 +02:00
Cyril Anisimov
111a60d81c fix PageSetupWizardProtocolSettings titles 2025-06-17 09:28:56 +02:00
Cyril Anisimov
8b01638424 fix PageSetupWizardEasy 2025-06-17 09:28:56 +02:00
Cyril Anisimov
65e7237610 Update client/ui/controllers/listViewFocusController.cpp
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-06-17 09:28:56 +02:00
Cyril Anisimov
7d187bf881 replace FlickableType and ListView with
`ListViewType`
2025-06-17 09:28:56 +02:00
120 changed files with 6992 additions and 8329 deletions

View file

@ -255,6 +255,7 @@ jobs:
env: env:
# Keep compat with MacOS 10.15 aka Catalina by Qt 6.4 # Keep compat with MacOS 10.15 aka Catalina by Qt 6.4
QT_VERSION: 6.4.3 QT_VERSION: 6.4.3
QIF_VERSION: 4.6
PROD_AGW_PUBLIC_KEY: ${{ secrets.PROD_AGW_PUBLIC_KEY }} PROD_AGW_PUBLIC_KEY: ${{ secrets.PROD_AGW_PUBLIC_KEY }}
PROD_S3_ENDPOINT: ${{ secrets.PROD_S3_ENDPOINT }} PROD_S3_ENDPOINT: ${{ secrets.PROD_S3_ENDPOINT }}
DEV_AGW_PUBLIC_KEY: ${{ secrets.DEV_AGW_PUBLIC_KEY }} DEV_AGW_PUBLIC_KEY: ${{ secrets.DEV_AGW_PUBLIC_KEY }}
@ -282,6 +283,11 @@ jobs:
set-env: 'true' set-env: 'true'
extra: '--external 7z --base ${{ env.QT_MIRROR }}' extra: '--external 7z --base ${{ env.QT_MIRROR }}'
- name: 'Install Qt Installer Framework ${{ env.QIF_VERSION }}'
run: |
mkdir -pv ${{ runner.temp }}/Qt/Tools/QtInstallerFramework
wget https://qt.amzsvc.com/tools/ifw/${{ env.QIF_VERSION }}.zip
unzip ${{ env.QIF_VERSION }}.zip -d ${{ runner.temp }}/Qt/Tools/QtInstallerFramework/
- name: 'Get sources' - name: 'Get sources'
uses: actions/checkout@v4 uses: actions/checkout@v4
@ -295,13 +301,14 @@ jobs:
- name: 'Build project' - name: 'Build project'
run: | run: |
export QT_BIN_DIR="${{ runner.temp }}/Qt/${{ env.QT_VERSION }}/macos/bin" export QT_BIN_DIR="${{ runner.temp }}/Qt/${{ env.QT_VERSION }}/macos/bin"
export QIF_BIN_DIR="${{ runner.temp }}/Qt/Tools/QtInstallerFramework/${{ env.QIF_VERSION }}/bin"
bash deploy/build_macos.sh bash deploy/build_macos.sh
- name: 'Upload installer artifact' - name: 'Upload installer artifact'
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v4
with: with:
name: AmneziaVPN_MacOS_old_installer name: AmneziaVPN_MacOS_old_installer
path: deploy/build/pkg/AmneziaVPN.pkg path: AmneziaVPN.dmg
retention-days: 7 retention-days: 7
- name: 'Upload unpacked artifact' - name: 'Upload unpacked artifact'
@ -318,6 +325,7 @@ jobs:
env: env:
QT_VERSION: 6.8.0 QT_VERSION: 6.8.0
QIF_VERSION: 4.8.1
PROD_AGW_PUBLIC_KEY: ${{ secrets.PROD_AGW_PUBLIC_KEY }} PROD_AGW_PUBLIC_KEY: ${{ secrets.PROD_AGW_PUBLIC_KEY }}
PROD_S3_ENDPOINT: ${{ secrets.PROD_S3_ENDPOINT }} PROD_S3_ENDPOINT: ${{ secrets.PROD_S3_ENDPOINT }}
DEV_AGW_PUBLIC_KEY: ${{ secrets.DEV_AGW_PUBLIC_KEY }} DEV_AGW_PUBLIC_KEY: ${{ secrets.DEV_AGW_PUBLIC_KEY }}
@ -345,6 +353,11 @@ jobs:
set-env: 'true' set-env: 'true'
extra: '--external 7z --base ${{ env.QT_MIRROR }}' extra: '--external 7z --base ${{ env.QT_MIRROR }}'
- name: 'Install Qt Installer Framework ${{ env.QIF_VERSION }}'
run: |
mkdir -pv ${{ runner.temp }}/Qt/Tools/QtInstallerFramework
wget https://qt.amzsvc.com/tools/ifw/${{ env.QIF_VERSION }}.zip
unzip ${{ env.QIF_VERSION }}.zip -d ${{ runner.temp }}/Qt/Tools/QtInstallerFramework/
- name: 'Get sources' - name: 'Get sources'
uses: actions/checkout@v4 uses: actions/checkout@v4
@ -358,13 +371,14 @@ jobs:
- name: 'Build project' - name: 'Build project'
run: | run: |
export QT_BIN_DIR="${{ runner.temp }}/Qt/${{ env.QT_VERSION }}/macos/bin" export QT_BIN_DIR="${{ runner.temp }}/Qt/${{ env.QT_VERSION }}/macos/bin"
export QIF_BIN_DIR="${{ runner.temp }}/Qt/Tools/QtInstallerFramework/${{ env.QIF_VERSION }}/bin"
bash deploy/build_macos.sh bash deploy/build_macos.sh
- name: 'Upload installer artifact' - name: 'Upload installer artifact'
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v4
with: with:
name: AmneziaVPN_MacOS_installer name: AmneziaVPN_MacOS_installer
path: deploy/build/pkg/AmneziaVPN.pkg path: AmneziaVPN.dmg
retention-days: 7 retention-days: 7
- name: 'Upload unpacked artifact' - name: 'Upload unpacked artifact'

1
.gitmodules vendored
View file

@ -7,7 +7,6 @@
[submodule "client/3rd-prebuilt"] [submodule "client/3rd-prebuilt"]
path = client/3rd-prebuilt path = client/3rd-prebuilt
url = https://github.com/amnezia-vpn/3rd-prebuilt url = https://github.com/amnezia-vpn/3rd-prebuilt
branch = feature/special-handshake
[submodule "client/3rd/amneziawg-apple"] [submodule "client/3rd/amneziawg-apple"]
path = client/3rd/amneziawg-apple path = client/3rd/amneziawg-apple
url = https://github.com/amnezia-vpn/amneziawg-apple url = https://github.com/amnezia-vpn/amneziawg-apple

View file

@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.25.0 FATAL_ERROR)
set(PROJECT AmneziaVPN) set(PROJECT AmneziaVPN)
project(${PROJECT} VERSION 4.8.8.1 project(${PROJECT} VERSION 4.8.7.2
DESCRIPTION "AmneziaVPN" DESCRIPTION "AmneziaVPN"
HOMEPAGE_URL "https://amnezia.org/" HOMEPAGE_URL "https://amnezia.org/"
) )
@ -11,7 +11,7 @@ string(TIMESTAMP CURRENT_DATE "%Y-%m-%d")
set(RELEASE_DATE "${CURRENT_DATE}") set(RELEASE_DATE "${CURRENT_DATE}")
set(APP_MAJOR_VERSION ${CMAKE_PROJECT_VERSION_MAJOR}.${CMAKE_PROJECT_VERSION_MINOR}.${CMAKE_PROJECT_VERSION_PATCH}) set(APP_MAJOR_VERSION ${CMAKE_PROJECT_VERSION_MAJOR}.${CMAKE_PROJECT_VERSION_MINOR}.${CMAKE_PROJECT_VERSION_PATCH})
set(APP_ANDROID_VERSION_CODE 2087) set(APP_ANDROID_VERSION_CODE 2086)
if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux") if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
set(MZ_PLATFORM_NAME "linux") set(MZ_PLATFORM_NAME "linux")

@ -1 +1 @@
Subproject commit 840b7b070e6ac8b90dda2fac6e98859b23727c0c Subproject commit 0f3748efd7cc04e0c914304b68931f925bed1259

@ -1 +1 @@
Subproject commit 811af0a83b3faeade89a9093a588595666d32066 Subproject commit 76e7db556a6d7e2582f9481df91db188a46c009c

View file

@ -120,21 +120,10 @@ open class Wireguard : Protocol() {
configData.optStringOrNull("Jmax")?.let { setJmax(it.toInt()) } configData.optStringOrNull("Jmax")?.let { setJmax(it.toInt()) }
configData.optStringOrNull("S1")?.let { setS1(it.toInt()) } configData.optStringOrNull("S1")?.let { setS1(it.toInt()) }
configData.optStringOrNull("S2")?.let { setS2(it.toInt()) } configData.optStringOrNull("S2")?.let { setS2(it.toInt()) }
configData.optStringOrNull("S3")?.let { setS3(it.toInt()) }
configData.optStringOrNull("S4")?.let { setS4(it.toInt()) }
configData.optStringOrNull("H1")?.let { setH1(it.toLong()) } configData.optStringOrNull("H1")?.let { setH1(it.toLong()) }
configData.optStringOrNull("H2")?.let { setH2(it.toLong()) } configData.optStringOrNull("H2")?.let { setH2(it.toLong()) }
configData.optStringOrNull("H3")?.let { setH3(it.toLong()) } configData.optStringOrNull("H3")?.let { setH3(it.toLong()) }
configData.optStringOrNull("H4")?.let { setH4(it.toLong()) } configData.optStringOrNull("H4")?.let { setH4(it.toLong()) }
configData.optStringOrNull("I1")?.let { setI1(it) }
configData.optStringOrNull("I2")?.let { setI2(it) }
configData.optStringOrNull("I3")?.let { setI3(it) }
configData.optStringOrNull("I4")?.let { setI4(it) }
configData.optStringOrNull("I5")?.let { setI5(it) }
configData.optStringOrNull("J1")?.let { setJ1(it) }
configData.optStringOrNull("J2")?.let { setJ2(it) }
configData.optStringOrNull("J3")?.let { setJ3(it) }
configData.optStringOrNull("Itime")?.let { setItime(it.toInt()) }
} }
private fun start(config: WireguardConfig, vpnBuilder: Builder, protect: (Int) -> Boolean) { private fun start(config: WireguardConfig, vpnBuilder: Builder, protect: (Int) -> Boolean) {

View file

@ -20,21 +20,10 @@ open class WireguardConfig protected constructor(
val jmax: Int?, val jmax: Int?,
val s1: Int?, val s1: Int?,
val s2: Int?, val s2: Int?,
val s3: Int?,
val s4: Int?,
val h1: Long?, val h1: Long?,
val h2: Long?, val h2: Long?,
val h3: Long?, val h3: Long?,
val h4: Long?, val h4: Long?
var i1: String?,
var i2: String?,
var i3: String?,
var i4: String?,
var i5: String?,
var j1: String?,
var j2: String?,
var j3: String?,
var itime: Int?
) : ProtocolConfig(protocolConfigBuilder) { ) : ProtocolConfig(protocolConfigBuilder) {
protected constructor(builder: Builder) : this( protected constructor(builder: Builder) : this(
@ -50,21 +39,10 @@ open class WireguardConfig protected constructor(
builder.jmax, builder.jmax,
builder.s1, builder.s1,
builder.s2, builder.s2,
builder.s3,
builder.s4,
builder.h1, builder.h1,
builder.h2, builder.h2,
builder.h3, builder.h3,
builder.h4, builder.h4
builder.i1,
builder.i2,
builder.i3,
builder.i4,
builder.i5,
builder.j1,
builder.j2,
builder.j3,
builder.itime
) )
fun toWgUserspaceString(): String = with(StringBuilder()) { fun toWgUserspaceString(): String = with(StringBuilder()) {
@ -83,21 +61,10 @@ open class WireguardConfig protected constructor(
appendLine("jmax=$jmax") appendLine("jmax=$jmax")
appendLine("s1=$s1") appendLine("s1=$s1")
appendLine("s2=$s2") appendLine("s2=$s2")
s3?.let { appendLine("s3=$it") }
s4?.let { appendLine("s4=$it") }
appendLine("h1=$h1") appendLine("h1=$h1")
appendLine("h2=$h2") appendLine("h2=$h2")
appendLine("h3=$h3") appendLine("h3=$h3")
appendLine("h4=$h4") appendLine("h4=$h4")
i1?.let { appendLine("i1=$it") }
i2?.let { appendLine("i2=$it") }
i3?.let { appendLine("i3=$it") }
i4?.let { appendLine("i4=$it") }
i5?.let { appendLine("i5=$it") }
j1?.let { appendLine("j1=$it") }
j2?.let { appendLine("j2=$it") }
j3?.let { appendLine("j3=$it") }
itime?.let { appendLine("itime=$it") }
} }
} }
@ -150,21 +117,10 @@ open class WireguardConfig protected constructor(
internal var jmax: Int? = null internal var jmax: Int? = null
internal var s1: Int? = null internal var s1: Int? = null
internal var s2: Int? = null internal var s2: Int? = null
internal var s3: Int? = null
internal var s4: Int? = null
internal var h1: Long? = null internal var h1: Long? = null
internal var h2: Long? = null internal var h2: Long? = null
internal var h3: Long? = null internal var h3: Long? = null
internal var h4: Long? = null internal var h4: Long? = null
internal var i1: String? = null
internal var i2: String? = null
internal var i3: String? = null
internal var i4: String? = null
internal var i5: String? = null
internal var j1: String? = null
internal var j2: String? = null
internal var j3: String? = null
internal var itime: Int? = null
fun setEndpoint(endpoint: InetEndpoint) = apply { this.endpoint = endpoint } fun setEndpoint(endpoint: InetEndpoint) = apply { this.endpoint = endpoint }
@ -183,21 +139,10 @@ open class WireguardConfig protected constructor(
fun setJmax(jmax: Int) = apply { this.jmax = jmax } fun setJmax(jmax: Int) = apply { this.jmax = jmax }
fun setS1(s1: Int) = apply { this.s1 = s1 } fun setS1(s1: Int) = apply { this.s1 = s1 }
fun setS2(s2: Int) = apply { this.s2 = s2 } fun setS2(s2: Int) = apply { this.s2 = s2 }
fun setS3(s3: Int) = apply { this.s3 = s3 }
fun setS4(s4: Int) = apply { this.s4 = s4 }
fun setH1(h1: Long) = apply { this.h1 = h1 } fun setH1(h1: Long) = apply { this.h1 = h1 }
fun setH2(h2: Long) = apply { this.h2 = h2 } fun setH2(h2: Long) = apply { this.h2 = h2 }
fun setH3(h3: Long) = apply { this.h3 = h3 } fun setH3(h3: Long) = apply { this.h3 = h3 }
fun setH4(h4: Long) = apply { this.h4 = h4 } fun setH4(h4: Long) = apply { this.h4 = h4 }
fun setI1(i1: String) = apply { this.i1 = i1 }
fun setI2(i2: String) = apply { this.i2 = i2 }
fun setI3(i3: String) = apply { this.i3 = i3 }
fun setI4(i4: String) = apply { this.i4 = i4 }
fun setI5(i5: String) = apply { this.i5 = i5 }
fun setJ1(j1: String) = apply { this.j1 = j1 }
fun setJ2(j2: String) = apply { this.j2 = j2 }
fun setJ3(j3: String) = apply { this.j3 = j3 }
fun setItime(itime: Int) = apply { this.itime = itime }
override fun build(): WireguardConfig = configBuild().run { WireguardConfig(this@Builder) } override fun build(): WireguardConfig = configBuild().run { WireguardConfig(this@Builder) }
} }

View file

@ -1,5 +1,4 @@
#include "awg_configurator.h" #include "awg_configurator.h"
#include "protocols/protocols_defs.h"
#include <QJsonDocument> #include <QJsonDocument>
#include <QJsonObject> #include <QJsonObject>
@ -40,20 +39,6 @@ QString AwgConfigurator::createConfig(const ServerCredentials &credentials, Dock
jsonConfig[config_key::responsePacketMagicHeader] = configMap.value(config_key::responsePacketMagicHeader); jsonConfig[config_key::responsePacketMagicHeader] = configMap.value(config_key::responsePacketMagicHeader);
jsonConfig[config_key::underloadPacketMagicHeader] = configMap.value(config_key::underloadPacketMagicHeader); jsonConfig[config_key::underloadPacketMagicHeader] = configMap.value(config_key::underloadPacketMagicHeader);
jsonConfig[config_key::transportPacketMagicHeader] = configMap.value(config_key::transportPacketMagicHeader); jsonConfig[config_key::transportPacketMagicHeader] = configMap.value(config_key::transportPacketMagicHeader);
// jsonConfig[config_key::cookieReplyPacketJunkSize] = configMap.value(config_key::cookieReplyPacketJunkSize);
// jsonConfig[config_key::transportPacketJunkSize] = configMap.value(config_key::transportPacketJunkSize);
// jsonConfig[config_key::specialJunk1] = configMap.value(amnezia::config_key::specialJunk1);
// jsonConfig[config_key::specialJunk2] = configMap.value(amnezia::config_key::specialJunk2);
// jsonConfig[config_key::specialJunk3] = configMap.value(amnezia::config_key::specialJunk3);
// jsonConfig[config_key::specialJunk4] = configMap.value(amnezia::config_key::specialJunk4);
// jsonConfig[config_key::specialJunk5] = configMap.value(amnezia::config_key::specialJunk5);
// jsonConfig[config_key::controlledJunk1] = configMap.value(amnezia::config_key::controlledJunk1);
// jsonConfig[config_key::controlledJunk2] = configMap.value(amnezia::config_key::controlledJunk2);
// jsonConfig[config_key::controlledJunk3] = configMap.value(amnezia::config_key::controlledJunk3);
// jsonConfig[config_key::specialHandshakeTimeout] = configMap.value(amnezia::config_key::specialHandshakeTimeout);
jsonConfig[config_key::mtu] = jsonConfig[config_key::mtu] =
containerConfig.value(ProtocolProps::protoToString(Proto::Awg)).toObject().value(config_key::mtu).toString(protocols::awg::defaultMtu); containerConfig.value(ProtocolProps::protoToString(Proto::Awg)).toObject().value(config_key::mtu).toString(protocols::awg::defaultMtu);

View file

@ -118,12 +118,6 @@ QString OpenVpnConfigurator::processConfigWithLocalSettings(const QPair<QString,
QRegularExpression regex("redirect-gateway.*"); QRegularExpression regex("redirect-gateway.*");
config.replace(regex, ""); config.replace(regex, "");
// We don't use secondary DNS if primary DNS is AmneziaDNS
if (dns.first.contains(protocols::dns::amneziaDnsIp)) {
QRegularExpression dnsRegex("dhcp-option DNS " + dns.second);
config.replace(dnsRegex, "");
}
if (!m_settings->isSitesSplitTunnelingEnabled()) { if (!m_settings->isSitesSplitTunnelingEnabled()) {
config.append("\nredirect-gateway def1 ipv6 bypass-dhcp\n"); config.append("\nredirect-gateway def1 ipv6 bypass-dhcp\n");
config.append("block-ipv6\n"); config.append("block-ipv6\n");
@ -167,12 +161,6 @@ QString OpenVpnConfigurator::processConfigWithExportSettings(const QPair<QString
QRegularExpression regex("redirect-gateway.*"); QRegularExpression regex("redirect-gateway.*");
config.replace(regex, ""); config.replace(regex, "");
// We don't use secondary DNS if primary DNS is AmneziaDNS
if (dns.first.contains(protocols::dns::amneziaDnsIp)) {
QRegularExpression dnsRegex("dhcp-option DNS " + dns.second);
config.replace(dnsRegex, "");
}
config.append("\nredirect-gateway def1 ipv6 bypass-dhcp\n"); config.append("\nredirect-gateway def1 ipv6 bypass-dhcp\n");
// Prevent ipv6 leak // Prevent ipv6 leak

View file

@ -32,7 +32,6 @@ namespace apiDefs
constexpr QLatin1String stackType("stack_type"); constexpr QLatin1String stackType("stack_type");
constexpr QLatin1String serviceType("service_type"); constexpr QLatin1String serviceType("service_type");
constexpr QLatin1String cliVersion("cli_version"); constexpr QLatin1String cliVersion("cli_version");
constexpr QLatin1String supportedProtocols("supported_protocols");
constexpr QLatin1String vpnKey("vpn_key"); constexpr QLatin1String vpnKey("vpn_key");
constexpr QLatin1String config("config"); constexpr QLatin1String config("config");

View file

@ -349,7 +349,7 @@ bool ServerController::isReinstallContainerRequired(DockerContainer container, c
if ((oldProtoConfig.value(config_key::subnet_address).toString(protocols::wireguard::defaultSubnetAddress) if ((oldProtoConfig.value(config_key::subnet_address).toString(protocols::wireguard::defaultSubnetAddress)
!= newProtoConfig.value(config_key::subnet_address).toString(protocols::wireguard::defaultSubnetAddress)) != newProtoConfig.value(config_key::subnet_address).toString(protocols::wireguard::defaultSubnetAddress))
|| (oldProtoConfig.value(config_key::port).toString(protocols::awg::defaultPort) || (oldProtoConfig.value(config_key::port).toString(protocols::awg::defaultPort)
!= newProtoConfig.value(config_key::port).toString(protocols::awg::defaultPort)) != newProtoConfig.value(config_key::port).toString(protocols::awg::defaultPort))
|| (oldProtoConfig.value(config_key::junkPacketCount).toString(protocols::awg::defaultJunkPacketCount) || (oldProtoConfig.value(config_key::junkPacketCount).toString(protocols::awg::defaultJunkPacketCount)
!= newProtoConfig.value(config_key::junkPacketCount).toString(protocols::awg::defaultJunkPacketCount)) != newProtoConfig.value(config_key::junkPacketCount).toString(protocols::awg::defaultJunkPacketCount))
|| (oldProtoConfig.value(config_key::junkPacketMinSize).toString(protocols::awg::defaultJunkPacketMinSize) || (oldProtoConfig.value(config_key::junkPacketMinSize).toString(protocols::awg::defaultJunkPacketMinSize)
@ -366,13 +366,8 @@ bool ServerController::isReinstallContainerRequired(DockerContainer container, c
!= newProtoConfig.value(config_key::responsePacketMagicHeader).toString(protocols::awg::defaultResponsePacketMagicHeader)) != newProtoConfig.value(config_key::responsePacketMagicHeader).toString(protocols::awg::defaultResponsePacketMagicHeader))
|| (oldProtoConfig.value(config_key::underloadPacketMagicHeader).toString(protocols::awg::defaultUnderloadPacketMagicHeader) || (oldProtoConfig.value(config_key::underloadPacketMagicHeader).toString(protocols::awg::defaultUnderloadPacketMagicHeader)
!= newProtoConfig.value(config_key::underloadPacketMagicHeader).toString(protocols::awg::defaultUnderloadPacketMagicHeader)) != newProtoConfig.value(config_key::underloadPacketMagicHeader).toString(protocols::awg::defaultUnderloadPacketMagicHeader))
|| (oldProtoConfig.value(config_key::transportPacketMagicHeader).toString(protocols::awg::defaultTransportPacketMagicHeader)) || (oldProtoConfig.value(config_key::transportPacketMagicHeader).toString(protocols::awg::defaultTransportPacketMagicHeader)
!= newProtoConfig.value(config_key::transportPacketMagicHeader).toString(protocols::awg::defaultTransportPacketMagicHeader)) != newProtoConfig.value(config_key::transportPacketMagicHeader).toString(protocols::awg::defaultTransportPacketMagicHeader)))
// || (oldProtoConfig.value(config_key::cookieReplyPacketJunkSize).toString(protocols::awg::defaultCookieReplyPacketJunkSize)
// != newProtoConfig.value(config_key::cookieReplyPacketJunkSize).toString(protocols::awg::defaultCookieReplyPacketJunkSize))
// || (oldProtoConfig.value(config_key::transportPacketJunkSize).toString(protocols::awg::defaultTransportPacketJunkSize)
// != newProtoConfig.value(config_key::transportPacketJunkSize).toString(protocols::awg::defaultTransportPacketJunkSize))
return true; return true;
} }
@ -380,7 +375,7 @@ bool ServerController::isReinstallContainerRequired(DockerContainer container, c
if ((oldProtoConfig.value(config_key::subnet_address).toString(protocols::wireguard::defaultSubnetAddress) if ((oldProtoConfig.value(config_key::subnet_address).toString(protocols::wireguard::defaultSubnetAddress)
!= newProtoConfig.value(config_key::subnet_address).toString(protocols::wireguard::defaultSubnetAddress)) != newProtoConfig.value(config_key::subnet_address).toString(protocols::wireguard::defaultSubnetAddress))
|| (oldProtoConfig.value(config_key::port).toString(protocols::wireguard::defaultPort) || (oldProtoConfig.value(config_key::port).toString(protocols::wireguard::defaultPort)
!= newProtoConfig.value(config_key::port).toString(protocols::wireguard::defaultPort))) != newProtoConfig.value(config_key::port).toString(protocols::wireguard::defaultPort)))
return true; return true;
} }
@ -460,13 +455,11 @@ ErrorCode ServerController::buildContainerWorker(const ServerCredentials &creden
runScript(credentials, runScript(credentials,
replaceVars(amnezia::scriptData(SharedScriptType::build_container), genVarsForScript(credentials, container, config)), replaceVars(amnezia::scriptData(SharedScriptType::build_container), genVarsForScript(credentials, container, config)),
cbReadStdOut, cbReadStdErr); cbReadStdOut, cbReadStdErr);
if (stdOut.contains("doesn't work on cgroups v2")) if (stdOut.contains("doesn't work on cgroups v2"))
return ErrorCode::ServerDockerOnCgroupsV2; return ErrorCode::ServerDockerOnCgroupsV2;
if (stdOut.contains("cgroup mountpoint does not exist")) if (stdOut.contains("cgroup mountpoint does not exist"))
return ErrorCode::ServerCgroupMountpoint; return ErrorCode::ServerCgroupMountpoint;
if (stdOut.contains("have reached") && stdOut.contains("pull rate limit"))
return ErrorCode::DockerPullRateLimit;
return error; return error;
} }
@ -646,9 +639,6 @@ ServerController::Vars ServerController::genVarsForScript(const ServerCredential
vars.append({ { "$UNDERLOAD_PACKET_MAGIC_HEADER", amneziaWireguarConfig.value(config_key::underloadPacketMagicHeader).toString() } }); vars.append({ { "$UNDERLOAD_PACKET_MAGIC_HEADER", amneziaWireguarConfig.value(config_key::underloadPacketMagicHeader).toString() } });
vars.append({ { "$TRANSPORT_PACKET_MAGIC_HEADER", amneziaWireguarConfig.value(config_key::transportPacketMagicHeader).toString() } }); vars.append({ { "$TRANSPORT_PACKET_MAGIC_HEADER", amneziaWireguarConfig.value(config_key::transportPacketMagicHeader).toString() } });
vars.append({ { "$COOKIE_REPLY_PACKET_JUNK_SIZE", amneziaWireguarConfig.value(config_key::cookieReplyPacketJunkSize).toString() } });
vars.append({ { "$TRANSPORT_PACKET_JUNK_SIZE", amneziaWireguarConfig.value(config_key::transportPacketJunkSize).toString() } });
// Socks5 proxy vars // Socks5 proxy vars
vars.append({ { "$SOCKS5_PROXY_PORT", socks5ProxyConfig.value(config_key::port).toString(protocols::socks5Proxy::defaultPort) } }); vars.append({ { "$SOCKS5_PROXY_PORT", socks5ProxyConfig.value(config_key::port).toString(protocols::socks5Proxy::defaultPort) } });
auto username = socks5ProxyConfig.value(config_key::userName).toString(); auto username = socks5ProxyConfig.value(config_key::userName).toString();
@ -835,7 +825,7 @@ ErrorCode ServerController::isServerDpkgBusy(const ServerCredentials &credential
if (stdOut.contains("Packet manager not found")) if (stdOut.contains("Packet manager not found"))
return ErrorCode::ServerPacketManagerError; return ErrorCode::ServerPacketManagerError;
if (stdOut.contains("fuser not installed") || stdOut.contains("cat not installed")) if (stdOut.contains("fuser not installed"))
return ErrorCode::NoError; return ErrorCode::NoError;
if (stdOut.isEmpty()) { if (stdOut.isEmpty()) {

View file

@ -60,7 +60,6 @@ namespace amnezia
ServerUserPasswordRequired = 210, ServerUserPasswordRequired = 210,
ServerDockerOnCgroupsV2 = 211, ServerDockerOnCgroupsV2 = 211,
ServerCgroupMountpoint = 212, ServerCgroupMountpoint = 212,
DockerPullRateLimit = 213,
// Ssh connection errors // Ssh connection errors
SshRequestDeniedError = 300, SshRequestDeniedError = 300,

View file

@ -28,7 +28,6 @@ QString errorString(ErrorCode code) {
case(ErrorCode::ServerUserPasswordRequired): errorMessage = QObject::tr("The user's password is required"); break; case(ErrorCode::ServerUserPasswordRequired): errorMessage = QObject::tr("The user's password is required"); break;
case(ErrorCode::ServerDockerOnCgroupsV2): errorMessage = QObject::tr("Docker error: runc doesn't work on cgroups v2"); break; case(ErrorCode::ServerDockerOnCgroupsV2): errorMessage = QObject::tr("Docker error: runc doesn't work on cgroups v2"); break;
case(ErrorCode::ServerCgroupMountpoint): errorMessage = QObject::tr("Server error: cgroup mountpoint does not exist"); break; case(ErrorCode::ServerCgroupMountpoint): errorMessage = QObject::tr("Server error: cgroup mountpoint does not exist"); break;
case(ErrorCode::DockerPullRateLimit): errorMessage = QObject::tr("Docker error: The pull rate limit has been reached"); break;
// Libssh errors // Libssh errors
case(ErrorCode::SshRequestDeniedError): errorMessage = QObject::tr("SSH request was denied"); break; case(ErrorCode::SshRequestDeniedError): errorMessage = QObject::tr("SSH request was denied"); break;

View file

@ -169,14 +169,11 @@ bool Daemon::maybeUpdateResolvers(const InterfaceConfig& config) {
if ((config.m_hopType == InterfaceConfig::MultiHopExit) || if ((config.m_hopType == InterfaceConfig::MultiHopExit) ||
(config.m_hopType == InterfaceConfig::SingleHop)) { (config.m_hopType == InterfaceConfig::SingleHop)) {
QList<QHostAddress> resolvers; QList<QHostAddress> resolvers;
resolvers.append(QHostAddress(config.m_primaryDnsServer)); resolvers.append(QHostAddress(config.m_dnsServer));
if (!config.m_secondaryDnsServer.isEmpty()) {
resolvers.append(QHostAddress(config.m_secondaryDnsServer));
}
// If the DNS is not the Gateway, it's a user defined DNS // If the DNS is not the Gateway, it's a user defined DNS
// thus, not add any other :) // thus, not add any other :)
if (config.m_primaryDnsServer == config.m_serverIpv4Gateway) { if (config.m_dnsServer == config.m_serverIpv4Gateway) {
resolvers.append(QHostAddress(config.m_serverIpv6Gateway)); resolvers.append(QHostAddress(config.m_serverIpv6Gateway));
} }
@ -282,26 +279,15 @@ bool Daemon::parseConfig(const QJsonObject& obj, InterfaceConfig& config) {
config.m_serverIpv4Gateway = obj.value("serverIpv4Gateway").toString(); config.m_serverIpv4Gateway = obj.value("serverIpv4Gateway").toString();
config.m_serverIpv6Gateway = obj.value("serverIpv6Gateway").toString(); config.m_serverIpv6Gateway = obj.value("serverIpv6Gateway").toString();
if (!obj.contains("primaryDnsServer")) { if (!obj.contains("dnsServer")) {
config.m_primaryDnsServer = QString(); config.m_dnsServer = QString();
} else { } else {
QJsonValue value = obj.value("primaryDnsServer"); QJsonValue value = obj.value("dnsServer");
if (!value.isString()) { if (!value.isString()) {
logger.error() << "dnsServer is not a string"; logger.error() << "dnsServer is not a string";
return false; return false;
} }
config.m_primaryDnsServer = value.toString(); config.m_dnsServer = value.toString();
}
if (!obj.contains("secondaryDnsServer")) {
config.m_secondaryDnsServer = QString();
} else {
QJsonValue value = obj.value("secondaryDnsServer");
if (!value.isString()) {
logger.error() << "dnsServer is not a string";
return false;
}
config.m_secondaryDnsServer = value.toString();
} }
if (!obj.contains("hopType")) { if (!obj.contains("hopType")) {
@ -405,13 +391,6 @@ bool Daemon::parseConfig(const QJsonObject& obj, InterfaceConfig& config) {
if (!obj.value("S2").isNull()) { if (!obj.value("S2").isNull()) {
config.m_responsePacketJunkSize = obj.value("S2").toString(); config.m_responsePacketJunkSize = obj.value("S2").toString();
} }
if (!obj.value("S3").isNull()) {
config.m_cookieReplyPacketJunkSize = obj.value("S3").toString();
}
if (!obj.value("S4").isNull()) {
config.m_transportPacketJunkSize = obj.value("S4").toString();
}
if (!obj.value("H1").isNull()) { if (!obj.value("H1").isNull()) {
config.m_initPacketMagicHeader = obj.value("H1").toString(); config.m_initPacketMagicHeader = obj.value("H1").toString();
} }
@ -425,34 +404,6 @@ bool Daemon::parseConfig(const QJsonObject& obj, InterfaceConfig& config) {
config.m_transportPacketMagicHeader = obj.value("H4").toString(); config.m_transportPacketMagicHeader = obj.value("H4").toString();
} }
if (!obj.value("I1").isNull()) {
config.m_specialJunk["I1"] = obj.value("I1").toString();
}
if (!obj.value("I2").isNull()) {
config.m_specialJunk["I2"] = obj.value("I2").toString();
}
if (!obj.value("I3").isNull()) {
config.m_specialJunk["I3"] = obj.value("I3").toString();
}
if (!obj.value("I4").isNull()) {
config.m_specialJunk["I4"] = obj.value("I4").toString();
}
if (!obj.value("I5").isNull()) {
config.m_specialJunk["I5"] = obj.value("I5").toString();
}
if (!obj.value("J1").isNull()) {
config.m_controlledJunk["J1"] = obj.value("J1").toString();
}
if (!obj.value("J2").isNull()) {
config.m_controlledJunk["J2"] = obj.value("J2").toString();
}
if (!obj.value("J3").isNull()) {
config.m_controlledJunk["J3"] = obj.value("J3").toString();
}
if (!obj.value("Itime").isNull()) {
config.m_specialHandshakeTimeout = obj.value("Itime").toString();
}
return true; return true;
} }
@ -495,7 +446,7 @@ bool Daemon::deactivate(bool emitSignals) {
m_connections.clear(); m_connections.clear();
// Delete the interface // Delete the interface
return wgutils()->deleteInterface(); return wgutils()->deleteInterface();
} }
QString Daemon::logs() { QString Daemon::logs() {

View file

@ -28,8 +28,7 @@ QJsonObject InterfaceConfig::toJson() const {
(m_hopType == InterfaceConfig::SingleHop)) { (m_hopType == InterfaceConfig::SingleHop)) {
json.insert("serverIpv4Gateway", QJsonValue(m_serverIpv4Gateway)); json.insert("serverIpv4Gateway", QJsonValue(m_serverIpv4Gateway));
json.insert("serverIpv6Gateway", QJsonValue(m_serverIpv6Gateway)); json.insert("serverIpv6Gateway", QJsonValue(m_serverIpv6Gateway));
json.insert("primaryDnsServer", QJsonValue(m_primaryDnsServer)); json.insert("dnsServer", QJsonValue(m_dnsServer));
json.insert("secondaryDnsServer", QJsonValue(m_secondaryDnsServer));
} }
QJsonArray allowedIPAddesses; QJsonArray allowedIPAddesses;
@ -101,15 +100,11 @@ QString InterfaceConfig::toWgConf(const QMap<QString, QString>& extra) const {
out << "MTU = " << m_deviceMTU << "\n"; out << "MTU = " << m_deviceMTU << "\n";
} }
if (!m_primaryDnsServer.isNull()) { if (!m_dnsServer.isNull()) {
QStringList dnsServers; QStringList dnsServers(m_dnsServer);
dnsServers.append(m_primaryDnsServer);
if (!m_secondaryDnsServer.isNull()) {
dnsServers.append(m_secondaryDnsServer);
}
// If the DNS is not the Gateway, it's a user defined DNS // If the DNS is not the Gateway, it's a user defined DNS
// thus, not add any other :) // thus, not add any other :)
if (m_primaryDnsServer == m_serverIpv4Gateway) { if (m_dnsServer == m_serverIpv4Gateway) {
dnsServers.append(m_serverIpv6Gateway); dnsServers.append(m_serverIpv6Gateway);
} }
out << "DNS = " << dnsServers.join(", ") << "\n"; out << "DNS = " << dnsServers.join(", ") << "\n";
@ -130,12 +125,6 @@ QString InterfaceConfig::toWgConf(const QMap<QString, QString>& extra) const {
if (!m_responsePacketJunkSize.isNull()) { if (!m_responsePacketJunkSize.isNull()) {
out << "S2 = " << m_responsePacketJunkSize << "\n"; out << "S2 = " << m_responsePacketJunkSize << "\n";
} }
if (!m_cookieReplyPacketJunkSize.isNull()) {
out << "S3 = " << m_cookieReplyPacketJunkSize << "\n";
}
if (!m_transportPacketJunkSize.isNull()) {
out << "S4 = " << m_transportPacketJunkSize << "\n";
}
if (!m_initPacketMagicHeader.isNull()) { if (!m_initPacketMagicHeader.isNull()) {
out << "H1 = " << m_initPacketMagicHeader << "\n"; out << "H1 = " << m_initPacketMagicHeader << "\n";
} }
@ -149,16 +138,6 @@ QString InterfaceConfig::toWgConf(const QMap<QString, QString>& extra) const {
out << "H4 = " << m_transportPacketMagicHeader << "\n"; out << "H4 = " << m_transportPacketMagicHeader << "\n";
} }
for (const QString& key : m_specialJunk.keys()) {
out << key << " = " << m_specialJunk[key] << "\n";
}
for (const QString& key : m_controlledJunk.keys()) {
out << key << " = " << m_controlledJunk[key] << "\n";
}
if (!m_specialHandshakeTimeout.isNull()) {
out << "Itime = " << m_specialHandshakeTimeout << "\n";
}
// If any extra config was provided, append it now. // If any extra config was provided, append it now.
for (const QString& key : extra.keys()) { for (const QString& key : extra.keys()) {
out << key << " = " << extra[key] << "\n"; out << key << " = " << extra[key] << "\n";

View file

@ -32,8 +32,7 @@ class InterfaceConfig {
QString m_serverIpv4AddrIn; QString m_serverIpv4AddrIn;
QString m_serverPskKey; QString m_serverPskKey;
QString m_serverIpv6AddrIn; QString m_serverIpv6AddrIn;
QString m_primaryDnsServer; QString m_dnsServer;
QString m_secondaryDnsServer;
int m_serverPort = 0; int m_serverPort = 0;
int m_deviceMTU = 1420; int m_deviceMTU = 1420;
QList<IPAddress> m_allowedIPAddressRanges; QList<IPAddress> m_allowedIPAddressRanges;
@ -50,15 +49,10 @@ class InterfaceConfig {
QString m_junkPacketMaxSize; QString m_junkPacketMaxSize;
QString m_initPacketJunkSize; QString m_initPacketJunkSize;
QString m_responsePacketJunkSize; QString m_responsePacketJunkSize;
QString m_cookieReplyPacketJunkSize;
QString m_transportPacketJunkSize;
QString m_initPacketMagicHeader; QString m_initPacketMagicHeader;
QString m_responsePacketMagicHeader; QString m_responsePacketMagicHeader;
QString m_underloadPacketMagicHeader; QString m_underloadPacketMagicHeader;
QString m_transportPacketMagicHeader; QString m_transportPacketMagicHeader;
QMap<QString, QString> m_specialJunk;
QMap<QString, QString> m_controlledJunk;
QString m_specialHandshakeTimeout;
QJsonObject toJson() const; QJsonObject toJson() const;
QString toWgConf( QString toWgConf(

View file

@ -38,7 +38,7 @@ LocalSocketController::LocalSocketController() {
m_socket = new QLocalSocket(this); m_socket = new QLocalSocket(this);
connect(m_socket, &QLocalSocket::connected, this, connect(m_socket, &QLocalSocket::connected, this,
&LocalSocketController::daemonConnected); &LocalSocketController::daemonConnected);
connect(m_socket, &QLocalSocket::disconnected, this, connect(m_socket, &QLocalSocket::disconnected, this,
[&] { errorOccurred(QLocalSocket::PeerClosedError); }); [&] { errorOccurred(QLocalSocket::PeerClosedError); });
connect(m_socket, &QLocalSocket::errorOccurred, this, connect(m_socket, &QLocalSocket::errorOccurred, this,
&LocalSocketController::errorOccurred); &LocalSocketController::errorOccurred);
@ -135,7 +135,7 @@ void LocalSocketController::activate(const QJsonObject &rawConfig) {
// set up IPv6 unique-local-address, ULA, with "fd00::/8" prefix, not globally routable. // set up IPv6 unique-local-address, ULA, with "fd00::/8" prefix, not globally routable.
// this will be default IPv6 gateway, OS recognizes that IPv6 link is local and switches to IPv4. // this will be default IPv6 gateway, OS recognizes that IPv6 link is local and switches to IPv4.
// Otherwise some OSes (Linux) try IPv6 forever and hang. // Otherwise some OSes (Linux) try IPv6 forever and hang.
// https://en.wikipedia.org/wiki/Unique_local_address (RFC 4193) // https://en.wikipedia.org/wiki/Unique_local_address (RFC 4193)
// https://man7.org/linux/man-pages/man5/gai.conf.5.html // https://man7.org/linux/man-pages/man5/gai.conf.5.html
json.insert("deviceIpv6Address", "fd58:baa6:dead::1"); // simply "dead::1" is globally-routable, don't use it json.insert("deviceIpv6Address", "fd58:baa6:dead::1"); // simply "dead::1" is globally-routable, don't use it
@ -149,14 +149,7 @@ void LocalSocketController::activate(const QJsonObject &rawConfig) {
json.insert("serverPort", wgConfig.value(amnezia::config_key::port).toInt()); json.insert("serverPort", wgConfig.value(amnezia::config_key::port).toInt());
json.insert("serverIpv4Gateway", wgConfig.value(amnezia::config_key::hostName)); json.insert("serverIpv4Gateway", wgConfig.value(amnezia::config_key::hostName));
// json.insert("serverIpv6Gateway", QJsonValue(hop.m_server.ipv6Gateway())); // json.insert("serverIpv6Gateway", QJsonValue(hop.m_server.ipv6Gateway()));
json.insert("dnsServer", rawConfig.value(amnezia::config_key::dns1));
json.insert("primaryDnsServer", rawConfig.value(amnezia::config_key::dns1));
// We don't use secondary DNS if primary DNS is AmneziaDNS
if (!rawConfig.value(amnezia::config_key::dns1).toString().
contains(amnezia::protocols::dns::amneziaDnsIp)) {
json.insert("secondaryDnsServer", rawConfig.value(amnezia::config_key::dns2));
}
QJsonArray jsAllowedIPAddesses; QJsonArray jsAllowedIPAddesses;
@ -244,61 +237,28 @@ void LocalSocketController::activate(const QJsonObject &rawConfig) {
json.insert(amnezia::config_key::junkPacketMaxSize, wgConfig.value(amnezia::config_key::junkPacketMaxSize)); json.insert(amnezia::config_key::junkPacketMaxSize, wgConfig.value(amnezia::config_key::junkPacketMaxSize));
json.insert(amnezia::config_key::initPacketJunkSize, wgConfig.value(amnezia::config_key::initPacketJunkSize)); json.insert(amnezia::config_key::initPacketJunkSize, wgConfig.value(amnezia::config_key::initPacketJunkSize));
json.insert(amnezia::config_key::responsePacketJunkSize, wgConfig.value(amnezia::config_key::responsePacketJunkSize)); json.insert(amnezia::config_key::responsePacketJunkSize, wgConfig.value(amnezia::config_key::responsePacketJunkSize));
json.insert(amnezia::config_key::cookieReplyPacketJunkSize, wgConfig.value(amnezia::config_key::cookieReplyPacketJunkSize));
json.insert(amnezia::config_key::transportPacketJunkSize, wgConfig.value(amnezia::config_key::transportPacketJunkSize));
json.insert(amnezia::config_key::initPacketMagicHeader, wgConfig.value(amnezia::config_key::initPacketMagicHeader)); json.insert(amnezia::config_key::initPacketMagicHeader, wgConfig.value(amnezia::config_key::initPacketMagicHeader));
json.insert(amnezia::config_key::responsePacketMagicHeader, wgConfig.value(amnezia::config_key::responsePacketMagicHeader)); json.insert(amnezia::config_key::responsePacketMagicHeader, wgConfig.value(amnezia::config_key::responsePacketMagicHeader));
json.insert(amnezia::config_key::underloadPacketMagicHeader, wgConfig.value(amnezia::config_key::underloadPacketMagicHeader)); json.insert(amnezia::config_key::underloadPacketMagicHeader, wgConfig.value(amnezia::config_key::underloadPacketMagicHeader));
json.insert(amnezia::config_key::transportPacketMagicHeader, wgConfig.value(amnezia::config_key::transportPacketMagicHeader)); json.insert(amnezia::config_key::transportPacketMagicHeader, wgConfig.value(amnezia::config_key::transportPacketMagicHeader));
json.insert(amnezia::config_key::specialJunk1, wgConfig.value(amnezia::config_key::specialJunk1));
json.insert(amnezia::config_key::specialJunk2, wgConfig.value(amnezia::config_key::specialJunk2));
json.insert(amnezia::config_key::specialJunk3, wgConfig.value(amnezia::config_key::specialJunk3));
json.insert(amnezia::config_key::specialJunk4, wgConfig.value(amnezia::config_key::specialJunk4));
json.insert(amnezia::config_key::specialJunk5, wgConfig.value(amnezia::config_key::specialJunk5));
json.insert(amnezia::config_key::controlledJunk1, wgConfig.value(amnezia::config_key::controlledJunk1));
json.insert(amnezia::config_key::controlledJunk2, wgConfig.value(amnezia::config_key::controlledJunk2));
json.insert(amnezia::config_key::controlledJunk3, wgConfig.value(amnezia::config_key::controlledJunk3));
json.insert(amnezia::config_key::specialHandshakeTimeout, wgConfig.value(amnezia::config_key::specialHandshakeTimeout));
} else if (!wgConfig.value(amnezia::config_key::junkPacketCount).isUndefined() } else if (!wgConfig.value(amnezia::config_key::junkPacketCount).isUndefined()
&& !wgConfig.value(amnezia::config_key::junkPacketMinSize).isUndefined() && !wgConfig.value(amnezia::config_key::junkPacketMinSize).isUndefined()
&& !wgConfig.value(amnezia::config_key::junkPacketMaxSize).isUndefined() && !wgConfig.value(amnezia::config_key::junkPacketMaxSize).isUndefined()
&& !wgConfig.value(amnezia::config_key::initPacketJunkSize).isUndefined() && !wgConfig.value(amnezia::config_key::initPacketJunkSize).isUndefined()
&& !wgConfig.value(amnezia::config_key::responsePacketJunkSize).isUndefined() && !wgConfig.value(amnezia::config_key::responsePacketJunkSize).isUndefined()
&& !wgConfig.value(amnezia::config_key::cookieReplyPacketJunkSize).isUndefined()
&& !wgConfig.value(amnezia::config_key::transportPacketJunkSize).isUndefined()
&& !wgConfig.value(amnezia::config_key::initPacketMagicHeader).isUndefined() && !wgConfig.value(amnezia::config_key::initPacketMagicHeader).isUndefined()
&& !wgConfig.value(amnezia::config_key::responsePacketMagicHeader).isUndefined() && !wgConfig.value(amnezia::config_key::responsePacketMagicHeader).isUndefined()
&& !wgConfig.value(amnezia::config_key::underloadPacketMagicHeader).isUndefined() && !wgConfig.value(amnezia::config_key::underloadPacketMagicHeader).isUndefined()
&& !wgConfig.value(amnezia::config_key::transportPacketMagicHeader).isUndefined() && !wgConfig.value(amnezia::config_key::transportPacketMagicHeader).isUndefined()) {
&& !wgConfig.value(amnezia::config_key::specialJunk1).isUndefined()
&& !wgConfig.value(amnezia::config_key::specialJunk2).isUndefined()
&& !wgConfig.value(amnezia::config_key::specialJunk3).isUndefined()
&& !wgConfig.value(amnezia::config_key::specialJunk4).isUndefined()
&& !wgConfig.value(amnezia::config_key::specialJunk5).isUndefined()
&& !wgConfig.value(amnezia::config_key::controlledJunk1).isUndefined()
&& !wgConfig.value(amnezia::config_key::controlledJunk2).isUndefined()
&& !wgConfig.value(amnezia::config_key::controlledJunk3).isUndefined()
&& !wgConfig.value(amnezia::config_key::specialHandshakeTimeout).isUndefined()) {
json.insert(amnezia::config_key::junkPacketCount, wgConfig.value(amnezia::config_key::junkPacketCount)); json.insert(amnezia::config_key::junkPacketCount, wgConfig.value(amnezia::config_key::junkPacketCount));
json.insert(amnezia::config_key::junkPacketMinSize, wgConfig.value(amnezia::config_key::junkPacketMinSize)); json.insert(amnezia::config_key::junkPacketMinSize, wgConfig.value(amnezia::config_key::junkPacketMinSize));
json.insert(amnezia::config_key::junkPacketMaxSize, wgConfig.value(amnezia::config_key::junkPacketMaxSize)); json.insert(amnezia::config_key::junkPacketMaxSize, wgConfig.value(amnezia::config_key::junkPacketMaxSize));
json.insert(amnezia::config_key::initPacketJunkSize, wgConfig.value(amnezia::config_key::initPacketJunkSize)); json.insert(amnezia::config_key::initPacketJunkSize, wgConfig.value(amnezia::config_key::initPacketJunkSize));
json.insert(amnezia::config_key::responsePacketJunkSize, wgConfig.value(amnezia::config_key::responsePacketJunkSize)); json.insert(amnezia::config_key::responsePacketJunkSize, wgConfig.value(amnezia::config_key::responsePacketJunkSize));
json.insert(amnezia::config_key::cookieReplyPacketJunkSize, wgConfig.value(amnezia::config_key::cookieReplyPacketJunkSize));
json.insert(amnezia::config_key::transportPacketJunkSize, wgConfig.value(amnezia::config_key::transportPacketJunkSize));
json.insert(amnezia::config_key::initPacketMagicHeader, wgConfig.value(amnezia::config_key::initPacketMagicHeader)); json.insert(amnezia::config_key::initPacketMagicHeader, wgConfig.value(amnezia::config_key::initPacketMagicHeader));
json.insert(amnezia::config_key::responsePacketMagicHeader, wgConfig.value(amnezia::config_key::responsePacketMagicHeader)); json.insert(amnezia::config_key::responsePacketMagicHeader, wgConfig.value(amnezia::config_key::responsePacketMagicHeader));
json.insert(amnezia::config_key::underloadPacketMagicHeader, wgConfig.value(amnezia::config_key::underloadPacketMagicHeader)); json.insert(amnezia::config_key::underloadPacketMagicHeader, wgConfig.value(amnezia::config_key::underloadPacketMagicHeader));
json.insert(amnezia::config_key::transportPacketMagicHeader, wgConfig.value(amnezia::config_key::transportPacketMagicHeader)); json.insert(amnezia::config_key::transportPacketMagicHeader, wgConfig.value(amnezia::config_key::transportPacketMagicHeader));
json.insert(amnezia::config_key::specialJunk1, wgConfig.value(amnezia::config_key::specialJunk1));
json.insert(amnezia::config_key::specialJunk2, wgConfig.value(amnezia::config_key::specialJunk2));
json.insert(amnezia::config_key::specialJunk3, wgConfig.value(amnezia::config_key::specialJunk3));
json.insert(amnezia::config_key::specialJunk4, wgConfig.value(amnezia::config_key::specialJunk4));
json.insert(amnezia::config_key::specialJunk5, wgConfig.value(amnezia::config_key::specialJunk5));
json.insert(amnezia::config_key::controlledJunk1, wgConfig.value(amnezia::config_key::controlledJunk1));
json.insert(amnezia::config_key::controlledJunk2, wgConfig.value(amnezia::config_key::controlledJunk2));
json.insert(amnezia::config_key::controlledJunk3, wgConfig.value(amnezia::config_key::controlledJunk3));
json.insert(amnezia::config_key::specialHandshakeTimeout, wgConfig.value(amnezia::config_key::specialHandshakeTimeout));
} }
write(json); write(json);

View file

@ -4,10 +4,7 @@ struct WGConfig: Decodable {
let initPacketMagicHeader, responsePacketMagicHeader: String? let initPacketMagicHeader, responsePacketMagicHeader: String?
let underloadPacketMagicHeader, transportPacketMagicHeader: String? let underloadPacketMagicHeader, transportPacketMagicHeader: String?
let junkPacketCount, junkPacketMinSize, junkPacketMaxSize: String? let junkPacketCount, junkPacketMinSize, junkPacketMaxSize: String?
let initPacketJunkSize, responsePacketJunkSize, cookieReplyPacketJunkSize, transportPacketJunkSize: String? let initPacketJunkSize, responsePacketJunkSize: String?
let specialJunk1, specialJunk2, specialJunk3, specialJunk4, specialJunk5: String?
let controlledJunk1, controlledJunk2, controlledJunk3: String?
let specialHandshakeTimeout: String?
let dns1: String let dns1: String
let dns2: String let dns2: String
let mtu: String let mtu: String
@ -26,10 +23,7 @@ struct WGConfig: Decodable {
case initPacketMagicHeader = "H1", responsePacketMagicHeader = "H2" case initPacketMagicHeader = "H1", responsePacketMagicHeader = "H2"
case underloadPacketMagicHeader = "H3", transportPacketMagicHeader = "H4" case underloadPacketMagicHeader = "H3", transportPacketMagicHeader = "H4"
case junkPacketCount = "Jc", junkPacketMinSize = "Jmin", junkPacketMaxSize = "Jmax" case junkPacketCount = "Jc", junkPacketMinSize = "Jmin", junkPacketMaxSize = "Jmax"
case initPacketJunkSize = "S1", responsePacketJunkSize = "S2", cookieReplyPacketJunkSize = "S3", transportPacketJunkSize = "S4" case initPacketJunkSize = "S1", responsePacketJunkSize = "S2"
case specialJunk1 = "I1", specialJunk2 = "I2", specialJunk3 = "I3", specialJunk4 = "I4", specialJunk5 = "I5"
case controlledJunk1 = "J1", controlledJunk2 = "J2", controlledJunk3 = "J3"
case specialHandshakeTimeout = "Itime"
case dns1 case dns1
case dns2 case dns2
case mtu case mtu
@ -46,59 +40,19 @@ struct WGConfig: Decodable {
} }
var settings: String { var settings: String {
guard junkPacketCount != nil else { return "" } junkPacketCount == nil ? "" :
"""
var settingsLines: [String] = [] Jc = \(junkPacketCount!)
Jmin = \(junkPacketMinSize!)
// Required parameters when junkPacketCount is present Jmax = \(junkPacketMaxSize!)
settingsLines.append("Jc = \(junkPacketCount!)") S1 = \(initPacketJunkSize!)
settingsLines.append("Jmin = \(junkPacketMinSize!)") S2 = \(responsePacketJunkSize!)
settingsLines.append("Jmax = \(junkPacketMaxSize!)") H1 = \(initPacketMagicHeader!)
settingsLines.append("S1 = \(initPacketJunkSize!)") H2 = \(responsePacketMagicHeader!)
settingsLines.append("S2 = \(responsePacketJunkSize!)") H3 = \(underloadPacketMagicHeader!)
H4 = \(transportPacketMagicHeader!)
settingsLines.append("H1 = \(initPacketMagicHeader!)")
settingsLines.append("H2 = \(responsePacketMagicHeader!)")
settingsLines.append("H3 = \(underloadPacketMagicHeader!)")
settingsLines.append("H4 = \(transportPacketMagicHeader!)")
// Optional parameters - only add if not nil and not empty """
if let s3 = cookieReplyPacketJunkSize, !s3.isEmpty {
settingsLines.append("S3 = \(s3)")
}
if let s4 = transportPacketJunkSize, !s4.isEmpty {
settingsLines.append("S4 = \(s4)")
}
if let i1 = specialJunk1, !i1.isEmpty {
settingsLines.append("I1 = \(i1)")
}
if let i2 = specialJunk2, !i2.isEmpty {
settingsLines.append("I2 = \(i2)")
}
if let i3 = specialJunk3, !i3.isEmpty {
settingsLines.append("I3 = \(i3)")
}
if let i4 = specialJunk4, !i4.isEmpty {
settingsLines.append("I4 = \(i4)")
}
if let i5 = specialJunk5, !i5.isEmpty {
settingsLines.append("I5 = \(i5)")
}
if let j1 = controlledJunk1, !j1.isEmpty {
settingsLines.append("J1 = \(j1)")
}
if let j2 = controlledJunk2, !j2.isEmpty {
settingsLines.append("J2 = \(j2)")
}
if let j3 = controlledJunk3, !j3.isEmpty {
settingsLines.append("J3 = \(j3)")
}
if let itime = specialHandshakeTimeout, !itime.isEmpty {
settingsLines.append("Itime = \(itime)")
}
return settingsLines.joined(separator: "\n")
} }
var str: String { var str: String {

View file

@ -507,8 +507,6 @@ bool IosController::setupWireGuard()
wgConfig.insert(config_key::initPacketJunkSize, config[config_key::initPacketJunkSize]); wgConfig.insert(config_key::initPacketJunkSize, config[config_key::initPacketJunkSize]);
wgConfig.insert(config_key::responsePacketJunkSize, config[config_key::responsePacketJunkSize]); wgConfig.insert(config_key::responsePacketJunkSize, config[config_key::responsePacketJunkSize]);
wgConfig.insert(config_key::cookieReplyPacketJunkSize, config[config_key::cookieReplyPacketJunkSize]);
wgConfig.insert(config_key::transportPacketJunkSize, config[config_key::transportPacketJunkSize]);
wgConfig.insert(config_key::junkPacketCount, config[config_key::junkPacketCount]); wgConfig.insert(config_key::junkPacketCount, config[config_key::junkPacketCount]);
wgConfig.insert(config_key::junkPacketMinSize, config[config_key::junkPacketMinSize]); wgConfig.insert(config_key::junkPacketMinSize, config[config_key::junkPacketMinSize]);
@ -607,23 +605,11 @@ bool IosController::setupAwg()
wgConfig.insert(config_key::initPacketJunkSize, config[config_key::initPacketJunkSize]); wgConfig.insert(config_key::initPacketJunkSize, config[config_key::initPacketJunkSize]);
wgConfig.insert(config_key::responsePacketJunkSize, config[config_key::responsePacketJunkSize]); wgConfig.insert(config_key::responsePacketJunkSize, config[config_key::responsePacketJunkSize]);
wgConfig.insert(config_key::cookieReplyPacketJunkSize, config[config_key::cookieReplyPacketJunkSize]);
wgConfig.insert(config_key::transportPacketJunkSize, config[config_key::transportPacketJunkSize]);
wgConfig.insert(config_key::junkPacketCount, config[config_key::junkPacketCount]); wgConfig.insert(config_key::junkPacketCount, config[config_key::junkPacketCount]);
wgConfig.insert(config_key::junkPacketMinSize, config[config_key::junkPacketMinSize]); wgConfig.insert(config_key::junkPacketMinSize, config[config_key::junkPacketMinSize]);
wgConfig.insert(config_key::junkPacketMaxSize, config[config_key::junkPacketMaxSize]); wgConfig.insert(config_key::junkPacketMaxSize, config[config_key::junkPacketMaxSize]);
wgConfig.insert(config_key::specialJunk1, config[config_key::specialJunk1]);
wgConfig.insert(config_key::specialJunk2, config[config_key::specialJunk2]);
wgConfig.insert(config_key::specialJunk3, config[config_key::specialJunk3]);
wgConfig.insert(config_key::specialJunk4, config[config_key::specialJunk4]);
wgConfig.insert(config_key::specialJunk5, config[config_key::specialJunk5]);
wgConfig.insert(config_key::controlledJunk1, config[config_key::controlledJunk1]);
wgConfig.insert(config_key::controlledJunk2, config[config_key::controlledJunk2]);
wgConfig.insert(config_key::controlledJunk3, config[config_key::controlledJunk3]);
wgConfig.insert(config_key::specialHandshakeTimeout, config[config_key::specialHandshakeTimeout]);
QJsonDocument wgConfigDoc(wgConfig); QJsonDocument wgConfigDoc(wgConfig);
QString wgConfigDocStr(wgConfigDoc.toJson(QJsonDocument::Compact)); QString wgConfigDocStr(wgConfigDoc.toJson(QJsonDocument::Compact));
@ -808,9 +794,9 @@ bool IosController::shareText(const QStringList& filesToSend) {
if (!qtController) return; if (!qtController) return;
UIActivityViewController *activityController = [[UIActivityViewController alloc] initWithActivityItems:sharingItems applicationActivities:nil]; UIActivityViewController *activityController = [[UIActivityViewController alloc] initWithActivityItems:sharingItems applicationActivities:nil];
__block bool isAccepted = false; __block bool isAccepted = false;
[activityController setCompletionWithItemsHandler:^(NSString *activityType, BOOL completed, NSArray *returnedItems, NSError *activityError) { [activityController setCompletionWithItemsHandler:^(NSString *activityType, BOOL completed, NSArray *returnedItems, NSError *activityError) {
isAccepted = completed; isAccepted = completed;
emit finished(); emit finished();
@ -822,11 +808,11 @@ bool IosController::shareText(const QStringList& filesToSend) {
popController.sourceView = qtController.view; popController.sourceView = qtController.view;
popController.sourceRect = CGRectMake(100, 100, 100, 100); popController.sourceRect = CGRectMake(100, 100, 100, 100);
} }
QEventLoop wait; QEventLoop wait;
QObject::connect(this, &IosController::finished, &wait, &QEventLoop::quit); QObject::connect(this, &IosController::finished, &wait, &QEventLoop::quit);
wait.exec(); wait.exec();
return isAccepted; return isAccepted;
} }
@ -840,7 +826,7 @@ QString IosController::openFile() {
if (!qtController) return; if (!qtController) return;
[qtController presentViewController:documentPicker animated:YES completion:nil]; [qtController presentViewController:documentPicker animated:YES completion:nil];
__block QString filePath; __block QString filePath;
documentPickerDelegate.documentPickerClosedCallback = ^(NSString *path) { documentPickerDelegate.documentPickerClosedCallback = ^(NSString *path) {
@ -855,7 +841,7 @@ QString IosController::openFile() {
QEventLoop wait; QEventLoop wait;
QObject::connect(this, &IosController::finished, &wait, &QEventLoop::quit); QObject::connect(this, &IosController::finished, &wait, &QEventLoop::quit);
wait.exec(); wait.exec();
return filePath; return filePath;
} }

View file

@ -121,12 +121,6 @@ bool WireguardUtilsLinux::addInterface(const InterfaceConfig& config) {
if (!config.m_responsePacketJunkSize.isEmpty()) { if (!config.m_responsePacketJunkSize.isEmpty()) {
out << "s2=" << config.m_responsePacketJunkSize << "\n"; out << "s2=" << config.m_responsePacketJunkSize << "\n";
} }
if (!config.m_cookieReplyPacketJunkSize.isEmpty()) {
out << "s3=" << config.m_cookieReplyPacketJunkSize << "\n";
}
if (!config.m_transportPacketJunkSize.isEmpty()) {
out << "s4=" << config.m_transportPacketJunkSize << "\n";
}
if (!config.m_initPacketMagicHeader.isEmpty()) { if (!config.m_initPacketMagicHeader.isEmpty()) {
out << "h1=" << config.m_initPacketMagicHeader << "\n"; out << "h1=" << config.m_initPacketMagicHeader << "\n";
} }
@ -140,26 +134,13 @@ bool WireguardUtilsLinux::addInterface(const InterfaceConfig& config) {
out << "h4=" << config.m_transportPacketMagicHeader << "\n"; out << "h4=" << config.m_transportPacketMagicHeader << "\n";
} }
for (const QString& key : config.m_specialJunk.keys()) {
out << key.toLower() << "=" << config.m_specialJunk.value(key) << "\n";
}
for (const QString& key : config.m_controlledJunk.keys()) {
out << key.toLower() << "=" << config.m_controlledJunk.value(key) << "\n";
}
if (!config.m_specialHandshakeTimeout.isEmpty()) {
out << "itime=" << config.m_specialHandshakeTimeout << "\n";
}
int err = uapiErrno(uapiCommand(message)); int err = uapiErrno(uapiCommand(message));
if (err != 0) { if (err != 0) {
logger.error() << "Interface configuration failed:" << strerror(err); logger.error() << "Interface configuration failed:" << strerror(err);
} else { } else {
if (config.m_killSwitchEnabled) { if (config.m_killSwitchEnabled) {
FirewallParams params { }; FirewallParams params { };
params.dnsServers.append(config.m_primaryDnsServer); params.dnsServers.append(config.m_dnsServer);
if (!config.m_secondaryDnsServer.isEmpty()) {
params.dnsServers.append(config.m_secondaryDnsServer);
}
if (config.m_allowedIPAddressRanges.contains(IPAddress("0.0.0.0/0"))) { if (config.m_allowedIPAddressRanges.contains(IPAddress("0.0.0.0/0"))) {
params.blockAll = true; params.blockAll = true;
if (config.m_excludedAddresses.size()) { if (config.m_excludedAddresses.size()) {

View file

@ -43,16 +43,8 @@ namespace {
#include "macosfirewall.h" #include "macosfirewall.h"
#include <QDir> #define ResourceDir qApp->applicationDirPath() + "/pf"
#include <QStandardPaths> #define DaemonDataDir qApp->applicationDirPath() + "/pf"
// Read-only rules bundled with the application.
#define ResourceDir (qApp->applicationDirPath() + "/pf")
// Writable location that does NOT live inside the signed bundle. Using a
// constant path under /Library/Application Support keeps the signature intact
// and is accessible to the root helper.
#define DaemonDataDir QStringLiteral("/Library/Application Support/AmneziaVPN/pf")
#include <QProcess> #include <QProcess>
@ -129,8 +121,6 @@ void MacOSFirewall::install()
logger.info() << "Installing PF root anchor"; logger.info() << "Installing PF root anchor";
installRootAnchors(); installRootAnchors();
// Ensure writable directory exists, then store the token there.
QDir().mkpath(DaemonDataDir);
execute(QStringLiteral("pfctl -E 2>&1 | grep -F 'Token : ' | cut -c9- > '%1/pf.token'").arg(DaemonDataDir)); execute(QStringLiteral("pfctl -E 2>&1 | grep -F 'Token : ' | cut -c9- > '%1/pf.token'").arg(DaemonDataDir));
} }

View file

@ -119,12 +119,6 @@ bool WireguardUtilsMacos::addInterface(const InterfaceConfig& config) {
if (!config.m_responsePacketJunkSize.isEmpty()) { if (!config.m_responsePacketJunkSize.isEmpty()) {
out << "s2=" << config.m_responsePacketJunkSize << "\n"; out << "s2=" << config.m_responsePacketJunkSize << "\n";
} }
if (!config.m_cookieReplyPacketJunkSize.isEmpty()) {
out << "s3=" << config.m_cookieReplyPacketJunkSize << "\n";
}
if (!config.m_transportPacketJunkSize.isEmpty()) {
out << "s4=" << config.m_transportPacketJunkSize << "\n";
}
if (!config.m_initPacketMagicHeader.isEmpty()) { if (!config.m_initPacketMagicHeader.isEmpty()) {
out << "h1=" << config.m_initPacketMagicHeader << "\n"; out << "h1=" << config.m_initPacketMagicHeader << "\n";
} }
@ -138,43 +132,30 @@ bool WireguardUtilsMacos::addInterface(const InterfaceConfig& config) {
out << "h4=" << config.m_transportPacketMagicHeader << "\n"; out << "h4=" << config.m_transportPacketMagicHeader << "\n";
} }
for (const QString& key : config.m_specialJunk.keys()) {
out << key.toLower() << "=" << config.m_specialJunk.value(key) << "\n";
}
for (const QString& key : config.m_controlledJunk.keys()) {
out << key.toLower() << "=" << config.m_controlledJunk.value(key) << "\n";
}
if (!config.m_specialHandshakeTimeout.isEmpty()) {
out << "itime=" << config.m_specialHandshakeTimeout << "\n";
}
int err = uapiErrno(uapiCommand(message)); int err = uapiErrno(uapiCommand(message));
if (err != 0) { if (err != 0) {
logger.error() << "Interface configuration failed:" << strerror(err); logger.error() << "Interface configuration failed:" << strerror(err);
} else { } else {
if (config.m_killSwitchEnabled) { if (config.m_killSwitchEnabled) {
FirewallParams params { }; FirewallParams params { };
params.dnsServers.append(config.m_primaryDnsServer); params.dnsServers.append(config.m_dnsServer);
if (!config.m_secondaryDnsServer.isEmpty()) {
params.dnsServers.append(config.m_secondaryDnsServer);
}
if (config.m_allowedIPAddressRanges.contains(IPAddress("0.0.0.0/0"))) { if (config.m_allowedIPAddressRanges.contains(IPAddress("0.0.0.0/0"))) {
params.blockAll = true; params.blockAll = true;
if (config.m_excludedAddresses.size()) { if (config.m_excludedAddresses.size()) {
params.allowNets = true; params.allowNets = true;
foreach (auto net, config.m_excludedAddresses) { foreach (auto net, config.m_excludedAddresses) {
params.allowAddrs.append(net.toUtf8()); params.allowAddrs.append(net.toUtf8());
} }
} }
} else { } else {
params.blockNets = true; params.blockNets = true;
foreach (auto net, config.m_allowedIPAddressRanges) { foreach (auto net, config.m_allowedIPAddressRanges) {
params.blockAddrs.append(net.toString()); params.blockAddrs.append(net.toString());
} }
}
applyFirewallRules(params);
} }
applyFirewallRules(params);
}
} }
return (err == 0); return (err == 0);
} }

View file

@ -291,32 +291,15 @@ bool WindowsFirewall::enablePeerTraffic(const InterfaceConfig& config) {
"Block Internet", config.m_serverPublicKey)) { "Block Internet", config.m_serverPublicKey)) {
return false; return false;
} }
if (!config.m_primaryDnsServer.isEmpty()) { if (!config.m_dnsServer.isEmpty()) {
if (!allowTrafficTo(QHostAddress(config.m_primaryDnsServer), 53, HIGH_WEIGHT, if (!allowTrafficTo(QHostAddress(config.m_dnsServer), 53, HIGH_WEIGHT,
"Allow DNS-Server", config.m_serverPublicKey)) { "Allow DNS-Server", config.m_serverPublicKey)) {
return false; return false;
} }
// In some cases, we might configure a 2nd DNS server for IPv6, however // In some cases, we might configure a 2nd DNS server for IPv6, however
// this should probably be cleaned up by converting m_dnsServer into // this should probably be cleaned up by converting m_dnsServer into
// a QStringList instead. // a QStringList instead.
if (config.m_primaryDnsServer == config.m_serverIpv4Gateway) { if (config.m_dnsServer == config.m_serverIpv4Gateway) {
if (!allowTrafficTo(QHostAddress(config.m_serverIpv6Gateway), 53,
HIGH_WEIGHT, "Allow extra IPv6 DNS-Server",
config.m_serverPublicKey)) {
return false;
}
}
}
if (!config.m_secondaryDnsServer.isEmpty()) {
if (!allowTrafficTo(QHostAddress(config.m_secondaryDnsServer), 53, HIGH_WEIGHT,
"Allow DNS-Server", config.m_serverPublicKey)) {
return false;
}
// In some cases, we might configure a 2nd DNS server for IPv6, however
// this should probably be cleaned up by converting m_dnsServer into
// a QStringList instead.
if (config.m_secondaryDnsServer == config.m_serverIpv4Gateway) {
if (!allowTrafficTo(QHostAddress(config.m_serverIpv6Gateway), 53, if (!allowTrafficTo(QHostAddress(config.m_serverIpv6Gateway), 53,
HIGH_WEIGHT, "Allow extra IPv6 DNS-Server", HIGH_WEIGHT, "Allow extra IPv6 DNS-Server",
config.m_serverPublicKey)) { config.m_serverPublicKey)) {

View file

@ -130,7 +130,6 @@ bool WireguardUtilsWindows::addInterface(const InterfaceConfig& config) {
// Enable the windows firewall // Enable the windows firewall
NET_IFINDEX ifindex; NET_IFINDEX ifindex;
ConvertInterfaceLuidToIndex(&luid, &ifindex); ConvertInterfaceLuidToIndex(&luid, &ifindex);
m_firewall->allowAllTraffic();
m_firewall->enableInterface(ifindex); m_firewall->enableInterface(ifindex);
} }

View file

@ -343,7 +343,7 @@ void OpenVpnProtocol::updateVpnGateway(const QString &line)
// killSwitch toggle // killSwitch toggle
if (m_vpnLocalAddress == netInterfaces.at(i).addressEntries().at(j).ip().toString()) { if (m_vpnLocalAddress == netInterfaces.at(i).addressEntries().at(j).ip().toString()) {
if (QVariant(m_configData.value(config_key::killSwitchOption).toString()).toBool()) { if (QVariant(m_configData.value(config_key::killSwitchOption).toString()).toBool()) {
IpcClient::Interface()->enableKillSwitch(m_configData, netInterfaces.at(i).index()); IpcClient::Interface()->enableKillSwitch(QJsonObject(), netInterfaces.at(i).index());
} }
m_configData.insert("vpnAdapterIndex", netInterfaces.at(i).index()); m_configData.insert("vpnAdapterIndex", netInterfaces.at(i).index());
m_configData.insert("vpnGateway", m_vpnGateway); m_configData.insert("vpnGateway", m_vpnGateway);

View file

@ -72,21 +72,10 @@ namespace amnezia
constexpr char junkPacketMaxSize[] = "Jmax"; constexpr char junkPacketMaxSize[] = "Jmax";
constexpr char initPacketJunkSize[] = "S1"; constexpr char initPacketJunkSize[] = "S1";
constexpr char responsePacketJunkSize[] = "S2"; constexpr char responsePacketJunkSize[] = "S2";
constexpr char cookieReplyPacketJunkSize[] = "S3";
constexpr char transportPacketJunkSize[] = "S4";
constexpr char initPacketMagicHeader[] = "H1"; constexpr char initPacketMagicHeader[] = "H1";
constexpr char responsePacketMagicHeader[] = "H2"; constexpr char responsePacketMagicHeader[] = "H2";
constexpr char underloadPacketMagicHeader[] = "H3"; constexpr char underloadPacketMagicHeader[] = "H3";
constexpr char transportPacketMagicHeader[] = "H4"; constexpr char transportPacketMagicHeader[] = "H4";
constexpr char specialJunk1[] = "I1";
constexpr char specialJunk2[] = "I2";
constexpr char specialJunk3[] = "I3";
constexpr char specialJunk4[] = "I4";
constexpr char specialJunk5[] = "I5";
constexpr char controlledJunk1[] = "J1";
constexpr char controlledJunk2[] = "J2";
constexpr char controlledJunk3[] = "J3";
constexpr char specialHandshakeTimeout[] = "Itime";
constexpr char openvpn[] = "openvpn"; constexpr char openvpn[] = "openvpn";
constexpr char wireguard[] = "wireguard"; constexpr char wireguard[] = "wireguard";
@ -227,22 +216,10 @@ namespace amnezia
constexpr char defaultJunkPacketMaxSize[] = "30"; constexpr char defaultJunkPacketMaxSize[] = "30";
constexpr char defaultInitPacketJunkSize[] = "15"; constexpr char defaultInitPacketJunkSize[] = "15";
constexpr char defaultResponsePacketJunkSize[] = "18"; constexpr char defaultResponsePacketJunkSize[] = "18";
constexpr char defaultCookieReplyPacketJunkSize[] = "20";
constexpr char defaultTransportPacketJunkSize[] = "23";
constexpr char defaultInitPacketMagicHeader[] = "1020325451"; constexpr char defaultInitPacketMagicHeader[] = "1020325451";
constexpr char defaultResponsePacketMagicHeader[] = "3288052141"; constexpr char defaultResponsePacketMagicHeader[] = "3288052141";
constexpr char defaultTransportPacketMagicHeader[] = "2528465083"; constexpr char defaultTransportPacketMagicHeader[] = "2528465083";
constexpr char defaultUnderloadPacketMagicHeader[] = "1766607858"; constexpr char defaultUnderloadPacketMagicHeader[] = "1766607858";
constexpr char defaultSpecialJunk1[] = "";
constexpr char defaultSpecialJunk2[] = "";
constexpr char defaultSpecialJunk3[] = "";
constexpr char defaultSpecialJunk4[] = "";
constexpr char defaultSpecialJunk5[] = "";
constexpr char defaultControlledJunk1[] = "";
constexpr char defaultControlledJunk2[] = "";
constexpr char defaultControlledJunk3[] = "";
constexpr char defaultSpecialHandshakeTimeout[] = "";
} }
namespace socks5Proxy namespace socks5Proxy

View file

@ -98,13 +98,8 @@ ErrorCode XrayProtocol::startTun2Sock()
if (vpnState == Vpn::ConnectionState::Connected) { if (vpnState == Vpn::ConnectionState::Connected) {
setConnectionState(Vpn::ConnectionState::Connecting); setConnectionState(Vpn::ConnectionState::Connecting);
QList<QHostAddress> dnsAddr; QList<QHostAddress> dnsAddr;
dnsAddr.push_back(QHostAddress(m_configData.value(config_key::dns1).toString())); dnsAddr.push_back(QHostAddress(m_configData.value(config_key::dns1).toString()));
// We don't use secondary DNS if primary DNS is AmneziaDNS dnsAddr.push_back(QHostAddress(m_configData.value(config_key::dns2).toString()));
if (!m_configData.value(amnezia::config_key::dns1).toString().
contains(amnezia::protocols::dns::amneziaDnsIp)) {
dnsAddr.push_back(QHostAddress(m_configData.value(config_key::dns2).toString()));
}
#ifdef Q_OS_WIN #ifdef Q_OS_WIN
QThread::msleep(8000); QThread::msleep(8000);
#endif #endif
@ -139,7 +134,7 @@ ErrorCode XrayProtocol::startTun2Sock()
// killSwitch toggle // killSwitch toggle
if (m_vpnLocalAddress == netInterfaces.at(i).addressEntries().at(j).ip().toString()) { if (m_vpnLocalAddress == netInterfaces.at(i).addressEntries().at(j).ip().toString()) {
if (QVariant(m_configData.value(config_key::killSwitchOption).toString()).toBool()) { if (QVariant(m_configData.value(config_key::killSwitchOption).toString()).toBool()) {
IpcClient::Interface()->enableKillSwitch(m_configData, netInterfaces.at(i).index()); IpcClient::Interface()->enableKillSwitch(QJsonObject(), netInterfaces.at(i).index());
} }
m_configData.insert("vpnAdapterIndex", netInterfaces.at(i).index()); m_configData.insert("vpnAdapterIndex", netInterfaces.at(i).index());
m_configData.insert("vpnGateway", m_vpnGateway); m_configData.insert("vpnGateway", m_vpnGateway);

View file

@ -239,7 +239,6 @@
<file>ui/qml/Components/ApiPremV1MigrationDrawer.qml</file> <file>ui/qml/Components/ApiPremV1MigrationDrawer.qml</file>
<file>ui/qml/Components/ApiPremV1SubListDrawer.qml</file> <file>ui/qml/Components/ApiPremV1SubListDrawer.qml</file>
<file>ui/qml/Components/OtpCodeDrawer.qml</file> <file>ui/qml/Components/OtpCodeDrawer.qml</file>
<file>ui/qml/Components/AwgTextField.qml</file>
</qresource> </qresource>
<qresource prefix="/countriesFlags"> <qresource prefix="/countriesFlags">
<file>images/flagKit/ZW.svg</file> <file>images/flagKit/ZW.svg</file>

View file

@ -10,7 +10,7 @@ RUN mkdir -p /opt/amnezia
RUN echo -e "#!/bin/bash\ntail -f /dev/null" > /opt/amnezia/start.sh RUN echo -e "#!/bin/bash\ntail -f /dev/null" > /opt/amnezia/start.sh
RUN chmod a+x /opt/amnezia/start.sh RUN chmod a+x /opt/amnezia/start.sh
# Tune network # Tune network
RUN echo -e " \n\ RUN echo -e " \n\
fs.file-max = 51200 \n\ fs.file-max = 51200 \n\
\n\ \n\
@ -40,8 +40,7 @@ RUN echo -e " \n\
echo -e " \n\ echo -e " \n\
* soft nofile 51200 \n\ * soft nofile 51200 \n\
* hard nofile 51200 \n\ * hard nofile 51200 \n\
" | sed -e 's/^\s\+//g' | tee -a /etc/security/limits.conf " | sed -e 's/^\s\+//g' | tee -a /etc/security/limits.conf
ENTRYPOINT [ "dumb-init", "/opt/amnezia/start.sh" ] ENTRYPOINT [ "dumb-init", "/opt/amnezia/start.sh" ]
CMD [ "" ] CMD [ "" ]

View file

@ -23,5 +23,4 @@ H1 = $INIT_PACKET_MAGIC_HEADER
H2 = $RESPONSE_PACKET_MAGIC_HEADER H2 = $RESPONSE_PACKET_MAGIC_HEADER
H3 = $UNDERLOAD_PACKET_MAGIC_HEADER H3 = $UNDERLOAD_PACKET_MAGIC_HEADER
H4 = $TRANSPORT_PACKET_MAGIC_HEADER H4 = $TRANSPORT_PACKET_MAGIC_HEADER
EOF EOF

View file

@ -1,7 +1,6 @@
if which apt-get > /dev/null 2>&1; then LOCK_CMD="fuser"; LOCK_FILE="/var/lib/dpkg/lock-frontend";\ if which apt-get > /dev/null 2>&1; then LOCK_FILE="/var/lib/dpkg/lock-frontend";\
elif which dnf > /dev/null 2>&1; then LOCK_CMD="fuser"; LOCK_FILE="/var/cache/dnf/* /var/run/dnf/* /var/lib/dnf/* /var/lib/rpm/*";\ elif which dnf > /dev/null 2>&1; then LOCK_FILE="/var/run/dnf.pid";\
elif which yum > /dev/null 2>&1; then LOCK_CMD="cat"; LOCK_FILE="/var/run/yum.pid";\ elif which yum > /dev/null 2>&1; then LOCK_FILE="/var/run/yum.pid";\
elif which zypper > /dev/null 2>&1; then LOCK_CMD="cat"; LOCK_FILE="/var/run/zypp.pid";\ elif which pacman > /dev/null 2>&1; then LOCK_FILE="/var/lib/pacman/db.lck";\
elif which pacman > /dev/null 2>&1; then LOCK_CMD="fuser"; LOCK_FILE="/var/lib/pacman/db.lck";\
else echo "Packet manager not found"; echo "Internal error"; exit 1; fi;\ else echo "Packet manager not found"; echo "Internal error"; exit 1; fi;\
if command -v $LOCK_CMD > /dev/null 2>&1; then sudo $LOCK_CMD $LOCK_FILE 2>/dev/null; else echo "$LOCK_CMD not installed"; fi if command -v fuser > /dev/null 2>&1; then sudo fuser $LOCK_FILE 2>/dev/null; else echo "fuser not installed"; fi

View file

@ -1,7 +1,6 @@
if which apt-get > /dev/null 2>&1; then pm=$(which apt-get); opt="--version";\ if which apt-get > /dev/null 2>&1; then pm=$(which apt-get); opt="--version";\
elif which dnf > /dev/null 2>&1; then pm=$(which dnf); opt="--version";\ elif which dnf > /dev/null 2>&1; then pm=$(which dnf); opt="--version";\
elif which yum > /dev/null 2>&1; then pm=$(which yum); opt="--version";\ elif which yum > /dev/null 2>&1; then pm=$(which yum); opt="--version";\
elif which zypper > /dev/null 2>&1; then pm=$(which zypper); opt="--version";\
elif which pacman > /dev/null 2>&1; then pm=$(which pacman); opt="--version";\ elif which pacman > /dev/null 2>&1; then pm=$(which pacman); opt="--version";\
else pm="uname"; opt="-a";\ else pm="uname"; opt="-a";\
fi;\ fi;\

View file

@ -1,7 +1,6 @@
if which apt-get > /dev/null 2>&1; then pm=$(which apt-get); silent_inst="-yq install"; check_pkgs="-yq update"; docker_pkg="docker.io"; dist="debian";\ if which apt-get > /dev/null 2>&1; then pm=$(which apt-get); silent_inst="-yq install"; check_pkgs="-yq update"; docker_pkg="docker.io"; dist="debian";\
elif which dnf > /dev/null 2>&1; then pm=$(which dnf); silent_inst="-yq install"; check_pkgs="-yq check-update"; docker_pkg="docker"; dist="fedora";\ elif which dnf > /dev/null 2>&1; then pm=$(which dnf); silent_inst="-yq install"; check_pkgs="-yq check-update"; docker_pkg="docker"; dist="fedora";\
elif which yum > /dev/null 2>&1; then pm=$(which yum); silent_inst="-y -q install"; check_pkgs="-y -q check-update"; docker_pkg="docker"; dist="centos";\ elif which yum > /dev/null 2>&1; then pm=$(which yum); silent_inst="-y -q install"; check_pkgs="-y -q check-update"; docker_pkg="docker"; dist="centos";\
elif which zypper > /dev/null 2>&1; then pm=$(which zypper); silent_inst="-nq install"; check_pkgs="-nq refresh"; docker_pkg="docker"; dist="opensuse";\
elif which pacman > /dev/null 2>&1; then pm=$(which pacman); silent_inst="-S --noconfirm --noprogressbar --quiet"; check_pkgs="-Sup"; docker_pkg="docker"; dist="archlinux";\ elif which pacman > /dev/null 2>&1; then pm=$(which pacman); silent_inst="-S --noconfirm --noprogressbar --quiet"; check_pkgs="-Sup"; docker_pkg="docker"; dist="archlinux";\
else echo "Packet manager not found"; exit 1; fi;\ else echo "Packet manager not found"; exit 1; fi;\
echo "Dist: $dist, Packet manager: $pm, Install command: $silent_inst, Check pkgs command: $check_pkgs, Docker pkg: $docker_pkg";\ echo "Dist: $dist, Packet manager: $pm, Install command: $silent_inst, Check pkgs command: $check_pkgs, Docker pkg: $docker_pkg";\

View file

@ -18,7 +18,6 @@ namespace
{ {
constexpr char cloak[] = "cloak"; constexpr char cloak[] = "cloak";
constexpr char awg[] = "awg"; constexpr char awg[] = "awg";
constexpr char vless[] = "vless";
constexpr char apiEndpoint[] = "api_endpoint"; constexpr char apiEndpoint[] = "api_endpoint";
constexpr char accessToken[] = "api_key"; constexpr char accessToken[] = "api_key";
@ -36,6 +35,10 @@ namespace
constexpr char serviceInfo[] = "service_info"; constexpr char serviceInfo[] = "service_info";
constexpr char serviceProtocol[] = "service_protocol"; constexpr char serviceProtocol[] = "service_protocol";
constexpr char aesKey[] = "aes_key";
constexpr char aesIv[] = "aes_iv";
constexpr char aesSalt[] = "aes_salt";
constexpr char apiPayload[] = "api_payload"; constexpr char apiPayload[] = "api_payload";
constexpr char keyPayload[] = "key_payload"; constexpr char keyPayload[] = "key_payload";
@ -44,185 +47,6 @@ namespace
constexpr char config[] = "config"; constexpr char config[] = "config";
} }
struct ProtocolData
{
OpenVpnConfigurator::ConnectionData certRequest;
QString wireGuardClientPrivKey;
QString wireGuardClientPubKey;
QString xrayUuid;
};
struct GatewayRequestData
{
QString osVersion;
QString appVersion;
QString installationUuid;
QString userCountryCode;
QString serverCountryCode;
QString serviceType;
QString serviceProtocol;
QJsonObject authData;
QJsonObject toJsonObject() const
{
QJsonObject obj;
if (!osVersion.isEmpty()) {
obj[configKey::osVersion] = osVersion;
}
if (!appVersion.isEmpty()) {
obj[configKey::appVersion] = appVersion;
}
if (!installationUuid.isEmpty()) {
obj[configKey::uuid] = installationUuid;
}
if (!userCountryCode.isEmpty()) {
obj[configKey::userCountryCode] = userCountryCode;
}
if (!serverCountryCode.isEmpty()) {
obj[configKey::serverCountryCode] = serverCountryCode;
}
if (!serviceType.isEmpty()) {
obj[configKey::serviceType] = serviceType;
}
if (!serviceProtocol.isEmpty()) {
obj[configKey::serviceProtocol] = serviceProtocol;
}
if (!authData.isEmpty()) {
obj[configKey::authData] = authData;
}
return obj;
}
};
ProtocolData generateProtocolData(const QString &protocol)
{
ProtocolData protocolData;
if (protocol == configKey::cloak) {
protocolData.certRequest = OpenVpnConfigurator::createCertRequest();
} else if (protocol == configKey::awg) {
auto connData = WireguardConfigurator::genClientKeys();
protocolData.wireGuardClientPubKey = connData.clientPubKey;
protocolData.wireGuardClientPrivKey = connData.clientPrivKey;
} else if (protocol == configKey::vless) {
protocolData.xrayUuid = QUuid::createUuid().toString(QUuid::WithoutBraces);
}
return protocolData;
}
void appendProtocolDataToApiPayload(const QString &protocol, const ProtocolData &protocolData, QJsonObject &apiPayload)
{
if (protocol == configKey::cloak) {
apiPayload[configKey::certificate] = protocolData.certRequest.request;
} else if (protocol == configKey::awg) {
apiPayload[configKey::publicKey] = protocolData.wireGuardClientPubKey;
} else if (protocol == configKey::vless) {
apiPayload[configKey::publicKey] = protocolData.xrayUuid;
}
}
ErrorCode fillServerConfig(const QString &protocol, const ProtocolData &apiPayloadData, const QByteArray &apiResponseBody,
QJsonObject &serverConfig)
{
QString data = QJsonDocument::fromJson(apiResponseBody).object().value(config_key::config).toString();
data.replace("vpn://", "");
QByteArray ba = QByteArray::fromBase64(data.toUtf8(), QByteArray::Base64UrlEncoding | QByteArray::OmitTrailingEquals);
if (ba.isEmpty()) {
qDebug() << "empty vpn key";
return ErrorCode::ApiConfigEmptyError;
}
QByteArray ba_uncompressed = qUncompress(ba);
if (!ba_uncompressed.isEmpty()) {
ba = ba_uncompressed;
}
QString configStr = ba;
if (protocol == configKey::cloak) {
configStr.replace("<key>", "<key>\n");
configStr.replace("$OPENVPN_PRIV_KEY", apiPayloadData.certRequest.privKey);
} else if (protocol == configKey::awg) {
configStr.replace("$WIREGUARD_CLIENT_PRIVATE_KEY", apiPayloadData.wireGuardClientPrivKey);
auto newServerConfig = QJsonDocument::fromJson(configStr.toUtf8()).object();
auto containers = newServerConfig.value(config_key::containers).toArray();
if (containers.isEmpty()) {
qDebug() << "missing containers field";
return ErrorCode::ApiConfigEmptyError;
}
auto container = containers.at(0).toObject();
QString containerName = ContainerProps::containerTypeToString(DockerContainer::Awg);
auto serverProtocolConfig = container.value(containerName).toObject();
auto clientProtocolConfig =
QJsonDocument::fromJson(serverProtocolConfig.value(config_key::last_config).toString().toUtf8()).object();
//TODO looks like this block can be removed after v1 configs EOL
serverProtocolConfig[config_key::junkPacketCount] = clientProtocolConfig.value(config_key::junkPacketCount);
serverProtocolConfig[config_key::junkPacketMinSize] = clientProtocolConfig.value(config_key::junkPacketMinSize);
serverProtocolConfig[config_key::junkPacketMaxSize] = clientProtocolConfig.value(config_key::junkPacketMaxSize);
serverProtocolConfig[config_key::initPacketJunkSize] = clientProtocolConfig.value(config_key::initPacketJunkSize);
serverProtocolConfig[config_key::responsePacketJunkSize] = clientProtocolConfig.value(config_key::responsePacketJunkSize);
serverProtocolConfig[config_key::initPacketMagicHeader] = clientProtocolConfig.value(config_key::initPacketMagicHeader);
serverProtocolConfig[config_key::responsePacketMagicHeader] = clientProtocolConfig.value(config_key::responsePacketMagicHeader);
serverProtocolConfig[config_key::underloadPacketMagicHeader] = clientProtocolConfig.value(config_key::underloadPacketMagicHeader);
serverProtocolConfig[config_key::transportPacketMagicHeader] = clientProtocolConfig.value(config_key::transportPacketMagicHeader);
serverProtocolConfig[config_key::cookieReplyPacketJunkSize] = clientProtocolConfig.value(config_key::cookieReplyPacketJunkSize);
serverProtocolConfig[config_key::transportPacketJunkSize] = clientProtocolConfig.value(config_key::transportPacketJunkSize);
serverProtocolConfig[config_key::specialJunk1] = clientProtocolConfig.value(config_key::specialJunk1);
serverProtocolConfig[config_key::specialJunk2] = clientProtocolConfig.value(config_key::specialJunk2);
serverProtocolConfig[config_key::specialJunk3] = clientProtocolConfig.value(config_key::specialJunk3);
serverProtocolConfig[config_key::specialJunk4] = clientProtocolConfig.value(config_key::specialJunk4);
serverProtocolConfig[config_key::specialJunk5] = clientProtocolConfig.value(config_key::specialJunk5);
serverProtocolConfig[config_key::controlledJunk1] = clientProtocolConfig.value(config_key::controlledJunk1);
serverProtocolConfig[config_key::controlledJunk2] = clientProtocolConfig.value(config_key::controlledJunk2);
serverProtocolConfig[config_key::controlledJunk3] = clientProtocolConfig.value(config_key::controlledJunk3);
serverProtocolConfig[config_key::specialHandshakeTimeout] = clientProtocolConfig.value(config_key::specialHandshakeTimeout);
//
container[containerName] = serverProtocolConfig;
containers.replace(0, container);
newServerConfig[config_key::containers] = containers;
configStr = QString(QJsonDocument(newServerConfig).toJson());
}
QJsonObject newServerConfig = QJsonDocument::fromJson(configStr.toUtf8()).object();
serverConfig[config_key::dns1] = newServerConfig.value(config_key::dns1);
serverConfig[config_key::dns2] = newServerConfig.value(config_key::dns2);
serverConfig[config_key::containers] = newServerConfig.value(config_key::containers);
serverConfig[config_key::hostName] = newServerConfig.value(config_key::hostName);
if (newServerConfig.value(config_key::configVersion).toInt() == apiDefs::ConfigSource::AmneziaGateway) {
serverConfig[config_key::configVersion] = newServerConfig.value(config_key::configVersion);
serverConfig[config_key::description] = newServerConfig.value(config_key::description);
serverConfig[config_key::name] = newServerConfig.value(config_key::name);
}
auto defaultContainer = newServerConfig.value(config_key::defaultContainer).toString();
serverConfig[config_key::defaultContainer] = defaultContainer;
QVariantMap map = serverConfig.value(configKey::apiConfig).toObject().toVariantMap();
map.insert(newServerConfig.value(configKey::apiConfig).toObject().toVariantMap());
auto apiConfig = QJsonObject::fromVariantMap(map);
if (newServerConfig.value(config_key::configVersion).toInt() == apiDefs::ConfigSource::AmneziaGateway) {
apiConfig.insert(apiDefs::key::supportedProtocols,
QJsonDocument::fromJson(apiResponseBody).object().value(apiDefs::key::supportedProtocols).toArray());
}
serverConfig[configKey::apiConfig] = apiConfig;
return ErrorCode::NoError;
}
} }
ApiConfigsController::ApiConfigsController(const QSharedPointer<ServersModel> &serversModel, ApiConfigsController::ApiConfigsController(const QSharedPointer<ServersModel> &serversModel,
@ -239,26 +63,24 @@ bool ApiConfigsController::exportNativeConfig(const QString &serverCountryCode,
return false; return false;
} }
GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), apiDefs::requestTimeoutMsecs,
m_settings->isStrictKillSwitchEnabled());
auto serverConfigObject = m_serversModel->getServerConfig(m_serversModel->getProcessedServerIndex()); auto serverConfigObject = m_serversModel->getServerConfig(m_serversModel->getProcessedServerIndex());
auto apiConfigObject = serverConfigObject.value(configKey::apiConfig).toObject(); auto apiConfigObject = serverConfigObject.value(configKey::apiConfig).toObject();
GatewayRequestData gatewayRequestData { QSysInfo::productType(),
QString(APP_VERSION),
m_settings->getInstallationUuid(true),
apiConfigObject.value(configKey::userCountryCode).toString(),
serverCountryCode,
apiConfigObject.value(configKey::serviceType).toString(),
m_apiServicesModel->getSelectedServiceProtocol(),
serverConfigObject.value(configKey::authData).toObject() };
QString protocol = apiConfigObject.value(configKey::serviceProtocol).toString(); QString protocol = apiConfigObject.value(configKey::serviceProtocol).toString();
ProtocolData protocolData = generateProtocolData(protocol); ApiPayloadData apiPayloadData = generateApiPayloadData(protocol);
QJsonObject apiPayload = gatewayRequestData.toJsonObject(); QJsonObject apiPayload = fillApiPayload(protocol, apiPayloadData);
appendProtocolDataToApiPayload(gatewayRequestData.serviceProtocol, protocolData, apiPayload); apiPayload[configKey::userCountryCode] = apiConfigObject.value(configKey::userCountryCode);
apiPayload[configKey::serverCountryCode] = serverCountryCode;
apiPayload[configKey::serviceType] = apiConfigObject.value(configKey::serviceType);
apiPayload[configKey::authData] = serverConfigObject.value(configKey::authData);
apiPayload[apiDefs::key::cliVersion] = QString(APP_VERSION);
QByteArray responseBody; QByteArray responseBody;
ErrorCode errorCode = executeRequest(QString("%1v1/native_config"), apiPayload, responseBody); ErrorCode errorCode = gatewayController.post(QString("%1v1/native_config"), apiPayload, responseBody);
if (errorCode != ErrorCode::NoError) { if (errorCode != ErrorCode::NoError) {
emit errorOccurred(errorCode); emit errorOccurred(errorCode);
return false; return false;
@ -266,7 +88,7 @@ bool ApiConfigsController::exportNativeConfig(const QString &serverCountryCode,
QJsonObject jsonConfig = QJsonDocument::fromJson(responseBody).object(); QJsonObject jsonConfig = QJsonDocument::fromJson(responseBody).object();
QString nativeConfig = jsonConfig.value(configKey::config).toString(); QString nativeConfig = jsonConfig.value(configKey::config).toString();
nativeConfig.replace("$WIREGUARD_CLIENT_PRIVATE_KEY", protocolData.wireGuardClientPrivKey); nativeConfig.replace("$WIREGUARD_CLIENT_PRIVATE_KEY", apiPayloadData.wireGuardClientPrivKey);
SystemController::saveFile(fileName, nativeConfig); SystemController::saveFile(fileName, nativeConfig);
return true; return true;
@ -274,22 +96,24 @@ bool ApiConfigsController::exportNativeConfig(const QString &serverCountryCode,
bool ApiConfigsController::revokeNativeConfig(const QString &serverCountryCode) bool ApiConfigsController::revokeNativeConfig(const QString &serverCountryCode)
{ {
GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), apiDefs::requestTimeoutMsecs,
m_settings->isStrictKillSwitchEnabled());
auto serverConfigObject = m_serversModel->getServerConfig(m_serversModel->getProcessedServerIndex()); auto serverConfigObject = m_serversModel->getServerConfig(m_serversModel->getProcessedServerIndex());
auto apiConfigObject = serverConfigObject.value(configKey::apiConfig).toObject(); auto apiConfigObject = serverConfigObject.value(configKey::apiConfig).toObject();
GatewayRequestData gatewayRequestData { QSysInfo::productType(), QString protocol = apiConfigObject.value(configKey::serviceProtocol).toString();
QString(APP_VERSION), ApiPayloadData apiPayloadData = generateApiPayloadData(protocol);
m_settings->getInstallationUuid(true),
apiConfigObject.value(configKey::userCountryCode).toString(),
serverCountryCode,
apiConfigObject.value(configKey::serviceType).toString(),
m_apiServicesModel->getSelectedServiceProtocol(),
serverConfigObject.value(configKey::authData).toObject() };
QJsonObject apiPayload = gatewayRequestData.toJsonObject(); QJsonObject apiPayload = fillApiPayload(protocol, apiPayloadData);
apiPayload[configKey::userCountryCode] = apiConfigObject.value(configKey::userCountryCode);
apiPayload[configKey::serverCountryCode] = serverCountryCode;
apiPayload[configKey::serviceType] = apiConfigObject.value(configKey::serviceType);
apiPayload[configKey::authData] = serverConfigObject.value(configKey::authData);
apiPayload[apiDefs::key::cliVersion] = QString(APP_VERSION);
QByteArray responseBody; QByteArray responseBody;
ErrorCode errorCode = executeRequest(QString("%1v1/revoke_native_config"), apiPayload, responseBody); ErrorCode errorCode = gatewayController.post(QString("%1v1/revoke_native_config"), apiPayload, responseBody);
if (errorCode != ErrorCode::NoError && errorCode != ErrorCode::ApiNotFoundError) { if (errorCode != ErrorCode::NoError && errorCode != ErrorCode::ApiNotFoundError) {
emit errorOccurred(errorCode); emit errorOccurred(errorCode);
return false; return false;
@ -320,11 +144,14 @@ void ApiConfigsController::copyVpnKeyToClipboard()
bool ApiConfigsController::fillAvailableServices() bool ApiConfigsController::fillAvailableServices()
{ {
GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), apiDefs::requestTimeoutMsecs,
m_settings->isStrictKillSwitchEnabled());
QJsonObject apiPayload; QJsonObject apiPayload;
apiPayload[configKey::osVersion] = QSysInfo::productType(); apiPayload[configKey::osVersion] = QSysInfo::productType();
QByteArray responseBody; QByteArray responseBody;
ErrorCode errorCode = executeRequest(QString("%1v1/services"), apiPayload, responseBody); ErrorCode errorCode = gatewayController.post(QString("%1v1/services"), apiPayload, responseBody);
if (errorCode == ErrorCode::NoError) { if (errorCode == ErrorCode::NoError) {
if (!responseBody.contains("services")) { if (!responseBody.contains("services")) {
errorCode = ErrorCode::ApiServicesMissingError; errorCode = ErrorCode::ApiServicesMissingError;
@ -343,36 +170,34 @@ bool ApiConfigsController::fillAvailableServices()
bool ApiConfigsController::importServiceFromGateway() bool ApiConfigsController::importServiceFromGateway()
{ {
GatewayRequestData gatewayRequestData { QSysInfo::productType(), if (m_serversModel->isServerFromApiAlreadyExists(m_apiServicesModel->getCountryCode(), m_apiServicesModel->getSelectedServiceType(),
QString(APP_VERSION), m_apiServicesModel->getSelectedServiceProtocol())) {
m_settings->getInstallationUuid(true),
m_apiServicesModel->getCountryCode(),
"",
m_apiServicesModel->getSelectedServiceType(),
m_apiServicesModel->getSelectedServiceProtocol(),
QJsonObject() };
if (m_serversModel->isServerFromApiAlreadyExists(gatewayRequestData.userCountryCode, gatewayRequestData.serviceType,
gatewayRequestData.serviceProtocol)) {
emit errorOccurred(ErrorCode::ApiConfigAlreadyAdded); emit errorOccurred(ErrorCode::ApiConfigAlreadyAdded);
return false; return false;
} }
ProtocolData protocolData = generateProtocolData(gatewayRequestData.serviceProtocol); GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), apiDefs::requestTimeoutMsecs,
m_settings->isStrictKillSwitchEnabled());
QJsonObject apiPayload = gatewayRequestData.toJsonObject(); auto installationUuid = m_settings->getInstallationUuid(true);
appendProtocolDataToApiPayload(gatewayRequestData.serviceProtocol, protocolData, apiPayload); auto userCountryCode = m_apiServicesModel->getCountryCode();
auto serviceType = m_apiServicesModel->getSelectedServiceType();
auto serviceProtocol = m_apiServicesModel->getSelectedServiceProtocol();
ApiPayloadData apiPayloadData = generateApiPayloadData(serviceProtocol);
QJsonObject apiPayload = fillApiPayload(serviceProtocol, apiPayloadData);
apiPayload[configKey::userCountryCode] = userCountryCode;
apiPayload[configKey::serviceType] = serviceType;
apiPayload[configKey::uuid] = installationUuid;
apiPayload[apiDefs::key::cliVersion] = QString(APP_VERSION);
QByteArray responseBody; QByteArray responseBody;
ErrorCode errorCode = executeRequest(QString("%1v1/config"), apiPayload, responseBody); ErrorCode errorCode = gatewayController.post(QString("%1v1/config"), apiPayload, responseBody);
QJsonObject serverConfig; QJsonObject serverConfig;
if (errorCode == ErrorCode::NoError) { if (errorCode == ErrorCode::NoError) {
errorCode = fillServerConfig(gatewayRequestData.serviceProtocol, protocolData, responseBody, serverConfig); fillServerConfig(serviceProtocol, apiPayloadData, responseBody, serverConfig);
if (errorCode != ErrorCode::NoError) {
emit errorOccurred(errorCode);
return false;
}
QJsonObject apiConfig = serverConfig.value(configKey::apiConfig).toObject(); QJsonObject apiConfig = serverConfig.value(configKey::apiConfig).toObject();
apiConfig.insert(configKey::userCountryCode, m_apiServicesModel->getCountryCode()); apiConfig.insert(configKey::userCountryCode, m_apiServicesModel->getCountryCode());
@ -393,33 +218,39 @@ bool ApiConfigsController::importServiceFromGateway()
bool ApiConfigsController::updateServiceFromGateway(const int serverIndex, const QString &newCountryCode, const QString &newCountryName, bool ApiConfigsController::updateServiceFromGateway(const int serverIndex, const QString &newCountryCode, const QString &newCountryName,
bool reloadServiceConfig) bool reloadServiceConfig)
{ {
GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), apiDefs::requestTimeoutMsecs,
m_settings->isStrictKillSwitchEnabled());
auto serverConfig = m_serversModel->getServerConfig(serverIndex); auto serverConfig = m_serversModel->getServerConfig(serverIndex);
auto apiConfig = serverConfig.value(configKey::apiConfig).toObject(); auto apiConfig = serverConfig.value(configKey::apiConfig).toObject();
auto authData = serverConfig.value(configKey::authData).toObject();
GatewayRequestData gatewayRequestData { QSysInfo::productType(), auto installationUuid = m_settings->getInstallationUuid(true);
QString(APP_VERSION), auto userCountryCode = apiConfig.value(configKey::userCountryCode).toString();
m_settings->getInstallationUuid(true), auto serviceType = apiConfig.value(configKey::serviceType).toString();
apiConfig.value(configKey::userCountryCode).toString(), auto serviceProtocol = apiConfig.value(configKey::serviceProtocol).toString();
newCountryCode,
apiConfig.value(configKey::serviceType).toString(),
apiConfig.value(configKey::serviceProtocol).toString(),
serverConfig.value(configKey::authData).toObject() };
ProtocolData protocolData = generateProtocolData(gatewayRequestData.serviceProtocol); ApiPayloadData apiPayloadData = generateApiPayloadData(serviceProtocol);
QJsonObject apiPayload = gatewayRequestData.toJsonObject(); QJsonObject apiPayload = fillApiPayload(serviceProtocol, apiPayloadData);
appendProtocolDataToApiPayload(gatewayRequestData.serviceProtocol, protocolData, apiPayload); apiPayload[configKey::userCountryCode] = userCountryCode;
apiPayload[configKey::serviceType] = serviceType;
apiPayload[configKey::uuid] = installationUuid;
apiPayload[apiDefs::key::cliVersion] = QString(APP_VERSION);
if (!newCountryCode.isEmpty()) {
apiPayload[configKey::serverCountryCode] = newCountryCode;
}
if (!authData.isEmpty()) {
apiPayload[configKey::authData] = authData;
}
QByteArray responseBody; QByteArray responseBody;
ErrorCode errorCode = executeRequest(QString("%1v1/config"), apiPayload, responseBody); ErrorCode errorCode = gatewayController.post(QString("%1v1/config"), apiPayload, responseBody);
QJsonObject newServerConfig; QJsonObject newServerConfig;
if (errorCode == ErrorCode::NoError) { if (errorCode == ErrorCode::NoError) {
errorCode = fillServerConfig(gatewayRequestData.serviceProtocol, protocolData, responseBody, newServerConfig); fillServerConfig(serviceProtocol, apiPayloadData, responseBody, newServerConfig);
if (errorCode != ErrorCode::NoError) {
emit errorOccurred(errorCode);
return false;
}
QJsonObject newApiConfig = newServerConfig.value(configKey::apiConfig).toObject(); QJsonObject newApiConfig = newServerConfig.value(configKey::apiConfig).toObject();
newApiConfig.insert(configKey::userCountryCode, apiConfig.value(configKey::userCountryCode)); newApiConfig.insert(configKey::userCountryCode, apiConfig.value(configKey::userCountryCode));
@ -428,7 +259,7 @@ bool ApiConfigsController::updateServiceFromGateway(const int serverIndex, const
newApiConfig.insert(apiDefs::key::vpnKey, apiConfig.value(apiDefs::key::vpnKey)); newApiConfig.insert(apiDefs::key::vpnKey, apiConfig.value(apiDefs::key::vpnKey));
newServerConfig.insert(configKey::apiConfig, newApiConfig); newServerConfig.insert(configKey::apiConfig, newApiConfig);
newServerConfig.insert(configKey::authData, gatewayRequestData.authData); newServerConfig.insert(configKey::authData, authData);
if (serverConfig.value(config_key::nameOverriddenByUser).toBool()) { if (serverConfig.value(config_key::nameOverriddenByUser).toBool()) {
newServerConfig.insert(config_key::name, serverConfig.value(config_key::name)); newServerConfig.insert(config_key::name, serverConfig.value(config_key::name));
@ -463,13 +294,10 @@ bool ApiConfigsController::updateServiceFromTelegram(const int serverIndex)
auto installationUuid = m_settings->getInstallationUuid(true); auto installationUuid = m_settings->getInstallationUuid(true);
QString serviceProtocol = serverConfig.value(configKey::protocol).toString(); QString serviceProtocol = serverConfig.value(configKey::protocol).toString();
ProtocolData protocolData = generateProtocolData(serviceProtocol); ApiPayloadData apiPayloadData = generateApiPayloadData(serviceProtocol);
QJsonObject apiPayload; QJsonObject apiPayload = fillApiPayload(serviceProtocol, apiPayloadData);
appendProtocolDataToApiPayload(serviceProtocol, protocolData, apiPayload);
apiPayload[configKey::uuid] = installationUuid; apiPayload[configKey::uuid] = installationUuid;
apiPayload[configKey::osVersion] = QSysInfo::productType();
apiPayload[configKey::appVersion] = QString(APP_VERSION);
apiPayload[configKey::accessToken] = serverConfig.value(configKey::accessToken).toString(); apiPayload[configKey::accessToken] = serverConfig.value(configKey::accessToken).toString();
apiPayload[configKey::apiEndpoint] = serverConfig.value(configKey::apiEndpoint).toString(); apiPayload[configKey::apiEndpoint] = serverConfig.value(configKey::apiEndpoint).toString();
@ -477,11 +305,7 @@ bool ApiConfigsController::updateServiceFromTelegram(const int serverIndex)
ErrorCode errorCode = gatewayController.post(QString("%1v1/proxy_config"), apiPayload, responseBody); ErrorCode errorCode = gatewayController.post(QString("%1v1/proxy_config"), apiPayload, responseBody);
if (errorCode == ErrorCode::NoError) { if (errorCode == ErrorCode::NoError) {
errorCode = fillServerConfig(serviceProtocol, protocolData, responseBody, serverConfig); fillServerConfig(serviceProtocol, apiPayloadData, responseBody, serverConfig);
if (errorCode != ErrorCode::NoError) {
emit errorOccurred(errorCode);
return false;
}
m_serversModel->editServer(serverConfig, serverIndex); m_serversModel->editServer(serverConfig, serverIndex);
emit updateServerFromApiFinished(); emit updateServerFromApiFinished();
@ -494,6 +318,9 @@ bool ApiConfigsController::updateServiceFromTelegram(const int serverIndex)
bool ApiConfigsController::deactivateDevice() bool ApiConfigsController::deactivateDevice()
{ {
GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), apiDefs::requestTimeoutMsecs,
m_settings->isStrictKillSwitchEnabled());
auto serverIndex = m_serversModel->getProcessedServerIndex(); auto serverIndex = m_serversModel->getProcessedServerIndex();
auto serverConfigObject = m_serversModel->getServerConfig(serverIndex); auto serverConfigObject = m_serversModel->getServerConfig(serverIndex);
auto apiConfigObject = serverConfigObject.value(configKey::apiConfig).toObject(); auto apiConfigObject = serverConfigObject.value(configKey::apiConfig).toObject();
@ -502,19 +329,19 @@ bool ApiConfigsController::deactivateDevice()
return true; return true;
} }
GatewayRequestData gatewayRequestData { QSysInfo::productType(), QString protocol = apiConfigObject.value(configKey::serviceProtocol).toString();
QString(APP_VERSION), ApiPayloadData apiPayloadData = generateApiPayloadData(protocol);
m_settings->getInstallationUuid(true),
apiConfigObject.value(configKey::userCountryCode).toString(),
apiConfigObject.value(configKey::serverCountryCode).toString(),
apiConfigObject.value(configKey::serviceType).toString(),
"",
serverConfigObject.value(configKey::authData).toObject() };
QJsonObject apiPayload = gatewayRequestData.toJsonObject(); QJsonObject apiPayload = fillApiPayload(protocol, apiPayloadData);
apiPayload[configKey::userCountryCode] = apiConfigObject.value(configKey::userCountryCode);
apiPayload[configKey::serverCountryCode] = apiConfigObject.value(configKey::serverCountryCode);
apiPayload[configKey::serviceType] = apiConfigObject.value(configKey::serviceType);
apiPayload[configKey::authData] = serverConfigObject.value(configKey::authData);
apiPayload[configKey::uuid] = m_settings->getInstallationUuid(true);
apiPayload[apiDefs::key::cliVersion] = QString(APP_VERSION);
QByteArray responseBody; QByteArray responseBody;
ErrorCode errorCode = executeRequest(QString("%1v1/revoke_config"), apiPayload, responseBody); ErrorCode errorCode = gatewayController.post(QString("%1v1/revoke_config"), apiPayload, responseBody);
if (errorCode != ErrorCode::NoError && errorCode != ErrorCode::ApiNotFoundError) { if (errorCode != ErrorCode::NoError && errorCode != ErrorCode::ApiNotFoundError) {
emit errorOccurred(errorCode); emit errorOccurred(errorCode);
return false; return false;
@ -528,6 +355,9 @@ bool ApiConfigsController::deactivateDevice()
bool ApiConfigsController::deactivateExternalDevice(const QString &uuid, const QString &serverCountryCode) bool ApiConfigsController::deactivateExternalDevice(const QString &uuid, const QString &serverCountryCode)
{ {
GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), apiDefs::requestTimeoutMsecs,
m_settings->isStrictKillSwitchEnabled());
auto serverIndex = m_serversModel->getProcessedServerIndex(); auto serverIndex = m_serversModel->getProcessedServerIndex();
auto serverConfigObject = m_serversModel->getServerConfig(serverIndex); auto serverConfigObject = m_serversModel->getServerConfig(serverIndex);
auto apiConfigObject = serverConfigObject.value(configKey::apiConfig).toObject(); auto apiConfigObject = serverConfigObject.value(configKey::apiConfig).toObject();
@ -536,19 +366,19 @@ bool ApiConfigsController::deactivateExternalDevice(const QString &uuid, const Q
return true; return true;
} }
GatewayRequestData gatewayRequestData { QSysInfo::productType(), QString protocol = apiConfigObject.value(configKey::serviceProtocol).toString();
QString(APP_VERSION), ApiPayloadData apiPayloadData = generateApiPayloadData(protocol);
uuid,
apiConfigObject.value(configKey::userCountryCode).toString(),
serverCountryCode,
apiConfigObject.value(configKey::serviceType).toString(),
"",
serverConfigObject.value(configKey::authData).toObject() };
QJsonObject apiPayload = gatewayRequestData.toJsonObject(); QJsonObject apiPayload = fillApiPayload(protocol, apiPayloadData);
apiPayload[configKey::userCountryCode] = apiConfigObject.value(configKey::userCountryCode);
apiPayload[configKey::serverCountryCode] = serverCountryCode;
apiPayload[configKey::serviceType] = apiConfigObject.value(configKey::serviceType);
apiPayload[configKey::authData] = serverConfigObject.value(configKey::authData);
apiPayload[configKey::uuid] = uuid;
apiPayload[apiDefs::key::cliVersion] = QString(APP_VERSION);
QByteArray responseBody; QByteArray responseBody;
ErrorCode errorCode = executeRequest(QString("%1v1/revoke_config"), apiPayload, responseBody); ErrorCode errorCode = gatewayController.post(QString("%1v1/revoke_config"), apiPayload, responseBody);
if (errorCode != ErrorCode::NoError && errorCode != ErrorCode::ApiNotFoundError) { if (errorCode != ErrorCode::NoError && errorCode != ErrorCode::ApiNotFoundError) {
emit errorOccurred(errorCode); emit errorOccurred(errorCode);
return false; return false;
@ -587,29 +417,108 @@ bool ApiConfigsController::isConfigValid()
return true; return true;
} }
void ApiConfigsController::setCurrentProtocol(const QString &protocolName) ApiConfigsController::ApiPayloadData ApiConfigsController::generateApiPayloadData(const QString &protocol)
{ {
auto serverIndex = m_serversModel->getProcessedServerIndex(); ApiConfigsController::ApiPayloadData apiPayload;
auto serverConfigObject = m_serversModel->getServerConfig(serverIndex); if (protocol == configKey::cloak) {
auto apiConfigObject = serverConfigObject.value(configKey::apiConfig).toObject(); apiPayload.certRequest = OpenVpnConfigurator::createCertRequest();
} else if (protocol == configKey::awg) {
apiConfigObject[configKey::serviceProtocol] = protocolName; auto connData = WireguardConfigurator::genClientKeys();
apiPayload.wireGuardClientPubKey = connData.clientPubKey;
serverConfigObject.insert(configKey::apiConfig, apiConfigObject); apiPayload.wireGuardClientPrivKey = connData.clientPrivKey;
}
m_serversModel->editServer(serverConfigObject, serverIndex); return apiPayload;
} }
bool ApiConfigsController::isVlessProtocol() QJsonObject ApiConfigsController::fillApiPayload(const QString &protocol, const ApiPayloadData &apiPayloadData)
{ {
auto serverIndex = m_serversModel->getProcessedServerIndex(); QJsonObject obj;
auto serverConfigObject = m_serversModel->getServerConfig(serverIndex); if (protocol == configKey::cloak) {
auto apiConfigObject = serverConfigObject.value(configKey::apiConfig).toObject(); obj[configKey::certificate] = apiPayloadData.certRequest.request;
} else if (protocol == configKey::awg) {
if (apiConfigObject[configKey::serviceProtocol].toString() == "vless") { obj[configKey::publicKey] = apiPayloadData.wireGuardClientPubKey;
return true;
} }
return false;
obj[configKey::osVersion] = QSysInfo::productType();
obj[configKey::appVersion] = QString(APP_VERSION);
return obj;
}
void ApiConfigsController::fillServerConfig(const QString &protocol, const ApiPayloadData &apiPayloadData,
const QByteArray &apiResponseBody, QJsonObject &serverConfig)
{
QString data = QJsonDocument::fromJson(apiResponseBody).object().value(config_key::config).toString();
data.replace("vpn://", "");
QByteArray ba = QByteArray::fromBase64(data.toUtf8(), QByteArray::Base64UrlEncoding | QByteArray::OmitTrailingEquals);
if (ba.isEmpty()) {
emit errorOccurred(ErrorCode::ApiConfigEmptyError);
return;
}
QByteArray ba_uncompressed = qUncompress(ba);
if (!ba_uncompressed.isEmpty()) {
ba = ba_uncompressed;
}
QString configStr = ba;
if (protocol == configKey::cloak) {
configStr.replace("<key>", "<key>\n");
configStr.replace("$OPENVPN_PRIV_KEY", apiPayloadData.certRequest.privKey);
} else if (protocol == configKey::awg) {
configStr.replace("$WIREGUARD_CLIENT_PRIVATE_KEY", apiPayloadData.wireGuardClientPrivKey);
auto newServerConfig = QJsonDocument::fromJson(configStr.toUtf8()).object();
auto containers = newServerConfig.value(config_key::containers).toArray();
if (containers.isEmpty()) {
return; // todo process error
}
auto container = containers.at(0).toObject();
QString containerName = ContainerProps::containerTypeToString(DockerContainer::Awg);
auto containerConfig = container.value(containerName).toObject();
auto protocolConfig = QJsonDocument::fromJson(containerConfig.value(config_key::last_config).toString().toUtf8()).object();
containerConfig[config_key::junkPacketCount] = protocolConfig.value(config_key::junkPacketCount);
containerConfig[config_key::junkPacketMinSize] = protocolConfig.value(config_key::junkPacketMinSize);
containerConfig[config_key::junkPacketMaxSize] = protocolConfig.value(config_key::junkPacketMaxSize);
containerConfig[config_key::initPacketJunkSize] = protocolConfig.value(config_key::initPacketJunkSize);
containerConfig[config_key::responsePacketJunkSize] = protocolConfig.value(config_key::responsePacketJunkSize);
containerConfig[config_key::initPacketMagicHeader] = protocolConfig.value(config_key::initPacketMagicHeader);
containerConfig[config_key::responsePacketMagicHeader] = protocolConfig.value(config_key::responsePacketMagicHeader);
containerConfig[config_key::underloadPacketMagicHeader] = protocolConfig.value(config_key::underloadPacketMagicHeader);
containerConfig[config_key::transportPacketMagicHeader] = protocolConfig.value(config_key::transportPacketMagicHeader);
container[containerName] = containerConfig;
containers.replace(0, container);
newServerConfig[config_key::containers] = containers;
configStr = QString(QJsonDocument(newServerConfig).toJson());
}
QJsonObject newServerConfig = QJsonDocument::fromJson(configStr.toUtf8()).object();
serverConfig[config_key::dns1] = newServerConfig.value(config_key::dns1);
serverConfig[config_key::dns2] = newServerConfig.value(config_key::dns2);
serverConfig[config_key::containers] = newServerConfig.value(config_key::containers);
serverConfig[config_key::hostName] = newServerConfig.value(config_key::hostName);
if (newServerConfig.value(config_key::configVersion).toInt() == apiDefs::ConfigSource::AmneziaGateway) {
serverConfig[config_key::configVersion] = newServerConfig.value(config_key::configVersion);
serverConfig[config_key::description] = newServerConfig.value(config_key::description);
serverConfig[config_key::name] = newServerConfig.value(config_key::name);
}
auto defaultContainer = newServerConfig.value(config_key::defaultContainer).toString();
serverConfig[config_key::defaultContainer] = defaultContainer;
QVariantMap map = serverConfig.value(configKey::apiConfig).toObject().toVariantMap();
map.insert(newServerConfig.value(configKey::apiConfig).toObject().toVariantMap());
auto apiConfig = QJsonObject::fromVariantMap(map);
if (newServerConfig.value(config_key::configVersion).toInt() == apiDefs::ConfigSource::AmneziaGateway) {
apiConfig.insert(configKey::serviceInfo, QJsonDocument::fromJson(apiResponseBody).object().value(configKey::serviceInfo).toObject());
}
serverConfig[configKey::apiConfig] = apiConfig;
return;
} }
QList<QString> ApiConfigsController::getQrCodes() QList<QString> ApiConfigsController::getQrCodes()
@ -626,10 +535,3 @@ QString ApiConfigsController::getVpnKey()
{ {
return m_vpnKey; return m_vpnKey;
} }
ErrorCode ApiConfigsController::executeRequest(const QString &endpoint, const QJsonObject &apiPayload, QByteArray &responseBody)
{
GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), apiDefs::requestTimeoutMsecs,
m_settings->isStrictKillSwitchEnabled());
return gatewayController.post(endpoint, apiPayload, responseBody);
}

View file

@ -35,9 +35,6 @@ public slots:
bool isConfigValid(); bool isConfigValid();
void setCurrentProtocol(const QString &protocolName);
bool isVlessProtocol();
signals: signals:
void errorOccurred(ErrorCode errorCode); void errorOccurred(ErrorCode errorCode);
@ -49,12 +46,23 @@ signals:
void vpnKeyExportReady(); void vpnKeyExportReady();
private: private:
struct ApiPayloadData
{
OpenVpnConfigurator::ConnectionData certRequest;
QString wireGuardClientPrivKey;
QString wireGuardClientPubKey;
};
ApiPayloadData generateApiPayloadData(const QString &protocol);
QJsonObject fillApiPayload(const QString &protocol, const ApiPayloadData &apiPayloadData);
void fillServerConfig(const QString &protocol, const ApiPayloadData &apiPayloadData, const QByteArray &apiResponseBody,
QJsonObject &serverConfig);
QList<QString> getQrCodes(); QList<QString> getQrCodes();
int getQrCodesCount(); int getQrCodesCount();
QString getVpnKey(); QString getVpnKey();
ErrorCode executeRequest(const QString &endpoint, const QJsonObject &apiPayload, QByteArray &responseBody);
QList<QString> m_qrCodes; QList<QString> m_qrCodes;
QString m_vpnKey; QString m_vpnKey;

View file

@ -12,7 +12,6 @@
#include "core/errorstrings.h" #include "core/errorstrings.h"
#include "core/qrCodeUtils.h" #include "core/qrCodeUtils.h"
#include "core/serialization/serialization.h" #include "core/serialization/serialization.h"
#include "protocols/protocols_defs.h"
#include "systemController.h" #include "systemController.h"
#include "utilities.h" #include "utilities.h"
@ -287,19 +286,6 @@ void ImportController::processNativeWireGuardConfig()
clientProtocolConfig[config_key::underloadPacketMagicHeader] = "3"; clientProtocolConfig[config_key::underloadPacketMagicHeader] = "3";
clientProtocolConfig[config_key::transportPacketMagicHeader] = "4"; clientProtocolConfig[config_key::transportPacketMagicHeader] = "4";
// clientProtocolConfig[config_key::cookieReplyPacketJunkSize] = "0";
// clientProtocolConfig[config_key::transportPacketJunkSize] = "0";
// clientProtocolConfig[config_key::specialJunk1] = "";
// clientProtocolConfig[config_key::specialJunk2] = "";
// clientProtocolConfig[config_key::specialJunk3] = "";
// clientProtocolConfig[config_key::specialJunk4] = "";
// clientProtocolConfig[config_key::specialJunk5] = "";
// clientProtocolConfig[config_key::controlledJunk1] = "";
// clientProtocolConfig[config_key::controlledJunk2] = "";
// clientProtocolConfig[config_key::controlledJunk3] = "";
// clientProtocolConfig[config_key::specialHandshakeTimeout] = "0";
clientProtocolConfig[config_key::isObfuscationEnabled] = true; clientProtocolConfig[config_key::isObfuscationEnabled] = true;
serverProtocolConfig[config_key::last_config] = QString(QJsonDocument(clientProtocolConfig).toJson()); serverProtocolConfig[config_key::last_config] = QString(QJsonDocument(clientProtocolConfig).toJson());
@ -452,33 +438,21 @@ QJsonObject ImportController::extractWireGuardConfig(const QString &data)
lastConfig[config_key::allowed_ips] = allowedIpsJsonArray; lastConfig[config_key::allowed_ips] = allowedIpsJsonArray;
QString protocolName = "wireguard"; QString protocolName = "wireguard";
if (!configMap.value(config_key::junkPacketCount).isEmpty() && !configMap.value(config_key::junkPacketMinSize).isEmpty()
const QStringList requiredJunkFields = { config_key::junkPacketCount, config_key::junkPacketMinSize, && !configMap.value(config_key::junkPacketMaxSize).isEmpty() && !configMap.value(config_key::initPacketJunkSize).isEmpty()
config_key::junkPacketMaxSize, config_key::initPacketJunkSize, && !configMap.value(config_key::responsePacketJunkSize).isEmpty() && !configMap.value(config_key::initPacketMagicHeader).isEmpty()
config_key::responsePacketJunkSize, config_key::initPacketMagicHeader, && !configMap.value(config_key::responsePacketMagicHeader).isEmpty()
config_key::responsePacketMagicHeader, config_key::underloadPacketMagicHeader, && !configMap.value(config_key::underloadPacketMagicHeader).isEmpty()
config_key::transportPacketMagicHeader }; && !configMap.value(config_key::transportPacketMagicHeader).isEmpty()) {
lastConfig[config_key::junkPacketCount] = configMap.value(config_key::junkPacketCount);
const QStringList optionalJunkFields = { // config_key::cookieReplyPacketJunkSize, lastConfig[config_key::junkPacketMinSize] = configMap.value(config_key::junkPacketMinSize);
// config_key::transportPacketJunkSize, lastConfig[config_key::junkPacketMaxSize] = configMap.value(config_key::junkPacketMaxSize);
config_key::specialJunk1, config_key::specialJunk2, config_key::specialJunk3, lastConfig[config_key::initPacketJunkSize] = configMap.value(config_key::initPacketJunkSize);
config_key::specialJunk4, config_key::specialJunk5, config_key::controlledJunk1, lastConfig[config_key::responsePacketJunkSize] = configMap.value(config_key::responsePacketJunkSize);
config_key::controlledJunk2, config_key::controlledJunk3, config_key::specialHandshakeTimeout lastConfig[config_key::initPacketMagicHeader] = configMap.value(config_key::initPacketMagicHeader);
}; lastConfig[config_key::responsePacketMagicHeader] = configMap.value(config_key::responsePacketMagicHeader);
lastConfig[config_key::underloadPacketMagicHeader] = configMap.value(config_key::underloadPacketMagicHeader);
bool hasAllRequiredFields = std::all_of(requiredJunkFields.begin(), requiredJunkFields.end(), lastConfig[config_key::transportPacketMagicHeader] = configMap.value(config_key::transportPacketMagicHeader);
[&configMap](const QString &field) { return !configMap.value(field).isEmpty(); });
if (hasAllRequiredFields) {
for (const QString &field : requiredJunkFields) {
lastConfig[field] = configMap.value(field);
}
for (const QString &field : optionalJunkFields) {
if (!configMap.value(field).isEmpty()) {
lastConfig[field] = configMap.value(field);
}
}
protocolName = "awg"; protocolName = "awg";
m_configType = ConfigTypes::Awg; m_configType = ConfigTypes::Awg;
} }

View file

@ -8,7 +8,6 @@
#include <QStandardPaths> #include <QStandardPaths>
#include <QtConcurrent> #include <QtConcurrent>
#include "core/api/apiUtils.h"
#include "core/controllers/serverController.h" #include "core/controllers/serverController.h"
#include "core/controllers/vpnConfigurationController.h" #include "core/controllers/vpnConfigurationController.h"
#include "core/networkUtilities.h" #include "core/networkUtilities.h"
@ -16,6 +15,7 @@
#include "ui/models/protocols/awgConfigModel.h" #include "ui/models/protocols/awgConfigModel.h"
#include "ui/models/protocols/wireguardConfigModel.h" #include "ui/models/protocols/wireguardConfigModel.h"
#include "utilities.h" #include "utilities.h"
#include "core/api/apiUtils.h"
namespace namespace
{ {
@ -79,36 +79,12 @@ void InstallController::install(DockerContainer container, int port, TransportPr
int s1 = QRandomGenerator::global()->bounded(15, 150); int s1 = QRandomGenerator::global()->bounded(15, 150);
int s2 = QRandomGenerator::global()->bounded(15, 150); int s2 = QRandomGenerator::global()->bounded(15, 150);
// int s3 = QRandomGenerator::global()->bounded(15, 150); while (s1 + AwgConstant::messageInitiationSize == s2 + AwgConstant::messageResponseSize) {
// int s4 = QRandomGenerator::global()->bounded(15, 150);
// Ensure all values are unique and don't create equal packet sizes
QSet<int> usedValues;
usedValues.insert(s1);
while (usedValues.contains(s2) || s1 + AwgConstant::messageInitiationSize == s2 + AwgConstant::messageResponseSize) {
s2 = QRandomGenerator::global()->bounded(15, 150); s2 = QRandomGenerator::global()->bounded(15, 150);
} }
usedValues.insert(s2);
// while (usedValues.contains(s3)
// || s1 + AwgConstant::messageInitiationSize == s3 + AwgConstant::messageCookieReplySize
// || s2 + AwgConstant::messageResponseSize == s3 + AwgConstant::messageCookieReplySize) {
// s3 = QRandomGenerator::global()->bounded(15, 150);
// }
// usedValues.insert(s3);
// while (usedValues.contains(s4)
// || s1 + AwgConstant::messageInitiationSize == s4 + AwgConstant::messageTransportSize
// || s2 + AwgConstant::messageResponseSize == s4 + AwgConstant::messageTransportSize
// || s3 + AwgConstant::messageCookieReplySize == s4 + AwgConstant::messageTransportSize) {
// s4 = QRandomGenerator::global()->bounded(15, 150);
// }
QString initPacketJunkSize = QString::number(s1); QString initPacketJunkSize = QString::number(s1);
QString responsePacketJunkSize = QString::number(s2); QString responsePacketJunkSize = QString::number(s2);
// QString cookieReplyPacketJunkSize = QString::number(s3);
// QString transportPacketJunkSize = QString::number(s4);
QSet<QString> headersValue; QSet<QString> headersValue;
while (headersValue.size() != 4) { while (headersValue.size() != 4) {
@ -132,21 +108,6 @@ void InstallController::install(DockerContainer container, int port, TransportPr
containerConfig[config_key::responsePacketMagicHeader] = responsePacketMagicHeader; containerConfig[config_key::responsePacketMagicHeader] = responsePacketMagicHeader;
containerConfig[config_key::underloadPacketMagicHeader] = underloadPacketMagicHeader; containerConfig[config_key::underloadPacketMagicHeader] = underloadPacketMagicHeader;
containerConfig[config_key::transportPacketMagicHeader] = transportPacketMagicHeader; containerConfig[config_key::transportPacketMagicHeader] = transportPacketMagicHeader;
// TODO:
// containerConfig[config_key::cookieReplyPacketJunkSize] = cookieReplyPacketJunkSize;
// containerConfig[config_key::transportPacketJunkSize] = transportPacketJunkSize;
// containerConfig[config_key::specialJunk1] = specialJunk1;
// containerConfig[config_key::specialJunk2] = specialJunk2;
// containerConfig[config_key::specialJunk3] = specialJunk3;
// containerConfig[config_key::specialJunk4] = specialJunk4;
// containerConfig[config_key::specialJunk5] = specialJunk5;
// containerConfig[config_key::controlledJunk1] = controlledJunk1;
// containerConfig[config_key::controlledJunk2] = controlledJunk2;
// containerConfig[config_key::controlledJunk3] = controlledJunk3;
// containerConfig[config_key::specialHandshakeTimeout] = specialHandshakeTimeout;
} else if (container == DockerContainer::Sftp) { } else if (container == DockerContainer::Sftp) {
containerConfig.insert(config_key::userName, protocols::sftp::defaultUserName); containerConfig.insert(config_key::userName, protocols::sftp::defaultUserName);
containerConfig.insert(config_key::password, Utils::getRandomString(16)); containerConfig.insert(config_key::password, Utils::getRandomString(16));
@ -440,19 +401,6 @@ ErrorCode InstallController::getAlreadyInstalledContainers(const ServerCredentia
containerConfig[config_key::transportPacketMagicHeader] = containerConfig[config_key::transportPacketMagicHeader] =
serverConfigMap.value(config_key::transportPacketMagicHeader); serverConfigMap.value(config_key::transportPacketMagicHeader);
// containerConfig[config_key::cookieReplyPacketJunkSize] = serverConfigMap.value(config_key::cookieReplyPacketJunkSize);
// containerConfig[config_key::transportPacketJunkSize] = serverConfigMap.value(config_key::transportPacketJunkSize);
// containerConfig[config_key::specialJunk1] = serverConfigMap.value(config_key::specialJunk1);
// containerConfig[config_key::specialJunk2] = serverConfigMap.value(config_key::specialJunk2);
// containerConfig[config_key::specialJunk3] = serverConfigMap.value(config_key::specialJunk3);
// containerConfig[config_key::specialJunk4] = serverConfigMap.value(config_key::specialJunk4);
// containerConfig[config_key::specialJunk5] = serverConfigMap.value(config_key::specialJunk5);
// containerConfig[config_key::controlledJunk1] = serverConfigMap.value(config_key::controlledJunk1);
// containerConfig[config_key::controlledJunk2] = serverConfigMap.value(config_key::controlledJunk2);
// containerConfig[config_key::controlledJunk3] = serverConfigMap.value(config_key::controlledJunk3);
// containerConfig[config_key::specialHandshakeTimeout] = serverConfigMap.value(config_key::specialHandshakeTimeout);
} else if (protocol == Proto::WireGuard) { } else if (protocol == Proto::WireGuard) {
QString serverConfig = serverController->getTextFileFromContainer(container, credentials, QString serverConfig = serverController->getTextFileFromContainer(container, credentials,
protocols::wireguard::serverConfigPath, errorCode); protocols::wireguard::serverConfigPath, errorCode);

View file

@ -1,309 +1,311 @@
#include "listViewFocusController.h" #include "listViewFocusController.h"
#include "utils/qmlUtils.h" #include "utils/qmlUtils.h"
#include <QQuickWindow> #include <QQuickWindow>
ListViewFocusController::ListViewFocusController(QQuickItem *listView, QObject *parent) ListViewFocusController::ListViewFocusController(QQuickItem *listView, QObject *parent)
: QObject { parent }, : QObject { parent },
m_listView { listView }, m_listView { listView },
m_focusChain {}, m_focusChain {},
m_currentSection { Section::Default }, m_currentSection { Section::Default },
m_header { nullptr }, m_header { nullptr },
m_footer { nullptr }, m_footer { nullptr },
m_focusedItem { nullptr }, m_focusedItem { nullptr },
m_focusedItemIndex { -1 }, m_focusedItemIndex { -1 },
m_delegateIndex { 0 }, m_delegateIndex { 0 },
m_isReturnNeeded { false }, m_isReturnNeeded { false },
m_currentSectionString { "Default", "Header", "Delegate", "Footer" } m_currentSectionString { "Default", "Header", "Delegate", "Footer" }
{ {
QVariant headerItemProperty = m_listView->property("headerItem"); QVariant headerItemProperty = m_listView->property("headerItem");
m_header = headerItemProperty.canConvert<QQuickItem *>() ? headerItemProperty.value<QQuickItem *>() : nullptr; m_header = headerItemProperty.canConvert<QQuickItem *>() ? headerItemProperty.value<QQuickItem *>() : nullptr;
QVariant footerItemProperty = m_listView->property("footerItem"); QVariant footerItemProperty = m_listView->property("footerItem");
m_footer = footerItemProperty.canConvert<QQuickItem *>() ? footerItemProperty.value<QQuickItem *>() : nullptr; m_footer = footerItemProperty.canConvert<QQuickItem *>() ? footerItemProperty.value<QQuickItem *>() : nullptr;
} }
ListViewFocusController::~ListViewFocusController() ListViewFocusController::~ListViewFocusController()
{ {
} }
void ListViewFocusController::viewAtCurrentIndex() const void ListViewFocusController::viewAtCurrentIndex() const
{ {
switch (m_currentSection) { switch (m_currentSection) {
case Section::Default: [[fallthrough]]; case Section::Default: [[fallthrough]];
case Section::Header: { case Section::Header: {
QMetaObject::invokeMethod(m_listView, "positionViewAtBeginning"); QMetaObject::invokeMethod(m_listView, "positionViewAtBeginning");
break; break;
} }
case Section::Delegate: { case Section::Delegate: {
QMetaObject::invokeMethod(m_listView, "positionViewAtIndex", Q_ARG(int, m_delegateIndex), // Index QMetaObject::invokeMethod(m_listView, "positionViewAtIndex", Q_ARG(int, m_delegateIndex), // Index
Q_ARG(int, 2)); // PositionMode (0 = Visible) Q_ARG(int, 6)); // PositionMode (0 = Beginning; 1 = Center; 2 = End; 3 = Visible; 4 = Contain; 5 = SnapPosition)
break; break;
} }
case Section::Footer: { case Section::Footer: {
QMetaObject::invokeMethod(m_listView, "positionViewAtEnd"); QMetaObject::invokeMethod(m_listView, "positionViewAtEnd");
break; break;
} }
} }
} }
int ListViewFocusController::size() const int ListViewFocusController::size() const
{ {
return m_listView->property("count").toInt(); return m_listView->property("count").toInt();
} }
int ListViewFocusController::currentIndex() const int ListViewFocusController::currentIndex() const
{ {
return m_delegateIndex; return m_delegateIndex;
} }
void ListViewFocusController::setDelegateIndex(int index) void ListViewFocusController::setDelegateIndex(int index)
{ {
m_delegateIndex = index; m_delegateIndex = index;
m_listView->setProperty("currentIndex", index); m_listView->setProperty("currentIndex", index);
} }
void ListViewFocusController::nextDelegate() void ListViewFocusController::nextDelegate()
{ {
switch (m_currentSection) { switch (m_currentSection) {
case Section::Default: { case Section::Default: {
if (hasHeader()) { if (hasHeader()) {
m_currentSection = Section::Header; m_currentSection = Section::Header;
viewAtCurrentIndex(); viewAtCurrentIndex();
break; break;
} }
[[fallthrough]]; [[fallthrough]];
} }
case Section::Header: { case Section::Header: {
if (size() > 0) { if (size() > 0) {
m_currentSection = Section::Delegate; m_currentSection = Section::Delegate;
viewAtCurrentIndex(); viewAtCurrentIndex();
break; break;
} }
[[fallthrough]]; [[fallthrough]];
} }
case Section::Delegate: case Section::Delegate:
if (m_delegateIndex < (size() - 1)) { if (m_delegateIndex < (size() - 1)) {
setDelegateIndex(m_delegateIndex + 1); setDelegateIndex(m_delegateIndex + 1);
viewAtCurrentIndex(); viewAtCurrentIndex();
break; break;
} else if (hasFooter()) { } else if (hasFooter()) {
m_currentSection = Section::Footer; m_currentSection = Section::Footer;
viewAtCurrentIndex(); viewAtCurrentIndex();
break; break;
} }
[[fallthrough]]; [[fallthrough]];
case Section::Footer: { case Section::Footer: {
m_isReturnNeeded = true; m_isReturnNeeded = true;
m_currentSection = Section::Default; m_currentSection = Section::Default;
viewAtCurrentIndex(); viewAtCurrentIndex();
break; break;
} }
default: { default: {
qCritical() << "Current section is invalid!"; qCritical() << "Current section is invalid!";
break; break;
} }
} }
} }
void ListViewFocusController::previousDelegate() void ListViewFocusController::previousDelegate()
{ {
switch (m_currentSection) { switch (m_currentSection) {
case Section::Default: { case Section::Default: {
if (hasFooter()) { if (hasFooter()) {
m_currentSection = Section::Footer; m_currentSection = Section::Footer;
break; break;
} }
[[fallthrough]]; [[fallthrough]];
} }
case Section::Footer: { case Section::Footer: {
if (size() > 0) { if (size() > 0) {
m_currentSection = Section::Delegate; m_currentSection = Section::Delegate;
setDelegateIndex(size() - 1); setDelegateIndex(size() - 1);
break; break;
} }
[[fallthrough]]; [[fallthrough]];
} }
case Section::Delegate: { case Section::Delegate: {
if (m_delegateIndex > 0) { if (m_delegateIndex > 0) {
setDelegateIndex(m_delegateIndex - 1); setDelegateIndex(m_delegateIndex - 1);
break; break;
} else if (hasHeader()) { } else if (hasHeader()) {
m_currentSection = Section::Header; m_currentSection = Section::Header;
break; break;
} }
[[fallthrough]]; [[fallthrough]];
} }
case Section::Header: { case Section::Header: {
m_isReturnNeeded = true; m_isReturnNeeded = true;
m_currentSection = Section::Default; m_currentSection = Section::Default;
break; break;
} }
default: { default: {
qCritical() << "Current section is invalid!"; qCritical() << "Current section is invalid!";
break; break;
} }
} }
} }
void ListViewFocusController::decrementIndex() void ListViewFocusController::decrementIndex()
{ {
m_delegateIndex--; m_delegateIndex--;
} }
QQuickItem *ListViewFocusController::itemAtIndex(const int index) const QQuickItem *ListViewFocusController::itemAtIndex(const int index) const
{ {
QQuickItem *item { nullptr }; QQuickItem *item { nullptr };
QMetaObject::invokeMethod(m_listView, "itemAtIndex", Q_RETURN_ARG(QQuickItem *, item), Q_ARG(int, index)); QMetaObject::invokeMethod(m_listView, "itemAtIndex", Q_RETURN_ARG(QQuickItem *, item), Q_ARG(int, index));
return item; return item;
} }
QQuickItem *ListViewFocusController::currentDelegate() const QQuickItem *ListViewFocusController::currentDelegate() const
{ {
QQuickItem *result { nullptr }; QQuickItem *result { nullptr };
switch (m_currentSection) { switch (m_currentSection) {
case Section::Default: { case Section::Default: {
qWarning() << "No elements..."; qWarning() << "No elements...";
break; break;
} }
case Section::Header: { case Section::Header: {
result = m_header; result = m_header;
break; break;
} }
case Section::Delegate: { case Section::Delegate: {
result = itemAtIndex(m_delegateIndex); result = itemAtIndex(m_delegateIndex);
break; break;
} }
case Section::Footer: { case Section::Footer: {
result = m_footer; result = m_footer;
break; break;
} }
} }
return result; return result;
} }
QQuickItem *ListViewFocusController::focusedItem() const QQuickItem *ListViewFocusController::focusedItem() const
{ {
return m_focusedItem; return m_focusedItem;
} }
void ListViewFocusController::focusNextItem() void ListViewFocusController::focusNextItem()
{ {
if (m_isReturnNeeded) { if (m_isReturnNeeded) {
return; return;
} }
reloadFocusChain(); reloadFocusChain();
if (m_focusChain.empty()) { if (m_focusChain.empty()) {
qWarning() << "No elements found in the delegate. Going to next delegate..."; qWarning() << "No elements found in the delegate. Going to next delegate...";
nextDelegate(); nextDelegate();
focusNextItem(); focusNextItem();
return; return;
} }
m_focusedItemIndex++; m_focusedItemIndex++;
m_focusedItem = qobject_cast<QQuickItem *>(m_focusChain.at(m_focusedItemIndex)); m_focusedItem = qobject_cast<QQuickItem *>(m_focusChain.at(m_focusedItemIndex));
m_focusedItem->forceActiveFocus(Qt::TabFocusReason); m_focusedItem->forceActiveFocus(Qt::TabFocusReason);
} qDebug() << "Next focus is set to item: " << m_focusedItem;
}
void ListViewFocusController::focusPreviousItem()
{ void ListViewFocusController::focusPreviousItem()
if (m_isReturnNeeded) { {
return; if (m_isReturnNeeded) {
} return;
}
if (m_focusChain.empty()) {
qInfo() << "Empty focusChain with current delegate: " << currentDelegate() << "Scanning for elements..."; if (m_focusChain.empty()) {
reloadFocusChain(); qInfo() << "Empty focusChain with current delegate: " << currentDelegate() << "Scanning for elements...";
} reloadFocusChain();
if (m_focusChain.empty()) { }
qWarning() << "No elements found in the delegate. Going to next delegate..."; if (m_focusChain.empty()) {
previousDelegate(); qWarning() << "No elements found in the delegate. Going to next delegate...";
focusPreviousItem(); previousDelegate();
return; focusPreviousItem();
} return;
if (m_focusedItemIndex == -1) { }
m_focusedItemIndex = m_focusChain.size(); if (m_focusedItemIndex == -1) {
} m_focusedItemIndex = m_focusChain.size();
m_focusedItemIndex--; }
m_focusedItem = qobject_cast<QQuickItem *>(m_focusChain.at(m_focusedItemIndex)); m_focusedItemIndex--;
m_focusedItem->forceActiveFocus(Qt::TabFocusReason); m_focusedItem = qobject_cast<QQuickItem *>(m_focusChain.at(m_focusedItemIndex));
} m_focusedItem->forceActiveFocus(Qt::TabFocusReason);
}
void ListViewFocusController::resetFocusChain()
{ void ListViewFocusController::resetFocusChain()
m_focusChain.clear(); {
m_focusedItem = nullptr; m_focusChain.clear();
m_focusedItemIndex = -1; m_focusedItem = nullptr;
} m_focusedItemIndex = -1;
qDebug() << "Focus chain was reset";
void ListViewFocusController::reloadFocusChain() }
{
m_focusChain = FocusControl::getItemsChain(currentDelegate()); void ListViewFocusController::reloadFocusChain()
} {
m_focusChain = FocusControl::getItemsChain(currentDelegate());
bool ListViewFocusController::isFirstFocusItemInDelegate() const }
{
return m_focusedItem && (m_focusedItem == m_focusChain.first()); bool ListViewFocusController::isFirstFocusItemInDelegate() const
} {
return m_focusedItem && (m_focusedItem == m_focusChain.first());
bool ListViewFocusController::isLastFocusItemInDelegate() const }
{
return m_focusedItem && (m_focusedItem == m_focusChain.last()); bool ListViewFocusController::isLastFocusItemInDelegate() const
} {
return m_focusedItem && (m_focusedItem == m_focusChain.last());
bool ListViewFocusController::hasHeader() const }
{
return m_header && !FocusControl::getItemsChain(m_header).isEmpty(); bool ListViewFocusController::hasHeader() const
} {
return m_header && !FocusControl::getItemsChain(m_header).isEmpty();
bool ListViewFocusController::hasFooter() const }
{
return m_footer && !FocusControl::getItemsChain(m_footer).isEmpty(); bool ListViewFocusController::hasFooter() const
} {
return m_footer && !FocusControl::getItemsChain(m_footer).isEmpty();
bool ListViewFocusController::isFirstFocusItemInListView() const }
{
switch (m_currentSection) { bool ListViewFocusController::isFirstFocusItemInListView() const
case Section::Footer: { {
return isFirstFocusItemInDelegate() && !hasHeader() && (size() == 0); switch (m_currentSection) {
} case Section::Footer: {
case Section::Delegate: { return isFirstFocusItemInDelegate() && !hasHeader() && (size() == 0);
return isFirstFocusItemInDelegate() && (m_delegateIndex == 0) && !hasHeader(); }
} case Section::Delegate: {
case Section::Header: { return isFirstFocusItemInDelegate() && (m_delegateIndex == 0) && !hasHeader();
isFirstFocusItemInDelegate(); }
} case Section::Header: {
case Section::Default: { isFirstFocusItemInDelegate();
return true; }
} case Section::Default: {
default: qWarning() << "Wrong section"; return true; return true;
} }
} default: qWarning() << "Wrong section"; return true;
}
bool ListViewFocusController::isLastFocusItemInListView() const }
{
switch (m_currentSection) { bool ListViewFocusController::isLastFocusItemInListView() const
case Section::Default: { {
return !hasHeader() && (size() == 0) && !hasFooter(); switch (m_currentSection) {
} case Section::Default: {
case Section::Header: { return !hasHeader() && (size() == 0) && !hasFooter();
return isLastFocusItemInDelegate() && (size() == 0) && !hasFooter(); }
} case Section::Header: {
case Section::Delegate: { return isLastFocusItemInDelegate() && (size() == 0) && !hasFooter();
return isLastFocusItemInDelegate() && (m_delegateIndex == size() - 1) && !hasFooter(); }
} case Section::Delegate: {
case Section::Footer: { return isLastFocusItemInDelegate() && (m_delegateIndex == size() - 1) && !hasFooter();
return isLastFocusItemInDelegate(); }
} case Section::Footer: {
default: qWarning() << "Wrong section"; return true; return isLastFocusItemInDelegate();
} }
} default: qWarning() << "Wrong section"; return true;
}
bool ListViewFocusController::isReturnNeeded() const }
{
return m_isReturnNeeded; bool ListViewFocusController::isReturnNeeded() const
} {
return m_isReturnNeeded;
}

View file

@ -75,12 +75,6 @@ QVariant ApiAccountInfoModel::data(const QModelIndex &index, int role) const
} }
return false; return false;
} }
case IsProtocolSelectionSupportedRole: {
if (m_accountInfoData.supportedProtocols.size() > 1) {
return true;
}
return false;
}
} }
return QVariant(); return QVariant();
@ -101,10 +95,6 @@ void ApiAccountInfoModel::updateModel(const QJsonObject &accountInfoObject, cons
accountInfoData.configType = apiUtils::getConfigType(serverConfig); accountInfoData.configType = apiUtils::getConfigType(serverConfig);
for (const auto &protocol : accountInfoObject.value(apiDefs::key::supportedProtocols).toArray()) {
accountInfoData.supportedProtocols.push_back(protocol.toString());
}
m_accountInfoData = accountInfoData; m_accountInfoData = accountInfoData;
m_supportInfo = accountInfoObject.value(apiDefs::key::supportInfo).toObject(); m_supportInfo = accountInfoObject.value(apiDefs::key::supportInfo).toObject();
@ -169,7 +159,6 @@ QHash<int, QByteArray> ApiAccountInfoModel::roleNames() const
roles[ServiceDescriptionRole] = "serviceDescription"; roles[ServiceDescriptionRole] = "serviceDescription";
roles[IsComponentVisibleRole] = "isComponentVisible"; roles[IsComponentVisibleRole] = "isComponentVisible";
roles[HasExpiredWorkerRole] = "hasExpiredWorker"; roles[HasExpiredWorkerRole] = "hasExpiredWorker";
roles[IsProtocolSelectionSupportedRole] = "isProtocolSelectionSupported";
return roles; return roles;
} }

View file

@ -18,8 +18,7 @@ public:
ServiceDescriptionRole, ServiceDescriptionRole,
EndDateRole, EndDateRole,
IsComponentVisibleRole, IsComponentVisibleRole,
HasExpiredWorkerRole, HasExpiredWorkerRole
IsProtocolSelectionSupportedRole
}; };
explicit ApiAccountInfoModel(QObject *parent = nullptr); explicit ApiAccountInfoModel(QObject *parent = nullptr);
@ -52,8 +51,6 @@ private:
int maxDeviceCount; int maxDeviceCount;
apiDefs::ConfigType configType; apiDefs::ConfigType configType;
QStringList supportedProtocols;
}; };
AccountInfoData m_accountInfoData; AccountInfoData m_accountInfoData;

View file

@ -28,17 +28,7 @@ bool AwgConfigModel::setData(const QModelIndex &index, const QVariant &value, in
case Roles::ClientJunkPacketCountRole: m_clientProtocolConfig.insert(config_key::junkPacketCount, value.toString()); break; case Roles::ClientJunkPacketCountRole: m_clientProtocolConfig.insert(config_key::junkPacketCount, value.toString()); break;
case Roles::ClientJunkPacketMinSizeRole: m_clientProtocolConfig.insert(config_key::junkPacketMinSize, value.toString()); break; case Roles::ClientJunkPacketMinSizeRole: m_clientProtocolConfig.insert(config_key::junkPacketMinSize, value.toString()); break;
case Roles::ClientJunkPacketMaxSizeRole: m_clientProtocolConfig.insert(config_key::junkPacketMaxSize, value.toString()); break; case Roles::ClientJunkPacketMaxSizeRole: m_clientProtocolConfig.insert(config_key::junkPacketMaxSize, value.toString()); break;
case Roles::ClientSpecialJunk1Role: m_clientProtocolConfig.insert(config_key::specialJunk1, value.toString()); break;
case Roles::ClientSpecialJunk2Role: m_clientProtocolConfig.insert(config_key::specialJunk2, value.toString()); break;
case Roles::ClientSpecialJunk3Role: m_clientProtocolConfig.insert(config_key::specialJunk3, value.toString()); break;
case Roles::ClientSpecialJunk4Role: m_clientProtocolConfig.insert(config_key::specialJunk4, value.toString()); break;
case Roles::ClientSpecialJunk5Role: m_clientProtocolConfig.insert(config_key::specialJunk5, value.toString()); break;
case Roles::ClientControlledJunk1Role: m_clientProtocolConfig.insert(config_key::controlledJunk1, value.toString()); break;
case Roles::ClientControlledJunk2Role: m_clientProtocolConfig.insert(config_key::controlledJunk2, value.toString()); break;
case Roles::ClientControlledJunk3Role: m_clientProtocolConfig.insert(config_key::controlledJunk3, value.toString()); break;
case Roles::ClientSpecialHandshakeTimeoutRole:
m_clientProtocolConfig.insert(config_key::specialHandshakeTimeout, value.toString());
break;
case Roles::ServerJunkPacketCountRole: m_serverProtocolConfig.insert(config_key::junkPacketCount, value.toString()); break; case Roles::ServerJunkPacketCountRole: m_serverProtocolConfig.insert(config_key::junkPacketCount, value.toString()); break;
case Roles::ServerJunkPacketMinSizeRole: m_serverProtocolConfig.insert(config_key::junkPacketMinSize, value.toString()); break; case Roles::ServerJunkPacketMinSizeRole: m_serverProtocolConfig.insert(config_key::junkPacketMinSize, value.toString()); break;
case Roles::ServerJunkPacketMaxSizeRole: m_serverProtocolConfig.insert(config_key::junkPacketMaxSize, value.toString()); break; case Roles::ServerJunkPacketMaxSizeRole: m_serverProtocolConfig.insert(config_key::junkPacketMaxSize, value.toString()); break;
@ -46,12 +36,6 @@ bool AwgConfigModel::setData(const QModelIndex &index, const QVariant &value, in
case Roles::ServerResponsePacketJunkSizeRole: case Roles::ServerResponsePacketJunkSizeRole:
m_serverProtocolConfig.insert(config_key::responsePacketJunkSize, value.toString()); m_serverProtocolConfig.insert(config_key::responsePacketJunkSize, value.toString());
break; break;
// case Roles::ServerCookieReplyPacketJunkSizeRole:
// m_serverProtocolConfig.insert(config_key::cookieReplyPacketJunkSize, value.toString());
// break;
// case Roles::ServerTransportPacketJunkSizeRole:
// m_serverProtocolConfig.insert(config_key::transportPacketJunkSize, value.toString());
// break;
case Roles::ServerInitPacketMagicHeaderRole: m_serverProtocolConfig.insert(config_key::initPacketMagicHeader, value.toString()); break; case Roles::ServerInitPacketMagicHeaderRole: m_serverProtocolConfig.insert(config_key::initPacketMagicHeader, value.toString()); break;
case Roles::ServerResponsePacketMagicHeaderRole: case Roles::ServerResponsePacketMagicHeaderRole:
m_serverProtocolConfig.insert(config_key::responsePacketMagicHeader, value.toString()); m_serverProtocolConfig.insert(config_key::responsePacketMagicHeader, value.toString());
@ -82,23 +66,12 @@ QVariant AwgConfigModel::data(const QModelIndex &index, int role) const
case Roles::ClientJunkPacketCountRole: return m_clientProtocolConfig.value(config_key::junkPacketCount); case Roles::ClientJunkPacketCountRole: return m_clientProtocolConfig.value(config_key::junkPacketCount);
case Roles::ClientJunkPacketMinSizeRole: return m_clientProtocolConfig.value(config_key::junkPacketMinSize); case Roles::ClientJunkPacketMinSizeRole: return m_clientProtocolConfig.value(config_key::junkPacketMinSize);
case Roles::ClientJunkPacketMaxSizeRole: return m_clientProtocolConfig.value(config_key::junkPacketMaxSize); case Roles::ClientJunkPacketMaxSizeRole: return m_clientProtocolConfig.value(config_key::junkPacketMaxSize);
case Roles::ClientSpecialJunk1Role: return m_clientProtocolConfig.value(config_key::specialJunk1);
case Roles::ClientSpecialJunk2Role: return m_clientProtocolConfig.value(config_key::specialJunk2);
case Roles::ClientSpecialJunk3Role: return m_clientProtocolConfig.value(config_key::specialJunk3);
case Roles::ClientSpecialJunk4Role: return m_clientProtocolConfig.value(config_key::specialJunk4);
case Roles::ClientSpecialJunk5Role: return m_clientProtocolConfig.value(config_key::specialJunk5);
case Roles::ClientControlledJunk1Role: return m_clientProtocolConfig.value(config_key::controlledJunk1);
case Roles::ClientControlledJunk2Role: return m_clientProtocolConfig.value(config_key::controlledJunk2);
case Roles::ClientControlledJunk3Role: return m_clientProtocolConfig.value(config_key::controlledJunk3);
case Roles::ClientSpecialHandshakeTimeoutRole: return m_clientProtocolConfig.value(config_key::specialHandshakeTimeout);
case Roles::ServerJunkPacketCountRole: return m_serverProtocolConfig.value(config_key::junkPacketCount); case Roles::ServerJunkPacketCountRole: return m_serverProtocolConfig.value(config_key::junkPacketCount);
case Roles::ServerJunkPacketMinSizeRole: return m_serverProtocolConfig.value(config_key::junkPacketMinSize); case Roles::ServerJunkPacketMinSizeRole: return m_serverProtocolConfig.value(config_key::junkPacketMinSize);
case Roles::ServerJunkPacketMaxSizeRole: return m_serverProtocolConfig.value(config_key::junkPacketMaxSize); case Roles::ServerJunkPacketMaxSizeRole: return m_serverProtocolConfig.value(config_key::junkPacketMaxSize);
case Roles::ServerInitPacketJunkSizeRole: return m_serverProtocolConfig.value(config_key::initPacketJunkSize); case Roles::ServerInitPacketJunkSizeRole: return m_serverProtocolConfig.value(config_key::initPacketJunkSize);
case Roles::ServerResponsePacketJunkSizeRole: return m_serverProtocolConfig.value(config_key::responsePacketJunkSize); case Roles::ServerResponsePacketJunkSizeRole: return m_serverProtocolConfig.value(config_key::responsePacketJunkSize);
// case Roles::ServerCookieReplyPacketJunkSizeRole: return m_serverProtocolConfig.value(config_key::cookieReplyPacketJunkSize);
// case Roles::ServerTransportPacketJunkSizeRole: return m_serverProtocolConfig.value(config_key::transportPacketJunkSize);
case Roles::ServerInitPacketMagicHeaderRole: return m_serverProtocolConfig.value(config_key::initPacketMagicHeader); case Roles::ServerInitPacketMagicHeaderRole: return m_serverProtocolConfig.value(config_key::initPacketMagicHeader);
case Roles::ServerResponsePacketMagicHeaderRole: return m_serverProtocolConfig.value(config_key::responsePacketMagicHeader); case Roles::ServerResponsePacketMagicHeaderRole: return m_serverProtocolConfig.value(config_key::responsePacketMagicHeader);
case Roles::ServerUnderloadPacketMagicHeaderRole: return m_serverProtocolConfig.value(config_key::underloadPacketMagicHeader); case Roles::ServerUnderloadPacketMagicHeaderRole: return m_serverProtocolConfig.value(config_key::underloadPacketMagicHeader);
@ -121,8 +94,7 @@ void AwgConfigModel::updateModel(const QJsonObject &config)
m_serverProtocolConfig.insert(config_key::transport_proto, m_serverProtocolConfig.insert(config_key::transport_proto,
serverProtocolConfig.value(config_key::transport_proto).toString(defaultTransportProto)); serverProtocolConfig.value(config_key::transport_proto).toString(defaultTransportProto));
m_serverProtocolConfig[config_key::last_config] = serverProtocolConfig.value(config_key::last_config); m_serverProtocolConfig[config_key::last_config] = serverProtocolConfig.value(config_key::last_config);
m_serverProtocolConfig[config_key::subnet_address] = m_serverProtocolConfig[config_key::subnet_address] = serverProtocolConfig.value(config_key::subnet_address).toString(protocols::wireguard::defaultSubnetAddress);
serverProtocolConfig.value(config_key::subnet_address).toString(protocols::wireguard::defaultSubnetAddress);
m_serverProtocolConfig[config_key::port] = serverProtocolConfig.value(config_key::port).toString(protocols::awg::defaultPort); m_serverProtocolConfig[config_key::port] = serverProtocolConfig.value(config_key::port).toString(protocols::awg::defaultPort);
m_serverProtocolConfig[config_key::junkPacketCount] = m_serverProtocolConfig[config_key::junkPacketCount] =
serverProtocolConfig.value(config_key::junkPacketCount).toString(protocols::awg::defaultJunkPacketCount); serverProtocolConfig.value(config_key::junkPacketCount).toString(protocols::awg::defaultJunkPacketCount);
@ -134,10 +106,6 @@ void AwgConfigModel::updateModel(const QJsonObject &config)
serverProtocolConfig.value(config_key::initPacketJunkSize).toString(protocols::awg::defaultInitPacketJunkSize); serverProtocolConfig.value(config_key::initPacketJunkSize).toString(protocols::awg::defaultInitPacketJunkSize);
m_serverProtocolConfig[config_key::responsePacketJunkSize] = m_serverProtocolConfig[config_key::responsePacketJunkSize] =
serverProtocolConfig.value(config_key::responsePacketJunkSize).toString(protocols::awg::defaultResponsePacketJunkSize); serverProtocolConfig.value(config_key::responsePacketJunkSize).toString(protocols::awg::defaultResponsePacketJunkSize);
// m_serverProtocolConfig[config_key::cookieReplyPacketJunkSize] =
// serverProtocolConfig.value(config_key::cookieReplyPacketJunkSize).toString(protocols::awg::defaultCookieReplyPacketJunkSize);
// m_serverProtocolConfig[config_key::transportPacketJunkSize] =
// serverProtocolConfig.value(config_key::transportPacketJunkSize).toString(protocols::awg::defaultTransportPacketJunkSize);
m_serverProtocolConfig[config_key::initPacketMagicHeader] = m_serverProtocolConfig[config_key::initPacketMagicHeader] =
serverProtocolConfig.value(config_key::initPacketMagicHeader).toString(protocols::awg::defaultInitPacketMagicHeader); serverProtocolConfig.value(config_key::initPacketMagicHeader).toString(protocols::awg::defaultInitPacketMagicHeader);
m_serverProtocolConfig[config_key::responsePacketMagicHeader] = m_serverProtocolConfig[config_key::responsePacketMagicHeader] =
@ -156,24 +124,6 @@ void AwgConfigModel::updateModel(const QJsonObject &config)
clientProtocolConfig.value(config_key::junkPacketMinSize).toString(m_serverProtocolConfig[config_key::junkPacketMinSize].toString()); clientProtocolConfig.value(config_key::junkPacketMinSize).toString(m_serverProtocolConfig[config_key::junkPacketMinSize].toString());
m_clientProtocolConfig[config_key::junkPacketMaxSize] = m_clientProtocolConfig[config_key::junkPacketMaxSize] =
clientProtocolConfig.value(config_key::junkPacketMaxSize).toString(m_serverProtocolConfig[config_key::junkPacketMaxSize].toString()); clientProtocolConfig.value(config_key::junkPacketMaxSize).toString(m_serverProtocolConfig[config_key::junkPacketMaxSize].toString());
m_clientProtocolConfig[config_key::specialJunk1] =
clientProtocolConfig.value(config_key::specialJunk1).toString(protocols::awg::defaultSpecialJunk1);
m_clientProtocolConfig[config_key::specialJunk2] =
clientProtocolConfig.value(config_key::specialJunk2).toString(protocols::awg::defaultSpecialJunk2);
m_clientProtocolConfig[config_key::specialJunk3] =
clientProtocolConfig.value(config_key::specialJunk3).toString(protocols::awg::defaultSpecialJunk3);
m_clientProtocolConfig[config_key::specialJunk4] =
clientProtocolConfig.value(config_key::specialJunk4).toString(protocols::awg::defaultSpecialJunk4);
m_clientProtocolConfig[config_key::specialJunk5] =
clientProtocolConfig.value(config_key::specialJunk5).toString(protocols::awg::defaultSpecialJunk5);
m_clientProtocolConfig[config_key::controlledJunk1] =
clientProtocolConfig.value(config_key::controlledJunk1).toString(protocols::awg::defaultControlledJunk1);
m_clientProtocolConfig[config_key::controlledJunk2] =
clientProtocolConfig.value(config_key::controlledJunk2).toString(protocols::awg::defaultControlledJunk2);
m_clientProtocolConfig[config_key::controlledJunk3] =
clientProtocolConfig.value(config_key::controlledJunk3).toString(protocols::awg::defaultControlledJunk3);
m_clientProtocolConfig[config_key::specialHandshakeTimeout] =
clientProtocolConfig.value(config_key::specialHandshakeTimeout).toString(protocols::awg::defaultSpecialHandshakeTimeout);
endResetModel(); endResetModel();
} }
@ -191,15 +141,6 @@ QJsonObject AwgConfigModel::getConfig()
jsonConfig[config_key::junkPacketCount] = m_clientProtocolConfig[config_key::junkPacketCount]; jsonConfig[config_key::junkPacketCount] = m_clientProtocolConfig[config_key::junkPacketCount];
jsonConfig[config_key::junkPacketMinSize] = m_clientProtocolConfig[config_key::junkPacketMinSize]; jsonConfig[config_key::junkPacketMinSize] = m_clientProtocolConfig[config_key::junkPacketMinSize];
jsonConfig[config_key::junkPacketMaxSize] = m_clientProtocolConfig[config_key::junkPacketMaxSize]; jsonConfig[config_key::junkPacketMaxSize] = m_clientProtocolConfig[config_key::junkPacketMaxSize];
jsonConfig[config_key::specialJunk1] = m_clientProtocolConfig[config_key::specialJunk1];
jsonConfig[config_key::specialJunk2] = m_clientProtocolConfig[config_key::specialJunk2];
jsonConfig[config_key::specialJunk3] = m_clientProtocolConfig[config_key::specialJunk3];
jsonConfig[config_key::specialJunk4] = m_clientProtocolConfig[config_key::specialJunk4];
jsonConfig[config_key::specialJunk5] = m_clientProtocolConfig[config_key::specialJunk5];
jsonConfig[config_key::controlledJunk1] = m_clientProtocolConfig[config_key::controlledJunk1];
jsonConfig[config_key::controlledJunk2] = m_clientProtocolConfig[config_key::controlledJunk2];
jsonConfig[config_key::controlledJunk3] = m_clientProtocolConfig[config_key::controlledJunk3];
jsonConfig[config_key::specialHandshakeTimeout] = m_clientProtocolConfig[config_key::specialHandshakeTimeout];
m_serverProtocolConfig[config_key::last_config] = QString(QJsonDocument(jsonConfig).toJson()); m_serverProtocolConfig[config_key::last_config] = QString(QJsonDocument(jsonConfig).toJson());
} }
@ -218,17 +159,6 @@ bool AwgConfigModel::isPacketSizeEqual(const int s1, const int s2)
return (AwgConstant::messageInitiationSize + s1 == AwgConstant::messageResponseSize + s2); return (AwgConstant::messageInitiationSize + s1 == AwgConstant::messageResponseSize + s2);
} }
// bool AwgConfigModel::isPacketSizeEqual(const int s1, const int s2, const int s3, const int s4)
// {
// int initSize = AwgConstant::messageInitiationSize + s1;
// int responseSize = AwgConstant::messageResponseSize + s2;
// int cookieSize = AwgConstant::messageCookieReplySize + s3;
// int transportSize = AwgConstant::messageTransportSize + s4;
// return (initSize == responseSize || initSize == cookieSize || initSize == transportSize || responseSize == cookieSize
// || responseSize == transportSize || cookieSize == transportSize);
// }
bool AwgConfigModel::isServerSettingsEqual() bool AwgConfigModel::isServerSettingsEqual()
{ {
const AwgConfig oldConfig(m_fullConfig.value(config_key::awg).toObject()); const AwgConfig oldConfig(m_fullConfig.value(config_key::awg).toObject());
@ -248,24 +178,12 @@ QHash<int, QByteArray> AwgConfigModel::roleNames() const
roles[ClientJunkPacketCountRole] = "clientJunkPacketCount"; roles[ClientJunkPacketCountRole] = "clientJunkPacketCount";
roles[ClientJunkPacketMinSizeRole] = "clientJunkPacketMinSize"; roles[ClientJunkPacketMinSizeRole] = "clientJunkPacketMinSize";
roles[ClientJunkPacketMaxSizeRole] = "clientJunkPacketMaxSize"; roles[ClientJunkPacketMaxSizeRole] = "clientJunkPacketMaxSize";
roles[ClientSpecialJunk1Role] = "clientSpecialJunk1";
roles[ClientSpecialJunk2Role] = "clientSpecialJunk2";
roles[ClientSpecialJunk3Role] = "clientSpecialJunk3";
roles[ClientSpecialJunk4Role] = "clientSpecialJunk4";
roles[ClientSpecialJunk5Role] = "clientSpecialJunk5";
roles[ClientControlledJunk1Role] = "clientControlledJunk1";
roles[ClientControlledJunk2Role] = "clientControlledJunk2";
roles[ClientControlledJunk3Role] = "clientControlledJunk3";
roles[ClientSpecialHandshakeTimeoutRole] = "clientSpecialHandshakeTimeout";
roles[ServerJunkPacketCountRole] = "serverJunkPacketCount"; roles[ServerJunkPacketCountRole] = "serverJunkPacketCount";
roles[ServerJunkPacketMinSizeRole] = "serverJunkPacketMinSize"; roles[ServerJunkPacketMinSizeRole] = "serverJunkPacketMinSize";
roles[ServerJunkPacketMaxSizeRole] = "serverJunkPacketMaxSize"; roles[ServerJunkPacketMaxSizeRole] = "serverJunkPacketMaxSize";
roles[ServerInitPacketJunkSizeRole] = "serverInitPacketJunkSize"; roles[ServerInitPacketJunkSizeRole] = "serverInitPacketJunkSize";
roles[ServerResponsePacketJunkSizeRole] = "serverResponsePacketJunkSize"; roles[ServerResponsePacketJunkSizeRole] = "serverResponsePacketJunkSize";
roles[ServerCookieReplyPacketJunkSizeRole] = "serverCookieReplyPacketJunkSize";
roles[ServerTransportPacketJunkSizeRole] = "serverTransportPacketJunkSize";
roles[ServerInitPacketMagicHeaderRole] = "serverInitPacketMagicHeader"; roles[ServerInitPacketMagicHeaderRole] = "serverInitPacketMagicHeader";
roles[ServerResponsePacketMagicHeaderRole] = "serverResponsePacketMagicHeader"; roles[ServerResponsePacketMagicHeaderRole] = "serverResponsePacketMagicHeader";
roles[ServerUnderloadPacketMagicHeaderRole] = "serverUnderloadPacketMagicHeader"; roles[ServerUnderloadPacketMagicHeaderRole] = "serverUnderloadPacketMagicHeader";
@ -282,16 +200,6 @@ AwgConfig::AwgConfig(const QJsonObject &serverProtocolConfig)
clientJunkPacketCount = clientProtocolConfig.value(config_key::junkPacketCount).toString(protocols::awg::defaultJunkPacketCount); clientJunkPacketCount = clientProtocolConfig.value(config_key::junkPacketCount).toString(protocols::awg::defaultJunkPacketCount);
clientJunkPacketMinSize = clientProtocolConfig.value(config_key::junkPacketMinSize).toString(protocols::awg::defaultJunkPacketMinSize); clientJunkPacketMinSize = clientProtocolConfig.value(config_key::junkPacketMinSize).toString(protocols::awg::defaultJunkPacketMinSize);
clientJunkPacketMaxSize = clientProtocolConfig.value(config_key::junkPacketMaxSize).toString(protocols::awg::defaultJunkPacketMaxSize); clientJunkPacketMaxSize = clientProtocolConfig.value(config_key::junkPacketMaxSize).toString(protocols::awg::defaultJunkPacketMaxSize);
clientSpecialJunk1 = clientProtocolConfig.value(config_key::specialJunk1).toString(protocols::awg::defaultSpecialJunk1);
clientSpecialJunk2 = clientProtocolConfig.value(config_key::specialJunk2).toString(protocols::awg::defaultSpecialJunk2);
clientSpecialJunk3 = clientProtocolConfig.value(config_key::specialJunk3).toString(protocols::awg::defaultSpecialJunk3);
clientSpecialJunk4 = clientProtocolConfig.value(config_key::specialJunk4).toString(protocols::awg::defaultSpecialJunk4);
clientSpecialJunk5 = clientProtocolConfig.value(config_key::specialJunk5).toString(protocols::awg::defaultSpecialJunk5);
clientControlledJunk1 = clientProtocolConfig.value(config_key::controlledJunk1).toString(protocols::awg::defaultControlledJunk1);
clientControlledJunk2 = clientProtocolConfig.value(config_key::controlledJunk2).toString(protocols::awg::defaultControlledJunk2);
clientControlledJunk3 = clientProtocolConfig.value(config_key::controlledJunk3).toString(protocols::awg::defaultControlledJunk3);
clientSpecialHandshakeTimeout =
clientProtocolConfig.value(config_key::specialHandshakeTimeout).toString(protocols::awg::defaultSpecialHandshakeTimeout);
subnetAddress = serverProtocolConfig.value(config_key::subnet_address).toString(protocols::wireguard::defaultSubnetAddress); subnetAddress = serverProtocolConfig.value(config_key::subnet_address).toString(protocols::wireguard::defaultSubnetAddress);
port = serverProtocolConfig.value(config_key::port).toString(protocols::awg::defaultPort); port = serverProtocolConfig.value(config_key::port).toString(protocols::awg::defaultPort);
@ -301,10 +209,6 @@ AwgConfig::AwgConfig(const QJsonObject &serverProtocolConfig)
serverInitPacketJunkSize = serverProtocolConfig.value(config_key::initPacketJunkSize).toString(protocols::awg::defaultInitPacketJunkSize); serverInitPacketJunkSize = serverProtocolConfig.value(config_key::initPacketJunkSize).toString(protocols::awg::defaultInitPacketJunkSize);
serverResponsePacketJunkSize = serverResponsePacketJunkSize =
serverProtocolConfig.value(config_key::responsePacketJunkSize).toString(protocols::awg::defaultResponsePacketJunkSize); serverProtocolConfig.value(config_key::responsePacketJunkSize).toString(protocols::awg::defaultResponsePacketJunkSize);
// serverCookieReplyPacketJunkSize =
// serverProtocolConfig.value(config_key::cookieReplyPacketJunkSize).toString(protocols::awg::defaultCookieReplyPacketJunkSize);
// serverTransportPacketJunkSize =
// serverProtocolConfig.value(config_key::transportPacketJunkSize).toString(protocols::awg::defaultTransportPacketJunkSize);
serverInitPacketMagicHeader = serverInitPacketMagicHeader =
serverProtocolConfig.value(config_key::initPacketMagicHeader).toString(protocols::awg::defaultInitPacketMagicHeader); serverProtocolConfig.value(config_key::initPacketMagicHeader).toString(protocols::awg::defaultInitPacketMagicHeader);
serverResponsePacketMagicHeader = serverResponsePacketMagicHeader =
@ -320,8 +224,6 @@ bool AwgConfig::hasEqualServerSettings(const AwgConfig &other) const
if (subnetAddress != other.subnetAddress || port != other.port || serverJunkPacketCount != other.serverJunkPacketCount if (subnetAddress != other.subnetAddress || port != other.port || serverJunkPacketCount != other.serverJunkPacketCount
|| serverJunkPacketMinSize != other.serverJunkPacketMinSize || serverJunkPacketMaxSize != other.serverJunkPacketMaxSize || serverJunkPacketMinSize != other.serverJunkPacketMinSize || serverJunkPacketMaxSize != other.serverJunkPacketMaxSize
|| serverInitPacketJunkSize != other.serverInitPacketJunkSize || serverResponsePacketJunkSize != other.serverResponsePacketJunkSize || serverInitPacketJunkSize != other.serverInitPacketJunkSize || serverResponsePacketJunkSize != other.serverResponsePacketJunkSize
// || serverCookieReplyPacketJunkSize != other.serverCookieReplyPacketJunkSize
// || serverTransportPacketJunkSize != other.serverTransportPacketJunkSize
|| serverInitPacketMagicHeader != other.serverInitPacketMagicHeader || serverInitPacketMagicHeader != other.serverInitPacketMagicHeader
|| serverResponsePacketMagicHeader != other.serverResponsePacketMagicHeader || serverResponsePacketMagicHeader != other.serverResponsePacketMagicHeader
|| serverUnderloadPacketMagicHeader != other.serverUnderloadPacketMagicHeader || serverUnderloadPacketMagicHeader != other.serverUnderloadPacketMagicHeader
@ -334,12 +236,7 @@ bool AwgConfig::hasEqualServerSettings(const AwgConfig &other) const
bool AwgConfig::hasEqualClientSettings(const AwgConfig &other) const bool AwgConfig::hasEqualClientSettings(const AwgConfig &other) const
{ {
if (clientMtu != other.clientMtu || clientJunkPacketCount != other.clientJunkPacketCount if (clientMtu != other.clientMtu || clientJunkPacketCount != other.clientJunkPacketCount
|| clientJunkPacketMinSize != other.clientJunkPacketMinSize || clientJunkPacketMaxSize != other.clientJunkPacketMaxSize || clientJunkPacketMinSize != other.clientJunkPacketMinSize || clientJunkPacketMaxSize != other.clientJunkPacketMaxSize) {
|| clientSpecialJunk1 != other.clientSpecialJunk1 || clientSpecialJunk2 != other.clientSpecialJunk2
|| clientSpecialJunk3 != other.clientSpecialJunk3 || clientSpecialJunk4 != other.clientSpecialJunk4
|| clientSpecialJunk5 != other.clientSpecialJunk5 || clientControlledJunk1 != other.clientControlledJunk1
|| clientControlledJunk2 != other.clientControlledJunk2 || clientControlledJunk3 != other.clientControlledJunk3
|| clientSpecialHandshakeTimeout != other.clientSpecialHandshakeTimeout) {
return false; return false;
} }
return true; return true;

View file

@ -6,12 +6,9 @@
#include "containers/containers_defs.h" #include "containers/containers_defs.h"
namespace AwgConstant namespace AwgConstant {
{
const int messageInitiationSize = 148; const int messageInitiationSize = 148;
const int messageResponseSize = 92; const int messageResponseSize = 92;
const int messageCookieReplySize = 64;
const int messageTransportSize = 32;
} }
struct AwgConfig struct AwgConfig
@ -25,23 +22,12 @@ struct AwgConfig
QString clientJunkPacketCount; QString clientJunkPacketCount;
QString clientJunkPacketMinSize; QString clientJunkPacketMinSize;
QString clientJunkPacketMaxSize; QString clientJunkPacketMaxSize;
QString clientSpecialJunk1;
QString clientSpecialJunk2;
QString clientSpecialJunk3;
QString clientSpecialJunk4;
QString clientSpecialJunk5;
QString clientControlledJunk1;
QString clientControlledJunk2;
QString clientControlledJunk3;
QString clientSpecialHandshakeTimeout;
QString serverJunkPacketCount; QString serverJunkPacketCount;
QString serverJunkPacketMinSize; QString serverJunkPacketMinSize;
QString serverJunkPacketMaxSize; QString serverJunkPacketMaxSize;
QString serverInitPacketJunkSize; QString serverInitPacketJunkSize;
QString serverResponsePacketJunkSize; QString serverResponsePacketJunkSize;
QString serverCookieReplyPacketJunkSize;
QString serverTransportPacketJunkSize;
QString serverInitPacketMagicHeader; QString serverInitPacketMagicHeader;
QString serverResponsePacketMagicHeader; QString serverResponsePacketMagicHeader;
QString serverUnderloadPacketMagicHeader; QString serverUnderloadPacketMagicHeader;
@ -49,6 +35,7 @@ struct AwgConfig
bool hasEqualServerSettings(const AwgConfig &other) const; bool hasEqualServerSettings(const AwgConfig &other) const;
bool hasEqualClientSettings(const AwgConfig &other) const; bool hasEqualClientSettings(const AwgConfig &other) const;
}; };
class AwgConfigModel : public QAbstractListModel class AwgConfigModel : public QAbstractListModel
@ -64,28 +51,16 @@ public:
ClientJunkPacketCountRole, ClientJunkPacketCountRole,
ClientJunkPacketMinSizeRole, ClientJunkPacketMinSizeRole,
ClientJunkPacketMaxSizeRole, ClientJunkPacketMaxSizeRole,
ClientSpecialJunk1Role,
ClientSpecialJunk2Role,
ClientSpecialJunk3Role,
ClientSpecialJunk4Role,
ClientSpecialJunk5Role,
ClientControlledJunk1Role,
ClientControlledJunk2Role,
ClientControlledJunk3Role,
ClientSpecialHandshakeTimeoutRole,
ServerJunkPacketCountRole, ServerJunkPacketCountRole,
ServerJunkPacketMinSizeRole, ServerJunkPacketMinSizeRole,
ServerJunkPacketMaxSizeRole, ServerJunkPacketMaxSizeRole,
ServerInitPacketJunkSizeRole, ServerInitPacketJunkSizeRole,
ServerResponsePacketJunkSizeRole, ServerResponsePacketJunkSizeRole,
ServerCookieReplyPacketJunkSizeRole,
ServerTransportPacketJunkSizeRole,
ServerInitPacketMagicHeaderRole, ServerInitPacketMagicHeaderRole,
ServerResponsePacketMagicHeaderRole, ServerResponsePacketMagicHeaderRole,
ServerUnderloadPacketMagicHeaderRole, ServerUnderloadPacketMagicHeaderRole,
ServerTransportPacketMagicHeaderRole, ServerTransportPacketMagicHeaderRole
}; };
explicit AwgConfigModel(QObject *parent = nullptr); explicit AwgConfigModel(QObject *parent = nullptr);
@ -100,7 +75,7 @@ public slots:
QJsonObject getConfig(); QJsonObject getConfig();
bool isHeadersEqual(const QString &h1, const QString &h2, const QString &h3, const QString &h4); bool isHeadersEqual(const QString &h1, const QString &h2, const QString &h3, const QString &h4);
bool isPacketSizeEqual(const int s1, const int s2/*, const int s3, const int s4*/); bool isPacketSizeEqual(const int s1, const int s2);
bool isServerSettingsEqual(); bool isServerSettingsEqual();

View file

@ -2,72 +2,128 @@
#include "protocols/protocols_defs.h" #include "protocols/protocols_defs.h"
OpenVpnConfigModel::OpenVpnConfigModel(QObject *parent) : QAbstractListModel(parent) OpenVpnConfigModel::OpenVpnConfigModel(QObject *parent)
: QObject(parent)
{ {
} }
int OpenVpnConfigModel::rowCount(const QModelIndex &parent) const QString OpenVpnConfigModel::subnetAddress() const
{ {
Q_UNUSED(parent); return m_protocolConfig.value(amnezia::config_key::subnet_address).toString(amnezia::protocols::openvpn::defaultSubnetAddress);
return 1;
} }
bool OpenVpnConfigModel::setData(const QModelIndex &index, const QVariant &value, int role) void OpenVpnConfigModel::setSubnetAddress(const QString &subnetAddress)
{ {
if (!index.isValid() || index.row() < 0 || index.row() >= ContainerProps::allContainers().size()) { m_protocolConfig.insert(amnezia::config_key::subnet_address, subnetAddress);
return false;
}
switch (role) {
case Roles::SubnetAddressRole: m_protocolConfig.insert(amnezia::config_key::subnet_address, value.toString()); break;
case Roles::TransportProtoRole: m_protocolConfig.insert(config_key::transport_proto, value.toString()); break;
case Roles::PortRole: m_protocolConfig.insert(config_key::port, value.toString()); break;
case Roles::AutoNegotiateEncryprionRole: m_protocolConfig.insert(config_key::ncp_disable, !value.toBool()); break;
case Roles::HashRole: m_protocolConfig.insert(config_key::hash, value.toString()); break;
case Roles::CipherRole: m_protocolConfig.insert(config_key::cipher, value.toString()); break;
case Roles::TlsAuthRole: m_protocolConfig.insert(config_key::tls_auth, value.toBool()); break;
case Roles::BlockDnsRole: m_protocolConfig.insert(config_key::block_outside_dns, value.toBool()); break;
case Roles::AdditionalClientCommandsRole: m_protocolConfig.insert(config_key::additional_client_config, value.toString()); break;
case Roles::AdditionalServerCommandsRole: m_protocolConfig.insert(config_key::additional_server_config, value.toString()); break;
}
emit dataChanged(index, index, QList { role });
return true;
} }
QVariant OpenVpnConfigModel::data(const QModelIndex &index, int role) const QString OpenVpnConfigModel::transportProto() const
{ {
if (!index.isValid() || index.row() < 0 || index.row() >= rowCount()) { return m_protocolConfig.value(config_key::transport_proto).toString(protocols::openvpn::defaultTransportProto);
return false; }
}
switch (role) { void OpenVpnConfigModel::setTransportProto(const QString &transportProto)
case Roles::SubnetAddressRole: {
return m_protocolConfig.value(amnezia::config_key::subnet_address).toString(amnezia::protocols::openvpn::defaultSubnetAddress); m_protocolConfig.insert(config_key::transport_proto, transportProto);
case Roles::TransportProtoRole: }
return m_protocolConfig.value(config_key::transport_proto).toString(protocols::openvpn::defaultTransportProto);
case Roles::PortRole: return m_protocolConfig.value(config_key::port).toString(protocols::openvpn::defaultPort); QString OpenVpnConfigModel::port() const
case Roles::AutoNegotiateEncryprionRole: {
return !m_protocolConfig.value(config_key::ncp_disable).toBool(protocols::openvpn::defaultNcpDisable); return m_protocolConfig.value(config_key::port).toString(protocols::openvpn::defaultPort);
case Roles::HashRole: return m_protocolConfig.value(config_key::hash).toString(protocols::openvpn::defaultHash); }
case Roles::CipherRole: return m_protocolConfig.value(config_key::cipher).toString(protocols::openvpn::defaultCipher);
case Roles::TlsAuthRole: return m_protocolConfig.value(config_key::tls_auth).toBool(protocols::openvpn::defaultTlsAuth); void OpenVpnConfigModel::setPort(const QString &port)
case Roles::BlockDnsRole: {
return m_protocolConfig.value(config_key::block_outside_dns).toBool(protocols::openvpn::defaultBlockOutsideDns); m_protocolConfig.insert(config_key::port, port);
case Roles::AdditionalClientCommandsRole: }
return m_protocolConfig.value(config_key::additional_client_config).toString(protocols::openvpn::defaultAdditionalClientConfig);
case Roles::AdditionalServerCommandsRole: bool OpenVpnConfigModel::autoNegotiateEncryption() const
return m_protocolConfig.value(config_key::additional_server_config).toString(protocols::openvpn::defaultAdditionalServerConfig); {
case Roles::IsPortEditable: return m_container == DockerContainer::OpenVpn ? true : false; return !m_protocolConfig.value(config_key::ncp_disable).toBool(protocols::openvpn::defaultNcpDisable);
case Roles::IsTransportProtoEditable: return m_container == DockerContainer::OpenVpn ? true : false; }
case Roles::HasRemoveButton: return m_container == DockerContainer::OpenVpn ? true : false;
} void OpenVpnConfigModel::setAutoNegotiateEncryption(bool enabled)
return QVariant(); {
m_protocolConfig.insert(config_key::ncp_disable, !enabled);
}
QString OpenVpnConfigModel::hash() const
{
return m_protocolConfig.value(config_key::hash).toString(protocols::openvpn::defaultHash);
}
void OpenVpnConfigModel::setHash(const QString &hash)
{
m_protocolConfig.insert(config_key::hash, hash);
}
QString OpenVpnConfigModel::cipher() const
{
return m_protocolConfig.value(config_key::cipher).toString(protocols::openvpn::defaultCipher);
}
void OpenVpnConfigModel::setCipher(const QString &cipher)
{
m_protocolConfig.insert(config_key::cipher, cipher);
}
bool OpenVpnConfigModel::tlsAuth() const
{
return m_protocolConfig.value(config_key::tls_auth).toBool(protocols::openvpn::defaultTlsAuth);
}
void OpenVpnConfigModel::setTlsAuth(bool enabled)
{
m_protocolConfig.insert(config_key::tls_auth, enabled);
}
bool OpenVpnConfigModel::blockDns() const
{
return m_protocolConfig.value(config_key::block_outside_dns).toBool(protocols::openvpn::defaultBlockOutsideDns);
}
void OpenVpnConfigModel::setBlockDns(bool enabled)
{
m_protocolConfig.insert(config_key::block_outside_dns, enabled);
}
QString OpenVpnConfigModel::additionalClientCommands() const
{
return m_protocolConfig.value(config_key::additional_client_config).toString(protocols::openvpn::defaultAdditionalClientConfig);
}
void OpenVpnConfigModel::setAdditionalClientCommands(const QString &commands)
{
m_protocolConfig.insert(config_key::additional_client_config, commands);
}
QString OpenVpnConfigModel::additionalServerCommands() const
{
return m_protocolConfig.value(config_key::additional_server_config).toString(protocols::openvpn::defaultAdditionalServerConfig);
}
void OpenVpnConfigModel::setAdditionalServerCommands(const QString &commands)
{
m_protocolConfig.insert(config_key::additional_server_config, commands);
}
bool OpenVpnConfigModel::isPortEditable() const
{
return m_container == DockerContainer::OpenVpn;
}
bool OpenVpnConfigModel::isTransportProtoEditable() const
{
return m_container == DockerContainer::OpenVpn;
}
bool OpenVpnConfigModel::hasRemoveButton() const
{
return m_container == DockerContainer::OpenVpn;
} }
void OpenVpnConfigModel::updateModel(const QJsonObject &config) void OpenVpnConfigModel::updateModel(const QJsonObject &config)
{ {
beginResetModel();
m_container = ContainerProps::containerFromString(config.value(config_key::container).toString()); m_container = ContainerProps::containerFromString(config.value(config_key::container).toString());
m_fullConfig = config; m_fullConfig = config;
@ -100,8 +156,6 @@ void OpenVpnConfigModel::updateModel(const QJsonObject &config)
m_protocolConfig.insert( m_protocolConfig.insert(
config_key::additional_server_config, config_key::additional_server_config,
protocolConfig.value(config_key::additional_server_config).toString(protocols::openvpn::defaultAdditionalServerConfig)); protocolConfig.value(config_key::additional_server_config).toString(protocols::openvpn::defaultAdditionalServerConfig));
endResetModel();
} }
QJsonObject OpenVpnConfigModel::getConfig() QJsonObject OpenVpnConfigModel::getConfig()
@ -109,26 +163,3 @@ QJsonObject OpenVpnConfigModel::getConfig()
m_fullConfig.insert(config_key::openvpn, m_protocolConfig); m_fullConfig.insert(config_key::openvpn, m_protocolConfig);
return m_fullConfig; return m_fullConfig;
} }
QHash<int, QByteArray> OpenVpnConfigModel::roleNames() const
{
QHash<int, QByteArray> roles;
roles[SubnetAddressRole] = "subnetAddress";
roles[TransportProtoRole] = "transportProto";
roles[PortRole] = "port";
roles[AutoNegotiateEncryprionRole] = "autoNegotiateEncryprion";
roles[HashRole] = "hash";
roles[CipherRole] = "cipher";
roles[TlsAuthRole] = "tlsAuth";
roles[BlockDnsRole] = "blockDns";
roles[AdditionalClientCommandsRole] = "additionalClientCommands";
roles[AdditionalServerCommandsRole] = "additionalServerCommands";
roles[IsPortEditable] = "isPortEditable";
roles[IsTransportProtoEditable] = "isTransportProtoEditable";
roles[HasRemoveButton] = "hasRemoveButton";
return roles;
}

View file

@ -1,47 +1,90 @@
#ifndef OPENVPNCONFIGMODEL_H #ifndef OPENVPNCONFIGMODEL_H
#define OPENVPNCONFIGMODEL_H #define OPENVPNCONFIGMODEL_H
#include <QAbstractListModel> #include <QObject>
#include <QJsonObject> #include <QJsonObject>
#include "containers/containers_defs.h" #include "containers/containers_defs.h"
class OpenVpnConfigModel : public QAbstractListModel class OpenVpnConfigModel : public QObject
{ {
Q_OBJECT Q_OBJECT
Q_PROPERTY(QString subnetAddress READ subnetAddress WRITE setSubnetAddress NOTIFY subnetAddressChanged)
Q_PROPERTY(QString transportProto READ transportProto WRITE setTransportProto NOTIFY transportProtoChanged)
Q_PROPERTY(QString port READ port WRITE setPort NOTIFY portChanged)
Q_PROPERTY(bool autoNegotiateEncryption READ autoNegotiateEncryption WRITE setAutoNegotiateEncryption NOTIFY autoNegotiateEncryptionChanged)
Q_PROPERTY(QString hash READ hash WRITE setHash NOTIFY hashChanged)
Q_PROPERTY(QString cipher READ cipher WRITE setCipher NOTIFY cipherChanged)
Q_PROPERTY(bool tlsAuth READ tlsAuth WRITE setTlsAuth NOTIFY tlsAuthChanged)
Q_PROPERTY(bool blockDns READ blockDns WRITE setBlockDns NOTIFY blockDnsChanged)
Q_PROPERTY(QString additionalClientCommands READ additionalClientCommands WRITE setAdditionalClientCommands NOTIFY additionalClientCommandsChanged)
Q_PROPERTY(QString additionalServerCommands READ additionalServerCommands WRITE setAdditionalServerCommands NOTIFY additionalServerCommandsChanged)
Q_PROPERTY(bool isPortEditable READ isPortEditable NOTIFY isPortEditableChanged)
Q_PROPERTY(bool isTransportProtoEditable READ isTransportProtoEditable NOTIFY isTransportProtoEditableChanged)
Q_PROPERTY(bool hasRemoveButton READ hasRemoveButton NOTIFY hasRemoveButtonChanged)
public: public:
enum Roles {
SubnetAddressRole = Qt::UserRole + 1,
TransportProtoRole,
PortRole,
AutoNegotiateEncryprionRole,
HashRole,
CipherRole,
TlsAuthRole,
BlockDnsRole,
AdditionalClientCommandsRole,
AdditionalServerCommandsRole,
IsPortEditable,
IsTransportProtoEditable,
HasRemoveButton
};
explicit OpenVpnConfigModel(QObject *parent = nullptr); explicit OpenVpnConfigModel(QObject *parent = nullptr);
~OpenVpnConfigModel() override = default;
int rowCount(const QModelIndex &parent = QModelIndex()) const override; OpenVpnConfigModel(const OpenVpnConfigModel &) = delete;
OpenVpnConfigModel &operator=(const OpenVpnConfigModel &) = delete;
OpenVpnConfigModel(OpenVpnConfigModel &&) = delete;
OpenVpnConfigModel &operator=(OpenVpnConfigModel &&) = delete;
bool setData(const QModelIndex &index, const QVariant &value, int role) override; QString subnetAddress() const;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; void setSubnetAddress(const QString &subnetAddress);
QString transportProto() const;
void setTransportProto(const QString &transportProto);
QString port() const;
void setPort(const QString &port);
bool autoNegotiateEncryption() const;
void setAutoNegotiateEncryption(bool enabled);
QString hash() const;
void setHash(const QString &hash);
QString cipher() const;
void setCipher(const QString &cipher);
bool tlsAuth() const;
void setTlsAuth(bool enabled);
bool blockDns() const;
void setBlockDns(bool enabled);
QString additionalClientCommands() const;
void setAdditionalClientCommands(const QString &commands);
QString additionalServerCommands() const;
void setAdditionalServerCommands(const QString &commands);
bool isPortEditable() const;
bool isTransportProtoEditable() const;
bool hasRemoveButton() const;
Q_INVOKABLE QJsonObject getConfig();
signals:
void subnetAddressChanged(const QString &);
void transportProtoChanged(const QString &);
void portChanged(const QString &);
void autoNegotiateEncryptionChanged(bool);
void hashChanged(const QString &);
void cipherChanged(const QString &);
void tlsAuthChanged(bool);
void blockDnsChanged(bool);
void additionalClientCommandsChanged(const QString &);
void additionalServerCommandsChanged(const QString &);
void isPortEditableChanged(bool);
void isTransportProtoEditableChanged(bool);
void hasRemoveButtonChanged(bool);
public slots: public slots:
void updateModel(const QJsonObject &config); void updateModel(const QJsonObject &config);
QJsonObject getConfig();
protected:
QHash<int, QByteArray> roleNames() const override;
private: private:
DockerContainer m_container; DockerContainer m_container;

View file

@ -8,8 +8,6 @@
#include <AmneziaVPN-Swift.h> #include <AmneziaVPN-Swift.h>
#endif #endif
#include "core/api/apiUtils.h"
namespace namespace
{ {
namespace configKey namespace configKey
@ -429,7 +427,7 @@ void ServersModel::updateDefaultServerContainersModel()
emit defaultServerContainersUpdated(containers); emit defaultServerContainersUpdated(containers);
} }
QJsonObject ServersModel::getServerConfig(const int serverIndex) const QJsonObject ServersModel::getServerConfig(const int serverIndex)
{ {
return m_servers.at(serverIndex).toObject(); return m_servers.at(serverIndex).toObject();
} }
@ -816,8 +814,3 @@ const QString ServersModel::getDefaultServerImagePathCollapsed()
} }
return QString("qrc:/countriesFlags/images/flagKit/%1.svg").arg(countryCode.toUpper()); return QString("qrc:/countriesFlags/images/flagKit/%1.svg").arg(countryCode.toUpper());
} }
bool ServersModel::processedServerIsPremium() const
{
return apiUtils::isPremiumServer(getServerConfig(m_processedServerIndex));
}

View file

@ -63,9 +63,6 @@ public:
Q_PROPERTY(bool isDefaultServerFromApi READ isDefaultServerFromApi NOTIFY defaultServerIndexChanged) Q_PROPERTY(bool isDefaultServerFromApi READ isDefaultServerFromApi NOTIFY defaultServerIndexChanged)
Q_PROPERTY(int processedIndex READ getProcessedServerIndex WRITE setProcessedServerIndex NOTIFY processedServerIndexChanged) Q_PROPERTY(int processedIndex READ getProcessedServerIndex WRITE setProcessedServerIndex NOTIFY processedServerIndexChanged)
Q_PROPERTY(bool processedServerIsPremium READ processedServerIsPremium NOTIFY processedServerChanged)
bool processedServerIsPremium() const;
public slots: public slots:
void setDefaultServerIndex(const int index); void setDefaultServerIndex(const int index);
@ -95,7 +92,7 @@ public slots:
void removeServer(); void removeServer();
void removeServer(const int serverIndex); void removeServer(const int serverIndex);
QJsonObject getServerConfig(const int serverIndex) const; QJsonObject getServerConfig(const int serverIndex);
void reloadDefaultServerContainerConfig(); void reloadDefaultServerContainerConfig();
void updateContainerConfig(const int containerIndex, const QJsonObject config); void updateContainerConfig(const int containerIndex, const QJsonObject config);

View file

@ -1,15 +0,0 @@
pragma ComponentBehavior: Bound
import QtQuick
import QtQuick.Layouts
import "../Controls2"
TextFieldWithHeaderType {
Layout.fillWidth: true
Layout.topMargin: 16
textField.validator: IntValidator { bottom: 0 }
checkEmptyText: true
}

View file

@ -10,8 +10,7 @@ import ProtocolEnum 1.0
import "../Controls2" import "../Controls2"
import "../Controls2/TextTypes" import "../Controls2/TextTypes"
ListViewType {
ListView {
id: menuContent id: menuContent
property var rootWidth property var rootWidth
@ -21,13 +20,6 @@ ListView {
anchors.top: parent.top anchors.top: parent.top
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
clip: true
snapMode: ListView.SnapToItem
ScrollBar.vertical: ScrollBarType {}
property bool isFocusable: true
ButtonGroup { ButtonGroup {
id: containersRadioButtonGroup id: containersRadioButtonGroup
} }

View file

@ -1,97 +1,97 @@
import QtQuick import QtQuick
import QtQuick.Controls import QtQuick.Controls
import QtQuick.Layouts import QtQuick.Layouts
import PageEnum 1.0 import PageEnum 1.0
import "../Controls2" import "../Controls2"
import "../Controls2/TextTypes" import "../Controls2/TextTypes"
import "../Config" import "../Config"
DrawerType2 { DrawerType2 {
id: root id: root
property bool isAppSplitTinnelingEnabled: Qt.platform.os === "windows" || Qt.platform.os === "android" property bool isAppSplitTinnelingEnabled: Qt.platform.os === "windows" || Qt.platform.os === "android"
anchors.fill: parent anchors.fill: parent
expandedHeight: parent.height * 0.9 expandedHeight: parent.height * 0.9
expandedStateContent: ColumnLayout { expandedStateContent: ColumnLayout {
id: content id: content
anchors.top: parent.top anchors.top: parent.top
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
spacing: 0 spacing: 0
Header2Type { Header2Type {
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 24 Layout.topMargin: 24
Layout.rightMargin: 16 Layout.rightMargin: 16
Layout.leftMargin: 16 Layout.leftMargin: 16
Layout.bottomMargin: 16 Layout.bottomMargin: 16
headerText: qsTr("Split tunneling") headerText: qsTr("Split tunneling")
descriptionText: qsTr("Allows you to connect to some sites or applications through a VPN connection and bypass others") descriptionText: qsTr("Allows you to connect to some sites or applications through a VPN connection and bypass others")
} }
LabelWithButtonType { LabelWithButtonType {
id: splitTunnelingSwitch id: splitTunnelingSwitch
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 16 Layout.topMargin: 16
visible: ServersModel.isDefaultServerDefaultContainerHasSplitTunneling visible: ServersModel.isDefaultServerDefaultContainerHasSplitTunneling
text: qsTr("Split tunneling on the server") text: qsTr("Split tunneling on the server")
descriptionText: qsTr("Enabled \nCan't be disabled for current server") descriptionText: qsTr("Enabled \nCan't be disabled for current server")
rightImageSource: "qrc:/images/controls/chevron-right.svg" rightImageSource: "qrc:/images/controls/chevron-right.svg"
clickedFunction: function() { clickedFunction: function() {
PageController.goToPage(PageEnum.PageSettingsSplitTunneling) PageController.goToPage(PageEnum.PageSettingsSplitTunneling)
root.closeTriggered() root.closeTriggered()
} }
} }
DividerType { DividerType {
visible: ServersModel.isDefaultServerDefaultContainerHasSplitTunneling visible: ServersModel.isDefaultServerDefaultContainerHasSplitTunneling
} }
LabelWithButtonType { LabelWithButtonType {
id: siteBasedSplitTunnelingSwitch id: siteBasedSplitTunnelingSwitch
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 16 Layout.topMargin: 16
text: qsTr("Site-based split tunneling") text: qsTr("Site-based split tunneling")
descriptionText: enabled && SitesModel.isTunnelingEnabled ? qsTr("Enabled") : qsTr("Disabled") descriptionText: enabled && SitesModel.isTunnelingEnabled ? qsTr("Enabled") : qsTr("Disabled")
rightImageSource: "qrc:/images/controls/chevron-right.svg" rightImageSource: "qrc:/images/controls/chevron-right.svg"
clickedFunction: function() { clickedFunction: function() {
PageController.goToPage(PageEnum.PageSettingsSplitTunneling) PageController.goToPage(PageEnum.PageSettingsSplitTunneling)
root.closeTriggered() root.closeTriggered()
} }
} }
DividerType { DividerType {
} }
LabelWithButtonType { LabelWithButtonType {
id: appSplitTunnelingSwitch id: appSplitTunnelingSwitch
visible: isAppSplitTinnelingEnabled visible: isAppSplitTinnelingEnabled
Layout.fillWidth: true Layout.fillWidth: true
text: qsTr("App-based split tunneling") text: qsTr("App-based split tunneling")
descriptionText: AppSplitTunnelingModel.isTunnelingEnabled ? qsTr("Enabled") : qsTr("Disabled") descriptionText: AppSplitTunnelingModel.isTunnelingEnabled ? qsTr("Enabled") : qsTr("Disabled")
rightImageSource: "qrc:/images/controls/chevron-right.svg" rightImageSource: "qrc:/images/controls/chevron-right.svg"
clickedFunction: function() { clickedFunction: function() {
PageController.goToPage(PageEnum.PageSettingsAppSplitTunneling) PageController.goToPage(PageEnum.PageSettingsAppSplitTunneling)
root.closeTriggered() root.closeTriggered()
} }
} }
DividerType { DividerType {
visible: isAppSplitTinnelingEnabled visible: isAppSplitTinnelingEnabled
} }
} }
} }

View file

@ -57,7 +57,7 @@ DrawerType2 {
headerText: qsTr("Choose application") headerText: qsTr("Choose application")
} }
ListView { ListViewType {
id: listView id: listView
Layout.fillWidth: true Layout.fillWidth: true
@ -66,11 +66,6 @@ DrawerType2 {
Layout.rightMargin: 16 Layout.rightMargin: 16
Layout.leftMargin: 16 Layout.leftMargin: 16
clip: true
interactive: true
property bool isFocusable: true
model: SortFilterProxyModel { model: SortFilterProxyModel {
id: proxyInstalledAppsModel id: proxyInstalledAppsModel
sourceModel: installedAppsModel sourceModel: installedAppsModel
@ -81,44 +76,37 @@ DrawerType2 {
} }
} }
ScrollBar.vertical: ScrollBarType {}
ButtonGroup { ButtonGroup {
id: buttonGroup id: buttonGroup
} }
delegate: Item { delegate: ColumnLayout {
implicitWidth: root.width id: delegateContent
implicitHeight: delegateContent.implicitHeight
ColumnLayout { width: listView.width
id: delegateContent
anchors.fill: parent RowLayout {
CheckBoxType {
Layout.fillWidth: true
RowLayout { text: appName
CheckBoxType { checked: isAppSelected
Layout.fillWidth: true onCheckedChanged: {
installedAppsModel.selectedStateChanged(proxyInstalledAppsModel.mapToSource(index), checked)
text: appName
checked: isAppSelected
onCheckedChanged: {
installedAppsModel.selectedStateChanged(proxyInstalledAppsModel.mapToSource(index), checked)
}
}
Image {
source: "image://installedAppImage/" + appIcon
sourceSize.width: 24
sourceSize.height: 24
Layout.rightMargin: 48
} }
} }
DividerType {} Image {
source: "image://installedAppImage/" + appIcon
sourceSize.width: 24
sourceSize.height: 24
Layout.rightMargin: 48
}
} }
DividerType {}
} }
} }
} }

View file

@ -49,7 +49,7 @@ DrawerType2 {
} }
} }
ListView { ListViewType {
id: listView id: listView
anchors.top: backButtonLayout.bottom anchors.top: backButtonLayout.bottom
@ -57,14 +57,8 @@ DrawerType2 {
anchors.right: parent.right anchors.right: parent.right
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
property bool isFocusable: true
property int selectedIndex: LanguageModel.currentLanguageIndex property int selectedIndex: LanguageModel.currentLanguageIndex
clip: true
reuseItems: true
ScrollBar.vertical: ScrollBarType {}
model: LanguageModel model: LanguageModel
ButtonGroup { ButtonGroup {

View file

@ -15,7 +15,7 @@ import "../Controls2"
import "../Controls2/TextTypes" import "../Controls2/TextTypes"
import "../Config" import "../Config"
ListView { ListViewType {
id: root id: root
property int selectedIndex: ServersModel.defaultIndex property int selectedIndex: ServersModel.defaultIndex
@ -28,10 +28,6 @@ ListView {
model: ServersModel model: ServersModel
ScrollBar.vertical: ScrollBarType {}
property bool isFocusable: true
Connections { Connections {
target: ServersModel target: ServersModel
function onDefaultServerIndexChanged(serverIndex) { function onDefaultServerIndexChanged(serverIndex) {
@ -39,9 +35,6 @@ ListView {
} }
} }
clip: true
reuseItems: true
delegate: Item { delegate: Item {
id: menuContentDelegate id: menuContentDelegate
objectName: "menuContentDelegate" objectName: "menuContentDelegate"

View file

@ -13,78 +13,64 @@ import "../Controls2"
import "../Controls2/TextTypes" import "../Controls2/TextTypes"
ListView { ListViewType {
id: root id: root
width: parent.width anchors.fill: parent
height: root.contentItem.height
clip: true delegate: ColumnLayout {
reuseItems: true width: root.width
property bool isFocusable: false LabelWithButtonType {
Layout.fillWidth: true
delegate: Item { text: name
implicitWidth: root.width descriptionText: description
implicitHeight: delegateContent.implicitHeight rightImageSource: isInstalled ? "qrc:/images/controls/chevron-right.svg" : "qrc:/images/controls/download.svg"
ColumnLayout { clickedFunction: function() {
id: delegateContent if (isInstalled) {
var containerIndex = root.model.mapToSource(index)
ContainersModel.setProcessedContainerIndex(containerIndex)
anchors.fill: parent if (serviceType !== ProtocolEnum.Other) {
if (config[ContainerProps.containerTypeToString(containerIndex)]["isThirdPartyConfig"]) {
LabelWithButtonType {
id: containerRadioButton
implicitWidth: parent.width
text: name
descriptionText: description
rightImageSource: isInstalled ? "qrc:/images/controls/chevron-right.svg" : "qrc:/images/controls/download.svg"
clickedFunction: function() {
if (isInstalled) {
var containerIndex = root.model.mapToSource(index)
ContainersModel.setProcessedContainerIndex(containerIndex)
if (serviceType !== ProtocolEnum.Other) {
if (config[ContainerProps.containerTypeToString(containerIndex)]["isThirdPartyConfig"]) {
ProtocolsModel.updateModel(config)
PageController.goToPage(PageEnum.PageProtocolRaw)
return
}
}
switch (containerIndex) {
case ContainerEnum.Ipsec: {
ProtocolsModel.updateModel(config) ProtocolsModel.updateModel(config)
PageController.goToPage(PageEnum.PageProtocolRaw) PageController.goToPage(PageEnum.PageProtocolRaw)
break return
} }
case ContainerEnum.Dns: {
PageController.goToPage(PageEnum.PageServiceDnsSettings)
break
}
default: {
ProtocolsModel.updateModel(config)
PageController.goToPage(PageEnum.PageSettingsServerProtocol)
}
}
} else {
ContainersModel.setProcessedContainerIndex(root.model.mapToSource(index))
InstallController.setShouldCreateServer(false)
PageController.goToPage(PageEnum.PageSetupWizardProtocolSettings)
} }
}
MouseArea { switch (containerIndex) {
anchors.fill: parent case ContainerEnum.Ipsec: {
cursorShape: Qt.PointingHandCursor ProtocolsModel.updateModel(config)
enabled: false PageController.goToPage(PageEnum.PageProtocolRaw)
break
}
case ContainerEnum.Dns: {
PageController.goToPage(PageEnum.PageServiceDnsSettings)
break
}
default: {
ProtocolsModel.updateModel(config)
PageController.goToPage(PageEnum.PageSettingsServerProtocol)
}
}
} else {
ContainersModel.setProcessedContainerIndex(root.model.mapToSource(index))
InstallController.setShouldCreateServer(false)
PageController.goToPage(PageEnum.PageSetupWizardProtocolSettings)
} }
} }
DividerType {} MouseArea {
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
enabled: false
}
} }
DividerType {}
} }
} }

View file

@ -53,7 +53,7 @@ DrawerType2 {
headerText: root.headerText headerText: root.headerText
} }
ListView { ListViewType {
id: listView id: listView
anchors.top: header.bottom anchors.top: header.bottom
@ -61,14 +61,7 @@ DrawerType2 {
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
property bool isFocusable: true model: 1 // fake model to force the ListView to be created without a model
ScrollBar.vertical: ScrollBarType {}
model: 1
clip: true
reuseItems: true
header: ColumnLayout { header: ColumnLayout {
width: listView.width width: listView.width
@ -185,9 +178,21 @@ DrawerType2 {
Connections { Connections {
target: copyNativeConfigStringButton target: copyNativeConfigStringButton
function onClicked() { function onClicked() {
nativeConfigString.selectAll() const headerItem = configListView.headerItem;
nativeConfigString.copy() if (!headerItem) {
nativeConfigString.select(0, 0) console.error("Failed to copy: header item not found in ListView")
return
}
const nativeConfigStringItem = configListView.findChildWithObjectName(headerItem.children, "nativeConfigString");
if (!nativeConfigStringItem) {
console.error("Failed to copy: nativeConfigString item not found in ListView")
return
}
nativeConfigStringItem.selectAll()
nativeConfigStringItem.copy()
nativeConfigStringItem.select(0, 0)
PageController.showNotificationMessage(qsTr("Copied")) PageController.showNotificationMessage(qsTr("Copied"))
} }
} }
@ -195,11 +200,22 @@ DrawerType2 {
Connections { Connections {
target: copyConfigTextButton target: copyConfigTextButton
function onClicked() { function onClicked() {
configText.selectAll() const headerItem = configListView.headerItem;
configText.copy() if (!headerItem) {
configText.select(0, 0) console.error("Failed to copy: header item not found in ListView")
return
}
const configTextItem = configListView.findChildWithObjectName(headerItem.children, "configText");
if (!configTextItem) {
console.error("Failed to copy: configText item not found in ListView")
return
}
configTextItem.selectAll()
configTextItem.copy()
configTextItem.select(0, 0)
PageController.showNotificationMessage(qsTr("Copied")) PageController.showNotificationMessage(qsTr("Copied"))
header.forceActiveFocus()
} }
} }
@ -214,30 +230,37 @@ DrawerType2 {
backButtonFunction: function() { configContentDrawer.closeTriggered() } backButtonFunction: function() { configContentDrawer.closeTriggered() }
} }
FlickableType { ListViewType {
id: configListView
anchors.top: backButton.bottom anchors.top: backButton.bottom
anchors.bottom: parent.bottom
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
anchors.bottom: parent.bottom
contentHeight: configContent.implicitHeight + configContent.anchors.topMargin + configContent.anchors.bottomMargin
ColumnLayout { model: 1 // fake model to force the ListView to be created without a model
id: configContent
anchors.fill: parent header: ColumnLayout {
anchors.rightMargin: 16 width: configListView.width
anchors.leftMargin: 16
Header2Type { Header2Type {
id: configContentHeader id: configContentHeader
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 16 Layout.topMargin: 16
Layout.leftMargin: 16
Layout.rightMargin: 16
headerText: root.configContentHeaderText headerText: root.configContentHeaderText
} }
TextField { TextField {
id: nativeConfigString id: nativeConfigString
objectName: "nativeConfigString"
Layout.leftMargin: 16
Layout.rightMargin: 16
visible: false visible: false
text: ExportController.nativeConfigString text: ExportController.nativeConfigString
@ -248,10 +271,13 @@ DrawerType2 {
TextArea { TextArea {
id: configText id: configText
objectName: "configText"
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 16 Layout.topMargin: 16
Layout.bottomMargin: 16 Layout.bottomMargin: 16
Layout.leftMargin: 16
Layout.rightMargin: 16
padding: 0 padding: 0
leftPadding: 0 leftPadding: 0

View file

@ -1,220 +1,210 @@
import QtQuick import QtQuick
import QtQuick.Controls import QtQuick.Controls
import QtQuick.Layouts import QtQuick.Layouts
import Qt5Compat.GraphicalEffects import Qt5Compat.GraphicalEffects
import Style 1.0 import Style 1.0
import "TextTypes" import "TextTypes"
Button { Button {
id: root id: root
property string hoveredColor: AmneziaStyle.color.lightGray property string hoveredColor: AmneziaStyle.color.lightGray
property string defaultColor: AmneziaStyle.color.paleGray property string defaultColor: AmneziaStyle.color.paleGray
property string disabledColor: AmneziaStyle.color.charcoalGray property string disabledColor: AmneziaStyle.color.charcoalGray
property string pressedColor: AmneziaStyle.color.mutedGray property string pressedColor: AmneziaStyle.color.mutedGray
property string textColor: AmneziaStyle.color.midnightBlack property string textColor: AmneziaStyle.color.midnightBlack
property string borderColor: AmneziaStyle.color.paleGray property string borderColor: AmneziaStyle.color.paleGray
property string borderFocusedColor: AmneziaStyle.color.paleGray property string borderFocusedColor: AmneziaStyle.color.paleGray
property int borderWidth: 0 property int borderWidth: 0
property int borderFocusedWidth: 1 property int borderFocusedWidth: 1
property string leftImageSource property string leftImageSource
property string rightImageSource property string rightImageSource
property string leftImageColor: textColor property string leftImageColor: textColor
property bool changeLeftImageSize: true property bool changeLeftImageSize: true
property bool squareLeftSide: false property bool squareLeftSide: false
property FlickableType parentFlickable property var clickedFunc
property var clickedFunc property alias buttonTextLabel: buttonText
property alias buttonTextLabel: buttonText property bool isFocusable: true
property bool isFocusable: true Keys.onTabPressed: {
FocusController.nextKeyTabItem()
Keys.onTabPressed: { }
FocusController.nextKeyTabItem()
} Keys.onBacktabPressed: {
FocusController.previousKeyTabItem()
Keys.onBacktabPressed: { }
FocusController.previousKeyTabItem()
} Keys.onUpPressed: {
FocusController.nextKeyUpItem()
Keys.onUpPressed: { }
FocusController.nextKeyUpItem()
} Keys.onDownPressed: {
FocusController.nextKeyDownItem()
Keys.onDownPressed: { }
FocusController.nextKeyDownItem()
} Keys.onLeftPressed: {
FocusController.nextKeyLeftItem()
Keys.onLeftPressed: { }
FocusController.nextKeyLeftItem()
} Keys.onRightPressed: {
FocusController.nextKeyRightItem()
Keys.onRightPressed: { }
FocusController.nextKeyRightItem()
} implicitHeight: 56
implicitHeight: 56 hoverEnabled: true
hoverEnabled: true background: Rectangle {
id: focusBorder
onFocusChanged: {
if (root.activeFocus) { color: AmneziaStyle.color.transparent
if (root.parentFlickable) { border.color: root.activeFocus ? root.borderFocusedColor : AmneziaStyle.color.transparent
root.parentFlickable.ensureVisible(this) border.width: root.activeFocus ? root.borderFocusedWidth : 0
}
} anchors.fill: parent
}
radius: 16
background: Rectangle {
id: focusBorder Rectangle {
id: background
color: AmneziaStyle.color.transparent
border.color: root.activeFocus ? root.borderFocusedColor : AmneziaStyle.color.transparent anchors.fill: focusBorder
border.width: root.activeFocus ? root.borderFocusedWidth : 0 anchors.margins: root.activeFocus ? 2 : 0
anchors.fill: parent radius: root.activeFocus ? 14 : 16
color: {
radius: 16 if (root.enabled) {
if (root.pressed) {
Rectangle { return pressedColor
id: background }
return root.hovered ? hoveredColor : defaultColor
anchors.fill: focusBorder } else {
anchors.margins: root.activeFocus ? 2 : 0 return disabledColor
}
radius: root.activeFocus ? 14 : 16 }
color: { border.color: borderColor
if (root.enabled) { border.width: borderWidth
if (root.pressed) {
return pressedColor Behavior on color {
} PropertyAnimation { duration: 200 }
return root.hovered ? hoveredColor : defaultColor }
} else {
return disabledColor Rectangle {
} visible: root.squareLeftSide
}
border.color: borderColor z: 1
border.width: borderWidth
width: parent.radius
Behavior on color { height: parent.radius
PropertyAnimation { duration: 200 } anchors.top: parent.top
} anchors.bottom: parent.bottom
anchors.left: parent.left
Rectangle { color: {
visible: root.squareLeftSide if (root.enabled) {
if (root.pressed) {
z: 1 return pressedColor
}
width: parent.radius return root.hovered ? hoveredColor : defaultColor
height: parent.radius } else {
anchors.top: parent.top return disabledColor
anchors.bottom: parent.bottom }
anchors.left: parent.left }
color: {
if (root.enabled) { Behavior on color {
if (root.pressed) { PropertyAnimation { duration: 200 }
return pressedColor }
} }
return root.hovered ? hoveredColor : defaultColor }
} else { }
return disabledColor
} MouseArea {
} anchors.fill: focusBorder
enabled: false
Behavior on color { cursorShape: Qt.PointingHandCursor
PropertyAnimation { duration: 200 } }
}
} contentItem: Item {
} anchors.fill: focusBorder
}
implicitWidth: content.implicitWidth
MouseArea { implicitHeight: content.implicitHeight
anchors.fill: focusBorder
enabled: false RowLayout {
cursorShape: Qt.PointingHandCursor id: content
} anchors.centerIn: parent
contentItem: Item { Image {
anchors.fill: focusBorder id: leftImage
source: root.leftImageSource
implicitWidth: content.implicitWidth visible: root.leftImageSource === "" ? false : true
implicitHeight: content.implicitHeight
layer {
RowLayout { enabled: leftImageColor !== "" ? true : false
id: content effect: ColorOverlay {
anchors.centerIn: parent color: leftImageColor
}
Image { }
id: leftImage
source: root.leftImageSource Component.onCompleted: {
visible: root.leftImageSource === "" ? false : true if (root.changeLeftImageSize) {
leftImage.Layout.preferredHeight = 20
layer { leftImage.Layout.preferredWidth = 20
enabled: leftImageColor !== "" ? true : false }
effect: ColorOverlay { }
color: leftImageColor }
}
} ButtonTextType {
id: buttonText
Component.onCompleted: {
if (root.changeLeftImageSize) { color: root.textColor
leftImage.Layout.preferredHeight = 20 text: root.text
leftImage.Layout.preferredWidth = 20 visible: root.text === "" ? false : true
}
} horizontalAlignment: Text.AlignLeft
} verticalAlignment: Text.AlignVCenter
}
ButtonTextType {
id: buttonText Image {
Layout.preferredHeight: 20
color: root.textColor Layout.preferredWidth: 20
text: root.text
visible: root.text === "" ? false : true source: root.rightImageSource
visible: root.rightImageSource === "" ? false : true
horizontalAlignment: Text.AlignLeft
verticalAlignment: Text.AlignVCenter layer {
} enabled: true
effect: ColorOverlay {
Image { color: textColor
Layout.preferredHeight: 20 }
Layout.preferredWidth: 20 }
}
source: root.rightImageSource }
visible: root.rightImageSource === "" ? false : true }
layer { Keys.onEnterPressed: {
enabled: true if (root.clickedFunc && typeof root.clickedFunc === "function") {
effect: ColorOverlay { root.clickedFunc()
color: textColor }
} }
}
} Keys.onReturnPressed: {
} if (root.clickedFunc && typeof root.clickedFunc === "function") {
} root.clickedFunc()
}
Keys.onEnterPressed: { }
if (root.clickedFunc && typeof root.clickedFunc === "function") {
root.clickedFunc() onClicked: {
} if (root.clickedFunc && typeof root.clickedFunc === "function") {
} root.clickedFunc()
}
Keys.onReturnPressed: { }
if (root.clickedFunc && typeof root.clickedFunc === "function") { }
root.clickedFunc()
}
}
onClicked: {
if (root.clickedFunc && typeof root.clickedFunc === "function") {
root.clickedFunc()
}
}
}

View file

@ -1,203 +1,185 @@
import QtQuick import QtQuick
import QtQuick.Controls import QtQuick.Controls
import QtQuick.Layouts import QtQuick.Layouts
import Style 1.0 import Style 1.0
import "TextTypes" import "TextTypes"
Button { Button {
id: root id: root
property string headerText property string headerText
property string bodyText property string bodyText
property string footerText property string footerText
property string hoveredColor: AmneziaStyle.color.slateGray property string hoveredColor: AmneziaStyle.color.slateGray
property string defaultColor: AmneziaStyle.color.onyxBlack property string defaultColor: AmneziaStyle.color.onyxBlack
property string textColor: AmneziaStyle.color.midnightBlack property string textColor: AmneziaStyle.color.midnightBlack
property string rightImageSource property string rightImageSource
property string rightImageColor: AmneziaStyle.color.paleGray property string rightImageColor: AmneziaStyle.color.paleGray
property string leftImageSource property string leftImageSource
property real textOpacity: 1.0 property real textOpacity: 1.0
property alias focusItem: rightImage property alias focusItem: rightImage
property FlickableType parentFlickable hoverEnabled: true
hoverEnabled: true background: Rectangle {
id: backgroundRect
background: Rectangle {
id: backgroundRect anchors.fill: parent
radius: 16
anchors.fill: parent
radius: 16 color: defaultColor
color: defaultColor Behavior on color {
PropertyAnimation { duration: 200 }
Behavior on color { }
PropertyAnimation { duration: 200 } }
}
} contentItem: Item {
anchors.left: parent.left
function ensureVisible(item) { anchors.right: parent.right
if (item.activeFocus) {
if (root.parentFlickable) { implicitHeight: content.implicitHeight
root.parentFlickable.ensureVisible(root)
} RowLayout {
} id: content
}
anchors.fill: parent
onFocusChanged: {
ensureVisible(root) Image {
} id: leftImage
source: leftImageSource
focusItem.onFocusChanged: {
root.ensureVisible(focusItem) visible: leftImageSource !== ""
}
Layout.alignment: Qt.AlignLeft | Qt.AlignTop
contentItem: Item { Layout.topMargin: 24
anchors.left: parent.left Layout.bottomMargin: 24
anchors.right: parent.right Layout.leftMargin: 24
}
implicitHeight: content.implicitHeight
ColumnLayout {
RowLayout {
id: content ListItemTitleType {
text: root.headerText
anchors.fill: parent visible: text !== ""
Image { Layout.fillWidth: true
id: leftImage Layout.rightMargin: 16
source: leftImageSource Layout.leftMargin: 16
Layout.topMargin: 16
visible: leftImageSource !== "" Layout.bottomMargin: root.bodyText !== "" ? 0 : 16
Layout.alignment: Qt.AlignLeft | Qt.AlignTop opacity: root.textOpacity
Layout.topMargin: 24 }
Layout.bottomMargin: 24
Layout.leftMargin: 24 CaptionTextType {
} text: root.bodyText
visible: text !== ""
ColumnLayout {
color: AmneziaStyle.color.mutedGray
ListItemTitleType { textFormat: Text.RichText
text: root.headerText
visible: text !== "" Layout.fillWidth: true
Layout.rightMargin: 16
Layout.fillWidth: true Layout.leftMargin: 16
Layout.rightMargin: 16 Layout.bottomMargin: root.footerText !== "" ? 0 : 16
Layout.leftMargin: 16
Layout.topMargin: 16 opacity: root.textOpacity
Layout.bottomMargin: root.bodyText !== "" ? 0 : 16 }
opacity: root.textOpacity ButtonTextType {
} text: root.footerText
visible: text !== ""
CaptionTextType {
text: root.bodyText color: AmneziaStyle.color.mutedGray
visible: text !== ""
Layout.fillWidth: true
color: AmneziaStyle.color.mutedGray Layout.rightMargin: 16
textFormat: Text.RichText Layout.leftMargin: 16
Layout.topMargin: 16
Layout.fillWidth: true Layout.bottomMargin: 16
Layout.rightMargin: 16
Layout.leftMargin: 16 opacity: root.textOpacity
Layout.bottomMargin: root.footerText !== "" ? 0 : 16 }
}
opacity: root.textOpacity
} ImageButtonType {
id: rightImage
ButtonTextType {
text: root.footerText implicitWidth: 40
visible: text !== "" implicitHeight: 40
color: AmneziaStyle.color.mutedGray hoverEnabled: false
image: rightImageSource
Layout.fillWidth: true imageColor: rightImageColor
Layout.rightMargin: 16 visible: rightImageSource ? true : false
Layout.leftMargin: 16
Layout.topMargin: 16 Layout.alignment: Qt.AlignRight | Qt.AlignTop
Layout.bottomMargin: 16 Layout.topMargin: 16
Layout.bottomMargin: 16
opacity: root.textOpacity Layout.rightMargin: 16
}
} Rectangle {
id: rightImageBackground
ImageButtonType {
id: rightImage anchors.fill: parent
radius: 12
implicitWidth: 40 color: "transparent"
implicitHeight: 40
Behavior on color {
hoverEnabled: false PropertyAnimation { duration: 200 }
image: rightImageSource }
imageColor: rightImageColor }
visible: rightImageSource ? true : false
onClicked: {
Layout.alignment: Qt.AlignRight | Qt.AlignTop root.clicked()
Layout.topMargin: 16 }
Layout.bottomMargin: 16 }
Layout.rightMargin: 16 }
}
Rectangle {
id: rightImageBackground MouseArea {
anchors.fill: parent
anchors.fill: parent
radius: 12 cursorShape: Qt.PointingHandCursor
color: "transparent" hoverEnabled: true
enabled: root.enabled
Behavior on color {
PropertyAnimation { duration: 200 } onEntered: {
} backgroundRect.color = root.hoveredColor
}
if (rightImageSource) {
onClicked: { rightImageBackground.color = rightImage.hoveredColor
root.clicked() }
} root.textOpacity = 0.8
} }
}
} onExited: {
backgroundRect.color = root.defaultColor
MouseArea {
anchors.fill: parent if (rightImageSource) {
rightImageBackground.color = rightImage.defaultColor
cursorShape: Qt.PointingHandCursor }
hoverEnabled: true root.textOpacity = 1
enabled: root.enabled }
onEntered: { onPressedChanged: {
backgroundRect.color = root.hoveredColor if (rightImageSource) {
rightImageBackground.color = pressed ? rightImage.pressedColor : entered ? rightImage.hoveredColor : rightImage.defaultColor
if (rightImageSource) { }
rightImageBackground.color = rightImage.hoveredColor root.textOpacity = 0.7
} }
root.textOpacity = 0.8
} onClicked: {
root.clicked()
onExited: { }
backgroundRect.color = root.defaultColor }
}
if (rightImageSource) {
rightImageBackground.color = rightImage.defaultColor
}
root.textOpacity = 1
}
onPressedChanged: {
if (rightImageSource) {
rightImageBackground.color = pressed ? rightImage.pressedColor : entered ? rightImage.hoveredColor : rightImage.defaultColor
}
root.textOpacity = 0.7
}
onClicked: {
root.clicked()
}
}
}

View file

@ -1,170 +1,187 @@
import QtQuick import QtQuick
import QtQuick.Controls import QtQuick.Controls
import QtQuick.Layouts import QtQuick.Layouts
import Qt5Compat.GraphicalEffects import Qt5Compat.GraphicalEffects
import Style 1.0 import Style 1.0
import "TextTypes" import "TextTypes"
CheckBox { CheckBox {
id: root id: root
property string descriptionText property string descriptionText
property string descriptionTextColor: AmneziaStyle.color.mutedGray property string descriptionTextColor: AmneziaStyle.color.mutedGray
property string descriptionTextDisabledColor: AmneziaStyle.color.charcoalGray property string descriptionTextDisabledColor: AmneziaStyle.color.charcoalGray
property string textColor: AmneziaStyle.color.paleGray property string textColor: AmneziaStyle.color.paleGray
property string textDisabledColor: AmneziaStyle.color.mutedGray property string textDisabledColor: AmneziaStyle.color.mutedGray
property string hoveredColor: AmneziaStyle.color.barelyTranslucentWhite property string hoveredColor: AmneziaStyle.color.barelyTranslucentWhite
property string defaultColor: AmneziaStyle.color.transparent property string defaultColor: AmneziaStyle.color.transparent
property string pressedColor: AmneziaStyle.color.barelyTranslucentWhite property string pressedColor: AmneziaStyle.color.barelyTranslucentWhite
property string defaultBorderColor: AmneziaStyle.color.paleGray property string defaultBorderColor: AmneziaStyle.color.paleGray
property string checkedBorderColor: AmneziaStyle.color.goldenApricot property string checkedBorderColor: AmneziaStyle.color.goldenApricot
property string checkedBorderDisabledColor: AmneziaStyle.color.deepBrown property string checkedBorderDisabledColor: AmneziaStyle.color.deepBrown
property string borderFocusedColor: AmneziaStyle.color.paleGray property string borderFocusedColor: AmneziaStyle.color.paleGray
property string checkedImageColor: AmneziaStyle.color.goldenApricot property string checkedImageColor: AmneziaStyle.color.goldenApricot
property string pressedImageColor: AmneziaStyle.color.burntOrange property string pressedImageColor: AmneziaStyle.color.burntOrange
property string defaultImageColor: AmneziaStyle.color.transparent property string defaultImageColor: AmneziaStyle.color.transparent
property string checkedDisabledImageColor: AmneziaStyle.color.mutedBrown property string checkedDisabledImageColor: AmneziaStyle.color.mutedBrown
property string imageSource: "qrc:/images/controls/check.svg" property string imageSource: "qrc:/images/controls/check.svg"
property var parentFlickable property bool isFocusable: true
onFocusChanged: {
if (root.activeFocus) { Keys.onTabPressed: {
if (root.parentFlickable) { FocusController.nextKeyTabItem()
root.parentFlickable.ensureVisible(root) }
}
} Keys.onBacktabPressed: {
} FocusController.previousKeyTabItem()
}
hoverEnabled: enabled ? true : false
focusPolicy: Qt.NoFocus Keys.onUpPressed: {
FocusController.nextKeyUpItem()
background: Rectangle { }
color: AmneziaStyle.color.transparent
border.color: root.focus ? borderFocusedColor : AmneziaStyle.color.transparent Keys.onDownPressed: {
border.width: 1 FocusController.nextKeyDownItem()
radius: 16 }
}
Keys.onLeftPressed: {
indicator: Rectangle { FocusController.nextKeyLeftItem()
id: background }
anchors.verticalCenter: parent.verticalCenter Keys.onRightPressed: {
FocusController.nextKeyRightItem()
implicitWidth: 56 }
implicitHeight: 56
radius: 16 hoverEnabled: enabled ? true : false
focusPolicy: Qt.NoFocus
color: {
if (root.hovered) { background: Rectangle {
return hoveredColor color: AmneziaStyle.color.transparent
} border.color: root.focus ? borderFocusedColor : AmneziaStyle.color.transparent
return defaultColor border.width: 1
} radius: 16
}
Behavior on color {
PropertyAnimation { duration: 200 } indicator: Rectangle {
} id: background
Rectangle { anchors.verticalCenter: parent.verticalCenter
id: imageBorder
implicitWidth: 56
anchors.centerIn: parent implicitHeight: 56
width: 24 radius: 16
height: 24
color: AmneziaStyle.color.transparent color: {
border.color: root.checked ? if (root.hovered) {
(root.enabled ? return hoveredColor
checkedBorderColor : }
checkedBorderDisabledColor) : return defaultColor
defaultBorderColor }
border.width: 1
radius: 4 Behavior on color {
PropertyAnimation { duration: 200 }
Image { }
anchors.centerIn: parent
Rectangle {
source: root.pressed ? imageSource : root.checked ? imageSource : "" id: imageBorder
layer {
enabled: true anchors.centerIn: parent
effect: ColorOverlay { width: 24
color: { height: 24
if (root.pressed) { color: AmneziaStyle.color.transparent
return root.pressedImageColor border.color: root.checked ?
} else if (root.checked) { (root.enabled ?
if (root.enabled) { checkedBorderColor :
return root.checkedImageColor checkedBorderDisabledColor) :
} else { defaultBorderColor
return root.checkedDisabledImageColor border.width: 1
} radius: 4
} else {
return root.defaultImageColor Image {
} anchors.centerIn: parent
}
} source: root.pressed ? imageSource : root.checked ? imageSource : ""
} layer {
} enabled: true
} effect: ColorOverlay {
} color: {
if (root.pressed) {
contentItem: Item { return root.pressedImageColor
anchors.left: parent.left } else if (root.checked) {
anchors.right: parent.right if (root.enabled) {
anchors.leftMargin: 8 + background.width return root.checkedImageColor
} else {
implicitHeight: content.implicitHeight return root.checkedDisabledImageColor
}
ColumnLayout { } else {
id: content return root.defaultImageColor
}
anchors.left: parent.left }
anchors.right: parent.right }
anchors.verticalCenter: parent.verticalCenter }
}
spacing: 4 }
}
ListItemTitleType {
Layout.fillWidth: true contentItem: Item {
anchors.left: parent.left
text: root.text anchors.right: parent.right
color: root.enabled ? root.textColor : root.textDisabledColor anchors.leftMargin: 8 + background.width
}
implicitHeight: content.implicitHeight
CaptionTextType {
id: description ColumnLayout {
id: content
Layout.fillWidth: true
anchors.left: parent.left
text: root.descriptionText anchors.right: parent.right
color: root.enabled ? root.descriptionTextColor : root.descriptionTextDisabledColor anchors.verticalCenter: parent.verticalCenter
visible: root.descriptionText !== "" spacing: 4
}
} ListItemTitleType {
} Layout.fillWidth: true
MouseArea { text: root.text
anchors.fill: parent color: root.enabled ? root.textColor : root.textDisabledColor
cursorShape: Qt.PointingHandCursor }
enabled: false
} CaptionTextType {
id: description
Keys.onEnterPressed: { Layout.fillWidth: true
root.checked = !root.checked
} text: root.descriptionText
color: root.enabled ? root.descriptionTextColor : root.descriptionTextDisabledColor
Keys.onReturnPressed: {
root.checked = !root.checked visible: root.descriptionText !== ""
} }
}
} }
MouseArea {
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
enabled: false
}
Keys.onEnterPressed: {
root.checked = !root.checked
}
Keys.onReturnPressed: {
root.checked = !root.checked
}
}

View file

@ -1,328 +1,309 @@
import QtQuick import QtQuick
import QtQuick.Controls import QtQuick.Controls
import QtQuick.Layouts import QtQuick.Layouts
import Style 1.0 import Style 1.0
import "TextTypes" import "TextTypes"
Item { Item {
id: root id: root
property string text // property alias focusObjectName: eyeImage.objectName
property int textMaximumLineCount: 2 property string text
property int textElide: Qt.ElideRight property int textMaximumLineCount: 2
property int textElide: Qt.ElideRight
property string descriptionText
property string descriptionText
property var clickedFunction
property var clickedFunction
property string buttonImageSource
property string rightImageSource property string buttonImageSource
property string leftImageSource property string rightImageSource
property bool isLeftImageHoverEnabled: true property string leftImageSource
property bool isSmallLeftImage: false property bool isLeftImageHoverEnabled: true
property bool isSmallLeftImage: false
property alias rightButton: rightImage
property alias eyeButton: eyeImage property alias rightButton: rightImage
property FlickableType parentFlickable property alias eyeButton: eyeImage
property string textColor: AmneziaStyle.color.paleGray property string textColor: AmneziaStyle.color.paleGray
property string textDisabledColor: AmneziaStyle.color.mutedGray property string textDisabledColor: AmneziaStyle.color.mutedGray
property string descriptionColor: AmneziaStyle.color.mutedGray property string descriptionColor: AmneziaStyle.color.mutedGray
property string descriptionDisabledColor: AmneziaStyle.color.charcoalGray property string descriptionDisabledColor: AmneziaStyle.color.charcoalGray
property real textOpacity: 1.0 property real textOpacity: 1.0
property string borderFocusedColor: AmneziaStyle.color.paleGray property string borderFocusedColor: AmneziaStyle.color.paleGray
property int borderFocusedWidth: 1 property int borderFocusedWidth: 1
property string rightImageColor: AmneziaStyle.color.paleGray property string rightImageColor: AmneziaStyle.color.paleGray
property bool descriptionOnTop: false property bool descriptionOnTop: false
property bool hideDescription: true property bool hideDescription: true
property bool isFocusable: !(eyeImage.visible || rightImage.visible) // TODO: this component already has focusable items property bool isFocusable: !(eyeImage.visible || rightImage.visible) // TODO: this component already has focusable items
Keys.onTabPressed: { Keys.onTabPressed: {
FocusController.nextKeyTabItem() FocusController.nextKeyTabItem()
} }
Keys.onBacktabPressed: { Keys.onBacktabPressed: {
FocusController.previousKeyTabItem() FocusController.previousKeyTabItem()
} }
Keys.onUpPressed: { Keys.onUpPressed: {
FocusController.nextKeyUpItem() FocusController.nextKeyUpItem()
} }
Keys.onDownPressed: { Keys.onDownPressed: {
FocusController.nextKeyDownItem() FocusController.nextKeyDownItem()
} }
Keys.onLeftPressed: { Keys.onLeftPressed: {
FocusController.nextKeyLeftItem() FocusController.nextKeyLeftItem()
} }
Keys.onRightPressed: { Keys.onRightPressed: {
FocusController.nextKeyRightItem() FocusController.nextKeyRightItem()
} }
implicitWidth: content.implicitWidth + content.anchors.topMargin + content.anchors.bottomMargin implicitWidth: content.implicitWidth + content.anchors.topMargin + content.anchors.bottomMargin
implicitHeight: content.implicitHeight + content.anchors.leftMargin + content.anchors.rightMargin implicitHeight: content.implicitHeight + content.anchors.leftMargin + content.anchors.rightMargin
onFocusChanged: { MouseArea {
if (root.activeFocus) { anchors.fill: parent
if (root.parentFlickable) { cursorShape: Qt.PointingHandCursor
root.parentFlickable.ensureVisible(root) hoverEnabled: root.enabled
}
} onEntered: {
} if (rightImageSource) {
rightImageBackground.color = rightImage.hoveredColor
Connections { } else if (leftImageSource) {
target: rightImage leftImageBackground.color = rightImage.hoveredColor
function onFocusChanged() { }
if (rightImage.activeFocus) { root.textOpacity = 0.8
if (root.parentFlickable) { }
root.parentFlickable.ensureVisible(root)
} onExited: {
} if (rightImageSource) {
} rightImageBackground.color = rightImage.defaultColor
} } else if (leftImageSource) {
leftImageBackground.color = rightImage.defaultColor
MouseArea { }
anchors.fill: parent root.textOpacity = 1
cursorShape: Qt.PointingHandCursor }
hoverEnabled: root.enabled
onPressedChanged: {
onEntered: { if (rightImageSource) {
if (rightImageSource) { rightImageBackground.color = pressed ? rightImage.pressedColor : entered ? rightImage.hoveredColor : rightImage.defaultColor
rightImageBackground.color = rightImage.hoveredColor } else if (leftImageSource) {
} else if (leftImageSource) { leftImageBackground.color = pressed ? rightImage.pressedColor : entered ? rightImage.hoveredColor : rightImage.defaultColor
leftImageBackground.color = rightImage.hoveredColor }
} root.textOpacity = 0.7
root.textOpacity = 0.8 }
}
onClicked: {
onExited: { if (clickedFunction && typeof clickedFunction === "function") {
if (rightImageSource) { clickedFunction()
rightImageBackground.color = rightImage.defaultColor }
} else if (leftImageSource) { }
leftImageBackground.color = rightImage.defaultColor }
}
root.textOpacity = 1 RowLayout {
} id: content
anchors.fill: parent
onPressedChanged: { anchors.leftMargin: 16
if (rightImageSource) { anchors.rightMargin: 16
rightImageBackground.color = pressed ? rightImage.pressedColor : entered ? rightImage.hoveredColor : rightImage.defaultColor anchors.topMargin: 16
} else if (leftImageSource) { anchors.bottomMargin: 16
leftImageBackground.color = pressed ? rightImage.pressedColor : entered ? rightImage.hoveredColor : rightImage.defaultColor
} Rectangle {
root.textOpacity = 0.7 id: leftImageBackground
}
visible: leftImageSource ? true : false
onClicked: {
if (clickedFunction && typeof clickedFunction === "function") { Layout.preferredHeight: (rightImageSource || !isLeftImageHoverEnabled || isSmallLeftImage) ? 40 : 56
clickedFunction() Layout.preferredWidth: (rightImageSource || !isLeftImageHoverEnabled || isSmallLeftImage)? 40 : 56
} Layout.rightMargin: isSmallLeftImage ? 8 : (rightImageSource || !isLeftImageHoverEnabled) ? 16 : 0
}
} radius: 12
color: AmneziaStyle.color.transparent
RowLayout {
id: content Behavior on color {
anchors.fill: parent PropertyAnimation { duration: 200 }
anchors.leftMargin: 16 }
anchors.rightMargin: 16
anchors.topMargin: 16 Image {
anchors.bottomMargin: 16 id: leftImage
Rectangle { anchors.centerIn: parent
id: leftImageBackground source: leftImageSource
}
visible: leftImageSource ? true : false }
Layout.preferredHeight: (rightImageSource || !isLeftImageHoverEnabled || isSmallLeftImage) ? 40 : 56 ColumnLayout {
Layout.preferredWidth: (rightImageSource || !isLeftImageHoverEnabled || isSmallLeftImage)? 40 : 56 property real textLineHeight: 21.6
Layout.rightMargin: isSmallLeftImage ? 8 : (rightImageSource || !isLeftImageHoverEnabled) ? 16 : 0 property real descriptionTextLineHeight: 16
radius: 12 property int textPixelSize: 18
color: AmneziaStyle.color.transparent property int descriptionTextSize: 13
Behavior on color { ListItemTitleType {
PropertyAnimation { duration: 200 } text: root.text
} color: {
if (root.enabled) {
Image { return root.descriptionOnTop ? root.descriptionColor : root.textColor
id: leftImage } else {
return root.descriptionOnTop ? root.descriptionDisabledColor : root.textDisabledColor
anchors.centerIn: parent }
source: leftImageSource }
}
} maximumLineCount: root.textMaximumLineCount
elide: root.textElide
ColumnLayout {
property real textLineHeight: 21.6 opacity: root.textOpacity
property real descriptionTextLineHeight: 16
Layout.fillWidth: true
property int textPixelSize: 18
property int descriptionTextSize: 13 lineHeight: root.descriptionOnTop ? parent.descriptionTextLineHeight : parent.textLineHeight
font.pixelSize: root.descriptionOnTop ? parent.descriptionTextSize : parent.textPixelSize
ListItemTitleType { font.letterSpacing: root.descriptionOnTop ? 0.02 : 0
text: root.text
color: { horizontalAlignment: Text.AlignLeft
if (root.enabled) { verticalAlignment: Text.AlignVCenter
return root.descriptionOnTop ? root.descriptionColor : root.textColor
} else { Behavior on opacity {
return root.descriptionOnTop ? root.descriptionDisabledColor : root.textDisabledColor PropertyAnimation { duration: 200 }
} }
} }
maximumLineCount: root.textMaximumLineCount
elide: root.textElide CaptionTextType {
id: description
opacity: root.textOpacity
text: (eyeImage.visible && hideDescription) ? replaceWithAsterisks(root.descriptionText) : root.descriptionText
Layout.fillWidth: true color: {
if (root.enabled) {
lineHeight: root.descriptionOnTop ? parent.descriptionTextLineHeight : parent.textLineHeight return root.descriptionOnTop ? root.textColor : root.descriptionColor
font.pixelSize: root.descriptionOnTop ? parent.descriptionTextSize : parent.textPixelSize } else {
font.letterSpacing: root.descriptionOnTop ? 0.02 : 0 return root.descriptionOnTop ? root.textDisabledColor : root.descriptionDisabledColor
}
horizontalAlignment: Text.AlignLeft }
verticalAlignment: Text.AlignVCenter
opacity: root.textOpacity
Behavior on opacity {
PropertyAnimation { duration: 200 } visible: root.descriptionText !== ""
}
} Layout.fillWidth: true
lineHeight: root.descriptionOnTop ? parent.textLineHeight : parent.descriptionTextLineHeight
CaptionTextType { font.pixelSize: root.descriptionOnTop ? parent.textPixelSize : parent.descriptionTextSize
id: description font.letterSpacing: root.descriptionOnTop ? 0 : 0.02
text: (eyeImage.visible && hideDescription) ? replaceWithAsterisks(root.descriptionText) : root.descriptionText horizontalAlignment: Text.AlignLeft
color: { verticalAlignment: Text.AlignVCenter
if (root.enabled) {
return root.descriptionOnTop ? root.textColor : root.descriptionColor Behavior on opacity {
} else { PropertyAnimation { duration: 200 }
return root.descriptionOnTop ? root.textDisabledColor : root.descriptionDisabledColor }
}
} function replaceWithAsterisks(input) {
return '*'.repeat(input.length)
opacity: root.textOpacity }
}
visible: root.descriptionText !== "" }
Layout.fillWidth: true ImageButtonType {
id: eyeImage
lineHeight: root.descriptionOnTop ? parent.textLineHeight : parent.descriptionTextLineHeight visible: buttonImageSource !== ""
font.pixelSize: root.descriptionOnTop ? parent.textPixelSize : parent.descriptionTextSize
font.letterSpacing: root.descriptionOnTop ? 0 : 0.02 implicitWidth: 40
implicitHeight: 40
horizontalAlignment: Text.AlignLeft
verticalAlignment: Text.AlignVCenter hoverEnabled: true
image: buttonImageSource
Behavior on opacity { imageColor: rightImageColor
PropertyAnimation { duration: 200 }
} Layout.alignment: Qt.AlignRight
function replaceWithAsterisks(input) { Rectangle {
return '*'.repeat(input.length) id: eyeImageBackground
} anchors.fill: parent
} radius: 12
} color: AmneziaStyle.color.transparent
ImageButtonType { Behavior on color {
id: eyeImage PropertyAnimation { duration: 200 }
visible: buttonImageSource !== "" }
}
implicitWidth: 40
implicitHeight: 40 onClicked: {
hideDescription = !hideDescription
hoverEnabled: true }
image: buttonImageSource
imageColor: rightImageColor Keys.onEnterPressed: {
clicked()
Layout.alignment: Qt.AlignRight }
Rectangle { Keys.onReturnPressed: {
id: eyeImageBackground clicked()
anchors.fill: parent }
radius: 12 }
color: AmneziaStyle.color.transparent
ImageButtonType {
Behavior on color { id: rightImage
PropertyAnimation { duration: 200 }
} implicitWidth: 40
} implicitHeight: 40
onClicked: { hoverEnabled: false
hideDescription = !hideDescription image: rightImageSource
} imageColor: rightImageColor
visible: rightImageSource ? true : false
Keys.onEnterPressed: {
clicked() Layout.alignment: Qt.AlignRight
}
Rectangle {
Keys.onReturnPressed: { id: rightImageBackground
clicked() anchors.fill: parent
} radius: 12
} color: AmneziaStyle.color.transparent
ImageButtonType { Behavior on color {
id: rightImage PropertyAnimation { duration: 200 }
}
implicitWidth: 40 }
implicitHeight: 40 onClicked: {
if (clickedFunction && typeof clickedFunction === "function") {
hoverEnabled: false clickedFunction()
image: rightImageSource }
imageColor: rightImageColor }
visible: rightImageSource ? true : false }
}
Layout.alignment: Qt.AlignRight
Rectangle {
Rectangle { id: background
id: rightImageBackground anchors.fill: root
anchors.fill: parent color: AmneziaStyle.color.transparent
radius: 12
color: AmneziaStyle.color.transparent border.color: root.activeFocus ? root.borderFocusedColor : AmneziaStyle.color.transparent
border.width: root.activeFocus ? root.borderFocusedWidth : 0
Behavior on color {
PropertyAnimation { duration: 200 }
} Behavior on color {
} PropertyAnimation { duration: 200 }
onClicked: { }
if (clickedFunction && typeof clickedFunction === "function") { }
clickedFunction()
} Keys.onEnterPressed: {
} if (clickedFunction && typeof clickedFunction === "function") {
} clickedFunction()
} }
}
Rectangle {
id: background Keys.onReturnPressed: {
anchors.fill: root if (clickedFunction && typeof clickedFunction === "function") {
color: AmneziaStyle.color.transparent clickedFunction()
}
border.color: root.activeFocus ? root.borderFocusedColor : AmneziaStyle.color.transparent }
border.width: root.activeFocus ? root.borderFocusedWidth : 0 }
Behavior on color {
PropertyAnimation { duration: 200 }
}
}
Keys.onEnterPressed: {
if (clickedFunction && typeof clickedFunction === "function") {
clickedFunction()
}
}
Keys.onReturnPressed: {
if (clickedFunction && typeof clickedFunction === "function") {
clickedFunction()
}
}
}

View file

@ -6,33 +6,16 @@ ListView {
property bool isFocusable: true property bool isFocusable: true
Keys.onTabPressed: {
FocusController.nextKeyTabItem()
}
Keys.onBacktabPressed: {
FocusController.previousKeyTabItem()
}
Keys.onUpPressed: {
FocusController.nextKeyUpItem()
}
Keys.onDownPressed: {
FocusController.nextKeyDownItem()
}
Keys.onLeftPressed: {
FocusController.nextKeyLeftItem()
}
Keys.onRightPressed: {
FocusController.nextKeyRightItem()
}
ScrollBar.vertical: ScrollBarType {} ScrollBar.vertical: ScrollBarType {}
clip: true clip: true
reuseItems: true reuseItems: true
snapMode: ListView.SnapToItem
function findChildWithObjectName(items, name) {
for (var i = 0; i < items.length; ++i) {
if (items[i].objectName === name)
return items[i];
}
return null;
}
} }

View file

@ -6,7 +6,7 @@ import Style 1.0
import "TextTypes" import "TextTypes"
ListView { ListViewType {
id: root id: root
property var rootWidth property var rootWidth
@ -25,13 +25,6 @@ ListView {
width: rootWidth width: rootWidth
height: root.contentItem.height height: root.contentItem.height
clip: true
reuseItems: true
property bool isFocusable: true
ScrollBar.vertical: ScrollBarType {}
ButtonGroup { ButtonGroup {
id: buttonGroup id: buttonGroup
} }

View file

@ -1,172 +1,162 @@
import QtQuick import QtQuick
import QtQuick.Controls import QtQuick.Controls
import QtQuick.Layouts import QtQuick.Layouts
import Style 1.0 import Style 1.0
import "TextTypes" import "TextTypes"
Switch { Switch {
id: root id: root
property alias descriptionText: description.text property alias descriptionText: description.text
property string descriptionTextColor: AmneziaStyle.color.mutedGray property string descriptionTextColor: AmneziaStyle.color.mutedGray
property string descriptionTextDisabledColor: AmneziaStyle.color.charcoalGray property string descriptionTextDisabledColor: AmneziaStyle.color.charcoalGray
property string textColor: AmneziaStyle.color.paleGray property string textColor: AmneziaStyle.color.paleGray
property string textDisabledColor: AmneziaStyle.color.mutedGray property string textDisabledColor: AmneziaStyle.color.mutedGray
property string checkedIndicatorColor: AmneziaStyle.color.richBrown property string checkedIndicatorColor: AmneziaStyle.color.richBrown
property string defaultIndicatorColor: AmneziaStyle.color.transparent property string defaultIndicatorColor: AmneziaStyle.color.transparent
property string checkedDisabledIndicatorColor: AmneziaStyle.color.deepBrown property string checkedDisabledIndicatorColor: AmneziaStyle.color.deepBrown
property string borderFocusedColor: AmneziaStyle.color.paleGray property string borderFocusedColor: AmneziaStyle.color.paleGray
property int borderFocusedWidth: 1 property int borderFocusedWidth: 1
property string checkedIndicatorBorderColor: AmneziaStyle.color.richBrown property string checkedIndicatorBorderColor: AmneziaStyle.color.richBrown
property string defaultIndicatorBorderColor: AmneziaStyle.color.charcoalGray property string defaultIndicatorBorderColor: AmneziaStyle.color.charcoalGray
property string checkedDisabledIndicatorBorderColor: AmneziaStyle.color.deepBrown property string checkedDisabledIndicatorBorderColor: AmneziaStyle.color.deepBrown
property string checkedInnerCircleColor: AmneziaStyle.color.goldenApricot property string checkedInnerCircleColor: AmneziaStyle.color.goldenApricot
property string defaultInnerCircleColor: AmneziaStyle.color.paleGray property string defaultInnerCircleColor: AmneziaStyle.color.paleGray
property string checkedDisabledInnerCircleColor: AmneziaStyle.color.mutedBrown property string checkedDisabledInnerCircleColor: AmneziaStyle.color.mutedBrown
property string defaultDisabledInnerCircleColor: AmneziaStyle.color.charcoalGray property string defaultDisabledInnerCircleColor: AmneziaStyle.color.charcoalGray
property string hoveredIndicatorBackgroundColor: AmneziaStyle.color.translucentWhite property string hoveredIndicatorBackgroundColor: AmneziaStyle.color.translucentWhite
property string defaultIndicatorBackgroundColor: AmneziaStyle.color.transparent property string defaultIndicatorBackgroundColor: AmneziaStyle.color.transparent
property bool isFocusable: true property bool isFocusable: true
Keys.onTabPressed: { Keys.onTabPressed: {
FocusController.nextKeyTabItem() FocusController.nextKeyTabItem()
} }
Keys.onBacktabPressed: { Keys.onBacktabPressed: {
FocusController.previousKeyTabItem() FocusController.previousKeyTabItem()
} }
Keys.onUpPressed: { Keys.onUpPressed: {
FocusController.nextKeyUpItem() FocusController.nextKeyUpItem()
} }
Keys.onDownPressed: { Keys.onDownPressed: {
FocusController.nextKeyDownItem() FocusController.nextKeyDownItem()
} }
Keys.onLeftPressed: { Keys.onLeftPressed: {
FocusController.nextKeyLeftItem() FocusController.nextKeyLeftItem()
} }
Keys.onRightPressed: { Keys.onRightPressed: {
FocusController.nextKeyRightItem() FocusController.nextKeyRightItem()
} }
hoverEnabled: enabled ? true : false hoverEnabled: enabled ? true : false
focusPolicy: Qt.TabFocus focusPolicy: Qt.TabFocus
property FlickableType parentFlickable: null indicator: Rectangle {
id: switcher
onFocusChanged: {
if (root.activeFocus) { anchors.right: parent.right
if (root.parentFlickable) { anchors.verticalCenter: parent.verticalCenter
root.parentFlickable.ensureVisible(root)
} implicitWidth: 52
} implicitHeight: 32
}
radius: 16
indicator: Rectangle { color: root.checked ? (root.enabled ? root.checkedIndicatorColor : root.checkedDisabledIndicatorColor)
id: switcher : root.defaultIndicatorColor
anchors.right: parent.right border.color: root.activeFocus ? root.borderFocusedColor : (root.checked ? (root.enabled ? root.checkedIndicatorBorderColor : root.checkedDisabledIndicatorBorderColor)
anchors.verticalCenter: parent.verticalCenter : root.defaultIndicatorBorderColor)
implicitWidth: 52 Behavior on color {
implicitHeight: 32 PropertyAnimation { duration: 200 }
}
radius: 16 Behavior on border.color {
color: root.checked ? (root.enabled ? root.checkedIndicatorColor : root.checkedDisabledIndicatorColor) PropertyAnimation { duration: 200 }
: root.defaultIndicatorColor }
border.color: root.activeFocus ? root.borderFocusedColor : (root.checked ? (root.enabled ? root.checkedIndicatorBorderColor : root.checkedDisabledIndicatorBorderColor) Rectangle {
: root.defaultIndicatorBorderColor) id: innerCircle
Behavior on color { anchors.verticalCenter: parent.verticalCenter
PropertyAnimation { duration: 200 } x: root.checked ? parent.width - width - 4 : 8
} width: root.checked ? 24 : 16
Behavior on border.color { height: root.checked ? 24 : 16
PropertyAnimation { duration: 200 } radius: 23
} color: root.checked ? (root.enabled ? root.checkedInnerCircleColor : root.checkedDisabledInnerCircleColor)
: (root.enabled ? root.defaultInnerCircleColor : root.defaultDisabledInnerCircleColor)
Rectangle {
id: innerCircle Behavior on x {
PropertyAnimation { duration: 200 }
anchors.verticalCenter: parent.verticalCenter }
x: root.checked ? parent.width - width - 4 : 8 }
width: root.checked ? 24 : 16
height: root.checked ? 24 : 16 Rectangle {
radius: 23 anchors.centerIn: innerCircle
color: root.checked ? (root.enabled ? root.checkedInnerCircleColor : root.checkedDisabledInnerCircleColor) width: 40
: (root.enabled ? root.defaultInnerCircleColor : root.defaultDisabledInnerCircleColor) height: 40
radius: 23
Behavior on x { color: root.hovered ? root.hoveredIndicatorBackgroundColor : root.defaultIndicatorBackgroundColor
PropertyAnimation { duration: 200 }
} Behavior on color {
} PropertyAnimation { duration: 200 }
}
Rectangle { }
anchors.centerIn: innerCircle }
width: 40
height: 40 contentItem: ColumnLayout {
radius: 23 id: content
color: root.hovered ? root.hoveredIndicatorBackgroundColor : root.defaultIndicatorBackgroundColor
anchors.verticalCenter: parent.verticalCenter
Behavior on color { anchors.left: parent.left
PropertyAnimation { duration: 200 }
} ListItemTitleType {
} Layout.fillWidth: true
} rightPadding: indicator.width
contentItem: ColumnLayout { text: root.text
id: content color: root.enabled ? root.textColor : root.textDisabledColor
}
anchors.verticalCenter: parent.verticalCenter
anchors.left: parent.left CaptionTextType {
id: description
ListItemTitleType {
Layout.fillWidth: true Layout.fillWidth: true
rightPadding: indicator.width rightPadding: indicator.width
text: root.text color: root.enabled ? root.descriptionTextColor : root.descriptionTextDisabledColor
color: root.enabled ? root.textColor : root.textDisabledColor
} visible: text !== ""
}
CaptionTextType { }
id: description
MouseArea {
Layout.fillWidth: true anchors.fill: parent
rightPadding: indicator.width cursorShape: Qt.PointingHandCursor
enabled: false
color: root.enabled ? root.descriptionTextColor : root.descriptionTextDisabledColor }
visible: text !== "" Keys.onEnterPressed: event => handleSwitch(event)
} Keys.onReturnPressed: event => handleSwitch(event)
} Keys.onSpacePressed: event => handleSwitch(event)
MouseArea { function handleSwitch(event) {
anchors.fill: parent if (!event.isAutoRepeat) {
cursorShape: Qt.PointingHandCursor root.checked = !root.checked
enabled: false root.checkedChanged()
} }
event.accepted = true
Keys.onEnterPressed: event => handleSwitch(event) }
Keys.onReturnPressed: event => handleSwitch(event) }
Keys.onSpacePressed: event => handleSwitch(event)
function handleSwitch(event) {
if (!event.isAutoRepeat) {
root.checked = !root.checked
root.checkedChanged()
}
event.accepted = true
}
}

View file

@ -1,118 +1,135 @@
import QtQuick import QtQuick
import QtQuick.Controls import QtQuick.Controls
import Style 1.0 import Style 1.0
Rectangle { Rectangle {
id: root id: root
property string placeholderText property string placeholderText
property string text property string text
property alias textArea: textArea property alias textArea: textArea
property alias textAreaText: textArea.text property alias textAreaText: textArea.text
property string borderHoveredColor: AmneziaStyle.color.charcoalGray property string borderHoveredColor: AmneziaStyle.color.charcoalGray
property string borderNormalColor: AmneziaStyle.color.slateGray property string borderNormalColor: AmneziaStyle.color.slateGray
property string borderFocusedColor: AmneziaStyle.color.paleGray property string borderFocusedColor: AmneziaStyle.color.paleGray
height: 148 height: 148
color: AmneziaStyle.color.onyxBlack color: AmneziaStyle.color.onyxBlack
border.width: 1 border.width: 1
border.color: getBorderColor(borderNormalColor) border.color: getBorderColor(borderNormalColor)
radius: 16 radius: 16
property FlickableType parentFlickable: null MouseArea {
onFocusChanged: { id: parentMouse
if (root.activeFocus) { anchors.fill: parent
if (root.parentFlickable) { cursorShape: Qt.IBeamCursor
root.parentFlickable.ensureVisible(root) onClicked: textArea.forceActiveFocus()
} hoverEnabled: true
}
} FlickableType {
id: fl
MouseArea { interactive: false
id: parentMouse
anchors.fill: parent anchors.top: parent.top
cursorShape: Qt.IBeamCursor anchors.bottom: parent.bottom
onClicked: textArea.forceActiveFocus() contentHeight: textArea.implicitHeight
hoverEnabled: true TextArea {
id: textArea
FlickableType {
id: fl width: parent.width
interactive: false
topPadding: 16
anchors.top: parent.top leftPadding: 16
anchors.bottom: parent.bottom anchors.topMargin: 16
contentHeight: textArea.implicitHeight anchors.bottomMargin: 16
TextArea {
id: textArea property bool isFocusable: true
width: parent.width Keys.onTabPressed: {
FocusController.nextKeyTabItem()
topPadding: 16 }
leftPadding: 16
anchors.topMargin: 16 Keys.onBacktabPressed: {
anchors.bottomMargin: 16 FocusController.previousKeyTabItem()
}
color: AmneziaStyle.color.paleGray
selectionColor: AmneziaStyle.color.richBrown Keys.onUpPressed: {
selectedTextColor: AmneziaStyle.color.paleGray FocusController.nextKeyUpItem()
placeholderTextColor: AmneziaStyle.color.mutedGray }
font.pixelSize: 16 Keys.onDownPressed: {
font.weight: Font.Medium FocusController.nextKeyDownItem()
font.family: "PT Root UI VF" }
placeholderText: root.placeholderText Keys.onLeftPressed: {
text: root.text FocusController.nextKeyLeftItem()
}
onCursorVisibleChanged: {
if (textArea.cursorVisible) { Keys.onRightPressed: {
fl.interactive = true FocusController.nextKeyRightItem()
} else { }
fl.interactive = false
} color: AmneziaStyle.color.paleGray
} selectionColor: AmneziaStyle.color.richBrown
selectedTextColor: AmneziaStyle.color.paleGray
wrapMode: Text.Wrap placeholderTextColor: AmneziaStyle.color.mutedGray
MouseArea { font.pixelSize: 16
id: textAreaMouse font.weight: Font.Medium
anchors.fill: parent font.family: "PT Root UI VF"
acceptedButtons: Qt.RightButton
hoverEnabled: true placeholderText: root.placeholderText
onClicked: { text: root.text
fl.interactive = true
contextMenu.open() onCursorVisibleChanged: {
} if (textArea.cursorVisible) {
} fl.interactive = true
} else {
onFocusChanged: { fl.interactive = false
root.border.color = getBorderColor(borderNormalColor) }
} }
ContextMenuType { wrapMode: Text.Wrap
id: contextMenu
textObj: textArea MouseArea {
} id: textAreaMouse
} anchors.fill: parent
} acceptedButtons: Qt.RightButton
hoverEnabled: true
onPressed: { onClicked: {
root.border.color = getBorderColor(borderFocusedColor) fl.interactive = true
} contextMenu.open()
}
onExited: { }
root.border.color = getBorderColor(borderNormalColor)
} onFocusChanged: {
root.border.color = getBorderColor(borderNormalColor)
onEntered: { }
root.border.color = getBorderColor(borderHoveredColor)
} ContextMenuType {
} id: contextMenu
textObj: textArea
}
function getBorderColor(noneFocusedColor) { }
return textArea.focus ? root.borderFocusedColor : noneFocusedColor }
}
} onPressed: {
root.border.color = getBorderColor(borderFocusedColor)
}
onExited: {
root.border.color = getBorderColor(borderNormalColor)
}
onEntered: {
root.border.color = getBorderColor(borderHoveredColor)
}
}
function getBorderColor(noneFocusedColor) {
return textArea.focus ? root.borderFocusedColor : noneFocusedColor
}
}

View file

@ -1,180 +1,171 @@
import QtQuick import QtQuick
import QtQuick.Controls import QtQuick.Controls
import QtQuick.Layouts import QtQuick.Layouts
import Style 1.0 import Style 1.0
import "TextTypes" import "TextTypes"
Rectangle { Rectangle {
id: root id: root
property string placeholderText property string placeholderText
property string text property string text
property string headerText property string headerText
property alias textArea: textArea property alias textArea: textArea
property alias textAreaText: textArea.text property alias textAreaText: textArea.text
property string borderHoveredColor: AmneziaStyle.color.charcoalGray property string borderHoveredColor: AmneziaStyle.color.charcoalGray
property string borderNormalColor: AmneziaStyle.color.slateGray property string borderNormalColor: AmneziaStyle.color.slateGray
property string borderFocusedColor: AmneziaStyle.color.paleGray property string borderFocusedColor: AmneziaStyle.color.paleGray
property string firstButtonImage property string firstButtonImage
property string secondButtonImage property string secondButtonImage
property var firstButtonClickedFunc property var firstButtonClickedFunc
property var secondButtonClickedFunc property var secondButtonClickedFunc
height: 148 height: 148
color: AmneziaStyle.color.onyxBlack color: AmneziaStyle.color.onyxBlack
border.width: 1 border.width: 1
border.color: getBorderColor(borderNormalColor) border.color: getBorderColor(borderNormalColor)
radius: 16 radius: 16
property FlickableType parentFlickable: null MouseArea {
onFocusChanged: { id: parentMouse
if (root.activeFocus) { anchors.fill: parent
if (root.parentFlickable) { cursorShape: Qt.IBeamCursor
root.parentFlickable.ensureVisible(root) onClicked: textArea.forceActiveFocus()
} hoverEnabled: true
}
} ColumnLayout {
anchors.fill: parent
MouseArea { anchors.margins: 16
id: parentMouse spacing: 0
anchors.fill: parent
cursorShape: Qt.IBeamCursor LabelTextType {
onClicked: textArea.forceActiveFocus() Layout.fillWidth: true
hoverEnabled: true text: root.headerText
}
ColumnLayout {
anchors.fill: parent TextArea {
anchors.margins: 16 id: textArea
spacing: 0
Layout.fillWidth: true
LabelTextType { Layout.fillHeight: true
Layout.fillWidth: true
text: root.headerText leftPadding: 0
} Layout.bottomMargin: 16
TextArea { color: AmneziaStyle.color.paleGray
id: textArea selectionColor: AmneziaStyle.color.richBrown
selectedTextColor: AmneziaStyle.color.paleGray
Layout.fillWidth: true placeholderTextColor: AmneziaStyle.color.mutedGray
Layout.fillHeight: true
font.pixelSize: 16
leftPadding: 0 font.weight: Font.Medium
Layout.bottomMargin: 16 font.family: "PT Root UI VF"
color: AmneziaStyle.color.paleGray placeholderText: root.placeholderText
selectionColor: AmneziaStyle.color.richBrown text: root.text
selectedTextColor: AmneziaStyle.color.paleGray
placeholderTextColor: AmneziaStyle.color.mutedGray onCursorVisibleChanged: {
if (textArea.cursorVisible) {
font.pixelSize: 16 fl.interactive = true
font.weight: Font.Medium } else {
font.family: "PT Root UI VF" fl.interactive = false
}
placeholderText: root.placeholderText }
text: root.text
wrapMode: Text.Wrap
onCursorVisibleChanged: {
if (textArea.cursorVisible) { MouseArea {
fl.interactive = true id: textAreaMouse
} else { anchors.fill: parent
fl.interactive = false acceptedButtons: Qt.RightButton
} hoverEnabled: true
} onClicked: {
fl.interactive = true
wrapMode: Text.Wrap contextMenu.open()
}
MouseArea { }
id: textAreaMouse
anchors.fill: parent onFocusChanged: {
acceptedButtons: Qt.RightButton root.border.color = getBorderColor(borderNormalColor)
hoverEnabled: true }
onClicked: {
fl.interactive = true ContextMenuType {
contextMenu.open() id: contextMenu
} textObj: textArea
} }
}
onFocusChanged: {
root.border.color = getBorderColor(borderNormalColor) RowLayout {
} Layout.fillWidth: true
Layout.leftMargin: -8
ContextMenuType { spacing: 0
id: contextMenu ImageButtonType {
textObj: textArea id: firstButton
} visible: root.firstButtonImage !== ""
}
imageColor: AmneziaStyle.color.paleGray
RowLayout {
Layout.fillWidth: true image: root.firstButtonImage
Layout.leftMargin: -8 onClicked: function() {
spacing: 0 if (root.firstButtonClickedFunc && typeof root.firstButtonClickedFunc === "function") {
ImageButtonType { root.firstButtonClickedFunc()
id: firstButton }
visible: root.firstButtonImage !== "" }
}
imageColor: AmneziaStyle.color.paleGray
ImageButtonType {
image: root.firstButtonImage id: secondButton
onClicked: function() { visible: root.secondButtonImage !== ""
if (root.firstButtonClickedFunc && typeof root.firstButtonClickedFunc === "function") {
root.firstButtonClickedFunc() imageColor: AmneziaStyle.color.paleGray
}
} image: root.secondButtonImage
} onClicked: function() {
if (root.secondButtonClickedFunc && typeof root.secondButtonClickedFunc === "function") {
ImageButtonType { root.secondButtonClickedFunc()
id: secondButton }
visible: root.secondButtonImage !== "" }
}
imageColor: AmneziaStyle.color.paleGray
Item {
image: root.secondButtonImage Layout.fillWidth: true
onClicked: function() { }
if (root.secondButtonClickedFunc && typeof root.secondButtonClickedFunc === "function") {
root.secondButtonClickedFunc() ImageButtonType {
} id: resetButton
} imageColor: AmneziaStyle.color.paleGray
}
visible: root.textAreaText !== ""
Item { image: "qrc:/images/controls/close.svg"
Layout.fillWidth: true
} onClicked: function() {
root.textAreaText = ""
ImageButtonType { textArea.focus = true
id: resetButton }
imageColor: AmneziaStyle.color.paleGray }
}
visible: root.textAreaText !== "" }
image: "qrc:/images/controls/close.svg"
onPressed: {
onClicked: function() { root.border.color = getBorderColor(borderFocusedColor)
root.textAreaText = "" }
textArea.focus = true
} onExited: {
} root.border.color = getBorderColor(borderNormalColor)
} }
}
onEntered: {
onPressed: { root.border.color = getBorderColor(borderHoveredColor)
root.border.color = getBorderColor(borderFocusedColor) }
} }
onExited: {
root.border.color = getBorderColor(borderNormalColor) function getBorderColor(noneFocusedColor) {
} return textArea.focus ? root.borderFocusedColor : noneFocusedColor
}
onEntered: { }
root.border.color = getBorderColor(borderHoveredColor)
}
}
function getBorderColor(noneFocusedColor) {
return textArea.focus ? root.borderFocusedColor : noneFocusedColor
}
}

View file

@ -1,233 +1,220 @@
import QtQuick import QtQuick
import QtQuick.Controls import QtQuick.Controls
import QtQuick.Layouts import QtQuick.Layouts
import Style 1.0 import Style 1.0
import "TextTypes" import "TextTypes"
Item { Item {
id: root id: root
property string headerText property string headerText
property string headerTextDisabledColor: AmneziaStyle.color.charcoalGray property string headerTextDisabledColor: AmneziaStyle.color.charcoalGray
property string headerTextColor: AmneziaStyle.color.mutedGray property string headerTextColor: AmneziaStyle.color.mutedGray
property alias errorText: errorField.text property alias errorText: errorField.text
property bool checkEmptyText: false property bool checkEmptyText: false
property bool rightButtonClickedOnEnter: false property bool rightButtonClickedOnEnter: false
property string buttonText property string buttonText
property string buttonImageSource property string buttonImageSource
property var clickedFunc property var clickedFunc
property alias textField: textField property alias textField: textField
property string textFieldTextColor: AmneziaStyle.color.paleGray property string textFieldTextColor: AmneziaStyle.color.paleGray
property string textFieldTextDisabledColor: AmneziaStyle.color.mutedGray property string textFieldTextDisabledColor: AmneziaStyle.color.mutedGray
property bool textFieldEditable: true property bool textFieldEditable: true
property string borderColor: AmneziaStyle.color.slateGray property string borderColor: AmneziaStyle.color.slateGray
property string borderFocusedColor: AmneziaStyle.color.paleGray property string borderFocusedColor: AmneziaStyle.color.paleGray
property string backgroundColor: AmneziaStyle.color.onyxBlack property string backgroundColor: AmneziaStyle.color.onyxBlack
property string backgroundDisabledColor: AmneziaStyle.color.transparent property string backgroundDisabledColor: AmneziaStyle.color.transparent
property string bgBorderHoveredColor: AmneziaStyle.color.charcoalGray property string bgBorderHoveredColor: AmneziaStyle.color.charcoalGray
implicitWidth: content.implicitWidth implicitWidth: content.implicitWidth
implicitHeight: content.implicitHeight implicitHeight: content.implicitHeight
property FlickableType parentFlickable ColumnLayout {
id: content
Connections { anchors.fill: parent
target: textField
function onFocusChanged() { Rectangle {
if (textField.activeFocus) { id: backgroud
if (root.parentFlickable) { Layout.fillWidth: true
root.parentFlickable.ensureVisible(root) Layout.preferredHeight: input.implicitHeight
} color: root.enabled ? root.backgroundColor : root.backgroundDisabledColor
} radius: 16
} border.color: getBackgroundBorderColor(root.borderColor)
} border.width: 1
ColumnLayout { Behavior on border.color {
id: content PropertyAnimation { duration: 200 }
anchors.fill: parent }
Rectangle { RowLayout {
id: backgroud id: input
Layout.fillWidth: true anchors.fill: backgroud
Layout.preferredHeight: input.implicitHeight ColumnLayout {
color: root.enabled ? root.backgroundColor : root.backgroundDisabledColor Layout.margins: 16
radius: 16 LabelTextType {
border.color: getBackgroundBorderColor(root.borderColor) text: root.headerText
border.width: 1 color: root.enabled ? root.headerTextColor : root.headerTextDisabledColor
Behavior on border.color { visible: text !== ""
PropertyAnimation { duration: 200 }
} Layout.fillWidth: true
}
RowLayout {
id: input TextField {
anchors.fill: backgroud id: textField
ColumnLayout {
Layout.margins: 16 property bool isFocusable: true
LabelTextType {
text: root.headerText Keys.onTabPressed: {
color: root.enabled ? root.headerTextColor : root.headerTextDisabledColor FocusController.nextKeyTabItem()
}
visible: text !== ""
Keys.onBacktabPressed: {
Layout.fillWidth: true FocusController.previousKeyTabItem()
} }
TextField { enabled: root.textFieldEditable
id: textField color: root.enabled ? root.textFieldTextColor : root.textFieldTextDisabledColor
property bool isFocusable: true inputMethodHints: Qt.ImhNoAutoUppercase | Qt.ImhSensitiveData | Qt.ImhNoPredictiveText
Keys.onTabPressed: { placeholderTextColor: AmneziaStyle.color.charcoalGray
FocusController.nextKeyTabItem()
} selectionColor: AmneziaStyle.color.richBrown
selectedTextColor: AmneziaStyle.color.paleGray
Keys.onBacktabPressed: {
FocusController.previousKeyTabItem() font.pixelSize: 16
} font.weight: 400
font.family: "PT Root UI VF"
enabled: root.textFieldEditable
color: root.enabled ? root.textFieldTextColor : root.textFieldTextDisabledColor height: 24
Layout.fillWidth: true
inputMethodHints: Qt.ImhNoAutoUppercase | Qt.ImhSensitiveData | Qt.ImhNoPredictiveText
topPadding: 0
placeholderTextColor: AmneziaStyle.color.charcoalGray rightPadding: 0
leftPadding: 0
selectionColor: AmneziaStyle.color.richBrown bottomPadding: 0
selectedTextColor: AmneziaStyle.color.paleGray
background: Rectangle {
font.pixelSize: 16 anchors.fill: parent
font.weight: 400 color: root.enabled ? root.backgroundColor : root.backgroundDisabledColor
font.family: "PT Root UI VF" }
height: 24 onTextChanged: {
Layout.fillWidth: true root.errorText = ""
}
topPadding: 0
rightPadding: 0 onActiveFocusChanged: {
leftPadding: 0 if (root.checkEmptyText && text === "") {
bottomPadding: 0 root.errorText = qsTr("The field can't be empty")
}
background: Rectangle { }
anchors.fill: parent
color: root.enabled ? root.backgroundColor : root.backgroundDisabledColor MouseArea {
} anchors.fill: parent
acceptedButtons: Qt.RightButton
onTextChanged: { onClicked: contextMenu.open()
root.errorText = "" enabled: true
} }
onActiveFocusChanged: { ContextMenuType {
if (root.checkEmptyText && text === "") { id: contextMenu
root.errorText = qsTr("The field can't be empty") textObj: textField
} }
}
onFocusChanged: {
MouseArea { backgroud.border.color = getBackgroundBorderColor(root.borderColor)
anchors.fill: parent }
acceptedButtons: Qt.RightButton }
onClicked: contextMenu.open() }
enabled: true }
} }
ContextMenuType { SmallTextType {
id: contextMenu id: errorField
textObj: textField
} text: root.errorText
visible: root.errorText !== ""
onFocusChanged: { color: AmneziaStyle.color.vibrantRed
backgroud.border.color = getBackgroundBorderColor(root.borderColor)
} Layout.fillWidth: true
} }
} }
}
} MouseArea {
anchors.fill: root
SmallTextType { cursorShape: Qt.IBeamCursor
id: errorField
hoverEnabled: true
text: root.errorText
visible: root.errorText !== "" onPressed: function(mouse) {
color: AmneziaStyle.color.vibrantRed textField.forceActiveFocus()
mouse.accepted = false
Layout.fillWidth: true
} backgroud.border.color = getBackgroundBorderColor(root.borderColor)
} }
MouseArea { onEntered: {
anchors.fill: root backgroud.border.color = getBackgroundBorderColor(bgBorderHoveredColor)
cursorShape: Qt.IBeamCursor }
hoverEnabled: true
onExited: {
onPressed: function(mouse) { backgroud.border.color = getBackgroundBorderColor(root.borderColor)
textField.forceActiveFocus() }
mouse.accepted = false }
backgroud.border.color = getBackgroundBorderColor(root.borderColor) BasicButtonType {
} visible: (root.buttonText !== "") || (root.buttonImageSource !== "")
onEntered: { focusPolicy: Qt.NoFocus
backgroud.border.color = getBackgroundBorderColor(bgBorderHoveredColor) text: root.buttonText
} leftImageSource: root.buttonImageSource
anchors.top: content.top
onExited: { anchors.bottom: content.bottom
backgroud.border.color = getBackgroundBorderColor(root.borderColor) anchors.right: content.right
}
} height: content.implicitHeight
width: content.implicitHeight
BasicButtonType { squareLeftSide: true
visible: (root.buttonText !== "") || (root.buttonImageSource !== "")
clickedFunc: function() {
focusPolicy: Qt.NoFocus if (root.clickedFunc && typeof root.clickedFunc === "function") {
text: root.buttonText root.clickedFunc()
leftImageSource: root.buttonImageSource }
}
anchors.top: content.top }
anchors.bottom: content.bottom
anchors.right: content.right function getBackgroundBorderColor(noneFocusedColor) {
return textField.focus ? root.borderFocusedColor : noneFocusedColor
height: content.implicitHeight }
width: content.implicitHeight
squareLeftSide: true Keys.onEnterPressed: {
if (root.rightButtonClickedOnEnter && root.clickedFunc && typeof root.clickedFunc === "function") {
clickedFunc: function() { clickedFunc()
if (root.clickedFunc && typeof root.clickedFunc === "function") { }
root.clickedFunc()
} // if (KeyNavigation.tab) {
} // KeyNavigation.tab.forceActiveFocus();
} // }
}
function getBackgroundBorderColor(noneFocusedColor) {
return textField.focus ? root.borderFocusedColor : noneFocusedColor Keys.onReturnPressed: {
} if (root.rightButtonClickedOnEnter &&root.clickedFunc && typeof root.clickedFunc === "function") {
clickedFunc()
Keys.onEnterPressed: { }
if (root.rightButtonClickedOnEnter && root.clickedFunc && typeof root.clickedFunc === "function") {
clickedFunc() // if (KeyNavigation.tab) {
} // KeyNavigation.tab.forceActiveFocus();
// }
// if (KeyNavigation.tab) { }
// KeyNavigation.tab.forceActiveFocus(); }
// }
}
Keys.onReturnPressed: {
if (root.rightButtonClickedOnEnter &&root.clickedFunc && typeof root.clickedFunc === "function") {
clickedFunc()
}
// if (KeyNavigation.tab) {
// KeyNavigation.tab.forceActiveFocus();
// }
}
}

View file

@ -20,7 +20,9 @@ PageType {
SortFilterProxyModel { SortFilterProxyModel {
id: proxyServersModel id: proxyServersModel
sourceModel: ServersModel sourceModel: ServersModel
filters: [ filters: [
ValueFilter { ValueFilter {
roleName: "isCurrentlyProcessed" roleName: "isCurrentlyProcessed"
@ -29,67 +31,55 @@ PageType {
] ]
} }
FlickableType { ListViewType {
id: fl id: listView
anchors.fill: parent anchors.fill: parent
contentHeight: content.height
Column { spacing: 16
id: content
anchors.top: parent.top model: proxyServersModel
anchors.left: parent.left
anchors.right: parent.right
spacing: 16 delegate: ColumnLayout {
width: listView.width
Repeater { BaseHeaderType {
model: proxyServersModel Layout.fillWidth: true
delegate: Item { Layout.topMargin: 20
implicitWidth: parent.width Layout.leftMargin: 16
implicitHeight: delegateContent.implicitHeight Layout.rightMargin: 16
ColumnLayout { headerText: qsTr("Removing services from %1").arg(name)
id: delegateContent }
anchors.fill: parent ProgressBarType {
anchors.rightMargin: 16 id: progressBar
anchors.leftMargin: 16
BaseHeaderType { Layout.fillWidth: true
Layout.fillWidth: true Layout.topMargin: 32
Layout.topMargin: 20 Layout.leftMargin: 16
Layout.rightMargin: 16
headerText: qsTr("Removing services from %1").arg(name) Timer {
} id: timer
ProgressBarType { interval: 300
id: progressBar repeat: true
running: true
Layout.fillWidth: true onTriggered: {
Layout.topMargin: 32 progressBar.value += 0.003
Timer {
id: timer
interval: 300
repeat: true
running: true
onTriggered: {
progressBar.value += 0.003
}
}
}
ParagraphTextType {
Layout.fillWidth: true
Layout.topMargin: 8
text: qsTr("Usually it takes no more than 5 minutes")
}
} }
} }
} }
ParagraphTextType {
Layout.fillWidth: true
Layout.topMargin: 8
Layout.leftMargin: 16
Layout.rightMargin: 16
text: qsTr("Usually it takes no more than 5 minutes")
}
} }
} }
} }

View file

@ -25,23 +25,17 @@ PageType {
anchors.topMargin: 20 anchors.topMargin: 20
} }
ListView { ListViewType {
id: listView id: listView
anchors.top: backButton.bottom anchors.top: backButton.bottom
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
anchors.right: parent.right anchors.right: parent.right
anchors.left: parent.left anchors.left: parent.left
property bool isFocusable: true
ScrollBar.vertical: ScrollBarType {}
header: ColumnLayout { header: ColumnLayout {
width: listView.width width: listView.width
BaseHeaderType { BaseHeaderType {
id: header
Layout.fillWidth: true Layout.fillWidth: true
Layout.rightMargin: 16 Layout.rightMargin: 16
Layout.leftMargin: 16 Layout.leftMargin: 16
@ -50,16 +44,14 @@ PageType {
} }
} }
model: 1 model: 1 // fake model to force the ListView to be created without a model
clip: true
spacing: 16 spacing: 16
delegate: ColumnLayout { delegate: ColumnLayout {
width: listView.width width: listView.width
TextFieldWithHeaderType { TextFieldWithHeaderType {
id: passwordTextField
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 16 Layout.topMargin: 16
Layout.rightMargin: 16 Layout.rightMargin: 16
@ -87,8 +79,6 @@ PageType {
width: listView.width width: listView.width
SwitcherType { SwitcherType {
id: switcher
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 24 Layout.topMargin: 24
Layout.rightMargin: 16 Layout.rightMargin: 16

View file

@ -30,335 +30,223 @@ PageType {
} }
} }
ListView { ListViewType {
id: listview id: listView
anchors.top: backButtonLayout.bottom anchors.top: backButtonLayout.bottom
anchors.bottom: saveButton.top anchors.bottom: saveButton.top
width: parent.width width: parent.width
clip: true
property bool isFocusable: true
Keys.onTabPressed: {
FocusController.nextKeyTabItem()
}
Keys.onBacktabPressed: {
FocusController.previousKeyTabItem()
}
Keys.onUpPressed: {
FocusController.nextKeyUpItem()
}
Keys.onDownPressed: {
FocusController.nextKeyDownItem()
}
Keys.onLeftPressed: {
FocusController.nextKeyLeftItem()
}
Keys.onRightPressed: {
FocusController.nextKeyRightItem()
}
model: AwgConfigModel model: AwgConfigModel
delegate: Item { delegate: ColumnLayout {
id: delegateItem width: listView.width
implicitWidth: listview.width
implicitHeight: col.implicitHeight
property alias mtuTextField: mtuTextField
property bool isSaveButtonEnabled: mtuTextField.errorText === "" && property bool isSaveButtonEnabled: mtuTextField.errorText === "" &&
junkPacketMaxSizeTextField.errorText === "" && junkPacketMaxSizeTextField.errorText === "" &&
junkPacketMinSizeTextField.errorText === "" && junkPacketMinSizeTextField.errorText === "" &&
junkPacketCountTextField.errorText === "" junkPacketCountTextField.errorText === ""
ColumnLayout { spacing: 0
id: col
anchors.top: parent.top BaseHeaderType {
anchors.left: parent.left Layout.fillWidth: true
anchors.right: parent.right Layout.leftMargin: 16
Layout.rightMargin: 16
anchors.leftMargin: 16 headerText: qsTr("AmneziaWG settings")
anchors.rightMargin: 16 }
spacing: 0 TextFieldWithHeaderType {
id: mtuTextField
BaseHeaderType { Layout.fillWidth: true
Layout.fillWidth: true Layout.topMargin: 40
Layout.leftMargin: 16
Layout.rightMargin: 16
headerText: qsTr("AmneziaWG settings") headerText: qsTr("MTU")
} textField.text: clientMtu
textField.validator: IntValidator { bottom: 576; top: 65535 }
TextFieldWithHeaderType { textField.onEditingFinished: {
id: mtuTextField if (textField.text !== clientMtu) {
Layout.fillWidth: true clientMtu = textField.text
Layout.topMargin: 40
headerText: qsTr("MTU")
textField.text: clientMtu
textField.validator: IntValidator { bottom: 576; top: 65535 }
textField.onEditingFinished: {
if (textField.text !== clientMtu) {
clientMtu = textField.text
}
} }
checkEmptyText: true
KeyNavigation.tab: junkPacketCountTextField.textField
} }
checkEmptyText: true
}
AwgTextField { TextFieldWithHeaderType {
id: junkPacketCountTextField id: junkPacketCountTextField
headerText: "Jc - Junk packet count"
textField.text: clientJunkPacketCount
textField.onEditingFinished: { Layout.fillWidth: true
if (textField.text !== clientJunkPacketCount) { Layout.topMargin: 16
clientJunkPacketCount = textField.text Layout.leftMargin: 16
} Layout.rightMargin: 16
}
KeyNavigation.tab: junkPacketMinSizeTextField.textField headerText: "Jc - Junk packet count"
} textField.text: clientJunkPacketCount
textField.validator: IntValidator { bottom: 0 }
AwgTextField { textField.onEditingFinished: {
id: junkPacketMinSizeTextField if (textField.text !== clientJunkPacketCount) {
headerText: "Jmin - Junk packet minimum size" clientJunkPacketCount = textField.text
textField.text: clientJunkPacketMinSize
textField.onEditingFinished: {
if (textField.text !== clientJunkPacketMinSize) {
clientJunkPacketMinSize = textField.text
}
}
KeyNavigation.tab: junkPacketMaxSizeTextField.textField
}
AwgTextField {
id: junkPacketMaxSizeTextField
headerText: "Jmax - Junk packet maximum size"
textField.text: clientJunkPacketMaxSize
textField.onEditingFinished: {
if (textField.text !== clientJunkPacketMaxSize) {
clientJunkPacketMaxSize = textField.text
}
} }
} }
AwgTextField { checkEmptyText: true
id: specialJunk1TextField }
headerText: qsTr("I1 - First special junk packet")
textField.text: clientSpecialJunk1
textField.validator: null
checkEmptyText: false
textField.onEditingFinished: { TextFieldWithHeaderType {
if (textField.text !== clientSpecialJunk1) { id: junkPacketMinSizeTextField
clientSpecialJunk1 = textField.text
} Layout.fillWidth: true
Layout.topMargin: 16
Layout.leftMargin: 16
Layout.rightMargin: 16
headerText: "Jmin - Junk packet minimum size"
textField.text: clientJunkPacketMinSize
textField.validator: IntValidator { bottom: 0 }
textField.onEditingFinished: {
if (textField.text !== clientJunkPacketMinSize) {
clientJunkPacketMinSize = textField.text
} }
} }
AwgTextField { checkEmptyText: true
id: specialJunk2TextField }
headerText: qsTr("I2 - Second special junk packet")
textField.text: clientSpecialJunk2
textField.validator: null
checkEmptyText: false
textField.onEditingFinished: { TextFieldWithHeaderType {
if (textField.text !== clientSpecialJunk2) { id: junkPacketMaxSizeTextField
clientSpecialJunk2 = textField.text
} Layout.fillWidth: true
Layout.topMargin: 16
Layout.leftMargin: 16
Layout.rightMargin: 16
headerText: "Jmax - Junk packet maximum size"
textField.text: clientJunkPacketMaxSize
textField.validator: IntValidator { bottom: 0 }
textField.onEditingFinished: {
if (textField.text !== clientJunkPacketMaxSize) {
clientJunkPacketMaxSize = textField.text
} }
} }
AwgTextField { checkEmptyText: true
id: specialJunk3TextField
headerText: qsTr("I3 - Third special junk packet")
textField.text: clientSpecialJunk3
textField.validator: null
checkEmptyText: false
textField.onEditingFinished: { }
if (textField.text !== clientSpecialJunk3) {
clientSpecialJunk3 = textField.text
}
}
}
AwgTextField { Header2TextType {
id: specialJunk4TextField Layout.fillWidth: true
headerText: qsTr("I4 - Fourth special junk packet") Layout.topMargin: 16
textField.text: clientSpecialJunk4 Layout.leftMargin: 16
textField.validator: null Layout.rightMargin: 16
checkEmptyText: false
textField.onEditingFinished: { text: qsTr("Server settings")
if (textField.text !== clientSpecialJunk4) { }
clientSpecialJunk4 = textField.text
}
}
}
AwgTextField { TextFieldWithHeaderType {
id: specialJunk5TextField id: portTextField
headerText: qsTr("I5 - Fifth special junk packet")
textField.text: clientSpecialJunk5
textField.validator: null
checkEmptyText: false
textField.onEditingFinished: { Layout.fillWidth: true
if (textField.text !== clientSpecialJunk5 ) { Layout.topMargin: 8
clientSpecialJunk5 = textField.text Layout.leftMargin: 16
} Layout.rightMargin: 16
}
}
AwgTextField { enabled: false
id: controlledJunk1TextField
headerText: qsTr("J1 - First controlled junk packet")
textField.text: clientControlledJunk1
textField.validator: null
checkEmptyText: false
textField.onEditingFinished: { headerText: qsTr("Port")
if (textField.text !== clientControlledJunk1) { textField.text: port
clientControlledJunk1 = textField.text }
}
}
}
AwgTextField { TextFieldWithHeaderType {
id: controlledJunk2TextField id: initPacketJunkSizeTextField
headerText: qsTr("J2 - Second controlled junk packet")
textField.text: clientControlledJunk2
textField.validator: null
checkEmptyText: false
textField.onEditingFinished: { Layout.fillWidth: true
if (textField.text !== clientControlledJunk2) { Layout.topMargin: 16
clientControlledJunk2 = textField.text Layout.leftMargin: 16
} Layout.rightMargin: 16
}
}
AwgTextField { enabled: false
id: controlledJunk3TextField
headerText: qsTr("J3 - Third controlled junk packet")
textField.text: clientControlledJunk3
textField.validator: null
checkEmptyText: false
textField.onEditingFinished: { headerText: "S1 - Init packet junk size"
if (textField.text !== clientControlledJunk3) { textField.text: serverInitPacketJunkSize
clientControlledJunk3 = textField.text }
}
}
}
AwgTextField { TextFieldWithHeaderType {
id: iTimeTextField id: responsePacketJunkSizeTextField
headerText: qsTr("Itime - Special handshake timeout")
textField.text: clientSpecialHandshakeTimeout
checkEmptyText: false
textField.onEditingFinished: { Layout.fillWidth: true
if (textField.text !== clientSpecialHandshakeTimeout) { Layout.topMargin: 16
clientSpecialHandshakeTimeout = textField.text Layout.leftMargin: 16
} Layout.rightMargin: 16
}
}
Header2TextType { enabled: false
Layout.fillWidth: true
Layout.topMargin: 16
text: qsTr("Server settings") headerText: "S2 - Response packet junk size"
} textField.text: serverResponsePacketJunkSize
}
AwgTextField { TextFieldWithHeaderType {
id: portTextField id: initPacketMagicHeaderTextField
enabled: false
headerText: qsTr("Port") Layout.fillWidth: true
textField.text: port Layout.topMargin: 16
} Layout.leftMargin: 16
Layout.rightMargin: 16
AwgTextField { enabled: false
id: initPacketJunkSizeTextField
enabled: false
headerText: "S1 - Init packet junk size" headerText: "H1 - Init packet magic header"
textField.text: serverInitPacketJunkSize textField.text: serverInitPacketMagicHeader
} }
AwgTextField { TextFieldWithHeaderType {
id: responsePacketJunkSizeTextField id: responsePacketMagicHeaderTextField
enabled: false
headerText: "S2 - Response packet junk size" Layout.fillWidth: true
textField.text: serverResponsePacketJunkSize Layout.topMargin: 16
} Layout.leftMargin: 16
Layout.rightMargin: 16
// AwgTextField { enabled: false
// id: cookieReplyPacketJunkSizeTextField
// enabled: false
// headerText: "S3 - Cookie Reply packet junk size" headerText: "H2 - Response packet magic header"
// textField.text: serverCookieReplyPacketJunkSize textField.text: serverResponsePacketMagicHeader
// } }
// AwgTextField { TextFieldWithHeaderType {
// id: transportPacketJunkSizeTextField id: underloadPacketMagicHeaderTextField
// enabled: false
// headerText: "S4 - Transport packet junk size" Layout.fillWidth: true
// textField.text: serverTransportPacketJunkSize Layout.topMargin: 16
// } Layout.leftMargin: 16
Layout.rightMargin: 16
AwgTextField { enabled: false
id: initPacketMagicHeaderTextField
enabled: false
headerText: "H1 - Init packet magic header" headerText: "H3 - Underload packet magic header"
textField.text: serverInitPacketMagicHeader textField.text: serverUnderloadPacketMagicHeader
} }
AwgTextField { TextFieldWithHeaderType {
id: responsePacketMagicHeaderTextField id: transportPacketMagicHeaderTextField
enabled: false
headerText: "H2 - Response packet magic header" Layout.fillWidth: true
textField.text: serverResponsePacketMagicHeader Layout.topMargin: 16
} Layout.leftMargin: 16
Layout.rightMargin: 16
AwgTextField { enabled: false
id: underloadPacketMagicHeaderTextField
enabled: false
headerText: "H3 - Underload packet magic header"
textField.text: serverUnderloadPacketMagicHeader
}
AwgTextField {
id: transportPacketMagicHeaderTextField
enabled: false
headerText: "H4 - Transport packet magic header"
textField.text: serverTransportPacketMagicHeader
}
headerText: "H4 - Transport packet magic header"
textField.text: serverTransportPacketMagicHeader
} }
} }
} }
@ -375,18 +263,17 @@ PageType {
anchors.rightMargin: 16 anchors.rightMargin: 16
anchors.leftMargin: 16 anchors.leftMargin: 16
enabled: listview.currentItem.isSaveButtonEnabled enabled: listView.currentItem.isSaveButtonEnabled
text: qsTr("Save") text: qsTr("Save")
onActiveFocusChanged: { onActiveFocusChanged: {
if(activeFocus) { if(activeFocus) {
listview.positionViewAtEnd() listView.positionViewAtEnd()
} }
} }
clickedFunc: function() { clickedFunc: function() {
forceActiveFocus()
var headerText = qsTr("Save settings?") var headerText = qsTr("Save settings?")
var descriptionText = qsTr("Only the settings for this device will be changed") var descriptionText = qsTr("Only the settings for this device will be changed")
var yesButtonText = qsTr("Continue") var yesButtonText = qsTr("Continue")
@ -401,11 +288,9 @@ PageType {
PageController.goToPage(PageEnum.PageSetupWizardInstalling); PageController.goToPage(PageEnum.PageSetupWizardInstalling);
InstallController.updateContainer(AwgConfigModel.getConfig()) InstallController.updateContainer(AwgConfigModel.getConfig())
} }
var noButtonFunction = function() {
if (!GC.isMobile()) { var noButtonFunction = function() {}
saveButton.forceActiveFocus()
}
}
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction) showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
} }
} }

View file

@ -33,320 +33,339 @@ PageType {
} }
} }
ListView { ListViewType {
id: listview id: listView
property bool isFocusable: true
anchors.top: backButtonLayout.bottom anchors.top: backButtonLayout.bottom
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
width: parent.width width: parent.width
Keys.onTabPressed: {
FocusController.nextKeyTabItem()
}
Keys.onBacktabPressed: {
FocusController.previousKeyTabItem()
}
Keys.onUpPressed: {
FocusController.nextKeyUpItem()
}
Keys.onDownPressed: {
FocusController.nextKeyDownItem()
}
Keys.onLeftPressed: {
FocusController.nextKeyLeftItem()
}
Keys.onRightPressed: {
FocusController.nextKeyRightItem()
}
clip: true
model: AwgConfigModel model: AwgConfigModel
delegate: Item { delegate: ColumnLayout {
id: delegateItem id: delegateItem
implicitWidth: listview.width
implicitHeight: col.implicitHeight width: listView.width
property alias vpnAddressSubnetTextField: vpnAddressSubnetTextField property alias vpnAddressSubnetTextField: vpnAddressSubnetTextField
property bool isEnabled: ServersModel.isProcessedServerHasWriteAccess() property bool isEnabled: ServersModel.isProcessedServerHasWriteAccess()
ColumnLayout { spacing: 0
id: col
anchors.top: parent.top BaseHeaderType {
anchors.left: parent.left Layout.fillWidth: true
anchors.right: parent.right Layout.leftMargin: 16
Layout.rightMargin: 16
anchors.leftMargin: 16 headerText: qsTr("AmneziaWG settings")
anchors.rightMargin: 16 }
spacing: 0 TextFieldWithHeaderType {
id: vpnAddressSubnetTextField
BaseHeaderType { Layout.fillWidth: true
Layout.fillWidth: true Layout.topMargin: 40
Layout.leftMargin: 16
Layout.rightMargin: 16
headerText: qsTr("AmneziaWG settings") enabled: delegateItem.isEnabled
}
TextFieldWithHeaderType { headerText: qsTr("VPN address subnet")
id: vpnAddressSubnetTextField textField.text: subnetAddress
Layout.fillWidth: true textField.onEditingFinished: {
Layout.topMargin: 40 if (textField.text !== subnetAddress) {
subnetAddress = textField.text
enabled: delegateItem.isEnabled
headerText: qsTr("VPN address subnet")
textField.text: subnetAddress
textField.onEditingFinished: {
if (textField.text !== subnetAddress) {
subnetAddress = textField.text
}
}
checkEmptyText: true
}
TextFieldWithHeaderType {
id: portTextField
Layout.fillWidth: true
Layout.topMargin: 16
enabled: delegateItem.isEnabled
headerText: qsTr("Port")
textField.text: port
textField.maximumLength: 5
textField.validator: IntValidator { bottom: 1; top: 65535 }
textField.onEditingFinished: {
if (textField.text !== port) {
port = textField.text
}
}
checkEmptyText: true
}
AwgTextField {
id: junkPacketCountTextField
headerText: qsTr("Jc - Junk packet count")
textField.text: serverJunkPacketCount
textField.onEditingFinished: {
if (textField.text !== serverJunkPacketCount) {
serverJunkPacketCount = textField.text
}
} }
} }
AwgTextField { checkEmptyText: true
id: junkPacketMinSizeTextField }
headerText: qsTr("Jmin - Junk packet minimum size")
textField.text: serverJunkPacketMinSize
textField.onEditingFinished: { TextFieldWithHeaderType {
if (textField.text !== serverJunkPacketMinSize) { id: portTextField
serverJunkPacketMinSize = textField.text Layout.fillWidth: true
} Layout.topMargin: 16
Layout.leftMargin: 16
Layout.rightMargin: 16
enabled: delegateItem.isEnabled
headerText: qsTr("Port")
textField.text: port
textField.maximumLength: 5
textField.validator: IntValidator { bottom: 1; top: 65535 }
textField.onEditingFinished: {
if (textField.text !== port) {
port = textField.text
} }
} }
AwgTextField { checkEmptyText: true
id: junkPacketMaxSizeTextField }
headerText: qsTr("Jmax - Junk packet maximum size")
textField.text: serverJunkPacketMaxSize
textField.onEditingFinished: { TextFieldWithHeaderType {
if (textField.text !== serverJunkPacketMaxSize) { id: junkPacketCountTextField
serverJunkPacketMaxSize = textField.text Layout.fillWidth: true
} Layout.topMargin: 16
Layout.leftMargin: 16
Layout.rightMargin: 16
headerText: qsTr("Jc - Junk packet count")
textField.text: serverJunkPacketCount
textField.validator: IntValidator { bottom: 0 }
textField.onEditingFinished: {
if (textField.text === "") {
textField.text = "0"
}
if (textField.text !== serverJunkPacketCount) {
serverJunkPacketCount = textField.text
} }
} }
AwgTextField { checkEmptyText: true
id: initPacketJunkSizeTextField }
headerText: qsTr("S1 - Init packet junk size")
textField.text: serverInitPacketJunkSize
textField.onEditingFinished: { TextFieldWithHeaderType {
if (textField.text !== serverInitPacketJunkSize) { id: junkPacketMinSizeTextField
serverInitPacketJunkSize = textField.text Layout.fillWidth: true
} Layout.topMargin: 16
Layout.leftMargin: 16
Layout.rightMargin: 16
headerText: qsTr("Jmin - Junk packet minimum size")
textField.text: serverJunkPacketMinSize
textField.validator: IntValidator { bottom: 0 }
textField.onEditingFinished: {
if (textField.text !== serverJunkPacketMinSize) {
serverJunkPacketMinSize = textField.text
} }
} }
AwgTextField { checkEmptyText: true
id: responsePacketJunkSizeTextField }
headerText: qsTr("S2 - Response packet junk size")
textField.text: serverResponsePacketJunkSize
textField.onEditingFinished: { TextFieldWithHeaderType {
if (textField.text !== serverResponsePacketJunkSize) { id: junkPacketMaxSizeTextField
serverResponsePacketJunkSize = textField.text Layout.fillWidth: true
} Layout.topMargin: 16
Layout.leftMargin: 16
Layout.rightMargin: 16
headerText: qsTr("Jmax - Junk packet maximum size")
textField.text: serverJunkPacketMaxSize
textField.validator: IntValidator { bottom: 0 }
textField.onEditingFinished: {
if (textField.text !== serverJunkPacketMaxSize) {
serverJunkPacketMaxSize = textField.text
} }
} }
// AwgTextField { checkEmptyText: true
// id: cookieReplyPacketJunkSizeTextField }
// headerText: qsTr("S3 - Cookie reply packet junk size")
// textField.text: serverCookieReplyPacketJunkSize
// textField.onEditingFinished: { TextFieldWithHeaderType {
// if (textField.text !== serverCookieReplyPacketJunkSize) { id: initPacketJunkSizeTextField
// serverCookieReplyPacketJunkSize = textField.text Layout.fillWidth: true
// } Layout.topMargin: 16
// } Layout.leftMargin: 16
// } Layout.rightMargin: 16
// AwgTextField { headerText: qsTr("S1 - Init packet junk size")
// id: transportPacketJunkSizeTextField textField.text: serverInitPacketJunkSize
// headerText: qsTr("S4 - Transport packet junk size") textField.validator: IntValidator { bottom: 0 }
// textField.text: serverTransportPacketJunkSize
// textField.onEditingFinished: { textField.onEditingFinished: {
// if (textField.text !== serverTransportPacketJunkSize) { if (textField.text !== serverInitPacketJunkSize) {
// serverTransportPacketJunkSize = textField.text serverInitPacketJunkSize = textField.text
// }
// }
// }
AwgTextField {
id: initPacketMagicHeaderTextField
headerText: qsTr("H1 - Init packet magic header")
textField.text: serverInitPacketMagicHeader
textField.onEditingFinished: {
if (textField.text !== serverInitPacketMagicHeader) {
serverInitPacketMagicHeader = textField.text
}
} }
} }
AwgTextField { checkEmptyText: true
id: responsePacketMagicHeaderTextField
headerText: qsTr("H2 - Response packet magic header")
textField.text: serverResponsePacketMagicHeader
textField.onEditingFinished: { onActiveFocusChanged: {
if (textField.text !== serverResponsePacketMagicHeader) { if(activeFocus) {
serverResponsePacketMagicHeader = textField.text listview.positionViewAtEnd()
} }
}
}
TextFieldWithHeaderType {
id: responsePacketJunkSizeTextField
Layout.fillWidth: true
Layout.topMargin: 16
Layout.leftMargin: 16
Layout.rightMargin: 16
headerText: qsTr("S2 - Response packet junk size")
textField.text: serverResponsePacketJunkSize
textField.validator: IntValidator { bottom: 0 }
textField.onEditingFinished: {
if (textField.text !== serverResponsePacketJunkSize) {
serverResponsePacketJunkSize = textField.text
} }
} }
AwgTextField { checkEmptyText: true
id: underloadPacketMagicHeaderTextField
headerText: qsTr("H3 - Underload packet magic header")
textField.text: serverUnderloadPacketMagicHeader
textField.onEditingFinished: { onActiveFocusChanged: {
if (textField.text !== serverUnderloadPacketMagicHeader) { if(activeFocus) {
serverUnderloadPacketMagicHeader = textField.text listview.positionViewAtEnd()
} }
}
}
TextFieldWithHeaderType {
id: initPacketMagicHeaderTextField
Layout.fillWidth: true
Layout.topMargin: 16
Layout.leftMargin: 16
Layout.rightMargin: 16
headerText: qsTr("H1 - Init packet magic header")
textField.text: serverInitPacketMagicHeader
textField.validator: IntValidator { bottom: 0 }
textField.onEditingFinished: {
if (textField.text !== serverInitPacketMagicHeader) {
serverInitPacketMagicHeader = textField.text
} }
} }
AwgTextField { checkEmptyText: true
id: transportPacketMagicHeaderTextField }
headerText: qsTr("H4 - Transport packet magic header")
textField.text: serverTransportPacketMagicHeader
textField.onEditingFinished: { TextFieldWithHeaderType {
if (textField.text !== serverTransportPacketMagicHeader) { id: responsePacketMagicHeaderTextField
serverTransportPacketMagicHeader = textField.text Layout.fillWidth: true
} Layout.topMargin: 16
Layout.leftMargin: 16
Layout.rightMargin: 16
headerText: qsTr("H2 - Response packet magic header")
textField.text: serverResponsePacketMagicHeader
textField.validator: IntValidator { bottom: 0 }
textField.onEditingFinished: {
if (textField.text !== serverResponsePacketMagicHeader) {
serverResponsePacketMagicHeader = textField.text
} }
} }
checkEmptyText: true
}
BasicButtonType { TextFieldWithHeaderType {
id: saveRestartButton id: transportPacketMagicHeaderTextField
Layout.fillWidth: true
Layout.topMargin: 16
Layout.leftMargin: 16
Layout.rightMargin: 16
Layout.fillWidth: true headerText: qsTr("H4 - Transport packet magic header")
Layout.topMargin: 24 textField.text: serverTransportPacketMagicHeader
Layout.bottomMargin: 24 textField.validator: IntValidator { bottom: 0 }
enabled: underloadPacketMagicHeaderTextField.errorText === "" && textField.onEditingFinished: {
transportPacketMagicHeaderTextField.errorText === "" && if (textField.text !== serverTransportPacketMagicHeader) {
responsePacketMagicHeaderTextField.errorText === "" && serverTransportPacketMagicHeader = textField.text
initPacketMagicHeaderTextField.errorText === "" && }
responsePacketJunkSizeTextField.errorText === "" && }
// cookieReplyHeaderJunkTextField.errorText === "" &&
// transportHeaderJunkTextField.errorText === "" &&
initPacketJunkSizeTextField.errorText === "" &&
junkPacketMaxSizeTextField.errorText === "" &&
junkPacketMinSizeTextField.errorText === "" &&
junkPacketCountTextField.errorText === "" &&
portTextField.errorText === "" &&
vpnAddressSubnetTextField.errorText === ""
text: qsTr("Save") checkEmptyText: true
}
onActiveFocusChanged: { TextFieldWithHeaderType {
if(activeFocus) { id: underloadPacketMagicHeaderTextField
listview.positionViewAtEnd() Layout.fillWidth: true
Layout.topMargin: 16
Layout.leftMargin: 16
Layout.rightMargin: 16
headerText: qsTr("H3 - Underload packet magic header")
textField.text: serverUnderloadPacketMagicHeader
textField.validator: IntValidator { bottom: 0 }
textField.onEditingFinished: {
if (textField.text !== serverUnderloadPacketMagicHeader) {
serverUnderloadPacketMagicHeader = textField.text
}
}
checkEmptyText: true
}
BasicButtonType {
id: saveRestartButton
Layout.fillWidth: true
Layout.topMargin: 24
Layout.bottomMargin: 24
Layout.leftMargin: 16
Layout.rightMargin: 16
enabled: underloadPacketMagicHeaderTextField.errorText === "" &&
transportPacketMagicHeaderTextField.errorText === "" &&
responsePacketMagicHeaderTextField.errorText === "" &&
initPacketMagicHeaderTextField.errorText === "" &&
responsePacketJunkSizeTextField.errorText === "" &&
initPacketJunkSizeTextField.errorText === "" &&
junkPacketMaxSizeTextField.errorText === "" &&
junkPacketMinSizeTextField.errorText === "" &&
junkPacketCountTextField.errorText === "" &&
portTextField.errorText === "" &&
vpnAddressSubnetTextField.errorText === ""
text: qsTr("Save")
onActiveFocusChanged: {
if(activeFocus) {
listView.positionViewAtEnd()
}
}
clickedFunc: function() {
if (delegateItem.isEnabled) {
if (AwgConfigModel.isHeadersEqual(underloadPacketMagicHeaderTextField.textField.text,
transportPacketMagicHeaderTextField.textField.text,
responsePacketMagicHeaderTextField.textField.text,
initPacketMagicHeaderTextField.textField.text)) {
PageController.showErrorMessage(qsTr("The values of the H1-H4 fields must be unique"))
return
}
if (AwgConfigModel.isPacketSizeEqual(parseInt(initPacketJunkSizeTextField.textField.text),
parseInt(responsePacketJunkSizeTextField.textField.text))) {
PageController.showErrorMessage(qsTr("The value of the field S1 + message initiation size (148) must not equal S2 + message response size (92)"))
return
} }
} }
clickedFunc: function() { var headerText = qsTr("Save settings?")
forceActiveFocus() 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")
if (delegateItem.isEnabled) { var yesButtonFunction = function() {
if (AwgConfigModel.isHeadersEqual(underloadPacketMagicHeaderTextField.textField.text, if (ConnectionController.isConnected && ServersModel.getDefaultServerData("defaultContainer") === ContainersModel.getProcessedContainerIndex()) {
transportPacketMagicHeaderTextField.textField.text, PageController.showNotificationMessage(qsTr("Unable change settings while there is an active connection"))
responsePacketMagicHeaderTextField.textField.text, return
initPacketMagicHeaderTextField.textField.text)) {
PageController.showErrorMessage(qsTr("The values of the H1-H4 fields must be unique"))
return
}
if (AwgConfigModel.isPacketSizeEqual(parseInt(initPacketJunkSizeTextField.textField.text),
parseInt(responsePacketJunkSizeTextField.textField.text))) {
PageController.showErrorMessage(qsTr("The value of the field S1 + message initiation size (148) must not equal S2 + message response size (92)"))
return
}
// if (AwgConfigModel.isPacketSizeEqual(parseInt(initPacketJunkSizeTextField.textField.text),
// parseInt(responsePacketJunkSizeTextField.textField.text),
// parseInt(cookieReplyPacketJunkSizeTextField.textField.text),
// parseInt(transportPacketJunkSizeTextField.textField.text))) {
// PageController.showErrorMessage(qsTr("The value of the field S1 + message initiation size (148) must not equal S2 + message response size (92) + S3 + cookie reply size (64) + S4 + transport packet size (32)"))
// return
// }
} }
var headerText = qsTr("Save settings?") PageController.goToPage(PageEnum.PageSetupWizardInstalling);
var descriptionText = qsTr("All users with whom you shared a connection with will no longer be able to connect to it.") InstallController.updateContainer(AwgConfigModel.getConfig())
var yesButtonText = qsTr("Continue")
var noButtonText = qsTr("Cancel")
var yesButtonFunction = function() {
if (ConnectionController.isConnected && ServersModel.getDefaultServerData("defaultContainer") === ContainersModel.getProcessedContainerIndex()) {
PageController.showNotificationMessage(qsTr("Unable change settings while there is an active connection"))
return
}
PageController.goToPage(PageEnum.PageSetupWizardInstalling);
InstallController.updateContainer(AwgConfigModel.getConfig())
}
var noButtonFunction = function() {
if (!GC.isMobile()) {
saveRestartButton.forceActiveFocus()
}
}
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
} }
var noButtonFunction = function() {}
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
} }
} }
} }

View file

@ -16,212 +16,161 @@ import "../Components"
PageType { PageType {
id: root id: root
ColumnLayout { BackButtonType {
id: backButtonLayout id: backButton
anchors.top: parent.top anchors.top: parent.top
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
anchors.topMargin: 20 anchors.topMargin: 20
BackButtonType {
id: backButton
}
} }
FlickableType { ListViewType {
id: fl id: listView
anchors.top: backButtonLayout.bottom
anchors.top: backButton.bottom
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
contentHeight: content.implicitHeight anchors.left: parent.left
anchors.right: parent.right
Column { property int selectedIndex: 0
id: content
anchors.top: parent.top enabled: ServersModel.isProcessedServerHasWriteAccess()
anchors.left: parent.left
anchors.right: parent.right
enabled: ServersModel.isProcessedServerHasWriteAccess() header: ColumnLayout {
ListView { width: listView.width
id: listview
property int selectedIndex: 0 BaseHeaderType {
Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 16
width: parent.width headerText: qsTr("Cloak settings")
height: listview.contentItem.height }
}
clip: true model: CloakConfigModel
reuseItems: true
model: CloakConfigModel delegate: ColumnLayout {
delegate: Item { width: listView.width
id: delegateItem
property alias trafficFromField: trafficFromField property alias trafficFromField: trafficFromField
property bool isEnabled: ServersModel.isProcessedServerHasWriteAccess()
implicitWidth: listview.width spacing: 0
implicitHeight: col.implicitHeight
ColumnLayout { TextFieldWithHeaderType {
id: col id: trafficFromField
anchors.top: parent.top Layout.fillWidth: true
anchors.left: parent.left Layout.topMargin: 32
anchors.right: parent.right Layout.leftMargin: 16
Layout.rightMargin: 16
anchors.leftMargin: 16 headerText: qsTr("Disguised as traffic from")
anchors.rightMargin: 16 textField.text: site
spacing: 0 textField.onEditingFinished: {
if (textField.text !== site) {
var tmpText = textField.text
tmpText = tmpText.toLocaleLowerCase()
BaseHeaderType { var indexHttps = tmpText.indexOf("https://")
Layout.fillWidth: true if (indexHttps === 0) {
headerText: qsTr("Cloak settings") tmpText = textField.text.substring(8)
} } else {
site = textField.text
TextFieldWithHeaderType {
id: trafficFromField
Layout.fillWidth: true
Layout.topMargin: 32
enabled: delegateItem.isEnabled
headerText: qsTr("Disguised as traffic from")
textField.text: site
textField.onEditingFinished: {
if (textField.text !== site) {
var tmpText = textField.text
tmpText = tmpText.toLocaleLowerCase()
var indexHttps = tmpText.indexOf("https://")
if (indexHttps === 0) {
tmpText = textField.text.substring(8)
} else {
site = textField.text
}
}
}
checkEmptyText: true
}
TextFieldWithHeaderType {
id: portTextField
Layout.fillWidth: true
Layout.topMargin: 16
enabled: delegateItem.isEnabled
headerText: qsTr("Port")
textField.text: port
textField.maximumLength: 5
textField.validator: IntValidator { bottom: 1; top: 65535 }
textField.onEditingFinished: {
if (textField.text !== port) {
port = textField.text
}
}
checkEmptyText: true
}
DropDownType {
id: cipherDropDown
Layout.fillWidth: true
Layout.topMargin: 16
enabled: delegateItem.isEnabled
descriptionText: qsTr("Cipher")
headerText: qsTr("Cipher")
drawerParent: root
listView: ListViewWithRadioButtonType {
id: cipherListView
rootWidth: root.width
model: ListModel {
ListElement { name : "chacha20-ietf-poly1305" }
ListElement { name : "xchacha20-ietf-poly1305" }
ListElement { name : "aes-256-gcm" }
ListElement { name : "aes-192-gcm" }
ListElement { name : "aes-128-gcm" }
}
clickedFunction: function() {
cipherDropDown.text = selectedText
cipher = cipherDropDown.text
cipherDropDown.closeTriggered()
}
Component.onCompleted: {
cipherDropDown.text = cipher
for (var i = 0; i < cipherListView.model.count; i++) {
if (cipherListView.model.get(i).name === cipherDropDown.text) {
selectedIndex = i
}
}
}
}
}
BasicButtonType {
id: saveButton
Layout.fillWidth: true
Layout.topMargin: 24
Layout.bottomMargin: 24
enabled: trafficFromField.errorText === "" &&
portTextField.errorText === ""
text: qsTr("Save")
clickedFunc: function() {
forceActiveFocus()
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 && ServersModel.getDefaultServerData("defaultContainer") === ContainersModel.getProcessedContainerIndex()) {
PageController.showNotificationMessage(qsTr("Unable change settings while there is an active connection"))
return
}
PageController.goToPage(PageEnum.PageSetupWizardInstalling)
InstallController.updateContainer(CloakConfigModel.getConfig())
}
var noButtonFunction = function() {
if (!GC.isMobile()) {
saveButton.forceActiveFocus()
}
}
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
}
Keys.onEnterPressed: saveButton.clicked()
Keys.onReturnPressed: saveButton.clicked()
} }
} }
} }
} }
TextFieldWithHeaderType {
id: portTextField
Layout.fillWidth: true
Layout.topMargin: 16
Layout.leftMargin: 16
Layout.rightMargin: 16
headerText: qsTr("Port")
textField.text: port
textField.maximumLength: 5
textField.validator: IntValidator { bottom: 1; top: 65535 }
textField.onEditingFinished: {
if (textField.text !== port) {
port = textField.text
}
}
}
DropDownType {
id: cipherDropDown
Layout.fillWidth: true
Layout.topMargin: 16
Layout.leftMargin: 16
Layout.rightMargin: 16
descriptionText: qsTr("Cipher")
headerText: qsTr("Cipher")
drawerParent: root
listView: ListViewWithRadioButtonType {
id: cipherListView
rootWidth: root.width
model: ListModel {
ListElement { name : "chacha20-ietf-poly1305" }
ListElement { name : "xchacha20-ietf-poly1305" }
ListElement { name : "aes-256-gcm" }
ListElement { name : "aes-192-gcm" }
ListElement { name : "aes-128-gcm" }
}
clickedFunction: function() {
cipherDropDown.text = selectedText
cipher = cipherDropDown.text
cipherDropDown.closeTriggered()
}
Component.onCompleted: {
cipherDropDown.text = cipher
for (var i = 0; i < cipherListView.model.count; i++) {
if (cipherListView.model.get(i).name === cipherDropDown.text) {
selectedIndex = i
}
}
}
}
}
BasicButtonType {
id: saveRestartButton
Layout.fillWidth: true
Layout.topMargin: 24
Layout.bottomMargin: 24
Layout.leftMargin: 16
Layout.rightMargin: 16
text: qsTr("Save")
clickedFunc: function() {
if (ConnectionController.isConnected && ServersModel.getDefaultServerData("defaultContainer") === ContainersModel.getProcessedContainerIndex()) {
PageController.showNotificationMessage(qsTr("Unable change settings while there is an active connection"))
return
}
PageController.goToPage(PageEnum.PageSetupWizardInstalling);
InstallController.updateContainer(CloakConfigModel.getConfig())
}
}
} }
} }
} }

View file

@ -17,428 +17,465 @@ import "../Components"
PageType { PageType {
id: root id: root
ColumnLayout { BackButtonType {
id: backButtonLayout id: backButton
anchors.top: parent.top anchors.top: parent.top
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
anchors.topMargin: 20 anchors.topMargin: 20
BackButtonType { onFocusChanged: {
id: backButton if (this.activeFocus) {
listView.positionViewAtBeginning()
}
} }
} }
FlickableType { ListViewType {
id: fl id: listView
anchors.top: backButtonLayout.bottom
anchors.top: backButton.bottom
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
contentHeight: content.implicitHeight anchors.right: parent.right
anchors.left: parent.left
Column { enabled: ServersModel.isProcessedServerHasWriteAccess()
id: content
anchors.top: parent.top header: ColumnLayout {
anchors.left: parent.left width: listView.width
anchors.right: parent.right
enabled: ServersModel.isProcessedServerHasWriteAccess() BaseHeaderType {
Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 16
ListView { headerText: qsTr("OpenVPN settings")
id: listview }
}
width: parent.width model: ListModel {
height: listview.contentItem.height ListElement { type: "subnetHeader" }
ListElement { type: "networkProtocolText" }
ListElement { type: "protoSelector" }
ListElement { type: "portTextField" }
ListElement { type: "encryptionSection" }
ListElement { type: "checkboxSection" }
ListElement { type: "clientCommands" }
ListElement { type: "serverCommands" }
}
clip: true delegate: DelegateChooser {
interactive: false
model: OpenVpnConfigModel role: "type"
delegate: Item { DelegateChoice {
id: delegateItem // property alias vpnAddressSubnetTextField: vpnAddressSubnetTextField
roleValue: "subnetHeader"
ColumnLayout {
width: listView.width
property alias vpnAddressSubnetTextField: vpnAddressSubnetTextField TextFieldWithHeaderType {
property bool isEnabled: ServersModel.isProcessedServerHasWriteAccess() id: vpnAddressSubnetTextField
implicitWidth: listview.width Layout.fillWidth: true
implicitHeight: col.implicitHeight Layout.topMargin: 32
Layout.leftMargin: 16
Layout.rightMargin: 16
ColumnLayout { headerText: qsTr("VPN address subnet")
id: col textField.text: OpenVpnConfigModel.subnetAddress
anchors.top: parent.top textField.onEditingFinished: {
anchors.left: parent.left if (textField.text !== OpenVpnConfigModel.subnetAddress) {
anchors.right: parent.right OpenVpnConfigModel.subnetAddress = textField.text
anchors.leftMargin: 16
anchors.rightMargin: 16
spacing: 0
BaseHeaderType {
Layout.fillWidth: true
headerText: qsTr("OpenVPN settings")
}
TextFieldWithHeaderType {
id: vpnAddressSubnetTextField
Layout.fillWidth: true
Layout.topMargin: 32
enabled: delegateItem.isEnabled
headerText: qsTr("VPN address subnet")
textField.text: subnetAddress
parentFlickable: fl
textField.onEditingFinished: {
if (textField.text !== subnetAddress) {
subnetAddress = textField.text
}
} }
}
}
}
}
checkEmptyText: true DelegateChoice {
roleValue: "networkProtocolText"
ColumnLayout {
width: listView.width
ParagraphTextType {
Layout.fillWidth: true
Layout.topMargin: 32
Layout.leftMargin: 16
Layout.rightMargin: 16
text: qsTr("Network protocol")
}
}
}
DelegateChoice {
roleValue: "protoSelector"
ColumnLayout {
width: listView.width
TransportProtoSelector {
id: transportProtoSelector
Layout.fillWidth: true
Layout.topMargin: 16
Layout.leftMargin: 16
Layout.rightMargin: 16
rootWidth: root.width
enabled: OpenVpnConfigModel.isTransportProtoEditable
currentIndex: {
return OpenVpnConfigModel.transportProto === "tcp" ? 1 : 0
} }
ParagraphTextType { onCurrentIndexChanged: {
Layout.fillWidth: true if (OpenVpnConfigModel.transportProto === "tcp" && currentIndex === 0) {
Layout.topMargin: 32 OpenVpnConfigModel.transportProto = "udp"
} else if (OpenVpnConfigModel.transportProto === "udp" && currentIndex === 1) {
text: qsTr("Network protocol") OpenVpnConfigModel.transportProto = "tcp"
}
} }
}
}
}
DelegateChoice {
roleValue: "portTextField"
ColumnLayout {
width: listView.width
TextFieldWithHeaderType {
id: portTextField
Layout.fillWidth: true
Layout.topMargin: 40
Layout.leftMargin: 16
Layout.rightMargin: 16
enabled: OpenVpnConfigModel.isPortEditable
headerText: qsTr("Port")
textField.text: OpenVpnConfigModel.port
textField.maximumLength: 5
textField.validator: IntValidator { bottom: 1; top: 65535 }
textField.onEditingFinished: {
if (textField.text !== OpenVpnConfigModel.port) {
OpenVpnConfigModel.port = textField.text
}
}
}
}
}
DelegateChoice {
roleValue: "encryptionSection"
ColumnLayout {
width: listView.width
SwitcherType {
id: autoNegotiateEncryprionSwitcher
Layout.fillWidth: true
Layout.topMargin: 24
Layout.leftMargin: 16
Layout.rightMargin: 16
text: qsTr("Auto-negotiate encryption")
checked: OpenVpnConfigModel.autoNegotiateEncryption
onCheckedChanged: {
if (checked !== OpenVpnConfigModel.autoNegotiateEncryprion) {
OpenVpnConfigModel.autoNegotiateEncryprion = checked
}
}
}
DropDownType {
id: hashDropDown
Layout.fillWidth: true
Layout.topMargin: 20
Layout.leftMargin: 16
Layout.rightMargin: 16
enabled: !autoNegotiateEncryprionSwitcher.checked
descriptionText: qsTr("Hash")
headerText: qsTr("Hash")
drawerParent: root
listView: ListViewWithRadioButtonType {
id: hashListView
TransportProtoSelector {
id: transportProtoSelector
Layout.fillWidth: true
Layout.topMargin: 16
rootWidth: root.width rootWidth: root.width
enabled: isTransportProtoEditable model: ListModel {
ListElement { name : qsTr("SHA512") }
currentIndex: { ListElement { name : qsTr("SHA384") }
return transportProto === "tcp" ? 1 : 0 ListElement { name : qsTr("SHA256") }
ListElement { name : qsTr("SHA3-512") }
ListElement { name : qsTr("SHA3-384") }
ListElement { name : qsTr("SHA3-256") }
ListElement { name : qsTr("whirlpool") }
ListElement { name : qsTr("BLAKE2b512") }
ListElement { name : qsTr("BLAKE2s256") }
ListElement { name : qsTr("SHA1") }
} }
onCurrentIndexChanged: { clickedFunction: function() {
if (transportProto === "tcp" && currentIndex === 0) { hashDropDown.text = selectedText
transportProto = "udp" OpenVpnConfigModel.hash = hashDropDown.text
} else if (transportProto === "udp" && currentIndex === 1) { hashDropDown.closeTriggered()
transportProto = "tcp"
}
}
}
TextFieldWithHeaderType {
id: portTextField
Layout.fillWidth: true
Layout.topMargin: 40
parentFlickable: fl
enabled: delegateItem.isEnabled
headerText: qsTr("Port")
textField.text: port
textField.maximumLength: 5
textField.validator: IntValidator { bottom: 1; top: 65535 }
textField.onEditingFinished: {
if (textField.text !== port) {
port = textField.text
}
} }
checkEmptyText: true Component.onCompleted: {
} hashDropDown.text = OpenVpnConfigModel.hash
SwitcherType { for (var i = 0; i < hashListView.model.count; i++) {
id: autoNegotiateEncryprionSwitcher if (hashListView.model.get(i).name === hashDropDown.text) {
currentIndex = i
Layout.fillWidth: true
Layout.topMargin: 24
parentFlickable: fl
text: qsTr("Auto-negotiate encryption")
checked: autoNegotiateEncryprion
onCheckedChanged: {
if (checked !== autoNegotiateEncryprion) {
autoNegotiateEncryprion = checked
}
}
}
DropDownType {
id: hashDropDown
Layout.fillWidth: true
Layout.topMargin: 20
enabled: !autoNegotiateEncryprionSwitcher.checked
descriptionText: qsTr("Hash")
headerText: qsTr("Hash")
drawerParent: root
listView: ListViewWithRadioButtonType {
id: hashListView
rootWidth: root.width
model: ListModel {
ListElement { name : qsTr("SHA512") }
ListElement { name : qsTr("SHA384") }
ListElement { name : qsTr("SHA256") }
ListElement { name : qsTr("SHA3-512") }
ListElement { name : qsTr("SHA3-384") }
ListElement { name : qsTr("SHA3-256") }
ListElement { name : qsTr("whirlpool") }
ListElement { name : qsTr("BLAKE2b512") }
ListElement { name : qsTr("BLAKE2s256") }
ListElement { name : qsTr("SHA1") }
}
clickedFunction: function() {
hashDropDown.text = selectedText
hash = hashDropDown.text
hashDropDown.closeTriggered()
}
Component.onCompleted: {
hashDropDown.text = hash
for (var i = 0; i < hashListView.model.count; i++) {
if (hashListView.model.get(i).name === hashDropDown.text) {
currentIndex = i
}
} }
} }
} }
} }
}
DropDownType { DropDownType {
id: cipherDropDown id: cipherDropDown
Layout.fillWidth: true
Layout.topMargin: 16
enabled: !autoNegotiateEncryprionSwitcher.checked Layout.fillWidth: true
Layout.topMargin: 16
Layout.leftMargin: 16
Layout.rightMargin: 16
descriptionText: qsTr("Cipher") enabled: !autoNegotiateEncryprionSwitcher.checked
headerText: qsTr("Cipher")
drawerParent: root descriptionText: qsTr("Cipher")
headerText: qsTr("Cipher")
listView: ListViewWithRadioButtonType { drawerParent: root
id: cipherListView
rootWidth: root.width listView: ListViewWithRadioButtonType {
id: cipherListView
model: ListModel { rootWidth: root.width
ListElement { name : qsTr("AES-256-GCM") }
ListElement { name : qsTr("AES-192-GCM") }
ListElement { name : qsTr("AES-128-GCM") }
ListElement { name : qsTr("AES-256-CBC") }
ListElement { name : qsTr("AES-192-CBC") }
ListElement { name : qsTr("AES-128-CBC") }
ListElement { name : qsTr("ChaCha20-Poly1305") }
ListElement { name : qsTr("ARIA-256-CBC") }
ListElement { name : qsTr("CAMELLIA-256-CBC") }
ListElement { name : qsTr("none") }
}
clickedFunction: function() { model: ListModel {
cipherDropDown.text = selectedText ListElement { name : qsTr("AES-256-GCM") }
cipher = cipherDropDown.text ListElement { name : qsTr("AES-192-GCM") }
cipherDropDown.closeTriggered() ListElement { name : qsTr("AES-128-GCM") }
} ListElement { name : qsTr("AES-256-CBC") }
ListElement { name : qsTr("AES-192-CBC") }
ListElement { name : qsTr("AES-128-CBC") }
ListElement { name : qsTr("ChaCha20-Poly1305") }
ListElement { name : qsTr("ARIA-256-CBC") }
ListElement { name : qsTr("CAMELLIA-256-CBC") }
ListElement { name : qsTr("none") }
}
Component.onCompleted: { clickedFunction: function() {
cipherDropDown.text = cipher cipherDropDown.text = selectedText
OpenVpnConfigModel.cipher = cipherDropDown.text
cipherDropDown.closeTriggered()
}
for (var i = 0; i < cipherListView.model.count; i++) { Component.onCompleted: {
if (cipherListView.model.get(i).name === cipherDropDown.text) { cipherDropDown.text = OpenVpnConfigModel.cipher
currentIndex = i
} for (var i = 0; i < cipherListView.model.count; i++) {
if (cipherListView.model.get(i).name === cipherDropDown.text) {
currentIndex = i
} }
} }
} }
} }
}
}
}
Rectangle { DelegateChoice {
id: contentRect roleValue: "checkboxSection"
Layout.fillWidth: true ColumnLayout {
Layout.topMargin: 32 width: listView.width
Layout.preferredHeight: checkboxLayout.implicitHeight
color: AmneziaStyle.color.onyxBlack
radius: 16
Connections { Rectangle {
target: tlsAuthCheckBox id: contentRect
enabled: !GC.isMobile()
function onFocusChanged() { Layout.fillWidth: true
if (tlsAuthCheckBox.activeFocus) { Layout.topMargin: 32
fl.ensureVisible(contentRect) Layout.leftMargin: 16
Layout.rightMargin: 16
Layout.preferredHeight: checkboxLayout.implicitHeight
color: AmneziaStyle.color.onyxBlack
radius: 16
ColumnLayout {
id: checkboxLayout
anchors.fill: parent
CheckBoxType {
id: tlsAuthCheckBox
Layout.fillWidth: true
text: qsTr("TLS auth")
checked: OpenVpnConfigModel.tlsAuth
onCheckedChanged: {
if (checked !== OpenVpnConfigModel.tlsAuth) {
console.log("tlsAuth changed to: " + checked)
OpenVpnConfigModel.tlsAuth = checked
} }
} }
} }
ColumnLayout { DividerType {}
id: checkboxLayout
anchors.fill: parent CheckBoxType {
CheckBoxType { id: blockDnsCheckBox
id: tlsAuthCheckBox
Layout.fillWidth: true
text: qsTr("TLS auth") Layout.fillWidth: true
checked: tlsAuth
onCheckedChanged: { text: qsTr("Block DNS requests outside of VPN")
if (checked !== tlsAuth) { checked: OpenVpnConfigModel.blockDns
console.log("tlsAuth changed to: " + checked)
tlsAuth = checked
}
}
}
DividerType {} onCheckedChanged: {
if (checked !== OpenVpnConfigModel.blockDns) {
CheckBoxType { OpenVpnConfigModel.blockDns = checked
id: blockDnsCheckBox
Layout.fillWidth: true
text: qsTr("Block DNS requests outside of VPN")
checked: blockDns
onCheckedChanged: {
if (checked !== blockDns) {
blockDns = checked
}
} }
} }
} }
} }
}
}
}
SwitcherType { DelegateChoice {
id: additionalClientCommandsSwitcher roleValue: "clientCommands"
Layout.fillWidth: true ColumnLayout {
Layout.topMargin: 32
parentFlickable: fl
checked: additionalClientCommands !== "" width: listView.width
text: qsTr("Additional client configuration commands") SwitcherType {
id: additionalClientCommandsSwitcher
onCheckedChanged: { Layout.fillWidth: true
if (!checked) { Layout.topMargin: 32
additionalClientCommands = "" Layout.leftMargin: 16
} Layout.rightMargin: 16
checked: OpenVpnConfigModel.additionalClientCommands !== ""
text: qsTr("Additional client configuration commands")
onCheckedChanged: {
if (!checked) {
OpenVpnConfigModel.additionalClientCommands = ""
}
// listView.positionViewAtIndex(index, ListView.Beginning)
}
}
TextAreaType {
id: additionalClientCommandsTextArea
Layout.fillWidth: true
Layout.topMargin: 16
Layout.leftMargin: 16
Layout.rightMargin: 16
visible: additionalClientCommandsSwitcher.checked
textAreaText: OpenVpnConfigModel.additionalClientCommands
placeholderText: qsTr("Commands:")
textArea.onEditingFinished: {
if (OpenVpnConfigModel.additionalClientCommands !== textAreaText) {
OpenVpnConfigModel.additionalClientCommands = textAreaText
} }
} }
}
}
}
TextAreaType { DelegateChoice {
id: additionalClientCommandsTextArea roleValue: "serverCommands"
Layout.fillWidth: true ColumnLayout {
Layout.topMargin: 16 width: listView.width
visible: additionalClientCommandsSwitcher.checked SwitcherType {
id: additionalServerCommandsSwitcher
parentFlickable: fl Layout.fillWidth: true
Layout.topMargin: 16
Layout.leftMargin: 16
Layout.rightMargin: 16
textAreaText: additionalClientCommands checked: OpenVpnConfigModel.additionalServerCommands !== ""
placeholderText: qsTr("Commands:")
textArea.onEditingFinished: { text: qsTr("Additional server configuration commands")
if (additionalClientCommands !== textAreaText) {
additionalClientCommands = textAreaText onCheckedChanged: {
} if (!checked) {
OpenVpnConfigModel.additionalServerCommands = ""
} }
// listView.positionViewAtIndex(index, ListView.Beginning)
} }
}
SwitcherType { TextAreaType {
id: additionalServerCommandsSwitcher id: additionalServerCommandsTextArea
Layout.fillWidth: true
Layout.topMargin: 16
parentFlickable: fl
checked: additionalServerCommands !== "" Layout.fillWidth: true
Layout.topMargin: 16
Layout.leftMargin: 16
Layout.rightMargin: 16
text: qsTr("Additional server configuration commands") visible: additionalServerCommandsSwitcher.checked
onCheckedChanged: { textAreaText: OpenVpnConfigModel.additionalServerCommands
if (!checked) { placeholderText: qsTr("Commands:")
additionalServerCommands = ""
} textArea.onEditingFinished: {
if (OpenVpnConfigModel.additionalServerCommands !== textAreaText) {
OpenVpnConfigModel.additionalServerCommands = textAreaText
} }
} }
TextAreaType {
id: additionalServerCommandsTextArea
Layout.fillWidth: true
Layout.topMargin: 16
visible: additionalServerCommandsSwitcher.checked
textAreaText: additionalServerCommands
placeholderText: qsTr("Commands:")
parentFlickable: fl
textArea.onEditingFinished: {
if (additionalServerCommands !== textAreaText) {
additionalServerCommands = textAreaText
}
}
}
BasicButtonType {
id: saveButton
Layout.fillWidth: true
Layout.topMargin: 24
Layout.bottomMargin: 24
enabled: vpnAddressSubnetTextField.errorText === "" &&
portTextField.errorText === ""
text: qsTr("Save")
parentFlickable: fl
onClicked: function() {
forceActiveFocus()
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 && ServersModel.getDefaultServerData("defaultContainer") === ContainersModel.getProcessedContainerIndex()) {
PageController.showNotificationMessage(qsTr("Unable change settings while there is an active connection"))
return
}
PageController.goToPage(PageEnum.PageSetupWizardInstalling);
InstallController.updateContainer(OpenVpnConfigModel.getConfig())
}
var noButtonFunction = function() {
if (!GC.isMobile()) {
saveButton.forceActiveFocus()
}
}
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
}
Keys.onEnterPressed: saveButton.clicked()
Keys.onReturnPressed: saveButton.clicked()
}
} }
} }
} }
} }
footer: ColumnLayout {
width: listView.width
BasicButtonType {
id: saveRestartButton
Layout.fillWidth: true
Layout.topMargin: 24
Layout.bottomMargin: 24
Layout.leftMargin: 16
Layout.rightMargin: 16
text: qsTr("Save")
clickedFunc: function() {
if (ConnectionController.isConnected && ServersModel.getDefaultServerData("defaultContainer") === ContainersModel.getProcessedContainerIndex()) {
PageController.showNotificationMessage(qsTr("Unable change settings while there is an active connection"))
return
}
PageController.goToPage(PageEnum.PageSetupWizardInstalling);
InstallController.updateContainer(OpenVpnConfigModel.getConfig())
}
}
}
} }
} }

View file

@ -19,164 +19,154 @@ import "../Components"
PageType { PageType {
id: root id: root
ColumnLayout { BackButtonType {
id: header id: backButton
anchors.top: parent.top anchors.top: parent.top
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
anchors.topMargin: 20 anchors.topMargin: 20
BackButtonType { onFocusChanged: {
id: backButton if (this.activeFocus) {
} listView.positionViewAtBeginning()
}
BaseHeaderType {
Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 16
headerText: ContainersModel.getProcessedContainerName() + qsTr(" settings")
} }
} }
FlickableType { ListViewType {
id: fl id: listView
anchors.top: header.bottom
anchors.left: parent.left anchors.top: backButton.bottom
anchors.bottom: parent.bottom
anchors.right: parent.right anchors.right: parent.right
contentHeight: content.height anchors.left: parent.left
Column { header: ColumnLayout {
id: content width: listView.width
anchors.top: parent.top BaseHeaderType {
anchors.left: parent.left Layout.fillWidth: true
anchors.right: parent.right Layout.leftMargin: 16
anchors.topMargin: 32 Layout.rightMargin: 16
Layout.bottomMargin: 16
ListView { headerText: ContainersModel.getProcessedContainerName() + qsTr(" settings")
id: listView }
width: parent.width }
height: contentItem.height
clip: true
interactive: false
model: ProtocolsModel
activeFocusOnTab: true model: ProtocolsModel
focus: true
delegate: Item { delegate: ColumnLayout {
implicitWidth: parent.width width: listView.width
implicitHeight: delegateContent.implicitHeight
property alias focusItem: button LabelWithButtonType {
id: button
ColumnLayout { Layout.fillWidth: true
id: delegateContent Layout.leftMargin: 16
Layout.rightMargin: 16
anchors.fill: parent text: qsTr("Show connection options")
LabelWithButtonType { clickedFunction: function() {
id: button configContentDrawer.openTriggered()
}
Layout.fillWidth: true MouseArea {
anchors.fill: button
cursorShape: Qt.PointingHandCursor
enabled: false
}
}
text: qsTr("Show connection options") DividerType {}
clickedFunction: function() { DrawerType2 {
configContentDrawer.openTriggered() id: configContentDrawer
}
MouseArea { expandedHeight: root.height * 0.9
anchors.fill: button
cursorShape: Qt.PointingHandCursor parent: root
enabled: false anchors.fill: parent
expandedStateContent: Item {
implicitHeight: configContentDrawer.expandedHeight
BackButtonType {
id: drawerBackButton
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
anchors.topMargin: 16
backButtonFunction: function() {
configContentDrawer.closeTriggered()
}
}
ListViewType {
id: drawerListView
anchors.top: drawerBackButton.bottom
anchors.bottom: parent.bottom
anchors.right: parent.right
anchors.left: parent.left
header: ColumnLayout {
width: drawerListView.width
Header2Type {
Layout.fillWidth: true
Layout.topMargin: 16
Layout.leftMargin: 16
Layout.rightMargin: 16
headerText: qsTr("Connection options %1").arg(protocolName)
} }
} }
DividerType {} model: 1 // fake model to force the ListView to be created without a model
DrawerType2 { delegate: ColumnLayout {
id: configContentDrawer width: drawerListView.width
expandedHeight: root.height * 0.9 TextArea {
id: configText
parent: root Layout.fillWidth: true
anchors.fill: parent Layout.topMargin: 16
Layout.leftMargin: 16
Layout.rightMargin: 16
expandedStateContent: Item { padding: 0
implicitHeight: configContentDrawer.expandedHeight height: 24
BackButtonType { color: AmneziaStyle.color.paleGray
id: backButton1 selectionColor: AmneziaStyle.color.richBrown
selectedTextColor: AmneziaStyle.color.paleGray
anchors.top: parent.top font.pixelSize: 16
anchors.left: parent.left font.weight: Font.Medium
anchors.right: parent.right font.family: "PT Root UI VF"
anchors.topMargin: 16
backButtonFunction: function() { text: rawConfig
configContentDrawer.closeTriggered()
}
}
FlickableType { wrapMode: Text.Wrap
anchors.top: backButton1.bottom
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: parent.bottom
contentHeight: configContent.implicitHeight + configContent.anchors.topMargin + configContent.anchors.bottomMargin
ColumnLayout { background: Rectangle {
id: configContent color: AmneziaStyle.color.transparent
anchors.fill: parent
anchors.rightMargin: 16
anchors.leftMargin: 16
Header2Type {
Layout.fillWidth: true
Layout.topMargin: 16
headerText: qsTr("Connection options %1").arg(protocolName)
}
TextArea {
id: configText
Layout.fillWidth: true
Layout.topMargin: 16
Layout.bottomMargin: 16
padding: 0
leftPadding: 0
height: 24
color: AmneziaStyle.color.paleGray
selectionColor: AmneziaStyle.color.richBrown
selectedTextColor: AmneziaStyle.color.paleGray
font.pixelSize: 16
font.weight: Font.Medium
font.family: "PT Root UI VF"
text: rawConfig
wrapMode: Text.Wrap
background: Rectangle {
color: AmneziaStyle.color.transparent
}
}
}
} }
} }
} }
} }
} }
} }
}
footer: ColumnLayout {
width: listView.width
LabelWithButtonType { LabelWithButtonType {
id: removeButton id: removeButton
@ -198,11 +188,7 @@ PageType {
PageController.goToPage(PageEnum.PageDeinstalling) PageController.goToPage(PageEnum.PageDeinstalling)
InstallController.removeProcessedContainer() InstallController.removeProcessedContainer()
} }
var noButtonFunction = function() { var noButtonFunction = function() {}
if (!GC.isMobile()) {
focusItem.forceActiveFocus()
}
}
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction) showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
} }

View file

@ -16,179 +16,138 @@ import "../Components"
PageType { PageType {
id: root id: root
ColumnLayout { BackButtonType {
id: backButtonLayout id: backButton
anchors.top: parent.top anchors.top: parent.top
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
anchors.topMargin: 20 anchors.topMargin: 20
BackButtonType { onFocusChanged: {
id: backButton if (this.activeFocus) {
listView.positionViewAtBeginning()
}
} }
} }
FlickableType { ListViewType {
id: fl id: listView
anchors.top: backButtonLayout.bottom
anchors.top: backButton.bottom
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
contentHeight: content.implicitHeight anchors.right: parent.right
anchors.left: parent.left
Column { enabled: ServersModel.isProcessedServerHasWriteAccess()
id: content
anchors.top: parent.top model: ShadowSocksConfigModel
anchors.left: parent.left
anchors.right: parent.right
enabled: ServersModel.isProcessedServerHasWriteAccess() delegate: ColumnLayout {
width: listView.width
ListView { spacing: 0
id: listview
width: parent.width BaseHeaderType {
height: listview.contentItem.height Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 16
clip: true headerText: qsTr("Shadowsocks settings")
interactive: false }
model: ShadowSocksConfigModel TextFieldWithHeaderType {
id: portTextField
delegate: Item { Layout.fillWidth: true
id: delegateItem Layout.topMargin: 40
Layout.leftMargin: 16
Layout.rightMargin: 16
property bool isEnabled: ServersModel.isProcessedServerHasWriteAccess() enabled: isPortEditable
implicitWidth: listview.width headerText: qsTr("Port")
implicitHeight: col.implicitHeight textField.text: port
textField.maximumLength: 5
textField.validator: IntValidator { bottom: 1; top: 65535 }
ColumnLayout { textField.onEditingFinished: {
id: col if (textField.text !== port) {
port = textField.text
}
}
}
anchors.top: parent.top DropDownType {
anchors.left: parent.left id: cipherDropDown
anchors.right: parent.right
anchors.leftMargin: 16 Layout.fillWidth: true
anchors.rightMargin: 16 Layout.topMargin: 20
Layout.leftMargin: 16
Layout.rightMargin: 16
spacing: 0 enabled: isCipherEditable
BaseHeaderType { descriptionText: qsTr("Cipher")
Layout.fillWidth: true headerText: qsTr("Cipher")
headerText: qsTr("Shadowsocks settings")
}
TextFieldWithHeaderType { drawerParent: root
id: portTextField
Layout.fillWidth: true listView: ListViewWithRadioButtonType {
Layout.topMargin: 40
enabled: delegateItem.isEnabled id: cipherListView
headerText: qsTr("Port") rootWidth: root.width
textField.text: port
textField.maximumLength: 5
textField.validator: IntValidator { bottom: 1; top: 65535 }
textField.onEditingFinished: { model: ListModel {
if (textField.text !== port) { ListElement { name : "chacha20-ietf-poly1305" }
port = textField.text ListElement { name : "xchacha20-ietf-poly1305" }
} ListElement { name : "aes-256-gcm" }
ListElement { name : "aes-192-gcm" }
ListElement { name : "aes-128-gcm" }
}
clickedFunction: function() {
cipherDropDown.text = selectedText
cipher = cipherDropDown.text
cipherDropDown.closeTriggered()
}
Component.onCompleted: {
cipherDropDown.text = cipher
for (var i = 0; i < cipherListView.model.count; i++) {
if (cipherListView.model.get(i).name === cipherDropDown.text) {
currentIndex = i
} }
checkEmptyText: true
}
DropDownType {
id: cipherDropDown
Layout.fillWidth: true
Layout.topMargin: 20
enabled: delegateItem.isEnabled
descriptionText: qsTr("Cipher")
headerText: qsTr("Cipher")
drawerParent: root
listView: ListViewWithRadioButtonType {
id: cipherListView
rootWidth: root.width
model: ListModel {
ListElement { name : "chacha20-ietf-poly1305" }
ListElement { name : "xchacha20-ietf-poly1305" }
ListElement { name : "aes-256-gcm" }
ListElement { name : "aes-192-gcm" }
ListElement { name : "aes-128-gcm" }
}
clickedFunction: function() {
cipherDropDown.text = selectedText
cipher = cipherDropDown.text
cipherDropDown.closeTriggered()
}
Component.onCompleted: {
cipherDropDown.text = cipher
for (var i = 0; i < cipherListView.model.count; i++) {
if (cipherListView.model.get(i).name === cipherDropDown.text) {
currentIndex = i
}
}
}
}
}
BasicButtonType {
id: saveButton
Layout.fillWidth: true
Layout.topMargin: 24
Layout.bottomMargin: 24
enabled: portTextField.errorText === ""
text: qsTr("Save")
clickedFunc: function() {
forceActiveFocus()
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 && ServersModel.getDefaultServerData("defaultContainer") === ContainersModel.getProcessedContainerIndex()) {
PageController.showNotificationMessage(qsTr("Unable change settings while there is an active connection"))
return
}
PageController.goToPage(PageEnum.PageSetupWizardInstalling);
InstallController.updateContainer(ShadowSocksConfigModel.getConfig())
}
var noButtonFunction = function() {
if (!GC.isMobile()) {
saveButton.forceActiveFocus()
}
}
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
}
Keys.onEnterPressed: saveButton.clicked()
Keys.onReturnPressed: saveButton.clicked()
} }
} }
} }
} }
BasicButtonType {
id: saveRestartButton
Layout.fillWidth: true
Layout.topMargin: 24
Layout.bottomMargin: 24
Layout.leftMargin: 16
Layout.rightMargin: 16
enabled: isPortEditable | isCipherEditable
text: qsTr("Save")
clickedFunc: function() {
if (ConnectionController.isConnected && ServersModel.getDefaultServerData("defaultContainer") === ContainersModel.getProcessedContainerIndex()) {
PageController.showNotificationMessage(qsTr("Unable change settings while there is an active connection"))
return
}
PageController.goToPage(PageEnum.PageSetupWizardInstalling);
InstallController.updateContainer(ShadowSocksConfigModel.getConfig())
}
}
} }
} }
} }

View file

@ -16,160 +16,124 @@ import "../Components"
PageType { PageType {
id: root id: root
Item { BackButtonType {
id: focusItem id: backButton
onFocusChanged: {
if (activeFocus) {
fl.ensureVisible(focusItem)
}
}
KeyNavigation.tab: backButton
}
ColumnLayout {
id: backButtonLayout
anchors.top: parent.top anchors.top: parent.top
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
anchors.topMargin: 20 anchors.topMargin: 20
BackButtonType { onFocusChanged: {
id: backButton if (this.activeFocus) {
KeyNavigation.tab: listview.currentItem.mtuTextField.textField listView.positionViewAtBeginning()
}
} }
} }
FlickableType { ListViewType {
id: fl id: listView
anchors.top: backButtonLayout.bottom
anchors.top: backButton.bottom
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
contentHeight: content.implicitHeight + saveButton.implicitHeight + saveButton.anchors.bottomMargin + saveButton.anchors.topMargin anchors.right: parent.right
anchors.left: parent.left
Column { model: WireGuardConfigModel
id: content
anchors.top: parent.top delegate: ColumnLayout {
anchors.left: parent.left width: listView.width
anchors.right: parent.right
ListView { property alias mtuTextField: mtuTextField
id: listview property bool isSaveButtonEnabled: mtuTextField.errorText === ""
width: parent.width spacing: 0
height: listview.contentItem.height
clip: true BaseHeaderType {
interactive: false Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 16
model: WireGuardConfigModel headerText: qsTr("WG settings")
}
delegate: Item { TextFieldWithHeaderType {
id: delegateItem id: mtuTextField
implicitWidth: listview.width Layout.fillWidth: true
implicitHeight: col.implicitHeight Layout.topMargin: 40
Layout.leftMargin: 16
Layout.rightMargin: 16
property alias mtuTextField: mtuTextField headerText: qsTr("MTU")
property bool isSaveButtonEnabled: mtuTextField.errorText === "" textField.text: clientMtu
textField.validator: IntValidator { bottom: 576; top: 65535 }
ColumnLayout { textField.onEditingFinished: {
id: col if (textField.text !== clientMtu) {
clientMtu = textField.text
anchors.top: parent.top }
anchors.left: parent.left
anchors.right: parent.right
anchors.leftMargin: 16
anchors.rightMargin: 16
spacing: 0
BaseHeaderType {
Layout.fillWidth: true
headerText: qsTr("WG settings")
}
TextFieldWithHeaderType {
id: mtuTextField
Layout.fillWidth: true
Layout.topMargin: 40
headerText: qsTr("MTU")
textField.text: clientMtu
textField.validator: IntValidator { bottom: 576; top: 65535 }
textField.onEditingFinished: {
if (textField.text !== clientMtu) {
clientMtu = textField.text
}
}
checkEmptyText: true
KeyNavigation.tab: saveButton
}
Header2TextType {
Layout.fillWidth: true
Layout.topMargin: 16
text: qsTr("Server settings")
}
TextFieldWithHeaderType {
id: portTextField
Layout.fillWidth: true
Layout.topMargin: 8
enabled: false
headerText: qsTr("Port")
textField.text: port
}
}
} }
checkEmptyText: true
}
Header2TextType {
Layout.fillWidth: true
Layout.topMargin: 16
Layout.leftMargin: 16
Layout.rightMargin: 16
text: qsTr("Server settings")
}
TextFieldWithHeaderType {
id: portTextField
Layout.fillWidth: true
Layout.topMargin: 8
Layout.leftMargin: 16
Layout.rightMargin: 16
enabled: false
headerText: qsTr("Port")
textField.text: port
} }
} }
}
BasicButtonType { footer: ColumnLayout {
id: saveButton width: listView.width
anchors.right: root.right BasicButtonType {
anchors.left: root.left id: saveButton
anchors.bottom: root.bottom
anchors.topMargin: 24 Layout.fillWidth: true
anchors.bottomMargin: 24 Layout.topMargin: 24
anchors.rightMargin: 16 Layout.bottomMargin: 24
anchors.leftMargin: 16 Layout.rightMargin: 16
Layout.leftMargin: 16
enabled: listview.currentItem.isSaveButtonEnabled enabled: listView.currentItem.isSaveButtonEnabled
text: qsTr("Save") text: qsTr("Save")
clickedFunc: function() { clickedFunc: function() {
forceActiveFocus() var headerText = qsTr("Save settings?")
var headerText = qsTr("Save settings?") var descriptionText = qsTr("Only the settings for this device will be changed")
var descriptionText = qsTr("Only the settings for this device will be changed") var yesButtonText = qsTr("Continue")
var yesButtonText = qsTr("Continue") var noButtonText = qsTr("Cancel")
var noButtonText = qsTr("Cancel")
var yesButtonFunction = function() { var yesButtonFunction = function() {
if (ConnectionController.isConnected && ServersModel.getDefaultServerData("defaultContainer") === ContainersModel.getProcessedContainerIndex()) { if (ConnectionController.isConnected && ServersModel.getDefaultServerData("defaultContainer") === ContainersModel.getProcessedContainerIndex()) {
PageController.showNotificationMessage(qsTr("Unable change settings while there is an active connection")) PageController.showNotificationMessage(qsTr("Unable change settings while there is an active connection"))
return return
} }
PageController.goToPage(PageEnum.PageSetupWizardInstalling); PageController.goToPage(PageEnum.PageSetupWizardInstalling);
InstallController.updateContainer(WireGuardConfigModel.getConfig()) InstallController.updateContainer(WireGuardConfigModel.getConfig())
} }
var noButtonFunction = function() { var noButtonFunction = function() {}
if (!GC.isMobile()) { showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
saveButton.forceActiveFocus()
} }
} }
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
} }
} }
} }

View file

@ -16,153 +16,134 @@ import "../Components"
PageType { PageType {
id: root id: root
ColumnLayout { BackButtonType {
id: backButtonLayout id: backButton
anchors.top: parent.top anchors.top: parent.top
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
anchors.topMargin: 20 anchors.topMargin: 20
BackButtonType { onFocusChanged: {
id: backButton if (this.activeFocus) {
listView.positionViewAtBeginning()
}
} }
} }
FlickableType { ListViewType {
id: fl id: listView
anchors.top: backButtonLayout.bottom
anchors.top: backButton.bottom
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
contentHeight: content.implicitHeight anchors.right: parent.right
anchors.left: parent.left
Column { enabled: ServersModel.isProcessedServerHasWriteAccess()
id: content
anchors.top: parent.top model: WireGuardConfigModel
anchors.left: parent.left
anchors.right: parent.right
enabled: ServersModel.isProcessedServerHasWriteAccess() delegate: ColumnLayout {
width: listView.width
ListView { property bool isEnabled: ServersModel.isProcessedServerHasWriteAccess()
id: listview
width: parent.width spacing: 0
height: listview.contentItem.height
clip: true BaseHeaderType {
interactive: false Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 16
model: WireGuardConfigModel headerText: qsTr("WG settings")
}
delegate: Item { TextFieldWithHeaderType {
id: delegateItem id: vpnAddressSubnetTextField
property alias focusItemId: vpnAddressSubnetTextField Layout.fillWidth: true
property bool isEnabled: ServersModel.isProcessedServerHasWriteAccess() Layout.topMargin: 40
Layout.leftMargin: 16
Layout.rightMargin: 16
implicitWidth: listview.width enabled: delegateItem.isEnabled
implicitHeight: col.implicitHeight
ColumnLayout { headerText: qsTr("VPN address subnet")
id: col textField.text: subnetAddress
anchors.top: parent.top textField.onEditingFinished: {
anchors.left: parent.left if (textField.text !== subnetAddress) {
anchors.right: parent.right subnetAddress = textField.text
anchors.leftMargin: 16
anchors.rightMargin: 16
spacing: 0
BaseHeaderType {
Layout.fillWidth: true
headerText: qsTr("WG settings")
}
TextFieldWithHeaderType {
id: vpnAddressSubnetTextField
Layout.fillWidth: true
Layout.topMargin: 40
enabled: delegateItem.isEnabled
headerText: qsTr("VPN address subnet")
textField.text: subnetAddress
textField.onEditingFinished: {
if (textField.text !== subnetAddress) {
subnetAddress = textField.text
}
}
checkEmptyText: true
}
TextFieldWithHeaderType {
id: portTextField
Layout.fillWidth: true
Layout.topMargin: 16
enabled: delegateItem.isEnabled
headerText: qsTr("Port")
textField.text: port
textField.maximumLength: 5
textField.validator: IntValidator { bottom: 1; top: 65535 }
textField.onEditingFinished: {
if (textField.text !== port) {
port = textField.text
}
}
checkEmptyText: true
}
BasicButtonType {
id: saveButton
Layout.fillWidth: true
Layout.topMargin: 24
Layout.bottomMargin: 24
enabled: portTextField.errorText === "" &&
vpnAddressSubnetTextField.errorText === ""
text: qsTr("Save")
onClicked: function() {
forceActiveFocus()
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 && ServersModel.getDefaultServerData("defaultContainer") === ContainersModel.getProcessedContainerIndex()) {
PageController.showNotificationMessage(qsTr("Unable change settings while there is an active connection"))
return
}
PageController.goToPage(PageEnum.PageSetupWizardInstalling);
InstallController.updateContainer(WireGuardConfigModel.getConfig())
}
var noButtonFunction = function() {
if (!GC.isMobile()) {
saveButton.forceActiveFocus()
}
}
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
}
Keys.onEnterPressed: saveButton.clicked()
Keys.onReturnPressed: saveButton.clicked()
}
} }
} }
checkEmptyText: true
}
TextFieldWithHeaderType {
id: portTextField
Layout.fillWidth: true
Layout.topMargin: 16
Layout.leftMargin: 16
Layout.rightMargin: 16
enabled: delegateItem.isEnabled
headerText: qsTr("Port")
textField.text: port
textField.maximumLength: 5
textField.validator: IntValidator { bottom: 1; top: 65535 }
textField.onEditingFinished: {
if (textField.text !== port) {
port = textField.text
}
}
checkEmptyText: true
}
BasicButtonType {
id: saveButton
Layout.fillWidth: true
Layout.topMargin: 24
Layout.bottomMargin: 24
Layout.leftMargin: 16
Layout.rightMargin: 16
enabled: portTextField.errorText === "" &&
vpnAddressSubnetTextField.errorText === ""
text: qsTr("Save")
onClicked: function() {
forceActiveFocus()
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 && ServersModel.getDefaultServerData("defaultContainer") === ContainersModel.getProcessedContainerIndex()) {
PageController.showNotificationMessage(qsTr("Unable change settings while there is an active connection"))
return
}
PageController.goToPage(PageEnum.PageSetupWizardInstalling);
InstallController.updateContainer(WireGuardConfigModel.getConfig())
}
var noButtonFunction = function() {
if (!GC.isMobile()) {
saveRestartButton.forceActiveFocus()
}
}
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
}
Keys.onEnterPressed: saveButton.clicked()
Keys.onReturnPressed: saveButton.clicked()
} }
} }
} }

View file

@ -17,163 +17,114 @@ import "../Components"
PageType { PageType {
id: root id: root
ColumnLayout { BackButtonType {
id: backButtonLayout id: backButton
anchors.top: parent.top anchors.top: parent.top
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
anchors.topMargin: 20 anchors.topMargin: 20
BackButtonType {
id: backButton
}
} }
FlickableType { ListViewType {
id: fl id: listView
anchors.top: backButtonLayout.bottom
anchors.top: backButton.bottom
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
contentHeight: content.implicitHeight anchors.left: parent.left
anchors.right: parent.right
Column { enabled: ServersModel.isProcessedServerHasWriteAccess()
id: content model: XrayConfigModel
anchors.top: parent.top delegate: ColumnLayout {
anchors.left: parent.left
anchors.right: parent.right
enabled: ServersModel.isProcessedServerHasWriteAccess() width: listView.width
ListView { property alias focusItemId: textFieldWithHeaderType.textField
id: listview
width: parent.width spacing: 0
height: listview.contentItem.height
clip: true BaseHeaderType {
interactive: false Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 16
headerText: qsTr("XRay settings")
}
model: XrayConfigModel TextFieldWithHeaderType {
id: textFieldWithHeaderType
delegate: Item { Layout.fillWidth: true
id: delegateItem Layout.topMargin: 32
Layout.leftMargin: 16
Layout.rightMargin: 16
property alias focusItemId: textFieldWithHeaderType.textField headerText: qsTr("Disguised as traffic from")
property bool isEnabled: ServersModel.isProcessedServerHasWriteAccess() textField.text: site
implicitWidth: listview.width textField.onEditingFinished: {
implicitHeight: col.implicitHeight if (textField.text !== site) {
var tmpText = textField.text
tmpText = tmpText.toLocaleLowerCase()
ColumnLayout { if (tmpText.startsWith("https://")) {
id: col tmpText = textField.text.substring(8)
site = tmpText
anchors.top: parent.top } else {
anchors.left: parent.left site = textField.text
anchors.right: parent.right
anchors.leftMargin: 16
anchors.rightMargin: 16
spacing: 0
BaseHeaderType {
Layout.fillWidth: true
headerText: qsTr("XRay settings")
}
TextFieldWithHeaderType {
id: textFieldWithHeaderType
Layout.fillWidth: true
Layout.topMargin: 32
enabled: delegateItem.isEnabled
headerText: qsTr("Disguised as traffic from")
textField.text: site
textField.onEditingFinished: {
if (textField.text !== site) {
var tmpText = textField.text
tmpText = tmpText.toLocaleLowerCase()
if (tmpText.startsWith("https://")) {
tmpText = textField.text.substring(8)
site = tmpText
} else {
site = textField.text
}
}
}
checkEmptyText: true
}
TextFieldWithHeaderType {
id: portTextField
Layout.fillWidth: true
Layout.topMargin: 16
enabled: delegateItem.isEnabled
headerText: qsTr("Port")
textField.text: port
textField.maximumLength: 5
textField.validator: IntValidator { bottom: 1; top: 65535 }
textField.onEditingFinished: {
if (textField.text !== port) {
port = textField.text
}
}
checkEmptyText: true
}
BasicButtonType {
id: saveButton
Layout.fillWidth: true
Layout.topMargin: 24
Layout.bottomMargin: 24
enabled: portTextField.errorText === ""
text: qsTr("Save")
onClicked: function() {
forceActiveFocus()
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 && ServersModel.getDefaultServerData("defaultContainer") === ContainersModel.getProcessedContainerIndex()) {
PageController.showNotificationMessage(qsTr("Unable change settings while there is an active connection"))
return
}
PageController.goToPage(PageEnum.PageSetupWizardInstalling);
InstallController.updateContainer(XrayConfigModel.getConfig())
//focusItem.forceActiveFocus()
}
var noButtonFunction = function() {
if (!GC.isMobile()) {
saveButton.forceActiveFocus()
}
}
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
}
Keys.onEnterPressed: saveButton.clicked()
Keys.onReturnPressed: saveButton.clicked()
} }
} }
} }
} }
TextFieldWithHeaderType {
id: portTextField
Layout.fillWidth: true
Layout.topMargin: 16
Layout.leftMargin: 16
Layout.rightMargin: 16
enabled: delegateItem.isEnabled
headerText: qsTr("Port")
textField.text: port
textField.maximumLength: 5
textField.validator: IntValidator { bottom: 1; top: 65535 }
textField.onEditingFinished: {
if (textField.text !== port) {
port = textField.text
}
}
checkEmptyText: true
}
BasicButtonType {
id: saveButton
Layout.fillWidth: true
Layout.topMargin: 24
Layout.bottomMargin: 24
Layout.leftMargin: 16
Layout.rightMargin: 16
text: qsTr("Save")
onClicked: {
if (ConnectionController.isConnected && ServersModel.getDefaultServerData("defaultContainer") === ContainersModel.getProcessedContainerIndex()) {
PageController.showNotificationMessage(qsTr("Unable change settings while there is an active connection"))
return
}
PageController.goToPage(PageEnum.PageSetupWizardInstalling);
InstallController.updateContainer(XrayConfigModel.getConfig())
}
Keys.onEnterPressed: saveButton.clicked()
Keys.onReturnPressed: saveButton.clicked()
}
} }
} }
} }

View file

@ -16,50 +16,47 @@ import "../Components"
PageType { PageType {
id: root id: root
ColumnLayout { BackButtonType {
id: backButtonLayout id: backButton
anchors.top: parent.top anchors.top: parent.top
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
anchors.topMargin: 20 anchors.topMargin: 20
BackButtonType {
id: backButton
}
} }
FlickableType { ListViewType {
id: fl id: listView
anchors.top: backButtonLayout.bottom
anchors.top: backButton.bottom
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
contentHeight: content.implicitHeight anchors.right: parent.right
anchors.left: parent.left
ColumnLayout { header: ColumnLayout {
id: content width: listView.width
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
BaseHeaderType { BaseHeaderType {
id: header
Layout.fillWidth: true Layout.fillWidth: true
Layout.rightMargin: 16 Layout.rightMargin: 16
Layout.leftMargin: 16 Layout.leftMargin: 16
Layout.bottomMargin: 24
headerText: "AmneziaDNS" headerText: "AmneziaDNS"
descriptionText: qsTr("A DNS service is installed on your server, and it is only accessible via VPN.\n") + descriptionText: qsTr("A DNS service is installed on your server, and it is only accessible via VPN.\n") +
qsTr("The DNS address is the same as the address of your server. You can configure DNS in the settings, under the connections tab.") qsTr("The DNS address is the same as the address of your server. You can configure DNS in the settings, under the connections tab.")
} }
}
model: 1 // fake model to force the ListView to be created without a model
delegate: ColumnLayout {
width: listView.width
LabelWithButtonType { LabelWithButtonType {
id: removeButton Layout.fillWidth: true
Layout.leftMargin: 16
Layout.topMargin: 24 Layout.rightMargin: 16
width: parent.width
text: qsTr("Remove ") + ContainersModel.getProcessedContainerName() text: qsTr("Remove ") + ContainersModel.getProcessedContainerName()
textColor: AmneziaStyle.color.vibrantRed textColor: AmneziaStyle.color.vibrantRed
@ -71,19 +68,14 @@ PageType {
var yesButtonFunction = function() { var yesButtonFunction = function() {
if (ServersModel.isDefaultServerCurrentlyProcessed() && ConnectionController.isConnected if (ServersModel.isDefaultServerCurrentlyProcessed() && ConnectionController.isConnected
&& SettingsController.isAmneziaDnsEnabled()) { && SettingsController.isAmneziaDnsEnabled()) {
PageController.showNotificationMessage(qsTr("Cannot remove AmneziaDNS from running server")) PageController.showNotificationMessage(qsTr("Cannot remove AmneziaDNS from running server"))
} else } else {
{
PageController.goToPage(PageEnum.PageDeinstalling) PageController.goToPage(PageEnum.PageDeinstalling)
InstallController.removeProcessedContainer() InstallController.removeProcessedContainer()
} }
} }
var noButtonFunction = function() { var noButtonFunction = function() {}
if (!GC.isMobile()) {
removeButton.rightButton.forceActiveFocus()
}
}
showQuestionDrawer(headerText, "", yesButtonText, noButtonText, yesButtonFunction, noButtonFunction) showQuestionDrawer(headerText, "", yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
} }

View file

@ -24,258 +24,215 @@ PageType {
} }
} }
ColumnLayout { BackButtonType {
id: backButtonLayout id: backButton
anchors.top: parent.top anchors.top: parent.top
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
anchors.topMargin: 20 anchors.topMargin: 20
BackButtonType { onFocusChanged: {
id: backButton if (this.activeFocus) {
listView.positionViewAtBeginning()
}
} }
} }
FlickableType { ListViewType {
id: fl id: listView
anchors.top: backButtonLayout.bottom
anchors.top: backButton.bottom
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
contentHeight: content.implicitHeight anchors.right: parent.right
anchors.left: parent.left
Column { enabled: ServersModel.isProcessedServerHasWriteAccess()
id: content
anchors.top: parent.top model: SftpConfigModel
anchors.left: parent.left
anchors.right: parent.right
enabled: ServersModel.isProcessedServerHasWriteAccess() delegate: ColumnLayout {
width: listView.width
ListView { spacing: 0
id: listview
width: parent.width BaseHeaderType {
height: listview.contentItem.height Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 16
clip: true headerText: qsTr("SFTP settings")
interactive: false }
model: SftpConfigModel LabelWithButtonType {
id: hostLabel
onFocusChanged: { Layout.fillWidth: true
if (focus) { Layout.topMargin: 32
listview.currentItem.listViewFocusItem.forceActiveFocus() Layout.leftMargin: 16
} Layout.rightMargin: 16
text: qsTr("Host")
descriptionText: ServersModel.getProcessedServerData("hostName")
descriptionOnTop: true
rightImageSource: "qrc:/images/controls/copy.svg"
rightImageColor: AmneziaStyle.color.paleGray
clickedFunction: function() {
GC.copyToClipBoard(descriptionText)
PageController.showNotificationMessage(qsTr("Copied"))
}
}
LabelWithButtonType {
id: portLabel
Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 16
text: qsTr("Port")
descriptionText: port
descriptionOnTop: true
rightImageSource: "qrc:/images/controls/copy.svg"
rightImageColor: AmneziaStyle.color.paleGray
clickedFunction: function() {
GC.copyToClipBoard(descriptionText)
PageController.showNotificationMessage(qsTr("Copied"))
}
}
LabelWithButtonType {
id: usernameLabel
Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 16
text: qsTr("User name")
descriptionText: username
descriptionOnTop: true
rightImageSource: "qrc:/images/controls/copy.svg"
rightImageColor: AmneziaStyle.color.paleGray
clickedFunction: function() {
GC.copyToClipBoard(descriptionText)
PageController.showNotificationMessage(qsTr("Copied"))
}
}
LabelWithButtonType {
id: passwordLabel
Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 16
text: qsTr("Password")
descriptionText: password
descriptionOnTop: true
rightImageSource: "qrc:/images/controls/copy.svg"
rightImageColor: AmneziaStyle.color.paleGray
buttonImageSource: hideDescription ? "qrc:/images/controls/eye.svg" : "qrc:/images/controls/eye-off.svg"
clickedFunction: function() {
GC.copyToClipBoard(descriptionText)
PageController.showNotificationMessage(qsTr("Copied"))
}
}
BasicButtonType {
id: mountButton
visible: !GC.isMobile()
Layout.fillWidth: true
Layout.topMargin: 24
Layout.bottomMargin: 24
Layout.leftMargin: 16
Layout.rightMargin: 16
defaultColor: AmneziaStyle.color.transparent
hoveredColor: AmneziaStyle.color.translucentWhite
pressedColor: AmneziaStyle.color.sheerWhite
disabledColor: AmneziaStyle.color.mutedGray
textColor: AmneziaStyle.color.paleGray
borderWidth: 1
text: qsTr("Mount folder on device")
clickedFunc: function() {
PageController.showBusyIndicator(true)
InstallController.mountSftpDrive(port, password, username)
PageController.showBusyIndicator(false)
}
}
ParagraphTextType {
Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 16
readonly property string windowsFirstLink: "<a href=\"https://github.com/billziss-gh/winfsp/releases/latest\" style=\"color: #FBB26A;\">WinFsp</a>"
readonly property string windowsSecondLink: "<a href=\"https://github.com/billziss-gh/sshfs-win/releases\" style=\"color: #FBB26A;\">SSHFS-Win</a>"
readonly property string macosFirstLink: "<a href=\"https://osxfuse.github.io/\" style=\"color: #FBB26A;\">macFUSE</a>"
readonly property string macosSecondLink: "<a href=\"https://osxfuse.github.io/\" style=\"color: #FBB26A;\">SSHFS</a>"
onLinkActivated: function(link) {
Qt.openUrlExternally(link)
}
textFormat: Text.RichText
text: {
var str = qsTr("In order to mount remote SFTP folder as local drive, perform following steps: <br>")
if (Qt.platform.os === "windows") {
str += qsTr("<br>1. Install the latest version of ") + windowsFirstLink + "\n"
str += qsTr("<br>2. Install the latest version of ") + windowsSecondLink + "\n"
} else if (Qt.platform.os === "osx") {
str += qsTr("<br>1. Install the latest version of ") + macosFirstLink + "\n"
str += qsTr("<br>2. Install the latest version of ") + macosSecondLink + "\n"
} else if (Qt.platform.os === "linux") {
return ""
} else return ""
return str
} }
delegate: Item { MouseArea {
implicitWidth: listview.width anchors.fill: parent
implicitHeight: col.implicitHeight acceptedButtons: Qt.NoButton
cursorShape: parent.hoveredLink ? Qt.PointingHandCursor : Qt.ArrowCursor
}
}
property alias listViewFocusItem: hostLabel.rightButton BasicButtonType {
id: detailedInstructionsButton
ColumnLayout { Layout.topMargin: 16
id: col Layout.bottomMargin: 16
Layout.leftMargin: 8
implicitHeight: 32
anchors.top: parent.top defaultColor: AmneziaStyle.color.transparent
anchors.left: parent.left hoveredColor: AmneziaStyle.color.translucentWhite
anchors.right: parent.right pressedColor: AmneziaStyle.color.sheerWhite
disabledColor: AmneziaStyle.color.mutedGray
textColor: AmneziaStyle.color.goldenApricot
spacing: 0 text: qsTr("Detailed instructions")
BaseHeaderType { clickedFunc: function() {
Layout.fillWidth: true // Qt.openUrlExternally("https://github.com/amnezia-vpn/desktop-client/releases/latest")
Layout.leftMargin: 16
Layout.rightMargin: 16
headerText: qsTr("SFTP settings")
}
LabelWithButtonType {
id: hostLabel
Layout.fillWidth: true
Layout.topMargin: 32
parentFlickable: fl
text: qsTr("Host")
descriptionText: ServersModel.getProcessedServerData("hostName")
descriptionOnTop: true
rightImageSource: "qrc:/images/controls/copy.svg"
rightImageColor: AmneziaStyle.color.paleGray
clickedFunction: function() {
GC.copyToClipBoard(descriptionText)
PageController.showNotificationMessage(qsTr("Copied"))
if (!GC.isMobile()) {
this.rightButton.forceActiveFocus()
}
}
}
LabelWithButtonType {
id: portLabel
Layout.fillWidth: true
text: qsTr("Port")
descriptionText: port
descriptionOnTop: true
parentFlickable: fl
rightImageSource: "qrc:/images/controls/copy.svg"
rightImageColor: AmneziaStyle.color.paleGray
clickedFunction: function() {
GC.copyToClipBoard(descriptionText)
PageController.showNotificationMessage(qsTr("Copied"))
if (!GC.isMobile()) {
this.rightButton.forceActiveFocus()
}
}
}
LabelWithButtonType {
id: usernameLabel
Layout.fillWidth: true
text: qsTr("User name")
descriptionText: username
descriptionOnTop: true
parentFlickable: fl
rightImageSource: "qrc:/images/controls/copy.svg"
rightImageColor: AmneziaStyle.color.paleGray
clickedFunction: function() {
GC.copyToClipBoard(descriptionText)
PageController.showNotificationMessage(qsTr("Copied"))
if (!GC.isMobile()) {
this.rightButton.forceActiveFocus()
}
}
}
LabelWithButtonType {
id: passwordLabel
Layout.fillWidth: true
text: qsTr("Password")
descriptionText: password
descriptionOnTop: true
parentFlickable: fl
rightImageSource: "qrc:/images/controls/copy.svg"
rightImageColor: AmneziaStyle.color.paleGray
buttonImageSource: hideDescription ? "qrc:/images/controls/eye.svg" : "qrc:/images/controls/eye-off.svg"
clickedFunction: function() {
GC.copyToClipBoard(descriptionText)
PageController.showNotificationMessage(qsTr("Copied"))
if (!GC.isMobile()) {
this.rightButton.forceActiveFocus()
}
}
}
BasicButtonType {
id: mountButton
visible: !GC.isMobile()
Layout.fillWidth: true
Layout.topMargin: 24
Layout.bottomMargin: 24
Layout.leftMargin: 16
Layout.rightMargin: 16
defaultColor: AmneziaStyle.color.transparent
hoveredColor: AmneziaStyle.color.translucentWhite
pressedColor: AmneziaStyle.color.sheerWhite
disabledColor: AmneziaStyle.color.mutedGray
textColor: AmneziaStyle.color.paleGray
borderWidth: 1
parentFlickable: fl
text: qsTr("Mount folder on device")
clickedFunc: function() {
PageController.showBusyIndicator(true)
InstallController.mountSftpDrive(port, password, username)
PageController.showBusyIndicator(false)
}
}
ParagraphTextType {
Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 16
readonly property string windowsFirstLink: "<a href=\"https://github.com/billziss-gh/winfsp/releases/latest\" style=\"color: #FBB26A;\">WinFsp</a>"
readonly property string windowsSecondLink: "<a href=\"https://github.com/billziss-gh/sshfs-win/releases\" style=\"color: #FBB26A;\">SSHFS-Win</a>"
readonly property string macosFirstLink: "<a href=\"https://osxfuse.github.io/\" style=\"color: #FBB26A;\">macFUSE</a>"
readonly property string macosSecondLink: "<a href=\"https://osxfuse.github.io/\" style=\"color: #FBB26A;\">SSHFS</a>"
onLinkActivated: function(link) {
Qt.openUrlExternally(link)
}
textFormat: Text.RichText
text: {
var str = qsTr("In order to mount remote SFTP folder as local drive, perform following steps: <br>")
if (Qt.platform.os === "windows") {
str += qsTr("<br>1. Install the latest version of ") + windowsFirstLink + "\n"
str += qsTr("<br>2. Install the latest version of ") + windowsSecondLink + "\n"
} else if (Qt.platform.os === "osx") {
str += qsTr("<br>1. Install the latest version of ") + macosFirstLink + "\n"
str += qsTr("<br>2. Install the latest version of ") + macosSecondLink + "\n"
} else if (Qt.platform.os === "linux") {
return ""
} else return ""
return str
}
MouseArea {
anchors.fill: parent
acceptedButtons: Qt.NoButton
cursorShape: parent.hoveredLink ? Qt.PointingHandCursor : Qt.ArrowCursor
}
}
BasicButtonType {
id: detailedInstructionsButton
Layout.topMargin: 16
Layout.bottomMargin: 16
Layout.leftMargin: 8
implicitHeight: 32
defaultColor: AmneziaStyle.color.transparent
hoveredColor: AmneziaStyle.color.translucentWhite
pressedColor: AmneziaStyle.color.sheerWhite
disabledColor: AmneziaStyle.color.mutedGray
textColor: AmneziaStyle.color.goldenApricot
text: qsTr("Detailed instructions")
parentFlickable: fl
clickedFunc: function() {
// Qt.openUrlExternally("https://github.com/amnezia-vpn/desktop-client/releases/latest")
}
}
}
} }
} }
} }

View file

@ -25,327 +25,290 @@ PageType {
} }
} }
ColumnLayout { BackButtonType {
id: backButtonLayout id: backButton
anchors.top: parent.top anchors.top: parent.top
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
anchors.topMargin: 20 anchors.topMargin: 20
BackButtonType { onFocusChanged: {
id: backButton if (this.activeFocus) {
listView.positionViewAtBeginning()
}
} }
} }
FlickableType { ListViewType {
id: fl id: listView
anchors.top: backButtonLayout.bottom
anchors.top: backButton.bottom
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
contentHeight: listview.implicitHeight anchors.right: parent.right
anchors.left: parent.left
ListView { model: Socks5ProxyConfigModel
id: listview
width: parent.width delegate: ColumnLayout {
height: listview.contentItem.height width: listView.width
clip: true spacing: 0
interactive: false
model: Socks5ProxyConfigModel BaseHeaderType {
Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 16
onFocusChanged: { headerText: qsTr("SOCKS5 settings")
if (focus) { }
listview.currentItem.focusItemId.forceActiveFocus()
LabelWithButtonType {
Layout.fillWidth: true
Layout.topMargin: 32
Layout.rightMargin: 16
Layout.bottomMargin: 16
text: qsTr("Host")
descriptionText: ServersModel.getProcessedServerData("hostName")
descriptionOnTop: true
rightImageSource: "qrc:/images/controls/copy.svg"
rightImageColor: AmneziaStyle.color.paleGray
clickedFunction: function() {
GC.copyToClipBoard(descriptionText)
PageController.showNotificationMessage(qsTr("Copied"))
} }
} }
delegate: Item { LabelWithButtonType {
implicitWidth: listview.width Layout.fillWidth: true
implicitHeight: content.implicitHeight Layout.rightMargin: 16
Layout.bottomMargin: 16
property alias focusItemId: hostLabel.rightButton text: qsTr("Port")
descriptionText: port
ColumnLayout { descriptionOnTop: true
id: content
rightImageSource: "qrc:/images/controls/copy.svg"
rightImageColor: AmneziaStyle.color.paleGray
clickedFunction: function() {
GC.copyToClipBoard(descriptionText)
PageController.showNotificationMessage(qsTr("Copied"))
}
}
LabelWithButtonType {
Layout.fillWidth: true
Layout.rightMargin: 16
Layout.bottomMargin: 16
text: qsTr("User name")
descriptionText: username
descriptionOnTop: true
rightImageSource: "qrc:/images/controls/copy.svg"
rightImageColor: AmneziaStyle.color.paleGray
clickedFunction: function() {
GC.copyToClipBoard(descriptionText)
PageController.showNotificationMessage(qsTr("Copied"))
}
}
LabelWithButtonType {
Layout.fillWidth: true
Layout.rightMargin: 16
Layout.bottomMargin: 16
text: qsTr("Password")
descriptionText: password
descriptionOnTop: true
rightImageSource: "qrc:/images/controls/copy.svg"
rightImageColor: AmneziaStyle.color.paleGray
buttonImageSource: hideDescription ? "qrc:/images/controls/eye.svg" : "qrc:/images/controls/eye-off.svg"
clickedFunction: function() {
GC.copyToClipBoard(descriptionText)
PageController.showNotificationMessage(qsTr("Copied"))
}
}
DrawerType2 {
id: changeSettingsDrawer
parent: root
anchors.fill: parent
expandedHeight: root.height * 0.9
expandedStateContent: ColumnLayout {
property string tempPort: port
property string tempUsername: username
property string tempPassword: password
anchors.top: parent.top anchors.top: parent.top
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
anchors.topMargin: 32
anchors.leftMargin: 16
anchors.rightMargin: 16
spacing: 0 spacing: 0
Connections {
target: changeSettingsDrawer
function onOpened() {
tempPort = port
tempUsername = username
tempPassword = password
}
function onClosed() {
port = tempPort
username = tempUsername
password = tempPassword
portTextField.textField.text = port
usernameTextField.textField.text = username
passwordTextField.textField.text = password
}
}
BaseHeaderType { BaseHeaderType {
Layout.fillWidth: true Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 16 Layout.rightMargin: 16
Layout.bottomMargin: 16
headerText: qsTr("SOCKS5 settings") headerText: qsTr("SOCKS5 settings")
} }
LabelWithButtonType { TextFieldWithHeaderType {
id: hostLabel id: portTextField
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 32 Layout.topMargin: 40
Layout.rightMargin: 16
Layout.bottomMargin: 16
parentFlickable: fl headerText: qsTr("Port")
textField.text: port
textField.maximumLength: 5
textField.validator: IntValidator { bottom: 1; top: 65535 }
text: qsTr("Host") textField.onEditingFinished: {
descriptionText: ServersModel.getProcessedServerData("hostName") textField.text = textField.text.replace(/^\s+|\s+$/g, '')
if (textField.text !== port) {
descriptionOnTop: true port = textField.text
rightImageSource: "qrc:/images/controls/copy.svg"
rightImageColor: AmneziaStyle.color.paleGray
clickedFunction: function() {
GC.copyToClipBoard(descriptionText)
PageController.showNotificationMessage(qsTr("Copied"))
if (!GC.isMobile()) {
this.rightButton.forceActiveFocus()
} }
} }
} }
LabelWithButtonType { TextFieldWithHeaderType {
id: portLabel id: usernameTextField
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 16
Layout.rightMargin: 16
Layout.bottomMargin: 16
text: qsTr("Port") headerText: qsTr("Username")
descriptionText: port textField.placeholderText: "username"
textField.text: username
textField.maximumLength: 32
descriptionOnTop: true textField.onEditingFinished: {
textField.text = textField.text.replace(/^\s+|\s+$/g, '')
parentFlickable: fl if (textField.text !== username) {
username = textField.text
rightImageSource: "qrc:/images/controls/copy.svg"
rightImageColor: AmneziaStyle.color.paleGray
clickedFunction: function() {
GC.copyToClipBoard(descriptionText)
PageController.showNotificationMessage(qsTr("Copied"))
if (!GC.isMobile()) {
this.rightButton.forceActiveFocus()
} }
} }
} }
LabelWithButtonType { TextFieldWithHeaderType {
id: usernameLabel id: passwordTextField
property bool hidePassword: true
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 16
Layout.rightMargin: 16
Layout.bottomMargin: 16
text: qsTr("User name") headerText: qsTr("Password")
descriptionText: username textField.placeholderText: "password"
textField.text: password
textField.maximumLength: 32
descriptionOnTop: true textField.echoMode: hidePassword ? TextInput.Password : TextInput.Normal
buttonImageSource: textField.text !== "" ? (hidePassword ? "qrc:/images/controls/eye.svg" : "qrc:/images/controls/eye-off.svg")
: ""
parentFlickable: fl clickedFunc: function() {
hidePassword = !hidePassword
rightImageSource: "qrc:/images/controls/copy.svg"
rightImageColor: AmneziaStyle.color.paleGray
clickedFunction: function() {
GC.copyToClipBoard(descriptionText)
PageController.showNotificationMessage(qsTr("Copied"))
if (!GC.isMobile()) {
this.rightButton.forceActiveFocus()
}
} }
}
LabelWithButtonType { textField.onFocusChanged: {
id: passwordLabel textField.text = textField.text.replace(/^\s+|\s+$/g, '')
Layout.fillWidth: true if (textField.text !== password) {
password = textField.text
text: qsTr("Password")
descriptionText: password
descriptionOnTop: true
parentFlickable: fl
rightImageSource: "qrc:/images/controls/copy.svg"
rightImageColor: AmneziaStyle.color.paleGray
buttonImageSource: hideDescription ? "qrc:/images/controls/eye.svg" : "qrc:/images/controls/eye-off.svg"
clickedFunction: function() {
GC.copyToClipBoard(descriptionText)
PageController.showNotificationMessage(qsTr("Copied"))
if (!GC.isMobile()) {
this.rightButton.forceActiveFocus()
}
}
}
DrawerType2 {
id: changeSettingsDrawer
parent: root
anchors.fill: parent
expandedHeight: root.height * 0.9
expandedStateContent: ColumnLayout {
property string tempPort: port
property string tempUsername: username
property string tempPassword: password
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
anchors.topMargin: 32
anchors.leftMargin: 16
anchors.rightMargin: 16
spacing: 0
Connections {
target: changeSettingsDrawer
function onOpened() {
tempPort = port
tempUsername = username
tempPassword = password
}
function onClosed() {
port = tempPort
username = tempUsername
password = tempPassword
portTextField.textField.text = port
usernameTextField.textField.text = username
passwordTextField.textField.text = password
}
}
BaseHeaderType {
Layout.fillWidth: true
headerText: qsTr("SOCKS5 settings")
}
TextFieldWithHeaderType {
id: portTextField
Layout.fillWidth: true
Layout.topMargin: 40
parentFlickable: fl
headerText: qsTr("Port")
textField.text: port
textField.maximumLength: 5
textField.validator: IntValidator { bottom: 1; top: 65535 }
textField.onEditingFinished: {
textField.text = textField.text.replace(/^\s+|\s+$/g, '')
if (textField.text !== port) {
port = textField.text
}
}
}
TextFieldWithHeaderType {
id: usernameTextField
Layout.fillWidth: true
Layout.topMargin: 16
parentFlickable: fl
headerText: qsTr("Username")
textField.placeholderText: "username"
textField.text: username
textField.maximumLength: 32
textField.onEditingFinished: {
textField.text = textField.text.replace(/^\s+|\s+$/g, '')
if (textField.text !== username) {
username = textField.text
}
}
}
TextFieldWithHeaderType {
id: passwordTextField
property bool hidePassword: true
Layout.fillWidth: true
Layout.topMargin: 16
parentFlickable: fl
headerText: qsTr("Password")
textField.placeholderText: "password"
textField.text: password
textField.maximumLength: 32
textField.echoMode: hidePassword ? TextInput.Password : TextInput.Normal
buttonImageSource: textField.text !== "" ? (hidePassword ? "qrc:/images/controls/eye.svg" : "qrc:/images/controls/eye-off.svg")
: ""
clickedFunc: function() {
hidePassword = !hidePassword
}
textField.onFocusChanged: {
textField.text = textField.text.replace(/^\s+|\s+$/g, '')
if (textField.text !== password) {
password = textField.text
}
}
}
BasicButtonType {
id: saveButton
Layout.fillWidth: true
Layout.topMargin: 24
Layout.bottomMargin: 24
text: qsTr("Change connection settings")
clickedFunc: function() {
forceActiveFocus()
if (!portTextField.textField.acceptableInput) {
portTextField.errorText = qsTr("The port must be in the range of 1 to 65535")
return
}
if (usernameTextField.textField.text && passwordTextField.textField.text === "") {
passwordTextField.errorText = qsTr("Password cannot be empty")
return
} else if (usernameTextField.textField.text === "" && passwordTextField.textField.text) {
usernameTextField.errorText = qsTr("Username cannot be empty")
return
}
PageController.goToPage(PageEnum.PageSetupWizardInstalling)
InstallController.updateContainer(Socks5ProxyConfigModel.getConfig())
tempPort = portTextField.textField.text
tempUsername = usernameTextField.textField.text
tempPassword = passwordTextField.textField.text
changeSettingsDrawer.closeTriggered()
}
} }
} }
} }
BasicButtonType { BasicButtonType {
id: changeSettingsButton id: saveButton
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 24 Layout.topMargin: 24
Layout.bottomMargin: 24 Layout.bottomMargin: 24
Layout.leftMargin: 16
Layout.rightMargin: 16 Layout.rightMargin: 16
text: qsTr("Change connection settings") text: qsTr("Change connection settings")
clickedFunc: function() { clickedFunc: function() {
forceActiveFocus() if (!portTextField.textField.acceptableInput) {
changeSettingsDrawer.openTriggered() portTextField.errorText = qsTr("The port must be in the range of 1 to 65535")
return
}
if (usernameTextField.textField.text && passwordTextField.textField.text === "") {
passwordTextField.errorText = qsTr("Password cannot be empty")
return
} else if (usernameTextField.textField.text === "" && passwordTextField.textField.text) {
usernameTextField.errorText = qsTr("Username cannot be empty")
return
}
PageController.goToPage(PageEnum.PageSetupWizardInstalling)
InstallController.updateContainer(Socks5ProxyConfigModel.getConfig())
tempPort = portTextField.textField.text
tempUsername = usernameTextField.textField.text
tempPassword = passwordTextField.textField.text
changeSettingsDrawer.closeTriggered()
} }
} }
} }
} }
BasicButtonType {
id: changeSettingsButton
Layout.fillWidth: true
Layout.topMargin: 24
Layout.bottomMargin: 24
Layout.leftMargin: 16
Layout.rightMargin: 16
text: qsTr("Change connection settings")
clickedFunc: function() {
changeSettingsDrawer.openTriggered()
}
}
} }
} }
} }

View file

@ -25,34 +25,25 @@ PageType {
} }
} }
ColumnLayout { BackButtonType {
id: backButtonLayout id: backButton
anchors.top: parent.top anchors.top: parent.top
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
anchors.topMargin: 20 anchors.topMargin: 20
BackButtonType {
id: backButton
}
} }
FlickableType { ListViewType {
id: fl id: listView
anchors.top: backButtonLayout.bottom
anchors.top: backButton.bottom
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
contentHeight: content.implicitHeight anchors.right: parent.right
anchors.left: parent.left
ColumnLayout { header: ColumnLayout {
id: content width: listView.width
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
spacing: 0
BaseHeaderType { BaseHeaderType {
Layout.fillWidth: true Layout.fillWidth: true
@ -61,11 +52,19 @@ PageType {
headerText: qsTr("Tor website settings") headerText: qsTr("Tor website settings")
} }
}
model: 1 // fake model to force the ListView to be created without a model
delegate: ColumnLayout {
width: listView.width
LabelWithButtonType { LabelWithButtonType {
id: websiteName id: websiteName
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 32 Layout.topMargin: 32
Layout.bottomMargin: 24
text: qsTr("Website address") text: qsTr("Website address")
descriptionText: { descriptionText: {
@ -83,15 +82,16 @@ PageType {
clickedFunction: function() { clickedFunction: function() {
GC.copyToClipBoard(descriptionText) GC.copyToClipBoard(descriptionText)
PageController.showNotificationMessage(qsTr("Copied")) PageController.showNotificationMessage(qsTr("Copied"))
if (!GC.isMobile()) {
this.rightButton.forceActiveFocus()
}
} }
} }
}
footer: ColumnLayout {
width: listView.width
ParagraphTextType { ParagraphTextType {
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 40 Layout.topMargin: 16
Layout.leftMargin: 16 Layout.leftMargin: 16
Layout.rightMargin: 16 Layout.rightMargin: 16

View file

@ -1,156 +1,167 @@
import QtQuick import QtQuick
import QtQuick.Controls import QtQuick.Controls
import QtQuick.Layouts import QtQuick.Layouts
import QtQuick.Dialogs import QtQuick.Dialogs
import PageEnum 1.0 import PageEnum 1.0
import Style 1.0 import Style 1.0
import "./" import "./"
import "../Controls2" import "../Controls2"
import "../Controls2/TextTypes" import "../Controls2/TextTypes"
import "../Config" import "../Config"
PageType { PageType {
id: root id: root
FlickableType { ListViewType {
id: fl id: listView
anchors.top: parent.top
anchors.bottom: parent.bottom anchors.fill: parent
contentHeight: content.height
header: ColumnLayout {
ColumnLayout { width: listView.width
id: content
BaseHeaderType {
anchors.top: parent.top id: header
anchors.left: parent.left Layout.fillWidth: true
anchors.right: parent.right Layout.topMargin: 24
Layout.bottomMargin: 16
spacing: 0 Layout.rightMargin: 16
Layout.leftMargin: 16
BaseHeaderType {
id: header headerText: qsTr("Settings")
Layout.fillWidth: true }
Layout.topMargin: 24 }
Layout.rightMargin: 16
Layout.leftMargin: 16 model: settingsEntries
headerText: qsTr("Settings") delegate: ColumnLayout {
} width: listView.width
LabelWithButtonType { spacing: 0
id: account
Layout.fillWidth: true LabelWithButtonType {
Layout.topMargin: 16 Layout.fillWidth: true
Layout.leftMargin: 16
text: qsTr("Servers") Layout.rightMargin: 16
rightImageSource: "qrc:/images/controls/chevron-right.svg"
leftImageSource: "qrc:/images/controls/server.svg" visible: isVisible
clickedFunction: function() { text: title
PageController.goToPage(PageEnum.PageSettingsServersList) rightImageSource: "qrc:/images/controls/chevron-right.svg"
} leftImageSource: leftImagePath
}
clickedFunction: clickedHandler
DividerType {} }
LabelWithButtonType { DividerType {
id: connection visible: isVisible
Layout.fillWidth: true }
}
text: qsTr("Connection")
rightImageSource: "qrc:/images/controls/chevron-right.svg" footer: ColumnLayout {
leftImageSource: "qrc:/images/controls/radio.svg" width: listView.width
clickedFunction: function() { LabelWithButtonType {
PageController.goToPage(PageEnum.PageSettingsConnection) id: close
}
} visible: GC.isDesktop()
Layout.fillWidth: true
DividerType {} Layout.leftMargin: 16
Layout.rightMargin: 16
LabelWithButtonType {
id: application text: qsTr("Close application")
Layout.fillWidth: true leftImageSource: "qrc:/images/controls/x-circle.svg"
isLeftImageHoverEnabled: false
text: qsTr("Application")
rightImageSource: "qrc:/images/controls/chevron-right.svg" clickedFunction: function() {
leftImageSource: "qrc:/images/controls/app.svg" PageController.closeApplication()
}
clickedFunction: function() { }
PageController.goToPage(PageEnum.PageSettingsApplication)
} DividerType {
} Layout.fillWidth: true
Layout.leftMargin: 16
DividerType {} Layout.rightMargin: 16
LabelWithButtonType { visible: GC.isDesktop()
id: backup }
Layout.fillWidth: true }
}
text: qsTr("Backup")
rightImageSource: "qrc:/images/controls/chevron-right.svg" property list<QtObject> settingsEntries: [
leftImageSource: "qrc:/images/controls/save.svg" servers,
connection,
clickedFunction: function() { application,
PageController.goToPage(PageEnum.PageSettingsBackup) backup,
} about,
} devConsole
]
DividerType {}
QtObject {
LabelWithButtonType { id: servers
id: about
Layout.fillWidth: true property string title: qsTr("Servers")
readonly property string leftImagePath: "qrc:/images/controls/server.svg"
text: qsTr("About AmneziaVPN") property bool isVisible: true
rightImageSource: "qrc:/images/controls/chevron-right.svg" readonly property var clickedHandler: function() {
leftImageSource: "qrc:/images/controls/amnezia.svg" PageController.goToPage(PageEnum.PageSettingsServersList)
}
clickedFunction: function() { }
PageController.goToPage(PageEnum.PageSettingsAbout)
} QtObject {
} id: connection
DividerType {} property string title: qsTr("Connection")
readonly property string leftImagePath: "qrc:/images/controls/radio.svg"
LabelWithButtonType { property bool isVisible: true
id: devConsole readonly property var clickedHandler: function() {
visible: SettingsController.isDevModeEnabled PageController.goToPage(PageEnum.PageSettingsConnection)
Layout.fillWidth: true }
}
text: qsTr("Dev console")
rightImageSource: "qrc:/images/controls/chevron-right.svg" QtObject {
leftImageSource: "qrc:/images/controls/bug.svg" id: application
clickedFunction: function() { property string title: qsTr("Application")
PageController.goToPage(PageEnum.PageDevMenu) readonly property string leftImagePath: "qrc:/images/controls/app.svg"
} property bool isVisible: true
} readonly property var clickedHandler: function() {
PageController.goToPage(PageEnum.PageSettingsApplication)
DividerType { }
visible: SettingsController.isDevModeEnabled }
}
QtObject {
LabelWithButtonType { id: backup
id: close
visible: GC.isDesktop() property string title: qsTr("Backup")
Layout.fillWidth: true readonly property string leftImagePath: "qrc:/images/controls/save.svg"
Layout.preferredHeight: about.height property bool isVisible: true
readonly property var clickedHandler: function() {
text: qsTr("Close application") PageController.goToPage(PageEnum.PageSettingsBackup)
leftImageSource: "qrc:/images/controls/x-circle.svg" }
isLeftImageHoverEnabled: false }
clickedFunction: function() { QtObject {
PageController.closeApplication() id: about
}
} property string title: qsTr("About AmneziaVPN")
readonly property string leftImagePath: "qrc:/images/controls/amnezia.svg"
DividerType { property bool isVisible: true
visible: GC.isDesktop() readonly property var clickedHandler: function() {
} PageController.goToPage(PageEnum.PageSettingsAbout)
} }
} }
}
QtObject {
id: devConsole
property string title: qsTr("Dev console")
readonly property string leftImagePath: "qrc:/images/controls/bug.svg"
property bool isVisible: SettingsController.isDevModeEnabled
readonly property var clickedHandler: function() {
PageController.goToPage(PageEnum.PageDevMenu)
}
}
}

View file

@ -29,58 +29,7 @@ PageType {
} }
} }
QtObject { ListViewType {
id: telegramGroup
readonly property string title: qsTr("Telegram group")
readonly property string description: qsTr("To discuss features")
readonly property string imageSource: "qrc:/images/controls/telegram.svg"
readonly property var handler: function() {
Qt.openUrlExternally(qsTr("https://t.me/amnezia_vpn_en"))
}
}
QtObject {
id: mail
readonly property string title: qsTr("support@amnezia.org")
readonly property string description: qsTr("For reviews and bug reports")
readonly property string imageSource: "qrc:/images/controls/mail.svg"
readonly property var handler: function() {
Qt.openUrlExternally(qsTr("mailto:support@amnezia.org"))
}
}
QtObject {
id: github
readonly property string title: qsTr("GitHub")
readonly property string description: qsTr("Discover the source code")
readonly property string imageSource: "qrc:/images/controls/github.svg"
readonly property var handler: function() {
Qt.openUrlExternally(qsTr("https://github.com/amnezia-vpn/amnezia-client"))
}
}
QtObject {
id: website
readonly property string title: qsTr("Website")
readonly property string description: qsTr("Visit official website")
readonly property string imageSource: "qrc:/images/controls/amnezia.svg"
readonly property var handler: function() {
Qt.openUrlExternally(LanguageModel.getCurrentSiteUrl())
}
}
property list<QtObject> contacts: [
telegramGroup,
mail,
github,
website
]
ListView {
id: listView id: listView
anchors.top: backButton.bottom anchors.top: backButton.bottom
@ -88,38 +37,6 @@ PageType {
anchors.right: parent.right anchors.right: parent.right
anchors.left: parent.left anchors.left: parent.left
property bool isFocusable: true
Keys.onTabPressed: {
FocusController.nextKeyTabItem()
}
Keys.onBacktabPressed: {
FocusController.previousKeyTabItem()
}
Keys.onUpPressed: {
FocusController.nextKeyUpItem()
}
Keys.onDownPressed: {
FocusController.nextKeyDownItem()
}
Keys.onLeftPressed: {
FocusController.nextKeyLeftItem()
}
Keys.onRightPressed: {
FocusController.nextKeyRightItem()
}
ScrollBar.vertical: ScrollBarType {}
model: contacts
clip: true
header: ColumnLayout { header: ColumnLayout {
width: listView.width width: listView.width
@ -170,6 +87,8 @@ PageType {
} }
} }
model: contacts
delegate: ColumnLayout { delegate: ColumnLayout {
width: listView.width width: listView.width
@ -257,4 +176,55 @@ PageType {
} }
} }
} }
QtObject {
id: telegramGroup
readonly property string title: qsTr("Telegram group")
readonly property string description: qsTr("To discuss features")
readonly property string imageSource: "qrc:/images/controls/telegram.svg"
readonly property var handler: function() {
Qt.openUrlExternally(qsTr("https://t.me/amnezia_vpn_en"))
}
}
QtObject {
id: mail
readonly property string title: qsTr("support@amnezia.org")
readonly property string description: qsTr("For reviews and bug reports")
readonly property string imageSource: "qrc:/images/controls/mail.svg"
readonly property var handler: function() {
Qt.openUrlExternally(qsTr("mailto:support@amnezia.org"))
}
}
QtObject {
id: github
readonly property string title: qsTr("GitHub")
readonly property string description: qsTr("Discover the source code")
readonly property string imageSource: "qrc:/images/controls/github.svg"
readonly property var handler: function() {
Qt.openUrlExternally(qsTr("https://github.com/amnezia-vpn/amnezia-client"))
}
}
QtObject {
id: website
readonly property string title: qsTr("Website")
readonly property string description: qsTr("Visit official website")
readonly property string imageSource: "qrc:/images/controls/amnezia.svg"
readonly property var handler: function() {
Qt.openUrlExternally(LanguageModel.getCurrentSiteUrl())
}
}
property list<QtObject> contacts: [
telegramGroup,
mail,
github,
website
]
} }

View file

@ -1,220 +1,236 @@
import QtQuick import QtQuick
import QtQuick.Controls import QtQuick.Controls
import QtQuick.Layouts import QtQuick.Layouts
import QtQuick.Dialogs import QtQuick.Dialogs
import QtCore import QtCore
import SortFilterProxyModel 0.2 import SortFilterProxyModel 0.2
import PageEnum 1.0 import PageEnum 1.0
import Style 1.0 import Style 1.0
import "./" import "./"
import "../Controls2" import "../Controls2"
import "../Controls2/TextTypes" import "../Controls2/TextTypes"
import "../Config" import "../Config"
import "../Components" import "../Components"
PageType { PageType {
id: root id: root
property string configExtension: ".conf" property string configExtension: ".conf"
property string configCaption: qsTr("Save AmneziaVPN config") property string configCaption: qsTr("Save AmneziaVPN config")
ListViewType { BackButtonType {
id: listView id: backButton
anchors.fill: parent anchors.top: parent.top
anchors.topMargin: 20 anchors.left: parent.left
anchors.bottomMargin: 24 anchors.right: parent.right
anchors.topMargin: 20
model: ApiCountryModel }
header: ColumnLayout { ListViewType {
width: listView.width id: listView
BackButtonType { anchors.top: backButton.bottom
id: backButton anchors.bottom: parent.bottom
} anchors.right: parent.right
anchors.left: parent.left
BaseHeaderType {
id: header model: ApiCountryModel
Layout.fillWidth: true header: ColumnLayout {
Layout.rightMargin: 16 width: listView.width
Layout.leftMargin: 16
BaseHeaderType {
headerText: qsTr("Configuration Files") id: header
descriptionText: qsTr("For router setup or the AmneziaWG app")
} Layout.fillWidth: true
} Layout.rightMargin: 16
Layout.leftMargin: 16
delegate: ColumnLayout {
width: listView.width headerText: qsTr("Configuration Files")
descriptionText: qsTr("For router setup or the AmneziaWG app")
LabelWithButtonType { }
Layout.fillWidth: true }
Layout.topMargin: 6
delegate: ColumnLayout {
text: countryName width: listView.width
descriptionText: isWorkerExpired ? qsTr("The configuration needs to be reissued") : ""
descriptionColor: AmneziaStyle.color.vibrantRed LabelWithButtonType {
Layout.fillWidth: true
leftImageSource: "qrc:/countriesFlags/images/flagKit/" + countryImageCode + ".svg" Layout.topMargin: 6
rightImageSource: isIssued ? "qrc:/images/controls/more-vertical.svg" : "qrc:/images/controls/download.svg"
text: countryName
clickedFunction: function() { descriptionText: isWorkerExpired ? qsTr("The configuration needs to be reissued") : ""
if (isIssued) { descriptionColor: AmneziaStyle.color.vibrantRed
moreOptionsDrawer.countryName = countryName
moreOptionsDrawer.countryCode = countryCode leftImageSource: "qrc:/countriesFlags/images/flagKit/" + countryImageCode + ".svg"
moreOptionsDrawer.openTriggered() rightImageSource: isIssued ? "qrc:/images/controls/more-vertical.svg" : "qrc:/images/controls/download.svg"
} else {
issueConfig(countryCode) clickedFunction: function() {
} if (isIssued) {
} moreOptionsDrawer.countryName = countryName
} moreOptionsDrawer.countryCode = countryCode
moreOptionsDrawer.openTriggered()
DividerType {} } else {
} issueConfig(countryCode)
} }
}
DrawerType2 { }
id: moreOptionsDrawer
DividerType {}
property string countryName }
property string countryCode }
anchors.fill: parent DrawerType2 {
expandedHeight: parent.height * 0.4375 id: moreOptionsDrawer
expandedStateContent: Item { property string countryName
implicitHeight: moreOptionsDrawer.expandedHeight property string countryCode
BackButtonType { anchors.fill: parent
id: moreOptionsDrawerBackButton expandedHeight: parent.height * 0.4375
anchors.top: parent.top expandedStateContent: Item {
anchors.left: parent.left implicitHeight: moreOptionsDrawer.expandedHeight
anchors.right: parent.right
anchors.topMargin: 16 BackButtonType {
id: moreOptionsDrawerBackButton
backButtonFunction: function() {
moreOptionsDrawer.closeTriggered() anchors.top: parent.top
} anchors.left: parent.left
} anchors.right: parent.right
anchors.topMargin: 16
FlickableType {
anchors.top: moreOptionsDrawerBackButton.bottom backButtonFunction: function() {
anchors.left: parent.left moreOptionsDrawer.closeTriggered()
anchors.right: parent.right }
anchors.bottom: parent.bottom }
contentHeight: moreOptionsDrawerContent.height ListViewType {
id: drawerListView
ColumnLayout {
id: moreOptionsDrawerContent anchors.top: moreOptionsDrawerBackButton.bottom
anchors.bottom: parent.bottom
anchors.top: parent.top anchors.left: parent.left
anchors.left: parent.left anchors.right: parent.right
anchors.right: parent.right
header: ColumnLayout {
Header2Type { width: drawerListView.width
Layout.fillWidth: true
Layout.margins: 16 Header2Type {
Layout.fillWidth: true
headerText: moreOptionsDrawer.countryName + qsTr(" configuration file") Layout.margins: 16
}
headerText: moreOptionsDrawer.countryName + qsTr(" configuration file")
LabelWithButtonType { }
Layout.fillWidth: true }
text: qsTr("Generate a new configuration file") model: 1 // fake model to force the ListView to be created without a model
descriptionText: qsTr("The previously created one will stop working")
delegate: ColumnLayout {
clickedFunction: function() { width: drawerListView.width
showQuestion(true, moreOptionsDrawer.countryCode, moreOptionsDrawer.countryName)
} LabelWithButtonType {
} Layout.fillWidth: true
Layout.leftMargin: 16
DividerType {} Layout.rightMargin: 16
LabelWithButtonType { text: qsTr("Generate a new configuration file")
Layout.fillWidth: true descriptionText: qsTr("The previously created one will stop working")
text: qsTr("Revoke the current configuration file")
clickedFunction: function() {
clickedFunction: function() { showQuestion(true, moreOptionsDrawer.countryCode, moreOptionsDrawer.countryName)
showQuestion(false, moreOptionsDrawer.countryCode, moreOptionsDrawer.countryName) }
} }
}
DividerType {}
DividerType {} }
}
} footer: ColumnLayout {
} width: drawerListView.width
}
LabelWithButtonType {
function issueConfig(countryCode) { Layout.fillWidth: true
var fileName = "" Layout.leftMargin: 16
if (GC.isMobile()) { Layout.rightMargin: 16
fileName = countryCode + configExtension
} else { text: qsTr("Revoke the current configuration file")
fileName = SystemController.getFileName(configCaption,
qsTr("Config files (*" + configExtension + ")"), clickedFunction: function() {
StandardPaths.standardLocations(StandardPaths.DocumentsLocation) + "/" + countryCode, showQuestion(false, moreOptionsDrawer.countryCode, moreOptionsDrawer.countryName)
true, }
configExtension) }
}
if (fileName !== "") { DividerType {}
PageController.showBusyIndicator(true) }
let result = ApiConfigsController.exportNativeConfig(countryCode, fileName) }
if (result) { }
ApiSettingsController.getAccountInfo(true) }
}
function issueConfig(countryCode) {
PageController.showBusyIndicator(false) var fileName = ""
if (result) { if (GC.isMobile()) {
PageController.showNotificationMessage(qsTr("Config file saved")) fileName = countryCode + configExtension
} } else {
} fileName = SystemController.getFileName(configCaption,
} qsTr("Config files (*" + configExtension + ")"),
StandardPaths.standardLocations(StandardPaths.DocumentsLocation) + "/" + countryCode,
function revokeConfig(countryCode) { true,
PageController.showBusyIndicator(true) configExtension)
let result = ApiConfigsController.revokeNativeConfig(countryCode) }
if (result) { if (fileName !== "") {
ApiSettingsController.getAccountInfo(true) PageController.showBusyIndicator(true)
} let result = ApiConfigsController.exportNativeConfig(countryCode, fileName)
PageController.showBusyIndicator(false) if (result) {
ApiSettingsController.getAccountInfo(true)
if (result) { }
PageController.showNotificationMessage(qsTr("The config has been revoked"))
} PageController.showBusyIndicator(false)
} if (result) {
PageController.showNotificationMessage(qsTr("Config file saved"))
function showQuestion(isConfigIssue, countryCode, countryName) { }
var headerText }
if (isConfigIssue) { }
headerText = qsTr("Generate a new %1 configuration file?").arg(countryName)
} else { function revokeConfig(countryCode) {
headerText = qsTr("Revoke the current %1 configuration file?").arg(countryName) PageController.showBusyIndicator(true)
} let result = ApiConfigsController.revokeNativeConfig(countryCode)
if (result) {
var descriptionText = qsTr("Your previous configuration file will no longer work, and it will not be possible to connect using it") ApiSettingsController.getAccountInfo(true)
var yesButtonText = isConfigIssue ? qsTr("Download") : qsTr("Continue") }
var noButtonText = qsTr("Cancel") PageController.showBusyIndicator(false)
var yesButtonFunction = function() { if (result) {
if (isConfigIssue) { PageController.showNotificationMessage(qsTr("The config has been revoked"))
issueConfig(countryCode) }
} else { }
revokeConfig(countryCode)
} function showQuestion(isConfigIssue, countryCode, countryName) {
moreOptionsDrawer.closeTriggered() var headerText
} if (isConfigIssue) {
var noButtonFunction = function() { headerText = qsTr("Generate a new %1 configuration file?").arg(countryName)
} } else {
headerText = qsTr("Revoke the current %1 configuration file?").arg(countryName)
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction) }
}
} var descriptionText = qsTr("Your previous configuration file will no longer work, and it will not be possible to connect using it")
var yesButtonText = isConfigIssue ? qsTr("Download") : qsTr("Continue")
var noButtonText = qsTr("Cancel")
var yesButtonFunction = function() {
if (isConfigIssue) {
issueConfig(countryCode)
} else {
revokeConfig(countryCode)
}
moreOptionsDrawer.closeTriggered()
}
var noButtonFunction = function() {}
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
}
}

View file

@ -158,32 +158,6 @@ PageType {
readonly property bool isVisibleForAmneziaFree: ApiAccountInfoModel.data("isComponentVisible") readonly property bool isVisibleForAmneziaFree: ApiAccountInfoModel.data("isComponentVisible")
SwitcherType {
id: switcher
readonly property bool isVlessProtocol: ApiConfigsController.isVlessProtocol()
Layout.fillWidth: true
Layout.topMargin: 24
Layout.rightMargin: 16
Layout.leftMargin: 16
visible: ApiAccountInfoModel.data("isProtocolSelectionSupported")
text: qsTr("Use VLESS protocol")
checked: switcher.isVlessProtocol
onToggled: function() {
if (ServersModel.isDefaultServerCurrentlyProcessed() && ConnectionController.isConnected) {
PageController.showNotificationMessage(qsTr("Cannot change protocol during active connection"))
} else {
PageController.showBusyIndicator(true)
ApiConfigsController.setCurrentProtocol(switcher.isVlessProtocol ? "awg" : "vless")
ApiConfigsController.updateServiceFromGateway(ServersModel.processedIndex, "", "", true)
PageController.showBusyIndicator(false)
}
}
}
WarningType { WarningType {
id: warning id: warning

View file

@ -50,6 +50,7 @@ PageType {
readonly property string name: qsTr("Only the apps from the list should have access via VPN") readonly property string name: qsTr("Only the apps from the list should have access via VPN")
readonly property int type: routeMode.onlyForwardApps readonly property int type: routeMode.onlyForwardApps
} }
QtObject { QtObject {
id: allExceptApps id: allExceptApps
@ -146,77 +147,56 @@ PageType {
} }
} }
FlickableType { ListViewType {
id: listView
anchors.top: header.bottom anchors.top: header.bottom
anchors.topMargin: 16 anchors.bottom: addAppButton.top
contentHeight: col.implicitHeight + addAppButton.implicitHeight + addAppButton.anchors.bottomMargin + addAppButton.anchors.topMargin anchors.left: parent.left
anchors.right: parent.right
enabled: root.pageEnabled model: SortFilterProxyModel {
id: proxyAppSplitTunnelingModel
sourceModel: AppSplitTunnelingModel
filters: RegExpFilter {
roleName: "appPath"
pattern: ".*" + searchField.textField.text + ".*"
caseSensitivity: Qt.CaseInsensitive
}
sorters: [
RoleSorter { roleName: "appPath"; sortOrder: Qt.AscendingOrder }
]
}
Column { delegate: ColumnLayout {
id: col width: listView.width
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
ListView { LabelWithButtonType {
id: apps Layout.fillWidth: true
width: parent.width
height: apps.contentItem.height
model: SortFilterProxyModel { Layout.leftMargin: 16
id: proxyAppSplitTunnelingModel Layout.rightMargin: 16
sourceModel: AppSplitTunnelingModel
filters: RegExpFilter { text: appPath
roleName: "appPath" rightImageSource: "qrc:/images/controls/trash.svg"
pattern: ".*" + searchField.textField.text + ".*" rightImageColor: AmneziaStyle.color.paleGray
caseSensitivity: Qt.CaseInsensitive
clickedFunction: function() {
var headerText = qsTr("Remove ") + appPath + "?"
var yesButtonText = qsTr("Continue")
var noButtonText = qsTr("Cancel")
var yesButtonFunction = function() {
AppSplitTunnelingController.removeApp(proxyAppSplitTunnelingModel.mapToSource(index))
} }
sorters: [ var noButtonFunction = function() {
RoleSorter { roleName: "appPath"; sortOrder: Qt.AscendingOrder }
]
}
clip: true
interactive: false
delegate: Item {
implicitWidth: apps.width
implicitHeight: delegateContent.implicitHeight
ColumnLayout {
id: delegateContent
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
LabelWithButtonType {
Layout.fillWidth: true
text: appPath
rightImageSource: "qrc:/images/controls/trash.svg"
rightImageColor: AmneziaStyle.color.paleGray
clickedFunction: function() {
var headerText = qsTr("Remove ") + appPath + "?"
var yesButtonText = qsTr("Continue")
var noButtonText = qsTr("Cancel")
var yesButtonFunction = function() {
AppSplitTunnelingController.removeApp(proxyAppSplitTunnelingModel.mapToSource(index))
}
var noButtonFunction = function() {
}
showQuestionDrawer(headerText, "", yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
}
}
DividerType {}
} }
showQuestionDrawer(headerText, "", yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
} }
} }
DividerType {}
} }
} }

View file

@ -23,20 +23,16 @@ PageType {
anchors.topMargin: 20 anchors.topMargin: 20
} }
FlickableType { ListViewType {
id: fl id: listView
anchors.top: backButton.bottom anchors.top: backButton.bottom
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
contentHeight: content.height anchors.left: parent.left
anchors.right: parent.right
ColumnLayout { header: ColumnLayout {
id: content width: listView.width
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
spacing: 0
BaseHeaderType { BaseHeaderType {
Layout.fillWidth: true Layout.fillWidth: true
@ -45,9 +41,17 @@ PageType {
headerText: qsTr("Application") headerText: qsTr("Application")
} }
}
model: 1 // fake model to force the ListView to be created without a model
delegate: ColumnLayout {
width: listView.width
SwitcherType { SwitcherType {
id: switcher id: switcherAllowScreenshots
visible: GC.isMobile() visible: GC.isMobile()
Layout.fillWidth: true Layout.fillWidth: true
@ -61,10 +65,6 @@ PageType {
SettingsController.toggleScreenshotsEnabled(checked) SettingsController.toggleScreenshotsEnabled(checked)
} }
} }
// KeyNavigation.tab: Qt.platform.os === "android" && !SettingsController.isNotificationPermissionGranted ?
// labelWithButtonNotification.rightButton : labelWithButtonLanguage.rightButton
parentFlickable: fl
} }
DividerType { DividerType {
@ -73,15 +73,15 @@ PageType {
LabelWithButtonType { LabelWithButtonType {
id: labelWithButtonNotification id: labelWithButtonNotification
visible: Qt.platform.os === "android" && !SettingsController.isNotificationPermissionGranted visible: Qt.platform.os === "android" && !SettingsController.isNotificationPermissionGranted
Layout.fillWidth: true Layout.fillWidth: true
text: qsTr("Enable notifications") text: qsTr("Enable notifications")
descriptionText: qsTr("Enable notifications to show the VPN state in the status bar") descriptionText: qsTr("Enable notifications to show the VPN state in the status bar")
rightImageSource: "qrc:/images/controls/chevron-right.svg" rightImageSource: "qrc:/images/controls/chevron-right.svg"
parentFlickable: fl
clickedFunction: function() { clickedFunction: function() {
SettingsController.requestNotificationPermission() SettingsController.requestNotificationPermission()
} }
@ -93,6 +93,7 @@ PageType {
SwitcherType { SwitcherType {
id: switcherAutoStart id: switcherAutoStart
visible: !GC.isMobile() visible: !GC.isMobile()
Layout.fillWidth: true Layout.fillWidth: true
@ -101,8 +102,6 @@ PageType {
text: qsTr("Auto start") text: qsTr("Auto start")
descriptionText: qsTr("Launch the application every time the device is starts") descriptionText: qsTr("Launch the application every time the device is starts")
parentFlickable: fl
checked: SettingsController.isAutoStartEnabled() checked: SettingsController.isAutoStartEnabled()
onCheckedChanged: { onCheckedChanged: {
if (checked !== SettingsController.isAutoStartEnabled()) { if (checked !== SettingsController.isAutoStartEnabled()) {
@ -117,6 +116,7 @@ PageType {
SwitcherType { SwitcherType {
id: switcherAutoConnect id: switcherAutoConnect
visible: !GC.isMobile() visible: !GC.isMobile()
Layout.fillWidth: true Layout.fillWidth: true
@ -125,8 +125,6 @@ PageType {
text: qsTr("Auto connect") text: qsTr("Auto connect")
descriptionText: qsTr("Connect to VPN on app start") descriptionText: qsTr("Connect to VPN on app start")
parentFlickable: fl
checked: SettingsController.isAutoConnectEnabled() checked: SettingsController.isAutoConnectEnabled()
onCheckedChanged: { onCheckedChanged: {
if (checked !== SettingsController.isAutoConnectEnabled()) { if (checked !== SettingsController.isAutoConnectEnabled()) {
@ -141,6 +139,7 @@ PageType {
SwitcherType { SwitcherType {
id: switcherStartMinimized id: switcherStartMinimized
visible: !GC.isMobile() visible: !GC.isMobile()
Layout.fillWidth: true Layout.fillWidth: true
@ -149,8 +148,6 @@ PageType {
text: qsTr("Start minimized") text: qsTr("Start minimized")
descriptionText: qsTr("Launch application minimized") descriptionText: qsTr("Launch application minimized")
parentFlickable: fl
checked: SettingsController.isStartMinimizedEnabled() checked: SettingsController.isStartMinimizedEnabled()
onCheckedChanged: { onCheckedChanged: {
if (checked !== SettingsController.isStartMinimizedEnabled()) { if (checked !== SettingsController.isStartMinimizedEnabled()) {
@ -162,17 +159,21 @@ PageType {
DividerType { DividerType {
visible: !GC.isMobile() visible: !GC.isMobile()
} }
}
footer: ColumnLayout {
width: listView.width
LabelWithButtonType { LabelWithButtonType {
id: labelWithButtonLanguage id: labelWithButtonLanguage
Layout.fillWidth: true Layout.fillWidth: true
text: qsTr("Language") text: qsTr("Language")
descriptionText: LanguageModel.currentLanguageName descriptionText: LanguageModel.currentLanguageName
rightImageSource: "qrc:/images/controls/chevron-right.svg" rightImageSource: "qrc:/images/controls/chevron-right.svg"
parentFlickable: fl
clickedFunction: function() { clickedFunction: function() {
selectLanguageDrawer.openTriggered() selectLanguageDrawer.openTriggered()
} }
@ -182,14 +183,13 @@ PageType {
LabelWithButtonType { LabelWithButtonType {
id: labelWithButtonLogging id: labelWithButtonLogging
Layout.fillWidth: true Layout.fillWidth: true
text: qsTr("Logging") text: qsTr("Logging")
descriptionText: SettingsController.isLoggingEnabled ? qsTr("Enabled") : qsTr("Disabled") descriptionText: SettingsController.isLoggingEnabled ? qsTr("Enabled") : qsTr("Disabled")
rightImageSource: "qrc:/images/controls/chevron-right.svg" rightImageSource: "qrc:/images/controls/chevron-right.svg"
parentFlickable: fl
clickedFunction: function() { clickedFunction: function() {
PageController.goToPage(PageEnum.PageSettingsLogging) PageController.goToPage(PageEnum.PageSettingsLogging)
} }
@ -199,14 +199,13 @@ PageType {
LabelWithButtonType { LabelWithButtonType {
id: labelWithButtonReset id: labelWithButtonReset
Layout.fillWidth: true Layout.fillWidth: true
text: qsTr("Reset settings and remove all data from the application") text: qsTr("Reset settings and remove all data from the application")
rightImageSource: "qrc:/images/controls/chevron-right.svg" rightImageSource: "qrc:/images/controls/chevron-right.svg"
textColor: AmneziaStyle.color.vibrantRed textColor: AmneziaStyle.color.vibrantRed
parentFlickable: fl
clickedFunction: function() { clickedFunction: function() {
var headerText = qsTr("Reset settings and remove all data from the application?") var headerText = qsTr("Reset settings and remove all data from the application?")
var descriptionText = qsTr("All settings will be reset to default. All installed AmneziaVPN services will still remain on the server.") var descriptionText = qsTr("All settings will be reset to default. All installed AmneziaVPN services will still remain on the server.")

View file

@ -43,49 +43,60 @@ PageType {
anchors.topMargin: 20 anchors.topMargin: 20
} }
FlickableType { ListViewType {
id: fl id: listView
anchors.top: backButton.bottom anchors.top: backButton.bottom
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
contentHeight: content.height anchors.left: parent.left
anchors.right: parent.right
ColumnLayout { header: ColumnLayout {
id: content
anchors.top: parent.top width: listView.width
anchors.left: parent.left
anchors.right: parent.right
anchors.leftMargin: 16
anchors.rightMargin: 16
spacing: 16 spacing: 16
BaseHeaderType { BaseHeaderType {
Layout.fillWidth: true Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 16
headerText: qsTr("Back up your configuration") headerText: qsTr("Back up your configuration")
descriptionText: qsTr("You can save your settings to a backup file to restore them the next time you install the application.") descriptionText: qsTr("You can save your settings to a backup file to restore them the next time you install the application.")
} }
}
model: 1 // fake model to force the ListView to be created without a model
delegate: ColumnLayout {
width: listView.width
spacing: 16
WarningType { WarningType {
Layout.topMargin: 16
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 16
Layout.leftMargin: 16
Layout.rightMargin: 16
textString: qsTr("The backup will contain your passwords and private keys for all servers added " + textString: qsTr("The backup will contain your passwords and private keys for all servers added " +
"to AmneziaVPN. Keep this information in a secure place.") "to AmneziaVPN. Keep this information in a secure place.")
iconPath: "qrc:/images/controls/alert-circle.svg" iconPath: "qrc:/images/controls/alert-circle.svg"
} }
BasicButtonType { BasicButtonType {
id: makeBackupButton id: makeBackupButton
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 14 Layout.topMargin: 14
Layout.leftMargin: 16
Layout.rightMargin: 16
text: qsTr("Make a backup") text: qsTr("Make a backup")
parentFlickable: fl
clickedFunc: function() { clickedFunc: function() {
var fileName = "" var fileName = ""
if (GC.isMobile()) { if (GC.isMobile()) {
@ -108,8 +119,11 @@ PageType {
BasicButtonType { BasicButtonType {
id: restoreBackupButton id: restoreBackupButton
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: -8 Layout.topMargin: -8
Layout.leftMargin: 16
Layout.rightMargin: 16
defaultColor: AmneziaStyle.color.transparent defaultColor: AmneziaStyle.color.transparent
hoveredColor: AmneziaStyle.color.translucentWhite hoveredColor: AmneziaStyle.color.translucentWhite
@ -120,8 +134,6 @@ PageType {
text: qsTr("Restore from backup") text: qsTr("Restore from backup")
parentFlickable: fl
clickedFunc: function() { clickedFunc: function() {
var filePath = SystemController.getFileName(qsTr("Open backup file"), var filePath = SystemController.getFileName(qsTr("Open backup file"),
qsTr("Backup files (*.backup)")) qsTr("Backup files (*.backup)"))

View file

@ -23,18 +23,17 @@ PageType {
anchors.topMargin: 20 anchors.topMargin: 20
} }
FlickableType { ListViewType {
id: fl id: listView
anchors.top: backButton.bottom anchors.top: backButton.bottom
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
contentHeight: content.height anchors.left: parent.left
anchors.right: parent.right
ColumnLayout { header: ColumnLayout {
id: content
anchors.top: parent.top width: listView.width
anchors.left: parent.left
anchors.right: parent.right
BaseHeaderType { BaseHeaderType {
Layout.fillWidth: true Layout.fillWidth: true
@ -43,9 +42,17 @@ PageType {
headerText: qsTr("Connection") headerText: qsTr("Connection")
} }
}
model: 1 // fake model to force the ListView to be created without a model
delegate: ColumnLayout {
width: listView.width
SwitcherType { SwitcherType {
id: amneziaDnsSwitch id: amneziaDnsSwitch
Layout.fillWidth: true Layout.fillWidth: true
Layout.margins: 16 Layout.margins: 16
@ -64,14 +71,13 @@ PageType {
LabelWithButtonType { LabelWithButtonType {
id: dnsServersButton id: dnsServersButton
Layout.fillWidth: true Layout.fillWidth: true
text: qsTr("DNS servers") text: qsTr("DNS servers")
descriptionText: qsTr("When AmneziaDNS is not used or installed") descriptionText: qsTr("When AmneziaDNS is not used or installed")
rightImageSource: "qrc:/images/controls/chevron-right.svg" rightImageSource: "qrc:/images/controls/chevron-right.svg"
parentFlickable: fl
clickedFunction: function() { clickedFunction: function() {
PageController.goToPage(PageEnum.PageSettingsDns) PageController.goToPage(PageEnum.PageSettingsDns)
} }
@ -81,14 +87,13 @@ PageType {
LabelWithButtonType { LabelWithButtonType {
id: splitTunnelingButton id: splitTunnelingButton
Layout.fillWidth: true Layout.fillWidth: true
text: qsTr("Site-based split tunneling") text: qsTr("Site-based split tunneling")
descriptionText: qsTr("Allows you to select which sites you want to access through the VPN") descriptionText: qsTr("Allows you to select which sites you want to access through the VPN")
rightImageSource: "qrc:/images/controls/chevron-right.svg" rightImageSource: "qrc:/images/controls/chevron-right.svg"
parentFlickable: fl
clickedFunction: function() { clickedFunction: function() {
PageController.goToPage(PageEnum.PageSettingsSplitTunneling) PageController.goToPage(PageEnum.PageSettingsSplitTunneling)
} }
@ -96,8 +101,15 @@ PageType {
DividerType {} DividerType {}
}
footer: ColumnLayout {
width: listView.width
LabelWithButtonType { LabelWithButtonType {
id: splitTunnelingButton2 id: splitTunnelingButton2
visible: root.isAppSplitTinnelingEnabled visible: root.isAppSplitTinnelingEnabled
Layout.fillWidth: true Layout.fillWidth: true
@ -106,8 +118,6 @@ PageType {
descriptionText: qsTr("Allows you to use the VPN only for certain Apps") descriptionText: qsTr("Allows you to use the VPN only for certain Apps")
rightImageSource: "qrc:/images/controls/chevron-right.svg" rightImageSource: "qrc:/images/controls/chevron-right.svg"
parentFlickable: fl
clickedFunction: function() { clickedFunction: function() {
PageController.goToPage(PageEnum.PageSettingsAppSplitTunneling) PageController.goToPage(PageEnum.PageSettingsAppSplitTunneling)
} }
@ -127,8 +137,6 @@ PageType {
descriptionText: qsTr("Blocks network connections without VPN") descriptionText: qsTr("Blocks network connections without VPN")
rightImageSource: "qrc:/images/controls/chevron-right.svg" rightImageSource: "qrc:/images/controls/chevron-right.svg"
parentFlickable: fl
clickedFunction: function() { clickedFunction: function() {
PageController.goToPage(PageEnum.PageSettingsKillSwitch) PageController.goToPage(PageEnum.PageSettingsKillSwitch)
} }

View file

@ -1,140 +1,167 @@
import QtQuick import QtQuick
import QtQuick.Controls import QtQuick.Controls
import QtQuick.Layouts import QtQuick.Layouts
import PageEnum 1.0 import PageEnum 1.0
import Style 1.0 import Style 1.0
import "./" import "./"
import "../Controls2" import "../Controls2"
import "../Config" import "../Config"
import "../Controls2/TextTypes" import "../Controls2/TextTypes"
import "../Components" import "../Components"
PageType { PageType {
id: root id: root
BackButtonType { BackButtonType {
id: backButton id: backButton
anchors.top: parent.top anchors.top: parent.top
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
anchors.topMargin: 20 anchors.topMargin: 20
}
onFocusChanged: {
FlickableType { if (this.activeFocus) {
id: fl listView.positionViewAtBeginning()
anchors.top: backButton.bottom }
anchors.bottom: parent.bottom }
contentHeight: content.height }
property var isServerFromApi: ServersModel.isServerFromApi(ServersModel.defaultIndex) ListViewType {
id: listView
enabled: !isServerFromApi
anchors.top: backButton.bottom
Component.onCompleted: { anchors.bottom: parent.bottom
if (isServerFromApi) { anchors.right: parent.right
PageController.showNotificationMessage(qsTr("Default server does not support custom DNS")) anchors.left: parent.left
}
} property var isServerFromApi: ServersModel.isServerFromApi(ServersModel.defaultIndex)
ColumnLayout { enabled: !isServerFromApi
id: content
Component.onCompleted: {
anchors.top: parent.top if (isServerFromApi) {
anchors.left: parent.left PageController.showNotificationMessage(qsTr("Default server does not support custom DNS"))
anchors.right: parent.right }
anchors.leftMargin: 16 }
anchors.rightMargin: 16
header: ColumnLayout {
spacing: 16 width: listView.width
spacing: 16
BaseHeaderType {
Layout.fillWidth: true BaseHeaderType {
Layout.fillWidth: true
headerText: qsTr("DNS servers") Layout.leftMargin: 16
} Layout.rightMargin: 16
ParagraphTextType { headerText: qsTr("DNS servers")
Layout.fillWidth: true }
text: qsTr("If AmneziaDNS is not used or installed")
} ParagraphTextType {
Layout.fillWidth: true
TextFieldWithHeaderType { Layout.leftMargin: 16
id: primaryDns Layout.rightMargin: 16
Layout.fillWidth: true text: qsTr("If AmneziaDNS is not used or installed")
headerText: qsTr("Primary DNS") }
textField.text: SettingsController.primaryDns TextFieldWithHeaderType {
textField.validator: RegularExpressionValidator { id: primaryDns
regularExpression: InstallController.ipAddressRegExp()
} Layout.fillWidth: true
} Layout.leftMargin: 16
Layout.rightMargin: 16
TextFieldWithHeaderType {
id: secondaryDns headerText: qsTr("Primary DNS")
Layout.fillWidth: true textField.text: SettingsController.primaryDns
headerText: qsTr("Secondary DNS") textField.validator: RegularExpressionValidator {
regularExpression: InstallController.ipAddressRegExp()
textField.text: SettingsController.secondaryDns }
textField.validator: RegularExpressionValidator { }
regularExpression: InstallController.ipAddressRegExp()
} TextFieldWithHeaderType {
} id: secondaryDns
BasicButtonType { Layout.fillWidth: true
id: restoreDefaultButton Layout.leftMargin: 16
Layout.fillWidth: true Layout.rightMargin: 16
defaultColor: AmneziaStyle.color.transparent headerText: qsTr("Secondary DNS")
hoveredColor: AmneziaStyle.color.translucentWhite
pressedColor: AmneziaStyle.color.sheerWhite textField.text: SettingsController.secondaryDns
disabledColor: AmneziaStyle.color.mutedGray textField.validator: RegularExpressionValidator {
textColor: AmneziaStyle.color.paleGray regularExpression: InstallController.ipAddressRegExp()
borderWidth: 1 }
}
text: qsTr("Restore default") }
clickedFunc: function() { model: 1 // fake model to force the ListView to be created without a model
var headerText = qsTr("Restore default DNS settings?") spacing: 16
var yesButtonText = qsTr("Continue")
var noButtonText = qsTr("Cancel") delegate: ColumnLayout {
width: listView.width
var yesButtonFunction = function() {
SettingsController.primaryDns = "1.1.1.1" BasicButtonType {
primaryDns.textField.text = SettingsController.primaryDns id: restoreDefaultButton
SettingsController.secondaryDns = "1.0.0.1"
secondaryDns.textField.text = SettingsController.secondaryDns Layout.fillWidth: true
PageController.showNotificationMessage(qsTr("Settings have been reset")) Layout.topMargin: 16
} Layout.leftMargin: 16
var noButtonFunction = function() { Layout.rightMargin: 16
}
defaultColor: AmneziaStyle.color.transparent
showQuestionDrawer(headerText, "", yesButtonText, noButtonText, yesButtonFunction, noButtonFunction) hoveredColor: AmneziaStyle.color.translucentWhite
} pressedColor: AmneziaStyle.color.sheerWhite
} disabledColor: AmneziaStyle.color.mutedGray
textColor: AmneziaStyle.color.paleGray
BasicButtonType { borderWidth: 1
id: saveButton
text: qsTr("Restore default")
Layout.fillWidth: true
clickedFunc: function() {
text: qsTr("Save") var headerText = qsTr("Restore default DNS settings?")
var yesButtonText = qsTr("Continue")
clickedFunc: function() { var noButtonText = qsTr("Cancel")
if (primaryDns.textField.text !== SettingsController.primaryDns) {
SettingsController.primaryDns = primaryDns.textField.text var yesButtonFunction = function() {
} SettingsController.primaryDns = "1.1.1.1"
if (secondaryDns.textField.text !== SettingsController.secondaryDns) { primaryDns.textField.text = SettingsController.primaryDns
SettingsController.secondaryDns = secondaryDns.textField.text SettingsController.secondaryDns = "1.0.0.1"
} secondaryDns.textField.text = SettingsController.secondaryDns
PageController.showNotificationMessage(qsTr("Settings saved")) PageController.showNotificationMessage(qsTr("Settings have been reset"))
} }
} var noButtonFunction = function() {
} }
}
showQuestionDrawer(headerText, "", yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
} }
}
}
footer: ColumnLayout {
width: listView.width
BasicButtonType {
id: saveButton
Layout.fillWidth: true
Layout.margins: 16
text: qsTr("Save")
clickedFunc: function() {
if (primaryDns.textField.text !== SettingsController.primaryDns) {
SettingsController.primaryDns = primaryDns.textField.text
}
if (secondaryDns.textField.text !== SettingsController.secondaryDns) {
SettingsController.secondaryDns = secondaryDns.textField.text
}
PageController.showNotificationMessage(qsTr("Settings saved"))
}
}
}
}
}

View file

@ -82,8 +82,7 @@ PageType {
Layout.rightMargin: 16 Layout.rightMargin: 16
visible: false visible: false
enabled: false enabled: false //SettingsController.isKillSwitchEnabled && !ConnectionController.isConnected
// enabled: SettingsController.isKillSwitchEnabled && !ConnectionController.isConnected
checked: SettingsController.strictKillSwitchEnabled checked: SettingsController.strictKillSwitchEnabled
text: qsTr("Strict KillSwitch") text: qsTr("Strict KillSwitch")

View file

@ -25,7 +25,7 @@ PageType {
anchors.topMargin: 20 anchors.topMargin: 20
} }
ListView { ListViewType {
id: listView id: listView
anchors.top: backButton.bottom anchors.top: backButton.bottom
@ -33,10 +33,6 @@ PageType {
anchors.right: parent.right anchors.right: parent.right
anchors.left: parent.left anchors.left: parent.left
property bool isFocusable: true
ScrollBar.vertical: ScrollBarType {}
header: ColumnLayout { header: ColumnLayout {
width: listView.width width: listView.width
@ -101,8 +97,7 @@ PageType {
} }
model: logTypes model: logTypes
clip: true
reuseItems: true
snapMode: ListView.SnapOneItem snapMode: ListView.SnapOneItem
delegate: ColumnLayout { delegate: ColumnLayout {

View file

@ -18,10 +18,6 @@ PageType {
signal lastItemTabClickedSignal() signal lastItemTabClickedSignal()
onFocusChanged: content.isServerWithWriteAccess ?
labelWithButton.forceActiveFocus() :
labelWithButton3.forceActiveFocus()
Connections { Connections {
target: InstallController target: InstallController
@ -63,218 +59,194 @@ PageType {
target: ServersModel target: ServersModel
function onProcessedServerIndexChanged() { function onProcessedServerIndexChanged() {
content.isServerWithWriteAccess = ServersModel.isProcessedServerHasWriteAccess() listView.isServerWithWriteAccess = ServersModel.isProcessedServerHasWriteAccess()
} }
} }
FlickableType { ListViewType {
id: fl id: listView
anchors.top: parent.top
anchors.bottom: parent.bottom
contentHeight: content.height
ColumnLayout { property bool isServerWithWriteAccess: ServersModel.isProcessedServerHasWriteAccess()
id: content
anchors.top: parent.top anchors.fill: parent
anchors.left: parent.left
anchors.right: parent.right
property bool isServerWithWriteAccess: ServersModel.isProcessedServerHasWriteAccess() model: serverActions
delegate: ColumnLayout {
width: listView.width
LabelWithButtonType { LabelWithButtonType {
id: labelWithButton
visible: content.isServerWithWriteAccess
Layout.fillWidth: true Layout.fillWidth: true
text: qsTr("Check the server for previously installed Amnezia services") visible: isVisible
descriptionText: qsTr("Add them to the application if they were not displayed")
text: title
descriptionText: description
textColor: tColor
clickedFunction: function() { clickedFunction: function() {
clickedHandler()
}
}
DividerType {
visible: isVisible
}
}
}
property list<QtObject> serverActions: [
check,
reboot,
remove,
clear,
reset,
switch_to_premium,
]
QtObject {
id: check
property bool isVisible: true
readonly property string title: qsTr("Check the server for previously installed Amnezia services")
readonly property string description: qsTr("Add them to the application if they were not displayed")
readonly property var tColor: AmneziaStyle.color.paleGray
readonly property var clickedHandler: function() {
PageController.showBusyIndicator(true)
InstallController.scanServerForInstalledContainers()
PageController.showBusyIndicator(false)
}
}
QtObject {
id: reboot
property bool isVisible: true
readonly property string title: qsTr("Reboot server")
readonly property string description: ""
readonly property var tColor: AmneziaStyle.color.vibrantRed
readonly property var clickedHandler: function() {
var headerText = qsTr("Do you want to reboot the server?")
var descriptionText = qsTr("The reboot process may take approximately 30 seconds. Are you sure you wish to proceed?")
var yesButtonText = qsTr("Continue")
var noButtonText = qsTr("Cancel")
var yesButtonFunction = function() {
if (ServersModel.isDefaultServerCurrentlyProcessed() && ConnectionController.isConnected) {
PageController.showNotificationMessage(qsTr("Cannot reboot server during active connection"))
} else {
PageController.showBusyIndicator(true) PageController.showBusyIndicator(true)
InstallController.scanServerForInstalledContainers() InstallController.rebootProcessedServer()
PageController.showBusyIndicator(false) PageController.showBusyIndicator(false)
} }
} }
var noButtonFunction = function() {
DividerType {
visible: content.isServerWithWriteAccess
} }
LabelWithButtonType { showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
id: labelWithButton2 }
visible: content.isServerWithWriteAccess }
Layout.fillWidth: true
text: qsTr("Reboot server") QtObject {
textColor: AmneziaStyle.color.vibrantRed id: remove
clickedFunction: function() { property bool isVisible: true
var headerText = qsTr("Do you want to reboot the server?") readonly property string title: qsTr("Remove server from application")
var descriptionText = qsTr("The reboot process may take approximately 30 seconds. Are you sure you wish to proceed?") readonly property string description: ""
var yesButtonText = qsTr("Continue") readonly property var tColor: AmneziaStyle.color.vibrantRed
var noButtonText = qsTr("Cancel") readonly property var clickedHandler: function() {
var headerText = qsTr("Do you want to remove the server from application?")
var descriptionText = qsTr("All installed AmneziaVPN services will still remain on the server.")
var yesButtonText = qsTr("Continue")
var noButtonText = qsTr("Cancel")
var yesButtonFunction = function() { var yesButtonFunction = function() {
if (ServersModel.isDefaultServerCurrentlyProcessed() && ConnectionController.isConnected) { if (ServersModel.isDefaultServerCurrentlyProcessed() && ConnectionController.isConnected) {
PageController.showNotificationMessage(qsTr("Cannot reboot server during active connection")) PageController.showNotificationMessage(qsTr("Cannot remove server during active connection"))
} else { } else {
PageController.showBusyIndicator(true) PageController.showBusyIndicator(true)
InstallController.rebootProcessedServer() InstallController.removeProcessedServer()
PageController.showBusyIndicator(false) PageController.showBusyIndicator(false)
}
if (!GC.isMobile()) {
labelWithButton5.forceActiveFocus()
}
}
var noButtonFunction = function() {
if (!GC.isMobile()) {
labelWithButton2.forceActiveFocus()
}
}
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
} }
} }
var noButtonFunction = function() {
DividerType {
visible: content.isServerWithWriteAccess
} }
LabelWithButtonType { showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
id: labelWithButton3 }
Layout.fillWidth: true }
text: qsTr("Remove server from application") QtObject {
textColor: AmneziaStyle.color.vibrantRed id: clear
clickedFunction: function() { property bool isVisible: true
var headerText = qsTr("Do you want to remove the server from application?") readonly property string title: qsTr("Clear server from Amnezia software")
var descriptionText = qsTr("All installed AmneziaVPN services will still remain on the server.") readonly property string description: ""
var yesButtonText = qsTr("Continue") readonly property var tColor: AmneziaStyle.color.vibrantRed
var noButtonText = qsTr("Cancel") readonly property var clickedHandler: function() {
var headerText = qsTr("Do you want to clear server from Amnezia software?")
var descriptionText = qsTr("All users 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() { var yesButtonFunction = function() {
if (ServersModel.isDefaultServerCurrentlyProcessed() && ConnectionController.isConnected) { if (ServersModel.isDefaultServerCurrentlyProcessed() && ConnectionController.isConnected) {
PageController.showNotificationMessage(qsTr("Cannot remove server during active connection")) PageController.showNotificationMessage(qsTr("Cannot clear server from Amnezia software during active connection"))
} else { } else {
PageController.showBusyIndicator(true) PageController.goToPage(PageEnum.PageDeinstalling)
InstallController.removeProcessedServer() InstallController.removeAllContainers()
PageController.showBusyIndicator(false)
}
if (!GC.isMobile()) {
labelWithButton5.forceActiveFocus()
}
}
var noButtonFunction = function() {
if (!GC.isMobile()) {
labelWithButton3.forceActiveFocus()
}
}
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
} }
} }
var noButtonFunction = function() {
DividerType {} }
LabelWithButtonType { showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
id: labelWithButton4 }
visible: content.isServerWithWriteAccess }
Layout.fillWidth: true
text: qsTr("Clear server from Amnezia software") QtObject {
textColor: AmneziaStyle.color.vibrantRed id: reset
clickedFunction: function() { property bool isVisible: ServersModel.getProcessedServerData("isServerFromTelegramApi")
var headerText = qsTr("Do you want to clear server from Amnezia software?") readonly property string title: qsTr("Reset API config")
var descriptionText = qsTr("All users whom you shared a connection with will no longer be able to connect to it.") readonly property string description: ""
var yesButtonText = qsTr("Continue") readonly property var tColor: AmneziaStyle.color.vibrantRed
var noButtonText = qsTr("Cancel") readonly property var clickedHandler: function() {
var headerText = qsTr("Do you want to reset API config?")
var descriptionText = ""
var yesButtonText = qsTr("Continue")
var noButtonText = qsTr("Cancel")
var yesButtonFunction = function() { var yesButtonFunction = function() {
if (ServersModel.isDefaultServerCurrentlyProcessed() && ConnectionController.isConnected) { if (ServersModel.isDefaultServerCurrentlyProcessed() && ConnectionController.isConnected) {
PageController.showNotificationMessage(qsTr("Cannot clear server from Amnezia software during active connection")) PageController.showNotificationMessage(qsTr("Cannot reset API config during active connection"))
} else { } else {
PageController.goToPage(PageEnum.PageDeinstalling) PageController.showBusyIndicator(true)
InstallController.removeAllContainers() InstallController.removeApiConfig(ServersModel.processedIndex)
} PageController.showBusyIndicator(false)
if (!GC.isMobile()) {
labelWithButton5.forceActiveFocus()
}
}
var noButtonFunction = function() {
if (!GC.isMobile()) {
labelWithButton4.forceActiveFocus()
}
}
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
} }
} }
var noButtonFunction = function() {
DividerType {
visible: content.isServerWithWriteAccess
} }
LabelWithButtonType { showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
id: labelWithButton5 }
visible: ServersModel.getProcessedServerData("isServerFromTelegramApi") }
Layout.fillWidth: true
text: qsTr("Reset API config") QtObject {
textColor: AmneziaStyle.color.vibrantRed id: switch_to_premium
clickedFunction: function() { property bool isVisible: ServersModel.getProcessedServerData("isServerFromTelegramApi")
var headerText = qsTr("Do you want to reset API config?") readonly property string title: qsTr("Switch to the new Amnezia Premium subscription")
var descriptionText = "" readonly property string description: ""
var yesButtonText = qsTr("Continue") readonly property var tColor: AmneziaStyle.color.vibrantRed
var noButtonText = qsTr("Cancel") readonly property var clickedHandler: function() {
PageController.goToPageHome()
var yesButtonFunction = function() { ApiPremV1MigrationController.showMigrationDrawer()
if (ServersModel.isDefaultServerCurrentlyProcessed() && ConnectionController.isConnected) {
PageController.showNotificationMessage(qsTr("Cannot reset API config during active connection"))
} else {
PageController.showBusyIndicator(true)
InstallController.removeApiConfig(ServersModel.processedIndex)
PageController.showBusyIndicator(false)
}
if (!GC.isMobile()) {
labelWithButton5.forceActiveFocus()
}
}
var noButtonFunction = function() {
if (!GC.isMobile()) {
labelWithButton5.forceActiveFocus()
}
}
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
}
}
DividerType {
visible: ServersModel.getProcessedServerData("isServerFromTelegramApi")
}
LabelWithButtonType {
id: labelWithButton6
visible: ServersModel.getProcessedServerData("isServerFromTelegramApi") && ServersModel.processedServerIsPremium
Layout.fillWidth: true
text: qsTr("Switch to the new Amnezia Premium subscription")
textColor: AmneziaStyle.color.vibrantRed
clickedFunction: function() {
PageController.goToPageHome()
ApiPremV1MigrationController.showMigrationDrawer()
}
}
DividerType {
visible: ServersModel.getProcessedServerData("isServerFromTelegramApi") && ServersModel.processedServerIsPremium
}
} }
} }
} }

View file

@ -21,249 +21,205 @@ PageType {
property bool isClearCacheVisible: ServersModel.isProcessedServerHasWriteAccess() && !ContainersModel.isServiceContainer(ContainersModel.getProcessedContainerIndex()) property bool isClearCacheVisible: ServersModel.isProcessedServerHasWriteAccess() && !ContainersModel.isServiceContainer(ContainersModel.getProcessedContainerIndex())
ColumnLayout { BackButtonType {
id: header id: backButton
anchors.top: parent.top anchors.top: parent.top
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
anchors.topMargin: 20 anchors.topMargin: 20
}
BackButtonType { ListViewType {
id: backButton id: listView
}
BaseHeaderType { anchors.top: backButton.bottom
Layout.fillWidth: true anchors.bottom: parent.bottom
Layout.leftMargin: 16 anchors.right: parent.right
Layout.rightMargin: 16 anchors.left: parent.left
Layout.bottomMargin: 32
headerText: ContainersModel.getProcessedContainerName() + qsTr(" settings") header: ColumnLayout {
} width: listView.width
ListView { BaseHeaderType {
id: protocols Layout.fillWidth: true
Layout.fillWidth: true Layout.leftMargin: 16
height: protocols.contentItem.height Layout.rightMargin: 16
clip: true Layout.bottomMargin: 32
interactive: true
property bool isFocusable: true headerText: ContainersModel.getProcessedContainerName() + qsTr(" settings")
Keys.onTabPressed: {
FocusController.nextKeyTabItem()
}
Keys.onBacktabPressed: {
FocusController.previousKeyTabItem()
}
Keys.onUpPressed: {
FocusController.nextKeyUpItem()
}
Keys.onDownPressed: {
FocusController.nextKeyDownItem()
}
Keys.onLeftPressed: {
FocusController.nextKeyLeftItem()
}
Keys.onRightPressed: {
FocusController.nextKeyRightItem()
}
model: ProtocolsModel
delegate: Item {
implicitWidth: protocols.width
implicitHeight: delegateContent.implicitHeight
ColumnLayout {
id: delegateContent
anchors.fill: parent
property bool isClientSettingsVisible: protocolIndex === ProtocolEnum.WireGuard || protocolIndex === ProtocolEnum.Awg
property bool isServerSettingsVisible: ServersModel.isProcessedServerHasWriteAccess()
LabelWithButtonType {
id: clientSettings
Layout.fillWidth: true
text: protocolName + qsTr(" connection settings")
rightImageSource: "qrc:/images/controls/chevron-right.svg"
visible: delegateContent.isClientSettingsVisible
clickedFunction: function() {
if (isClientProtocolExists) {
switch (protocolIndex) {
case ProtocolEnum.WireGuard: WireGuardConfigModel.updateModel(ProtocolsModel.getConfig()); break;
case ProtocolEnum.Awg: AwgConfigModel.updateModel(ProtocolsModel.getConfig()); break;
}
PageController.goToPage(clientProtocolPage);
} else {
PageController.showNotificationMessage(qsTr("Click the \"connect\" button to create a connection configuration"))
}
}
MouseArea {
anchors.fill: clientSettings
cursorShape: Qt.PointingHandCursor
enabled: false
}
}
DividerType {
visible: delegateContent.isClientSettingsVisible
}
LabelWithButtonType {
id: serverSettings
Layout.fillWidth: true
text: protocolName + qsTr(" server settings")
rightImageSource: "qrc:/images/controls/chevron-right.svg"
visible: delegateContent.isServerSettingsVisible
clickedFunction: function() {
switch (protocolIndex) {
case ProtocolEnum.OpenVpn: OpenVpnConfigModel.updateModel(ProtocolsModel.getConfig()); break;
case ProtocolEnum.ShadowSocks: ShadowSocksConfigModel.updateModel(ProtocolsModel.getConfig()); break;
case ProtocolEnum.Cloak: CloakConfigModel.updateModel(ProtocolsModel.getConfig()); break;
case ProtocolEnum.WireGuard: WireGuardConfigModel.updateModel(ProtocolsModel.getConfig()); break;
case ProtocolEnum.Awg: AwgConfigModel.updateModel(ProtocolsModel.getConfig()); break;
case ProtocolEnum.Xray: XrayConfigModel.updateModel(ProtocolsModel.getConfig()); break;
case ProtocolEnum.Sftp: SftpConfigModel.updateModel(ProtocolsModel.getConfig()); break;
case ProtocolEnum.Ipsec: Ikev2ConfigModel.updateModel(ProtocolsModel.getConfig()); break;
case ProtocolEnum.Socks5Proxy: Socks5ProxyConfigModel.updateModel(ProtocolsModel.getConfig()); break;
}
PageController.goToPage(serverProtocolPage);
}
MouseArea {
anchors.fill: serverSettings
cursorShape: Qt.PointingHandCursor
enabled: false
}
}
DividerType {
visible: delegateContent.isServerSettingsVisible
}
}
}
footer: ColumnLayout {
width: header.width
LabelWithButtonType {
id: clearCacheButton
Layout.fillWidth: true
visible: root.isClearCacheVisible
text: qsTr("Clear profile")
clickedFunction: function() {
var headerText = qsTr("Clear %1 profile?").arg(ContainersModel.getProcessedContainerName())
var descriptionText = qsTr("The connection configuration will be deleted for this device only")
var yesButtonText = qsTr("Continue")
var noButtonText = qsTr("Cancel")
var yesButtonFunction = function() {
if (ConnectionController.isConnected && ServersModel.getDefaultServerData("defaultContainer") === ContainersModel.getProcessedContainerIndex()) {
var message = qsTr("Unable to clear %1 profile while there is an active connection").arg(ContainersModel.getProcessedContainerName())
PageController.showNotificationMessage(message)
return
}
PageController.showBusyIndicator(true)
InstallController.clearCachedProfile()
PageController.showBusyIndicator(false)
}
var noButtonFunction = function() {
// if (!GC.isMobile()) {
// focusItem.forceActiveFocus()
// }
}
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
}
MouseArea {
anchors.fill: clearCacheButton
cursorShape: Qt.PointingHandCursor
enabled: false
}
}
DividerType {
Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 16
visible: root.isClearCacheVisible
}
LabelWithButtonType {
id: removeButton
Layout.fillWidth: true
visible: ServersModel.isProcessedServerHasWriteAccess()
text: qsTr("Remove ")
textColor: AmneziaStyle.color.vibrantRed
clickedFunction: function() {
var headerText = qsTr("Remove %1 from server?").arg(ContainersModel.getProcessedContainerName())
var descriptionText = qsTr("All users with whom you shared a connection will no longer be able to connect to it.")
var yesButtonText = qsTr("Continue")
var noButtonText = qsTr("Cancel")
var yesButtonFunction = function() {
if (ServersModel.isDefaultServerCurrentlyProcessed() && ConnectionController.isConnected
&& ServersModel.getDefaultServerData("defaultContainer") === ContainersModel.getProcessedContainerIndex()) {
PageController.showNotificationMessage(qsTr("Cannot remove active container"))
} else
{
PageController.goToPage(PageEnum.PageDeinstalling)
InstallController.removeProcessedContainer()
}
}
var noButtonFunction = function() {
if (!GC.isMobile()) {
focusItem.forceActiveFocus()
}
}
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
}
MouseArea {
anchors.fill: removeButton
cursorShape: Qt.PointingHandCursor
enabled: false
}
}
DividerType {
Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 16
visible: ServersModel.isProcessedServerHasWriteAccess()
}
} }
} }
model: ProtocolsModel
delegate: ColumnLayout {
id: delegateContent
width: listView.width
property bool isClientSettingsVisible: protocolIndex === ProtocolEnum.WireGuard || protocolIndex === ProtocolEnum.Awg
property bool isServerSettingsVisible: ServersModel.isProcessedServerHasWriteAccess()
LabelWithButtonType {
id: clientSettings
Layout.fillWidth: true
text: protocolName + qsTr(" connection settings")
rightImageSource: "qrc:/images/controls/chevron-right.svg"
visible: delegateContent.isClientSettingsVisible
clickedFunction: function() {
if (isClientProtocolExists) {
switch (protocolIndex) {
case ProtocolEnum.WireGuard: WireGuardConfigModel.updateModel(ProtocolsModel.getConfig()); break;
case ProtocolEnum.Awg: AwgConfigModel.updateModel(ProtocolsModel.getConfig()); break;
}
PageController.goToPage(clientProtocolPage);
} else {
PageController.showNotificationMessage(qsTr("Click the \"connect\" button to create a connection configuration"))
}
}
MouseArea {
anchors.fill: clientSettings
cursorShape: Qt.PointingHandCursor
enabled: false
}
}
DividerType {
visible: delegateContent.isClientSettingsVisible
}
LabelWithButtonType {
id: serverSettings
Layout.fillWidth: true
text: protocolName + qsTr(" server settings")
rightImageSource: "qrc:/images/controls/chevron-right.svg"
visible: delegateContent.isServerSettingsVisible
clickedFunction: function() {
switch (protocolIndex) {
case ProtocolEnum.OpenVpn: OpenVpnConfigModel.updateModel(ProtocolsModel.getConfig()); break;
case ProtocolEnum.ShadowSocks: ShadowSocksConfigModel.updateModel(ProtocolsModel.getConfig()); break;
case ProtocolEnum.Cloak: CloakConfigModel.updateModel(ProtocolsModel.getConfig()); break;
case ProtocolEnum.WireGuard: WireGuardConfigModel.updateModel(ProtocolsModel.getConfig()); break;
case ProtocolEnum.Awg: AwgConfigModel.updateModel(ProtocolsModel.getConfig()); break;
case ProtocolEnum.Xray: XrayConfigModel.updateModel(ProtocolsModel.getConfig()); break;
case ProtocolEnum.Sftp: SftpConfigModel.updateModel(ProtocolsModel.getConfig()); break;
case ProtocolEnum.Ipsec: Ikev2ConfigModel.updateModel(ProtocolsModel.getConfig()); break;
case ProtocolEnum.Socks5Proxy: Socks5ProxyConfigModel.updateModel(ProtocolsModel.getConfig()); break;
}
PageController.goToPage(serverProtocolPage);
}
MouseArea {
anchors.fill: serverSettings
cursorShape: Qt.PointingHandCursor
enabled: false
}
}
DividerType {
visible: delegateContent.isServerSettingsVisible
}
}
footer: ColumnLayout {
width: listView.width
LabelWithButtonType {
id: clearCacheButton
Layout.fillWidth: true
visible: root.isClearCacheVisible
text: qsTr("Clear profile")
clickedFunction: function() {
var headerText = qsTr("Clear %1 profile?").arg(ContainersModel.getProcessedContainerName())
var descriptionText = qsTr("The connection configuration will be deleted for this device only")
var yesButtonText = qsTr("Continue")
var noButtonText = qsTr("Cancel")
var yesButtonFunction = function() {
if (ConnectionController.isConnected && ServersModel.getDefaultServerData("defaultContainer") === ContainersModel.getProcessedContainerIndex()) {
var message = qsTr("Unable to clear %1 profile while there is an active connection").arg(ContainersModel.getProcessedContainerName())
PageController.showNotificationMessage(message)
return
}
PageController.showBusyIndicator(true)
InstallController.clearCachedProfile()
PageController.showBusyIndicator(false)
}
var noButtonFunction = function() {
}
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
}
MouseArea {
anchors.fill: clearCacheButton
cursorShape: Qt.PointingHandCursor
enabled: false
}
}
DividerType {
visible: root.isClearCacheVisible
}
LabelWithButtonType {
id: removeButton
Layout.fillWidth: true
visible: ServersModel.isProcessedServerHasWriteAccess()
text: qsTr("Remove ")
textColor: AmneziaStyle.color.vibrantRed
clickedFunction: function() {
var headerText = qsTr("Remove %1 from server?").arg(ContainersModel.getProcessedContainerName())
var descriptionText = qsTr("All users with whom you shared a connection will no longer be able to connect to it.")
var yesButtonText = qsTr("Continue")
var noButtonText = qsTr("Cancel")
var yesButtonFunction = function() {
if (ServersModel.isDefaultServerCurrentlyProcessed() && ConnectionController.isConnected
&& ServersModel.getDefaultServerData("defaultContainer") === ContainersModel.getProcessedContainerIndex()) {
PageController.showNotificationMessage(qsTr("Cannot remove active container"))
} else
{
PageController.goToPage(PageEnum.PageDeinstalling)
InstallController.removeProcessedContainer()
}
}
var noButtonFunction = function() {
}
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
}
MouseArea {
anchors.fill: removeButton
cursorShape: Qt.PointingHandCursor
enabled: false
}
}
DividerType {
visible: ServersModel.isProcessedServerHasWriteAccess()
}
}
} }
} }

View file

@ -40,25 +40,20 @@ PageType {
} }
} }
ListView { ListViewType {
id: servers id: servers
objectName: "servers" objectName: "servers"
width: parent.width width: parent.width
anchors.top: header.bottom anchors.top: header.bottom
anchors.topMargin: 16 anchors.topMargin: 16
anchors.bottom: parent.bottom
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
height: 500
property bool isFocusable: true
model: ServersModel model: ServersModel
clip: true
reuseItems: true
delegate: Item { delegate: Item {
implicitWidth: servers.width implicitWidth: servers.width
implicitHeight: delegateContent.implicitHeight implicitHeight: delegateContent.implicitHeight

View file

@ -1,445 +1,450 @@
import QtQuick import QtQuick
import QtQuick.Controls import QtQuick.Controls
import QtQuick.Layouts import QtQuick.Layouts
import QtQuick.Dialogs import QtQuick.Dialogs
import QtCore import QtCore
import SortFilterProxyModel 0.2 import SortFilterProxyModel 0.2
import PageEnum 1.0 import PageEnum 1.0
import ProtocolEnum 1.0 import ProtocolEnum 1.0
import ContainerProps 1.0 import ContainerProps 1.0
import Style 1.0 import Style 1.0
import "./" import "./"
import "../Controls2" import "../Controls2"
import "../Controls2/TextTypes" import "../Controls2/TextTypes"
import "../Config" import "../Config"
import "../Components" import "../Components"
PageType { PageType {
id: root id: root
property var isServerFromTelegramApi: ServersModel.getDefaultServerData("isServerFromTelegramApi") property var isServerFromTelegramApi: ServersModel.getDefaultServerData("isServerFromTelegramApi")
property bool pageEnabled property bool pageEnabled
Component.onCompleted: { Component.onCompleted: {
if (ConnectionController.isConnected) { if (ConnectionController.isConnected) {
PageController.showNotificationMessage(qsTr("Cannot change split tunneling settings during active connection")) PageController.showNotificationMessage(qsTr("Cannot change split tunneling settings during active connection"))
root.pageEnabled = false root.pageEnabled = false
} else if (ServersModel.isDefaultServerDefaultContainerHasSplitTunneling) { } else if (ServersModel.isDefaultServerDefaultContainerHasSplitTunneling) {
PageController.showNotificationMessage(qsTr("Default server does not support split tunneling function")) PageController.showNotificationMessage(qsTr("Default server does not support split tunneling function"))
root.pageEnabled = false root.pageEnabled = false
} else { } else {
root.pageEnabled = true root.pageEnabled = true
} }
} }
Connections { Connections {
target: SitesController target: SitesController
function onFinished(message) { function onFinished(message) {
PageController.showNotificationMessage(message) PageController.showNotificationMessage(message)
} }
function onErrorOccurred(errorMessage) { function onErrorOccurred(errorMessage) {
PageController.showErrorMessage(errorMessage) PageController.showErrorMessage(errorMessage)
} }
} }
QtObject { QtObject {
id: routeMode id: routeMode
property int allSites: 0 property int allSites: 0
property int onlyForwardSites: 1 property int onlyForwardSites: 1
property int allExceptSites: 2 property int allExceptSites: 2
} }
property list<QtObject> routeModesModel: [ property list<QtObject> routeModesModel: [
onlyForwardSites, onlyForwardSites,
allExceptSites allExceptSites
] ]
QtObject { QtObject {
id: onlyForwardSites id: onlyForwardSites
property string name: qsTr("Only the sites listed here will be accessed through the VPN") property string name: qsTr("Only the sites listed here will be accessed through the VPN")
property int type: routeMode.onlyForwardSites property int type: routeMode.onlyForwardSites
} }
QtObject { QtObject {
id: allExceptSites id: allExceptSites
property string name: qsTr("Addresses from the list should not be accessed via VPN") property string name: qsTr("Addresses from the list should not be accessed via VPN")
property int type: routeMode.allExceptSites property int type: routeMode.allExceptSites
} }
function getRouteModesModelIndex() { function getRouteModesModelIndex() {
var currentRouteMode = SitesModel.routeMode var currentRouteMode = SitesModel.routeMode
if ((routeMode.onlyForwardSites === currentRouteMode) || (routeMode.allSites === currentRouteMode)) { if ((routeMode.onlyForwardSites === currentRouteMode) || (routeMode.allSites === currentRouteMode)) {
return 0 return 0
} else if (routeMode.allExceptSites === currentRouteMode) { } else if (routeMode.allExceptSites === currentRouteMode) {
return 1 return 1
} }
} }
ColumnLayout { ColumnLayout {
id: header id: header
anchors.top: parent.top anchors.top: parent.top
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
anchors.topMargin: 20 anchors.topMargin: 20
BackButtonType { BackButtonType {
id: backButton id: backButton
} }
HeaderTypeWithSwitcher { HeaderTypeWithSwitcher {
Layout.fillWidth: true Layout.fillWidth: true
Layout.leftMargin: 16 Layout.leftMargin: 16
Layout.rightMargin: 16 Layout.rightMargin: 16
headerText: qsTr("Split tunneling") headerText: qsTr("Split tunneling")
enabled: root.pageEnabled enabled: root.pageEnabled
showSwitcher: true showSwitcher: true
switcher { switcher {
checked: SitesModel.isTunnelingEnabled checked: SitesModel.isTunnelingEnabled
enabled: root.pageEnabled enabled: root.pageEnabled
} }
switcherFunction: function(checked) { switcherFunction: function(checked) {
SitesModel.toggleSplitTunneling(checked) SitesModel.toggleSplitTunneling(checked)
selector.text = root.routeModesModel[getRouteModesModelIndex()].name selector.text = root.routeModesModel[getRouteModesModelIndex()].name
} }
} }
DropDownType { DropDownType {
id: selector id: selector
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 32 Layout.topMargin: 32
Layout.leftMargin: 16 Layout.leftMargin: 16
Layout.rightMargin: 16 Layout.rightMargin: 16
drawerHeight: 0.4375 drawerHeight: 0.4375
drawerParent: root drawerParent: root
enabled: root.pageEnabled enabled: root.pageEnabled
headerText: qsTr("Mode") headerText: qsTr("Mode")
listView: ListViewWithRadioButtonType { listView: ListViewWithRadioButtonType {
rootWidth: root.width rootWidth: root.width
model: root.routeModesModel model: root.routeModesModel
selectedIndex: getRouteModesModelIndex() selectedIndex: getRouteModesModelIndex()
clickedFunction: function() { clickedFunction: function() {
selector.text = selectedText selector.text = selectedText
selector.closeTriggered() selector.closeTriggered()
if (SitesModel.routeMode !== root.routeModesModel[selectedIndex].type) { if (SitesModel.routeMode !== root.routeModesModel[selectedIndex].type) {
SitesModel.routeMode = root.routeModesModel[selectedIndex].type SitesModel.routeMode = root.routeModesModel[selectedIndex].type
} }
} }
Component.onCompleted: { Component.onCompleted: {
if (root.routeModesModel[selectedIndex].type === SitesModel.routeMode) { if (root.routeModesModel[selectedIndex].type === SitesModel.routeMode) {
selector.text = selectedText selector.text = selectedText
} else { } else {
selector.text = root.routeModesModel[0].name selector.text = root.routeModesModel[0].name
} }
} }
Connections { Connections {
target: SitesModel target: SitesModel
function onRouteModeChanged() { function onRouteModeChanged() {
selectedIndex = getRouteModesModelIndex() selectedIndex = getRouteModesModelIndex()
} }
} }
} }
} }
} }
ListView { ListViewType {
id: listView id: listView
anchors.top: header.bottom anchors.top: header.bottom
anchors.topMargin: 16 anchors.topMargin: 16
anchors.bottom: addSiteButton.top anchors.bottom: addSiteButton.top
width: parent.width width: parent.width
enabled: root.pageEnabled enabled: root.pageEnabled
property bool isFocusable: true model: SortFilterProxyModel {
id: proxySitesModel
model: SortFilterProxyModel { sourceModel: SitesModel
id: proxySitesModel filters: [
sourceModel: SitesModel AnyOf {
filters: [ RegExpFilter {
AnyOf { roleName: "url"
RegExpFilter { pattern: ".*" + searchField.textField.text + ".*"
roleName: "url" caseSensitivity: Qt.CaseInsensitive
pattern: ".*" + searchField.textField.text + ".*" }
caseSensitivity: Qt.CaseInsensitive RegExpFilter {
} roleName: "ip"
RegExpFilter { pattern: ".*" + searchField.textField.text + ".*"
roleName: "ip" caseSensitivity: Qt.CaseInsensitive
pattern: ".*" + searchField.textField.text + ".*" }
caseSensitivity: Qt.CaseInsensitive }
} ]
} }
]
} delegate: ColumnLayout {
width: listView.width
clip: true
LabelWithButtonType {
reuseItems: true id: site
Layout.fillWidth: true
delegate: ColumnLayout {
id: delegateContent text: url
descriptionText: ip
width: listView.width rightImageSource: "qrc:/images/controls/trash.svg"
rightImageColor: AmneziaStyle.color.paleGray
LabelWithButtonType {
id: site clickedFunction: function() {
Layout.fillWidth: true var headerText = qsTr("Remove ") + url + "?"
var yesButtonText = qsTr("Continue")
text: url var noButtonText = qsTr("Cancel")
descriptionText: ip
rightImageSource: "qrc:/images/controls/trash.svg" var yesButtonFunction = function() {
rightImageColor: AmneziaStyle.color.paleGray SitesController.removeSite(proxySitesModel.mapToSource(index))
if (!GC.isMobile()) {
clickedFunction: function() { site.rightButton.forceActiveFocus()
var headerText = qsTr("Remove ") + url + "?" }
var yesButtonText = qsTr("Continue") }
var noButtonText = qsTr("Cancel") var noButtonFunction = function() {
if (!GC.isMobile()) {
var yesButtonFunction = function() { site.rightButton.forceActiveFocus()
SitesController.removeSite(proxySitesModel.mapToSource(index)) }
if (!GC.isMobile()) { }
site.rightButton.forceActiveFocus()
} showQuestionDrawer(headerText, "", yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
} }
var noButtonFunction = function() { }
if (!GC.isMobile()) {
site.rightButton.forceActiveFocus() DividerType {}
} }
} }
showQuestionDrawer(headerText, "", yesButtonText, noButtonText, yesButtonFunction, noButtonFunction) Rectangle {
} anchors.fill: addSiteButton
} anchors.bottomMargin: -24
color: AmneziaStyle.color.midnightBlack
DividerType {} opacity: 0.8
} }
}
RowLayout {
id: addSiteButton
Rectangle {
anchors.fill: addSiteButton enabled: root.pageEnabled
anchors.bottomMargin: -24
color: AmneziaStyle.color.midnightBlack anchors.bottom: parent.bottom
opacity: 0.8 anchors.left: parent.left
} anchors.right: parent.right
anchors.topMargin: 24
RowLayout { anchors.rightMargin: 16
id: addSiteButton anchors.leftMargin: 16
anchors.bottomMargin: 24
enabled: root.pageEnabled
TextFieldWithHeaderType {
anchors.bottom: parent.bottom id: searchField
anchors.left: parent.left
anchors.right: parent.right Layout.fillWidth: true
anchors.topMargin: 24 rightButtonClickedOnEnter: true
anchors.rightMargin: 16
anchors.leftMargin: 16 textField.placeholderText: qsTr("website or IP")
anchors.bottomMargin: 24 buttonImageSource: "qrc:/images/controls/plus.svg"
TextFieldWithHeaderType { clickedFunc: function() {
id: searchField PageController.showBusyIndicator(true)
SitesController.addSite(textField.text)
Layout.fillWidth: true textField.text = ""
rightButtonClickedOnEnter: true PageController.showBusyIndicator(false)
}
textField.placeholderText: qsTr("website or IP") }
buttonImageSource: "qrc:/images/controls/plus.svg"
ImageButtonType {
clickedFunc: function() { id: addSiteButtonImage
PageController.showBusyIndicator(true) implicitWidth: 56
SitesController.addSite(textField.text) implicitHeight: 56
textField.text = ""
PageController.showBusyIndicator(false) image: "qrc:/images/controls/more-vertical.svg"
} imageColor: AmneziaStyle.color.paleGray
}
onClicked: function () {
ImageButtonType { moreActionsDrawer.openTriggered()
id: addSiteButtonImage }
implicitWidth: 56
implicitHeight: 56 Keys.onReturnPressed: addSiteButtonImage.clicked()
Keys.onEnterPressed: addSiteButtonImage.clicked()
image: "qrc:/images/controls/more-vertical.svg" }
imageColor: AmneziaStyle.color.paleGray }
onClicked: function () { DrawerType2 {
moreActionsDrawer.openTriggered() id: moreActionsDrawer
}
anchors.fill: parent
Keys.onReturnPressed: addSiteButtonImage.clicked() expandedHeight: parent.height * 0.4375
Keys.onEnterPressed: addSiteButtonImage.clicked()
} expandedStateContent: ColumnLayout {
} id: moreActionsDrawerContent
DrawerType2 { anchors.top: parent.top
id: moreActionsDrawer anchors.left: parent.left
anchors.right: parent.right
anchors.fill: parent
expandedHeight: parent.height * 0.4375 Header2Type {
Layout.fillWidth: true
expandedStateContent: ColumnLayout { Layout.margins: 16
id: moreActionsDrawerContent
headerText: qsTr("Import / Export Sites")
anchors.top: parent.top }
anchors.left: parent.left
anchors.right: parent.right LabelWithButtonType {
id: importSitesButton
Header2Type { Layout.fillWidth: true
Layout.fillWidth: true
Layout.margins: 16 text: qsTr("Import")
rightImageSource: "qrc:/images/controls/chevron-right.svg"
headerText: qsTr("Import / Export Sites")
} clickedFunction: function() {
importSitesDrawer.openTriggered()
LabelWithButtonType { }
id: importSitesButton }
Layout.fillWidth: true
DividerType {}
text: qsTr("Import")
rightImageSource: "qrc:/images/controls/chevron-right.svg" LabelWithButtonType {
id: exportSitesButton
clickedFunction: function() { Layout.fillWidth: true
importSitesDrawer.openTriggered() text: qsTr("Save site list")
}
} clickedFunction: function() {
var fileName = ""
DividerType {} if (GC.isMobile()) {
fileName = "amnezia_sites.json"
LabelWithButtonType { } else {
id: exportSitesButton fileName = SystemController.getFileName(qsTr("Save sites"),
Layout.fillWidth: true qsTr("Sites files (*.json)"),
text: qsTr("Save site list") StandardPaths.standardLocations(StandardPaths.DocumentsLocation) + "/amnezia_sites",
true,
clickedFunction: function() { ".json")
var fileName = "" }
if (GC.isMobile()) { if (fileName !== "") {
fileName = "amnezia_sites.json" PageController.showBusyIndicator(true)
} else { SitesController.exportSites(fileName)
fileName = SystemController.getFileName(qsTr("Save sites"), moreActionsDrawer.closeTriggered()
qsTr("Sites files (*.json)"), PageController.showBusyIndicator(false)
StandardPaths.standardLocations(StandardPaths.DocumentsLocation) + "/amnezia_sites", }
true, }
".json") }
}
if (fileName !== "") { DividerType {}
PageController.showBusyIndicator(true) }
SitesController.exportSites(fileName) }
moreActionsDrawer.closeTriggered()
PageController.showBusyIndicator(false) DrawerType2 {
} id: importSitesDrawer
}
} anchors.fill: parent
expandedHeight: parent.height * 0.4375
DividerType {}
} expandedStateContent: Item {
} implicitHeight: importSitesDrawer.expandedHeight
DrawerType2 { BackButtonType {
id: importSitesDrawer id: importSitesDrawerBackButton
anchors.fill: parent anchors.top: parent.top
expandedHeight: parent.height * 0.4375 anchors.left: parent.left
anchors.right: parent.right
expandedStateContent: Item { anchors.topMargin: 16
implicitHeight: importSitesDrawer.expandedHeight
backButtonFunction: function() {
BackButtonType { importSitesDrawer.closeTriggered()
id: importSitesDrawerBackButton }
}
anchors.top: parent.top
anchors.left: parent.left ListViewType {
anchors.right: parent.right id: importSitesDrawerListView
anchors.topMargin: 16
anchors.top: importSitesDrawerBackButton.bottom
backButtonFunction: function() { anchors.left: parent.left
importSitesDrawer.closeTriggered() anchors.right: parent.right
} anchors.bottom: parent.bottom
}
header: ColumnLayout {
FlickableType { width: importSitesDrawerListView.width
anchors.top: importSitesDrawerBackButton.bottom
anchors.left: parent.left Header2Type {
anchors.right: parent.right Layout.fillWidth: true
anchors.bottom: parent.bottom Layout.margins: 16
contentHeight: importSitesDrawerContent.height headerText: qsTr("Import a list of sites")
}
ColumnLayout { }
id: importSitesDrawerContent
model: importOptions
anchors.top: parent.top
anchors.left: parent.left delegate: ColumnLayout {
anchors.right: parent.right width: importSitesDrawerListView.width
Header2Type { LabelWithButtonType {
Layout.fillWidth: true Layout.fillWidth: true
Layout.margins: 16 Layout.leftMargin: 16
Layout.rightMargin: 16
headerText: qsTr("Import a list of sites")
} text: title
LabelWithButtonType { clickedFunction: function() {
id: importSitesButton2 clickedHandler()
Layout.fillWidth: true }
}
text: qsTr("Replace site list")
DividerType {}
clickedFunction: function() { }
var fileName = SystemController.getFileName(qsTr("Open sites file"), }
qsTr("Sites files (*.json)")) }
if (fileName !== "") { }
importSitesDrawerContent.importSites(fileName, true)
} property list<QtObject> importOptions: [
} replaceOption,
} addOption,
]
DividerType {}
QtObject {
LabelWithButtonType { id: replaceOption
id: importSitesButton3
Layout.fillWidth: true readonly property string title: qsTr("Replace site list")
text: qsTr("Add imported sites to existing ones") readonly property var clickedHandler: function() {
var fileName = SystemController.getFileName(qsTr("Open sites file"),
clickedFunction: function() { qsTr("Sites files (*.json)"))
var fileName = SystemController.getFileName(qsTr("Open sites file"), if (fileName !== "") {
qsTr("Sites files (*.json)")) root.importSites(fileName, true)
if (fileName !== "") { }
importSitesDrawerContent.importSites(fileName, false) }
} }
}
} QtObject {
id: addOption
function importSites(fileName, replaceExistingSites) {
PageController.showBusyIndicator(true) readonly property string title: qsTr("Add imported sites to existing ones")
SitesController.importSites(fileName, replaceExistingSites) readonly property var clickedHandler: function() {
PageController.showBusyIndicator(false) var fileName = SystemController.getFileName(qsTr("Open sites file"),
importSitesDrawer.closeTriggered() qsTr("Sites files (*.json)"))
moreActionsDrawer.closeTriggered() if (fileName !== "") {
} root.importSites(fileName, false)
}
DividerType {} }
} }
}
} function importSites(fileName, replaceExistingSites) {
} PageController.showBusyIndicator(true)
} SitesController.importSites(fileName, replaceExistingSites)
PageController.showBusyIndicator(false)
importSitesDrawer.closeTriggered()
moreActionsDrawer.closeTriggered()
}
}

View file

@ -1,146 +1,177 @@
import QtQuick import QtQuick
import QtQuick.Controls import QtQuick.Controls
import QtQuick.Layouts import QtQuick.Layouts
import QtQuick.Dialogs import QtQuick.Dialogs
import PageEnum 1.0 import PageEnum 1.0
import Style 1.0 import Style 1.0
import "./" import "./"
import "../Controls2" import "../Controls2"
import "../Controls2/TextTypes" import "../Controls2/TextTypes"
import "../Config" import "../Config"
import "../Components" import "../Components"
PageType { PageType {
id: root id: root
FlickableType { BackButtonType {
id: fl id: backButton
anchors.top: parent.top
anchors.bottom: parent.bottom anchors.top: parent.top
contentHeight: content.height + continueButton.implicitHeight + continueButton.anchors.bottomMargin + continueButton.anchors.topMargin anchors.left: parent.left
anchors.right: parent.right
ColumnLayout { anchors.topMargin: 20
id: content
onFocusChanged: {
anchors.top: parent.top if (this.activeFocus) {
anchors.left: parent.left listView.positionViewAtBeginning()
anchors.right: parent.right }
}
spacing: 0 }
BackButtonType { ListViewType {
id: backButton id: listView
Layout.topMargin: 20
} anchors.top: backButton.bottom
anchors.bottom: parent.bottom
BaseHeaderType { anchors.right: parent.right
Layout.fillWidth: true anchors.left: parent.left
Layout.topMargin: 8
Layout.rightMargin: 16 header: ColumnLayout {
Layout.leftMargin: 16 width: listView.width
Layout.bottomMargin: 32
BaseHeaderType {
headerText: ApiServicesModel.getSelectedServiceData("name") Layout.fillWidth: true
descriptionText: ApiServicesModel.getSelectedServiceData("serviceDescription") Layout.topMargin: 8
} Layout.rightMargin: 16
Layout.leftMargin: 16
LabelWithImageType { Layout.bottomMargin: 32
Layout.fillWidth: true
Layout.margins: 16 headerText: ApiServicesModel.getSelectedServiceData("name")
descriptionText: ApiServicesModel.getSelectedServiceData("serviceDescription")
imageSource: "qrc:/images/controls/map-pin.svg" }
leftText: qsTr("For the region") }
rightText: ApiServicesModel.getSelectedServiceData("region")
} model: inputFields
spacing: 0
LabelWithImageType {
Layout.fillWidth: true delegate: ColumnLayout {
Layout.margins: 16 width: listView.width
imageSource: "qrc:/images/controls/tag.svg" LabelWithImageType {
leftText: qsTr("Price") Layout.fillWidth: true
rightText: ApiServicesModel.getSelectedServiceData("price") Layout.margins: 16
}
imageSource: imagePath
LabelWithImageType { leftText: lText
Layout.fillWidth: true rightText: rText
Layout.margins: 16 }
}
imageSource: "qrc:/images/controls/history.svg"
leftText: qsTr("Work period") footer: ColumnLayout {
rightText: ApiServicesModel.getSelectedServiceData("timeLimit") width: listView.width
visible: rightText !== "" spacing: 0
}
ParagraphTextType {
LabelWithImageType { Layout.fillWidth: true
Layout.fillWidth: true Layout.rightMargin: 16
Layout.margins: 16 Layout.leftMargin: 16
imageSource: "qrc:/images/controls/gauge.svg" onLinkActivated: function(link) {
leftText: qsTr("Speed") Qt.openUrlExternally(link)
rightText: ApiServicesModel.getSelectedServiceData("speed") }
} textFormat: Text.RichText
text: {
LabelWithImageType { var text = ApiServicesModel.getSelectedServiceData("features")
Layout.fillWidth: true return text.replace("%1", LanguageModel.getCurrentSiteUrl())
Layout.margins: 16 }
imageSource: "qrc:/images/controls/info.svg" MouseArea {
leftText: qsTr("Features") anchors.fill: parent
rightText: "" acceptedButtons: Qt.NoButton
} cursorShape: parent.hoveredLink ? Qt.PointingHandCursor : Qt.ArrowCursor
}
ParagraphTextType { }
Layout.fillWidth: true
Layout.rightMargin: 16 BasicButtonType {
Layout.leftMargin: 16 id: continueButton
onLinkActivated: function(link) { Layout.fillWidth: true
Qt.openUrlExternally(link) Layout.topMargin: 32
} Layout.bottomMargin: 32
textFormat: Text.RichText Layout.leftMargin: 16
text: { Layout.rightMargin: 16
var text = ApiServicesModel.getSelectedServiceData("features")
return text.replace("%1", LanguageModel.getCurrentSiteUrl()) text: qsTr("Connect")
}
clickedFunc: function() {
MouseArea { var endpoint = ApiServicesModel.getStoreEndpoint()
anchors.fill: parent if (endpoint !== undefined && endpoint !== "") {
acceptedButtons: Qt.NoButton Qt.openUrlExternally(endpoint)
cursorShape: parent.hoveredLink ? Qt.PointingHandCursor : Qt.ArrowCursor PageController.closePage()
} PageController.closePage()
} } else {
} PageController.showBusyIndicator(true)
} ApiConfigsController.importServiceFromGateway()
PageController.showBusyIndicator(false)
BasicButtonType { }
id: continueButton }
}
anchors.right: parent.right }
anchors.left: parent.left }
anchors.bottom: parent.bottom
property list<QtObject> inputFields: [
anchors.topMargin: 32 region,
anchors.rightMargin: 16 price,
anchors.leftMargin: 16 timeLimit,
anchors.bottomMargin: 32 speed,
features
text: qsTr("Connect") ]
clickedFunc: function() { QtObject {
var endpoint = ApiServicesModel.getStoreEndpoint() id: region
if (endpoint !== undefined && endpoint !== "") {
Qt.openUrlExternally(endpoint) readonly property string imagePath: "qrc:/images/controls/map-pin.svg"
PageController.closePage() readonly property string lText: qsTr("For the region")
PageController.closePage() readonly property string rText: ApiServicesModel.getSelectedServiceData("region")
} else { property bool isVisible: true
PageController.showBusyIndicator(true) }
ApiConfigsController.importServiceFromGateway()
PageController.showBusyIndicator(false) QtObject {
} id: price
}
} readonly property string imagePath: "qrc:/images/controls/tag.svg"
} readonly property string lText: qsTr("Price")
readonly property string rText: ApiServicesModel.getSelectedServiceData("price")
property bool isVisible: true
}
QtObject {
id: timeLimit
readonly property string imagePath: "qrc:/images/controls/history.svg"
readonly property string lText: qsTr("Work period")
readonly property string rText: ApiServicesModel.getSelectedServiceData("timeLimit")
property bool isVisible: rText !== ""
}
QtObject {
id: speed
readonly property string imagePath: "qrc:/images/controls/gauge.svg"
readonly property string lText: qsTr("Speed")
readonly property string rText: ApiServicesModel.getSelectedServiceData("speed")
property bool isVisible: true
}
QtObject {
id: features
readonly property string imagePath: "qrc:/images/controls/info.svg"
readonly property string lText: qsTr("Features")
readonly property string rText: ""
property bool isVisible: true
}
}

View file

@ -40,7 +40,7 @@ PageType {
} }
} }
ListView { ListViewType {
id: servicesListView id: servicesListView
anchors.top: header.bottom anchors.top: header.bottom
@ -48,17 +48,11 @@ PageType {
anchors.left: parent.left anchors.left: parent.left
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
anchors.topMargin: 16 anchors.topMargin: 16
spacing: 0 spacing: 0
property bool isFocusable: true
clip: true
reuseItems: true
model: ApiServicesModel model: ApiServicesModel
ScrollBar.vertical: ScrollBarType {}
delegate: Item { delegate: Item {
implicitWidth: servicesListView.width implicitWidth: servicesListView.width
implicitHeight: delegateContent.implicitHeight implicitHeight: delegateContent.implicitHeight

View file

@ -27,21 +27,13 @@ PageType {
} }
} }
ListView { ListViewType {
id: listView id: listView
anchors.fill: parent anchors.fill: parent
property bool isFocusable: true
ScrollBar.vertical: ScrollBarType {}
model: variants model: variants
clip: true
reuseItems: true
header: ColumnLayout { header: ColumnLayout {
width: listView.width width: listView.width

View file

@ -28,41 +28,14 @@ PageType {
} }
} }
ListView { ListViewType {
id: listView id: listView
anchors.top: backButton.bottom anchors.top: backButton.bottom
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
anchors.right: parent.right anchors.right: parent.right
anchors.left: parent.left anchors.left: parent.left
property bool isFocusable: true
Keys.onTabPressed: {
FocusController.nextKeyTabItem()
}
Keys.onBacktabPressed: {
FocusController.previousKeyTabItem()
}
Keys.onUpPressed: {
FocusController.nextKeyUpItem()
}
Keys.onDownPressed: {
FocusController.nextKeyDownItem()
}
Keys.onLeftPressed: {
FocusController.nextKeyLeftItem()
}
Keys.onRightPressed: {
FocusController.nextKeyRightItem()
}
ScrollBar.vertical: ScrollBarType {}
header: ColumnLayout { header: ColumnLayout {
width: listView.width width: listView.width
@ -78,8 +51,6 @@ PageType {
model: inputFields model: inputFields
spacing: 16 spacing: 16
clip: true
reuseItems: true
delegate: ColumnLayout { delegate: ColumnLayout {
width: listView.width width: listView.width

View file

@ -20,6 +20,7 @@ PageType {
SortFilterProxyModel { SortFilterProxyModel {
id: proxyContainersModel id: proxyContainersModel
sourceModel: ContainersModel sourceModel: ContainersModel
filters: [ filters: [
ValueFilter { ValueFilter {
@ -42,102 +43,95 @@ PageType {
anchors.topMargin: 20 anchors.topMargin: 20
} }
FlickableType { ButtonGroup {
id: fl id: buttonGroup
}
ListViewType {
id: listView
property int dockerContainer
property int containerDefaultPort
property int containerDefaultTransportProto
anchors.top: backButton.bottom anchors.top: backButton.bottom
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
contentHeight: content.implicitHeight + setupLaterButton.anchors.bottomMargin anchors.left: parent.left
anchors.right: parent.right
Column { spacing: 16
header: ColumnLayout {
id: content id: content
anchors.top: parent.top width: listView.width
anchors.left: parent.left
anchors.right: parent.right
anchors.rightMargin: 16
anchors.leftMargin: 16
spacing: 16 spacing: 16
BaseHeaderType { BaseHeaderType {
id: header id: header
implicitWidth: parent.width Layout.fillWidth: true
Layout.rightMargin: 16
Layout.leftMargin: 16
Layout.bottomMargin: 16
headerTextMaximumLineCount: 10 headerTextMaximumLineCount: 10
headerText: qsTr("Choose Installation Type") headerText: qsTr("Choose Installation Type")
} }
}
ButtonGroup { model: proxyContainersModel
id: buttonGroup currentIndex: 0
}
ListView { delegate: ColumnLayout {
id: containers
width: parent.width
height: containers.contentItem.height
spacing: 16
currentIndex: 0 width: listView.width
clip: true
interactive: false
model: proxyContainersModel
property int dockerContainer CardType {
property int containerDefaultPort id: card
property int containerDefaultTransportProto
property bool isFocusable: true Layout.fillWidth: true
Layout.rightMargin: 16
Layout.leftMargin: 16
Layout.bottomMargin: 16
delegate: Item { headerText: easySetupHeader
implicitWidth: containers.width bodyText: easySetupDescription
implicitHeight: delegateContent.implicitHeight
ColumnLayout { ButtonGroup.group: buttonGroup
id: delegateContent
anchors.top: parent.top onClicked: function() {
anchors.left: parent.left checked = true
anchors.right: parent.right isEasySetup = true
var defaultContainerProto = ContainerProps.defaultProtocol(dockerContainer)
CardType { listView.dockerContainer = dockerContainer
id: card listView.containerDefaultPort = ProtocolProps.getPortForInstall(defaultContainerProto)
listView.containerDefaultTransportProto = ProtocolProps.defaultTransportProto(defaultContainerProto)
Layout.fillWidth: true
headerText: easySetupHeader
bodyText: easySetupDescription
ButtonGroup.group: buttonGroup
onClicked: function() {
isEasySetup = true
var defaultContainerProto = ContainerProps.defaultProtocol(dockerContainer)
containers.dockerContainer = dockerContainer
containers.containerDefaultPort = ProtocolProps.getPortForInstall(defaultContainerProto)
containers.containerDefaultTransportProto = ProtocolProps.defaultTransportProto(defaultContainerProto)
}
}
}
} }
Component.onCompleted: { Keys.onReturnPressed: this.clicked()
var item = containers.itemAtIndex(containers.currentIndex) Keys.onEnterPressed: this.clicked()
if (item !== null) {
var button = item.children[0].children[0]
button.checked = true
button.clicked()
}
}
} }
}
footer: ColumnLayout {
width: listView.width
spacing: 16
DividerType { DividerType {
implicitWidth: parent.width Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 16
} }
CardType { CardType {
implicitWidth: parent.width Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 16
headerText: qsTr("Manual") headerText: qsTr("Manual")
bodyText: qsTr("Choose a VPN protocol") bodyText: qsTr("Choose a VPN protocol")
@ -146,25 +140,29 @@ PageType {
onClicked: function() { onClicked: function() {
isEasySetup = false isEasySetup = false
checked = true
} }
Keys.onReturnPressed: this.clicked()
Keys.onEnterPressed: this.clicked()
} }
BasicButtonType { BasicButtonType {
id: continueButton id: continueButton
implicitWidth: parent.width Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 16
text: qsTr("Continue") text: qsTr("Continue")
parentFlickable: fl
clickedFunc: function() { clickedFunc: function() {
if (root.isEasySetup) { if (root.isEasySetup) {
ContainersModel.setProcessedContainerIndex(containers.dockerContainer) ContainersModel.setProcessedContainerIndex(listView.dockerContainer)
PageController.goToPage(PageEnum.PageSetupWizardInstalling) PageController.goToPage(PageEnum.PageSetupWizardInstalling)
InstallController.install(containers.dockerContainer, InstallController.install(listView.dockerContainer,
containers.containerDefaultPort, listView.containerDefaultPort,
containers.containerDefaultTransportProto) listView.containerDefaultTransportProto)
} else { } else {
PageController.goToPage(PageEnum.PageSetupWizardProtocols) PageController.goToPage(PageEnum.PageSetupWizardProtocols)
} }
@ -174,9 +172,11 @@ PageType {
BasicButtonType { BasicButtonType {
id: setupLaterButton id: setupLaterButton
implicitWidth: parent.width Layout.fillWidth: true
anchors.topMargin: 8 Layout.topMargin: 8
anchors.bottomMargin: 24 Layout.bottomMargin: 24
Layout.leftMargin: 16
Layout.rightMargin: 16
defaultColor: AmneziaStyle.color.transparent defaultColor: AmneziaStyle.color.transparent
hoveredColor: AmneziaStyle.color.translucentWhite hoveredColor: AmneziaStyle.color.translucentWhite
@ -185,9 +185,6 @@ PageType {
textColor: AmneziaStyle.color.paleGray textColor: AmneziaStyle.color.paleGray
borderWidth: 1 borderWidth: 1
Keys.onTabPressed: lastItemTabClicked(focusItem)
parentFlickable: fl
visible: { visible: {
if (PageController.isTriggeredByConnectButton()) { if (PageController.isTriggeredByConnectButton()) {
PageController.setTriggeredByConnectButton(false) PageController.setTriggeredByConnectButton(false)
@ -205,5 +202,15 @@ PageType {
} }
} }
} }
Component.onCompleted: {
var item = listView.itemAtIndex(listView.currentIndex)
if (item !== null) {
var button = item.children[0]
button.checked = true
button.clicked()
}
}
} }
} }

View file

@ -85,92 +85,76 @@ PageType {
] ]
} }
FlickableType { ListViewType {
id: listView
anchors.fill: parent anchors.fill: parent
contentHeight: content.height
Column { currentIndex: -1
id: content
anchors.top: parent.top model: proxyContainersModel
anchors.left: parent.left
anchors.right: parent.right
spacing: 16 delegate: ColumnLayout {
width: listView.width
ListView { BaseHeaderType {
id: container Layout.fillWidth: true
width: parent.width Layout.topMargin: 20
height: container.contentItem.height Layout.leftMargin: 16
currentIndex: -1 Layout.rightMargin: 16
clip: true
interactive: false
model: proxyContainersModel
delegate: Item { headerText: qsTr("Installing")
implicitWidth: container.width descriptionText: name
implicitHeight: delegateContent.implicitHeight }
ColumnLayout { ProgressBarType {
id: delegateContent id: progressBar
anchors.fill: parent Layout.fillWidth: true
anchors.rightMargin: 16 Layout.topMargin: 32
anchors.leftMargin: 16 Layout.leftMargin: 16
Layout.rightMargin: 16
BaseHeaderType { Timer {
Layout.fillWidth: true id: timer
Layout.topMargin: 20
headerText: qsTr("Installing") interval: 300
descriptionText: name repeat: true
} running: root.isTimerRunning
onTriggered: {
ProgressBarType { progressBar.value += 0.003
id: progressBar
Layout.fillWidth: true
Layout.topMargin: 32
Timer {
id: timer
interval: 300
repeat: true
running: root.isTimerRunning
onTriggered: {
progressBar.value += 0.003
}
}
}
ParagraphTextType {
id: progressText
Layout.fillWidth: true
Layout.topMargin: 8
text: root.progressBarText
}
BasicButtonType {
id: cancelIntallationButton
Layout.fillWidth: true
Layout.topMargin: 24
visible: root.isCancelButtonVisible
text: qsTr("Cancel installation")
clickedFunc: function() {
InstallController.cancelInstallation()
PageController.showBusyIndicator(true)
}
}
} }
} }
} }
ParagraphTextType {
id: progressText
Layout.fillWidth: true
Layout.topMargin: 8
Layout.leftMargin: 16
Layout.rightMargin: 16
text: root.progressBarText
}
BasicButtonType {
id: cancelIntallationButton
Layout.fillWidth: true
Layout.topMargin: 24
Layout.leftMargin: 16
Layout.rightMargin: 16
visible: root.isCancelButtonVisible
text: qsTr("Cancel installation")
clickedFunc: function() {
InstallController.cancelInstallation()
PageController.showBusyIndicator(true)
}
}
} }
} }
} }

Some files were not shown because too many files have changed in this diff Show more