New start page and AmneziaFree support (#865)

New start page and AmneziaFree support
This commit is contained in:
Nethius 2024-08-20 16:54:05 +07:00 committed by GitHub
parent 01413e5a4c
commit 843156cf1b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
443 changed files with 11759 additions and 2758 deletions

View file

@ -8,6 +8,7 @@
#include <QtConcurrent>
#include "core/controllers/vpnConfigurationController.h"
#include "core/enums/apiEnums.h"
#include "version.h"
ConnectionController::ConnectionController(const QSharedPointer<ServersModel> &serversModel,
@ -16,7 +17,6 @@ ConnectionController::ConnectionController(const QSharedPointer<ServersModel> &s
const QSharedPointer<VpnConnection> &vpnConnection, const std::shared_ptr<Settings> &settings,
QObject *parent)
: QObject(parent),
m_apiController(this),
m_serversModel(serversModel),
m_containersModel(containersModel),
m_clientManagementModel(clientManagementModel),
@ -27,9 +27,7 @@ ConnectionController::ConnectionController(const QSharedPointer<ServersModel> &s
connect(this, &ConnectionController::connectToVpn, m_vpnConnection.get(), &VpnConnection::connectToVpn, Qt::QueuedConnection);
connect(this, &ConnectionController::disconnectFromVpn, m_vpnConnection.get(), &VpnConnection::disconnectFromVpn, Qt::QueuedConnection);
connect(&m_apiController, &ApiController::configUpdated, this,
static_cast<void (ConnectionController::*)(const bool, const QJsonObject &, const int)>(&ConnectionController::openConnection));
connect(&m_apiController, qOverload<ErrorCode>(&ApiController::errorOccurred), this, qOverload<ErrorCode>(&ConnectionController::connectionErrorOccurred));
connect(this, &ConnectionController::configFromApiUpdated, this, &ConnectionController::continueConnection);
m_state = Vpn::ConnectionState::Disconnected;
}
@ -46,14 +44,22 @@ void ConnectionController::openConnection()
int serverIndex = m_serversModel->getDefaultServerIndex();
QJsonObject serverConfig = m_serversModel->getServerConfig(serverIndex);
auto configVersion = serverConfig.value(config_key::configVersion).toInt();
emit m_vpnConnection->connectionStateChanged(Vpn::ConnectionState::Preparing);
if (serverConfig.value(config_key::configVersion).toInt()
if (configVersion == ApiConfigSources::Telegram
&& !m_serversModel->data(serverIndex, ServersModel::Roles::HasInstalledContainers).toBool()) {
m_apiController.updateServerConfigFromApi(m_settings->getInstallationUuid(true), serverIndex, serverConfig);
emit updateApiConfigFromTelegram();
} else if (configVersion && m_serversModel->isApiKeyExpired(serverIndex)) {
qDebug() << "attempt to update api config by end_date event";
if (configVersion == ApiConfigSources::Telegram) {
emit updateApiConfigFromTelegram();
} else {
emit updateApiConfigFromGateway();
}
} else {
openConnection(false, serverConfig, serverIndex);
continueConnection();
}
}
@ -185,12 +191,11 @@ bool ConnectionController::isProtocolConfigExists(const QJsonObject &containerCo
return true;
}
void ConnectionController::openConnection(const bool updateConfig, const QJsonObject &config, const int serverIndex)
void ConnectionController::continueConnection()
{
// Update config for this server as it was received from API
if (updateConfig) {
m_serversModel->editServer(config, serverIndex);
}
int serverIndex = m_serversModel->getDefaultServerIndex();
QJsonObject serverConfig = m_serversModel->getServerConfig(serverIndex);
auto configVersion = serverConfig.value(config_key::configVersion).toInt();
if (!m_serversModel->data(serverIndex, ServersModel::Roles::HasInstalledContainers).toBool()) {
emit noInstalledContainers();
@ -223,7 +228,7 @@ void ConnectionController::openConnection(const bool updateConfig, const QJsonOb
auto dns = m_serversModel->getDnsPair(serverIndex);
auto vpnConfiguration = vpnConfigurationController.createVpnConfiguration(dns, config, containerConfig, container, errorCode);
auto vpnConfiguration = vpnConfigurationController.createVpnConfiguration(dns, serverConfig, containerConfig, container, errorCode);
if (errorCode != ErrorCode::NoError) {
emit connectionErrorOccurred(tr("unable to create configuration"));
return;

View file

@ -1,7 +1,6 @@
#ifndef CONNECTIONCONTROLLER_H
#define CONNECTIONCONTROLLER_H
#include "core/controllers/apiController.h"
#include "protocols/vpnprotocol.h"
#include "ui/models/clientManagementModel.h"
#include "ui/models/containers_model.h"
@ -58,13 +57,15 @@ signals:
void connectButtonClicked();
void preparingConfig();
void updateApiConfigFromGateway();
void updateApiConfigFromTelegram();
void configFromApiUpdated();
private:
Vpn::ConnectionState getCurrentConnectionState();
bool isProtocolConfigExists(const QJsonObject &containerConfig, const DockerContainer container);
void openConnection(const bool updateConfig, const QJsonObject &config, const int serverIndex);
ApiController m_apiController;
void continueConnection();
QSharedPointer<ServersModel> m_serversModel;
QSharedPointer<ContainersModel> m_containersModel;

View file

@ -133,7 +133,7 @@ ErrorCode ExportController::generateNativeConfig(const DockerContainer container
int serverIndex = m_serversModel->getProcessedServerIndex();
ServerCredentials credentials = m_serversModel->getServerCredentials(serverIndex);
auto dns = m_serversModel->getDnsPair(serverIndex);
bool isApiConfig = qvariant_cast<bool>(m_serversModel->data(serverIndex, ServersModel::IsServerFromApiRole));
bool isApiConfig = qvariant_cast<bool>(m_serversModel->data(serverIndex, ServersModel::IsServerFromTelegramApiRole));
QJsonObject containerConfig = m_containersModel->getContainerConfig(container);
containerConfig.insert(config_key::container, ContainerProps::containerToString(container));

View file

@ -7,6 +7,7 @@
#include <QRandomGenerator>
#include <QStandardPaths>
#include "core/controllers/apiController.h"
#include "core/controllers/serverController.h"
#include "core/controllers/vpnConfigurationController.h"
#include "core/networkUtilities.h"
@ -15,14 +16,24 @@
#include "ui/models/protocols/wireguardConfigModel.h"
#include "utilities.h"
#ifdef Q_OS_IOS
#include <AmneziaVPN-Swift.h>
#endif
namespace
{
Logger logger("ServerController");
namespace configKey
{
constexpr char serviceInfo[] = "service_info";
constexpr char serviceType[] = "service_type";
constexpr char serviceProtocol[] = "service_protocol";
constexpr char userCountryCode[] = "user_country_code";
constexpr char serverCountryCode[] = "server_country_code";
constexpr char serverCountryName[] = "server_country_name";
constexpr char availableCountries[] = "available_countries";
constexpr char apiConfig[] = "api_config";
}
#ifdef Q_OS_WINDOWS
QString getNextDriverLetter()
{
@ -52,12 +63,14 @@ namespace
InstallController::InstallController(const QSharedPointer<ServersModel> &serversModel, const QSharedPointer<ContainersModel> &containersModel,
const QSharedPointer<ProtocolsModel> &protocolsModel,
const QSharedPointer<ClientManagementModel> &clientManagementModel,
const std::shared_ptr<Settings> &settings, QObject *parent)
const QSharedPointer<ApiServicesModel> &apiServicesModel, const std::shared_ptr<Settings> &settings,
QObject *parent)
: QObject(parent),
m_serversModel(serversModel),
m_containersModel(containersModel),
m_protocolModel(protocolsModel),
m_clientManagementModel(clientManagementModel),
m_apiServicesModel(apiServicesModel),
m_settings(settings)
{
}
@ -432,7 +445,7 @@ ErrorCode InstallController::getAlreadyInstalledContainers(const ServerCredentia
containerConfig.insert(config_key::password, password);
} else if (protocol == Proto::Socks5Proxy) {
QString proxyConfig = serverController->getTextFileFromContainer(container, credentials,
protocols::socks5Proxy::proxyConfigPath, errorCode);
protocols::socks5Proxy::proxyConfigPath, errorCode);
const static QRegularExpression usernameAndPasswordRegExp("users (\\w+):CL:(\\w+)");
QRegularExpressionMatch usernameAndPasswordMatch = usernameAndPasswordRegExp.match(proxyConfig);
@ -591,25 +604,8 @@ void InstallController::removeProcessedContainer()
void InstallController::removeApiConfig(const int serverIndex)
{
auto serverConfig = m_serversModel->getServerConfig(serverIndex);
#ifdef Q_OS_IOS
QString vpncName = QString("%1 (%2) %3")
.arg(serverConfig[config_key::description].toString())
.arg(serverConfig[config_key::hostName].toString())
.arg(serverConfig[config_key::vpnproto].toString());
AmneziaVPN::removeVPNC(vpncName.toStdString());
#endif
serverConfig.remove(config_key::dns1);
serverConfig.remove(config_key::dns2);
serverConfig.remove(config_key::containers);
serverConfig.remove(config_key::hostName);
serverConfig.insert(config_key::defaultContainer, ContainerProps::containerToString(DockerContainer::None));
m_serversModel->editServer(serverConfig, serverIndex);
m_serversModel->removeApiConfig(serverIndex);
emit apiConfigRemoved(tr("Api config removed"));
}
void InstallController::clearCachedProfile(QSharedPointer<ServerController> serverController)
@ -801,6 +797,110 @@ void InstallController::addEmptyServer()
emit installServerFinished(tr("Server added successfully"));
}
bool InstallController::fillAvailableServices()
{
ApiController apiController(m_settings->getGatewayEndpoint());
QByteArray responseBody;
ErrorCode errorCode = apiController.getServicesList(responseBody);
if (errorCode != ErrorCode::NoError) {
emit installationErrorOccurred(errorCode);
return false;
}
QJsonObject data = QJsonDocument::fromJson(responseBody).object();
m_apiServicesModel->updateModel(data);
return true;
}
bool InstallController::installServiceFromApi()
{
if (m_serversModel->isServerFromApiAlreadyExists(m_apiServicesModel->getCountryCode(), m_apiServicesModel->getSelectedServiceType(),
m_apiServicesModel->getSelectedServiceProtocol())) {
emit installationErrorOccurred(ErrorCode::ApiConfigAlreadyAdded);
return false;
}
ApiController apiController(m_settings->getGatewayEndpoint());
QJsonObject serverConfig;
ErrorCode errorCode = apiController.getConfigForService(m_settings->getInstallationUuid(true), m_apiServicesModel->getCountryCode(),
m_apiServicesModel->getSelectedServiceType(),
m_apiServicesModel->getSelectedServiceProtocol(), "", serverConfig);
if (errorCode != ErrorCode::NoError) {
emit installationErrorOccurred(errorCode);
return false;
}
auto serviceInfo = m_apiServicesModel->getSelectedServiceInfo();
QJsonObject apiConfig = serverConfig.value(configKey::apiConfig).toObject();
apiConfig.insert(configKey::serviceInfo, serviceInfo);
apiConfig.insert(configKey::userCountryCode, m_apiServicesModel->getCountryCode());
apiConfig.insert(configKey::serviceType, m_apiServicesModel->getSelectedServiceType());
apiConfig.insert(configKey::serviceProtocol, m_apiServicesModel->getSelectedServiceProtocol());
serverConfig.insert(configKey::apiConfig, apiConfig);
m_serversModel->addServer(serverConfig);
emit installServerFromApiFinished(tr("%1 installed successfully.").arg(m_apiServicesModel->getSelectedServiceName()));
return true;
}
bool InstallController::updateServiceFromApi(const int serverIndex, const QString &newCountryCode, const QString &newCountryName,
bool reloadServiceConfig)
{
ApiController apiController(m_settings->getGatewayEndpoint());
auto serverConfig = m_serversModel->getServerConfig(serverIndex);
auto apiConfig = serverConfig.value(configKey::apiConfig).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, newServerConfig);
if (errorCode != ErrorCode::NoError) {
emit installationErrorOccurred(errorCode);
return false;
}
QJsonObject newApiConfig = newServerConfig.value(configKey::apiConfig).toObject();
newApiConfig.insert(configKey::serviceInfo, apiConfig.value(configKey::serviceInfo));
newApiConfig.insert(configKey::userCountryCode, apiConfig.value(configKey::userCountryCode));
newApiConfig.insert(configKey::serviceType, apiConfig.value(configKey::serviceType));
newApiConfig.insert(configKey::serviceProtocol, apiConfig.value(configKey::serviceProtocol));
newServerConfig.insert(configKey::apiConfig, newApiConfig);
m_serversModel->editServer(newServerConfig, serverIndex);
if (reloadServiceConfig) {
emit reloadServerFromApiFinished(tr("API config reloaded"));
} else if (newCountryName.isEmpty()) {
emit updateServerFromApiFinished();
} else {
emit changeApiCountryFinished(tr("Successfully changed the country of connection to %1").arg(newCountryName));
}
return true;
}
void InstallController::updateServiceFromTelegram(const int serverIndex)
{
ApiController *apiController = new ApiController(m_settings->getGatewayEndpoint());
auto serverConfig = m_serversModel->getServerConfig(serverIndex);
apiController->updateServerConfigFromApi(m_settings->getInstallationUuid(true), serverIndex, serverConfig);
connect(apiController, &ApiController::finished, this, [this, apiController](const QJsonObject &config, const int serverIndex) {
m_serversModel->editServer(config, serverIndex);
emit updateServerFromApiFinished();
apiController->deleteLater();
});
connect(apiController, &ApiController::errorOccurred, this, [this, apiController](ErrorCode errorCode) {
emit installationErrorOccurred(errorCode);
apiController->deleteLater();
});
}
bool InstallController::isUpdateDockerContainerRequired(const DockerContainer container, const QJsonObject &oldConfig,
const QJsonObject &newConfig)
{

View file

@ -10,6 +10,7 @@
#include "ui/models/containers_model.h"
#include "ui/models/protocols_model.h"
#include "ui/models/servers_model.h"
#include "ui/models/apiServicesModel.h"
class InstallController : public QObject
{
@ -18,6 +19,7 @@ public:
explicit InstallController(const QSharedPointer<ServersModel> &serversModel, const QSharedPointer<ContainersModel> &containersModel,
const QSharedPointer<ProtocolsModel> &protocolsModel,
const QSharedPointer<ClientManagementModel> &clientManagementModel,
const QSharedPointer<ApiServicesModel> &apiServicesModel,
const std::shared_ptr<Settings> &settings, QObject *parent = nullptr);
~InstallController();
@ -50,11 +52,21 @@ public slots:
void addEmptyServer();
bool fillAvailableServices();
bool installServiceFromApi();
bool updateServiceFromApi(const int serverIndex, const QString &newCountryCode, const QString &newCountryName, bool reloadServiceConfig = false);
void updateServiceFromTelegram(const int serverIndex);
signals:
void installContainerFinished(const QString &finishMessage, bool isServiceInstall);
void installServerFinished(const QString &finishMessage);
void installServerFromApiFinished(const QString &message);
void updateContainerFinished(const QString &message);
void updateServerFromApiFinished();
void changeApiCountryFinished(const QString &message);
void reloadServerFromApiFinished(const QString &message);
void scanServerFinished(bool isInstalledContainerFound);
@ -77,6 +89,7 @@ signals:
void currentContainerUpdated();
void cachedProfileCleared(const QString &message);
void apiConfigRemoved(const QString &message);
private:
void installServer(const DockerContainer container, const QMap<DockerContainer, QJsonObject> &installedContainers,
@ -95,6 +108,8 @@ private:
QSharedPointer<ContainersModel> m_containersModel;
QSharedPointer<ProtocolsModel> m_protocolModel;
QSharedPointer<ClientManagementModel> m_clientManagementModel;
QSharedPointer<ApiServicesModel> m_apiServicesModel;
std::shared_ptr<Settings> m_settings;
ServerCredentials m_processedServerCredentials;

View file

@ -46,16 +46,16 @@ PageController::PageController(const QSharedPointer<ServersModel> &serversModel,
m_isTriggeredByConnectButton = false;
}
QString PageController::getInitialPage()
bool PageController::isStartPageVisible()
{
if (m_serversModel->getServersCount()) {
if (m_serversModel->getDefaultServerIndex() < 0) {
auto defaultServerIndex = m_serversModel->index(0);
m_serversModel->setData(defaultServerIndex, true, ServersModel::Roles::IsDefaultRole);
}
return getPagePath(PageLoader::PageEnum::PageStart);
return false;
} else {
return getPagePath(PageLoader::PageEnum::PageSetupWizardStart);
return true;
}
}

View file

@ -47,6 +47,8 @@ namespace PageLoader
PageSetupWizardTextKey,
PageSetupWizardViewConfig,
PageSetupWizardQrReader,
PageSetupWizardApiServicesList,
PageSetupWizardApiServiceInfo,
PageProtocolOpenVpnSettings,
PageProtocolShadowSocksSettings,
@ -57,7 +59,9 @@ namespace PageLoader
PageProtocolIKev2Settings,
PageProtocolRaw,
PageShareFullAccess
PageShareFullAccess,
PageDevMenu
};
Q_ENUM_NS(PageEnum)
@ -75,7 +79,7 @@ public:
QObject *parent = nullptr);
public slots:
QString getInitialPage();
bool isStartPageVisible();
QString getPagePath(PageLoader::PageEnum page);
void closeWindow();
@ -110,7 +114,6 @@ signals:
void closePage();
void restorePageHomeState(bool isContainerInstalled = false);
void replaceStartPage();
void showErrorMessage(amnezia::ErrorCode);
void showErrorMessage(const QString &errorMessage);

View file

@ -252,3 +252,36 @@ void SettingsController::requestNotificationPermission()
AndroidController::instance()->requestNotificationPermission();
#endif
}
QString SettingsController::getInstallationUuid()
{
return m_settings->getInstallationUuid(false);
}
void SettingsController::enableDevMode()
{
m_isDevModeEnabled = true;
emit devModeEnabled();
}
bool SettingsController::isDevModeEnabled()
{
return m_isDevModeEnabled;
}
void SettingsController::resetGatewayEndpoint()
{
m_settings->resetGatewayEndpoint();
emit gatewayEndpointChanged(m_settings->getGatewayEndpoint());
}
void SettingsController::setGatewayEndpoint(const QString &endpoint)
{
m_settings->setGatewayEndpoint(endpoint);
emit gatewayEndpointChanged(endpoint);
}
QString SettingsController::getGatewayEndpoint()
{
return m_settings->getGatewayEndpoint();
}

View file

@ -25,6 +25,9 @@ public:
Q_PROPERTY(bool isLoggingEnabled READ isLoggingEnabled WRITE toggleLogging NOTIFY loggingStateChanged)
Q_PROPERTY(bool isNotificationPermissionGranted READ isNotificationPermissionGranted NOTIFY onNotificationStateChanged)
Q_PROPERTY(bool isDevModeEnabled READ isDevModeEnabled NOTIFY devModeEnabled)
Q_PROPERTY(QString gatewayEndpoint READ getGatewayEndpoint WRITE setGatewayEndpoint NOTIFY gatewayEndpointChanged)
public slots:
void toggleAmneziaDns(bool enable);
bool isAmneziaDnsEnabled();
@ -70,6 +73,15 @@ public slots:
bool isNotificationPermissionGranted();
void requestNotificationPermission();
QString getInstallationUuid();
void enableDevMode();
bool isDevModeEnabled();
void resetGatewayEndpoint();
void setGatewayEndpoint(const QString &endpoint);
QString getGatewayEndpoint();
signals:
void primaryDnsChanged();
void secondaryDnsChanged();
@ -89,6 +101,9 @@ signals:
void onNotificationStateChanged();
void devModeEnabled();
void gatewayEndpointChanged(const QString &endpoint);
private:
QSharedPointer<ServersModel> m_serversModel;
QSharedPointer<ContainersModel> m_containersModel;
@ -101,6 +116,8 @@ private:
QDateTime m_loggingDisableDate;
bool m_isDevModeEnabled = false;
void checkIfNeedDisableLogs();
};

View file

@ -3,7 +3,7 @@
#ifndef Q_OS_IOS
#include <QDialog>
#include <QWidget>
#include <QWidget>
void setDockIconVisible(bool visible);
void fixWidget(QWidget *widget);

View file

@ -0,0 +1,80 @@
#include "apiCountryModel.h"
#include <QJsonObject>
#include "logger.h"
namespace
{
Logger logger("ApiCountryModel");
namespace configKey
{
constexpr char serverCountryCode[] = "server_country_code";
constexpr char serverCountryName[] = "server_country_name";
}
}
ApiCountryModel::ApiCountryModel(QObject *parent) : QAbstractListModel(parent)
{
}
int ApiCountryModel::rowCount(const QModelIndex &parent) const
{
Q_UNUSED(parent)
return m_countries.size();
}
QVariant ApiCountryModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid() || index.row() < 0 || index.row() >= static_cast<int>(rowCount()))
return QVariant();
QJsonObject countryInfo = m_countries.at(index.row()).toObject();
switch (role) {
case CountryCodeRole: {
return countryInfo.value(configKey::serverCountryCode).toString();
}
case CountryNameRole: {
return countryInfo.value(configKey::serverCountryName).toString();
}
}
return QVariant();
}
void ApiCountryModel::updateModel(const QJsonArray &data, const QString &currentCountryCode)
{
beginResetModel();
m_countries = data;
for (int i = 0; i < m_countries.size(); i++) {
if (m_countries.at(i).toObject().value(configKey::serverCountryCode).toString() == currentCountryCode) {
m_currentIndex = i;
emit currentIndexChanged(m_currentIndex);
break;
}
}
endResetModel();
}
int ApiCountryModel::getCurrentIndex()
{
return m_currentIndex;
}
void ApiCountryModel::setCurrentIndex(const int i)
{
m_currentIndex = i;
emit currentIndexChanged(m_currentIndex);
}
QHash<int, QByteArray> ApiCountryModel::roleNames() const
{
QHash<int, QByteArray> roles;
roles[CountryNameRole] = "countryName";
roles[CountryCodeRole] = "countryCode";
return roles;
}

View file

@ -0,0 +1,42 @@
#ifndef APICOUNTRYMODEL_H
#define APICOUNTRYMODEL_H
#include <QAbstractListModel>
#include <QJsonArray>
class ApiCountryModel : public QAbstractListModel
{
Q_OBJECT
public:
enum Roles {
CountryNameRole = Qt::UserRole + 1,
CountryCodeRole
};
explicit ApiCountryModel(QObject *parent = nullptr);
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
Q_PROPERTY(int currentIndex READ getCurrentIndex WRITE setCurrentIndex NOTIFY currentIndexChanged)
public slots:
void updateModel(const QJsonArray &data, const QString &currentCountryCode);
int getCurrentIndex();
void setCurrentIndex(const int i);
signals:
void currentIndexChanged(const int index);
protected:
QHash<int, QByteArray> roleNames() const override;
private:
QJsonArray m_countries;
int m_currentIndex;
};
#endif // APICOUNTRYMODEL_H

View file

@ -0,0 +1,203 @@
#include "apiServicesModel.h"
#include <QJsonObject>
#include "logger.h"
namespace
{
Logger logger("ApiServicesModel");
namespace configKey
{
constexpr char userCountryCode[] = "user_country_code";
constexpr char services[] = "services";
constexpr char serviceInfo[] = "service_info";
constexpr char serviceType[] = "service_type";
constexpr char serviceProtocol[] = "service_protocol";
constexpr char name[] = "name";
constexpr char price[] = "price";
constexpr char speed[] = "speed";
constexpr char timelimit[] = "timelimit";
constexpr char region[] = "region";
constexpr char availableCountries[] = "available_countries";
constexpr char storeEndpoint[] = "store_endpoint";
}
namespace serviceType
{
constexpr char amneziaFree[] = "amnezia-free";
constexpr char amneziaPremium[] = "amnezia-premium";
}
}
ApiServicesModel::ApiServicesModel(QObject *parent) : QAbstractListModel(parent)
{
}
int ApiServicesModel::rowCount(const QModelIndex &parent) const
{
Q_UNUSED(parent)
return m_services.size();
}
QVariant ApiServicesModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid() || index.row() < 0 || index.row() >= static_cast<int>(rowCount()))
return QVariant();
QJsonObject service = m_services.at(index.row()).toObject();
QJsonObject serviceInfo = service.value(configKey::serviceInfo).toObject();
auto serviceType = service.value(configKey::serviceType).toString();
switch (role) {
case NameRole: {
return serviceInfo.value(configKey::name).toString();
}
case CardDescriptionRole: {
auto speed = serviceInfo.value(configKey::speed).toString();
if (serviceType == serviceType::amneziaPremium) {
return tr("Classic VPN for comfortable work, downloading large files and watching videos. "
"Works for any sites. Speed up to %1 MBit/s")
.arg(speed);
} else {
return tr("VPN to access blocked sites in regions with high levels of Internet censorship. ");
}
}
case ServiceDescriptionRole: {
if (serviceType == serviceType::amneziaPremium) {
return tr("Amnezia Premium - A classic VPN for comfortable work, downloading large files, and watching videos in high resolution. "
"It works for all websites, even in countries with the highest level of internet censorship.");
} else {
return tr("Amnezia Free is a free VPN to bypass blocking in countries with high levels of internet censorship");
}
}
case SpeedRole: {
auto speed = serviceInfo.value(configKey::speed).toString();
return tr("%1 MBit/s").arg(speed);
}
case WorkPeriodRole: {
auto timelimit = serviceInfo.value(configKey::timelimit).toString();
if (timelimit == "0") {
return "";
}
return tr("%1 days").arg(timelimit);
}
case RegionRole: {
return serviceInfo.value(configKey::region).toString();
}
case FeaturesRole: {
if (serviceType == serviceType::amneziaPremium) {
return tr("");
} else {
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, "
"<a href=\"%1/free\" style=\"color: #FBB26A;\">more details on the website.</a>");
}
}
case PriceRole: {
auto price = serviceInfo.value(configKey::price).toString();
if (price == "free") {
return tr("Free");
}
return tr("%1 $/month").arg(price);
}
}
return QVariant();
}
void ApiServicesModel::updateModel(const QJsonObject &data)
{
beginResetModel();
m_countryCode = data.value(configKey::userCountryCode).toString();
m_services = data.value(configKey::services).toArray();
if (m_services.isEmpty()) {
QJsonObject service;
service.insert(configKey::serviceInfo, data.value(configKey::serviceInfo));
service.insert(configKey::serviceType, data.value(configKey::serviceType));
m_services.push_back(service);
m_selectedServiceIndex = 0;
}
endResetModel();
}
void ApiServicesModel::setServiceIndex(const int index)
{
m_selectedServiceIndex = index;
}
QJsonObject ApiServicesModel::getSelectedServiceInfo()
{
QJsonObject service = m_services.at(m_selectedServiceIndex).toObject();
return service.value(configKey::serviceInfo).toObject();
}
QString ApiServicesModel::getSelectedServiceType()
{
QJsonObject service = m_services.at(m_selectedServiceIndex).toObject();
return service.value(configKey::serviceType).toString();
}
QString ApiServicesModel::getSelectedServiceProtocol()
{
QJsonObject service = m_services.at(m_selectedServiceIndex).toObject();
return service.value(configKey::serviceProtocol).toString();
}
QString ApiServicesModel::getSelectedServiceName()
{
auto modelIndex = index(m_selectedServiceIndex, 0);
return data(modelIndex, ApiServicesModel::Roles::NameRole).toString();
}
QJsonArray ApiServicesModel::getSelectedServiceCountries()
{
QJsonObject service = m_services.at(m_selectedServiceIndex).toObject();
return service.value(configKey::availableCountries).toArray();
}
QString ApiServicesModel::getCountryCode()
{
return m_countryCode;
}
QString ApiServicesModel::getStoreEndpoint()
{
QJsonObject service = m_services.at(m_selectedServiceIndex).toObject();
return service.value(configKey::storeEndpoint).toString();
}
QVariant ApiServicesModel::getSelectedServiceData(const QString roleString)
{
QModelIndex modelIndex = index(m_selectedServiceIndex);
auto roles = roleNames();
for (auto it = roles.begin(); it != roles.end(); it++) {
if (QString(it.value()) == roleString) {
return data(modelIndex, it.key());
}
}
return {};
}
QHash<int, QByteArray> ApiServicesModel::roleNames() const
{
QHash<int, QByteArray> roles;
roles[NameRole] = "name";
roles[CardDescriptionRole] = "cardDescription";
roles[ServiceDescriptionRole] = "serviceDescription";
roles[SpeedRole] = "speed";
roles[WorkPeriodRole] = "workPeriod";
roles[RegionRole] = "region";
roles[FeaturesRole] = "features";
roles[PriceRole] = "price";
return roles;
}

