feat(networking): refactor NetworkService to use Quickshell.Networking and improve state synchronization

- Use Quickshell.Networking for wifiEnabled
- Improve state synchronization between processes
- Fix race conditions in Wi-Fi scanning
- Ensure proper startup initialization
- Another icon fix xD (4th one)
- Merge deviceStatus with wifiDetails

Co-Authored-By: notiant <notiant@users.noreply.github.com>
This commit is contained in:
Turann_
2026-03-22 22:34:37 +03:00
parent 5eaefd8bfd
commit 849bf16388
9 changed files with 555 additions and 1163 deletions
-1
View File
@@ -378,7 +378,6 @@
"indicatorOpacity": 0.6
},
"network": {
"wifiEnabled": true,
"airplaneModeEnabled": false,
"bluetoothRssiPollingEnabled": false,
"bluetoothRssiPollIntervalMs": 60000,
-1
View File
@@ -575,7 +575,6 @@ Singleton {
// network
property JsonObject network: JsonObject {
property bool wifiEnabled: true
property bool airplaneModeEnabled: false
property bool bluetoothRssiPollingEnabled: false // Opt-in Bluetooth RSSI polling (uses bluetoothctl)
property int bluetoothRssiPollIntervalMs: 60000 // Polling interval in milliseconds for RSSI queries
+5 -5
View File
@@ -46,9 +46,9 @@ Item {
model: [
{
"label": Settings.data.network.wifiEnabled ? I18n.tr("actions.disable-wifi") : I18n.tr("actions.enable-wifi"),
"label": NetworkService.wifiEnabled ? I18n.tr("actions.disable-wifi") : I18n.tr("actions.enable-wifi"),
"action": "toggle-wifi",
"icon": Settings.data.network.wifiEnabled ? "wifi-off" : "wifi",
"icon": NetworkService.wifiEnabled ? "wifi-off" : "wifi",
"enabled": !Settings.data.network.airplaneModeEnabled && NetworkService.wifiAvailable
},
{
@@ -68,7 +68,7 @@ Item {
PanelService.closeContextMenu(screen);
if (action === "toggle-wifi") {
NetworkService.setWifiEnabled(!Settings.data.network.wifiEnabled);
NetworkService.setWifiEnabled(!NetworkService.wifiEnabled);
} else if (action === "wifi-settings") {
SettingsPanelService.openToTab(SettingsPanel.Tab.Connections, 0, screen);
} else if (action === "widget-settings") {
@@ -84,7 +84,7 @@ Item {
customIconColor: Color.resolveColorKeyOptional(root.iconColorKey)
customTextColor: Color.resolveColorKeyOptional(root.textColorKey)
icon: NetworkService.getIcon()
text: NetworkService.getStatusText()
text: NetworkService.getStatusText(false)
autoHide: false
forceOpen: !isBarVertical && root.displayMode === "alwaysShow"
forceClose: isBarVertical || root.displayMode === "alwaysHide" || text === ""
@@ -99,7 +99,7 @@ Item {
if (PanelService.getPanel("networkPanel", screen)?.isPanelOpen) {
return "";
}
return pill.text; // pill.text is exact copy of getStatusText
return NetworkService.getStatusText(true);
}
}
}
@@ -8,14 +8,14 @@ import qs.Widgets
NIconButtonHot {
property ShellScreen screen
icon: NetworkService.getIcon()
tooltipText: NetworkService.getStatusText()
tooltipText: NetworkService.getStatusText(true)
onClicked: {
var panel = PanelService.getPanel("networkPanel", screen);
panel?.toggle(this);
}
onRightClicked: {
if (!Settings.data.network.airplaneModeEnabled) {
NetworkService.setWifiEnabled(!Settings.data.network.wifiEnabled);
NetworkService.setWifiEnabled(!NetworkService.wifiEnabled);
}
}
}
+16 -15
View File
@@ -34,14 +34,12 @@ SmartPanel {
}
if (panelViewMode === "wifi") {
ethernetInfoExpanded = false;
if (Settings.data.network.wifiEnabled && !NetworkService.scanning) {
if (NetworkService.wifiEnabled && !NetworkService.scanningActive) {
NetworkService.scan();
}
} else {
if (NetworkService.ethernetConnected) {
NetworkService.refreshActiveEthernetDetails();
} else {
NetworkService.refreshEthernet();
}
}
}
@@ -66,13 +64,13 @@ SmartPanel {
// Restore last view if valid, otherwise choose what's available (prefer WiFi when both exist)
if (Settings.data.network.networkPanelView) {
const last = Settings.data.network.networkPanelView;
if (last === "ethernet" && NetworkService.hasEthernet()) {
if (last === "ethernet" && NetworkService.ethernetAvailable) {
panelViewMode = "ethernet";
} else {
panelViewMode = "wifi";
}
} else {
if (!Settings.data.network.wifiEnabled && NetworkService.hasEthernet()) {
if (!NetworkService.wifiEnabled && NetworkService.ethernetAvailable) {
panelViewMode = "ethernet";
} else {
panelViewMode = "wifi";
@@ -106,11 +104,11 @@ SmartPanel {
RowLayout {
NIcon {
id: modeIcon
icon: panelViewMode === "wifi" ? (Settings.data.network.wifiEnabled ? "wifi" : "wifi-off") : (NetworkService.hasEthernet() ? (NetworkService.ethernetConnected ? "ethernet" : "ethernet") : "ethernet-off")
icon: panelViewMode === "wifi" ? (NetworkService.wifiEnabled ? "wifi" : "wifi-off") : (NetworkService.ethernetAvailable ? (NetworkService.ethernetConnected ? "ethernet" : "ethernet") : "ethernet-off")
pointSize: Style.fontSizeXXL
color: {
if (panelViewMode === "wifi") {
return Settings.data.network.wifiEnabled ? Color.mPrimary : Color.mOnSurfaceVariant;
return NetworkService.wifiEnabled ? Color.mPrimary : Color.mOnSurfaceVariant;
} else {
return NetworkService.ethernetConnected ? Color.mPrimary : Color.mOnSurfaceVariant;
}
@@ -120,7 +118,7 @@ SmartPanel {
hoverEnabled: true
onClicked: {
if (panelViewMode === "wifi") {
if (NetworkService.hasEthernet()) {
if (NetworkService.ethernetAvailable) {
panelViewMode = "ethernet";
} else {
TooltipService.show(parent, I18n.tr("wifi.panel.no-ethernet-devices"));
@@ -142,7 +140,7 @@ SmartPanel {
NToggle {
id: wifiSwitch
visible: panelViewMode === "wifi"
checked: Settings.data.network.wifiEnabled
checked: NetworkService.wifiEnabled
enabled: !Settings.data.network.airplaneModeEnabled && NetworkService.wifiAvailable
onToggled: checked => NetworkService.setWifiEnabled(checked)
baseSize: Style.baseWidgetSize * 0.7 // Slightly smaller
@@ -166,7 +164,7 @@ SmartPanel {
// Mode switch (WiFi / Ethernet)
NTabBar {
id: modeTabBar
visible: NetworkService.hasEthernet()
visible: NetworkService.ethernetAvailable && NetworkService.wifiAvailable
margins: Style.marginS
Layout.fillWidth: true
spacing: Style.marginM
@@ -180,12 +178,14 @@ SmartPanel {
text: I18n.tr("common.wifi")
tabIndex: 0
checked: modeTabBar.currentIndex === 0
enabled: NetworkService.wifiAvailable
}
NTabButton {
text: I18n.tr("common.ethernet")
tabIndex: 1
checked: modeTabBar.currentIndex === 1
enabled: NetworkService.ethernetAvailable
}
}
}
@@ -254,7 +254,7 @@ SmartPanel {
// WiFi disabled state
NBox {
id: disabledBox
visible: panelViewMode === "wifi" && !Settings.data.network.wifiEnabled
visible: panelViewMode === "wifi" && !NetworkService.wifiEnabled
Layout.fillWidth: true
Layout.preferredHeight: disabledColumn.implicitHeight + Style.margin2M
@@ -300,7 +300,7 @@ SmartPanel {
// Scanning state (show when no networks and we haven't had any yet)
NBox {
id: scanningBox
visible: panelViewMode === "wifi" && Settings.data.network.wifiEnabled && Object.keys(NetworkService.networks).length === 0 && NetworkService.scanning
visible: panelViewMode === "wifi" && NetworkService.wifiEnabled && Object.keys(NetworkService.networks).length === 0 && NetworkService.scanningActive
Layout.fillWidth: true
Layout.preferredHeight: scanningColumn.implicitHeight + Style.margin2M
@@ -337,7 +337,7 @@ SmartPanel {
// Empty state when no networks (only show after we've had networks before, meaning a real empty result)
NBox {
id: emptyBox
visible: panelViewMode === "wifi" && Settings.data.network.wifiEnabled && !NetworkService.scanning && Object.keys(NetworkService.networks).length === 0 && !NetworkService.scanning
visible: panelViewMode === "wifi" && NetworkService.wifiEnabled && Object.keys(NetworkService.networks).length === 0 && !NetworkService.scanningActive
Layout.fillWidth: true
Layout.preferredHeight: emptyColumn.implicitHeight + Style.margin2M
@@ -352,7 +352,7 @@ SmartPanel {
}
NIcon {
icon: "search"
icon: "wifi-question"
pointSize: 48
color: Color.mOnSurfaceVariant
Layout.alignment: Qt.AlignHCenter
@@ -374,7 +374,7 @@ SmartPanel {
// Networks list container (WiFi)
ColumnLayout {
id: networksList
visible: panelViewMode === "wifi" && Settings.data.network.wifiEnabled && Object.keys(NetworkService.networks).length > 0
visible: panelViewMode === "wifi" && NetworkService.wifiEnabled && Object.keys(NetworkService.networks).length > 0
width: parent.width
spacing: Style.marginM
@@ -578,6 +578,7 @@ SmartPanel {
colorBorder: "transparent"
colorBorderHover: "transparent"
enabled: true
visible: NetworkService.ethernetConnected
onClicked: {
if (NetworkService.activeEthernetIf === modelData.ifname && ethernetInfoExpanded) {
ethernetInfoExpanded = false;
@@ -49,37 +49,38 @@ Item {
readonly property var activeNetworks: (passwordSsid && passwordSsid.length > 0) ? Object.values(cachedNetworks) : Object.values(NetworkService.networks)
readonly property var connectedNetworks: {
if (!Settings.data.network.wifiEnabled) {
if (!NetworkService.wifiEnabled) {
return [];
}
return activeNetworks.filter(n => n.connected).sort((a, b) => b.signal - a.signal);
}
readonly property var savedNetworks: {
if (!Settings.data.network.wifiEnabled) {
if (!NetworkService.wifiEnabled) {
return [];
}
return activeNetworks.filter(n => !n.connected && (n.existing || n.cached)).sort((a, b) => b.signal - a.signal);
return activeNetworks.filter(n => !n.connected && n.existing).sort((a, b) => b.signal - a.signal);
}
readonly property var availableNetworks: {
if (!Settings.data.network.wifiEnabled) {
if (!NetworkService.wifiEnabled) {
return [];
}
return activeNetworks.filter(n => !n.connected && !n.existing && !n.cached).sort((a, b) => b.signal - a.signal);
return activeNetworks.filter(n => !n.connected && !n.existing).sort((a, b) => b.signal - a.signal);
}
// Combined visibility check: tab must be visible AND the window must be visible
readonly property bool effectivelyVisible: root.visible && Window.window && Window.window.visible
onEffectivelyVisibleChanged: {
if (effectivelyVisible && Settings.data.network.wifiEnabled && !showOnlyLists) {
NetworkService.scan();
}
if (effectivelyVisible) {
SystemStatService.registerComponent("wifi-subtab");
if (NetworkService.wifiEnabled && !NetworkService.scanningActive) {
NetworkService.scan();
}
} else {
SystemStatService.unregisterComponent("wifi-subtab");
NetworkService.scanningActive = false;
}
}
@@ -91,6 +92,9 @@ Item {
Component.onDestruction: {
SystemStatService.unregisterComponent("wifi-subtab");
if (effectivelyVisible) {
NetworkService.scanningActive = false;
}
}
// Actions
@@ -151,8 +155,8 @@ Item {
NToggle {
label: I18n.tr("common.wifi")
icon: NetworkService.getIcon(false)
checked: Settings.data.network.wifiEnabled
icon: NetworkService.wifiEnabled ? "wifi" : "wifi-off"
checked: NetworkService.wifiEnabled
onToggled: checked => NetworkService.setWifiEnabled(checked)
enabled: ProgramCheckerService.nmcliAvailable && !Settings.data.network.airplaneModeEnabled && NetworkService.wifiAvailable
Layout.alignment: Qt.AlignVCenter
@@ -161,11 +165,11 @@ Item {
NDivider {
Layout.fillWidth: true
visible: Settings.data.network.wifiEnabled && root.connectedNetworks.length > 0
visible: NetworkService.wifiEnabled
}
NText {
visible: !root.showOnlyLists && Settings.data.network.wifiEnabled
visible: !root.showOnlyLists && NetworkService.wifiEnabled
Layout.fillWidth: true
text: I18n.tr("panels.connections.wifi-header-text")
color: Color.mOnSurfaceVariant
@@ -184,7 +188,7 @@ Item {
// Network List [1] (Connected)
NBox {
id: connectedBox
visible: root.connectedNetworks.length > 0 && Settings.data.network.wifiEnabled
visible: root.connectedNetworks.length > 0 && NetworkService.wifiEnabled
Layout.fillWidth: true
Layout.preferredHeight: connectedCol.implicitHeight + Style.margin2M
border.color: showOnlyLists ? Style.boxBorderColor : "transparent"
@@ -215,7 +219,7 @@ Item {
// Network List [2] (Saved)
NBox {
id: savedBox
visible: root.savedNetworks.length > 0 && Settings.data.network.wifiEnabled
visible: root.savedNetworks.length > 0 && NetworkService.wifiEnabled
Layout.fillWidth: true
Layout.preferredHeight: savedCol.implicitHeight + Style.margin2M
border.color: showOnlyLists ? Style.boxBorderColor : "transparent"
@@ -246,7 +250,7 @@ Item {
// Network List [3] (Available)
NBox {
id: availableBox
visible: root.availableNetworks.length > 0 && Settings.data.network.wifiEnabled
visible: root.availableNetworks.length > 0 && NetworkService.wifiEnabled
Layout.fillWidth: true
Layout.preferredHeight: availableCol.implicitHeight + Style.margin2M
border.color: showOnlyLists ? Style.boxBorderColor : "transparent"
@@ -272,15 +276,6 @@ Item {
}
}
// Auto-scan timer when panel is visible
Timer {
id: autoScanTimer
interval: 5000
running: root.effectivelyVisible && Settings.data.network.wifiEnabled
repeat: true
onTriggered: NetworkService.scan()
}
Repeater {
model: root.availableNetworks
delegate: nboxDelegate
@@ -331,7 +326,7 @@ Item {
}
Item {
visible: !showOnlyLists && Settings.data.network.wifiEnabled
visible: !showOnlyLists && NetworkService.wifiEnabled
Layout.fillWidth: true
}
@@ -450,12 +445,12 @@ Item {
onTextChanged: addNetworkPopup.customSsid = text
onEditingFinished: {
if (addNetworkPopup.customSsid.length > 0 && (addNetworkPopup.customSecurityKey === "open" || addNetworkPopup.customPassword.length > 0)) {
NetworkService.connectManual(addNetworkPopup.customSsid, addNetworkPopup.customPassword, addNetworkPopup.customSecurityKey, addNetworkPopup.customIdentity, {
eap: addNetworkPopup.customEnterpriseEap,
phase2: addNetworkPopup.customEnterprisePhase2,
anonIdentity: addNetworkPopup.customEnterpriseAnonIdentity,
caCert: addNetworkPopup.customEnterpriseCaCert
}, addNetworkPopup.customIsHidden);
NetworkService.connect(addNetworkPopup.customSsid, addNetworkPopup.customPassword, addNetworkPopup.customIsHidden, addNetworkPopup.customSecurityKey, addNetworkPopup.customIdentity, {
eap: addNetworkPopup.customEnterpriseEap,
phase2: addNetworkPopup.customEnterprisePhase2,
anonIdentity: addNetworkPopup.customEnterpriseAnonIdentity,
caCert: addNetworkPopup.customEnterpriseCaCert
});
addNetworkPopup.close();
}
}
@@ -574,12 +569,12 @@ Item {
inputItem.echoMode: addNetworkPopup.customShowPassword ? TextInput.Normal : TextInput.Password
onEditingFinished: {
if (addNetworkPopup.customSsid.length > 0 && addNetworkPopup.customPassword.length > 0) {
NetworkService.connectManual(addNetworkPopup.customSsid, addNetworkPopup.customPassword, addNetworkPopup.customSecurityKey, addNetworkPopup.customIdentity, {
eap: addNetworkPopup.customEnterpriseEap,
phase2: addNetworkPopup.customEnterprisePhase2,
anonIdentity: addNetworkPopup.customEnterpriseAnonIdentity,
caCert: addNetworkPopup.customEnterpriseCaCert
}, addNetworkPopup.customIsHidden);
NetworkService.connect(addNetworkPopup.customSsid, addNetworkPopup.customPassword, addNetworkPopup.customIsHidden, addNetworkPopup.customSecurityKey, addNetworkPopup.customIdentity, {
eap: addNetworkPopup.customEnterpriseEap,
phase2: addNetworkPopup.customEnterprisePhase2,
anonIdentity: addNetworkPopup.customEnterpriseAnonIdentity,
caCert: addNetworkPopup.customEnterpriseCaCert
});
addNetworkPopup.close();
}
}
@@ -629,12 +624,12 @@ Item {
textColor: Color.mOnPrimary
enabled: addNetworkPopup.customSsid.length > 0 && (addNetworkPopup.customSecurityKey === "open" || addNetworkPopup.customPassword.length > 0) && (addNetworkPopup.customSecurityKey.indexOf("-eap") === -1 || addNetworkPopup.customIdentity.length > 0)
onClicked: {
NetworkService.connectManual(addNetworkPopup.customSsid, addNetworkPopup.customPassword, addNetworkPopup.customSecurityKey, addNetworkPopup.customIdentity, {
eap: addNetworkPopup.customEnterpriseEap,
phase2: addNetworkPopup.customEnterprisePhase2,
anonIdentity: addNetworkPopup.customEnterpriseAnonIdentity,
caCert: addNetworkPopup.customEnterpriseCaCert
}, addNetworkPopup.customIsHidden);
NetworkService.connect(addNetworkPopup.customSsid, addNetworkPopup.customPassword, addNetworkPopup.customIsHidden, addNetworkPopup.customSecurityKey, addNetworkPopup.customIdentity, {
eap: addNetworkPopup.customEnterpriseEap,
phase2: addNetworkPopup.customEnterprisePhase2,
anonIdentity: addNetworkPopup.customEnterpriseAnonIdentity,
caCert: addNetworkPopup.customEnterpriseCaCert
});
addNetworkPopup.close();
}
}
@@ -745,9 +740,6 @@ Item {
return NetworkService.networkConnectivity;
}
}
if (modelData.cached && !modelData.existing) {
return I18n.tr("wifi.panel.saved");
}
return NetworkService.isSecured(modelData.security) ? modelData.security : I18n.tr("wifi.panel.security-open");
}
pointSize: Style.fontSizeXXS
@@ -834,7 +826,7 @@ Item {
}
NIconButton {
visible: !root.showOnlyLists && (modelData.existing || modelData.cached) && !modelData.connected && !networkItem.isBusy
visible: !root.showOnlyLists && modelData.existing && !modelData.connected && !networkItem.isBusy
icon: "trash"
tooltipText: I18n.tr("tooltips.forget-network")
baseSize: Style.baseWidgetSize * 0.75
@@ -854,7 +846,7 @@ Item {
textColor: Color.mOnPrimary
text: I18n.tr("common.connect")
onClicked: {
if (modelData.existing || modelData.cached || !NetworkService.isSecured(modelData.security)) {
if (modelData.existing || !NetworkService.isSecured(modelData.security)) {
NetworkService.connect(modelData.ssid);
} else {
root.requestPassword(modelData.ssid);
+1 -1
View File
@@ -622,7 +622,7 @@ Singleton {
IpcHandler {
target: "wifi"
function toggle() {
NetworkService.setWifiEnabled(!Settings.data.network.wifiEnabled);
NetworkService.setWifiEnabled(!NetworkService.wifiEnabled);
}
function enable() {
NetworkService.setWifiEnabled(true);
File diff suppressed because it is too large Load Diff
+1 -3
View File
@@ -17,7 +17,6 @@ Singleton {
property bool gnomeCalendarAvailable: false
property bool pythonAvailable: false
property bool wtypeAvailable: false
property bool ethtoolAvailable: false
// Programs to check - maps property names to commands
readonly property var programsToCheck: ({
@@ -26,8 +25,7 @@ Singleton {
"wlsunsetAvailable": ["sh", "-c", "command -v wlsunset"],
"gnomeCalendarAvailable": ["sh", "-c", "command -v gnome-calendar"],
"wtypeAvailable": ["sh", "-c", "command -v wtype"],
"pythonAvailable": ["sh", "-c", "command -v python3"],
"ethtoolAvailable": ["sh", "-c", "command -v ethtool"]
"pythonAvailable": ["sh", "-c", "command -v python3"]
})
// Discord client auto-detection