From 69484fc3022a35f1489ed131ad4dd3d5b5bf4733 Mon Sep 17 00:00:00 2001 From: Janis Hutz Date: Tue, 22 Apr 2025 17:38:41 +0200 Subject: [PATCH] [AGS] Bar: Improve QuickActions --- .../bar/ui/QuickActions/QuickActions.tsx | 52 +++--- .../modules/Bluetooth/Bluetooth.tsx | 0 .../modules/Bluetooth/bthelper.ts | 0 .../Networking.tsx | 0 .../{Networking => Networking-old}/README.md | 0 .../modules/Networking-old/network.d.ts | 14 ++ .../networkinghelper.ts | 0 .../modules/Networking/Network.tsx | 90 ++++++++++ .../modules/Networking/network-helper.ts | 30 ++++ .../modules/Networking/wifi-helper.ts | 161 ++++++++++++++++++ .../bar/ui/QuickActions/modules/Power.tsx | 31 ++++ .../components/bar/ui/modules/QuickView.tsx | 87 +++++----- 12 files changed, 390 insertions(+), 75 deletions(-) create mode 100644 config/astal/components/bar/ui/QuickActions/modules/Bluetooth/Bluetooth.tsx create mode 100644 config/astal/components/bar/ui/QuickActions/modules/Bluetooth/bthelper.ts rename config/astal/components/bar/ui/QuickActions/modules/{Networking => Networking-old}/Networking.tsx (100%) rename config/astal/components/bar/ui/QuickActions/modules/{Networking => Networking-old}/README.md (100%) create mode 100644 config/astal/components/bar/ui/QuickActions/modules/Networking-old/network.d.ts rename config/astal/components/bar/ui/QuickActions/modules/{Networking => Networking-old}/networkinghelper.ts (100%) create mode 100644 config/astal/components/bar/ui/QuickActions/modules/Networking/Network.tsx create mode 100644 config/astal/components/bar/ui/QuickActions/modules/Networking/network-helper.ts create mode 100644 config/astal/components/bar/ui/QuickActions/modules/Networking/wifi-helper.ts create mode 100644 config/astal/components/bar/ui/QuickActions/modules/Power.tsx diff --git a/config/astal/components/bar/ui/QuickActions/QuickActions.tsx b/config/astal/components/bar/ui/QuickActions/QuickActions.tsx index d08cec9..3bc681c 100644 --- a/config/astal/components/bar/ui/QuickActions/QuickActions.tsx +++ b/config/astal/components/bar/ui/QuickActions/QuickActions.tsx @@ -1,31 +1,25 @@ -import { Astal, App } from "astal/gtk4"; -import PowerProfiles from "gi://AstalPowerProfiles"; -import { Variable } from "astal"; -import { Sliders } from "./modules/Sliders"; -import { Toggles } from "./modules/Toggles"; -import { PowerProfileBox } from "./modules/PowerProfileBox"; -import { BatteryBox } from "./modules/BatteryBox"; +import { Gtk } from "astal/gtk4" +import Network from "./modules/Networking/Network"; +import Power from "./modules/Power"; -export default function QuickActions() { - const powerprofiles = PowerProfiles.get_default(); - const hasProfiles = powerprofiles?.get_profiles()?.length > 0; - const { TOP, RIGHT } = Astal.WindowAnchor; - const visible = Variable(false); - return ( - - - - {hasProfiles && } - - - - - ); +const QuickActions = () => { + const popover = new Gtk.Popover( { cssClasses: [ 'quick-actions-popover' ] } ); + + popover.set_child( createQuickActionMenu() ); + + return popover; } + + +const createQuickActionMenu = () => { + return + + + +} + + +// TODO: Expose additional functions to be usable through CLI +export default { + QuickActions +}; diff --git a/config/astal/components/bar/ui/QuickActions/modules/Bluetooth/Bluetooth.tsx b/config/astal/components/bar/ui/QuickActions/modules/Bluetooth/Bluetooth.tsx new file mode 100644 index 0000000..e69de29 diff --git a/config/astal/components/bar/ui/QuickActions/modules/Bluetooth/bthelper.ts b/config/astal/components/bar/ui/QuickActions/modules/Bluetooth/bthelper.ts new file mode 100644 index 0000000..e69de29 diff --git a/config/astal/components/bar/ui/QuickActions/modules/Networking/Networking.tsx b/config/astal/components/bar/ui/QuickActions/modules/Networking-old/Networking.tsx similarity index 100% rename from config/astal/components/bar/ui/QuickActions/modules/Networking/Networking.tsx rename to config/astal/components/bar/ui/QuickActions/modules/Networking-old/Networking.tsx diff --git a/config/astal/components/bar/ui/QuickActions/modules/Networking/README.md b/config/astal/components/bar/ui/QuickActions/modules/Networking-old/README.md similarity index 100% rename from config/astal/components/bar/ui/QuickActions/modules/Networking/README.md rename to config/astal/components/bar/ui/QuickActions/modules/Networking-old/README.md diff --git a/config/astal/components/bar/ui/QuickActions/modules/Networking-old/network.d.ts b/config/astal/components/bar/ui/QuickActions/modules/Networking-old/network.d.ts new file mode 100644 index 0000000..4be5e37 --- /dev/null +++ b/config/astal/components/bar/ui/QuickActions/modules/Networking-old/network.d.ts @@ -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; +} + diff --git a/config/astal/components/bar/ui/QuickActions/modules/Networking/networkinghelper.ts b/config/astal/components/bar/ui/QuickActions/modules/Networking-old/networkinghelper.ts similarity index 100% rename from config/astal/components/bar/ui/QuickActions/modules/Networking/networkinghelper.ts rename to config/astal/components/bar/ui/QuickActions/modules/Networking-old/networkinghelper.ts diff --git a/config/astal/components/bar/ui/QuickActions/modules/Networking/Network.tsx b/config/astal/components/bar/ui/QuickActions/modules/Networking/Network.tsx new file mode 100644 index 0000000..20e38ef --- /dev/null +++ b/config/astal/components/bar/ui/QuickActions/modules/Networking/Network.tsx @@ -0,0 +1,90 @@ +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 + + +} + +const Network = () => { + const wifiList = WiFiList(); + + return ( + + + + + + + {wifiList} + + ); +}; + +export default Network; diff --git a/config/astal/components/bar/ui/QuickActions/modules/Networking/network-helper.ts b/config/astal/components/bar/ui/QuickActions/modules/Networking/network-helper.ts new file mode 100644 index 0000000..4e738c6 --- /dev/null +++ b/config/astal/components/bar/ui/QuickActions/modules/Networking/network-helper.ts @@ -0,0 +1,30 @@ +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 = () => { + print( 'Hello World' ); + return 'Hello World'; + // return exec( "ip addr show | grep 'inet ' | awk '{print $2}' | grep -v '127'" ).split( '/' )[ 0 ]; +} + + +export default { + networkEnabled, + setNetworking, + getIP +} diff --git a/config/astal/components/bar/ui/QuickActions/modules/Networking/wifi-helper.ts b/config/astal/components/bar/ui/QuickActions/modules/Networking/wifi-helper.ts new file mode 100644 index 0000000..b4bd6aa --- /dev/null +++ b/config/astal/components/bar/ui/QuickActions/modules/Networking/wifi-helper.ts @@ -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 = Variable([]); +export const savedNetworks: Variable = Variable([]); +export const activeNetwork: Variable = Variable(null); +export const isConnecting: Variable = Variable(false); +export const showPasswordDialog: Variable = Variable(false); +export const errorMessage: Variable = Variable(""); +export const isExpanded: Variable = Variable(false); +export const passwordInput: Variable = Variable(""); +export const selectedNetwork: Variable = Variable(null); +export const refreshIntervalId: Variable< + number | null | ReturnType +> = 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); + }); +}; diff --git a/config/astal/components/bar/ui/QuickActions/modules/Power.tsx b/config/astal/components/bar/ui/QuickActions/modules/Power.tsx new file mode 100644 index 0000000..2cf4014 --- /dev/null +++ b/config/astal/components/bar/ui/QuickActions/modules/Power.tsx @@ -0,0 +1,31 @@ +import { exec } from "astal"; +import { Gtk } from "astal/gtk4"; + +const PowerMenu = (): Gtk.Popover => { + const popover = new Gtk.Popover( { cssClasses: [ 'PowerMenu' ] } ); + + const powerMenuBox = () => { + return + + + + + + + } + + + popover.set_child( powerMenuBox() ); + return popover; +} + +const Power = () => { + const pm = PowerMenu(); + return + } const NetworkWidget = () => { const network = AstalNetwork.get_default(); - const status = bind( network, "state" ); - const wifiStrength = bind( network.wifi, 'strength' ); - const states = { - "off": "" - } - return + return + { + if ( state === AstalNetwork.State.CONNECTING ) { + return 'chronometer-reset-symbolic'; + } else if ( state === AstalNetwork.State.CONNECTED_LOCAL || state === AstalNetwork.State.CONNECTED_SITE || state === AstalNetwork.State.CONNECTED_GLOBAL ) { + print( 'Wired connected' ); + return 'network-wired-activated-symbolic'; + } else { + print( 'Unknown state' ); + return 'paint-unknown-symbolic'; + } + } )} cssClasses={[ 'network-widget' ]} visible={bind( network.wifi, 'state' ).as( state => state !== STATE.ACTIVATED )}> + { + if ( state === STATE.ACTIVATED ) { + return network.wifi.iconName + } else { + return ''; + } + } )} cssClasses={[ 'network-widget' ]} visible={bind( network.wifi, 'state' ).as( state => state === STATE.ACTIVATED )}> + + } @@ -36,21 +61,9 @@ const BluetoothWidget = () => { const BatteryWidget = () => { const battery = AstalBattery.get_default(); if ( battery.get_is_present() ) { - const states = { - "100": "", - "90": "", - "80": "", - "70": "", - "60": "", - "50": "", - "40": "", - "30": "", - "20": "", - "10": "", - "critical": "", - "charging":"", - "plugged": " ", - } + return + } else { + return } // Else, no battery available -> Don't show the widget } @@ -68,31 +81,13 @@ const BrightnessWidget = () => { const Audio = () => { const wireplumber = AstalWp.get_default(); if ( wireplumber ) { - // With the states, split up the icons according to number of elements available - const speakerMuted = " "; - const speakersStates = [ - "", - "", - "" - ] - const micStates = { - "on": " ", - "muted": " ", - } const volume_speakers = bind( wireplumber.defaultSpeaker, 'volume' ); - const muted_speakers = bind( wireplumber.defaultSpeaker, 'mute' ); - const muted_mic = bind( wireplumber.defaultMicrophone, 'mute' ); return - - + + - + } else { print( '[ WirePlumber ] Could not connect, Audio support in bar will be missing' );