View file

@ -0,0 +1,56 @@
#ifndef APISERVICESMODEL_H
#define APISERVICESMODEL_H
#include <QAbstractListModel>
#include <QJsonArray>
class ApiServicesModel : public QAbstractListModel
{
Q_OBJECT
public:
enum Roles {
NameRole = Qt::UserRole + 1,
CardDescriptionRole,
ServiceDescriptionRole,
SpeedRole,
WorkPeriodRole,
RegionRole,
FeaturesRole,
PriceRole
};
explicit ApiServicesModel(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 &data);
void setServiceIndex(const int index);
QJsonObject getSelectedServiceInfo();
QString getSelectedServiceType();
QString getSelectedServiceProtocol();
QString getSelectedServiceName();
QJsonArray getSelectedServiceCountries();
QString getCountryCode();
QString getStoreEndpoint();
QVariant getSelectedServiceData(const QString roleString);
protected:
QHash<int, QByteArray> roleNames() const override;
private:
QString m_countryCode;
QJsonArray m_services;
int m_selectedServiceIndex;
};
#endif // APISERVICESMODEL_H

View file

@ -94,6 +94,26 @@ bool ContainersModel::isServiceContainer(const int containerIndex)
return qvariant_cast<amnezia::ServiceType>(data(index(containerIndex), ServiceTypeRole) == ServiceType::Other);
}
bool ContainersModel::hasInstalledServices()
{
for (const auto &container : m_containers.keys()) {
if (ContainerProps::containerService(container) == ServiceType::Other) {
return true;
}
}
return false;
}
bool ContainersModel::hasInstalledProtocols()
{
for (const auto &container : m_containers.keys()) {
if (ContainerProps::containerService(container) == ServiceType::Vpn) {
return true;
}
}
return false;
}
QHash<int, QByteArray> ContainersModel::roleNames() const
{
QHash<int, QByteArray> roles;

View file

@ -54,6 +54,9 @@ public slots:
bool isSupportedByCurrentPlatform(const int containerIndex);
bool isServiceContainer(const int containerIndex);
bool hasInstalledServices();
bool hasInstalledProtocols();
protected:
QHash<int, QByteArray> roleNames() const override;

View file

@ -103,3 +103,12 @@ QString LanguageModel::getCurrentLanguageName()
{
return m_availableLanguages[getCurrentLanguageIndex()].name;
}
QString LanguageModel::getCurrentSiteUrl()
{
auto language = static_cast<LanguageSettings::AvailableLanguageEnum>(getCurrentLanguageIndex());
switch (language) {
case LanguageSettings::AvailableLanguageEnum::Russian: return "https://storage.googleapis.com/kldscp/amnezia.org";
default: return "https://amnezia.org";
}
}

View file

@ -59,6 +59,7 @@ public slots:
int getCurrentLanguageIndex();
int getLineHeightAppend();
QString getCurrentLanguageName();
QString getCurrentSiteUrl();
signals:
void updateTranslations(const QLocale &locale);

View file

@ -1,8 +1,31 @@
#include "servers_model.h"
#include "core/controllers/serverController.h"
#include "core/enums/apiEnums.h"
#include "core/networkUtilities.h"
#ifdef Q_OS_IOS
#include <AmneziaVPN-Swift.h>
#endif
namespace
{
namespace configKey
{
constexpr char apiConfig[] = "api_config";
constexpr char serviceInfo[] = "service_info";
constexpr char availableCountries[] = "available_countries";
constexpr char serverCountryCode[] = "server_country_code";
constexpr char serverCountryName[] = "server_country_name";
constexpr char userCountryCode[] = "user_country_code";
constexpr char serviceType[] = "service_type";
constexpr char serviceProtocol[] = "service_protocol";
constexpr char publicKeyInfo[] = "public_key";
constexpr char endDate[] = "end_date";
}
}
ServersModel::ServersModel(std::shared_ptr<Settings> settings, QObject *parent) : m_settings(settings), QAbstractListModel(parent)
{
m_isAmneziaDnsEnabled = m_settings->useAmneziaDns();
@ -63,6 +86,7 @@ QVariant ServersModel::data(const QModelIndex &index, int role) const
}
const QJsonObject server = m_servers.at(index.row()).toObject();
const auto apiConfig = server.value(configKey::apiConfig).toObject();
const auto configVersion = server.value(config_key::configVersion).toInt();
switch (role) {
case NameRole: {
@ -98,8 +122,23 @@ QVariant ServersModel::data(const QModelIndex &index, int role) const
case HasInstalledContainers: {
return serverHasInstalledContainers(index.row());
}
case IsServerFromApiRole: {
return server.value(config_key::configVersion).toInt();
case IsServerFromTelegramApiRole: {
return server.value(config_key::configVersion).toInt() == ApiConfigSources::Telegram;
}
case IsServerFromGatewayApiRole: {
return server.value(config_key::configVersion).toInt() == ApiConfigSources::AmneziaGateway;
}
case ApiConfigRole: {
return apiConfig;
}
case IsCountrySelectionAvailableRole: {
return !apiConfig.value(configKey::availableCountries).toArray().isEmpty();
}
case ApiAvailableCountriesRole: {
return apiConfig.value(configKey::availableCountries).toArray();
}
case ApiServerCountryCodeRole: {
return apiConfig.value(configKey::serverCountryCode).toString();
}
case HasAmneziaDns: {
QString primaryDns = server.value(config_key::dns1).toString();
@ -146,10 +185,13 @@ const QString ServersModel::getDefaultServerName()
QString ServersModel::getServerDescription(const QJsonObject &server, const int index) const
{
const auto configVersion = server.value(config_key::configVersion).toInt();
const auto apiConfig = server.value(configKey::apiConfig).toObject();
QString description;
if (configVersion) {
if (configVersion && !apiConfig.value(configKey::serverCountryCode).toString().isEmpty()) {
return apiConfig.value(configKey::serverCountryName).toString();
} else if (configVersion) {
return server.value(config_key::description).toString();
} else if (data(index, HasWriteAccessRole).toBool()) {
if (m_isAmneziaDnsEnabled && isAmneziaDnsContainerInstalled(index)) {
@ -208,6 +250,12 @@ void ServersModel::setProcessedServerIndex(const int index)
{
m_processedServerIndex = index;
updateContainersModel();
if (data(index, IsServerFromGatewayApiRole).toBool()) {
if (data(index, IsCountrySelectionAvailableRole).toBool()) {
emit updateApiLanguageModel();
}
emit updateApiServicesModel();
}
emit processedServerIndexChanged(m_processedServerIndex);
}
@ -233,7 +281,8 @@ bool ServersModel::isDefaultServerCurrentlyProcessed()
bool ServersModel::isDefaultServerFromApi()
{
return qvariant_cast<bool>(data(m_defaultServerIndex, IsServerFromApiRole));
return data(m_defaultServerIndex, IsServerFromTelegramApiRole).toBool()
|| data(m_defaultServerIndex, IsServerFromGatewayApiRole).toBool();
}
bool ServersModel::isProcessedServerHasWriteAccess()
@ -315,7 +364,12 @@ QHash<int, QByteArray> ServersModel::roleNames() const
roles[DefaultContainerRole] = "defaultContainer";
roles[HasInstalledContainers] = "hasInstalledContainers";
roles[IsServerFromApiRole] = "isServerFromApi";
roles[IsServerFromTelegramApiRole] = "isServerFromTelegramApi";
roles[IsServerFromGatewayApiRole] = "isServerFromGatewayApi";
roles[ApiConfigRole] = "apiConfig";
roles[IsCountrySelectionAvailableRole] = "isCountrySelectionAvailable";
roles[ApiAvailableCountriesRole] = "apiAvailableCountries";
roles[ApiServerCountryCodeRole] = "apiServerCountryCode";
return roles;
}
@ -399,8 +453,7 @@ void ServersModel::addContainerConfig(const int containerIndex, const QJsonObjec
auto defaultContainer = server.value(config_key::defaultContainer).toString();
if (ContainerProps::containerFromString(defaultContainer) == DockerContainer::None
&& ContainerProps::containerService(container) != ServiceType::Other
&& ContainerProps::isSupportedByCurrentPlatform(container)) {
&& ContainerProps::containerService(container) != ServiceType::Other && ContainerProps::isSupportedByCurrentPlatform(container)) {
server.insert(config_key::defaultContainer, ContainerProps::containerToString(container));
}
@ -565,7 +618,7 @@ void ServersModel::toggleAmneziaDns(bool enabled)
bool ServersModel::isServerFromApiAlreadyExists(const quint16 crc)
{
for (const auto &server : qAsConst(m_servers)) {
for (const auto &server : std::as_const(m_servers)) {
if (static_cast<quint16>(server.toObject().value(config_key::crc).toInt()) == crc) {
return true;
}
@ -573,6 +626,19 @@ bool ServersModel::isServerFromApiAlreadyExists(const quint16 crc)
return false;
}
bool ServersModel::isServerFromApiAlreadyExists(const QString &userCountryCode, const QString &serviceType, const QString &serviceProtocol)
{
for (const auto &server : std::as_const(m_servers)) {
const auto apiConfig = server.toObject().value(configKey::apiConfig).toObject();
if (apiConfig.value(configKey::userCountryCode).toString() == userCountryCode
&& apiConfig.value(configKey::serviceType).toString() == serviceType
&& apiConfig.value(configKey::serviceProtocol).toString() == serviceProtocol) {
return true;
}
}
return false;
}
bool ServersModel::serverHasInstalledContainers(const int serverIndex) const
{
QJsonObject server = m_servers.at(serverIndex).toObject();
@ -621,14 +687,89 @@ bool ServersModel::isDefaultServerDefaultContainerHasSplitTunneling()
auto containers = server.value(config_key::containers).toArray();
for (auto i = 0; i < containers.size(); i++) {
auto container = containers.at(i).toObject();
if (container.value(config_key::container).toString() != ContainerProps::containerToString(defaultContainer)) {
continue;
}
if (defaultContainer == DockerContainer::Awg || defaultContainer == DockerContainer::WireGuard) {
auto containerConfig = container.value(ContainerProps::containerTypeToString(defaultContainer)).toObject();
return !(containerConfig.value(config_key::last_config).toString().contains("AllowedIPs = 0.0.0.0/0, ::/0"));
QJsonObject serverProtocolConfig = container.value(ContainerProps::containerTypeToString(defaultContainer)).toObject();
QString clientProtocolConfigString = serverProtocolConfig.value(config_key::last_config).toString();
QJsonObject clientProtocolConfig = QJsonDocument::fromJson(clientProtocolConfigString.toUtf8()).object();
return (clientProtocolConfigString.contains("AllowedIPs") && !clientProtocolConfigString.contains("AllowedIPs = 0.0.0.0/0, ::/0"))
|| (!clientProtocolConfig.value(config_key::allowed_ips).toArray().isEmpty()
&& !clientProtocolConfig.value(config_key::allowed_ips).toArray().contains("0.0.0.0/0"));
} else if (defaultContainer == DockerContainer::Cloak || defaultContainer == DockerContainer::OpenVpn
|| defaultContainer == DockerContainer::ShadowSocks) {
auto containerConfig = container.value(ContainerProps::containerTypeToString(DockerContainer::OpenVpn)).toObject();
return !(containerConfig.value(config_key::last_config).toString().contains("redirect-gateway"));
auto serverProtocolConfig = container.value(ContainerProps::containerTypeToString(DockerContainer::OpenVpn)).toObject();
QString clientProtocolConfigString = serverProtocolConfig.value(config_key::last_config).toString();
return !clientProtocolConfigString.isEmpty() && !clientProtocolConfigString.contains("redirect-gateway");
}
}
return false;
}
bool ServersModel::isServerFromApi(const int serverIndex)
{
return data(serverIndex, IsServerFromTelegramApiRole).toBool() || data(serverIndex, IsServerFromGatewayApiRole).toBool();
}
bool ServersModel::isApiKeyExpired(const int serverIndex)
{
auto serverConfig = m_servers.at(serverIndex).toObject();
auto apiConfig = serverConfig.value(configKey::apiConfig).toObject();
auto publicKeyInfo = apiConfig.value(configKey::publicKeyInfo).toObject();
const QString endDate = publicKeyInfo.value(configKey::endDate).toString();
if (endDate.isEmpty()) {
publicKeyInfo.insert(configKey::endDate, QDateTime::currentDateTimeUtc().addDays(1).toString(Qt::ISODate));
apiConfig.insert(configKey::publicKeyInfo, publicKeyInfo);
serverConfig.insert(configKey::apiConfig, apiConfig);
editServer(serverConfig, serverIndex);
return false;
}
auto endDateDateTime = QDateTime::fromString(endDate, Qt::ISODate).toUTC();
if (endDateDateTime < QDateTime::currentDateTimeUtc()) {
return true;
}
return false;
}
void ServersModel::removeApiConfig(const int serverIndex)
{
auto serverConfig = getServerConfig(serverIndex);
#ifdef Q_OS_IOS
QString vpncName = QString("%1 (%2) %3")
.arg(serverConfig[config_key::description].toString())
.arg(serverConfig[config_key::hostName].toString())
.arg(serverConfig[config_key::vpnproto].toString());
AmneziaVPN::removeVPNC(vpncName.toStdString());
#endif
serverConfig.remove(config_key::dns1);
serverConfig.remove(config_key::dns2);
serverConfig.remove(config_key::containers);
serverConfig.remove(config_key::hostName);
auto apiConfig = serverConfig.value(configKey::apiConfig).toObject();
apiConfig.remove(configKey::publicKeyInfo);
serverConfig.insert(configKey::apiConfig, apiConfig);
serverConfig.insert(config_key::defaultContainer, ContainerProps::containerToString(DockerContainer::None));
editServer(serverConfig, serverIndex);
}
const QString ServersModel::getDefaultServerImagePathCollapsed()
{
const auto server = m_servers.at(m_defaultServerIndex).toObject();
const auto apiConfig = server.value(configKey::apiConfig).toObject();
const auto countryCode = apiConfig.value(configKey::serverCountryCode).toString();
if (countryCode.isEmpty()) {
return "";
}
return QString("qrc:/countriesFlags/images/flagKit/%1.svg").arg(countryCode);
}

View file

@ -3,8 +3,8 @@
#include <QAbstractListModel>
#include "settings.h"
#include "core/controllers/serverController.h"
#include "settings.h"
class ServersModel : public QAbstractListModel
{
@ -30,7 +30,13 @@ public:
DefaultContainerRole,
HasInstalledContainers,
IsServerFromApiRole,
IsServerFromTelegramApiRole,
IsServerFromGatewayApiRole,
ApiConfigRole,
IsCountrySelectionAvailableRole,
ApiAvailableCountriesRole,
ApiServerCountryCodeRole,
HasAmneziaDns
};
@ -49,8 +55,10 @@ public:
Q_PROPERTY(QString defaultServerName READ getDefaultServerName NOTIFY defaultServerNameChanged)
Q_PROPERTY(QString defaultServerDefaultContainerName READ getDefaultServerDefaultContainerName NOTIFY defaultServerDefaultContainerChanged)
Q_PROPERTY(QString defaultServerDescriptionCollapsed READ getDefaultServerDescriptionCollapsed NOTIFY defaultServerDefaultContainerChanged)
Q_PROPERTY(QString defaultServerImagePathCollapsed READ getDefaultServerImagePathCollapsed NOTIFY defaultServerDefaultContainerChanged)
Q_PROPERTY(QString defaultServerDescriptionExpanded READ getDefaultServerDescriptionExpanded NOTIFY defaultServerDefaultContainerChanged)
Q_PROPERTY(bool isDefaultServerDefaultContainerHasSplitTunneling READ isDefaultServerDefaultContainerHasSplitTunneling NOTIFY defaultServerDefaultContainerChanged)
Q_PROPERTY(bool isDefaultServerDefaultContainerHasSplitTunneling READ isDefaultServerDefaultContainerHasSplitTunneling NOTIFY
defaultServerDefaultContainerChanged)
Q_PROPERTY(bool isDefaultServerFromApi READ isDefaultServerFromApi NOTIFY defaultServerIndexChanged)
Q_PROPERTY(int processedIndex READ getProcessedServerIndex WRITE setProcessedServerIndex NOTIFY processedServerIndexChanged)
@ -60,6 +68,7 @@ public slots:
const int getDefaultServerIndex();
const QString getDefaultServerName();
const QString getDefaultServerDescriptionCollapsed();
const QString getDefaultServerImagePathCollapsed();
const QString getDefaultServerDescriptionExpanded();
const QString getDefaultServerDefaultContainerName();
bool isDefaultServerCurrentlyProcessed();
@ -101,6 +110,7 @@ public slots:
QPair<QString, QString> getDnsPair(const int serverIndex);
bool isServerFromApiAlreadyExists(const quint16 crc);
bool isServerFromApiAlreadyExists(const QString &userCountryCode, const QString &serviceType, const QString &serviceProtocol);
QVariant getDefaultServerData(const QString roleString);
@ -108,6 +118,10 @@ public slots:
bool isDefaultServerDefaultContainerHasSplitTunneling();
bool isServerFromApi(const int serverIndex);
bool isApiKeyExpired(const int serverIndex);
void removeApiConfig(const int serverIndex);
protected:
QHash<int, QByteArray> roleNames() const override;
@ -121,6 +135,9 @@ signals:
void defaultServerContainersUpdated(const QJsonArray &containers);
void defaultServerDefaultContainerChanged(const int containerIndex);
void updateApiLanguageModel();
void updateApiServicesModel();
private:
ServerCredentials serverCredentials(int index) const;

View file

@ -11,9 +11,9 @@ import Style 1.0
Button {
id: root
property string defaultButtonColor: AmneziaStyle.color.white
property string progressButtonColor: AmneziaStyle.color.white
property string connectedButtonColor: AmneziaStyle.color.orange
property string defaultButtonColor: AmneziaStyle.color.paleGray
property string progressButtonColor: AmneziaStyle.color.paleGray
property string connectedButtonColor: AmneziaStyle.color.goldenApricot
implicitWidth: 190
implicitHeight: 190
@ -50,13 +50,13 @@ Button {
verticalOffset: 0
radius: 10
samples: 25
color: root.activeFocus ? AmneziaStyle.color.white : AmneziaStyle.color.orange
color: root.activeFocus ? AmneziaStyle.color.paleGray : AmneziaStyle.color.goldenApricot
source: backgroundCircle
}
ShapePath {
fillColor: AmneziaStyle.color.transparent
strokeColor: AmneziaStyle.color.white
strokeColor: AmneziaStyle.color.paleGray
strokeWidth: root.activeFocus ? 1 : 0
capStyle: ShapePath.RoundCap
@ -74,7 +74,7 @@ Button {
fillColor: AmneziaStyle.color.transparent
strokeColor: {
if (ConnectionController.isConnectionInProgress) {
return AmneziaStyle.color.connectionInProgress
return AmneziaStyle.color.darkCharcoal
} else if (ConnectionController.isConnected) {
return connectedButtonColor
} else {
@ -115,7 +115,7 @@ Button {
ShapePath {
fillColor: AmneziaStyle.color.transparent
strokeColor: AmneziaStyle.color.white
strokeColor: AmneziaStyle.color.paleGray
strokeWidth: 3
capStyle: ShapePath.RoundCap

View file

@ -53,7 +53,7 @@ DrawerType2 {
Layout.fillWidth: true
Layout.topMargin: 16
visible: ServersModel.isDefaultServerDefaultContainerHasSplitTunneling && ServersModel.getDefaultServerData("isServerFromApi")
visible: ServersModel.isDefaultServerDefaultContainerHasSplitTunneling
text: qsTr("Split tunneling on the server")
descriptionText: qsTr("Enabled \nCan't be disabled for current server")
@ -68,7 +68,7 @@ DrawerType2 {
}
DividerType {
visible: ServersModel.isDefaultServerDefaultContainerHasSplitTunneling && ServersModel.getDefaultServerData("isServerFromApi")
visible: ServersModel.isDefaultServerDefaultContainerHasSplitTunneling
}
LabelWithButtonType {

View file

@ -134,7 +134,7 @@ DrawerType2 {
anchors.rightMargin: 16
anchors.leftMargin: 16
backgroundColor: AmneziaStyle.color.greyDark
backgroundColor: AmneziaStyle.color.slateGray
textFieldPlaceholderText: qsTr("application name")
}

View file

@ -89,10 +89,10 @@ DrawerType2 {
Layout.leftMargin: 16
defaultColor: AmneziaStyle.color.transparent
hoveredColor: AmneziaStyle.color.blackHovered
pressedColor: AmneziaStyle.color.blackPressed
disabledColor: AmneziaStyle.color.grey
textColor: AmneziaStyle.color.white
hoveredColor: AmneziaStyle.color.translucentWhite
pressedColor: AmneziaStyle.color.sheerWhite
disabledColor: AmneziaStyle.color.mutedGray
textColor: AmneziaStyle.color.paleGray
borderWidth: 1
text: noButtonText

View file

@ -147,8 +147,8 @@ DrawerType2 {
indicator: Rectangle {
width: parent.width - 1
height: parent.height
color: radioButton.hovered ? AmneziaStyle.color.greyDark : AmneziaStyle.color.blackLight
border.color: radioButton.focus ? AmneziaStyle.color.white : AmneziaStyle.color.transparent
color: radioButton.hovered ? AmneziaStyle.color.slateGray : AmneziaStyle.color.onyxBlack
border.color: radioButton.focus ? AmneziaStyle.color.paleGray : AmneziaStyle.color.transparent
border.width: radioButton.focus ? 1 : 0
Behavior on color {

View file

@ -113,10 +113,10 @@ DrawerType2 {
Layout.topMargin: 8
defaultColor: AmneziaStyle.color.transparent
hoveredColor: AmneziaStyle.color.blackHovered
pressedColor: AmneziaStyle.color.blackPressed
disabledColor: AmneziaStyle.color.grey
textColor: AmneziaStyle.color.white
hoveredColor: AmneziaStyle.color.translucentWhite
pressedColor: AmneziaStyle.color.sheerWhite
disabledColor: AmneziaStyle.color.mutedGray
textColor: AmneziaStyle.color.paleGray
borderWidth: 1
text: qsTr("Copy")
@ -136,10 +136,10 @@ DrawerType2 {
visible: false
defaultColor: AmneziaStyle.color.transparent
hoveredColor: AmneziaStyle.color.blackHovered
pressedColor: AmneziaStyle.color.blackPressed
disabledColor: AmneziaStyle.color.grey
textColor: AmneziaStyle.color.white
hoveredColor: AmneziaStyle.color.translucentWhite
pressedColor: AmneziaStyle.color.sheerWhite
disabledColor: AmneziaStyle.color.mutedGray
textColor: AmneziaStyle.color.paleGray
borderWidth: 1
text: qsTr("Copy config string")
@ -155,10 +155,10 @@ DrawerType2 {
Layout.topMargin: 24
defaultColor: AmneziaStyle.color.transparent
hoveredColor: AmneziaStyle.color.blackHovered
pressedColor: AmneziaStyle.color.blackPressed
disabledColor: AmneziaStyle.color.grey
textColor: AmneziaStyle.color.white
hoveredColor: AmneziaStyle.color.translucentWhite
pressedColor: AmneziaStyle.color.sheerWhite
disabledColor: AmneziaStyle.color.mutedGray
textColor: AmneziaStyle.color.paleGray
borderWidth: 1
text: qsTr("Show connection settings")
@ -282,9 +282,9 @@ DrawerType2 {
readOnly: true
activeFocusOnTab: false
color: AmneziaStyle.color.white
selectionColor: AmneziaStyle.color.brown
selectedTextColor: AmneziaStyle.color.white
color: AmneziaStyle.color.paleGray
selectionColor: AmneziaStyle.color.richBrown
selectedTextColor: AmneziaStyle.color.paleGray
font.pixelSize: 16
font.weight: Font.Medium

View file

@ -16,7 +16,7 @@ Rectangle {
implicitWidth: transportProtoButtonGroup.implicitWidth
implicitHeight: transportProtoButtonGroup.implicitHeight
color: AmneziaStyle.color.blackLight
color: AmneziaStyle.color.onyxBlack
radius: 16
onFocusChanged: {

View file

@ -30,7 +30,7 @@ Item {
ImageButtonType {
id: backButton
image: backButtonImage
imageColor: AmneziaStyle.color.white
imageColor: AmneziaStyle.color.paleGray
implicitWidth: 40
implicitHeight: 40

View file

@ -10,15 +10,15 @@ import "TextTypes"
Button {
id: root
property string hoveredColor: AmneziaStyle.color.whiteHovered
property string defaultColor: AmneziaStyle.color.white
property string disabledColor: AmneziaStyle.color.greyDisabled
property string pressedColor: AmneziaStyle.color.grey
property string hoveredColor: AmneziaStyle.color.lightGray
property string defaultColor: AmneziaStyle.color.paleGray
property string disabledColor: AmneziaStyle.color.charcoalGray
property string pressedColor: AmneziaStyle.color.mutedGray
property string textColor: AmneziaStyle.color.black
property string textColor: AmneziaStyle.color.midnightBlack
property string borderColor: AmneziaStyle.color.white
property string borderFocusedColor: AmneziaStyle.color.white
property string borderColor: AmneziaStyle.color.paleGray
property string borderFocusedColor: AmneziaStyle.color.paleGray
property int borderWidth: 0
property int borderFocusedWidth: 1

View file

@ -43,7 +43,7 @@ Popup {
ShapePath {
fillColor: AmneziaStyle.color.transparent
strokeColor: AmneziaStyle.color.greyDisabled
strokeColor: AmneziaStyle.color.charcoalGray
strokeWidth: 3
capStyle: ShapePath.RoundCap

View file

@ -11,16 +11,16 @@ RadioButton {
property string bodyText
property string footerText
property string hoveredColor: AmneziaStyle.color.blackHovered
property string hoveredColor: AmneziaStyle.color.barelyTranslucentWhite
property string defaultColor: AmneziaStyle.color.transparent
property string disabledColor: AmneziaStyle.color.transparent
property string pressedColor: AmneziaStyle.color.blackPressed
property string pressedColor: AmneziaStyle.color.barelyTranslucentWhite
property string selectedColor: AmneziaStyle.color.transparent
property string textColor: AmneziaStyle.color.black
property string textColor: AmneziaStyle.color.midnightBlack
property string pressedBorderColor: Qt.rgba(251/255, 178/255, 106/255, 0.3)
property string selectedBorderColor: AmneziaStyle.color.orange
property string selectedBorderColor: AmneziaStyle.color.goldenApricot
property string defaultBodredColor: AmneziaStyle.color.transparent
property int borderWidth: 0
@ -84,7 +84,7 @@ RadioButton {
Text {
text: root.headerText
wrapMode: Text.WordWrap
color: AmneziaStyle.color.white
color: AmneziaStyle.color.paleGray
font.pixelSize: 25
font.weight: 700
font.family: "PT Root UI VF"
@ -99,7 +99,7 @@ RadioButton {
Text {
text: root.bodyText
wrapMode: Text.WordWrap
color: AmneziaStyle.color.white
color: AmneziaStyle.color.paleGray
font.pixelSize: 16
font.weight: 400
font.family: "PT Root UI VF"
@ -115,7 +115,7 @@ RadioButton {
text: root.footerText
wrapMode: Text.WordWrap
visible: root.footerText !== ""
color: AmneziaStyle.color.grey
color: AmneziaStyle.color.mutedGray
font.pixelSize: 13
font.weight: 400
font.family: "PT Root UI VF"

View file

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

View file

@ -11,26 +11,26 @@ CheckBox {
id: root
property string descriptionText
property string descriptionTextColor: AmneziaStyle.color.grey
property string descriptionTextDisabledColor: AmneziaStyle.color.greyDisabled
property string descriptionTextColor: AmneziaStyle.color.mutedGray
property string descriptionTextDisabledColor: AmneziaStyle.color.charcoalGray
property string textColor: AmneziaStyle.color.white
property string textDisabledColor: AmneziaStyle.color.grey
property string textColor: AmneziaStyle.color.paleGray
property string textDisabledColor: AmneziaStyle.color.mutedGray
property string hoveredColor: AmneziaStyle.color.blackHovered
property string hoveredColor: AmneziaStyle.color.barelyTranslucentWhite
property string defaultColor: AmneziaStyle.color.transparent
property string pressedColor: AmneziaStyle.color.blackPressed
property string pressedColor: AmneziaStyle.color.barelyTranslucentWhite
property string defaultBorderColor: AmneziaStyle.color.white
property string checkedBorderColor: AmneziaStyle.color.orange
property string checkedBorderDisabledColor: AmneziaStyle.color.brownDark
property string defaultBorderColor: AmneziaStyle.color.paleGray
property string checkedBorderColor: AmneziaStyle.color.goldenApricot
property string checkedBorderDisabledColor: AmneziaStyle.color.deepBrown
property string borderFocusedColor: AmneziaStyle.color.white
property string borderFocusedColor: AmneziaStyle.color.paleGray
property string checkedImageColor: AmneziaStyle.color.orange
property string pressedImageColor: AmneziaStyle.color.orangeDark
property string checkedImageColor: AmneziaStyle.color.goldenApricot
property string pressedImageColor: AmneziaStyle.color.burntOrange
property string defaultImageColor: AmneziaStyle.color.transparent
property string checkedDisabledImageColor: AmneziaStyle.color.brownLight
property string checkedDisabledImageColor: AmneziaStyle.color.mutedBrown
property string imageSource: "qrc:/images/controls/check.svg"

View file

@ -10,5 +10,5 @@ Rectangle {
Layout.rightMargin: 16
height: 1
color: AmneziaStyle.color.greyDark
color: AmneziaStyle.color.slateGray
}

View file

@ -21,8 +21,8 @@ Item {
property Component collapsedContent
property Component expandedContent
property string defaultColor: AmneziaStyle.color.blackLight
property string borderColor: AmneziaStyle.color.greyDark
property string defaultColor: AmneziaStyle.color.onyxBlack
property string borderColor: AmneziaStyle.color.slateGray
property real expandedHeight
property real collapsedHeight: 0

View file

@ -11,31 +11,31 @@ Item {
id: root
property string text
property string textColor: AmneziaStyle.color.white
property string textDisabledColor: AmneziaStyle.color.grey
property string textColor: AmneziaStyle.color.paleGray
property string textDisabledColor: AmneziaStyle.color.mutedGray
property int textMaximumLineCount: 2
property int textElide: Qt.ElideRight
property string descriptionText
property string descriptionTextColor: AmneziaStyle.color.grey
property string descriptionTextDisabledColor: AmneziaStyle.color.greyDisabled
property string descriptionTextColor: AmneziaStyle.color.mutedGray
property string descriptionTextDisabledColor: AmneziaStyle.color.charcoalGray
property string headerText
property string headerBackButtonImage
property var rootButtonClickedFunction
property string rootButtonImage: "qrc:/images/controls/chevron-down.svg"
property string rootButtonImageColor: AmneziaStyle.color.white
property string rootButtonBackgroundColor: AmneziaStyle.color.blackLight
property string rootButtonBackgroundHoveredColor: AmneziaStyle.color.blackLight
property string rootButtonBackgroundPressedColor: AmneziaStyle.color.blackLight
property string rootButtonImageColor: AmneziaStyle.color.paleGray
property string rootButtonBackgroundColor: AmneziaStyle.color.onyxBlack
property string rootButtonBackgroundHoveredColor: AmneziaStyle.color.onyxBlack
property string rootButtonBackgroundPressedColor: AmneziaStyle.color.onyxBlack
property string borderFocusedColor: AmneziaStyle.color.white
property string borderFocusedColor: AmneziaStyle.color.paleGray
property int borderFocusedWidth: 1
property string rootButtonHoveredBorderColor: AmneziaStyle.color.greyDisabled
property string rootButtonDefaultBorderColor: AmneziaStyle.color.greyDark
property string rootButtonPressedBorderColor: AmneziaStyle.color.white
property string rootButtonHoveredBorderColor: AmneziaStyle.color.charcoalGray
property string rootButtonDefaultBorderColor: AmneziaStyle.color.slateGray
property string rootButtonPressedBorderColor: AmneziaStyle.color.paleGray
property int rootButtonTextLeftMargins: 16
property int rootButtonTextTopMargin: 16

View file

@ -39,7 +39,7 @@ Item {
implicitHeight: 40
image: root.actionButtonImage
imageColor: AmneziaStyle.color.white
imageColor: AmneziaStyle.color.paleGray
visible: image ? true : false
@ -59,7 +59,7 @@ Item {
text: root.descriptionText
color: AmneziaStyle.color.grey
color: AmneziaStyle.color.mutedGray
visible: root.descriptionText !== ""
}

View file

@ -48,7 +48,7 @@ Item {
Layout.alignment: Qt.AlignRight
image: root.actionButtonImage
imageColor: AmneziaStyle.color.white
imageColor: AmneziaStyle.color.paleGray
visible: image ? true : false
@ -68,7 +68,7 @@ Item {
text: root.descriptionText
color: AmneziaStyle.color.grey
color: AmneziaStyle.color.mutedGray
visible: root.descriptionText !== ""
}

View file

@ -9,19 +9,19 @@ import "TextTypes"
RadioButton {
id: root
property string hoveredColor: AmneziaStyle.color.blackHovered
property string hoveredColor: AmneziaStyle.color.barelyTranslucentWhite
property string defaultColor: AmneziaStyle.color.transparent
property string checkedColor: AmneziaStyle.color.transparent
property string disabledColor: AmneziaStyle.color.transparent
property string textColor: AmneziaStyle.color.white
property string textDisabledColor: AmneziaStyle.color.grey
property string textColor: AmneziaStyle.color.paleGray
property string textDisabledColor: AmneziaStyle.color.mutedGray
property string pressedBorderColor: AmneziaStyle.color.greyDisabled
property string checkedBorderColor: AmneziaStyle.color.orange
property string pressedBorderColor: AmneziaStyle.color.charcoalGray
property string checkedBorderColor: AmneziaStyle.color.goldenApricot
property string defaultBodredColor: AmneziaStyle.color.transparent
property string checkedDisabledBorderColor: AmneziaStyle.color.brownLight
property string borderFocusedColor: AmneziaStyle.color.white
property string checkedDisabledBorderColor: AmneziaStyle.color.mutedBrown
property string borderFocusedColor: AmneziaStyle.color.paleGray
property int borderWidth: 0
implicitWidth: content.implicitWidth

View file

@ -9,18 +9,18 @@ Button {
property string image
property string hoveredColor: AmneziaStyle.color.blackHovered
property string hoveredColor: AmneziaStyle.color.translucentWhite
property string defaultColor: AmneziaStyle.color.transparent
property string pressedColor: AmneziaStyle.color.blackPressed
property string disableColor: AmneziaStyle.color.greyDark
property string pressedColor: AmneziaStyle.color.sheerWhite
property string disableColor: AmneziaStyle.color.slateGray
property string imageColor: AmneziaStyle.color.grey
property string disableImageColor: AmneziaStyle.color.greyDark
property string imageColor: AmneziaStyle.color.mutedGray
property string disableImageColor: AmneziaStyle.color.slateGray
property alias backgroundColor: background.color
property alias backgroundRadius: background.radius
property string borderFocusedColor: AmneziaStyle.color.white
property string borderFocusedColor: AmneziaStyle.color.paleGray
property int borderFocusedWidth: 1
hoverEnabled: true

View file

@ -26,16 +26,16 @@ Item {
property alias eyeButton: eyeImage
property FlickableType parentFlickable
property string textColor: AmneziaStyle.color.white
property string textDisabledColor: AmneziaStyle.color.grey
property string descriptionColor: AmneziaStyle.color.grey
property string descriptionDisabledColor: AmneziaStyle.color.greyDisabled
property string textColor: AmneziaStyle.color.paleGray
property string textDisabledColor: AmneziaStyle.color.mutedGray
property string descriptionColor: AmneziaStyle.color.mutedGray
property string descriptionDisabledColor: AmneziaStyle.color.charcoalGray
property real textOpacity: 1.0
property string borderFocusedColor: AmneziaStyle.color.white
property string borderFocusedColor: AmneziaStyle.color.paleGray
property int borderFocusedWidth: 1
property string rightImageColor: AmneziaStyle.color.white
property string rightImageColor: AmneziaStyle.color.paleGray
property bool descriptionOnTop: false
property bool hideDescription: true

View file

@ -0,0 +1,38 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import Style 1.0
import "TextTypes"
RowLayout {
property string imageSource
property string leftText
property var rightText
property bool isRightTextUndefined: rightText === undefined
visible: !isRightTextUndefined
Image {
Layout.preferredHeight: 18
Layout.preferredWidth: 18
source: imageSource
}
ListItemTitleType {
Layout.fillWidth: true
Layout.rightMargin: 10
Layout.alignment: Qt.AlignRight
text: leftText
}
ParagraphTextType {
visible: rightText !== ""
Layout.alignment: Qt.AlignLeft
text: isRightTextUndefined ? "" : rightText
}
}

View file

@ -105,8 +105,8 @@ ListView {
indicator: Rectangle {
width: parent.width - 1
height: parent.height
color: radioButton.hovered ? AmneziaStyle.color.greyDark : AmneziaStyle.color.blackLight
border.color: radioButton.focus ? AmneziaStyle.color.white : AmneziaStyle.color.transparent
color: radioButton.hovered ? AmneziaStyle.color.slateGray : AmneziaStyle.color.onyxBlack
border.color: radioButton.focus ? AmneziaStyle.color.paleGray : AmneziaStyle.color.transparent
border.width: radioButton.focus ? 1 : 0
Behavior on color {

View file

@ -84,11 +84,11 @@ Popup {
implicitHeight: 32
defaultColor: "white"
hoveredColor: AmneziaStyle.color.whiteHovered
pressedColor: AmneziaStyle.color.whiteHovered
disabledColor: AmneziaStyle.color.greyDisabled
hoveredColor: AmneziaStyle.color.lightGray
pressedColor: AmneziaStyle.color.lightGray
disabledColor: AmneziaStyle.color.charcoalGray
textColor: AmneziaStyle.color.black
textColor: AmneziaStyle.color.midnightBlack
borderWidth: 0
text: qsTr("Close")

View file

@ -10,14 +10,14 @@ ProgressBar {
implicitHeight: 4
background: Rectangle {
color: AmneziaStyle.color.brown
color: AmneziaStyle.color.richBrown
}
contentItem: Item {
Rectangle {
width: root.visualPosition * parent.width
height: parent.height
color: AmneziaStyle.color.orange
color: AmneziaStyle.color.goldenApricot
}
}
}

View file

@ -10,29 +10,29 @@ Switch {
id: root
property alias descriptionText: description.text
property string descriptionTextColor: AmneziaStyle.color.grey
property string descriptionTextDisabledColor: AmneziaStyle.color.greyDisabled
property string descriptionTextColor: AmneziaStyle.color.mutedGray
property string descriptionTextDisabledColor: AmneziaStyle.color.charcoalGray
property string textColor: AmneziaStyle.color.white
property string textDisabledColor: AmneziaStyle.color.grey
property string textColor: AmneziaStyle.color.paleGray
property string textDisabledColor: AmneziaStyle.color.mutedGray
property string checkedIndicatorColor: AmneziaStyle.color.brown
property string checkedIndicatorColor: AmneziaStyle.color.richBrown
property string defaultIndicatorColor: AmneziaStyle.color.transparent
property string checkedDisabledIndicatorColor: AmneziaStyle.color.brownDark
property string checkedDisabledIndicatorColor: AmneziaStyle.color.deepBrown
property string borderFocusedColor: AmneziaStyle.color.white
property string borderFocusedColor: AmneziaStyle.color.paleGray
property int borderFocusedWidth: 1
property string checkedIndicatorBorderColor: AmneziaStyle.color.brown
property string defaultIndicatorBorderColor: AmneziaStyle.color.greyDisabled
property string checkedDisabledIndicatorBorderColor: AmneziaStyle.color.brownDark
property string checkedIndicatorBorderColor: AmneziaStyle.color.richBrown
property string defaultIndicatorBorderColor: AmneziaStyle.color.charcoalGray
property string checkedDisabledIndicatorBorderColor: AmneziaStyle.color.deepBrown
property string checkedInnerCircleColor: AmneziaStyle.color.orange
property string defaultInnerCircleColor: AmneziaStyle.color.white
property string checkedDisabledInnerCircleColor: AmneziaStyle.color.brownLight
property string defaultDisabledInnerCircleColor: AmneziaStyle.color.greyDisabled
property string checkedInnerCircleColor: AmneziaStyle.color.goldenApricot
property string defaultInnerCircleColor: AmneziaStyle.color.paleGray
property string checkedDisabledInnerCircleColor: AmneziaStyle.color.mutedBrown
property string defaultDisabledInnerCircleColor: AmneziaStyle.color.charcoalGray
property string hoveredIndicatorBackgroundColor: AmneziaStyle.color.blackHovered
property string hoveredIndicatorBackgroundColor: AmneziaStyle.color.translucentWhite
property string defaultIndicatorBackgroundColor: AmneziaStyle.color.transparent
hoverEnabled: enabled ? true : false

View file

@ -6,13 +6,13 @@ import Style 1.0
TabButton {
id: root
property string hoveredColor: AmneziaStyle.color.brown
property string defaultColor: AmneziaStyle.color.greyDark
property string selectedColor: AmneziaStyle.color.orange
property string hoveredColor: AmneziaStyle.color.richBrown
property string defaultColor: AmneziaStyle.color.slateGray
property string selectedColor: AmneziaStyle.color.goldenApricot
property string textColor: AmneziaStyle.color.white
property string textColor: AmneziaStyle.color.paleGray
property string borderFocusedColor: AmneziaStyle.color.white
property string borderFocusedColor: AmneziaStyle.color.paleGray
property int borderFocusedWidth: 1
property bool isSelected: false

View file

@ -6,15 +6,15 @@ import Style 1.0
TabButton {
id: root
property string hoveredColor: AmneziaStyle.color.brown
property string defaultColor: AmneziaStyle.color.white
property string selectedColor: AmneziaStyle.color.orange
property string hoveredColor: AmneziaStyle.color.richBrown
property string defaultColor: AmneziaStyle.color.paleGray
property string selectedColor: AmneziaStyle.color.goldenApricot
property string image
property bool isSelected: false
property string borderFocusedColor: AmneziaStyle.color.white
property string borderFocusedColor: AmneziaStyle.color.paleGray
property int borderFocusedWidth: 1
property var clickedFunc

View file

@ -11,12 +11,12 @@ Rectangle {
property alias textArea: textArea
property alias textAreaText: textArea.text
property string borderHoveredColor: AmneziaStyle.color.greyDisabled
property string borderNormalColor: AmneziaStyle.color.greyDark
property string borderFocusedColor: AmneziaStyle.color.white
property string borderHoveredColor: AmneziaStyle.color.charcoalGray
property string borderNormalColor: AmneziaStyle.color.slateGray
property string borderFocusedColor: AmneziaStyle.color.paleGray
height: 148
color: AmneziaStyle.color.blackLight
color: AmneziaStyle.color.onyxBlack
border.width: 1
border.color: getBorderColor(borderNormalColor)
radius: 16
@ -54,10 +54,10 @@ Rectangle {
anchors.topMargin: 16
anchors.bottomMargin: 16
color: AmneziaStyle.color.white
selectionColor: AmneziaStyle.color.brown
selectedTextColor: AmneziaStyle.color.white
placeholderTextColor: AmneziaStyle.color.grey
color: AmneziaStyle.color.paleGray
selectionColor: AmneziaStyle.color.richBrown
selectedTextColor: AmneziaStyle.color.paleGray
placeholderTextColor: AmneziaStyle.color.mutedGray
font.pixelSize: 16
font.weight: Font.Medium

View file

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

View file

@ -10,8 +10,8 @@ Item {
id: root
property string headerText
property string headerTextDisabledColor: AmneziaStyle.color.greyDisabled
property string headerTextColor: AmneziaStyle.color.grey
property string headerTextDisabledColor: AmneziaStyle.color.charcoalGray
property string headerTextColor: AmneziaStyle.color.mutedGray
property alias errorText: errorField.text
property bool checkEmptyText: false
@ -23,18 +23,18 @@ Item {
property alias textField: textField
property alias textFieldText: textField.text
property string textFieldTextColor: AmneziaStyle.color.white
property string textFieldTextDisabledColor: AmneziaStyle.color.grey
property string textFieldTextColor: AmneziaStyle.color.paleGray
property string textFieldTextDisabledColor: AmneziaStyle.color.mutedGray
property string textFieldPlaceholderText
property bool textFieldEditable: true
property string borderColor: AmneziaStyle.color.greyDark
property string borderFocusedColor: AmneziaStyle.color.white
property string borderColor: AmneziaStyle.color.slateGray
property string borderFocusedColor: AmneziaStyle.color.paleGray
property string backgroundColor: AmneziaStyle.color.blackLight
property string backgroundColor: AmneziaStyle.color.onyxBlack
property string backgroundDisabledColor: AmneziaStyle.color.transparent
property string bgBorderHoveredColor: AmneziaStyle.color.greyDisabled
property string bgBorderHoveredColor: AmneziaStyle.color.charcoalGray
implicitWidth: content.implicitWidth
implicitHeight: content.implicitHeight
@ -92,10 +92,10 @@ Item {
inputMethodHints: Qt.ImhNoAutoUppercase | Qt.ImhSensitiveData | Qt.ImhNoPredictiveText
placeholderText: root.textFieldPlaceholderText
placeholderTextColor: AmneziaStyle.color.greyDisabled
placeholderTextColor: AmneziaStyle.color.charcoalGray
selectionColor: AmneziaStyle.color.brown
selectedTextColor: AmneziaStyle.color.white
selectionColor: AmneziaStyle.color.richBrown
selectedTextColor: AmneziaStyle.color.paleGray
font.pixelSize: 16
font.weight: 400
@ -149,7 +149,9 @@ Item {
text: root.errorText
visible: root.errorText !== ""
color: AmneziaStyle.color.red
color: AmneziaStyle.color.vibrantRed
Layout.fillWidth: true
}
}

View file

@ -6,7 +6,7 @@ Text {
lineHeight: 24
lineHeightMode: Text.FixedHeight
color: AmneziaStyle.color.white
color: AmneziaStyle.color.paleGray
font.pixelSize: 16
font.weight: 600
font.family: "PT Root UI VF"

View file

@ -6,7 +6,7 @@ Text {
lineHeight: 16 + LanguageModel.getLineHeightAppend()
lineHeightMode: Text.FixedHeight
color: AmneziaStyle.color.black
color: AmneziaStyle.color.midnightBlack
font.pixelSize: 13
font.weight: 400
font.family: "PT Root UI VF"

View file

@ -6,7 +6,7 @@ Text {
lineHeight: 38 + LanguageModel.getLineHeightAppend()
lineHeightMode: Text.FixedHeight
color: AmneziaStyle.color.white
color: AmneziaStyle.color.paleGray
font.pixelSize: 32
font.weight: 700
font.family: "PT Root UI VF"

View file

@ -6,7 +6,7 @@ Text {
lineHeight: 30 + LanguageModel.getLineHeightAppend()
lineHeightMode: Text.FixedHeight
color: AmneziaStyle.color.white
color: AmneziaStyle.color.paleGray
font.pixelSize: 25
font.weight: 700
font.family: "PT Root UI VF"

View file

@ -6,7 +6,7 @@ Text {
lineHeight: 16 + LanguageModel.getLineHeightAppend()
lineHeightMode: Text.FixedHeight
color: AmneziaStyle.color.grey
color: AmneziaStyle.color.mutedGray
font.pixelSize: 13
font.weight: 400
font.family: "PT Root UI VF"

View file

@ -6,7 +6,7 @@ Text {
lineHeight: 21.6 + LanguageModel.getLineHeightAppend()
lineHeightMode: Text.FixedHeight
color: AmneziaStyle.color.white
color: AmneziaStyle.color.paleGray
font.pixelSize: 18
font.weight: 400
font.family: "PT Root UI VF"

View file

@ -5,7 +5,7 @@ Text {
lineHeight: 24 + LanguageModel.getLineHeightAppend()
lineHeightMode: Text.FixedHeight
color: AmneziaStyle.color.white
color: AmneziaStyle.color.paleGray
font.pixelSize: 16
font.weight: 400
font.family: "PT Root UI VF"

View file

@ -6,7 +6,7 @@ Text {
lineHeight: 20 + LanguageModel.getLineHeightAppend()
lineHeightMode: Text.FixedHeight
color: AmneziaStyle.color.white
color: AmneziaStyle.color.paleGray
font.pixelSize: 14
font.weight: 400
font.family: "PT Root UI VF"

View file

@ -25,7 +25,7 @@ Popup {
id: button
image: "qrc:/images/svg/close_black_24dp.svg"
imageColor: AmneziaStyle.color.white
imageColor: AmneziaStyle.color.paleGray
implicitWidth: 40
implicitHeight: 40

View file

@ -14,15 +14,15 @@ RadioButton {
property int textElide: Qt.ElideRight
property string descriptionText
property string hoveredColor: AmneziaStyle.color.blackHovered
property string hoveredColor: AmneziaStyle.color.barelyTranslucentWhite
property string defaultColor: AmneziaStyle.color.transparent
property string disabledColor: AmneziaStyle.color.transparent
property string selectedColor: AmneziaStyle.color.transparent
property string textColor: AmneziaStyle.color.white
property string selectedTextColor: AmneziaStyle.color.orange
property string textColor: AmneziaStyle.color.paleGray
property string selectedTextColor: AmneziaStyle.color.goldenApricot
property string borderFocusedColor: AmneziaStyle.color.white
property string borderFocusedColor: AmneziaStyle.color.paleGray
property int borderFocusedWidth: 1
property string imageSource
@ -139,7 +139,7 @@ RadioButton {
CaptionTextType {
id: description
color: AmneziaStyle.color.grey
color: AmneziaStyle.color.mutedGray
text: root.descriptionText
visible: root.descriptionText !== ""

View file

@ -10,9 +10,9 @@ import "TextTypes"
Rectangle {
id: root
property string textColor: AmneziaStyle.color.white
property string backGroundColor: AmneziaStyle.color.blackLight
property string imageColor: AmneziaStyle.color.white
property string textColor: AmneziaStyle.color.paleGray
property string backGroundColor: AmneziaStyle.color.onyxBlack
property string imageColor: AmneziaStyle.color.paleGray
property string textString
property int textFormat: Text.PlainText

View file

@ -5,22 +5,22 @@ import QtQuick
QtObject {
property QtObject color: QtObject {
readonly property color transparent: 'transparent'
readonly property color white: '#D7D8DB'
readonly property color whiteHovered: '#C1C2C5'
readonly property color grey: '#878B91'
readonly property color greyDisabled: '#494B50'
readonly property color greyDark: '#2C2D30'
readonly property color blackLight: '#1C1D21'
readonly property color blackHovered: '#01010114'
readonly property color blackPressed: '#0101011f'
readonly property color black: '#0E0E11'
readonly property color orange: '#FBB26A'
readonly property color orangeDark: '#A85809'
readonly property color brownLight: '#84603D'
readonly property color brown: '#633303'
readonly property color brownDark: '#402102'
readonly property color red: '#EB5757'
readonly property color connectionInProgress: '#261E1A'
readonly property color paleGray: '#D7D8DB'
readonly property color lightGray: '#C1C2C5'
readonly property color mutedGray: '#878B91'
readonly property color charcoalGray: '#494B50'
readonly property color slateGray: '#2C2D30'
readonly property color onyxBlack: '#1C1D21'
readonly property color midnightBlack: '#0E0E11'
readonly property color goldenApricot: '#FBB26A'
readonly property color burntOrange: '#A85809'
readonly property color mutedBrown: '#84603D'
readonly property color richBrown: '#633303'
readonly property color deepBrown: '#402102'
readonly property color vibrantRed: '#EB5757'
readonly property color darkCharcoal: '#261E1A'
readonly property color sheerWhite: Qt.rgba(1, 1, 1, 0.12)
readonly property color translucentWhite: Qt.rgba(1, 1, 1, 0.08)
readonly property color barelyTranslucentWhite: Qt.rgba(1, 1, 1, 0.05)
}
}
}

View file

@ -5,6 +5,7 @@ import QtQuick.Layouts
import SortFilterProxyModel 0.2
import PageEnum 1.0
import Style 1.0
import "./"
import "../Controls2"

View file

@ -0,0 +1,94 @@
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
defaultActiveFocusItem: focusItem
Item {
id: focusItem
KeyNavigation.tab: backButton
}
ColumnLayout {
id: backButtonLayout
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
anchors.topMargin: 20
BackButtonType {
id: backButton
// KeyNavigation.tab: removeButton
}
}
FlickableType {
id: fl
anchors.top: backButtonLayout.bottom
anchors.bottom: parent.bottom
contentHeight: content.implicitHeight
ColumnLayout {
id: content
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
HeaderType {
id: header
Layout.fillWidth: true
Layout.rightMargin: 16
Layout.leftMargin: 16
headerText: "Dev menu"
}
TextFieldWithHeaderType {
id: passwordTextField
Layout.fillWidth: true
Layout.topMargin: 16
Layout.rightMargin: 16
Layout.leftMargin: 16
parentFlickable: fl
headerText: qsTr("Gateway endpoint")
textFieldText: SettingsController.gatewayEndpoint
buttonImageSource: textFieldText !== "" ? "qrc:/images/controls/refresh-cw.svg" : ""
clickedFunc: function() {
SettingsController.resetGatewayEndpoint()
}
textField.onEditingFinished: {
textFieldText = textField.text.replace(/^\s+|\s+$/g, '')
if (textFieldText !== SettingsController.gatewayEndpoint) {
SettingsController.gatewayEndpoint = textFieldText
}
}
// KeyNavigation.tab: saveButton
}
}
}
}

View file

@ -57,10 +57,10 @@ PageType {
implicitHeight: 36
defaultColor: AmneziaStyle.color.transparent
hoveredColor: AmneziaStyle.color.blackHovered
pressedColor: AmneziaStyle.color.blackPressed
disabledColor: AmneziaStyle.color.grey
textColor: AmneziaStyle.color.grey
hoveredColor: AmneziaStyle.color.translucentWhite
pressedColor: AmneziaStyle.color.sheerWhite
disabledColor: AmneziaStyle.color.mutedGray
textColor: AmneziaStyle.color.mutedGray
borderWidth: 0
visible: isLoggingEnabled ? true : false
@ -94,10 +94,10 @@ PageType {
implicitHeight: 36
defaultColor: AmneziaStyle.color.transparent
hoveredColor: AmneziaStyle.color.blackHovered
pressedColor: AmneziaStyle.color.blackPressed
disabledColor: AmneziaStyle.color.grey
textColor: AmneziaStyle.color.grey
hoveredColor: AmneziaStyle.color.translucentWhite
pressedColor: AmneziaStyle.color.sheerWhite
disabledColor: AmneziaStyle.color.mutedGray
textColor: AmneziaStyle.color.mutedGray
leftImageColor: AmneziaStyle.color.transparent
borderWidth: 0
@ -106,7 +106,7 @@ PageType {
buttonTextLabel.font.weight: 500
property bool isSplitTunnelingEnabled: SitesModel.isTunnelingEnabled || AppSplitTunnelingModel.isTunnelingEnabled ||
(ServersModel.isDefaultServerDefaultContainerHasSplitTunneling && ServersModel.getDefaultServerData("isServerFromApi"))
ServersModel.isDefaultServerDefaultContainerHasSplitTunneling
text: isSplitTunnelingEnabled ? qsTr("Split tunneling enabled") : qsTr("Split tunneling disabled")
@ -243,7 +243,7 @@ PageType {
hoverEnabled: false
image: "qrc:/images/controls/chevron-down.svg"
imageColor: AmneziaStyle.color.white
imageColor: AmneziaStyle.color.paleGray
icon.width: 18
icon.height: 18
@ -265,11 +265,21 @@ PageType {
}
}
LabelTextType {
id: collapsedServerMenuDescription
Layout.bottomMargin: drawer.isCollapsed ? 44 : ServersModel.isDefaultServerFromApi ? 89 : 44
RowLayout {
Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter
text: drawer.isCollapsed ? ServersModel.defaultServerDescriptionCollapsed : ServersModel.defaultServerDescriptionExpanded
Layout.bottomMargin: drawer.isCollapsed ? 44 : ServersModel.isDefaultServerFromApi ? 89 : 44
spacing: 0
Image {
Layout.rightMargin: 8
visible: source !== ""
source: ServersModel.defaultServerImagePathCollapsed
}
LabelTextType {
id: collapsedServerMenuDescription
text: drawer.isCollapsed ? ServersModel.defaultServerDescriptionCollapsed : ServersModel.defaultServerDescriptionExpanded
}
}
}
@ -304,8 +314,8 @@ PageType {
DropDownType {
id: containersDropDown
rootButtonImageColor: AmneziaStyle.color.black
rootButtonBackgroundColor: AmneziaStyle.color.white
rootButtonImageColor: AmneziaStyle.color.midnightBlack
rootButtonBackgroundColor: AmneziaStyle.color.paleGray
rootButtonBackgroundHoveredColor: Qt.rgba(215, 216, 219, 0.8)
rootButtonBackgroundPressedColor: Qt.rgba(215, 216, 219, 0.65)
rootButtonHoveredBorderColor: AmneziaStyle.color.transparent
@ -314,7 +324,7 @@ PageType {
rootButtonTextBottomMargin: 8
text: ServersModel.defaultServerDefaultContainerName
textColor: AmneziaStyle.color.black
textColor: AmneziaStyle.color.midnightBlack
headerText: qsTr("VPN protocol")
headerBackButtonImage: "qrc:/images/controls/arrow-left.svg"
@ -505,7 +515,7 @@ PageType {
ImageButtonType {
id: serverInfoButton
image: "qrc:/images/controls/settings.svg"
imageColor: AmneziaStyle.color.white
imageColor: AmneziaStyle.color.paleGray
implicitWidth: 56
implicitHeight: 56

View file

@ -5,6 +5,7 @@ import QtQuick.Layouts
import SortFilterProxyModel 0.2
import PageEnum 1.0
import Style 1.0
import "./"
import "../Controls2"

View file

@ -5,6 +5,7 @@ import QtQuick.Layouts
import SortFilterProxyModel 0.2
import PageEnum 1.0
import Style 1.0
import "./"
import "../Controls2"

View file

@ -295,7 +295,7 @@ PageType {
Layout.fillWidth: true
Layout.topMargin: 32
Layout.preferredHeight: checkboxLayout.implicitHeight
color: AmneziaStyle.color.blackLight
color: AmneziaStyle.color.onyxBlack
radius: 16
Connections {

View file

@ -192,9 +192,9 @@ PageType {
leftPadding: 0
height: 24
color: AmneziaStyle.color.white
selectionColor: AmneziaStyle.color.brown
selectedTextColor: AmneziaStyle.color.white
color: AmneziaStyle.color.paleGray
selectionColor: AmneziaStyle.color.richBrown
selectedTextColor: AmneziaStyle.color.paleGray
font.pixelSize: 16
font.weight: Font.Medium
@ -224,7 +224,7 @@ PageType {
visible: ServersModel.isProcessedServerHasWriteAccess()
text: qsTr("Remove ") + ContainersModel.getProcessedContainerName()
textColor: AmneziaStyle.color.red
textColor: AmneziaStyle.color.vibrantRed
Keys.onTabPressed: lastItemTabClicked(focusItem)
clickedFunction: function() {

View file

@ -5,6 +5,7 @@ import QtQuick.Layouts
import SortFilterProxyModel 0.2
import PageEnum 1.0
import Style 1.0
import "./"
import "../Controls2"

View file

@ -5,6 +5,7 @@ import QtQuick.Layouts
import SortFilterProxyModel 0.2
import PageEnum 1.0
import Style 1.0
import "./"
import "../Controls2"

View file

@ -6,6 +6,7 @@ import SortFilterProxyModel 0.2
import PageEnum 1.0
import ContainerEnum 1.0
import Style 1.0
import "./"
import "../Controls2"

View file

@ -70,7 +70,7 @@ PageType {
width: parent.width
text: qsTr("Remove ") + ContainersModel.getProcessedContainerName()
textColor: AmneziaStyle.color.red
textColor: AmneziaStyle.color.vibrantRed
Keys.onTabPressed: root.lastItemTabClicked()

View file

@ -115,7 +115,7 @@ PageType {
descriptionOnTop: true
rightImageSource: "qrc:/images/controls/copy.svg"
rightImageColor: AmneziaStyle.color.white
rightImageColor: AmneziaStyle.color.paleGray
clickedFunction: function() {
GC.copyToClipBoard(descriptionText)
@ -139,7 +139,7 @@ PageType {
KeyNavigation.tab: usernameLabel.rightButton
rightImageSource: "qrc:/images/controls/copy.svg"
rightImageColor: AmneziaStyle.color.white
rightImageColor: AmneziaStyle.color.paleGray
clickedFunction: function() {
GC.copyToClipBoard(descriptionText)
@ -163,7 +163,7 @@ PageType {
KeyNavigation.tab: passwordLabel.eyeButton
rightImageSource: "qrc:/images/controls/copy.svg"
rightImageColor: AmneziaStyle.color.white
rightImageColor: AmneziaStyle.color.paleGray
clickedFunction: function() {
GC.copyToClipBoard(descriptionText)
@ -194,7 +194,7 @@ PageType {
}
rightImageSource: "qrc:/images/controls/copy.svg"
rightImageColor: AmneziaStyle.color.white
rightImageColor: AmneziaStyle.color.paleGray
buttonImageSource: hideDescription ? "qrc:/images/controls/eye.svg" : "qrc:/images/controls/eye-off.svg"
@ -218,10 +218,10 @@ PageType {
Layout.rightMargin: 16
defaultColor: AmneziaStyle.color.transparent
hoveredColor: AmneziaStyle.color.blackHovered
pressedColor: AmneziaStyle.color.blackPressed
disabledColor: AmneziaStyle.color.grey
textColor: AmneziaStyle.color.white
hoveredColor: AmneziaStyle.color.translucentWhite
pressedColor: AmneziaStyle.color.sheerWhite
disabledColor: AmneziaStyle.color.mutedGray
textColor: AmneziaStyle.color.paleGray
borderWidth: 1
parentFlickable: fl
@ -282,10 +282,10 @@ PageType {
implicitHeight: 32
defaultColor: AmneziaStyle.color.transparent
hoveredColor: AmneziaStyle.color.blackHovered
pressedColor: AmneziaStyle.color.blackPressed
disabledColor: AmneziaStyle.color.grey
textColor: AmneziaStyle.color.orange
hoveredColor: AmneziaStyle.color.translucentWhite
pressedColor: AmneziaStyle.color.sheerWhite
disabledColor: AmneziaStyle.color.mutedGray
textColor: AmneziaStyle.color.goldenApricot
text: qsTr("Detailed instructions")

View file

@ -6,6 +6,7 @@ import SortFilterProxyModel 0.2
import PageEnum 1.0
import ContainerProps 1.0
import Style 1.0
import "./"
import "../Controls2"
@ -106,7 +107,7 @@ PageType {
descriptionOnTop: true
rightImageSource: "qrc:/images/controls/copy.svg"
rightImageColor: "#D7D8DB"
rightImageColor: AmneziaStyle.color.paleGray
clickedFunction: function() {
GC.copyToClipBoard(descriptionText)
@ -130,7 +131,7 @@ PageType {
KeyNavigation.tab: usernameLabel.rightButton
rightImageSource: "qrc:/images/controls/copy.svg"
rightImageColor: "#D7D8DB"
rightImageColor: AmneziaStyle.color.paleGray
clickedFunction: function() {
GC.copyToClipBoard(descriptionText)
@ -154,7 +155,7 @@ PageType {
KeyNavigation.tab: passwordLabel.eyeButton
rightImageSource: "qrc:/images/controls/copy.svg"
rightImageColor: "#D7D8DB"
rightImageColor: AmneziaStyle.color.paleGray
clickedFunction: function() {
GC.copyToClipBoard(descriptionText)
@ -179,7 +180,7 @@ PageType {
rightButton.KeyNavigation.tab: changeSettingsButton
rightImageSource: "qrc:/images/controls/copy.svg"
rightImageColor: "#D7D8DB"
rightImageColor: AmneziaStyle.color.paleGray
buttonImageSource: hideDescription ? "qrc:/images/controls/eye.svg" : "qrc:/images/controls/eye-off.svg"

View file

@ -83,10 +83,10 @@ PageType {
}
descriptionOnTop: true
textColor: AmneziaStyle.color.orange
textColor: AmneziaStyle.color.goldenApricot
rightImageSource: "qrc:/images/controls/copy.svg"
rightImageColor: AmneziaStyle.color.white
rightImageColor: AmneziaStyle.color.paleGray
Keys.onTabPressed: lastItemTabClicked(focusItem)

View file

@ -4,6 +4,7 @@ import QtQuick.Layouts
import QtQuick.Dialogs
import PageEnum 1.0
import Style 1.0
import "./"
import "../Controls2"
@ -128,6 +129,26 @@ PageType {
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"
// Keys.onTabPressed: lastItemTabClicked(header)
clickedFunction: function() {
PageController.goToPage(PageEnum.PageDevMenu)
}
}
DividerType {
visible: SettingsController.isDevModeEnabled
}
LabelWithButtonType {
id: close
visible: GC.isDesktop()

View file

@ -85,7 +85,7 @@ PageType {
font.pixelSize: 14
text: qsTr("Amnezia is a free and open-source application. You can support the developers if you like it.")
color: AmneziaStyle.color.white
color: AmneziaStyle.color.paleGray
}
ParagraphTextType {
@ -163,7 +163,7 @@ PageType {
parentFlickable: fl
clickedFunction: function() {
Qt.openUrlExternally(qsTr("https://amnezia.org"))
Qt.openUrlExternally(LanguageModel.getCurrentSiteUrl())
}
}
@ -177,7 +177,19 @@ PageType {
horizontalAlignment: Text.AlignHCenter
text: qsTr("Software version: %1").arg(SettingsController.getAppVersion())
color: AmneziaStyle.color.grey
color: AmneziaStyle.color.mutedGray
MouseArea {
property int clickCount: 0
anchors.fill: parent
onClicked: {
if (clickCount > 10) {
SettingsController.enableDevMode()
} else {
clickCount++
}
}
}
}
BasicButtonType {
@ -188,10 +200,10 @@ PageType {
implicitHeight: 32
defaultColor: AmneziaStyle.color.transparent
hoveredColor: AmneziaStyle.color.blackHovered
pressedColor: AmneziaStyle.color.blackPressed
disabledColor: AmneziaStyle.color.grey
textColor: AmneziaStyle.color.orange
hoveredColor: AmneziaStyle.color.translucentWhite
pressedColor: AmneziaStyle.color.sheerWhite
disabledColor: AmneziaStyle.color.mutedGray
textColor: AmneziaStyle.color.goldenApricot
text: qsTr("Check for updates")
@ -211,10 +223,10 @@ PageType {
implicitHeight: 25
defaultColor: AmneziaStyle.color.transparent
hoveredColor: AmneziaStyle.color.blackHovered
pressedColor: AmneziaStyle.color.blackPressed
disabledColor: AmneziaStyle.color.grey
textColor: AmneziaStyle.color.orange
hoveredColor: AmneziaStyle.color.translucentWhite
pressedColor: AmneziaStyle.color.sheerWhite
disabledColor: AmneziaStyle.color.mutedGray
textColor: AmneziaStyle.color.goldenApricot
text: qsTr("Privacy Policy")
@ -222,7 +234,7 @@ PageType {
parentFlickable: fl
clickedFunc: function() {
Qt.openUrlExternally("https://amnezia.org/en/policy")
Qt.openUrlExternally(LanguageModel.getCurrentSiteUrl() + "/policy")
}
}
}

View file

@ -0,0 +1,103 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import QtQuick.Dialogs
import PageEnum 1.0
import Style 1.0
import "./"
import "../Controls2"
import "../Controls2/TextTypes"
import "../Config"
import "../Components"
PageType {
id: root
ListView {
id: menuContent
property var selectedText
width: parent.width
height: menuContent.contentItem.height
clip: true
interactive: false
model: ApiCountryModel
ButtonGroup {
id: containersRadioButtonGroup
}
delegate: Item {
implicitWidth: parent.width
implicitHeight: content.implicitHeight
ColumnLayout {
id: content
anchors.fill: parent
RowLayout {
VerticalRadioButton {
id: containerRadioButton
Layout.fillWidth: true
Layout.leftMargin: 16
text: countryName
ButtonGroup.group: containersRadioButtonGroup
imageSource: "qrc:/images/controls/download.svg"
checked: index === ApiCountryModel.currentIndex
onClicked: {
if (index !== ApiCountryModel.currentIndex) {
PageController.showBusyIndicator(true)
var prevIndex = ApiCountryModel.currentIndex
ApiCountryModel.currentIndex = index
if (!InstallController.updateServiceFromApi(ServersModel.defaultIndex, countryCode, countryName)) {
ApiCountryModel.currentIndex = prevIndex
}
}
}
MouseArea {
anchors.fill: containerRadioButton
cursorShape: Qt.PointingHandCursor
enabled: false
}
Keys.onEnterPressed: {
if (checkable) {
checked = true
}
containerRadioButton.clicked()
}
Keys.onReturnPressed: {
if (checkable) {
checked = true
}
containerRadioButton.clicked()
}
}
Image {
Layout.rightMargin: 32
Layout.alignment: Qt.AlignRight
source: "qrc:/countriesFlags/images/flagKit/" + countryCode + ".svg"
}
}
DividerType {
Layout.fillWidth: true
}
}
}
}
}

View file

@ -0,0 +1,208 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import QtQuick.Dialogs
import PageEnum 1.0
import Style 1.0
import "./"
import "../Controls2"
import "../Controls2/TextTypes"
import "../Config"
import "../Components"
PageType {
id: root
defaultActiveFocusItem: focusItem
FlickableType {
id: fl
anchors.top: parent.top
anchors.bottom: parent.bottom
contentHeight: content.height
ColumnLayout {
id: content
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
spacing: 0
Item {
id: focusItem
// KeyNavigation.tab: backButton
}
LabelWithImageType {
Layout.fillWidth: true
Layout.margins: 16
imageSource: "qrc:/images/controls/map-pin.svg"
leftText: qsTr("For the region")
rightText: ApiServicesModel.getSelectedServiceData("region")
}
LabelWithImageType {
Layout.fillWidth: true
Layout.margins: 16
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("workPeriod")
visible: rightText !== ""
}
LabelWithImageType {
Layout.fillWidth: true
Layout.margins: 16
imageSource: "qrc:/images/controls/gauge.svg"
leftText: qsTr("Speed")
rightText: ApiServicesModel.getSelectedServiceData("speed")
}
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())
}
MouseArea {
anchors.fill: parent
acceptedButtons: Qt.NoButton
cursorShape: parent.hoveredLink ? Qt.PointingHandCursor : Qt.ArrowCursor
}
}
LabelWithButtonType {
id: supportUuid
Layout.fillWidth: true
text: qsTr("Support tag")
descriptionText: SettingsController.getInstallationUuid()
descriptionOnTop: true
// parentFlickable: fl
// KeyNavigation.tab: passwordLabel.eyeButton
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()
}
}
}
BasicButtonType {
id: resetButton
Layout.alignment: Qt.AlignHCenter
Layout.topMargin: 24
Layout.bottomMargin: 16
Layout.leftMargin: 8
implicitHeight: 32
defaultColor: "transparent"
hoveredColor: Qt.rgba(1, 1, 1, 0.08)
pressedColor: Qt.rgba(1, 1, 1, 0.12)
textColor: AmneziaStyle.color.vibrantRed
text: qsTr("Reload API config")
// Keys.onTabPressed: lastItemTabClicked(focusItem)
clickedFunc: function() {
var headerText = qsTr("Reload API config?")
var yesButtonText = qsTr("Continue")
var noButtonText = qsTr("Cancel")
var yesButtonFunction = function() {
if (ServersModel.isDefaultServerCurrentlyProcessed() && ConnectionController.isConnected) {
PageController.showNotificationMessage(qsTr("Cannot reload API config during active connection"))
} else {
PageController.showBusyIndicator(true)
InstallController.updateServiceFromApi(ServersModel.processedIndex, "", "", true)
PageController.showBusyIndicator(false)
}
}
var noButtonFunction = function() {
if (!GC.isMobile()) {
removeButton.forceActiveFocus()
}
}
showQuestionDrawer(headerText, "", yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
}
}
BasicButtonType {
id: removeButton
Layout.alignment: Qt.AlignHCenter
Layout.bottomMargin: 16
Layout.leftMargin: 8
implicitHeight: 32
defaultColor: "transparent"
hoveredColor: Qt.rgba(1, 1, 1, 0.08)
pressedColor: Qt.rgba(1, 1, 1, 0.12)
textColor: AmneziaStyle.color.vibrantRed
text: qsTr("Remove from application")
// Keys.onTabPressed: lastItemTabClicked(focusItem)
clickedFunc: function() {
var headerText = qsTr("Remove from application?")
var yesButtonText = qsTr("Continue")
var noButtonText = qsTr("Cancel")
var yesButtonFunction = function() {
if (ServersModel.isDefaultServerCurrentlyProcessed() && ConnectionController.isConnected) {
PageController.showNotificationMessage(qsTr("Cannot remove server during active connection"))
} else {
PageController.showBusyIndicator(true)
InstallController.removeProcessedServer()
PageController.showBusyIndicator(false)
}
}
var noButtonFunction = function() {
if (!GC.isMobile()) {
removeButton.forceActiveFocus()
}
}
showQuestionDrawer(headerText, "", yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
}
}
}
}
}

View file

@ -215,7 +215,7 @@ PageType {
text: appPath
rightImageSource: "qrc:/images/controls/trash.svg"
rightImageColor: AmneziaStyle.color.white
rightImageColor: AmneziaStyle.color.paleGray
clickedFunction: function() {
var headerText = qsTr("Remove ") + appPath + "?"
@ -242,7 +242,7 @@ PageType {
Rectangle {
anchors.fill: addAppButton
anchors.bottomMargin: -24
color: AmneziaStyle.color.black
color: AmneziaStyle.color.midnightBlack
opacity: 0.8
}

View file

@ -224,7 +224,7 @@ PageType {
text: qsTr("Reset settings and remove all data from the application")
rightImageSource: "qrc:/images/controls/chevron-right.svg"
textColor: AmneziaStyle.color.red
textColor: AmneziaStyle.color.vibrantRed
Keys.onTabPressed: lastItemTabClicked()
parentFlickable: fl
@ -241,7 +241,7 @@ PageType {
} else
{
SettingsController.clearSettings()
PageController.replaceStartPage()
PageController.goToPageHome()
}
if (!GC.isMobile()) {

View file

@ -28,7 +28,6 @@ PageType {
function onRestoreBackupFinished() {
PageController.showNotificationMessage(qsTr("Settings restored from backup file"))
//goToStartPage()
PageController.goToPageHome()
}
@ -122,10 +121,10 @@ PageType {
Layout.topMargin: -8
defaultColor: AmneziaStyle.color.transparent
hoveredColor: AmneziaStyle.color.blackHovered
pressedColor: AmneziaStyle.color.blackPressed
disabledColor: AmneziaStyle.color.grey
textColor: AmneziaStyle.color.white
hoveredColor: AmneziaStyle.color.translucentWhite
pressedColor: AmneziaStyle.color.sheerWhite
disabledColor: AmneziaStyle.color.mutedGray
textColor: AmneziaStyle.color.paleGray
borderWidth: 1
text: qsTr("Restore from backup")

View file

@ -3,6 +3,7 @@ import QtQuick.Controls
import QtQuick.Layouts
import PageEnum 1.0
import Style 1.0
import "./"
import "../Controls2"

View file

@ -38,7 +38,7 @@ PageType {
anchors.bottom: parent.bottom
contentHeight: content.height
property var isServerFromApi: ServersModel.getDefaultServerData("isServerFromApi")
property var isServerFromApi: ServersModel.isServerFromApi(ServersModel.defaultIndex)
enabled: !isServerFromApi
@ -103,10 +103,10 @@ PageType {
Layout.fillWidth: true
defaultColor: AmneziaStyle.color.transparent
hoveredColor: AmneziaStyle.color.blackHovered
pressedColor: AmneziaStyle.color.blackPressed
disabledColor: AmneziaStyle.color.grey
textColor: AmneziaStyle.color.white
hoveredColor: AmneziaStyle.color.translucentWhite
pressedColor: AmneziaStyle.color.sheerWhite
disabledColor: AmneziaStyle.color.mutedGray
textColor: AmneziaStyle.color.paleGray
borderWidth: 1
text: qsTr("Restore default")

View file

@ -115,7 +115,7 @@ disabled after 14 days, and all log files will be deleted.")
Layout.fillWidth: true
text: qsTr("Open folder with logs")
color: AmneziaStyle.color.white
color: AmneziaStyle.color.paleGray
}
}
@ -160,7 +160,7 @@ disabled after 14 days, and all log files will be deleted.")
Layout.fillWidth: true
text: qsTr("Save logs to file")
color: AmneziaStyle.color.white
color: AmneziaStyle.color.paleGray
}
}
@ -209,7 +209,7 @@ disabled after 14 days, and all log files will be deleted.")
Layout.fillWidth: true
text: qsTr("Clear logs")
color: AmneziaStyle.color.white
color: AmneziaStyle.color.paleGray
}
}
}

View file

@ -38,7 +38,7 @@ PageType {
function onRemoveProcessedServerFinished(finishedMessage) {
if (!ServersModel.getServersCount()) {
PageController.replaceStartPage()
PageController.goToPageHome()
} else {
PageController.goToStartPage()
PageController.goToPage(PageEnum.PageSettingsServersList)
@ -119,7 +119,7 @@ PageType {
Layout.fillWidth: true
text: qsTr("Reboot server")
textColor: AmneziaStyle.color.red
textColor: AmneziaStyle.color.vibrantRed
KeyNavigation.tab: labelWithButton3
@ -160,7 +160,7 @@ PageType {
Layout.fillWidth: true
text: qsTr("Remove server from application")
textColor: AmneziaStyle.color.red
textColor: AmneziaStyle.color.vibrantRed
Keys.onTabPressed: {
if (content.isServerWithWriteAccess) {
@ -208,7 +208,7 @@ PageType {
Layout.fillWidth: true
text: qsTr("Clear server from Amnezia software")
textColor: AmneziaStyle.color.red
textColor: AmneziaStyle.color.vibrantRed
Keys.onTabPressed: labelWithButton5.visible ?
labelWithButton5.forceActiveFocus() :
@ -247,11 +247,11 @@ PageType {
LabelWithButtonType {
id: labelWithButton5
visible: ServersModel.getProcessedServerData("isServerFromApi")
visible: ServersModel.getProcessedServerData("isServerFromTelegramApi")
Layout.fillWidth: true
text: qsTr("Reset API config")
textColor: AmneziaStyle.color.red
textColor: AmneziaStyle.color.vibrantRed
Keys.onTabPressed: root.lastItemTabClickedSignal()
@ -285,7 +285,7 @@ PageType {
}
DividerType {
visible: ServersModel.getProcessedServerData("isServerFromApi")
visible: ServersModel.getProcessedServerData("isServerFromTelegramApi")
}
}
}

View file

@ -19,13 +19,19 @@ import "../Components"
PageType {
id: root
property int pageSettingsServerProtocols: 0
property int pageSettingsServerServices: 1
property int pageSettingsServerData: 2
property int pageSettingsApiServerInfo: 3
property int pageSettingsApiLanguageList: 4
defaultActiveFocusItem: focusItem
Connections {
target: PageController
function onGoToPageSettingsServerServices() {
tabBar.currentIndex = 1
tabBar.currentIndex = root.pageSettingsServerServices
}
}
@ -70,6 +76,15 @@ PageType {
BackButtonType {
id: backButton
KeyNavigation.tab: headerContent.actionButton
backButtonFunction: function() {
if (nestedStackView.currentIndex === root.pageSettingsApiServerInfo &&
ServersModel.getProcessedServerData("isCountrySelectionAvailable")) {
nestedStackView.currentIndex = root.pageSettingsApiLanguageList
} else {
PageController.closePage()
}
}
}
HeaderType {
@ -78,11 +93,15 @@ PageType {
Layout.leftMargin: 16
Layout.rightMargin: 16
actionButtonImage: "qrc:/images/controls/edit-3.svg"
actionButtonImage: nestedStackView.currentIndex === root.pageSettingsApiLanguageList ? "qrc:/images/controls/settings.svg" : "qrc:/images/controls/edit-3.svg"
headerText: name
descriptionText: {
if (ServersModel.isProcessedServerHasWriteAccess()) {
if (ServersModel.getProcessedServerData("isServerFromGatewayApi")) {
return ApiServicesModel.getSelectedServiceData("serviceDescription")
} else if (ServersModel.getProcessedServerData("isServerFromTelegramApi")) {
return serverDescription
} else if (ServersModel.isProcessedServerHasWriteAccess()) {
return credentialsLogin + " · " + hostName
} else {
return hostName
@ -92,7 +111,11 @@ PageType {
KeyNavigation.tab: tabBar
actionButtonFunction: function() {
serverNameEditDrawer.open()
if (nestedStackView.currentIndex === root.pageSettingsApiLanguageList) {
nestedStackView.currentIndex = root.pageSettingsApiServerInfo
} else {
serverNameEditDrawer.open()
}
}
}
@ -172,13 +195,16 @@ PageType {
Layout.fillWidth: true
currentIndex: (ServersModel.getProcessedServerData("isServerFromApi")
&& !ServersModel.getProcessedServerData("hasInstalledContainers")) ? 2 : 0
currentIndex: (ServersModel.getProcessedServerData("isServerFromTelegramApi")
&& !ServersModel.getProcessedServerData("hasInstalledContainers")) ?
root.pageSettingsServerData : root.pageSettingsServerProtocols
background: Rectangle {
color: AmneziaStyle.color.transparent
}
visible: !ServersModel.getProcessedServerData("isServerFromGatewayApi")
activeFocusOnTab: true
onFocusChanged: {
if (activeFocus) {
@ -190,45 +216,53 @@ PageType {
id: protocolsTab
visible: protocolsPage.installedProtocolsCount
width: protocolsPage.installedProtocolsCount ? undefined : 0
isSelected: tabBar.currentIndex === 0
isSelected: tabBar.currentIndex === root.pageSettingsServerProtocols
text: qsTr("Protocols")
KeyNavigation.tab: servicesTab
Keys.onReturnPressed: tabBar.currentIndex = 0
Keys.onEnterPressed: tabBar.currentIndex = 0
Keys.onReturnPressed: tabBar.currentIndex = root.pageSettingsServerProtocols
Keys.onEnterPressed: tabBar.currentIndex = root.pageSettingsServerProtocols
}
TabButtonType {
id: servicesTab
visible: servicesPage.installedServicesCount
width: servicesPage.installedServicesCount ? undefined : 0
isSelected: tabBar.currentIndex === 1
isSelected: tabBar.currentIndex === root.pageSettingsServerServices
text: qsTr("Services")
KeyNavigation.tab: dataTab
Keys.onReturnPressed: tabBar.currentIndex = 1
Keys.onEnterPressed: tabBar.currentIndex = 1
Keys.onReturnPressed: tabBar.currentIndex = root.pageSettingsServerServices
Keys.onEnterPressed: tabBar.currentIndex = root.pageSettingsServerServices
}
TabButtonType {
id: dataTab
isSelected: tabBar.currentIndex === 2
isSelected: tabBar.currentIndex === root.pageSettingsServerData
text: qsTr("Management")
Keys.onReturnPressed: tabBar.currentIndex = 2
Keys.onEnterPressed: tabBar.currentIndex = 2
KeyNavigation.tab: stackView.currentIndex === 0 ?
protocolsPage :
stackView.currentIndex === 1 ?
servicesPage :
dataPage
Keys.onReturnPressed: tabBar.currentIndex = root.pageSettingsServerData
Keys.onEnterPressed: tabBar.currentIndex = root.pageSettingsServerData
Keys.onTabPressed: function() {
if (nestedStackView.currentIndex === root.pageSettingsServerProtocols) {
return protocolsPage
} else if (nestedStackView.currentIndex === root.pageSettingsServerProtocols) {
return servicesPage
} else {
return dataPage
}
}
}
}
StackLayout {
id: stackView
id: nestedStackView
Layout.preferredWidth: root.width
Layout.preferredHeight: root.height - tabBar.implicitHeight - header.implicitHeight
currentIndex: tabBar.currentIndex
currentIndex: ServersModel.getProcessedServerData("isServerFromGatewayApi") ?
(ServersModel.getProcessedServerData("isCountrySelectionAvailable") ?
root.pageSettingsApiLanguageList : root.pageSettingsApiServerInfo) : tabBar.currentIndex
PageSettingsServerProtocols {
id: protocolsPage
@ -236,18 +270,34 @@ PageType {
onLastItemTabClickedSignal: lastItemTabClicked(focusItem)
}
PageSettingsServerServices {
id: servicesPage
stackView: root.stackView
onLastItemTabClickedSignal: lastItemTabClicked(focusItem)
}
PageSettingsServerData {
id: dataPage
stackView: root.stackView
onLastItemTabClickedSignal: lastItemTabClicked(focusItem)
}
PageSettingsApiServerInfo {
id: apiInfoPage
stackView: root.stackView
// onLastItemTabClickedSignal: lastItemTabClicked(focusItem)
}
PageSettingsApiLanguageList {
id: apiLanguageListPage
stackView: root.stackView
// onLastItemTabClickedSignal: lastItemTabClicked(focusItem)
}
}
}

View file

@ -184,7 +184,7 @@ PageType {
Keys.onTabPressed: lastItemTabClicked(focusItem)
text: qsTr("Remove ") + ContainersModel.getProcessedContainerName()
textColor: AmneziaStyle.color.red
textColor: AmneziaStyle.color.vibrantRed
clickedFunction: function() {
var headerText = qsTr("Remove %1 from server?").arg(ContainersModel.getProcessedContainerName())

View file

@ -8,6 +8,7 @@ import PageEnum 1.0
import ProtocolEnum 1.0
import ContainerProps 1.0
import ContainersModelFilters 1.0
import Style 1.0
import "./"
import "../Controls2"

View file

@ -8,6 +8,7 @@ import PageEnum 1.0
import ProtocolEnum 1.0
import ContainerProps 1.0
import ContainersModelFilters 1.0
import Style 1.0
import "./"
import "../Controls2"

View file

@ -7,6 +7,7 @@ import SortFilterProxyModel 0.2
import PageEnum 1.0
import ProtocolEnum 1.0
import ContainerProps 1.0
import Style 1.0
import "./"
import "../Controls2"
@ -119,7 +120,11 @@ PageType {
servicesNameString += servicesName[i] + " · "
}
return servicesNameString + hostName
if (ServersModel.isServerFromApi(index)) {
return servicesNameString + serverDescription
} else {
return servicesNameString + hostName
}
}
rightImageSource: "qrc:/images/controls/chevron-right.svg"

View file

@ -21,7 +21,7 @@ import "../Components"
PageType {
id: root
property var isServerFromApi: ServersModel.getDefaultServerData("isServerFromApi")
property var isServerFromTelegramApi: ServersModel.getDefaultServerData("isServerFromTelegramApi")
defaultActiveFocusItem: searchField.textField
@ -36,7 +36,7 @@ PageType {
if (ConnectionController.isConnected) {
PageController.showNotificationMessage(qsTr("Cannot change split tunneling settings during active connection"))
root.pageEnabled = false
} else if (ServersModel.isDefaultServerDefaultContainerHasSplitTunneling && isServerFromApi) {
} else if (ServersModel.isDefaultServerDefaultContainerHasSplitTunneling) {
PageController.showNotificationMessage(qsTr("Default server does not support split tunneling function"))
root.pageEnabled = false
} else {
@ -266,7 +266,7 @@ PageType {
text: url
descriptionText: ip
rightImageSource: "qrc:/images/controls/trash.svg"
rightImageColor: AmneziaStyle.color.white
rightImageColor: AmneziaStyle.color.paleGray
clickedFunction: function() {
var headerText = qsTr("Remove ") + url + "?"
@ -300,7 +300,7 @@ PageType {
Rectangle {
anchors.fill: addSiteButton
anchors.bottomMargin: -24
color: AmneziaStyle.color.black
color: AmneziaStyle.color.midnightBlack
opacity: 0.8
}
@ -341,7 +341,7 @@ PageType {
implicitHeight: 56
image: "qrc:/images/controls/more-vertical.svg"
imageColor: AmneziaStyle.color.white
imageColor: AmneziaStyle.color.paleGray
onClicked: function () {
moreActionsDrawer.open()

View file

@ -0,0 +1,154 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import QtQuick.Dialogs
import PageEnum 1.0
import Style 1.0
import "./"
import "../Controls2"
import "../Controls2/TextTypes"
import "../Config"
import "../Components"
PageType {
id: root
defaultActiveFocusItem: focusItem
FlickableType {
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.left: parent.left
anchors.right: parent.right
spacing: 0
Item {
id: focusItem
KeyNavigation.tab: backButton
}
BackButtonType {
id: backButton
Layout.topMargin: 20
// KeyNavigation.tab: fileButton.rightButton
}
HeaderType {
Layout.fillWidth: true
Layout.topMargin: 8
Layout.rightMargin: 16
Layout.leftMargin: 16
Layout.bottomMargin: 32
headerText: ApiServicesModel.getSelectedServiceData("name")
descriptionText: ApiServicesModel.getSelectedServiceData("serviceDescription")
}
LabelWithImageType {
Layout.fillWidth: true
Layout.margins: 16
imageSource: "qrc:/images/controls/map-pin.svg"
leftText: qsTr("For the region")
rightText: ApiServicesModel.getSelectedServiceData("region")
}
LabelWithImageType {
Layout.fillWidth: true
Layout.margins: 16
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("workPeriod")
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 {
Layout.fillWidth: true
Layout.rightMargin: 16
Layout.leftMargin: 16
onLinkActivated: function(link) {
Qt.openUrlExternally(link)
}
textFormat: Text.RichText
text: {
var text = ApiServicesModel.getSelectedServiceData("features")
return text.replace("%1", LanguageModel.getCurrentSiteUrl())
}
MouseArea {
anchors.fill: parent
acceptedButtons: Qt.NoButton
cursorShape: parent.hoveredLink ? Qt.PointingHandCursor : Qt.ArrowCursor
}
}
}
}
BasicButtonType {
id: continueButton
anchors.right: parent.right
anchors.left: parent.left
anchors.bottom: parent.bottom
anchors.topMargin: 32
anchors.rightMargin: 16
anchors.leftMargin: 16
anchors.bottomMargin: 32
text: qsTr("Connect")
clickedFunc: function() {
var endpoint = ApiServicesModel.getStoreEndpoint()
if (endpoint !== undefined && endpoint !== "") {
Qt.openUrlExternally(endpoint)
PageController.closePage()
PageController.closePage()
} else {
PageController.showBusyIndicator(true)
InstallController.installServiceFromApi()
PageController.showBusyIndicator(false)
}
}
}
}

View file

@ -0,0 +1,100 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import QtQuick.Dialogs
import PageEnum 1.0
import Style 1.0
import "./"
import "../Controls2"
import "../Controls2/TextTypes"
import "../Config"
PageType {
id: root
defaultActiveFocusItem: focusItem
FlickableType {
id: fl
anchors.top: parent.top
anchors.bottom: parent.bottom
contentHeight: content.height
ColumnLayout {
id: content
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
spacing: 0
Item {
id: focusItem
KeyNavigation.tab: backButton
}
BackButtonType {
id: backButton
Layout.topMargin: 20
// KeyNavigation.tab: fileButton.rightButton
}
HeaderType {
Layout.fillWidth: true
Layout.topMargin: 8
Layout.rightMargin: 16
Layout.leftMargin: 16
Layout.bottomMargin: 32
headerText: qsTr("VPN by Amnezia")
descriptionText: qsTr("Choose a VPN service that suits your needs.")
}
ListView {
id: containers
width: parent.width
height: containers.contentItem.height
spacing: 16
currentIndex: 1
interactive: false
model: ApiServicesModel
delegate: Item {
implicitWidth: containers.width
implicitHeight: delegateContent.implicitHeight
ColumnLayout {
id: delegateContent
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
CardWithIconsType {
id: card
Layout.fillWidth: true
Layout.rightMargin: 16
Layout.leftMargin: 16
headerText: name
bodyText: cardDescription
footerText: price
rightImageSource: "qrc:/images/controls/chevron-right.svg"
onClicked: {
ApiServicesModel.setServiceIndex(index)
PageController.goToPage(PageEnum.PageSetupWizardApiServiceInfo)
}
}
}
}
}
}
}
}

View file

@ -4,6 +4,7 @@ import QtQuick.Layouts
import QtQuick.Dialogs
import PageEnum 1.0
import Style 1.0
import "./"
import "../Controls2"
@ -17,7 +18,9 @@ PageType {
target: ImportController
function onQrDecodingFinished() {
PageController.closePage()
if (Qt.platform.os === "ios") {
PageController.closePage()
}
PageController.goToPage(PageEnum.PageSetupWizardViewConfig)
}
}
@ -41,46 +44,163 @@ PageType {
Item {
id: focusItem
KeyNavigation.tab: backButton
KeyNavigation.tab: textKey.textField
}
BackButtonType {
id: backButton
Layout.topMargin: 20
KeyNavigation.tab: fileButton.rightButton
}
HeaderType {
Layout.fillWidth: true
Layout.topMargin: 8
Layout.topMargin: 24
Layout.rightMargin: 16
Layout.leftMargin: 16
headerText: qsTr("Server connection")
descriptionText: qsTr("Do not use connection codes from untrusted sources, as they may be created to intercept your data.")
headerText: qsTr("Connection")
}
Header2TextType {
ParagraphTextType {
Layout.fillWidth: true
Layout.topMargin: 32
Layout.rightMargin: 16
Layout.leftMargin: 16
Layout.bottomMargin: 24
text: qsTr("Insert the key, add a configuration file or scan the QR-code")
}
TextFieldWithHeaderType {
id: textKey
Layout.fillWidth: true
Layout.topMargin: 48
Layout.rightMargin: 16
Layout.leftMargin: 16
text: qsTr("What do you have?")
headerText: qsTr("Insert key")
buttonText: qsTr("Insert")
clickedFunc: function() {
textField.text = ""
textField.paste()
}
KeyNavigation.tab: continueButton
}
LabelWithButtonType {
id: fileButton
BasicButtonType {
id: continueButton
Layout.fillWidth: true
Layout.topMargin: 16
Layout.rightMargin: 16
Layout.leftMargin: 16
visible: textKey.textFieldText !== ""
text: qsTr("Continue")
Keys.onTabPressed: lastItemTabClicked(focusItem)
clickedFunc: function() {
if (ImportController.extractConfigFromData(textKey.textFieldText)) {
PageController.goToPage(PageEnum.PageSetupWizardViewConfig)
}
}
}
ParagraphTextType {
Layout.fillWidth: true
Layout.topMargin: 32
Layout.rightMargin: 16
Layout.leftMargin: 16
Layout.bottomMargin: 24
color: AmneziaStyle.color.charcoalGray
text: qsTr("Other connection options")
}
CardWithIconsType {
id: apiInstalling
visible: false
Layout.fillWidth: true
Layout.rightMargin: 16
Layout.leftMargin: 16
Layout.bottomMargin: 16
headerText: qsTr("VPN by Amnezia")
bodyText: qsTr("Connect to classic paid and free VPN services from Amnezia")
text: !ServersModel.getServersCount() ? qsTr("File with connection settings or backup") : qsTr("File with connection settings")
rightImageSource: "qrc:/images/controls/chevron-right.svg"
leftImageSource: "qrc:/images/controls/folder-open.svg"
leftImageSource: "qrc:/images/controls/amnezia.svg"
KeyNavigation.tab: qrButton.visible ? qrButton.rightButton : textButton.rightButton
onClicked: function() {
PageController.showBusyIndicator(true)
var result = InstallController.fillAvailableServices()
PageController.showBusyIndicator(false)
if (result) {
PageController.goToPage(PageEnum.PageSetupWizardApiServicesList)
}
}
}
clickedFunction: function() {
CardWithIconsType {
id: manualInstalling
Layout.fillWidth: true
Layout.rightMargin: 16
Layout.leftMargin: 16
Layout.bottomMargin: 16
headerText: qsTr("Self-hosted VPN")
bodyText: qsTr("Configure Amnezia VPN on your own server")
rightImageSource: "qrc:/images/controls/chevron-right.svg"
leftImageSource: "qrc:/images/controls/server.svg"
onClicked: {
PageController.goToPage(PageEnum.PageSetupWizardCredentials)
}
}
CardWithIconsType {
id: backupRestore
Layout.fillWidth: true
Layout.rightMargin: 16
Layout.leftMargin: 16
Layout.bottomMargin: 16
visible: PageController.isStartPageVisible()
headerText: qsTr("Restore from backup")
rightImageSource: "qrc:/images/controls/chevron-right.svg"
leftImageSource: "qrc:/images/controls/archive-restore.svg"
onClicked: {
var filePath = SystemController.getFileName(qsTr("Open backup file"),
qsTr("Backup files (*.backup)"))
if (filePath !== "") {
PageController.showBusyIndicator(true)
SettingsController.restoreAppConfig(filePath)
PageController.showBusyIndicator(false)
}
}
}
CardWithIconsType {
id: openFile
Layout.fillWidth: true
Layout.rightMargin: 16
Layout.leftMargin: 16
Layout.bottomMargin: 16
headerText: qsTr("File with connection settings")
rightImageSource: "qrc:/images/controls/chevron-right.svg"
leftImageSource: "qrc:/images/controls/folder-search-2.svg"
onClicked: {
var nameFilter = !ServersModel.getServersCount() ? "Config or backup files (*.vpn *.ovpn *.conf *.json *.backup)" :
"Config files (*.vpn *.ovpn *.conf *.json)"
var fileName = SystemController.getFileName(qsTr("Open config file"), nameFilter)
@ -92,20 +212,22 @@ PageType {
}
}
DividerType {}
CardWithIconsType {
id: scanQr
LabelWithButtonType {
id: qrButton
Layout.fillWidth: true
Layout.rightMargin: 16
Layout.leftMargin: 16
Layout.bottomMargin: 16
visible: SettingsController.isCameraPresent()
text: qsTr("QR code")
headerText: qsTr("QR code")
rightImageSource: "qrc:/images/controls/chevron-right.svg"
leftImageSource: "qrc:/images/controls/qr-code.svg"
leftImageSource: "qrc:/images/controls/scan-line.svg"
KeyNavigation.tab: textButton.rightButton
clickedFunction: function() {
onClicked: {
ImportController.startDecodingQr()
if (Qt.platform.os === "ios") {
PageController.goToPage(PageEnum.PageSetupWizardQrReader)
@ -113,26 +235,25 @@ PageType {
}
}
DividerType {
visible: SettingsController.isCameraPresent()
}
CardWithIconsType {
id: siteLink
LabelWithButtonType {
id: textButton
Layout.fillWidth: true
Layout.rightMargin: 16
Layout.leftMargin: 16
Layout.bottomMargin: 16
visible: PageController.isStartPageVisible()
headerText: qsTr("I have nothing")
text: qsTr("Key as text")
rightImageSource: "qrc:/images/controls/chevron-right.svg"
leftImageSource: "qrc:/images/controls/text-cursor.svg"
leftImageSource: "qrc:/images/controls/help-circle.svg"
Keys.onTabPressed: lastItemTabClicked(focusItem)
clickedFunction: function() {
PageController.goToPage(PageEnum.PageSetupWizardTextKey)
onClicked: {
Qt.openUrlExternally(LanguageModel.getCurrentSiteUrl())
}
}
DividerType {}
}
}
}

View file

@ -3,6 +3,7 @@ import QtQuick.Controls
import QtQuick.Layouts
import PageEnum 1.0
import Style 1.0
import "./"
import "../Controls2"
@ -142,6 +143,23 @@ PageType {
text: qsTr("All data you enter will remain strictly confidential and will not be shared or disclosed to the Amnezia or any third parties")
}
CardWithIconsType {
id: siteLink
Layout.fillWidth: true
Layout.bottomMargin: 16
headerText: qsTr("How to run your VPN server")
bodyText: qsTr("Where to get connection data, step-by-step instructions for buying a VPS")
rightImageSource: "qrc:/images/controls/chevron-right.svg"
leftImageSource: "qrc:/images/controls/help-circle.svg"
onClicked: {
Qt.openUrlExternally(LanguageModel.getCurrentSiteUrl() + "/starter-guide")
}
}
}
}

View file

@ -187,10 +187,10 @@ PageType {
anchors.bottomMargin: 24
defaultColor: AmneziaStyle.color.transparent
hoveredColor: AmneziaStyle.color.blackHovered
pressedColor: AmneziaStyle.color.blackPressed
disabledColor: AmneziaStyle.color.grey
textColor: AmneziaStyle.color.white
hoveredColor: AmneziaStyle.color.translucentWhite
pressedColor: AmneziaStyle.color.sheerWhite
disabledColor: AmneziaStyle.color.mutedGray
textColor: AmneziaStyle.color.paleGray
borderWidth: 1
Keys.onTabPressed: lastItemTabClicked(focusItem)

View file

@ -5,6 +5,7 @@ import QtQuick.Layouts
import SortFilterProxyModel 0.2
import PageEnum 1.0
import Style 1.0
import "./"
import "../Controls2"
@ -46,14 +47,7 @@ PageType {
ServersModel.processedIndex = ServersModel.defaultIndex
}
PageController.goToStartPage()
if (stackView.currentItem.objectName === PageController.getPagePath(PageEnum.PageSetupWizardStart)) {
PageController.replaceStartPage()
}
if (stackView.currentItem.objectName !== PageController.getPagePath(PageEnum.PageHome)) {
PageController.goToPageHome()
}
PageController.goToPageHome()
PageController.showNotificationMessage(finishedMessage)
}

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