Compare commits

...

71 Commits

Author SHA1 Message Date
vladimir.kuznetsov
3883b8ff34 fixed initial value of m_drawerDepth 2024-02-28 17:31:46 +03:00
pokamest
d286664763 Merge pull request #649 from sa6ta6ni6c/patch-1
Ru translation update
2024-02-28 05:31:14 -08:00
Nethius
b05ad2392b added escape key handler (#461)
Added escape key handler for drawer2type
2024-02-28 12:39:28 +00:00
Nethius
6dbdb85aaf fixed "file does not exist" error when opening a file for saving (#636) 2024-02-28 12:32:25 +00:00
sa6ta6ni6c
26b48cfe4f Обновил перевод 2024-02-27 20:47:41 +00:00
Andrey Zaharow
2f39136143 Fix translations (#646)
Fix awg texts
2024-02-26 21:17:57 +00:00
pokamest
8d0d3c5ce9 Merge pull request #641 from amnezia-vpn/feature/linux-ipv6
Remove ipv6 address for Linux WG/AWG interface
2024-02-26 12:07:50 -08:00
lunardunno
256081e4ed Improved server cleaning (#639)
Deleting the amnezia directory in opt when cleaning the server.
2024-02-26 12:35:31 +00:00
pokamest
1dd7b0a221 Merge pull request #647 from amnezia-vpn/bugfix/ru-translations
fixed ru translations file
2024-02-26 04:17:42 -08:00
vladimir.kuznetsov
82c0b28906 fixed ru translations file 2024-02-26 17:12:46 +05:00
KsZnak
985fe083f0 split-tunneling translate (#640)
Update amneziavpn_ru.ts
2024-02-26 11:53:22 +00:00
pokamest
6a0000dc4b Merge pull request #642 from amnezia-vpn/translations/update_zh_CN
Update amneziavpn_zh_CN.ts
2024-02-26 03:47:31 -08:00
pokamest
1dd2f38066 Merge pull request #643 from amnezia-vpn/translations/update_fa_IR.ts
Update amneziavpn_fa_IR.ts
2024-02-26 03:45:04 -08:00
pokamest
004e1e3ca5 MacOS GH actions QIF fix (#645)
Install Qt Installer Framework 4.6 from R2 to keep compatibility for old MacOS. In addition, update Qt version in build scripts.
2024-02-26 10:44:28 +00:00
KsZnak
7c560d709b Update amneziavpn_fa_IR.ts 2024-02-24 22:16:18 +02:00
KsZnak
d3743ad62f Update amneziavpn_zh_CN.ts 2024-02-24 22:06:05 +02:00
pokamest
ac234b77e2 Merge pull request #638 from amnezia-vpn/update/update_ts
Update all translations
2024-02-24 06:59:50 -08:00
Mykola Baibuz
9886987e68 Remove ipv6 address for Linux WG/AWG interface 2024-02-24 16:07:59 +02:00
pokamest
d34cb8898f Update all translations 2024-02-24 11:51:22 +00:00
pokamest
13aadbda64 Merge pull request #637 from amnezia-vpn/bugfix/connection-drawer-close-button
fixed connection drawer close button
2024-02-23 09:56:52 -08:00
agalehaga
c7c7c8eb01 added export awg native format (#635)
add export awg native format
2024-02-23 17:55:59 +00:00
vladimir.kuznetsov
b1e5bba33f fixed connection drawer close button 2024-02-23 22:51:46 +05:00
pokamest
474e7c6d62 Merge pull request #634 from amnezia-vpn/update/gh_actions_qt_update
Update Qt in deploy.yml
2024-02-22 06:02:04 -08:00
pokamest
794ec921b8 Update Qt in deploy.yml 2024-02-22 13:28:37 +00:00
pokamest
b674240362 Merge pull request #632 from amnezia-vpn/refactoring/changing-settings-item-location
moving settings item to other settings page
2024-02-22 05:02:13 -08:00
pokamest
a768c7c451 Merge pull request #633 from rodionos/patch-1
MacOS build: increase image size to 256Mb
2024-02-22 04:58:21 -08:00
Sergei Rodionov
28d2a4ec2c MacOS build: increase image size to 256Mb
In my case, using Qt 6.6.2, the size of the AmneziaVPN.dmg file is 226Mb so a higher image size is needed for the hdiutil command.
2024-02-22 13:57:58 +03:00
Shehab Ahmed
9f1210d18f changed the location of Auto connect item from settings connections page to settings application page 2024-02-22 02:31:51 +02:00
pokamest
3012559627 Merge pull request #630 from amnezia-vpn/feature/api-containers-listview
for api servers, removed the ability to select a container
2024-02-21 11:03:18 -08:00
vladimir.kuznetsov
b3ed57aee7 for api servers, removed the ability to select a container 2024-02-21 23:41:47 +05:00
pokamest
89d0a8107d Merge pull request #620 from amnezia-vpn/translations/fix-for-pr618
Fix translation for #618
2024-02-21 06:03:12 -08:00
Andrey Zaharow
6c0b71bd1b Fix translation on About Page (#618)
Fix About Page
2024-02-21 14:01:53 +00:00
Nethius
61abf74b2d feature/page-home-split-tunneling (#540)
Added split tunneling button on home page
2024-02-21 11:27:27 +00:00
pokamest
21fdf02921 Merge pull request #625 from amnezia-vpn/bugfix/default-server-default-container-update
fixed the use of defaultServerDefaultContainerChanged
2024-02-21 03:22:09 -08:00
vladimir.kuznetsov
7a245d80ee fixed the use of defaultServerDefaultContainerChanged 2024-02-21 13:06:39 +05:00
KsZnak
da85922f23 Update amneziavpn_zh_CN.ts (#617)
Update amneziavpn_zh_CN.ts
2024-02-20 20:49:26 +00:00
pokamest
a5356b6319 Merge pull request #623 from amnezia-vpn/update/Arabic-translation
updated the Arabic translation for fixing some sentences
2024-02-20 12:47:41 -08:00
KsZnak
3828891b9b Update amneziavpn_fa_IR.ts (#622)
Update amneziavpn_fa_IR.ts
2024-02-20 20:46:23 +00:00
pokamest
15d866ce04 WG/AWG ipv6 fix (#621)
WG/AWG ipv6 fix
2024-02-20 19:05:36 +00:00
Shehab Ahmed
560eb3d620 updated the Arabic translation for fixing some sentences 2024-02-20 20:37:19 +02:00
Andrey Zaharow
ac894254cc Fix translation for #618 2024-02-20 00:23:20 +01:00
pokamest
17e3fbde25 Merge pull request #616 from amnezia-vpn/bugfix/cursor-changing-fix
Fix cursor change when hover over elements
2024-02-19 12:20:46 -08:00
Andrey Zaharow
ee11a8410c Fix cursor change when hover over elements 2024-02-19 18:28:29 +01:00
pokamest
ff5c51cfd9 Merge pull request #615 from amnezia-vpn/KsZnak-patch-1
Update amneziavpn_ru.ts
2024-02-19 07:10:49 -08:00
Nethius
b3943ae5e3 serversModel cleanup (#599) 2024-02-19 14:54:15 +00:00
pokamest
a32952fde6 Qt.ImhNoAutoUppercase | Qt.ImhSensitiveData | Qt.ImhNoPredictiveText for
all TextFields
2024-02-19 14:06:18 +00:00
isamnezia
9c4ee4014d Fix for Codacy: variable name should be between 3 and 40 characters long (#608)
Tiny fixes for iOS
2024-02-19 13:13:10 +00:00
KsZnak
dc9069f1f4 Update_2_amneziavpn_ru.ts
Add new change
2024-02-19 13:37:40 +02:00
pokamest
e402cacc05 Merge pull request #614 from amnezia-vpn/bugfix/translations
returned translation files to commit fab167bb34a9f7199359e3d8589a1cd1…
2024-02-19 02:23:39 -08:00
vladimir.kuznetsov
a98cd248d6 returned translation files to commit fab167bb34 2024-02-19 09:31:31 +05:00
pokamest
00fbfb6a01 Merge pull request #611 from amnezia-vpn/refactoring/show-installed-containers-first
show installed protocols first
2024-02-18 11:10:37 -08:00
vladimir.kuznetsov
86c31c3766 show installed protocols first in services tab and page home containers listview 2024-02-18 13:24:21 +05:00
agalehaga
698cfe910c add navigation using enter + buttons will be clicked if enter (if but… (#556)
Enter navigation
2024-02-17 21:09:05 +00:00
pokamest
16db23c159 Rewrite sftp file copy to Qt way (#562)
Rewrite sftp file copy to Qt way
2024-02-17 21:07:17 +00:00
Andrey Zaharow
b05a5ee1c6 fix connection button behavior (#595)
Fix connection button behavior
2024-02-17 19:57:31 +00:00
pokamest
8cb298937f Merge pull request #604 from amnezia-vpn/KsZnak-ru_translate
Update amneziavpn_ru.ts
2024-02-17 11:52:18 -08:00
Andrey Zaharow
68fe20ddf6 UI fixes (#596)
UI fixes
2024-02-17 19:48:41 +00:00
KsZnak
fab167bb34 Update amneziavpn_ru.ts 2024-02-17 20:29:25 +02:00
isamnezia
f640d4b5f5 Remove config string dependency (#577)
Remove WG/AWG config string dependency
2024-02-16 10:30:00 +00:00
Nethius
074562b141 feature/custom-drawer (#563)
Replaced all the DrawerType with DrawerType2
2024-02-16 10:24:06 +00:00
Shehab Ahmed
fd030a5fd4 Arabic translation (#594)
added Arabic translation
2024-02-16 10:19:47 +00:00
albexk
82fa6b13c6 Fix foreground service type (#592)
Fix foreground service type
2024-02-14 16:35:40 +00:00
pokamest
bf16298c40 Version bump - 4.4.0.0 2024-02-13 21:10:47 +00:00
pokamest
bcebb0a2b5 Merge pull request #580 from amnezia-vpn/feature/update-cloak-binary
Update AWG and Cloak libraries
2024-02-13 12:03:02 -08:00
pokamest
b27442cf74 Merge pull request #583 from amnezia-vpn/bugfix/double_clear_server_from_amnezia
fixed bug with double button clear server from amnezia software
2024-02-13 07:50:35 -08:00
Nethius
92fbbd4812 bugfix/default-container-index (#578)
fixed get/set DefaultContainer
2024-02-13 15:20:13 +00:00
agalehaga
321ed810e3 fixed bug with double button clear server from amnezia software 2024-02-13 15:16:04 +02:00
albexk
17ff530683 Merge branch 'fix/android' into feature/update-cloak-binary 2024-02-13 12:32:36 +03:00
pokamest
a416d03614 Merge pull request #581 from amnezia-vpn/fix/amn-go-version
Update amneziawg-apple to amneziawg-go v0.2.1
2024-02-12 13:08:19 -08:00
Igor Sorokin
4de9a274dd Update amneziawg-apple to amneziawg-go v0.2.1 2024-02-12 23:25:11 +03:00
Mykola Baibuz
0b8f3c9d9d Update Cloak binary to v2.8.0 2024-02-12 21:01:44 +02:00
96 changed files with 7015 additions and 2941 deletions

View File

@@ -14,8 +14,8 @@ jobs:
runs-on: ubuntu-20.04
env:
QT_VERSION: 6.5.1
QIF_VERSION: 4.6
QT_VERSION: 6.6.2
QIF_VERSION: 4.7
steps:
- name: 'Install Qt'
@@ -72,8 +72,8 @@ jobs:
runs-on: windows-latest
env:
QT_VERSION: 6.5.1
QIF_VERSION: 4.6
QT_VERSION: 6.6.2
QIF_VERSION: 4.7
BUILD_ARCH: 64
steps:
@@ -134,7 +134,7 @@ jobs:
runs-on: macos-13
env:
QT_VERSION: 6.5.2
QT_VERSION: 6.6.2
CC: cc
CXX: c++
@@ -245,10 +245,15 @@ jobs:
modules: 'qtremoteobjects qt5compat qtshadertools'
dir: ${{ runner.temp }}
setup-python: 'true'
tools: 'tools_ifw'
set-env: 'true'
extra: '--external 7z --base ${{ env.QT_MIRROR }}'
- name: 'Install Qt Installer Framework ${{ env.QIF_VERSION }}'
run: |
mkdir -pv ${{ runner.temp }}/Qt/Tools/QtInstallerFramework
wget https://qt.amzsvc.com/tools/ifw/${{ env.QIF_VERSION }}.zip
unzip ${{ env.QIF_VERSION }}.zip -d ${{ runner.temp }}/Qt/Tools/QtInstallerFramework/
- name: 'Get sources'
uses: actions/checkout@v4
with:
@@ -286,7 +291,7 @@ jobs:
env:
ANDROID_BUILD_PLATFORM: android-34
QT_VERSION: 6.6.1
QT_VERSION: 6.6.2
QT_MODULES: 'qtremoteobjects qt5compat qtimageformats qtshadertools'
steps:

3
.gitignore vendored
View File

@@ -131,3 +131,6 @@ client/3rd/ShadowSocks/ss_ios.xcconfig
# UML generated pics
out/
# CMake files
CMakeFiles/

View File

@@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.25.0 FATAL_ERROR)
set(PROJECT AmneziaVPN)
project(${PROJECT} VERSION 4.3.0.0
project(${PROJECT} VERSION 4.4.0.0
DESCRIPTION "AmneziaVPN"
HOMEPAGE_URL "https://amnezia.org/"
)
@@ -11,7 +11,7 @@ string(TIMESTAMP CURRENT_DATE "%Y-%m-%d")
set(RELEASE_DATE "${CURRENT_DATE}")
set(APP_MAJOR_VERSION ${CMAKE_PROJECT_VERSION_MAJOR}.${CMAKE_PROJECT_VERSION_MINOR}.${CMAKE_PROJECT_VERSION_PATCH})
set(APP_ANDROID_VERSION_CODE 44)
set(APP_ANDROID_VERSION_CODE 46)
if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
set(MZ_PLATFORM_NAME "linux")

View File

@@ -15,6 +15,15 @@ set(PACKAGES
Core5Compat Concurrent LinguistTools
)
execute_process(
WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
COMMAND git rev-parse --short HEAD
OUTPUT_VARIABLE GIT_COMMIT_HASH
OUTPUT_STRIP_TRAILING_WHITESPACE
)
add_definitions(-DGIT_COMMIT_HASH="${GIT_COMMIT_HASH}")
if(IOS)
set(PACKAGES ${PACKAGES} Multimedia)
endif()
@@ -57,6 +66,7 @@ set(AMNEZIAVPN_TS_FILES
${CMAKE_CURRENT_LIST_DIR}/translations/amneziavpn_ru.ts
${CMAKE_CURRENT_LIST_DIR}/translations/amneziavpn_zh_CN.ts
${CMAKE_CURRENT_LIST_DIR}/translations/amneziavpn_fa_IR.ts
${CMAKE_CURRENT_LIST_DIR}/translations/amneziavpn_ar.ts
)
file(GLOB_RECURSE AMNEZIAVPN_TS_SOURCES *.qrc *.cpp *.h *.ui)

View File

@@ -286,13 +286,16 @@ void AmneziaApplication::initModels()
m_containersModel.reset(new ContainersModel(this));
m_engine->rootContext()->setContextProperty("ContainersModel", m_containersModel.get());
m_defaultServerContainersModel.reset(new ContainersModel(this));
m_engine->rootContext()->setContextProperty("DefaultServerContainersModel", m_defaultServerContainersModel.get());
m_serversModel.reset(new ServersModel(m_settings, this));
m_engine->rootContext()->setContextProperty("ServersModel", m_serversModel.get());
connect(m_serversModel.get(), &ServersModel::containersUpdated, m_containersModel.get(),
&ContainersModel::updateModel);
connect(m_serversModel.get(), &ServersModel::defaultContainerChanged, m_containersModel.get(),
&ContainersModel::setDefaultContainer);
m_containersModel->setDefaultContainer(m_serversModel->getDefaultContainer()); // make better?
connect(m_serversModel.get(), &ServersModel::defaultServerContainersUpdated, m_defaultServerContainersModel.get(),
&ContainersModel::updateModel);
m_serversModel->resetModel();
m_languageModel.reset(new LanguageModel(m_settings, this));
m_engine->rootContext()->setContextProperty("LanguageModel", m_languageModel.get());
@@ -336,7 +339,7 @@ void AmneziaApplication::initModels()
connect(m_configurator.get(), &VpnConfigurator::newVpnConfigCreated, this,
[this](const QString &clientId, const QString &clientName, const DockerContainer container,
ServerCredentials credentials) {
m_serversModel->reloadContainerConfig();
m_serversModel->reloadDefaultServerContainerConfig();
m_clientManagementModel->appendClient(clientId, clientName, container, credentials);
emit m_configurator->clientModelUpdated();
});
@@ -388,7 +391,13 @@ void AmneziaApplication::initControllers()
m_engine->rootContext()->setContextProperty("ApiController", m_apiController.get());
connect(m_apiController.get(), &ApiController::updateStarted, this,
[this]() { emit m_vpnConnection->connectionStateChanged(Vpn::ConnectionState::Connecting); });
connect(m_apiController.get(), &ApiController::errorOccurred, this,
[this]() { emit m_vpnConnection->connectionStateChanged(Vpn::ConnectionState::Disconnected); });
connect(m_apiController.get(), &ApiController::updateFinished, m_connectionController.get(), &ConnectionController::toggleConnection);
connect(m_apiController.get(), &ApiController::errorOccurred, this, [this](const QString &errorMessage) {
if (m_connectionController->isConnectionInProgress()) {
emit m_pageController->showErrorMessage(errorMessage);
}
emit m_vpnConnection->connectionStateChanged(Vpn::ConnectionState::Disconnected);
});
connect(m_apiController.get(), &ApiController::updateFinished, m_connectionController.get(),
&ConnectionController::toggleConnection);
}

View File

@@ -92,6 +92,7 @@ private:
QCommandLineParser m_parser;
QSharedPointer<ContainersModel> m_containersModel;
QSharedPointer<ContainersModel> m_defaultServerContainersModel;
QSharedPointer<ServersModel> m_serversModel;
QSharedPointer<LanguageModel> m_languageModel;
QSharedPointer<ProtocolsModel> m_protocolsModel;

View File

@@ -22,7 +22,7 @@
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="28" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_SPECIAL_USE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_SYSTEM_EXEMPTED" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<!-- Enable when VPN-per-app mode will be implemented -->
@@ -137,14 +137,13 @@
android:name=".AmneziaVpnService"
android:process=":amneziaVpnService"
android:permission="android.permission.BIND_VPN_SERVICE"
android:foregroundServiceType="specialUse"
android:exported="false">
android:foregroundServiceType="systemExempted"
android:exported="false"
tools:ignore="ForegroundServicePermission">
<intent-filter>
<action android:name="android.net.VpnService" />
</intent-filter>
<property android:name="android.app.PROPERTY_SPECIAL_USE_FGS_SUBTYPE" android:value="vpn" />
</service>
<provider

View File

@@ -4,7 +4,7 @@ import android.app.Notification
import android.app.PendingIntent
import android.content.Intent
import android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_MANIFEST
import android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_SPECIAL_USE
import android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_SYSTEM_EXEMPTED
import android.net.VpnService
import android.os.Build
import android.os.Handler
@@ -156,7 +156,7 @@ class AmneziaVpnService : VpnService() {
*/
private val foregroundServiceTypeCompat
get() = when {
Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE -> FOREGROUND_SERVICE_TYPE_SPECIAL_USE
Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE -> FOREGROUND_SERVICE_TYPE_SYSTEM_EXEMPTED
Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q -> FOREGROUND_SERVICE_TYPE_MANIFEST
else -> 0
}

View File

@@ -211,13 +211,7 @@ ErrorCode ServerController::uploadFileToHost(const ServerCredentials &credential
localFile.write(data);
localFile.close();
#ifdef Q_OS_WINDOWS
error = m_sshClient.sftpFileCopy(overwriteMode, localFile.fileName().toLocal8Bit().toStdString(), remotePath.toStdString(),
"non_desc");
#else
error = m_sshClient.sftpFileCopy(overwriteMode, localFile.fileName().toStdString(), remotePath.toStdString(),
"non_desc");
#endif
error = m_sshClient.sftpFileCopy(overwriteMode, localFile.fileName(), remotePath, "non_desc");
if (error != ErrorCode::NoError) {
return error;

View File

@@ -222,7 +222,7 @@ namespace libssh {
return fromLibsshErrorCode();
}
ErrorCode Client::sftpFileCopy(const SftpOverwriteMode overwriteMode, const std::string& localPath, const std::string& remotePath, const std::string& fileDesc)
ErrorCode Client::sftpFileCopy(const SftpOverwriteMode overwriteMode, const QString& localPath, const QString& remotePath, const QString &fileDesc)
{
m_sftpSession = sftp_new(m_session);
@@ -245,40 +245,38 @@ namespace libssh {
const size_t bufferSize = 16384;
char buffer[bufferSize];
file = sftp_open(m_sftpSession, remotePath.c_str(), accessType, S_IRWXU);
file = sftp_open(m_sftpSession, remotePath.toStdString().c_str(), accessType, S_IRWXU);
if (file == nullptr) {
return closeSftpSession();
}
int localFileSize = std::filesystem::file_size(localPath);
int localFileSize = QFileInfo(localPath).size();
int chunksCount = localFileSize / (bufferSize);
std::ifstream fin(localPath, std::ios::binary | std::ios::in);
QFile fin(localPath);
if (fin.is_open()) {
if (fin.open(QIODevice::ReadOnly)) {
for (int currentChunkId = 0; currentChunkId < chunksCount; currentChunkId++) {
fin.read(buffer, bufferSize);
QByteArray chunk = fin.read(bufferSize);
if (chunk.size() != bufferSize) return ErrorCode::SshSftpEofError;
int bytesWritten = sftp_write(file, buffer, bufferSize);
int bytesWritten = sftp_write(file, chunk.data(), chunk.size());
std::string chunk(buffer, bufferSize);
if (bytesWritten != bufferSize) {
if (bytesWritten != chunk.size()) {
fin.close();
sftp_close(file);
return closeSftpSession();
}
}
int lastChunkSize = localFileSize % (bufferSize);
int lastChunkSize = localFileSize % bufferSize;
if (lastChunkSize != 0) {
fin.read(buffer, lastChunkSize);
QByteArray lastChunk = fin.read(lastChunkSize);
if (lastChunk.size() != lastChunkSize) return ErrorCode::SshSftpEofError;
std::string chunk(buffer, lastChunkSize);
int bytesWritten = sftp_write(file, buffer, lastChunkSize);
int bytesWritten = sftp_write(file, lastChunk.data(), lastChunkSize);
if (bytesWritten != lastChunkSize) {
fin.close();

View File

@@ -33,9 +33,9 @@ namespace libssh {
const std::function<ErrorCode (const QString &, Client &)> &cbReadStdErr);
ErrorCode writeResponse(const QString &data);
ErrorCode sftpFileCopy(const SftpOverwriteMode overwriteMode,
const std::string& localPath,
const std::string& remotePath,
const std::string& fileDesc);
const QString &localPath,
const QString &remotePath,
const QString& fileDesc);
ErrorCode getDecryptedPrivateKey(const ServerCredentials &credentials, QString &decryptedPrivateKey, const std::function<QString()> &passphraseCallback);
private:
ErrorCode closeChannel();

View File

@@ -0,0 +1,6 @@
<svg width="19" height="18" viewBox="0 0 19 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="0.5" width="18" height="18" rx="5" fill="white"/>
<path d="M8.49219 13.5L8.49219 9.44141L14.0191 4.99484" stroke="#0E0E11" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M4.47363 5.49805L6.98828 8.0127" stroke="#0E0E11" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M14.4727 9.5L14.4727 4.5033L9.50195 4.5033" stroke="#0E0E11" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 511 B

View File

@@ -85,6 +85,7 @@ target_sources(networkextension PRIVATE
${CLIENT_ROOT_DIR}/platforms/ios/LogRecord.swift
${CLIENT_ROOT_DIR}/platforms/ios/PacketTunnelProvider.swift
${CLIENT_ROOT_DIR}/platforms/ios/PacketTunnelProvider+OpenVPNAdapterDelegate.swift
${CLIENT_ROOT_DIR}/platforms/ios/WGConfig.swift
${CLIENT_ROOT_DIR}/platforms/ios/iosglue.mm
)

View File

@@ -124,7 +124,10 @@ void LocalSocketController::activate(const QJsonObject &rawConfig) {
// json.insert("hopindex", QJsonValue((double)hop.m_hopindex));
json.insert("privateKey", wgConfig.value(amnezia::config_key::client_priv_key));
json.insert("deviceIpv4Address", wgConfig.value(amnezia::config_key::client_ip));
// todo review wg ipv6
#ifdef Q_OS_MACOS
json.insert("deviceIpv6Address", "dead::1");
#endif
json.insert("serverPublicKey", wgConfig.value(amnezia::config_key::server_pub_key));
json.insert("serverPskKey", wgConfig.value(amnezia::config_key::psk_key));
json.insert("serverIpv4AddrIn", wgConfig.value(amnezia::config_key::hostName));

View File

@@ -59,10 +59,6 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
var stopHandler: (() -> Void)?
var protoType: TunnelProtoType = .none
override init() {
super.init()
}
override func handleAppMessage(_ messageData: Data, completionHandler: ((Data?) -> Void)? = nil) {
let tmpStr = String(data: messageData, encoding: .utf8)!
wg_log(.error, message: tmpStr)
@@ -71,7 +67,7 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
return
}
guard let completionHandler = completionHandler else {
guard let completionHandler else {
log(.error, message: "Missing message completion handler")
return
}
@@ -179,14 +175,16 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
return
}
let wgConfigStr = String(data: wgConfig, encoding: .utf8)!
guard let tunnelConfiguration = try? TunnelConfiguration(fromWgQuickConfig: wgConfigStr) else {
guard let wgConfigStr = try? JSONDecoder().decode(WGConfig.self, from: wgConfig).str,
let tunnelConfiguration = try? TunnelConfiguration(fromWgQuickConfig: wgConfigStr)
else {
wg_log(.error, message: "Can't parse WireGuard config")
completionHandler(nil)
return
}
log(.info, message: "wgConfig: \(wgConfigStr.replacingOccurrences(of: "\n", with: " "))")
if tunnelConfiguration.peers.first!.allowedIPs
.map({ $0.stringRepresentation })
.joined(separator: ", ") == "0.0.0.0/0, ::/0" {

View File

@@ -0,0 +1,135 @@
import Foundation
struct WGConfigData: Decodable {
let initPacketMagicHeader, responsePacketMagicHeader: String?
let underloadPacketMagicHeader, transportPacketMagicHeader: String?
let junkPacketCount, junkPacketMinSize, junkPacketMaxSize: String?
let initPacketJunkSize, responsePacketJunkSize: String?
var settings: String {
junkPacketCount == nil ? "" :
"""
Jc = \(junkPacketCount!)
Jmin = \(junkPacketMinSize!)
Jmax = \(junkPacketMaxSize!)
S1 = \(initPacketJunkSize!)
S2 = \(responsePacketJunkSize!)
H1 = \(initPacketMagicHeader!)
H2 = \(responsePacketMagicHeader!)
H3 = \(underloadPacketMagicHeader!)
H4 = \(transportPacketMagicHeader!)
"""
}
let clientIP: String
let clientPrivateKey: String
let clientPublicKey: String
let serverPublicKey: String
let presharedKey: String
let hostName: String
let port: Int
var allowedIPs: [String]
var persistentKeepAlive: String
enum CodingKeys: String, CodingKey {
case initPacketMagicHeader = "H1", responsePacketMagicHeader = "H2"
case underloadPacketMagicHeader = "H3", transportPacketMagicHeader = "H4"
case junkPacketCount = "Jc", junkPacketMinSize = "Jmin", junkPacketMaxSize = "Jmax"
case initPacketJunkSize = "S1", responsePacketJunkSize = "S2"
case clientIP = "client_ip" // "10.8.1.16"
case clientPrivateKey = "client_priv_key"
case clientPublicKey = "client_pub_key"
case serverPublicKey = "server_pub_key"
case presharedKey = "psk_key"
case allowedIPs = "allowed_ips"
case persistentKeepAlive = "persistent_keep_alive"
case hostName
case port
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.initPacketMagicHeader = try container.decodeIfPresent(String.self, forKey: .initPacketMagicHeader)
self.responsePacketMagicHeader = try container.decodeIfPresent(String.self, forKey: .responsePacketMagicHeader)
self.underloadPacketMagicHeader = try container.decodeIfPresent(String.self, forKey: .underloadPacketMagicHeader)
self.transportPacketMagicHeader = try container.decodeIfPresent(String.self, forKey: .transportPacketMagicHeader)
self.junkPacketCount = try container.decodeIfPresent(String.self, forKey: .junkPacketCount)
self.junkPacketMinSize = try container.decodeIfPresent(String.self, forKey: .junkPacketMinSize)
self.junkPacketMaxSize = try container.decodeIfPresent(String.self, forKey: .junkPacketMaxSize)
self.initPacketJunkSize = try container.decodeIfPresent(String.self, forKey: .initPacketJunkSize)
self.responsePacketJunkSize = try container.decodeIfPresent(String.self, forKey: .responsePacketJunkSize)
self.clientIP = try container.decode(String.self, forKey: .clientIP)
self.clientPrivateKey = try container.decode(String.self, forKey: .clientPrivateKey)
self.clientPublicKey = try container.decode(String.self, forKey: .clientPublicKey)
self.serverPublicKey = try container.decode(String.self, forKey: .serverPublicKey)
self.presharedKey = try container.decode(String.self, forKey: .presharedKey)
self.allowedIPs = try container.decodeIfPresent([String].self, forKey: .allowedIPs) ?? ["0.0.0.0/0", "::/0"]
self.persistentKeepAlive = try container.decodeIfPresent(String.self, forKey: .persistentKeepAlive) ?? "25"
self.hostName = try container.decode(String.self, forKey: .hostName)
self.port = try container.decode(Int.self, forKey: .port)
}
}
struct WGConfig: Decodable {
let data: WGConfigData
let configVersion: Int
let description: String
let dns1: String
let dns2: String
let hostName: String
let `protocol`: String
let splitTunnelSites: [String]
let splitTunnelType: Int
enum CodingKeys: String, CodingKey {
case awgConfigData = "awg_config_data", wgConfigData = "wireguard_config_data"
case configData
case configVersion = "config_version"
case description
case dns1
case dns2
case hostName
case `protocol`
case splitTunnelSites
case splitTunnelType
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
if container.contains(.awgConfigData) {
self.data = try container.decode(WGConfigData.self, forKey: .awgConfigData)
} else {
self.data = try container.decode(WGConfigData.self, forKey: .wgConfigData)
}
self.configVersion = try container.decode(Int.self, forKey: .configVersion)
self.description = try container.decode(String.self, forKey: .description)
self.dns1 = try container.decode(String.self, forKey: .dns1)
self.dns2 = try container.decode(String.self, forKey: .dns2)
self.hostName = try container.decode(String.self, forKey: .hostName)
self.protocol = try container.decode(String.self, forKey: .protocol)
self.splitTunnelSites = try container.decode([String].self, forKey: .splitTunnelSites)
self.splitTunnelType = try container.decode(Int.self, forKey: .splitTunnelType)
}
var str: String {
"""
[Interface]
Address = \(data.clientIP)/32
DNS = \(dns1), \(dns2)
PrivateKey = \(data.clientPrivateKey)
\(data.settings)
[Peer]
PublicKey = \(data.serverPublicKey)
PresharedKey = \(data.presharedKey)
AllowedIPs = \(data.allowedIPs.joined(separator: ", "))
Endpoint = \(data.hostName):\(data.port)
PersistentKeepalive = \(data.persistentKeepAlive)
"""
}
}

View File

@@ -400,9 +400,10 @@ bool IosController::setupCloak()
bool IosController::setupWireGuard()
{
QJsonObject config = m_rawConfig[ProtocolProps::key_proto_config_data(amnezia::Proto::WireGuard)].toObject();
QString wgConfig = config[config_key::config].toString();
QJsonDocument doc(m_rawConfig);
QString wgConfig(doc.toJson(QJsonDocument::Compact));
return startWireGuard(wgConfig);
}
@@ -410,8 +411,9 @@ bool IosController::setupAwg()
{
QJsonObject config = m_rawConfig[ProtocolProps::key_proto_config_data(amnezia::Proto::Awg)].toObject();
QString wgConfig = config[config_key::config].toString();
QJsonDocument doc(m_rawConfig);
QString wgConfig(doc.toJson(QJsonDocument::Compact));
return startWireGuard(wgConfig);
}

View File

@@ -160,7 +160,6 @@
<file>ui/qml/Components/SettingsContainersListView.qml</file>
<file>ui/qml/Controls2/TextTypes/ListItemTitleType.qml</file>
<file>ui/qml/Controls2/DividerType.qml</file>
<file>ui/qml/Controls2/DrawerType.qml</file>
<file>ui/qml/Controls2/StackViewType.qml</file>
<file>ui/qml/Pages2/PageSettings.qml</file>
<file>images/controls/amnezia.svg</file>
@@ -225,5 +224,8 @@
<file>ui/qml/Pages2/PageShareFullAccess.qml</file>
<file>images/controls/close.svg</file>
<file>images/controls/search.svg</file>
<file>ui/qml/Components/HomeSplitTunnelingDrawer.qml</file>
<file>images/controls/split-tunneling.svg</file>
<file>ui/qml/Controls2/DrawerType2.qml</file>
</qresource>
</RCC>

View File

@@ -1,4 +1,5 @@
sudo docker ps -a | grep amnezia | awk '{print $1}' | xargs sudo docker stop
sudo docker ps -a | grep amnezia | awk '{print $1}' | xargs sudo docker rm -fv
sudo docker images -a | grep amnezia | awk '{print $3}' | xargs sudo docker rmi
sudo docker network ls | grep amnezia-dns-net | awk '{print $1}' | xargs sudo docker network rm
sudo docker ps -a | grep amnezia | awk '{print $1}' | xargs sudo docker stop;\
sudo docker ps -a | grep amnezia | awk '{print $1}' | xargs sudo docker rm -fv;\
sudo docker images -a | grep amnezia | awk '{print $3}' | xargs sudo docker rmi;\
sudo docker network ls | grep amnezia-dns-net | awk '{print $1}' | xargs sudo docker network rm;\
sudo rm -frd /opt/amnezia

View File

@@ -1,3 +1,3 @@
sudo docker stop $CONTAINER_NAME
sudo docker rm -fv $CONTAINER_NAME
sudo docker stop $CONTAINER_NAME;\
sudo docker rm -fv $CONTAINER_NAME;\
sudo docker rmi $CONTAINER_NAME

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -70,14 +70,17 @@ QJsonObject ApiController::fillApiPayload(const QString &protocol, const ApiCont
void ApiController::updateServerConfigFromApi()
{
QtConcurrent::run([this]() {
if (m_isConfigUpdateStarted) {
emit updateFinished(false);
return;
}
auto serverConfig = m_serversModel->getDefaultServerConfig();
auto containerConfig = serverConfig.value(config_key::containers).toArray();
bool isConfigUpdateStarted = false;
if (serverConfig.value(config_key::configVersion).toInt() && containerConfig.isEmpty()) {
emit updateStarted();
isConfigUpdateStarted = true;
m_isConfigUpdateStarted = true;
QNetworkAccessManager manager;
@@ -110,6 +113,12 @@ void ApiController::updateServerConfigFromApi()
QByteArray ba = QByteArray::fromBase64(data.toUtf8(),
QByteArray::Base64UrlEncoding | QByteArray::OmitTrailingEquals);
if (ba.isEmpty()) {
emit errorOccurred(errorString(ApiConfigDownloadError));
m_isConfigUpdateStarted = false;
return;
}
QByteArray ba_uncompressed = qUncompress(ba);
if (!ba_uncompressed.isEmpty()) {
ba = ba_uncompressed;
@@ -127,17 +136,18 @@ void ApiController::updateServerConfigFromApi()
auto defaultContainer = apiConfig.value(config_key::defaultContainer).toString();
serverConfig.insert(config_key::defaultContainer, defaultContainer);
m_serversModel->editServer(serverConfig);
emit m_serversModel->defaultContainerChanged(ContainerProps::containerFromString(defaultContainer));
m_serversModel->editServer(serverConfig, m_serversModel->getDefaultServerIndex());
} else {
qDebug() << reply->error();
qDebug() << reply->attribute(QNetworkRequest::HttpStatusCodeAttribute);
emit errorOccurred(errorString(ApiConfigDownloadError));
m_isConfigUpdateStarted = false;
return;
}
}
emit updateFinished(isConfigUpdateStarted);
emit updateFinished(m_isConfigUpdateStarted);
m_isConfigUpdateStarted = false;
return;
});
}
@@ -153,5 +163,5 @@ void ApiController::clearApiConfig()
serverConfig.insert(config_key::defaultContainer, ContainerProps::containerToString(DockerContainer::None));
m_serversModel->editServer(serverConfig);
m_serversModel->editServer(serverConfig, m_serversModel->getDefaultServerIndex());
}

View File

@@ -39,6 +39,8 @@ private:
QSharedPointer<ServersModel> m_serversModel;
QSharedPointer<ContainersModel> m_containersModel;
bool m_isConfigUpdateStarted = false;
};
#endif // APICONTROLLER_H

View File

@@ -33,7 +33,7 @@ void ConnectionController::openConnection()
int serverIndex = m_serversModel->getDefaultServerIndex();
ServerCredentials credentials = m_serversModel->getServerCredentials(serverIndex);
DockerContainer container = m_containersModel->getDefaultContainer();
DockerContainer container = qvariant_cast<DockerContainer>(m_serversModel->data(serverIndex, ServersModel::Roles::DefaultContainerRole));
const QJsonObject &containerConfig = m_containersModel->getContainerConfig(container);
if (container == DockerContainer::None) {

View File

@@ -8,6 +8,7 @@
#include <QImage>
#include <QStandardPaths>
#include "configurators/awg_configurator.h"
#include "configurators/cloak_configurator.h"
#include "configurators/openvpn_configurator.h"
#include "configurators/shadowsocks_configurator.h"
@@ -45,7 +46,7 @@ void ExportController::generateFullAccessConfig()
{
clearPreviousConfig();
int serverIndex = m_serversModel->getCurrentlyProcessedServerIndex();
int serverIndex = m_serversModel->getProcessedServerIndex();
QJsonObject config = m_settings->server(serverIndex);
QJsonArray containers = config.value(config_key::containers).toArray();
@@ -99,7 +100,7 @@ void ExportController::generateConnectionConfig(const QString &clientName)
{
clearPreviousConfig();
int serverIndex = m_serversModel->getCurrentlyProcessedServerIndex();
int serverIndex = m_serversModel->getProcessedServerIndex();
ServerCredentials credentials = m_serversModel->getServerCredentials(serverIndex);
DockerContainer container = static_cast<DockerContainer>(m_containersModel->getCurrentlyProcessedContainerIndex());
@@ -155,7 +156,7 @@ void ExportController::generateOpenVpnConfig(const QString &clientName)
{
clearPreviousConfig();
int serverIndex = m_serversModel->getCurrentlyProcessedServerIndex();
int serverIndex = m_serversModel->getProcessedServerIndex();
ServerCredentials credentials = m_serversModel->getServerCredentials(serverIndex);
DockerContainer container = static_cast<DockerContainer>(m_containersModel->getCurrentlyProcessedContainerIndex());
@@ -193,7 +194,7 @@ void ExportController::generateWireGuardConfig(const QString &clientName)
{
clearPreviousConfig();
int serverIndex = m_serversModel->getCurrentlyProcessedServerIndex();
int serverIndex = m_serversModel->getProcessedServerIndex();
ServerCredentials credentials = m_serversModel->getServerCredentials(serverIndex);
DockerContainer container = static_cast<DockerContainer>(m_containersModel->getCurrentlyProcessedContainerIndex());
@@ -228,11 +229,50 @@ void ExportController::generateWireGuardConfig(const QString &clientName)
emit exportConfigChanged();
}
void ExportController::generateAwgConfig(const QString &clientName)
{
clearPreviousConfig();
int serverIndex = m_serversModel->getProcessedServerIndex();
ServerCredentials credentials = m_serversModel->getServerCredentials(serverIndex);
DockerContainer container = static_cast<DockerContainer>(m_containersModel->getCurrentlyProcessedContainerIndex());
QJsonObject containerConfig = m_containersModel->getContainerConfig(container);
containerConfig.insert(config_key::container, ContainerProps::containerToString(container));
QString clientId;
ErrorCode errorCode = ErrorCode::NoError;
QString config = m_configurator->awgConfigurator->genAwgConfig(credentials, container, containerConfig,
clientId, &errorCode);
if (errorCode) {
emit exportErrorOccurred(errorString(errorCode));
return;
}
config = m_configurator->processConfigWithExportSettings(serverIndex, container, Proto::Awg, config);
auto configJson = QJsonDocument::fromJson(config.toUtf8()).object();
QStringList lines = configJson.value(config_key::config).toString().replace("\r", "").split("\n");
for (const QString &line : lines) {
m_config.append(line + "\n");
}
qrcodegen::QrCode qr = qrcodegen::QrCode::encodeText(m_config.toUtf8(), qrcodegen::QrCode::Ecc::LOW);
m_qrCodes << svgToBase64(QString::fromStdString(toSvgString(qr, 1)));
errorCode = m_clientManagementModel->appendClient(clientId, clientName, container, credentials);
if (errorCode) {
emit exportErrorOccurred(errorString(errorCode));
return;
}
emit exportConfigChanged();
}
void ExportController::generateShadowSocksConfig()
{
clearPreviousConfig();
int serverIndex = m_serversModel->getCurrentlyProcessedServerIndex();
int serverIndex = m_serversModel->getProcessedServerIndex();
ServerCredentials credentials = m_serversModel->getServerCredentials(serverIndex);
DockerContainer container = static_cast<DockerContainer>(m_containersModel->getCurrentlyProcessedContainerIndex());
@@ -268,7 +308,7 @@ void ExportController::generateCloakConfig()
{
clearPreviousConfig();
int serverIndex = m_serversModel->getCurrentlyProcessedServerIndex();
int serverIndex = m_serversModel->getProcessedServerIndex();
ServerCredentials credentials = m_serversModel->getServerCredentials(serverIndex);
DockerContainer container = static_cast<DockerContainer>(m_containersModel->getCurrentlyProcessedContainerIndex());
@@ -328,7 +368,7 @@ void ExportController::updateClientManagementModel(const DockerContainer contain
void ExportController::revokeConfig(const int row, const DockerContainer container, ServerCredentials credentials)
{
ErrorCode errorCode = m_clientManagementModel->revokeClient(row, container, credentials,
m_serversModel->getCurrentlyProcessedServerIndex());
m_serversModel->getProcessedServerIndex());
if (errorCode != ErrorCode::NoError) {
emit exportErrorOccurred(errorString(errorCode));
}

View File

@@ -34,6 +34,7 @@ public slots:
void generateConnectionConfig(const QString &clientName);
void generateOpenVpnConfig(const QString &clientName);
void generateWireGuardConfig(const QString &clientName);
void generateAwgConfig(const QString &clientName);
void generateShadowSocksConfig();
void generateCloakConfig();

View File

@@ -176,7 +176,7 @@ void InstallController::installServer(DockerContainer container, QJsonObject &co
void InstallController::installContainer(DockerContainer container, QJsonObject &config)
{
int serverIndex = m_serversModel->getCurrentlyProcessedServerIndex();
int serverIndex = m_serversModel->getProcessedServerIndex();
ServerCredentials serverCredentials =
qvariant_cast<ServerCredentials>(m_serversModel->data(serverIndex, ServersModel::Roles::CredentialsRole));
@@ -238,7 +238,7 @@ bool InstallController::isServerAlreadyExists()
void InstallController::scanServerForInstalledContainers()
{
int serverIndex = m_serversModel->getCurrentlyProcessedServerIndex();
int serverIndex = m_serversModel->getProcessedServerIndex();
ServerCredentials serverCredentials =
qvariant_cast<ServerCredentials>(m_serversModel->data(serverIndex, ServersModel::Roles::CredentialsRole));
@@ -267,7 +267,7 @@ void InstallController::scanServerForInstalledContainers()
void InstallController::updateContainer(QJsonObject config)
{
int serverIndex = m_serversModel->getCurrentlyProcessedServerIndex();
int serverIndex = m_serversModel->getProcessedServerIndex();
ServerCredentials serverCredentials =
qvariant_cast<ServerCredentials>(m_serversModel->data(serverIndex, ServersModel::Roles::CredentialsRole));
@@ -283,8 +283,8 @@ void InstallController::updateContainer(QJsonObject config)
m_serversModel->updateContainerConfig(container, config);
m_protocolModel->updateModel(config);
if ((serverIndex == m_serversModel->getDefaultServerIndex())
&& (container == m_containersModel->getDefaultContainer())) {
auto defaultContainer = qvariant_cast<DockerContainer>(m_serversModel->data(serverIndex, ServersModel::Roles::DefaultContainerRole));
if ((serverIndex == m_serversModel->getDefaultServerIndex()) && (container == defaultContainer)) {
emit currentContainerUpdated();
} else {
emit updateContainerFinished(tr("Settings updated successfully"));
@@ -296,27 +296,27 @@ void InstallController::updateContainer(QJsonObject config)
emit installationErrorOccurred(errorString(errorCode));
}
void InstallController::rebootCurrentlyProcessedServer()
void InstallController::rebootProcessedServer()
{
int serverIndex = m_serversModel->getCurrentlyProcessedServerIndex();
int serverIndex = m_serversModel->getProcessedServerIndex();
QString serverName = m_serversModel->data(serverIndex, ServersModel::Roles::NameRole).toString();
m_serversModel->rebootServer();
emit rebootCurrentlyProcessedServerFinished(tr("Server '%1' was rebooted").arg(serverName));
emit rebootProcessedServerFinished(tr("Server '%1' was rebooted").arg(serverName));
}
void InstallController::removeCurrentlyProcessedServer()
void InstallController::removeProcessedServer()
{
int serverIndex = m_serversModel->getCurrentlyProcessedServerIndex();
int serverIndex = m_serversModel->getProcessedServerIndex();
QString serverName = m_serversModel->data(serverIndex, ServersModel::Roles::NameRole).toString();
m_serversModel->removeServer();
emit removeCurrentlyProcessedServerFinished(tr("Server '%1' was removed").arg(serverName));
emit removeProcessedServerFinished(tr("Server '%1' was removed").arg(serverName));
}
void InstallController::removeAllContainers()
{
int serverIndex = m_serversModel->getCurrentlyProcessedServerIndex();
int serverIndex = m_serversModel->getProcessedServerIndex();
QString serverName = m_serversModel->data(serverIndex, ServersModel::Roles::NameRole).toString();
ErrorCode errorCode = m_serversModel->removeAllContainers();
@@ -329,7 +329,7 @@ void InstallController::removeAllContainers()
void InstallController::removeCurrentlyProcessedContainer()
{
int serverIndex = m_serversModel->getCurrentlyProcessedServerIndex();
int serverIndex = m_serversModel->getProcessedServerIndex();
QString serverName = m_serversModel->data(serverIndex, ServersModel::Roles::NameRole).toString();
int container = m_containersModel->getCurrentlyProcessedContainerIndex();
@@ -377,7 +377,7 @@ void InstallController::mountSftpDrive(const QString &port, const QString &passw
QString mountPath;
QString cmd;
int serverIndex = m_serversModel->getCurrentlyProcessedServerIndex();
int serverIndex = m_serversModel->getProcessedServerIndex();
ServerCredentials serverCredentials =
qvariant_cast<ServerCredentials>(m_serversModel->data(serverIndex, ServersModel::Roles::CredentialsRole));
QString hostname = serverCredentials.hostName;

View File

@@ -30,8 +30,8 @@ public slots:
void updateContainer(QJsonObject config);
void removeCurrentlyProcessedServer();
void rebootCurrentlyProcessedServer();
void removeProcessedServer();
void rebootProcessedServer();
void removeAllContainers();
void removeCurrentlyProcessedContainer();
@@ -54,8 +54,8 @@ signals:
void scanServerFinished(bool isInstalledContainerFound);
void rebootCurrentlyProcessedServerFinished(const QString &finishedMessage);
void removeCurrentlyProcessedServerFinished(const QString &finishedMessage);
void rebootProcessedServerFinished(const QString &finishedMessage);
void removeProcessedServerFinished(const QString &finishedMessage);
void removeAllContainersFinished(const QString &finishedMessage);
void removeCurrentlyProcessedContainerFinished(const QString &finishedMessage);

View File

@@ -77,7 +77,16 @@ void PageController::closeWindow()
void PageController::keyPressEvent(Qt::Key key)
{
switch (key) {
case Qt::Key_Back: emit closePage();
case Qt::Key_Back:
case Qt::Key_Escape: {
if (m_drawerDepth) {
emit closeTopDrawer();
setDrawerDepth(getDrawerDepth() - 1);
} else {
emit escapePressed();
}
break;
}
default: return;
}
}
@@ -118,36 +127,6 @@ void PageController::showOnStartup()
}
}
void PageController::updateDrawerRootPage(PageLoader::PageEnum page)
{
m_drawerLayer = 0;
m_currentRootPage = page;
}
void PageController::goToDrawerRootPage()
{
m_drawerLayer = 0;
emit showTopCloseButton(false);
emit forceCloseDrawer();
}
void PageController::drawerOpen()
{
m_drawerLayer = m_drawerLayer + 1;
emit showTopCloseButton(true);
}
void PageController::drawerClose()
{
m_drawerLayer = m_drawerLayer -1;
if (m_drawerLayer <= 0) {
emit showTopCloseButton(false);
m_drawerLayer = 0;
}
}
bool PageController::isTriggeredByConnectButton()
{
return m_isTriggeredByConnectButton;
@@ -162,3 +141,15 @@ void PageController::closeApplication()
{
qApp->quit();
}
void PageController::setDrawerDepth(const int depth)
{
if (depth >= 0) {
m_drawerDepth = depth;
}
}
int PageController::getDrawerDepth()
{
return m_drawerDepth;
}

View File

@@ -82,16 +82,14 @@ public slots:
void showOnStartup();
void updateDrawerRootPage(PageLoader::PageEnum page);
void goToDrawerRootPage();
void drawerOpen();
void drawerClose();
bool isTriggeredByConnectButton();
void setTriggeredBtConnectButton(bool trigger);
void closeApplication();
void setDrawerDepth(const int depth);
int getDrawerDepth();
signals:
void goToPage(PageLoader::PageEnum page, bool slide = true);
void goToStartPage();
@@ -110,7 +108,7 @@ signals:
void showNotificationMessage(const QString &message);
void showBusyIndicator(bool visible);
void enableTabBar(bool enabled);
void disableControls(bool disabled);
void hideMainWindow();
void raiseMainWindow();
@@ -118,18 +116,17 @@ signals:
void showPassphraseRequestDrawer();
void passphraseRequestDrawerClosed(QString passphrase);
void showTopCloseButton(bool visible);
void forceCloseDrawer();
void escapePressed();
void closeTopDrawer();
private:
QSharedPointer<ServersModel> m_serversModel;
std::shared_ptr<Settings> m_settings;
PageLoader::PageEnum m_currentRootPage;
int m_drawerLayer;
bool m_isTriggeredByConnectButton;
int m_drawerDepth = 0;
};
#endif // PAGECONTROLLER_H

View File

@@ -28,7 +28,7 @@ SettingsController::SettingsController(const QSharedPointer<ServersModel> &serve
m_sitesModel(sitesModel),
m_settings(settings)
{
m_appVersion = QString("%1: %2 (%3)").arg(tr("Software version"), QString(APP_VERSION), __DATE__);
m_appVersion = QString("%1 (%2, %3)").arg(QString(APP_VERSION), __DATE__, GIT_COMMIT_HASH);
#ifdef Q_OS_ANDROID
if (!m_settings->isScreenshotsEnabled()) {

View File

@@ -92,11 +92,11 @@ QString SystemController::getFileName(const QString &acceptLabel, const QString
mainFileDialog->setProperty("acceptLabel", QVariant::fromValue(acceptLabel));
mainFileDialog->setProperty("nameFilters", QVariant::fromValue(QStringList(nameFilter)));
if (!selectedFile.isEmpty()) {
mainFileDialog->setProperty("selectedFile", QVariant::fromValue(selectedFile));
}
mainFileDialog->setProperty("isSaveMode", QVariant::fromValue(isSaveMode));
mainFileDialog->setProperty("defaultSuffix", QVariant::fromValue(defaultSuffix));
mainFileDialog->setProperty("isSaveMode", QVariant::fromValue(isSaveMode));
if (!selectedFile.isEmpty()) {
mainFileDialog->setProperty("selectedFile", QVariant::fromValue(QUrl(selectedFile)));
}
QMetaObject::invokeMethod(mainFileDialog, "open");
bool isFileDialogAccepted = false;

View File

@@ -39,7 +39,6 @@ QVariant ContainersModel::data(const QModelIndex &index, int role) const
case EasySetupOrderRole: return ContainerProps::easySetupOrder(container);
case IsInstalledRole: return m_containers.contains(container);
case IsCurrentlyProcessedRole: return container == static_cast<DockerContainer>(m_currentlyProcessedContainerIndex);
case IsDefaultRole: return container == m_defaultContainerIndex;
case IsSupportedRole: return ContainerProps::isSupportedByCurrentPlatform(container);
case IsShareableRole: return ContainerProps::isShareable(container);
}
@@ -64,18 +63,6 @@ void ContainersModel::updateModel(const QJsonArray &containers)
endResetModel();
}
void ContainersModel::setDefaultContainer(const int containerIndex)
{
m_defaultContainerIndex = static_cast<DockerContainer>(containerIndex);
emit dataChanged(index(containerIndex, 0), index(containerIndex, 0));
}
DockerContainer ContainersModel::getDefaultContainer()
{
return m_defaultContainerIndex;
}
void ContainersModel::setCurrentlyProcessedContainerIndex(int index)
{
m_currentlyProcessedContainerIndex = index;
@@ -127,7 +114,6 @@ QHash<int, QByteArray> ContainersModel::roleNames() const
roles[IsInstalledRole] = "isInstalled";
roles[IsCurrentlyProcessedRole] = "isCurrentlyProcessed";
roles[IsDefaultRole] = "isDefault";
roles[IsSupportedRole] = "isSupported";
roles[IsShareableRole] = "isShareable";
return roles;

View File

@@ -42,9 +42,6 @@ public:
public slots:
void updateModel(const QJsonArray &containers);
DockerContainer getDefaultContainer();
void setDefaultContainer(const int containerIndex);
void setCurrentlyProcessedContainerIndex(int containerIndex);
int getCurrentlyProcessedContainerIndex();
@@ -58,14 +55,12 @@ protected:
QHash<int, QByteArray> roleNames() const override;
signals:
void defaultContainerChanged();
void containersModelUpdated();
private:
QMap<DockerContainer, QJsonObject> m_containers;
int m_currentlyProcessedContainerIndex;
DockerContainer m_defaultContainerIndex;
};
#endif // CONTAINERS_MODEL_H

View File

@@ -45,6 +45,7 @@ QString LanguageModel::getLocalLanguageName(const LanguageSettings::AvailableLan
case LanguageSettings::AvailableLanguageEnum::Russian: strLanguage = "Русский"; break;
case LanguageSettings::AvailableLanguageEnum::China_cn: strLanguage = "\347\256\200\344\275\223\344\270\255\346\226\207"; break;
case LanguageSettings::AvailableLanguageEnum::Persian: strLanguage = "فارسی"; break;
case LanguageSettings::AvailableLanguageEnum::Arabic: strLanguage = "العربية"; break;
default:
break;
}
@@ -59,6 +60,7 @@ void LanguageModel::changeLanguage(const LanguageSettings::AvailableLanguageEnum
case LanguageSettings::AvailableLanguageEnum::Russian: emit updateTranslations(QLocale::Russian); break;
case LanguageSettings::AvailableLanguageEnum::China_cn: emit updateTranslations(QLocale::Chinese); break;
case LanguageSettings::AvailableLanguageEnum::Persian: emit updateTranslations(QLocale::Persian); break;
case LanguageSettings::AvailableLanguageEnum::Arabic: emit updateTranslations(QLocale::Arabic); break;
default: emit updateTranslations(QLocale::English); break;
}
}
@@ -71,6 +73,7 @@ int LanguageModel::getCurrentLanguageIndex()
case QLocale::Russian: return static_cast<int>(LanguageSettings::AvailableLanguageEnum::Russian); break;
case QLocale::Chinese: return static_cast<int>(LanguageSettings::AvailableLanguageEnum::China_cn); break;
case QLocale::Persian: return static_cast<int>(LanguageSettings::AvailableLanguageEnum::Persian); break;
case QLocale::Arabic: return static_cast<int>(LanguageSettings::AvailableLanguageEnum::Arabic); break;
default: return static_cast<int>(LanguageSettings::AvailableLanguageEnum::English); break;
}
}

View File

@@ -13,7 +13,8 @@ namespace LanguageSettings
English,
Russian,
China_cn,
Persian
Persian,
Arabic
};
Q_ENUM_NS(AvailableLanguageEnum)

View File

@@ -5,19 +5,13 @@
ServersModel::ServersModel(std::shared_ptr<Settings> settings, QObject *parent)
: m_settings(settings), QAbstractListModel(parent)
{
m_servers = m_settings->serversArray();
m_defaultServerIndex = m_settings->defaultServerIndex();
m_currentlyProcessedServerIndex = m_defaultServerIndex;
connect(this, &ServersModel::defaultServerIndexChanged, this, &ServersModel::defaultServerNameChanged);
connect(this, &ServersModel::defaultContainerChanged, this, &ServersModel::defaultServerDescriptionChanged);
connect(this, &ServersModel::defaultServerIndexChanged, this, [this](const int serverIndex) {
auto defaultContainer = ContainerProps::containerFromString(m_servers.at(serverIndex).toObject().value(config_key::defaultContainer).toString());
emit ServersModel::defaultContainerChanged(defaultContainer);
});
connect(this, &ServersModel::currentlyProcessedServerIndexChanged, this, [this](const int serverIndex) {
auto defaultContainer = ContainerProps::containerFromString(m_servers.at(serverIndex).toObject().value(config_key::defaultContainer).toString());
emit ServersModel::defaultContainerChanged(defaultContainer);
emit ServersModel::defaultServerDefaultContainerChanged(defaultContainer);
emit ServersModel::defaultServerNameChanged();
updateDefaultServerContainersModel();
});
}
@@ -74,16 +68,14 @@ QVariant ServersModel::data(const QModelIndex &index, int role) const
return name;
}
case ServerDescriptionRole: {
if (configVersion) {
return server.value(config_key::description).toString();
}
return server.value(config_key::hostName).toString();
auto description = getServerDescription(server, index.row());
return configVersion ? description : description + server.value(config_key::hostName).toString();
}
case HostNameRole: return server.value(config_key::hostName).toString();
case CredentialsRole: return QVariant::fromValue(serverCredentials(index.row()));
case CredentialsLoginRole: return serverCredentials(index.row()).userName;
case IsDefaultRole: return index.row() == m_defaultServerIndex;
case IsCurrentlyProcessedRole: return index.row() == m_currentlyProcessedServerIndex;
case IsCurrentlyProcessedRole: return index.row() == m_processedServerIndex;
case HasWriteAccessRole: {
auto credentials = serverCredentials(index.row());
return (!credentials.userName.isEmpty() && !credentials.secretData.isEmpty());
@@ -95,6 +87,13 @@ QVariant ServersModel::data(const QModelIndex &index, int role) const
case DefaultContainerRole: {
return ContainerProps::containerFromString(server.value(config_key::defaultContainer).toString());
}
case IsServerFromApiRole: {
return server.value(config_key::configVersion).toInt();
}
case HasAmneziaDns: {
QString primaryDns = server.value(config_key::dns1).toString();
return primaryDns == protocols::dns::amneziaDnsIp;
}
}
return QVariant();
@@ -111,8 +110,9 @@ void ServersModel::resetModel()
beginResetModel();
m_servers = m_settings->serversArray();
m_defaultServerIndex = m_settings->defaultServerIndex();
m_currentlyProcessedServerIndex = m_defaultServerIndex;
m_processedServerIndex = m_defaultServerIndex;
endResetModel();
emit defaultServerIndexChanged(m_defaultServerIndex);
}
void ServersModel::setDefaultServerIndex(const int index)
@@ -132,12 +132,7 @@ const QString ServersModel::getDefaultServerName()
return qvariant_cast<QString>(data(m_defaultServerIndex, NameRole));
}
const QString ServersModel::getDefaultServerHostName()
{
return qvariant_cast<QString>(data(m_defaultServerIndex, HostNameRole));
}
QString ServersModel::getDefaultServerDescription(const QJsonObject &server)
QString ServersModel::getServerDescription(const QJsonObject &server, const int index) const
{
const auto configVersion = server.value(config_key::configVersion).toInt();
@@ -145,13 +140,12 @@ QString ServersModel::getDefaultServerDescription(const QJsonObject &server)
if (configVersion) {
return server.value(config_key::description).toString();
} else if (isDefaultServerHasWriteAccess()) {
if (m_isAmneziaDnsEnabled
&& isAmneziaDnsContainerInstalled(m_defaultServerIndex)) {
} else if (data(index, HasWriteAccessRole).toBool()) {
if (m_isAmneziaDnsEnabled && isAmneziaDnsContainerInstalled(index)) {
description += "Amnezia DNS | ";
}
} else {
if (isDefaultServerConfigContainsAmneziaDns()) {
if (data(index, HasAmneziaDns).toBool()) {
description += "Amnezia DNS | ";
}
}
@@ -162,7 +156,7 @@ const QString ServersModel::getDefaultServerDescriptionCollapsed()
{
const QJsonObject server = m_servers.at(m_defaultServerIndex).toObject();
const auto configVersion = server.value(config_key::configVersion).toInt();
auto description = getDefaultServerDescription(server);
auto description = getServerDescription(server, m_defaultServerIndex);
if (configVersion) {
return description;
}
@@ -176,7 +170,7 @@ const QString ServersModel::getDefaultServerDescriptionExpanded()
{
const QJsonObject server = m_servers.at(m_defaultServerIndex).toObject();
const auto configVersion = server.value(config_key::configVersion).toInt();
auto description = getDefaultServerDescription(server);
auto description = getServerDescription(server, m_defaultServerIndex);
if (configVersion) {
return description;
}
@@ -199,26 +193,21 @@ bool ServersModel::hasServerWithWriteAccess()
return false;
}
void ServersModel::setCurrentlyProcessedServerIndex(const int index)
void ServersModel::setProcessedServerIndex(const int index)
{
m_currentlyProcessedServerIndex = index;
m_processedServerIndex = index;
updateContainersModel();
emit currentlyProcessedServerIndexChanged(m_currentlyProcessedServerIndex);
emit processedServerIndexChanged(m_processedServerIndex);
}
int ServersModel::getCurrentlyProcessedServerIndex()
int ServersModel::getProcessedServerIndex()
{
return m_currentlyProcessedServerIndex;
return m_processedServerIndex;
}
QString ServersModel::getCurrentlyProcessedServerHostName()
const ServerCredentials ServersModel::getProcessedServerCredentials()
{
return qvariant_cast<QString>(data(m_currentlyProcessedServerIndex, HostNameRole));
}
const ServerCredentials ServersModel::getCurrentlyProcessedServerCredentials()
{
return serverCredentials(m_currentlyProcessedServerIndex);
return serverCredentials(m_processedServerIndex);
}
const ServerCredentials ServersModel::getServerCredentials(const int index)
@@ -228,12 +217,17 @@ const ServerCredentials ServersModel::getServerCredentials(const int index)
bool ServersModel::isDefaultServerCurrentlyProcessed()
{
return m_defaultServerIndex == m_currentlyProcessedServerIndex;
return m_defaultServerIndex == m_processedServerIndex;
}
bool ServersModel::isCurrentlyProcessedServerHasWriteAccess()
bool ServersModel::isDefaultServerFromApi()
{
return qvariant_cast<bool>(data(m_currentlyProcessedServerIndex, HasWriteAccessRole));
return qvariant_cast<bool>(data(m_defaultServerIndex, IsServerFromApiRole));
}
bool ServersModel::isProcessedServerHasWriteAccess()
{
return qvariant_cast<bool>(data(m_processedServerIndex, HasWriteAccessRole));
}
bool ServersModel::isDefaultServerHasWriteAccess()
@@ -249,40 +243,42 @@ void ServersModel::addServer(const QJsonObject &server)
endResetModel();
}
void ServersModel::editServer(const QJsonObject &server)
void ServersModel::editServer(const QJsonObject &server, const int serverIndex)
{
m_settings->editServer(m_currentlyProcessedServerIndex, server);
m_servers.replace(m_currentlyProcessedServerIndex, m_settings->serversArray().at(m_currentlyProcessedServerIndex));
emit dataChanged(index(m_currentlyProcessedServerIndex, 0), index(m_currentlyProcessedServerIndex, 0));
m_settings->editServer(serverIndex, server);
m_servers.replace(serverIndex, m_settings->serversArray().at(serverIndex));
emit dataChanged(index(serverIndex, 0), index(serverIndex, 0));
if (serverIndex == m_defaultServerIndex) {
updateDefaultServerContainersModel();
}
updateContainersModel();
if (serverIndex == m_defaultServerIndex) {
auto defaultContainer = qvariant_cast<DockerContainer>(getDefaultServerData("defaultContainer"));
emit defaultServerDefaultContainerChanged(defaultContainer);
}
}
void ServersModel::removeServer()
{
beginResetModel();
m_settings->removeServer(m_currentlyProcessedServerIndex);
m_settings->removeServer(m_processedServerIndex);
m_servers = m_settings->serversArray();
if (m_settings->defaultServerIndex() == m_currentlyProcessedServerIndex) {
if (m_settings->defaultServerIndex() == m_processedServerIndex) {
setDefaultServerIndex(0);
} else if (m_settings->defaultServerIndex() > m_currentlyProcessedServerIndex) {
} else if (m_settings->defaultServerIndex() > m_processedServerIndex) {
setDefaultServerIndex(m_settings->defaultServerIndex() - 1);
}
if (m_settings->serversCount() == 0) {
setDefaultServerIndex(-1);
}
setCurrentlyProcessedServerIndex(m_defaultServerIndex);
setProcessedServerIndex(m_defaultServerIndex);
endResetModel();
}
bool ServersModel::isDefaultServerConfigContainsAmneziaDns()
{
const QJsonObject server = m_servers.at(m_defaultServerIndex).toObject();
QString primaryDns = server.value(config_key::dns1).toString();
return primaryDns == protocols::dns::amneziaDnsIp;
}
QHash<int, QByteArray> ServersModel::roleNames() const
{
QHash<int, QByteArray> roles;
@@ -290,6 +286,8 @@ QHash<int, QByteArray> ServersModel::roleNames() const
roles[NameRole] = "serverName";
roles[NameRole] = "name";
roles[ServerDescriptionRole] = "serverDescription";
roles[CollapsedServerDescriptionRole] = "collapsedServerDescription";
roles[ExpandedServerDescriptionRole] = "expandedServerDescription";
roles[HostNameRole] = "hostName";
@@ -304,6 +302,8 @@ QHash<int, QByteArray> ServersModel::roleNames() const
roles[ContainsAmneziaDnsRole] = "containsAmneziaDns";
roles[DefaultContainerRole] = "defaultContainer";
roles[IsServerFromApiRole] = "isServerFromApi";
return roles;
}
@@ -322,28 +322,29 @@ ServerCredentials ServersModel::serverCredentials(int index) const
void ServersModel::updateContainersModel()
{
auto containers = m_servers.at(m_currentlyProcessedServerIndex).toObject().value(config_key::containers).toArray();
auto containers = m_servers.at(m_processedServerIndex).toObject().value(config_key::containers).toArray();
emit containersUpdated(containers);
}
void ServersModel::updateDefaultServerContainersModel()
{
auto containers = m_servers.at(m_defaultServerIndex).toObject().value(config_key::containers).toArray();
emit defaultServerContainersUpdated(containers);
}
QJsonObject ServersModel::getDefaultServerConfig()
{
return m_servers.at(m_defaultServerIndex).toObject();
}
QJsonObject ServersModel::getCurrentlyProcessedServerConfig()
void ServersModel::reloadDefaultServerContainerConfig()
{
return m_servers.at(m_currentlyProcessedServerIndex).toObject();
}
void ServersModel::reloadContainerConfig()
{
QJsonObject server = m_servers.at(m_currentlyProcessedServerIndex).toObject();
QJsonObject server = m_servers.at(m_defaultServerIndex).toObject();
auto container = ContainerProps::containerFromString(server.value(config_key::defaultContainer).toString());
auto containers = server.value(config_key::containers).toArray();
auto config = m_settings->containerConfig(m_currentlyProcessedServerIndex, container);
auto config = m_settings->containerConfig(m_defaultServerIndex, container);
for (auto i = 0; i < containers.size(); i++) {
auto c = ContainerProps::containerFromString(containers.at(i).toObject().value(config_key::container).toString());
if (c == container) {
@@ -353,13 +354,13 @@ void ServersModel::reloadContainerConfig()
}
server.insert(config_key::containers, containers);
editServer(server);
editServer(server, m_defaultServerIndex);
}
void ServersModel::updateContainerConfig(const int containerIndex, const QJsonObject config)
{
auto container = static_cast<DockerContainer>(containerIndex);
QJsonObject server = m_servers.at(m_currentlyProcessedServerIndex).toObject();
QJsonObject server = m_servers.at(m_processedServerIndex).toObject();
auto containers = server.value(config_key::containers).toArray();
for (auto i = 0; i < containers.size(); i++) {
@@ -377,49 +378,38 @@ void ServersModel::updateContainerConfig(const int containerIndex, const QJsonOb
server.insert(config_key::defaultContainer, ContainerProps::containerToString(container));
}
editServer(server);
editServer(server, m_processedServerIndex);
}
void ServersModel::addContainerConfig(const int containerIndex, const QJsonObject config)
{
auto container = static_cast<DockerContainer>(containerIndex);
QJsonObject server = m_servers.at(m_currentlyProcessedServerIndex).toObject();
QJsonObject server = m_servers.at(m_processedServerIndex).toObject();
auto containers = server.value(config_key::containers).toArray();
containers.push_back(config);
server.insert(config_key::containers, containers);
bool isDefaultContainerChanged = false;
auto defaultContainer = server.value(config_key::defaultContainer).toString();
if ((ContainerProps::containerFromString(defaultContainer) == DockerContainer::None || ContainerProps::containerService(container) != ServiceType::Other)) {
server.insert(config_key::defaultContainer, ContainerProps::containerToString(container));
isDefaultContainerChanged = true;
}
editServer(server);
if (isDefaultContainerChanged) {
emit defaultContainerChanged(container);
}
editServer(server, m_processedServerIndex);
}
void ServersModel::setDefaultContainer(const int containerIndex)
void ServersModel::setDefaultContainer(const int serverIndex, const int containerIndex)
{
auto container = static_cast<DockerContainer>(containerIndex);
QJsonObject s = m_servers.at(m_currentlyProcessedServerIndex).toObject();
QJsonObject s = m_servers.at(serverIndex).toObject();
s.insert(config_key::defaultContainer, ContainerProps::containerToString(container));
editServer(s); //check
emit defaultContainerChanged(container);
editServer(s, serverIndex); //check
}
DockerContainer ServersModel::getDefaultContainer()
const QString ServersModel::getDefaultServerDefaultContainerName()
{
return qvariant_cast<DockerContainer>(data(m_currentlyProcessedServerIndex, DefaultContainerRole));
}
const QString ServersModel::getDefaultContainerName()
{
auto defaultContainer = getDefaultContainer();
auto defaultContainer = qvariant_cast<DockerContainer>(getDefaultServerData("defaultContainer"));
return ContainerProps::containerHumanNames().value(defaultContainer);
}
@@ -427,15 +417,14 @@ ErrorCode ServersModel::removeAllContainers()
{
ServerController serverController(m_settings);
ErrorCode errorCode =
serverController.removeAllContainers(m_settings->serverCredentials(m_currentlyProcessedServerIndex));
serverController.removeAllContainers(m_settings->serverCredentials(m_processedServerIndex));
if (errorCode == ErrorCode::NoError) {
QJsonObject s = m_servers.at(m_currentlyProcessedServerIndex).toObject();
QJsonObject s = m_servers.at(m_processedServerIndex).toObject();
s.insert(config_key::containers, {});
s.insert(config_key::defaultContainer, ContainerProps::containerToString(DockerContainer::None));
editServer(s);
emit defaultContainerChanged(DockerContainer::None);
editServer(s, m_processedServerIndex);
}
return errorCode;
}
@@ -443,7 +432,7 @@ ErrorCode ServersModel::removeAllContainers()
ErrorCode ServersModel::rebootServer()
{
ServerController serverController(m_settings);
auto credentials = m_settings->serverCredentials(m_currentlyProcessedServerIndex);
auto credentials = m_settings->serverCredentials(m_processedServerIndex);
ErrorCode errorCode = serverController.rebootServer(credentials);
return errorCode;
@@ -452,13 +441,13 @@ ErrorCode ServersModel::rebootServer()
ErrorCode ServersModel::removeContainer(const int containerIndex)
{
ServerController serverController(m_settings);
auto credentials = m_settings->serverCredentials(m_currentlyProcessedServerIndex);
auto credentials = m_settings->serverCredentials(m_processedServerIndex);
auto dockerContainer = static_cast<DockerContainer>(containerIndex);
ErrorCode errorCode = serverController.removeContainer(credentials, dockerContainer);
if (errorCode == ErrorCode::NoError) {
QJsonObject server = m_servers.at(m_currentlyProcessedServerIndex).toObject();
QJsonObject server = m_servers.at(m_processedServerIndex).toObject();
auto containers = server.value(config_key::containers).toArray();
for (auto it = containers.begin(); it != containers.end(); it++) {
@@ -480,32 +469,37 @@ ErrorCode ServersModel::removeContainer(const int containerIndex)
server.insert(config_key::defaultContainer, ContainerProps::containerToString(defaultContainer));
}
editServer(server);
emit defaultContainerChanged(defaultContainer);
editServer(server, m_processedServerIndex);
}
return errorCode;
}
void ServersModel::clearCachedProfiles()
{
const auto &containers = m_settings->containers(m_currentlyProcessedServerIndex);
const auto &containers = m_settings->containers(m_processedServerIndex);
for (DockerContainer container : containers.keys()) {
m_settings->clearLastConnectionConfig(m_currentlyProcessedServerIndex, container);
m_settings->clearLastConnectionConfig(m_processedServerIndex, container);
}
m_servers.replace(m_currentlyProcessedServerIndex, m_settings->server(m_currentlyProcessedServerIndex));
m_servers.replace(m_processedServerIndex, m_settings->server(m_processedServerIndex));
if (m_processedServerIndex == m_defaultServerIndex) {
updateDefaultServerContainersModel();
}
updateContainersModel();
}
void ServersModel::clearCachedProfile(const DockerContainer container)
{
m_settings->clearLastConnectionConfig(m_currentlyProcessedServerIndex, container);
m_settings->clearLastConnectionConfig(m_processedServerIndex, container);
m_servers.replace(m_currentlyProcessedServerIndex, m_settings->server(m_currentlyProcessedServerIndex));
m_servers.replace(m_processedServerIndex, m_settings->server(m_processedServerIndex));
if (m_processedServerIndex == m_defaultServerIndex) {
updateDefaultServerContainersModel();
}
updateContainersModel();
}
bool ServersModel::isAmneziaDnsContainerInstalled(const int serverIndex)
bool ServersModel::isAmneziaDnsContainerInstalled(const int serverIndex) const
{
QJsonObject server = m_servers.at(serverIndex).toObject();
auto containers = server.value(config_key::containers).toArray();
@@ -544,16 +538,6 @@ void ServersModel::toggleAmneziaDns(bool enabled)
emit defaultServerDescriptionChanged();
}
bool ServersModel::isDefaultServerFromApi()
{
return m_settings->server(m_defaultServerIndex).value(config_key::configVersion).toInt();
}
bool ServersModel::isCurrentlyProcessedServerFromApi()
{
return m_settings->server(m_currentlyProcessedServerIndex).value(config_key::configVersion).toInt();
}
bool ServersModel::isServerFromApiAlreadyExists(const quint16 crc)
{
for (const auto &server : qAsConst(m_servers)) {
@@ -564,3 +548,52 @@ bool ServersModel::isServerFromApiAlreadyExists(const quint16 crc)
return false;
}
QVariant ServersModel::getDefaultServerData(const QString roleString)
{
auto roles = roleNames();
for (auto it = roles.begin(); it != roles.end(); it++) {
if (QString(it.value()) == roleString) {
return data(m_defaultServerIndex, it.key());
}
}
return {};
}
void ServersModel::setDefaultServerData(const QString roleString, const QVariant &value)
{
}
QVariant ServersModel::getProcessedServerData(const QString roleString)
{
auto roles = roleNames();
for (auto it = roles.begin(); it != roles.end(); it++) {
if (QString(it.value()) == roleString) {
return data(m_processedServerIndex, it.key());
}
}
return {};
}
void ServersModel::setProcessedServerData(const QString roleString, const QVariant &value)
{
}
bool ServersModel::isDefaultServerDefaultContainerHasSplitTunneling()
{
auto server = m_servers.at(m_defaultServerIndex).toObject();
auto defaultContainer = ContainerProps::containerFromString(server.value(config_key::defaultContainer).toString());
auto containerConfig = server.value(config_key::containers).toArray().at(defaultContainer).toObject();
auto protocolConfig = containerConfig.value(ContainerProps::containerTypeToString(defaultContainer)).toObject();
if (defaultContainer == DockerContainer::Awg || defaultContainer == DockerContainer::WireGuard) {
return !(protocolConfig.value(config_key::last_config).toString().contains("AllowedIPs = 0.0.0.0/0, ::/0"));
} else if (defaultContainer == DockerContainer::Cloak || defaultContainer == DockerContainer::OpenVpn || defaultContainer == DockerContainer::ShadowSocks) {
return !(protocolConfig.value(config_key::last_config).toString().contains("redirect-gateway"));
}
return false;
}

View File

@@ -12,7 +12,8 @@ public:
enum Roles {
NameRole = Qt::UserRole + 1,
ServerDescriptionRole,
CollapsedServerDescriptionRole,
ExpandedServerDescriptionRole,
HostNameRole,
CredentialsRole,
@@ -25,7 +26,11 @@ public:
ContainsAmneziaDnsRole,
DefaultContainerRole
DefaultContainerRole,
IsServerFromApiRole,
HasAmneziaDns
};
ServersModel(std::shared_ptr<Settings> settings, QObject *parent = nullptr);
@@ -40,47 +45,43 @@ public:
Q_PROPERTY(int defaultIndex READ getDefaultServerIndex WRITE setDefaultServerIndex NOTIFY defaultServerIndexChanged)
Q_PROPERTY(QString defaultServerName READ getDefaultServerName NOTIFY defaultServerNameChanged)
Q_PROPERTY(QString defaultServerHostName READ getDefaultServerHostName NOTIFY defaultServerIndexChanged)
Q_PROPERTY(QString defaultContainerName READ getDefaultContainerName NOTIFY defaultContainerChanged)
Q_PROPERTY(QString defaultServerDescriptionCollapsed READ getDefaultServerDescriptionCollapsed NOTIFY defaultServerDescriptionChanged)
Q_PROPERTY(QString defaultServerDescriptionExpanded READ getDefaultServerDescriptionExpanded NOTIFY defaultServerDescriptionChanged)
Q_PROPERTY(QString defaultServerDefaultContainerName READ getDefaultServerDefaultContainerName NOTIFY defaultServerDefaultContainerChanged)
Q_PROPERTY(QString defaultServerDescriptionCollapsed READ getDefaultServerDescriptionCollapsed NOTIFY defaultServerDefaultContainerChanged)
Q_PROPERTY(QString defaultServerDescriptionExpanded READ getDefaultServerDescriptionExpanded NOTIFY defaultServerDefaultContainerChanged)
Q_PROPERTY(bool isDefaultServerDefaultContainerHasSplitTunneling READ isDefaultServerDefaultContainerHasSplitTunneling NOTIFY defaultServerDefaultContainerChanged)
Q_PROPERTY(bool isDefaultServerFromApi READ isDefaultServerFromApi NOTIFY defaultServerIndexChanged)
Q_PROPERTY(int currentlyProcessedIndex READ getCurrentlyProcessedServerIndex WRITE setCurrentlyProcessedServerIndex
NOTIFY currentlyProcessedServerIndexChanged)
Q_PROPERTY(int processedIndex READ getProcessedServerIndex WRITE setProcessedServerIndex NOTIFY processedServerIndexChanged)
public slots:
void setDefaultServerIndex(const int index);
const int getDefaultServerIndex();
const QString getDefaultServerName();
const QString getDefaultServerHostName();
const QString getDefaultServerDescriptionCollapsed();
const QString getDefaultServerDescriptionExpanded();
const QString getDefaultServerDefaultContainerName();
bool isDefaultServerCurrentlyProcessed();
bool isDefaultServerFromApi();
bool isCurrentlyProcessedServerHasWriteAccess();
bool isProcessedServerHasWriteAccess();
bool isDefaultServerHasWriteAccess();
bool hasServerWithWriteAccess();
const int getServersCount();
void setCurrentlyProcessedServerIndex(const int index);
int getCurrentlyProcessedServerIndex();
void setProcessedServerIndex(const int index);
int getProcessedServerIndex();
QString getCurrentlyProcessedServerHostName();
const ServerCredentials getCurrentlyProcessedServerCredentials();
const ServerCredentials getProcessedServerCredentials();
const ServerCredentials getServerCredentials(const int index);
void addServer(const QJsonObject &server);
void editServer(const QJsonObject &server);
void editServer(const QJsonObject &server, const int serverIndex);
void removeServer();
bool isDefaultServerConfigContainsAmneziaDns();
bool isAmneziaDnsContainerInstalled(const int serverIndex);
QJsonObject getDefaultServerConfig();
QJsonObject getCurrentlyProcessedServerConfig();
void reloadContainerConfig();
void reloadDefaultServerContainerConfig();
void updateContainerConfig(const int containerIndex, const QJsonObject config);
void addContainerConfig(const int containerIndex, const QJsonObject config);
@@ -91,43 +92,50 @@ public slots:
ErrorCode removeAllContainers();
ErrorCode rebootServer();
void setDefaultContainer(const int containerIndex);
DockerContainer getDefaultContainer();
const QString getDefaultContainerName();
void setDefaultContainer(const int serverIndex, const int containerIndex);
QStringList getAllInstalledServicesName(const int serverIndex);
void toggleAmneziaDns(bool enabled);
bool isDefaultServerFromApi();
bool isCurrentlyProcessedServerFromApi();
bool isServerFromApiAlreadyExists(const quint16 crc);
QVariant getDefaultServerData(const QString roleString);
void setDefaultServerData(const QString roleString, const QVariant &value);
QVariant getProcessedServerData(const QString roleString);
void setProcessedServerData(const QString roleString, const QVariant &value);
bool isDefaultServerDefaultContainerHasSplitTunneling();
protected:
QHash<int, QByteArray> roleNames() const override;
signals:
void currentlyProcessedServerIndexChanged(const int index);
void processedServerIndexChanged(const int index);
void defaultServerIndexChanged(const int index);
void defaultServerNameChanged();
void defaultServerDescriptionChanged();
void containersUpdated(const QJsonArray &containers);
void defaultContainerChanged(const int containerIndex);
void defaultServerContainersUpdated(const QJsonArray &containers);
void defaultServerDefaultContainerChanged(const int containerIndex);
private:
ServerCredentials serverCredentials(int index) const;
void updateContainersModel();
void updateDefaultServerContainersModel();
QString getDefaultServerDescription(const QJsonObject &server);
QString getServerDescription(const QJsonObject &server, const int index) const;
bool isAmneziaDnsContainerInstalled(const int serverIndex) const;
QJsonArray m_servers;
std::shared_ptr<Settings> m_settings;
int m_defaultServerIndex;
int m_currentlyProcessedServerIndex;
int m_processedServerIndex;
bool m_isAmneziaDnsEnabled = m_settings->useAmneziaDns();
};

View File

@@ -113,6 +113,7 @@ void SitesModel::toggleSplitTunneling(bool enabled)
m_settings->setRouteMode(Settings::RouteMode::VpnAllSites);
}
m_isSplitTunnelingEnabled = enabled;
emit splitTunnelingToggled();
}
QVector<QPair<QString, QString> > SitesModel::getCurrentSites()

View File

@@ -22,6 +22,7 @@ public:
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
Q_PROPERTY(int routeMode READ getRouteMode WRITE setRouteMode NOTIFY routeModeChanged)
Q_PROPERTY(bool isTunnelingEnabled READ isSplitTunnelingEnabled NOTIFY splitTunnelingToggled)
public slots:
bool addSite(const QString &hostname, const QString &ip);
@@ -38,6 +39,7 @@ public slots:
signals:
void routeModeChanged();
void splitTunnelingToggled();
protected:
QHash<int, QByteArray> roleNames() const override;

View File

@@ -138,8 +138,7 @@ Button {
}
onClicked: {
if (!ConnectionController.isConnectionInProgress) {
ApiController.updateServerConfigFromApi()
}
ServersModel.setProcessedServerIndex(ServersModel.defaultIndex)
ApiController.updateServerConfigFromApi()
}
}

View File

@@ -8,18 +8,24 @@ import "../Controls2"
import "../Controls2/TextTypes"
import "../Config"
DrawerType {
DrawerType2 {
id: root
width: parent.width
height: parent.height * 0.4375
height: parent.height
expandedContent: ColumnLayout {
id: content
ColumnLayout {
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
spacing: 0
Component.onCompleted: {
root.expandedHeight = content.implicitHeight + 32
}
Header2Type {
Layout.fillWidth: true
Layout.topMargin: 24
@@ -40,7 +46,7 @@ DrawerType {
clickedFunction: function() {
PageController.goToPage(PageEnum.PageSetupWizardCredentials)
root.visible = false
root.close()
}
}
@@ -54,7 +60,7 @@ DrawerType {
clickedFunction: function() {
PageController.goToPage(PageEnum.PageSetupWizardConfigSource)
root.visible = false
root.close()
}
}

View File

@@ -15,6 +15,7 @@ ListView {
id: menuContent
property var rootWidth
property var selectedText
width: rootWidth
height: menuContent.contentItem.height
@@ -26,24 +27,6 @@ ListView {
id: containersRadioButtonGroup
}
Connections {
target: ServersModel
function onCurrentlyProcessedServerIndexChanged() {
if (ContainersModel.getDefaultContainer()) {
menuContent.checkCurrentItem()
}
}
}
function checkCurrentItem() {
var item = menuContent.itemAtIndex(currentIndex)
if (item !== null) {
var radioButton = item.children[0].children[0]
radioButton.checked = true
}
}
delegate: Item {
implicitWidth: rootWidth
implicitHeight: content.implicitHeight
@@ -69,7 +52,7 @@ ListView {
showImage: !isInstalled
checkable: isInstalled && !ConnectionController.isConnected && isSupported
checked: isDefault
checked: proxyDefaultServerContainersModel.mapToSource(index) === ServersModel.getDefaultServerData("defaultContainer")
onClicked: {
if (ConnectionController.isConnected && isInstalled) {
@@ -78,18 +61,18 @@ ListView {
}
if (checked) {
containersDropDown.menuVisible = false
ServersModel.setDefaultContainer(proxyContainersModel.mapToSource(index))
containersDropDown.close()
ServersModel.setDefaultContainer(ServersModel.defaultIndex, proxyDefaultServerContainersModel.mapToSource(index))
} else {
if (!isSupported && isInstalled) {
PageController.showErrorMessage(qsTr("The selected protocol is not supported on the current platform"))
return
}
ContainersModel.setCurrentlyProcessedContainerIndex(proxyContainersModel.mapToSource(index))
ContainersModel.setCurrentlyProcessedContainerIndex(proxyDefaultServerContainersModel.mapToSource(index))
InstallController.setShouldCreateServer(false)
PageController.goToPage(PageEnum.PageSetupWizardProtocolSettings)
containersDropDown.menuVisible = false
containersDropDown.close()
}
}

View File

@@ -0,0 +1,92 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import PageEnum 1.0
import "../Controls2"
import "../Controls2/TextTypes"
import "../Config"
DrawerType2 {
id: root
anchors.fill: parent
expandedHeight: parent.height * 0.7
expandedContent: ColumnLayout {
id: content
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
spacing: 0
Header2Type {
Layout.fillWidth: true
Layout.topMargin: 24
Layout.rightMargin: 16
Layout.leftMargin: 16
Layout.bottomMargin: 16
headerText: qsTr("Split tunneling")
descriptionText: qsTr("Allows you to connect to some sites or applications through a VPN connection and bypass others")
}
LabelWithButtonType {
Layout.fillWidth: true
Layout.topMargin: 16
visible: ServersModel.isDefaultServerDefaultContainerHasSplitTunneling && ServersModel.getDefaultServerData("isServerFromApi")
text: qsTr("Split tunneling on the server")
descriptionText: qsTr("Enabled \nCan't be disabled for current server")
rightImageSource: "qrc:/images/controls/chevron-right.svg"
clickedFunction: function() {
// PageController.goToPage(PageEnum.PageSettingsSplitTunneling)
// root.close()
}
}
DividerType {
visible: ServersModel.isDefaultServerDefaultContainerHasSplitTunneling && ServersModel.getDefaultServerData("isServerFromApi")
}
LabelWithButtonType {
Layout.fillWidth: true
Layout.topMargin: 16
enabled: ! ServersModel.isDefaultServerDefaultContainerHasSplitTunneling || !ServersModel.getDefaultServerData("isServerFromApi")
text: qsTr("Site-based split tunneling")
descriptionText: enabled && SitesModel.isTunnelingEnabled ? qsTr("Enabled") : qsTr("Disabled")
rightImageSource: "qrc:/images/controls/chevron-right.svg"
clickedFunction: function() {
PageController.goToPage(PageEnum.PageSettingsSplitTunneling)
root.close()
}
}
DividerType {
}
LabelWithButtonType {
Layout.fillWidth: true
visible: false
text: qsTr("App-based split tunneling")
rightImageSource: "qrc:/images/controls/chevron-right.svg"
clickedFunction: function() {
// PageController.goToPage(PageEnum.PageSetupWizardConfigSource)
root.close()
}
}
DividerType {
visible: false
}
}
}

View File

@@ -5,7 +5,7 @@ import QtQuick.Layouts
import "../Controls2"
import "../Controls2/TextTypes"
DrawerType {
DrawerType2 {
id: root
property string headerText
@@ -16,23 +16,24 @@ DrawerType {
property var yesButtonFunction
property var noButtonFunction
width: parent.width
height: content.implicitHeight + 32
ColumnLayout {
expandedContent: ColumnLayout {
id: content
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
anchors.topMargin: 16
anchors.rightMargin: 16
anchors.leftMargin: 16
spacing: 8
onImplicitHeightChanged: {
root.expandedHeight = content.implicitHeight + 32
}
Header2TextType {
Layout.fillWidth: true
Layout.topMargin: 16
Layout.rightMargin: 16
Layout.leftMargin: 16
text: headerText
}
@@ -40,6 +41,8 @@ DrawerType {
ParagraphTextType {
Layout.fillWidth: true
Layout.topMargin: 8
Layout.rightMargin: 16
Layout.leftMargin: 16
text: descriptionText
}
@@ -47,10 +50,12 @@ DrawerType {
BasicButtonType {
Layout.fillWidth: true
Layout.topMargin: 16
Layout.rightMargin: 16
Layout.leftMargin: 16
text: yesButtonText
onClicked: {
clickedFunc: function() {
if (yesButtonFunction && typeof yesButtonFunction === "function") {
yesButtonFunction()
}
@@ -59,6 +64,8 @@ DrawerType {
BasicButtonType {
Layout.fillWidth: true
Layout.rightMargin: 16
Layout.leftMargin: 16
defaultColor: "transparent"
hoveredColor: Qt.rgba(1, 1, 1, 0.08)
@@ -69,7 +76,7 @@ DrawerType {
text: noButtonText
onClicked: {
clickedFunc: function() {
if (noButtonFunction && typeof noButtonFunction === "function") {
noButtonFunction()
}

View File

@@ -5,129 +5,136 @@ import QtQuick.Layouts
import "../Controls2"
import "../Controls2/TextTypes"
DrawerType {
DrawerType2 {
id: root
width: parent.width
height: parent.height * 0.9
expandedContent: Item {
id: container
ColumnLayout {
id: backButton
implicitHeight: root.height * 0.9
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
anchors.topMargin: 16
BackButtonType {
backButtonImage: "qrc:/images/controls/arrow-left.svg"
backButtonFunction: function() {
root.close()
}
Component.onCompleted: {
root.expandedHeight = container.implicitHeight
}
}
FlickableType {
anchors.top: backButton.bottom
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: parent.bottom
contentHeight: content.implicitHeight
ColumnLayout {
id: content
id: backButton
anchors.fill: parent
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
anchors.topMargin: 16
Header2Type {
id: header
Layout.fillWidth: true
Layout.topMargin: 16
Layout.rightMargin: 16
Layout.leftMargin: 16
headerText: qsTr("Choose language")
BackButtonType {
backButtonImage: "qrc:/images/controls/arrow-left.svg"
backButtonFunction: function() {
root.close()
}
}
}
ListView {
id: listView
FlickableType {
anchors.top: backButton.bottom
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: parent.bottom
contentHeight: content.implicitHeight
Layout.fillWidth: true
height: listView.contentItem.height
ColumnLayout {
id: content
clip: true
interactive: false
anchors.fill: parent
model: LanguageModel
currentIndex: LanguageModel.currentLanguageIndex
Header2Type {
id: header
Layout.fillWidth: true
Layout.topMargin: 16
Layout.rightMargin: 16
Layout.leftMargin: 16
ButtonGroup {
id: buttonGroup
headerText: qsTr("Choose language")
}
delegate: Item {
implicitWidth: root.width
implicitHeight: delegateContent.implicitHeight
ListView {
id: listView
ColumnLayout {
id: delegateContent
Layout.fillWidth: true
height: listView.contentItem.height
anchors.fill: parent
clip: true
interactive: false
RadioButton {
id: radioButton
model: LanguageModel
currentIndex: LanguageModel.currentLanguageIndex
implicitWidth: parent.width
implicitHeight: radioButtonContent.implicitHeight
ButtonGroup {
id: buttonGroup
}
hoverEnabled: true
delegate: Item {
implicitWidth: root.width
implicitHeight: delegateContent.implicitHeight
indicator: Rectangle {
anchors.fill: parent
color: radioButton.hovered ? "#2C2D30" : "#1C1D21"
ColumnLayout {
id: delegateContent
Behavior on color {
PropertyAnimation { duration: 200 }
}
}
anchors.fill: parent
RowLayout {
id: radioButtonContent
anchors.fill: parent
RadioButton {
id: radioButton
anchors.rightMargin: 16
anchors.leftMargin: 16
implicitWidth: parent.width
implicitHeight: radioButtonContent.implicitHeight
spacing: 0
hoverEnabled: true
z: 1
indicator: Rectangle {
anchors.fill: parent
color: radioButton.hovered ? "#2C2D30" : "#1C1D21"
ParagraphTextType {
Layout.fillWidth: true
Layout.topMargin: 20
Layout.bottomMargin: 20
text: languageName
Behavior on color {
PropertyAnimation { duration: 200 }
}
}
Image {
source: "qrc:/images/controls/check.svg"
visible: radioButton.checked
RowLayout {
id: radioButtonContent
anchors.fill: parent
width: 24
height: 24
anchors.rightMargin: 16
anchors.leftMargin: 16
Layout.rightMargin: 8
spacing: 0
z: 1
ParagraphTextType {
Layout.fillWidth: true
Layout.topMargin: 20
Layout.bottomMargin: 20
text: languageName
}
Image {
source: "qrc:/images/controls/check.svg"
visible: radioButton.checked
width: 24
height: 24
Layout.rightMargin: 8
}
}
}
ButtonGroup.group: buttonGroup
checked: listView.currentIndex === index
ButtonGroup.group: buttonGroup
checked: listView.currentIndex === index
onClicked: {
listView.currentIndex = index
LanguageModel.changeLanguage(languageIndex)
root.close()
onClicked: {
listView.currentIndex = index
LanguageModel.changeLanguage(languageIndex)
root.close()
}
}
}
}

View File

@@ -16,19 +16,18 @@ import "../Controls2/TextTypes"
import "../Config"
import "../Components"
DrawerType {
DrawerType2 {
id: root
property alias headerText: header.headerText
property alias configContentHeaderText: configContentHeader.headerText
property alias contentVisible: content.visible
property string headerText
property string configContentHeaderText
property string contentVisible
property string configExtension: ".vpn"
property string configCaption: qsTr("Save AmneziaVPN config")
property string configFileName: "amnezia_config"
width: parent.width
height: parent.height * 0.9
expandedHeight: parent.height * 0.9
onClosed: {
configExtension = ".vpn"
@@ -36,8 +35,8 @@ DrawerType {
configFileName = "amnezia_config"
}
Item {
anchors.fill: parent
expandedContent: Item {
implicitHeight: root.expandedHeight
Header2Type {
id: header
@@ -47,6 +46,8 @@ DrawerType {
anchors.topMargin: 20
anchors.leftMargin: 16
anchors.rightMargin: 16
headerText: root.headerText
}
FlickableType {
@@ -64,6 +65,8 @@ DrawerType {
anchors.leftMargin: 16
anchors.rightMargin: 16
visible: root.contentVisible
BasicButtonType {
Layout.fillWidth: true
Layout.topMargin: 16
@@ -71,7 +74,7 @@ DrawerType {
text: qsTr("Share")
imageSource: "qrc:/images/controls/share-2.svg"
onClicked: {
clickedFunc: function() {
var fileName = ""
if (GC.isMobile()) {
fileName = configFileName + configExtension
@@ -91,6 +94,7 @@ DrawerType {
}
BasicButtonType {
id: copyConfigTextButton
Layout.fillWidth: true
Layout.topMargin: 8
@@ -103,20 +107,14 @@ DrawerType {
text: qsTr("Copy")
imageSource: "qrc:/images/controls/copy.svg"
onClicked: {
configText.selectAll()
configText.copy()
configText.select(0, 0)
PageController.showNotificationMessage(qsTr("Copied"))
}
}
BasicButtonType {
id: copyNativeConfigStringButton
Layout.fillWidth: true
Layout.topMargin: 8
visible: nativeConfigString.text !== ""
visible: false
defaultColor: "transparent"
hoveredColor: Qt.rgba(1, 1, 1, 0.08)
@@ -127,13 +125,6 @@ DrawerType {
text: qsTr("Copy config string")
imageSource: "qrc:/images/controls/copy.svg"
onClicked: {
nativeConfigString.selectAll()
nativeConfigString.copy()
nativeConfigString.select(0, 0)
PageController.showNotificationMessage(qsTr("Copied"))
}
}
BasicButtonType {
@@ -149,83 +140,117 @@ DrawerType {
text: qsTr("Show connection settings")
onClicked: {
configContentDrawer.visible = true
clickedFunc: function() {
configContentDrawer.open()
}
}
DrawerType {
DrawerType2 {
id: configContentDrawer
width: parent.width
height: parent.height * 0.9
parent: root.parent
BackButtonType {
id: backButton
anchors.fill: parent
expandedHeight: parent.height * 0.9
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
anchors.topMargin: 16
expandedContent: Item {
id: configContentContainer
backButtonFunction: function() {
configContentDrawer.visible = false
implicitHeight: configContentDrawer.expandedHeight
Connections {
target: copyNativeConfigStringButton
function onClicked() {
nativeConfigString.selectAll()
nativeConfigString.copy()
nativeConfigString.select(0, 0)
PageController.showNotificationMessage(qsTr("Copied"))
}
}
}
FlickableType {
anchors.top: backButton.bottom
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: parent.bottom
contentHeight: configContent.implicitHeight + configContent.anchors.topMargin + configContent.anchors.bottomMargin
ColumnLayout {
id: configContent
anchors.fill: parent
anchors.rightMargin: 16
anchors.leftMargin: 16
Header2Type {
id: configContentHeader
Layout.fillWidth: true
Layout.topMargin: 16
Connections {
target: copyConfigTextButton
function onClicked() {
configText.selectAll()
configText.copy()
configText.select(0, 0)
PageController.showNotificationMessage(qsTr("Copied"))
}
}
TextField {
id: nativeConfigString
visible: false
text: ExportController.nativeConfigString
BackButtonType {
id: backButton
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
anchors.topMargin: 16
backButtonFunction: function() {
configContentDrawer.close()
}
}
TextArea {
id: configText
FlickableType {
anchors.top: backButton.bottom
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: parent.bottom
contentHeight: configContent.implicitHeight + configContent.anchors.topMargin + configContent.anchors.bottomMargin
Layout.fillWidth: true
Layout.topMargin: 16
Layout.bottomMargin: 16
ColumnLayout {
id: configContent
padding: 0
leftPadding: 0
height: 24
anchors.fill: parent
anchors.rightMargin: 16
anchors.leftMargin: 16
readOnly: true
Header2Type {
id: configContentHeader
Layout.fillWidth: true
Layout.topMargin: 16
color: "#D7D8DB"
selectionColor: "#633303"
selectedTextColor: "#D7D8DB"
headerText: root.configContentHeaderText
}
font.pixelSize: 16
font.weight: Font.Medium
font.family: "PT Root UI VF"
TextField {
id: nativeConfigString
visible: false
text: ExportController.nativeConfigString
text: ExportController.config
onTextChanged: {
copyNativeConfigStringButton.visible = nativeConfigString.text !== ""
}
}
wrapMode: Text.Wrap
TextArea {
id: configText
background: Rectangle {
color: "transparent"
Layout.fillWidth: true
Layout.topMargin: 16
Layout.bottomMargin: 16
padding: 0
leftPadding: 0
height: 24
readOnly: true
color: "#D7D8DB"
selectionColor: "#633303"
selectedTextColor: "#D7D8DB"
font.pixelSize: 16
font.weight: Font.Medium
font.family: "PT Root UI VF"
text: ExportController.config
wrapMode: Text.Wrap
background: Rectangle {
color: "transparent"
}
}
}
}

View File

@@ -16,47 +16,39 @@ Button {
property string textColor: "#0E0E11"
property string borderColor: "#D7D8DB"
property string borderFocusedColor: "#D7D8DB"
property int borderWidth: 0
property int borderFocusedWidth: 1
property string imageSource
property string rightImageSource
property string leftImageColor: textColor
property bool squareLeftSide: false
property var clickedFunc
implicitHeight: 56
hoverEnabled: true
background: Rectangle {
id: background
id: background_border
color: "transparent"
border.color: root.activeFocus ? root.borderFocusedColor : "transparent"
border.width: root.activeFocus ? root.borderFocusedWidth : "transparent"
anchors.fill: parent
radius: 16
color: {
if (root.enabled) {
if (root.pressed) {
return pressedColor
}
return root.hovered ? hoveredColor : defaultColor
} else {
return disabledColor
}
}
border.color: borderColor
border.width: borderWidth
Behavior on color {
PropertyAnimation { duration: 200 }
}
Rectangle {
visible: root.squareLeftSide
id: background
z: 1
anchors.fill: background_border
anchors.margins: root.activeFocus ? 2: 0
width: parent.radius
height: parent.radius
anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.left: parent.left
radius: 16
color: {
if (root.enabled) {
if (root.pressed) {
@@ -67,24 +59,53 @@ Button {
return disabledColor
}
}
border.color: root.activeFocus ? "transparent" : borderColor
border.width: root.activeFocus ? 0 : borderWidth
Behavior on color {
PropertyAnimation { duration: 200 }
}
Rectangle {
visible: root.squareLeftSide
z: 1
width: parent.radius
height: parent.radius
anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.left: parent.left
color: {
if (root.enabled) {
if (root.pressed) {
return pressedColor
}
return root.hovered ? hoveredColor : defaultColor
} else {
return disabledColor
}
}
Behavior on color {
PropertyAnimation { duration: 200 }
}
}
}
}
MouseArea {
anchors.fill: background
anchors.fill: background_border
enabled: false
cursorShape: Qt.PointingHandCursor
}
contentItem: Item {
anchors.fill: background
anchors.fill: background_border
implicitWidth: content.implicitWidth
implicitHeight: content.implicitHeight
RowLayout {
id: content
anchors.centerIn: parent
@@ -99,7 +120,7 @@ Button {
layer {
enabled: true
effect: ColorOverlay {
color: textColor
color: leftImageColor
}
}
}
@@ -112,6 +133,39 @@ Button {
horizontalAlignment: Text.AlignLeft
verticalAlignment: Text.AlignVCenter
}
Image {
Layout.preferredHeight: 20
Layout.preferredWidth: 20
source: root.rightImageSource
visible: root.rightImageSource === "" ? false : true
layer {
enabled: true
effect: ColorOverlay {
color: textColor
}
}
}
}
}
Keys.onEnterPressed: {
if (root.clickedFunc && typeof root.clickedFunc === "function") {
root.clickedFunc()
}
}
Keys.onReturnPressed: {
if (root.clickedFunc && typeof root.clickedFunc === "function") {
root.clickedFunc()
}
}
onClicked: {
if (root.clickedFunc && typeof root.clickedFunc === "function") {
root.clickedFunc()
}
}
}

View File

@@ -1,84 +0,0 @@
import QtQuick
import QtQuick.Controls
import "../Config"
Drawer {
id: drawer
property bool needCloseButton: true
Connections {
target: PageController
function onForceCloseDrawer() {
visible = false
}
}
edge: Qt.BottomEdge
clip: true
modal: true
dragMargin: -10
enter: Transition {
SmoothedAnimation {
velocity: 4
}
}
exit: Transition {
SmoothedAnimation {
velocity: 4
}
}
background: Rectangle {
anchors.fill: parent
anchors.bottomMargin: -radius
radius: 16
color: "#1C1D21"
border.color: "#2C2D30"
border.width: 1
Rectangle {
visible: GC.isMobile()
anchors.top: parent.top
anchors.horizontalCenter: parent.horizontalCenter
anchors.topMargin: 10
width: 20
height: 2
color: "#2C2D30"
}
}
Overlay.modal: Rectangle {
color: Qt.rgba(14/255, 14/255, 17/255, 0.8)
}
onAboutToShow: {
if (PageController.getInitialPageNavigationBarColor() !== 0xFF1C1D21) {
PageController.updateNavigationBarColor(0xFF1C1D21)
}
}
onOpened: {
if (needCloseButton) {
PageController.drawerOpen()
}
}
onClosed: {
if (needCloseButton) {
PageController.drawerClose()
}
var initialPageNavigationBarColor = PageController.getInitialPageNavigationBarColor()
if (initialPageNavigationBarColor !== 0xFF1C1D21) {
PageController.updateNavigationBarColor(initialPageNavigationBarColor)
}
}
}

View File

@@ -0,0 +1,266 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import "TextTypes"
Item {
id: root
readonly property string drawerExpanded: "expanded"
readonly property string drawerCollapsed: "collapsed"
readonly property bool isOpened: drawerContent.state === root.drawerExpanded || (drawerContent.state === root.drawerCollapsed && dragArea.drag.active === true)
readonly property bool isClosed: drawerContent.state === root.drawerCollapsed && dragArea.drag.active === false
readonly property bool isExpanded: drawerContent.state === root.drawerExpanded
readonly property bool isCollapsed: drawerContent.state === root.drawerCollapsed
property Component collapsedContent
property Component expandedContent
property string defaultColor: "#1C1D21"
property string borderColor: "#2C2D30"
property real expandedHeight
property real collapsedHeight: 0
property int depthIndex: 0
signal entered
signal exited
signal pressed(bool pressed, bool entered)
signal aboutToHide
signal aboutToShow
signal close
signal open
signal closed
signal opened
Connections {
target: PageController
function onCloseTopDrawer() {
if (depthIndex === PageController.getDrawerDepth()) {
if (isCollapsed) {
return
}
aboutToHide()
drawerContent.state = root.drawerCollapsed
depthIndex = 0
closed()
}
}
}
Connections {
target: root
function onClose() {
if (isCollapsed) {
return
}
aboutToHide()
drawerContent.state = root.drawerCollapsed
depthIndex = 0
PageController.setDrawerDepth(PageController.getDrawerDepth() - 1)
closed()
}
function onOpen() {
if (isExpanded) {
return
}
aboutToShow()
drawerContent.state = root.drawerExpanded
depthIndex = PageController.getDrawerDepth() + 1
PageController.setDrawerDepth(depthIndex)
opened()
}
}
/** Set once based on first implicit height change once all children are layed out */
Component.onCompleted: {
if (root.isCollapsed && root.collapsedHeight == 0) {
root.collapsedHeight = drawerContent.implicitHeight
}
}
Rectangle {
id: background
anchors.fill: parent
color: root.isCollapsed ? "transparent" : Qt.rgba(14/255, 14/255, 17/255, 0.8)
Behavior on color {
PropertyAnimation { duration: 200 }
}
}
MouseArea {
id: emptyArea
anchors.fill: parent
enabled: root.isExpanded
visible: enabled
onClicked: {
root.close()
}
}
MouseArea {
id: dragArea
anchors.fill: drawerContentBackground
cursorShape: root.isCollapsed ? Qt.PointingHandCursor : Qt.ArrowCursor
hoverEnabled: true
enabled: drawerContent.implicitHeight > 0
drag.target: drawerContent
drag.axis: Drag.YAxis
drag.maximumY: root.height - root.collapsedHeight
drag.minimumY: root.height - root.expandedHeight
/** If drag area is released at any point other than min or max y, transition to the other state */
onReleased: {
if (root.isCollapsed && drawerContent.y < dragArea.drag.maximumY) {
root.open()
return
}
if (root.isExpanded && drawerContent.y > dragArea.drag.minimumY) {
root.close()
return
}
}
onEntered: {
root.entered()
}
onExited: {
root.exited()
}
onPressedChanged: {
root.pressed(pressed, entered)
}
onClicked: {
if (root.isCollapsed) {
root.open()
}
}
}
Rectangle {
id: drawerContentBackground
anchors { left: drawerContent.left; right: drawerContent.right; top: drawerContent.top }
height: root.height
radius: 16
color: root.defaultColor
border.color: root.borderColor
border.width: 1
Rectangle {
width: parent.radius
height: parent.radius
anchors.bottom: parent.bottom
anchors.right: parent.right
anchors.left: parent.left
color: parent.color
}
}
Item {
id: drawerContent
Drag.active: dragArea.drag.active
anchors.right: root.right
anchors.left: root.left
y: root.height - drawerContent.height
state: root.drawerCollapsed
implicitHeight: root.isCollapsed ? collapsedLoader.implicitHeight : expandedLoader.implicitHeight
onStateChanged: {
if (root.isCollapsed) {
var initialPageNavigationBarColor = PageController.getInitialPageNavigationBarColor()
if (initialPageNavigationBarColor !== 0xFF1C1D21) {
PageController.updateNavigationBarColor(initialPageNavigationBarColor)
}
return
}
if (root.isExpanded) {
if (PageController.getInitialPageNavigationBarColor() !== 0xFF1C1D21) {
PageController.updateNavigationBarColor(0xFF1C1D21)
}
return
}
}
states: [
State {
name: root.drawerCollapsed
PropertyChanges {
target: drawerContent
y: root.height - root.collapsedHeight
}
},
State {
name: root.drawerExpanded
PropertyChanges {
target: drawerContent
y: dragArea.drag.minimumY
}
}
]
transitions: [
Transition {
from: root.drawerCollapsed
to: root.drawerExpanded
PropertyAnimation {
target: drawerContent
properties: "y"
duration: 200
}
},
Transition {
from: root.drawerExpanded
to: root.drawerCollapsed
PropertyAnimation {
target: drawerContent
properties: "y"
duration: 200
}
}
]
Loader {
id: collapsedLoader
visible: root.isCollapsed
sourceComponent: root.collapsedContent
anchors.right: parent.right
anchors.left: parent.left
}
Loader {
id: expandedLoader
visible: root.isExpanded
sourceComponent: root.expandedContent
anchors.right: parent.right
anchors.left: parent.left
}
}
}

View File

@@ -36,19 +36,23 @@ Item {
property int rootButtonTextBottomMargin: 16
property real drawerHeight: 0.9
property Item drawerParent
property Component listView
property alias menuVisible: menu.visible
signal open
signal close
implicitWidth: rootButtonContent.implicitWidth
implicitHeight: rootButtonContent.implicitHeight
onMenuVisibleChanged: {
if (menuVisible) {
rootButtonBackground.border.color = rootButtonPressedBorderColor
} else {
rootButtonBackground.border.color = rootButtonDefaultBorderColor
}
onOpen: {
menu.open()
rootButtonBackground.border.color = rootButtonPressedBorderColor
}
onClose: {
menu.close()
rootButtonBackground.border.color = rootButtonDefaultBorderColor
}
onEnabledChanged: {
@@ -133,21 +137,21 @@ Item {
hoverEnabled: root.enabled ? true : false
onEntered: {
if (menu.visible === false) {
if (menu.isClosed) {
rootButtonBackground.border.color = rootButtonHoveredBorderColor
rootButtonBackground.color = rootButtonBackgroundHoveredColor
}
}
onExited: {
if (menu.visible === false) {
if (menu.isClosed) {
rootButtonBackground.border.color = rootButtonDefaultBorderColor
rootButtonBackground.color = rootButtonBackgroundColor
}
}
onPressed: {
if (menu.visible === false) {
if (menu.isClosed) {
rootButtonBackground.color = pressed ? rootButtonBackgroundPressedColor : entered ? rootButtonHoveredBorderColor : rootButtonDefaultBorderColor
}
}
@@ -156,60 +160,68 @@ Item {
if (rootButtonClickedFunction && typeof rootButtonClickedFunction === "function") {
rootButtonClickedFunction()
} else {
menu.visible = true
menu.open()
}
}
}
DrawerType {
DrawerType2 {
id: menu
width: parent.width
height: parent.height * drawerHeight
parent: drawerParent
ColumnLayout {
id: header
anchors.fill: parent
expandedHeight: drawerParent.height * drawerHeight
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
anchors.topMargin: 16
expandedContent: Item {
id: container
BackButtonType {
backButtonImage: root.headerBackButtonImage
backButtonFunction: function() {
root.menuVisible = false
}
}
}
implicitHeight: menu.expandedHeight
FlickableType {
anchors.top: header.bottom
anchors.topMargin: 16
contentHeight: col.implicitHeight
ColumnLayout {
id: header
Column {
id: col
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
anchors.topMargin: 16
spacing: 16
BackButtonType {
backButtonImage: root.headerBackButtonImage
backButtonFunction: function() {
menu.close()
}
}
}
Header2Type {
FlickableType {
anchors.top: header.bottom
anchors.topMargin: 16
contentHeight: col.implicitHeight
Column {
id: col
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
anchors.leftMargin: 16
anchors.rightMargin: 16
headerText: root.headerText
spacing: 16
width: parent.width
}
Header2Type {
anchors.left: parent.left
anchors.right: parent.right
anchors.leftMargin: 16
anchors.rightMargin: 16
Loader {
id: listViewLoader
sourceComponent: root.listView
headerText: root.headerText
width: parent.width
}
Loader {
id: listViewLoader
sourceComponent: root.listView
}
}
}
}

View File

@@ -7,6 +7,8 @@ Item {
property StackView stackView: StackView.view
property var defaultActiveFocusItem: null
// MouseArea {
// id: globalMouseArea
// z: 99
@@ -19,4 +21,17 @@ Item {
// mouse.accepted = false
// }
// }
// Set a timer to set focus after a short delay
Timer {
id: timer
interval: 100 // Milliseconds
onTriggered: {
if (defaultActiveFocusItem) {
defaultActiveFocusItem.forceActiveFocus()
}
}
repeat: false // Stop the timer after one trigger
running: true // Start the timer
}
}

View File

@@ -66,7 +66,7 @@ Popup {
borderWidth: 0
text: qsTr("Close")
onClicked: {
clickedFunc: function() {
root.close()
}
}

View File

@@ -69,10 +69,13 @@ Item {
TextField {
id: textField
activeFocusOnTab: false
enabled: root.textFieldEditable
color: root.enabled ? root.textFieldTextColor : root.textFieldTextDisabledColor
inputMethodHints: Qt.ImhNoAutoUppercase | Qt.ImhSensitiveData | Qt.ImhNoPredictiveText
placeholderText: root.textFieldPlaceholderText
placeholderTextColor: "#494B50"
@@ -142,7 +145,7 @@ Item {
Layout.preferredWidth: content.implicitHeight
squareLeftSide: true
onClicked: {
clickedFunc: function() {
if (root.clickedFunc && typeof root.clickedFunc === "function") {
root.clickedFunc()
}
@@ -186,4 +189,12 @@ Item {
function getBackgroundBorderColor(noneFocusedColor) {
return textField.focus ? root.borderFocusedColor : noneFocusedColor
}
Keys.onEnterPressed: {
KeyNavigation.tab.forceActiveFocus();
}
Keys.onReturnPressed: {
KeyNavigation.tab.forceActiveFocus();
}
}

View File

@@ -14,8 +14,8 @@ import "../Config"
PageType {
id: root
Component.onCompleted: PageController.enableTabBar(false)
Component.onDestruction: PageController.enableTabBar(true)
Component.onCompleted: PageController.disableControls(true)
Component.onDestruction: PageController.disableControls(false)
SortFilterProxyModel {
id: proxyServersModel

View File

@@ -18,484 +18,383 @@ import "../Components"
PageType {
id: root
property string defaultColor: "#1C1D21"
property string borderColor: "#2C2D30"
Connections {
target: PageController
function onRestorePageHomeState(isContainerInstalled) {
buttonContent.state = "expanded"
drawer.open()
if (isContainerInstalled) {
containersDropDown.rootButtonClickedFunction()
}
}
function onForceCloseDrawer() {
buttonContent.state = "collapsed"
}
}
MouseArea {
anchors.fill: parent
enabled: buttonContent.state === "expanded"
onClicked: {
buttonContent.state = "collapsed"
}
}
Item {
anchors.fill: parent
anchors.bottomMargin: buttonContent.collapsedHeight
anchors.bottomMargin: drawer.collapsedHeight
ConnectButton {
id: connectButton
anchors.centerIn: parent
}
}
MouseArea {
id: dragArea
anchors.fill: buttonBackground
cursorShape: buttonContent.state === "collapsed" ? Qt.PointingHandCursor : Qt.ArrowCursor
hoverEnabled: true
drag.target: buttonContent
drag.axis: Drag.YAxis
drag.maximumY: root.height - buttonContent.collapsedHeight
drag.minimumY: root.height - root.height * 0.9
/** If drag area is released at any point other than min or max y, transition to the other state */
onReleased: {
if (buttonContent.state === "collapsed" && buttonContent.y < dragArea.drag.maximumY) {
buttonContent.state = "expanded"
return
}
if (buttonContent.state === "expanded" && buttonContent.y > dragArea.drag.minimumY) {
buttonContent.state = "collapsed"
return
}
}
onEntered: {
collapsedButtonChevron.backgroundColor = collapsedButtonChevron.hoveredColor
collapsedButtonHeader.opacity = 0.8
}
onExited: {
collapsedButtonChevron.backgroundColor = collapsedButtonChevron.defaultColor
collapsedButtonHeader.opacity = 1
}
onPressedChanged: {
collapsedButtonChevron.backgroundColor = pressed ? collapsedButtonChevron.pressedColor : entered ? collapsedButtonChevron.hoveredColor : collapsedButtonChevron.defaultColor
collapsedButtonHeader.opacity = 0.7
}
onClicked: {
if (buttonContent.state === "collapsed") {
buttonContent.state = "expanded"
}
}
}
Rectangle {
id: buttonBackground
anchors { left: buttonContent.left; right: buttonContent.right; top: buttonContent.top }
height: root.height
radius: 16
color: root.defaultColor
border.color: root.borderColor
border.width: 1
Rectangle {
width: parent.radius
height: parent.radius
BasicButtonType {
anchors.horizontalCenter: parent.horizontalCenter
anchors.bottom: parent.bottom
anchors.right: parent.right
anchors.left: parent.left
color: parent.color
anchors.bottomMargin: 34
leftPadding: 16
rightPadding: 16
implicitHeight: 36
defaultColor: "transparent"
hoveredColor: Qt.rgba(1, 1, 1, 0.08)
pressedColor: Qt.rgba(1, 1, 1, 0.12)
disabledColor: "#878B91"
textColor: "#878B91"
leftImageColor: "transparent"
borderWidth: 0
property bool isSplitTunnelingEnabled: SitesModel.isTunnelingEnabled ||
(ServersModel.isDefaultServerDefaultContainerHasSplitTunneling && ServersModel.getDefaultServerData("isServerFromApi"))
text: isSplitTunnelingEnabled ? qsTr("Split tunneling enabled") : qsTr("Split tunneling disabled")
imageSource: isSplitTunnelingEnabled ? "qrc:/images/controls/split-tunneling.svg" : ""
rightImageSource: "qrc:/images/controls/chevron-down.svg"
onClicked: {
homeSplitTunnelingDrawer.open()
}
HomeSplitTunnelingDrawer {
id: homeSplitTunnelingDrawer
parent: root
}
}
}
ColumnLayout {
id: buttonContent
/** Initial height of button content */
property int collapsedHeight: 0
/** True when expanded objects should be visible */
property bool expandedVisibility: buttonContent.state === "expanded" || (buttonContent.state === "collapsed" && dragArea.drag.active === true)
/** True when collapsed objects should be visible */
property bool collapsedVisibility: buttonContent.state === "collapsed" && dragArea.drag.active === false
DrawerType2 {
id: drawer
anchors.fill: parent
Drag.active: dragArea.drag.active
anchors.right: root.right
anchors.left: root.left
y: root.height - buttonContent.height
Component.onCompleted: {
buttonContent.state = "collapsed"
}
/** Set once based on first implicit height change once all children are layed out */
onImplicitHeightChanged: {
if (buttonContent.state === "collapsed" && collapsedHeight == 0) {
collapsedHeight = implicitHeight
}
}
onStateChanged: {
if (buttonContent.state === "collapsed") {
var initialPageNavigationBarColor = PageController.getInitialPageNavigationBarColor()
if (initialPageNavigationBarColor !== 0xFF1C1D21) {
PageController.updateNavigationBarColor(initialPageNavigationBarColor)
}
PageController.drawerClose()
return
}
if (buttonContent.state === "expanded") {
if (PageController.getInitialPageNavigationBarColor() !== 0xFF1C1D21) {
PageController.updateNavigationBarColor(0xFF1C1D21)
}
PageController.drawerOpen()
return
}
}
/** Two states of buttonContent, great place to add any future animations for the drawer */
states: [
State {
name: "collapsed"
PropertyChanges {
target: buttonContent
y: root.height - collapsedHeight
}
},
State {
name: "expanded"
PropertyChanges {
target: buttonContent
y: dragArea.drag.minimumY
}
}
]
transitions: [
Transition {
from: "collapsed"
to: "expanded"
PropertyAnimation {
target: buttonContent
properties: "y"
duration: 200
}
},
Transition {
from: "expanded"
to: "collapsed"
PropertyAnimation {
target: buttonContent
properties: "y"
duration: 200
}
}
]
DividerType {
Layout.topMargin: 10
Layout.fillWidth: false
Layout.preferredWidth: 20
Layout.preferredHeight: 2
Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter
visible: (buttonContent.collapsedVisibility || buttonContent.expandedVisibility)
}
RowLayout {
Layout.topMargin: 14
Layout.leftMargin: 24
Layout.rightMargin: 24
Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter
visible: buttonContent.collapsedVisibility
spacing: 0
Header1TextType {
id: collapsedButtonHeader
Layout.maximumWidth: buttonContent.width - 48 - 18 - 12 // todo
maximumLineCount: 2
elide: Qt.ElideRight
text: ServersModel.defaultServerName
horizontalAlignment: Qt.AlignHCenter
Behavior on opacity {
PropertyAnimation { duration: 200 }
}
}
ImageButtonType {
id: collapsedButtonChevron
Layout.leftMargin: 8
hoverEnabled: false
image: "qrc:/images/controls/chevron-down.svg"
imageColor: "#d7d8db"
icon.width: 18
icon.height: 18
backgroundRadius: 16
horizontalPadding: 4
topPadding: 4
bottomPadding: 3
onClicked: {
if (buttonContent.state === "collapsed") {
buttonContent.state = "expanded"
}
}
}
}
LabelTextType {
id: collapsedServerMenuDescription
Layout.bottomMargin: 44
Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter
visible: buttonContent.collapsedVisibility
text: ServersModel.defaultServerDescriptionCollapsed
}
ColumnLayout {
id: serversMenuHeader
Layout.alignment: Qt.AlignTop | Qt.AlignHCenter
Layout.fillWidth: true
visible: buttonContent.expandedVisibility
Header1TextType {
Layout.fillWidth: true
Layout.topMargin: 14
Layout.leftMargin: 16
Layout.rightMargin: 16
text: ServersModel.defaultServerName
horizontalAlignment: Qt.AlignHCenter
maximumLineCount: 2
elide: Qt.ElideRight
}
LabelTextType {
id: expandedServersMenuDescription
Layout.bottomMargin: 24
Layout.fillWidth: true
horizontalAlignment: Qt.AlignHCenter
verticalAlignment: Qt.AlignVCenter
text: ServersModel.defaultServerDescriptionExpanded
collapsedContent: ColumnLayout {
DividerType {
Layout.topMargin: 10
Layout.fillWidth: false
Layout.preferredWidth: 20
Layout.preferredHeight: 2
Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter
}
RowLayout {
Layout.topMargin: 14
Layout.leftMargin: 24
Layout.rightMargin: 24
Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter
spacing: 8
DropDownType {
id: containersDropDown
spacing: 0
rootButtonImageColor: "#0E0E11"
rootButtonBackgroundColor: "#D7D8DB"
rootButtonBackgroundHoveredColor: Qt.rgba(215, 216, 219, 0.8)
rootButtonBackgroundPressedColor: Qt.rgba(215, 216, 219, 0.65)
rootButtonHoveredBorderColor: "transparent"
rootButtonDefaultBorderColor: "transparent"
rootButtonTextTopMargin: 8
rootButtonTextBottomMargin: 8
text: ServersModel.defaultContainerName
textColor: "#0E0E11"
headerText: qsTr("VPN protocol")
headerBackButtonImage: "qrc:/images/controls/arrow-left.svg"
rootButtonClickedFunction: function() {
ServersModel.currentlyProcessedIndex = serversMenuContent.currentIndex
containersDropDown.menuVisible = true
Connections {
target: drawer
function onEntered() {
collapsedButtonChevron.backgroundColor = collapsedButtonChevron.hoveredColor
collapsedButtonHeader.opacity = 0.8
}
listView: HomeContainersListView {
rootWidth: root.width
function onExited() {
collapsedButtonChevron.backgroundColor = collapsedButtonChevron.defaultColor
collapsedButtonHeader.opacity = 1
}
function onPressed(pressed, entered) {
collapsedButtonChevron.backgroundColor = pressed ? collapsedButtonChevron.pressedColor : entered ? collapsedButtonChevron.hoveredColor : collapsedButtonChevron.defaultColor
collapsedButtonHeader.opacity = 0.7
}
}
Header1TextType {
id: collapsedButtonHeader
Layout.maximumWidth: drawer.width - 48 - 18 - 12 // todo
maximumLineCount: 2
elide: Qt.ElideRight
text: ServersModel.defaultServerName
horizontalAlignment: Qt.AlignHCenter
Behavior on opacity {
PropertyAnimation { duration: 200 }
}
}
ImageButtonType {
id: collapsedButtonChevron
Layout.leftMargin: 8
hoverEnabled: false
image: "qrc:/images/controls/chevron-down.svg"
imageColor: "#d7d8db"
icon.width: 18
icon.height: 18
backgroundRadius: 16
horizontalPadding: 4
topPadding: 4
bottomPadding: 3
onClicked: {
if (drawer.isCollapsed) {
drawer.open()
}
}
}
}
LabelTextType {
id: collapsedServerMenuDescription
Layout.bottomMargin: 44
Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter
text: ServersModel.defaultServerDescriptionCollapsed
}
}
expandedContent: Item {
id: serverMenuContainer
implicitHeight: root.height * 0.9
Component.onCompleted: {
drawer.expandedHeight = serverMenuContainer.implicitHeight
}
ColumnLayout {
id: serversMenuHeader
anchors.top: parent.top
anchors.right: parent.right
anchors.left: parent.left
Header1TextType {
Layout.fillWidth: true
Layout.topMargin: 14
Layout.leftMargin: 16
Layout.rightMargin: 16
text: ServersModel.defaultServerName
horizontalAlignment: Qt.AlignHCenter
maximumLineCount: 2
elide: Qt.ElideRight
}
LabelTextType {
id: expandedServersMenuDescription
Layout.bottomMargin: ServersModel.isDefaultServerFromApi ? 69 : 24
Layout.fillWidth: true
horizontalAlignment: Qt.AlignHCenter
verticalAlignment: Qt.AlignVCenter
text: ServersModel.defaultServerDescriptionExpanded
}
RowLayout {
Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter
spacing: 8
visible: !ServersModel.isDefaultServerFromApi
onVisibleChanged: expandedServersMenuDescription.Layout
DropDownType {
id: containersDropDown
rootButtonImageColor: "#0E0E11"
rootButtonBackgroundColor: "#D7D8DB"
rootButtonBackgroundHoveredColor: Qt.rgba(215, 216, 219, 0.8)
rootButtonBackgroundPressedColor: Qt.rgba(215, 216, 219, 0.65)
rootButtonHoveredBorderColor: "transparent"
rootButtonDefaultBorderColor: "transparent"
rootButtonTextTopMargin: 8
rootButtonTextBottomMargin: 8
text: ServersModel.defaultServerDefaultContainerName
textColor: "#0E0E11"
headerText: qsTr("VPN protocol")
headerBackButtonImage: "qrc:/images/controls/arrow-left.svg"
rootButtonClickedFunction: function() {
containersDropDown.open()
}
drawerParent: root
listView: HomeContainersListView {
rootWidth: root.width
Connections {
target: ServersModel
function onDefaultServerIndexChanged() {
updateContainersModelFilters()
}
}
function updateContainersModelFilters() {
if (ServersModel.isDefaultServerHasWriteAccess()) {
proxyDefaultServerContainersModel.filters = ContainersModelFilters.getWriteAccessProtocolsListFilters()
} else {
proxyDefaultServerContainersModel.filters = ContainersModelFilters.getReadAccessProtocolsListFilters()
}
}
model: SortFilterProxyModel {
id: proxyDefaultServerContainersModel
sourceModel: DefaultServerContainersModel
sorters: [
RoleSorter { roleName: "isInstalled"; sortOrder: Qt.DescendingOrder }
]
}
Component.onCompleted: updateContainersModelFilters()
}
}
}
Header2Type {
Layout.fillWidth: true
Layout.topMargin: 48
Layout.leftMargin: 16
Layout.rightMargin: 16
headerText: qsTr("Servers")
}
}
Flickable {
id: serversContainer
anchors.top: serversMenuHeader.bottom
anchors.right: parent.right
anchors.left: parent.left
anchors.topMargin: 16
contentHeight: col.height + col.anchors.bottomMargin
implicitHeight: parent.height - serversMenuHeader.implicitHeight
clip: true
ScrollBar.vertical: ScrollBar {
id: scrollBar
policy: serversContainer.height >= serversContainer.contentHeight ? ScrollBar.AlwaysOff : ScrollBar.AlwaysOn
}
Keys.onUpPressed: scrollBar.decrease()
Keys.onDownPressed: scrollBar.increase()
Column {
id: col
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
anchors.bottomMargin: 32
spacing: 16
ButtonGroup {
id: serversRadioButtonGroup
}
ListView {
id: serversMenuContent
width: parent.width
height: serversMenuContent.contentItem.height
model: ServersModel
currentIndex: ServersModel.defaultIndex
Connections {
target: ServersModel
function onCurrentlyProcessedServerIndexChanged() {
updateContainersModelFilters()
function onDefaultServerIndexChanged(serverIndex) {
serversMenuContent.currentIndex = serverIndex
}
}
function updateContainersModelFilters() {
if (ServersModel.isCurrentlyProcessedServerHasWriteAccess()) {
proxyContainersModel.filters = ContainersModelFilters.getWriteAccessProtocolsListFilters()
} else {
proxyContainersModel.filters = ContainersModelFilters.getReadAccessProtocolsListFilters()
}
}
clip: true
interactive: false
model: SortFilterProxyModel {
id: proxyContainersModel
sourceModel: ContainersModel
}
delegate: Item {
id: menuContentDelegate
Component.onCompleted: updateContainersModelFilters()
}
}
}
property variant delegateData: model
Header2Type {
Layout.fillWidth: true
Layout.topMargin: 48
Layout.leftMargin: 16
Layout.rightMargin: 16
visible: buttonContent.expandedVisibility
implicitWidth: serversMenuContent.width
implicitHeight: serverRadioButtonContent.implicitHeight
headerText: qsTr("Servers")
}
}
ColumnLayout {
id: serverRadioButtonContent
Flickable {
id: serversContainer
Layout.alignment: Qt.AlignTop | Qt.AlignHCenter
Layout.fillWidth: true
Layout.topMargin: 16
contentHeight: col.implicitHeight
implicitHeight: root.height - (root.height * 0.1) - serversMenuHeader.implicitHeight - 52 //todo 52 is tabbar height
visible: buttonContent.expandedVisibility
clip: true
anchors.fill: parent
anchors.rightMargin: 16
anchors.leftMargin: 16
ScrollBar.vertical: ScrollBar {
id: scrollBar
policy: serversContainer.height >= serversContainer.contentHeight ? ScrollBar.AlwaysOff : ScrollBar.AlwaysOn
}
spacing: 0
Keys.onUpPressed: scrollBar.decrease()
Keys.onDownPressed: scrollBar.increase()
RowLayout {
VerticalRadioButton {
id: serverRadioButton
Column {
id: col
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
Layout.fillWidth: true
spacing: 16
text: name
descriptionText: serverDescription
ButtonGroup {
id: serversRadioButtonGroup
}
checked: index === serversMenuContent.currentIndex
checkable: !ConnectionController.isConnected
ListView {
id: serversMenuContent
width: parent.width
height: serversMenuContent.contentItem.height
ButtonGroup.group: serversRadioButtonGroup
model: ServersModel
currentIndex: ServersModel.defaultIndex
onClicked: {
if (ConnectionController.isConnected) {
PageController.showNotificationMessage(qsTr("Unable change server while there is an active connection"))
return
}
Connections {
target: ServersModel
function onDefaultServerIndexChanged(serverIndex) {
serversMenuContent.currentIndex = serverIndex
}
}
serversMenuContent.currentIndex = index
clip: true
interactive: false
ServersModel.defaultIndex = index
}
delegate: Item {
id: menuContentDelegate
MouseArea {
anchors.fill: serverRadioButton
cursorShape: Qt.PointingHandCursor
enabled: false
}
}
property variant delegateData: model
ImageButtonType {
image: "qrc:/images/controls/settings.svg"
imageColor: "#D7D8DB"
implicitWidth: serversMenuContent.width
implicitHeight: serverRadioButtonContent.implicitHeight
implicitWidth: 56
implicitHeight: 56
ColumnLayout {
id: serverRadioButtonContent
z: 1
anchors.fill: parent
anchors.rightMargin: 16
anchors.leftMargin: 16
spacing: 0
RowLayout {
VerticalRadioButton {
id: serverRadioButton
onClicked: function() {
ServersModel.processedIndex = index
PageController.goToPage(PageEnum.PageSettingsServerInfo)
drawer.close()
}
}
}
DividerType {
Layout.fillWidth: true
text: name
descriptionText: {
var fullDescription = ""
if (hasWriteAccess) {
if (SettingsController.isAmneziaDnsEnabled()
&& ServersModel.isAmneziaDnsContainerInstalled(index)) {
fullDescription += "Amnezia DNS | "
}
} else {
if (containsAmneziaDns) {
fullDescription += "Amnezia DNS | "
}
}
return fullDescription += serverDescription
}
checked: index === serversMenuContent.currentIndex
checkable: !ConnectionController.isConnected
ButtonGroup.group: serversRadioButtonGroup
onClicked: {
if (ConnectionController.isConnected) {
PageController.showNotificationMessage(qsTr("Unable change server while there is an active connection"))
return
}
serversMenuContent.currentIndex = index
ServersModel.currentlyProcessedIndex = index
ServersModel.defaultIndex = index
}
MouseArea {
anchors.fill: serverRadioButton
cursorShape: Qt.PointingHandCursor
enabled: false
}
Layout.leftMargin: 0
Layout.rightMargin: 0
}
ImageButtonType {
image: "qrc:/images/controls/settings.svg"
imageColor: "#D7D8DB"
implicitWidth: 56
implicitHeight: 56
z: 1
onClicked: function() {
ServersModel.currentlyProcessedIndex = index
PageController.goToPage(PageEnum.PageSettingsServerInfo)
buttonContent.state = "collapsed"
}
}
}
DividerType {
Layout.fillWidth: true
Layout.leftMargin: 0
Layout.rightMargin: 0
}
}
}

View File

@@ -12,9 +12,12 @@ import "../Controls2/TextTypes"
import "../Config"
import "../Components"
PageType {
id: root
defaultActiveFocusItem: listview.currentItem.portTextField.textField
ColumnLayout {
id: backButton
@@ -41,9 +44,11 @@ PageType {
anchors.left: parent.left
anchors.right: parent.right
enabled: ServersModel.isCurrentlyProcessedServerHasWriteAccess()
enabled: ServersModel.isProcessedServerHasWriteAccess()
ListView {
id: listview
width: parent.width
@@ -55,9 +60,13 @@ PageType {
model: AwgConfigModel
delegate: Item {
id: _delegate
implicitWidth: listview.width
implicitHeight: col.implicitHeight
property alias portTextField:portTextField
ColumnLayout {
id: col
@@ -93,6 +102,8 @@ PageType {
}
checkEmptyText: true
KeyNavigation.tab: junkPacketCountTextField.textField
}
TextFieldWithHeaderType {
@@ -100,7 +111,7 @@ PageType {
Layout.fillWidth: true
Layout.topMargin: 16
headerText: qsTr("Junk packet count")
headerText: "Jc - Junk packet count"
textFieldText: junkPacketCount
textField.validator: IntValidator { bottom: 0 }
@@ -116,6 +127,8 @@ PageType {
}
checkEmptyText: true
KeyNavigation.tab: junkPacketMinSizeTextField.textField
}
TextFieldWithHeaderType {
@@ -123,7 +136,7 @@ PageType {
Layout.fillWidth: true
Layout.topMargin: 16
headerText: qsTr("Junk packet minimum size")
headerText: "Jmin - Junk packet minimum size"
textFieldText: junkPacketMinSize
textField.validator: IntValidator { bottom: 0 }
@@ -134,6 +147,8 @@ PageType {
}
checkEmptyText: true
KeyNavigation.tab: junkPacketMaxSizeTextField.textField
}
TextFieldWithHeaderType {
@@ -141,7 +156,7 @@ PageType {
Layout.fillWidth: true
Layout.topMargin: 16
headerText: qsTr("Junk packet maximum size")
headerText: "Jmax - Junk packet maximum size"
textFieldText: junkPacketMaxSize
textField.validator: IntValidator { bottom: 0 }
@@ -152,6 +167,8 @@ PageType {
}
checkEmptyText: true
KeyNavigation.tab: initPacketJunkSizeTextField.textField
}
TextFieldWithHeaderType {
@@ -159,7 +176,7 @@ PageType {
Layout.fillWidth: true
Layout.topMargin: 16
headerText: qsTr("Init packet junk size")
headerText: "S1 - Init packet junk size"
textFieldText: initPacketJunkSize
textField.validator: IntValidator { bottom: 0 }
@@ -170,6 +187,8 @@ PageType {
}
checkEmptyText: true
KeyNavigation.tab: responsePacketJunkSizeTextField.textField
}
TextFieldWithHeaderType {
@@ -177,7 +196,7 @@ PageType {
Layout.fillWidth: true
Layout.topMargin: 16
headerText: qsTr("Response packet junk size")
headerText: "S2 - Response packet junk size"
textFieldText: responsePacketJunkSize
textField.validator: IntValidator { bottom: 0 }
@@ -188,6 +207,8 @@ PageType {
}
checkEmptyText: true
KeyNavigation.tab: initPacketMagicHeaderTextField.textField
}
TextFieldWithHeaderType {
@@ -195,7 +216,7 @@ PageType {
Layout.fillWidth: true
Layout.topMargin: 16
headerText: qsTr("Init packet magic header")
headerText: "H1 - Init packet magic header"
textFieldText: initPacketMagicHeader
textField.validator: IntValidator { bottom: 0 }
@@ -206,6 +227,8 @@ PageType {
}
checkEmptyText: true
KeyNavigation.tab: responsePacketMagicHeaderTextField.textField
}
TextFieldWithHeaderType {
@@ -213,7 +236,7 @@ PageType {
Layout.fillWidth: true
Layout.topMargin: 16
headerText: qsTr("Response packet magic header")
headerText: "H2 - Response packet magic header"
textFieldText: responsePacketMagicHeader
textField.validator: IntValidator { bottom: 0 }
@@ -224,6 +247,8 @@ PageType {
}
checkEmptyText: true
KeyNavigation.tab: transportPacketMagicHeaderTextField.textField
}
TextFieldWithHeaderType {
@@ -231,7 +256,7 @@ PageType {
Layout.fillWidth: true
Layout.topMargin: 16
headerText: qsTr("Transport packet magic header")
headerText: "H4 - Transport packet magic header"
textFieldText: transportPacketMagicHeader
textField.validator: IntValidator { bottom: 0 }
@@ -242,6 +267,8 @@ PageType {
}
checkEmptyText: true
KeyNavigation.tab: underloadPacketMagicHeaderTextField.textField
}
TextFieldWithHeaderType {
@@ -249,7 +276,7 @@ PageType {
Layout.fillWidth: true
Layout.topMargin: 16
headerText: qsTr("Underload packet magic header")
headerText: "H3 - Underload packet magic header"
textFieldText: underloadPacketMagicHeader
textField.validator: IntValidator { bottom: 0 }
@@ -260,6 +287,8 @@ PageType {
}
checkEmptyText: true
KeyNavigation.tab: saveRestartButton
}
BasicButtonType {
@@ -275,24 +304,24 @@ PageType {
text: qsTr("Remove AmneziaWG")
onClicked: {
questionDrawer.headerText = qsTr("Remove AmneziaWG from server?")
questionDrawer.descriptionText = qsTr("All users with whom you shared a connection will no longer be able to connect to it.")
questionDrawer.yesButtonText = qsTr("Continue")
questionDrawer.noButtonText = qsTr("Cancel")
var headerText = qsTr("Remove AmneziaWG from server?")
var descriptionText = qsTr("All users with whom you shared a connection will no longer be able to connect to it.")
var yesButtonText = qsTr("Continue")
var noButtonText = qsTr("Cancel")
questionDrawer.yesButtonFunction = function() {
questionDrawer.visible = false
var yesButtonFunction = function() {
PageController.goToPage(PageEnum.PageDeinstalling)
InstallController.removeCurrentlyProcessedContainer()
}
questionDrawer.noButtonFunction = function() {
questionDrawer.visible = false
var noButtonFunction = function() {
}
questionDrawer.visible = true
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
}
}
BasicButtonType {
id: saveRestartButton
Layout.fillWidth: true
Layout.topMargin: 24
Layout.bottomMargin: 24
@@ -310,7 +339,7 @@ PageType {
text: qsTr("Save and Restart Amnezia")
onClicked: {
clickedFunc: function() {
forceActiveFocus()
PageController.goToPage(PageEnum.PageSetupWizardInstalling);
InstallController.updateContainer(AwgConfigModel.getConfig())
@@ -318,11 +347,8 @@ PageType {
}
}
}
}
}
QuestionDrawer {
id: questionDrawer
}
}
}
}

View File

@@ -15,6 +15,8 @@ import "../Components"
PageType {
id: root
defaultActiveFocusItem: listview.currentItem.trafficFromField.textField
ColumnLayout {
id: backButton
@@ -41,7 +43,7 @@ PageType {
anchors.left: parent.left
anchors.right: parent.right
enabled: ServersModel.isCurrentlyProcessedServerHasWriteAccess()
enabled: ServersModel.isProcessedServerHasWriteAccess()
ListView {
id: listview
@@ -58,6 +60,8 @@ PageType {
implicitWidth: listview.width
implicitHeight: col.implicitHeight
property alias trafficFromField: trafficFromField
ColumnLayout {
id: col
@@ -77,6 +81,8 @@ PageType {
}
TextFieldWithHeaderType {
id: trafficFromField
Layout.fillWidth: true
Layout.topMargin: 32
@@ -96,9 +102,13 @@ PageType {
}
}
}
KeyNavigation.tab: portTextField.textField
}
TextFieldWithHeaderType {
id: portTextField
Layout.fillWidth: true
Layout.topMargin: 16
@@ -112,6 +122,8 @@ PageType {
port = textFieldText
}
}
KeyNavigation.tab: saveRestartButton
}
DropDownType {
@@ -122,6 +134,8 @@ PageType {
descriptionText: qsTr("Cipher")
headerText: qsTr("Cipher")
drawerParent: root
listView: ListViewWithRadioButtonType {
id: cipherListView
@@ -138,7 +152,7 @@ PageType {
clickedFunction: function() {
cipherDropDown.text = selectedText
cipher = cipherDropDown.text
cipherDropDown.menuVisible = false
cipherDropDown.close()
}
Component.onCompleted: {
@@ -154,13 +168,15 @@ PageType {
}
BasicButtonType {
id: saveRestartButton
Layout.fillWidth: true
Layout.topMargin: 24
Layout.bottomMargin: 24
text: qsTr("Save and Restart Amnezia")
onClicked: {
clickedFunc: function() {
forceActiveFocus()
PageController.goToPage(PageEnum.PageSetupWizardInstalling);
InstallController.updateContainer(CloakConfigModel.getConfig())

View File

@@ -16,6 +16,8 @@ import "../Components"
PageType {
id: root
defaultActiveFocusItem: listview.currentItem.vpnAddressSubnetTextField.textField
ColumnLayout {
id: backButton
@@ -42,7 +44,7 @@ PageType {
anchors.left: parent.left
anchors.right: parent.right
enabled: ServersModel.isCurrentlyProcessedServerHasWriteAccess()
enabled: ServersModel.isProcessedServerHasWriteAccess()
ListView {
id: listview
@@ -53,12 +55,14 @@ PageType {
clip: true
interactive: false
model: OpenVpnConfigModel
model: OpenVpnConfigModel
delegate: Item {
implicitWidth: listview.width
implicitHeight: col.implicitHeight
property alias vpnAddressSubnetTextField: vpnAddressSubnetTextField
ColumnLayout {
id: col
@@ -78,6 +82,8 @@ PageType {
}
TextFieldWithHeaderType {
id: vpnAddressSubnetTextField
Layout.fillWidth: true
Layout.topMargin: 32
@@ -89,6 +95,8 @@ PageType {
subnetAddress = textFieldText
}
}
KeyNavigation.tab: portTextField.enabled ? portTextField.textField : saveRestartButton
}
ParagraphTextType {
@@ -119,6 +127,9 @@ PageType {
}
TextFieldWithHeaderType {
id: portTextField
Layout.fillWidth: true
Layout.topMargin: 40
@@ -134,6 +145,8 @@ PageType {
port = textFieldText
}
}
KeyNavigation.tab: saveRestartButton
}
SwitcherType {
@@ -162,6 +175,8 @@ PageType {
descriptionText: qsTr("Hash")
headerText: qsTr("Hash")
drawerParent: root
listView: ListViewWithRadioButtonType {
id: hashListView
@@ -183,7 +198,7 @@ PageType {
clickedFunction: function() {
hashDropDown.text = selectedText
hash = hashDropDown.text
hashDropDown.menuVisible = false
hashDropDown.close()
}
Component.onCompleted: {
@@ -208,6 +223,8 @@ PageType {
descriptionText: qsTr("Cipher")
headerText: qsTr("Cipher")
drawerParent: root
listView: ListViewWithRadioButtonType {
id: cipherListView
@@ -229,7 +246,7 @@ PageType {
clickedFunction: function() {
cipherDropDown.text = selectedText
cipher = cipherDropDown.text
cipherDropDown.menuVisible = false
cipherDropDown.close()
}
Component.onCompleted: {
@@ -363,32 +380,33 @@ PageType {
text: qsTr("Remove OpenVPN")
onClicked: {
questionDrawer.headerText = qsTr("Remove OpenVpn from server?")
questionDrawer.descriptionText = qsTr("All users with whom you shared a connection will no longer be able to connect to it.")
questionDrawer.yesButtonText = qsTr("Continue")
questionDrawer.noButtonText = qsTr("Cancel")
clickedFunc: function() {
var headerText = qsTr("Remove OpenVpn from server?")
var descriptionText = qsTr("All users with whom you shared a connection will no longer be able to connect to it.")
var yesButtonText = qsTr("Continue")
var noButtonText = qsTr("Cancel")
questionDrawer.yesButtonFunction = function() {
questionDrawer.visible = false
var yesButtonFunction = function() {
PageController.goToPage(PageEnum.PageDeinstalling)
InstallController.removeCurrentlyProcessedContainer()
}
questionDrawer.noButtonFunction = function() {
questionDrawer.visible = false
var noButtonFunction = function() {
}
questionDrawer.visible = true
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
}
}
BasicButtonType {
id: saveRestartButton
Layout.fillWidth: true
Layout.topMargin: 24
Layout.bottomMargin: 24
text: qsTr("Save and Restart Amnezia")
onClicked: {
clickedFunc: function() {
forceActiveFocus()
PageController.goToPage(PageEnum.PageSetupWizardInstalling);
InstallController.updateContainer(OpenVpnConfigModel.getConfig())
@@ -398,9 +416,5 @@ PageType {
}
}
}
QuestionDrawer {
id: questionDrawer
}
}
}

View File

@@ -90,71 +90,77 @@ PageType {
DividerType {}
DrawerType {
DrawerType2 {
id: configContentDrawer
width: parent.width
height: parent.height * 0.9
expandedHeight: root.height * 0.9
BackButtonType {
id: backButton
parent: root
anchors.fill: parent
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
anchors.topMargin: 16
expandedContent: Item {
implicitHeight: configContentDrawer.expandedHeight
backButtonFunction: function() {
configContentDrawer.visible = false
}
}
BackButtonType {
id: backButton
FlickableType {
anchors.top: backButton.bottom
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: parent.bottom
contentHeight: configContent.implicitHeight + configContent.anchors.topMargin + configContent.anchors.bottomMargin
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
anchors.topMargin: 16
ColumnLayout {
id: configContent
anchors.fill: parent
anchors.rightMargin: 16
anchors.leftMargin: 16
Header2Type {
Layout.fillWidth: true
Layout.topMargin: 16
headerText: qsTr("Connection options %1").arg(protocolName)
backButtonFunction: function() {
configContentDrawer.close()
}
}
TextArea {
id: configText
FlickableType {
anchors.top: backButton.bottom
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: parent.bottom
contentHeight: configContent.implicitHeight + configContent.anchors.topMargin + configContent.anchors.bottomMargin
Layout.fillWidth: true
Layout.topMargin: 16
Layout.bottomMargin: 16
ColumnLayout {
id: configContent
padding: 0
leftPadding: 0
height: 24
anchors.fill: parent
anchors.rightMargin: 16
anchors.leftMargin: 16
color: "#D7D8DB"
selectionColor: "#633303"
selectedTextColor: "#D7D8DB"
Header2Type {
Layout.fillWidth: true
Layout.topMargin: 16
font.pixelSize: 16
font.weight: Font.Medium
font.family: "PT Root UI VF"
headerText: qsTr("Connection options %1").arg(protocolName)
}
text: rawConfig
TextArea {
id: configText
wrapMode: Text.Wrap
Layout.fillWidth: true
Layout.topMargin: 16
Layout.bottomMargin: 16
background: Rectangle {
color: "transparent"
padding: 0
leftPadding: 0
height: 24
color: "#D7D8DB"
selectionColor: "#633303"
selectedTextColor: "#D7D8DB"
font.pixelSize: 16
font.weight: Font.Medium
font.family: "PT Root UI VF"
text: rawConfig
wrapMode: Text.Wrap
background: Rectangle {
color: "transparent"
}
}
}
}
@@ -169,26 +175,25 @@ PageType {
width: parent.width
visible: ServersModel.isCurrentlyProcessedServerHasWriteAccess()
visible: ServersModel.isProcessedServerHasWriteAccess()
text: qsTr("Remove ") + ContainersModel.getCurrentlyProcessedContainerName()
textColor: "#EB5757"
clickedFunction: function() {
questionDrawer.headerText = qsTr("Remove %1 from server?").arg(ContainersModel.getCurrentlyProcessedContainerName())
questionDrawer.descriptionText = qsTr("All users with whom you shared a connection will no longer be able to connect to it.")
questionDrawer.yesButtonText = qsTr("Continue")
questionDrawer.noButtonText = qsTr("Cancel")
var headerText = qsTr("Remove %1 from server?").arg(ContainersModel.getCurrentlyProcessedContainerName())
var descriptionText = qsTr("All users with whom you shared a connection will no longer be able to connect to it.")
var yesButtonText = qsTr("Continue")
var noButtonText = qsTr("Cancel")
questionDrawer.yesButtonFunction = function() {
questionDrawer.visible = false
var yesButtonFunction = function() {
PageController.goToPage(PageEnum.PageDeinstalling)
InstallController.removeCurrentlyProcessedContainer()
}
questionDrawer.noButtonFunction = function() {
questionDrawer.visible = false
var noButtonFunction = function() {
}
questionDrawer.visible = true
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
}
MouseArea {
@@ -200,9 +205,5 @@ PageType {
DividerType {}
}
QuestionDrawer {
id: questionDrawer
}
}
}

View File

@@ -15,6 +15,8 @@ import "../Components"
PageType {
id: root
defaultActiveFocusItem: listview.currentItem.portTextField.textField
ColumnLayout {
id: backButton
@@ -41,7 +43,7 @@ PageType {
anchors.left: parent.left
anchors.right: parent.right
enabled: ServersModel.isCurrentlyProcessedServerHasWriteAccess()
enabled: ServersModel.isProcessedServerHasWriteAccess()
ListView {
id: listview
@@ -58,6 +60,8 @@ PageType {
implicitWidth: listview.width
implicitHeight: col.implicitHeight
property alias portTextField: portTextField
ColumnLayout {
id: col
@@ -77,6 +81,8 @@ PageType {
}
TextFieldWithHeaderType {
id: portTextField
Layout.fillWidth: true
Layout.topMargin: 40
@@ -90,6 +96,8 @@ PageType {
port = textFieldText
}
}
KeyNavigation.tab: saveRestartButton
}
DropDownType {
@@ -100,6 +108,8 @@ PageType {
descriptionText: qsTr("Cipher")
headerText: qsTr("Cipher")
drawerParent: root
listView: ListViewWithRadioButtonType {
id: cipherListView
@@ -116,7 +126,7 @@ PageType {
clickedFunction: function() {
cipherDropDown.text = selectedText
cipher = cipherDropDown.text
cipherDropDown.menuVisible = false
cipherDropDown.close()
}
Component.onCompleted: {
@@ -132,13 +142,15 @@ PageType {
}
BasicButtonType {
id: saveRestartButton
Layout.fillWidth: true
Layout.topMargin: 24
Layout.bottomMargin: 24
text: qsTr("Save and Restart Amnezia")
onClicked: {
clickedFunc: function() {
forceActiveFocus()
PageController.goToPage(PageEnum.PageSetupWizardInstalling);
InstallController.updateContainer(ShadowSocksConfigModel.getConfig())

View File

@@ -63,19 +63,18 @@ PageType {
textColor: "#EB5757"
clickedFunction: function() {
questionDrawer.headerText = qsTr("Remove %1 from server?").arg(ContainersModel.getCurrentlyProcessedContainerName())
questionDrawer.yesButtonText = qsTr("Continue")
questionDrawer.noButtonText = qsTr("Cancel")
var headerText = qsTr("Remove %1 from server?").arg(ContainersModel.getCurrentlyProcessedContainerName())
var yesButtonText = qsTr("Continue")
var noButtonText = qsTr("Cancel")
questionDrawer.yesButtonFunction = function() {
questionDrawer.visible = false
var yesButtonFunction = function() {
PageController.goToPage(PageEnum.PageDeinstalling)
InstallController.removeCurrentlyProcessedContainer()
}
questionDrawer.noButtonFunction = function() {
questionDrawer.visible = false
var noButtonFunction = function() {
}
questionDrawer.visible = true
showQuestionDrawer(headerText, "", yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
}
MouseArea {
@@ -86,10 +85,6 @@ PageType {
}
DividerType {}
QuestionDrawer {
id: questionDrawer
}
}
}
}

View File

@@ -49,7 +49,7 @@ PageType {
anchors.left: parent.left
anchors.right: parent.right
enabled: ServersModel.isCurrentlyProcessedServerHasWriteAccess()
enabled: ServersModel.isProcessedServerHasWriteAccess()
ListView {
id: listview
@@ -88,7 +88,7 @@ PageType {
Layout.topMargin: 32
text: qsTr("Host")
descriptionText: ServersModel.getCurrentlyProcessedServerHostName()
descriptionText: ServersModel.getProcessedServerData("HostName")
descriptionOnTop: true
@@ -170,7 +170,7 @@ PageType {
text: qsTr("Mount folder on device")
onClicked: {
clickedFunc: function() {
PageController.showBusyIndicator(true)
InstallController.mountSftpDrive(port, password, username)
PageController.showBusyIndicator(false)
@@ -229,7 +229,7 @@ PageType {
text: qsTr("Detailed instructions")
onClicked: {
clickedFunc: function() {
// Qt.openUrlExternally("https://github.com/amnezia-vpn/desktop-client/releases/latest")
}
}
@@ -247,29 +247,24 @@ PageType {
text: qsTr("Remove SFTP and all data stored there")
onClicked: {
questionDrawer.headerText = qsTr("Remove SFTP and all data stored there?")
questionDrawer.yesButtonText = qsTr("Continue")
questionDrawer.noButtonText = qsTr("Cancel")
clickedFunc: function() {
var headerText = qsTr("Remove SFTP and all data stored there?")
var yesButtonText = qsTr("Continue")
var noButtonText = qsTr("Cancel")
questionDrawer.yesButtonFunction = function() {
questionDrawer.visible = false
var yesButtonFunction = function() {
PageController.goToPage(PageEnum.PageDeinstalling)
InstallController.removeCurrentlyProcessedContainer()
}
questionDrawer.noButtonFunction = function() {
questionDrawer.visible = false
var noButtonFunction = function() {
}
questionDrawer.visible = true
showQuestionDrawer(headerText, "", yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
}
}
}
}
}
}
QuestionDrawer {
id: questionDrawer
}
}
}

View File

@@ -125,26 +125,21 @@ PageType {
text: qsTr("Remove website")
onClicked: {
questionDrawer.headerText = qsTr("The site with all data will be removed from the tor network.")
questionDrawer.yesButtonText = qsTr("Continue")
questionDrawer.noButtonText = qsTr("Cancel")
clickedFunc: function() {
var headerText = qsTr("The site with all data will be removed from the tor network.")
var yesButtonText = qsTr("Continue")
var noButtonText = qsTr("Cancel")
questionDrawer.yesButtonFunction = function() {
questionDrawer.visible = false
var yesButtonFunction = function() {
PageController.goToPage(PageEnum.PageDeinstalling)
InstallController.removeCurrentlyProcessedContainer()
}
questionDrawer.noButtonFunction = function() {
questionDrawer.visible = false
var noButtonFunction = function() {
}
questionDrawer.visible = true
showQuestionDrawer(headerText, "", yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
}
}
}
QuestionDrawer {
id: questionDrawer
}
}
}

View File

@@ -81,7 +81,7 @@ PageType {
text: qsTr("Card on Patreon")
onClicked: function() {
clickedFunc: function() {
Qt.openUrlExternally(qsTr("https://www.patreon.com/amneziavpn"))
}
}
@@ -101,7 +101,9 @@ PageType {
text: qsTr("Show other methods on Github")
onClicked: Qt.openUrlExternally(qsTr("https://github.com/amnezia-vpn/amnezia-client#donate"))
clickedFunc: function() {
Qt.openUrlExternally(qsTr("https://github.com/amnezia-vpn/amnezia-client#donate"))
}
}
ParagraphTextType {
@@ -173,7 +175,7 @@ PageType {
horizontalAlignment: Text.AlignHCenter
text: SettingsController.getAppVersion()
text: qsTr("Software version: %1").arg(SettingsController.getAppVersion())
color: "#878B91"
}
@@ -191,7 +193,7 @@ PageType {
text: qsTr("Check for updates")
onClicked: {
clickedFunc: function() {
Qt.openUrlExternally("https://github.com/amnezia-vpn/desktop-client/releases/latest")
}
}

View File

@@ -84,6 +84,27 @@ PageType {
visible: !GC.isMobile()
}
SwitcherType {
visible: !GC.isMobile()
Layout.fillWidth: true
Layout.margins: 16
text: qsTr("Auto connect")
descriptionText: qsTr("Connect to VPN on app start")
checked: SettingsController.isAutoConnectEnabled()
onCheckedChanged: {
if (checked !== SettingsController.isAutoConnectEnabled()) {
SettingsController.toggleAutoConnect(checked)
}
}
}
DividerType {
visible: !GC.isMobile()
}
SwitcherType {
visible: !GC.isMobile()
@@ -117,10 +138,6 @@ PageType {
}
}
SelectLanguageDrawer {
id: selectLanguageDrawer
}
DividerType {}
@@ -143,30 +160,33 @@ PageType {
text: qsTr("Reset settings and remove all data from the application")
rightImageSource: "qrc:/images/controls/chevron-right.svg"
textColor: "#EB5757"
clickedFunction: function() {
questionDrawer.headerText = qsTr("Reset settings and remove all data from the application?")
questionDrawer.descriptionText = qsTr("All settings will be reset to default. All installed AmneziaVPN services will still remain on the server.")
questionDrawer.yesButtonText = qsTr("Continue")
questionDrawer.noButtonText = qsTr("Cancel")
var headerText = qsTr("Reset settings and remove all data from the application?")
var descriptionText = qsTr("All settings will be reset to default. All installed AmneziaVPN services will still remain on the server.")
var yesButtonText = qsTr("Continue")
var noButtonText = qsTr("Cancel")
questionDrawer.yesButtonFunction = function() {
questionDrawer.visible = false
var yesButtonFunction = function() {
SettingsController.clearSettings()
PageController.replaceStartPage()
}
questionDrawer.noButtonFunction = function() {
questionDrawer.visible = false
var noButtonFunction = function() {
}
questionDrawer.visible = true
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
}
}
DividerType {}
QuestionDrawer {
id: questionDrawer
}
}
}
SelectLanguageDrawer {
id: selectLanguageDrawer
width: root.width
height: root.height
}
}

View File

@@ -88,7 +88,7 @@ PageType {
text: qsTr("Make a backup")
onClicked: {
clickedFunc: function() {
var fileName = ""
if (GC.isMobile()) {
fileName = "AmneziaVPN.backup"
@@ -121,7 +121,7 @@ PageType {
text: qsTr("Restore from backup")
onClicked: {
clickedFunc: function() {
var filePath = SystemController.getFileName(qsTr("Open backup file"),
qsTr("Backup files (*.backup)"))
if (filePath !== "") {
@@ -133,24 +133,19 @@ PageType {
}
function restoreBackup(filePath) {
questionDrawer.headerText = qsTr("Import settings from a backup file?")
questionDrawer.descriptionText = qsTr("All current settings will be reset");
questionDrawer.yesButtonText = qsTr("Continue")
questionDrawer.noButtonText = qsTr("Cancel")
var headerText = qsTr("Import settings from a backup file?")
var descriptionText = qsTr("All current settings will be reset");
var yesButtonText = qsTr("Continue")
var noButtonText = qsTr("Cancel")
questionDrawer.yesButtonFunction = function() {
questionDrawer.visible = false
var yesButtonFunction = function() {
PageController.showBusyIndicator(true)
SettingsController.restoreAppConfig(filePath)
PageController.showBusyIndicator(false)
}
questionDrawer.noButtonFunction = function() {
questionDrawer.visible = false
var noButtonFunction = function() {
}
questionDrawer.visible = true
}
QuestionDrawer {
id: questionDrawer
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
}
}

View File

@@ -41,27 +41,6 @@ PageType {
headerText: qsTr("Connection")
}
SwitcherType {
visible: !GC.isMobile()
Layout.fillWidth: true
Layout.margins: 16
text: qsTr("Auto connect")
descriptionText: qsTr("Connect to VPN on app start")
checked: SettingsController.isAutoConnectEnabled()
onCheckedChanged: {
if (checked !== SettingsController.isAutoConnectEnabled()) {
SettingsController.toggleAutoConnect(checked)
}
}
}
DividerType {
visible: !GC.isMobile()
}
SwitcherType {
Layout.fillWidth: true
Layout.margins: 16

View File

@@ -13,6 +13,8 @@ import "../Components"
PageType {
id: root
defaultActiveFocusItem: primaryDns.textField
BackButtonType {
id: backButton
@@ -28,10 +30,12 @@ PageType {
anchors.bottom: parent.bottom
contentHeight: content.height
enabled: !ServersModel.isDefaultServerFromApi()
property var isServerFromApi: ServersModel.getDefaultServerData("isServerFromApi")
enabled: !isServerFromApi
Component.onCompleted: {
if (ServersModel.isDefaultServerFromApi()) {
if (isServerFromApi) {
PageController.showNotificationMessage(qsTr("Default server does not support custom dns"))
}
}
@@ -68,6 +72,8 @@ PageType {
textField.validator: RegularExpressionValidator {
regularExpression: InstallController.ipAddressRegExp()
}
KeyNavigation.tab: secondaryDns.textField
}
TextFieldWithHeaderType {
@@ -80,6 +86,8 @@ PageType {
textField.validator: RegularExpressionValidator {
regularExpression: InstallController.ipAddressRegExp()
}
KeyNavigation.tab: saveButton
}
BasicButtonType {
@@ -94,32 +102,33 @@ PageType {
text: qsTr("Restore default")
onClicked: function() {
questionDrawer.headerText = qsTr("Restore default DNS settings?")
questionDrawer.yesButtonText = qsTr("Continue")
questionDrawer.noButtonText = qsTr("Cancel")
clickedFunc: function() {
var headerText = qsTr("Restore default DNS settings?")
var yesButtonText = qsTr("Continue")
var noButtonText = qsTr("Cancel")
questionDrawer.yesButtonFunction = function() {
questionDrawer.visible = false
var yesButtonFunction = function() {
SettingsController.primaryDns = "1.1.1.1"
primaryDns.textFieldText = SettingsController.primaryDns
SettingsController.secondaryDns = "1.0.0.1"
secondaryDns.textFieldText = SettingsController.secondaryDns
PageController.showNotificationMessage(qsTr("Settings have been reset"))
}
questionDrawer.noButtonFunction = function() {
questionDrawer.visible = false
var noButtonFunction = function() {
}
questionDrawer.visible = true
showQuestionDrawer(headerText, "", yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
}
}
BasicButtonType {
id: saveButton
Layout.fillWidth: true
text: qsTr("Save")
onClicked: function() {
clickedFunc: function() {
if (primaryDns.textFieldText !== SettingsController.primaryDns) {
SettingsController.primaryDns = primaryDns.textFieldText
}
@@ -130,8 +139,6 @@ PageType {
}
}
}
QuestionDrawer {
id: questionDrawer
}
}
}

View File

@@ -143,21 +143,20 @@ PageType {
image: "qrc:/images/controls/delete.svg"
onClicked: function() {
questionDrawer.headerText = qsTr("Clear logs?")
questionDrawer.yesButtonText = qsTr("Continue")
questionDrawer.noButtonText = qsTr("Cancel")
var headerText = qsTr("Clear logs?")
var yesButtonText = qsTr("Continue")
var noButtonText = qsTr("Cancel")
questionDrawer.yesButtonFunction = function() {
questionDrawer.visible = false
var yesButtonFunction = function() {
PageController.showBusyIndicator(true)
SettingsController.clearLogs()
PageController.showBusyIndicator(false)
PageController.showNotificationMessage(qsTr("Logs have been cleaned up"))
}
questionDrawer.noButtonFunction = function() {
questionDrawer.visible = false
var noButtonFunction = function() {
}
questionDrawer.visible = true
showQuestionDrawer(headerText, "", yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
}
}
@@ -170,10 +169,6 @@ PageType {
}
}
}
QuestionDrawer {
id: questionDrawer
}
}
}
}

View File

@@ -28,7 +28,7 @@ PageType {
PageController.showErrorMessage(message)
}
function onRemoveCurrentlyProcessedServerFinished(finishedMessage) {
function onRemoveProcessedServerFinished(finishedMessage) {
if (!ServersModel.getServersCount()) {
PageController.replaceStartPage()
} else {
@@ -38,7 +38,7 @@ PageType {
PageController.showNotificationMessage(finishedMessage)
}
function onRebootCurrentlyProcessedServerFinished(finishedMessage) {
function onRebootProcessedServerFinished(finishedMessage) {
PageController.showNotificationMessage(finishedMessage)
}
@@ -64,8 +64,8 @@ PageType {
Connections {
target: ServersModel
function onCurrentlyProcessedServerIndexChanged() {
content.isServerWithWriteAccess = ServersModel.isCurrentlyProcessedServerHasWriteAccess()
function onProcessedServerIndexChanged() {
content.isServerWithWriteAccess = ServersModel.isProcessedServerHasWriteAccess()
}
}
@@ -82,7 +82,7 @@ PageType {
anchors.left: parent.left
anchors.right: parent.right
property bool isServerWithWriteAccess: ServersModel.isCurrentlyProcessedServerHasWriteAccess()
property bool isServerWithWriteAccess: ServersModel.isProcessedServerHasWriteAccess()
LabelWithButtonType {
visible: content.isServerWithWriteAccess
@@ -92,21 +92,20 @@ PageType {
descriptionText: qsTr("May be needed when changing other settings")
clickedFunction: function() {
questionDrawer.headerText = qsTr("Clear cached profiles?")
questionDrawer.descriptionText = qsTr("")
questionDrawer.yesButtonText = qsTr("Continue")
questionDrawer.noButtonText = qsTr("Cancel")
var headerText = qsTr("Clear cached profiles?")
var descriptionText = qsTr("")
var yesButtonText = qsTr("Continue")
var noButtonText = qsTr("Cancel")
questionDrawer.yesButtonFunction = function() {
questionDrawer.visible = false
var yesButtonFunction = function() {
PageController.showBusyIndicator(true)
SettingsController.clearCachedProfiles()
PageController.showBusyIndicator(false)
}
questionDrawer.noButtonFunction = function() {
questionDrawer.visible = false
var noButtonFunction = function() {
}
questionDrawer.visible = true
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
}
}
@@ -140,24 +139,23 @@ PageType {
textColor: "#EB5757"
clickedFunction: function() {
questionDrawer.headerText = qsTr("Do you want to reboot the server?")
questionDrawer.descriptionText = qsTr("The reboot process may take approximately 30 seconds. Are you sure you wish to proceed?")
questionDrawer.yesButtonText = qsTr("Continue")
questionDrawer.noButtonText = qsTr("Cancel")
var headerText = qsTr("Do you want to reboot the server?")
var descriptionText = qsTr("The reboot process may take approximately 30 seconds. Are you sure you wish to proceed?")
var yesButtonText = qsTr("Continue")
var noButtonText = qsTr("Cancel")
questionDrawer.yesButtonFunction = function() {
questionDrawer.visible = false
var yesButtonFunction = function() {
PageController.showBusyIndicator(true)
if (ServersModel.isDefaultServerCurrentlyProcessed() && ConnectionController.isConnected) {
ConnectionController.closeConnection()
}
InstallController.rebootCurrentlyProcessedServer()
InstallController.rebootProcessedServer()
PageController.showBusyIndicator(false)
}
questionDrawer.noButtonFunction = function() {
questionDrawer.visible = false
var noButtonFunction = function() {
}
questionDrawer.visible = true
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
}
}
@@ -172,24 +170,23 @@ PageType {
textColor: "#EB5757"
clickedFunction: function() {
questionDrawer.headerText = qsTr("Do you want to remove the server from application?")
questionDrawer.descriptionText = qsTr("All installed AmneziaVPN services will still remain on the server.")
questionDrawer.yesButtonText = qsTr("Continue")
questionDrawer.noButtonText = qsTr("Cancel")
var headerText = qsTr("Do you want to remove the server from application?")
var descriptionText = qsTr("All installed AmneziaVPN services will still remain on the server.")
var yesButtonText = qsTr("Continue")
var noButtonText = qsTr("Cancel")
questionDrawer.yesButtonFunction = function() {
questionDrawer.visible = false
var yesButtonFunction = function() {
PageController.showBusyIndicator(true)
if (ServersModel.isDefaultServerCurrentlyProcessed() && ConnectionController.isConnected) {
ConnectionController.closeConnection()
}
InstallController.removeCurrentlyProcessedServer()
InstallController.removeProcessedServer()
PageController.showBusyIndicator(false)
}
questionDrawer.noButtonFunction = function() {
questionDrawer.visible = false
var noButtonFunction = function() {
}
questionDrawer.visible = true
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
}
}
@@ -203,23 +200,22 @@ PageType {
textColor: "#EB5757"
clickedFunction: function() {
questionDrawer.headerText = qsTr("Do you want to clear server from Amnezia software?")
questionDrawer.descriptionText = qsTr("All containers will be deleted on the server. This means that configuration files, keys and certificates will be deleted.")
questionDrawer.yesButtonText = qsTr("Continue")
questionDrawer.noButtonText = qsTr("Cancel")
var headerText = qsTr("Do you want to clear server from Amnezia software?")
var descriptionText = qsTr("All containers will be deleted on the server. This means that configuration files, keys and certificates will be deleted.")
var yesButtonText = qsTr("Continue")
var noButtonText = qsTr("Cancel")
questionDrawer.yesButtonFunction = function() {
questionDrawer.visible = false
var yesButtonFunction = function() {
PageController.goToPage(PageEnum.PageDeinstalling)
if (ServersModel.isDefaultServerCurrentlyProcessed() && ConnectionController.isConnected) {
ConnectionController.closeConnection()
}
InstallController.removeAllContainers()
}
questionDrawer.noButtonFunction = function() {
questionDrawer.visible = false
var noButtonFunction = function() {
}
questionDrawer.visible = true
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
}
}
@@ -228,69 +224,32 @@ PageType {
}
LabelWithButtonType {
visible: content.isServerWithWriteAccess
Layout.fillWidth: true
text: qsTr("Clear server from Amnezia software")
textColor: "#EB5757"
clickedFunction: function() {
questionDrawer.headerText = qsTr("Do you want to clear server from Amnezia software?")
questionDrawer.descriptionText = qsTr("All containers will be deleted on the server. This means that configuration files, keys and certificates will be deleted.")
questionDrawer.yesButtonText = qsTr("Continue")
questionDrawer.noButtonText = qsTr("Cancel")
questionDrawer.yesButtonFunction = function() {
questionDrawer.visible = false
PageController.goToPage(PageEnum.PageDeinstalling)
if (ServersModel.isDefaultServerCurrentlyProcessed() && ConnectionController.isConnected) {
ConnectionController.closeConnection()
}
InstallController.removeAllContainers()
}
questionDrawer.noButtonFunction = function() {
questionDrawer.visible = false
}
questionDrawer.visible = true
}
}
DividerType {
visible: content.isServerWithWriteAccess
}
LabelWithButtonType {
visible: ServersModel.isCurrentlyProcessedServerFromApi()
visible: ServersModel.getProcessedServerData("isServerFromApi")
Layout.fillWidth: true
text: qsTr("Reset API config")
textColor: "#EB5757"
clickedFunction: function() {
questionDrawer.headerText = qsTr("Do you want to reset API config?")
questionDrawer.descriptionText = ""
questionDrawer.yesButtonText = qsTr("Continue")
questionDrawer.noButtonText = qsTr("Cancel")
var headerText = qsTr("Do you want to reset API config?")
var descriptionText = ""
var yesButtonText = qsTr("Continue")
var noButtonText = qsTr("Cancel")
questionDrawer.yesButtonFunction = function() {
questionDrawer.visible = false
var yesButtonFunction = function() {
PageController.showBusyIndicator(true)
ApiController.clearApiConfig()
PageController.showBusyIndicator(false)
}
questionDrawer.noButtonFunction = function() {
questionDrawer.visible = false
var noButtonFunction = function() {
}
questionDrawer.visible = true
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
}
}
DividerType {
visible: ServersModel.isCurrentlyProcessedServerFromApi()
}
QuestionDrawer {
id: questionDrawer
visible: ServersModel.getProcessedServerData("isServerFromApi")
}
}
}

View File

@@ -63,7 +63,7 @@ PageType {
headerText: name
descriptionText: {
if (ServersModel.isCurrentlyProcessedServerHasWriteAccess()) {
if (ServersModel.isProcessedServerHasWriteAccess()) {
return credentialsLogin + " · " + hostName
} else {
return hostName
@@ -71,30 +71,33 @@ PageType {
}
actionButtonFunction: function() {
serverNameEditDrawer.visible = true
serverNameEditDrawer.open()
}
}
DrawerType {
DrawerType2 {
id: serverNameEditDrawer
width: root.width
height: root.height * 0.35
parent: root
onVisibleChanged: {
if (serverNameEditDrawer.visible) {
serverName.textField.forceActiveFocus()
}
}
anchors.fill: parent
expandedHeight: root.height * 0.35
ColumnLayout {
expandedContent: ColumnLayout {
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
anchors.topMargin: 16
anchors.topMargin: 32
anchors.leftMargin: 16
anchors.rightMargin: 16
Connections {
target: serverNameEditDrawer
function onOpened() {
serverName.textField.forceActiveFocus()
}
}
TextFieldWithHeaderType {
id: serverName
@@ -103,14 +106,18 @@ PageType {
textFieldText: name
textField.maximumLength: 30
checkEmptyText: true
KeyNavigation.tab: saveButton
}
BasicButtonType {
id: saveButton
Layout.fillWidth: true
text: qsTr("Save")
onClicked: {
clickedFunc: function() {
if (serverName.textFieldText === "") {
return
}
@@ -118,7 +125,13 @@ PageType {
if (serverName.textFieldText !== name) {
name = serverName.textFieldText
}
serverNameEditDrawer.visible = false
serverNameEditDrawer.close()
}
}
Component.onCompleted: {
if (header.itemAt(0)) {
defaultActiveFocusItem = serverName.textField
}
}
}

View File

@@ -107,26 +107,25 @@ PageType {
width: parent.width
visible: ServersModel.isCurrentlyProcessedServerHasWriteAccess()
visible: ServersModel.isProcessedServerHasWriteAccess()
text: qsTr("Remove ") + ContainersModel.getCurrentlyProcessedContainerName()
textColor: "#EB5757"
clickedFunction: function() {
questionDrawer.headerText = qsTr("Remove %1 from server?").arg(ContainersModel.getCurrentlyProcessedContainerName())
questionDrawer.descriptionText = qsTr("All users with whom you shared a connection will no longer be able to connect to it.")
questionDrawer.yesButtonText = qsTr("Continue")
questionDrawer.noButtonText = qsTr("Cancel")
var headerText = qsTr("Remove %1 from server?").arg(ContainersModel.getCurrentlyProcessedContainerName())
var descriptionText = qsTr("All users with whom you shared a connection will no longer be able to connect to it.")
var yesButtonText = qsTr("Continue")
var noButtonText = qsTr("Cancel")
questionDrawer.yesButtonFunction = function() {
questionDrawer.visible = false
var yesButtonFunction = function() {
PageController.goToPage(PageEnum.PageDeinstalling)
InstallController.removeCurrentlyProcessedContainer()
}
questionDrawer.noButtonFunction = function() {
questionDrawer.visible = false
var noButtonFunction = function() {
}
questionDrawer.visible = true
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
}
MouseArea {
@@ -138,9 +137,9 @@ PageType {
DividerType {}
}
}
QuestionDrawer {
id: questionDrawer
}
QuestionDrawer {
id: questionDrawer
}
}

View File

@@ -38,13 +38,13 @@ PageType {
Connections {
target: ServersModel
function onCurrentlyProcessedServerIndexChanged() {
function onProcessedServerIndexChanged() {
settingsContainersListView.updateContainersModelFilters()
}
}
function updateContainersModelFilters() {
if (ServersModel.isCurrentlyProcessedServerHasWriteAccess()) {
if (ServersModel.isProcessedServerHasWriteAccess()) {
proxyContainersModel.filters = ContainersModelFilters.getWriteAccessProtocolsListFilters()
} else {
proxyContainersModel.filters = ContainersModelFilters.getReadAccessProtocolsListFilters()

View File

@@ -38,13 +38,13 @@ PageType {
Connections {
target: ServersModel
function onCurrentlyProcessedServerIndexChanged() {
function onProcessedServerIndexChanged() {
settingsContainersListView.updateContainersModelFilters()
}
}
function updateContainersModelFilters() {
if (ServersModel.isCurrentlyProcessedServerHasWriteAccess()) {
if (ServersModel.isProcessedServerHasWriteAccess()) {
proxyContainersModel.filters = ContainersModelFilters.getWriteAccessServicesListFilters()
} else {
proxyContainersModel.filters = ContainersModelFilters.getReadAccessServicesListFilters()
@@ -55,6 +55,9 @@ PageType {
model: SortFilterProxyModel {
id: proxyContainersModel
sourceModel: ContainersModel
sorters: [
RoleSorter { roleName: "isInstalled"; sortOrder: Qt.DescendingOrder }
]
}
Component.onCompleted: updateContainersModelFilters()

View File

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

View File

@@ -20,13 +20,23 @@ import "../Components"
PageType {
id: root
property var isServerFromApi: ServersModel.getDefaultServerData("isServerFromApi")
defaultActiveFocusItem: website_ip_field.textField
property bool pageEnabled: {
return !ConnectionController.isConnected && !ServersModel.isDefaultServerFromApi()
return !ConnectionController.isConnected && !isServerFromApi
}
Component.onCompleted: {
if (ServersModel.isDefaultServerFromApi()) {
if (ConnectionController.isConnected) {
PageController.showNotificationMessage(qsTr("Cannot change split tunneling settings during active connection"))
root.pageEnabled = false
} else if (ServersModel.isDefaultServerDefaultContainerHasSplitTunneling && isServerFromApi) {
PageController.showNotificationMessage(qsTr("Default server does not support split tunneling function"))
root.pageEnabled = false
} else {
root.pageEnabled = true
}
}
@@ -104,7 +114,7 @@ PageType {
Layout.fillWidth: true
Layout.rightMargin: 16
checked: SitesModel.isSplitTunnelingEnabled()
checked: SitesModel.isTunnelingEnabled
onToggled: {
SitesModel.toggleSplitTunneling(checked)
selector.text = root.routeModesModel[getRouteModesModelIndex()].name
@@ -121,6 +131,7 @@ PageType {
Layout.rightMargin: 16
drawerHeight: 0.4375
drawerParent: root
enabled: root.pageEnabled
@@ -135,7 +146,7 @@ PageType {
clickedFunction: function() {
selector.text = selectedText
selector.menuVisible = false
selector.close()
if (SitesModel.routeMode !== root.routeModesModel[currentIndex].type) {
SitesModel.routeMode = root.routeModesModel[currentIndex].type
}
@@ -202,26 +213,21 @@ PageType {
rightImageColor: "#D7D8DB"
clickedFunction: function() {
questionDrawer.headerText = qsTr("Remove ") + url + "?"
questionDrawer.yesButtonText = qsTr("Continue")
questionDrawer.noButtonText = qsTr("Cancel")
var headerText = qsTr("Remove ") + url + "?"
var yesButtonText = qsTr("Continue")
var noButtonText = qsTr("Cancel")
questionDrawer.yesButtonFunction = function() {
questionDrawer.visible = false
var yesButtonFunction = function() {
SitesController.removeSite(index)
}
questionDrawer.noButtonFunction = function() {
questionDrawer.visible = false
var noButtonFunction = function() {
}
questionDrawer.visible = true
showQuestionDrawer(headerText, "", yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
}
}
DividerType {}
QuestionDrawer {
id: questionDrawer
}
}
}
}
@@ -249,6 +255,8 @@ PageType {
anchors.bottomMargin: 24
TextFieldWithHeaderType {
id: website_ip_field
Layout.fillWidth: true
textFieldPlaceholderText: qsTr("website or IP")
@@ -275,151 +283,155 @@ PageType {
}
}
DrawerType {
DrawerType2 {
id: moreActionsDrawer
width: parent.width
height: parent.height * 0.4375
anchors.fill: parent
expandedHeight: parent.height * 0.4375
FlickableType {
anchors.fill: parent
contentHeight: moreActionsDrawerContent.height
ColumnLayout {
id: moreActionsDrawerContent
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
Header2Type {
Layout.fillWidth: true
Layout.margins: 16
headerText: qsTr("Import / Export Sites")
}
LabelWithButtonType {
Layout.fillWidth: true
text: qsTr("Import")
rightImageSource: "qrc:/images/controls/chevron-right.svg"
clickedFunction: function() {
importSitesDrawer.open()
}
}
DividerType {}
LabelWithButtonType {
Layout.fillWidth: true
text: qsTr("Save site list")
clickedFunction: function() {
var fileName = ""
if (GC.isMobile()) {
fileName = "amnezia_sites.json"
} else {
fileName = SystemController.getFileName(qsTr("Save sites"),
qsTr("Sites files (*.json)"),
StandardPaths.standardLocations(StandardPaths.DocumentsLocation) + "/amnezia_sites",
true,
".json")
}
if (fileName !== "") {
PageController.showBusyIndicator(true)
SitesController.exportSites(fileName)
moreActionsDrawer.close()
PageController.showBusyIndicator(false)
}
}
}
DividerType {}
}
}
}
DrawerType {
id: importSitesDrawer
width: parent.width
height: parent.height * 0.4375
BackButtonType {
id: importSitesDrawerBackButton
expandedContent: ColumnLayout {
id: moreActionsDrawerContent
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
anchors.topMargin: 16
backButtonFunction: function() {
importSitesDrawer.close()
Header2Type {
Layout.fillWidth: true
Layout.margins: 16
headerText: qsTr("Import / Export Sites")
}
LabelWithButtonType {
Layout.fillWidth: true
text: qsTr("Import")
rightImageSource: "qrc:/images/controls/chevron-right.svg"
clickedFunction: function() {
importSitesDrawer.open()
}
}
DividerType {}
LabelWithButtonType {
Layout.fillWidth: true
text: qsTr("Save site list")
clickedFunction: function() {
var fileName = ""
if (GC.isMobile()) {
fileName = "amnezia_sites.json"
} else {
fileName = SystemController.getFileName(qsTr("Save sites"),
qsTr("Sites files (*.json)"),
StandardPaths.standardLocations(StandardPaths.DocumentsLocation) + "/amnezia_sites",
true,
".json")
}
if (fileName !== "") {
PageController.showBusyIndicator(true)
SitesController.exportSites(fileName)
moreActionsDrawer.close()
PageController.showBusyIndicator(false)
}
}
}
DividerType {}
}
}
FlickableType {
anchors.top: importSitesDrawerBackButton.bottom
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: parent.bottom
DrawerType2 {
id: importSitesDrawer
contentHeight: importSitesDrawerContent.height
anchors.fill: parent
expandedHeight: parent.height * 0.4375
ColumnLayout {
id: importSitesDrawerContent
expandedContent: Item {
implicitHeight: importSitesDrawer.expandedHeight
BackButtonType {
id: importSitesDrawerBackButton
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
anchors.topMargin: 16
Header2Type {
Layout.fillWidth: true
Layout.margins: 16
headerText: qsTr("Import a list of sites")
}
LabelWithButtonType {
Layout.fillWidth: true
text: qsTr("Replace site list")
clickedFunction: function() {
var fileName = SystemController.getFileName(qsTr("Open sites file"),
qsTr("Sites files (*.json)"))
if (fileName !== "") {
importSitesDrawerContent.importSites(fileName, true)
}
}
}
DividerType {}
LabelWithButtonType {
Layout.fillWidth: true
text: qsTr("Add imported sites to existing ones")
clickedFunction: function() {
var fileName = SystemController.getFileName(qsTr("Open sites file"),
qsTr("Sites files (*.json)"))
if (fileName !== "") {
importSitesDrawerContent.importSites(fileName, false)
}
}
}
function importSites(fileName, replaceExistingSites) {
PageController.showBusyIndicator(true)
SitesController.importSites(fileName, replaceExistingSites)
PageController.showBusyIndicator(false)
backButtonFunction: function() {
importSitesDrawer.close()
moreActionsDrawer.close()
}
}
DividerType {}
FlickableType {
anchors.top: importSitesDrawerBackButton.bottom
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: parent.bottom
contentHeight: importSitesDrawerContent.height
ColumnLayout {
id: importSitesDrawerContent
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
Header2Type {
Layout.fillWidth: true
Layout.margins: 16
headerText: qsTr("Import a list of sites")
}
LabelWithButtonType {
Layout.fillWidth: true
text: qsTr("Replace site list")
clickedFunction: function() {
var fileName = SystemController.getFileName(qsTr("Open sites file"),
qsTr("Sites files (*.json)"))
if (fileName !== "") {
importSitesDrawerContent.importSites(fileName, true)
}
}
}
DividerType {}
LabelWithButtonType {
Layout.fillWidth: true
text: qsTr("Add imported sites to existing ones")
clickedFunction: function() {
var fileName = SystemController.getFileName(qsTr("Open sites file"),
qsTr("Sites files (*.json)"))
if (fileName !== "") {
importSitesDrawerContent.importSites(fileName, false)
}
}
}
function importSites(fileName, replaceExistingSites) {
PageController.showBusyIndicator(true)
SitesController.importSites(fileName, replaceExistingSites)
PageController.showBusyIndicator(false)
importSitesDrawer.close()
moreActionsDrawer.close()
}
DividerType {}
}
}
}
}
QuestionDrawer {
id: questionDrawer
}
}

View File

@@ -12,6 +12,8 @@ import "../Controls2/TextTypes"
PageType {
id: root
defaultActiveFocusItem: hostname.textField
BackButtonType {
id: backButton
@@ -57,6 +59,8 @@ PageType {
onFocusChanged: {
textField.text = textField.text.replace(/^\s+|\s+$/g, '');
}
KeyNavigation.tab: username.textField
}
TextFieldWithHeaderType {
@@ -65,6 +69,8 @@ PageType {
Layout.fillWidth: true
headerText: qsTr("Login to connect via SSH")
textFieldPlaceholderText: "root"
KeyNavigation.tab: secretData.textField
}
TextFieldWithHeaderType {
@@ -85,15 +91,19 @@ PageType {
onFocusChanged: {
textField.text = textField.text.replace(/^\s+|\s+$/g, '');
}
KeyNavigation.tab: continueButton
}
BasicButtonType {
id: continueButton
Layout.fillWidth: true
Layout.topMargin: 24
text: qsTr("Continue")
onClicked: function() {
clickedFunc: function() {
forceActiveFocus()
if (!isCredentialsFilled()) {
return

View File

@@ -158,7 +158,7 @@ PageType {
text: qsTr("Continue")
onClicked: function() {
clickedFunc: function() {
if (root.isEasySetup) {
ContainersModel.setCurrentlyProcessedContainerIndex(containers.dockerContainer)
PageController.goToPage(PageEnum.PageSetupWizardInstalling)
@@ -192,13 +192,12 @@ PageType {
return ContainersModel.isAnyContainerInstalled()
}
return true
}
text: qsTr("Set up later")
onClicked: function() {
clickedFunc: function() {
PageController.goToPage(PageEnum.PageSetupWizardInstalling)
InstallController.addEmptyServer()
}

View File

@@ -14,8 +14,8 @@ import "../Config"
PageType {
id: root
Component.onCompleted: PageController.enableTabBar(false)
Component.onDestruction: PageController.enableTabBar(true)
Component.onCompleted: PageController.disableControls(true)
Component.onDestruction: PageController.disableControls(false)
property bool isTimerRunning: true
property string progressBarText: qsTr("Usually it takes no more than 5 minutes")
@@ -26,7 +26,7 @@ PageType {
function onInstallContainerFinished(finishedMessage, isServiceInstall) {
if (!ConnectionController.isConnected && !isServiceInstall) {
ServersModel.setDefaultContainer(ContainersModel.getCurrentlyProcessedContainerIndex())
ServersModel.setDefaultContainer(ServersModel.processedIndex, ContainersModel.getCurrentlyProcessedContainerIndex())
}
PageController.closePage() // close installing page
@@ -42,7 +42,7 @@ PageType {
function onInstallServerFinished(finishedMessage) {
if (!ConnectionController.isConnected) {
ServersModel.setDefaultServerIndex(ServersModel.getServersCount() - 1);
ServersModel.currentlyProcessedIndex = ServersModel.defaultIndex
ServersModel.processedIndex = ServersModel.defaultIndex
}
PageController.goToStartPage()
@@ -55,7 +55,7 @@ PageType {
function onServerAlreadyExists(serverIndex) {
PageController.goToStartPage()
ServersModel.currentlyProcessedIndex = serverIndex
ServersModel.processedIndex = serverIndex
PageController.goToPage(PageEnum.PageSettingsServerInfo, false)
PageController.showErrorMessage(qsTr("The server has already been added to the application"))
@@ -165,7 +165,7 @@ PageType {
text: qsTr("Cancel installation")
onClicked: {
clickedFunc: function() {
InstallController.cancelInstallation()
PageController.showBusyIndicator(true)
}

View File

@@ -52,6 +52,8 @@ PageType {
implicitWidth: processedContainerListView.width
implicitHeight: (delegateContent.implicitHeight > root.height) ? delegateContent.implicitHeight : root.height
property alias port:port
ColumnLayout {
id: delegateContent
@@ -92,81 +94,85 @@ PageType {
text: qsTr("More detailed")
onClicked: {
clickedFunc: function() {
showDetailsDrawer.open()
}
}
DrawerType {
DrawerType2 {
id: showDetailsDrawer
parent: root
width: parent.width
height: parent.height * 0.9
anchors.fill: parent
expandedHeight: parent.height * 0.9
expandedContent: Item {
implicitHeight: showDetailsDrawer.expandedHeight
BackButtonType {
id: showDetailsBackButton
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
anchors.topMargin: 16
backButtonFunction: function() {
showDetailsDrawer.close()
}
}
FlickableType {
anchors.top: showDetailsBackButton.bottom
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: parent.bottom
contentHeight: {
var emptySpaceHeight = parent.height - showDetailsBackButton.implicitHeight - showDetailsBackButton.anchors.topMargin
return (showDetailsDrawerContent.height > emptySpaceHeight) ?
showDetailsDrawerContent.height : emptySpaceHeight
}
ColumnLayout {
id: showDetailsDrawerContent
BackButtonType {
id: showDetailsBackButton
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
anchors.rightMargin: 16
anchors.leftMargin: 16
anchors.topMargin: 16
Header2Type {
id: showDetailsDrawerHeader
Layout.fillWidth: true
Layout.topMargin: 16
backButtonFunction: function() {
showDetailsDrawer.close()
}
}
headerText: name
FlickableType {
anchors.top: showDetailsBackButton.bottom
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: parent.bottom
contentHeight: {
var emptySpaceHeight = parent.height - showDetailsBackButton.implicitHeight - showDetailsBackButton.anchors.topMargin
return (showDetailsDrawerContent.height > emptySpaceHeight) ?
showDetailsDrawerContent.height : emptySpaceHeight
}
ParagraphTextType {
Layout.fillWidth: true
Layout.topMargin: 16
Layout.bottomMargin: 16
ColumnLayout {
id: showDetailsDrawerContent
text: detailedDescription
textFormat: Text.MarkdownText
}
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
anchors.rightMargin: 16
anchors.leftMargin: 16
Header2Type {
id: showDetailsDrawerHeader
Layout.fillWidth: true
Layout.topMargin: 16
headerText: name
}
ParagraphTextType {
Layout.fillWidth: true
Layout.topMargin: 16
Layout.bottomMargin: 16
text: detailedDescription
textFormat: Text.MarkdownText
}
Rectangle {
Layout.fillHeight: true
color: "transparent"
}
Rectangle {
Layout.fillHeight: true
color: "transparent"
}
BasicButtonType {
Layout.fillWidth: true
Layout.bottomMargin: 32
BasicButtonType {
Layout.fillWidth: true
Layout.bottomMargin: 32
text: qsTr("Close")
text: qsTr("Close")
onClicked: function() {
showDetailsDrawer.close()
clickedFunc: function() {
showDetailsDrawer.close()
}
}
}
}
@@ -197,6 +203,8 @@ PageType {
headerText: qsTr("Port")
textField.maximumLength: 5
textField.validator: IntValidator { bottom: 1; top: 65535 }
KeyNavigation.tab: installButton
}
Rectangle {
@@ -212,7 +220,7 @@ PageType {
text: qsTr("Install")
onClicked: function() {
clickedFunc: function() {
PageController.goToPage(PageEnum.PageSetupWizardInstalling);
InstallController.install(dockerContainer, port.textFieldText, transportProtoSelector.currentIndex)
}
@@ -232,6 +240,8 @@ PageType {
var protocolSelectorVisible = ProtocolProps.defaultTransportProtoChangeable(defaultContainerProto)
transportProtoSelector.visible = protocolSelectorVisible
transportProtoHeader.visible = protocolSelectorVisible
defaultActiveFocusItem = port.textField
}
}
}

View File

@@ -13,6 +13,8 @@ import "../Components"
PageType {
id: root
property bool isControlsDisabled: false
Connections {
target: PageController
@@ -45,6 +47,18 @@ PageType {
stackView.pop()
}
}
function onDisableControls(disabled) {
isControlsDisabled = disabled
}
function onEscapePressed() {
if (isControlsDisabled || busyIndicator.visible) {
return
}
PageController.closePage()
}
}
Connections {
@@ -115,8 +129,8 @@ PageType {
text: qsTr("I have the data to connect")
onClicked: {
connectionTypeSelection.visible = true
clickedFunc: function() {
connectionTypeSelection.open()
}
}
@@ -135,13 +149,15 @@ PageType {
text: qsTr("I have nothing")
onClicked: Qt.openUrlExternally(qsTr("https://amnezia.org/instructions/0_starter-guide"))
clickedFunc: function() {
Qt.openUrlExternally(qsTr("https://amnezia.org/instructions/0_starter-guide"))
}
}
}
}
ConnectionTypeSelectionDrawer {
id: connectionTypeSelection
}
ConnectionTypeSelectionDrawer {
id: connectionTypeSelection
}
BusyIndicatorType {

View File

@@ -12,6 +12,8 @@ import "../Config"
PageType {
id: root
defaultActiveFocusItem: textKey.textField
FlickableType {
id: fl
anchors.top: parent.top
@@ -56,11 +58,15 @@ PageType {
textField.text = ""
textField.paste()
}
KeyNavigation.tab: continueButton
}
}
}
BasicButtonType {
id: continueButton
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.right: parent.right
@@ -70,7 +76,7 @@ PageType {
text: qsTr("Continue")
onClicked: function() {
clickedFunc: function() {
ImportController.extractConfigFromCode(textKey.textFieldText)
PageController.goToPage(PageEnum.PageSetupWizardViewConfig)
}

View File

@@ -30,7 +30,7 @@ PageType {
function onImportFinished() {
if (!ConnectionController.isConnected) {
ServersModel.setDefaultServerIndex(ServersModel.getServersCount() - 1);
ServersModel.currentlyProcessedIndex = ServersModel.defaultIndex
ServersModel.processedIndex = ServersModel.defaultIndex
}
PageController.goToStartPage()
@@ -109,7 +109,7 @@ PageType {
text: showContent ? qsTr("Collapse content") : qsTr("Show content")
onClicked: {
clickedFunc: function() {
showContent = !showContent
}
}
@@ -151,7 +151,7 @@ PageType {
Layout.bottomMargin: 32
text: qsTr("Connect")
onClicked: {
clickedFunc: function() {
ImportController.importConfig()
}
}

View File

@@ -16,10 +16,13 @@ import "../Components"
PageType {
id: root
defaultActiveFocusItem: clientNameTextField.textField
enum ConfigType {
AmneziaConnection,
OpenVpn,
WireGuard,
Awg,
ShadowSocks,
Cloak
}
@@ -29,7 +32,7 @@ PageType {
PageController.showBusyIndicator(true)
ExportController.revokeConfig(index,
ContainersModel.getCurrentlyProcessedContainerIndex(),
ServersModel.getCurrentlyProcessedServerCredentials())
ServersModel.getProcessedServerCredentials())
PageController.showBusyIndicator(false)
PageController.showNotificationMessage(qsTr("Config revoked"))
}
@@ -41,14 +44,15 @@ PageType {
shareConnectionDrawer.headerText = qsTr("Connection to ") + serverSelector.text
shareConnectionDrawer.configContentHeaderText = qsTr("File with connection settings to ") + serverSelector.text
shareConnectionDrawer.needCloseButton = false
shareConnectionDrawer.open()
shareConnectionDrawer.contentVisible = false
PageController.showBusyIndicator(true)
switch (type) {
case PageShare.ConfigType.AmneziaConnection: ExportController.generateConnectionConfig(clientNameTextField.textFieldText); break;
case PageShare.ConfigType.AmneziaConnection: {
ExportController.generateConnectionConfig(clientNameTextField.textFieldText);
break;
}
case PageShare.ConfigType.OpenVpn: {
ExportController.generateOpenVpnConfig(clientNameTextField.textFieldText)
shareConnectionDrawer.configCaption = qsTr("Save OpenVPN config")
@@ -63,6 +67,13 @@ PageType {
shareConnectionDrawer.configFileName = "amnezia_for_wireguard"
break
}
case PageShare.ConfigType.Awg: {
ExportController.generateAwgConfig(clientNameTextField.textFieldText)
shareConnectionDrawer.configCaption = qsTr("Save AmneziaWG config")
shareConnectionDrawer.configExtension = ".conf"
shareConnectionDrawer.configFileName = "amnezia_for_awg"
break
}
case PageShare.ConfigType.ShadowSocks: {
ExportController.generateShadowSocksConfig()
shareConnectionDrawer.configCaption = qsTr("Save ShadowSocks config")
@@ -80,11 +91,6 @@ PageType {
}
PageController.showBusyIndicator(false)
shareConnectionDrawer.needCloseButton = true
PageController.showTopCloseButton(true)
shareConnectionDrawer.contentVisible = true
}
function onExportErrorOccurred(errorMessage) {
@@ -115,6 +121,11 @@ PageType {
property string name: qsTr("WireGuard native format")
property var type: PageShare.ConfigType.WireGuard
}
QtObject {
id: awgConnectionFormat
property string name: qsTr("AmneziaWG native format")
property var type: PageShare.ConfigType.Awg
}
QtObject {
id: shadowSocksConnectionFormat
property string name: qsTr("ShadowSocks native format")
@@ -129,7 +140,7 @@ PageType {
FlickableType {
anchors.top: parent.top
anchors.bottom: parent.bottom
contentHeight: content.height
contentHeight: content.height + 10
ColumnLayout {
id: content
@@ -154,14 +165,15 @@ PageType {
shareFullAccessDrawer.open()
}
DrawerType {
DrawerType2 {
id: shareFullAccessDrawer
width: root.width
height: root.height * 0.45
parent: root
anchors.fill: parent
expandedHeight: root.height * 0.45
ColumnLayout {
expandedContent: ColumnLayout {
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
@@ -234,7 +246,7 @@ PageType {
accessTypeSelector.currentIndex = 1
PageController.showBusyIndicator(true)
ExportController.updateClientManagementModel(ContainersModel.getCurrentlyProcessedContainerIndex(),
ServersModel.getCurrentlyProcessedServerCredentials())
ServersModel.getProcessedServerCredentials())
PageController.showBusyIndicator(false)
}
}
@@ -264,6 +276,8 @@ PageType {
textField.maximumLength: 20
checkEmptyText: true
KeyNavigation.tab: shareButton
}
DropDownType {
@@ -276,6 +290,7 @@ PageType {
Layout.topMargin: 16
drawerHeight: 0.4375
drawerParent: root
descriptionText: qsTr("Server")
headerText: qsTr("Server")
@@ -305,7 +320,7 @@ PageType {
serverSelector.severSelectorIndexChanged()
}
serverSelector.menuVisible = false
serverSelector.close()
}
Component.onCompleted: {
@@ -316,7 +331,7 @@ PageType {
function handler() {
serverSelector.text = selectedText
ServersModel.currentlyProcessedIndex = proxyServersModel.mapToSource(currentIndex)
ServersModel.processedIndex = proxyServersModel.mapToSource(currentIndex)
}
}
}
@@ -328,6 +343,7 @@ PageType {
Layout.topMargin: 16
drawerHeight: 0.5
drawerParent: root
descriptionText: qsTr("Protocol")
headerText: qsTr("Protocol")
@@ -358,14 +374,15 @@ PageType {
clickedFunction: function() {
handler()
protocolSelector.menuVisible = false
protocolSelector.close()
}
Connections {
target: serverSelector
function onSeverSelectorIndexChanged() {
protocolSelectorListView.currentIndex = proxyContainersModel.mapFromSource(ServersModel.getDefaultContainer())
var defaultContainer = proxyContainersModel.mapFromSource(ServersModel.getProcessedServerData("defaultContainer"))
protocolSelectorListView.currentIndex = defaultContainer
protocolSelectorListView.triggerCurrentItem()
}
}
@@ -387,7 +404,7 @@ PageType {
if (accessTypeSelector.currentIndex === 1) {
PageController.showBusyIndicator(true)
ExportController.updateClientManagementModel(ContainersModel.getCurrentlyProcessedContainerIndex(),
ServersModel.getCurrentlyProcessedServerCredentials())
ServersModel.getProcessedServerCredentials())
PageController.showBusyIndicator(false)
}
}
@@ -401,6 +418,8 @@ PageType {
root.connectionTypesModel.push(openVpnConnectionFormat)
} else if (index === ContainerProps.containerFromString("amnezia-wireguard")) {
root.connectionTypesModel.push(wireGuardConnectionFormat)
} else if (index === ContainerProps.containerFromString("amnezia-awg")) {
root.connectionTypesModel.push(awgConnectionFormat)
} else if (index === ContainerProps.containerFromString("amnezia-shadowsocks")) {
root.connectionTypesModel.push(openVpnConnectionFormat)
root.connectionTypesModel.push(shadowSocksConnectionFormat)
@@ -422,6 +441,7 @@ PageType {
Layout.topMargin: 16
drawerHeight: 0.4375
drawerParent: root
visible: accessTypeSelector.currentIndex === 0
enabled: root.connectionTypesModel.length > 1
@@ -445,7 +465,7 @@ PageType {
clickedFunction: function() {
exportTypeSelector.text = selectedText
exportTypeSelector.currentIndex = currentIndex
exportTypeSelector.menuVisible = false
exportTypeSelector.close()
}
Component.onCompleted: {
@@ -455,11 +475,9 @@ PageType {
}
}
ShareConnectionDrawer {
id: shareConnectionDrawer
}
BasicButtonType {
id: shareButton
Layout.fillWidth: true
Layout.topMargin: 40
@@ -469,7 +487,7 @@ PageType {
text: qsTr("Share")
imageSource: "qrc:/images/controls/share-2.svg"
onClicked: {
clickedFunc: function(){
if (clientNameTextField.textFieldText !== "") {
ExportController.generateConfig(root.connectionTypesModel[exportTypeSelector.currentIndex].type)
}
@@ -560,13 +578,15 @@ PageType {
DividerType {}
DrawerType {
DrawerType2 {
id: clientInfoDrawer
width: root.width
height: root.height * 0.5
parent: root
ColumnLayout {
anchors.fill: parent
expandedHeight: root.height * 0.5
expandedContent: ColumnLayout {
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
@@ -597,30 +617,33 @@ PageType {
text: qsTr("Rename")
onClicked: function() {
clickedFunc: function() {
clientNameEditDrawer.open()
}
DrawerType {
DrawerType2 {
id: clientNameEditDrawer
width: root.width
height: root.height * 0.35
parent: root
onVisibleChanged: {
if (clientNameEditDrawer.visible) {
clientNameEditor.textField.forceActiveFocus()
}
}
anchors.fill: parent
expandedHeight: root.height * 0.35
ColumnLayout {
expandedContent: ColumnLayout {
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
anchors.topMargin: 16
anchors.topMargin: 32
anchors.leftMargin: 16
anchors.rightMargin: 16
Connections {
target: clientNameEditDrawer
function onOpened() {
clientNameEditor.textField.forceActiveFocus()
}
}
TextFieldWithHeaderType {
id: clientNameEditor
Layout.fillWidth: true
@@ -628,14 +651,18 @@ PageType {
textFieldText: clientName
textField.maximumLength: 20
checkEmptyText: true
KeyNavigation.tab: saveButton
}
BasicButtonType {
id: saveButton
Layout.fillWidth: true
text: qsTr("Save")
onClicked: {
clickedFunc: function() {
if (clientNameEditor.textFieldText === "") {
return
}
@@ -645,7 +672,7 @@ PageType {
ExportController.renameClient(index,
clientNameEditor.textFieldText,
ContainersModel.getCurrentlyProcessedContainerIndex(),
ServersModel.getCurrentlyProcessedServerCredentials())
ServersModel.getProcessedServerCredentials())
PageController.showBusyIndicator(false)
clientNameEditDrawer.close()
}
@@ -667,21 +694,20 @@ PageType {
text: qsTr("Revoke")
onClicked: function() {
questionDrawer.headerText = qsTr("Revoke the config for a user - %1?").arg(clientName)
questionDrawer.descriptionText = qsTr("The user will no longer be able to connect to your server.")
questionDrawer.yesButtonText = qsTr("Continue")
questionDrawer.noButtonText = qsTr("Cancel")
clickedFunc: function() {
var headerText = qsTr("Revoke the config for a user - %1?").arg(clientName)
var descriptionText = qsTr("The user will no longer be able to connect to your server.")
var yesButtonText = qsTr("Continue")
var noButtonText = qsTr("Cancel")
questionDrawer.yesButtonFunction = function() {
questionDrawer.close()
var yesButtonFunction = function() {
clientInfoDrawer.close()
root.revokeConfig(index)
}
questionDrawer.noButtonFunction = function() {
questionDrawer.close()
var noButtonFunction = function() {
}
questionDrawer.open()
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
}
}
}
@@ -689,12 +715,15 @@ PageType {
}
}
}
QuestionDrawer {
id: questionDrawer
}
}
}
ShareConnectionDrawer {
id: shareConnectionDrawer
anchors.fill: parent
}
MouseArea {
anchors.fill: parent
onPressed: function(mouse) {

View File

@@ -69,6 +69,7 @@ PageType {
Layout.topMargin: 16
drawerHeight: 0.4375
drawerParent: root
descriptionText: qsTr("Server")
headerText: qsTr("Server")
@@ -99,7 +100,7 @@ PageType {
shareConnectionDrawer.headerText = qsTr("Accessing ") + serverSelector.text
shareConnectionDrawer.configContentHeaderText = qsTr("File with accessing settings to ") + serverSelector.text
serverSelector.menuVisible = false
serverSelector.close()
}
Component.onCompleted: {
@@ -110,7 +111,7 @@ PageType {
function handler() {
serverSelector.text = selectedText
ServersModel.currentlyProcessedIndex = proxyServersModel.mapToSource(currentIndex)
ServersModel.processedIndex = proxyServersModel.mapToSource(currentIndex)
}
}
}
@@ -122,12 +123,10 @@ PageType {
text: qsTr("Share")
imageSource: "qrc:/images/controls/share-2.svg"
onClicked: function() {
clickedFunc: function() {
shareConnectionDrawer.headerText = qsTr("Connection to ") + serverSelector.text
shareConnectionDrawer.configContentHeaderText = qsTr("File with connection settings to ") + serverSelector.text
shareConnectionDrawer.needCloseButton = false
shareConnectionDrawer.open()
shareConnectionDrawer.contentVisible = false
PageController.showBusyIndicator(true)
@@ -140,16 +139,15 @@ PageType {
PageController.showBusyIndicator(false)
shareConnectionDrawer.needCloseButton = true
PageController.showTopCloseButton(true)
shareConnectionDrawer.contentVisible = true
}
}
ShareConnectionDrawer {
id: shareConnectionDrawer
}
}
}
ShareConnectionDrawer {
id: shareConnectionDrawer
anchors.fill: parent
}
}

View File

@@ -20,22 +20,16 @@ PageType {
function onGoToPageHome() {
tabBar.setCurrentIndex(0)
tabBarStackView.goToTabBarPage(PageEnum.PageHome)
PageController.updateDrawerRootPage(PageEnum.PageHome)
}
function onGoToPageSettings() {
tabBar.setCurrentIndex(2)
tabBarStackView.goToTabBarPage(PageEnum.PageSettings)
PageController.updateDrawerRootPage(PageEnum.PageSettings)
}
function onGoToPageViewConfig() {
var pagePath = PageController.getPagePath(PageEnum.PageSetupWizardViewConfig)
tabBarStackView.push(pagePath, { "objectName" : pagePath }, StackView.PushTransition)
PageController.updateDrawerRootPage(PageEnum.PageSetupWizardViewConfig)
}
function onShowBusyIndicator(visible) {
@@ -44,15 +38,14 @@ PageType {
tabBar.enabled = !visible
}
// function onShowTopCloseButton(visible) {
// topCloseButton.visible = visible
// }
function onEnableTabBar(enabled) {
tabBar.enabled = enabled
function onDisableControls(disabled) {
tabBar.enabled = !disabled
}
function onClosePage() {
tabBar.isServerInfoShow = tabBarStackView.currentItem.objectName !== PageController.getPagePath(PageEnum.PageSettingsServerInfo)
&& tabBarStackView.currentItem.objectName !== PageController.getPagePath(PageEnum.PageSettingsSplitTunneling)
if (tabBarStackView.depth <= 1) {
return
}
@@ -61,13 +54,14 @@ PageType {
function onGoToPage(page, slide) {
var pagePath = PageController.getPagePath(page)
if (slide) {
tabBarStackView.push(pagePath, { "objectName" : pagePath }, StackView.PushTransition)
} else {
tabBarStackView.push(pagePath, { "objectName" : pagePath }, StackView.Immediate)
}
PageController.updateDrawerRootPage(page)
tabBar.isServerInfoShow = page === PageEnum.PageSettingsServerInfo || PageEnum.PageSettingsSplitTunneling || tabBar.isServerInfoShow
}
function onGoToStartPage() {
@@ -76,6 +70,21 @@ PageType {
tabBarStackView.pop()
}
}
function onEscapePressed() {
if (!tabBar.enabled || busyIndicator.visible) {
return
}
var pageName = tabBarStackView.currentItem.objectName
if ((pageName === PageController.getPagePath(PageEnum.PageShare)) ||
(pageName === PageController.getPagePath(PageEnum.PageSettings))) {
PageController.goToPageHome()
tabBar.previousIndex = 0
} else {
PageController.closePage()
}
}
}
Connections {
@@ -115,20 +124,12 @@ PageType {
function onNoInstalledContainers() {
PageController.setTriggeredBtConnectButton(true)
ServersModel.currentlyProcessedIndex = ServersModel.getDefaultServerIndex()
ServersModel.processedIndex = ServersModel.getDefaultServerIndex()
InstallController.setShouldCreateServer(false)
PageController.goToPage(PageEnum.PageSetupWizardEasy)
}
}
Connections {
target: ApiController
function onErrorOccurred(errorMessage) {
PageController.showErrorMessage(errorMessage)
}
}
StackViewType {
id: tabBarStackView
@@ -146,26 +147,21 @@ PageType {
var pagePath = PageController.getPagePath(page)
tabBarStackView.clear(StackView.Immediate)
tabBarStackView.replace(pagePath, { "objectName" : pagePath }, StackView.Immediate)
PageController.updateDrawerRootPage(page)
tabBar.isServerInfoShow = false
}
Component.onCompleted: {
var pagePath = PageController.getPagePath(PageEnum.PageHome)
ServersModel.currentlyProcessedIndex = ServersModel.defaultIndex
ServersModel.processedIndex = ServersModel.defaultIndex
tabBarStackView.push(pagePath, { "objectName" : pagePath })
}
// onWidthChanged: {
// topCloseButton.x = tabBarStackView.x + tabBarStackView.width -
// topCloseButton.buttonWidth - topCloseButton.rightPadding
// }
}
TabBar {
id: tabBar
property int previousIndex: 0
property bool isServerInfoShow: false
anchors.right: parent.right
anchors.left: parent.left
@@ -196,11 +192,11 @@ PageType {
}
TabImageButtonType {
isSelected: tabBar.currentIndex === 0
isSelected: tabBar.isServerInfoShow ? false : tabBar.currentIndex === 0
image: "qrc:/images/controls/home.svg"
onClicked: {
tabBarStackView.goToTabBarPage(PageEnum.PageHome)
ServersModel.currentlyProcessedIndex = ServersModel.defaultIndex
ServersModel.processedIndex = ServersModel.defaultIndex
tabBar.previousIndex = 0
}
}
@@ -230,7 +226,7 @@ PageType {
}
TabImageButtonType {
isSelected: tabBar.currentIndex === 2
isSelected: tabBar.isServerInfoShow ? true : tabBar.currentIndex === 2
image: "qrc:/images/controls/settings-2.svg"
onClicked: {
tabBarStackView.goToTabBarPage(PageEnum.PageSettings)
@@ -253,13 +249,6 @@ PageType {
z: 1
}
// TopCloseButtonType {
// id: topCloseButton
// x: tabBarStackView.width - topCloseButton.buttonWidth - topCloseButton.rightPadding
// z: 1
// }
ConnectionTypeSelectionDrawer {
id: connectionTypeSelection

View File

@@ -8,6 +8,7 @@ import PageEnum 1.0
import "Config"
import "Controls2"
import "Components"
Window {
id: root
@@ -130,32 +131,15 @@ Window {
}
Item {
anchors.right: parent.right
anchors.left: parent.left
anchors.bottom: parent.bottom
anchors.fill: parent
implicitHeight: popupErrorMessage.height
DrawerType {
DrawerType2 {
id: privateKeyPassphraseDrawer
width: root.width
height: root.height * 0.35
anchors.fill: parent
expandedHeight: root.height * 0.35
onVisibleChanged: {
if (privateKeyPassphraseDrawer.visible) {
passphrase.textFieldText = ""
passphrase.textField.forceActiveFocus()
}
}
onAboutToHide: {
PageController.showBusyIndicator(true)
}
onAboutToShow: {
PageController.showBusyIndicator(false)
}
ColumnLayout {
expandedContent: ColumnLayout {
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
@@ -163,6 +147,24 @@ Window {
anchors.leftMargin: 16
anchors.rightMargin: 16
Connections {
target: privateKeyPassphraseDrawer
function onOpened() {
passphrase.textFieldText = ""
passphrase.textField.forceActiveFocus()
}
function onAboutToHide() {
if (passphrase.textFieldText !== "") {
PageController.showBusyIndicator(true)
}
}
function onAboutToShow() {
PageController.showBusyIndicator(false)
}
}
TextFieldWithHeaderType {
id: passphrase
@@ -176,9 +178,13 @@ Window {
clickedFunc: function() {
hidePassword = !hidePassword
}
KeyNavigation.tab: saveButton
}
BasicButtonType {
id: saveButton
Layout.fillWidth: true
defaultColor: "transparent"
@@ -190,7 +196,7 @@ Window {
text: qsTr("Save")
onClicked: {
clickedFunc: function() {
privateKeyPassphraseDrawer.close()
PageController.passphraseRequestDrawerClosed(passphrase.textFieldText)
}
@@ -199,6 +205,37 @@ Window {
}
}
Item {
anchors.fill: parent
QuestionDrawer {
id: questionDrawer
anchors.fill: parent
}
}
function showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction) {
questionDrawer.headerText = headerText
questionDrawer.descriptionText = descriptionText
questionDrawer.yesButtonText = yesButtonText
questionDrawer.noButtonText = noButtonText
questionDrawer.yesButtonFunction = function() {
questionDrawer.close()
if (yesButtonFunction && typeof yesButtonFunction === "function") {
yesButtonFunction()
}
}
questionDrawer.noButtonFunction = function() {
questionDrawer.close()
if (noButtonFunction && typeof noButtonFunction === "function") {
noButtonFunction()
}
}
questionDrawer.open()
}
FileDialog {
id: mainFileDialog

View File

@@ -20,7 +20,7 @@ PLIST_NAME=$APP_NAME.plist
# Search Qt
if [ -z "${QT_VERSION+x}" ]; then
QT_VERSION=6.5.2;
QT_VERSION=6.6.2;
QT_BIN_DIR=$HOME/Qt/$QT_VERSION/ios/bin
fi

View File

@@ -36,7 +36,7 @@ QMAKE_STASH_FILE=$PROJECT_DIR/.qmake_stash
# Search Qt
if [ -z "${QT_VERSION+x}" ]; then
QT_VERSION=5.15.2
QT_VERSION=6.6.2
if [ -f /opt/Qt/$QT_VERSION/gcc_64/bin/qmake ]; then
QT_BIN_DIR=/opt/Qt/$QT_VERSION/gcc_64/bin
elif [ -f $HOME/Qt/$QT_VERSION/gcc_64/bin/qmake ]; then

View File

@@ -37,7 +37,7 @@ DMG_FILENAME=$PROJECT_DIR/${APP_NAME}.dmg
# Search Qt
if [ -z "${QT_VERSION+x}" ]; then
QT_VERSION=6.5.1;
QT_VERSION=6.4.3;
QIF_VERSION=4.6
QT_BIN_DIR=$HOME/Qt/$QT_VERSION/macos/bin
QIF_BIN_DIR=$QT_BIN_DIR/../../../Tools/QtInstallerFramework/$QIF_VERSION/bin
@@ -146,7 +146,8 @@ if [ "${MAC_CERT_PW+x}" ]; then
fi
echo "Building DMG installer..."
hdiutil create -size 120mb -volname AmneziaVPN -srcfolder $BUILD_DIR/installer/$APP_NAME.app -ov -format UDZO $DMG_FILENAME
# Allow Terminal to make changes in Privacy & Security > App Management
hdiutil create -size 256mb -volname AmneziaVPN -srcfolder $BUILD_DIR/installer/$APP_NAME.app -ov -format UDZO $DMG_FILENAME
if [ "${MAC_CERT_PW+x}" ]; then
echo "Signing DMG installer..."