✨ 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:
@@ -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
@@ -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;
|
||||||
|
|||||||
@@ -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({
|
|
||||||
hexpand: true,
|
|
||||||
onClick: () => console.log("connect to " + ap.get_ssid().toArray().toString()), // TODO I don't have a WiFi board :(
|
|
||||||
child: new Widget.Box({
|
|
||||||
hexpand: true,
|
|
||||||
children: [
|
|
||||||
new Widget.Icon({
|
|
||||||
halign: Gtk.Align.START,
|
|
||||||
className: "icon",
|
|
||||||
icon: "network-wireless-signal-excellent-symbolic"
|
|
||||||
} as Widget.IconProps),
|
|
||||||
new Widget.Label({
|
new Widget.Label({
|
||||||
className: "ssid",
|
className: "sub-header",
|
||||||
halign: Gtk.Align.START,
|
label: "Wi-Fi"
|
||||||
label: (getDecoded(ap.ssid.get_data()) ?? ap.ssid.get_data().toString()) ?? "Wi-Fi"
|
|
||||||
} as Widget.LabelProps),
|
} as Widget.LabelProps),
|
||||||
new Widget.Label({
|
...aps.filter(ap => ap.ssid).map(ap => PageButton({
|
||||||
className: "status",
|
className: bind(AstalNetwork.get_default().wifi, "activeAccessPoint").as(activeAP =>
|
||||||
} as Widget.LabelProps)
|
activeAP.ssid === ap.ssid ? "active" : ""),
|
||||||
|
title: bind(ap, "ssid").as(ssid =>
|
||||||
|
ssid ?? "Unknown SSID"),
|
||||||
|
icon: bind(ap, "iconName"),
|
||||||
|
endWidget: new Widget.Icon({
|
||||||
|
icon: bind(ap, "flags").as(flags => flags & NM.__80211ApFlags.PRIVACY ?
|
||||||
|
"channel-secure-symbolic"
|
||||||
|
: "channel-insecure-symbolic"),
|
||||||
|
css: "font-size: 18px;"
|
||||||
|
} as Widget.IconProps),
|
||||||
|
extraButtons: [
|
||||||
|
new Widget.Button({
|
||||||
|
image: new Widget.Icon({
|
||||||
|
icon: "window-close-symbolic",
|
||||||
|
css: "font-size: 18px;"
|
||||||
|
} 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.ButtonProps))) : [],
|
|
||||||
} 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);
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user