2025-04-25 20:56:59 +02:00

210 lines
7.4 KiB
TypeScript

import { bind, exec, 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 = () => {
return (
<box>
<button
cssClasses={bind(bt.adapter, "powered").as(powered =>
powered
? ["toggle-button", "toggle-on"]
: ["toggle-button"],
)}
onClicked={() =>
bt.adapter.set_powered(!bt.adapter.get_powered())
}
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.adapter, "powered").as(
p => !p,
)}
label="Disabled"
></label>
<label
visible={bind(bt.adapter, "powered")}
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.adapter, "powered")}
child={
<box>
<image iconName={"arrow-right-symbolic"}></image>
{picker}
</box>
}
tooltipText={"View available devices"}
onClicked={() => openBTPicker()}
></button>
</box>
);
};
const openBTPicker = () => {
picker.popup();
try {
bt.adapter.start_discovery();
} catch (_) { }
};
const BluetoothPickerList = () => {
let btEnableState = readFile("./btconf") === "true" ? true : false;
if (bt.adapter) {
bt.adapter.set_powered(btEnableState);
} else {
timeout( 1000, () => {
if (bt.adapter) {
bt.adapter.set_powered(btEnableState);
} else {
timeout( 2000, () => {
if ( bt.adapter ) {
bt.adapter.set_powered(btEnableState);
} else {
timeout( 10000, () => {
if ( bt.adapter ) {
bt.adapter.set_powered(btEnableState);
} else {
exec( `/bin/bash -c "notify-send 'Failed to connect to bluetooth adapter'"` );
}
} )
}
} )
}
} );
}
const updateState = () => {
btEnableState = !btEnableState;
writeFile("./btconf", "" + btEnableState);
};
return (
<box
vertical
onDestroy={() => bt.adapter.stop_discovery()}
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", () => bt.adapter.stop_discovery());
return popover;
};
const picker = BluetoothPicker();
export default {
BluetoothModule,
};