From ac82875a3dad617371e8f1d2e012d7aba4c4b92a Mon Sep 17 00:00:00 2001 From: Joakim Hulthe Date: Sun, 28 Dec 2025 22:18:06 +0100 Subject: [PATCH] Add refresh button --- gui/src/main.rs | 89 ++++++++++++++++++++++++++-------------------- gui/ui/app.slint | 16 +++++++++ gui/ui/state.slint | 1 + 3 files changed, 68 insertions(+), 38 deletions(-) diff --git a/gui/src/main.rs b/gui/src/main.rs index 52a3a58..73faaae 100644 --- a/gui/src/main.rs +++ b/gui/src/main.rs @@ -19,7 +19,6 @@ type MagicAnyError = anyhow::Error; /// GET "/api/list" from the server async fn get_list(conf: &Mutex) -> Result, MagicAnyError> { - // TODO: don't hardcode url let base = Url::parse(&conf.lock().unwrap().address)?; let url = base.join("/api/list")?; let list = reqwest::get(url).await?.json::>().await?; @@ -62,37 +61,59 @@ fn update_slint_list(new_list: Vec, list: &VecModel) { #[tokio::main] async fn main() -> Result<(), MagicAnyError> { - let ui = App::new()?; - let state = ui.global::(); - let list = Rc::new(VecModel::default()); - state.set_list(ModelRc::new(list.clone())); + let app = App::new()?; + let state = app.global::(); + let list_shared = Rc::new(VecModel::default()); + state.set_list(ModelRc::new(list_shared.clone())); let conf = get_config() .await .inspect_err(|err| eprintln!("{err:?}")) .unwrap_or_default(); state.set_server_address(conf.address.to_shared_string()); - let conf = Arc::new(Mutex::new(conf)); + let conf_shared = Arc::new(Mutex::new(conf)); // HACK: load shopping list from server on start - let conf2 = conf.clone(); - match get_list(&conf2).await { - Ok(new_list) => update_slint_list(new_list, &list), + match get_list(&conf_shared).await { + Ok(new_list) => update_slint_list(new_list, &list_shared), Err(e) => { eprintln!("{e:?}"); } } - let list2 = list.clone(); - let conf2 = conf.clone(); + + let conf = conf_shared.clone(); + let app_weak = app.as_weak(); + state.on_refresh_list(move || { + let conf = conf.clone(); + let app_weak = app_weak.clone(); + tokio::spawn(async move { + let new_list = match get_list(&conf).await { + Ok(new_list) => new_list, + Err(e) => { + eprintln!("{e:?}"); + return; + } + }; + let _ = app_weak.upgrade_in_event_loop(|app| { + let list: ModelRc = app.global::().get_list(); + let list: &VecModel = + list.as_any().downcast_ref().expect("list is a VecModel"); + update_slint_list(new_list, &list); + }); + }); + }); + + let conf = conf_shared.clone(); + let list = list_shared.clone(); state.on_add_to_list(move |item| { - if list2.iter().any(|item2| item2.name == item) { + if list.iter().any(|item2| item2.name == item) { return; // TODO: implement increment when number is added } - list2.push(SItem { + list.push(SItem { checked: false, name: item.clone(), }); - let conf2 = conf2.clone(); + let conf = conf.clone(); tokio::spawn(async move { // TODO: add number to increment let res = put_list( @@ -101,7 +122,7 @@ async fn main() -> Result<(), MagicAnyError> { checked: false, amount: 1, }, - &conf2, + &conf, ) .await; if let Err(err) = res { @@ -110,10 +131,10 @@ async fn main() -> Result<(), MagicAnyError> { }); }); - let conf2 = conf.clone(); - let list2 = list.clone(); + let conf = conf_shared.clone(); + let list = list_shared.clone(); state.on_check_item(move |item| { - let positem = list2 + let positem = list .iter() .enumerate() .find(|(_, item2)| item2.name == item); @@ -122,8 +143,8 @@ async fn main() -> Result<(), MagicAnyError> { None => return, }; item.checked = !item.checked; - list2.set_row_data(pos, item.clone()); - let conf2 = conf2.clone(); + list.set_row_data(pos, item.clone()); + let conf = conf.clone(); tokio::spawn(async move { let res = put_list( @@ -132,7 +153,7 @@ async fn main() -> Result<(), MagicAnyError> { checked: item.checked, amount: 1, }, - &conf2, + &conf, ) .await; if let Err(err) = res { @@ -141,45 +162,37 @@ async fn main() -> Result<(), MagicAnyError> { }); }); - let conf2 = conf.clone(); - let list2 = list.clone(); + let conf = conf_shared.clone(); + let list = list_shared.clone(); state.on_delete_checked(move || { - let poss: Vec<_> = list2 + let poss: Vec<_> = list .iter() .enumerate() .filter(|(_, item)| item.checked) .map(|(pos, item)| (pos, item.name)) .collect(); for &(pos, _) in poss.iter().rev() { - list2.remove(pos); + list.remove(pos); } - let conf2 = conf2.clone(); + let conf = conf.clone(); tokio::spawn(async move { for (_, name) in poss { - let res = delete_item(&name, &conf2).await; + let res = delete_item(&name, &conf).await; if let Err(err) = res { eprintln!("{err:?}"); } } }); }); + state.on_set_server_address(move |address| { let _ = save_config(address.to_string()); - conf.lock().unwrap().address = address.to_string(); - - // create type for config file ✅ - // derives serde ✅ - // add library that gives us folder to save file in xdg typ ✅ - // write funciton that uses library to save file to folder place_config_file() ✅ - // call function on callback through tokio - // write function to load file get_config_file(path) finns i xdg ✅ - // call function on start ✅ - // use config on get put delete server stuffs. (instead of the static string) ✅ + conf_shared.lock().unwrap().address = address.to_string(); }); - ui.run()?; + app.run()?; Ok(()) } diff --git a/gui/ui/app.slint b/gui/ui/app.slint index de62420..53d3cdb 100644 --- a/gui/ui/app.slint +++ b/gui/ui/app.slint @@ -83,8 +83,22 @@ component GoodButtons inherits ScrollView { } } +component RefreshButton inherits HorizontalLayout { + alignment: center; + Button { + text: "Refresh"; + width: 40%; + clicked() => { + State.refresh-list(); + } + } +} + component AddView inherits VerticalBox { in property <[SItem]> list; + + RefreshButton {} + goods := GoodsList { items <=> list; } @@ -112,6 +126,8 @@ component AddView inherits VerticalBox { component ShopView inherits VerticalBox { in property <[SItem]> list; + RefreshButton {} + goods := GoodsList { items <=> list; } diff --git a/gui/ui/state.slint b/gui/ui/state.slint index 2cc1440..1105745 100644 --- a/gui/ui/state.slint +++ b/gui/ui/state.slint @@ -9,6 +9,7 @@ export global State { { name: "Test 2", checked: false }, { name: "Test 3", checked: false }, ]; + callback refresh-list(); callback add-to-list(string); callback check-item(string); callback delete-checked();