[AGS] Bar: Done (WiFi still missing, will be added at some later point)
This commit is contained in:
@@ -0,0 +1,97 @@
|
||||
import { bind } from "astal";
|
||||
import { Gtk } from "astal/gtk4";
|
||||
import AstalNetwork from "gi://AstalNetwork";
|
||||
import networkHelper from "./network-helper";
|
||||
import NetworkMenu from "./NetworkMenu";
|
||||
|
||||
const net = AstalNetwork.get_default();
|
||||
const STATE = AstalNetwork.DeviceState;
|
||||
|
||||
const Network = () => {
|
||||
const netMenu = NetworkMenu.NetworkMenu();
|
||||
return (
|
||||
<box>
|
||||
<button
|
||||
cssClasses={networkHelper.networkEnabled(en => {
|
||||
if (en) return ["toggle-button", "toggle-on"];
|
||||
else return ["toggle-button"];
|
||||
})}
|
||||
onClicked={() =>
|
||||
networkHelper.setNetworking(
|
||||
!networkHelper.networkEnabled.get(),
|
||||
)
|
||||
}
|
||||
child={
|
||||
<box vertical>
|
||||
<label
|
||||
label={bind(net.wifi, "enabled").as(
|
||||
stat => `Network (${stat ? "WiFi" : "Wired"})`,
|
||||
)}
|
||||
cssClasses={["title-2"]}
|
||||
></label>
|
||||
<label
|
||||
label={bind(net.wired, "state").as(state => {
|
||||
if (state === STATE.ACTIVATED) {
|
||||
return (
|
||||
"Wired. IP: " + networkHelper.getIP()
|
||||
);
|
||||
} else if (state === STATE.DISCONNECTED) {
|
||||
return "Disconnected";
|
||||
} else if (state === STATE.FAILED) {
|
||||
return "Error";
|
||||
} else if (
|
||||
state === STATE.PREPARE ||
|
||||
state === STATE.CONFIG ||
|
||||
state === STATE.IP_CHECK ||
|
||||
state === STATE.IP_CONFIG
|
||||
) {
|
||||
return "Connecting...";
|
||||
} else {
|
||||
return "Unavailable";
|
||||
}
|
||||
})}
|
||||
visible={bind(net.wifi, "enabled").as(v => !v)}
|
||||
></label>
|
||||
<label
|
||||
label={bind(net.wifi, "state").as(state => {
|
||||
if (state === STATE.ACTIVATED) {
|
||||
return `${net.wifi.get_ssid()} (${networkHelper.getIP()})`;
|
||||
} else if (state === STATE.DISCONNECTED) {
|
||||
return "Disconnected";
|
||||
} else if (state === STATE.FAILED) {
|
||||
return "Error";
|
||||
} else if (
|
||||
state === STATE.PREPARE ||
|
||||
state === STATE.CONFIG ||
|
||||
state === STATE.IP_CHECK ||
|
||||
state === STATE.IP_CONFIG
|
||||
) {
|
||||
return "Connecting...";
|
||||
} else {
|
||||
return "Unavailable";
|
||||
}
|
||||
})}
|
||||
visible={bind(net.wifi, "enabled")}
|
||||
></label>
|
||||
</box>
|
||||
}
|
||||
></button>
|
||||
<button
|
||||
cssClasses={["actions-button"]}
|
||||
visible={networkHelper.networkEnabled()}
|
||||
onClicked={() => netMenu.popup()}
|
||||
child={
|
||||
<box>
|
||||
<image iconName={"arrow-right-symbolic"}></image>
|
||||
{ netMenu }
|
||||
</box>
|
||||
}
|
||||
tooltipText={"View available devices"}
|
||||
></button>
|
||||
</box>
|
||||
);
|
||||
};
|
||||
|
||||
export default {
|
||||
Network,
|
||||
};
|
@@ -0,0 +1,18 @@
|
||||
import { Gtk } from "astal/gtk4";
|
||||
|
||||
const NetworkMenu = () => {
|
||||
const popover = new Gtk.Popover();
|
||||
popover.set_child( renderMenu() );
|
||||
return popover;
|
||||
};
|
||||
|
||||
const renderMenu = () => {
|
||||
return <box vertical>
|
||||
<image iconName={"appointment-soon-symbolic"} iconSize={Gtk.IconSize.LARGE}></image>
|
||||
<label label={"Coming later"}></label>
|
||||
</box>;
|
||||
};
|
||||
|
||||
export default {
|
||||
NetworkMenu,
|
||||
};
|
91
config/astal/components/QuickActions/modules/Networking/dump
Normal file
91
config/astal/components/QuickActions/modules/Networking/dump
Normal file
@@ -0,0 +1,91 @@
|
||||
import { bind } from "astal";
|
||||
import { Gtk } from "astal/gtk4";
|
||||
import AstalNetwork from "gi://AstalNetwork";
|
||||
import networkHelper from "./network-helper";
|
||||
|
||||
const net = AstalNetwork.get_default();
|
||||
const STATE = AstalNetwork.DeviceState;
|
||||
|
||||
const WiFiList = () => {
|
||||
const popover = new Gtk.Popover({ cssClasses: ["WiFiPicker"] });
|
||||
|
||||
return popover;
|
||||
};
|
||||
|
||||
const renderWiFiList = () => {
|
||||
return <box>
|
||||
<label label="Test"></label>
|
||||
</box>
|
||||
}
|
||||
|
||||
const Network = () => {
|
||||
const wifiList = WiFiList();
|
||||
|
||||
return (
|
||||
<box>
|
||||
<button
|
||||
cssClasses={networkHelper.networkEnabled(en => {
|
||||
if (en) return ["network-button", "net-on"];
|
||||
else return ["network-button"];
|
||||
})}
|
||||
onClicked={() =>
|
||||
networkHelper.setNetworking(
|
||||
!networkHelper.networkEnabled.get(),
|
||||
)
|
||||
}
|
||||
child={<box vertical>
|
||||
<label label="Wired" cssClasses={[ 'button-name' ]}></label>
|
||||
<label label={bind( net.wired, 'state' ).as( state => {
|
||||
if ( state === STATE.ACTIVATED ) {
|
||||
return 'Connected. IP: ' + networkHelper.getIP();
|
||||
} else if ( state === STATE.DISCONNECTED ) {
|
||||
return 'Disconnected';
|
||||
} else if ( state === STATE.FAILED ) {
|
||||
return 'Error';
|
||||
} else if ( state === STATE.PREPARE || state === STATE.CONFIG || state === STATE.IP_CHECK || state === STATE.IP_CONFIG ) {
|
||||
return 'Connecting...';
|
||||
} else {
|
||||
return 'Unavailable';
|
||||
}
|
||||
} )}></label>
|
||||
</box>}
|
||||
></button>
|
||||
<box>
|
||||
<button
|
||||
cssClasses={bind(net.wifi, "enabled").as(b => {
|
||||
const classes = ["network-button"];
|
||||
if (b) {
|
||||
classes.push("wifi-on");
|
||||
}
|
||||
return classes;
|
||||
})}
|
||||
child={<box vertical>
|
||||
<label label="WiFi" cssClasses={[ 'button-name' ]}></label>
|
||||
<label label={bind( net.wifi, 'state' ).as( state => {
|
||||
if ( state === STATE.ACTIVATED ) {
|
||||
return 'Connected. IP: ' + networkHelper.getIP();
|
||||
} else if ( state === STATE.DISCONNECTED ) {
|
||||
return 'Disconnected';
|
||||
} else if ( state === STATE.FAILED ) {
|
||||
return 'Error';
|
||||
} else if ( state === STATE.PREPARE || state === STATE.CONFIG || state === STATE.IP_CHECK || state === STATE.IP_CONFIG ) {
|
||||
return 'Connecting...';
|
||||
} else {
|
||||
return 'Unavailable';
|
||||
}
|
||||
} )} visible={bind(net.wifi, 'enabled').as( en => en )}></label>
|
||||
<label label="Disabled" visible={bind(net.wifi, 'enabled').as( en => !en )}></label>
|
||||
</box>}
|
||||
></button>
|
||||
<button
|
||||
cssClasses={["network-button-context"]}
|
||||
visible={bind(net.wifi, "enabled").as(b => b)}
|
||||
onClicked={() => wifiList.popup()}
|
||||
></button>
|
||||
</box>
|
||||
{wifiList}
|
||||
</box>
|
||||
);
|
||||
};
|
||||
|
||||
export default Network;
|
@@ -0,0 +1,28 @@
|
||||
import { exec, Variable } from "astal";
|
||||
import AstalNetwork from "gi://AstalNetwork";
|
||||
|
||||
const networkEnabled = Variable( exec( 'nmcli networking connectivity' ) !== 'none' );
|
||||
const network = AstalNetwork.get_default();
|
||||
|
||||
|
||||
const setNetworking = ( status: boolean ) => {
|
||||
if ( status === true ) {
|
||||
exec( 'nmcli networking on' );
|
||||
networkEnabled.set( true );
|
||||
} else {
|
||||
exec( 'nmcli networking off' );
|
||||
networkEnabled.set( false );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const getIP = () => {
|
||||
return exec( `/bin/bash -c "ip addr show | grep 'inet ' | awk '{print $2}' | grep -v '127'"` ).split( '/' )[ 0 ];
|
||||
}
|
||||
|
||||
|
||||
export default {
|
||||
networkEnabled,
|
||||
setNetworking,
|
||||
getIP
|
||||
}
|
14
config/astal/components/QuickActions/modules/Networking/network.d.ts
vendored
Normal file
14
config/astal/components/QuickActions/modules/Networking/network.d.ts
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
import AstalNetwork from "gi://AstalNetwork?version=0.1";
|
||||
|
||||
interface CurrentWiFi {
|
||||
ssid: string;
|
||||
strength: number;
|
||||
secured: boolean;
|
||||
}
|
||||
|
||||
interface WiFiDetails extends CurrentWiFi {
|
||||
active: boolean;
|
||||
accessPoint: AstalNetwork.AccessPoint;
|
||||
iconName: string;
|
||||
}
|
||||
|
@@ -0,0 +1,161 @@
|
||||
// From https://github.com/Neurarian/matshell/blob/master/utils/wifi.ts
|
||||
|
||||
import { execAsync, Variable } from "astal";
|
||||
import Network from "gi://AstalNetwork";
|
||||
import { CurrentWiFi, WiFiDetails } from "./network";
|
||||
|
||||
// State trackers
|
||||
export const availableNetworks: Variable<WiFiDetails[]> = Variable([]);
|
||||
export const savedNetworks: Variable<string[]> = Variable([]);
|
||||
export const activeNetwork: Variable<CurrentWiFi | null> = Variable(null);
|
||||
export const isConnecting: Variable<boolean> = Variable(false);
|
||||
export const showPasswordDialog: Variable<boolean> = Variable(false);
|
||||
export const errorMessage: Variable<string> = Variable("");
|
||||
export const isExpanded: Variable<boolean> = Variable(false);
|
||||
export const passwordInput: Variable<string> = Variable("");
|
||||
export const selectedNetwork: Variable<null | WiFiDetails> = Variable(null);
|
||||
export const refreshIntervalId: Variable<
|
||||
number | null | ReturnType<typeof setTimeout>
|
||||
> = Variable(null);
|
||||
|
||||
// Function to scan for available networks
|
||||
export const scanNetworks = () => {
|
||||
const network = Network.get_default();
|
||||
if (network && network.wifi) {
|
||||
network.wifi.scan();
|
||||
|
||||
// Get available networks from access points
|
||||
const networks: WiFiDetails[] = network.wifi.accessPoints
|
||||
.map(ap => ({
|
||||
ssid: ap.ssid,
|
||||
strength: ap.strength,
|
||||
secured: ap.flags !== 0,
|
||||
active: network.wifi.activeAccessPoint?.ssid === ap.ssid,
|
||||
accessPoint: ap,
|
||||
iconName: ap.iconName,
|
||||
}))
|
||||
.filter(n => n.ssid);
|
||||
|
||||
// Sort by signal strength
|
||||
networks.sort((a, b) => b.strength - a.strength);
|
||||
|
||||
// Remove duplicates (same SSID)
|
||||
const uniqueNetworks: WiFiDetails[] = [];
|
||||
const seen = new Set();
|
||||
networks.forEach(network => {
|
||||
if (!seen.has(network.ssid)) {
|
||||
seen.add(network.ssid);
|
||||
uniqueNetworks.push(network);
|
||||
}
|
||||
});
|
||||
|
||||
availableNetworks.set(uniqueNetworks);
|
||||
|
||||
// Update active network
|
||||
if (network.wifi.activeAccessPoint) {
|
||||
activeNetwork.set({
|
||||
ssid: network.wifi.activeAccessPoint.ssid,
|
||||
strength: network.wifi.activeAccessPoint.strength,
|
||||
secured: network.wifi.activeAccessPoint.flags !== 0,
|
||||
});
|
||||
} else {
|
||||
activeNetwork.set(null);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Function to list saved networks
|
||||
export const getSavedNetworks = () => {
|
||||
execAsync(["bash", "-c", "nmcli -t -f NAME,TYPE connection show"])
|
||||
.then(output => {
|
||||
if (typeof output === "string") {
|
||||
const savedWifiNetworks = output
|
||||
.split("\n")
|
||||
.filter(line => line.includes("802-11-wireless"))
|
||||
.map(line => line.split(":")[0].trim());
|
||||
savedNetworks.set(savedWifiNetworks);
|
||||
}
|
||||
})
|
||||
.catch(error => console.error("Error fetching saved networks:", error));
|
||||
};
|
||||
|
||||
// Function to connect to a network
|
||||
|
||||
export const connectToNetwork = (
|
||||
ssid: string,
|
||||
password: null | string = null,
|
||||
) => {
|
||||
isConnecting.set(true);
|
||||
errorMessage.set("");
|
||||
const network = Network.get_default();
|
||||
const currentSsid = network.wifi.ssid;
|
||||
|
||||
// Function to perform the actual connection
|
||||
const performConnection = () => {
|
||||
let command = "";
|
||||
if (password) {
|
||||
// Connect with password
|
||||
command = `echo '${password}' | nmcli device wifi connect "${ssid}" --ask`;
|
||||
} else {
|
||||
// Connect without password (saved or open network)
|
||||
command = `nmcli connection up "${ssid}" || nmcli device wifi connect "${ssid}"`;
|
||||
}
|
||||
|
||||
execAsync(["bash", "-c", command])
|
||||
.then(() => {
|
||||
showPasswordDialog.set(false);
|
||||
isConnecting.set(false);
|
||||
scanNetworks(); // Refresh network list
|
||||
})
|
||||
.catch(error => {
|
||||
console.error("Connection error:", error);
|
||||
errorMessage.set("Failed to connect. Check password.");
|
||||
isConnecting.set(false);
|
||||
});
|
||||
};
|
||||
|
||||
// If already connected to a network, disconnect first
|
||||
if (currentSsid && currentSsid !== ssid) {
|
||||
console.log(
|
||||
`Disconnecting from ${currentSsid} before connecting to ${ssid}`,
|
||||
);
|
||||
execAsync(["bash", "-c", `nmcli connection down "${currentSsid}"`])
|
||||
.then(() => {
|
||||
// Wait a moment for the disconnection to complete fully
|
||||
setTimeout(() => {
|
||||
performConnection();
|
||||
}, 500); // 500ms delay for clean disconnection
|
||||
})
|
||||
.catch(error => {
|
||||
console.error("Disconnect error:", error);
|
||||
// Continue with connection attempt even if disconnect fails
|
||||
performConnection();
|
||||
});
|
||||
} else {
|
||||
// No active connection or connecting to same network (reconnect case)
|
||||
performConnection();
|
||||
}
|
||||
};
|
||||
|
||||
// Function to disconnect from a network
|
||||
export const disconnectNetwork = (ssid: string) => {
|
||||
execAsync(["bash", "-c", `nmcli connection down "${ssid}"`])
|
||||
.then(() => {
|
||||
scanNetworks(); // Refresh network list
|
||||
})
|
||||
.catch(error => {
|
||||
console.error("Disconnect error:", error);
|
||||
});
|
||||
};
|
||||
|
||||
// Function to forget a saved network
|
||||
export const forgetNetwork = (ssid: string) => {
|
||||
execAsync(["bash", "-c", `nmcli connection delete "${ssid}"`])
|
||||
.then(() => {
|
||||
getSavedNetworks(); // Refresh saved networks list
|
||||
scanNetworks(); // Refresh network list
|
||||
})
|
||||
.catch(error => {
|
||||
console.error("Forget network error:", error);
|
||||
});
|
||||
};
|
Reference in New Issue
Block a user