diff --git a/client/images/controls/split-tunneling.svg b/client/images/controls/split-tunneling.svg
new file mode 100644
index 000000000..3062054df
--- /dev/null
+++ b/client/images/controls/split-tunneling.svg
@@ -0,0 +1,6 @@
+
diff --git a/client/resources.qrc b/client/resources.qrc
index 59a540cc9..b9a69023f 100644
--- a/client/resources.qrc
+++ b/client/resources.qrc
@@ -224,6 +224,8 @@
ui/qml/Pages2/PageShareFullAccess.qml
images/controls/close.svg
images/controls/search.svg
+ ui/qml/Components/HomeSplitTunnelingDrawer.qml
+ images/controls/split-tunneling.svg
ui/qml/Controls2/DrawerType2.qml
diff --git a/client/ui/models/servers_model.cpp b/client/ui/models/servers_model.cpp
index 2449de77a..8294cc012 100644
--- a/client/ui/models/servers_model.cpp
+++ b/client/ui/models/servers_model.cpp
@@ -577,3 +577,18 @@ void ServersModel::setProcessedServerData(const QString roleString, const QVaria
}
+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;
+}
diff --git a/client/ui/models/servers_model.h b/client/ui/models/servers_model.h
index 1adaebaef..b694b6f63 100644
--- a/client/ui/models/servers_model.h
+++ b/client/ui/models/servers_model.h
@@ -48,6 +48,8 @@ public:
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(int processedIndex READ getProcessedServerIndex WRITE setProcessedServerIndex NOTIFY processedServerIndexChanged)
@@ -103,6 +105,8 @@ public slots:
QVariant getProcessedServerData(const QString roleString);
void setProcessedServerData(const QString roleString, const QVariant &value);
+ bool isDefaultServerDefaultContainerHasSplitTunneling();
+
protected:
QHash roleNames() const override;
diff --git a/client/ui/models/sites_model.cpp b/client/ui/models/sites_model.cpp
index f6cb9b136..96b6ca605 100644
--- a/client/ui/models/sites_model.cpp
+++ b/client/ui/models/sites_model.cpp
@@ -113,6 +113,7 @@ void SitesModel::toggleSplitTunneling(bool enabled)
m_settings->setRouteMode(Settings::RouteMode::VpnAllSites);
}
m_isSplitTunnelingEnabled = enabled;
+ emit splitTunnelingToggled();
}
QVector > SitesModel::getCurrentSites()
diff --git a/client/ui/models/sites_model.h b/client/ui/models/sites_model.h
index ad16b7a33..803b7fd16 100644
--- a/client/ui/models/sites_model.h
+++ b/client/ui/models/sites_model.h
@@ -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 roleNames() const override;
diff --git a/client/ui/qml/Components/HomeSplitTunnelingDrawer.qml b/client/ui/qml/Components/HomeSplitTunnelingDrawer.qml
new file mode 100644
index 000000000..bc1f1008b
--- /dev/null
+++ b/client/ui/qml/Components/HomeSplitTunnelingDrawer.qml
@@ -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
+ }
+ }
+}
diff --git a/client/ui/qml/Controls2/BasicButtonType.qml b/client/ui/qml/Controls2/BasicButtonType.qml
index 77d4b5fb7..257486d62 100644
--- a/client/ui/qml/Controls2/BasicButtonType.qml
+++ b/client/ui/qml/Controls2/BasicButtonType.qml
@@ -21,6 +21,8 @@ Button {
property int borderFocusedWidth: 1
property string imageSource
+ property string rightImageSource
+ property string leftImageColor: textColor
property bool squareLeftSide: false
@@ -118,7 +120,7 @@ Button {
layer {
enabled: true
effect: ColorOverlay {
- color: textColor
+ color: leftImageColor
}
}
}
@@ -131,6 +133,21 @@ 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
+ }
+ }
+ }
}
}
diff --git a/client/ui/qml/Pages2/PageHome.qml b/client/ui/qml/Pages2/PageHome.qml
index 4b5ba53d5..04f59627c 100644
--- a/client/ui/qml/Pages2/PageHome.qml
+++ b/client/ui/qml/Pages2/PageHome.qml
@@ -34,8 +34,45 @@ PageType {
anchors.bottomMargin: drawer.collapsedHeight
ConnectButton {
+ id: connectButton
anchors.centerIn: parent
}
+
+ BasicButtonType {
+ anchors.horizontalCenter: parent.horizontalCenter
+ anchors.bottom: parent.bottom
+ 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
+ }
+ }
}
diff --git a/client/ui/qml/Pages2/PageSettingsSplitTunneling.qml b/client/ui/qml/Pages2/PageSettingsSplitTunneling.qml
index 729a6e9d0..1ce3cd646 100644
--- a/client/ui/qml/Pages2/PageSettingsSplitTunneling.qml
+++ b/client/ui/qml/Pages2/PageSettingsSplitTunneling.qml
@@ -29,8 +29,14 @@ PageType {
}
Component.onCompleted: {
- if (isServerFromApi) {
+ 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
}
}
@@ -108,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
diff --git a/client/ui/qml/Pages2/PageStart.qml b/client/ui/qml/Pages2/PageStart.qml
index 38a8e0b88..dac9db937 100644
--- a/client/ui/qml/Pages2/PageStart.qml
+++ b/client/ui/qml/Pages2/PageStart.qml
@@ -44,6 +44,7 @@ PageType {
function onClosePage() {
tabBar.isServerInfoShow = tabBarStackView.currentItem.objectName !== PageController.getPagePath(PageEnum.PageSettingsServerInfo)
+ && tabBarStackView.currentItem.objectName !== PageController.getPagePath(PageEnum.PageSettingsSplitTunneling)
if (tabBarStackView.depth <= 1) {
return
@@ -60,7 +61,7 @@ PageType {
tabBarStackView.push(pagePath, { "objectName" : pagePath }, StackView.Immediate)
}
- tabBar.isServerInfoShow = page === PageEnum.PageSettingsServerInfo || tabBar.isServerInfoShow
+ tabBar.isServerInfoShow = page === PageEnum.PageSettingsServerInfo || PageEnum.PageSettingsSplitTunneling || tabBar.isServerInfoShow
}
function onGoToStartPage() {