added separation for read/write and readonly servers for pageSettingsServerProtocols, PageSettingsServerServices, PageSettingsServerData

- added fields validations for pageSetupWizardCredentials
This commit is contained in:
vladimir.kuznetsov 2023-06-23 15:24:40 +09:00
parent 249be451f7
commit 2ef53c6df9
22 changed files with 466 additions and 325 deletions

View file

@ -1,13 +1,13 @@
#include "amnezia_application.h" #include "amnezia_application.h"
#include <QFontDatabase> #include <QFontDatabase>
#include <QQuickStyle>
#include <QStandardPaths> #include <QStandardPaths>
#include <QTimer> #include <QTimer>
#include <QTranslator> #include <QTranslator>
#include <QQuickStyle>
#include "logger.h"
#include "defines.h" #include "defines.h"
#include "logger.h"
#include "platforms/ios/QRCodeReaderBase.h" #include "platforms/ios/QRCodeReaderBase.h"
@ -18,12 +18,11 @@
#endif #endif
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS) #if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
AmneziaApplication::AmneziaApplication(int &argc, char *argv[]): AmneziaApplication::AmneziaApplication(int &argc, char *argv[]) : AMNEZIA_BASE_CLASS(argc, argv)
AMNEZIA_BASE_CLASS(argc, argv)
#else #else
AmneziaApplication::AmneziaApplication(int &argc, char *argv[], bool allowSecondary, AmneziaApplication::AmneziaApplication(int &argc, char *argv[], bool allowSecondary, SingleApplication::Options options,
SingleApplication::Options options, int timeout, const QString &userData): int timeout, const QString &userData)
SingleApplication(argc, argv, allowSecondary, options, timeout, userData) : SingleApplication(argc, argv, allowSecondary, options, timeout, userData)
#endif #endif
{ {
setQuitOnLastWindowClosed(false); setQuitOnLastWindowClosed(false);
@ -55,8 +54,10 @@ AmneziaApplication::~AmneziaApplication()
delete m_engine; delete m_engine;
} }
if (m_protocolProps) delete m_protocolProps; if (m_protocolProps)
if (m_containerProps) delete m_containerProps; delete m_protocolProps;
if (m_containerProps)
delete m_containerProps;
} }
void AmneziaApplication::init() void AmneziaApplication::init()
@ -64,11 +65,13 @@ void AmneziaApplication::init()
m_engine = new QQmlApplicationEngine; m_engine = new QQmlApplicationEngine;
const QUrl url(QStringLiteral("qrc:/ui/qml/main2.qml")); const QUrl url(QStringLiteral("qrc:/ui/qml/main2.qml"));
QObject::connect(m_engine, &QQmlApplicationEngine::objectCreated, QObject::connect(
this, [url](QObject *obj, const QUrl &objUrl) { m_engine, &QQmlApplicationEngine::objectCreated, this,
[url](QObject *obj, const QUrl &objUrl) {
if (!obj && url == objUrl) if (!obj && url == objUrl)
QCoreApplication::exit(-1); QCoreApplication::exit(-1);
}, Qt::QueuedConnection); },
Qt::QueuedConnection);
m_engine->rootContext()->setContextProperty("Debug", &Logger::Instance()); m_engine->rootContext()->setContextProperty("Debug", &Logger::Instance());
@ -78,6 +81,8 @@ void AmneziaApplication::init()
m_serversModel.reset(new ServersModel(m_settings, this)); m_serversModel.reset(new ServersModel(m_settings, this));
m_engine->rootContext()->setContextProperty("ServersModel", m_serversModel.get()); m_engine->rootContext()->setContextProperty("ServersModel", m_serversModel.get());
connect(m_serversModel.get(), &ServersModel::currentlyProcessedServerIndexChanged, m_containersModel.get(),
&ContainersModel::setCurrentlyProcessedServerIndex);
m_configurator = std::shared_ptr<VpnConfigurator>(new VpnConfigurator(m_settings, this)); m_configurator = std::shared_ptr<VpnConfigurator>(new VpnConfigurator(m_settings, this));
m_vpnConnection.reset(new VpnConnection(m_settings, m_configurator)); m_vpnConnection.reset(new VpnConnection(m_settings, m_configurator));
@ -94,12 +99,10 @@ void AmneziaApplication::init()
m_importController.reset(new ImportController(m_serversModel, m_containersModel, m_settings)); m_importController.reset(new ImportController(m_serversModel, m_containersModel, m_settings));
m_engine->rootContext()->setContextProperty("ImportController", m_importController.get()); m_engine->rootContext()->setContextProperty("ImportController", m_importController.get());
m_exportController.reset( m_exportController.reset(new ExportController(m_serversModel, m_containersModel, m_settings, m_configurator));
new ExportController(m_serversModel, m_containersModel, m_settings, m_configurator));
m_engine->rootContext()->setContextProperty("ExportController", m_exportController.get()); m_engine->rootContext()->setContextProperty("ExportController", m_exportController.get());
m_settingsController.reset( m_settingsController.reset(new SettingsController(m_serversModel, m_containersModel, m_settings));
new SettingsController(m_serversModel, m_containersModel, m_settings));
m_engine->rootContext()->setContextProperty("SettingsController", m_settingsController.get()); m_engine->rootContext()->setContextProperty("SettingsController", m_settingsController.get());
// //
@ -133,7 +136,6 @@ void AmneziaApplication::init()
// }); // });
// } // }
// #endif // #endif
} }
void AmneziaApplication::registerTypes() void AmneziaApplication::registerTypes()
@ -156,6 +158,9 @@ void AmneziaApplication::registerTypes()
m_protocolProps = new ProtocolProps; m_protocolProps = new ProtocolProps;
qmlRegisterSingletonInstance("ProtocolProps", 1, 0, "ProtocolProps", m_protocolProps); qmlRegisterSingletonInstance("ProtocolProps", 1, 0, "ProtocolProps", m_protocolProps);
qmlRegisterSingletonType(QUrl("qrc:/ui/qml/Filters/ContainersModelFilters.qml"), "ContainersModelFilters", 1, 0,
"ContainersModelFilters");
// //
Vpn::declareQmlVpnConnectionStateEnum(); Vpn::declareQmlVpnConnectionStateEnum();
PageLoader::declareQmlPageEnum(); PageLoader::declareQmlPageEnum();
@ -192,9 +197,7 @@ bool AmneziaApplication::parseCommands()
if (m_parser.isSet(c_cleanup)) { if (m_parser.isSet(c_cleanup)) {
Logger::cleanUp(); Logger::cleanUp();
QTimer::singleShot(100, this, [this]{ QTimer::singleShot(100, this, [this] { quit(); });
quit();
});
exec(); exec();
return false; return false;
} }
@ -205,4 +208,3 @@ QQmlApplicationEngine *AmneziaApplication::qmlEngine() const
{ {
return m_engine; return m_engine;
} }

