Compare commits

..

5 Commits

Author SHA1 Message Date
vladimir.kuznetsov 35f5101fa8 test: test endpoint 2025-07-17 11:24:36 +08:00
vladimir.kuznetsov 63c336d96e Merge branch 'dev' of github.com:amnezia-vpn/amnezia-client into HEAD 2025-07-17 11:14:10 +08:00
aiamnezia 09a67572fb Remove news caching 2025-07-16 19:17:47 +04:00
aiamnezia 4b4b81b395 Add localization for news and notifications 2025-07-16 16:54:39 +04:00
aiamnezia 470ce0f9c8 Add news and notifications 2025-06-20 04:23:32 +04:00
114 changed files with 7342 additions and 6947 deletions
+1
View File
@@ -9,6 +9,7 @@ deploy/build_32/*
deploy/build_64/* deploy/build_64/*
winbuild*.bat winbuild*.bat
.cache/ .cache/
.vscode/
# Qt-es # Qt-es
+2 -2
View File
@@ -1,7 +1,7 @@
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) set(AMNEZIAVPN_VERSION 4.8.9.0)
project(${PROJECT} VERSION ${AMNEZIAVPN_VERSION} project(${PROJECT} VERSION ${AMNEZIAVPN_VERSION}
DESCRIPTION "AmneziaVPN" DESCRIPTION "AmneziaVPN"
@@ -12,7 +12,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 2090)
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"
} }
+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)
+8 -7
View File
@@ -2,6 +2,7 @@
#include <QDirIterator> #include <QDirIterator>
#include <QTranslator> #include <QTranslator>
#include <QCoreApplication>
#if defined(Q_OS_ANDROID) #if defined(Q_OS_ANDROID)
#include "core/installedAppsImageProvider.h" #include "core/installedAppsImageProvider.h"
@@ -100,6 +101,9 @@ void CoreController::initModels()
m_apiDevicesModel.reset(new ApiDevicesModel(m_settings, this)); m_apiDevicesModel.reset(new ApiDevicesModel(m_settings, this));
m_engine->rootContext()->setContextProperty("ApiDevicesModel", m_apiDevicesModel.get()); m_engine->rootContext()->setContextProperty("ApiDevicesModel", m_apiDevicesModel.get());
m_newsModel.reset(new NewsModel(m_settings, this));
m_engine->rootContext()->setContextProperty("NewsModel", m_newsModel.get());
} }
void CoreController::initControllers() void CoreController::initControllers()
@@ -120,9 +124,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());
@@ -154,6 +155,10 @@ void CoreController::initControllers()
m_apiPremV1MigrationController.reset(new ApiPremV1MigrationController(m_serversModel, m_settings, this)); m_apiPremV1MigrationController.reset(new ApiPremV1MigrationController(m_serversModel, m_settings, this));
m_engine->rootContext()->setContextProperty("ApiPremV1MigrationController", m_apiPremV1MigrationController.get()); m_engine->rootContext()->setContextProperty("ApiPremV1MigrationController", m_apiPremV1MigrationController.get());
m_apiNewsController.reset(new ApiNewsController(m_newsModel, m_settings));
m_engine->rootContext()->setContextProperty("ApiNewsController", m_apiNewsController.get());
m_apiNewsController->fetchNews();
} }
void CoreController::initAndroidController() void CoreController::initAndroidController()
@@ -245,9 +250,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 +287,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()
+4 -5
View File
@@ -5,13 +5,10 @@
#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"
#include "ui/controllers/api/apiNewsController.h"
#include "ui/controllers/appSplitTunnelingController.h" #include "ui/controllers/appSplitTunnelingController.h"
#include "ui/controllers/allowedDnsController.h" #include "ui/controllers/allowedDnsController.h"
#include "ui/controllers/connectionController.h" #include "ui/controllers/connectionController.h"
@@ -47,6 +44,7 @@
#include "ui/models/services/sftpConfigModel.h" #include "ui/models/services/sftpConfigModel.h"
#include "ui/models/services/socks5ProxyConfigModel.h" #include "ui/models/services/socks5ProxyConfigModel.h"
#include "ui/models/sites_model.h" #include "ui/models/sites_model.h"
#include "ui/models/newsmodel.h"
#ifndef Q_OS_ANDROID #ifndef Q_OS_ANDROID
#include "ui/notificationhandler.h" #include "ui/notificationhandler.h"
@@ -65,7 +63,6 @@ public:
signals: signals:
void translationsUpdated(); void translationsUpdated();
void websiteUrlChanged(const QString &newUrl);
private: private:
void initModels(); void initModels();
@@ -118,6 +115,7 @@ private:
QScopedPointer<ApiSettingsController> m_apiSettingsController; QScopedPointer<ApiSettingsController> m_apiSettingsController;
QScopedPointer<ApiConfigsController> m_apiConfigsController; QScopedPointer<ApiConfigsController> m_apiConfigsController;
QScopedPointer<ApiPremV1MigrationController> m_apiPremV1MigrationController; QScopedPointer<ApiPremV1MigrationController> m_apiPremV1MigrationController;
QScopedPointer<ApiNewsController> m_apiNewsController;
QSharedPointer<ContainersModel> m_containersModel; QSharedPointer<ContainersModel> m_containersModel;
QSharedPointer<ContainersModel> m_defaultServerContainersModel; QSharedPointer<ContainersModel> m_defaultServerContainersModel;
@@ -125,6 +123,7 @@ private:
QSharedPointer<LanguageModel> m_languageModel; QSharedPointer<LanguageModel> m_languageModel;
QSharedPointer<ProtocolsModel> m_protocolsModel; QSharedPointer<ProtocolsModel> m_protocolsModel;
QSharedPointer<SitesModel> m_sitesModel; QSharedPointer<SitesModel> m_sitesModel;
QSharedPointer<NewsModel> m_newsModel;
QSharedPointer<AllowedDnsModel> m_allowedDnsModel; QSharedPointer<AllowedDnsModel> m_allowedDnsModel;
QSharedPointer<AppSplitTunnelingModel> m_appSplitTunnelingModel; QSharedPointer<AppSplitTunnelingModel> m_appSplitTunnelingModel;
QSharedPointer<ClientManagementModel> m_clientManagementModel; QSharedPointer<ClientManagementModel> m_clientManagementModel;
+14
View File
@@ -0,0 +1,14 @@
<svg width="24" height="24" viewBox="0 0 74 74" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_4_34)">
<path d="M55.5 12.3333H18.5C15.0942 12.3333 12.3333 15.0943 12.3333 18.5V55.5C12.3333 58.9058 15.0942 61.6667 18.5 61.6667H55.5C58.9057 61.6667 61.6666 58.9058 61.6666 55.5V18.5C61.6666 15.0943 58.9057 12.3333 55.5 12.3333Z" stroke="#CBCAC8" stroke-width="5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M21.5833 24.6667H52.4167" stroke="#CBCAC8" stroke-width="5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M21.5833 37H52.4167" stroke="#CBCAC8" stroke-width="5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M21.5833 49.3333H40.0833" stroke="#CBCAC8" stroke-width="5" stroke-linecap="round" stroke-linejoin="round"/>
<circle cx="61.5" cy="12.5" r="15" fill="#FBB36B" stroke="#1C1D21" stroke-width="5"/>
</g>
<defs>
<clipPath id="clip0_4_34">
<rect width="74" height="74" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 982 B

+8
View File
@@ -0,0 +1,8 @@
<svg width="24" height="24" xmlns="http://www.w3.org/2000/svg" fill="none" stroke="#CBCAC8" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
<!-- Основа газеты -->
<rect x="4" y="4" width="16" height="16" rx="2"/>
<!-- Линии текста -->
<line x1="7" y1="8" x2="17" y2="8"/>
<line x1="7" y1="12" x2="17" y2="12"/>
<line x1="7" y1="16" x2="13" y2="16"/>
</svg>

After

Width:  |  Height:  |  Size: 410 B

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 5.9 KiB

+3
View File
@@ -0,0 +1,3 @@
<svg width="16" height="16" viewBox="0 0 35 35" fill="none" xmlns="http://www.w3.org/2000/svg">
<circle cx="17.5" cy="17.5" r="15" fill="#FBB36B" stroke="#1C1D21" stroke-width="5"/>
</svg>

After

Width:  |  Height:  |  Size: 188 B

+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>
+7 -3
View File
@@ -35,6 +35,9 @@
<file>images/controls/mail.svg</file> <file>images/controls/mail.svg</file>
<file>images/controls/map-pin.svg</file> <file>images/controls/map-pin.svg</file>
<file>images/controls/more-vertical.svg</file> <file>images/controls/more-vertical.svg</file>
<file>images/controls/news.svg</file>
<file>images/controls/news-unread.svg</file>
<file>images/controls/unread-dot.svg</file>
<file>images/controls/plus.svg</file> <file>images/controls/plus.svg</file>
<file>images/controls/qr-code.svg</file> <file>images/controls/qr-code.svg</file>
<file>images/controls/radio-button-inner-circle-pressed.png</file> <file>images/controls/radio-button-inner-circle-pressed.png</file>
@@ -49,6 +52,7 @@
<file>images/controls/server.svg</file> <file>images/controls/server.svg</file>
<file>images/controls/settings-2.svg</file> <file>images/controls/settings-2.svg</file>
<file>images/controls/settings.svg</file> <file>images/controls/settings.svg</file>
<file>images/controls/settings-news.svg</file>
<file>images/controls/share-2.svg</file> <file>images/controls/share-2.svg</file>
<file>images/controls/split-tunneling.svg</file> <file>images/controls/split-tunneling.svg</file>
<file>images/controls/tag.svg</file> <file>images/controls/tag.svg</file>
@@ -127,7 +131,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>
@@ -212,6 +216,8 @@
<file>ui/qml/Pages2/PageSettingsServerServices.qml</file> <file>ui/qml/Pages2/PageSettingsServerServices.qml</file>
<file>ui/qml/Pages2/PageSettingsServersList.qml</file> <file>ui/qml/Pages2/PageSettingsServersList.qml</file>
<file>ui/qml/Pages2/PageSettingsSplitTunneling.qml</file> <file>ui/qml/Pages2/PageSettingsSplitTunneling.qml</file>
<file>ui/qml/Pages2/PageSettingsNewsNotifications.qml</file>
<file>ui/qml/Pages2/PageSettingsNewsDetail.qml</file>
<file>ui/qml/Pages2/PageProtocolAwgClientSettings.qml</file> <file>ui/qml/Pages2/PageProtocolAwgClientSettings.qml</file>
<file>ui/qml/Pages2/PageProtocolWireGuardClientSettings.qml</file> <file>ui/qml/Pages2/PageProtocolWireGuardClientSettings.qml</file>
<file>ui/qml/Pages2/PageSetupWizardApiServiceInfo.qml</file> <file>ui/qml/Pages2/PageSetupWizardApiServiceInfo.qml</file>
@@ -228,7 +234,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 +246,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
+11 -1
View File
@@ -14,7 +14,7 @@ namespace
const char cloudFlareNs1[] = "1.1.1.1"; const char cloudFlareNs1[] = "1.1.1.1";
const char cloudFlareNs2[] = "1.0.0.1"; const char cloudFlareNs2[] = "1.0.0.1";
constexpr char gatewayEndpoint[] = "http://gw.amnezia.org:80/"; constexpr char gatewayEndpoint[] = "http://192.168.0.222:80/";
} }
Settings::Settings(QObject *parent) : QObject(parent), m_settings(ORGANIZATION_NAME, APPLICATION_NAME, this) Settings::Settings(QObject *parent) : QObject(parent), m_settings(ORGANIZATION_NAME, APPLICATION_NAME, this)
@@ -578,3 +578,13 @@ void Settings::setAllowedDnsServers(const QStringList &servers)
{ {
setValue("Conf/allowedDnsServers", servers); setValue("Conf/allowedDnsServers", servers);
} }
QStringList Settings::readNewsIds() const
{
return value("News/readIds").toStringList();
}
void Settings::setReadNewsIds(const QStringList &ids)
{
setValue("News/readIds", ids);
}
+3
View File
@@ -236,6 +236,9 @@ public:
QStringList allowedDnsServers() const; QStringList allowedDnsServers() const;
void setAllowedDnsServers(const QStringList &servers); void setAllowedDnsServers(const QStringList &servers);
QStringList readNewsIds() const;
void setReadNewsIds(const QStringList &ids);
signals: signals:
void saveLogsChanged(bool enabled); void saveLogsChanged(bool enabled);
void screenshotsEnabledChanged(bool enabled); void screenshotsEnabledChanged(bool enabled);
@@ -0,0 +1,40 @@
#include "apiNewsController.h"
#include <QJsonDocument>
#include <QJsonObject>
ApiNewsController::ApiNewsController(const QSharedPointer<NewsModel> &newsModel,
const std::shared_ptr<Settings> &settings,
QObject *parent)
: QObject(parent), m_newsModel(newsModel), m_settings(settings)
{
}
void ApiNewsController::fetchNews()
{
GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(),
apiDefs::requestTimeoutMsecs, m_settings->isStrictKillSwitchEnabled());
QByteArray responseBody;
QJsonObject payload;
payload.insert("locale", m_settings->getAppLanguage().name().split("_").first());
ErrorCode errorCode = gatewayController.post(QString("%1v1/news"), payload, responseBody);
qDebug() << "fetchNews" << errorCode;
if (errorCode != ErrorCode::NoError) {
emit errorOccurred(errorCode);
return;
}
QJsonDocument doc = QJsonDocument::fromJson(responseBody);
QJsonArray newsArray;
if (doc.isArray()) {
newsArray = doc.array();
} else if (doc.isObject()) {
QJsonObject obj = doc.object();
if (obj.value("news").isArray()) {
newsArray = obj.value("news").toArray();
}
}
m_newsModel->updateModel(newsArray);
}
@@ -0,0 +1,32 @@
#ifndef APINEWSCONTROLLER_H
#define APINEWSCONTROLLER_H
#include <QObject>
#include <QSharedPointer>
#include <memory>
#include <QJsonArray>
#include "settings.h"
#include "ui/models/newsmodel.h"
#include "core/controllers/gatewayController.h"
#include "core/api/apiDefs.h"
class ApiNewsController : public QObject
{
Q_OBJECT
public:
explicit ApiNewsController(const QSharedPointer<NewsModel> &newsModel,
const std::shared_ptr<Settings> &settings,
QObject *parent = nullptr);
Q_INVOKABLE void fetchNews();
signals:
void errorOccurred(ErrorCode errorCode);
private:
QSharedPointer<NewsModel> m_newsModel;
std::shared_ptr<Settings> m_settings;
};
#endif // APINEWSCONTROLLER_H
+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;
} }
+1 -96
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 -2
View File
@@ -26,6 +26,8 @@ namespace PageLoader
PageSettingsConnection, PageSettingsConnection,
PageSettingsDns, PageSettingsDns,
PageSettingsApplication, PageSettingsApplication,
PageSettingsNewsNotifications,
PageSettingsNewsDetail,
PageSettingsBackup, PageSettingsBackup,
PageSettingsAbout, PageSettingsAbout,
PageSettingsLogging, PageSettingsLogging,
@@ -38,7 +40,6 @@ namespace PageLoader
PageSettingsApiInstructions, PageSettingsApiInstructions,
PageSettingsApiNativeConfigs, PageSettingsApiNativeConfigs,
PageSettingsApiDevices, PageSettingsApiDevices,
PageSettingsApiSubscriptionKey,
PageSettingsKillSwitchExceptions, PageSettingsKillSwitchExceptions,
PageServiceSftpSettings, PageServiceSftpSettings,
@@ -72,7 +73,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: {
@@ -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);
+143
View File
@@ -0,0 +1,143 @@
#include "ui/models/newsmodel.h"
#include <QJsonArray>
#include <QJsonObject>
#include <QJsonValue>
#include <QQmlEngine>
#include <QFile>
#include <QDir>
#include <QStandardPaths>
#include <QJsonDocument>
#include <algorithm>
NewsModel::NewsModel(const std::shared_ptr<Settings> &settings, QObject *parent)
: QAbstractListModel(parent)
, m_settings(settings)
{
loadReadIds();
}
int NewsModel::rowCount(const QModelIndex &parent) const
{
Q_UNUSED(parent);
return m_items.size();
}
QVariant NewsModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid() || index.row() < 0 || index.row() >= m_items.size())
return QVariant();
const NewsItem &item = m_items.at(index.row());
switch (role) {
case IdRole:
return item.id;
case TitleRole:
return item.title;
case ContentRole:
return item.content;
case TimestampRole:
return item.timestamp.toString(Qt::ISODate);
case IsReadRole:
return item.read;
case IsProcessedRole:
return index.row() == m_processedIndex;
default:
return QVariant();
}
}
QHash<int, QByteArray> NewsModel::roleNames() const
{
QHash<int, QByteArray> roles;
roles[IdRole] = "id";
roles[TitleRole] = "title";
roles[ContentRole] = "content";
roles[TimestampRole] = "timestamp";
roles[IsReadRole] = "read";
roles[IsProcessedRole] = "isProcessed";
return roles;
}
void NewsModel::markAsRead(int index)
{
if (index < 0 || index >= m_items.size())
return;
if (!m_items[index].read) {
m_items[index].read = true;
m_readIds.insert(m_items[index].id);
saveReadIds();
QModelIndex idx = createIndex(index, 0);
emit dataChanged(idx, idx, {IsReadRole});
emit hasUnreadChanged();
}
}
int NewsModel::processedIndex() const
{
return m_processedIndex;
}
void NewsModel::setProcessedIndex(int index)
{
if (index < 0 || index >= m_items.size() || m_processedIndex == index)
return;
m_processedIndex = index;
emit processedIndexChanged(index);
}
void NewsModel::updateModel(const QJsonArray &serverItems)
{
QSet<QString> existingIds;
for (const NewsItem &item : m_items) {
existingIds.insert(item.id);
}
QList<NewsItem> newItems;
for (const QJsonValue &value : serverItems) {
if (!value.isObject()) continue;
const QJsonObject obj = value.toObject();
QString id = obj.value("id").toString();
if (!existingIds.contains(id)) {
NewsItem item;
item.id = id;
item.title = obj.value("title").toString();
item.content = obj.value("content").toString();
item.timestamp = QDateTime::fromString(obj.value("timestamp").toString(), Qt::ISODate);
item.read = m_readIds.contains(id);
newItems.append(item);
existingIds.insert(id);
}
}
if (!newItems.isEmpty()) {
beginResetModel();
m_items.append(newItems);
// Sort descending by timestamp (newest first)
std::sort(m_items.begin(), m_items.end(), [](const NewsItem &a, const NewsItem &b) {
return a.timestamp > b.timestamp;
});
endResetModel();
emit hasUnreadChanged();
}
}
bool NewsModel::hasUnread() const
{
for (const NewsItem &item : m_items) {
if (!item.read)
return true;
}
return false;
}
void NewsModel::loadReadIds()
{
QStringList ids = m_settings->readNewsIds();
m_readIds = QSet<QString>(ids.begin(), ids.end());
}
void NewsModel::saveReadIds() const
{
m_settings->setReadNewsIds(QStringList(m_readIds.begin(), m_readIds.end()));
}
+61
View File
@@ -0,0 +1,61 @@
#ifndef NEWSMODEL_H
#define NEWSMODEL_H
#include <QAbstractListModel>
#include <QDateTime>
#include <QVector>
#include <QString>
#include <QJsonArray>
#include <QSet>
#include <memory>
#include "settings.h"
struct NewsItem {
QString id;
QString title;
QString content;
QDateTime timestamp;
bool read;
};
class NewsModel : public QAbstractListModel
{
Q_OBJECT
public:
enum Roles {
IdRole = Qt::UserRole + 1,
TitleRole,
ContentRole,
TimestampRole,
IsReadRole,
IsProcessedRole
};
explicit NewsModel(const std::shared_ptr<Settings> &settings, QObject *parent = nullptr);
Q_INVOKABLE void markAsRead(int index);
Q_PROPERTY(int processedIndex READ processedIndex WRITE setProcessedIndex NOTIFY processedIndexChanged)
Q_PROPERTY(bool hasUnread READ hasUnread NOTIFY hasUnreadChanged)
int processedIndex() const;
void setProcessedIndex(int index);
void updateModel(const QJsonArray &items);
bool hasUnread() const;
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
QHash<int, QByteArray> roleNames() const override;
signals:
void processedIndexChanged(int index);
void hasUnreadChanged();
private:
QVector<NewsItem> m_items;
int m_processedIndex = -1;
std::shared_ptr<Settings> m_settings;
QSet<QString> m_readIds;
void loadReadIds();
void saveReadIds() const;
};
#endif // NEWSMODEL_H
-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
} }
@@ -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,12 +81,20 @@ DrawerType2 {
} }
} }
ScrollBar.vertical: ScrollBarType {}
ButtonGroup { ButtonGroup {
id: buttonGroup id: buttonGroup
} }
delegate: ColumnLayout { delegate: Item {
width: listView.width implicitWidth: root.width
implicitHeight: delegateContent.implicitHeight
ColumnLayout {
id: delegateContent
anchors.fill: parent
RowLayout { RowLayout {
CheckBoxType { CheckBoxType {
@@ -108,6 +121,7 @@ DrawerType2 {
} }
} }
} }
}
TextFieldWithHeaderType { TextFieldWithHeaderType {
id: searchField id: searchField
@@ -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,16 +13,29 @@ import "../Controls2"
import "../Controls2/TextTypes" import "../Controls2/TextTypes"
ListViewType { ListView {
id: root id: root
width: parent.width
height: root.contentItem.height
clip: true
reuseItems: true
property bool isFocusable: false
delegate: Item {
implicitWidth: root.width
implicitHeight: delegateContent.implicitHeight
ColumnLayout {
id: delegateContent
anchors.fill: parent anchors.fill: parent
delegate: ColumnLayout {
width: root.width
LabelWithButtonType { LabelWithButtonType {
Layout.fillWidth: true id: containerRadioButton
implicitWidth: parent.width
text: name text: name
descriptionText: description descriptionText: description
@@ -74,3 +87,4 @@ ListViewType {
DividerType {} 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
+5 -22
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
+23 -13
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,23 +29,36 @@ PageType {
] ]
} }
ListViewType { FlickableType {
id: listView id: fl
anchors.fill: parent anchors.fill: parent
contentHeight: content.height
Column {
id: content
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
spacing: 16 spacing: 16
Repeater {
model: proxyServersModel model: proxyServersModel
delegate: Item {
implicitWidth: parent.width
implicitHeight: delegateContent.implicitHeight
delegate: ColumnLayout { ColumnLayout {
width: listView.width id: delegateContent
anchors.fill: parent
anchors.rightMargin: 16
anchors.leftMargin: 16
BaseHeaderType { BaseHeaderType {
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 20 Layout.topMargin: 20
Layout.leftMargin: 16
Layout.rightMargin: 16
headerText: qsTr("Removing services from %1").arg(name) headerText: qsTr("Removing services from %1").arg(name)
} }
@@ -57,8 +68,6 @@ PageType {
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 32 Layout.topMargin: 32
Layout.leftMargin: 16
Layout.rightMargin: 16
Timer { Timer {
id: timer id: timer
@@ -75,11 +84,12 @@ PageType {
ParagraphTextType { ParagraphTextType {
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 8 Layout.topMargin: 8
Layout.leftMargin: 16
Layout.rightMargin: 16
text: qsTr("Usually it takes no more than 5 minutes") 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,60 +16,91 @@ 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 === ""
ColumnLayout {
id: col
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
anchors.leftMargin: 16
anchors.rightMargin: 16
spacing: 0 spacing: 0
BaseHeaderType {
Layout.fillWidth: true
headerText: qsTr("AmneziaWG settings")
}
TextFieldWithHeaderType { TextFieldWithHeaderType {
id: mtuTextField id: mtuTextField
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 40 Layout.topMargin: 40
Layout.leftMargin: 16
Layout.rightMargin: 16
headerText: qsTr("MTU") headerText: qsTr("MTU")
textField.text: clientMtu textField.text: clientMtu
@@ -81,14 +112,11 @@ PageType {
} }
} }
checkEmptyText: true checkEmptyText: true
KeyNavigation.tab: junkPacketCountTextField.textField
} }
AwgTextField { AwgTextField {
id: junkPacketCountTextField id: junkPacketCountTextField
Layout.leftMargin: 16
Layout.rightMargin: 16
headerText: "Jc - Junk packet count" headerText: "Jc - Junk packet count"
textField.text: clientJunkPacketCount textField.text: clientJunkPacketCount
@@ -97,14 +125,12 @@ PageType {
clientJunkPacketCount = textField.text clientJunkPacketCount = textField.text
} }
} }
KeyNavigation.tab: junkPacketMinSizeTextField.textField
} }
AwgTextField { AwgTextField {
id: junkPacketMinSizeTextField id: junkPacketMinSizeTextField
Layout.leftMargin: 16
Layout.rightMargin: 16
headerText: "Jmin - Junk packet minimum size" headerText: "Jmin - Junk packet minimum size"
textField.text: clientJunkPacketMinSize textField.text: clientJunkPacketMinSize
@@ -113,14 +139,12 @@ PageType {
clientJunkPacketMinSize = textField.text clientJunkPacketMinSize = textField.text
} }
} }
KeyNavigation.tab: junkPacketMaxSizeTextField.textField
} }
AwgTextField { AwgTextField {
id: junkPacketMaxSizeTextField id: junkPacketMaxSizeTextField
Layout.leftMargin: 16
Layout.rightMargin: 16
headerText: "Jmax - Junk packet maximum size" headerText: "Jmax - Junk packet maximum size"
textField.text: clientJunkPacketMaxSize textField.text: clientJunkPacketMaxSize
@@ -133,10 +157,6 @@ PageType {
AwgTextField { AwgTextField {
id: specialJunk1TextField id: specialJunk1TextField
Layout.leftMargin: 16
Layout.rightMargin: 16
headerText: qsTr("I1 - First special junk packet") headerText: qsTr("I1 - First special junk packet")
textField.text: clientSpecialJunk1 textField.text: clientSpecialJunk1
textField.validator: null textField.validator: null
@@ -151,10 +171,6 @@ PageType {
AwgTextField { AwgTextField {
id: specialJunk2TextField id: specialJunk2TextField
Layout.leftMargin: 16
Layout.rightMargin: 16
headerText: qsTr("I2 - Second special junk packet") headerText: qsTr("I2 - Second special junk packet")
textField.text: clientSpecialJunk2 textField.text: clientSpecialJunk2
textField.validator: null textField.validator: null
@@ -169,10 +185,6 @@ PageType {
AwgTextField { AwgTextField {
id: specialJunk3TextField id: specialJunk3TextField
Layout.leftMargin: 16
Layout.rightMargin: 16
headerText: qsTr("I3 - Third special junk packet") headerText: qsTr("I3 - Third special junk packet")
textField.text: clientSpecialJunk3 textField.text: clientSpecialJunk3
textField.validator: null textField.validator: null
@@ -187,10 +199,6 @@ PageType {
AwgTextField { AwgTextField {
id: specialJunk4TextField id: specialJunk4TextField
Layout.leftMargin: 16
Layout.rightMargin: 16
headerText: qsTr("I4 - Fourth special junk packet") headerText: qsTr("I4 - Fourth special junk packet")
textField.text: clientSpecialJunk4 textField.text: clientSpecialJunk4
textField.validator: null textField.validator: null
@@ -205,10 +213,6 @@ PageType {
AwgTextField { AwgTextField {
id: specialJunk5TextField id: specialJunk5TextField
Layout.leftMargin: 16
Layout.rightMargin: 16
headerText: qsTr("I5 - Fifth special junk packet") headerText: qsTr("I5 - Fifth special junk packet")
textField.text: clientSpecialJunk5 textField.text: clientSpecialJunk5
textField.validator: null textField.validator: null
@@ -223,10 +227,6 @@ PageType {
AwgTextField { AwgTextField {
id: controlledJunk1TextField id: controlledJunk1TextField
Layout.leftMargin: 16
Layout.rightMargin: 16
headerText: qsTr("J1 - First controlled junk packet") headerText: qsTr("J1 - First controlled junk packet")
textField.text: clientControlledJunk1 textField.text: clientControlledJunk1
textField.validator: null textField.validator: null
@@ -241,10 +241,6 @@ PageType {
AwgTextField { AwgTextField {
id: controlledJunk2TextField id: controlledJunk2TextField
Layout.leftMargin: 16
Layout.rightMargin: 16
headerText: qsTr("J2 - Second controlled junk packet") headerText: qsTr("J2 - Second controlled junk packet")
textField.text: clientControlledJunk2 textField.text: clientControlledJunk2
textField.validator: null textField.validator: null
@@ -259,10 +255,6 @@ PageType {
AwgTextField { AwgTextField {
id: controlledJunk3TextField id: controlledJunk3TextField
Layout.leftMargin: 16
Layout.rightMargin: 16
headerText: qsTr("J3 - Third controlled junk packet") headerText: qsTr("J3 - Third controlled junk packet")
textField.text: clientControlledJunk3 textField.text: clientControlledJunk3
textField.validator: null textField.validator: null
@@ -277,10 +269,6 @@ PageType {
AwgTextField { AwgTextField {
id: iTimeTextField id: iTimeTextField
Layout.leftMargin: 16
Layout.rightMargin: 16
headerText: qsTr("Itime - Special handshake timeout") headerText: qsTr("Itime - Special handshake timeout")
textField.text: clientSpecialHandshakeTimeout textField.text: clientSpecialHandshakeTimeout
checkEmptyText: false checkEmptyText: false
@@ -295,18 +283,12 @@ PageType {
Header2TextType { Header2TextType {
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 16 Layout.topMargin: 16
Layout.leftMargin: 16
Layout.rightMargin: 16
text: qsTr("Server settings") text: qsTr("Server settings")
} }
AwgTextField { AwgTextField {
id: portTextField id: portTextField
Layout.leftMargin: 16
Layout.rightMargin: 16
enabled: false enabled: false
headerText: qsTr("Port") headerText: qsTr("Port")
@@ -315,10 +297,6 @@ PageType {
AwgTextField { AwgTextField {
id: initPacketJunkSizeTextField id: initPacketJunkSizeTextField
Layout.leftMargin: 16
Layout.rightMargin: 16
enabled: false enabled: false
headerText: "S1 - Init packet junk size" headerText: "S1 - Init packet junk size"
@@ -327,10 +305,6 @@ PageType {
AwgTextField { AwgTextField {
id: responsePacketJunkSizeTextField id: responsePacketJunkSizeTextField
Layout.leftMargin: 16
Layout.rightMargin: 16
enabled: false enabled: false
headerText: "S2 - Response packet junk size" headerText: "S2 - Response packet junk size"
@@ -339,10 +313,6 @@ PageType {
// AwgTextField { // AwgTextField {
// id: cookieReplyPacketJunkSizeTextField // id: cookieReplyPacketJunkSizeTextField
// Layout.leftMargin: 16
// Layout.rightMargin: 16
// enabled: false // enabled: false
// headerText: "S3 - Cookie Reply packet junk size" // headerText: "S3 - Cookie Reply packet junk size"
@@ -351,10 +321,6 @@ PageType {
// AwgTextField { // AwgTextField {
// id: transportPacketJunkSizeTextField // id: transportPacketJunkSizeTextField
// Layout.leftMargin: 16
// Layout.rightMargin: 16
// enabled: false // enabled: false
// headerText: "S4 - Transport packet junk size" // headerText: "S4 - Transport packet junk size"
@@ -363,10 +329,6 @@ PageType {
AwgTextField { AwgTextField {
id: initPacketMagicHeaderTextField id: initPacketMagicHeaderTextField
Layout.leftMargin: 16
Layout.rightMargin: 16
enabled: false enabled: false
headerText: "H1 - Init packet magic header" headerText: "H1 - Init packet magic header"
@@ -375,10 +337,6 @@ PageType {
AwgTextField { AwgTextField {
id: responsePacketMagicHeaderTextField id: responsePacketMagicHeaderTextField
Layout.leftMargin: 16
Layout.rightMargin: 16
enabled: false enabled: false
headerText: "H2 - Response packet magic header" headerText: "H2 - Response packet magic header"
@@ -387,10 +345,6 @@ PageType {
AwgTextField { AwgTextField {
id: underloadPacketMagicHeaderTextField id: underloadPacketMagicHeaderTextField
Layout.leftMargin: 16
Layout.rightMargin: 16
enabled: false enabled: false
headerText: "H3 - Underload packet magic header" headerText: "H3 - Underload packet magic header"
@@ -399,15 +353,13 @@ PageType {
AwgTextField { AwgTextField {
id: transportPacketMagicHeaderTextField id: transportPacketMagicHeaderTextField
Layout.leftMargin: 16
Layout.rightMargin: 16
enabled: false enabled: false
headerText: "H4 - Transport packet magic header" headerText: "H4 - Transport packet magic header"
textField.text: serverTransportPacketMagicHeader 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)
} }
} }
@@ -19,45 +19,80 @@ 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()
ColumnLayout {
id: col
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
anchors.leftMargin: 16
anchors.rightMargin: 16
spacing: 0 spacing: 0
BaseHeaderType { BaseHeaderType {
Layout.fillWidth: true Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 16
headerText: qsTr("AmneziaWG settings") headerText: qsTr("AmneziaWG settings")
} }
@@ -67,8 +102,6 @@ PageType {
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 40 Layout.topMargin: 40
Layout.leftMargin: 16
Layout.rightMargin: 16
enabled: delegateItem.isEnabled enabled: delegateItem.isEnabled
@@ -88,8 +121,6 @@ PageType {
id: portTextField id: portTextField
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 16 Layout.topMargin: 16
Layout.leftMargin: 16
Layout.rightMargin: 16
enabled: delegateItem.isEnabled enabled: delegateItem.isEnabled
@@ -109,10 +140,6 @@ PageType {
AwgTextField { AwgTextField {
id: junkPacketCountTextField id: junkPacketCountTextField
Layout.leftMargin: 16
Layout.rightMargin: 16
headerText: qsTr("Jc - Junk packet count") headerText: qsTr("Jc - Junk packet count")
textField.text: serverJunkPacketCount textField.text: serverJunkPacketCount
@@ -125,10 +152,6 @@ PageType {
AwgTextField { AwgTextField {
id: junkPacketMinSizeTextField id: junkPacketMinSizeTextField
Layout.leftMargin: 16
Layout.rightMargin: 16
headerText: qsTr("Jmin - Junk packet minimum size") headerText: qsTr("Jmin - Junk packet minimum size")
textField.text: serverJunkPacketMinSize textField.text: serverJunkPacketMinSize
@@ -141,10 +164,6 @@ PageType {
AwgTextField { AwgTextField {
id: junkPacketMaxSizeTextField id: junkPacketMaxSizeTextField
Layout.leftMargin: 16
Layout.rightMargin: 16
headerText: qsTr("Jmax - Junk packet maximum size") headerText: qsTr("Jmax - Junk packet maximum size")
textField.text: serverJunkPacketMaxSize textField.text: serverJunkPacketMaxSize
@@ -157,10 +176,6 @@ PageType {
AwgTextField { AwgTextField {
id: initPacketJunkSizeTextField id: initPacketJunkSizeTextField
Layout.leftMargin: 16
Layout.rightMargin: 16
headerText: qsTr("S1 - Init packet junk size") headerText: qsTr("S1 - Init packet junk size")
textField.text: serverInitPacketJunkSize textField.text: serverInitPacketJunkSize
@@ -173,10 +188,6 @@ PageType {
AwgTextField { AwgTextField {
id: responsePacketJunkSizeTextField id: responsePacketJunkSizeTextField
Layout.leftMargin: 16
Layout.rightMargin: 16
headerText: qsTr("S2 - Response packet junk size") headerText: qsTr("S2 - Response packet junk size")
textField.text: serverResponsePacketJunkSize textField.text: serverResponsePacketJunkSize
@@ -189,10 +200,6 @@ PageType {
// AwgTextField { // AwgTextField {
// id: cookieReplyPacketJunkSizeTextField // id: cookieReplyPacketJunkSizeTextField
// Layout.leftMargin: 16
// Layout.rightMargin: 16
// headerText: qsTr("S3 - Cookie reply packet junk size") // headerText: qsTr("S3 - Cookie reply packet junk size")
// textField.text: serverCookieReplyPacketJunkSize // textField.text: serverCookieReplyPacketJunkSize
@@ -205,10 +212,6 @@ PageType {
// AwgTextField { // AwgTextField {
// id: transportPacketJunkSizeTextField // id: transportPacketJunkSizeTextField
// Layout.leftMargin: 16
// Layout.rightMargin: 16
// headerText: qsTr("S4 - Transport packet junk size") // headerText: qsTr("S4 - Transport packet junk size")
// textField.text: serverTransportPacketJunkSize // textField.text: serverTransportPacketJunkSize
@@ -221,10 +224,6 @@ PageType {
AwgTextField { AwgTextField {
id: initPacketMagicHeaderTextField id: initPacketMagicHeaderTextField
Layout.leftMargin: 16
Layout.rightMargin: 16
headerText: qsTr("H1 - Init packet magic header") headerText: qsTr("H1 - Init packet magic header")
textField.text: serverInitPacketMagicHeader textField.text: serverInitPacketMagicHeader
@@ -237,10 +236,6 @@ PageType {
AwgTextField { AwgTextField {
id: responsePacketMagicHeaderTextField id: responsePacketMagicHeaderTextField
Layout.leftMargin: 16
Layout.rightMargin: 16
headerText: qsTr("H2 - Response packet magic header") headerText: qsTr("H2 - Response packet magic header")
textField.text: serverResponsePacketMagicHeader textField.text: serverResponsePacketMagicHeader
@@ -253,10 +248,6 @@ PageType {
AwgTextField { AwgTextField {
id: underloadPacketMagicHeaderTextField id: underloadPacketMagicHeaderTextField
Layout.leftMargin: 16
Layout.rightMargin: 16
headerText: qsTr("H3 - Underload packet magic header") headerText: qsTr("H3 - Underload packet magic header")
textField.text: serverUnderloadPacketMagicHeader textField.text: serverUnderloadPacketMagicHeader
@@ -269,10 +260,6 @@ PageType {
AwgTextField { AwgTextField {
id: transportPacketMagicHeaderTextField id: transportPacketMagicHeaderTextField
Layout.leftMargin: 16
Layout.rightMargin: 16
headerText: qsTr("H4 - Transport packet magic header") headerText: qsTr("H4 - Transport packet magic header")
textField.text: serverTransportPacketMagicHeader textField.text: serverTransportPacketMagicHeader
@@ -283,14 +270,13 @@ PageType {
} }
} }
BasicButtonType { BasicButtonType {
id: saveRestartButton id: saveRestartButton
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: underloadPacketMagicHeaderTextField.errorText === "" && enabled: underloadPacketMagicHeaderTextField.errorText === "" &&
transportPacketMagicHeaderTextField.errorText === "" && transportPacketMagicHeaderTextField.errorText === "" &&
@@ -310,11 +296,13 @@ PageType {
onActiveFocusChanged: { onActiveFocusChanged: {
if(activeFocus) { if(activeFocus) {
listView.positionViewAtEnd() listview.positionViewAtEnd()
} }
} }
clickedFunc: function() { clickedFunc: function() {
forceActiveFocus()
if (delegateItem.isEnabled) { if (delegateItem.isEnabled) {
if (AwgConfigModel.isHeadersEqual(underloadPacketMagicHeaderTextField.textField.text, if (AwgConfigModel.isHeadersEqual(underloadPacketMagicHeaderTextField.textField.text,
transportPacketMagicHeaderTextField.textField.text, transportPacketMagicHeaderTextField.textField.text,
@@ -352,12 +340,15 @@ 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()) {
saveRestartButton.forceActiveFocus()
}
}
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction) showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
} }
} }
} }
} }
} }
}
@@ -16,61 +16,81 @@ 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
contentHeight: content.implicitHeight
Column {
id: content
anchors.top: parent.top
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
property int selectedIndex: 0
enabled: ServersModel.isProcessedServerHasWriteAccess() enabled: ServersModel.isProcessedServerHasWriteAccess()
header: ColumnLayout { ListView {
width: listView.width id: listview
BaseHeaderType { property int selectedIndex: 0
Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 16
headerText: qsTr("Cloak settings") width: parent.width
} height: listview.contentItem.height
}
clip: true
reuseItems: true
model: CloakConfigModel model: CloakConfigModel
delegate: ColumnLayout { delegate: Item {
width: listView.width id: delegateItem
property alias trafficFromField: trafficFromField property alias trafficFromField: trafficFromField
property bool isEnabled: ServersModel.isProcessedServerHasWriteAccess()
implicitWidth: listview.width
implicitHeight: col.implicitHeight
ColumnLayout {
id: col
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
anchors.leftMargin: 16
anchors.rightMargin: 16
spacing: 0 spacing: 0
BaseHeaderType {
Layout.fillWidth: true
headerText: qsTr("Cloak settings")
}
TextFieldWithHeaderType { TextFieldWithHeaderType {
id: trafficFromField id: trafficFromField
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 32 Layout.topMargin: 32
Layout.leftMargin: 16
Layout.rightMargin: 16 enabled: delegateItem.isEnabled
headerText: qsTr("Disguised as traffic from") headerText: qsTr("Disguised as traffic from")
textField.text: site textField.text: site
@@ -97,8 +117,8 @@ PageType {
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 16 Layout.topMargin: 16
Layout.leftMargin: 16
Layout.rightMargin: 16 enabled: delegateItem.isEnabled
headerText: qsTr("Port") headerText: qsTr("Port")
textField.text: port textField.text: port
@@ -116,11 +136,10 @@ PageType {
DropDownType { DropDownType {
id: cipherDropDown id: cipherDropDown
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 16 Layout.topMargin: 16
Layout.leftMargin: 16
Layout.rightMargin: 16 enabled: delegateItem.isEnabled
descriptionText: qsTr("Cipher") descriptionText: qsTr("Cipher")
headerText: qsTr("Cipher") headerText: qsTr("Cipher")
@@ -164,8 +183,6 @@ PageType {
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 === ""
@@ -205,3 +222,6 @@ PageType {
} }
} }
} }
}
}
}
@@ -17,65 +17,85 @@ 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
Column {
id: content
anchors.top: parent.top
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right
enabled: ServersModel.isProcessedServerHasWriteAccess() enabled: ServersModel.isProcessedServerHasWriteAccess()
header: ColumnLayout { ListView {
width: listView.width id: listview
BaseHeaderType { width: parent.width
id: header height: listview.contentItem.height
Layout.fillWidth: true clip: true
Layout.rightMargin: 16 interactive: false
Layout.leftMargin: 16
headerText: qsTr("OpenVPN Settings")
}
}
model: OpenVpnConfigModel model: OpenVpnConfigModel
delegate: ColumnLayout { delegate: Item {
width: listView.width id: delegateItem
property alias vpnAddressSubnetTextField: vpnAddressSubnetTextField
property bool isEnabled: ServersModel.isProcessedServerHasWriteAccess()
implicitWidth: listview.width
implicitHeight: col.implicitHeight
ColumnLayout {
id: col
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
anchors.leftMargin: 16
anchors.rightMargin: 16
spacing: 0 spacing: 0
BaseHeaderType {
Layout.fillWidth: true
headerText: qsTr("OpenVPN settings")
}
TextFieldWithHeaderType { TextFieldWithHeaderType {
id: vpnAddressSubnetTextField id: vpnAddressSubnetTextField
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 32 Layout.topMargin: 32
Layout.leftMargin: 16
Layout.rightMargin: 16
enabled: listView.enabled enabled: delegateItem.isEnabled
headerText: qsTr("VPN address subnet") headerText: qsTr("VPN address subnet")
textField.text: subnetAddress textField.text: subnetAddress
parentFlickable: fl
textField.onEditingFinished: { textField.onEditingFinished: {
if (textField.text !== subnetAddress) { if (textField.text !== subnetAddress) {
subnetAddress = textField.text subnetAddress = textField.text
@@ -88,8 +108,6 @@ PageType {
ParagraphTextType { ParagraphTextType {
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 32 Layout.topMargin: 32
Layout.leftMargin: 16
Layout.rightMargin: 16
text: qsTr("Network protocol") text: qsTr("Network protocol")
} }
@@ -98,9 +116,6 @@ PageType {
id: transportProtoSelector id: transportProtoSelector
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 16 Layout.topMargin: 16
Layout.leftMargin: 16
Layout.rightMargin: 16
rootWidth: root.width rootWidth: root.width
enabled: isTransportProtoEditable enabled: isTransportProtoEditable
@@ -123,10 +138,9 @@ PageType {
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 40 Layout.topMargin: 40
Layout.leftMargin: 16 parentFlickable: fl
Layout.rightMargin: 16
enabled: listView.enabled enabled: delegateItem.isEnabled
headerText: qsTr("Port") headerText: qsTr("Port")
textField.text: port textField.text: port
@@ -147,8 +161,7 @@ PageType {
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 24 Layout.topMargin: 24
Layout.leftMargin: 16 parentFlickable: fl
Layout.rightMargin: 16
text: qsTr("Auto-negotiate encryption") text: qsTr("Auto-negotiate encryption")
checked: autoNegotiateEncryprion checked: autoNegotiateEncryprion
@@ -164,8 +177,6 @@ PageType {
id: hashDropDown id: hashDropDown
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 20 Layout.topMargin: 20
Layout.leftMargin: 16
Layout.rightMargin: 16
enabled: !autoNegotiateEncryprionSwitcher.checked enabled: !autoNegotiateEncryprionSwitcher.checked
@@ -214,8 +225,6 @@ PageType {
id: cipherDropDown id: cipherDropDown
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 16 Layout.topMargin: 16
Layout.leftMargin: 16
Layout.rightMargin: 16
enabled: !autoNegotiateEncryprionSwitcher.checked enabled: !autoNegotiateEncryprionSwitcher.checked
@@ -264,18 +273,25 @@ PageType {
id: contentRect id: contentRect
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 32 Layout.topMargin: 32
Layout.leftMargin: 16
Layout.rightMargin: 16
Layout.preferredHeight: checkboxLayout.implicitHeight Layout.preferredHeight: checkboxLayout.implicitHeight
color: AmneziaStyle.color.onyxBlack color: AmneziaStyle.color.onyxBlack
radius: 16 radius: 16
Connections {
target: tlsAuthCheckBox
enabled: !GC.isMobile()
function onFocusChanged() {
if (tlsAuthCheckBox.activeFocus) {
fl.ensureVisible(contentRect)
}
}
}
ColumnLayout { ColumnLayout {
id: checkboxLayout id: checkboxLayout
anchors.fill: parent anchors.fill: parent
CheckBoxType { CheckBoxType {
id: tlsAuthCheckBox id: tlsAuthCheckBox
Layout.fillWidth: true Layout.fillWidth: true
@@ -313,8 +329,7 @@ PageType {
id: additionalClientCommandsSwitcher id: additionalClientCommandsSwitcher
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 32 Layout.topMargin: 32
Layout.leftMargin: 16 parentFlickable: fl
Layout.rightMargin: 16
checked: additionalClientCommands !== "" checked: additionalClientCommands !== ""
@@ -331,11 +346,11 @@ PageType {
id: additionalClientCommandsTextArea id: additionalClientCommandsTextArea
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 16 Layout.topMargin: 16
Layout.leftMargin: 16
Layout.rightMargin: 16
visible: additionalClientCommandsSwitcher.checked visible: additionalClientCommandsSwitcher.checked
parentFlickable: fl
textAreaText: additionalClientCommands textAreaText: additionalClientCommands
placeholderText: qsTr("Commands:") placeholderText: qsTr("Commands:")
@@ -350,8 +365,7 @@ PageType {
id: additionalServerCommandsSwitcher id: additionalServerCommandsSwitcher
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 16 Layout.topMargin: 16
Layout.leftMargin: 16 parentFlickable: fl
Layout.rightMargin: 16
checked: additionalServerCommands !== "" checked: additionalServerCommands !== ""
@@ -368,14 +382,12 @@ PageType {
id: additionalServerCommandsTextArea id: additionalServerCommandsTextArea
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 16 Layout.topMargin: 16
Layout.leftMargin: 16
Layout.rightMargin: 16
visible: additionalServerCommandsSwitcher.checked visible: additionalServerCommandsSwitcher.checked
textAreaText: additionalServerCommands textAreaText: additionalServerCommands
placeholderText: qsTr("Commands:") placeholderText: qsTr("Commands:")
parentFlickable: fl
textArea.onEditingFinished: { textArea.onEditingFinished: {
if (additionalServerCommands !== textAreaText) { if (additionalServerCommands !== textAreaText) {
additionalServerCommands = textAreaText additionalServerCommands = textAreaText
@@ -389,13 +401,12 @@ PageType {
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: vpnAddressSubnetTextField.errorText === "" && enabled: vpnAddressSubnetTextField.errorText === "" &&
portTextField.errorText === "" portTextField.errorText === ""
text: qsTr("Save") text: qsTr("Save")
parentFlickable: fl
onClicked: function() { onClicked: function() {
forceActiveFocus() forceActiveFocus()
@@ -428,3 +439,6 @@ PageType {
} }
} }
} }
}
}
}
+60 -46
View File
@@ -19,53 +19,69 @@ 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()
} }
}
}
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 { BaseHeaderType {
Layout.fillWidth: true Layout.fillWidth: true
Layout.leftMargin: 16 Layout.leftMargin: 16
Layout.rightMargin: 16 Layout.rightMargin: 16
Layout.bottomMargin: 16
headerText: ContainersModel.getProcessedContainerName() + qsTr(" settings") headerText: ContainersModel.getProcessedContainerName() + qsTr(" settings")
} }
} }
FlickableType {
id: fl
anchors.top: header.bottom
anchors.left: parent.left
anchors.right: parent.right
contentHeight: content.height
Column {
id: content
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
anchors.topMargin: 32
ListView {
id: listView
width: parent.width
height: contentItem.height
clip: true
interactive: false
model: ProtocolsModel model: ProtocolsModel
delegate: ColumnLayout { activeFocusOnTab: true
width: listView.width focus: true
delegate: Item {
implicitWidth: parent.width
implicitHeight: delegateContent.implicitHeight
property alias focusItem: button
ColumnLayout {
id: delegateContent
anchors.fill: parent
LabelWithButtonType { LabelWithButtonType {
id: button id: button
Layout.fillWidth: true Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 16
text: qsTr("Show connection options") text: qsTr("Show connection options")
@@ -94,7 +110,7 @@ PageType {
implicitHeight: configContentDrawer.expandedHeight implicitHeight: configContentDrawer.expandedHeight
BackButtonType { BackButtonType {
id: drawerBackButton id: backButton1
anchors.top: parent.top anchors.top: parent.top
anchors.left: parent.left anchors.left: parent.left
@@ -106,41 +122,36 @@ PageType {
} }
} }
ListViewType { FlickableType {
id: drawerListView anchors.top: backButton1.bottom
anchors.top: drawerBackButton.bottom
anchors.bottom: parent.bottom
anchors.right: parent.right
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: parent.bottom
contentHeight: configContent.implicitHeight + configContent.anchors.topMargin + configContent.anchors.bottomMargin
header: ColumnLayout { ColumnLayout {
width: drawerListView.width id: configContent
anchors.fill: parent
anchors.rightMargin: 16
anchors.leftMargin: 16
Header2Type { Header2Type {
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 16 Layout.topMargin: 16
Layout.leftMargin: 16
Layout.rightMargin: 16
headerText: qsTr("Connection options %1").arg(protocolName) headerText: qsTr("Connection options %1").arg(protocolName)
} }
}
model: 1 // fake model to force the ListView to be created without a model
delegate: ColumnLayout {
width: drawerListView.width
TextArea { TextArea {
id: configText id: configText
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 16 Layout.topMargin: 16
Layout.leftMargin: 16 Layout.bottomMargin: 16
Layout.rightMargin: 16
padding: 0 padding: 0
leftPadding: 0
height: 24 height: 24
color: AmneziaStyle.color.paleGray color: AmneziaStyle.color.paleGray
@@ -164,9 +175,8 @@ PageType {
} }
} }
} }
}
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,43 +16,68 @@ 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
Column {
id: content
anchors.top: parent.top
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right
enabled: ServersModel.isProcessedServerHasWriteAccess() enabled: ServersModel.isProcessedServerHasWriteAccess()
ListView {
id: listview
width: parent.width
height: listview.contentItem.height
clip: true
interactive: false
model: ShadowSocksConfigModel model: ShadowSocksConfigModel
delegate: ColumnLayout { delegate: Item {
width: listView.width id: delegateItem
property bool isEnabled: ServersModel.isProcessedServerHasWriteAccess()
implicitWidth: listview.width
implicitHeight: col.implicitHeight
ColumnLayout {
id: col
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
anchors.leftMargin: 16
anchors.rightMargin: 16
spacing: 0 spacing: 0
BaseHeaderType { BaseHeaderType {
Layout.fillWidth: true Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 16
headerText: qsTr("Shadowsocks settings") headerText: qsTr("Shadowsocks settings")
} }
@@ -61,10 +86,8 @@ PageType {
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 40 Layout.topMargin: 40
Layout.leftMargin: 16
Layout.rightMargin: 16
enabled: listView.enabled enabled: delegateItem.isEnabled
headerText: qsTr("Port") headerText: qsTr("Port")
textField.text: port textField.text: port
@@ -82,13 +105,10 @@ PageType {
DropDownType { DropDownType {
id: cipherDropDown id: cipherDropDown
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 20 Layout.topMargin: 20
Layout.leftMargin: 16
Layout.rightMargin: 16
enabled: listView.enabled enabled: delegateItem.isEnabled
descriptionText: qsTr("Cipher") descriptionText: qsTr("Cipher")
headerText: qsTr("Cipher") headerText: qsTr("Cipher")
@@ -133,8 +153,6 @@ PageType {
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 === ""
@@ -171,3 +189,6 @@ PageType {
} }
} }
} }
}
}
}
@@ -16,43 +16,77 @@ 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
}
} }
} }
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 + saveButton.implicitHeight + saveButton.anchors.bottomMargin + saveButton.anchors.topMargin
Column {
id: content
anchors.top: parent.top
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right
ListView {
id: listview
width: parent.width
height: listview.contentItem.height
clip: true
interactive: false
model: WireGuardConfigModel model: WireGuardConfigModel
delegate: ColumnLayout { delegate: Item {
width: listView.width id: delegateItem
implicitWidth: listview.width
implicitHeight: col.implicitHeight
property alias mtuTextField: mtuTextField property alias mtuTextField: mtuTextField
property bool isSaveButtonEnabled: mtuTextField.errorText === "" 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 spacing: 0
BaseHeaderType { BaseHeaderType {
Layout.fillWidth: true Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 16
headerText: qsTr("WG settings") headerText: qsTr("WG settings")
} }
@@ -61,8 +95,6 @@ PageType {
id: mtuTextField id: mtuTextField
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 40 Layout.topMargin: 40
Layout.leftMargin: 16
Layout.rightMargin: 16
headerText: qsTr("MTU") headerText: qsTr("MTU")
textField.text: clientMtu textField.text: clientMtu
@@ -74,13 +106,12 @@ PageType {
} }
} }
checkEmptyText: true checkEmptyText: true
KeyNavigation.tab: saveButton
} }
Header2TextType { Header2TextType {
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 16 Layout.topMargin: 16
Layout.leftMargin: 16
Layout.rightMargin: 16
text: qsTr("Server settings") text: qsTr("Server settings")
} }
@@ -89,8 +120,6 @@ PageType {
id: portTextField id: portTextField
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 8 Layout.topMargin: 8
Layout.leftMargin: 16
Layout.rightMargin: 16
enabled: false enabled: false
@@ -98,24 +127,29 @@ PageType {
textField.text: port textField.text: port
} }
} }
}
footer: ColumnLayout { }
width: listView.width }
}
BasicButtonType { BasicButtonType {
id: saveButton id: saveButton
Layout.fillWidth: true anchors.right: root.right
Layout.topMargin: 24 anchors.left: root.left
Layout.bottomMargin: 24 anchors.bottom: root.bottom
Layout.rightMargin: 16
Layout.leftMargin: 16
enabled: listView.currentItem.isSaveButtonEnabled anchors.topMargin: 24
anchors.bottomMargin: 24
anchors.rightMargin: 16
anchors.leftMargin: 16
enabled: listview.currentItem.isSaveButtonEnabled
text: qsTr("Save") text: qsTr("Save")
clickedFunc: function() { clickedFunc: function() {
forceActiveFocus()
var headerText = qsTr("Save settings?") var headerText = qsTr("Save settings?")
var descriptionText = qsTr("Only the settings for this device will be changed") var descriptionText = qsTr("Only the settings for this device will be changed")
var yesButtonText = qsTr("Continue") var yesButtonText = qsTr("Continue")
@@ -130,10 +164,12 @@ PageType {
PageController.goToPage(PageEnum.PageSetupWizardInstalling); PageController.goToPage(PageEnum.PageSetupWizardInstalling);
InstallController.updateContainer(WireGuardConfigModel.getConfig()) InstallController.updateContainer(WireGuardConfigModel.getConfig())
} }
var noButtonFunction = function() {} var noButtonFunction = function() {
if (!GC.isMobile()) {
saveButton.forceActiveFocus()
}
}
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction) showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
} }
} }
} }
}
}
@@ -16,55 +16,78 @@ 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
Column {
id: content
anchors.top: parent.top
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right
enabled: ServersModel.isProcessedServerHasWriteAccess() enabled: ServersModel.isProcessedServerHasWriteAccess()
ListView {
id: listview
width: parent.width
height: listview.contentItem.height
clip: true
interactive: false
model: WireGuardConfigModel model: WireGuardConfigModel
delegate: ColumnLayout { delegate: Item {
width: listView.width id: delegateItem
property alias focusItemId: vpnAddressSubnetTextField
property bool isEnabled: ServersModel.isProcessedServerHasWriteAccess() property bool isEnabled: ServersModel.isProcessedServerHasWriteAccess()
implicitWidth: listview.width
implicitHeight: col.implicitHeight
ColumnLayout {
id: col
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
anchors.leftMargin: 16
anchors.rightMargin: 16
spacing: 0 spacing: 0
BaseHeaderType { BaseHeaderType {
Layout.fillWidth: true Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 16
headerText: qsTr("WG settings") headerText: qsTr("WG settings")
} }
TextFieldWithHeaderType { TextFieldWithHeaderType {
id: vpnAddressSubnetTextField id: vpnAddressSubnetTextField
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 40 Layout.topMargin: 40
Layout.leftMargin: 16
Layout.rightMargin: 16 enabled: delegateItem.isEnabled
headerText: qsTr("VPN address subnet") headerText: qsTr("VPN address subnet")
textField.text: subnetAddress textField.text: subnetAddress
@@ -82,8 +105,8 @@ PageType {
id: portTextField id: portTextField
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 16 Layout.topMargin: 16
Layout.leftMargin: 16
Layout.rightMargin: 16 enabled: delegateItem.isEnabled
headerText: qsTr("Port") headerText: qsTr("Port")
textField.text: port textField.text: port
@@ -101,12 +124,9 @@ PageType {
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 === "" &&
vpnAddressSubnetTextField.errorText === "" vpnAddressSubnetTextField.errorText === ""
@@ -144,3 +164,6 @@ PageType {
} }
} }
} }
}
}
}
@@ -17,55 +17,78 @@ 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
contentHeight: content.implicitHeight
Column {
id: content
anchors.top: parent.top
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
enabled: ServersModel.isProcessedServerHasWriteAccess() enabled: ServersModel.isProcessedServerHasWriteAccess()
ListView {
id: listview
width: parent.width
height: listview.contentItem.height
clip: true
interactive: false
model: XrayConfigModel model: XrayConfigModel
delegate: ColumnLayout { delegate: Item {
width: listView.width id: delegateItem
property alias focusItemId: textFieldWithHeaderType.textField property alias focusItemId: textFieldWithHeaderType.textField
property bool isEnabled: ServersModel.isProcessedServerHasWriteAccess()
implicitWidth: listview.width
implicitHeight: col.implicitHeight
ColumnLayout {
id: col
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
anchors.leftMargin: 16
anchors.rightMargin: 16
spacing: 0 spacing: 0
BaseHeaderType { BaseHeaderType {
Layout.fillWidth: true Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 16
headerText: qsTr("XRay settings") headerText: qsTr("XRay settings")
} }
TextFieldWithHeaderType { TextFieldWithHeaderType {
id: textFieldWithHeaderType id: textFieldWithHeaderType
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 32 Layout.topMargin: 32
Layout.leftMargin: 16
Layout.rightMargin: 16
enabled: listView.enabled enabled: delegateItem.isEnabled
headerText: qsTr("Disguised as traffic from") headerText: qsTr("Disguised as traffic from")
textField.text: site textField.text: site
@@ -89,13 +112,10 @@ PageType {
TextFieldWithHeaderType { TextFieldWithHeaderType {
id: portTextField id: portTextField
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 16 Layout.topMargin: 16
Layout.leftMargin: 16
Layout.rightMargin: 16
enabled: listView.enabled enabled: delegateItem.isEnabled
headerText: qsTr("Port") headerText: qsTr("Port")
textField.text: port textField.text: port
@@ -113,12 +133,9 @@ PageType {
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 === ""
@@ -140,6 +157,7 @@ PageType {
PageController.goToPage(PageEnum.PageSetupWizardInstalling); PageController.goToPage(PageEnum.PageSetupWizardInstalling);
InstallController.updateContainer(XrayConfigModel.getConfig()) InstallController.updateContainer(XrayConfigModel.getConfig())
//focusItem.forceActiveFocus()
} }
var noButtonFunction = function() { var noButtonFunction = function() {
if (!GC.isMobile()) { if (!GC.isMobile()) {
@@ -155,3 +173,7 @@ PageType {
} }
} }
} }
}
}
}
+28 -26
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
@@ -76,12 +73,17 @@ PageType {
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)
} }
@@ -24,35 +24,64 @@ 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
Column {
id: content
anchors.top: parent.top
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right
enabled: ServersModel.isProcessedServerHasWriteAccess() enabled: ServersModel.isProcessedServerHasWriteAccess()
ListView {
id: listview
width: parent.width
height: listview.contentItem.height
clip: true
interactive: false
model: SftpConfigModel model: SftpConfigModel
delegate: ColumnLayout { onFocusChanged: {
width: listView.width if (focus) {
listview.currentItem.listViewFocusItem.forceActiveFocus()
}
}
delegate: Item {
implicitWidth: listview.width
implicitHeight: col.implicitHeight
property alias listViewFocusItem: hostLabel.rightButton
ColumnLayout {
id: col
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
spacing: 0 spacing: 0
@@ -66,11 +95,10 @@ PageType {
LabelWithButtonType { LabelWithButtonType {
id: hostLabel id: hostLabel
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 32 Layout.topMargin: 32
Layout.leftMargin: 16
Layout.rightMargin: 16 parentFlickable: fl
text: qsTr("Host") text: qsTr("Host")
descriptionText: ServersModel.getProcessedServerData("hostName") descriptionText: ServersModel.getProcessedServerData("hostName")
@@ -83,63 +111,69 @@ 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()
}
} }
} }
LabelWithButtonType { LabelWithButtonType {
id: portLabel id: portLabel
Layout.fillWidth: true Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 16
text: qsTr("Port") text: qsTr("Port")
descriptionText: port descriptionText: port
descriptionOnTop: true descriptionOnTop: true
parentFlickable: fl
rightImageSource: "qrc:/images/controls/copy.svg" rightImageSource: "qrc:/images/controls/copy.svg"
rightImageColor: AmneziaStyle.color.paleGray rightImageColor: AmneziaStyle.color.paleGray
clickedFunction: function() { clickedFunction: function() {
GC.copyToClipBoard(descriptionText) GC.copyToClipBoard(descriptionText)
PageController.showNotificationMessage(qsTr("Copied")) PageController.showNotificationMessage(qsTr("Copied"))
if (!GC.isMobile()) {
this.rightButton.forceActiveFocus()
}
} }
} }
LabelWithButtonType { LabelWithButtonType {
id: usernameLabel id: usernameLabel
Layout.fillWidth: true Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 16
text: qsTr("User name") text: qsTr("User name")
descriptionText: username descriptionText: username
descriptionOnTop: true descriptionOnTop: true
parentFlickable: fl
rightImageSource: "qrc:/images/controls/copy.svg" rightImageSource: "qrc:/images/controls/copy.svg"
rightImageColor: AmneziaStyle.color.paleGray rightImageColor: AmneziaStyle.color.paleGray
clickedFunction: function() { clickedFunction: function() {
GC.copyToClipBoard(descriptionText) GC.copyToClipBoard(descriptionText)
PageController.showNotificationMessage(qsTr("Copied")) PageController.showNotificationMessage(qsTr("Copied"))
if (!GC.isMobile()) {
this.rightButton.forceActiveFocus()
}
} }
} }
LabelWithButtonType { LabelWithButtonType {
id: passwordLabel id: passwordLabel
Layout.fillWidth: true Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 16
text: qsTr("Password") text: qsTr("Password")
descriptionText: password descriptionText: password
descriptionOnTop: true descriptionOnTop: true
parentFlickable: fl
rightImageSource: "qrc:/images/controls/copy.svg" rightImageSource: "qrc:/images/controls/copy.svg"
rightImageColor: AmneziaStyle.color.paleGray rightImageColor: AmneziaStyle.color.paleGray
@@ -148,12 +182,14 @@ 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()
}
} }
} }
BasicButtonType { BasicButtonType {
id: mountButton id: mountButton
visible: !GC.isMobile() visible: !GC.isMobile()
Layout.fillWidth: true Layout.fillWidth: true
@@ -169,6 +205,8 @@ PageType {
textColor: AmneziaStyle.color.paleGray textColor: AmneziaStyle.color.paleGray
borderWidth: 1 borderWidth: 1
parentFlickable: fl
text: qsTr("Mount folder on device") text: qsTr("Mount folder on device")
clickedFunc: function() { clickedFunc: function() {
@@ -208,6 +246,7 @@ PageType {
return str return str
} }
MouseArea { MouseArea {
anchors.fill: parent anchors.fill: parent
acceptedButtons: Qt.NoButton acceptedButtons: Qt.NoButton
@@ -217,7 +256,6 @@ PageType {
BasicButtonType { BasicButtonType {
id: detailedInstructionsButton id: detailedInstructionsButton
Layout.topMargin: 16 Layout.topMargin: 16
Layout.bottomMargin: 16 Layout.bottomMargin: 16
Layout.leftMargin: 8 Layout.leftMargin: 8
@@ -231,6 +269,8 @@ PageType {
text: qsTr("Detailed instructions") text: qsTr("Detailed instructions")
parentFlickable: fl
clickedFunc: function() { clickedFunc: function() {
// Qt.openUrlExternally("https://github.com/amnezia-vpn/desktop-client/releases/latest") // Qt.openUrlExternally("https://github.com/amnezia-vpn/desktop-client/releases/latest")
} }
@@ -238,3 +278,6 @@ PageType {
} }
} }
} }
}
}
}
@@ -25,33 +25,55 @@ 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
ListView {
id: listview
width: parent.width
height: listview.contentItem.height
clip: true
interactive: false
model: Socks5ProxyConfigModel model: Socks5ProxyConfigModel
delegate: ColumnLayout { onFocusChanged: {
width: listView.width if (focus) {
listview.currentItem.focusItemId.forceActiveFocus()
}
}
delegate: Item {
implicitWidth: listview.width
implicitHeight: content.implicitHeight
property alias focusItemId: hostLabel.rightButton
ColumnLayout {
id: content
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
spacing: 0 spacing: 0
@@ -64,10 +86,11 @@ PageType {
} }
LabelWithButtonType { LabelWithButtonType {
id: hostLabel
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 32 Layout.topMargin: 32
Layout.rightMargin: 16
Layout.bottomMargin: 16 parentFlickable: fl
text: qsTr("Host") text: qsTr("Host")
descriptionText: ServersModel.getProcessedServerData("hostName") descriptionText: ServersModel.getProcessedServerData("hostName")
@@ -80,57 +103,69 @@ 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()
}
} }
} }
LabelWithButtonType { LabelWithButtonType {
id: portLabel
Layout.fillWidth: true Layout.fillWidth: true
Layout.rightMargin: 16
Layout.bottomMargin: 16
text: qsTr("Port") text: qsTr("Port")
descriptionText: port descriptionText: port
descriptionOnTop: true descriptionOnTop: true
parentFlickable: fl
rightImageSource: "qrc:/images/controls/copy.svg" rightImageSource: "qrc:/images/controls/copy.svg"
rightImageColor: AmneziaStyle.color.paleGray rightImageColor: AmneziaStyle.color.paleGray
clickedFunction: function() { clickedFunction: function() {
GC.copyToClipBoard(descriptionText) GC.copyToClipBoard(descriptionText)
PageController.showNotificationMessage(qsTr("Copied")) PageController.showNotificationMessage(qsTr("Copied"))
if (!GC.isMobile()) {
this.rightButton.forceActiveFocus()
}
} }
} }
LabelWithButtonType { LabelWithButtonType {
id: usernameLabel
Layout.fillWidth: true Layout.fillWidth: true
Layout.rightMargin: 16
Layout.bottomMargin: 16
text: qsTr("User name") text: qsTr("User name")
descriptionText: username descriptionText: username
descriptionOnTop: true descriptionOnTop: true
parentFlickable: fl
rightImageSource: "qrc:/images/controls/copy.svg" rightImageSource: "qrc:/images/controls/copy.svg"
rightImageColor: AmneziaStyle.color.paleGray rightImageColor: AmneziaStyle.color.paleGray
clickedFunction: function() { clickedFunction: function() {
GC.copyToClipBoard(descriptionText) GC.copyToClipBoard(descriptionText)
PageController.showNotificationMessage(qsTr("Copied")) PageController.showNotificationMessage(qsTr("Copied"))
if (!GC.isMobile()) {
this.rightButton.forceActiveFocus()
}
} }
} }
LabelWithButtonType { LabelWithButtonType {
id: passwordLabel
Layout.fillWidth: true Layout.fillWidth: true
Layout.rightMargin: 16
Layout.bottomMargin: 16
text: qsTr("Password") text: qsTr("Password")
descriptionText: password descriptionText: password
descriptionOnTop: true descriptionOnTop: true
parentFlickable: fl
rightImageSource: "qrc:/images/controls/copy.svg" rightImageSource: "qrc:/images/controls/copy.svg"
rightImageColor: AmneziaStyle.color.paleGray rightImageColor: AmneziaStyle.color.paleGray
@@ -139,6 +174,9 @@ 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()
}
} }
} }
@@ -181,8 +219,6 @@ PageType {
BaseHeaderType { BaseHeaderType {
Layout.fillWidth: true Layout.fillWidth: true
Layout.rightMargin: 16
Layout.bottomMargin: 16
headerText: qsTr("SOCKS5 settings") headerText: qsTr("SOCKS5 settings")
} }
@@ -192,8 +228,7 @@ PageType {
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 40 Layout.topMargin: 40
Layout.rightMargin: 16 parentFlickable: fl
Layout.bottomMargin: 16
headerText: qsTr("Port") headerText: qsTr("Port")
textField.text: port textField.text: port
@@ -213,8 +248,7 @@ PageType {
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 16 Layout.topMargin: 16
Layout.rightMargin: 16 parentFlickable: fl
Layout.bottomMargin: 16
headerText: qsTr("Username") headerText: qsTr("Username")
textField.placeholderText: "username" textField.placeholderText: "username"
@@ -236,8 +270,7 @@ PageType {
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 16 Layout.topMargin: 16
Layout.rightMargin: 16 parentFlickable: fl
Layout.bottomMargin: 16
headerText: qsTr("Password") headerText: qsTr("Password")
textField.placeholderText: "password" textField.placeholderText: "password"
@@ -266,11 +299,12 @@ PageType {
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 24 Layout.topMargin: 24
Layout.bottomMargin: 24 Layout.bottomMargin: 24
Layout.rightMargin: 16
text: qsTr("Change connection settings") text: qsTr("Change connection settings")
clickedFunc: function() { clickedFunc: function() {
forceActiveFocus()
if (!portTextField.textField.acceptableInput) { if (!portTextField.textField.acceptableInput) {
portTextField.errorText = qsTr("The port must be in the range of 1 to 65535") portTextField.errorText = qsTr("The port must be in the range of 1 to 65535")
return return
@@ -306,9 +340,12 @@ PageType {
text: qsTr("Change connection settings") text: qsTr("Change connection settings")
clickedFunc: function() { clickedFunc: function() {
forceActiveFocus()
changeSettingsDrawer.openTriggered() 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
+112 -104
View File
@@ -14,58 +14,145 @@ 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: news
Layout.fillWidth: true
text: qsTr("News & Notifications")
rightImageSource: "qrc:/images/controls/chevron-right.svg"
leftImageSource: NewsModel.hasUnread ? "qrc:/images/controls/news-unread.svg" : "qrc:/images/controls/news.svg"
clickedFunction: function() {
PageController.goToPage(PageEnum.PageSettingsNewsNotifications)
}
}
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 +164,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,14 +146,24 @@ PageType {
} }
} }
ListViewType { FlickableType {
id: listView
anchors.top: header.bottom anchors.top: header.bottom
anchors.bottom: addAppButton.top anchors.topMargin: 16
contentHeight: col.implicitHeight + addAppButton.implicitHeight + addAppButton.anchors.bottomMargin + addAppButton.anchors.topMargin
enabled: root.pageEnabled
Column {
id: col
anchors.top: parent.top
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
ListView {
id: apps
width: parent.width
height: apps.contentItem.height
model: SortFilterProxyModel { model: SortFilterProxyModel {
id: proxyAppSplitTunnelingModel id: proxyAppSplitTunnelingModel
sourceModel: AppSplitTunnelingModel sourceModel: AppSplitTunnelingModel
@@ -168,15 +177,23 @@ PageType {
] ]
} }
delegate: ColumnLayout { clip: true
width: listView.width 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 { LabelWithButtonType {
Layout.fillWidth: true Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 16
text: appPath text: appPath
rightImageSource: "qrc:/images/controls/trash.svg" rightImageSource: "qrc:/images/controls/trash.svg"
rightImageColor: AmneziaStyle.color.paleGray rightImageColor: AmneziaStyle.color.paleGray
@@ -199,6 +216,9 @@ PageType {
DividerType {} DividerType {}
} }
} }
}
}
}
Rectangle { Rectangle {
anchors.fill: addAppButton anchors.fill: addAppButton
@@ -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
contentHeight: content.height
ColumnLayout {
id: content
anchors.top: parent.top
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
header: ColumnLayout { spacing: 0
width: listView.width
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.")
+15 -33
View File
@@ -41,51 +41,35 @@ 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
contentHeight: content.height
ColumnLayout {
id: content
anchors.top: parent.top
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
anchors.leftMargin: 16
header: ColumnLayout { anchors.rightMargin: 16
width: listView.width
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.")
@@ -95,14 +79,13 @@ PageType {
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,26 +21,21 @@ 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
contentHeight: content.height
ColumnLayout {
id: content
anchors.top: parent.top
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
header: ColumnLayout {
width: listView.width
BaseHeaderType { BaseHeaderType {
Layout.fillWidth: true Layout.fillWidth: true
Layout.leftMargin: 16 Layout.leftMargin: 16
@@ -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 {
+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 {
@@ -0,0 +1,68 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import PageEnum 1.0
import Style 1.0
import "./"
import "../Controls2"
import "../Controls2/TextTypes"
import "../Config"
import SortFilterProxyModel 0.2
PageType {
id: root
property var newsItem
SortFilterProxyModel {
id: proxyNews
sourceModel: NewsModel
filters: [ ValueFilter { roleName: "isProcessed"; value: true } ]
Component.onCompleted: root.newsItem = proxyNews.get(0)
}
Connections {
target: NewsModel
function onProcessedIndexChanged() {
root.newsItem = proxyNews.get(0)
}
}
BackButtonType {
id: backButton
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
anchors.topMargin: 20
}
FlickableType {
id: fl
anchors.top: backButton.bottom
anchors.bottom: parent.bottom
contentHeight: content.height
ColumnLayout {
id: content
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
spacing: 0
BaseHeaderType {
Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 16
headerText: newsItem.title
}
ParagraphTextType {
Layout.fillWidth: true
Layout.topMargin: 16
Layout.leftMargin: 16
Layout.rightMargin: 16
text: newsItem.content
}
}
}
}
@@ -0,0 +1,81 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import PageEnum 1.0
import Style 1.0
import "./"
import "../Controls2"
import "../Controls2/TextTypes"
import "../Config"
PageType {
id: root
ColumnLayout {
id: header
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
anchors.topMargin: 20
BackButtonType {
id: backButton
}
BaseHeaderType {
Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 16
headerText: qsTr("News & Notifications")
}
}
ListView {
id: newsList
width: parent.width
anchors.top: header.bottom
anchors.topMargin: 16
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: parent.bottom
property bool isFocusable: true
model: NewsModel
clip: true
reuseItems: true
delegate: Item {
implicitWidth: newsList.width
implicitHeight: content.implicitHeight
ColumnLayout {
id: content
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
LabelWithButtonType {
Layout.fillWidth: true
leftImageSource: read ? "" : "qrc:/images/controls/unread-dot.svg"
isSmallLeftImage: !read
text: title
rightImageSource: "qrc:/images/controls/chevron-right.svg"
clickedFunction: function() {
NewsModel.markAsRead(index)
NewsModel.processedIndex = index
PageController.goToPage(PageEnum.PageSettingsNewsDetail)
}
}
DividerType {}
}
}
}
}
+107 -79
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,71 +63,53 @@ 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()
}
}
DividerType {
visible: isVisible
}
}
}
property list<QtObject> serverActions: [
check,
reboot,
remove,
clear,
reset,
switch_to_premium,
]
QtObject {
id: check
property bool isVisible: root.isServerWithWriteAccess
readonly property string title: qsTr("Check the server for previously installed Amnezia services")
readonly property string description: qsTr("Add them to the application if they were not displayed")
readonly property var tColor: AmneziaStyle.color.paleGray
readonly property var clickedHandler: function() {
PageController.showBusyIndicator(true) PageController.showBusyIndicator(true)
InstallController.scanServerForInstalledContainers() InstallController.scanServerForInstalledContainers()
PageController.showBusyIndicator(false) PageController.showBusyIndicator(false)
} }
} }
QtObject { DividerType {
id: reboot visible: content.isServerWithWriteAccess
}
property bool isVisible: root.isServerWithWriteAccess LabelWithButtonType {
readonly property string title: qsTr("Reboot server") id: labelWithButton2
readonly property string description: "" visible: content.isServerWithWriteAccess
readonly property var tColor: AmneziaStyle.color.vibrantRed Layout.fillWidth: true
readonly property var clickedHandler: function() {
text: qsTr("Reboot server")
textColor: AmneziaStyle.color.vibrantRed
clickedFunction: function() {
var headerText = qsTr("Do you want to reboot the server?") 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 descriptionText = qsTr("The reboot process may take approximately 30 seconds. Are you sure you wish to proceed?")
var yesButtonText = qsTr("Continue") var yesButtonText = qsTr("Continue")
@@ -139,23 +123,32 @@ PageType {
InstallController.rebootProcessedServer() InstallController.rebootProcessedServer()
PageController.showBusyIndicator(false) PageController.showBusyIndicator(false)
} }
if (!GC.isMobile()) {
labelWithButton5.forceActiveFocus()
}
} }
var noButtonFunction = function() { var noButtonFunction = function() {
if (!GC.isMobile()) {
labelWithButton2.forceActiveFocus()
}
} }
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction) showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
} }
} }
QtObject { DividerType {
id: remove visible: content.isServerWithWriteAccess
}
property bool isVisible: true LabelWithButtonType {
readonly property string title: qsTr("Remove server from application") id: labelWithButton3
readonly property string description: "" Layout.fillWidth: true
readonly property var tColor: AmneziaStyle.color.vibrantRed
readonly property var clickedHandler: function() { text: qsTr("Remove server from application")
textColor: AmneziaStyle.color.vibrantRed
clickedFunction: function() {
var headerText = qsTr("Do you want to remove the server from application?") 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 descriptionText = qsTr("All installed AmneziaVPN services will still remain on the server.")
var yesButtonText = qsTr("Continue") var yesButtonText = qsTr("Continue")
@@ -169,23 +162,31 @@ PageType {
InstallController.removeProcessedServer() InstallController.removeProcessedServer()
PageController.showBusyIndicator(false) PageController.showBusyIndicator(false)
} }
if (!GC.isMobile()) {
labelWithButton5.forceActiveFocus()
}
} }
var noButtonFunction = function() { var noButtonFunction = function() {
if (!GC.isMobile()) {
labelWithButton3.forceActiveFocus()
}
} }
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction) showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
} }
} }
QtObject { DividerType {}
id: clear
property bool isVisible: root.isServerWithWriteAccess LabelWithButtonType {
readonly property string title: qsTr("Clear server from Amnezia software") id: labelWithButton4
readonly property string description: "" visible: content.isServerWithWriteAccess
readonly property var tColor: AmneziaStyle.color.vibrantRed Layout.fillWidth: true
readonly property var clickedHandler: function() {
text: qsTr("Clear server from Amnezia software")
textColor: AmneziaStyle.color.vibrantRed
clickedFunction: function() {
var headerText = qsTr("Do you want to clear server from Amnezia software?") 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 descriptionText = qsTr("All users whom you shared a connection with will no longer be able to connect to it.")
var yesButtonText = qsTr("Continue") var yesButtonText = qsTr("Continue")
@@ -198,23 +199,33 @@ PageType {
PageController.goToPage(PageEnum.PageDeinstalling) PageController.goToPage(PageEnum.PageDeinstalling)
InstallController.removeAllContainers() InstallController.removeAllContainers()
} }
if (!GC.isMobile()) {
labelWithButton5.forceActiveFocus()
}
} }
var noButtonFunction = function() { var noButtonFunction = function() {
if (!GC.isMobile()) {
labelWithButton4.forceActiveFocus()
}
} }
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction) showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
} }
} }
QtObject { DividerType {
id: reset visible: content.isServerWithWriteAccess
}
property bool isVisible: ServersModel.getProcessedServerData("isServerFromTelegramApi") LabelWithButtonType {
readonly property string title: qsTr("Reset API config") id: labelWithButton5
readonly property string description: "" visible: ServersModel.getProcessedServerData("isServerFromTelegramApi")
readonly property var tColor: AmneziaStyle.color.vibrantRed Layout.fillWidth: true
readonly property var clickedHandler: function() {
text: qsTr("Reset API config")
textColor: AmneziaStyle.color.vibrantRed
clickedFunction: function() {
var headerText = qsTr("Do you want to reset API config?") var headerText = qsTr("Do you want to reset API config?")
var descriptionText = "" var descriptionText = ""
var yesButtonText = qsTr("Continue") var yesButtonText = qsTr("Continue")
@@ -228,25 +239,42 @@ PageType {
InstallController.removeApiConfig(ServersModel.processedIndex) InstallController.removeApiConfig(ServersModel.processedIndex)
PageController.showBusyIndicator(false) PageController.showBusyIndicator(false)
} }
if (!GC.isMobile()) {
labelWithButton5.forceActiveFocus()
}
} }
var noButtonFunction = function() { var noButtonFunction = function() {
if (!GC.isMobile()) {
labelWithButton5.forceActiveFocus()
}
} }
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction) showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
} }
} }
QtObject { DividerType {
id: switch_to_premium visible: ServersModel.getProcessedServerData("isServerFromTelegramApi")
}
property bool isVisible: ServersModel.getProcessedServerData("isServerFromTelegramApi") && ServersModel.processedServerIsPremium LabelWithButtonType {
readonly property string title: qsTr("Switch to the new Amnezia Premium subscription") id: labelWithButton6
readonly property string description: "" visible: ServersModel.getProcessedServerData("isServerFromTelegramApi") && ServersModel.processedServerIsPremium
readonly property var tColor: AmneziaStyle.color.vibrantRed Layout.fillWidth: true
readonly property var clickedHandler: function() {
text: qsTr("Switch to the new Amnezia Premium subscription")
textColor: AmneziaStyle.color.vibrantRed
clickedFunction: function() {
PageController.goToPageHome() PageController.goToPageHome()
ApiPremV1MigrationController.showMigrationDrawer() ApiPremV1MigrationController.showMigrationDrawer()
} }
} }
DividerType {
visible: ServersModel.getProcessedServerData("isServerFromTelegramApi") && ServersModel.processedServerIsPremium
}
}
}
} }
@@ -21,31 +21,18 @@ 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 { BaseHeaderType {
Layout.fillWidth: true Layout.fillWidth: true
@@ -55,16 +42,52 @@ PageType {
headerText: ContainersModel.getProcessedContainerName() + qsTr(" settings") headerText: ContainersModel.getProcessedContainerName() + qsTr(" settings")
} }
ListView {
id: protocols
Layout.fillWidth: true
height: protocols.contentItem.height
clip: true
interactive: true
property bool isFocusable: true
Keys.onTabPressed: {
FocusController.nextKeyTabItem()
}
Keys.onBacktabPressed: {
FocusController.previousKeyTabItem()
}
Keys.onUpPressed: {
FocusController.nextKeyUpItem()
}
Keys.onDownPressed: {
FocusController.nextKeyDownItem()
}
Keys.onLeftPressed: {
FocusController.nextKeyLeftItem()
}
Keys.onRightPressed: {
FocusController.nextKeyRightItem()
} }
model: ProtocolsModel model: ProtocolsModel
delegate: ColumnLayout { delegate: Item {
implicitWidth: protocols.width
implicitHeight: delegateContent.implicitHeight
ColumnLayout {
id: delegateContent id: delegateContent
width: listView.width anchors.fill: parent
property bool isClientSettingsVisible: (protocolIndex === ProtocolEnum.WireGuard) || (protocolIndex === ProtocolEnum.Awg) property bool isClientSettingsVisible: protocolIndex === ProtocolEnum.WireGuard || protocolIndex === ProtocolEnum.Awg
property bool isServerSettingsVisible: ServersModel.isProcessedServerHasWriteAccess() property bool isServerSettingsVisible: ServersModel.isProcessedServerHasWriteAccess()
LabelWithButtonType { LabelWithButtonType {
@@ -134,10 +157,10 @@ PageType {
visible: delegateContent.isServerSettingsVisible visible: delegateContent.isServerSettingsVisible
} }
} }
}
footer: ColumnLayout { footer: ColumnLayout {
width: header.width
width: listView.width
LabelWithButtonType { LabelWithButtonType {
id: clearCacheButton id: clearCacheButton
@@ -165,8 +188,10 @@ PageType {
InstallController.clearCachedProfile() InstallController.clearCachedProfile()
PageController.showBusyIndicator(false) PageController.showBusyIndicator(false)
} }
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)
@@ -180,6 +205,10 @@ PageType {
} }
DividerType { DividerType {
Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 16
visible: root.isClearCacheVisible visible: root.isClearCacheVisible
} }
@@ -210,7 +239,9 @@ PageType {
} }
} }
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)
@@ -224,8 +255,15 @@ PageType {
} }
DividerType { DividerType {
Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 16
visible: ServersModel.isProcessedServerHasWriteAccess() 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,58 +397,34 @@ 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 {}
}
}
}
}
property list<QtObject> importOptions: [ LabelWithButtonType {
replaceOption, id: importSitesButton3
addOption, Layout.fillWidth: true
] text: qsTr("Add imported sites to existing ones")
QtObject { clickedFunction: function() {
id: replaceOption
readonly property string title: qsTr("Replace site list")
readonly property var clickedHandler: function() {
var fileName = SystemController.getFileName(qsTr("Open sites file"), var fileName = SystemController.getFileName(qsTr("Open sites file"),
qsTr("Sites files (*.json)")) qsTr("Sites files (*.json)"))
if (fileName !== "") { if (fileName !== "") {
root.importSites(fileName, true) importSitesDrawerContent.importSites(fileName, false)
}
}
}
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)
} }
} }
} }
@@ -481,4 +436,10 @@ PageType {
importSitesDrawer.closeTriggered() importSitesDrawer.closeTriggered()
moreActionsDrawer.closeTriggered() moreActionsDrawer.closeTriggered()
} }
DividerType {}
}
}
}
}
} }
@@ -15,31 +15,25 @@ import "../Components"
PageType { PageType {
id: root id: root
BackButtonType { FlickableType {
id: backButton id: fl
anchors.top: parent.top
anchors.bottom: parent.bottom
contentHeight: content.height + continueButton.implicitHeight + continueButton.anchors.bottomMargin + continueButton.anchors.topMargin
ColumnLayout {
id: content
anchors.top: parent.top anchors.top: parent.top
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
anchors.topMargin: 20
onFocusChanged: { spacing: 0
if (this.activeFocus) {
listView.positionViewAtBeginning() BackButtonType {
id: backButton
Layout.topMargin: 20
} }
}
}
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 { 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,15 +113,20 @@ PageType {
cursorShape: parent.hoveredLink ? Qt.PointingHandCursor : Qt.ArrowCursor cursorShape: parent.hoveredLink ? Qt.PointingHandCursor : Qt.ArrowCursor
} }
} }
}
}
BasicButtonType { BasicButtonType {
id: continueButton id: continueButton
Layout.fillWidth: true anchors.right: parent.right
Layout.topMargin: 32 anchors.left: parent.left
Layout.bottomMargin: 32 anchors.bottom: parent.bottom
Layout.leftMargin: 16
Layout.rightMargin: 16 anchors.topMargin: 32
anchors.rightMargin: 16
anchors.leftMargin: 16
anchors.bottomMargin: 32
text: qsTr("Connect") text: qsTr("Connect")
@@ -120,58 +144,3 @@ PageType {
} }
} }
} }
}
property list<QtObject> inputFields: [
region,
price,
timeLimit,
speed,
features
]
QtObject {
id: region
readonly property string imagePath: "qrc:/images/controls/map-pin.svg"
readonly property string lText: qsTr("For the region")
readonly property string rText: ApiServicesModel.getSelectedServiceData("region")
property bool isVisible: true
}
QtObject {
id: price
readonly property string imagePath: "qrc:/images/controls/tag.svg"
readonly property string lText: qsTr("Price")
readonly property string rText: ApiServicesModel.getSelectedServiceData("price")
property bool isVisible: true
}
QtObject {
id: timeLimit
readonly property string imagePath: "qrc:/images/controls/history.svg"
readonly property string lText: qsTr("Work period")
readonly property string rText: ApiServicesModel.getSelectedServiceData("timeLimit")
property bool isVisible: rText !== ""
}
QtObject {
id: speed
readonly property string imagePath: "qrc:/images/controls/gauge.svg"
readonly property string lText: qsTr("Speed")
readonly property string rText: ApiServicesModel.getSelectedServiceData("speed")
property bool isVisible: true
}
QtObject {
id: features
readonly property string imagePath: "qrc:/images/controls/info.svg"
readonly property string lText: qsTr("Features")
readonly property string rText: ""
property bool isVisible: true
}
}
@@ -14,54 +14,62 @@ 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
spacing: 0
BackButtonType {
id: backButton
Layout.topMargin: 20 Layout.topMargin: 20
onActiveFocusChanged: {
if(backButton.enabled && backButton.activeFocus) {
listView.positionViewAtBeginning()
} }
}
}
ListViewType {
id: listView
anchors.top: backButton.bottom
anchors.right: parent.right
anchors.left: parent.left
anchors.bottom: parent.bottom
anchors.topMargin: 16
header: ColumnLayout {
width: listView.width
BaseHeaderType { BaseHeaderType {
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 8
Layout.rightMargin: 16 Layout.rightMargin: 16
Layout.leftMargin: 16 Layout.leftMargin: 16
Layout.bottomMargin: 24 Layout.bottomMargin: 16
headerText: qsTr("VPN by Amnezia") headerText: qsTr("VPN by Amnezia")
descriptionText: qsTr("Choose a VPN service that suits your needs.") descriptionText: qsTr("Choose a VPN service that suits your needs.")
} }
} }
ListView {
id: servicesListView
anchors.top: header.bottom
anchors.right: parent.right
anchors.left: parent.left
anchors.bottom: parent.bottom
anchors.topMargin: 16
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
ColumnLayout {
id: delegateContent
anchors.fill: parent
CardWithIconsType { CardWithIconsType {
id: card id: card
@@ -89,3 +97,4 @@ PageType {
} }
} }
} }
}
@@ -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()
} }
} }
} }
+68 -76
View File
@@ -20,7 +20,6 @@ PageType {
SortFilterProxyModel { SortFilterProxyModel {
id: proxyContainersModel id: proxyContainersModel
sourceModel: ContainersModel sourceModel: ContainersModel
filters: [ filters: [
ValueFilter { ValueFilter {
@@ -41,64 +40,70 @@ 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
contentHeight: content.implicitHeight + setupLaterButton.anchors.bottomMargin
Column {
id: content
anchors.top: parent.top
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
anchors.rightMargin: 16
spacing: 16 anchors.leftMargin: 16
header: ColumnLayout {
width: listView.width
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")
} }
ButtonGroup {
id: buttonGroup
} }
model: proxyContainersModel ListView {
currentIndex: 0 id: containers
width: parent.width
height: containers.contentItem.height
spacing: 16
delegate: ColumnLayout { currentIndex: 0
width: listView.width clip: true
interactive: false
model: proxyContainersModel
property int dockerContainer
property int containerDefaultPort
property int containerDefaultTransportProto
property bool isFocusable: true
delegate: Item {
implicitWidth: containers.width
implicitHeight: delegateContent.implicitHeight
ColumnLayout {
id: delegateContent
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
CardType { CardType {
id: card id: card
Layout.fillWidth: true Layout.fillWidth: true
Layout.rightMargin: 16
Layout.leftMargin: 16
Layout.bottomMargin: 16
headerText: easySetupHeader headerText: easySetupHeader
bodyText: easySetupDescription bodyText: easySetupDescription
@@ -109,30 +114,30 @@ PageType {
isEasySetup = true isEasySetup = true
var defaultContainerProto = ContainerProps.defaultProtocol(dockerContainer) var defaultContainerProto = ContainerProps.defaultProtocol(dockerContainer)
listView.dockerContainer = dockerContainer containers.dockerContainer = dockerContainer
listView.containerDefaultPort = ProtocolProps.getPortForInstall(defaultContainerProto) containers.containerDefaultPort = ProtocolProps.getPortForInstall(defaultContainerProto)
listView.containerDefaultTransportProto = ProtocolProps.defaultTransportProto(defaultContainerProto) containers.containerDefaultTransportProto = ProtocolProps.defaultTransportProto(defaultContainerProto)
}
} }
Keys.onReturnPressed: this.clicked()
Keys.onEnterPressed: this.clicked()
} }
} }
footer: ColumnLayout { Component.onCompleted: {
width: listView.width var item = containers.itemAtIndex(containers.currentIndex)
spacing: 16 if (item !== null) {
var button = item.children[0].children[0]
button.checked = true
button.clicked()
}
}
}
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,23 +85,42 @@ PageType {
] ]
} }
ListViewType { FlickableType {
id: listView
anchors.fill: parent anchors.fill: parent
contentHeight: content.height
Column {
id: content
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
spacing: 16
ListView {
id: container
width: parent.width
height: container.contentItem.height
currentIndex: -1 currentIndex: -1
clip: true
interactive: false
model: proxyContainersModel model: proxyContainersModel
delegate: ColumnLayout { delegate: Item {
width: listView.width implicitWidth: container.width
implicitHeight: delegateContent.implicitHeight
ColumnLayout {
id: delegateContent
anchors.fill: parent
anchors.rightMargin: 16
anchors.leftMargin: 16
BaseHeaderType { BaseHeaderType {
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 20 Layout.topMargin: 20
Layout.leftMargin: 16
Layout.rightMargin: 16
headerText: qsTr("Installing") headerText: qsTr("Installing")
descriptionText: name descriptionText: name
@@ -112,8 +131,6 @@ PageType {
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 32 Layout.topMargin: 32
Layout.leftMargin: 16
Layout.rightMargin: 16
Timer { Timer {
id: timer id: timer
@@ -132,8 +149,6 @@ PageType {
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 8 Layout.topMargin: 8
Layout.leftMargin: 16
Layout.rightMargin: 16
text: root.progressBarText text: root.progressBarText
} }
@@ -143,8 +158,6 @@ PageType {
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 24 Layout.topMargin: 24
Layout.leftMargin: 16
Layout.rightMargin: 16
visible: root.isCancelButtonVisible visible: root.isCancelButtonVisible
@@ -158,3 +171,6 @@ PageType {
} }
} }
} }
}
}
}
@@ -29,42 +29,77 @@ PageType {
] ]
} }
BackButtonType { FlickableType {
id: backButton anchors.fill: parent
contentHeight: content.height
Column {
id: content
anchors.top: parent.top anchors.top: parent.top
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
anchors.topMargin: 20
onFocusChanged: {
if (this.activeFocus) {
listView.positionViewAtBeginning()
}
}
}
ListViewType {
id: listView
anchors.top: backButton.bottom
anchors.bottom: parent.bottom
anchors.right: parent.right
anchors.left: parent.left
ListView {
id: processedContainerListView
width: parent.width
height: contentItem.height
currentIndex: -1 currentIndex: -1
clip: true
interactive: false
model: proxyContainersModel model: proxyContainersModel
delegate: ColumnLayout { property bool isFocusable: true
width: listView.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()
}
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
}
BaseHeaderType { BaseHeaderType {
id: header id: header
Layout.fillWidth: true Layout.fillWidth: true
Layout.rightMargin: 16
Layout.leftMargin: 16
headerText: qsTr("Installing %1").arg(name) headerText: qsTr("Installing %1").arg(name)
descriptionText: description descriptionText: description
@@ -74,8 +109,7 @@ PageType {
id: showDetailsButton id: showDetailsButton
Layout.topMargin: 16 Layout.topMargin: 16
Layout.rightMargin: 16 Layout.leftMargin: -8
Layout.leftMargin: 16
implicitHeight: 32 implicitHeight: 32
@@ -86,6 +120,7 @@ PageType {
textColor: AmneziaStyle.color.goldenApricot textColor: AmneziaStyle.color.goldenApricot
text: qsTr("More detailed") text: qsTr("More detailed")
KeyNavigation.tab: transportProtoSelector
clickedFunc: function() { clickedFunc: function() {
showDetailsDrawer.openTriggered() showDetailsDrawer.openTriggered()
@@ -114,40 +149,39 @@ PageType {
} }
} }
ListViewType { FlickableType {
id: showDetailsListView id: fl
anchors.top: showDetailsBackButton.bottom anchors.top: showDetailsBackButton.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
contentHeight: {
var emptySpaceHeight = parent.height - showDetailsBackButton.implicitHeight - showDetailsBackButton.anchors.topMargin
return (showDetailsDrawerContent.height > emptySpaceHeight) ?
showDetailsDrawerContent.height : emptySpaceHeight
}
header: ColumnLayout { ColumnLayout {
width: showDetailsListView.width id: showDetailsDrawerContent
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
anchors.rightMargin: 16
anchors.leftMargin: 16
Header2Type { Header2Type {
id: showDetailsDrawerHeader id: showDetailsDrawerHeader
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 16 Layout.topMargin: 16
Layout.rightMargin: 16
Layout.leftMargin: 16
headerText: name headerText: name
} }
}
model: 1 // fake model to force the ListView to be created without a model
delegate: ColumnLayout {
width: showDetailsListView.width
ParagraphTextType { ParagraphTextType {
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 16 Layout.topMargin: 16
Layout.bottomMargin: 16 Layout.bottomMargin: 16
Layout.leftMargin: 16
Layout.rightMargin: 16
text: detailedDescription text: detailedDescription
textFormat: Text.MarkdownText textFormat: Text.MarkdownText
@@ -155,22 +189,14 @@ PageType {
Rectangle { Rectangle {
Layout.fillHeight: true Layout.fillHeight: true
Layout.leftMargin: 16
Layout.rightMargin: 16
color: AmneziaStyle.color.transparent color: AmneziaStyle.color.transparent
} }
}
footer: ColumnLayout {
width: showDetailsListView.width
BasicButtonType { BasicButtonType {
id: showDetailsCloseButton id: showDetailsCloseButton
Layout.fillWidth: true Layout.fillWidth: true
Layout.bottomMargin: 32 Layout.bottomMargin: 32
Layout.leftMargin: 16 parentFlickable: fl
Layout.rightMargin: 16
text: qsTr("Close") text: qsTr("Close")
@@ -187,8 +213,6 @@ PageType {
id: transportProtoHeader id: transportProtoHeader
Layout.topMargin: 16 Layout.topMargin: 16
Layout.rightMargin: 16
Layout.leftMargin: 16
text: qsTr("Network protocol") text: qsTr("Network protocol")
} }
@@ -197,9 +221,6 @@ PageType {
id: transportProtoSelector id: transportProtoSelector
Layout.fillWidth: true Layout.fillWidth: true
Layout.rightMargin: 16
Layout.leftMargin: 16
rootWidth: root.width rootWidth: root.width
} }
@@ -208,8 +229,6 @@ PageType {
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 16 Layout.topMargin: 16
Layout.rightMargin: 16
Layout.leftMargin: 16
headerText: qsTr("Port") headerText: qsTr("Port")
textField.maximumLength: 5 textField.maximumLength: 5
@@ -218,9 +237,6 @@ PageType {
Rectangle { Rectangle {
Layout.fillHeight: true Layout.fillHeight: true
Layout.rightMargin: 16
Layout.leftMargin: 16
color: AmneziaStyle.color.transparent color: AmneziaStyle.color.transparent
} }
@@ -229,8 +245,6 @@ PageType {
Layout.fillWidth: true Layout.fillWidth: true
Layout.bottomMargin: 32 Layout.bottomMargin: 32
Layout.rightMargin: 16
Layout.leftMargin: 16
text: qsTr("Install") text: qsTr("Install")
@@ -265,3 +279,6 @@ PageType {
} }
} }
} }
}
}
}
@@ -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 {
@@ -89,8 +88,8 @@ PageType {
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)
} }
} }

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