nosignal-shell/utils/NetworkConnection.qml
28allday 152923f5d8 fold: bake NoSignal shell patches into the fork (Phase 2)
We now own the pinned shell, so the runtime patches that the pacman-hook +
patch-script machinery re-applied after every caelestia-shell upgrade are
baked directly into source. Folds in all five caelestia-shell-targeting
patches from the builder's `os updates/`:

- Lock PAM (F1, blocker): ship faillock-free assets/pam.d/caelestia and point
  modules/lock/Pam.qml's passwd PamContext at it (config "passwd" -> "caelestia").
  A desktop screen-lock must never lock the user out of their own session.
- Updates page: add modules/nexus/pages/UpdatesPage.qml + register it in the
  first System slot of PageCompRegistry.qml.
- Additions page: add modules/nexus/pages/AdditionsPage.qml + register it in the
  Plugins slot; relabel Plugins -> Additions in PageRegistry.qml.
- Sudo toggle: add modules/nexus/common/SudoToggleRow.qml + insert it into
  ServicesPage.qml after the Smart colour scheme toggle.
- Wi-Fi wrong-password recovery: NetworkConnection.qml saved-profile branch now
  passes a real callback that forgets the bad profile and reopens the dialog.

The builder will drop the corresponding patch-*.sh calls + pacman hooks and
bump NOSIGNAL_SHELL_COMMIT to this commit.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-13 22:08:59 +01:00

137 lines
5.6 KiB
QML

pragma Singleton
import QtQuick
import qs.services
/**
* NetworkConnection
*
* Centralized utility for network connection logic. Provides a single source of truth
* for connecting to wireless networks, eliminating code duplication across
* controlcenter components and bar popouts.
*
* Usage:
* ```qml
* import qs.utils
*
* // With Session object (controlcenter)
* NetworkConnection.handleConnect(network, session);
*
* // Without Session object (bar popouts) - provide password dialog callback
* NetworkConnection.handleConnect(network, null, (network) => {
* // Show password dialog
* root.passwordNetwork = network;
* root.showPasswordDialog = true;
* });
* ```
*/
QtObject {
id: root
/**
* Handle network connection with automatic disconnection if needed.
* If there's an active network different from the target, disconnects first,
* then connects to the target network.
*
* @param network The network object to connect to (must have ssid property)
* @param session Optional Session object (for controlcenter - must have network property with showPasswordDialog and pendingNetwork)
* @param onPasswordNeeded Optional callback function(network) called when password is needed (for bar popouts)
*/
function handleConnect(network, session, onPasswordNeeded): void {
if (!network) {
return;
}
if (Nmcli.active && Nmcli.active.ssid !== network.ssid) {
Nmcli.disconnectFromNetwork();
Qt.callLater(() => {
root.connectToNetwork(network, session, onPasswordNeeded);
});
} else {
root.connectToNetwork(network, session, onPasswordNeeded);
}
}
/**
* Connect to a wireless network.
* Handles both secured and open networks, checks for saved profiles,
* and shows password dialog if needed.
*
* @param network The network object to connect to (must have ssid, isSecure, bssid properties)
* @param session Optional Session object (for controlcenter - must have network property with showPasswordDialog and pendingNetwork)
* @param onPasswordNeeded Optional callback function(network) called when password is needed (for bar popouts)
*/
function connectToNetwork(network, session, onPasswordNeeded): void {
if (!network) {
return;
}
if (network.isSecure) {
const hasSavedProfile = Nmcli.hasSavedProfile(network.ssid);
if (hasSavedProfile) {
// NoSignal wifi-password-retry: a saved profile can hold a wrong
// password. Stock code passed a null callback here, so a failed
// activation never re-prompted — the dialog never reopened. On
// auth failure: forget the bad profile and reopen the dialog
// (same cleanup + dialog path the no-saved-profile branch uses).
Nmcli.connectToNetwork(network.ssid, "", network.bssid, result => {
if (result && result.needsPassword) {
if (Nmcli.pendingConnection) {
Nmcli.connectionCheckTimer.stop();
Nmcli.immediateCheckTimer.stop();
Nmcli.immediateCheckTimer.checkCount = 0;
Nmcli.pendingConnection = null;
}
Nmcli.forgetNetwork(network.ssid);
if (session && session.network) {
session.network.showPasswordDialog = true;
session.network.pendingNetwork = network;
} else if (onPasswordNeeded) {
onPasswordNeeded(network);
}
}
});
} else {
// Use password check with callback
Nmcli.connectToNetworkWithPasswordCheck(network.ssid, network.isSecure, result => {
if (result.needsPassword) {
// Clear pending connection if exists
if (Nmcli.pendingConnection) {
Nmcli.connectionCheckTimer.stop();
Nmcli.immediateCheckTimer.stop();
Nmcli.immediateCheckTimer.checkCount = 0;
Nmcli.pendingConnection = null;
}
// Handle password dialog - use session if available, otherwise use callback
if (session && session.network) {
session.network.showPasswordDialog = true;
session.network.pendingNetwork = network;
} else if (onPasswordNeeded) {
onPasswordNeeded(network);
}
}
}, network.bssid);
}
} else {
Nmcli.connectToNetwork(network.ssid, "", network.bssid, null);
}
}
/**
* Connect to a wireless network with a provided password.
* Used by password dialogs when the user has already entered a password.
*
* @param network The network object to connect to (must have ssid, bssid properties)
* @param password The password to use for connection
* @param onResult Optional callback function(result) called with connection result
*/
function connectWithPassword(network, password, onResult): void {
if (!network) {
return;
}
Nmcli.connectToNetwork(network.ssid, password || "", network.bssid || "", onResult || null);
}
}