feature/page-home-split-tunneling (#540)
Added split tunneling button on home page
This commit is contained in:
parent
21fdf02921
commit
61abf74b2d
11 changed files with 187 additions and 4 deletions
6
client/images/controls/split-tunneling.svg
Normal file
6
client/images/controls/split-tunneling.svg
Normal 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 |
|
@ -224,6 +224,8 @@
|
||||||
<file>ui/qml/Pages2/PageShareFullAccess.qml</file>
|
<file>ui/qml/Pages2/PageShareFullAccess.qml</file>
|
||||||
<file>images/controls/close.svg</file>
|
<file>images/controls/close.svg</file>
|
||||||
<file>images/controls/search.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>
|
<file>ui/qml/Controls2/DrawerType2.qml</file>
|
||||||
</qresource>
|
</qresource>
|
||||||
</RCC>
|
</RCC>
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
|
@ -48,6 +48,8 @@ public:
|
||||||
Q_PROPERTY(QString defaultServerDefaultContainerName READ getDefaultServerDefaultContainerName NOTIFY defaultServerDefaultContainerChanged)
|
Q_PROPERTY(QString defaultServerDefaultContainerName READ getDefaultServerDefaultContainerName NOTIFY defaultServerDefaultContainerChanged)
|
||||||
Q_PROPERTY(QString defaultServerDescriptionCollapsed READ getDefaultServerDescriptionCollapsed NOTIFY defaultServerDefaultContainerChanged)
|
Q_PROPERTY(QString defaultServerDescriptionCollapsed READ getDefaultServerDescriptionCollapsed NOTIFY defaultServerDefaultContainerChanged)
|
||||||
Q_PROPERTY(QString defaultServerDescriptionExpanded READ getDefaultServerDescriptionExpanded 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)
|
Q_PROPERTY(int processedIndex READ getProcessedServerIndex WRITE setProcessedServerIndex NOTIFY processedServerIndexChanged)
|
||||||
|
|
||||||
|
@ -103,6 +105,8 @@ public slots:
|
||||||
QVariant getProcessedServerData(const QString roleString);
|
QVariant getProcessedServerData(const QString roleString);
|
||||||
void setProcessedServerData(const QString roleString, const QVariant &value);
|
void setProcessedServerData(const QString roleString, const QVariant &value);
|
||||||
|
|
||||||
|
bool isDefaultServerDefaultContainerHasSplitTunneling();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
QHash<int, QByteArray> roleNames() const override;
|
QHash<int, QByteArray> roleNames() const override;
|
||||||
|
|
||||||
|
|
|
@ -113,6 +113,7 @@ void SitesModel::toggleSplitTunneling(bool enabled)
|
||||||
m_settings->setRouteMode(Settings::RouteMode::VpnAllSites);
|
m_settings->setRouteMode(Settings::RouteMode::VpnAllSites);
|
||||||
}
|
}
|
||||||
m_isSplitTunnelingEnabled = enabled;
|
m_isSplitTunnelingEnabled = enabled;
|
||||||
|
emit splitTunnelingToggled();
|
||||||
}
|
}
|
||||||
|
|
||||||
QVector<QPair<QString, QString> > SitesModel::getCurrentSites()
|
QVector<QPair<QString, QString> > SitesModel::getCurrentSites()
|
||||||
|
|
|
@ -22,6 +22,7 @@ public:
|
||||||
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
|
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
|
||||||
|
|
||||||
Q_PROPERTY(int routeMode READ getRouteMode WRITE setRouteMode NOTIFY routeModeChanged)
|
Q_PROPERTY(int routeMode READ getRouteMode WRITE setRouteMode NOTIFY routeModeChanged)
|
||||||
|
Q_PROPERTY(bool isTunnelingEnabled READ isSplitTunnelingEnabled NOTIFY splitTunnelingToggled)
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
bool addSite(const QString &hostname, const QString &ip);
|
bool addSite(const QString &hostname, const QString &ip);
|
||||||
|
@ -38,6 +39,7 @@ public slots:
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void routeModeChanged();
|
void routeModeChanged();
|
||||||
|
void splitTunnelingToggled();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
QHash<int, QByteArray> roleNames() const override;
|
QHash<int, QByteArray> roleNames() const override;
|
||||||
|
|
92
client/ui/qml/Components/HomeSplitTunnelingDrawer.qml
Normal file
92
client/ui/qml/Components/HomeSplitTunnelingDrawer.qml
Normal 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -21,6 +21,8 @@ Button {
|
||||||
property int borderFocusedWidth: 1
|
property int borderFocusedWidth: 1
|
||||||
|
|
||||||
property string imageSource
|
property string imageSource
|
||||||
|
property string rightImageSource
|
||||||
|
property string leftImageColor: textColor
|
||||||
|
|
||||||
property bool squareLeftSide: false
|
property bool squareLeftSide: false
|
||||||
|
|
||||||
|
@ -118,7 +120,7 @@ Button {
|
||||||
layer {
|
layer {
|
||||||
enabled: true
|
enabled: true
|
||||||
effect: ColorOverlay {
|
effect: ColorOverlay {
|
||||||
color: textColor
|
color: leftImageColor
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -131,6 +133,21 @@ Button {
|
||||||
horizontalAlignment: Text.AlignLeft
|
horizontalAlignment: Text.AlignLeft
|
||||||
verticalAlignment: Text.AlignVCenter
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -34,8 +34,45 @@ PageType {
|
||||||
anchors.bottomMargin: drawer.collapsedHeight
|
anchors.bottomMargin: drawer.collapsedHeight
|
||||||
|
|
||||||
ConnectButton {
|
ConnectButton {
|
||||||
|
id: connectButton
|
||||||
anchors.centerIn: parent
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -29,8 +29,14 @@ PageType {
|
||||||
}
|
}
|
||||||
|
|
||||||
Component.onCompleted: {
|
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"))
|
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.fillWidth: true
|
||||||
Layout.rightMargin: 16
|
Layout.rightMargin: 16
|
||||||
|
|
||||||
checked: SitesModel.isSplitTunnelingEnabled()
|
checked: SitesModel.isTunnelingEnabled
|
||||||
onToggled: {
|
onToggled: {
|
||||||
SitesModel.toggleSplitTunneling(checked)
|
SitesModel.toggleSplitTunneling(checked)
|
||||||
selector.text = root.routeModesModel[getRouteModesModelIndex()].name
|
selector.text = root.routeModesModel[getRouteModesModelIndex()].name
|
||||||
|
|
|
@ -44,6 +44,7 @@ PageType {
|
||||||
|
|
||||||
function onClosePage() {
|
function onClosePage() {
|
||||||
tabBar.isServerInfoShow = tabBarStackView.currentItem.objectName !== PageController.getPagePath(PageEnum.PageSettingsServerInfo)
|
tabBar.isServerInfoShow = tabBarStackView.currentItem.objectName !== PageController.getPagePath(PageEnum.PageSettingsServerInfo)
|
||||||
|
&& tabBarStackView.currentItem.objectName !== PageController.getPagePath(PageEnum.PageSettingsSplitTunneling)
|
||||||
|
|
||||||
if (tabBarStackView.depth <= 1) {
|
if (tabBarStackView.depth <= 1) {
|
||||||
return
|
return
|
||||||
|
@ -60,7 +61,7 @@ PageType {
|
||||||
tabBarStackView.push(pagePath, { "objectName" : pagePath }, StackView.Immediate)
|
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() {
|
function onGoToStartPage() {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue