223 lines
		
	
	
		
			7.5 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			223 lines
		
	
	
		
			7.5 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
import { bind, interval, readFile, timeout, writeFile } from "astal";
 | 
						|
import { Gtk } from "astal/gtk4";
 | 
						|
import AstalBluetooth from "gi://AstalBluetooth";
 | 
						|
import BTDevice from "./Device";
 | 
						|
const ALIGN = Gtk.Align;
 | 
						|
 | 
						|
const bt = AstalBluetooth.get_default();
 | 
						|
 | 
						|
const BluetoothModule = () => {
 | 
						|
    const picker = BluetoothPicker();
 | 
						|
 | 
						|
    const openBTPicker = () => {
 | 
						|
        try {
 | 
						|
            bt.adapter.start_discovery();
 | 
						|
        } catch (e) {
 | 
						|
            printerr(e);
 | 
						|
        }
 | 
						|
        picker.popup();
 | 
						|
    };
 | 
						|
 | 
						|
    return (
 | 
						|
        <box>
 | 
						|
            <button
 | 
						|
                cssClasses={bind(bt, "isPowered").as(powered =>
 | 
						|
                    powered
 | 
						|
                        ? ["toggle-button", "toggle-on"]
 | 
						|
                        : ["toggle-button"],
 | 
						|
                )}
 | 
						|
                onClicked={() => {
 | 
						|
                    try {
 | 
						|
                        bt.adapter.set_powered(!bt.adapter.get_powered())
 | 
						|
                    } catch (_) { }
 | 
						|
                }}
 | 
						|
                child={
 | 
						|
                    <box vertical>
 | 
						|
                        <label
 | 
						|
                            cssClasses={["title-2"]}
 | 
						|
                            label={"Bluetooth"}
 | 
						|
                            halign={ALIGN.CENTER}
 | 
						|
                            valign={ALIGN.CENTER}
 | 
						|
                        ></label>
 | 
						|
                        <box halign={ALIGN.CENTER} valign={ALIGN.CENTER}>
 | 
						|
                            <label
 | 
						|
                                visible={bind(bt, "isPowered").as(
 | 
						|
                                    p => !p,
 | 
						|
                                )}
 | 
						|
                                label="Disabled"
 | 
						|
                            ></label>
 | 
						|
                            <label
 | 
						|
                                visible={bind(bt, "isPowered")}
 | 
						|
                                label={bind(bt, "devices").as(devices => {
 | 
						|
                                    let count = 0;
 | 
						|
                                    devices.forEach(device => {
 | 
						|
                                        if (device.connected) {
 | 
						|
                                            count++;
 | 
						|
                                        }
 | 
						|
                                    });
 | 
						|
                                    return `On (${count} ${count === 1 ? "client" : "clients"} connected)`;
 | 
						|
                                })}
 | 
						|
                            ></label>
 | 
						|
                        </box>
 | 
						|
                        <label></label>
 | 
						|
                    </box>
 | 
						|
                }
 | 
						|
            ></button>
 | 
						|
            <button
 | 
						|
                cssClasses={["actions-button"]}
 | 
						|
                visible={bind(bt, "isPowered")}
 | 
						|
                child={
 | 
						|
                    <box>
 | 
						|
                        <image iconName={"arrow-right-symbolic"}></image>
 | 
						|
                        {picker}
 | 
						|
                    </box>
 | 
						|
                }
 | 
						|
                tooltipText={"View available devices"}
 | 
						|
                onClicked={() => openBTPicker()}
 | 
						|
            ></button>
 | 
						|
        </box>
 | 
						|
    );
 | 
						|
};
 | 
						|
 | 
						|
