feat(control-center/network): add provisory support for wireless connections

I call it "provisory" because I don't have a Wi-Fi card to test it, but in theory, everything should work
This commit is contained in:
retrozinndev
2025-06-19 19:10:25 -03:00
parent ca31a64234
commit e02f96b151
3 changed files with 146 additions and 48 deletions
+2 -4
View File
@@ -1,9 +1,7 @@
import { exec, execAsync, Gio, GLib } from "astal"; import { exec, execAsync, Gio, GLib } from "astal";
export function getDecoded(text: (Uint8Array)): string { export const decoder = new TextDecoder("utf-8"),
const decoder = new TextDecoder('utf-8'); encoder = new TextEncoder();
return decoder.decode(text);
}
export function getHyprlandInstanceSig(): (string|null) { export function getHyprlandInstanceSig(): (string|null) {
return GLib.getenv("HYPRLAND_INSTANCE_SIGNATURE"); return GLib.getenv("HYPRLAND_INSTANCE_SIGNATURE");
+20 -20
View File
@@ -1,26 +1,26 @@
// SCSS Variables // SCSS Variables
// Generated by 'wal' // Generated by 'wal'
$wallpaper: "/home/joaov/wallpapers/Frieren Blue.jpeg"; $wallpaper: "/home/joaov/wallpapers/Frieren Rain.jpg";
// Special // Special
$background: #22303d; $background: #25301c;
$foreground: #c7cbce; $foreground: #c8cbc6;
$cursor: #c7cbce; $cursor: #c8cbc6;
// Colors // Colors
$color0: #22303d; $color0: #25301c;
$color1: #9f847e; $color1: #6c8251;
$color2: #aa9381; $color2: #7c9357;
$color3: #7b8c92; $color3: #78846e;
$color4: #78999c; $color4: #948a88;
$color5: #76a3a9; $color5: #899869;
$color6: #78a4aa; $color6: #98a27b;
$color7: #9ca1a5; $color7: #9ba197;
$color8: #6b7883; $color8: #707c66;
$color9: #D5B1A9; $color9: #91AE6D;
$color10: #E3C5AD; $color10: #A6C574;
$color11: #A5BBC3; $color11: #A0B093;
$color12: #A1CDD0; $color12: #C6B9B6;
$color13: #9EDAE2; $color13: #B7CB8D;
$color14: #A1DBE3; $color14: #CBD9A4;
$color15: #c7cbce; $color15: #c8cbc6;
+124 -24
View File
@@ -1,11 +1,15 @@
import { Gtk, Widget } from "astal/gtk3"; import { Gtk, Widget } from "astal/gtk3";
import { Page, PageButton } from "./Page"; import { Page, PageButton } from "./Page";
import AstalNetwork from "gi://AstalNetwork"; import AstalNetwork from "gi://AstalNetwork";
import { bind } from "astal"; import { bind, GLib } from "astal";
import NM from "gi://NM"; import NM from "gi://NM";
import { Windows } from "../../../windows"; import { Windows } from "../../../windows";
import { tr } from "../../../i18n/intl"; import { tr } from "../../../i18n/intl";
import { execApp } from "../../../scripts/apps"; import { execApp } from "../../../scripts/apps";
import { EntryPopup, EntryPopupProps } from "../../EntryPopup";
import { Notifications } from "../../../scripts/notifications";
import { AskPopup, AskPopupProps } from "../../AskPopup";
import { encoder } from "../../../scripts/utils";
export const PageNetwork: (() => Page) = () => new Page({ export const PageNetwork: (() => Page) = () => new Page({
id: "network", id: "network",
@@ -77,30 +81,126 @@ export const PageNetwork: (() => Page) = () => new Page({
visible: bind(AstalNetwork.get_default(), "primary").as((primary) => primary === AstalNetwork.Primary.WIFI), visible: bind(AstalNetwork.get_default(), "primary").as((primary) => primary === AstalNetwork.Primary.WIFI),
hexpand: true, hexpand: true,
orientation: Gtk.Orientation.VERTICAL, orientation: Gtk.Orientation.VERTICAL,
children: AstalNetwork.get_default().wifi ? bind(AstalNetwork.get_default().wifi.get_device(), "accessPoints").as((aps) => children: AstalNetwork.get_default().wifi ? bind(AstalNetwork.get_default().wifi, "accessPoints").as((aps) => [
aps.map(ap => new Widget.Button({ new Widget.Label({
hexpand: true, className: "sub-header",
onClick: () => console.log("connect to " + ap.get_ssid().toArray().toString()), // TODO I don't have a WiFi board :( label: "Wi-Fi"
child: new Widget.Box({ } as Widget.LabelProps),
hexpand: true, ...aps.filter(ap => ap.ssid).map(ap => PageButton({
children: [ className: bind(AstalNetwork.get_default().wifi, "activeAccessPoint").as(activeAP =>
new Widget.Icon({ activeAP.ssid === ap.ssid ? "active" : ""),
halign: Gtk.Align.START, title: bind(ap, "ssid").as(ssid =>
className: "icon", ssid ?? "Unknown SSID"),
icon: "network-wireless-signal-excellent-symbolic" icon: bind(ap, "iconName"),
} as Widget.IconProps), endWidget: new Widget.Icon({
new Widget.Label({ icon: bind(ap, "flags").as(flags => flags & NM.__80211ApFlags.PRIVACY ?
className: "ssid", "channel-secure-symbolic"
halign: Gtk.Align.START, : "channel-insecure-symbolic"),
label: (getDecoded(ap.ssid.get_data()) ?? ap.ssid.get_data().toString()) ?? "Wi-Fi" css: "font-size: 18px;"
} as Widget.LabelProps), } as Widget.IconProps),
new Widget.Label({ extraButtons: [
className: "status", new Widget.Button({
} as Widget.LabelProps) image: new Widget.Icon({
] icon: "window-close-symbolic",
} as Widget.BoxProps) css: "font-size: 18px;"
} as Widget.ButtonProps))) : [], } as Widget.IconProps)
} as Widget.ButtonProps)
],
onClick: () => {
const ssid: string = ap.ssid ?? "Unknown SSID",
ssidBytes = GLib.Bytes.new(encoder.encode(ssid));
const connection = new NM.Connection();
const setting = NM.SettingWireless.new();
setting.ssid = ssidBytes;
setting.bssid = ap.bssid;
connection.add_setting(setting);
// Check if access point has encryption(needs a password)
if(ap.flags & NM.__80211ApFlags.PRIVACY) {
const passwdPopup = EntryPopup({
isPassword: true,
title: `${tr("connect")}: ${ssid}`,
acceptText: tr("connect"),
closeOnAccept: false,
text: `Input password for ${ssid}`,
onAccept: (input) => {
const pskSetting = NM.SettingWirelessSecurity.new();
pskSetting.keyMgmt = "wpa-psk";
if(ap.flags & NM.__80211ApSecurityFlags.KEY_MGMT_SAE)
pskSetting.keyMgmt = "sae";
pskSetting.psk = input;
AstalNetwork.get_default().get_client().add_connection_async(
connection, true, null, (client, asyncRes) => {
const remoteConnection = client!.add_connection_finish(asyncRes);
if(!remoteConnection) {
notifyConnectionError(ssid);
return;
}
passwdPopup.close();
saveToDisk(remoteConnection, ssid);
}
);
},
} as EntryPopupProps);
return;
}
AstalNetwork.get_default().get_client().add_connection_async(connection, false, null, (_, asyncRes) => {
const remoteConnection = AstalNetwork.get_default().get_client().add_connection_finish(asyncRes);
if(!remoteConnection) {
notifyConnectionError(ssid);
return;
}
activateWirelessConnection(remoteConnection, ssid);
});
}
}))
]
) : [],
} as Widget.BoxProps) } as Widget.BoxProps)
] ]
}); });
function activateWirelessConnection(connection: NM.RemoteConnection, ssid: string): void {
AstalNetwork.get_default().get_client().activate_connection_async(
connection, AstalNetwork.get_default().wifi.get_device(), null, null, (_, asyncRes) => {
const activeConnection = AstalNetwork.get_default().get_client().activate_connection_finish(asyncRes);
if(!activeConnection) {
Notifications.getDefault().sendNotification({
appName: "network",
summary: "Couldn't activate wireless connection",
body: `An error occurred while activating the wireless connection "${ssid}"`
});
return;
}
}
);
}
function notifyConnectionError(ssid: string): void {
Notifications.getDefault().sendNotification({
appName: "network",
summary: "Coudn't connect Wi-Fi",
body: `An error occurred while trying to connect to the "${ssid}" access point. \nMaybe the password is invalid?`
});
}
function saveToDisk(remoteConnection: NM.RemoteConnection, ssid: string): void {
AskPopup({
text: `Save password for connection "${ssid}"?`,
acceptText: "Yes",
onAccept: () => remoteConnection.commit_changes_async(true, null, (_, asyncRes) =>
!remoteConnection.commit_changes_finish(asyncRes) && Notifications.getDefault().sendNotification({
appName: "network",
summary: "Couldn't save Wi-Fi password",
body: `An error occurred while trying to write the password for "${ssid}" to disk`
}))
} as AskPopupProps);
}