222 lines
7.0 KiB
TypeScript

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>
);
};