feat: improve wifi connection handling with existing network detection and action panel
This commit is contained in:
parent
fbea11ee9c
commit
5ad7d731c0
1 changed files with 188 additions and 70 deletions
|
|
@ -17,6 +17,10 @@ Item {
|
||||||
wifiLogic.refreshNetworks();
|
wifiLogic.refreshNetworks();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Component.onCompleted: {
|
||||||
|
existingNetwork.running = true;
|
||||||
|
}
|
||||||
|
|
||||||
function signalIcon(signal) {
|
function signalIcon(signal) {
|
||||||
if (signal >= 80) return "network_wifi";
|
if (signal >= 80) return "network_wifi";
|
||||||
if (signal >= 60) return "network_wifi_3_bar";
|
if (signal >= 60) return "network_wifi_3_bar";
|
||||||
|
|
@ -25,16 +29,48 @@ Item {
|
||||||
return "wifi_0_bar";
|
return "wifi_0_bar";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Process {
|
||||||
|
id: existingNetwork
|
||||||
|
running: false
|
||||||
|
command: ["nmcli", "-t", "-f", "NAME,TYPE", "connection", "show"]
|
||||||
|
stdout: StdioCollector {
|
||||||
|
onStreamFinished: {
|
||||||
|
const lines = text.split("\n");
|
||||||
|
const networksMap = {};
|
||||||
|
|
||||||
|
refreshIndicator.running = true;
|
||||||
|
refreshIndicator.visible = true;
|
||||||
|
|
||||||
|
for (let i = 0; i < lines.length; ++i) {
|
||||||
|
const line = lines[i].trim();
|
||||||
|
if (!line) continue;
|
||||||
|
|
||||||
|
const parts = line.split(":");
|
||||||
|
if (parts.length < 2) {
|
||||||
|
console.warn("Malformed nmcli output line:", line);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ssid = parts[0];
|
||||||
|
const type = parts[1];
|
||||||
|
|
||||||
|
if (ssid) {
|
||||||
|
networksMap[ssid] = { ssid: ssid, type: type };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
scanProcess.existingNetwork = networksMap;
|
||||||
|
scanProcess.running = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Process {
|
Process {
|
||||||
id: scanProcess
|
id: scanProcess
|
||||||
running: false
|
running: false
|
||||||
command: ["nmcli", "-t", "-f", "SSID,SECURITY,SIGNAL,IN-USE", "device", "wifi", "list"]
|
command: ["nmcli", "-t", "-f", "SSID,SECURITY,SIGNAL,IN-USE", "device", "wifi", "list"]
|
||||||
onRunningChanged: {
|
|
||||||
console.log("scanProcess.running changed: " + running);
|
property var existingNetwork
|
||||||
// if (!running) {
|
|
||||||
// console.log("scanProcess finished.");
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
stdout: StdioCollector {
|
stdout: StdioCollector {
|
||||||
onStreamFinished: {
|
onStreamFinished: {
|
||||||
const lines = text.split("\n");
|
const lines = text.split("\n");
|
||||||
|
|
@ -49,7 +85,6 @@ Item {
|
||||||
console.warn("Malformed nmcli output line:", line);
|
console.warn("Malformed nmcli output line:", line);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ssid = parts[0];
|
const ssid = parts[0];
|
||||||
const security = parts[1];
|
const security = parts[1];
|
||||||
const signal = parseInt(parts[2]);
|
const signal = parseInt(parts[2]);
|
||||||
|
|
@ -57,7 +92,7 @@ Item {
|
||||||
|
|
||||||
if (ssid) {
|
if (ssid) {
|
||||||
if (!networksMap[ssid]) {
|
if (!networksMap[ssid]) {
|
||||||
networksMap[ssid] = { ssid: ssid, security: security, signal: signal, connected: inUse };
|
networksMap[ssid] = { ssid: ssid, security: security, signal: signal, connected: inUse, existing: ssid in scanProcess.existingNetwork };
|
||||||
} else {
|
} else {
|
||||||
const existingNet = networksMap[ssid];
|
const existingNet = networksMap[ssid];
|
||||||
if (inUse) {
|
if (inUse) {
|
||||||
|
|
@ -71,6 +106,9 @@ Item {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
wifiLogic.networks = Object.values(networksMap);
|
wifiLogic.networks = Object.values(networksMap);
|
||||||
|
console.log(JSON.stringify(wifiLogic.networks));
|
||||||
|
refreshIndicator.running = false;
|
||||||
|
refreshIndicator.visible = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -91,17 +129,15 @@ Item {
|
||||||
property string connectSecurity: ""
|
property string connectSecurity: ""
|
||||||
property var pendingConnect: null
|
property var pendingConnect: null
|
||||||
property string detectedInterface: ""
|
property string detectedInterface: ""
|
||||||
|
property string actionPanelSsid: ""
|
||||||
|
|
||||||
function profileNameForSsid(ssid) {
|
|
||||||
return "quickshell-" + ssid.replace(/[^a-zA-Z0-9]/g, "_");
|
|
||||||
}
|
|
||||||
function disconnectNetwork(ssid) {
|
function disconnectNetwork(ssid) {
|
||||||
var profileName = wifiLogic.profileNameForSsid(ssid);
|
const profileName = ssid;
|
||||||
disconnectProfileProcess.connectionName = profileName;
|
disconnectProfileProcess.connectionName = profileName;
|
||||||
disconnectProfileProcess.running = true;
|
disconnectProfileProcess.running = true;
|
||||||
}
|
}
|
||||||
function refreshNetworks() {
|
function refreshNetworks() {
|
||||||
scanProcess.running = true;
|
existingNetwork.running = true;
|
||||||
}
|
}
|
||||||
function showAt() {
|
function showAt() {
|
||||||
wifiPanelModal.visible = true;
|
wifiPanelModal.visible = true;
|
||||||
|
|
@ -109,15 +145,37 @@ Item {
|
||||||
}
|
}
|
||||||
function connectNetwork(ssid, security) {
|
function connectNetwork(ssid, security) {
|
||||||
wifiLogic.pendingConnect = {ssid: ssid, security: security, password: ""};
|
wifiLogic.pendingConnect = {ssid: ssid, security: security, password: ""};
|
||||||
listConnectionsProcess.running = true;
|
wifiLogic.doConnect();
|
||||||
}
|
}
|
||||||
function submitPassword() {
|
function submitPassword() {
|
||||||
wifiLogic.pendingConnect = {ssid: wifiLogic.passwordPromptSsid, security: wifiLogic.connectSecurity, password: wifiLogic.passwordInput};
|
wifiLogic.pendingConnect = {ssid: wifiLogic.passwordPromptSsid, security: wifiLogic.connectSecurity, password: wifiLogic.passwordInput};
|
||||||
listConnectionsProcess.running = true;
|
wifiLogic.doConnect();
|
||||||
}
|
}
|
||||||
function doConnect() {
|
function doConnect() {
|
||||||
const params = wifiLogic.pendingConnect;
|
const params = wifiLogic.pendingConnect;
|
||||||
|
if (!params) return;
|
||||||
|
|
||||||
wifiLogic.connectingSsid = params.ssid;
|
wifiLogic.connectingSsid = params.ssid;
|
||||||
|
|
||||||
|
// Find the target network in our networks data
|
||||||
|
let targetNetwork = null;
|
||||||
|
for (let i = 0; i < wifiLogic.networks.length; ++i) {
|
||||||
|
if (wifiLogic.networks[i].ssid === params.ssid) {
|
||||||
|
targetNetwork = wifiLogic.networks[i];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if profile already exists using existing field
|
||||||
|
if (targetNetwork && targetNetwork.existing) {
|
||||||
|
// Profile exists, just bring it up (no password prompt)
|
||||||
|
upConnectionProcess.profileName = params.ssid;
|
||||||
|
upConnectionProcess.running = true;
|
||||||
|
wifiLogic.pendingConnect = null;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// No existing profile, proceed with normal connection flow
|
||||||
if (params.security && params.security !== "--") {
|
if (params.security && params.security !== "--") {
|
||||||
getInterfaceProcess.running = true;
|
getInterfaceProcess.running = true;
|
||||||
return;
|
return;
|
||||||
|
|
@ -146,50 +204,7 @@ Item {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Process {
|
|
||||||
id: listConnectionsProcess
|
|
||||||
running: false
|
|
||||||
command: ["nmcli", "-t", "-f", "NAME", "connection", "show"]
|
|
||||||
stdout: StdioCollector {
|
|
||||||
onStreamFinished: {
|
|
||||||
var params = wifiLogic.pendingConnect;
|
|
||||||
var lines = text.split("\n");
|
|
||||||
var expectedProfile = wifiLogic.profileNameForSsid(params.ssid);
|
|
||||||
var foundProfile = null;
|
|
||||||
for (var i = 0; i < lines.length; ++i) {
|
|
||||||
if (lines[i] === expectedProfile) {
|
|
||||||
foundProfile = lines[i];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (foundProfile) {
|
|
||||||
// Profile exists, just bring it up (no password prompt)
|
|
||||||
upConnectionProcess.profileName = foundProfile;
|
|
||||||
upConnectionProcess.running = true;
|
|
||||||
} else {
|
|
||||||
// No profile: check if secured
|
|
||||||
if (wifiLogic.isSecured(params.security)) {
|
|
||||||
if (params.password && params.password.length > 0) {
|
|
||||||
// Password provided, proceed to connect
|
|
||||||
wifiLogic.doConnect();
|
|
||||||
} else {
|
|
||||||
// No password yet, prompt for it
|
|
||||||
wifiLogic.passwordPromptSsid = params.ssid;
|
|
||||||
wifiLogic.passwordInput = "";
|
|
||||||
wifiLogic.showPasswordPrompt = true;
|
|
||||||
wifiLogic.connectStatus = "";
|
|
||||||
wifiLogic.connectStatusSsid = "";
|
|
||||||
wifiLogic.connectError = "";
|
|
||||||
wifiLogic.connectSecurity = params.security;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Open, connect directly
|
|
||||||
wifiLogic.doConnect();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handles connecting to a Wi-Fi network, with or without password
|
// Handles connecting to a Wi-Fi network, with or without password
|
||||||
Process {
|
Process {
|
||||||
|
|
@ -256,7 +271,7 @@ Item {
|
||||||
addConnectionProcess.ifname = wifiLogic.detectedInterface;
|
addConnectionProcess.ifname = wifiLogic.detectedInterface;
|
||||||
addConnectionProcess.ssid = params.ssid;
|
addConnectionProcess.ssid = params.ssid;
|
||||||
addConnectionProcess.password = params.password;
|
addConnectionProcess.password = params.password;
|
||||||
addConnectionProcess.profileName = wifiLogic.profileNameForSsid(params.ssid);
|
addConnectionProcess.profileName = params.ssid;
|
||||||
addConnectionProcess.security = params.security;
|
addConnectionProcess.security = params.security;
|
||||||
addConnectionProcess.running = true;
|
addConnectionProcess.running = true;
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -413,8 +428,8 @@ Item {
|
||||||
Layout.preferredWidth: 24
|
Layout.preferredWidth: 24
|
||||||
Layout.preferredHeight: 24
|
Layout.preferredHeight: 24
|
||||||
Layout.alignment: Qt.AlignVCenter
|
Layout.alignment: Qt.AlignVCenter
|
||||||
visible: scanProcess.running
|
visible: false
|
||||||
running: scanProcess.running
|
running: false
|
||||||
color: Theme.accentPrimary // Assuming Spinner supports color property
|
color: Theme.accentPrimary // Assuming Spinner supports color property
|
||||||
size: 22 // Based on the existing Spinner usage
|
size: 22 // Based on the existing Spinner usage
|
||||||
}
|
}
|
||||||
|
|
@ -490,8 +505,13 @@ Item {
|
||||||
model: wifiLogic.networks
|
model: wifiLogic.networks
|
||||||
delegate: Item {
|
delegate: Item {
|
||||||
id: networkEntry
|
id: networkEntry
|
||||||
|
|
||||||
|
required property var modelData
|
||||||
|
property var signalIcon: wifiPanel.signalIcon
|
||||||
|
|
||||||
width: parent.width
|
width: parent.width
|
||||||
height: modelData.ssid === wifiLogic.passwordPromptSsid && wifiLogic.showPasswordPrompt ? 102 : 42
|
height: (modelData.ssid === wifiLogic.passwordPromptSsid && wifiLogic.showPasswordPrompt ? 102 : 42) +
|
||||||
|
(modelData.ssid === wifiLogic.actionPanelSsid ? 60 : 0)
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
spacing: 0
|
spacing: 0
|
||||||
|
|
@ -596,10 +616,11 @@ Item {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
onClicked: {
|
onClicked: {
|
||||||
if (modelData.connected) {
|
// Toggle the action panel for this network
|
||||||
wifiLogic.disconnectNetwork(modelData.ssid);
|
if (wifiLogic.actionPanelSsid === modelData.ssid) {
|
||||||
|
wifiLogic.actionPanelSsid = ""; // Close if already open
|
||||||
} else {
|
} else {
|
||||||
wifiLogic.connectNetwork(modelData.ssid, modelData.security);
|
wifiLogic.actionPanelSsid = modelData.ssid; // Open for this network
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -610,8 +631,9 @@ Item {
|
||||||
Layout.preferredHeight: 60
|
Layout.preferredHeight: 60
|
||||||
radius: 8
|
radius: 8
|
||||||
color: "transparent"
|
color: "transparent"
|
||||||
anchors.leftMargin: 32
|
Layout.alignment: Qt.AlignLeft
|
||||||
anchors.rightMargin: 32
|
Layout.leftMargin: 32
|
||||||
|
Layout.rightMargin: 32
|
||||||
z: 2
|
z: 2
|
||||||
RowLayout {
|
RowLayout {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
|
@ -651,8 +673,8 @@ Item {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Rectangle {
|
Rectangle {
|
||||||
width: 80
|
Layout.preferredWidth: 80
|
||||||
height: 36
|
Layout.preferredHeight: 36
|
||||||
radius: 18
|
radius: 18
|
||||||
color: Theme.accentPrimary
|
color: Theme.accentPrimary
|
||||||
border.color: Theme.accentPrimary
|
border.color: Theme.accentPrimary
|
||||||
|
|
@ -677,6 +699,102 @@ Item {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Action panel for network connection controls
|
||||||
|
Rectangle {
|
||||||
|
visible: modelData.ssid === wifiLogic.actionPanelSsid
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.preferredHeight: 60
|
||||||
|
radius: 8
|
||||||
|
color: "transparent"
|
||||||
|
Layout.alignment: Qt.AlignLeft
|
||||||
|
Layout.leftMargin: 32
|
||||||
|
Layout.rightMargin: 32
|
||||||
|
z: 2
|
||||||
|
RowLayout {
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.margins: 12
|
||||||
|
spacing: 10
|
||||||
|
// Password field for new secured networks
|
||||||
|
Item {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.preferredHeight: 36
|
||||||
|
visible: wifiLogic.isSecured(modelData.security) && !modelData.connected && !modelData.existing
|
||||||
|
Rectangle {
|
||||||
|
anchors.fill: parent
|
||||||
|
radius: 8
|
||||||
|
color: "transparent"
|
||||||
|
border.color: actionPanelPasswordField.activeFocus ? Theme.accentPrimary : Theme.outline
|
||||||
|
border.width: 1
|
||||||
|
TextInput {
|
||||||
|
id: actionPanelPasswordField
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.margins: 12
|
||||||
|
font.pixelSize: 13
|
||||||
|
color: Theme.textPrimary
|
||||||
|
verticalAlignment: TextInput.AlignVCenter
|
||||||
|
clip: true
|
||||||
|
selectByMouse: true
|
||||||
|
activeFocusOnTab: true
|
||||||
|
inputMethodHints: Qt.ImhNone
|
||||||
|
echoMode: TextInput.Password
|
||||||
|
onAccepted: {
|
||||||
|
// Connect with the entered password
|
||||||
|
wifiLogic.pendingConnect = {ssid: modelData.ssid, security: modelData.security, password: text};
|
||||||
|
wifiLogic.doConnect();
|
||||||
|
|
||||||
|
wifiLogic.actionPanelSsid = ""; // Close the panel
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Connect/Disconnect button
|
||||||
|
Rectangle {
|
||||||
|
Layout.preferredWidth: 80
|
||||||
|
Layout.preferredHeight: 36
|
||||||
|
radius: 18
|
||||||
|
color: modelData.connected ? Theme.error : Theme.accentPrimary
|
||||||
|
border.color: modelData.connected ? Theme.error : Theme.accentPrimary
|
||||||
|
border.width: 0
|
||||||
|
opacity: 1.0
|
||||||
|
Behavior on color { ColorAnimation { duration: 100 } }
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
onClicked: {
|
||||||
|
if (modelData.connected) {
|
||||||
|
// Disconnect from network
|
||||||
|
wifiLogic.disconnectNetwork(modelData.ssid);
|
||||||
|
} else {
|
||||||
|
// For secured networks, check if we need password
|
||||||
|
if (wifiLogic.isSecured(modelData.security) && !modelData.existing) {
|
||||||
|
// If password field is visible and has content, use it
|
||||||
|
if (actionPanelPasswordField.text.length > 0) {
|
||||||
|
wifiLogic.pendingConnect = {ssid: modelData.ssid, security: modelData.security, password: actionPanelPasswordField.text};
|
||||||
|
wifiLogic.doConnect();
|
||||||
|
}
|
||||||
|
// For new networks without password entered, we might want to show an error or handle differently
|
||||||
|
// For now, we'll just close the panel
|
||||||
|
} else {
|
||||||
|
// Connect to open network
|
||||||
|
wifiLogic.connectNetwork(modelData.ssid, modelData.security);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
wifiLogic.actionPanelSsid = ""; // Close the panel
|
||||||
|
}
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
hoverEnabled: true
|
||||||
|
onEntered: parent.color = modelData.connected ? Qt.darker(Theme.error, 1.1) : Qt.darker(Theme.accentPrimary, 1.1)
|
||||||
|
onExited: parent.color = modelData.connected ? Theme.error : Theme.accentPrimary
|
||||||
|
}
|
||||||
|
Text {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
text: modelData.connected ? "wifi_off" : "check"
|
||||||
|
font.family: "Material Symbols Outlined"
|
||||||
|
font.pixelSize: 20
|
||||||
|
color: Theme.backgroundPrimary
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue