[AGS] Bar: Done (WiFi still missing, will be added at some later point)
This commit is contained in:
@@ -0,0 +1,221 @@
|
||||
import { execAsync, bind } from "astal";
|
||||
import Network from "gi://AstalNetwork";
|
||||
import { App, Gtk } from "astal/gtk4";
|
||||
import { NetworkItem } from "./modules/NetworkItem";
|
||||
import { PasswordDialog } from "./modules/PasswordDialog";
|
||||
import {
|
||||
availableNetworks,
|
||||
savedNetworks,
|
||||
activeNetwork,
|
||||
showPasswordDialog,
|
||||
scanNetworks,
|
||||
getSavedNetworks,
|
||||
disconnectNetwork,
|
||||
forgetNetwork,
|
||||
isExpanded,
|
||||
refreshIntervalId,
|
||||
} from "./networkinghelper";
|
||||
|
||||
// Main WiFi Box component
|
||||
export const WiFiBox = () => {
|
||||
const network = Network.get_default();
|
||||
|
||||
// Initial scan when component is first used
|
||||
setTimeout(() => {
|
||||
scanNetworks();
|
||||
getSavedNetworks();
|
||||
}, 100);
|
||||
|
||||
return (
|
||||
<box vertical cssClasses={["wifi-menu", "toggle"]}>
|
||||
{/* WiFi Toggle Header */}
|
||||
<box cssClasses={["toggle", "wifi-toggle"]}>
|
||||
<button
|
||||
onClicked={() => {
|
||||
if (network.wifi.enabled) {
|
||||
network.wifi.set_enabled(false);
|
||||
} else network.wifi.set_enabled(true);
|
||||
}}
|
||||
cssClasses={bind(network.wifi, "enabled").as((enabled) =>
|
||||
enabled ? ["button"] : ["button-disabled"],
|
||||
)}
|
||||
>
|
||||
<image iconName={bind(network.wifi, "icon_name")} />
|
||||
</button>
|
||||
<button
|
||||
hexpand={true}
|
||||
onClicked={() => {
|
||||
if (network.wifi.enabled) {
|
||||
isExpanded.set(!isExpanded.get());
|
||||
if (isExpanded.get()) {
|
||||
scanNetworks();
|
||||
getSavedNetworks();
|
||||
}
|
||||
}
|
||||
}}
|
||||
>
|
||||
<box hexpand={true}>
|
||||
<label
|
||||
hexpand={true}
|
||||
xalign={0}
|
||||
label={bind(network.wifi, "ssid").as(
|
||||
(ssid) =>
|
||||
ssid || (network.wifi.enabled ? "Not Connected" : "WiFi Off"),
|
||||
)}
|
||||
/>
|
||||
<image
|
||||
iconName="pan-end-symbolic"
|
||||
halign={Gtk.Align.END}
|
||||
cssClasses={bind(isExpanded).as((expanded) =>
|
||||
expanded
|
||||
? ["arrow-indicator", "arrow-down"]
|
||||
: ["arrow-indicator"],
|
||||
)}
|
||||
/>
|
||||
</box>
|
||||
</button>
|
||||
</box>
|
||||
|
||||
{/* Networks List Revealer */}
|
||||
<revealer
|
||||
transitionType={Gtk.RevealerTransitionType.SLIDE_DOWN}
|
||||
transitionDuration={300}
|
||||
revealChild={bind(isExpanded)}
|
||||
setup={() => {
|
||||
const clearScanInterval = () => {
|
||||
if (refreshIntervalId.get()) {
|
||||
clearInterval( parseInt( '' + refreshIntervalId.get() ));
|
||||
refreshIntervalId.set(null);
|
||||
}
|
||||
};
|
||||
bind(isExpanded).subscribe((expanded) => {
|
||||
// Clear existing interval
|
||||
clearScanInterval();
|
||||
|
||||
if (expanded) {
|
||||
// Scan networks
|
||||
network.wifi?.scan();
|
||||
|
||||
// Set up new interval if WiFi is enabled
|
||||
if (network.wifi?.enabled) {
|
||||
refreshIntervalId.set(
|
||||
setInterval(() => {
|
||||
scanNetworks();
|
||||
getSavedNetworks();
|
||||
print("updated");
|
||||
}, 10000),
|
||||
);
|
||||
}
|
||||
} else {
|
||||
// Apply revealer bug fix when collapsed
|
||||
App.toggle_window("system-menu");
|
||||
App.toggle_window("system-menu");
|
||||
}
|
||||
});
|
||||
|
||||
// Monitor window toggling
|
||||
const windowListener = App.connect("window-toggled", (_, window) => {
|
||||
if (window.name === "system-menu" && isExpanded.get()) {
|
||||
isExpanded.set(false);
|
||||
}
|
||||
});
|
||||
|
||||
// Clean up resources when component is destroyed
|
||||
return () => {
|
||||
App.disconnect(windowListener);
|
||||
clearScanInterval();
|
||||
};
|
||||
}}
|
||||
>
|
||||
<box vertical cssClasses={["network-list"]}>
|
||||
<box visible={showPasswordDialog( v => v )}>
|
||||
<PasswordDialog />
|
||||
</box>
|
||||
|
||||
<label label="Available Networks" cssClasses={["section-label"]} />
|
||||
<label label="No networks found" cssClasses={["empty-label"]} visible={availableNetworks( net => net.length === 0 )}/>
|
||||
<box visible={availableNetworks( networks => networks.length > 1 )}>
|
||||
{availableNetworks( networks =>
|
||||
networks.map( (network) => <NetworkItem network={network} />)
|
||||
)}
|
||||
</box>
|
||||
|
||||
{savedNetworks((networks) => {
|
||||
// Filter out networks already shown in available networks
|
||||
const filteredNetworks = networks.filter(
|
||||
(ssid) => !availableNetworks.get().some((n) => n.ssid === ssid)
|
||||
);
|
||||
|
||||
// Only render the section if there are filtered networks to show
|
||||
return filteredNetworks.length > 0 ? (
|
||||
<box vertical>
|
||||
<label label="Saved Networks" cssClasses={["section-label"]} />
|
||||
{filteredNetworks.map((ssid) => (
|
||||
<box cssClasses={["saved-network"]}>
|
||||
<label label={ssid} />
|
||||
<box hexpand={true} />
|
||||
<button
|
||||
label="Forget"
|
||||
cssClasses={["forget-button", "button"]}
|
||||
onClicked={() => forgetNetwork(ssid)}
|
||||
/>
|
||||
</box>
|
||||
))}
|
||||
</box>
|
||||
) : (
|
||||
<box></box>
|
||||
);
|
||||
})}
|
||||
|
||||
<box hexpand>
|
||||
<button
|
||||
halign={Gtk.Align.START}
|
||||
cssClasses={["refresh-button"]}
|
||||
onClicked={() => {
|
||||
scanNetworks();
|
||||
getSavedNetworks();
|
||||
}}
|
||||
>
|
||||
<image iconName="view-refresh-symbolic" />
|
||||
</button>
|
||||
{/* Connected Network Options */}
|
||||
<box hexpand>
|
||||
{activeNetwork((active) =>
|
||||
active ? (
|
||||
<box vertical cssClasses={["connected-network"]} hexpand>
|
||||
<button
|
||||
label="Disconnect"
|
||||
cssClasses={["disconnect-button"]}
|
||||
onClicked={() => disconnectNetwork(active.ssid)}
|
||||
/>
|
||||
</box>
|
||||
) : (
|
||||
""
|
||||
),
|
||||
)}
|
||||
</box>
|
||||
<button
|
||||
cssClasses={["settings-button"]}
|
||||
halign={Gtk.Align.END}
|
||||
hexpand={false}
|
||||
onClicked={() => {
|
||||
execAsync([
|
||||
"sh",
|
||||
"-c",
|
||||
"XDG_CURRENT_DESKTOP=GNOME gnome-control-center wifi",
|
||||
]);
|
||||
isExpanded.set(false);
|
||||
}}
|
||||
>
|
||||
<image iconName={"emblem-system-symbolic"} />
|
||||
</button>
|
||||
) : (
|
||||
""
|
||||
),
|
||||
)}
|
||||
</box>
|
||||
</box>
|
||||
</revealer>
|
||||
</box>
|
||||
);
|
||||
};
|
@@ -0,0 +1,2 @@
|
||||
# Source
|
||||
This is a modified version from [MatShell](https://github.com/Neurian/matshell)
|
14
config/astal/components/QuickActions/modules/Networking-old/network.d.ts
vendored
Normal file
14
config/astal/components/QuickActions/modules/Networking-old/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