Compare commits

..

1 Commits

Author SHA1 Message Date
Yaroslav Gurov 20989ea832 feat: integrated xray as a library 2025-07-31 13:13:42 +02:00
110 changed files with 7136 additions and 7438 deletions
+2 -30
View File
@@ -255,20 +255,6 @@ 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
MAC_TEAM_ID: ${{ secrets.MAC_TEAM_ID }}
MAC_APP_CERT_CERT: ${{ secrets.MAC_APP_CERT_CERT }}
MAC_SIGNER_ID: ${{ secrets.MAC_SIGNER_ID }}
MAC_APP_CERT_PW: ${{ secrets.MAC_APP_CERT_PW }}
MAC_INSTALLER_SIGNER_CERT: ${{ secrets.MAC_INSTALLER_SIGNER_CERT }}
MAC_INSTALLER_SIGNER_ID: ${{ secrets.MAC_INSTALLER_SIGNER_ID }}
MAC_INSTALL_CERT_PW: ${{ secrets.MAC_INSTALL_CERT_PW }}
APPLE_DEV_EMAIL: ${{ secrets.APPLE_DEV_EMAIL }}
APPLE_DEV_PASSWORD: ${{ secrets.APPLE_DEV_PASSWORD }}
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 }}
@@ -309,7 +295,7 @@ 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"
bash deploy/build_macos.sh -n bash deploy/build_macos.sh
- name: 'Upload installer artifact' - name: 'Upload installer artifact'
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v4
@@ -332,20 +318,6 @@ jobs:
env: env:
QT_VERSION: 6.8.0 QT_VERSION: 6.8.0
MAC_TEAM_ID: ${{ secrets.MAC_TEAM_ID }}
MAC_APP_CERT_CERT: ${{ secrets.MAC_APP_CERT_CERT }}
MAC_SIGNER_ID: ${{ secrets.MAC_SIGNER_ID }}
MAC_APP_CERT_PW: ${{ secrets.MAC_APP_CERT_PW }}
MAC_INSTALLER_SIGNER_CERT: ${{ secrets.MAC_INSTALLER_SIGNER_CERT }}
MAC_INSTALLER_SIGNER_ID: ${{ secrets.MAC_INSTALLER_SIGNER_ID }}
MAC_INSTALL_CERT_PW: ${{ secrets.MAC_INSTALL_CERT_PW }}
APPLE_DEV_EMAIL: ${{ secrets.APPLE_DEV_EMAIL }}
APPLE_DEV_PASSWORD: ${{ secrets.APPLE_DEV_PASSWORD }}
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 }}
@@ -386,7 +358,7 @@ 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"
bash deploy/build_macos.sh -n bash deploy/build_macos.sh
- name: 'Upload installer artifact' - name: 'Upload installer artifact'
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v4
-1
View File
@@ -138,4 +138,3 @@ CMakeFiles/
ios-ne-build.sh ios-ne-build.sh
macos-ne-build.sh macos-ne-build.sh
macos-signed-build.sh macos-signed-build.sh
macos-with-sign-build.sh
+2 -3
View File
@@ -1,9 +1,8 @@
cmake_minimum_required(VERSION 3.25.0 FATAL_ERROR) cmake_minimum_required(VERSION 3.25.0 FATAL_ERROR)
set(PROJECT AmneziaVPN) set(PROJECT AmneziaVPN)
set(AMNEZIAVPN_VERSION 4.8.9.2)
project(${PROJECT} VERSION ${AMNEZIAVPN_VERSION} project(${PROJECT} VERSION 4.8.8.1
DESCRIPTION "AmneziaVPN" DESCRIPTION "AmneziaVPN"
HOMEPAGE_URL "https://amnezia.org/" HOMEPAGE_URL "https://amnezia.org/"
) )
@@ -12,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 2092) set(APP_ANDROID_VERSION_CODE 2087)
if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux") if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
set(MZ_PLATFORM_NAME "linux") set(MZ_PLATFORM_NAME "linux")
+5 -5
View File
@@ -9,17 +9,17 @@
### [English]([https://github.com/amnezia-vpn/amnezia-client/blob/dev/README_RU.md](https://github.com/amnezia-vpn/amnezia-client/tree/dev?tab=readme-ov-file#)) | [Русский](https://github.com/amnezia-vpn/amnezia-client/blob/dev/README_RU.md) ### [English]([https://github.com/amnezia-vpn/amnezia-client/blob/dev/README_RU.md](https://github.com/amnezia-vpn/amnezia-client/tree/dev?tab=readme-ov-file#)) | [Русский](https://github.com/amnezia-vpn/amnezia-client/blob/dev/README_RU.md)
[Amnezia](https://amnezia.org?utm_source=github&utm_campaign=amnezia_website-readme-en) is an open-source VPN client, with a key feature that enables you to deploy your own VPN server on your server. [Amnezia](https://amnezia.org) is an open-source VPN client, with a key feature that enables you to deploy your own VPN server on your server.
[![Image](https://github.com/amnezia-vpn/amnezia-client/blob/dev/metadata/img-readme/uipic4.png)](https://amnezia.org) [![Image](https://github.com/amnezia-vpn/amnezia-client/blob/dev/metadata/img-readme/uipic4.png)](https://amnezia.org)
### [Website](https://amnezia.org?utm_source=github&utm_campaign=amnezia_website-readme-en) | [Alt website link](https://storage.googleapis.com/amnezia/amnezia.org?utm_source=github&utm_campaign=amnezia_website-readme-en-mirror) | [Documentation](https://docs.amnezia.org) | [Troubleshooting](https://docs.amnezia.org/troubleshooting) ### [Website](https://amnezia.org) | [Alt website link](https://storage.googleapis.com/amnezia/amnezia.org) | [Documentation](https://docs.amnezia.org) | [Troubleshooting](https://docs.amnezia.org/troubleshooting)
> [!TIP] > [!TIP]
> If the [Amnezia website](https://amnezia.org?utm_source=github&utm_campaign=amnezia_website-readme-en) is blocked in your region, you can use an [Alternative website link](https://storage.googleapis.com/amnezia/amnezia.org?utm_source=github&utm_campaign=amnezia_website-readme-en-mirror). > If the [Amnezia website](https://amnezia.org) is blocked in your region, you can use an [Alternative website link](https://storage.googleapis.com/amnezia/amnezia.org ).
<a href="https://amnezia.org/en/downloads?utm_source=github&utm_campaign=amnezia_button-readme-en"><img src="https://github.com/amnezia-vpn/amnezia-client/blob/dev/metadata/img-readme/download-website.svg" width="150" style="max-width: 100%; margin-right: 10px"></a> <a href="https://amnezia.org/downloads"><img src="https://github.com/amnezia-vpn/amnezia-client/blob/dev/metadata/img-readme/download-website.svg" width="150" style="max-width: 100%; margin-right: 10px"></a>
<a href="https://storage.googleapis.com/amnezia/amnezia.org?m-path=/en/downloads&utm_source=github&utm_campaign=amnezia_button-readme-en-mirrow"><img src="https://github.com/amnezia-vpn/amnezia-client/blob/dev/metadata/img-readme/download-alt.svg" width="150" style="max-width: 100%;"></a> <a href="https://storage.googleapis.com/amnezia/q9p19109"><img src="https://github.com/amnezia-vpn/amnezia-client/blob/dev/metadata/img-readme/download-alt.svg" width="150" style="max-width: 100%;"></a>
[All releases](https://github.com/amnezia-vpn/amnezia-client/releases) [All releases](https://github.com/amnezia-vpn/amnezia-client/releases)
+4 -4
View File
@@ -6,16 +6,16 @@
[![Gitpod ready-to-code](https://img.shields.io/badge/Gitpod-ready--to--code-blue?logo=gitpod)](https://gitpod.io/#https://github.com/amnezia-vpn/amnezia-client) [![Gitpod ready-to-code](https://img.shields.io/badge/Gitpod-ready--to--code-blue?logo=gitpod)](https://gitpod.io/#https://github.com/amnezia-vpn/amnezia-client)
### [English](https://github.com/amnezia-vpn/amnezia-client/blob/dev/README.md) | Русский ### [English](https://github.com/amnezia-vpn/amnezia-client/blob/dev/README.md) | Русский
[AmneziaVPN](https://amnezia.org?utm_source=github&utm_campaign=amnezia_website-readme-ru) — это open source VPN-клиент, ключевая особенность которого заключается в возможности развернуть собственный VPN на вашем сервере. [AmneziaVPN](https://amnezia.org) — это open source VPN-клиент, ключевая особенность которого заключается в возможности развернуть собственный VPN на вашем сервере.
[![Image](https://github.com/amnezia-vpn/amnezia-client/blob/dev/metadata/img-readme/uipic4.png)](https://amnezia.org) [![Image](https://github.com/amnezia-vpn/amnezia-client/blob/dev/metadata/img-readme/uipic4.png)](https://amnezia.org)
### [Сайт](https://amnezia.org?utm_source=github&utm_campaign=amnezia_website-readme-ru) | [Зеркало сайта](https://storage.googleapis.com/amnezia/amnezia.org?utm_source=github&utm_campaign=amnezia_website-readme-ru-mirror) | [Документация](https://docs.amnezia.org) | [Решение проблем](https://docs.amnezia.org/troubleshooting) ### [Сайт](https://amnezia.org) | [Зеркало сайта](https://storage.googleapis.com/amnezia/amnezia.org) | [Документация](https://docs.amnezia.org) | [Решение проблем](https://docs.amnezia.org/troubleshooting)
> [!TIP] > [!TIP]
> Если [сайт Amnezia](https://amnezia.org?utm_source=github&utm_campaign=amnezia_website-readme-ru) заблокирован в вашем регионе, вы можете воспользоваться [ссылкой на зеркало](https://storage.googleapis.com/amnezia/amnezia.org?utm_source=github&utm_campaign=amnezia_website-readme-ru-mirror). > Если [сайт Amnezia](https://amnezia.org) заблокирован в вашем регионе, вы можете воспользоваться [ссылкой на зеркало](https://storage.googleapis.com/amnezia/amnezia.org).
<a href="https://storage.googleapis.com/amnezia/amnezia.org?m-path=/ru/downloads&utm_source=github&utm_campaign=amnezia_button-readme-ru-mirror"><img src="https://github.com/amnezia-vpn/amnezia-client/blob/dev/metadata/img-readme/download-website-ru.svg" width="150" style="max-width: 100%; margin-right: 10px"></a> <a href="https://storage.googleapis.com/amnezia/q9p19109"><img src="https://github.com/amnezia-vpn/amnezia-client/blob/dev/metadata/img-readme/download-website-ru.svg" width="150" style="max-width: 100%; margin-right: 10px"></a>
[Все релизы](https://github.com/amnezia-vpn/amnezia-client/releases) [Все релизы](https://github.com/amnezia-vpn/amnezia-client/releases)
+2 -4
View File
@@ -10,8 +10,6 @@ import java.nio.channels.FileChannel
import java.nio.channels.FileLock import java.nio.channels.FileLock
import java.time.LocalDateTime import java.time.LocalDateTime
import java.time.format.DateTimeFormatter import java.time.format.DateTimeFormatter
import java.time.ZonedDateTime
import java.time.ZoneOffset
import java.util.concurrent.locks.ReentrantLock import java.util.concurrent.locks.ReentrantLock
import org.amnezia.vpn.util.Log.Priority.D import org.amnezia.vpn.util.Log.Priority.D
import org.amnezia.vpn.util.Log.Priority.E import org.amnezia.vpn.util.Log.Priority.E
@@ -137,8 +135,8 @@ object Log {
} }
private fun formatLogMsg(tag: String, msg: String, priority: Priority): String { private fun formatLogMsg(tag: String, msg: String, priority: Priority): String {
val utcDate = ZonedDateTime.now(ZoneOffset.UTC).format(dateTimeFormat) val date = LocalDateTime.now().format(dateTimeFormat)
return "${utcDate}Z ${Process.myPid()} ${Process.myTid()} $priority [${Thread.currentThread().name}] " + return "$date ${Process.myPid()} ${Process.myTid()} $priority [${Thread.currentThread().name}] " +
"$tag: $msg\n" "$tag: $msg\n"
} }
+7
View File
@@ -10,6 +10,7 @@ include(${CLIENT_ROOT_DIR}/3rd/qrcodegen/qrcodegen.cmake)
set(LIBSSH_ROOT_DIR "${CLIENT_ROOT_DIR}/3rd-prebuilt/3rd-prebuilt/libssh/") set(LIBSSH_ROOT_DIR "${CLIENT_ROOT_DIR}/3rd-prebuilt/3rd-prebuilt/libssh/")
set(OPENSSL_ROOT_DIR "${CLIENT_ROOT_DIR}/3rd-prebuilt/3rd-prebuilt/openssl/") set(OPENSSL_ROOT_DIR "${CLIENT_ROOT_DIR}/3rd-prebuilt/3rd-prebuilt/openssl/")
set(LIBXRAY_ROOT_DIR "${CLIENT_ROOT_DIR}/3rd-prebuilt/3rd-prebuilt/libxray")
set(OPENSSL_LIBRARIES_DIR "${OPENSSL_ROOT_DIR}/lib") set(OPENSSL_LIBRARIES_DIR "${OPENSSL_ROOT_DIR}/lib")
@@ -27,6 +28,9 @@ if(WIN32)
set(OPENSSL_LIB_CRYPTO_PATH "${OPENSSL_ROOT_DIR}/windows/win32/libcrypto.lib") set(OPENSSL_LIB_CRYPTO_PATH "${OPENSSL_ROOT_DIR}/windows/win32/libcrypto.lib")
endif() endif()
elseif(APPLE AND NOT IOS) elseif(APPLE AND NOT IOS)
set(LIBS ${LIBS} "resolv")
set(LIBXRAY_LIB_PATH "${LIBXRAY_ROOT_DIR}/macos/x86_64/libxray.a")
set(LIBXRAY_INCLUDE_DIR "${LIBXRAY_ROOT_DIR}/macos/x86_64")
set(LIBSSH_LIB_PATH "${LIBSSH_ROOT_DIR}/macos/x86_64/libssh.a") set(LIBSSH_LIB_PATH "${LIBSSH_ROOT_DIR}/macos/x86_64/libssh.a")
set(ZLIB_LIB_PATH "${LIBSSH_ROOT_DIR}/macos/x86_64/libz.a") set(ZLIB_LIB_PATH "${LIBSSH_ROOT_DIR}/macos/x86_64/libz.a")
set(LIBSSH_INCLUDE_DIR "${LIBSSH_ROOT_DIR}/macos/x86_64") set(LIBSSH_INCLUDE_DIR "${LIBSSH_ROOT_DIR}/macos/x86_64")
@@ -72,6 +76,8 @@ set(LIBS ${LIBS}
${OPENSSL_LIB_CRYPTO_PATH} ${OPENSSL_LIB_CRYPTO_PATH}
) )
set(LIBS ${LIBS} ${LIBXRAY_LIB_PATH})
add_compile_definitions(_WINSOCKAPI_) add_compile_definitions(_WINSOCKAPI_)
set(BUILD_SHARED_LIBS OFF CACHE BOOL "" FORCE) set(BUILD_SHARED_LIBS OFF CACHE BOOL "" FORCE)
@@ -88,4 +94,5 @@ include_directories(
${CLIENT_ROOT_DIR}/3rd/qtkeychain/qtkeychain ${CLIENT_ROOT_DIR}/3rd/qtkeychain/qtkeychain
${CMAKE_CURRENT_BINARY_DIR}/3rd/qtkeychain ${CMAKE_CURRENT_BINARY_DIR}/3rd/qtkeychain
${CMAKE_CURRENT_BINARY_DIR}/3rd/libssh/include ${CMAKE_CURRENT_BINARY_DIR}/3rd/libssh/include
${LIBXRAY_INCLUDE_DIR}
) )
+1 -5
View File
@@ -18,11 +18,7 @@ set(LIBS ${LIBS}
${FW_NETWORK_EXTENSION} ${FW_NETWORK_EXTENSION}
) )
set_target_properties(${PROJECT} PROPERTIES set_target_properties(${PROJECT} PROPERTIES MACOSX_BUNDLE TRUE)
MACOSX_BUNDLE TRUE
MACOSX_BUNDLE_SHORT_VERSION_STRING "${CMAKE_PROJECT_VERSION_MAJOR}.${CMAKE_PROJECT_VERSION_MINOR}.${CMAKE_PROJECT_VERSION_PATCH}"
MACOSX_BUNDLE_BUNDLE_VERSION "${CMAKE_PROJECT_VERSION_TWEAK}"
)
set(CMAKE_OSX_ARCHITECTURES "x86_64" CACHE INTERNAL "" FORCE) set(CMAKE_OSX_ARCHITECTURES "x86_64" CACHE INTERNAL "" FORCE)
set(CMAKE_OSX_DEPLOYMENT_TARGET 10.15) set(CMAKE_OSX_DEPLOYMENT_TARGET 10.15)
+3 -7
View File
@@ -120,9 +120,6 @@ void CoreController::initControllers()
connect(m_installController.get(), &InstallController::currentContainerUpdated, m_connectionController.get(), connect(m_installController.get(), &InstallController::currentContainerUpdated, m_connectionController.get(),
&ConnectionController::onCurrentContainerUpdated); // TODO remove this &ConnectionController::onCurrentContainerUpdated); // TODO remove this
connect(m_installController.get(), &InstallController::profileCleared,
m_protocolsModel.get(), &ProtocolsModel::updateModel);
m_importController.reset(new ImportController(m_serversModel, m_containersModel, m_settings)); m_importController.reset(new ImportController(m_serversModel, m_containersModel, m_settings));
m_engine->rootContext()->setContextProperty("ImportController", m_importController.get()); m_engine->rootContext()->setContextProperty("ImportController", m_importController.get());
@@ -245,9 +242,6 @@ void CoreController::initNotificationHandler()
connect(m_notificationHandler.get(), &NotificationHandler::disconnectRequested, m_connectionController.get(), connect(m_notificationHandler.get(), &NotificationHandler::disconnectRequested, m_connectionController.get(),
&ConnectionController::closeConnection); &ConnectionController::closeConnection);
connect(this, &CoreController::translationsUpdated, m_notificationHandler.get(), &NotificationHandler::onTranslationsUpdated); connect(this, &CoreController::translationsUpdated, m_notificationHandler.get(), &NotificationHandler::onTranslationsUpdated);
auto* trayHandler = qobject_cast<SystemTrayNotificationHandler*>(m_notificationHandler.get());
connect(this, &CoreController::websiteUrlChanged, trayHandler, &SystemTrayNotificationHandler::updateWebsiteUrl);
#endif #endif
} }
@@ -285,7 +279,6 @@ void CoreController::updateTranslator(const QLocale &locale)
m_engine->retranslate(); m_engine->retranslate();
emit translationsUpdated(); emit translationsUpdated();
emit websiteUrlChanged(m_languageModel->getCurrentSiteUrl());
} }
void CoreController::initErrorMessagesHandler() void CoreController::initErrorMessagesHandler()
@@ -306,10 +299,13 @@ void CoreController::setQmlRoot()
void CoreController::initApiCountryModelUpdateHandler() void CoreController::initApiCountryModelUpdateHandler()
{ {
// TODO
connect(m_serversModel.get(), &ServersModel::updateApiCountryModel, this, [this]() { connect(m_serversModel.get(), &ServersModel::updateApiCountryModel, this, [this]() {
m_apiCountryModel->updateModel(m_serversModel->getProcessedServerData("apiAvailableCountries").toJsonArray(), m_apiCountryModel->updateModel(m_serversModel->getProcessedServerData("apiAvailableCountries").toJsonArray(),
m_serversModel->getProcessedServerData("apiServerCountryCode").toString()); m_serversModel->getProcessedServerData("apiServerCountryCode").toString());
}); });
connect(m_serversModel.get(), &ServersModel::updateApiServicesModel, this,
[this]() { m_apiServicesModel->updateModel(m_serversModel->getProcessedServerData("apiConfig").toJsonObject()); });
} }
void CoreController::initContainerModelUpdateHandler() void CoreController::initContainerModelUpdateHandler()
-5
View File
@@ -5,10 +5,6 @@
#include <QQmlContext> #include <QQmlContext>
#include <QThread> #include <QThread>
#ifndef Q_OS_ANDROID
#include "ui/systemtray_notificationhandler.h"
#endif
#include "ui/controllers/api/apiConfigsController.h" #include "ui/controllers/api/apiConfigsController.h"
#include "ui/controllers/api/apiSettingsController.h" #include "ui/controllers/api/apiSettingsController.h"
#include "ui/controllers/api/apiPremV1MigrationController.h" #include "ui/controllers/api/apiPremV1MigrationController.h"
@@ -65,7 +61,6 @@ public:
signals: signals:
void translationsUpdated(); void translationsUpdated();
void websiteUrlChanged(const QString &newUrl);
private: private:
void initModels(); void initModels();
+50
View File
@@ -0,0 +1,50 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleAllowMixedLocalizations</key>
<true/>
<key>CFBundleExecutable</key>
<string>${EXECUTABLE_NAME}</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
<key>CFBundleShortVersionString</key>
<string>$(MARKETING_VERSION)</string>
<key>CFBundleVersion</key>
<string>$(CURRENT_PROJECT_VERSION)</string>
<key>ITSAppUsesNonExemptEncryption</key>
<false/>
<key>LSApplicationCategoryType</key>
<string>public.app-category.utilities</string>
<key>LSMinimumSystemVersion</key>
<string>${MACOSX_DEPLOYMENT_TARGET}</string>
<key>LSMultipleInstancesProhibited</key>
<true/>
<key>NSPrincipalClass</key>
<string>NSApplication</string>
<key>NSSupportsAutomaticGraphicsSwitching</key>
<true/>
</dict>
</plist>
+50 -66
View File
@@ -1,12 +1,28 @@
#include "xrayprotocol.h" #include "xrayprotocol.h"
#include "core/defs.h"
#include "core/networkUtilities.h"
#include <QCryptographicHash> #include <QCryptographicHash>
#include <QJsonDocument> #include <QJsonDocument>
#include <QJsonObject> #include <QJsonObject>
#include <QNetworkInterface> #include <QNetworkInterface>
#include <QtCore/qcontainerfwd.h>
#include <QtCore/qdebug.h>
#include <QtCore/qlogging.h>
#include <QtCore/qobject.h>
#include <arpa/inet.h>
#include <cerrno>
#include <cstddef>
#include <cstdint>
#include <cstring>
#include <ifaddrs.h>
#include <net/if.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <sys/socket.h>
#include "core/networkUtilities.h" #include "core/ipcclient.h"
#include "utilities.h" #include "libxray.h"
XrayProtocol::XrayProtocol(const QJsonObject &configuration, QObject *parent) : VpnProtocol(configuration, parent) XrayProtocol::XrayProtocol(const QJsonObject &configuration, QObject *parent) : VpnProtocol(configuration, parent)
{ {
@@ -15,6 +31,9 @@ XrayProtocol::XrayProtocol(const QJsonObject &configuration, QObject *parent) :
m_vpnGateway = amnezia::protocols::xray::defaultLocalAddr; m_vpnGateway = amnezia::protocols::xray::defaultLocalAddr;
m_vpnLocalAddress = amnezia::protocols::xray::defaultLocalAddr; m_vpnLocalAddress = amnezia::protocols::xray::defaultLocalAddr;
m_t2sProcess = IpcClient::InterfaceTun2Socks(); m_t2sProcess = IpcClient::InterfaceTun2Socks();
amnezia_xray_setloghandler(&XrayProtocol::ctxLogHandler, this);
amnezia_xray_setsockcallback(&XrayProtocol::ctxSockCallback, this);
} }
XrayProtocol::~XrayProtocol() XrayProtocol::~XrayProtocol()
@@ -25,61 +44,20 @@ XrayProtocol::~XrayProtocol()
ErrorCode XrayProtocol::start() ErrorCode XrayProtocol::start()
{ {
qDebug().noquote() << "XrayProtocol xrayExecPath():" << xrayExecPath(); qDebug() << "XrayProtocol::start()";
if (!QFileInfo::exists(xrayExecPath())) { auto cfg = QJsonDocument(m_xrayConfig).toJson().toStdString();
setLastError(ErrorCode::XrayExecutableMissing); if (auto err = amnezia_xray_configure(cfg.data()); err != 0) {
return lastError(); return ErrorCode::InternalError;
} }
#ifdef QT_DEBUG if (auto err = amnezia_xray_start(); err != 0) {
m_xrayCfgFile.setAutoRemove(false); return ErrorCode::NotImplementedError;
#endif
m_xrayCfgFile.open();
QString config = QJsonDocument(m_xrayConfig).toJson();
config.replace(m_remoteHost, m_remoteAddress);
m_xrayCfgFile.write(config.toUtf8());
m_xrayCfgFile.close();
QStringList args = QStringList() << "-c" << m_xrayCfgFile.fileName() << "-format=json";
qDebug().noquote() << "XrayProtocol::start()" << xrayExecPath() << args.join(" ");
m_xrayProcess.setProcessChannelMode(QProcess::MergedChannels);
m_xrayProcess.setProgram(xrayExecPath());
if (Utils::processIsRunning(Utils::executable("xray", false))) {
qDebug().noquote() << "kill previos xray";
Utils::killProcessByName(Utils::executable("xray", false));
} }
m_xrayProcess.setArguments(args); setConnectionState(Vpn::ConnectionState::Connecting);
connect(&m_xrayProcess, &QProcess::readyReadStandardOutput, this, [this]() { return startTun2Sock();
#ifdef QT_DEBUG
qDebug().noquote() << "xray:" << m_xrayProcess.readAllStandardOutput();
#endif
});
connect(&m_xrayProcess, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished), this,
[this](int exitCode, QProcess::ExitStatus exitStatus) {
qDebug().noquote() << "XrayProtocol finished, exitCode, exitStatus" << exitCode << exitStatus;
setConnectionState(Vpn::ConnectionState::Disconnected);
if ((exitStatus != QProcess::NormalExit) || (exitCode != 0)) {
emit protocolError(amnezia::ErrorCode::XrayExecutableCrashed);
emit setConnectionState(Vpn::ConnectionState::Error);
}
});
m_xrayProcess.start();
m_xrayProcess.waitForStarted();
if (m_xrayProcess.state() == QProcess::ProcessState::Running) {
setConnectionState(Vpn::ConnectionState::Connecting);
QThread::msleep(1000);
return startTun2Sock();
} else
return ErrorCode::XrayExecutableMissing;
} }
ErrorCode XrayProtocol::startTun2Sock() ErrorCode XrayProtocol::startTun2Sock()
@@ -126,9 +104,7 @@ ErrorCode XrayProtocol::startTun2Sock()
} }
#endif #endif
if (m_routeMode == Settings::RouteMode::VpnAllSites) { if (m_routeMode == Settings::RouteMode::VpnAllSites) {
IpcClient::Interface()->routeAddList(m_vpnGateway, QStringList() << "0.0.0.0/1"); IpcClient::Interface()->routeAddList(m_vpnGateway, QStringList() << "1.0.0.0/8" << "2.0.0.0/7" << "4.0.0.0/6" << "8.0.0.0/5" << "16.0.0.0/4" << "32.0.0.0/3" << "64.0.0.0/2" << "128.0.0.0/1");
IpcClient::Interface()->routeAddList(m_vpnGateway, QStringList() << "128.0.0.0/1");
IpcClient::Interface()->routeAddList(m_routeGateway, QStringList() << m_remoteAddress);
} }
IpcClient::Interface()->StopRoutingIpv6(); IpcClient::Interface()->StopRoutingIpv6();
#ifdef Q_OS_WIN #ifdef Q_OS_WIN
@@ -171,9 +147,9 @@ void XrayProtocol::stop()
IpcClient::Interface()->StartRoutingIpv6(); IpcClient::Interface()->StartRoutingIpv6();
#endif #endif
qDebug() << "XrayProtocol::stop()"; qDebug() << "XrayProtocol::stop()";
m_xrayProcess.disconnect();
m_xrayProcess.kill(); amnezia_xray_stop();
m_xrayProcess.waitForFinished(3000);
if (m_t2sProcess) { if (m_t2sProcess) {
m_t2sProcess->stop(); m_t2sProcess->stop();
} }
@@ -181,15 +157,6 @@ void XrayProtocol::stop()
setConnectionState(Vpn::ConnectionState::Disconnected); setConnectionState(Vpn::ConnectionState::Disconnected);
} }
QString XrayProtocol::xrayExecPath()
{
#ifdef Q_OS_WIN
return Utils::executable(QString("xray/xray"), true);
#else
return Utils::executable(QString("xray"), true);
#endif
}
void XrayProtocol::readXrayConfiguration(const QJsonObject &configuration) void XrayProtocol::readXrayConfiguration(const QJsonObject &configuration)
{ {
m_configData = configuration; m_configData = configuration;
@@ -205,3 +172,20 @@ void XrayProtocol::readXrayConfiguration(const QJsonObject &configuration)
m_primaryDNS = configuration.value(amnezia::config_key::dns1).toString(); m_primaryDNS = configuration.value(amnezia::config_key::dns1).toString();
m_secondaryDNS = configuration.value(amnezia::config_key::dns2).toString(); m_secondaryDNS = configuration.value(amnezia::config_key::dns2).toString();
} }
void XrayProtocol::sockCallback(uintptr_t fd)
{
// #ifdef Q_OS_DARWIN
// const int iface = if_nametoindex("en0");
// if (iface > 0)
// {
// setsockopt(fd, IPPROTO_IP, IP_BOUND_IF, &iface, sizeof(iface));
// setsockopt(fd, IPPROTO_IPV6, IPV6_BOUND_IF, &iface, sizeof(iface));
// }
// #endif
}
void XrayProtocol::logHandler(char* str)
{
}
+12 -5
View File
@@ -3,9 +3,10 @@
#include "QProcess" #include "QProcess"
#include "containers/containers_defs.h" #include "protocols/vpnprotocol.h"
#include "openvpnprotocol.h" #include "rep_ipc_process_tun2socks_replica.h"
#include "settings.h" #include "settings.h"
#include <cstdint>
class XrayProtocol : public VpnProtocol class XrayProtocol : public VpnProtocol
{ {
@@ -24,10 +25,16 @@ protected:
QJsonObject m_xrayConfig; QJsonObject m_xrayConfig;
private: private:
static QString xrayExecPath(); static void ctxSockCallback(uintptr_t fd, void* ctx) {
static QString tun2SocksExecPath(); reinterpret_cast<XrayProtocol*>(ctx)->sockCallback(fd);
}
static void ctxLogHandler(char* str, void* ctx) {
reinterpret_cast<XrayProtocol*>(ctx)->logHandler(str);
}
void sockCallback(uintptr_t fd);
void logHandler(char* str);
private:
int m_localPort; int m_localPort;
QString m_remoteHost; QString m_remoteHost;
QString m_remoteAddress; QString m_remoteAddress;
+1 -3
View File
@@ -127,7 +127,7 @@
<file>ui/qml/Components/SelectLanguageDrawer.qml</file> <file>ui/qml/Components/SelectLanguageDrawer.qml</file>
<file>ui/qml/Components/ServersListView.qml</file> <file>ui/qml/Components/ServersListView.qml</file>
<file>ui/qml/Components/SettingsContainersListView.qml</file> <file>ui/qml/Components/SettingsContainersListView.qml</file>
<file>ui/qml/Components/ShareConnectionDrawer.qml</file>
<file>ui/qml/Components/TransportProtoSelector.qml</file> <file>ui/qml/Components/TransportProtoSelector.qml</file>
<file>ui/qml/Components/AddSitePanel.qml</file> <file>ui/qml/Components/AddSitePanel.qml</file>
<file>ui/qml/Config/GlobalConfig.qml</file> <file>ui/qml/Config/GlobalConfig.qml</file>
@@ -228,7 +228,6 @@
<file>ui/qml/Pages2/PageSetupWizardViewConfig.qml</file> <file>ui/qml/Pages2/PageSetupWizardViewConfig.qml</file>
<file>ui/qml/Pages2/PageShare.qml</file> <file>ui/qml/Pages2/PageShare.qml</file>
<file>ui/qml/Pages2/PageShareFullAccess.qml</file> <file>ui/qml/Pages2/PageShareFullAccess.qml</file>
<file>ui/qml/Pages2/PageShareConnection.qml</file>
<file>ui/qml/Pages2/PageStart.qml</file> <file>ui/qml/Pages2/PageStart.qml</file>
<file>ui/qml/Components/RenameServerDrawer.qml</file> <file>ui/qml/Components/RenameServerDrawer.qml</file>
<file>ui/qml/Controls2/ListViewType.qml</file> <file>ui/qml/Controls2/ListViewType.qml</file>
@@ -241,7 +240,6 @@
<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> <file>ui/qml/Components/AwgTextField.qml</file>
<file>ui/qml/Pages2/PageSettingsApiSubscriptionKey.qml</file>
</qresource> </qresource>
<qresource prefix="/countriesFlags"> <qresource prefix="/countriesFlags">
<file>images/flagKit/ZW.svg</file> <file>images/flagKit/ZW.svg</file>
+1 -1
View File
@@ -1,7 +1,7 @@
FROM alpine:3.15 FROM alpine:3.15
LABEL maintainer="AmneziaVPN" LABEL maintainer="AmneziaVPN"
ARG XRAY_RELEASE="v25.8.3" ARG XRAY_RELEASE="v1.8.6"
RUN apk add --no-cache curl unzip bash openssl netcat-openbsd dumb-init rng-tools xz RUN apk add --no-cache curl unzip bash openssl netcat-openbsd dumb-init rng-tools xz
RUN apk --update upgrade --no-cache RUN apk --update upgrade --no-cache
File diff suppressed because it is too large Load Diff
@@ -248,10 +248,10 @@ bool ApiConfigsController::exportNativeConfig(const QString &serverCountryCode,
apiConfigObject.value(configKey::userCountryCode).toString(), apiConfigObject.value(configKey::userCountryCode).toString(),
serverCountryCode, serverCountryCode,
apiConfigObject.value(configKey::serviceType).toString(), apiConfigObject.value(configKey::serviceType).toString(),
configKey::awg, // apiConfigObject.value(configKey::serviceProtocol).toString(), m_apiServicesModel->getSelectedServiceProtocol(),
serverConfigObject.value(configKey::authData).toObject() }; serverConfigObject.value(configKey::authData).toObject() };
QString protocol = gatewayRequestData.serviceProtocol; QString protocol = apiConfigObject.value(configKey::serviceProtocol).toString();
ProtocolData protocolData = generateProtocolData(protocol); ProtocolData protocolData = generateProtocolData(protocol);
QJsonObject apiPayload = gatewayRequestData.toJsonObject(); QJsonObject apiPayload = gatewayRequestData.toJsonObject();
@@ -283,7 +283,7 @@ bool ApiConfigsController::revokeNativeConfig(const QString &serverCountryCode)
apiConfigObject.value(configKey::userCountryCode).toString(), apiConfigObject.value(configKey::userCountryCode).toString(),
serverCountryCode, serverCountryCode,
apiConfigObject.value(configKey::serviceType).toString(), apiConfigObject.value(configKey::serviceType).toString(),
configKey::awg, // apiConfigObject.value(configKey::serviceProtocol).toString(), m_apiServicesModel->getSelectedServiceProtocol(),
serverConfigObject.value(configKey::authData).toObject() }; serverConfigObject.value(configKey::authData).toObject() };
QJsonObject apiPayload = gatewayRequestData.toJsonObject(); QJsonObject apiPayload = gatewayRequestData.toJsonObject();
+5 -11
View File
@@ -4,12 +4,6 @@
#include <QQmlApplicationEngine> #include <QQmlApplicationEngine>
#include <QQuickWindow> #include <QQuickWindow>
#include "logger.h"
namespace {
Logger logger("FocusController");
}
FocusController::FocusController(QQmlApplicationEngine *engine, QObject *parent) FocusController::FocusController(QQmlApplicationEngine *engine, QObject *parent)
: QObject { parent }, : QObject { parent },
m_engine { engine }, m_engine { engine },
@@ -91,7 +85,7 @@ void FocusController::dropRootObject(QObject *object)
dropListView(); dropListView();
setFocusOnDefaultItem(); setFocusOnDefaultItem();
} else { } else {
logger.warning() << "TRY TO DROP WRONG ROOT OBJECT: " << m_rootObjects.top() << " SHOULD BE: " << object; qWarning() << "===>> TRY TO DROP WRONG ROOT OBJECT: " << m_rootObjects.top() << " SHOULD BE: " << object;
} }
} }
@@ -107,7 +101,7 @@ void FocusController::reload(Direction direction)
QObject *rootObject = (m_rootObjects.empty() ? m_engine->rootObjects().value(0) : m_rootObjects.top()); QObject *rootObject = (m_rootObjects.empty() ? m_engine->rootObjects().value(0) : m_rootObjects.top());
if (!rootObject) { if (!rootObject) {
logger.error() << "No ROOT OBJECT found!"; qCritical() << "No ROOT OBJECT found!";
resetRootObject(); resetRootObject();
dropListView(); dropListView();
return; return;
@@ -119,7 +113,7 @@ void FocusController::reload(Direction direction)
direction == Direction::Forward ? FocusControl::isLess : FocusControl::isMore); direction == Direction::Forward ? FocusControl::isLess : FocusControl::isMore);
if (m_focusChain.empty()) { if (m_focusChain.empty()) {
logger.warning() << "Focus chain is empty!"; qWarning() << "Focus chain is empty!";
resetRootObject(); resetRootObject();
dropListView(); dropListView();
return; return;
@@ -137,7 +131,7 @@ void FocusController::nextItem(Direction direction)
} }
if (m_focusChain.empty()) { if (m_focusChain.empty()) {
logger.warning() << "There are no items to navigate"; qWarning() << "There are no items to navigate";
setFocusOnDefaultItem(); setFocusOnDefaultItem();
return; return;
} }
@@ -155,7 +149,7 @@ void FocusController::nextItem(Direction direction)
const auto focusedItem = qobject_cast<QQuickItem *>(m_focusChain.at(focusedItemIndex)); const auto focusedItem = qobject_cast<QQuickItem *>(m_focusChain.at(focusedItemIndex));
if (focusedItem == nullptr) { if (focusedItem == nullptr) {
logger.warning() << "Failed to get item to focus on. Setting focus on default"; qWarning() << "Failed to get item to focus on. Setting focus on default";
setFocusOnDefaultItem(); setFocusOnDefaultItem();
return; return;
} }
+2 -97
View File
@@ -403,19 +403,9 @@ ErrorCode InstallController::getAlreadyInstalledContainers(const ServerCredentia
QJsonObject config; QJsonObject config;
Proto mainProto = ContainerProps::defaultProtocol(container); Proto mainProto = ContainerProps::defaultProtocol(container);
const auto &protocols = ContainerProps::protocolsForContainer(container); const auto &protocols = ContainerProps::protocolsForContainer(container);
for (const auto &protocol : protocols) { for (const auto &protocol : protocols) {
QJsonObject containerConfig; QJsonObject containerConfig;
if (protocol == mainProto) {
// for Multiprotocols (OpenVPN over SS, OpenVPN over Cloak)
bool shouldProcessProtocol = false;
if (container == DockerContainer::ShadowSocks || container == DockerContainer::Cloak) {
shouldProcessProtocol = true;
} else {
shouldProcessProtocol = (protocol == mainProto);
}
if (shouldProcessProtocol) {
containerConfig.insert(config_key::port, port); containerConfig.insert(config_key::port, port);
containerConfig.insert(config_key::transport_proto, transportProto); containerConfig.insert(config_key::transport_proto, transportProto);
@@ -560,97 +550,14 @@ ErrorCode InstallController::getAlreadyInstalledContainers(const ServerCredentia
qDebug() << siteName; qDebug() << siteName;
containerConfig.insert(config_key::site, siteName); containerConfig.insert(config_key::site, siteName);
} else if (protocol == Proto::OpenVpn) {
QString serverConfig = serverController->getTextFileFromContainer(container, credentials,
protocols::openvpn::serverConfigPath, errorCode);
QMap<QString, QString> serverConfigMap;
auto serverConfigLines = serverConfig.split("\n");
for (auto &line : serverConfigLines) {
auto trimmedLine = line.trimmed();
if (trimmedLine.startsWith("#") || trimmedLine.isEmpty()) {
continue;
} else {
QStringList parts = trimmedLine.split(" ");
if (parts.count() >= 2) {
QString key = parts[0];
QString value = parts.mid(1).join(" ");
serverConfigMap.insert(key, value);
}
}
}
QString serverValue = serverConfigMap.value("server");
if (!serverValue.isEmpty()) {
QStringList serverParts = serverValue.split(" ");
if (serverParts.count() >= 1) {
containerConfig[config_key::subnet_address] = serverParts[0];
}
}
bool ncpDisable = serverConfig.contains("ncp-disable");
containerConfig[config_key::ncp_disable] = ncpDisable;
bool tlsAuth = serverConfig.contains("tls-auth");
containerConfig[config_key::tls_auth] = tlsAuth;
bool blockOutsideDns = serverConfig.contains("block-outside-dns");
containerConfig[config_key::block_outside_dns] = blockOutsideDns;
QString cipher = serverConfigMap.value("cipher");
if (!cipher.isEmpty()) {
containerConfig[config_key::cipher] = cipher;
}
QString hash = serverConfigMap.value("auth");
if (!hash.isEmpty()) {
containerConfig[config_key::hash] = hash;
}
} else if (protocol == Proto::Cloak) {
QString cloakConfig = serverController->getTextFileFromContainer(container, credentials,
"/opt/amnezia/cloak/ck-config.json", errorCode);
QJsonDocument doc = QJsonDocument::fromJson(cloakConfig.toUtf8());
if (!doc.isNull() && doc.isObject()) {
QJsonObject cloakConfigObj = doc.object();
QString site = cloakConfigObj.value("RedirAddr").toString();
if (!site.isEmpty()) {
containerConfig[config_key::site] = site;
}
} else {
qDebug() << "Failed to parse main loop Cloak JSON config";
}
} else if (protocol == Proto::ShadowSocks) {
QString shadowsocksConfig = serverController->getTextFileFromContainer(container, credentials,
"/opt/amnezia/shadowsocks/ss-config.json", errorCode);
QJsonDocument doc = QJsonDocument::fromJson(shadowsocksConfig.toUtf8());
if (!doc.isNull() && doc.isObject()) {
QJsonObject ssConfigObj = doc.object();
QString cipher = ssConfigObj.value("method").toString();
if (!cipher.isEmpty()) {
containerConfig[config_key::cipher] = cipher;
}
} else {
qDebug() << "Failed to parse main loop Shadowsocks JSON config";
}
} }
config.insert(config_key::container, ContainerProps::containerToString(container)); config.insert(config_key::container, ContainerProps::containerToString(container));
} }
if (shouldProcessProtocol) { config.insert(ProtocolProps::protoToString(protocol), containerConfig);
config.insert(ProtocolProps::protoToString(protocol), containerConfig);
}
} }
installedContainers.insert(container, config); installedContainers.insert(container, config);
} }
const static QRegularExpression torOrDnsRegExp("(amnezia-(?:torwebsite|dns)).*?([0-9]*)/(udp|tcp).*"); const static QRegularExpression torOrDnsRegExp("(amnezia-(?:torwebsite|dns)).*?([0-9]*)/(udp|tcp).*");
QRegularExpressionMatch torOrDnsRegMatch = torOrDnsRegExp.match(containerInfo); QRegularExpressionMatch torOrDnsRegMatch = torOrDnsRegExp.match(containerInfo);
if (torOrDnsRegMatch.hasMatch()) { if (torOrDnsRegMatch.hasMatch()) {
@@ -814,8 +721,6 @@ void InstallController::clearCachedProfile(QSharedPointer<ServerController> serv
m_clientManagementModel->revokeClient(containerConfig, container, serverCredentials, serverIndex, serverController); m_clientManagementModel->revokeClient(containerConfig, container, serverCredentials, serverIndex, serverController);
emit cachedProfileCleared(tr("%1 cached profile cleared").arg(ContainerProps::containerHumanNames().value(container))); emit cachedProfileCleared(tr("%1 cached profile cleared").arg(ContainerProps::containerHumanNames().value(container)));
QJsonObject updatedConfig = m_settings->containerConfig(serverIndex, container);
emit profileCleared(updatedConfig);
} }
QRegularExpression InstallController::ipAddressPortRegExp() QRegularExpression InstallController::ipAddressPortRegExp()
@@ -83,8 +83,6 @@ signals:
void noInstalledContainers(); void noInstalledContainers();
void profileCleared(const QJsonObject &config);
private: private:
void installServer(const DockerContainer container, const QMap<DockerContainer, QJsonObject> &installedContainers, void installServer(const DockerContainer container, const QMap<DockerContainer, QJsonObject> &installedContainers,
const ServerCredentials &serverCredentials, const QSharedPointer<ServerController> &serverController, const ServerCredentials &serverCredentials, const QSharedPointer<ServerController> &serverController,
@@ -37,7 +37,7 @@ void ListViewFocusController::viewAtCurrentIndex() const
} }
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, 6)); // PositionMode (0 = Beginning; 1 = Center; 2 = End; 3 = Visible; 4 = Contain; 5 = SnapPosition) Q_ARG(int, 2)); // PositionMode (0 = Visible)
break; break;
} }
case Section::Footer: { case Section::Footer: {
-2
View File
@@ -38,7 +38,6 @@ namespace PageLoader
PageSettingsApiInstructions, PageSettingsApiInstructions,
PageSettingsApiNativeConfigs, PageSettingsApiNativeConfigs,
PageSettingsApiDevices, PageSettingsApiDevices,
PageSettingsApiSubscriptionKey,
PageSettingsKillSwitchExceptions, PageSettingsKillSwitchExceptions,
PageServiceSftpSettings, PageServiceSftpSettings,
@@ -72,7 +71,6 @@ namespace PageLoader
PageProtocolAwgClientSettings, PageProtocolAwgClientSettings,
PageShareFullAccess, PageShareFullAccess,
PageShareConnection,
PageDevMenu PageDevMenu
}; };
+4 -43
View File
@@ -35,23 +35,6 @@ SettingsController::SettingsController(const QSharedPointer<ServersModel> &serve
#endif #endif
} }
QString getPlatformName()
{
#if defined(Q_OS_WINDOWS)
return "Windows";
#elif defined(Q_OS_ANDROID)
return "Android";
#elif defined(Q_OS_LINUX)
return "Linux";
#elif defined(Q_OS_MACX)
return "MacOS";
#elif defined(Q_OS_IOS)
return "iOS";
#else
return "Unknown";
#endif
}
void SettingsController::toggleAmneziaDns(bool enable) void SettingsController::toggleAmneziaDns(bool enable)
{ {
m_settings->setUseAmneziaDns(enable); m_settings->setUseAmneziaDns(enable);
@@ -147,10 +130,7 @@ void SettingsController::backupAppConfig(const QString &fileName)
QJsonDocument doc = QJsonDocument::fromJson(data); QJsonDocument doc = QJsonDocument::fromJson(data);
QJsonObject config = doc.object(); QJsonObject config = doc.object();
config["AppPlatform"] = getPlatformName();
config["Conf/autoStart"] = Autostart::isAutostart(); config["Conf/autoStart"] = Autostart::isAutostart();
config["Conf/killSwitchEnabled"] = isKillSwitchEnabled();
config["Conf/strictKillSwitchEnabled"] = isStrictKillSwitchEnabled();
SystemController::saveFile(fileName, QJsonDocument(config).toJson()); SystemController::saveFile(fileName, QJsonDocument(config).toJson());
} }
@@ -175,41 +155,21 @@ void SettingsController::restoreAppConfigFromData(const QByteArray &data)
} }
toggleAutoStart(autoStart); toggleAutoStart(autoStart);
#endif #endif
m_serversModel->resetModel(); m_serversModel->resetModel();
m_languageModel->changeLanguage( m_languageModel->changeLanguage(
static_cast<LanguageSettings::AvailableLanguageEnum>(m_languageModel->getCurrentLanguageIndex())); static_cast<LanguageSettings::AvailableLanguageEnum>(m_languageModel->getCurrentLanguageIndex()));
#if defined(Q_OS_WINDOWS) || defined(Q_OS_ANDROID) #if defined(Q_OS_WINDOWS) || defined(Q_OS_ANDROID)
int appSplitTunnelingRouteMode = newConfigData.value("Conf/appsRouteMode").toInt(); int appSplitTunnelingRouteMode = newConfigData.value("Conf/appsRouteMode").toInt();
bool appSplittunnelingEnabled = newConfigData.value("Conf/appsSplitTunnelingEnabled").toString().toLower() == "true"; bool appSplittunnelingEnabled = newConfigData.value("Conf/appsSplitTunnelingEnabled").toBool();
m_appSplitTunnelingModel->setRouteMode(appSplitTunnelingRouteMode); m_appSplitTunnelingModel->setRouteMode(appSplitTunnelingRouteMode);
#if defined(Q_OS_WINDOWS)
m_appSplitTunnelingModel->setRouteMode(static_cast<int>(Settings::AppsRouteMode::VpnAllExceptApps));
#endif
if (newConfigData.contains("AppPlatform")) { //if backup is from a new version
if (newConfigData.value("AppPlatform").toString() != getPlatformName()) {
m_appSplitTunnelingModel->clearAppsList();
}
}
m_appSplitTunnelingModel->toggleSplitTunneling(appSplittunnelingEnabled); m_appSplitTunnelingModel->toggleSplitTunneling(appSplittunnelingEnabled);
#endif #endif
int siteSplitTunnelingRouteMode = newConfigData.value("Conf/routeMode").toInt(); int siteSplitTunnelingRouteMode = newConfigData.value("Conf/routeMode").toInt();
bool siteSplittunnelingEnabled = newConfigData.value("Conf/sitesSplitTunnelingEnabled").toString().toLower() == "true"; bool siteSplittunnelingEnabled = newConfigData.value("Conf/sitesSplitTunnelingEnabled").toBool();
m_sitesModel->setRouteMode(siteSplitTunnelingRouteMode); m_sitesModel->setRouteMode(siteSplitTunnelingRouteMode);
m_sitesModel->toggleSplitTunneling(siteSplittunnelingEnabled); m_sitesModel->toggleSplitTunneling(siteSplittunnelingEnabled);
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
m_settings->setAutoConnect(false);
m_settings->setStartMinimized(false);
m_settings->setKillSwitchEnabled(false);
m_settings->setStrictKillSwitchEnabled(false);
#endif
emit restoreBackupFinished(); emit restoreBackupFinished();
} else { } else {
emit changeSettingsErrorOccurred(tr("Backup file is corrupted")); emit changeSettingsErrorOccurred(tr("Backup file is corrupted"));
@@ -225,7 +185,8 @@ void SettingsController::clearSettings()
{ {
m_settings->clearSettings(); m_settings->clearSettings();
m_serversModel->resetModel(); m_serversModel->resetModel();
m_languageModel->changeLanguage(m_languageModel->getSystemLanguageEnum()); m_languageModel->changeLanguage(
static_cast<LanguageSettings::AvailableLanguageEnum>(m_languageModel->getCurrentLanguageIndex()));
m_sitesModel->setRouteMode(Settings::RouteMode::VpnOnlyForwardSites); m_sitesModel->setRouteMode(Settings::RouteMode::VpnOnlyForwardSites);
m_sitesModel->toggleSplitTunneling(false); m_sitesModel->toggleSplitTunneling(false);
@@ -136,8 +136,6 @@ private:
QString m_appVersion; QString m_appVersion;
QString getPlatform();
QDateTime m_loggingDisableDate; QDateTime m_loggingDisableDate;
bool m_isDevModeEnabled = false; bool m_isDevModeEnabled = false;
@@ -78,13 +78,6 @@ void SitesController::removeSite(int index)
emit finished(tr("Site removed: %1").arg(hostname)); emit finished(tr("Site removed: %1").arg(hostname));
} }
void SitesController::removeSites()
{
m_sitesModel->removeSites();
emit finished(tr("Site list cleared!"));
}
void SitesController::importSites(const QString &fileName, bool replaceExisting) void SitesController::importSites(const QString &fileName, bool replaceExisting)
{ {
QByteArray jsonData; QByteArray jsonData;
-1
View File
@@ -19,7 +19,6 @@ public slots:
void addSite(QString hostname); void addSite(QString hostname);
void removeSite(int index); void removeSite(int index);
void removeSites();
void importSites(const QString &fileName, bool replaceExisting); void importSites(const QString &fileName, bool replaceExisting);
void exportSites(const QString &fileName); void exportSites(const QString &fileName);
+1 -1
View File
@@ -112,7 +112,7 @@ QVariant ApiServicesModel::data(const QModelIndex &index, int role) const
} else { } else {
return tr("VPN will open only popular sites blocked in your region, such as Instagram, Facebook, Twitter and others. " return tr("VPN will open only popular sites blocked in your region, such as Instagram, Facebook, Twitter and others. "
"Other sites will be opened from your real IP address, " "Other sites will be opened from your real IP address, "
"<a href=\"%1\" style=\"color: #FBB26A;\">more details on the website.</a>"); "<a href=\"%1/free\" style=\"color: #FBB26A;\">more details on the website.</a>");
} }
} }
case PriceRole: { case PriceRole: {
+6 -13
View File
@@ -26,12 +26,12 @@ QVariant AppSplitTunnelingModel::data(const QModelIndex &index, int role) const
return QVariant(); return QVariant();
switch (role) { switch (role) {
case AppPathRole: { case AppPathRole: {
return m_apps.at(index.row()).appName; return m_apps.at(index.row()).appName;
} }
default: { default: {
return true; return true;
} }
} }
return QVariant(); return QVariant();
@@ -59,13 +59,6 @@ void AppSplitTunnelingModel::removeApp(QModelIndex index)
endRemoveRows(); endRemoveRows();
} }
void AppSplitTunnelingModel::clearAppsList() {
beginResetModel();
m_apps.clear();
m_settings->setVpnApps(m_currentRouteMode, m_apps);
endResetModel();
}
int AppSplitTunnelingModel::getRouteMode() int AppSplitTunnelingModel::getRouteMode()
{ {
return m_currentRouteMode; return m_currentRouteMode;
@@ -29,7 +29,6 @@ public:
public slots: public slots:
bool addApp(const InstalledAppInfo &appInfo); bool addApp(const InstalledAppInfo &appInfo);
void removeApp(QModelIndex index); void removeApp(QModelIndex index);
void clearAppsList();
int getRouteMode(); int getRouteMode();
void setRouteMode(int routeMode); void setRouteMode(int routeMode);
-27
View File
@@ -101,23 +101,6 @@ QString LanguageModel::getCurrentLanguageName()
return m_availableLanguages[getCurrentLanguageIndex()].name; return m_availableLanguages[getCurrentLanguageIndex()].name;
} }
LanguageSettings::AvailableLanguageEnum LanguageModel::getSystemLanguageEnum()
{
QLocale locale = QLocale::system();
switch (locale.language()) {
case QLocale::Russian: return LanguageSettings::AvailableLanguageEnum::Russian;
case QLocale::Chinese: return LanguageSettings::AvailableLanguageEnum::China_cn;
case QLocale::Ukrainian: return LanguageSettings::AvailableLanguageEnum::Ukrainian;
case QLocale::Persian: return LanguageSettings::AvailableLanguageEnum::Persian;
case QLocale::Arabic: return LanguageSettings::AvailableLanguageEnum::Arabic;
case QLocale::Burmese: return LanguageSettings::AvailableLanguageEnum::Burmese;
case QLocale::Urdu: return LanguageSettings::AvailableLanguageEnum::Urdu;
case QLocale::Hindi: return LanguageSettings::AvailableLanguageEnum::Hindi;
case QLocale::English: return LanguageSettings::AvailableLanguageEnum::English;
default: return LanguageSettings::AvailableLanguageEnum::English;
}
}
QString LanguageModel::getCurrentSiteUrl(const QString &path) QString LanguageModel::getCurrentSiteUrl(const QString &path)
{ {
auto language = static_cast<LanguageSettings::AvailableLanguageEnum>(getCurrentLanguageIndex()); auto language = static_cast<LanguageSettings::AvailableLanguageEnum>(getCurrentLanguageIndex());
@@ -127,13 +110,3 @@ QString LanguageModel::getCurrentSiteUrl(const QString &path)
default: return QString("https://amnezia.org") + (path.isEmpty() ? "" : (QString("/%1").arg(path))); default: return QString("https://amnezia.org") + (path.isEmpty() ? "" : (QString("/%1").arg(path)));
} }
} }
QString LanguageModel::getCurrentDocsUrl(const QString &path)
{
auto language = static_cast<LanguageSettings::AvailableLanguageEnum>(getCurrentLanguageIndex());
switch (language) {
case LanguageSettings::AvailableLanguageEnum::Russian:
return "https://storage.googleapis.com/amnezia/docs" + (path.isEmpty() ? "" : (QString("?m-path=/%1").arg(path)));
default: return QString("https://docs.amnezia.org") + (path.isEmpty() ? "" : (QString("/%1").arg(path)));
}
}
-2
View File
@@ -46,7 +46,6 @@ public:
}; };
LanguageModel(std::shared_ptr<Settings> settings, QObject *parent = nullptr); LanguageModel(std::shared_ptr<Settings> settings, QObject *parent = nullptr);
LanguageSettings::AvailableLanguageEnum getSystemLanguageEnum();
int rowCount(const QModelIndex &parent = QModelIndex()) const override; int rowCount(const QModelIndex &parent = QModelIndex()) const override;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
@@ -61,7 +60,6 @@ public slots:
int getLineHeightAppend(); int getLineHeightAppend();
QString getCurrentLanguageName(); QString getCurrentLanguageName();
QString getCurrentSiteUrl(const QString &path = ""); QString getCurrentSiteUrl(const QString &path = "");
QString getCurrentDocsUrl(const QString &path = "");
signals: signals:
void updateTranslations(const QLocale &locale); void updateTranslations(const QLocale &locale);
-10
View File
@@ -83,16 +83,6 @@ void SitesModel::removeSite(QModelIndex index)
endRemoveRows(); endRemoveRows();
} }
void SitesModel::removeSites()
{
beginResetModel();
m_settings->removeAllVpnSites(m_currentRouteMode);
fillSites();
endResetModel();
}
int SitesModel::getRouteMode() int SitesModel::getRouteMode()
{ {
return m_currentRouteMode; return m_currentRouteMode;
-1
View File
@@ -28,7 +28,6 @@ public slots:
bool addSite(const QString &hostname, const QString &ip); bool addSite(const QString &hostname, const QString &ip);
void addSites(const QMap<QString, QString> &sites, bool replaceExisting); void addSites(const QMap<QString, QString> &sites, bool replaceExisting);
void removeSite(QModelIndex index); void removeSite(QModelIndex index);
void removeSites();
int getRouteMode(); int getRouteMode();
void setRouteMode(int routeMode); void setRouteMode(int routeMode);
@@ -10,7 +10,8 @@ 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
@@ -20,6 +21,13 @@ ListViewType {
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
} }
@@ -47,8 +47,8 @@ DrawerType2 {
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()
} }
} }
@@ -57,7 +57,7 @@ DrawerType2 {
headerText: qsTr("Choose application") headerText: qsTr("Choose application")
} }
ListViewType { ListView {
id: listView id: listView
Layout.fillWidth: true Layout.fillWidth: true
@@ -66,6 +66,11 @@ 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
@@ -76,35 +81,44 @@ DrawerType2 {
} }
} }
ScrollBar.vertical: ScrollBarType {}
ButtonGroup { ButtonGroup {
id: buttonGroup id: buttonGroup
} }
delegate: ColumnLayout { delegate: Item {
width: listView.width implicitWidth: root.width
implicitHeight: delegateContent.implicitHeight
RowLayout { ColumnLayout {
CheckBoxType { id: delegateContent
Layout.fillWidth: true
text: appName anchors.fill: parent
checked: isAppSelected
onCheckedChanged: { RowLayout {
installedAppsModel.selectedStateChanged(proxyInstalledAppsModel.mapToSource(index), checked) CheckBoxType {
Layout.fillWidth: true
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
} }
} }
Image { DividerType {}
source: "image://installedAppImage/" + appIcon
sourceSize.width: 24
sourceSize.height: 24
Layout.rightMargin: 48
}
} }
DividerType {}
} }
} }
} }
@@ -49,7 +49,7 @@ DrawerType2 {
} }
} }
ListViewType { ListView {
id: listView id: listView
anchors.top: backButtonLayout.bottom anchors.top: backButtonLayout.bottom
@@ -57,8 +57,14 @@ 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 {
+8 -1
View File
@@ -15,7 +15,7 @@ import "../Controls2"
import "../Controls2/TextTypes" import "../Controls2/TextTypes"
import "../Config" import "../Config"
ListViewType { ListView {
id: root id: root
property int selectedIndex: ServersModel.defaultIndex property int selectedIndex: ServersModel.defaultIndex
@@ -28,6 +28,10 @@ ListViewType {
model: ServersModel model: ServersModel
ScrollBar.vertical: ScrollBarType {}
property bool isFocusable: true
Connections { Connections {
target: ServersModel target: ServersModel
function onDefaultServerIndexChanged(serverIndex) { function onDefaultServerIndexChanged(serverIndex) {
@@ -35,6 +39,9 @@ ListViewType {
} }
} }
clip: true
reuseItems: true
delegate: Item { delegate: Item {
id: menuContentDelegate id: menuContentDelegate
objectName: "menuContentDelegate" objectName: "menuContentDelegate"
@@ -13,64 +13,78 @@ import "../Controls2"
import "../Controls2/TextTypes" import "../Controls2/TextTypes"
ListViewType { ListView {
id: root id: root
anchors.fill: parent width: parent.width
height: root.contentItem.height
delegate: ColumnLayout { clip: true
width: root.width reuseItems: true
LabelWithButtonType { property bool isFocusable: false
Layout.fillWidth: true
text: name delegate: Item {
descriptionText: description implicitWidth: root.width
rightImageSource: isInstalled ? "qrc:/images/controls/chevron-right.svg" : "qrc:/images/controls/download.svg" implicitHeight: delegateContent.implicitHeight
clickedFunction: function() { ColumnLayout {
if (isInstalled) { id: delegateContent
var containerIndex = root.model.mapToSource(index)
ContainersModel.setProcessedContainerIndex(containerIndex)
if (serviceType !== ProtocolEnum.Other) { anchors.fill: parent
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)
return break
}
case ContainerEnum.Dns: {
PageController.goToPage(PageEnum.PageServiceDnsSettings)
break
}
default: {
ProtocolsModel.updateModel(config)
PageController.goToPage(PageEnum.PageSettingsServerProtocol)
}
} }
}
switch (containerIndex) { } else {
case ContainerEnum.Ipsec: { ContainersModel.setProcessedContainerIndex(root.model.mapToSource(index))
ProtocolsModel.updateModel(config) InstallController.setShouldCreateServer(false)
PageController.goToPage(PageEnum.PageProtocolRaw) PageController.goToPage(PageEnum.PageSetupWizardProtocolSettings)
break
}
case ContainerEnum.Dns: {
PageController.goToPage(PageEnum.PageServiceDnsSettings)
break
}
default: {
ProtocolsModel.updateModel(config)
PageController.goToPage(PageEnum.PageSettingsServerProtocol)
}
} }
}
} else { MouseArea {
ContainersModel.setProcessedContainerIndex(root.model.mapToSource(index)) anchors.fill: parent
InstallController.setShouldCreateServer(false) cursorShape: Qt.PointingHandCursor
PageController.goToPage(PageEnum.PageSetupWizardProtocolSettings) enabled: false
} }
} }
MouseArea { DividerType {}
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
enabled: false
}
} }
DividerType {}
} }
} }
@@ -0,0 +1,375 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import QtQuick.Dialogs
import QtCore
import SortFilterProxyModel 0.2
import PageEnum 1.0
import ContainerProps 1.0
import Style 1.0
import "./"
import "../Controls2"
import "../Controls2/TextTypes"
import "../Config"
import "../Components"
DrawerType2 {
id: root
property string headerText
property string configContentHeaderText
property string shareButtonText: qsTr("Share")
property string copyButtonText: qsTr("Copy")
property bool isSelfHostedConfig: true
property string configExtension: ".vpn"
property string configCaption: qsTr("Save AmneziaVPN config")
property string configFileName: "amnezia_config"
expandedHeight: parent.height * 0.9
onClosed: {
configExtension = ".vpn"
configCaption = qsTr("Save AmneziaVPN config")
configFileName = "amnezia_config"
}
expandedStateContent: Item {
implicitHeight: root.expandedHeight
Header2Type {
id: header
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
anchors.topMargin: 20
anchors.leftMargin: 16
anchors.rightMargin: 16
headerText: root.headerText
}
ListView {
id: listView
anchors.top: header.bottom
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.right: parent.right
property bool isFocusable: true
ScrollBar.vertical: ScrollBarType {}
model: 1
clip: true
reuseItems: true
header: ColumnLayout {
width: listView.width
BasicButtonType {
id: shareButton
Layout.fillWidth: true
Layout.topMargin: 16
Layout.leftMargin: 16
Layout.rightMargin: 16
text: root.shareButtonText
leftImageSource: "qrc:/images/controls/share-2.svg"
clickedFunc: function() {
var fileName = ""
if (GC.isMobile()) {
fileName = configFileName + configExtension
} else {
fileName = SystemController.getFileName(configCaption,
qsTr("Config files (*" + configExtension + ")"),
StandardPaths.standardLocations(StandardPaths.DocumentsLocation) + "/" + configFileName,
true,
configExtension)
}
if (fileName !== "") {
PageController.showBusyIndicator(true)
ExportController.exportConfig(fileName)
PageController.showBusyIndicator(false)
}
}
}
BasicButtonType {
id: copyConfigTextButton
Layout.fillWidth: true
Layout.topMargin: 8
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: root.copyButtonText
leftImageSource: "qrc:/images/controls/copy.svg"
Keys.onReturnPressed: { copyConfigTextButton.clicked() }
Keys.onEnterPressed: { copyConfigTextButton.clicked() }
}
BasicButtonType {
id: copyNativeConfigStringButton
Layout.fillWidth: true
Layout.topMargin: 8
Layout.leftMargin: 16
Layout.rightMargin: 16
visible: false
defaultColor: AmneziaStyle.color.transparent
hoveredColor: AmneziaStyle.color.translucentWhite
pressedColor: AmneziaStyle.color.sheerWhite
disabledColor: AmneziaStyle.color.mutedGray
textColor: AmneziaStyle.color.paleGray
borderWidth: 1
text: qsTr("Copy config string")
leftImageSource: "qrc:/images/controls/copy.svg"
KeyNavigation.tab: showSettingsButton
}
BasicButtonType {
id: showSettingsButton
Layout.fillWidth: true
Layout.topMargin: 24
Layout.leftMargin: 16
Layout.rightMargin: 16
visible: root.isSelfHostedConfig
defaultColor: AmneziaStyle.color.transparent
hoveredColor: AmneziaStyle.color.translucentWhite
pressedColor: AmneziaStyle.color.sheerWhite
disabledColor: AmneziaStyle.color.mutedGray
textColor: AmneziaStyle.color.paleGray
borderWidth: 1
text: qsTr("Show connection settings")
clickedFunc: function() {
configContentDrawer.openTriggered()
}
}
DrawerType2 {
id: configContentDrawer
parent: root.parent
anchors.fill: parent
expandedHeight: parent.height * 0.9
expandedStateContent: Item {
id: configContentContainer
implicitHeight: configContentDrawer.expandedHeight
Connections {
target: copyNativeConfigStringButton
function onClicked() {
nativeConfigString.selectAll()
nativeConfigString.copy()
nativeConfigString.select(0, 0)
PageController.showNotificationMessage(qsTr("Copied"))
}
}
Connections {
target: copyConfigTextButton
function onClicked() {
configText.selectAll()
configText.copy()
configText.select(0, 0)
PageController.showNotificationMessage(qsTr("Copied"))
header.forceActiveFocus()
}
}
BackButtonType {
id: backButton
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
anchors.topMargin: 16
backButtonFunction: function() { configContentDrawer.closeTriggered() }
}
FlickableType {
anchors.top: backButton.bottom
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: parent.bottom
contentHeight: configContent.implicitHeight + configContent.anchors.topMargin + configContent.anchors.bottomMargin
ColumnLayout {
id: configContent
anchors.fill: parent
anchors.rightMargin: 16
anchors.leftMargin: 16
Header2Type {
id: configContentHeader
Layout.fillWidth: true
Layout.topMargin: 16
headerText: root.configContentHeaderText
}
TextField {
id: nativeConfigString
visible: false
text: ExportController.nativeConfigString
onTextChanged: {
copyNativeConfigStringButton.visible = nativeConfigString.text !== ""
}
}
TextArea {
id: configText
Layout.fillWidth: true
Layout.topMargin: 16
Layout.bottomMargin: 16
padding: 0
leftPadding: 0
height: 24
readOnly: true
activeFocusOnTab: false
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: ExportController.config
wrapMode: Text.Wrap
background: Rectangle {
color: AmneziaStyle.color.transparent
}
}
}
}
}
}
}
delegate: ColumnLayout {
width: listView.width
property bool isQrCodeVisible: root.isSelfHostedConfig ? ExportController.qrCodesCount > 0 : ApiConfigsController.qrCodesCount > 0
Rectangle {
id: qrCodeContainer
Layout.fillWidth: true
Layout.preferredHeight: width
Layout.topMargin: 20
Layout.leftMargin: 16
Layout.rightMargin: 16
visible: isQrCodeVisible
color: "white"
Image {
anchors.fill: parent
smooth: false
source: root.isSelfHostedConfig ? (isQrCodeVisible ? ExportController.qrCodes[0] : "") :
(isQrCodeVisible ? ApiConfigsController.qrCodes[0] : "")
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()
}
Timer {
property int index: 0
interval: 1000
running: isQrCodeVisible
repeat: true
onTriggered: {
if (isQrCodeVisible) {
index++
let qrCodesCount = root.isSelfHostedConfig ? ExportController.qrCodesCount : ApiConfigsController.qrCodesCount
if (index >= qrCodesCount) {
index = 0
}
parent.source = root.isSelfHostedConfig ? ExportController.qrCodes[index] : ApiConfigsController.qrCodes[index]
}
}
}
Behavior on source {
PropertyAnimation { duration: 200 }
}
}
}
ParagraphTextType {
Layout.fillWidth: true
Layout.topMargin: 24
Layout.bottomMargin: 32
Layout.leftMargin: 16
Layout.rightMargin: 16
visible: isQrCodeVisible
horizontalAlignment: Text.AlignHCenter
text: qsTr("To read the QR code in the Amnezia app, select \"Add server\" → \"I have data to connect\" → \"QR code, key or settings file\"")
}
}
}
}
}
@@ -29,6 +29,8 @@ Button {
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
@@ -63,6 +65,14 @@ Button {
hoverEnabled: true hoverEnabled: true
onFocusChanged: {
if (root.activeFocus) {
if (root.parentFlickable) {
root.parentFlickable.ensureVisible(this)
}
}
}
background: Rectangle { background: Rectangle {
id: focusBorder id: focusBorder
@@ -27,6 +27,8 @@ Button {
property alias focusItem: rightImage property alias focusItem: rightImage
property FlickableType parentFlickable
hoverEnabled: true hoverEnabled: true
background: Rectangle { background: Rectangle {
@@ -42,6 +44,22 @@ Button {
} }
} }
function ensureVisible(item) {
if (item.activeFocus) {
if (root.parentFlickable) {
root.parentFlickable.ensureVisible(root)
}
}
}
onFocusChanged: {
ensureVisible(root)
}
focusItem.onFocusChanged: {
root.ensureVisible(focusItem)
}
contentItem: Item { contentItem: Item {
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
+7 -24
View File
@@ -34,30 +34,13 @@ CheckBox {
property string imageSource: "qrc:/images/controls/check.svg" property string imageSource: "qrc:/images/controls/check.svg"
property bool isFocusable: true property var parentFlickable
onFocusChanged: {
Keys.onTabPressed: { if (root.activeFocus) {
FocusController.nextKeyTabItem() if (root.parentFlickable) {
} root.parentFlickable.ensureVisible(root)
}
Keys.onBacktabPressed: { }
FocusController.previousKeyTabItem()
}
Keys.onUpPressed: {
FocusController.nextKeyUpItem()
}
Keys.onDownPressed: {
FocusController.nextKeyDownItem()
}
Keys.onLeftPressed: {
FocusController.nextKeyLeftItem()
}
Keys.onRightPressed: {
FocusController.nextKeyRightItem()
} }
hoverEnabled: enabled ? true : false hoverEnabled: enabled ? true : false
@@ -27,5 +27,6 @@ Flickable {
ScrollBar.vertical: ScrollBarType { ScrollBar.vertical: ScrollBarType {
id: scrollBar id: scrollBar
policy: fl.height >= fl.contentHeight ? ScrollBar.AlwaysOff : ScrollBar.AlwaysOn
} }
} }
@@ -9,7 +9,6 @@ import "TextTypes"
Item { Item {
id: root id: root
// property alias focusObjectName: eyeImage.objectName
property string text property string text
property int textMaximumLineCount: 2 property int textMaximumLineCount: 2
property int textElide: Qt.ElideRight property int textElide: Qt.ElideRight
@@ -26,6 +25,7 @@ Item {
property alias rightButton: rightImage property alias rightButton: rightImage
property alias eyeButton: eyeImage property alias eyeButton: eyeImage
property FlickableType parentFlickable
property string textColor: AmneziaStyle.color.paleGray property string textColor: AmneziaStyle.color.paleGray
property string textDisabledColor: AmneziaStyle.color.mutedGray property string textDisabledColor: AmneziaStyle.color.mutedGray
@@ -70,6 +70,25 @@ Item {
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: {
if (root.activeFocus) {
if (root.parentFlickable) {
root.parentFlickable.ensureVisible(root)
}
}
}
Connections {
target: rightImage
function onFocusChanged() {
if (rightImage.activeFocus) {
if (root.parentFlickable) {
root.parentFlickable.ensureVisible(root)
}
}
}
}
MouseArea { MouseArea {
anchors.fill: parent anchors.fill: parent
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
+25 -8
View File
@@ -6,16 +6,33 @@ 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;
}
} }
@@ -6,7 +6,7 @@ import Style 1.0
import "TextTypes" import "TextTypes"
ListViewType { ListView {
id: root id: root
property var rootWidth property var rootWidth
@@ -25,6 +25,13 @@ ListViewType {
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
} }
+1 -1
View File
@@ -7,5 +7,5 @@ import "../Controls2"
ScrollBar { ScrollBar {
id: root id: root
policy: ScrollBar.AsNeeded policy: parent.height >= parent.contentHeight ? ScrollBar.AlwaysOff : ScrollBar.AlwaysOn
} }
+10
View File
@@ -64,6 +64,16 @@ Switch {
hoverEnabled: enabled ? true : false hoverEnabled: enabled ? true : false
focusPolicy: Qt.TabFocus focusPolicy: Qt.TabFocus
property FlickableType parentFlickable: null
onFocusChanged: {
if (root.activeFocus) {
if (root.parentFlickable) {
root.parentFlickable.ensureVisible(root)
}
}
}
indicator: Rectangle { indicator: Rectangle {
id: switcher id: switcher
+9 -26
View File
@@ -21,6 +21,15 @@ Rectangle {
border.color: getBorderColor(borderNormalColor) border.color: getBorderColor(borderNormalColor)
radius: 16 radius: 16
property FlickableType parentFlickable: null
onFocusChanged: {
if (root.activeFocus) {
if (root.parentFlickable) {
root.parentFlickable.ensureVisible(root)
}
}
}
MouseArea { MouseArea {
id: parentMouse id: parentMouse
anchors.fill: parent anchors.fill: parent
@@ -45,32 +54,6 @@ Rectangle {
anchors.topMargin: 16 anchors.topMargin: 16
anchors.bottomMargin: 16 anchors.bottomMargin: 16
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()
}
color: AmneziaStyle.color.paleGray color: AmneziaStyle.color.paleGray
selectionColor: AmneziaStyle.color.richBrown selectionColor: AmneziaStyle.color.richBrown
selectedTextColor: AmneziaStyle.color.paleGray selectedTextColor: AmneziaStyle.color.paleGray
@@ -31,6 +31,15 @@ Rectangle {
border.color: getBorderColor(borderNormalColor) border.color: getBorderColor(borderNormalColor)
radius: 16 radius: 16
property FlickableType parentFlickable: null
onFocusChanged: {
if (root.activeFocus) {
if (root.parentFlickable) {
root.parentFlickable.ensureVisible(root)
}
}
}
MouseArea { MouseArea {
id: parentMouse id: parentMouse
anchors.fill: parent anchors.fill: parent
@@ -37,6 +37,19 @@ Item {
implicitWidth: content.implicitWidth implicitWidth: content.implicitWidth
implicitHeight: content.implicitHeight implicitHeight: content.implicitHeight
property FlickableType parentFlickable
Connections {
target: textField
function onFocusChanged() {
if (textField.activeFocus) {
if (root.parentFlickable) {
root.parentFlickable.ensureVisible(root)
}
}
}
}
ColumnLayout { ColumnLayout {
id: content id: content
anchors.fill: parent anchors.fill: parent
+48 -38
View File
@@ -20,9 +20,7 @@ PageType {
SortFilterProxyModel { SortFilterProxyModel {
id: proxyServersModel id: proxyServersModel
sourceModel: ServersModel sourceModel: ServersModel
filters: [ filters: [
ValueFilter { ValueFilter {
roleName: "isCurrentlyProcessed" roleName: "isCurrentlyProcessed"
@@ -31,55 +29,67 @@ PageType {
] ]
} }
ListViewType { FlickableType {
id: listView id: fl
anchors.fill: parent anchors.fill: parent
contentHeight: content.height
spacing: 16 Column {
id: content
model: proxyServersModel anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
delegate: ColumnLayout { spacing: 16
width: listView.width
BaseHeaderType { Repeater {
Layout.fillWidth: true model: proxyServersModel
Layout.topMargin: 20 delegate: Item {
Layout.leftMargin: 16 implicitWidth: parent.width
Layout.rightMargin: 16 implicitHeight: delegateContent.implicitHeight
headerText: qsTr("Removing services from %1").arg(name) ColumnLayout {
} id: delegateContent
ProgressBarType { anchors.fill: parent
id: progressBar anchors.rightMargin: 16
anchors.leftMargin: 16
Layout.fillWidth: true BaseHeaderType {
Layout.topMargin: 32 Layout.fillWidth: true
Layout.leftMargin: 16 Layout.topMargin: 20
Layout.rightMargin: 16
Timer { headerText: qsTr("Removing services from %1").arg(name)
id: timer }
interval: 300 ProgressBarType {
repeat: true id: progressBar
running: true
onTriggered: { Layout.fillWidth: true
progressBar.value += 0.003 Layout.topMargin: 32
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")
}
} }
} }
} }
+13 -3
View File
@@ -25,17 +25,23 @@ PageType {
anchors.topMargin: 20 anchors.topMargin: 20
} }
ListViewType { ListView {
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
@@ -44,14 +50,16 @@ PageType {
} }
} }
model: 1 // fake model to force the ListView to be created without a model model: 1
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
@@ -79,6 +87,8 @@ 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
+6 -9
View File
@@ -101,8 +101,8 @@ PageType {
visible: isLoggingEnabled ? true : false visible: isLoggingEnabled ? true : false
text: qsTr("Logging enabled") text: qsTr("Logging enabled")
Keys.onEnterPressed: this.clicked() Keys.onEnterPressed: loggingButton.clicked()
Keys.onReturnPressed: this.clicked() Keys.onReturnPressed: loggingButton.clicked()
onClicked: { onClicked: {
PageController.goToPage(PageEnum.PageSettingsLogging) PageController.goToPage(PageEnum.PageSettingsLogging)
@@ -147,8 +147,8 @@ PageType {
leftImageColor: "" leftImageColor: ""
rightImageSource: "qrc:/images/controls/chevron-down.svg" rightImageSource: "qrc:/images/controls/chevron-down.svg"
Keys.onEnterPressed: this.clicked() Keys.onEnterPressed: splitTunnelingButton.clicked()
Keys.onReturnPressed: this.clicked() Keys.onReturnPressed: splitTunnelingButton.clicked()
onClicked: { onClicked: {
homeSplitTunnelingDrawer.openTriggered() homeSplitTunnelingDrawer.openTriggered()
@@ -276,8 +276,8 @@ PageType {
topPadding: 4 topPadding: 4
bottomPadding: 3 bottomPadding: 3
Keys.onEnterPressed: this.clicked() Keys.onEnterPressed: collapsedButtonChevron.clicked()
Keys.onReturnPressed: this.clicked() Keys.onReturnPressed: collapsedButtonChevron.clicked()
onClicked: { onClicked: {
if (drawer.isCollapsedStateActive()) { if (drawer.isCollapsedStateActive()) {
@@ -320,9 +320,6 @@ PageType {
rightImageSource: hoverEnabled ? "qrc:/images/controls/chevron-down.svg" : "" rightImageSource: hoverEnabled ? "qrc:/images/controls/chevron-down.svg" : ""
Keys.onEnterPressed: this.clicked()
Keys.onReturnPressed: this.clicked()
onClicked: { onClicked: {
ServersModel.processedIndex = ServersModel.defaultIndex ServersModel.processedIndex = ServersModel.defaultIndex
@@ -16,397 +16,349 @@ import "../Components"
PageType { PageType {
id: root id: root
BackButtonType { ColumnLayout {
id: backButton 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
onActiveFocusChanged: { BackButtonType {
if(backButton.enabled && backButton.activeFocus) { id: backButton
listView.positionViewAtBeginning()
}
} }
} }
ListViewType { ListView {
id: listView id: listview
anchors.top: backButton.bottom anchors.top: backButtonLayout.bottom
anchors.bottom: saveButton.top anchors.bottom: saveButton.top
anchors.right: parent.right
anchors.left: parent.left
header: ColumnLayout { width: parent.width
width: listView.width
BaseHeaderType { clip: true
Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 16
headerText: qsTr("AmneziaWG settings") 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: ColumnLayout { delegate: Item {
width: listView.width id: delegateItem
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 === ""
spacing: 0 ColumnLayout {
id: col
TextFieldWithHeaderType { anchors.top: parent.top
id: mtuTextField anchors.left: parent.left
anchors.right: parent.right
Layout.fillWidth: true anchors.leftMargin: 16
Layout.topMargin: 40 anchors.rightMargin: 16
Layout.leftMargin: 16
Layout.rightMargin: 16
headerText: qsTr("MTU") spacing: 0
textField.text: clientMtu
textField.validator: IntValidator { bottom: 576; top: 65535 }
textField.onEditingFinished: { BaseHeaderType {
if (textField.text !== clientMtu) { Layout.fillWidth: true
clientMtu = textField.text
headerText: qsTr("AmneziaWG 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: junkPacketCountTextField.textField
}
AwgTextField {
id: junkPacketCountTextField
headerText: "Jc - Junk packet count"
textField.text: clientJunkPacketCount
textField.onEditingFinished: {
if (textField.text !== clientJunkPacketCount) {
clientJunkPacketCount = textField.text
}
}
KeyNavigation.tab: junkPacketMinSizeTextField.textField
}
AwgTextField {
id: junkPacketMinSizeTextField
headerText: "Jmin - Junk packet minimum size"
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
}
} }
} }
checkEmptyText: true
}
AwgTextField { AwgTextField {
id: junkPacketCountTextField id: specialJunk1TextField
headerText: qsTr("I1 - First special junk packet")
textField.text: clientSpecialJunk1
textField.validator: null
checkEmptyText: false
Layout.leftMargin: 16 textField.onEditingFinished: {
Layout.rightMargin: 16 if (textField.text !== clientSpecialJunk1) {
clientSpecialJunk1 = textField.text
headerText: "Jc - Junk packet count" }
textField.text: clientJunkPacketCount
textField.onEditingFinished: {
if (textField.text !== clientJunkPacketCount) {
clientJunkPacketCount = textField.text
} }
} }
}
AwgTextField { AwgTextField {
id: junkPacketMinSizeTextField id: specialJunk2TextField
headerText: qsTr("I2 - Second special junk packet")
textField.text: clientSpecialJunk2
textField.validator: null
checkEmptyText: false
Layout.leftMargin: 16 textField.onEditingFinished: {
Layout.rightMargin: 16 if (textField.text !== clientSpecialJunk2) {
clientSpecialJunk2 = textField.text
headerText: "Jmin - Junk packet minimum size" }
textField.text: clientJunkPacketMinSize
textField.onEditingFinished: {
if (textField.text !== clientJunkPacketMinSize) {
clientJunkPacketMinSize = textField.text
} }
} }
}
AwgTextField { AwgTextField {
id: junkPacketMaxSizeTextField id: specialJunk3TextField
headerText: qsTr("I3 - Third special junk packet")
textField.text: clientSpecialJunk3
textField.validator: null
checkEmptyText: false
Layout.leftMargin: 16 textField.onEditingFinished: {
Layout.rightMargin: 16 if (textField.text !== clientSpecialJunk3) {
clientSpecialJunk3 = textField.text
headerText: "Jmax - Junk packet maximum size" }
textField.text: clientJunkPacketMaxSize
textField.onEditingFinished: {
if (textField.text !== clientJunkPacketMaxSize) {
clientJunkPacketMaxSize = textField.text
} }
} }
}
AwgTextField { AwgTextField {
id: specialJunk1TextField id: specialJunk4TextField
headerText: qsTr("I4 - Fourth special junk packet")
textField.text: clientSpecialJunk4
textField.validator: null
checkEmptyText: false
Layout.leftMargin: 16 textField.onEditingFinished: {
Layout.rightMargin: 16 if (textField.text !== clientSpecialJunk4) {
clientSpecialJunk4 = textField.text
headerText: qsTr("I1 - First special junk packet") }
textField.text: clientSpecialJunk1
textField.validator: null
checkEmptyText: false
textField.onEditingFinished: {
if (textField.text !== clientSpecialJunk1) {
clientSpecialJunk1 = textField.text
} }
} }
}
AwgTextField { AwgTextField {
id: specialJunk2TextField id: specialJunk5TextField
headerText: qsTr("I5 - Fifth special junk packet")
textField.text: clientSpecialJunk5
textField.validator: null
checkEmptyText: false
Layout.leftMargin: 16 textField.onEditingFinished: {
Layout.rightMargin: 16 if (textField.text !== clientSpecialJunk5 ) {
clientSpecialJunk5 = textField.text
headerText: qsTr("I2 - Second special junk packet") }
textField.text: clientSpecialJunk2
textField.validator: null
checkEmptyText: false
textField.onEditingFinished: {
if (textField.text !== clientSpecialJunk2) {
clientSpecialJunk2 = textField.text
} }
} }
}
AwgTextField { AwgTextField {
id: specialJunk3TextField id: controlledJunk1TextField
headerText: qsTr("J1 - First controlled junk packet")
textField.text: clientControlledJunk1
textField.validator: null
checkEmptyText: false
Layout.leftMargin: 16 textField.onEditingFinished: {
Layout.rightMargin: 16 if (textField.text !== clientControlledJunk1) {
clientControlledJunk1 = textField.text
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 { AwgTextField {
id: specialJunk4TextField id: controlledJunk2TextField
headerText: qsTr("J2 - Second controlled junk packet")
textField.text: clientControlledJunk2
textField.validator: null
checkEmptyText: false
Layout.leftMargin: 16 textField.onEditingFinished: {
Layout.rightMargin: 16 if (textField.text !== clientControlledJunk2) {
clientControlledJunk2 = textField.text
headerText: qsTr("I4 - Fourth special junk packet") }
textField.text: clientSpecialJunk4
textField.validator: null
checkEmptyText: false
textField.onEditingFinished: {
if (textField.text !== clientSpecialJunk4) {
clientSpecialJunk4 = textField.text
} }
} }
}
AwgTextField { AwgTextField {
id: specialJunk5TextField id: controlledJunk3TextField
headerText: qsTr("J3 - Third controlled junk packet")
textField.text: clientControlledJunk3
textField.validator: null
checkEmptyText: false
Layout.leftMargin: 16 textField.onEditingFinished: {
Layout.rightMargin: 16 if (textField.text !== clientControlledJunk3) {
clientControlledJunk3 = textField.text
headerText: qsTr("I5 - Fifth special junk packet") }
textField.text: clientSpecialJunk5
textField.validator: null
checkEmptyText: false
textField.onEditingFinished: {
if (textField.text !== clientSpecialJunk5 ) {
clientSpecialJunk5 = textField.text
} }
} }
}
AwgTextField { AwgTextField {
id: controlledJunk1TextField id: iTimeTextField
headerText: qsTr("Itime - Special handshake timeout")
textField.text: clientSpecialHandshakeTimeout
checkEmptyText: false
Layout.leftMargin: 16 textField.onEditingFinished: {
Layout.rightMargin: 16 if (textField.text !== clientSpecialHandshakeTimeout) {
clientSpecialHandshakeTimeout = textField.text
headerText: qsTr("J1 - First controlled junk packet") }
textField.text: clientControlledJunk1
textField.validator: null
checkEmptyText: false
textField.onEditingFinished: {
if (textField.text !== clientControlledJunk1) {
clientControlledJunk1 = textField.text
} }
} }
}
AwgTextField { Header2TextType {
id: controlledJunk2TextField Layout.fillWidth: true
Layout.topMargin: 16
Layout.leftMargin: 16 text: qsTr("Server settings")
Layout.rightMargin: 16
headerText: qsTr("J2 - Second controlled junk packet")
textField.text: clientControlledJunk2
textField.validator: null
checkEmptyText: false
textField.onEditingFinished: {
if (textField.text !== clientControlledJunk2) {
clientControlledJunk2 = textField.text
}
} }
}
AwgTextField { AwgTextField {
id: controlledJunk3TextField id: portTextField
enabled: false
Layout.leftMargin: 16 headerText: qsTr("Port")
Layout.rightMargin: 16 textField.text: port
headerText: qsTr("J3 - Third controlled junk packet")
textField.text: clientControlledJunk3
textField.validator: null
checkEmptyText: false
textField.onEditingFinished: {
if (textField.text !== clientControlledJunk3) {
clientControlledJunk3 = textField.text
}
} }
}
AwgTextField { AwgTextField {
id: iTimeTextField id: initPacketJunkSizeTextField
enabled: false
Layout.leftMargin: 16 headerText: "S1 - Init packet junk size"
Layout.rightMargin: 16 textField.text: serverInitPacketJunkSize
headerText: qsTr("Itime - Special handshake timeout")
textField.text: clientSpecialHandshakeTimeout
checkEmptyText: false
textField.onEditingFinished: {
if (textField.text !== clientSpecialHandshakeTimeout) {
clientSpecialHandshakeTimeout = textField.text
}
} }
}
Header2TextType { AwgTextField {
Layout.fillWidth: true id: responsePacketJunkSizeTextField
Layout.topMargin: 16 enabled: false
Layout.leftMargin: 16
Layout.rightMargin: 16
text: qsTr("Server settings") headerText: "S2 - Response packet junk size"
} textField.text: serverResponsePacketJunkSize
}
AwgTextField { // AwgTextField {
id: portTextField // id: cookieReplyPacketJunkSizeTextField
// enabled: false
Layout.leftMargin: 16 // headerText: "S3 - Cookie Reply packet junk size"
Layout.rightMargin: 16 // textField.text: serverCookieReplyPacketJunkSize
// }
enabled: false // AwgTextField {
// id: transportPacketJunkSizeTextField
// enabled: false
headerText: qsTr("Port") // headerText: "S4 - Transport packet junk size"
textField.text: port // textField.text: serverTransportPacketJunkSize
} // }
AwgTextField { AwgTextField {
id: initPacketJunkSizeTextField id: initPacketMagicHeaderTextField
enabled: false
Layout.leftMargin: 16 headerText: "H1 - Init packet magic header"
Layout.rightMargin: 16 textField.text: serverInitPacketMagicHeader
}
enabled: false AwgTextField {
id: responsePacketMagicHeaderTextField
enabled: false
headerText: "S1 - Init packet junk size" headerText: "H2 - Response packet magic header"
textField.text: serverInitPacketJunkSize textField.text: serverResponsePacketMagicHeader
} }
AwgTextField { AwgTextField {
id: responsePacketJunkSizeTextField id: underloadPacketMagicHeaderTextField
enabled: false
Layout.leftMargin: 16 headerText: "H3 - Underload packet magic header"
Layout.rightMargin: 16 textField.text: serverUnderloadPacketMagicHeader
}
enabled: false AwgTextField {
id: transportPacketMagicHeaderTextField
enabled: false
headerText: "S2 - Response packet junk size" headerText: "H4 - Transport packet magic header"
textField.text: serverResponsePacketJunkSize textField.text: serverTransportPacketMagicHeader
} }
// AwgTextField {
// id: cookieReplyPacketJunkSizeTextField
// Layout.leftMargin: 16
// Layout.rightMargin: 16
// enabled: false
// headerText: "S3 - Cookie Reply packet junk size"
// textField.text: serverCookieReplyPacketJunkSize
// }
// AwgTextField {
// id: transportPacketJunkSizeTextField
// Layout.leftMargin: 16
// Layout.rightMargin: 16
// enabled: false
// headerText: "S4 - Transport packet junk size"
// textField.text: serverTransportPacketJunkSize
// }
AwgTextField {
id: initPacketMagicHeaderTextField
Layout.leftMargin: 16
Layout.rightMargin: 16
enabled: false
headerText: "H1 - Init packet magic header"
textField.text: serverInitPacketMagicHeader
}
AwgTextField {
id: responsePacketMagicHeaderTextField
Layout.leftMargin: 16
Layout.rightMargin: 16
enabled: false
headerText: "H2 - Response packet magic header"
textField.text: serverResponsePacketMagicHeader
}
AwgTextField {
id: underloadPacketMagicHeaderTextField
Layout.leftMargin: 16
Layout.rightMargin: 16
enabled: false
headerText: "H3 - Underload packet magic header"
textField.text: serverUnderloadPacketMagicHeader
}
AwgTextField {
id: transportPacketMagicHeaderTextField
Layout.leftMargin: 16
Layout.rightMargin: 16
enabled: false
headerText: "H4 - Transport packet magic header"
textField.text: serverTransportPacketMagicHeader
} }
} }
} }
@@ -423,17 +375,18 @@ 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")
@@ -448,9 +401,11 @@ PageType {
PageController.goToPage(PageEnum.PageSetupWizardInstalling); PageController.goToPage(PageEnum.PageSetupWizardInstalling);
InstallController.updateContainer(AwgConfigModel.getConfig()) InstallController.updateContainer(AwgConfigModel.getConfig())
} }
var noButtonFunction = function() {
var noButtonFunction = function() {} if (!GC.isMobile()) {
saveButton.forceActiveFocus()
}
}
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction) showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
} }
} }
+286 -295
View File
@@ -19,343 +19,334 @@ import "../Components"
PageType { PageType {
id: root id: root
BackButtonType { ColumnLayout {
id: backButton 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
onActiveFocusChanged: { BackButtonType {
if(backButton.enabled && backButton.activeFocus) { id: backButton
listView.positionViewAtBeginning()
}
} }
} }
ListViewType { ListView {
id: listView id: listview
anchors.top: backButton.bottom property bool isFocusable: true
anchors.top: backButtonLayout.bottom
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.right: parent.right 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: ColumnLayout { delegate: Item {
id: delegateItem id: delegateItem
implicitWidth: listview.width
width: listView.width implicitHeight: col.implicitHeight
property alias vpnAddressSubnetTextField: vpnAddressSubnetTextField property alias vpnAddressSubnetTextField: vpnAddressSubnetTextField
property bool isEnabled: ServersModel.isProcessedServerHasWriteAccess() property bool isEnabled: ServersModel.isProcessedServerHasWriteAccess()
spacing: 0 ColumnLayout {
id: col
BaseHeaderType { anchors.top: parent.top
Layout.fillWidth: true anchors.left: parent.left
Layout.leftMargin: 16 anchors.right: parent.right
Layout.rightMargin: 16
headerText: qsTr("AmneziaWG settings") anchors.leftMargin: 16
} anchors.rightMargin: 16
TextFieldWithHeaderType { spacing: 0
id: vpnAddressSubnetTextField
Layout.fillWidth: true BaseHeaderType {
Layout.topMargin: 40 Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 16
enabled: delegateItem.isEnabled headerText: qsTr("AmneziaWG settings")
}
headerText: qsTr("VPN address subnet") TextFieldWithHeaderType {
textField.text: subnetAddress id: vpnAddressSubnetTextField
textField.onEditingFinished: { Layout.fillWidth: true
if (textField.text !== subnetAddress) { Layout.topMargin: 40
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
}
} }
} }
checkEmptyText: true AwgTextField {
} id: junkPacketMinSizeTextField
headerText: qsTr("Jmin - Junk packet minimum size")
textField.text: serverJunkPacketMinSize
TextFieldWithHeaderType { textField.onEditingFinished: {
id: portTextField if (textField.text !== serverJunkPacketMinSize) {
Layout.fillWidth: true serverJunkPacketMinSize = textField.text
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 AwgTextField {
} id: junkPacketMaxSizeTextField
headerText: qsTr("Jmax - Junk packet maximum size")
textField.text: serverJunkPacketMaxSize
AwgTextField { textField.onEditingFinished: {
id: junkPacketCountTextField if (textField.text !== serverJunkPacketMaxSize) {
serverJunkPacketMaxSize = textField.text
Layout.leftMargin: 16 }
Layout.rightMargin: 16
headerText: qsTr("Jc - Junk packet count")
textField.text: serverJunkPacketCount
textField.onEditingFinished: {
if (textField.text !== serverJunkPacketCount) {
serverJunkPacketCount = textField.text
}
}
}
AwgTextField {
id: junkPacketMinSizeTextField
Layout.leftMargin: 16
Layout.rightMargin: 16
headerText: qsTr("Jmin - Junk packet minimum size")
textField.text: serverJunkPacketMinSize
textField.onEditingFinished: {
if (textField.text !== serverJunkPacketMinSize) {
serverJunkPacketMinSize = textField.text
}
}
}
AwgTextField {
id: junkPacketMaxSizeTextField
Layout.leftMargin: 16
Layout.rightMargin: 16
headerText: qsTr("Jmax - Junk packet maximum size")
textField.text: serverJunkPacketMaxSize
textField.onEditingFinished: {
if (textField.text !== serverJunkPacketMaxSize) {
serverJunkPacketMaxSize = textField.text
}
}
}
AwgTextField {
id: initPacketJunkSizeTextField
Layout.leftMargin: 16
Layout.rightMargin: 16
headerText: qsTr("S1 - Init packet junk size")
textField.text: serverInitPacketJunkSize
textField.onEditingFinished: {
if (textField.text !== serverInitPacketJunkSize) {
serverInitPacketJunkSize = textField.text
}
}
}
AwgTextField {
id: responsePacketJunkSizeTextField
Layout.leftMargin: 16
Layout.rightMargin: 16
headerText: qsTr("S2 - Response packet junk size")
textField.text: serverResponsePacketJunkSize
textField.onEditingFinished: {
if (textField.text !== serverResponsePacketJunkSize) {
serverResponsePacketJunkSize = textField.text
}
}
}
// AwgTextField {
// id: cookieReplyPacketJunkSizeTextField
// Layout.leftMargin: 16
// Layout.rightMargin: 16
// headerText: qsTr("S3 - Cookie reply packet junk size")
// textField.text: serverCookieReplyPacketJunkSize
// textField.onEditingFinished: {
// if (textField.text !== serverCookieReplyPacketJunkSize) {
// serverCookieReplyPacketJunkSize = textField.text
// }
// }
// }
// AwgTextField {
// id: transportPacketJunkSizeTextField
// Layout.leftMargin: 16
// Layout.rightMargin: 16
// headerText: qsTr("S4 - Transport packet junk size")
// textField.text: serverTransportPacketJunkSize
// textField.onEditingFinished: {
// if (textField.text !== serverTransportPacketJunkSize) {
// serverTransportPacketJunkSize = textField.text
// }
// }
// }
AwgTextField {
id: initPacketMagicHeaderTextField
Layout.leftMargin: 16
Layout.rightMargin: 16
headerText: qsTr("H1 - Init packet magic header")
textField.text: serverInitPacketMagicHeader
textField.onEditingFinished: {
if (textField.text !== serverInitPacketMagicHeader) {
serverInitPacketMagicHeader = textField.text
}
}
}
AwgTextField {
id: responsePacketMagicHeaderTextField
Layout.leftMargin: 16
Layout.rightMargin: 16
headerText: qsTr("H2 - Response packet magic header")
textField.text: serverResponsePacketMagicHeader
textField.onEditingFinished: {
if (textField.text !== serverResponsePacketMagicHeader) {
serverResponsePacketMagicHeader = textField.text
}
}
}
AwgTextField {
id: underloadPacketMagicHeaderTextField
Layout.leftMargin: 16
Layout.rightMargin: 16
headerText: qsTr("H3 - Underload packet magic header")
textField.text: serverUnderloadPacketMagicHeader
textField.onEditingFinished: {
if (textField.text !== serverUnderloadPacketMagicHeader) {
serverUnderloadPacketMagicHeader = textField.text
}
}
}
AwgTextField {
id: transportPacketMagicHeaderTextField
Layout.leftMargin: 16
Layout.rightMargin: 16
headerText: qsTr("H4 - Transport packet magic header")
textField.text: serverTransportPacketMagicHeader
textField.onEditingFinished: {
if (textField.text !== serverTransportPacketMagicHeader) {
serverTransportPacketMagicHeader = textField.text
}
}
}
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 === "" &&
// cookieReplyHeaderJunkTextField.errorText === "" &&
// transportHeaderJunkTextField.errorText === "" &&
initPacketJunkSizeTextField.errorText === "" &&
junkPacketMaxSizeTextField.errorText === "" &&
junkPacketMinSizeTextField.errorText === "" &&
junkPacketCountTextField.errorText === "" &&
portTextField.errorText === "" &&
vpnAddressSubnetTextField.errorText === ""
text: qsTr("Save")
onActiveFocusChanged: {
if(activeFocus) {
listView.positionViewAtEnd()
} }
} }
clickedFunc: function() { AwgTextField {
if (delegateItem.isEnabled) { id: initPacketJunkSizeTextField
if (AwgConfigModel.isHeadersEqual(underloadPacketMagicHeaderTextField.textField.text, headerText: qsTr("S1 - Init packet junk size")
transportPacketMagicHeaderTextField.textField.text, textField.text: serverInitPacketJunkSize
responsePacketMagicHeaderTextField.textField.text,
initPacketMagicHeaderTextField.textField.text)) { textField.onEditingFinished: {
PageController.showErrorMessage(qsTr("The values of the H1-H4 fields must be unique")) if (textField.text !== serverInitPacketJunkSize) {
return serverInitPacketJunkSize = textField.text
}
}
}
AwgTextField {
id: responsePacketJunkSizeTextField
headerText: qsTr("S2 - Response packet junk size")
textField.text: serverResponsePacketJunkSize
textField.onEditingFinished: {
if (textField.text !== serverResponsePacketJunkSize) {
serverResponsePacketJunkSize = textField.text
}
}
}
// AwgTextField {
// id: cookieReplyPacketJunkSizeTextField
// headerText: qsTr("S3 - Cookie reply packet junk size")
// textField.text: serverCookieReplyPacketJunkSize
// textField.onEditingFinished: {
// if (textField.text !== serverCookieReplyPacketJunkSize) {
// serverCookieReplyPacketJunkSize = textField.text
// }
// }
// }
// AwgTextField {
// id: transportPacketJunkSizeTextField
// headerText: qsTr("S4 - Transport packet junk size")
// textField.text: serverTransportPacketJunkSize
// textField.onEditingFinished: {
// if (textField.text !== serverTransportPacketJunkSize) {
// serverTransportPacketJunkSize = 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 {
id: responsePacketMagicHeaderTextField
headerText: qsTr("H2 - Response packet magic header")
textField.text: serverResponsePacketMagicHeader
textField.onEditingFinished: {
if (textField.text !== serverResponsePacketMagicHeader) {
serverResponsePacketMagicHeader = textField.text
}
}
}
AwgTextField {
id: underloadPacketMagicHeaderTextField
headerText: qsTr("H3 - Underload packet magic header")
textField.text: serverUnderloadPacketMagicHeader
textField.onEditingFinished: {
if (textField.text !== serverUnderloadPacketMagicHeader) {
serverUnderloadPacketMagicHeader = textField.text
}
}
}
AwgTextField {
id: transportPacketMagicHeaderTextField
headerText: qsTr("H4 - Transport packet magic header")
textField.text: serverTransportPacketMagicHeader
textField.onEditingFinished: {
if (textField.text !== serverTransportPacketMagicHeader) {
serverTransportPacketMagicHeader = textField.text
}
}
}
BasicButtonType {
id: saveRestartButton
Layout.fillWidth: true
Layout.topMargin: 24
Layout.bottomMargin: 24
enabled: underloadPacketMagicHeaderTextField.errorText === "" &&
transportPacketMagicHeaderTextField.errorText === "" &&
responsePacketMagicHeaderTextField.errorText === "" &&
initPacketMagicHeaderTextField.errorText === "" &&
responsePacketJunkSizeTextField.errorText === "" &&
// cookieReplyHeaderJunkTextField.errorText === "" &&
// transportHeaderJunkTextField.errorText === "" &&
initPacketJunkSizeTextField.errorText === "" &&
junkPacketMaxSizeTextField.errorText === "" &&
junkPacketMinSizeTextField.errorText === "" &&
junkPacketCountTextField.errorText === "" &&
portTextField.errorText === "" &&
vpnAddressSubnetTextField.errorText === ""
text: qsTr("Save")
onActiveFocusChanged: {
if(activeFocus) {
listview.positionViewAtEnd()
}
}
clickedFunc: function() {
forceActiveFocus()
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
}
// 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
// }
} }
if (AwgConfigModel.isPacketSizeEqual(parseInt(initPacketJunkSizeTextField.textField.text), var headerText = qsTr("Save settings?")
parseInt(responsePacketJunkSizeTextField.textField.text))) { var descriptionText = qsTr("All users with whom you shared a connection with will no longer be able to connect to it.")
PageController.showErrorMessage(qsTr("The value of the field S1 + message initiation size (148) must not equal S2 + message response size (92)")) var yesButtonText = qsTr("Continue")
return 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())
} }
// if (AwgConfigModel.isPacketSizeEqual(parseInt(initPacketJunkSizeTextField.textField.text), var noButtonFunction = function() {
// parseInt(responsePacketJunkSizeTextField.textField.text), if (!GC.isMobile()) {
// parseInt(cookieReplyPacketJunkSizeTextField.textField.text), saveRestartButton.forceActiveFocus()
// 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?")
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
} }
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
PageController.goToPage(PageEnum.PageSetupWizardInstalling);
InstallController.updateContainer(AwgConfigModel.getConfig())
} }
var noButtonFunction = function() {}
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
} }
} }
} }
+150 -130
View File
@@ -16,191 +16,211 @@ import "../Components"
PageType { PageType {
id: root id: root
BackButtonType { ColumnLayout {
id: backButton 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
onActiveFocusChanged: { BackButtonType {
if(backButton.enabled && backButton.activeFocus) { id: backButton
listView.positionViewAtBeginning()
}
} }
} }
ListViewType { FlickableType {
id: listView id: fl
anchors.top: backButtonLayout.bottom
anchors.top: backButton.bottom
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
anchors.left: parent.left contentHeight: content.implicitHeight
anchors.right: parent.right
property int selectedIndex: 0 Column {
id: content
enabled: ServersModel.isProcessedServerHasWriteAccess() anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
header: ColumnLayout { enabled: ServersModel.isProcessedServerHasWriteAccess()
width: listView.width
BaseHeaderType { ListView {
Layout.fillWidth: true id: listview
Layout.leftMargin: 16
Layout.rightMargin: 16
headerText: qsTr("Cloak settings") property int selectedIndex: 0
}
}
model: CloakConfigModel width: parent.width
height: listview.contentItem.height
delegate: ColumnLayout { clip: true
width: listView.width reuseItems: true
property alias trafficFromField: trafficFromField model: CloakConfigModel
spacing: 0 delegate: Item {
id: delegateItem
TextFieldWithHeaderType { property alias trafficFromField: trafficFromField
id: trafficFromField property bool isEnabled: ServersModel.isProcessedServerHasWriteAccess()
Layout.fillWidth: true implicitWidth: listview.width
Layout.topMargin: 32 implicitHeight: col.implicitHeight
Layout.leftMargin: 16
Layout.rightMargin: 16
headerText: qsTr("Disguised as traffic from") ColumnLayout {
textField.text: site id: col
textField.onEditingFinished: { anchors.top: parent.top
if (textField.text !== site) { anchors.left: parent.left
var tmpText = textField.text anchors.right: parent.right
tmpText = tmpText.toLocaleLowerCase()
var indexHttps = tmpText.indexOf("https://") anchors.leftMargin: 16
if (indexHttps === 0) { anchors.rightMargin: 16
tmpText = textField.text.substring(8)
} else { spacing: 0
site = textField.text
BaseHeaderType {
Layout.fillWidth: true
headerText: qsTr("Cloak settings")
} }
}
}
checkEmptyText: true TextFieldWithHeaderType {
} id: trafficFromField
TextFieldWithHeaderType { Layout.fillWidth: true
id: portTextField Layout.topMargin: 32
Layout.fillWidth: true enabled: delegateItem.isEnabled
Layout.topMargin: 16
Layout.leftMargin: 16
Layout.rightMargin: 16
headerText: qsTr("Port") headerText: qsTr("Disguised as traffic from")
textField.text: port textField.text: site
textField.maximumLength: 5
textField.validator: IntValidator { bottom: 1; top: 65535 }
textField.onEditingFinished: { textField.onEditingFinished: {
if (textField.text !== port) { if (textField.text !== site) {
port = textField.text var tmpText = textField.text
} tmpText = tmpText.toLocaleLowerCase()
}
checkEmptyText: true var indexHttps = tmpText.indexOf("https://")
} if (indexHttps === 0) {
tmpText = textField.text.substring(8)
} else {
site = textField.text
}
}
}
DropDownType { checkEmptyText: true
id: cipherDropDown }
Layout.fillWidth: true TextFieldWithHeaderType {
Layout.topMargin: 16 id: portTextField
Layout.leftMargin: 16
Layout.rightMargin: 16
descriptionText: qsTr("Cipher") Layout.fillWidth: true
headerText: qsTr("Cipher") Layout.topMargin: 16
drawerParent: root enabled: delegateItem.isEnabled
listView: ListViewWithRadioButtonType { headerText: qsTr("Port")
id: cipherListView textField.text: port
textField.maximumLength: 5
textField.validator: IntValidator { bottom: 1; top: 65535 }
rootWidth: root.width textField.onEditingFinished: {
if (textField.text !== port) {
port = textField.text
}
}
model: ListModel { checkEmptyText: true
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() { DropDownType {
cipherDropDown.text = selectedText id: cipherDropDown
cipher = cipherDropDown.text Layout.fillWidth: true
cipherDropDown.closeTriggered() Layout.topMargin: 16
}
Component.onCompleted: { enabled: delegateItem.isEnabled
cipherDropDown.text = cipher
for (var i = 0; i < cipherListView.model.count; i++) { descriptionText: qsTr("Cipher")
if (cipherListView.model.get(i).name === cipherDropDown.text) { headerText: qsTr("Cipher")
selectedIndex = i
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 { BasicButtonType {
id: saveButton 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
enabled: trafficFromField.errorText === "" && enabled: trafficFromField.errorText === "" &&
portTextField.errorText === "" portTextField.errorText === ""
text: qsTr("Save") text: qsTr("Save")
clickedFunc: function() { clickedFunc: function() {
forceActiveFocus() forceActiveFocus()
var headerText = qsTr("Save settings?") 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 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 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(CloakConfigModel.getConfig()) InstallController.updateContainer(CloakConfigModel.getConfig())
} }
var noButtonFunction = function() { var noButtonFunction = function() {
if (!GC.isMobile()) { if (!GC.isMobile()) {
saveButton.forceActiveFocus() saveButton.forceActiveFocus()
}
}
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
}
Keys.onEnterPressed: saveButton.clicked()
Keys.onReturnPressed: saveButton.clicked()
} }
} }
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
} }
Keys.onEnterPressed: saveButton.clicked()
Keys.onReturnPressed: saveButton.clicked()
} }
} }
} }
@@ -17,413 +17,427 @@ import "../Components"
PageType { PageType {
id: root id: root
BackButtonType { ColumnLayout {
id: backButton 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
onActiveFocusChanged: { BackButtonType {
if(backButton.enabled && backButton.activeFocus) { id: backButton
listView.positionViewAtBeginning()
}
} }
} }
ListViewType { FlickableType {
id: listView id: fl
anchors.top: backButtonLayout.bottom
anchors.top: backButton.bottom
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
anchors.right: parent.right contentHeight: content.implicitHeight
anchors.left: parent.left
enabled: ServersModel.isProcessedServerHasWriteAccess() Column {
id: content
header: ColumnLayout { anchors.top: parent.top
width: listView.width anchors.left: parent.left
anchors.right: parent.right
BaseHeaderType { enabled: ServersModel.isProcessedServerHasWriteAccess()
id: header
Layout.fillWidth: true ListView {
Layout.rightMargin: 16 id: listview
Layout.leftMargin: 16
headerText: qsTr("OpenVPN Settings") width: parent.width
} height: listview.contentItem.height
}
model: OpenVpnConfigModel clip: true
interactive: false
delegate: ColumnLayout { model: OpenVpnConfigModel
width: listView.width
spacing: 0 delegate: Item {
id: delegateItem
TextFieldWithHeaderType { property alias vpnAddressSubnetTextField: vpnAddressSubnetTextField
id: vpnAddressSubnetTextField property bool isEnabled: ServersModel.isProcessedServerHasWriteAccess()
Layout.fillWidth: true implicitWidth: listview.width
Layout.topMargin: 32 implicitHeight: col.implicitHeight
Layout.leftMargin: 16
Layout.rightMargin: 16
enabled: listView.enabled ColumnLayout {
id: col
headerText: qsTr("VPN address subnet") anchors.top: parent.top
textField.text: subnetAddress anchors.left: parent.left
anchors.right: parent.right
textField.onEditingFinished: { anchors.leftMargin: 16
if (textField.text !== subnetAddress) { anchors.rightMargin: 16
subnetAddress = textField.text
}
}
checkEmptyText: true spacing: 0
}
ParagraphTextType { BaseHeaderType {
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 32 headerText: qsTr("OpenVPN settings")
Layout.leftMargin: 16 }
Layout.rightMargin: 16
text: qsTr("Network protocol") TextFieldWithHeaderType {
} id: vpnAddressSubnetTextField
TransportProtoSelector { Layout.fillWidth: true
id: transportProtoSelector Layout.topMargin: 32
Layout.fillWidth: true
Layout.topMargin: 16
Layout.leftMargin: 16
Layout.rightMargin: 16
rootWidth: root.width enabled: delegateItem.isEnabled
enabled: isTransportProtoEditable headerText: qsTr("VPN address subnet")
textField.text: subnetAddress
currentIndex: { parentFlickable: fl
return transportProto === "tcp" ? 1 : 0
}
onCurrentIndexChanged: { textField.onEditingFinished: {
if (transportProto === "tcp" && currentIndex === 0) { if (textField.text !== subnetAddress) {
transportProto = "udp" subnetAddress = textField.text
} else if (transportProto === "udp" && currentIndex === 1) { }
transportProto = "tcp" }
}
}
}
TextFieldWithHeaderType { checkEmptyText: true
id: portTextField }
Layout.fillWidth: true ParagraphTextType {
Layout.topMargin: 40 Layout.fillWidth: true
Layout.leftMargin: 16 Layout.topMargin: 32
Layout.rightMargin: 16
enabled: listView.enabled text: qsTr("Network protocol")
}
headerText: qsTr("Port") TransportProtoSelector {
textField.text: port id: transportProtoSelector
textField.maximumLength: 5 Layout.fillWidth: true
textField.validator: IntValidator { bottom: 1; top: 65535 } Layout.topMargin: 16
rootWidth: root.width
textField.onEditingFinished: { enabled: isTransportProtoEditable
if (textField.text !== port) {
port = textField.text
}
}
checkEmptyText: true currentIndex: {
} return transportProto === "tcp" ? 1 : 0
}
SwitcherType { onCurrentIndexChanged: {
id: autoNegotiateEncryprionSwitcher if (transportProto === "tcp" && currentIndex === 0) {
transportProto = "udp"
Layout.fillWidth: true } else if (transportProto === "udp" && currentIndex === 1) {
Layout.topMargin: 24 transportProto = "tcp"
Layout.leftMargin: 16 }
Layout.rightMargin: 16
text: qsTr("Auto-negotiate encryption")
checked: autoNegotiateEncryprion
onCheckedChanged: {
if (checked !== autoNegotiateEncryprion) {
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
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 { TextFieldWithHeaderType {
id: cipherDropDown id: portTextField
Layout.fillWidth: true
Layout.topMargin: 16
Layout.leftMargin: 16
Layout.rightMargin: 16
enabled: !autoNegotiateEncryprionSwitcher.checked Layout.fillWidth: true
Layout.topMargin: 40
parentFlickable: fl
descriptionText: qsTr("Cipher") enabled: delegateItem.isEnabled
headerText: qsTr("Cipher")
drawerParent: root headerText: qsTr("Port")
textField.text: port
textField.maximumLength: 5
textField.validator: IntValidator { bottom: 1; top: 65535 }
listView: ListViewWithRadioButtonType { textField.onEditingFinished: {
id: cipherListView if (textField.text !== port) {
port = textField.text
}
}
rootWidth: root.width checkEmptyText: true
}
model: ListModel { SwitcherType {
ListElement { name : qsTr("AES-256-GCM") } id: autoNegotiateEncryprionSwitcher
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() { Layout.fillWidth: true
cipherDropDown.text = selectedText Layout.topMargin: 24
cipher = cipherDropDown.text parentFlickable: fl
cipherDropDown.closeTriggered()
}
Component.onCompleted: { text: qsTr("Auto-negotiate encryption")
cipherDropDown.text = cipher checked: autoNegotiateEncryprion
for (var i = 0; i < cipherListView.model.count; i++) { onCheckedChanged: {
if (cipherListView.model.get(i).name === cipherDropDown.text) { if (checked !== autoNegotiateEncryprion) {
currentIndex = i autoNegotiateEncryprion = checked
}
} }
} }
}
}
}
Rectangle { DropDownType {
id: contentRect id: hashDropDown
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 32 Layout.topMargin: 20
Layout.leftMargin: 16
Layout.rightMargin: 16
Layout.preferredHeight: checkboxLayout.implicitHeight enabled: !autoNegotiateEncryprionSwitcher.checked
color: AmneziaStyle.color.onyxBlack
radius: 16
ColumnLayout { descriptionText: qsTr("Hash")
id: checkboxLayout headerText: qsTr("Hash")
anchors.fill: parent drawerParent: root
CheckBoxType { listView: ListViewWithRadioButtonType {
id: tlsAuthCheckBox id: hashListView
Layout.fillWidth: true
text: qsTr("TLS auth") rootWidth: root.width
checked: tlsAuth
onCheckedChanged: { model: ListModel {
if (checked !== tlsAuth) { ListElement { name : qsTr("SHA512") }
console.log("tlsAuth changed to: " + checked) ListElement { name : qsTr("SHA384") }
tlsAuth = checked 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
}
}
}
} }
} }
}
DividerType {} DropDownType {
id: cipherDropDown
Layout.fillWidth: true
Layout.topMargin: 16
CheckBoxType { enabled: !autoNegotiateEncryprionSwitcher.checked
id: blockDnsCheckBox
Layout.fillWidth: true
text: qsTr("Block DNS requests outside of VPN") descriptionText: qsTr("Cipher")
checked: blockDns headerText: qsTr("Cipher")
onCheckedChanged: { drawerParent: root
if (checked !== blockDns) {
blockDns = checked listView: ListViewWithRadioButtonType {
id: cipherListView
rootWidth: root.width
model: ListModel {
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() {
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
}
}
}
} }
} }
}
}
}
SwitcherType { Rectangle {
id: additionalClientCommandsSwitcher id: contentRect
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 32 Layout.topMargin: 32
Layout.leftMargin: 16 Layout.preferredHeight: checkboxLayout.implicitHeight
Layout.rightMargin: 16 color: AmneziaStyle.color.onyxBlack
radius: 16
checked: additionalClientCommands !== "" Connections {
target: tlsAuthCheckBox
enabled: !GC.isMobile()
text: qsTr("Additional client configuration commands") function onFocusChanged() {
if (tlsAuthCheckBox.activeFocus) {
fl.ensureVisible(contentRect)
}
}
}
onCheckedChanged: { ColumnLayout {
if (!checked) { id: checkboxLayout
additionalClientCommands = ""
}
}
}
TextAreaType { anchors.fill: parent
id: additionalClientCommandsTextArea CheckBoxType {
Layout.fillWidth: true id: tlsAuthCheckBox
Layout.topMargin: 16 Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 16
visible: additionalClientCommandsSwitcher.checked text: qsTr("TLS auth")
checked: tlsAuth
textAreaText: additionalClientCommands onCheckedChanged: {
placeholderText: qsTr("Commands:") if (checked !== tlsAuth) {
console.log("tlsAuth changed to: " + checked)
tlsAuth = checked
}
}
}
textArea.onEditingFinished: { DividerType {}
if (additionalClientCommands !== textAreaText) {
additionalClientCommands = textAreaText
}
}
}
SwitcherType { CheckBoxType {
id: additionalServerCommandsSwitcher id: blockDnsCheckBox
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 16
Layout.leftMargin: 16
Layout.rightMargin: 16
checked: additionalServerCommands !== "" text: qsTr("Block DNS requests outside of VPN")
checked: blockDns
text: qsTr("Additional server configuration commands") onCheckedChanged: {
if (checked !== blockDns) {
onCheckedChanged: { blockDns = checked
if (!checked) { }
additionalServerCommands = "" }
} }
} }
}
TextAreaType {
id: additionalServerCommandsTextArea
Layout.fillWidth: true
Layout.topMargin: 16
Layout.leftMargin: 16
Layout.rightMargin: 16
visible: additionalServerCommandsSwitcher.checked
textAreaText: additionalServerCommands
placeholderText: qsTr("Commands:")
textArea.onEditingFinished: {
if (additionalServerCommands !== textAreaText) {
additionalServerCommands = textAreaText
}
}
}
BasicButtonType {
id: saveButton
Layout.fillWidth: true
Layout.topMargin: 24
Layout.bottomMargin: 24
Layout.leftMargin: 16
Layout.rightMargin: 16
enabled: vpnAddressSubnetTextField.errorText === "" &&
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); SwitcherType {
InstallController.updateContainer(OpenVpnConfigModel.getConfig()) id: additionalClientCommandsSwitcher
} Layout.fillWidth: true
var noButtonFunction = function() { Layout.topMargin: 32
if (!GC.isMobile()) { parentFlickable: fl
saveButton.forceActiveFocus()
checked: additionalClientCommands !== ""
text: qsTr("Additional client configuration commands")
onCheckedChanged: {
if (!checked) {
additionalClientCommands = ""
}
}
}
TextAreaType {
id: additionalClientCommandsTextArea
Layout.fillWidth: true
Layout.topMargin: 16
visible: additionalClientCommandsSwitcher.checked
parentFlickable: fl
textAreaText: additionalClientCommands
placeholderText: qsTr("Commands:")
textArea.onEditingFinished: {
if (additionalClientCommands !== textAreaText) {
additionalClientCommands = textAreaText
}
}
}
SwitcherType {
id: additionalServerCommandsSwitcher
Layout.fillWidth: true
Layout.topMargin: 16
parentFlickable: fl
checked: additionalServerCommands !== ""
text: qsTr("Additional server configuration commands")
onCheckedChanged: {
if (!checked) {
additionalServerCommands = ""
}
}
}
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()
} }
} }
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
} }
Keys.onEnterPressed: saveButton.clicked()
Keys.onReturnPressed: saveButton.clicked()
} }
} }
} }
+122 -108
View File
@@ -19,154 +19,164 @@ import "../Components"
PageType { PageType {
id: root id: root
BackButtonType { ColumnLayout {
id: backButton 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
onFocusChanged: { BackButtonType {
if (this.activeFocus) { id: backButton
listView.positionViewAtBeginning() }
}
BaseHeaderType {
Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 16
headerText: ContainersModel.getProcessedContainerName() + qsTr(" settings")
} }
} }
ListViewType { FlickableType {
id: listView id: fl
anchors.top: header.bottom
anchors.top: backButton.bottom
anchors.bottom: parent.bottom
anchors.right: parent.right
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right
contentHeight: content.height
header: ColumnLayout { Column {
width: listView.width id: content
BaseHeaderType { anchors.top: parent.top
Layout.fillWidth: true anchors.left: parent.left
Layout.leftMargin: 16 anchors.right: parent.right
Layout.rightMargin: 16 anchors.topMargin: 32
Layout.bottomMargin: 16
headerText: ContainersModel.getProcessedContainerName() + qsTr(" settings") ListView {
} id: listView
} width: parent.width
height: contentItem.height
clip: true
interactive: false
model: ProtocolsModel
model: ProtocolsModel activeFocusOnTab: true
focus: true
delegate: ColumnLayout { delegate: Item {
width: listView.width implicitWidth: parent.width
implicitHeight: delegateContent.implicitHeight
LabelWithButtonType { property alias focusItem: button
id: button
Layout.fillWidth: true ColumnLayout {
Layout.leftMargin: 16 id: delegateContent
Layout.rightMargin: 16
text: qsTr("Show connection options") anchors.fill: parent
clickedFunction: function() { LabelWithButtonType {
configContentDrawer.openTriggered() id: button
}
MouseArea { Layout.fillWidth: true
anchors.fill: button
cursorShape: Qt.PointingHandCursor
enabled: false
}
}
DividerType {} text: qsTr("Show connection options")
DrawerType2 { clickedFunction: function() {
id: configContentDrawer configContentDrawer.openTriggered()
}
expandedHeight: root.height * 0.9 MouseArea {
anchors.fill: button
parent: root cursorShape: Qt.PointingHandCursor
anchors.fill: parent enabled: false
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)
} }
} }
model: 1 // fake model to force the ListView to be created without a model DividerType {}
delegate: ColumnLayout { DrawerType2 {
width: drawerListView.width id: configContentDrawer
TextArea { expandedHeight: root.height * 0.9
id: configText
Layout.fillWidth: true parent: root
Layout.topMargin: 16 anchors.fill: parent
Layout.leftMargin: 16
Layout.rightMargin: 16
padding: 0 expandedStateContent: Item {
height: 24 implicitHeight: configContentDrawer.expandedHeight
color: AmneziaStyle.color.paleGray BackButtonType {
selectionColor: AmneziaStyle.color.richBrown id: backButton1
selectedTextColor: AmneziaStyle.color.paleGray
font.pixelSize: 16 anchors.top: parent.top
font.weight: Font.Medium anchors.left: parent.left
font.family: "PT Root UI VF" anchors.right: parent.right
anchors.topMargin: 16
text: rawConfig backButtonFunction: function() {
configContentDrawer.closeTriggered()
}
}
wrapMode: Text.Wrap FlickableType {
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
background: Rectangle { ColumnLayout {
color: AmneziaStyle.color.transparent id: configContent
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
@@ -188,7 +198,11 @@ 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)
} }
@@ -16,157 +16,178 @@ import "../Components"
PageType { PageType {
id: root id: root
BackButtonType { ColumnLayout {
id: backButton 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
onFocusChanged: { BackButtonType {
if (this.activeFocus) { id: backButton
listView.positionViewAtBeginning()
}
} }
} }
ListViewType { FlickableType {
id: listView id: fl
anchors.top: backButtonLayout.bottom
anchors.top: backButton.bottom
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
anchors.right: parent.right contentHeight: content.implicitHeight
anchors.left: parent.left
enabled: ServersModel.isProcessedServerHasWriteAccess() Column {
id: content
model: ShadowSocksConfigModel anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
delegate: ColumnLayout { enabled: ServersModel.isProcessedServerHasWriteAccess()
width: listView.width
spacing: 0 ListView {
id: listview
BaseHeaderType { width: parent.width
Layout.fillWidth: true height: listview.contentItem.height
Layout.leftMargin: 16
Layout.rightMargin: 16
headerText: qsTr("Shadowsocks settings") clip: true
} interactive: false
TextFieldWithHeaderType { model: ShadowSocksConfigModel
id: portTextField
Layout.fillWidth: true delegate: Item {
Layout.topMargin: 40 id: delegateItem
Layout.leftMargin: 16
Layout.rightMargin: 16
enabled: listView.enabled property bool isEnabled: ServersModel.isProcessedServerHasWriteAccess()
headerText: qsTr("Port") implicitWidth: listview.width
textField.text: port implicitHeight: col.implicitHeight
textField.maximumLength: 5
textField.validator: IntValidator { bottom: 1; top: 65535 }
textField.onEditingFinished: { ColumnLayout {
if (textField.text !== port) { id: col
port = textField.text
}
}
checkEmptyText: true anchors.top: parent.top
} anchors.left: parent.left
anchors.right: parent.right
DropDownType { anchors.leftMargin: 16
id: cipherDropDown anchors.rightMargin: 16
Layout.fillWidth: true spacing: 0
Layout.topMargin: 20
Layout.leftMargin: 16
Layout.rightMargin: 16
enabled: listView.enabled BaseHeaderType {
Layout.fillWidth: true
headerText: qsTr("Shadowsocks settings")
}
descriptionText: qsTr("Cipher") TextFieldWithHeaderType {
headerText: qsTr("Cipher") id: portTextField
drawerParent: root Layout.fillWidth: true
Layout.topMargin: 40
listView: ListViewWithRadioButtonType { enabled: delegateItem.isEnabled
id: cipherListView headerText: qsTr("Port")
textField.text: port
textField.maximumLength: 5
textField.validator: IntValidator { bottom: 1; top: 65535 }
rootWidth: root.width textField.onEditingFinished: {
if (textField.text !== port) {
port = textField.text
}
}
model: ListModel { checkEmptyText: true
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() { DropDownType {
cipherDropDown.text = selectedText id: cipherDropDown
cipher = cipherDropDown.text Layout.fillWidth: true
cipherDropDown.closeTriggered() Layout.topMargin: 20
}
Component.onCompleted: { enabled: delegateItem.isEnabled
cipherDropDown.text = cipher
for (var i = 0; i < cipherListView.model.count; i++) { descriptionText: qsTr("Cipher")
if (cipherListView.model.get(i).name === cipherDropDown.text) { headerText: qsTr("Cipher")
currentIndex = i
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 { BasicButtonType {
id: saveButton 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
enabled: portTextField.errorText === "" enabled: portTextField.errorText === ""
text: qsTr("Save") text: qsTr("Save")
clickedFunc: function() { clickedFunc: function() {
forceActiveFocus() forceActiveFocus()
var headerText = qsTr("Save settings?") 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 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 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(ShadowSocksConfigModel.getConfig()) InstallController.updateContainer(ShadowSocksConfigModel.getConfig())
} }
var noButtonFunction = function() { var noButtonFunction = function() {
if (!GC.isMobile()) { if (!GC.isMobile()) {
saveButton.forceActiveFocus() saveButton.forceActiveFocus()
}
}
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
}
Keys.onEnterPressed: saveButton.clicked()
Keys.onReturnPressed: saveButton.clicked()
} }
} }
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
} }
Keys.onEnterPressed: saveButton.clicked()
Keys.onReturnPressed: saveButton.clicked()
} }
} }
} }
@@ -16,124 +16,160 @@ import "../Components"
PageType { PageType {
id: root id: root
BackButtonType { Item {
id: backButton id: focusItem
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
onFocusChanged: { BackButtonType {
if (this.activeFocus) { id: backButton
listView.positionViewAtBeginning() KeyNavigation.tab: listview.currentItem.mtuTextField.textField
}
}
FlickableType {
id: fl
anchors.top: backButtonLayout.bottom
anchors.bottom: parent.bottom
contentHeight: content.implicitHeight + saveButton.implicitHeight + saveButton.anchors.bottomMargin + saveButton.anchors.topMargin
Column {
id: content
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
ListView {
id: listview
width: parent.width
height: listview.contentItem.height
clip: true
interactive: false
model: WireGuardConfigModel
delegate: Item {
id: delegateItem
implicitWidth: listview.width
implicitHeight: col.implicitHeight
property alias mtuTextField: mtuTextField
property bool isSaveButtonEnabled: mtuTextField.errorText === ""
ColumnLayout {
id: col
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
}
}
}
} }
} }
} }
ListViewType { BasicButtonType {
id: listView id: saveButton
anchors.top: backButton.bottom anchors.right: root.right
anchors.bottom: parent.bottom anchors.left: root.left
anchors.right: parent.right anchors.bottom: root.bottom
anchors.left: parent.left
model: WireGuardConfigModel anchors.topMargin: 24
anchors.bottomMargin: 24
anchors.rightMargin: 16
anchors.leftMargin: 16
delegate: ColumnLayout { enabled: listview.currentItem.isSaveButtonEnabled
width: listView.width
property alias mtuTextField: mtuTextField text: qsTr("Save")
property bool isSaveButtonEnabled: mtuTextField.errorText === ""
spacing: 0 clickedFunc: function() {
forceActiveFocus()
var headerText = qsTr("Save settings?")
var descriptionText = qsTr("Only the settings for this device will be changed")
var yesButtonText = qsTr("Continue")
var noButtonText = qsTr("Cancel")
BaseHeaderType { var yesButtonFunction = function() {
Layout.fillWidth: true if (ConnectionController.isConnected && ServersModel.getDefaultServerData("defaultContainer") === ContainersModel.getProcessedContainerIndex()) {
Layout.leftMargin: 16 PageController.showNotificationMessage(qsTr("Unable change settings while there is an active connection"))
Layout.rightMargin: 16 return
headerText: qsTr("WG settings")
}
TextFieldWithHeaderType {
id: mtuTextField
Layout.fillWidth: true
Layout.topMargin: 40
Layout.leftMargin: 16
Layout.rightMargin: 16
headerText: qsTr("MTU")
textField.text: clientMtu
textField.validator: IntValidator { bottom: 576; top: 65535 }
textField.onEditingFinished: {
if (textField.text !== clientMtu) {
clientMtu = textField.text
}
} }
checkEmptyText: true
PageController.goToPage(PageEnum.PageSetupWizardInstalling);
InstallController.updateContainer(WireGuardConfigModel.getConfig())
} }
var noButtonFunction = function() {
Header2TextType { if (!GC.isMobile()) {
Layout.fillWidth: true saveButton.forceActiveFocus()
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
}
}
footer: ColumnLayout {
width: listView.width
BasicButtonType {
id: saveButton
Layout.fillWidth: true
Layout.topMargin: 24
Layout.bottomMargin: 24
Layout.rightMargin: 16
Layout.leftMargin: 16
enabled: listView.currentItem.isSaveButtonEnabled
text: qsTr("Save")
clickedFunc: function() {
var headerText = qsTr("Save settings?")
var descriptionText = qsTr("Only the settings for this device will be changed")
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() {}
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
} }
} }
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
} }
} }
} }
@@ -16,130 +16,153 @@ import "../Components"
PageType { PageType {
id: root id: root
BackButtonType { ColumnLayout {
id: backButton 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
onFocusChanged: { BackButtonType {
if (this.activeFocus) { id: backButton
listView.positionViewAtBeginning()
}
} }
} }
ListViewType { FlickableType {
id: listView id: fl
anchors.top: backButtonLayout.bottom
anchors.top: backButton.bottom
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
anchors.right: parent.right contentHeight: content.implicitHeight
anchors.left: parent.left
enabled: ServersModel.isProcessedServerHasWriteAccess() Column {
id: content
model: WireGuardConfigModel anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
delegate: ColumnLayout { enabled: ServersModel.isProcessedServerHasWriteAccess()
width: listView.width
property bool isEnabled: ServersModel.isProcessedServerHasWriteAccess() ListView {
id: listview
spacing: 0 width: parent.width
height: listview.contentItem.height
BaseHeaderType { clip: true
Layout.fillWidth: true interactive: false
Layout.leftMargin: 16
Layout.rightMargin: 16
headerText: qsTr("WG settings") model: WireGuardConfigModel
}
TextFieldWithHeaderType { delegate: Item {
id: vpnAddressSubnetTextField id: delegateItem
Layout.fillWidth: true property alias focusItemId: vpnAddressSubnetTextField
Layout.topMargin: 40 property bool isEnabled: ServersModel.isProcessedServerHasWriteAccess()
Layout.leftMargin: 16
Layout.rightMargin: 16
headerText: qsTr("VPN address subnet") implicitWidth: listview.width
textField.text: subnetAddress implicitHeight: col.implicitHeight
textField.onEditingFinished: { ColumnLayout {
if (textField.text !== subnetAddress) { id: col
subnetAddress = textField.text
}
}
checkEmptyText: true anchors.top: parent.top
} anchors.left: parent.left
anchors.right: parent.right
TextFieldWithHeaderType { anchors.leftMargin: 16
id: portTextField anchors.rightMargin: 16
Layout.fillWidth: true
Layout.topMargin: 16
Layout.leftMargin: 16
Layout.rightMargin: 16
headerText: qsTr("Port") spacing: 0
textField.text: port
textField.maximumLength: 5
textField.validator: IntValidator { bottom: 1; top: 65535 }
textField.onEditingFinished: { BaseHeaderType {
if (textField.text !== port) { Layout.fillWidth: true
port = textField.text headerText: qsTr("WG settings")
}
}
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); TextFieldWithHeaderType {
InstallController.updateContainer(WireGuardConfigModel.getConfig()) id: vpnAddressSubnetTextField
} Layout.fillWidth: true
var noButtonFunction = function() { Layout.topMargin: 40
if (!GC.isMobile()) {
saveButton.forceActiveFocus() 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()
} }
} }
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
} }
Keys.onEnterPressed: saveButton.clicked()
Keys.onReturnPressed: saveButton.clicked()
} }
} }
} }
+132 -110
View File
@@ -17,141 +17,163 @@ import "../Components"
PageType { PageType {
id: root id: root
BackButtonType { ColumnLayout {
id: backButton 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
onFocusChanged: { BackButtonType {
if (this.activeFocus) { id: backButton
listView.positionViewAtBeginning()
}
} }
} }
ListViewType { FlickableType {
id: listView id: fl
anchors.top: backButtonLayout.bottom
anchors.top: backButton.bottom
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
anchors.left: parent.left contentHeight: content.implicitHeight
anchors.right: parent.right
enabled: ServersModel.isProcessedServerHasWriteAccess() Column {
model: XrayConfigModel id: content
delegate: ColumnLayout { anchors.top: parent.top
width: listView.width anchors.left: parent.left
anchors.right: parent.right
property alias focusItemId: textFieldWithHeaderType.textField enabled: ServersModel.isProcessedServerHasWriteAccess()
spacing: 0 ListView {
id: listview
BaseHeaderType { width: parent.width
Layout.fillWidth: true height: listview.contentItem.height
Layout.leftMargin: 16
Layout.rightMargin: 16
headerText: qsTr("XRay settings")
}
TextFieldWithHeaderType { clip: true
id: textFieldWithHeaderType interactive: false
Layout.fillWidth: true model: XrayConfigModel
Layout.topMargin: 32
Layout.leftMargin: 16
Layout.rightMargin: 16
enabled: listView.enabled delegate: Item {
id: delegateItem
headerText: qsTr("Disguised as traffic from") property alias focusItemId: textFieldWithHeaderType.textField
textField.text: site property bool isEnabled: ServersModel.isProcessedServerHasWriteAccess()
textField.onEditingFinished: { implicitWidth: listview.width
if (textField.text !== site) { implicitHeight: col.implicitHeight
var tmpText = textField.text
tmpText = tmpText.toLocaleLowerCase()
if (tmpText.startsWith("https://")) { ColumnLayout {
tmpText = textField.text.substring(8) id: col
site = tmpText
} else { anchors.top: parent.top
site = textField.text anchors.left: parent.left
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()
} }
} }
} }
checkEmptyText: true
}
TextFieldWithHeaderType {
id: portTextField
Layout.fillWidth: true
Layout.topMargin: 16
Layout.leftMargin: 16
Layout.rightMargin: 16
enabled: listView.enabled
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 === ""
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())
}
var noButtonFunction = function() {
if (!GC.isMobile()) {
saveButton.forceActiveFocus()
}
}
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
}
Keys.onEnterPressed: saveButton.clicked()
Keys.onReturnPressed: saveButton.clicked()
} }
} }
} }
} }
+29 -27
View File
@@ -16,53 +16,50 @@ import "../Components"
PageType { PageType {
id: root id: root
BackButtonType { ColumnLayout {
id: backButton 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
onFocusChanged: { BackButtonType {
if (this.activeFocus) { id: backButton
listView.positionViewAtBeginning()
}
} }
} }
ListViewType { FlickableType {
id: listView id: fl
anchors.top: backButtonLayout.bottom
anchors.top: backButton.bottom
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
anchors.right: parent.right contentHeight: content.implicitHeight
anchors.left: parent.left
header: ColumnLayout { ColumnLayout {
width: listView.width id: content
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 {
Layout.fillWidth: true id: removeButton
Layout.leftMargin: 16
Layout.rightMargin: 16 Layout.topMargin: 24
width: parent.width
text: qsTr("Remove ") + ContainersModel.getProcessedContainerName() text: qsTr("Remove ") + ContainersModel.getProcessedContainerName()
textColor: AmneziaStyle.color.vibrantRed textColor: AmneziaStyle.color.vibrantRed
@@ -74,14 +71,19 @@ 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)
} }
+227 -184
View File
@@ -24,215 +24,258 @@ PageType {
} }
} }
BackButtonType { ColumnLayout {
id: backButton 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
onFocusChanged: { BackButtonType {
if (this.activeFocus) { id: backButton
listView.positionViewAtBeginning()
}
} }
} }
ListViewType { FlickableType {
id: listView id: fl
anchors.top: backButtonLayout.bottom
anchors.top: backButton.bottom
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
anchors.right: parent.right contentHeight: content.implicitHeight
anchors.left: parent.left
enabled: ServersModel.isProcessedServerHasWriteAccess() Column {
id: content
model: SftpConfigModel anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
delegate: ColumnLayout { enabled: ServersModel.isProcessedServerHasWriteAccess()
width: listView.width
spacing: 0 ListView {
id: listview
BaseHeaderType { width: parent.width
Layout.fillWidth: true height: listview.contentItem.height
Layout.leftMargin: 16
Layout.rightMargin: 16
headerText: qsTr("SFTP settings") clip: true
} interactive: false
LabelWithButtonType { model: SftpConfigModel
id: hostLabel
Layout.fillWidth: true onFocusChanged: {
Layout.topMargin: 32 if (focus) {
Layout.leftMargin: 16 listview.currentItem.listViewFocusItem.forceActiveFocus()
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
} }
MouseArea { delegate: Item {
anchors.fill: parent implicitWidth: listview.width
acceptedButtons: Qt.NoButton implicitHeight: col.implicitHeight
cursorShape: parent.hoveredLink ? Qt.PointingHandCursor : Qt.ArrowCursor
}
}
BasicButtonType { property alias listViewFocusItem: hostLabel.rightButton
id: detailedInstructionsButton
Layout.topMargin: 16 ColumnLayout {
Layout.bottomMargin: 16 id: col
Layout.leftMargin: 8
implicitHeight: 32
defaultColor: AmneziaStyle.color.transparent anchors.top: parent.top
hoveredColor: AmneziaStyle.color.translucentWhite anchors.left: parent.left
pressedColor: AmneziaStyle.color.sheerWhite anchors.right: parent.right
disabledColor: AmneziaStyle.color.mutedGray
textColor: AmneziaStyle.color.goldenApricot
text: qsTr("Detailed instructions") spacing: 0
clickedFunc: function() { BaseHeaderType {
// Qt.openUrlExternally("https://github.com/amnezia-vpn/desktop-client/releases/latest") Layout.fillWidth: true
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")
}
}
}
} }
} }
} }
@@ -25,290 +25,327 @@ PageType {
} }
} }
BackButtonType { ColumnLayout {
id: backButton 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
onFocusChanged: { BackButtonType {
if (this.activeFocus) { id: backButton
listView.positionViewAtBeginning()
}
} }
} }
ListViewType { FlickableType {
id: listView id: fl
anchors.top: backButtonLayout.bottom
anchors.top: backButton.bottom
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
anchors.right: parent.right contentHeight: listview.implicitHeight
anchors.left: parent.left
model: Socks5ProxyConfigModel ListView {
id: listview
delegate: ColumnLayout { width: parent.width
width: listView.width height: listview.contentItem.height
spacing: 0 clip: true
interactive: false
BaseHeaderType { model: Socks5ProxyConfigModel
Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 16
headerText: qsTr("SOCKS5 settings") onFocusChanged: {
} 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"))
} }
} }
LabelWithButtonType { delegate: Item {
Layout.fillWidth: true implicitWidth: listview.width
Layout.rightMargin: 16 implicitHeight: content.implicitHeight
Layout.bottomMargin: 16
text: qsTr("Port") property alias focusItemId: hostLabel.rightButton
descriptionText: port
descriptionOnTop: true ColumnLayout {
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
Connections { spacing: 0
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")
} }
TextFieldWithHeaderType { LabelWithButtonType {
id: portTextField id: hostLabel
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 40 Layout.topMargin: 32
Layout.rightMargin: 16
Layout.bottomMargin: 16
headerText: qsTr("Port") parentFlickable: fl
textField.text: port
textField.maximumLength: 5
textField.validator: IntValidator { bottom: 1; top: 65535 }
textField.onEditingFinished: { text: qsTr("Host")
textField.text = textField.text.replace(/^\s+|\s+$/g, '') descriptionText: ServersModel.getProcessedServerData("hostName")
if (textField.text !== port) {
port = textField.text 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()
} }
} }
} }
TextFieldWithHeaderType { LabelWithButtonType {
id: usernameTextField id: portLabel
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 16
Layout.rightMargin: 16
Layout.bottomMargin: 16
headerText: qsTr("Username") text: qsTr("Port")
textField.placeholderText: "username" descriptionText: port
textField.text: username
textField.maximumLength: 32
textField.onEditingFinished: { descriptionOnTop: true
textField.text = textField.text.replace(/^\s+|\s+$/g, '')
if (textField.text !== username) { parentFlickable: fl
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()
} }
} }
} }
TextFieldWithHeaderType { LabelWithButtonType {
id: passwordTextField id: usernameLabel
property bool hidePassword: true
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 16
Layout.rightMargin: 16
Layout.bottomMargin: 16
headerText: qsTr("Password") text: qsTr("User name")
textField.placeholderText: "password" descriptionText: username
textField.text: password
textField.maximumLength: 32
textField.echoMode: hidePassword ? TextInput.Password : TextInput.Normal descriptionOnTop: true
buttonImageSource: textField.text !== "" ? (hidePassword ? "qrc:/images/controls/eye.svg" : "qrc:/images/controls/eye-off.svg")
: ""
clickedFunc: function() { parentFlickable: fl
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()
}
} }
}
textField.onFocusChanged: { LabelWithButtonType {
textField.text = textField.text.replace(/^\s+|\s+$/g, '') id: passwordLabel
if (textField.text !== password) { Layout.fillWidth: true
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: saveButton id: changeSettingsButton
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() {
if (!portTextField.textField.acceptableInput) { forceActiveFocus()
portTextField.errorText = qsTr("The port must be in the range of 1 to 65535") changeSettingsDrawer.openTriggered()
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()
}
}
} }
} }
} }
@@ -25,31 +25,34 @@ PageType {
} }
} }
BackButtonType { ColumnLayout {
id: backButton 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
onFocusChanged: { BackButtonType {
if (this.activeFocus) { id: backButton
listView.positionViewAtBeginning()
}
} }
} }
ListViewType { FlickableType {
id: listView id: fl
anchors.top: backButtonLayout.bottom
anchors.top: backButton.bottom
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
anchors.right: parent.right contentHeight: content.implicitHeight
anchors.left: parent.left
header: ColumnLayout { ColumnLayout {
width: listView.width id: content
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
spacing: 0
BaseHeaderType { BaseHeaderType {
Layout.fillWidth: true Layout.fillWidth: true
@@ -58,19 +61,11 @@ 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 { // TODO(CyAn84): add DelegateChooser after migrate to 6.9
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: {
@@ -88,16 +83,15 @@ 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: 16 Layout.topMargin: 40
Layout.leftMargin: 16 Layout.leftMargin: 16
Layout.rightMargin: 16 Layout.rightMargin: 16
+97 -104
View File
@@ -14,58 +14,130 @@ import "../Config"
PageType { PageType {
id: root id: root
ListViewType { FlickableType {
id: listView id: fl
anchors.top: parent.top
anchors.bottom: parent.bottom
contentHeight: content.height
anchors.fill: parent ColumnLayout {
id: content
header: ColumnLayout { anchors.top: parent.top
width: listView.width anchors.left: parent.left
anchors.right: parent.right
spacing: 0
BaseHeaderType { BaseHeaderType {
id: header id: header
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 24 Layout.topMargin: 24
Layout.bottomMargin: 16
Layout.rightMargin: 16 Layout.rightMargin: 16
Layout.leftMargin: 16 Layout.leftMargin: 16
headerText: qsTr("Settings") headerText: qsTr("Settings")
} }
}
model: settingsEntries
delegate: ColumnLayout {
width: listView.width
spacing: 0
LabelWithButtonType { LabelWithButtonType {
id: account
Layout.fillWidth: true
Layout.topMargin: 16
text: qsTr("Servers")
rightImageSource: "qrc:/images/controls/chevron-right.svg"
leftImageSource: "qrc:/images/controls/server.svg"
clickedFunction: function() {
PageController.goToPage(PageEnum.PageSettingsServersList)
}
}
DividerType {}
LabelWithButtonType {
id: connection
Layout.fillWidth: true Layout.fillWidth: true
visible: isVisible text: qsTr("Connection")
text: title
rightImageSource: "qrc:/images/controls/chevron-right.svg" rightImageSource: "qrc:/images/controls/chevron-right.svg"
leftImageSource: leftImagePath leftImageSource: "qrc:/images/controls/radio.svg"
clickedFunction: clickedHandler clickedFunction: function() {
PageController.goToPage(PageEnum.PageSettingsConnection)
}
}
DividerType {}
LabelWithButtonType {
id: application
Layout.fillWidth: true
text: qsTr("Application")
rightImageSource: "qrc:/images/controls/chevron-right.svg"
leftImageSource: "qrc:/images/controls/app.svg"
clickedFunction: function() {
PageController.goToPage(PageEnum.PageSettingsApplication)
}
}
DividerType {}
LabelWithButtonType {
id: backup
Layout.fillWidth: true
text: qsTr("Backup")
rightImageSource: "qrc:/images/controls/chevron-right.svg"
leftImageSource: "qrc:/images/controls/save.svg"
clickedFunction: function() {
PageController.goToPage(PageEnum.PageSettingsBackup)
}
}
DividerType {}
LabelWithButtonType {
id: about
Layout.fillWidth: true
text: qsTr("About AmneziaVPN")
rightImageSource: "qrc:/images/controls/chevron-right.svg"
leftImageSource: "qrc:/images/controls/amnezia.svg"
clickedFunction: function() {
PageController.goToPage(PageEnum.PageSettingsAbout)
}
}
DividerType {}
LabelWithButtonType {
id: devConsole
visible: SettingsController.isDevModeEnabled
Layout.fillWidth: true
text: qsTr("Dev console")
rightImageSource: "qrc:/images/controls/chevron-right.svg"
leftImageSource: "qrc:/images/controls/bug.svg"
clickedFunction: function() {
PageController.goToPage(PageEnum.PageDevMenu)
}
} }
DividerType { DividerType {
visible: isVisible visible: SettingsController.isDevModeEnabled
} }
}
footer: ColumnLayout {
width: listView.width
LabelWithButtonType { LabelWithButtonType {
id: close id: close
visible: GC.isDesktop() visible: GC.isDesktop()
Layout.fillWidth: true Layout.fillWidth: true
Layout.preferredHeight: about.height
text: qsTr("Close application") text: qsTr("Close application")
leftImageSource: "qrc:/images/controls/x-circle.svg" leftImageSource: "qrc:/images/controls/x-circle.svg"
@@ -77,87 +149,8 @@ PageType {
} }
DividerType { DividerType {
Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 16
visible: GC.isDesktop() visible: GC.isDesktop()
} }
} }
} }
property list<QtObject> settingsEntries: [
servers,
connection,
application,
backup,
about,
devConsole
]
QtObject {
id: servers
property string title: qsTr("Servers")
readonly property string leftImagePath: "qrc:/images/controls/server.svg"
property bool isVisible: true
readonly property var clickedHandler: function() {
PageController.goToPage(PageEnum.PageSettingsServersList)
}
}
QtObject {
id: connection
property string title: qsTr("Connection")
readonly property string leftImagePath: "qrc:/images/controls/radio.svg"
property bool isVisible: true
readonly property var clickedHandler: function() {
PageController.goToPage(PageEnum.PageSettingsConnection)
}
}
QtObject {
id: application
property string title: qsTr("Application")
readonly property string leftImagePath: "qrc:/images/controls/app.svg"
property bool isVisible: true
readonly property var clickedHandler: function() {
PageController.goToPage(PageEnum.PageSettingsApplication)
}
}
QtObject {
id: backup
property string title: qsTr("Backup")
readonly property string leftImagePath: "qrc:/images/controls/save.svg"
property bool isVisible: true
readonly property var clickedHandler: function() {
PageController.goToPage(PageEnum.PageSettingsBackup)
}
}
QtObject {
id: about
property string title: qsTr("About AmneziaVPN")
readonly property string leftImagePath: "qrc:/images/controls/amnezia.svg"
property bool isVisible: true
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)
}
}
} }
+85 -54
View File
@@ -29,7 +29,58 @@ PageType {
} }
} }
ListViewType { 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
]
ListView {
id: listView id: listView
anchors.top: backButton.bottom anchors.top: backButton.bottom
@@ -37,6 +88,38 @@ 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
@@ -87,12 +170,11 @@ PageType {
} }
} }
model: contacts
delegate: ColumnLayout { delegate: ColumnLayout {
width: listView.width width: listView.width
LabelWithButtonType { LabelWithButtonType {
id: telegramButton
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 6 Layout.topMargin: 6
@@ -175,55 +257,4 @@ PageType {
} }
} }
} }
property list<QtObject> contacts: [
telegramGroup,
mail,
github,
website
]
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())
}
}
} }
@@ -119,10 +119,6 @@ PageType {
checkable: !ConnectionController.isConnected checkable: !ConnectionController.isConnected
onClicked: { onClicked: {
if (ConnectionController.isConnectionInProgress) {
PageController.showNotificationMessage(qsTr("Unable change server location while trying to make an active connection"))
return
}
if (ConnectionController.isConnected) { if (ConnectionController.isConnected) {
PageController.showNotificationMessage(qsTr("Unable change server location while there is an active connection")) PageController.showNotificationMessage(qsTr("Unable change server location while there is an active connection"))
return return
@@ -20,49 +20,49 @@ PageType {
id: windows id: windows
readonly property string title: qsTr("Windows") readonly property string title: qsTr("Windows")
readonly property string link: qsTr("documentation/instructions/connect-amnezia-premium#windows") readonly property string link: qsTr("https://docs.amnezia.org/documentation/instructions/connect-amnezia-premium#windows")
} }
QtObject { QtObject {
id: macos id: macos
readonly property string title: qsTr("macOS") readonly property string title: qsTr("macOS")
readonly property string link: qsTr("documentation/instructions/connect-amnezia-premium#macos") readonly property string link: qsTr("https://docs.amnezia.org/documentation/instructions/connect-amnezia-premium#macos")
} }
QtObject { QtObject {
id: android id: android
readonly property string title: qsTr("Android") readonly property string title: qsTr("Android")
readonly property string link: qsTr("documentation/instructions/connect-amnezia-premium#android") readonly property string link: qsTr("https://docs.amnezia.org/documentation/instructions/connect-amnezia-premium#android")
} }
QtObject { QtObject {
id: androidTv id: androidTv
readonly property string title: qsTr("AndroidTV") readonly property string title: qsTr("AndroidTV")
readonly property string link: qsTr("documentation/instructions/android_tv_connect/") readonly property string link: qsTr("https://docs.amnezia.org/ru/documentation/instructions/android_tv_connect/")
} }
QtObject { QtObject {
id: ios id: ios
readonly property string title: qsTr("iOS") readonly property string title: qsTr("iOS")
readonly property string link: qsTr("documentation/instructions/connect-amnezia-premium#ios") readonly property string link: qsTr("https://docs.amnezia.org/documentation/instructions/connect-amnezia-premium#ios")
} }
QtObject { QtObject {
id: linux id: linux
readonly property string title: qsTr("Linux") readonly property string title: qsTr("Linux")
readonly property string link: qsTr("documentation/instructions/connect-amnezia-premium#linux") readonly property string link: qsTr("https://docs.amnezia.org/documentation/instructions/connect-amnezia-premium#linux")
} }
QtObject { QtObject {
id: routers id: routers
readonly property string title: qsTr("Routers") readonly property string title: qsTr("Routers")
readonly property string link: qsTr("documentation/instructions/connect-amnezia-premium#routers") readonly property string link: qsTr("https://docs.amnezia.org/documentation/instructions/connect-amnezia-premium#routers")
} }
property list<QtObject> instructionsModel: [ property list<QtObject> instructionsModel: [
@@ -114,7 +114,7 @@ PageType {
rightImageSource: "qrc:/images/controls/external-link.svg" rightImageSource: "qrc:/images/controls/external-link.svg"
clickedFunction: function() { clickedFunction: function() {
Qt.openUrlExternally(LanguageModel.getCurrentDocsUrl(link)) Qt.openUrlExternally(link)
} }
} }
@@ -22,34 +22,22 @@ PageType {
property string configExtension: ".conf" property string configExtension: ".conf"
property string configCaption: qsTr("Save AmneziaVPN config") property string configCaption: qsTr("Save AmneziaVPN config")
BackButtonType {
id: backButton
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
anchors.topMargin: 20
onActiveFocusChanged: {
if(backButton.enabled && backButton.activeFocus) {
listView.positionViewAtBeginning()
}
}
}
ListViewType { ListViewType {
id: listView id: listView
anchors.top: backButton.bottom anchors.fill: parent
anchors.bottom: parent.bottom anchors.topMargin: 20
anchors.right: parent.right anchors.bottomMargin: 24
anchors.left: parent.left
model: ApiCountryModel model: ApiCountryModel
header: ColumnLayout { header: ColumnLayout {
width: listView.width width: listView.width
BackButtonType {
id: backButton
}
BaseHeaderType { BaseHeaderType {
id: header id: header
@@ -71,7 +59,6 @@ PageType {
text: countryName text: countryName
descriptionText: isWorkerExpired ? qsTr("The configuration needs to be reissued") : "" descriptionText: isWorkerExpired ? qsTr("The configuration needs to be reissued") : ""
hideDescription: isWorkerExpired ? true : false
descriptionColor: AmneziaStyle.color.vibrantRed descriptionColor: AmneziaStyle.color.vibrantRed
leftImageSource: "qrc:/countriesFlags/images/flagKit/" + countryImageCode + ".svg" leftImageSource: "qrc:/countriesFlags/images/flagKit/" + countryImageCode + ".svg"
@@ -117,16 +104,20 @@ PageType {
} }
} }
ListViewType { FlickableType {
id: drawerListView
anchors.top: moreOptionsDrawerBackButton.bottom anchors.top: moreOptionsDrawerBackButton.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
header: ColumnLayout { contentHeight: moreOptionsDrawerContent.height
width: drawerListView.width
ColumnLayout {
id: moreOptionsDrawerContent
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
Header2Type { Header2Type {
Layout.fillWidth: true Layout.fillWidth: true
@@ -134,17 +125,9 @@ PageType {
headerText: moreOptionsDrawer.countryName + qsTr(" configuration file") headerText: moreOptionsDrawer.countryName + qsTr(" configuration file")
} }
}
model: 1 // fake model to force the ListView to be created without a model
delegate: ColumnLayout {
width: drawerListView.width
LabelWithButtonType { LabelWithButtonType {
Layout.fillWidth: true Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 16
text: qsTr("Generate a new configuration file") text: qsTr("Generate a new configuration file")
descriptionText: qsTr("The previously created one will stop working") descriptionText: qsTr("The previously created one will stop working")
@@ -155,16 +138,9 @@ PageType {
} }
DividerType {} DividerType {}
}
footer: ColumnLayout {
width: drawerListView.width
LabelWithButtonType { LabelWithButtonType {
Layout.fillWidth: true Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 16
text: qsTr("Revoke the current configuration file") text: qsTr("Revoke the current configuration file")
clickedFunction: function() { clickedFunction: function() {
@@ -236,7 +212,8 @@ PageType {
} }
moreOptionsDrawer.closeTriggered() moreOptionsDrawer.closeTriggered()
} }
var noButtonFunction = function() {} var noButtonFunction = function() {
}
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction) showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
} }
@@ -111,6 +111,17 @@ PageType {
serverNameEditDrawer.openTriggered() serverNameEditDrawer.openTriggered()
} }
} }
RenameServerDrawer {
id: serverNameEditDrawer
parent: root
anchors.fill: parent
expandedHeight: root.height * 0.35
serverNameText: root.processedServer.name
}
} }
delegate: ColumnLayout { delegate: ColumnLayout {
@@ -187,13 +198,7 @@ PageType {
iconPath: "qrc:/images/controls/alert-circle.svg" iconPath: "qrc:/images/controls/alert-circle.svg"
visible: { visible: ApiAccountInfoModel.data("hasExpiredWorker")
for (let i = 0; i < ApiCountryModel.count; ++i) {
if (ApiCountryModel.get(i).isWorkerExpired)
return true;
}
return false;
}
} }
LabelWithButtonType { LabelWithButtonType {
@@ -202,30 +207,35 @@ PageType {
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: warning.visible ? 16 : 32 Layout.topMargin: warning.visible ? 16 : 32
visible: footer.isVisibleForAmneziaFree visible: false //footer.isVisibleForAmneziaFree
text: qsTr("Subscription Key") text: qsTr("Subscription Key")
rightImageSource: "qrc:/images/controls/chevron-right.svg" rightImageSource: "qrc:/images/controls/chevron-right.svg"
clickedFunction: function() { clickedFunction: function() {
PageController.goToPage(PageEnum.PageSettingsApiSubscriptionKey) shareConnectionDrawer.headerText = qsTr("Amnezia Premium subscription key")
shareConnectionDrawer.openTriggered()
shareConnectionDrawer.isSelfHostedConfig = false;
shareConnectionDrawer.shareButtonText = qsTr("Save VPN key as a file")
shareConnectionDrawer.copyButtonText = qsTr("Copy VPN key")
PageController.showBusyIndicator(true) PageController.showBusyIndicator(true)
ApiConfigsController.prepareVpnKeyExport() ApiConfigsController.prepareVpnKeyExport()
PageController.showBusyIndicator(false) PageController.showBusyIndicator(false)
// Navigate to PageShareConnection page
//PageController.goToPage(PageEnum.PageShareConnection)
} }
} }
DividerType { DividerType {
visible: footer.isVisibleForAmneziaFree visible: false //footer.isVisibleForAmneziaFree
} }
LabelWithButtonType { LabelWithButtonType {
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: warning.visible ? 16 : 32
visible: footer.isVisibleForAmneziaFree visible: footer.isVisibleForAmneziaFree
@@ -410,12 +420,9 @@ PageType {
} }
} }
RenameServerDrawer { ShareConnectionDrawer {
id: serverNameEditDrawer id: shareConnectionDrawer
anchors.fill: parent anchors.fill: parent
expandedHeight: parent.height * 0.35
serverNameText: root.processedServer.name
} }
} }
@@ -1,204 +0,0 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import QtQuick.Dialogs
import Qt.labs.platform 1.1
import QtCore
import PageEnum 1.0
import Style 1.0
import "../Controls2"
import "../Controls2/TextTypes"
import "../Config"
import "../Components"
PageType {
id: root
Component.onCompleted: {
PageController.showBusyIndicator(true)
ApiConfigsController.prepareVpnKeyExport()
PageController.showBusyIndicator(false)
}
FlickableType {
anchors.fill: parent
contentHeight: layout.implicitHeight
ColumnLayout {
id: layout
width: root.width
BackButtonType {
Layout.topMargin: 20
}
Label {
Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 16
Layout.topMargin: 16
text: qsTr("Amnezia Premium\nsubscription key")
font.pixelSize: 32
font.bold: true
color: AmneziaStyle.color.paleGray
wrapMode: Text.Wrap
}
BasicButtonType {
Layout.fillWidth: true
Layout.topMargin: 32
Layout.leftMargin: 16
Layout.rightMargin: 16
defaultColor: AmneziaStyle.color.paleGray
hoveredColor: AmneziaStyle.color.sheerWhite
pressedColor: AmneziaStyle.color.translucentWhite
disabledColor: AmneziaStyle.color.mutedGray
textColor: AmneziaStyle.color.black
leftImageColor: "black"
borderWidth: 1
text: qsTr("Copy key")
leftImageSource: "qrc:/images/controls/copy.svg"
onClicked: {
ApiConfigsController.copyVpnKeyToClipboard()
PageController.showNotificationMessage(qsTr("Copied"))
}
}
BasicButtonType {
Layout.fillWidth: true
Layout.topMargin: 4
Layout.leftMargin: 16
Layout.rightMargin: 16
defaultColor: "transparent"
hoveredColor: AmneziaStyle.color.translucentWhite
pressedColor: AmneziaStyle.color.sheerWhite
textColor: AmneziaStyle.color.paleGray
borderWidth: 1
text: qsTr("Save key as a file")
leftImageSource: "qrc:/images/controls/share-2.svg"
onClicked: {
var fileName = GC.isMobile()
? "amnezia_vpn_key.vpn"
: SystemController.getFileName(
qsTr("Save AmneziaVPN config"),
qsTr("Config files (*.vpn)"),
StandardPaths.standardLocations(StandardPaths.DocumentsLocation) + "/amnezia_vpn_key",
true,
".vpn"
)
if (fileName !== "") {
PageController.showBusyIndicator(true)
ExportController.exportConfig(fileName)
PageController.showBusyIndicator(false)
}
}
}
BasicButtonType {
Layout.fillWidth: true
Layout.topMargin: 24
Layout.leftMargin: 16
Layout.rightMargin: 16
defaultColor: "transparent"
hoveredColor: AmneziaStyle.color.translucentWhite
pressedColor: AmneziaStyle.color.sheerWhite
textColor: AmneziaStyle.color.paleGray
borderWidth: 1
text: qsTr("Show key text")
leftImageSource: "qrc:/images/controls/eye.svg"
onClicked: {
PageController.showBusyIndicator(true)
ApiConfigsController.prepareVpnKeyExport()
PageController.showBusyIndicator(false)
vpnKeyDrawer.openTriggered()
}
}
Rectangle {
Layout.fillWidth: true
Layout.preferredHeight: width
Layout.topMargin: 20
Layout.leftMargin: 16
Layout.rightMargin: 16
visible: ApiConfigsController.qrCodesCount > 0
color: "white"
radius: 12
Image {
anchors.fill: parent
smooth: false
source: ApiConfigsController.qrCodesCount > 0 && ApiConfigsController.qrCodes[0] ? ApiConfigsController.qrCodes[0] : ""
}
}
ParagraphTextType {
Layout.fillWidth: true
Layout.topMargin: 24
Layout.bottomMargin: 16
Layout.leftMargin: 16
Layout.rightMargin: 16
visible: ApiConfigsController.qrCodesCount > 0
horizontalAlignment: Text.AlignHCenter
text: qsTr("To read the QR code in the Amnezia app, tap + in the main menu → 'QR code'")
}
}
}
DrawerType2 {
id: vpnKeyDrawer
anchors.fill: root
expandedHeight: root.height * 0.9
expandedStateContent: Item {
BackButtonType {
anchors.top: parent.top
anchors.left: parent.left
anchors.topMargin: 16
backButtonFunction: function() { vpnKeyDrawer.closeTriggered() }
}
ColumnLayout {
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
anchors.topMargin: 56
anchors.leftMargin: 16
anchors.rightMargin: 16
Header2Type {
Layout.fillWidth: true
headerText: qsTr("Amnezia Premium Subscription key")
}
TextArea {
Layout.fillWidth: true
Layout.topMargin: 16
readOnly: true
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: ApiConfigsController.vpnKey //|| ""
wrapMode: Text.Wrap
background: Rectangle { color: AmneziaStyle.color.transparent }
}
}
}
}
}
@@ -50,7 +50,6 @@ 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
@@ -112,7 +111,7 @@ PageType {
headerText: qsTr("Mode") headerText: qsTr("Mode")
enabled: (Qt.platform.os === "android") && root.pageEnabled enabled: Qt.platform.os === "android" && root.pageEnabled
listView: ListViewWithRadioButtonType { listView: ListViewWithRadioButtonType {
rootWidth: root.width rootWidth: root.width
@@ -147,56 +146,77 @@ PageType {
} }
} }
ListViewType { FlickableType {
id: listView
anchors.top: header.bottom anchors.top: header.bottom
anchors.bottom: addAppButton.top anchors.topMargin: 16
anchors.left: parent.left contentHeight: col.implicitHeight + addAppButton.implicitHeight + addAppButton.anchors.bottomMargin + addAppButton.anchors.topMargin
anchors.right: parent.right
model: SortFilterProxyModel { enabled: root.pageEnabled
id: proxyAppSplitTunnelingModel
sourceModel: AppSplitTunnelingModel
filters: RegExpFilter {
roleName: "appPath"
pattern: ".*" + searchField.textField.text + ".*"
caseSensitivity: Qt.CaseInsensitive
}
sorters: [
RoleSorter { roleName: "appPath"; sortOrder: Qt.AscendingOrder }
]
}
delegate: ColumnLayout { Column {
width: listView.width id: col
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
LabelWithButtonType { ListView {
Layout.fillWidth: true id: apps
width: parent.width
height: apps.contentItem.height
Layout.leftMargin: 16 model: SortFilterProxyModel {
Layout.rightMargin: 16 id: proxyAppSplitTunnelingModel
sourceModel: AppSplitTunnelingModel
text: appPath filters: RegExpFilter {
rightImageSource: "qrc:/images/controls/trash.svg" roleName: "appPath"
rightImageColor: AmneziaStyle.color.paleGray pattern: ".*" + searchField.textField.text + ".*"
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))
}
var noButtonFunction = function() {
} }
sorters: [
RoleSorter { roleName: "appPath"; sortOrder: Qt.AscendingOrder }
]
}
showQuestionDrawer(headerText, "", yesButtonText, noButtonText, yesButtonFunction, noButtonFunction) 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 {}
}
} }
} }
DividerType {}
} }
} }
@@ -21,24 +21,22 @@ PageType {
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
anchors.topMargin: 20 anchors.topMargin: 20
onActiveFocusChanged: {
if(backButton.enabled && backButton.activeFocus) {
listView.positionViewAtBeginning()
}
}
} }
ListViewType { FlickableType {
id: listView id: fl
anchors.top: backButton.bottom anchors.top: backButton.bottom
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
anchors.left: parent.left contentHeight: content.height
anchors.right: parent.right
header: ColumnLayout { ColumnLayout {
width: listView.width id: content
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
spacing: 0
BaseHeaderType { BaseHeaderType {
Layout.fillWidth: true Layout.fillWidth: true
@@ -47,17 +45,9 @@ PageType {
headerText: qsTr("Application") headerText: qsTr("Application")
} }
}
model: 1 // fake model to force the ListView to be created without a model
delegate: ColumnLayout { // TODO(CyAn84): add DelegateChooser when have migrated to 6.9
width: listView.width
SwitcherType { SwitcherType {
id: switcherAllowScreenshots id: switcher
visible: GC.isMobile() visible: GC.isMobile()
Layout.fillWidth: true Layout.fillWidth: true
@@ -71,6 +61,10 @@ PageType {
SettingsController.toggleScreenshotsEnabled(checked) SettingsController.toggleScreenshotsEnabled(checked)
} }
} }
// KeyNavigation.tab: Qt.platform.os === "android" && !SettingsController.isNotificationPermissionGranted ?
// labelWithButtonNotification.rightButton : labelWithButtonLanguage.rightButton
parentFlickable: fl
} }
DividerType { DividerType {
@@ -79,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()
} }
@@ -99,7 +93,6 @@ PageType {
SwitcherType { SwitcherType {
id: switcherAutoStart id: switcherAutoStart
visible: !GC.isMobile() visible: !GC.isMobile()
Layout.fillWidth: true Layout.fillWidth: true
@@ -108,6 +101,8 @@ 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()) {
@@ -122,7 +117,6 @@ PageType {
SwitcherType { SwitcherType {
id: switcherAutoConnect id: switcherAutoConnect
visible: !GC.isMobile() visible: !GC.isMobile()
Layout.fillWidth: true Layout.fillWidth: true
@@ -131,6 +125,8 @@ 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()) {
@@ -145,17 +141,15 @@ PageType {
SwitcherType { SwitcherType {
id: switcherStartMinimized id: switcherStartMinimized
visible: !GC.isMobile() visible: !GC.isMobile()
Layout.fillWidth: true Layout.fillWidth: true
Layout.margins: 16 Layout.margins: 16
text: qsTr("Start minimized") text: qsTr("Start minimized")
descriptionText: qsTr("Launch application minimized (works with autostart option turned on)") descriptionText: qsTr("Launch application minimized")
enabled: switcherAutoStart.checked parentFlickable: fl
opacity: enabled ? 1.0 : 0.5
checked: SettingsController.isStartMinimizedEnabled() checked: SettingsController.isStartMinimizedEnabled()
onCheckedChanged: { onCheckedChanged: {
@@ -168,21 +162,17 @@ 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()
} }
@@ -192,13 +182,14 @@ 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)
} }
@@ -208,13 +199,14 @@ 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.")
+16 -34
View File
@@ -41,68 +41,51 @@ PageType {
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
anchors.topMargin: 20 anchors.topMargin: 20
onActiveFocusChanged: {
if(backButton.enabled && backButton.activeFocus) {
listView.positionViewAtBeginning()
}
}
} }
ListViewType { FlickableType {
id: listView id: fl
anchors.top: backButton.bottom anchors.top: backButton.bottom
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
anchors.left: parent.left contentHeight: content.height
anchors.right: parent.right
header: ColumnLayout { ColumnLayout {
id: content
width: listView.width anchors.top: parent.top
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 { // TODO(CyAn84): add DelegateChooser when have migrated to 6.9
width: listView.width
spacing: 16
WarningType { WarningType {
Layout.fillWidth: true
Layout.topMargin: 16 Layout.topMargin: 16
Layout.leftMargin: 16 Layout.fillWidth: true
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()) {
@@ -125,11 +108,8 @@ 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
@@ -140,6 +120,8 @@ 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)"))
+16 -30
View File
@@ -21,25 +21,20 @@ PageType {
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
anchors.topMargin: 20 anchors.topMargin: 20
onActiveFocusChanged: {
if(backButton.enabled && backButton.activeFocus) {
listView.positionViewAtBeginning()
}
}
} }
ListViewType { FlickableType {
id: listView id: fl
anchors.top: backButton.bottom anchors.top: backButton.bottom
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
anchors.left: parent.left contentHeight: content.height
anchors.right: parent.right
header: ColumnLayout { ColumnLayout {
id: content
width: listView.width anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
BaseHeaderType { BaseHeaderType {
Layout.fillWidth: true Layout.fillWidth: true
@@ -48,17 +43,9 @@ PageType {
headerText: qsTr("Connection") headerText: qsTr("Connection")
} }
}
model: 1 // fake model to force the ListView to be created without a model
delegate: ColumnLayout { // TODO(CyAn84): add DelegateChooser when have migrated to 6.9
width: listView.width
SwitcherType { SwitcherType {
id: amneziaDnsSwitch id: amneziaDnsSwitch
Layout.fillWidth: true Layout.fillWidth: true
Layout.margins: 16 Layout.margins: 16
@@ -77,13 +64,14 @@ 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)
} }
@@ -93,13 +81,14 @@ 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)
} }
@@ -107,15 +96,8 @@ PageType {
DividerType {} DividerType {}
}
footer: ColumnLayout { // TODO(CyAn84): move to delegate,add DelegateChooser when have migrated to 6.9
width: listView.width
LabelWithButtonType { LabelWithButtonType {
id: splitTunnelingButton2 id: splitTunnelingButton2
visible: root.isAppSplitTinnelingEnabled visible: root.isAppSplitTinnelingEnabled
Layout.fillWidth: true Layout.fillWidth: true
@@ -124,6 +106,8 @@ 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)
} }
@@ -143,6 +127,8 @@ 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)
} }
+13 -36
View File
@@ -21,21 +21,13 @@ PageType {
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
anchors.topMargin: 20 anchors.topMargin: 20
onFocusChanged: {
if (this.activeFocus) {
listView.positionViewAtBeginning()
}
}
} }
ListViewType { FlickableType {
id: listView id: fl
anchors.top: backButton.bottom anchors.top: backButton.bottom
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
anchors.right: parent.right contentHeight: content.height
anchors.left: parent.left
property var isServerFromApi: ServersModel.isServerFromApi(ServersModel.defaultIndex) property var isServerFromApi: ServersModel.isServerFromApi(ServersModel.defaultIndex)
@@ -47,40 +39,32 @@ PageType {
} }
} }
header: ColumnLayout { ColumnLayout {
width: listView.width id: content
anchors.top: parent.top
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("DNS servers") headerText: qsTr("DNS servers")
} }
ParagraphTextType { ParagraphTextType {
Layout.fillWidth: true Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 16
text: qsTr("If AmneziaDNS is not used or installed") text: qsTr("If AmneziaDNS is not used or installed")
} }
}
model: 1 // fake model to force the ListView to be created without a model
delegate: ColumnLayout {
width: listView.width
spacing: 16
TextFieldWithHeaderType { TextFieldWithHeaderType {
id: primaryDns id: primaryDns
Layout.fillWidth: true Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 16
headerText: qsTr("Primary DNS") headerText: qsTr("Primary DNS")
textField.text: SettingsController.primaryDns textField.text: SettingsController.primaryDns
@@ -93,9 +77,6 @@ PageType {
id: secondaryDns id: secondaryDns
Layout.fillWidth: true Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 16
headerText: qsTr("Secondary DNS") headerText: qsTr("Secondary DNS")
textField.text: SettingsController.secondaryDns textField.text: SettingsController.secondaryDns
@@ -106,11 +87,7 @@ PageType {
BasicButtonType { BasicButtonType {
id: restoreDefaultButton id: restoreDefaultButton
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 16
Layout.leftMargin: 16
Layout.rightMargin: 16
defaultColor: AmneziaStyle.color.transparent defaultColor: AmneziaStyle.color.transparent
hoveredColor: AmneziaStyle.color.translucentWhite hoveredColor: AmneziaStyle.color.translucentWhite
@@ -144,7 +121,6 @@ PageType {
id: saveButton id: saveButton
Layout.fillWidth: true Layout.fillWidth: true
Layout.margins: 16
text: qsTr("Save") text: qsTr("Save")
@@ -160,4 +136,5 @@ PageType {
} }
} }
} }
} }
@@ -71,9 +71,6 @@ PageType {
onClicked: function() { onClicked: function() {
SettingsController.strictKillSwitchEnabled = false SettingsController.strictKillSwitchEnabled = false
} }
Keys.onEnterPressed: this.clicked()
Keys.onReturnPressed: this.clicked()
} }
DividerType {} DividerType {}
@@ -106,9 +103,6 @@ PageType {
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction) showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
} }
Keys.onEnterPressed: this.clicked()
Keys.onReturnPressed: this.clicked()
} }
DividerType { DividerType {
@@ -64,7 +64,7 @@ PageType {
displayMarginBeginning: 40 displayMarginBeginning: 40
displayMarginEnd: 40 displayMarginEnd: 40
ScrollBar.vertical: ScrollBarType {} ScrollBar.vertical: ScrollBarType { }
footer: Item { footer: Item {
width: listView.width width: listView.width
+7 -8
View File
@@ -23,15 +23,9 @@ PageType {
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
anchors.topMargin: 20 anchors.topMargin: 20
onFocusChanged: {
if (this.activeFocus) {
listView.positionViewAtBeginning()
}
}
} }
ListViewType { ListView {
id: listView id: listView
anchors.top: backButton.bottom anchors.top: backButton.bottom
@@ -39,6 +33,10 @@ 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
@@ -103,7 +101,8 @@ PageType {
} }
model: logTypes model: logTypes
clip: true
reuseItems: true
snapMode: ListView.SnapOneItem snapMode: ListView.SnapOneItem
delegate: ColumnLayout { delegate: ColumnLayout {
+163 -135
View File
@@ -18,7 +18,9 @@ PageType {
signal lastItemTabClickedSignal() signal lastItemTabClickedSignal()
property bool isServerWithWriteAccess: ServersModel.isProcessedServerHasWriteAccess() onFocusChanged: content.isServerWithWriteAccess ?
labelWithButton.forceActiveFocus() :
labelWithButton3.forceActiveFocus()
Connections { Connections {
target: InstallController target: InstallController
@@ -61,192 +63,218 @@ PageType {
target: ServersModel target: ServersModel
function onProcessedServerIndexChanged() { function onProcessedServerIndexChanged() {
root.isServerWithWriteAccess = ServersModel.isProcessedServerHasWriteAccess() content.isServerWithWriteAccess = ServersModel.isProcessedServerHasWriteAccess()
} }
} }
ListViewType { FlickableType {
id: listView id: fl
anchors.top: parent.top
anchors.bottom: parent.bottom
contentHeight: content.height
anchors.fill: parent ColumnLayout {
id: content
model: serverActions anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
delegate: ColumnLayout { property bool isServerWithWriteAccess: ServersModel.isProcessedServerHasWriteAccess()
width: listView.width
LabelWithButtonType { LabelWithButtonType {
id: labelWithButton
visible: content.isServerWithWriteAccess
Layout.fillWidth: true Layout.fillWidth: true
visible: isVisible text: qsTr("Check the server for previously installed Amnezia services")
descriptionText: qsTr("Add them to the application if they were not displayed")
text: title
descriptionText: description
textColor: tColor
clickedFunction: function() { clickedFunction: function() {
clickedHandler() PageController.showBusyIndicator(true)
InstallController.scanServerForInstalledContainers()
PageController.showBusyIndicator(false)
} }
} }
DividerType { DividerType {
visible: isVisible visible: content.isServerWithWriteAccess
} }
}
}
property list<QtObject> serverActions: [ LabelWithButtonType {
check, id: labelWithButton2
reboot, visible: content.isServerWithWriteAccess
remove, Layout.fillWidth: true
clear,
reset,
switch_to_premium,
]
QtObject { text: qsTr("Reboot server")
id: check textColor: AmneziaStyle.color.vibrantRed
property bool isVisible: root.isServerWithWriteAccess clickedFunction: function() {
readonly property string title: qsTr("Check the server for previously installed Amnezia services") var headerText = qsTr("Do you want to reboot the server?")
readonly property string description: qsTr("Add them to the application if they were not displayed") var descriptionText = qsTr("The reboot process may take approximately 30 seconds. Are you sure you wish to proceed?")
readonly property var tColor: AmneziaStyle.color.paleGray var yesButtonText = qsTr("Continue")
readonly property var clickedHandler: function() { var noButtonText = qsTr("Cancel")
PageController.showBusyIndicator(true)
InstallController.scanServerForInstalledContainers()
PageController.showBusyIndicator(false)
}
}
QtObject { var yesButtonFunction = function() {
id: reboot if (ServersModel.isDefaultServerCurrentlyProcessed() && ConnectionController.isConnected) {
PageController.showNotificationMessage(qsTr("Cannot reboot server during active connection"))
} else {
PageController.showBusyIndicator(true)
InstallController.rebootProcessedServer()
PageController.showBusyIndicator(false)
}
if (!GC.isMobile()) {
labelWithButton5.forceActiveFocus()
}
}
var noButtonFunction = function() {
if (!GC.isMobile()) {
labelWithButton2.forceActiveFocus()
}
}
property bool isVisible: root.isServerWithWriteAccess showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
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)
InstallController.rebootProcessedServer()
PageController.showBusyIndicator(false)
} }
} }
var noButtonFunction = function() {
DividerType {
visible: content.isServerWithWriteAccess
} }
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction) LabelWithButtonType {
} id: labelWithButton3
} Layout.fillWidth: true
QtObject { text: qsTr("Remove server from application")
id: remove textColor: AmneziaStyle.color.vibrantRed
property bool isVisible: true clickedFunction: function() {
readonly property string title: qsTr("Remove server from application") var headerText = qsTr("Do you want to remove the server from application?")
readonly property string description: "" var descriptionText = qsTr("All installed AmneziaVPN services will still remain on the server.")
readonly property var tColor: AmneziaStyle.color.vibrantRed var yesButtonText = qsTr("Continue")
readonly property var clickedHandler: function() { var noButtonText = qsTr("Cancel")
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 remove server during active connection")) PageController.showNotificationMessage(qsTr("Cannot remove server during active connection"))
} else { } else {
PageController.showBusyIndicator(true) PageController.showBusyIndicator(true)
InstallController.removeProcessedServer() InstallController.removeProcessedServer()
PageController.showBusyIndicator(false) 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 {}
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction) LabelWithButtonType {
} id: labelWithButton4
} visible: content.isServerWithWriteAccess
Layout.fillWidth: true
QtObject { text: qsTr("Clear server from Amnezia software")
id: clear textColor: AmneziaStyle.color.vibrantRed
property bool isVisible: root.isServerWithWriteAccess clickedFunction: function() {
readonly property string title: qsTr("Clear server from Amnezia software") var headerText = qsTr("Do you want to clear server from Amnezia software?")
readonly property string description: "" var descriptionText = qsTr("All users whom you shared a connection with will no longer be able to connect to it.")
readonly property var tColor: AmneziaStyle.color.vibrantRed var yesButtonText = qsTr("Continue")
readonly property var clickedHandler: function() { var noButtonText = qsTr("Cancel")
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 clear server from Amnezia software during active connection")) PageController.showNotificationMessage(qsTr("Cannot clear server from Amnezia software during active connection"))
} else { } else {
PageController.goToPage(PageEnum.PageDeinstalling) PageController.goToPage(PageEnum.PageDeinstalling)
InstallController.removeAllContainers() InstallController.removeAllContainers()
}
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
} }
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction) LabelWithButtonType {
} id: labelWithButton5
} visible: ServersModel.getProcessedServerData("isServerFromTelegramApi")
Layout.fillWidth: true
QtObject { text: qsTr("Reset API config")
id: reset textColor: AmneziaStyle.color.vibrantRed
property bool isVisible: ServersModel.getProcessedServerData("isServerFromTelegramApi") clickedFunction: function() {
readonly property string title: qsTr("Reset API config") var headerText = qsTr("Do you want to reset API config?")
readonly property string description: "" var descriptionText = ""
readonly property var tColor: AmneziaStyle.color.vibrantRed var yesButtonText = qsTr("Continue")
readonly property var clickedHandler: function() { var noButtonText = qsTr("Cancel")
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 reset API config during active connection")) PageController.showNotificationMessage(qsTr("Cannot reset API config during active connection"))
} else { } else {
PageController.showBusyIndicator(true) PageController.showBusyIndicator(true)
InstallController.removeApiConfig(ServersModel.processedIndex) InstallController.removeApiConfig(ServersModel.processedIndex)
PageController.showBusyIndicator(false) PageController.showBusyIndicator(false)
}
if (!GC.isMobile()) {
labelWithButton5.forceActiveFocus()
}
}
var noButtonFunction = function() {
if (!GC.isMobile()) {
labelWithButton5.forceActiveFocus()
}
}
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
} }
} }
var noButtonFunction = function() {
DividerType {
visible: ServersModel.getProcessedServerData("isServerFromTelegramApi")
} }
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction) LabelWithButtonType {
} id: labelWithButton6
} visible: ServersModel.getProcessedServerData("isServerFromTelegramApi") && ServersModel.processedServerIsPremium
Layout.fillWidth: true
QtObject { text: qsTr("Switch to the new Amnezia Premium subscription")
id: switch_to_premium textColor: AmneziaStyle.color.vibrantRed
property bool isVisible: ServersModel.getProcessedServerData("isServerFromTelegramApi") && ServersModel.processedServerIsPremium clickedFunction: function() {
readonly property string title: qsTr("Switch to the new Amnezia Premium subscription") PageController.goToPageHome()
readonly property string description: "" ApiPremV1MigrationController.showMigrationDrawer()
readonly property var tColor: AmneziaStyle.color.vibrantRed }
readonly property var clickedHandler: function() { }
PageController.goToPageHome()
ApiPremV1MigrationController.showMigrationDrawer() DividerType {
visible: ServersModel.getProcessedServerData("isServerFromTelegramApi") && ServersModel.processedServerIsPremium
}
} }
} }
} }
@@ -21,211 +21,249 @@ PageType {
property bool isClearCacheVisible: ServersModel.isProcessedServerHasWriteAccess() && !ContainersModel.isServiceContainer(ContainersModel.getProcessedContainerIndex()) property bool isClearCacheVisible: ServersModel.isProcessedServerHasWriteAccess() && !ContainersModel.isServiceContainer(ContainersModel.getProcessedContainerIndex())
BackButtonType { ColumnLayout {
id: backButton 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
onFocusChanged: { BackButtonType {
if (this.activeFocus) { id: backButton
listView.positionViewAtBeginning()
}
}
}
ListViewType {
id: listView
anchors.top: backButton.bottom
anchors.bottom: parent.bottom
anchors.right: parent.right
anchors.left: parent.left
header: ColumnLayout {
width: listView.width
BaseHeaderType {
Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 16
Layout.bottomMargin: 32
headerText: ContainersModel.getProcessedContainerName() + qsTr(" settings")
}
} }
model: ProtocolsModel BaseHeaderType {
Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 16
Layout.bottomMargin: 32
delegate: ColumnLayout { headerText: ContainersModel.getProcessedContainerName() + qsTr(" settings")
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 { ListView {
id: protocols
Layout.fillWidth: true
height: protocols.contentItem.height
clip: true
interactive: true
width: listView.width property bool isFocusable: true
LabelWithButtonType { Keys.onTabPressed: {
id: clearCacheButton FocusController.nextKeyTabItem()
}
Layout.fillWidth: true Keys.onBacktabPressed: {
FocusController.previousKeyTabItem()
}
visible: root.isClearCacheVisible Keys.onUpPressed: {
FocusController.nextKeyUpItem()
}
text: qsTr("Clear profile") Keys.onDownPressed: {
FocusController.nextKeyDownItem()
}
clickedFunction: function() { Keys.onLeftPressed: {
var headerText = qsTr("Clear %1 profile?").arg(ContainersModel.getProcessedContainerName()) FocusController.nextKeyLeftItem()
var descriptionText = qsTr("The connection configuration will be deleted for this device only") }
var yesButtonText = qsTr("Continue")
var noButtonText = qsTr("Cancel")
var yesButtonFunction = function() { Keys.onRightPressed: {
if (ConnectionController.isConnected && ServersModel.getDefaultServerData("defaultContainer") === ContainersModel.getProcessedContainerIndex()) { FocusController.nextKeyRightItem()
var message = qsTr("Unable to clear %1 profile while there is an active connection").arg(ContainersModel.getProcessedContainerName()) }
PageController.showNotificationMessage(message)
return 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"))
}
} }
PageController.showBusyIndicator(true) MouseArea {
InstallController.clearCachedProfile() anchors.fill: clientSettings
PageController.showBusyIndicator(false) cursorShape: Qt.PointingHandCursor
} enabled: 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() {
DividerType {
visible: delegateContent.isClientSettingsVisible
} }
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction) LabelWithButtonType {
} id: serverSettings
MouseArea { Layout.fillWidth: true
anchors.fill: removeButton
cursorShape: Qt.PointingHandCursor text: protocolName + qsTr(" server settings")
enabled: false 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
}
} }
} }
DividerType { footer: ColumnLayout {
visible: ServersModel.isProcessedServerHasWriteAccess() 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()
}
} }
} }
} }
} }
@@ -40,20 +40,25 @@ PageType {
} }
} }
ListViewType { ListView {
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
@@ -161,7 +161,7 @@ PageType {
} }
} }
ListViewType { ListView {
id: listView id: listView
anchors.top: header.bottom anchors.top: header.bottom
@@ -172,6 +172,8 @@ PageType {
enabled: root.pageEnabled enabled: root.pageEnabled
property bool isFocusable: true
model: SortFilterProxyModel { model: SortFilterProxyModel {
id: proxySitesModel id: proxySitesModel
sourceModel: SitesModel sourceModel: SitesModel
@@ -191,7 +193,13 @@ PageType {
] ]
} }
clip: true
reuseItems: true
delegate: ColumnLayout { delegate: ColumnLayout {
id: delegateContent
width: listView.width width: listView.width
LabelWithButtonType { LabelWithButtonType {
@@ -228,6 +236,7 @@ PageType {
} }
} }
Rectangle { Rectangle {
anchors.fill: addSiteButton anchors.fill: addSiteButton
anchors.bottomMargin: -24 anchors.bottomMargin: -24
@@ -299,7 +308,7 @@ PageType {
Layout.fillWidth: true Layout.fillWidth: true
Layout.margins: 16 Layout.margins: 16
headerText: qsTr("Additional options") headerText: qsTr("Import / Export Sites")
} }
LabelWithButtonType { LabelWithButtonType {
@@ -342,34 +351,6 @@ PageType {
} }
DividerType {} DividerType {}
LabelWithButtonType {
id: clearSitesButton
Layout.fillWidth: true
text: qsTr("Clear site list")
rightImageSource: "qrc:/images/controls/trash.svg"
clickedFunction: function() {
var headerText = qsTr("Clear site list?")
var descriptionText = qsTr("All sites will be removed from list.")
var yesButtonText = qsTr("Continue")
var noButtonText = qsTr("Cancel")
var yesButtonFunction = function() {
PageController.showBusyIndicator(true)
SitesController.removeSites()
PageController.showBusyIndicator(false)
}
var noButtonFunction = function() {
}
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
}
}
DividerType {}
} }
} }
@@ -393,24 +374,22 @@ PageType {
backButtonFunction: function() { backButtonFunction: function() {
importSitesDrawer.closeTriggered() importSitesDrawer.closeTriggered()
} }
onFocusChanged: {
if (this.activeFocus) {
importSitesDrawerListView.positionViewAtBeginning()
}
}
} }
ListViewType { FlickableType {
id: importSitesDrawerListView
anchors.top: importSitesDrawerBackButton.bottom anchors.top: importSitesDrawerBackButton.bottom
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
header: ColumnLayout { contentHeight: importSitesDrawerContent.height
width: importSitesDrawerListView.width
ColumnLayout {
id: importSitesDrawerContent
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
Header2Type { Header2Type {
Layout.fillWidth: true Layout.fillWidth: true
@@ -418,67 +397,49 @@ PageType {
headerText: qsTr("Import a list of sites") headerText: qsTr("Import a list of sites")
} }
}
model: importOptions
delegate: ColumnLayout {
width: importSitesDrawerListView.width
LabelWithButtonType { LabelWithButtonType {
id: importSitesButton2
Layout.fillWidth: true Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 16
text: title text: qsTr("Replace site list")
clickedFunction: function() { clickedFunction: function() {
clickedHandler() var fileName = SystemController.getFileName(qsTr("Open sites file"),
qsTr("Sites files (*.json)"))
if (fileName !== "") {
importSitesDrawerContent.importSites(fileName, true)
}
} }
} }
DividerType {} DividerType {}
LabelWithButtonType {
id: importSitesButton3
Layout.fillWidth: true
text: qsTr("Add imported sites to existing ones")
clickedFunction: function() {
var fileName = SystemController.getFileName(qsTr("Open sites file"),
qsTr("Sites files (*.json)"))
if (fileName !== "") {
importSitesDrawerContent.importSites(fileName, false)
}
}
}
function importSites(fileName, replaceExistingSites) {
PageController.showBusyIndicator(true)
SitesController.importSites(fileName, replaceExistingSites)
PageController.showBusyIndicator(false)
importSitesDrawer.closeTriggered()
moreActionsDrawer.closeTriggered()
}
DividerType {}
} }
} }
} }
} }
property list<QtObject> importOptions: [
replaceOption,
addOption,
]
QtObject {
id: replaceOption
readonly property string title: qsTr("Replace site list")
readonly property var clickedHandler: function() {
var fileName = SystemController.getFileName(qsTr("Open sites file"),
qsTr("Sites files (*.json)"))
if (fileName !== "") {
root.importSites(fileName, true)
}
}
}
QtObject {
id: addOption
readonly property string title: qsTr("Add imported sites to existing ones")
readonly property var clickedHandler: function() {
var fileName = SystemController.getFileName(qsTr("Open sites file"),
qsTr("Sites files (*.json)"))
if (fileName !== "") {
root.importSites(fileName, false)
}
}
}
function importSites(fileName, replaceExistingSites) {
PageController.showBusyIndicator(true)
SitesController.importSites(fileName, replaceExistingSites)
PageController.showBusyIndicator(false)
importSitesDrawer.closeTriggered()
moreActionsDrawer.closeTriggered()
}
} }
@@ -15,31 +15,25 @@ import "../Components"
PageType { PageType {
id: root id: root
BackButtonType { FlickableType {
id: backButton id: fl
anchors.top: parent.top anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
anchors.topMargin: 20
onFocusChanged: {
if (this.activeFocus) {
listView.positionViewAtBeginning()
}
}
}
ListViewType {
id: listView
anchors.top: backButton.bottom
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
anchors.right: parent.right contentHeight: content.height + continueButton.implicitHeight + continueButton.anchors.bottomMargin + continueButton.anchors.topMargin
anchors.left: parent.left
header: ColumnLayout { ColumnLayout {
width: listView.width id: content
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
spacing: 0
BackButtonType {
id: backButton
Layout.topMargin: 20
}
BaseHeaderType { BaseHeaderType {
Layout.fillWidth: true Layout.fillWidth: true
@@ -51,28 +45,53 @@ PageType {
headerText: ApiServicesModel.getSelectedServiceData("name") headerText: ApiServicesModel.getSelectedServiceData("name")
descriptionText: ApiServicesModel.getSelectedServiceData("serviceDescription") descriptionText: ApiServicesModel.getSelectedServiceData("serviceDescription")
} }
}
model: inputFields
spacing: 0
delegate: ColumnLayout {
width: listView.width
LabelWithImageType { LabelWithImageType {
Layout.fillWidth: true Layout.fillWidth: true
Layout.margins: 16 Layout.margins: 16
imageSource: imagePath imageSource: "qrc:/images/controls/map-pin.svg"
leftText: lText leftText: qsTr("For the region")
rightText: rText rightText: ApiServicesModel.getSelectedServiceData("region")
} }
}
footer: ColumnLayout { LabelWithImageType {
width: listView.width Layout.fillWidth: true
Layout.margins: 16
spacing: 0 imageSource: "qrc:/images/controls/tag.svg"
leftText: qsTr("Price")
rightText: ApiServicesModel.getSelectedServiceData("price")
}
LabelWithImageType {
Layout.fillWidth: true
Layout.margins: 16
imageSource: "qrc:/images/controls/history.svg"
leftText: qsTr("Work period")
rightText: ApiServicesModel.getSelectedServiceData("timeLimit")
visible: rightText !== ""
}
LabelWithImageType {
Layout.fillWidth: true
Layout.margins: 16
imageSource: "qrc:/images/controls/gauge.svg"
leftText: qsTr("Speed")
rightText: ApiServicesModel.getSelectedServiceData("speed")
}
LabelWithImageType {
Layout.fillWidth: true
Layout.margins: 16
imageSource: "qrc:/images/controls/info.svg"
leftText: qsTr("Features")
rightText: ""
}
ParagraphTextType { ParagraphTextType {
Layout.fillWidth: true Layout.fillWidth: true
@@ -85,7 +104,7 @@ PageType {
textFormat: Text.RichText textFormat: Text.RichText
text: { text: {
var text = ApiServicesModel.getSelectedServiceData("features") var text = ApiServicesModel.getSelectedServiceData("features")
return text.replace("%1", LanguageModel.getCurrentSiteUrl("free")) return text.replace("%1", LanguageModel.getCurrentSiteUrl())
} }
MouseArea { MouseArea {
@@ -94,84 +113,34 @@ PageType {
cursorShape: parent.hoveredLink ? Qt.PointingHandCursor : Qt.ArrowCursor cursorShape: parent.hoveredLink ? Qt.PointingHandCursor : Qt.ArrowCursor
} }
} }
BasicButtonType {
id: continueButton
Layout.fillWidth: true
Layout.topMargin: 32
Layout.bottomMargin: 32
Layout.leftMargin: 16
Layout.rightMargin: 16
text: qsTr("Connect")
clickedFunc: function() {
var endpoint = ApiServicesModel.getStoreEndpoint()
if (endpoint !== undefined && endpoint !== "") {
Qt.openUrlExternally(endpoint)
PageController.closePage()
PageController.closePage()
} else {
PageController.showBusyIndicator(true)
ApiConfigsController.importServiceFromGateway()
PageController.showBusyIndicator(false)
}
}
}
} }
} }
property list<QtObject> inputFields: [ BasicButtonType {
region, id: continueButton
price,
timeLimit,
speed,
features
]
QtObject { anchors.right: parent.right
id: region anchors.left: parent.left
anchors.bottom: parent.bottom
readonly property string imagePath: "qrc:/images/controls/map-pin.svg" anchors.topMargin: 32
readonly property string lText: qsTr("For the region") anchors.rightMargin: 16
readonly property string rText: ApiServicesModel.getSelectedServiceData("region") anchors.leftMargin: 16
property bool isVisible: true anchors.bottomMargin: 32
}
QtObject { text: qsTr("Connect")
id: price
readonly property string imagePath: "qrc:/images/controls/tag.svg" clickedFunc: function() {
readonly property string lText: qsTr("Price") var endpoint = ApiServicesModel.getStoreEndpoint()
readonly property string rText: ApiServicesModel.getSelectedServiceData("price") if (endpoint !== undefined && endpoint !== "") {
property bool isVisible: true Qt.openUrlExternally(endpoint)
} PageController.closePage()
PageController.closePage()
QtObject { } else {
id: timeLimit PageController.showBusyIndicator(true)
ApiConfigsController.importServiceFromGateway()
readonly property string imagePath: "qrc:/images/controls/history.svg" PageController.showBusyIndicator(false)
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
} }
} }
@@ -14,77 +14,86 @@ import "../Config"
PageType { PageType {
id: root id: root
BackButtonType { ColumnLayout {
id: backButton 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
Layout.topMargin: 20
onActiveFocusChanged: { spacing: 0
if(backButton.enabled && backButton.activeFocus) {
listView.positionViewAtBeginning() BackButtonType {
} id: backButton
Layout.topMargin: 20
}
BaseHeaderType {
Layout.fillWidth: true
Layout.topMargin: 8
Layout.rightMargin: 16
Layout.leftMargin: 16
Layout.bottomMargin: 16
headerText: qsTr("VPN by Amnezia")
descriptionText: qsTr("Choose a VPN service that suits your needs.")
} }
} }
ListViewType { ListView {
id: listView id: servicesListView
anchors.top: backButton.bottom anchors.top: header.bottom
anchors.right: parent.right anchors.right: parent.right
anchors.left: parent.left anchors.left: parent.left
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
anchors.topMargin: 16 anchors.topMargin: 16
header: ColumnLayout {
width: listView.width
BaseHeaderType {
Layout.fillWidth: true
Layout.rightMargin: 16
Layout.leftMargin: 16
Layout.bottomMargin: 24
headerText: qsTr("VPN by Amnezia")
descriptionText: qsTr("Choose a VPN service that suits your needs.")
}
}
spacing: 0 spacing: 0
property bool isFocusable: true
clip: true
reuseItems: true
model: ApiServicesModel model: ApiServicesModel
delegate: ColumnLayout { ScrollBar.vertical: ScrollBarType {}
width: listView.width delegate: Item {
implicitWidth: servicesListView.width
implicitHeight: delegateContent.implicitHeight
enabled: isServiceAvailable enabled: isServiceAvailable
CardWithIconsType { ColumnLayout {
id: card id: delegateContent
Layout.fillWidth: true anchors.fill: parent
Layout.rightMargin: 16
Layout.leftMargin: 16
Layout.bottomMargin: 16
headerText: name CardWithIconsType {
bodyText: cardDescription id: card
footerText: price
rightImageSource: "qrc:/images/controls/chevron-right.svg" Layout.fillWidth: true
Layout.rightMargin: 16
Layout.leftMargin: 16
Layout.bottomMargin: 16
onClicked: { headerText: name
if (isServiceAvailable) { bodyText: cardDescription
ApiServicesModel.setServiceIndex(index) footerText: price
PageController.goToPage(PageEnum.PageSetupWizardApiServiceInfo)
rightImageSource: "qrc:/images/controls/chevron-right.svg"
onClicked: {
if (isServiceAvailable) {
ApiServicesModel.setServiceIndex(index)
PageController.goToPage(PageEnum.PageSetupWizardApiServiceInfo)
}
} }
}
Keys.onEnterPressed: clicked() Keys.onEnterPressed: clicked()
Keys.onReturnPressed: clicked() Keys.onReturnPressed: clicked()
}
} }
} }
} }
@@ -27,11 +27,21 @@ PageType {
} }
} }
ListViewType { ListView {
id: listView id: listView
anchors.fill: parent anchors.fill: parent
property bool isFocusable: true
ScrollBar.vertical: ScrollBarType {}
model: variants
clip: true
reuseItems: true
header: ColumnLayout { header: ColumnLayout {
width: listView.width width: listView.width
@@ -206,8 +216,6 @@ PageType {
} }
} }
model: variants
delegate: ColumnLayout { delegate: ColumnLayout {
width: listView.width width: listView.width
@@ -226,9 +234,6 @@ PageType {
leftImageSource: imageSource leftImageSource: imageSource
onClicked: { handler() } onClicked: { handler() }
Keys.onEnterPressed: this.clicked()
Keys.onReturnPressed: this.clicked()
} }
} }
@@ -28,14 +28,41 @@ PageType {
} }
} }
ListViewType { ListView {
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
@@ -51,6 +78,8 @@ PageType {
model: inputFields model: inputFields
spacing: 16 spacing: 16
clip: true
reuseItems: true
delegate: ColumnLayout { delegate: ColumnLayout {
width: listView.width width: listView.width
@@ -148,9 +177,6 @@ PageType {
onClicked: { onClicked: {
Qt.openUrlExternally(LanguageModel.getCurrentSiteUrl("starter-guide")) Qt.openUrlExternally(LanguageModel.getCurrentSiteUrl("starter-guide"))
} }
Keys.onEnterPressed: this.clicked()
Keys.onReturnPressed: this.clicked()
} }
} }
} }
+79 -87
View File
@@ -20,7 +20,6 @@ PageType {
SortFilterProxyModel { SortFilterProxyModel {
id: proxyContainersModel id: proxyContainersModel
sourceModel: ContainersModel sourceModel: ContainersModel
filters: [ filters: [
ValueFilter { ValueFilter {
@@ -41,98 +40,104 @@ PageType {
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
anchors.topMargin: 20 anchors.topMargin: 20
onActiveFocusChanged: {
if(backButton.enabled && backButton.activeFocus) {
listView.positionViewAtBeginning()
}
}
} }
ButtonGroup { FlickableType {
id: buttonGroup id: fl
}
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
anchors.left: parent.left contentHeight: content.implicitHeight + setupLaterButton.anchors.bottomMargin
anchors.right: parent.right
spacing: 16 Column {
id: content
header: ColumnLayout { 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
Layout.fillWidth: true implicitWidth: parent.width
Layout.rightMargin: 16
Layout.leftMargin: 16
Layout.bottomMargin: 16
headerTextMaximumLineCount: 10 headerTextMaximumLineCount: 10
headerText: qsTr("Choose Installation Type") headerText: qsTr("Choose Installation Type")
} }
}
model: proxyContainersModel ButtonGroup {
currentIndex: 0 id: buttonGroup
}
delegate: ColumnLayout { ListView {
width: listView.width id: containers
width: parent.width
height: containers.contentItem.height
spacing: 16
CardType { currentIndex: 0
id: card clip: true
interactive: false
model: proxyContainersModel
Layout.fillWidth: true property int dockerContainer
Layout.rightMargin: 16 property int containerDefaultPort
Layout.leftMargin: 16 property int containerDefaultTransportProto
Layout.bottomMargin: 16
headerText: easySetupHeader property bool isFocusable: true
bodyText: easySetupDescription
ButtonGroup.group: buttonGroup delegate: Item {
implicitWidth: containers.width
implicitHeight: delegateContent.implicitHeight
onClicked: function() { ColumnLayout {
isEasySetup = true id: delegateContent
var defaultContainerProto = ContainerProps.defaultProtocol(dockerContainer)
listView.dockerContainer = dockerContainer anchors.top: parent.top
listView.containerDefaultPort = ProtocolProps.getPortForInstall(defaultContainerProto) anchors.left: parent.left
listView.containerDefaultTransportProto = ProtocolProps.defaultTransportProto(defaultContainerProto) anchors.right: parent.right
CardType {
id: card
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)
}
}
}
} }
Keys.onReturnPressed: this.clicked() Component.onCompleted: {
Keys.onEnterPressed: this.clicked() var item = containers.itemAtIndex(containers.currentIndex)
if (item !== null) {
var button = item.children[0].children[0]
button.checked = true
button.clicked()
}
}
} }
}
footer: ColumnLayout {
width: listView.width
spacing: 16
DividerType { DividerType {
Layout.fillWidth: true implicitWidth: parent.width
Layout.leftMargin: 16
Layout.rightMargin: 16
} }
CardType { CardType {
Layout.fillWidth: true implicitWidth: parent.width
Layout.leftMargin: 16
Layout.rightMargin: 16
headerText: qsTr("Manual") headerText: qsTr("Manual")
bodyText: qsTr("Choose a VPN protocol") bodyText: qsTr("Choose a VPN protocol")
@@ -141,29 +146,25 @@ PageType {
onClicked: function() { onClicked: function() {
isEasySetup = false isEasySetup = false
checked = true
} }
Keys.onEnterPressed: this.clicked()
Keys.onReturnPressed: this.clicked()
} }
BasicButtonType { BasicButtonType {
id: continueButton id: continueButton
Layout.fillWidth: true implicitWidth: parent.width
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(listView.dockerContainer) ContainersModel.setProcessedContainerIndex(containers.dockerContainer)
PageController.goToPage(PageEnum.PageSetupWizardInstalling) PageController.goToPage(PageEnum.PageSetupWizardInstalling)
InstallController.install(listView.dockerContainer, InstallController.install(containers.dockerContainer,
listView.containerDefaultPort, containers.containerDefaultPort,
listView.containerDefaultTransportProto) containers.containerDefaultTransportProto)
} else { } else {
PageController.goToPage(PageEnum.PageSetupWizardProtocols) PageController.goToPage(PageEnum.PageSetupWizardProtocols)
} }
@@ -173,11 +174,9 @@ PageType {
BasicButtonType { BasicButtonType {
id: setupLaterButton id: setupLaterButton
Layout.fillWidth: true implicitWidth: parent.width
Layout.topMargin: 8 anchors.topMargin: 8
Layout.bottomMargin: 24 anchors.bottomMargin: 24
Layout.leftMargin: 16
Layout.rightMargin: 16
defaultColor: AmneziaStyle.color.transparent defaultColor: AmneziaStyle.color.transparent
hoveredColor: AmneziaStyle.color.translucentWhite hoveredColor: AmneziaStyle.color.translucentWhite
@@ -186,6 +185,9 @@ 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)
@@ -203,15 +205,5 @@ PageType {
} }
} }
} }
Component.onCompleted: {
var item = listView.itemAtIndex(listView.currentIndex)
if (item !== null) {
var button = item.children[0]
button.checked = true
button.clicked()
}
}
} }
} }
@@ -85,76 +85,92 @@ PageType {
] ]
} }
ListViewType { FlickableType {
id: listView
anchors.fill: parent anchors.fill: parent
contentHeight: content.height
currentIndex: -1 Column {
id: content
model: proxyContainersModel anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
delegate: ColumnLayout { spacing: 16
width: listView.width
BaseHeaderType { ListView {
Layout.fillWidth: true id: container
Layout.topMargin: 20 width: parent.width
Layout.leftMargin: 16 height: container.contentItem.height
Layout.rightMargin: 16 currentIndex: -1
clip: true
interactive: false
model: proxyContainersModel
headerText: qsTr("Installing") delegate: Item {
descriptionText: name implicitWidth: container.width
} implicitHeight: delegateContent.implicitHeight
ProgressBarType { ColumnLayout {
id: progressBar id: delegateContent
Layout.fillWidth: true anchors.fill: parent
Layout.topMargin: 32 anchors.rightMargin: 16
Layout.leftMargin: 16 anchors.leftMargin: 16
Layout.rightMargin: 16
Timer { BaseHeaderType {
id: timer Layout.fillWidth: true
Layout.topMargin: 20
interval: 300 headerText: qsTr("Installing")
repeat: true descriptionText: name
running: root.isTimerRunning }
onTriggered: {
progressBar.value += 0.003 ProgressBarType {
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)
}
}
} }
} }
} }
@@ -29,239 +29,256 @@ PageType {
] ]
} }
BackButtonType { FlickableType {
id: backButton anchors.fill: parent
contentHeight: content.height
anchors.top: parent.top Column {
anchors.left: parent.left id: content
anchors.right: parent.right
anchors.topMargin: 20
onFocusChanged: { anchors.top: parent.top
if (this.activeFocus) { anchors.left: parent.left
listView.positionViewAtBeginning() anchors.right: parent.right
}
}
}
ListViewType { ListView {
id: listView id: processedContainerListView
width: parent.width
height: contentItem.height
currentIndex: -1
clip: true
interactive: false
model: proxyContainersModel
anchors.top: backButton.bottom property bool isFocusable: true
anchors.bottom: parent.bottom
anchors.right: parent.right
anchors.left: parent.left
currentIndex: -1 Keys.onTabPressed: {
FocusController.nextKeyTabItem()
model: proxyContainersModel
delegate: ColumnLayout {
width: listView.width
BaseHeaderType {
id: header
Layout.fillWidth: true
Layout.rightMargin: 16
Layout.leftMargin: 16
headerText: qsTr("Installing %1").arg(name)
descriptionText: description
}
BasicButtonType {
id: showDetailsButton
Layout.topMargin: 16
Layout.rightMargin: 16
Layout.leftMargin: 16
implicitHeight: 32
defaultColor: AmneziaStyle.color.transparent
hoveredColor: AmneziaStyle.color.translucentWhite
pressedColor: AmneziaStyle.color.sheerWhite
disabledColor: AmneziaStyle.color.mutedGray
textColor: AmneziaStyle.color.goldenApricot
text: qsTr("More detailed")
clickedFunc: function() {
showDetailsDrawer.openTriggered()
} }
}
DrawerType2 { Keys.onBacktabPressed: {
id: showDetailsDrawer FocusController.previousKeyTabItem()
parent: root }
anchors.fill: parent Keys.onUpPressed: {
expandedHeight: parent.height * 0.9 FocusController.nextKeyUpItem()
expandedStateContent: Item { }
implicitHeight: showDetailsDrawer.expandedHeight
BackButtonType { Keys.onDownPressed: {
id: showDetailsBackButton FocusController.nextKeyDownItem()
}
anchors.top: parent.top Keys.onLeftPressed: {
anchors.left: parent.left FocusController.nextKeyLeftItem()
anchors.right: parent.right }
anchors.topMargin: 16
backButtonFunction: function() { Keys.onRightPressed: {
showDetailsDrawer.closeTriggered() FocusController.nextKeyRightItem()
}
delegate: Item {
implicitWidth: processedContainerListView.width
implicitHeight: (delegateContent.implicitHeight > root.height) ? delegateContent.implicitHeight : root.height
property alias port:port
ColumnLayout {
id: delegateContent
anchors.fill: parent
anchors.rightMargin: 16
anchors.leftMargin: 16
BackButtonType {
id: backButton
Layout.topMargin: 20
Layout.rightMargin: -16
Layout.leftMargin: -16
} }
}
ListViewType { BaseHeaderType {
id: showDetailsListView id: header
anchors.top: showDetailsBackButton.bottom Layout.fillWidth: true
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: parent.bottom
header: ColumnLayout { headerText: qsTr("Installing %1").arg(name)
width: showDetailsListView.width descriptionText: description
}
Header2Type { BasicButtonType {
id: showDetailsDrawerHeader id: showDetailsButton
Layout.fillWidth: true Layout.topMargin: 16
Layout.topMargin: 16 Layout.leftMargin: -8
Layout.rightMargin: 16
Layout.leftMargin: 16
headerText: name implicitHeight: 32
defaultColor: AmneziaStyle.color.transparent
hoveredColor: AmneziaStyle.color.translucentWhite
pressedColor: AmneziaStyle.color.sheerWhite
disabledColor: AmneziaStyle.color.mutedGray
textColor: AmneziaStyle.color.goldenApricot
text: qsTr("More detailed")
KeyNavigation.tab: transportProtoSelector
clickedFunc: function() {
showDetailsDrawer.openTriggered()
} }
} }
model: 1 // fake model to force the ListView to be created without a model DrawerType2 {
id: showDetailsDrawer
parent: root
delegate: ColumnLayout { anchors.fill: parent
width: showDetailsListView.width expandedHeight: parent.height * 0.9
expandedStateContent: Item {
implicitHeight: showDetailsDrawer.expandedHeight
ParagraphTextType { BackButtonType {
Layout.fillWidth: true id: showDetailsBackButton
Layout.topMargin: 16
Layout.bottomMargin: 16
Layout.leftMargin: 16
Layout.rightMargin: 16
text: detailedDescription anchors.top: parent.top
textFormat: Text.MarkdownText anchors.left: parent.left
} anchors.right: parent.right
anchors.topMargin: 16
Rectangle { backButtonFunction: function() {
Layout.fillHeight: true showDetailsDrawer.closeTriggered()
Layout.leftMargin: 16 }
Layout.rightMargin: 16 }
color: AmneziaStyle.color.transparent FlickableType {
} id: fl
} anchors.top: showDetailsBackButton.bottom
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: parent.bottom
contentHeight: {
var emptySpaceHeight = parent.height - showDetailsBackButton.implicitHeight - showDetailsBackButton.anchors.topMargin
return (showDetailsDrawerContent.height > emptySpaceHeight) ?
showDetailsDrawerContent.height : emptySpaceHeight
}
footer: ColumnLayout { ColumnLayout {
width: showDetailsListView.width id: showDetailsDrawerContent
BasicButtonType { anchors.top: parent.top
id: showDetailsCloseButton anchors.left: parent.left
Layout.fillWidth: true anchors.right: parent.right
Layout.bottomMargin: 32 anchors.rightMargin: 16
Layout.leftMargin: 16 anchors.leftMargin: 16
Layout.rightMargin: 16
text: qsTr("Close") Header2Type {
id: showDetailsDrawerHeader
Layout.fillWidth: true
Layout.topMargin: 16
clickedFunc: function() { headerText: name
showDetailsDrawer.closeTriggered() }
ParagraphTextType {
Layout.fillWidth: true
Layout.topMargin: 16
Layout.bottomMargin: 16
text: detailedDescription
textFormat: Text.MarkdownText
}
Rectangle {
Layout.fillHeight: true
color: AmneziaStyle.color.transparent
}
BasicButtonType {
id: showDetailsCloseButton
Layout.fillWidth: true
Layout.bottomMargin: 32
parentFlickable: fl
text: qsTr("Close")
clickedFunc: function() {
showDetailsDrawer.closeTriggered()
}
}
}
} }
} }
} }
ParagraphTextType {
id: transportProtoHeader
Layout.topMargin: 16
text: qsTr("Network protocol")
}
TransportProtoSelector {
id: transportProtoSelector
Layout.fillWidth: true
rootWidth: root.width
}
TextFieldWithHeaderType {
id: port
Layout.fillWidth: true
Layout.topMargin: 16
headerText: qsTr("Port")
textField.maximumLength: 5
textField.validator: IntValidator { bottom: 1; top: 65535 }
}
Rectangle {
Layout.fillHeight: true
color: AmneziaStyle.color.transparent
}
BasicButtonType {
id: installButton
Layout.fillWidth: true
Layout.bottomMargin: 32
text: qsTr("Install")
clickedFunc: function() {
if (!port.textField.acceptableInput &&
ContainerProps.containerTypeToString(dockerContainer) !== "torwebsite" &&
ContainerProps.containerTypeToString(dockerContainer) !== "ikev2") {
port.errorText = qsTr("The port must be in the range of 1 to 65535")
return
}
PageController.goToPage(PageEnum.PageSetupWizardInstalling);
InstallController.install(dockerContainer, port.textField.text, transportProtoSelector.currentIndex)
}
}
Component.onCompleted: {
var defaultContainerProto = ContainerProps.defaultProtocol(dockerContainer)
if (ProtocolProps.defaultPort(defaultContainerProto) < 0) {
port.visible = false
} else {
port.textField.text = ProtocolProps.getPortForInstall(defaultContainerProto)
}
transportProtoSelector.currentIndex = ProtocolProps.defaultTransportProto(defaultContainerProto)
port.enabled = ProtocolProps.defaultPortChangeable(defaultContainerProto)
var protocolSelectorVisible = ProtocolProps.defaultTransportProtoChangeable(defaultContainerProto)
transportProtoSelector.visible = protocolSelectorVisible
transportProtoHeader.visible = protocolSelectorVisible
}
} }
} }
} }
ParagraphTextType {
id: transportProtoHeader
Layout.topMargin: 16
Layout.rightMargin: 16
Layout.leftMargin: 16
text: qsTr("Network protocol")
}
TransportProtoSelector {
id: transportProtoSelector
Layout.fillWidth: true
Layout.rightMargin: 16
Layout.leftMargin: 16
rootWidth: root.width
}
TextFieldWithHeaderType {
id: port
Layout.fillWidth: true
Layout.topMargin: 16
Layout.rightMargin: 16
Layout.leftMargin: 16
headerText: qsTr("Port")
textField.maximumLength: 5
textField.validator: IntValidator { bottom: 1; top: 65535 }
}
Rectangle {
Layout.fillHeight: true
Layout.rightMargin: 16
Layout.leftMargin: 16
color: AmneziaStyle.color.transparent
}
BasicButtonType {
id: installButton
Layout.fillWidth: true
Layout.bottomMargin: 32
Layout.rightMargin: 16
Layout.leftMargin: 16
text: qsTr("Install")
clickedFunc: function() {
if (!port.textField.acceptableInput &&
ContainerProps.containerTypeToString(dockerContainer) !== "torwebsite" &&
ContainerProps.containerTypeToString(dockerContainer) !== "ikev2") {
port.errorText = qsTr("The port must be in the range of 1 to 65535")
return
}
PageController.goToPage(PageEnum.PageSetupWizardInstalling);
InstallController.install(dockerContainer, port.textField.text, transportProtoSelector.currentIndex)
}
}
Component.onCompleted: {
var defaultContainerProto = ContainerProps.defaultProtocol(dockerContainer)
if (ProtocolProps.defaultPort(defaultContainerProto) < 0) {
port.visible = false
} else {
port.textField.text = ProtocolProps.getPortForInstall(defaultContainerProto)
}
transportProtoSelector.currentIndex = ProtocolProps.defaultTransportProto(defaultContainerProto)
port.enabled = ProtocolProps.defaultPortChangeable(defaultContainerProto)
var protocolSelectorVisible = ProtocolProps.defaultTransportProtoChangeable(defaultContainerProto)
transportProtoSelector.visible = protocolSelectorVisible
transportProtoHeader.visible = protocolSelectorVisible
}
} }
} }
} }
@@ -42,21 +42,19 @@ PageType {
anchors.right: parent.right anchors.right: parent.right
anchors.topMargin: 20 anchors.topMargin: 20
onActiveFocusChanged: {
if(backButton.enabled && backButton.activeFocus) {
listView.positionViewAtBeginning()
}
}
} }
ListViewType { ListView {
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
@@ -74,8 +72,9 @@ PageType {
} }
model: proxyContainersModel model: proxyContainersModel
clip: true
spacing: 0 spacing: 0
reuseItems: true
snapMode: ListView.SnapToItem snapMode: ListView.SnapToItem
delegate: ColumnLayout { delegate: ColumnLayout {
@@ -88,9 +87,9 @@ PageType {
descriptionText: description descriptionText: description
rightImageSource: "qrc:/images/controls/chevron-right.svg" rightImageSource: "qrc:/images/controls/chevron-right.svg"
clickedFunction: function () { clickedFunction: function() {
ContainersModel.setProcessedContainerIndex(proxyContainersModel.mapToSource(index)); ContainersModel.setProcessedContainerIndex(proxyContainersModel.mapToSource(index))
PageController.goToPage(PageEnum.PageSetupWizardProtocolSettings); PageController.goToPage(PageEnum.PageSetupWizardProtocolSettings)
} }
} }
+29 -45
View File
@@ -13,31 +13,25 @@ import "../Config"
PageType { PageType {
id: root id: root
BackButtonType { FlickableType {
id: backButton id: fl
anchors.top: parent.top anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
anchors.topMargin: 20
onFocusChanged: {
if (this.activeFocus) {
listView.positionViewAtBeginning()
}
}
}
ListViewType {
id: listView
anchors.top: backButton.bottom
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
anchors.right: parent.right contentHeight: content.height
anchors.left: parent.left
header: ColumnLayout { ColumnLayout {
width: listView.width id: content
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
spacing: 16
BackButtonType {
id: backButton
Layout.topMargin: 20
}
BaseHeaderType { BaseHeaderType {
Layout.fillWidth: true Layout.fillWidth: true
@@ -47,13 +41,6 @@ PageType {
headerText: qsTr("Connection key") headerText: qsTr("Connection key")
descriptionText: qsTr("A line that starts with vpn://...") descriptionText: qsTr("A line that starts with vpn://...")
} }
}
spacing: 16
model: 1 // fake model to force the ListView to be created without a model
delegate: ColumnLayout {
width: listView.width
TextFieldWithHeaderType { TextFieldWithHeaderType {
id: textKey id: textKey
@@ -73,26 +60,23 @@ PageType {
} }
} }
} }
}
footer: ColumnLayout { BasicButtonType {
width: listView.width id: continueButton
BasicButtonType { anchors.bottom: parent.bottom
id: continueButton anchors.left: parent.left
anchors.right: parent.right
anchors.rightMargin: 16
anchors.leftMargin: 16
anchors.bottomMargin: 32
Layout.fillWidth: true text: qsTr("Continue")
Layout.rightMargin: 16
Layout.leftMargin: 16
Layout.topMargin: 16
Layout.bottomMargin: 32
text: qsTr("Continue") clickedFunc: function() {
if (ImportController.extractConfigFromData(textKey.textField.text)) {
clickedFunc: function() { PageController.goToPage(PageEnum.PageSetupWizardViewConfig)
if (ImportController.extractConfigFromData(textKey.textField.text)) {
PageController.goToPage(PageEnum.PageSetupWizardViewConfig)
}
}
} }
} }
} }
@@ -23,12 +23,6 @@ PageType {
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
anchors.topMargin: 20 anchors.topMargin: 20
onActiveFocusChanged: {
if(backButton.enabled && backButton.activeFocus) {
listView.positionViewAtBeginning()
}
}
} }
Connections { Connections {
@@ -52,29 +46,27 @@ PageType {
} }
} }
ListViewType { FlickableType {
id: listView id: fl
anchors.top: backButton.bottom anchors.top: backButton.bottom
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
anchors.right: parent.right contentHeight: content.implicitHeight + connectButton.implicitHeight
anchors.left: parent.left
header: ColumnLayout { ColumnLayout {
width: listView.width id: content
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
anchors.rightMargin: 16
anchors.leftMargin: 16
BaseHeaderType { BaseHeaderType {
Layout.leftMargin: 16
Layout.rightMargin: 16
headerText: qsTr("New connection") headerText: qsTr("New connection")
} }
RowLayout { RowLayout {
Layout.topMargin: 32 Layout.topMargin: 32
Layout.leftMargin: 16
Layout.rightMargin: 16
spacing: 8 spacing: 8
visible: fileName.text !== "" visible: fileName.text !== ""
@@ -96,9 +88,7 @@ PageType {
BasicButtonType { BasicButtonType {
id: showContentButton id: showContentButton
Layout.topMargin: 16 Layout.topMargin: 16
Layout.leftMargin: 16 Layout.leftMargin: -8
Layout.rightMargin: 16
implicitHeight: 32 implicitHeight: 32
defaultColor: AmneziaStyle.color.transparent defaultColor: AmneziaStyle.color.transparent
@@ -109,6 +99,8 @@ PageType {
text: showContent ? qsTr("Collapse content") : qsTr("Show content") text: showContent ? qsTr("Collapse content") : qsTr("Show content")
parentFlickable: fl
clickedFunc: function() { clickedFunc: function() {
showContent = !showContent showContent = !showContent
} }
@@ -116,28 +108,16 @@ PageType {
CheckBoxType { CheckBoxType {
id: cloakingCheckBox id: cloakingCheckBox
objectName: "cloakingCheckBox"
visible: ImportController.isNativeWireGuardConfig() visible: ImportController.isNativeWireGuardConfig()
Layout.fillWidth: true Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 16
text: qsTr("Enable WireGuard obfuscation. It may be useful if WireGuard is blocked on your provider.") text: qsTr("Enable WireGuard obfuscation. It may be useful if WireGuard is blocked on your provider.")
} }
}
model: 1 // fake model to force the ListView to be created without a model
delegate: ColumnLayout { // TODO(CyAn84): add DelegateChooser after have migrated to 6.9
width: listView.width
WarningType { WarningType {
Layout.fillWidth: true
Layout.topMargin: 16 Layout.topMargin: 16
Layout.leftMargin: 16 Layout.fillWidth: true
Layout.rightMargin: 16
textString: ImportController.getMaliciousWarningText() textString: ImportController.getMaliciousWarningText()
textFormat: Qt.RichText textFormat: Qt.RichText
@@ -150,10 +130,8 @@ PageType {
} }
WarningType { WarningType {
Layout.fillWidth: true
Layout.topMargin: 16 Layout.topMargin: 16
Layout.leftMargin: 16 Layout.fillWidth: true
Layout.rightMargin: 16
textString: qsTr("Use connection codes only from sources you trust. Codes from public sources may have been created to intercept your data.") textString: qsTr("Use connection codes only from sources you trust. Codes from public sources may have been created to intercept your data.")
@@ -162,10 +140,7 @@ PageType {
Rectangle { Rectangle {
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 16
Layout.bottomMargin: 48 Layout.bottomMargin: 48
Layout.rightMargin: 16
Layout.leftMargin: 16
implicitHeight: configContent.implicitHeight implicitHeight: configContent.implicitHeight
@@ -186,38 +161,34 @@ PageType {
} }
} }
} }
}
footer: ColumnLayout { Rectangle {
width: listView.width anchors.fill: columnContent
anchors.bottomMargin: -24
color: AmneziaStyle.color.midnightBlack
opacity: 0.8
}
BasicButtonType { ColumnLayout {
id: connectButton id: columnContent
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.right: parent.right
anchors.rightMargin: 16
anchors.leftMargin: 16
Layout.fillWidth: true BasicButtonType {
Layout.topMargin: 16 id: connectButton
Layout.bottomMargin: 32 Layout.fillWidth: true
Layout.rightMargin: 16 Layout.bottomMargin: 32
Layout.leftMargin: 16
text: qsTr("Connect") text: qsTr("Connect")
clickedFunc: function() { clickedFunc: function() {
const headerItem = listView.headerItem; if (cloakingCheckBox.checked) {
if (!headerItem) { ImportController.processNativeWireGuardConfig()
console.error("Header item not found in ListView")
return
}
const cloakingCheckBoxItem = listView.findChildWithObjectName(headerItem.children, "cloakingCheckBox");
if (!cloakingCheckBoxItem) {
console.error("cloakingCheckBox not found")
return
}
if (cloakingCheckBoxItem.checked) {
ImportController.processNativeWireGuardConfig()
}
ImportController.importConfig()
} }
ImportController.importConfig()
} }
} }
} }
+35 -9
View File
@@ -15,7 +15,6 @@ import "../Controls2/TextTypes"
import "../Components" import "../Components"
import "../Config" import "../Config"
PageType { PageType {
id: root id: root
@@ -43,6 +42,10 @@ PageType {
target: ExportController target: ExportController
function onGenerateConfig(type) { function onGenerateConfig(type) {
shareConnectionDrawer.headerText = qsTr("Connection to ") + serverSelector.text
shareConnectionDrawer.configContentHeaderText = qsTr("File with connection settings to ") + serverSelector.text
shareConnectionDrawer.openTriggered()
PageController.showBusyIndicator(true) PageController.showBusyIndicator(true)
switch (type) { switch (type) {
@@ -52,36 +55,54 @@ PageType {
} }
case PageShare.ConfigType.OpenVpn: { case PageShare.ConfigType.OpenVpn: {
ExportController.generateOpenVpnConfig(clientNameTextField.textField.text) ExportController.generateOpenVpnConfig(clientNameTextField.textField.text)
shareConnectionDrawer.configCaption = qsTr("Save OpenVPN config")
shareConnectionDrawer.configExtension = ".ovpn"
shareConnectionDrawer.configFileName = "amnezia_for_openvpn"
break break
} }
case PageShare.ConfigType.WireGuard: { case PageShare.ConfigType.WireGuard: {
ExportController.generateWireGuardConfig(clientNameTextField.textField.text) ExportController.generateWireGuardConfig(clientNameTextField.textField.text)
shareConnectionDrawer.configCaption = qsTr("Save WireGuard config")
shareConnectionDrawer.configExtension = ".conf"
shareConnectionDrawer.configFileName = "amnezia_for_wireguard"
break break
} }
case PageShare.ConfigType.Awg: { case PageShare.ConfigType.Awg: {
ExportController.generateAwgConfig(clientNameTextField.textField.text) ExportController.generateAwgConfig(clientNameTextField.textField.text)
shareConnectionDrawer.configCaption = qsTr("Save AmneziaWG config")
shareConnectionDrawer.configExtension = ".conf"
shareConnectionDrawer.configFileName = "amnezia_for_awg"
break break
} }
case PageShare.ConfigType.ShadowSocks: { case PageShare.ConfigType.ShadowSocks: {
ExportController.generateShadowSocksConfig() ExportController.generateShadowSocksConfig()
shareConnectionDrawer.configCaption = qsTr("Save Shadowsocks config")
shareConnectionDrawer.configExtension = ".json"
shareConnectionDrawer.configFileName = "amnezia_for_shadowsocks"
break break
} }
case PageShare.ConfigType.Cloak: { case PageShare.ConfigType.Cloak: {
ExportController.generateCloakConfig() ExportController.generateCloakConfig()
shareConnectionDrawer.configCaption = qsTr("Save Cloak config")
shareConnectionDrawer.configExtension = ".json"
shareConnectionDrawer.configFileName = "amnezia_for_cloak"
break break
} }
case PageShare.ConfigType.Xray: { case PageShare.ConfigType.Xray: {
ExportController.generateXrayConfig(clientNameTextField.textField.text) ExportController.generateXrayConfig(clientNameTextField.textField.text)
shareConnectionDrawer.configCaption = qsTr("Save XRay config")
shareConnectionDrawer.configExtension = ".json"
shareConnectionDrawer.configFileName = "amnezia_for_xray"
break break
} }
} }
PageController.showBusyIndicator(false) PageController.showBusyIndicator(false)
PageController.goToPage(PageEnum.PageShareConnection)
} }
function onExportErrorOccurred(error) { function onExportErrorOccurred(error) {
shareConnectionDrawer.closeTriggered()
PageController.showErrorMessage(error) PageController.showErrorMessage(error)
} }
} }
@@ -235,9 +256,6 @@ PageType {
onClicked: { onClicked: {
accessTypeSelector.currentIndex = 0 accessTypeSelector.currentIndex = 0
} }
Keys.onEnterPressed: this.clicked()
Keys.onReturnPressed: this.clicked()
} }
HorizontalRadioButton { HorizontalRadioButton {
@@ -254,9 +272,6 @@ PageType {
ServersModel.getProcessedServerCredentials()) ServersModel.getProcessedServerCredentials())
PageController.showBusyIndicator(false) PageController.showBusyIndicator(false)
} }
Keys.onEnterPressed: this.clicked()
Keys.onReturnPressed: this.clicked()
} }
} }
} }
@@ -510,6 +525,9 @@ PageType {
text: qsTr("Share") text: qsTr("Share")
leftImageSource: "qrc:/images/controls/share-2.svg" leftImageSource: "qrc:/images/controls/share-2.svg"
parentFlickable: a
clickedFunc: function(){ clickedFunc: function(){
if (clientNameTextField.textField.text !== "") { if (clientNameTextField.textField.text !== "") {
ExportController.generateConfig(root.connectionTypesModel[exportTypeSelector.currentIndex].type) ExportController.generateConfig(root.connectionTypesModel[exportTypeSelector.currentIndex].type)
@@ -808,6 +826,9 @@ PageType {
root.revokeConfig(index) root.revokeConfig(index)
} }
var noButtonFunction = function() { var noButtonFunction = function() {
if (!GC.isMobile()) {
// focusItem1.forceActiveFocus()
}
} }
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction) showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
@@ -821,4 +842,9 @@ PageType {
} }
} }
ShareConnectionDrawer {
id: shareConnectionDrawer
anchors.fill: parent
}
} }
@@ -1,329 +0,0 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import QtQuick.Dialogs
import QtCore
import SortFilterProxyModel 0.2
import PageEnum 1.0
import ContainerProps 1.0
import Style 1.0
import "./"
import "../Controls2"
import "../Controls2/TextTypes"
import "../Config"
import "../Components"
PageType {
id: pageShareConnection
property string headerText
Component.onCompleted: {
var serverName = ServersModel.getProcessedServerData("name") || ServersModel.getProcessedServerData("hostName") || "Server"
headerText = qsTr("Connection to ") + serverName
configContentHeaderText = qsTr("File with connection settings to ") + serverName
}
property string configContentHeaderText
property string shareButtonText: qsTr("Share")
property string copyButtonText: qsTr("Copy")
property bool isSelfHostedConfig: true
property string configExtension: ".vpn"
property string configCaption: qsTr("Save AmneziaVPN config")
property string configFileName: "amnezia_config"
onVisibleChanged: {
configExtension = ".vpn"
configCaption = qsTr("Save AmneziaVPN config")
configFileName = "amnezia_config"
if (visible) {
var serverName = ServersModel.getProcessedServerData("name") || ServersModel.getProcessedServerData("hostName") || "Server"
headerText = qsTr("Connection to ") + serverName
configContentHeaderText = qsTr("File with connection settings to ") + serverName
}
}
BackButtonType {
id: backButton
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
anchors.topMargin: 20
}
Text {
id: shareHeader
anchors.top: backButton.bottom
anchors.left: parent.left
anchors.right: parent.right
anchors.topMargin: 20
anchors.leftMargin: 16
anchors.rightMargin: 16
text: pageShareConnection.headerText
color: AmneziaStyle.color.paleGray
font.pixelSize: 32
font.weight: 700
font.family: "PT Root UI VF"
wrapMode: Text.WordWrap
}
ListView {
id: listView
anchors.top: shareHeader.bottom
anchors.topMargin: 16
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.right: parent.right
property bool isFocusable: true
ScrollBar.vertical: ScrollBarType {}
model: 1
clip: true
reuseItems: true
header: ColumnLayout {
width: listView.width
BasicButtonType {
id: shareButton
Layout.fillWidth: true
Layout.topMargin: 16
Layout.leftMargin: 16
Layout.rightMargin: 16
text: pageShareConnection.shareButtonText
leftImageSource: "qrc:/images/controls/share-2.svg"
clickedFunc: function() {
var fileName = ""
if (GC.isMobile()) {
fileName = configFileName + configExtension
} else {
fileName = SystemController.getFileName(configCaption,
qsTr("Config files (*" + configExtension + ")"),
StandardPaths.standardLocations(StandardPaths.DocumentsLocation) + "/" + configFileName,
true,
configExtension)
}
if (fileName !== "") {
PageController.showBusyIndicator(true)
ExportController.exportConfig(fileName)
PageController.showBusyIndicator(false)
}
}
}
BasicButtonType {
id: copyConfigTextButton
Layout.fillWidth: true
Layout.topMargin: 8
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: pageShareConnection.copyButtonText
leftImageSource: "qrc:/images/controls/copy.svg"
Keys.onReturnPressed: copyConfigTextButton.clicked()
Keys.onEnterPressed: copyConfigTextButton.clicked()
}
BasicButtonType {
id: copyNativeConfigStringButton
Layout.fillWidth: true
Layout.topMargin: 8
Layout.leftMargin: 16
Layout.rightMargin: 16
visible: false
defaultColor: AmneziaStyle.color.transparent
hoveredColor: AmneziaStyle.color.translucentWhite
pressedColor: AmneziaStyle.color.sheerWhite
disabledColor: AmneziaStyle.color.mutedGray
textColor: AmneziaStyle.color.paleGray
borderWidth: 1
text: qsTr("Copy config string")
leftImageSource: "qrc:/images/controls/copy.svg"
KeyNavigation.tab: showSettingsButton
}
BasicButtonType {
id: showSettingsButton
Layout.fillWidth: true
Layout.topMargin: 24
Layout.leftMargin: 16
Layout.rightMargin: 16
visible: pageShareConnection.isSelfHostedConfig
defaultColor: AmneziaStyle.color.transparent
hoveredColor: AmneziaStyle.color.translucentWhite
pressedColor: AmneziaStyle.color.sheerWhite
disabledColor: AmneziaStyle.color.mutedGray
textColor: AmneziaStyle.color.paleGray
borderWidth: 1
text: qsTr("Show connection settings")
clickedFunc: function() {
configContentDrawer.openTriggered()
}
}
DrawerType2 {
id: configContentDrawer
parent: pageShareConnection.parent
anchors.fill: parent
expandedHeight: parent.height * 0.9
expandedStateContent: Item {
id: configContentContainer
implicitHeight: configContentDrawer.expandedHeight
Connections {
target: copyNativeConfigStringButton
function onClicked() {
nativeConfigString.selectAll()
nativeConfigString.copy()
nativeConfigString.select(0, 0)
PageController.showNotificationMessage(qsTr("Copied"))
}
}
Connections {
target: copyConfigTextButton
function onClicked() {
configText.selectAll()
configText.copy()
configText.select(0, 0)
PageController.showNotificationMessage(qsTr("Copied"))
header.forceActiveFocus()
}
}
BackButtonType {
id: configBackButton
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
anchors.topMargin: 16
backButtonFunction: function() { configContentDrawer.closeTriggered() }
}
FlickableType {
anchors.top: configBackButton.bottom
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: parent.bottom
contentHeight: configContent.implicitHeight + configContent.anchors.topMargin + configContent.anchors.bottomMargin
ColumnLayout {
id: configContent
anchors.fill: parent
anchors.rightMargin: 16
anchors.leftMargin: 16
Header2Type {
id: configContentHeader
Layout.fillWidth: true
Layout.topMargin: 16
headerText: pageShareConnection.configContentHeaderText
}
TextField {
id: nativeConfigString
visible: false
text: ExportController.nativeConfigString
onTextChanged: copyNativeConfigStringButton.visible = nativeConfigString.text !== ""
}
TextArea {
id: configText
Layout.fillWidth: true
Layout.topMargin: 16
Layout.bottomMargin: 16
padding: 0
leftPadding: 0
height: 24
readOnly: true
activeFocusOnTab: false
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: ExportController.config
wrapMode: Text.Wrap
background: Rectangle { color: AmneziaStyle.color.transparent }
}
}
}
}
}
}
delegate: ColumnLayout {
width: listView.width
property bool isQrCodeVisible: pageShareConnection.isSelfHostedConfig ? ExportController.qrCodesCount > 0 : ApiConfigsController.qrCodesCount > 0
Rectangle {
id: qrCodeContainer
Layout.fillWidth: true
Layout.preferredHeight: width
Layout.topMargin: 20
Layout.leftMargin: 16
Layout.rightMargin: 16
visible: isQrCodeVisible
color: "white"
Image {
anchors.fill: parent
smooth: false
source: pageShareConnection.isSelfHostedConfig ? (isQrCodeVisible ? ExportController.qrCodes[0] : "") : (isQrCodeVisible ? ApiConfigsController.qrCodes[0] : "")
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()
Timer {
property int index: 0
interval: 1000
running: isQrCodeVisible
repeat: true
onTriggered: {
if (isQrCodeVisible) {
index++
let qrCodesCount = pageShareConnection.isSelfHostedConfig ? ExportController.qrCodesCount : ApiConfigsController.qrCodesCount
if (index >= qrCodesCount) index = 0
parent.source = pageShareConnection.isSelfHostedConfig ? ExportController.qrCodes[index] : ApiConfigsController.qrCodes[index]
}
}
}
Behavior on source { PropertyAnimation { duration: 200 } }
}
}
ParagraphTextType {
Layout.fillWidth: true
Layout.topMargin: 24
Layout.bottomMargin: 32
Layout.leftMargin: 16
Layout.rightMargin: 16
visible: isQrCodeVisible
horizontalAlignment: Text.AlignHCenter
text: qsTr("To read the QR code in the Amnezia app, select \"Add server\" → \"I have data to connect\" → \"QR code, key or settings file\"")
}
}
}
}

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