const BluetoothPickerList = () => {
 | 
						|
    let btEnableState = false;
 | 
						|
 | 
						|
    try {
 | 
						|
        btEnableState = readFile("./btconf") === "true" ? true : false;
 | 
						|
    } catch (_) { }
 | 
						|
 | 
						|
    if (bt.get_adapter()) {
 | 
						|
        print('Setting BT state to ' + btEnableState);
 | 
						|
        bt.adapter.set_powered(btEnableState);
 | 
						|
    } else {
 | 
						|
        timeout(5000, () => {
 | 
						|
            if (bt.get_adapter()) {
 | 
						|
                print('Setting BT state to ' + btEnableState);
 | 
						|
                bt.adapter.set_powered(btEnableState);
 | 
						|
            } else {
 | 
						|
                timeout(5000, () => {
 | 
						|
                    try {
 | 
						|
                        bt.adapter.set_powered(btEnableState);
 | 
						|
                    } catch (_) { }
 | 
						|
                })
 | 
						|
            }
 | 
						|
        })
 | 
						|
    }
 | 
						|
 | 
						|
    const updateState = () => {
 | 
						|
        btEnableState = !btEnableState;
 | 
						|
        writeFile("./btconf", "" + btEnableState);
 | 
						|
    };
 | 
						|
 | 
						|
    return (
 | 
						|
        <box
 | 
						|
            vertical
 | 
						|
            onDestroy={() => {
 | 
						|
                try {
 | 
						|
                    bt.adapter.stop_discovery()
 | 
						|
                } catch (_) { }
 | 
						|
            }}
 | 
						|
            cssClasses={["popover-box"]}
 | 
						|
        >
 | 
						|
            <label cssClasses={["title"]} label={"Bluetooth"}></label>
 | 
						|
            <Gtk.Separator marginTop={3} marginBottom={5}></Gtk.Separator>
 | 
						|
            <centerbox
 | 
						|
                startWidget={<label label={"Turn on at startup"}></label>}
 | 
						|
                endWidget={
 | 
						|
                    <switch
 | 
						|
                        valign={ALIGN.END}
 | 
						|
                        halign={ALIGN.END}
 | 
						|
                        active={btEnableState}
 | 
						|
                        onButtonPressed={() => updateState()}
 | 
						|
                    ></switch>
 | 
						|
                }
 | 
						|
            ></centerbox>
 | 
						|
            <label
 | 
						|
                marginTop={10}
 | 
						|
                label={"Connected & Trusted devices"}
 | 
						|
                cssClasses={["title-2"]}
 | 
						|
            ></label>
 | 
						|
            <Gtk.Separator marginTop={3} marginBottom={5}></Gtk.Separator>
 | 
						|
            <box vertical cssClasses={["devices-list"]}>
 | 
						|
                {bind(bt, "devices").as(devices => {
 | 
						|
                    return devices
 | 
						|
                        .filter(device => {
 | 
						|
                            if (device.get_connected() || device.get_paired()) {
 | 
						|
                                return device;
 | 
						|
                            }
 | 
						|
                        })
 | 
						|
                        .map(device => {
 | 
						|
                            return <BTDevice device={device}></BTDevice>;
 | 
						|
                        });
 | 
						|
                })}
 | 
						|
            </box>
 | 
						|
            <label
 | 
						|
                visible={bind(bt, "devices").as(devices => {
 | 
						|
                    return (
 | 
						|
                        devices.filter(device => {
 | 
						|
                            if (device.get_connected() || device.get_paired()) {
 | 
						|
                                return device;
 | 
						|
                            }
 | 
						|
                        }).length === 0
 | 
						|
                    );
 | 
						|
                })}
 | 
						|
                label={"No connected / trusted devices"}
 | 
						|
                cssClasses={["bt-no-found", "bt-conn-list"]}
 | 
						|
            ></label>
 | 
						|
            <label
 | 
						|
                label={"Discovered bluetooth devices"}
 | 
						|
                cssClasses={["title-2"]}
 | 
						|
            ></label>
 | 
						|
            <Gtk.Separator marginBottom={5} marginTop={3}></Gtk.Separator>
 | 
						|
            <box vertical>
 | 
						|
                {bind(bt, "devices").as(devices => {
 | 
						|
                    return devices
 | 
						|
                        .filter(data => {
 | 
						|
                            if (!data.get_connected() && !data.get_paired()) {
 | 
						|
                                return data;
 | 
						|
                            }
 | 
						|
                        })
 | 
						|
                        .map(device => {
 | 
						|
                            return <BTDevice device={device}></BTDevice>;
 | 
						|
                        });
 | 
						|
                })}
 | 
						|
            </box>
 | 
						|
            <label
 | 
						|
                visible={bind(bt, "devices").as(devices => {
 | 
						|
                    return (
 | 
						|
                        devices.filter(device => {
 | 
						|
                            if (
 | 
						|
                                !device.get_connected() &&
 | 
						|
                                !device.get_paired()
 | 
						|
                            ) {
 | 
						|
                                return device;
 | 
						|
                            }
 | 
						|
                        }).length === 0
 | 
						|
                    );
 | 
						|
                })}
 | 
						|
                label={"No discovered devices"}
 | 
						|
                cssClasses={["bt-no-found"]}
 | 
						|
            ></label>
 | 
						|
        </box>
 | 
						|
    );
 | 
						|
};
 | 
						|
 | 
						|
const BluetoothPicker = () => {
 | 
						|
    const popover = new Gtk.Popover();
 | 
						|
 | 
						|
    popover.set_child(BluetoothPickerList());
 | 
						|
    popover.connect("closed", () => {
 | 
						|
        try {
 | 
						|
            bt.adapter.stop_discovery();
 | 
						|
        } catch (e) {
 | 
						|
            printerr(e);
 | 
						|
        }
 | 
						|
    });
 | 
						|
 | 
						|
    return popover;
 | 
						|
};
 | 
						|
 | 
						|
export default {
 | 
						|
    BluetoothModule,
 | 
						|
};
 |