From b183a3b232c2c5161b5d838ce51230b42f256c74 Mon Sep 17 00:00:00 2001 From: "vladimir.kuznetsov" Date: Thu, 6 Feb 2025 15:26:47 +0700 Subject: [PATCH] feature: added pages for subscription settings feature --- client/CMakeLists.txt | 176 +--------------- client/amnezia_application.cpp | 6 + client/amnezia_application.h | 7 +- client/cmake/sources.cmake | 189 ++++++++++++++++++ client/core/api/apiUtils.cpp | 10 + client/core/api/apiUtils.h | 11 + client/core/controllers/apiController.cpp | 44 +++- client/core/controllers/apiController.h | 7 +- client/resources.qrc | 3 + .../controllers/api/apiSettingsController.cpp | 57 ++++++ .../controllers/api/apiSettingsController.h | 27 +++ .../ui/controllers/api/importController.cpp | 2 + client/ui/controllers/api/importController.h | 24 +++ client/ui/controllers/installController.cpp | 1 + client/ui/controllers/pageController.h | 6 +- client/ui/models/api/apiAccountInfoModel.cpp | 105 ++++++++++ client/ui/models/api/apiAccountInfoModel.h | 55 +++++ client/ui/qml/Components/ServersListView.qml | 3 +- client/ui/qml/Controls2/ListViewType.qml | 38 ++++ client/ui/qml/Pages2/PageHome.qml | 3 +- client/ui/qml/Pages2/PageSettingsAbout.qml | 3 +- .../Pages2/PageSettingsApiInstructions.qml | 87 ++++++++ .../qml/Pages2/PageSettingsApiServerInfo.qml | 145 ++++++-------- .../ui/qml/Pages2/PageSettingsApiSupport.qml | 107 ++++++++++ .../ui/qml/Pages2/PageSettingsServerData.qml | 10 - .../ui/qml/Pages2/PageSettingsServersList.qml | 7 +- client/ui/qml/Pages2/PageStart.qml | 10 + 27 files changed, 856 insertions(+), 287 deletions(-) create mode 100644 client/cmake/sources.cmake create mode 100644 client/core/api/apiUtils.cpp create mode 100644 client/core/api/apiUtils.h create mode 100644 client/ui/controllers/api/apiSettingsController.cpp create mode 100644 client/ui/controllers/api/apiSettingsController.h create mode 100644 client/ui/controllers/api/importController.cpp create mode 100644 client/ui/controllers/api/importController.h create mode 100644 client/ui/models/api/apiAccountInfoModel.cpp create mode 100644 client/ui/models/api/apiAccountInfoModel.h create mode 100644 client/ui/qml/Controls2/ListViewType.qml create mode 100644 client/ui/qml/Pages2/PageSettingsApiInstructions.qml create mode 100644 client/ui/qml/Pages2/PageSettingsApiSupport.qml diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index 4b7540f0..294b9646 100644 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -57,8 +57,7 @@ if(WIN32 OR (APPLE AND NOT IOS) OR (LINUX AND NOT ANDROID)) endif() qt_standard_project_setup() -qt_add_executable(${PROJECT} MANUAL_FINALIZATION - core/controllers/gatewayController.h core/controllers/gatewayController.cpp) +qt_add_executable(${PROJECT} MANUAL_FINALIZATION) if(WIN32 OR (APPLE AND NOT IOS) OR (LINUX AND NOT ANDROID)) qt_add_repc_replicas(${PROJECT} ${CMAKE_CURRENT_LIST_DIR}/../ipc/ipc_interface.rep) @@ -111,8 +110,8 @@ if(IS_CI) endif() endif() - include(${CMAKE_CURRENT_LIST_DIR}/cmake/3rdparty.cmake) +include(${CMAKE_CURRENT_LIST_DIR}/cmake/sources.cmake) include_directories( ${CMAKE_CURRENT_LIST_DIR}/../ipc @@ -121,167 +120,22 @@ include_directories( ${CMAKE_CURRENT_BINARY_DIR} ) -configure_file(${CMAKE_CURRENT_LIST_DIR}/../version.h.in ${CMAKE_CURRENT_BINARY_DIR}/version.h) - -set(HEADERS ${HEADERS} - ${CMAKE_CURRENT_LIST_DIR}/migrations.h - ${CMAKE_CURRENT_LIST_DIR}/../ipc/ipc.h - ${CMAKE_CURRENT_LIST_DIR}/amnezia_application.h - ${CMAKE_CURRENT_LIST_DIR}/containers/containers_defs.h - ${CMAKE_CURRENT_LIST_DIR}/core/defs.h - ${CMAKE_CURRENT_LIST_DIR}/core/errorstrings.h - ${CMAKE_CURRENT_LIST_DIR}/core/scripts_registry.h - ${CMAKE_CURRENT_LIST_DIR}/core/server_defs.h - ${CMAKE_CURRENT_LIST_DIR}/core/controllers/apiController.h - ${CMAKE_CURRENT_LIST_DIR}/core/controllers/serverController.h - ${CMAKE_CURRENT_LIST_DIR}/core/controllers/vpnConfigurationController.h - ${CMAKE_CURRENT_LIST_DIR}/protocols/protocols_defs.h - ${CMAKE_CURRENT_LIST_DIR}/protocols/qml_register_protocols.h - ${CMAKE_CURRENT_LIST_DIR}/ui/pages.h - ${CMAKE_CURRENT_LIST_DIR}/ui/qautostart.h - ${CMAKE_CURRENT_LIST_DIR}/protocols/vpnprotocol.h - ${CMAKE_CURRENT_BINARY_DIR}/version.h - ${CMAKE_CURRENT_LIST_DIR}/core/sshclient.h - ${CMAKE_CURRENT_LIST_DIR}/core/networkUtilities.h - ${CMAKE_CURRENT_LIST_DIR}/core/serialization/serialization.h - ${CMAKE_CURRENT_LIST_DIR}/core/serialization/transfer.h - ${CMAKE_CURRENT_LIST_DIR}/core/enums/apiEnums.h - ${CMAKE_CURRENT_LIST_DIR}/../common/logger/logger.h - ${CMAKE_CURRENT_LIST_DIR}/utils/qmlUtils.h -) - -# Mozilla headres -set(HEADERS ${HEADERS} - ${CMAKE_CURRENT_LIST_DIR}/mozilla/models/server.h - ${CMAKE_CURRENT_LIST_DIR}/mozilla/shared/ipaddress.h - ${CMAKE_CURRENT_LIST_DIR}/mozilla/shared/leakdetector.h - ${CMAKE_CURRENT_LIST_DIR}/mozilla/controllerimpl.h - ${CMAKE_CURRENT_LIST_DIR}/mozilla/localsocketcontroller.h -) - include_directories(mozilla) include_directories(mozilla/shared) include_directories(mozilla/models) -if(NOT IOS) - set(HEADERS ${HEADERS} - ${CMAKE_CURRENT_LIST_DIR}/platforms/ios/QRCodeReaderBase.h - ) -endif() - -if(NOT ANDROID) - set(HEADERS ${HEADERS} - ${CMAKE_CURRENT_LIST_DIR}/ui/notificationhandler.h - ) -endif() - -set(SOURCES ${SOURCES} - ${CMAKE_CURRENT_LIST_DIR}/migrations.cpp - ${CMAKE_CURRENT_LIST_DIR}/amnezia_application.cpp - ${CMAKE_CURRENT_LIST_DIR}/containers/containers_defs.cpp - ${CMAKE_CURRENT_LIST_DIR}/core/errorstrings.cpp - ${CMAKE_CURRENT_LIST_DIR}/core/scripts_registry.cpp - ${CMAKE_CURRENT_LIST_DIR}/core/server_defs.cpp - ${CMAKE_CURRENT_LIST_DIR}/core/controllers/apiController.cpp - ${CMAKE_CURRENT_LIST_DIR}/core/controllers/serverController.cpp - ${CMAKE_CURRENT_LIST_DIR}/core/controllers/vpnConfigurationController.cpp - ${CMAKE_CURRENT_LIST_DIR}/protocols/protocols_defs.cpp - ${CMAKE_CURRENT_LIST_DIR}/ui/qautostart.cpp - ${CMAKE_CURRENT_LIST_DIR}/protocols/vpnprotocol.cpp - ${CMAKE_CURRENT_LIST_DIR}/core/sshclient.cpp - ${CMAKE_CURRENT_LIST_DIR}/core/networkUtilities.cpp - ${CMAKE_CURRENT_LIST_DIR}/core/serialization/outbound.cpp - ${CMAKE_CURRENT_LIST_DIR}/core/serialization/inbound.cpp - ${CMAKE_CURRENT_LIST_DIR}/core/serialization/ss.cpp - ${CMAKE_CURRENT_LIST_DIR}/core/serialization/ssd.cpp - ${CMAKE_CURRENT_LIST_DIR}/core/serialization/vless.cpp - ${CMAKE_CURRENT_LIST_DIR}/core/serialization/trojan.cpp - ${CMAKE_CURRENT_LIST_DIR}/core/serialization/vmess.cpp - ${CMAKE_CURRENT_LIST_DIR}/core/serialization/vmess_new.cpp - ${CMAKE_CURRENT_LIST_DIR}/../common/logger/logger.cpp - ${CMAKE_CURRENT_LIST_DIR}/utils/qmlUtils.cpp -) - -# Mozilla sources -set(SOURCES ${SOURCES} - ${CMAKE_CURRENT_LIST_DIR}/mozilla/models/server.cpp - ${CMAKE_CURRENT_LIST_DIR}/mozilla/shared/ipaddress.cpp - ${CMAKE_CURRENT_LIST_DIR}/mozilla/shared/leakdetector.cpp - ${CMAKE_CURRENT_LIST_DIR}/mozilla/localsocketcontroller.cpp -) +configure_file(${CMAKE_CURRENT_LIST_DIR}/../version.h.in ${CMAKE_CURRENT_BINARY_DIR}/version.h) if(CMAKE_BUILD_TYPE STREQUAL "Debug") target_compile_definitions(${PROJECT} PRIVATE "MZ_DEBUG") endif() -if(NOT IOS) - set(SOURCES ${SOURCES} - ${CMAKE_CURRENT_LIST_DIR}/platforms/ios/QRCodeReaderBase.cpp - ) -endif() - -if(NOT ANDROID) - set(SOURCES ${SOURCES} - ${CMAKE_CURRENT_LIST_DIR}/ui/notificationhandler.cpp - ) -endif() - -file(GLOB COMMON_FILES_H CONFIGURE_DEPENDS ${CMAKE_CURRENT_LIST_DIR}/*.h) -file(GLOB COMMON_FILES_CPP CONFIGURE_DEPENDS ${CMAKE_CURRENT_LIST_DIR}/*.cpp) - -file(GLOB_RECURSE PAGE_LOGIC_H CONFIGURE_DEPENDS ${CMAKE_CURRENT_LIST_DIR}/ui/pages_logic/*.h) -file(GLOB_RECURSE PAGE_LOGIC_CPP CONFIGURE_DEPENDS ${CMAKE_CURRENT_LIST_DIR}/ui/pages_logic/*.cpp) - -file(GLOB CONFIGURATORS_H CONFIGURE_DEPENDS ${CMAKE_CURRENT_LIST_DIR}/configurators/*.h) -file(GLOB CONFIGURATORS_CPP CONFIGURE_DEPENDS ${CMAKE_CURRENT_LIST_DIR}/configurators/*.cpp) - -file(GLOB UI_MODELS_H CONFIGURE_DEPENDS - ${CMAKE_CURRENT_LIST_DIR}/ui/models/*.h - ${CMAKE_CURRENT_LIST_DIR}/ui/models/protocols/*.h - ${CMAKE_CURRENT_LIST_DIR}/ui/models/services/*.h -) -file(GLOB UI_MODELS_CPP CONFIGURE_DEPENDS - ${CMAKE_CURRENT_LIST_DIR}/ui/models/*.cpp - ${CMAKE_CURRENT_LIST_DIR}/ui/models/protocols/*.cpp - ${CMAKE_CURRENT_LIST_DIR}/ui/models/services/*.cpp -) - -file(GLOB UI_CONTROLLERS_H CONFIGURE_DEPENDS ${CMAKE_CURRENT_LIST_DIR}/ui/controllers/*.h) -file(GLOB UI_CONTROLLERS_CPP CONFIGURE_DEPENDS ${CMAKE_CURRENT_LIST_DIR}/ui/controllers/*.cpp) - -set(HEADERS ${HEADERS} - ${COMMON_FILES_H} - ${PAGE_LOGIC_H} - ${CONFIGURATORS_H} - ${UI_MODELS_H} - ${UI_CONTROLLERS_H} -) -set(SOURCES ${SOURCES} - ${COMMON_FILES_CPP} - ${PAGE_LOGIC_CPP} - ${CONFIGURATORS_CPP} - ${UI_MODELS_CPP} - ${UI_CONTROLLERS_CPP} -) - if(WIN32) configure_file( ${CMAKE_CURRENT_LIST_DIR}/platforms/windows/amneziavpn.rc.in ${CMAKE_CURRENT_BINARY_DIR}/amneziavpn.rc ) - set(HEADERS ${HEADERS} - ${CMAKE_CURRENT_LIST_DIR}/protocols/ikev2_vpn_protocol_windows.h - ) - - set(SOURCES ${SOURCES} - ${CMAKE_CURRENT_LIST_DIR}/protocols/ikev2_vpn_protocol_windows.cpp - ) - - set(RESOURCES ${RESOURCES} - ${CMAKE_CURRENT_BINARY_DIR}/amneziavpn.rc - ) - set(LIBS ${LIBS} user32 rasapi32 @@ -325,30 +179,6 @@ endif() if(WIN32 OR (APPLE AND NOT IOS) OR (LINUX AND NOT ANDROID)) message("Client desktop build") add_compile_definitions(AMNEZIA_DESKTOP) - - set(HEADERS ${HEADERS} - ${CMAKE_CURRENT_LIST_DIR}/core/ipcclient.h - ${CMAKE_CURRENT_LIST_DIR}/core/privileged_process.h - ${CMAKE_CURRENT_LIST_DIR}/ui/systemtray_notificationhandler.h - ${CMAKE_CURRENT_LIST_DIR}/protocols/openvpnprotocol.h - ${CMAKE_CURRENT_LIST_DIR}/protocols/openvpnovercloakprotocol.h - ${CMAKE_CURRENT_LIST_DIR}/protocols/shadowsocksvpnprotocol.h - ${CMAKE_CURRENT_LIST_DIR}/protocols/wireguardprotocol.h - ${CMAKE_CURRENT_LIST_DIR}/protocols/xrayprotocol.h - ${CMAKE_CURRENT_LIST_DIR}/protocols/awgprotocol.h - ) - - set(SOURCES ${SOURCES} - ${CMAKE_CURRENT_LIST_DIR}/core/ipcclient.cpp - ${CMAKE_CURRENT_LIST_DIR}/core/privileged_process.cpp - ${CMAKE_CURRENT_LIST_DIR}/ui/systemtray_notificationhandler.cpp - ${CMAKE_CURRENT_LIST_DIR}/protocols/openvpnprotocol.cpp - ${CMAKE_CURRENT_LIST_DIR}/protocols/openvpnovercloakprotocol.cpp - ${CMAKE_CURRENT_LIST_DIR}/protocols/shadowsocksvpnprotocol.cpp - ${CMAKE_CURRENT_LIST_DIR}/protocols/wireguardprotocol.cpp - ${CMAKE_CURRENT_LIST_DIR}/protocols/xrayprotocol.cpp - ${CMAKE_CURRENT_LIST_DIR}/protocols/awgprotocol.cpp - ) endif() if(ANDROID) diff --git a/client/amnezia_application.cpp b/client/amnezia_application.cpp index aeed439b..2bd0b991 100644 --- a/client/amnezia_application.cpp +++ b/client/amnezia_application.cpp @@ -378,6 +378,9 @@ void AmneziaApplication::initModels() }); connect(m_serversModel.get(), &ServersModel::updateApiServicesModel, this, [this]() { m_apiServicesModel->updateModel(m_serversModel->getProcessedServerData("apiConfig").toJsonObject()); }); + + m_apiAccountInfoModel.reset(new ApiAccountInfoModel(this)); + m_engine->rootContext()->setContextProperty("ApiAccountInfoModel", m_apiAccountInfoModel.get()); } void AmneziaApplication::initControllers() @@ -463,4 +466,7 @@ void AmneziaApplication::initControllers() m_systemController.reset(new SystemController(m_settings)); m_engine->rootContext()->setContextProperty("SystemController", m_systemController.get()); + + m_apiSettingsController.reset(new ApiSettingsController(m_serversModel, m_apiAccountInfoModel, m_settings)); + m_engine->rootContext()->setContextProperty("ApiSettingsController", m_apiSettingsController.get()); } diff --git a/client/amnezia_application.h b/client/amnezia_application.h index cfeac0d1..dcadb77b 100644 --- a/client/amnezia_application.h +++ b/client/amnezia_application.h @@ -25,6 +25,8 @@ #include "ui/controllers/sitesController.h" #include "ui/controllers/systemController.h" #include "ui/controllers/appSplitTunnelingController.h" +// #include "ui/controllers/api/importController.h" +#include "ui/controllers/api/apiSettingsController.h" #include "ui/models/containers_model.h" #include "ui/models/languageModel.h" #include "ui/models/protocols/cloakConfigModel.h" @@ -48,6 +50,7 @@ #include "ui/models/appSplitTunnelingModel.h" #include "ui/models/apiServicesModel.h" #include "ui/models/apiCountryModel.h" +#include "ui/models/api/apiAccountInfoModel.h" #define amnApp (static_cast(QCoreApplication::instance())) @@ -104,6 +107,7 @@ private: QSharedPointer m_clientManagementModel; QSharedPointer m_apiServicesModel; QSharedPointer m_apiCountryModel; + QSharedPointer m_apiAccountInfoModel; QScopedPointer m_openVpnConfigModel; QScopedPointer m_shadowSocksConfigModel; @@ -114,7 +118,6 @@ private: #ifdef Q_OS_WINDOWS QScopedPointer m_ikev2ConfigModel; #endif - QScopedPointer m_sftpConfigModel; QScopedPointer m_socks5ConfigModel; @@ -135,6 +138,8 @@ private: QScopedPointer m_systemController; QScopedPointer m_appSplitTunnelingController; + QScopedPointer m_apiSettingsController; + QNetworkAccessManager *m_nam; QMetaObject::Connection m_reloadConfigErrorOccurredConnection; diff --git a/client/cmake/sources.cmake b/client/cmake/sources.cmake new file mode 100644 index 00000000..ace83685 --- /dev/null +++ b/client/cmake/sources.cmake @@ -0,0 +1,189 @@ +set(CLIENT_ROOT_DIR ${CMAKE_CURRENT_LIST_DIR}/..) + +set(HEADERS ${HEADERS} + ${CLIENT_ROOT_DIR}/migrations.h + ${CLIENT_ROOT_DIR}/../ipc/ipc.h + ${CLIENT_ROOT_DIR}/amnezia_application.h + ${CLIENT_ROOT_DIR}/containers/containers_defs.h + ${CLIENT_ROOT_DIR}/core/defs.h + ${CLIENT_ROOT_DIR}/core/errorstrings.h + ${CLIENT_ROOT_DIR}/core/scripts_registry.h + ${CLIENT_ROOT_DIR}/core/server_defs.h + ${CLIENT_ROOT_DIR}/core/controllers/apiController.h + ${CLIENT_ROOT_DIR}/core/controllers/gatewayController.h + ${CLIENT_ROOT_DIR}/core/controllers/serverController.h + ${CLIENT_ROOT_DIR}/core/controllers/vpnConfigurationController.h + ${CLIENT_ROOT_DIR}/protocols/protocols_defs.h + ${CLIENT_ROOT_DIR}/protocols/qml_register_protocols.h + ${CLIENT_ROOT_DIR}/ui/pages.h + ${CLIENT_ROOT_DIR}/ui/qautostart.h + ${CLIENT_ROOT_DIR}/protocols/vpnprotocol.h + ${CMAKE_CURRENT_BINARY_DIR}/version.h + ${CLIENT_ROOT_DIR}/core/sshclient.h + ${CLIENT_ROOT_DIR}/core/networkUtilities.h + ${CLIENT_ROOT_DIR}/core/serialization/serialization.h + ${CLIENT_ROOT_DIR}/core/serialization/transfer.h + ${CLIENT_ROOT_DIR}/core/enums/apiEnums.h + ${CLIENT_ROOT_DIR}/../common/logger/logger.h + ${CLIENT_ROOT_DIR}/utils/qmlUtils.h + ${CLIENT_ROOT_DIR}/core/api/apiUtils.h +) + +# Mozilla headres +set(HEADERS ${HEADERS} + ${CLIENT_ROOT_DIR}/mozilla/models/server.h + ${CLIENT_ROOT_DIR}/mozilla/shared/ipaddress.h + ${CLIENT_ROOT_DIR}/mozilla/shared/leakdetector.h + ${CLIENT_ROOT_DIR}/mozilla/controllerimpl.h + ${CLIENT_ROOT_DIR}/mozilla/localsocketcontroller.h +) + +if(NOT IOS) + set(HEADERS ${HEADERS} + ${CLIENT_ROOT_DIR}/platforms/ios/QRCodeReaderBase.h + ) +endif() + +if(NOT ANDROID) + set(HEADERS ${HEADERS} + ${CLIENT_ROOT_DIR}/ui/notificationhandler.h + ) +endif() + +set(SOURCES ${SOURCES} + ${CLIENT_ROOT_DIR}/migrations.cpp + ${CLIENT_ROOT_DIR}/amnezia_application.cpp + ${CLIENT_ROOT_DIR}/containers/containers_defs.cpp + ${CLIENT_ROOT_DIR}/core/errorstrings.cpp + ${CLIENT_ROOT_DIR}/core/scripts_registry.cpp + ${CLIENT_ROOT_DIR}/core/server_defs.cpp + ${CLIENT_ROOT_DIR}/core/controllers/apiController.cpp + ${CLIENT_ROOT_DIR}/core/controllers/gatewayController.cpp + ${CLIENT_ROOT_DIR}/core/controllers/serverController.cpp + ${CLIENT_ROOT_DIR}/core/controllers/vpnConfigurationController.cpp + ${CLIENT_ROOT_DIR}/protocols/protocols_defs.cpp + ${CLIENT_ROOT_DIR}/ui/qautostart.cpp + ${CLIENT_ROOT_DIR}/protocols/vpnprotocol.cpp + ${CLIENT_ROOT_DIR}/core/sshclient.cpp + ${CLIENT_ROOT_DIR}/core/networkUtilities.cpp + ${CLIENT_ROOT_DIR}/core/serialization/outbound.cpp + ${CLIENT_ROOT_DIR}/core/serialization/inbound.cpp + ${CLIENT_ROOT_DIR}/core/serialization/ss.cpp + ${CLIENT_ROOT_DIR}/core/serialization/ssd.cpp + ${CLIENT_ROOT_DIR}/core/serialization/vless.cpp + ${CLIENT_ROOT_DIR}/core/serialization/trojan.cpp + ${CLIENT_ROOT_DIR}/core/serialization/vmess.cpp + ${CLIENT_ROOT_DIR}/core/serialization/vmess_new.cpp + ${CLIENT_ROOT_DIR}/../common/logger/logger.cpp + ${CLIENT_ROOT_DIR}/utils/qmlUtils.cpp + ${CLIENT_ROOT_DIR}/core/api/apiUtils.cpp +) + +# Mozilla sources +set(SOURCES ${SOURCES} + ${CLIENT_ROOT_DIR}/mozilla/models/server.cpp + ${CLIENT_ROOT_DIR}/mozilla/shared/ipaddress.cpp + ${CLIENT_ROOT_DIR}/mozilla/shared/leakdetector.cpp + ${CLIENT_ROOT_DIR}/mozilla/localsocketcontroller.cpp +) + +if(NOT IOS) + set(SOURCES ${SOURCES} + ${CLIENT_ROOT_DIR}/platforms/ios/QRCodeReaderBase.cpp + ) +endif() + +if(NOT ANDROID) + set(SOURCES ${SOURCES} + ${CLIENT_ROOT_DIR}/ui/notificationhandler.cpp + ) +endif() + +file(GLOB COMMON_FILES_H CONFIGURE_DEPENDS ${CLIENT_ROOT_DIR}/*.h) +file(GLOB COMMON_FILES_CPP CONFIGURE_DEPENDS ${CLIENT_ROOT_DIR}/*.cpp) + +file(GLOB_RECURSE PAGE_LOGIC_H CONFIGURE_DEPENDS ${CLIENT_ROOT_DIR}/ui/pages_logic/*.h) +file(GLOB_RECURSE PAGE_LOGIC_CPP CONFIGURE_DEPENDS ${CLIENT_ROOT_DIR}/ui/pages_logic/*.cpp) + +file(GLOB CONFIGURATORS_H CONFIGURE_DEPENDS ${CLIENT_ROOT_DIR}/configurators/*.h) +file(GLOB CONFIGURATORS_CPP CONFIGURE_DEPENDS ${CLIENT_ROOT_DIR}/configurators/*.cpp) + +file(GLOB UI_MODELS_H CONFIGURE_DEPENDS + ${CLIENT_ROOT_DIR}/ui/models/*.h + ${CLIENT_ROOT_DIR}/ui/models/protocols/*.h + ${CLIENT_ROOT_DIR}/ui/models/services/*.h + ${CLIENT_ROOT_DIR}/ui/models/api/*.h +) +file(GLOB UI_MODELS_CPP CONFIGURE_DEPENDS + ${CLIENT_ROOT_DIR}/ui/models/*.cpp + ${CLIENT_ROOT_DIR}/ui/models/protocols/*.cpp + ${CLIENT_ROOT_DIR}/ui/models/services/*.cpp + ${CLIENT_ROOT_DIR}/ui/models/api/*.cpp +) + +file(GLOB UI_CONTROLLERS_H CONFIGURE_DEPENDS + ${CLIENT_ROOT_DIR}/ui/controllers/*.h + ${CLIENT_ROOT_DIR}/ui/controllers/api/*.h +) +file(GLOB UI_CONTROLLERS_CPP CONFIGURE_DEPENDS + ${CLIENT_ROOT_DIR}/ui/controllers/*.cpp + ${CLIENT_ROOT_DIR}/ui/controllers/api/*.cpp +) + +set(HEADERS ${HEADERS} + ${COMMON_FILES_H} + ${PAGE_LOGIC_H} + ${CONFIGURATORS_H} + ${UI_MODELS_H} + ${UI_CONTROLLERS_H} +) +set(SOURCES ${SOURCES} + ${COMMON_FILES_CPP} + ${PAGE_LOGIC_CPP} + ${CONFIGURATORS_CPP} + ${UI_MODELS_CPP} + ${UI_CONTROLLERS_CPP} +) + +if(WIN32) + set(HEADERS ${HEADERS} + ${CLIENT_ROOT_DIR}/protocols/ikev2_vpn_protocol_windows.h + ) + + set(SOURCES ${SOURCES} + ${CLIENT_ROOT_DIR}/protocols/ikev2_vpn_protocol_windows.cpp + ) + + set(RESOURCES ${RESOURCES} + ${CMAKE_CURRENT_BINARY_DIR}/amneziavpn.rc + ) +endif() + +if(WIN32 OR (APPLE AND NOT IOS) OR (LINUX AND NOT ANDROID)) + message("Client desktop build") + add_compile_definitions(AMNEZIA_DESKTOP) + + set(HEADERS ${HEADERS} + ${CLIENT_ROOT_DIR}/core/ipcclient.h + ${CLIENT_ROOT_DIR}/core/privileged_process.h + ${CLIENT_ROOT_DIR}/ui/systemtray_notificationhandler.h + ${CLIENT_ROOT_DIR}/protocols/openvpnprotocol.h + ${CLIENT_ROOT_DIR}/protocols/openvpnovercloakprotocol.h + ${CLIENT_ROOT_DIR}/protocols/shadowsocksvpnprotocol.h + ${CLIENT_ROOT_DIR}/protocols/wireguardprotocol.h + ${CLIENT_ROOT_DIR}/protocols/xrayprotocol.h + ${CLIENT_ROOT_DIR}/protocols/awgprotocol.h + ) + + set(SOURCES ${SOURCES} + ${CLIENT_ROOT_DIR}/core/ipcclient.cpp + ${CLIENT_ROOT_DIR}/core/privileged_process.cpp + ${CLIENT_ROOT_DIR}/ui/systemtray_notificationhandler.cpp + ${CLIENT_ROOT_DIR}/protocols/openvpnprotocol.cpp + ${CLIENT_ROOT_DIR}/protocols/openvpnovercloakprotocol.cpp + ${CLIENT_ROOT_DIR}/protocols/shadowsocksvpnprotocol.cpp + ${CLIENT_ROOT_DIR}/protocols/wireguardprotocol.cpp + ${CLIENT_ROOT_DIR}/protocols/xrayprotocol.cpp + ${CLIENT_ROOT_DIR}/protocols/awgprotocol.cpp + ) +endif() diff --git a/client/core/api/apiUtils.cpp b/client/core/api/apiUtils.cpp new file mode 100644 index 00000000..eed34f65 --- /dev/null +++ b/client/core/api/apiUtils.cpp @@ -0,0 +1,10 @@ +#include "apiUtils.h" + +#include + +bool ApiUtils::isSubscriptionExpired(const QString &subscriptionEndDate) +{ + QDateTime now = QDateTime::currentDateTime(); + QDateTime endDate = QDateTime::fromString(subscriptionEndDate, Qt::ISODateWithMs); + return endDate < now; +} diff --git a/client/core/api/apiUtils.h b/client/core/api/apiUtils.h new file mode 100644 index 00000000..dc863c22 --- /dev/null +++ b/client/core/api/apiUtils.h @@ -0,0 +1,11 @@ +#ifndef APIUTILS_H +#define APIUTILS_H + +#include + +namespace ApiUtils +{ + bool isSubscriptionExpired(const QString &subscriptionEndDate); +} + +#endif // APIUTILS_H diff --git a/client/core/controllers/apiController.cpp b/client/core/controllers/apiController.cpp index 52ec86c2..4e43f148 100644 --- a/client/core/controllers/apiController.cpp +++ b/client/core/controllers/apiController.cpp @@ -8,8 +8,8 @@ #include "amnezia_application.h" #include "configurators/wireguard_configurator.h" #include "core/enums/apiEnums.h" -#include "version.h" #include "gatewayController.h" +#include "version.h" namespace { @@ -213,10 +213,29 @@ void ApiController::updateServerConfigFromApi(const QString &installationUuid, c } } +ErrorCode ApiController::getAccountInfo(const QString &userCountryCode, const QString &serviceType, const QJsonObject &authData, + QByteArray &responseBody) +{ + GatewayController gatewayController(m_gatewayEndpoint, m_isDevEnvironment, requestTimeoutMsecs); + + QJsonObject apiPayload; + apiPayload[configKey::userCountryCode] = userCountryCode; + apiPayload[configKey::serviceType] = serviceType; + apiPayload[configKey::authData] = authData; + + ErrorCode errorCode = gatewayController.post(QString("%1v1/account_info"), apiPayload, responseBody); + + return errorCode; +} + ErrorCode ApiController::getServicesList(QByteArray &responseBody) { GatewayController gatewayController(m_gatewayEndpoint, m_isDevEnvironment, requestTimeoutMsecs); - ErrorCode errorCode = gatewayController.get("%1v1/services", responseBody); + + QJsonObject apiPayload; + apiPayload[configKey::osVersion] = QSysInfo::productType(); + + ErrorCode errorCode = gatewayController.post(QString("%1v1/services"), apiPayload, responseBody); if (errorCode == ErrorCode::NoError) { if (!responseBody.contains("services")) { return ErrorCode::ApiServicesMissingError; @@ -254,3 +273,24 @@ ErrorCode ApiController::getConfigForService(const QString &installationUuid, co return errorCode; } + +ErrorCode ApiController::getNativeConfig(const QString &userCountryCode, const QString &serviceType, const QString &protocol, + const QString &serverCountryCode, const QJsonObject &authData, QString &nativeConfig) +{ + GatewayController gatewayController(m_gatewayEndpoint, m_isDevEnvironment, requestTimeoutMsecs); + + ApiPayloadData apiPayloadData = generateApiPayloadData(protocol); + + QJsonObject apiPayload = fillApiPayload(protocol, apiPayloadData); + apiPayload[configKey::userCountryCode] = userCountryCode; + apiPayload[configKey::serverCountryCode] = serverCountryCode; + apiPayload[configKey::serviceType] = serviceType; + apiPayload[configKey::authData] = authData; + + QByteArray responseBody; + ErrorCode errorCode = gatewayController.post(QString("%1v1/country_config"), apiPayload, responseBody); + + nativeConfig = responseBody; + + return errorCode; +} diff --git a/client/core/controllers/apiController.h b/client/core/controllers/apiController.h index bcb25f96..9fb1f49d 100644 --- a/client/core/controllers/apiController.h +++ b/client/core/controllers/apiController.h @@ -19,9 +19,14 @@ public: public slots: void updateServerConfigFromApi(const QString &installationUuid, const int serverIndex, QJsonObject serverConfig); + ErrorCode getAccountInfo(const QString &userCountryCode, const QString &serviceType, const QJsonObject &authData, + QByteArray &responseBody); ErrorCode getServicesList(QByteArray &responseBody); ErrorCode getConfigForService(const QString &installationUuid, const QString &userCountryCode, const QString &serviceType, - const QString &protocol, const QString &serverCountryCode, const QJsonObject &authData, QJsonObject &serverConfig); + const QString &protocol, const QString &serverCountryCode, const QJsonObject &authData, + QJsonObject &serverConfig); + ErrorCode getNativeConfig(const QString &userCountryCode, const QString &serviceType, const QString &protocol, + const QString &serverCountryCode, const QJsonObject &authData, QString &nativeConfig); signals: void errorOccurred(ErrorCode errorCode); diff --git a/client/resources.qrc b/client/resources.qrc index c5563618..308accda 100644 --- a/client/resources.qrc +++ b/client/resources.qrc @@ -225,6 +225,9 @@ ui/qml/Pages2/PageShareFullAccess.qml ui/qml/Pages2/PageStart.qml ui/qml/Components/RenameServerDrawer.qml + ui/qml/Controls2/ListViewType.qml + ui/qml/Pages2/PageSettingsApiSupport.qml + ui/qml/Pages2/PageSettingsApiInstructions.qml images/flagKit/ZW.svg diff --git a/client/ui/controllers/api/apiSettingsController.cpp b/client/ui/controllers/api/apiSettingsController.cpp new file mode 100644 index 00000000..d1025432 --- /dev/null +++ b/client/ui/controllers/api/apiSettingsController.cpp @@ -0,0 +1,57 @@ +#include "apiSettingsController.h" + +#include "core/controllers/gatewayController.h" + +namespace +{ + namespace configKey + { + constexpr char userCountryCode[] = "user_country_code"; + constexpr char serverCountryCode[] = "server_country_code"; + constexpr char serviceType[] = "service_type"; + constexpr char serviceInfo[] = "service_info"; + + constexpr char apiConfig[] = "api_config"; + constexpr char authData[] = "auth_data"; + } + + const int requestTimeoutMsecs = 12 * 1000; // 12 secs +} + +ApiSettingsController::ApiSettingsController(const QSharedPointer &serversModel, + const QSharedPointer &apiAccountInfoModel, const std::shared_ptr &settings, + QObject *parent) + : QObject(parent), m_serversModel(serversModel), m_apiAccountInfoModel(apiAccountInfoModel), m_settings(settings) +{ +} + +ApiSettingsController::~ApiSettingsController() +{ +} + +bool ApiSettingsController::getAccountInfo() +{ + GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), requestTimeoutMsecs); + + auto processedIndex = m_serversModel->getProcessedServerIndex(); + auto serverConfig = m_serversModel->getServerConfig(processedIndex); + auto apiConfig = serverConfig.value(configKey::apiConfig).toObject(); + auto authData = serverConfig.value(configKey::authData).toObject(); + + QJsonObject apiPayload; + apiPayload[configKey::userCountryCode] = apiConfig.value(configKey::userCountryCode).toString(); + apiPayload[configKey::serviceType] = apiConfig.value(configKey::serviceType).toString(); + apiPayload[configKey::authData] = authData; + + QByteArray responseBody; + ErrorCode errorCode = gatewayController.post(QString("%1v1/account_info"), apiPayload, responseBody); + if (errorCode != ErrorCode::NoError) { + // emit errorOccured(errorCode); + return false; + } + + QJsonObject accountInfo = QJsonDocument::fromJson(responseBody).object(); + m_apiAccountInfoModel->updateModel(accountInfo, serverConfig); + + return true; +} diff --git a/client/ui/controllers/api/apiSettingsController.h b/client/ui/controllers/api/apiSettingsController.h new file mode 100644 index 00000000..cad0e9a1 --- /dev/null +++ b/client/ui/controllers/api/apiSettingsController.h @@ -0,0 +1,27 @@ +#ifndef APISETTINGSCONTROLLER_H +#define APISETTINGSCONTROLLER_H + +#include + +#include "ui/models/api/apiAccountInfoModel.h" +#include "ui/models/servers_model.h" + +class ApiSettingsController : public QObject +{ + Q_OBJECT +public: + ApiSettingsController(const QSharedPointer &serversModel, const QSharedPointer &apiAccountInfoModel, + const std::shared_ptr &settings, QObject *parent = nullptr); + ~ApiSettingsController(); + +public slots: + bool getAccountInfo(); + +private: + QSharedPointer m_serversModel; + QSharedPointer m_apiAccountInfoModel; + + std::shared_ptr m_settings; +}; + +#endif // APISETTINGSCONTROLLER_H diff --git a/client/ui/controllers/api/importController.cpp b/client/ui/controllers/api/importController.cpp new file mode 100644 index 00000000..e352326f --- /dev/null +++ b/client/ui/controllers/api/importController.cpp @@ -0,0 +1,2 @@ +#include "importController.h" + diff --git a/client/ui/controllers/api/importController.h b/client/ui/controllers/api/importController.h new file mode 100644 index 00000000..f4580360 --- /dev/null +++ b/client/ui/controllers/api/importController.h @@ -0,0 +1,24 @@ +#ifndef IMPORTCONTROLLER_H +#define IMPORTCONTROLLER_H + +#include + +#include "ui/models/api/apiAccountInfoModel.h" +#include "ui/models/servers_model.h" + +// namespace api +// { +// class ImportController : public QObject +// { +// Q_OBJECT +// public: +// ImportController(const QSharedPointer &serversModel, QSharedPointer &accountInfoModel); +// ~ImportController(); + +// private: +// QSharedPointer m_serversModel; +// QSharedPointer m_accountInfoModel; +// }; +// } + +#endif // IMPORTCONTROLLER_H diff --git a/client/ui/controllers/installController.cpp b/client/ui/controllers/installController.cpp index ae0804cb..26e433e0 100755 --- a/client/ui/controllers/installController.cpp +++ b/client/ui/controllers/installController.cpp @@ -832,6 +832,7 @@ bool InstallController::updateServiceFromApi(const int serverIndex, const QStrin auto authData = serverConfig.value(configKey::authData).toObject(); QJsonObject newServerConfig; + ErrorCode errorCode = apiController.getConfigForService( m_settings->getInstallationUuid(true), apiConfig.value(configKey::userCountryCode).toString(), apiConfig.value(configKey::serviceType).toString(), apiConfig.value(configKey::serviceProtocol).toString(), newCountryCode, diff --git a/client/ui/controllers/pageController.h b/client/ui/controllers/pageController.h index 428cf4f8..6e1efda5 100644 --- a/client/ui/controllers/pageController.h +++ b/client/ui/controllers/pageController.h @@ -33,6 +33,8 @@ namespace PageLoader PageSettingsAppSplitTunneling, PageSettingsApiServerInfo, PageSettingsApiAvailableCountries, + PageSettingsApiSupport, + PageSettingsApiInstructions, PageServiceSftpSettings, PageServiceTorWebsiteSettings, @@ -55,7 +57,7 @@ namespace PageLoader PageProtocolOpenVpnSettings, PageProtocolShadowSocksSettings, PageProtocolCloakSettings, - PageProtocolXraySettings, + PageProtocolXraySettings, PageProtocolWireGuardSettings, PageProtocolAwgSettings, PageProtocolIKev2Settings, @@ -106,7 +108,7 @@ public slots: int incrementDrawerDepth(); int decrementDrawerDepth(); - private slots: +private slots: void onShowErrorMessage(amnezia::ErrorCode errorCode); signals: diff --git a/client/ui/models/api/apiAccountInfoModel.cpp b/client/ui/models/api/apiAccountInfoModel.cpp new file mode 100644 index 00000000..7679f0ff --- /dev/null +++ b/client/ui/models/api/apiAccountInfoModel.cpp @@ -0,0 +1,105 @@ +#include "apiAccountInfoModel.h" + +#include + +#include "core/api/apiUtils.h" +#include "logger.h" + +namespace +{ + Logger logger("AccountInfoModel"); + + namespace configKey + { + constexpr char availableCountries[] = "available_countries"; + constexpr char serverCountryCode[] = "server_country_code"; + constexpr char serverCountryName[] = "server_country_name"; + constexpr char lastUpdated[] = "last_updated"; + constexpr char activeDeviceCount[] = "active_device_count"; + constexpr char maxDeviceCount[] = "max_device_count"; + constexpr char subscriptionEndDate[] = "subscription_end_date"; + } +} + +ApiAccountInfoModel::ApiAccountInfoModel(QObject *parent) : QAbstractListModel(parent) +{ +} + +int ApiAccountInfoModel::rowCount(const QModelIndex &parent) const +{ + Q_UNUSED(parent) + return 1; +} + +QVariant ApiAccountInfoModel::data(const QModelIndex &index, int role) const +{ + if (!index.isValid() || index.row() < 0 || index.row() >= static_cast(rowCount())) + return QVariant(); + + switch (role) { + case SubscriptionStatusRole: { + return ApiUtils::isSubscriptionExpired(m_accountInfoData.subscriptionEndDate) ? tr("Inactive") : tr("Active"); + } + case EndDateRole: { + return QDateTime::fromString(m_accountInfoData.subscriptionEndDate, Qt::ISODate).toLocalTime().toString("d MMM yyyy"); + } + case ConnectedDevicesRole: { + return tr("%1 out of %2").arg(m_accountInfoData.activeDeviceCount).arg(m_accountInfoData.maxDeviceCount); + } + // case ServiceDescriptionRole: { + // return apiServiceData.serviceInfo.name; + // } + } + + return QVariant(); +} + +void ApiAccountInfoModel::updateModel(const QJsonObject &accountInfoObject, const QJsonObject &serverConfig) +{ + beginResetModel(); + + AccountInfoData accountInfoData; + + auto availableCountries = accountInfoObject.value(configKey::availableCountries).toArray(); + for (const auto &country : availableCountries) { + auto countryObject = country.toObject(); + CountryInfo countryInfo; + countryInfo.serverCountryCode = countryObject.value(configKey::serverCountryCode).toString(); + countryInfo.serverCountryName = countryObject.value(configKey::serverCountryName).toString(); + countryInfo.lastUpdated = countryObject.value(configKey::lastUpdated).toString(); + + accountInfoData.AvailableCountries.push_back(countryInfo); + } + + accountInfoData.activeDeviceCount = accountInfoObject.value(configKey::activeDeviceCount).toInt(); + accountInfoData.maxDeviceCount = accountInfoObject.value(configKey::maxDeviceCount).toInt(); + accountInfoData.subscriptionEndDate = accountInfoObject.value(configKey::subscriptionEndDate).toString(); + + m_accountInfoData = accountInfoData; + + endResetModel(); +} + +QVariant ApiAccountInfoModel::data(const QString &roleString) +{ + QModelIndex modelIndex = index(0); + auto roles = roleNames(); + for (auto it = roles.begin(); it != roles.end(); it++) { + if (QString(it.value()) == roleString) { + return data(modelIndex, it.key()); + } + } + + return {}; +} + +QHash ApiAccountInfoModel::roleNames() const +{ + QHash roles; + roles[SubscriptionStatusRole] = "subscriptionStatus"; + roles[EndDateRole] = "endDate"; + roles[ConnectedDevicesRole] = "connectedDevices"; + roles[ServiceDescriptionRole] = "serviceDescription"; + + return roles; +} diff --git a/client/ui/models/api/apiAccountInfoModel.h b/client/ui/models/api/apiAccountInfoModel.h new file mode 100644 index 00000000..f7cabb69 --- /dev/null +++ b/client/ui/models/api/apiAccountInfoModel.h @@ -0,0 +1,55 @@ +#ifndef APIACCOUNTINFOMODEL_H +#define APIACCOUNTINFOMODEL_H + +#include +#include +#include + +class ApiAccountInfoModel : public QAbstractListModel +{ + Q_OBJECT + +public: + enum Roles { + SubscriptionStatusRole = Qt::UserRole + 1, + ConnectedDevicesRole, + ServiceDescriptionRole, + EndDateRole + }; + + explicit ApiAccountInfoModel(QObject *parent = nullptr); + + int rowCount(const QModelIndex &parent = QModelIndex()) const override; + + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; + +public slots: + void updateModel(const QJsonObject &accountInfoObject, const QJsonObject &serverConfig); + QVariant data(const QString &roleString); + +protected: + QHash roleNames() const override; + +private: + struct CountryInfo + { + QString serverCountryCode; + QString serverCountryName; + QString lastUpdated; + }; + + struct AccountInfoData + { + QString subscriptionEndDate; + int activeDeviceCount; + int maxDeviceCount; + + QString vpnKey; + + QVector AvailableCountries; + }; + + AccountInfoData m_accountInfoData; +}; + +#endif // APIACCOUNTINFOMODEL_H diff --git a/client/ui/qml/Components/ServersListView.qml b/client/ui/qml/Components/ServersListView.qml index 8d591d86..870b83b7 100644 --- a/client/ui/qml/Components/ServersListView.qml +++ b/client/ui/qml/Components/ServersListView.qml @@ -112,9 +112,10 @@ ListView { ServersModel.processedIndex = index if (ServersModel.getProcessedServerData("isServerFromGatewayApi")) { + ApiSettingsController.getAccountInfo() + if (ServersModel.getProcessedServerData("isCountrySelectionAvailable")) { PageController.goToPage(PageEnum.PageSettingsApiAvailableCountries) - } else { PageController.goToPage(PageEnum.PageSettingsApiServerInfo) } diff --git a/client/ui/qml/Controls2/ListViewType.qml b/client/ui/qml/Controls2/ListViewType.qml new file mode 100644 index 00000000..0de43d77 --- /dev/null +++ b/client/ui/qml/Controls2/ListViewType.qml @@ -0,0 +1,38 @@ +import QtQuick +import QtQuick.Controls + +ListView { + id: root + + 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 {} + + clip: true + reuseItems: true + snapMode: ListView.SnapToItem +} diff --git a/client/ui/qml/Pages2/PageHome.qml b/client/ui/qml/Pages2/PageHome.qml index fc5f5326..38a87e72 100644 --- a/client/ui/qml/Pages2/PageHome.qml +++ b/client/ui/qml/Pages2/PageHome.qml @@ -299,9 +299,10 @@ PageType { ServersModel.processedIndex = ServersModel.defaultIndex if (ServersModel.getProcessedServerData("isServerFromGatewayApi")) { + ApiSettingsController.getAccountInfo() + if (ServersModel.getProcessedServerData("isCountrySelectionAvailable")) { PageController.goToPage(PageEnum.PageSettingsApiAvailableCountries) - } else { PageController.goToPage(PageEnum.PageSettingsApiServerInfo) } diff --git a/client/ui/qml/Pages2/PageSettingsAbout.qml b/client/ui/qml/Pages2/PageSettingsAbout.qml index 2160692b..37327313 100644 --- a/client/ui/qml/Pages2/PageSettingsAbout.qml +++ b/client/ui/qml/Pages2/PageSettingsAbout.qml @@ -47,8 +47,7 @@ PageType { readonly property string description: qsTr("For reviews and bug reports") readonly property string imageSource: "qrc:/images/controls/mail.svg" readonly property var handler: function() { - GC.copyToClipBoard(title) - PageController.showNotificationMessage(qsTr("Copied")) + Qt.openUrlExternally(qsTr("mailto:support@amnezia.org")) } } diff --git a/client/ui/qml/Pages2/PageSettingsApiInstructions.qml b/client/ui/qml/Pages2/PageSettingsApiInstructions.qml new file mode 100644 index 00000000..9106808a --- /dev/null +++ b/client/ui/qml/Pages2/PageSettingsApiInstructions.qml @@ -0,0 +1,87 @@ +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts + +import SortFilterProxyModel 0.2 + +import PageEnum 1.0 +import Style 1.0 + +import "./" +import "../Controls2" +import "../Controls2/TextTypes" +import "../Config" +import "../Components" + +PageType { + id: root + + QtObject { + id: windows + + readonly property string title: qsTr("Windows") + readonly property string imageSource: "qrc:/images/controls/external-link.svg" + readonly property var handler: function() { + Qt.openUrlExternally(LanguageModel.getCurrentSiteUrl()) + } + } + + QtObject { + id: linux + + readonly property string title: qsTr("Windows") + readonly property string imageSource: "qrc:/images/controls/external-link.svg" + readonly property var handler: function() { + Qt.openUrlExternally(LanguageModel.getCurrentSiteUrl()) + } + } + + property list instructionsModel: [ + windows, + linux + ] + + ListViewType { + id: listView + + anchors.fill: parent + + model: instructionsModel + + header: ColumnLayout { + width: listView.width + + BackButtonType { + id: backButton + } + + HeaderType { + id: header + + Layout.fillWidth: true + Layout.rightMargin: 16 + Layout.leftMargin: 16 + + headerText: "Support" + descriptionText: qsTr("Our technical support specialists are ready to help you at any time") + } + } + + delegate: ColumnLayout { + width: listView.width + + LabelWithButtonType { + id: telegramButton + Layout.fillWidth: true + Layout.topMargin: 6 + + text: title + leftImageSource: imageSource + + clickedFunction: handler + } + + DividerType {} + } + } +} diff --git a/client/ui/qml/Pages2/PageSettingsApiServerInfo.qml b/client/ui/qml/Pages2/PageSettingsApiServerInfo.qml index 4346e617..350f2ea2 100644 --- a/client/ui/qml/Pages2/PageSettingsApiServerInfo.qml +++ b/client/ui/qml/Pages2/PageSettingsApiServerInfo.qml @@ -18,28 +18,19 @@ PageType { id: root property list labelsModel: [ - regionObject, - priceObject, + statusObject, endDateObject, - speedObject + deviceCountObject ] QtObject { - id: regionObject + id: statusObject - readonly property string title: qsTr("For the region") - readonly property string contentKey: "region" + readonly property string title: qsTr("Subscription status") + readonly property string contentKey: "subscriptionStatus" readonly property string objectImageSource: "qrc:/images/controls/map-pin.svg" } - QtObject { - id: priceObject - - readonly property string title: qsTr("Price") - readonly property string contentKey: "price" - readonly property string objectImageSource: "qrc:/images/controls/tag.svg" - } - QtObject { id: endDateObject @@ -49,10 +40,10 @@ PageType { } QtObject { - id: speedObject + id: deviceCountObject - readonly property string title: qsTr("Speed") - readonly property string contentKey: "speed" + readonly property string title: qsTr("Connected devices") + readonly property string contentKey: "connectedDevices" readonly property string objectImageSource: "qrc:/images/controls/gauge.svg" } @@ -83,43 +74,11 @@ PageType { } } - ListView { + ListViewType { id: listView - property bool isFocusable: true - anchors.fill: parent - 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 {} - - clip: true - reuseItems: true - snapMode: ListView.SnapToItem - model: labelsModel header: ColumnLayout { @@ -175,7 +134,7 @@ PageType { imageSource: objectImageSource leftText: title - rightText: ApiServicesModel.getSelectedServiceData(contentKey) + rightText: ApiAccountInfoModel.data(contentKey) visible: rightText !== "" } @@ -185,53 +144,61 @@ PageType { width: listView.width spacing: 0 - ParagraphTextType { - Layout.fillWidth: true - Layout.rightMargin: 16 - Layout.leftMargin: 16 - - onLinkActivated: function(link) { - Qt.openUrlExternally(link) - } - textFormat: Text.RichText - text: { - var text = ApiServicesModel.getSelectedServiceData("features") - if (text === undefined) { - return "" - } - return text.replace("%1", LanguageModel.getCurrentSiteUrl()) - } - - visible: text !== "" - - MouseArea { - anchors.fill: parent - acceptedButtons: Qt.NoButton - cursorShape: parent.hoveredLink ? Qt.PointingHandCursor : Qt.ArrowCursor - } - } - LabelWithButtonType { - id: supportUuid Layout.fillWidth: true + Layout.topMargin: 32 - text: qsTr("Support tag") - descriptionText: SettingsController.getInstallationUuid() + visible: false - descriptionOnTop: true - - rightImageSource: "qrc:/images/controls/copy.svg" - rightImageColor: AmneziaStyle.color.paleGray + text: qsTr("Subscription key") + rightImageSource: "qrc:/images/controls/chevron-right.svg" clickedFunction: function() { - GC.copyToClipBoard(descriptionText) - PageController.showNotificationMessage(qsTr("Copied")) - if (!GC.isMobile()) { - this.rightButton.forceActiveFocus() - } } } + DividerType {} + + LabelWithButtonType { + Layout.fillWidth: true + + text: qsTr("Configuration files") + + descriptionText: qsTr("To connect the router") + rightImageSource: "qrc:/images/controls/chevron-right.svg" + + clickedFunction: function() { + } + } + + DividerType {} + + LabelWithButtonType { + Layout.fillWidth: true + + text: qsTr("Support") + rightImageSource: "qrc:/images/controls/chevron-right.svg" + + clickedFunction: function() { + PageController.goToPage(PageEnum.PageSettingsApiSupport) + } + } + + DividerType {} + + LabelWithButtonType { + Layout.fillWidth: true + + text: qsTr("How to connect on another devicey") + rightImageSource: "qrc:/images/controls/chevron-right.svg" + + clickedFunction: function() { + PageController.goToPage(PageEnum.PageSettingsApiInstructions) + } + } + + DividerType {} + BasicButtonType { id: resetButton Layout.alignment: Qt.AlignHCenter diff --git a/client/ui/qml/Pages2/PageSettingsApiSupport.qml b/client/ui/qml/Pages2/PageSettingsApiSupport.qml new file mode 100644 index 00000000..fafd1c02 --- /dev/null +++ b/client/ui/qml/Pages2/PageSettingsApiSupport.qml @@ -0,0 +1,107 @@ +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts + +import SortFilterProxyModel 0.2 + +import PageEnum 1.0 +import Style 1.0 + +import "./" +import "../Controls2" +import "../Controls2/TextTypes" +import "../Config" +import "../Components" + +PageType { + id: root + + ColumnLayout { + id: backButtonLayout + + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + + anchors.topMargin: 20 + + BackButtonType { + id: backButton + } + + + HeaderType { + id: header + + Layout.fillWidth: true + Layout.rightMargin: 16 + Layout.leftMargin: 16 + + headerText: "Support" + descriptionText: qsTr("Our technical support specialists are ready to help you at any time") + } + + LabelWithButtonType { + Layout.fillWidth: true + + text: qsTr("Telegram") + descriptionText: qsTr("@amnezia_premium_support_bot") + rightImageSource: "qrc:/images/controls/external-link.svg" + + clickedFunction: function() { + Qt.openUrlExternally(qsTr("https://t.me/amnezia_premium_support_bot")) + } + } + + DividerType {} + + LabelWithButtonType { + Layout.fillWidth: true + + text: qsTr("Mail") + descriptionText: qsTr("support@amnezia.org") + rightImageSource: "qrc:/images/controls/external-link.svg" + + clickedFunction: function() { + Qt.openUrlExternally(qsTr("mailto:support@amnezia.org")) + } + } + + DividerType {} + + LabelWithButtonType { + Layout.fillWidth: true + + text: qsTr("Site") + descriptionText: qsTr("amnezia.org") + rightImageSource: "qrc:/images/controls/external-link.svg" + + clickedFunction: function() { + Qt.openUrlExternally(LanguageModel.getCurrentSiteUrl()) + } + } + + DividerType {} + + LabelWithButtonType { + id: supportUuid + Layout.fillWidth: true + + text: qsTr("Support tag") + descriptionText: SettingsController.getInstallationUuid() + + descriptionOnTop: true + + rightImageSource: "qrc:/images/controls/copy.svg" + rightImageColor: AmneziaStyle.color.paleGray + + clickedFunction: function() { + GC.copyToClipBoard(descriptionText) + PageController.showNotificationMessage(qsTr("Copied")) + if (!GC.isMobile()) { + this.rightButton.forceActiveFocus() + } + } + } + } +} diff --git a/client/ui/qml/Pages2/PageSettingsServerData.qml b/client/ui/qml/Pages2/PageSettingsServerData.qml index cd736d39..977e669e 100644 --- a/client/ui/qml/Pages2/PageSettingsServerData.qml +++ b/client/ui/qml/Pages2/PageSettingsServerData.qml @@ -36,16 +36,6 @@ PageType { PageController.showErrorMessage(message) } - function onRemoveProcessedServerFinished(finishedMessage) { - if (!ServersModel.getServersCount()) { - PageController.goToPageHome() - } else { - PageController.goToStartPage() - PageController.goToPage(PageEnum.PageSettingsServersList) - } - PageController.showNotificationMessage(finishedMessage) - } - function onRebootProcessedServerFinished(finishedMessage) { PageController.showNotificationMessage(finishedMessage) } diff --git a/client/ui/qml/Pages2/PageSettingsServersList.qml b/client/ui/qml/Pages2/PageSettingsServersList.qml index b13f4947..31d9f72a 100644 --- a/client/ui/qml/Pages2/PageSettingsServersList.qml +++ b/client/ui/qml/Pages2/PageSettingsServersList.qml @@ -95,12 +95,9 @@ PageType { ServersModel.processedIndex = index if (ServersModel.getProcessedServerData("isServerFromGatewayApi")) { - if (ServersModel.getProcessedServerData("isCountrySelectionAvailable")) { - PageController.goToPage(PageEnum.PageSettingsApiAvailableCountries) + ApiSettingsController.getAccountInfo() - } else { - PageController.goToPage(PageEnum.PageSettingsApiServerInfo) - } + PageController.goToPage(PageEnum.PageSettingsApiServerInfo) } else { PageController.goToPage(PageEnum.PageSettingsServerInfo) } diff --git a/client/ui/qml/Pages2/PageStart.qml b/client/ui/qml/Pages2/PageStart.qml index 71009e28..b5a61e83 100644 --- a/client/ui/qml/Pages2/PageStart.qml +++ b/client/ui/qml/Pages2/PageStart.qml @@ -154,6 +154,16 @@ PageType { PageController.goToPageHome() PageController.showNotificationMessage(message) } + + function onRemoveProcessedServerFinished(finishedMessage) { + if (!ServersModel.getServersCount()) { + PageController.goToPageHome() + } else { + PageController.goToStartPage() + PageController.goToPage(PageEnum.PageSettingsServersList) + } + PageController.showNotificationMessage(finishedMessage) + } } Connections {