summaryrefslogtreecommitdiff
path: root/modules/controlcenter/network/VpnSettings.qml
diff options
context:
space:
mode:
Diffstat (limited to 'modules/controlcenter/network/VpnSettings.qml')
-rw-r--r--modules/controlcenter/network/VpnSettings.qml232
1 files changed, 232 insertions, 0 deletions
diff --git a/modules/controlcenter/network/VpnSettings.qml b/modules/controlcenter/network/VpnSettings.qml
new file mode 100644
index 0000000..7387ddc
--- /dev/null
+++ b/modules/controlcenter/network/VpnSettings.qml
@@ -0,0 +1,232 @@
+pragma ComponentBehavior: Bound
+
+import ".."
+import "../components"
+import qs.components
+import qs.components.controls
+import qs.components.containers
+import qs.components.effects
+import qs.services
+import qs.config
+import Quickshell
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Layouts
+
+ColumnLayout {
+ id: root
+
+ required property Session session
+
+ spacing: Appearance.spacing.normal
+
+ SettingsHeader {
+ icon: "vpn_key"
+ title: qsTr("VPN Settings")
+ }
+
+ SectionHeader {
+ Layout.topMargin: Appearance.spacing.large
+ title: qsTr("General")
+ description: qsTr("VPN configuration")
+ }
+
+ SectionContainer {
+ ToggleRow {
+ label: qsTr("VPN enabled")
+ checked: Config.utilities.vpn.enabled
+ toggle.onToggled: {
+ Config.utilities.vpn.enabled = checked;
+ Config.save();
+ }
+ }
+ }
+
+ SectionHeader {
+ Layout.topMargin: Appearance.spacing.large
+ title: qsTr("Providers")
+ description: qsTr("Manage VPN providers")
+ }
+
+ SectionContainer {
+ contentSpacing: Appearance.spacing.normal
+
+ ListView {
+ Layout.fillWidth: true
+ Layout.preferredHeight: contentHeight
+
+ interactive: false
+ spacing: Appearance.spacing.smaller
+
+ model: ScriptModel {
+ values: Config.utilities.vpn.provider.map((provider, index) => {
+ const isObject = typeof provider === "object";
+ const name = isObject ? (provider.name || "custom") : String(provider);
+ const displayName = isObject ? (provider.displayName || name) : name;
+ const iface = isObject ? (provider.interface || "") : "";
+
+ return {
+ index: index,
+ name: name,
+ displayName: displayName,
+ interface: iface,
+ provider: provider,
+ isActive: index === 0
+ };
+ })
+ }
+
+ delegate: Component {
+ StyledRect {
+ required property var modelData
+ required property int index
+
+ width: ListView.view ? ListView.view.width : undefined
+ color: Colours.tPalette.m3surfaceContainerHigh
+ radius: Appearance.rounding.normal
+
+ RowLayout {
+ anchors.left: parent.left
+ anchors.right: parent.right
+ anchors.verticalCenter: parent.verticalCenter
+ anchors.margins: Appearance.padding.normal
+ spacing: Appearance.spacing.normal
+
+ MaterialIcon {
+ text: modelData.isActive ? "vpn_key" : "vpn_key_off"
+ font.pointSize: Appearance.font.size.large
+ color: modelData.isActive ? Colours.palette.m3primary : Colours.palette.m3outline
+ }
+
+ ColumnLayout {
+ Layout.fillWidth: true
+ spacing: 0
+
+ StyledText {
+ text: modelData.displayName
+ font.weight: modelData.isActive ? 500 : 400
+ }
+
+ StyledText {
+ text: qsTr("%1 • %2").arg(modelData.name).arg(modelData.interface || qsTr("No interface"))
+ font.pointSize: Appearance.font.size.small
+ color: Colours.palette.m3outline
+ }
+ }
+
+ IconButton {
+ icon: modelData.isActive ? "arrow_downward" : "arrow_upward"
+ visible: !modelData.isActive || Config.utilities.vpn.provider.length > 1
+ onClicked: {
+ if (modelData.isActive && index < Config.utilities.vpn.provider.length - 1) {
+ // Move down
+ const providers = [...Config.utilities.vpn.provider];
+ const temp = providers[index];
+ providers[index] = providers[index + 1];
+ providers[index + 1] = temp;
+ Config.utilities.vpn.provider = providers;
+ Config.save();
+ } else if (!modelData.isActive) {
+ // Make active (move to top)
+ const providers = [...Config.utilities.vpn.provider];
+ const provider = providers.splice(index, 1)[0];
+ providers.unshift(provider);
+ Config.utilities.vpn.provider = providers;
+ Config.save();
+ }
+ }
+ }
+
+ IconButton {
+ icon: "delete"
+ onClicked: {
+ const providers = [...Config.utilities.vpn.provider];
+ providers.splice(index, 1);
+ Config.utilities.vpn.provider = providers;
+ Config.save();
+ }
+ }
+ }
+
+ implicitHeight: 60
+ }
+ }
+ }
+
+ TextButton {
+ Layout.fillWidth: true
+ Layout.topMargin: Appearance.spacing.normal
+ text: qsTr("+ Add Provider")
+ inactiveColour: Colours.palette.m3primaryContainer
+ inactiveOnColour: Colours.palette.m3onPrimaryContainer
+
+ onClicked: {
+ addProviderDialog.open();
+ }
+ }
+ }
+
+ SectionHeader {
+ Layout.topMargin: Appearance.spacing.large
+ title: qsTr("Quick Add")
+ description: qsTr("Add common VPN providers")
+ }
+
+ SectionContainer {
+ contentSpacing: Appearance.spacing.smaller
+
+ TextButton {
+ Layout.fillWidth: true
+ text: qsTr("+ Add NetBird")
+ inactiveColour: Colours.tPalette.m3surfaceContainerHigh
+ inactiveOnColour: Colours.palette.m3onSurface
+
+ onClicked: {
+ const providers = [...Config.utilities.vpn.provider];
+ providers.push({
+ name: "netbird",
+ displayName: "NetBird",
+ interface: "wt0"
+ });
+ Config.utilities.vpn.provider = providers;
+ Config.save();
+ }
+ }
+
+ TextButton {
+ Layout.fillWidth: true
+ text: qsTr("+ Add Tailscale")
+ inactiveColour: Colours.tPalette.m3surfaceContainerHigh
+ inactiveOnColour: Colours.palette.m3onSurface
+
+ onClicked: {
+ const providers = [...Config.utilities.vpn.provider];
+ providers.push({
+ name: "tailscale",
+ displayName: "Tailscale",
+ interface: "tailscale0"
+ });
+ Config.utilities.vpn.provider = providers;
+ Config.save();
+ }
+ }
+
+ TextButton {
+ Layout.fillWidth: true
+ text: qsTr("+ Add Cloudflare WARP")
+ inactiveColour: Colours.tPalette.m3surfaceContainerHigh
+ inactiveOnColour: Colours.palette.m3onSurface
+
+ onClicked: {
+ const providers = [...Config.utilities.vpn.provider];
+ providers.push({
+ name: "warp",
+ displayName: "Cloudflare WARP",
+ interface: "CloudflareWARP"
+ });
+ Config.utilities.vpn.provider = providers;
+ Config.save();
+ }
+ }
+ }
+}