View file

@ -269,5 +269,6 @@
<file>images/controls/mail.svg</file> <file>images/controls/mail.svg</file>
<file>images/controls/telegram.svg</file> <file>images/controls/telegram.svg</file>
<file>ui/qml/Controls2/TextTypes/SmallTextType.qml</file> <file>ui/qml/Controls2/TextTypes/SmallTextType.qml</file>
<file>ui/qml/Filters/ContainersModelFilters.qml</file>
</qresource> </qresource>
</RCC> </RCC>

View file

@ -16,14 +16,14 @@
ExportController::ExportController(const QSharedPointer<ServersModel> &serversModel, ExportController::ExportController(const QSharedPointer<ServersModel> &serversModel,
const QSharedPointer<ContainersModel> &containersModel, const QSharedPointer<ContainersModel> &containersModel,
const std::shared_ptr<Settings> &settings, const std::shared_ptr<Settings> &settings,
const std::shared_ptr<VpnConfigurator> &configurator, const std::shared_ptr<VpnConfigurator> &configurator, QObject *parent)
QObject *parent) : QObject(parent),
: QObject(parent) m_serversModel(serversModel),
, m_serversModel(serversModel) m_containersModel(containersModel),
, m_containersModel(containersModel) m_settings(settings),
, m_settings(settings) m_configurator(configurator)
, m_configurator(configurator) {
{} }
void ExportController::generateFullAccessConfig() void ExportController::generateFullAccessConfig()
{ {
@ -43,25 +43,21 @@ void ExportController::generateFullAccessConfig()
void ExportController::generateConnectionConfig() void ExportController::generateConnectionConfig()
{ {
int serverIndex = m_serversModel->getCurrentlyProcessedServerIndex(); int serverIndex = m_serversModel->getCurrentlyProcessedServerIndex();
ServerCredentials credentials = qvariant_cast<ServerCredentials>( ServerCredentials credentials =
m_serversModel->data(serverIndex, ServersModel::Roles::CredentialsRole)); qvariant_cast<ServerCredentials>(m_serversModel->data(serverIndex, ServersModel::Roles::CredentialsRole));
DockerContainer container = static_cast<DockerContainer>( DockerContainer container = static_cast<DockerContainer>(m_containersModel->getCurrentlyProcessedContainerIndex());
m_containersModel->getCurrentlyProcessedContainerIndex());
QModelIndex containerModelIndex = m_containersModel->index(container); QModelIndex containerModelIndex = m_containersModel->index(container);
QJsonObject containerConfig = qvariant_cast<QJsonObject>( QJsonObject containerConfig =
m_containersModel->data(containerModelIndex, ContainersModel::Roles::ConfigRole)); qvariant_cast<QJsonObject>(m_containersModel->data(containerModelIndex, ContainersModel::Roles::ConfigRole));
containerConfig.insert(config_key::container, ContainerProps::containerToString(container)); containerConfig.insert(config_key::container, ContainerProps::containerToString(container));
ErrorCode errorCode = ErrorCode::NoError; ErrorCode errorCode = ErrorCode::NoError;
for (Proto protocol : ContainerProps::protocolsForContainer(container)) { for (Proto protocol : ContainerProps::protocolsForContainer(container)) {
QJsonObject protocolConfig = m_settings->protocolConfig(serverIndex, container, protocol); QJsonObject protocolConfig = m_settings->protocolConfig(serverIndex, container, protocol);
QString vpnConfig = m_configurator->genVpnProtocolConfig(credentials, QString vpnConfig =
container, m_configurator->genVpnProtocolConfig(credentials, container, containerConfig, protocol, &errorCode);
containerConfig,
protocol,
&errorCode);
if (errorCode) { if (errorCode) {
emit exportErrorOccurred(errorString(errorCode)); emit exportErrorOccurred(errorString(errorCode));
return; return;
@ -108,10 +104,8 @@ void ExportController::saveFile()
QString fileExtension = ".vpn"; QString fileExtension = ".vpn";
QString docDir = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation); QString docDir = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation);
QUrl fileName; QUrl fileName;
fileName = QFileDialog::getSaveFileUrl(nullptr, fileName = QFileDialog::getSaveFileUrl(nullptr, tr("Save AmneziaVPN config"),
tr("Save AmneziaVPN config"), QUrl::fromLocalFile(docDir + "/" + "amnezia_config"), "*" + fileExtension);
QUrl::fromLocalFile(docDir + "/" + "amnezia_config"),
"*" + fileExtension);
if (fileName.isEmpty()) if (fileName.isEmpty())
return; return;
if (!fileName.toString().endsWith(fileExtension)) { if (!fileName.toString().endsWith(fileExtension)) {
@ -141,8 +135,7 @@ QList<QString> ExportController::generateQrCodeImageSeries(const QByteArray &dat
QDataStream s(&chunk, QIODevice::WriteOnly); QDataStream s(&chunk, QIODevice::WriteOnly);
s << amnezia::qrMagicCode << chunksCount << (quint8)std::round(i / k) << data.mid(i, k); s << amnezia::qrMagicCode << chunksCount << (quint8)std::round(i / k) << data.mid(i, k);
QByteArray ba = chunk.toBase64(QByteArray::Base64UrlEncoding QByteArray ba = chunk.toBase64(QByteArray::Base64UrlEncoding | QByteArray::OmitTrailingEquals);
| QByteArray::OmitTrailingEquals);
qrcodegen::QrCode qr = qrcodegen::QrCode::encodeText(ba, qrcodegen::QrCode::Ecc::LOW); qrcodegen::QrCode qr = qrcodegen::QrCode::encodeText(ba, qrcodegen::QrCode::Ecc::LOW);
QString svg = QString::fromStdString(toSvgString(qr, 0)); QString svg = QString::fromStdString(toSvgString(qr, 0));

View file

@ -5,8 +5,13 @@
#include "core/errorstrings.h" #include "core/errorstrings.h"
namespace { namespace
enum class ConfigTypes { Amnezia, OpenVpn, WireGuard }; {
enum class ConfigTypes {
Amnezia,
OpenVpn,
WireGuard
};
ConfigTypes checkConfigFormat(const QString &config) ConfigTypes checkConfigFormat(const QString &config)
{ {
@ -20,10 +25,8 @@ ConfigTypes checkConfigFormat(const QString &config)
const QString wireguardConfigPatternSectionPeer = "[Peer]"; const QString wireguardConfigPatternSectionPeer = "[Peer]";
if (config.contains(openVpnConfigPatternCli) if (config.contains(openVpnConfigPatternCli)
&& (config.contains(openVpnConfigPatternProto1) && (config.contains(openVpnConfigPatternProto1) || config.contains(openVpnConfigPatternProto2))
|| config.contains(openVpnConfigPatternProto2)) && (config.contains(openVpnConfigPatternDriver1) || config.contains(openVpnConfigPatternDriver2))) {
&& (config.contains(openVpnConfigPatternDriver1)
|| config.contains(openVpnConfigPatternDriver2))) {
return ConfigTypes::OpenVpn; return ConfigTypes::OpenVpn;
} else if (config.contains(wireguardConfigPatternSectionInterface) } else if (config.contains(wireguardConfigPatternSectionInterface)
&& config.contains(wireguardConfigPatternSectionPeer)) { && config.contains(wireguardConfigPatternSectionPeer)) {
@ -35,10 +38,9 @@ ConfigTypes checkConfigFormat(const QString &config)
ImportController::ImportController(const QSharedPointer<ServersModel> &serversModel, ImportController::ImportController(const QSharedPointer<ServersModel> &serversModel,
const QSharedPointer<ContainersModel> &containersModel, const QSharedPointer<ContainersModel> &containersModel,
const std::shared_ptr<Settings> &settings, const std::shared_ptr<Settings> &settings, QObject *parent)
QObject *parent) : QObject(parent), m_serversModel(serversModel), m_containersModel(containersModel), m_settings(settings) : QObject(parent), m_serversModel(serversModel), m_containersModel(containersModel), m_settings(settings)
{ {
} }
void ImportController::extractConfigFromFile(const QUrl &fileUrl) void ImportController::extractConfigFromFile(const QUrl &fileUrl)
@ -88,8 +90,7 @@ void ImportController::importConfig()
m_serversModel->addServer(m_config); m_serversModel->addServer(m_config);
if (!m_config.value(config_key::containers).toArray().isEmpty()) { if (!m_config.value(config_key::containers).toArray().isEmpty()) {
auto newServerIndex = m_serversModel->index(m_serversModel->getServersCount() - 1); m_serversModel->setDefaultServerIndex(m_serversModel->getServersCount() - 1);
m_serversModel->setData(newServerIndex, true, ServersModel::Roles::IsDefaultRole);
} }
emit importFinished(); emit importFinished();
@ -159,7 +160,6 @@ QJsonObject ImportController::extractOpenVpnConfig(const QString &data)
config[config_key::defaultContainer] = "amnezia-openvpn"; config[config_key::defaultContainer] = "amnezia-openvpn";
config[config_key::description] = m_settings->nextAvailableServerName(); config[config_key::description] = m_settings->nextAvailableServerName();
const static QRegularExpression dnsRegExp("dhcp-option DNS (\\b\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\b)"); const static QRegularExpression dnsRegExp("dhcp-option DNS (\\b\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\b)");
QRegularExpressionMatchIterator dnsMatch = dnsRegExp.globalMatch(data); QRegularExpressionMatchIterator dnsMatch = dnsRegExp.globalMatch(data);
if (dnsMatch.hasNext()) { if (dnsMatch.hasNext()) {
@ -206,7 +206,9 @@ QJsonObject ImportController::extractWireGuardConfig(const QString &data)
config[config_key::defaultContainer] = "amnezia-wireguard"; config[config_key::defaultContainer] = "amnezia-wireguard";
config[config_key::description] = m_settings->nextAvailableServerName(); config[config_key::description] = m_settings->nextAvailableServerName();
const static QRegularExpression dnsRegExp("DNS = (\\b\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\b).*(\\b\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\b)"); const static QRegularExpression dnsRegExp(
"DNS = "
"(\\b\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\b).*(\\b\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\b)");
QRegularExpressionMatch dnsMatch = dnsRegExp.match(data); QRegularExpressionMatch dnsMatch = dnsRegExp.match(data);
if (dnsMatch.hasMatch()) { if (dnsMatch.hasMatch()) {
config[config_key::dns1] = dnsMatch.captured(1); config[config_key::dns1] = dnsMatch.captured(1);

View file

@ -67,8 +67,7 @@ void InstallController::installServer(DockerContainer container, QJsonObject &co
server.insert(config_key::defaultContainer, ContainerProps::containerToString(container)); server.insert(config_key::defaultContainer, ContainerProps::containerToString(container));
m_serversModel->addServer(server); m_serversModel->addServer(server);
auto newServerIndex = m_serversModel->index(m_serversModel->getServersCount() - 1); m_serversModel->setDefaultServerIndex(m_serversModel->getServersCount() - 1);
m_serversModel->setData(newServerIndex, true, ServersModel::Roles::IsDefaultRole);
emit installServerFinished(isInstalledContainerFound); emit installServerFinished(isInstalledContainerFound);
return; return;

View file

@ -78,7 +78,7 @@ QVariant ContainersModel::data(const QModelIndex &index, int role) const
return QVariant(); return QVariant();
} }
void ContainersModel::setCurrentlyProcessedServerIndex(int index) void ContainersModel::setCurrentlyProcessedServerIndex(const int index)
{ {
beginResetModel(); beginResetModel();
m_currentlyProcessedServerIndex = index; m_currentlyProcessedServerIndex = index;

View file

@ -3,11 +3,11 @@
#include <QAbstractListModel> #include <QAbstractListModel>
#include <QJsonObject> #include <QJsonObject>
#include <vector>
#include <utility> #include <utility>
#include <vector>
#include "settings.h"
#include "containers/containers_defs.h" #include "containers/containers_defs.h"
#include "settings.h"
class ContainersModel : public QAbstractListModel class ContainersModel : public QAbstractListModel
{ {
@ -44,7 +44,7 @@ public slots:
DockerContainer getDefaultContainer(); DockerContainer getDefaultContainer();
QString getDefaultContainerName(); QString getDefaultContainerName();
void setCurrentlyProcessedServerIndex(int index); void setCurrentlyProcessedServerIndex(const int index);
void setCurrentlyProcessedContainerIndex(int index); void setCurrentlyProcessedContainerIndex(int index);
int getCurrentlyProcessedContainerIndex(); int getCurrentlyProcessedContainerIndex();
@ -57,7 +57,6 @@ protected:
private: private:
QMap<DockerContainer, QJsonObject> m_containers; QMap<DockerContainer, QJsonObject> m_containers;
int m_currentlyProcessedServerIndex; int m_currentlyProcessedServerIndex;
int m_currentlyProcessedContainerIndex; int m_currentlyProcessedContainerIndex;
DockerContainer m_defaultContainerIndex; DockerContainer m_defaultContainerIndex;

View file

@ -5,6 +5,7 @@ ServersModel::ServersModel(std::shared_ptr<Settings> settings, QObject *parent)
{ {
m_servers = m_settings->serversArray(); m_servers = m_settings->serversArray();
m_defaultServerIndex = m_settings->defaultServerIndex(); m_defaultServerIndex = m_settings->defaultServerIndex();
m_currenlyProcessedServerIndex = m_defaultServerIndex;
} }
int ServersModel::rowCount(const QModelIndex &parent) const int ServersModel::rowCount(const QModelIndex &parent) const
@ -28,10 +29,6 @@ bool ServersModel::setData(const QModelIndex &index, const QVariant &value, int
m_servers.replace(index.row(), server); m_servers.replace(index.row(), server);
break; break;
} }
case IsDefaultRole: {
setDefaultServerIndex(index.row());
break;
}
default: { default: {
return true; return true;
} }
@ -62,6 +59,10 @@ QVariant ServersModel::data(const QModelIndex &index, int role) const
case CredentialsLoginRole: return m_settings->serverCredentials(index.row()).userName; case CredentialsLoginRole: return m_settings->serverCredentials(index.row()).userName;
case IsDefaultRole: return index.row() == m_defaultServerIndex; case IsDefaultRole: return index.row() == m_defaultServerIndex;
case IsCurrentlyProcessedRole: return index.row() == m_currenlyProcessedServerIndex; case IsCurrentlyProcessedRole: return index.row() == m_currenlyProcessedServerIndex;
case HasWriteAccess: {
auto credentials = m_settings->serverCredentials(index.row());
return (!credentials.userName.isEmpty() && !credentials.secretData.isEmpty());
}
} }
return QVariant(); return QVariant();
@ -73,6 +74,13 @@ QVariant ServersModel::data(const int index, int role) const
return data(modelIndex, role); return data(modelIndex, role);
} }
void ServersModel::setDefaultServerIndex(const int index)
{
m_settings->setDefaultServer(index);
m_defaultServerIndex = m_settings->defaultServerIndex();
emit defaultServerIndexChanged();
}
const int ServersModel::getDefaultServerIndex() const int ServersModel::getDefaultServerIndex()
{ {
return m_defaultServerIndex; return m_defaultServerIndex;
@ -83,10 +91,10 @@ const int ServersModel::getServersCount()
return m_servers.count(); return m_servers.count();
} }
void ServersModel::setCurrentlyProcessedServerIndex(int index) void ServersModel::setCurrentlyProcessedServerIndex(const int index)
{ {
m_currenlyProcessedServerIndex = index; m_currenlyProcessedServerIndex = index;
emit currentlyProcessedServerIndexChanged(); emit currentlyProcessedServerIndexChanged(m_currenlyProcessedServerIndex);
} }
int ServersModel::getCurrentlyProcessedServerIndex() int ServersModel::getCurrentlyProcessedServerIndex()
@ -101,8 +109,7 @@ bool ServersModel::isDefaultServerCurrentlyProcessed()
bool ServersModel::isCurrentlyProcessedServerHasWriteAccess() bool ServersModel::isCurrentlyProcessedServerHasWriteAccess()
{ {
auto credentials = m_settings->serverCredentials(m_currenlyProcessedServerIndex); return qvariant_cast<bool>(data(m_currenlyProcessedServerIndex, HasWriteAccess));
return (!credentials.userName.isEmpty() && !credentials.secretData.isEmpty());
} }
void ServersModel::addServer(const QJsonObject &server) void ServersModel::addServer(const QJsonObject &server)
@ -140,11 +147,6 @@ QHash<int, QByteArray> ServersModel::roleNames() const
roles[CredentialsLoginRole] = "credentialsLogin"; roles[CredentialsLoginRole] = "credentialsLogin";
roles[IsDefaultRole] = "isDefault"; roles[IsDefaultRole] = "isDefault";
roles[IsCurrentlyProcessedRole] = "isCurrentlyProcessed"; roles[IsCurrentlyProcessedRole] = "isCurrentlyProcessed";
roles[HasWriteAccess] = "hasWriteAccess";
return roles; return roles;
} }
void ServersModel::setDefaultServerIndex(const int index)
{
m_settings->setDefaultServer(index);
m_defaultServerIndex = m_settings->defaultServerIndex();
}

View file

@ -5,13 +5,6 @@
#include "settings.h" #include "settings.h"
struct ServerModelContent
{
QString desc;
QString address;
bool isDefault;
};
class ServersModel : public QAbstractListModel class ServersModel : public QAbstractListModel
{ {
Q_OBJECT Q_OBJECT
@ -22,7 +15,8 @@ public:
CredentialsRole, CredentialsRole,
CredentialsLoginRole, CredentialsLoginRole,
IsDefaultRole, IsDefaultRole,
IsCurrentlyProcessedRole IsCurrentlyProcessedRole,
HasWriteAccess
}; };
ServersModel(std::shared_ptr<Settings> settings, QObject *parent = nullptr); ServersModel(std::shared_ptr<Settings> settings, QObject *parent = nullptr);
@ -33,14 +27,20 @@ public:
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
QVariant data(const int index, int role = Qt::DisplayRole) const; QVariant data(const int index, int role = Qt::DisplayRole) const;
Q_PROPERTY(int defaultIndex READ getDefaultServerIndex WRITE setDefaultServerIndex NOTIFY defaultServerIndexChanged)
Q_PROPERTY(int currentlyProcessedIndex READ getCurrentlyProcessedServerIndex WRITE setCurrentlyProcessedServerIndex
NOTIFY currentlyProcessedServerIndexChanged)
public slots: public slots:
void setDefaultServerIndex(const int index);
const int getDefaultServerIndex(); const int getDefaultServerIndex();
bool isDefaultServerCurrentlyProcessed(); bool isDefaultServerCurrentlyProcessed();
bool isCurrentlyProcessedServerHasWriteAccess(); bool isCurrentlyProcessedServerHasWriteAccess();
const int getServersCount(); const int getServersCount();
void setCurrentlyProcessedServerIndex(int index); void setCurrentlyProcessedServerIndex(const int index);
int getCurrentlyProcessedServerIndex(); int getCurrentlyProcessedServerIndex();
void addServer(const QJsonObject &server); void addServer(const QJsonObject &server);
@ -50,11 +50,10 @@ protected:
QHash<int, QByteArray> roleNames() const override; QHash<int, QByteArray> roleNames() const override;
signals: signals:
void currentlyProcessedServerIndexChanged(); void currentlyProcessedServerIndexChanged(const int index);
void defaultServerIndexChanged();
private: private:
void setDefaultServerIndex(const int index);
QJsonArray m_servers; QJsonArray m_servers;
std::shared_ptr<Settings> m_settings; std::shared_ptr<Settings> m_settings;

View file

@ -1,14 +1,12 @@
#include "ServerListLogic.h" #include "ServerListLogic.h"
#include "vpnconnection.h"
#include "../models/servers_model.h" #include "../models/servers_model.h"
#include "../uilogic.h" #include "../uilogic.h"
#include "vpnconnection.h"
ServerListLogic::ServerListLogic(UiLogic *logic, QObject *parent): ServerListLogic::ServerListLogic(UiLogic *logic, QObject *parent)
PageLogicBase(logic, parent), : PageLogicBase(logic, parent), m_serverListModel { new ServersModel(m_settings, this) }
m_serverListModel{new ServersModel(m_settings, this)}
{ {
} }
void ServerListLogic::onServerListPushbuttonDefaultClicked(int index) void ServerListLogic::onServerListPushbuttonDefaultClicked(int index)
@ -31,19 +29,19 @@ int ServerListLogic::currServerIdx() const
void ServerListLogic::onUpdatePage() void ServerListLogic::onUpdatePage()
{ {
const QJsonArray &servers = m_settings->serversArray(); // const QJsonArray &servers = m_settings->serversArray();
int defaultServer = m_settings->defaultServerIndex(); // int defaultServer = m_settings->defaultServerIndex();
QVector<ServerModelContent> serverListContent; // QVector<ServerModelContent> serverListContent;
for(int i = 0; i < servers.size(); i++) { // for(int i = 0; i < servers.size(); i++) {
ServerModelContent c; // ServerModelContent c;
auto server = servers.at(i).toObject(); // auto server = servers.at(i).toObject();
c.desc = server.value(config_key::description).toString(); // c.desc = server.value(config_key::description).toString();
c.address = server.value(config_key::hostName).toString(); // c.address = server.value(config_key::hostName).toString();
if (c.desc.isEmpty()) { // if (c.desc.isEmpty()) {
c.desc = c.address; // c.desc = c.address;
} // }
c.isDefault = (i == defaultServer); // c.isDefault = (i == defaultServer);
serverListContent.push_back(c); // serverListContent.push_back(c);
} // }
// qobject_cast<ServersModel*>(m_serverListModel)->setContent(serverListContent); // qobject_cast<ServersModel*>(m_serverListModel)->setContent(serverListContent);
} }

View file

@ -8,20 +8,27 @@ Item {
id: root id: root
property string headerText property string headerText
property string textFieldPlaceholderText property alias errorText: errorField.text
property bool textFieldEditable: true
property string buttonText property string buttonText
property var clickedFunc property var clickedFunc
property alias textField: textField property alias textField: textField
property alias textFieldText: textField.text property alias textFieldText: textField.text
property string textFieldPlaceholderText
property bool textFieldEditable: true
implicitHeight: 74 implicitWidth: content.implicitWidth
implicitHeight: content.implicitHeight
ColumnLayout {
id: content
anchors.fill: parent
Rectangle { Rectangle {
id: backgroud id: backgroud
anchors.fill: parent Layout.fillWidth: true
Layout.preferredHeight: 74
color: "#1c1d21" color: "#1c1d21"
radius: 16 radius: 16
border.color: textField.focus ? "#d7d8db" : "#2C2D30" border.color: textField.focus ? "#d7d8db" : "#2C2D30"
@ -30,12 +37,10 @@ Item {
Behavior on border.color { Behavior on border.color {
PropertyAnimation { duration: 200 } PropertyAnimation { duration: 200 }
} }
}
RowLayout { RowLayout {
anchors.fill: backgroud anchors.fill: backgroud
ColumnLayout { ColumnLayout {
LabelTextType { LabelTextType {
text: root.headerText text: root.headerText
color: "#878b91" color: "#878b91"
@ -76,6 +81,10 @@ Item {
anchors.fill: parent anchors.fill: parent
color: "#1c1d21" color: "#1c1d21"
} }
onTextChanged: {
root.errorText = ""
}
} }
} }
@ -101,3 +110,13 @@ Item {
} }
} }
} }
SmallTextType {
id: errorField
text: root.errorText
visible: root.errorText !== ""
color: "#EB5757"
}
}
}

View file

@ -0,0 +1,47 @@
pragma Singleton
import QtQuick 2.15
import SortFilterProxyModel 0.2
import ProtocolEnum 1.0
Item {
ValueFilter {
id: vpnTypeFilter
roleName: "serviceType"
value: ProtocolEnum.Vpn
}
ValueFilter {
id: serviceTypeFilter
roleName: "serviceType"
value: ProtocolEnum.Other
}
ValueFilter {
id: supportedFilter
roleName: "isSupported"
value: true
}
ValueFilter {
id: installedFilter
roleName: "isInstalled"
value: true
}
function getWriteAccessProtocolsListFilters() {
return [vpnTypeFilter, supportedFilter]
}
function getReadAccessProtocolsListFilters() {
return [vpnTypeFilter, supportedFilter, installedFilter]
}
function getWriteAccessServicesListFilters() {
return [serviceTypeFilter, supportedFilter]
}
function getReadAccessServicesListFilters() {
return [serviceTypeFilter, supportedFilter, installedFilter]
}
}

View file

@ -7,6 +7,7 @@ import SortFilterProxyModel 0.2
import PageEnum 1.0 import PageEnum 1.0
import ProtocolEnum 1.0 import ProtocolEnum 1.0
import ContainerProps 1.0 import ContainerProps 1.0
import ContainersModelFilters 1.0
import "./" import "./"
import "../Controls2" import "../Controls2"
@ -161,10 +162,7 @@ PageType {
headerBackButtonImage: "qrc:/images/controls/arrow-left.svg" headerBackButtonImage: "qrc:/images/controls/arrow-left.svg"
rootButtonClickedFunction: function() { rootButtonClickedFunction: function() {
// todo check if server index changed before set Currently processed ServersModel.currentlyProcessedIndex = serversMenuContent.currentIndex
// todo make signal slot for change server index in containersModel
ServersModel.setCurrentlyProcessedServerIndex(serversMenuContent.currentIndex)
ContainersModel.setCurrentlyProcessedServerIndex(serversMenuContent.currentIndex)
containersDropDown.menuVisible = true containersDropDown.menuVisible = true
} }
@ -177,39 +175,22 @@ PageType {
function onCurrentlyProcessedServerIndexChanged() { function onCurrentlyProcessedServerIndexChanged() {
updateContainersModelFilters() updateContainersModelFilters()
} }
}
function updateContainersModelFilters() { function updateContainersModelFilters() {
if (ServersModel.isCurrentlyProcessedServerHasWriteAccess()) { if (ServersModel.isCurrentlyProcessedServerHasWriteAccess()) {
proxyContainersModel.filters = [serviceTypeFilter, supportedFilter] proxyContainersModel.filters = ContainersModelFilters.getWriteAccessProtocolsListFilters()
} else { } else {
proxyContainersModel.filters = installedFilter proxyContainersModel.filters = ContainersModelFilters.getReadAccessProtocolsListFilters()
} }
} }
}
ValueFilter {
id: serviceTypeFilter
roleName: "serviceType"
value: ProtocolEnum.Vpn
}
ValueFilter {
id: supportedFilter
roleName: "isSupported"
value: true
}
ValueFilter {
id: installedFilter
roleName: "isInstalled"
value: true
}
model: SortFilterProxyModel { model: SortFilterProxyModel {
id: proxyContainersModel id: proxyContainersModel
sourceModel: ContainersModel sourceModel: ContainersModel
Component.onCompleted: updateContainersModelFilters()
} }
Component.onCompleted: updateContainersModelFilters()
currentIndex: ContainersModel.getDefaultContainer() currentIndex: ContainersModel.getDefaultContainer()
} }
} }
@ -267,7 +248,7 @@ PageType {
height: serversMenuContent.contentItem.height height: serversMenuContent.contentItem.height
model: ServersModel model: ServersModel
currentIndex: ServersModel.getDefaultServerIndex() currentIndex: ServersModel.defaultIndex
clip: true clip: true
interactive: false interactive: false
@ -305,8 +286,8 @@ PageType {
onClicked: { onClicked: {
serversMenuContent.currentIndex = index serversMenuContent.currentIndex = index
isDefault = true ServersModel.currentlyProcessedIndex = index
ContainersModel.setCurrentlyProcessedServerIndex(index) ServersModel.defaultIndex = index
root.currentServerName = name root.currentServerName = name
root.currentServerHostName = hostName root.currentServerHostName = hostName
@ -328,8 +309,7 @@ PageType {
z: 1 z: 1
onClicked: function() { onClicked: function() {
ServersModel.setCurrentlyProcessedServerIndex(index) ServersModel.currentlyProcessedIndex = index
ContainersModel.setCurrentlyProcessedServerIndex(index)
goToPage(PageEnum.PageSettingsServerInfo) goToPage(PageEnum.PageSettingsServerInfo)
menu.visible = false menu.visible = false
} }

View file

@ -29,6 +29,14 @@ PageType {
} }
} }
Connections {
target: ServersModel
function onCurrentlyProcessedServerIndexChanged() {
content.isServerWithWriteAccess = ServersModel.isCurrentlyProcessedServerHasWriteAccess()
}
}
FlickableType { FlickableType {
id: fl id: fl
anchors.top: parent.top anchors.top: parent.top
@ -42,7 +50,10 @@ PageType {
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
property bool isServerWithWriteAccess: ServersModel.isCurrentlyProcessedServerHasWriteAccess() //todo make it property?
LabelWithButtonType { LabelWithButtonType {
visible: content.isServerWithWriteAccess
Layout.fillWidth: true Layout.fillWidth: true
text: qsTr("Clear Amnezia cache") text: qsTr("Clear Amnezia cache")
@ -65,9 +76,12 @@ PageType {
} }
} }
DividerType {} DividerType {
visible: content.isServerWithWriteAccess
}
LabelWithButtonType { LabelWithButtonType {
visible: content.isServerWithWriteAccess
Layout.fillWidth: true Layout.fillWidth: true
text: qsTr("Проверить сервер на наличие ранее установленных сервисов Amnezia") text: qsTr("Проверить сервер на наличие ранее установленных сервисов Amnezia")
@ -78,12 +92,14 @@ PageType {
} }
} }
DividerType {} DividerType {
visible: content.isServerWithWriteAccess
}
LabelWithButtonType { LabelWithButtonType {
Layout.fillWidth: true Layout.fillWidth: true
text: "Remove server from application" text: qsTr("Remove server from application")
textColor: "#EB5757" textColor: "#EB5757"
clickedFunction: function() { clickedFunction: function() {
@ -115,9 +131,10 @@ PageType {
DividerType {} DividerType {}
LabelWithButtonType { LabelWithButtonType {
visible: content.isServerWithWriteAccess
Layout.fillWidth: true Layout.fillWidth: true
text: "Clear server from Amnezia software" text: qsTr("Clear server from Amnezia software")
textColor: "#EB5757" textColor: "#EB5757"
clickedFunction: function() { clickedFunction: function() {
@ -142,7 +159,9 @@ PageType {
} }
} }
DividerType {} DividerType {
visible: content.isServerWithWriteAccess
}
QuestionDrawer { QuestionDrawer {
id: questionDrawer id: questionDrawer

View file

@ -54,7 +54,13 @@ PageType {
actionButtonImage: "qrc:/images/controls/edit-3.svg" actionButtonImage: "qrc:/images/controls/edit-3.svg"
headerText: name headerText: name
descriptionText: credentialsLogin + " · " + hostName descriptionText: {
if (ServersModel.isCurrentlyProcessedServerHasWriteAccess()) {
return credentialsLogin + " · " + hostName
} else {
return hostName
}
}
actionButtonFunction: function() { actionButtonFunction: function() {
serverNameEditDrawer.visible = true serverNameEditDrawer.visible = true
@ -123,10 +129,14 @@ PageType {
} }
TabButtonType { TabButtonType {
visible: protocolsPage.installedProtocolsCount
width: protocolsPage.installedProtocolsCount ? undefined : 0
isSelected: tabBar.currentIndex === 0 isSelected: tabBar.currentIndex === 0
text: qsTr("Protocols") text: qsTr("Protocols")
} }
TabButtonType { TabButtonType {
visible: servicesPage.installedServicesCount
width: servicesPage.installedServicesCount ? undefined : 0
isSelected: tabBar.currentIndex === 1 isSelected: tabBar.currentIndex === 1
text: qsTr("Services") text: qsTr("Services")
} }
@ -143,9 +153,11 @@ PageType {
currentIndex: tabBar.currentIndex currentIndex: tabBar.currentIndex
PageSettingsServerProtocols { PageSettingsServerProtocols {
id: protocolsPage
stackView: root.stackView stackView: root.stackView
} }
PageSettingsServerServices { PageSettingsServerServices {
id: servicesPage
stackView: root.stackView stackView: root.stackView
} }
PageSettingsServerData { PageSettingsServerData {

View file

@ -7,6 +7,7 @@ import SortFilterProxyModel 0.2
import PageEnum 1.0 import PageEnum 1.0
import ProtocolEnum 1.0 import ProtocolEnum 1.0
import ContainerProps 1.0 import ContainerProps 1.0
import ContainersModelFilters 1.0
import "./" import "./"
import "../Controls2" import "../Controls2"
@ -17,22 +18,31 @@ import "../Components"
PageType { PageType {
id: root id: root
SortFilterProxyModel { property var installedProtocolsCount
id: containersProxyModel
sourceModel: ContainersModel
filters: [
ValueFilter {
roleName: "serviceType"
value: ProtocolEnum.Vpn
},
ValueFilter {
roleName: "isSupported"
value: true
}
]
}
SettingsContainersListView { SettingsContainersListView {
model: containersProxyModel Connections {
target: ServersModel
function onCurrentlyProcessedServerIndexChanged() {
updateContainersModelFilters()
}
}
function updateContainersModelFilters() {
if (ServersModel.isCurrentlyProcessedServerHasWriteAccess()) {
proxyContainersModel.filters = ContainersModelFilters.getWriteAccessProtocolsListFilters()
} else {
proxyContainersModel.filters = ContainersModelFilters.getReadAccessProtocolsListFilters()
}
root.installedProtocolsCount = proxyContainersModel.count
}
model: SortFilterProxyModel {
id: proxyContainersModel
sourceModel: ContainersModel
}
Component.onCompleted: updateContainersModelFilters()
} }
} }

View file

@ -7,6 +7,7 @@ import SortFilterProxyModel 0.2
import PageEnum 1.0 import PageEnum 1.0
import ProtocolEnum 1.0 import ProtocolEnum 1.0
import ContainerProps 1.0 import ContainerProps 1.0
import ContainersModelFilters 1.0
import "./" import "./"
import "../Controls2" import "../Controls2"
@ -17,22 +18,31 @@ import "../Components"
PageType { PageType {
id: root id: root
SortFilterProxyModel { property var installedServicesCount
id: containersProxyModel
sourceModel: ContainersModel
filters: [
ValueFilter {
roleName: "serviceType"
value: ProtocolEnum.Other
},
ValueFilter {
roleName: "isSupported"
value: true
}
]
}
SettingsContainersListView { SettingsContainersListView {
model: containersProxyModel Connections {
target: ServersModel
function onCurrentlyProcessedServerIndexChanged() {
updateContainersModelFilters()
}
}
function updateContainersModelFilters() {
if (ServersModel.isCurrentlyProcessedServerHasWriteAccess()) {
proxyContainersModel.filters = ContainersModelFilters.getWriteAccessServicesListFilters()
} else {
proxyContainersModel.filters = ContainersModelFilters.getReadAccessServicesListFilters()
}
root.installedServicesCount = proxyContainersModel.count
}
model: SortFilterProxyModel {
id: proxyContainersModel
sourceModel: ContainersModel
}
Component.onCompleted: updateContainersModelFilters()
} }
} }

View file

@ -89,8 +89,7 @@ PageType {
rightImageSource: "qrc:/images/controls/chevron-right.svg" rightImageSource: "qrc:/images/controls/chevron-right.svg"
clickedFunction: function() { clickedFunction: function() {
ServersModel.setCurrentlyProcessedServerIndex(index) ServersModel.currentlyProcessedIndex = index
ContainersModel.setCurrentlyProcessedServerIndex(index)
goToPage(PageEnum.PageSettingsServerInfo) goToPage(PageEnum.PageSettingsServerInfo)
} }
} }

View file

@ -79,6 +79,10 @@ PageType {
text: qsTr("Set up a server the easy way") text: qsTr("Set up a server the easy way")
onClicked: function() { onClicked: function() {
if (!isCredentialsFilled()) {
return
}
InstallController.setShouldCreateServer(true) InstallController.setShouldCreateServer(true)
InstallController.setCurrentlyInstalledServerCredentials(hostname.textField.text, username.textField.text, secretData.textField.text) InstallController.setCurrentlyInstalledServerCredentials(hostname.textField.text, username.textField.text, secretData.textField.text)
@ -100,6 +104,10 @@ PageType {
text: qsTr("Select protocol to install") text: qsTr("Select protocol to install")
onClicked: function() { onClicked: function() {
if (!isCredentialsFilled()) {
return
}
InstallController.setShouldCreateServer(true) InstallController.setShouldCreateServer(true)
InstallController.setCurrentlyInstalledServerCredentials(hostname.textField.text, username.textField.text, secretData.textField.text) InstallController.setCurrentlyInstalledServerCredentials(hostname.textField.text, username.textField.text, secretData.textField.text)
@ -108,4 +116,25 @@ PageType {
} }
} }
} }
function isCredentialsFilled() {
var hasEmptyField = false
if (hostname.textFieldText === "") {
hostname.errorText = qsTr("ip address cannot be empty")
hasEmptyField = true
} else if (!hostname.textField.acceptableInput) {
hostname.errorText = qsTr("Enter the address in the format 255.255.255.255:88")
}
if (username.textFieldText === "") {
username.errorText = qsTr("login cannot be empty")
hasEmptyField = true
}
if (secretData.textFieldText === "") {
secretData.errorText = qsTr("password/private key cannot be empty")
hasEmptyField = true
}
return !hasEmptyField
}
} }

View file

@ -61,7 +61,7 @@ PageType {
function onServerAlreadyExists(serverIndex) { function onServerAlreadyExists(serverIndex) {
goToStartPage() goToStartPage()
ServersModel.setCurrentlyProcessedServerIndex(serverIndex) ServersModel.currentlyProcessedIndex = serverIndex
goToPage(PageEnum.PageSettingsServerInfo, false) goToPage(PageEnum.PageSettingsServerInfo, false)
PageController.showErrorMessage(qsTr("The server has already been added to the application")) PageController.showErrorMessage(qsTr("The server has already been added to the application"))

View file

@ -147,18 +147,28 @@ PageType {
imageSource: "qrc:/images/controls/chevron-right.svg" imageSource: "qrc:/images/controls/chevron-right.svg"
model: ServersModel model: SortFilterProxyModel {
currentIndex: ServersModel.getDefaultServerIndex() id: proxyServersModel
sourceModel: ServersModel
filters: [
ValueFilter {
roleName: "hasWriteAccess"
value: true
}
]
}
currentIndex: 0
clickedFunction: function() { clickedFunction: function() {
serverSelector.text = selectedText serverSelector.text = selectedText
ContainersModel.setCurrentlyProcessedServerIndex(currentIndex) ServersModel.currentlyProcessedIndex = currentIndex
protocolSelector.visible = true protocolSelector.visible = true
} }
Component.onCompleted: { Component.onCompleted: {
serverSelector.text = selectedText serverSelector.text = selectedText
ContainersModel.setCurrentlyProcessedServerIndex(currentIndex) ServersModel.currentlyProcessedIndex = currentIndex
} }
} }
@ -169,7 +179,7 @@ PageType {
height: parent.height * 0.5 height: parent.height * 0.5
ColumnLayout { ColumnLayout {
id: header id: protocolSelectorHeader
anchors.top: parent.top anchors.top: parent.top
anchors.left: parent.left anchors.left: parent.left
@ -187,12 +197,12 @@ PageType {
} }
FlickableType { FlickableType {
anchors.top: header.bottom anchors.top: protocolSelectorHeader.bottom
anchors.topMargin: 16 anchors.topMargin: 16
contentHeight: col.implicitHeight contentHeight: protocolSelectorContent.implicitHeight
Column { Column {
id: col id: protocolSelectorContent
anchors.top: parent.top anchors.top: parent.top
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
@ -265,7 +275,7 @@ PageType {
} }
DropDownType { DropDownType {
id: connectionTypeSelector id: exportTypeSelector
property int currentIndex property int currentIndex
@ -283,8 +293,6 @@ PageType {
headerText: qsTr("Connection format") headerText: qsTr("Connection format")
listView: ListViewType { listView: ListViewType {
id: connectionTypeSelectorListView
rootWidth: root.width rootWidth: root.width
dividerVisible: true dividerVisible: true
@ -294,14 +302,14 @@ PageType {
currentIndex: 0 currentIndex: 0
clickedFunction: function() { clickedFunction: function() {
connectionTypeSelector.text = selectedText exportTypeSelector.text = selectedText
connectionTypeSelector.currentIndex = currentIndex exportTypeSelector.currentIndex = currentIndex
connectionTypeSelector.menuVisible = false exportTypeSelector.menuVisible = false
} }
Component.onCompleted: { Component.onCompleted: {
connectionTypeSelector.text = selectedText exportTypeSelector.text = selectedText
connectionTypeSelector.currentIndex = currentIndex exportTypeSelector.currentIndex = currentIndex
} }
} }
} }

View file

@ -50,8 +50,7 @@ PageType {
Component.onCompleted: { Component.onCompleted: {
var pagePath = PageController.getPagePath(PageEnum.PageHome) var pagePath = PageController.getPagePath(PageEnum.PageHome)
ServersModel.setCurrentlyProcessedServerIndex(ServersModel.getDefaultServerIndex()) ServersModel.currentlyProcessedIndex = ServersModel.defaultIndex
ContainersModel.setCurrentlyProcessedServerIndex(ServersModel.getDefaultServerIndex())
tabBarStackView.push(pagePath, { "objectName" : pagePath }) tabBarStackView.push(pagePath, { "objectName" : pagePath })
} }
} }
@ -65,8 +64,8 @@ PageType {
topPadding: 8 topPadding: 8
bottomPadding: 8//34 bottomPadding: 8//34
leftPadding: 96 leftPadding: shareTabButton.visible ? 96 : 128
rightPadding: 96 rightPadding: shareTabButton.visible ? 96 : 128
background: Rectangle { background: Rectangle {
border.width: 1 border.width: 1
@ -78,11 +77,25 @@ PageType {
isSelected: tabBar.currentIndex === 0 isSelected: tabBar.currentIndex === 0
image: "qrc:/images/controls/home.svg" image: "qrc:/images/controls/home.svg"
onClicked: { onClicked: {
ContainersModel.setCurrentlyProcessedServerIndex(ServersModel.getDefaultServerIndex()) ServersModel.currentlyProcessedIndex = ServersModel.defaultIndex
tabBarStackView.goToTabBarPage(PageEnum.PageHome) tabBarStackView.goToTabBarPage(PageEnum.PageHome)
} }
} }
TabImageButtonType { TabImageButtonType {
id: shareTabButton
Connections {
target: ServersModel
function onDefaultServerIndexChanged() {
shareTabButton.visible = ServersModel.isCurrentlyProcessedServerHasWriteAccess()
shareTabButton.width = ServersModel.isCurrentlyProcessedServerHasWriteAccess() ? undefined : 0
}
}
visible: ServersModel.isCurrentlyProcessedServerHasWriteAccess()
width: visible ? undefined : 0
isSelected: tabBar.currentIndex === 1 isSelected: tabBar.currentIndex === 1
image: "qrc:/images/controls/share-2.svg" image: "qrc:/images/controls/share-2.svg"
onClicked: { onClicked: {
@ -100,8 +113,8 @@ PageType {
MouseArea { MouseArea {
anchors.fill: tabBar anchors.fill: tabBar
anchors.leftMargin: 96 anchors.leftMargin: shareTabButton.visible ? 96 : 128
anchors.rightMargin: 96 anchors.rightMargin: shareTabButton.visible ? 96 : 128
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
enabled: false enabled: false
} }