fix(ui): async functions blocking ui thread

This commit is contained in:
Suyog Tandel
2026-02-17 23:08:37 +05:30
parent 02e571d29c
commit 03b089d879
11 changed files with 207 additions and 187 deletions
Generated
+152 -153
View File
File diff suppressed because it is too large Load Diff
+3 -2
View File
@@ -24,7 +24,7 @@ ctap-hid-fido2 = "3.5" # For fido2 interface operations
hidapi = "2.6" # For fido2 interface operations but non-standard commands
serde_cbor_2 = "0.13"
rand = "0.10"
bitflags = "2.10"
bitflags = "2.11"
ring = "0.17" # For signing fido2 messages with pin token
# For Application UI:
@@ -33,8 +33,9 @@ gpui-component = "0.5.1"
rust-embed = "8.11.0"
[profile.dev]
incremental = true # Compile your binary in smaller steps.
incremental = true # Compile your binary in smaller steps.
codegen-units = 256
opt-level = 1 # Prioritizes speed. Use `z` if you prefer small binary size.
[profile.release]
codegen-units = 1 # Allows LLVM to perform better optimization.
Generated
+3 -3
View File
@@ -20,11 +20,11 @@
},
"nixpkgs": {
"locked": {
"lastModified": 1770562336,
"narHash": "sha256-ub1gpAONMFsT/GU2hV6ZWJjur8rJ6kKxdm9IlCT0j84=",
"lastModified": 1771008912,
"narHash": "sha256-gf2AmWVTs8lEq7z/3ZAsgnZDhWIckkb+ZnAo5RzSxJg=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "d6c71932130818840fc8fe9509cf50be8c64634f",
"rev": "a82ccc39b39b621151d6732718e3e250109076fa",
"type": "github"
},
"original": {
+5 -5
View File
@@ -15,11 +15,11 @@ let
wayland-protocols
libunwind
libdrm
xorg.libX11
xorg.libXcursor
xorg.libXi
xorg.libXrandr
xorg.libxcb
libX11
libXcursor
libXi
libXrandr
libxcb
];
packages = with pkgs; [
+2 -2
View File
@@ -13,7 +13,7 @@ pub fn read_device_details() -> Result<FullDeviceStatus, PFError> {
}
}
pub async fn write_config(
pub fn write_config(
config: AppConfigInput,
method: DeviceMethod,
pin: Option<String>,
@@ -51,7 +51,7 @@ pub fn reboot(to_bootsel: bool) -> Result<String, PFError> {
rescue::reboot_device(to_bootsel)
}
pub async fn get_credentials(pin: String) -> Result<Vec<StoredCredential>, String> {
pub fn get_credentials(pin: String) -> Result<Vec<StoredCredential>, String> {
fido::get_credentials(pin)
}
-5
View File
@@ -10,11 +10,6 @@ mod device;
pub mod logging;
mod ui;
// NOTE: Hey this is me lockedmutex, right now the code quality of the entire ui module is shit okay, and this is because
// AI has been used to convert most of the frontend code from svelte+typescript to GPUI, while it has done most of the conversion,
// due to lack in complete context of code and the way it is structured, it has done a shit job. I did make a lot of changes to
// the code by myself, so it is not complete AI slop. Right now I am trading development time with code quality, I will later
// improve the code quality and correctly categorize the code into individial components.
fn main() {
logging::logger_init();
let app = Application::new().with_assets(ui::assets::Assets);
+6 -3
View File
@@ -68,14 +68,16 @@ impl<V: 'static> AppSidebar<V> {
v_flex()
.h_full()
.bg(rgb(colors::zinc::ZINC900))
.border_r_1()
.border_color(border_color)
.w(width)
.child({
let header = h_flex()
.w_full()
.items_center()
.bg(rgb(colors::zinc::ZINC900))
.border_r_1()
.border_color(border_color)
// .border_r_1()
// .border_color(border_color)
.pt_4();
let current_width = width;
@@ -126,6 +128,7 @@ impl<V: 'static> AppSidebar<V> {
.w_full()
.flex_grow()
.bg(rgb(colors::zinc::ZINC900))
.border_color(gpui::transparent_white())
.child(
SidebarGroup::new("Menu").child(
SidebarMenu::new()
@@ -172,7 +175,7 @@ impl<V: 'static> AppSidebar<V> {
v_flex()
.w_full()
.bg(rgb(0x111113))
.border_r_1()
// .border_r_1()
.border_t_1()
.border_color(border_color)
.p_2()
+1
View File
@@ -100,6 +100,7 @@ impl ApplicationRoot {
impl Render for ApplicationRoot {
fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
let target_width = if self.collapsed { px(48.) } else { px(255.) };
if (self.sidebar_width - target_width).abs() > px(0.1) {
self.sidebar_width = self.sidebar_width + (target_width - self.sidebar_width) * 0.2;
window.request_animation_frame();
+18 -3
View File
@@ -309,7 +309,20 @@ impl ConfigView {
let method = status.method.clone();
self._task = Some(cx.spawn(async move |_, cx| {
let result = io::write_config(changes, method, None).await;
let result = cx
.background_executor()
.spawn(async move { io::write_config(changes, method, None) })
.await;
let new_status_result = if result.is_ok() {
Some(
cx.background_executor()
.spawn(async move { io::read_device_details() })
.await,
)
} else {
None
};
let _ = entity.update(cx, |this, cx| {
this.loading = false;
@@ -318,7 +331,7 @@ impl ConfigView {
Ok(msg) => {
log::info!("Success: {}", msg);
if let Ok(new_status) = io::read_device_details() {
if let Some(Ok(new_status)) = new_status_result {
log::info!(
"Refreshed device status. LED Steady: {}",
new_status.config.led_steady
@@ -417,6 +430,7 @@ impl ConfigView {
v_flex().gap_2().child("Vendor ID (HEX)").child(
Input::new(&self.vid_input)
.font_family("Mono")
.bg(rgb(0x222225))
.disabled(!self.is_custom_vendor),
),
)
@@ -424,6 +438,7 @@ impl ConfigView {
v_flex().gap_2().child("Product ID (HEX)").child(
Input::new(&self.pid_input)
.font_family("Mono")
.bg(rgb(0x222225))
.disabled(!self.is_custom_vendor),
),
),
@@ -655,7 +670,7 @@ impl Render for ConfigView {
.child(options_card),
)
.child(
gpui_component::h_flex().justify_end().child(
gpui_component::h_flex().justify_end().pt_4().child(
Button::new("apply-changes")
.icon(Icon::default().path("icons/save.svg"))
.child("Apply Changes")
+3 -1
View File
@@ -103,11 +103,13 @@ impl Render for LogsView {
.flex_1()
.h(px(500.0))
.child(div().p_4().font_family("Mono").text_sm().child(
v_flex().gap_1().children(self.logs.iter().map(|log| {
v_flex().gap_0().children(self.logs.iter().map(|log| {
let color = if log.contains("ERROR") {
gpui::red()
} else if log.contains("WARN") {
gpui::yellow()
} else if log.contains("INFO") {
gpui::green()
} else {
theme.foreground
};
+14 -10
View File
@@ -88,12 +88,10 @@ impl PasskeysView {
self._task = Some(cx.spawn(async move |_, cx| {
let pin_for_bg = pin.clone();
// let result = cx
// .background_executor()
// .spawn(async move { io::get_credentials(pin_for_bg) })
// .await;
let result = io::get_credentials(pin_for_bg).await;
let result = cx
.background_executor()
.spawn(async move { io::get_credentials(pin_for_bg) })
.await;
let _ = entity.update(cx, |this, cx| {
this.loading = false;
@@ -158,7 +156,10 @@ impl PasskeysView {
fn refresh_credentials(&mut self, pin: String, cx: &mut Context<Self>) {
let entity = cx.entity().downgrade();
self._task = Some(cx.spawn(async move |_, cx| {
let result = io::get_credentials(pin).await;
let result = cx
.background_executor()
.spawn(async move { io::get_credentials(pin) })
.await;
let _ = entity.update(cx, |this, cx| {
this.loading = false;
@@ -187,6 +188,7 @@ impl PasskeysView {
.child(
v_flex()
.gap_4()
.pb_4()
.child("Enter your device PIN to view saved passkeys")
.child(Input::new(&pin_input)),
)
@@ -235,10 +237,10 @@ impl PasskeysView {
dialog
.confirm()
.title("Delete Passkey")
.child(format!(
.child(div().pb_4().child(format!(
"Are you sure you want to delete the passkey for {}?",
name
))
)))
.on_ok(move |_, _, cx| {
let _ = view_handle.update(cx, |this, cx| {
this.execute_delete(cred_id.clone(), pin_str.clone(), cx);
@@ -285,6 +287,7 @@ impl PasskeysView {
.child(
v_flex()
.gap_4()
.pb_4()
.child("Current PIN")
.child(Input::new(&current))
.child("New PIN")
@@ -395,6 +398,7 @@ impl PasskeysView {
.child(
v_flex()
.gap_4()
.pb_4()
.child(
v_flex()
.gap_2()
@@ -894,7 +898,7 @@ impl PasskeysView {
div()
.size_10()
.rounded_md()
.bg(theme.secondary)
.bg(rgb(0x3b3b3e))
.flex()
.items_center()
.justify_center()