From 39527c18d778a58cb1cd6f80465f2d47cc6c000e Mon Sep 17 00:00:00 2001
From: Nikita Podvirnyi <krypt0nn@vk.com>
Date: Fri, 19 Jul 2024 12:28:41 +0200
Subject: [PATCH] feat: updated libraries versions

---
 Cargo.lock                   | 118 ++++++-----
 Cargo.toml                   |  10 +-
 src/background.rs            |   2 +-
 src/main.rs                  |   9 +-
 src/ui/components/version.rs |  49 +++--
 src/ui/main/download_diff.rs |  49 +++--
 src/ui/main/download_wine.rs |  78 ++++---
 src/ui/main/mod.rs           | 400 ++++++++++++++++++++---------------
 src/ui/main/repair_game.rs   |  23 +-
 9 files changed, 413 insertions(+), 325 deletions(-)

diff --git a/Cargo.lock b/Cargo.lock
index 510ff4c..34caaef 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -521,9 +521,9 @@ checksum = "ade8366b8bd5ba243f0a58f036cc0ca8a2f069cff1a2351ef1cac6b083e16fc0"
 
 [[package]]
 name = "cairo-rs"
-version = "0.19.4"
+version = "0.20.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b2ac2a4d0e69036cf0062976f6efcba1aaee3e448594e6514bb2ddf87acce562"
+checksum = "797fd5a634dcb0ad0d7d583df794deb0a236d88e759cd34b7da20198c6c9d145"
 dependencies = [
  "bitflags 2.6.0",
  "cairo-sys-rs",
@@ -534,9 +534,9 @@ dependencies = [
 
 [[package]]
 name = "cairo-sys-rs"
-version = "0.19.2"
+version = "0.20.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fd3bb3119664efbd78b5e6c93957447944f16bdbced84c17a9f41c7829b81e64"
+checksum = "428290f914b9b86089f60f5d8a9f6e440508e1bcff23b25afd51502b0a2da88f"
 dependencies = [
  "glib-sys",
  "libc",
@@ -1156,9 +1156,9 @@ dependencies = [
 
 [[package]]
 name = "gdk-pixbuf"
-version = "0.19.8"
+version = "0.20.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "624eaba126021103c7339b2e179ae4ee8cdab842daab419040710f38ed9f8699"
+checksum = "28bb53ecb56857c683c9ec859908e076dd3969c7d67598bd8b1ce095d211304a"
 dependencies = [
  "gdk-pixbuf-sys",
  "gio",
@@ -1168,9 +1168,9 @@ dependencies = [
 
 [[package]]
 name = "gdk-pixbuf-sys"
-version = "0.19.8"
+version = "0.20.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4efa05a4f83c8cc50eb4d883787b919b85e5f1d8dd10b5a1df53bf5689782379"
+checksum = "9f6681a0c1330d1d3968bec1529f7172d62819ef0bdbb0d18022320654158b03"
 dependencies = [
  "gio-sys",
  "glib-sys",
@@ -1181,9 +1181,9 @@ dependencies = [
 
 [[package]]
 name = "gdk4"
-version = "0.8.2"
+version = "0.9.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "db265c9dd42d6a371e09e52deab3a84808427198b86ac792d75fd35c07990a07"
+checksum = "4b7d7237c1487ed4b300aac7744efcbf1319e12d60d7afcd6f505414bd5b5dea"
 dependencies = [
  "cairo-rs",
  "gdk-pixbuf",
@@ -1196,9 +1196,9 @@ dependencies = [
 
 [[package]]
 name = "gdk4-sys"
-version = "0.8.2"
+version = "0.9.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c9418fb4e8a67074919fe7604429c45aa74eb9df82e7ca529767c6d4e9dc66dd"
+checksum = "a67576c8ec012156d7f680e201a807b4432a77babb3157e0555e990ab6bcd878"
 dependencies = [
  "cairo-sys-rs",
  "gdk-pixbuf-sys",
@@ -1242,9 +1242,9 @@ checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd"
 
 [[package]]
 name = "gio"
-version = "0.19.8"
+version = "0.20.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4c49f117d373ffcc98a35d114db5478bc223341cff53e39a5d6feced9e2ddffe"
+checksum = "398e3da68749fdc32783cbf7521ec3f65c9cf946db8c7774f8460af49e52c6e2"
 dependencies = [
  "futures-channel",
  "futures-core",
@@ -1260,9 +1260,9 @@ dependencies = [
 
 [[package]]
 name = "gio-sys"
-version = "0.19.8"
+version = "0.20.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2cd743ba4714d671ad6b6234e8ab2a13b42304d0e13ab7eba1dcdd78a7d6d4ef"
+checksum = "e4feb96b31c32730ea3e1e89aecd2e4e37ecb1c473ad8f685e3430a159419f63"
 dependencies = [
  "glib-sys",
  "gobject-sys",
@@ -1273,9 +1273,9 @@ dependencies = [
 
 [[package]]
 name = "glib"
-version = "0.19.9"
+version = "0.20.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "39650279f135469465018daae0ba53357942a5212137515777d5fdca74984a44"
+checksum = "fee90a615ce05be7a32932cfb8adf2c4bbb4700e80d37713c981fb24c0c56238"
 dependencies = [
  "bitflags 2.6.0",
  "futures-channel",
@@ -1295,18 +1295,18 @@ dependencies = [
 
 [[package]]
 name = "glib-build-tools"
-version = "0.19.0"
+version = "0.20.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "108f374fff60efd14b0d70d8916e7213aed18d7dd071ba3e9334ed2dac1dc86a"
+checksum = "7029c2651d9b5d5a3eea93ec8a1995665c6d3a69ce9bf6042ad9064d134736d8"
 dependencies = [
  "gio",
 ]
 
 [[package]]
 name = "glib-macros"
-version = "0.19.9"
+version = "0.20.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4429b0277a14ae9751350ad9b658b1be0abb5b54faa5bcdf6e74a3372582fad7"
+checksum = "4da558d8177c0c8c54368818b508a4244e1286fce2858cef4e547023f0cfa5ef"
 dependencies = [
  "heck",
  "proc-macro-crate",
@@ -1317,9 +1317,9 @@ dependencies = [
 
 [[package]]
 name = "glib-sys"
-version = "0.19.8"
+version = "0.20.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5c2dc18d3a82b0006d470b13304fbbb3e0a9bd4884cf985a60a7ed733ac2c4a5"
+checksum = "4958c26e5a01c9af00dea669a97369eccbec29a8e6d125c24ea2d85ee7467b60"
 dependencies = [
  "libc",
  "system-deps",
@@ -1340,9 +1340,9 @@ dependencies = [
 
 [[package]]
 name = "gobject-sys"
-version = "0.19.8"
+version = "0.20.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2e697e252d6e0416fd1d9e169bda51c0f1c926026c39ca21fbe8b1bb5c3b8b9e"
+checksum = "c6908864f5ffff15b56df7e90346863904f49b949337ed0456b9287af61903b8"
 dependencies = [
  "glib-sys",
  "libc",
@@ -1351,9 +1351,9 @@ dependencies = [
 
 [[package]]
 name = "graphene-rs"
-version = "0.19.8"
+version = "0.20.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f5fb86031d24d9ec0a2a15978fc7a65d545a2549642cf1eb7c3dda358da42bcf"
+checksum = "630e940ad5824f90221d6579043a9cd1f8bec86b4a17faaf7827d58eb16e8c1f"
 dependencies = [
  "glib",
  "graphene-sys",
@@ -1362,9 +1362,9 @@ dependencies = [
 
 [[package]]
 name = "graphene-sys"
-version = "0.19.8"
+version = "0.20.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2f530e0944bccba4b55065e9c69f4975ad691609191ebac16e13ab8e1f27af05"
+checksum = "6fb8fade7b754982f47ebbed241fd2680816fdd4598321784da10b9e1168836a"
 dependencies = [
  "glib-sys",
  "libc",
@@ -1374,9 +1374,9 @@ dependencies = [
 
 [[package]]
 name = "gsk4"
-version = "0.8.2"
+version = "0.9.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7563884bf6939f4468e5d94654945bdd9afcaf8c3ba4c5dd17b5342b747221be"
+checksum = "1f3cf2091e1af185b347b3450817d93dea6fe435df7abd4c2cd7fb5bcb4cfda8"
 dependencies = [
  "cairo-rs",
  "gdk4",
@@ -1389,9 +1389,9 @@ dependencies = [
 
 [[package]]
 name = "gsk4-sys"
-version = "0.8.2"
+version = "0.9.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "23024bf2636c38bbd1f822f58acc9d1c25b28da896ff0f291a1a232d4272b3dc"
+checksum = "6aa69614a26d8760c186c3690f1b0fbb917572ca23ef83137445770ceddf8cde"
 dependencies = [
  "cairo-sys-rs",
  "gdk4-sys",
@@ -1405,9 +1405,9 @@ dependencies = [
 
 [[package]]
 name = "gtk4"
-version = "0.8.2"
+version = "0.9.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b04e11319b08af11358ab543105a9e49b0c491faca35e2b8e7e36bfba8b671ab"
+checksum = "eaffc6c743c9160514cc9b67eace364e5dc5798369fa809cdb04e035c21c5c5d"
 dependencies = [
  "cairo-rs",
  "field-offset",
@@ -1426,9 +1426,9 @@ dependencies = [
 
 [[package]]
 name = "gtk4-macros"
-version = "0.8.2"
+version = "0.9.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ec655a7ef88d8ce9592899deb8b2d0fa50bab1e6dd69182deb764e643c522408"
+checksum = "188211f546ce5801f6d0245c37b6249143a2cb4fa040e54829ca1e76796e9f09"
 dependencies = [
  "proc-macro-crate",
  "proc-macro2",
@@ -1438,9 +1438,9 @@ dependencies = [
 
 [[package]]
 name = "gtk4-sys"
-version = "0.8.2"
+version = "0.9.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8c8aa86b7f85ea71d66ea88c1d4bae1cfacf51ca4856274565133838d77e57b5"
+checksum = "1114a207af8ada02cf4658a76692f4190f06f093380d5be07e3ca8b43aa7c666"
 dependencies = [
  "cairo-sys-rs",
  "gdk-pixbuf-sys",
@@ -1656,11 +1656,10 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
 
 [[package]]
 name = "libadwaita"
-version = "0.6.0"
+version = "0.7.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "91b4990248b9e1ec5e72094a2ccaea70ec3809f88f6fd52192f2af306b87c5d9"
+checksum = "2ff9c222b5c783729de45185f07b2fec2d43a7f9c63961e777d3667e20443878"
 dependencies = [
- "gdk-pixbuf",
  "gdk4",
  "gio",
  "glib",
@@ -1672,9 +1671,9 @@ dependencies = [
 
 [[package]]
 name = "libadwaita-sys"
-version = "0.6.0"
+version = "0.7.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "23a748e4e92be1265cd9e93d569c0b5dfc7814107985aa6743d670ab281ea1a8"
+checksum = "1c44d8bdbad31d6639e1f20cc9c1424f1a8e02d751fc28d44659bf743fb9eca6"
 dependencies = [
  "gdk4-sys",
  "gio-sys",
@@ -1969,9 +1968,9 @@ checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39"
 
 [[package]]
 name = "pango"
-version = "0.19.8"
+version = "0.20.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3f0d328648058085cfd6897c9ae4272884098a926f3a833cd50c8c73e6eccecd"
+checksum = "54768854025df6903061d0084fd9702a253ddfd60db7d9b751d43b76689a7f0a"
 dependencies = [
  "gio",
  "glib",
@@ -1981,9 +1980,9 @@ dependencies = [
 
 [[package]]
 name = "pango-sys"
-version = "0.19.8"
+version = "0.20.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ff03da4fa086c0b244d4a4587d3e20622a3ecdb21daea9edf66597224c634ba0"
+checksum = "b07cc57d10cee4ec661f718a6902cee18c2f4cfae08e87e5a390525946913390"
 dependencies = [
  "glib-sys",
  "gobject-sys",
@@ -2198,9 +2197,9 @@ checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b"
 
 [[package]]
 name = "relm4"
-version = "0.8.1"
+version = "0.9.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e6e0e187b58db367305e8486d3228158251da1c8ba1e18baa9de61894e822649"
+checksum = "cf0363f92b6a7eefd985b47f27b7ae168dd2fd5cd4013a338c9b111c33744d1f"
 dependencies = [
  "flume",
  "fragile",
@@ -2208,16 +2207,23 @@ dependencies = [
  "gtk4",
  "libadwaita",
  "once_cell",
+ "relm4-css",
  "relm4-macros",
  "tokio",
  "tracing",
 ]
 
 [[package]]
-name = "relm4-macros"
-version = "0.8.1"
+name = "relm4-css"
+version = "0.9.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0774e846889823aa5766f5b62cface3189a5b36280e65b2faaa6df0319da1726"
+checksum = "1d3b924557df1cddc687b60b313c4b76620fdbf0e463afa4b29f67193ccf37f9"
+
+[[package]]
+name = "relm4-macros"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fc5885640821d60062497737dd42fd04248d13c7ecccee620caaa4b210fe9905"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -2589,9 +2595,9 @@ dependencies = [
 
 [[package]]
 name = "system-deps"
-version = "6.2.2"
+version = "7.0.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a3e535eb8dded36d55ec13eddacd30dec501792ff23a0b1682c38601b8cf2349"
+checksum = "6c81f13d9a334a6c242465140bd262fae382b752ff2011c4f7419919a9c97922"
 dependencies = [
  "cfg-expr",
  "heck",
diff --git a/Cargo.toml b/Cargo.toml
index bb82445..9c98078 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -15,7 +15,7 @@ lto = true
 opt-level = "s"
 
 [build-dependencies]
-glib-build-tools = "0.19"
+glib-build-tools = "0.20"
 
 [dependencies.anime-launcher-sdk]
 git = "https://github.com/an-anime-team/anime-launcher-sdk"
@@ -25,12 +25,12 @@ features = ["all", "genshin"]
 # path = "../anime-launcher-sdk" # ! for dev purposes only
 
 [dependencies]
-relm4 = { version = "0.8.1", features = ["macros", "libadwaita"] }
-gtk = { package = "gtk4", version = "0.8.2", features = ["v4_12"] }
-adw = { package = "libadwaita", version = "0.6.0", features = ["v1_4"] }
+relm4 = { version = "0.9.0", features = ["macros", "libadwaita"] }
+gtk = { package = "gtk4", version = "0.9.0", features = ["v4_12"] }
+adw = { package = "libadwaita", version = "0.7.0", features = ["v1_4"] }
 
 rfd = { version = "0.14.1", features = ["xdg-portal", "tokio"], default-features = false }
-open = "5.2.0"
+open = "5.3.0"
 whatadistro = "0.1.0"
 
 serde_json = "1.0"
diff --git a/src/background.rs b/src/background.rs
index 0f8e33a..bcef26f 100644
--- a/src/background.rs
+++ b/src/background.rs
@@ -21,7 +21,7 @@ pub fn get_uri() -> String {
     else {
         let uri = concat!("https://sg-hyp-api.", "ho", "yo", "verse", ".com/hyp/hyp-connect/api/getAllGameBasicInfo?launcher_id=VYTpXlbWo8&language=");
 
-        uri.to_owned() + &crate::i18n::format_lang(&lang)
+        uri.to_owned() + &crate::i18n::format_lang(lang)
     }
 }
 
diff --git a/src/main.rs b/src/main.rs
index 24fc643..868d7e8 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -201,6 +201,9 @@ fn main() -> anyhow::Result<()> {
     gtk::IconTheme::for_display(&gtk::gdk::Display::default().unwrap())
         .add_resource_path(&format!("{APP_RESOURCE_PATH}/icons"));
 
+    // Set global css
+    relm4::set_global_css(&GLOBAL_CSS);
+
     // Set application's title
     gtk::glib::set_application_name("An Anime Game Launcher");
     gtk::glib::set_program_name(Some("An Anime Game Launcher"));
@@ -218,9 +221,6 @@ fn main() -> anyhow::Result<()> {
         let app = RelmApp::new(APP_ID)
             .with_args(gtk_args);
 
-        // Set global css
-        app.set_global_css(&GLOBAL_CSS);
-
         // Show first run window
         app.run::<FirstRunApp>(());
     }
@@ -252,9 +252,6 @@ fn main() -> anyhow::Result<()> {
         let app = RelmApp::new(APP_ID)
             .with_args(gtk_args);
 
-        // Set global css
-        app.set_global_css(&GLOBAL_CSS);
-
         // Show main window
         app.run::<App>(());
     }
diff --git a/src/ui/components/version.rs b/src/ui/components/version.rs
index ccc6089..81572b2 100644
--- a/src/ui/components/version.rs
+++ b/src/ui/components/version.rs
@@ -162,33 +162,38 @@ impl SimpleAsyncComponent for ComponentVersion {
                             let progress_bar_sender = self.progress_bar.sender().clone();
 
                             #[allow(unused_must_use)]
-                            std::thread::spawn(clone!(@strong self.download_folder as download_folder => move || {
-                                progress_bar_sender.send(ProgressBarMsg::Reset);
-                                progress_bar_sender.send(ProgressBarMsg::SetVisible(true));
+                            std::thread::spawn(clone!(
+                                #[strong(rename_to = download_folder)]
+                                self.download_folder,
 
-                                installer.install(download_folder, move |state| {
-                                    match &state {
-                                        InstallerUpdate::UnpackingFinished |
-                                        InstallerUpdate::DownloadingError(_) |
-                                        InstallerUpdate::UnpackingError(_) => {
-                                            progress_bar_sender.send(ProgressBarMsg::SetVisible(false));
+                                move || {
+                                    progress_bar_sender.send(ProgressBarMsg::Reset);
+                                    progress_bar_sender.send(ProgressBarMsg::SetVisible(true));
 
-                                            if let InstallerUpdate::UnpackingFinished = &state {
-                                                sender.input(ComponentVersionMsg::SetState(VersionState::Downloaded));
-                                                sender.output(ComponentGroupMsg::CallOnDownloaded);
-                                            }
+                                    installer.install(download_folder, move |state| {
+                                        match &state {
+                                            InstallerUpdate::UnpackingFinished |
+                                            InstallerUpdate::DownloadingError(_) |
+                                            InstallerUpdate::UnpackingError(_) => {
+                                                progress_bar_sender.send(ProgressBarMsg::SetVisible(false));
 
-                                            else {
-                                                sender.input(ComponentVersionMsg::SetState(VersionState::NotDownloaded));
-                                            }
-                                        },
+                                                if let InstallerUpdate::UnpackingFinished = &state {
+                                                    sender.input(ComponentVersionMsg::SetState(VersionState::Downloaded));
+                                                    sender.output(ComponentGroupMsg::CallOnDownloaded);
+                                                }
 
-                                        _ => ()
-                                    }
+                                                else {
+                                                    sender.input(ComponentVersionMsg::SetState(VersionState::NotDownloaded));
+                                                }
+                                            },
 
-                                    progress_bar_sender.send(ProgressBarMsg::UpdateFromState(DiffUpdate::InstallerUpdate(state)));
-                                });
-                            }));
+                                            _ => ()
+                                        }
+
+                                        progress_bar_sender.send(ProgressBarMsg::UpdateFromState(DiffUpdate::InstallerUpdate(state)));
+                                    });
+                                }
+                            ));
                         }
                     }
 
diff --git a/src/ui/main/download_diff.rs b/src/ui/main/download_diff.rs
index cf58f25..f6ff382 100644
--- a/src/ui/main/download_diff.rs
+++ b/src/ui/main/download_diff.rs
@@ -21,33 +21,38 @@ pub fn download_diff(sender: ComponentSender<App>, progress_bar_input: Sender<Pr
             diff = diff.with_temp_folder(temp);
         }
 
-        let result = diff.install_to(game_path, clone!(@strong sender => move |state| {
-            match &state {
-                DiffUpdate::InstallerUpdate(InstallerUpdate::DownloadingError(err)) => {
-                    tracing::error!("Downloading failed: {err}");
+        let result = diff.install_to(game_path, clone!(
+            #[strong]
+            sender,
 
-                    sender.input(AppMsg::Toast {
-                        title: tr!("downloading-failed"),
-                        description: Some(err.to_string())
-                    });
+            move |state| {
+                match &state {
+                    DiffUpdate::InstallerUpdate(InstallerUpdate::DownloadingError(err)) => {
+                        tracing::error!("Downloading failed: {err}");
+
+                        sender.input(AppMsg::Toast {
+                            title: tr!("downloading-failed"),
+                            description: Some(err.to_string())
+                        });
+                    }
+
+                    DiffUpdate::InstallerUpdate(InstallerUpdate::UnpackingError(err)) => {
+                        tracing::error!("Unpacking failed: {err}");
+
+                        sender.input(AppMsg::Toast {
+                            title: tr!("unpacking-failed"),
+                            description: Some(err.clone())
+                        });
+                    }
+
+                    _ => ()
                 }
 
-                DiffUpdate::InstallerUpdate(InstallerUpdate::UnpackingError(err)) => {
-                    tracing::error!("Unpacking failed: {err}");
-
-                    sender.input(AppMsg::Toast {
-                        title: tr!("unpacking-failed"),
-                        description: Some(err.clone())
-                    });
+                #[allow(unused_must_use)] {
+                    progress_bar_input.send(ProgressBarMsg::UpdateFromState(state));
                 }
-
-                _ => ()
             }
-
-            #[allow(unused_must_use)] {
-                progress_bar_input.send(ProgressBarMsg::UpdateFromState(state));
-            }
-        }));
+        ));
 
         let mut perform_on_download_needed = true;
 
diff --git a/src/ui/main/download_wine.rs b/src/ui/main/download_wine.rs
index 06f2414..69898f2 100644
--- a/src/ui/main/download_wine.rs
+++ b/src/ui/main/download_wine.rs
@@ -52,45 +52,55 @@ pub fn download_wine(sender: ComponentSender<App>, progress_bar_input: Sender<Pr
 
                         sender.input(AppMsg::SetDownloading(true));
 
-                        std::thread::spawn(clone!(@strong sender => move || {
-                            installer.install(&config.game.wine.builds, clone!(@strong sender => move |state| {
-                                match &state {
-                                    InstallerUpdate::DownloadingError(err) => {
-                                        tracing::error!("Downloading failed: {err}");
+                        std::thread::spawn(clone!(
+                            #[strong]
+                            sender,
 
-                                        sender.input(AppMsg::Toast {
-                                            title: tr!("downloading-failed"),
-                                            description: Some(err.to_string())
-                                        });
+                            move || {
+                                installer.install(&config.game.wine.builds, clone!(
+                                    #[strong]
+                                    sender,
+
+                                    move |state| {
+                                        match &state {
+                                            InstallerUpdate::DownloadingError(err) => {
+                                                tracing::error!("Downloading failed: {err}");
+
+                                                sender.input(AppMsg::Toast {
+                                                    title: tr!("downloading-failed"),
+                                                    description: Some(err.to_string())
+                                                });
+                                            }
+
+                                            InstallerUpdate::UnpackingError(err) => {
+                                                tracing::error!("Unpacking failed: {err}");
+
+                                                sender.input(AppMsg::Toast {
+                                                    title: tr!("unpacking-failed"),
+                                                    description: Some(err.clone())
+                                                });
+                                            }
+
+                                            _ => ()
+                                        }
+
+                                        #[allow(unused_must_use)] {
+                                            progress_bar_input.send(ProgressBarMsg::UpdateFromState(DiffUpdate::InstallerUpdate(state)));
+                                        }
                                     }
+                                ));
 
-                                    InstallerUpdate::UnpackingError(err) => {
-                                        tracing::error!("Unpacking failed: {err}");
+                                config.game.wine.selected = Some(wine.name.clone());
 
-                                        sender.input(AppMsg::Toast {
-                                            title: tr!("unpacking-failed"),
-                                            description: Some(err.clone())
-                                        });
-                                    }
+                                Config::update(config);
 
-                                    _ => ()
-                                }
-
-                                #[allow(unused_must_use)] {
-                                    progress_bar_input.send(ProgressBarMsg::UpdateFromState(DiffUpdate::InstallerUpdate(state)));
-                                }
-                            }));
-
-                            config.game.wine.selected = Some(wine.name.clone());
-
-                            Config::update(config);
-
-                            sender.input(AppMsg::SetDownloading(false));
-                            sender.input(AppMsg::UpdateLauncherState {
-                                perform_on_download_needed: false,
-                                show_status_page: true
-                            });
-                        }));
+                                sender.input(AppMsg::SetDownloading(false));
+                                sender.input(AppMsg::UpdateLauncherState {
+                                    perform_on_download_needed: false,
+                                    show_status_page: true
+                                });
+                            }
+                        ));
                     }
 
                     Err(err) => sender.input(AppMsg::Toast {
diff --git a/src/ui/main/mod.rs b/src/ui/main/mod.rs
index 67965a3..ab2379f 100644
--- a/src/ui/main/mod.rs
+++ b/src/ui/main/mod.rs
@@ -516,11 +516,16 @@ impl SimpleComponent for App {
                                         connect_clicked[sender] => move |_| {
                                             sender.input(AppMsg::DisableKillGameButton(true));
 
-                                            std::thread::spawn(clone!(@strong sender => move || {
-                                                std::thread::sleep(std::time::Duration::from_secs(3));
+                                            std::thread::spawn(clone!(
+                                                #[strong]
+                                                sender,
 
-                                                sender.input(AppMsg::DisableKillGameButton(false));
-                                            }));
+                                                move || {
+                                                    std::thread::sleep(std::time::Duration::from_secs(3));
+
+                                                    sender.input(AppMsg::DisableKillGameButton(false));
+                                                }
+                                            ));
 
                                             let result = std::process::Command::new("pkill")
                                                 .arg("-f") // full text search
@@ -676,134 +681,164 @@ impl SimpleComponent for App {
 
         // TODO: reduce code somehow
 
-        group.add_action::<LauncherFolder>(RelmAction::new_stateless(clone!(@strong sender => move |_| {
-            if let Err(err) = open::that(LAUNCHER_FOLDER.as_path()) {
-                sender.input(AppMsg::Toast {
-                    title: tr!("launcher-folder-opening-error"),
-                    description: Some(err.to_string())
-                });
+        group.add_action::<LauncherFolder>(RelmAction::new_stateless(clone!(
+            #[strong]
+            sender,
 
-                tracing::error!("Failed to open launcher folder: {err}");
-            }
-        })));
-
-        group.add_action::<GameFolder>(RelmAction::new_stateless(clone!(@strong sender => move |_| {
-            let path = match Config::get() {
-                Ok(config) => config.game.path.for_edition(config.launcher.edition).to_path_buf(),
-                Err(_) => CONFIG.game.path.for_edition(CONFIG.launcher.edition).to_path_buf(),
-            };
-
-            if let Err(err) = open::that(path) {
-                sender.input(AppMsg::Toast {
-                    title: tr!("game-folder-opening-error"),
-                    description: Some(err.to_string())
-                });
-
-                tracing::error!("Failed to open game folder: {err}");
-            }
-        })));
-
-        group.add_action::<ConfigFile>(RelmAction::new_stateless(clone!(@strong sender => move |_| {
-            if let Ok(file) = config_file() {
-                if let Err(err) = open::that(file) {
+            move |_| {
+                if let Err(err) = open::that(LAUNCHER_FOLDER.as_path()) {
                     sender.input(AppMsg::Toast {
-                        title: tr!("config-file-opening-error"),
+                        title: tr!("launcher-folder-opening-error"),
                         description: Some(err.to_string())
                     });
 
-                    tracing::error!("Failed to open config file: {err}");
+                    tracing::error!("Failed to open launcher folder: {err}");
                 }
             }
-        })));
+        )));
 
-        group.add_action::<DebugFile>(RelmAction::new_stateless(clone!(@strong sender => move |_| {
-            if let Err(err) = open::that(crate::DEBUG_FILE.as_os_str()) {
-                sender.input(AppMsg::Toast {
-                    title: tr!("debug-file-opening-error"),
-                    description: Some(err.to_string())
-                });
+        group.add_action::<GameFolder>(RelmAction::new_stateless(clone!(
+            #[strong]
+            sender,
 
-                tracing::error!("Failed to open debug file: {err}");
+            move |_| {
+                let path = match Config::get() {
+                    Ok(config) => config.game.path.for_edition(config.launcher.edition).to_path_buf(),
+                    Err(_) => CONFIG.game.path.for_edition(CONFIG.launcher.edition).to_path_buf(),
+                };
+
+                if let Err(err) = open::that(path) {
+                    sender.input(AppMsg::Toast {
+                        title: tr!("game-folder-opening-error"),
+                        description: Some(err.to_string())
+                    });
+
+                    tracing::error!("Failed to open game folder: {err}");
+                }
             }
-        })));
+        )));
 
-        group.add_action::<WishUrl>(RelmAction::new_stateless(clone!(@strong sender => move |_| {
-            std::thread::spawn(clone!(@strong sender => move || {
-                let config = Config::get().unwrap_or_else(|_| CONFIG.clone());
+        group.add_action::<ConfigFile>(RelmAction::new_stateless(clone!(
+            #[strong]
+            sender,
 
-                let web_cache = config.game.path.for_edition(config.launcher.edition)
-                    .join(config.launcher.edition.data_folder())
-                    .join("webCaches");
+            move |_| {
+                if let Ok(file) = config_file() {
+                    if let Err(err) = open::that(file) {
+                        sender.input(AppMsg::Toast {
+                            title: tr!("config-file-opening-error"),
+                            description: Some(err.to_string())
+                        });
 
-                // Find newest cache folder
-                let mut web_cache_id = None;
-
-                if let Ok(entries) = web_cache.read_dir() {
-                    for entry in entries.flatten() {
-                        if entry.path().is_dir() &&
-                           entry.file_name().to_string_lossy().trim_matches(|c| "0123456789.".contains(c)).is_empty() &&
-                           Some(entry.file_name()) > web_cache_id
-                        {
-                            web_cache_id = Some(entry.file_name());
-                        }
+                        tracing::error!("Failed to open config file: {err}");
                     }
                 }
+            }
+        )));
 
-                if let Some(web_cache_id) = web_cache_id {
-                    let web_cache = web_cache
-                        .join(web_cache_id)
-                        .join("Cache/Cache_Data/data_2");
+        group.add_action::<DebugFile>(RelmAction::new_stateless(clone!(
+            #[strong]
+            sender,
 
-                    match std::fs::read(web_cache) {
-                        Ok(web_cache) => {
-                            let web_cache = String::from_utf8_lossy(&web_cache);
+            move |_| {
+                if let Err(err) = open::that(crate::DEBUG_FILE.as_os_str()) {
+                    sender.input(AppMsg::Toast {
+                        title: tr!("debug-file-opening-error"),
+                        description: Some(err.to_string())
+                    });
 
-                            // https://webstatic-sea.[ho-yo-ver-se].com/[ge-nsh-in]/event/e20190909gacha-v2/index.html?......
-                            if let Some(url) = web_cache.lines().rev().find(|line| line.contains("gacha-v3/index.html")) {
-                                let url_begin_pos = url.find("https://").unwrap();
-                                let url_end_pos = url_begin_pos + url[url_begin_pos..].find("\0\0\0\0").unwrap();
+                    tracing::error!("Failed to open debug file: {err}");
+                }
+            }
+        )));
 
-                                if let Err(err) = open::that(format!("{}#/log", &url[url_begin_pos..url_end_pos])) {
-                                    tracing::error!("Failed to open wishes URL: {err}");
+        group.add_action::<WishUrl>(RelmAction::new_stateless(clone!(
+            #[strong]
+            sender,
+
+            move |_| {
+                std::thread::spawn(clone!(
+                    #[strong]
+                    sender,
+
+                    move || {
+                        let config = Config::get().unwrap_or_else(|_| CONFIG.clone());
+
+                        let web_cache = config.game.path.for_edition(config.launcher.edition)
+                            .join(config.launcher.edition.data_folder())
+                            .join("webCaches");
+
+                        // Find newest cache folder
+                        let mut web_cache_id = None;
+
+                        if let Ok(entries) = web_cache.read_dir() {
+                            for entry in entries.flatten() {
+                                if entry.path().is_dir() &&
+                                entry.file_name().to_string_lossy().trim_matches(|c| "0123456789.".contains(c)).is_empty() &&
+                                Some(entry.file_name()) > web_cache_id
+                                {
+                                    web_cache_id = Some(entry.file_name());
+                                }
+                            }
+                        }
+
+                        if let Some(web_cache_id) = web_cache_id {
+                            let web_cache = web_cache
+                                .join(web_cache_id)
+                                .join("Cache/Cache_Data/data_2");
+
+                            match std::fs::read(web_cache) {
+                                Ok(web_cache) => {
+                                    let web_cache = String::from_utf8_lossy(&web_cache);
+
+                                    // https://webstatic-sea.[ho-yo-ver-se].com/[ge-nsh-in]/event/e20190909gacha-v2/index.html?......
+                                    if let Some(url) = web_cache.lines().rev().find(|line| line.contains("gacha-v3/index.html")) {
+                                        let url_begin_pos = url.find("https://").unwrap();
+                                        let url_end_pos = url_begin_pos + url[url_begin_pos..].find("\0\0\0\0").unwrap();
+
+                                        if let Err(err) = open::that(format!("{}#/log", &url[url_begin_pos..url_end_pos])) {
+                                            tracing::error!("Failed to open wishes URL: {err}");
+
+                                            sender.input(AppMsg::Toast {
+                                                title: tr!("wish-url-opening-error"),
+                                                description: Some(err.to_string())
+                                            });
+                                        }
+                                    }
+
+                                    else {
+                                        tracing::error!("Couldn't find wishes URL: no url found");
+
+                                        sender.input(AppMsg::Toast {
+                                            title: tr!("wish-url-search-failed"),
+                                            description: None
+                                        });
+                                    }
+                                }
+
+                                Err(err) => {
+                                    tracing::error!("Couldn't find wishes URL: failed to open cache file: {err}");
 
                                     sender.input(AppMsg::Toast {
-                                        title: tr!("wish-url-opening-error"),
+                                        title: tr!("wish-url-search-failed"),
                                         description: Some(err.to_string())
                                     });
                                 }
                             }
-
-                            else {
-                                tracing::error!("Couldn't find wishes URL: no url found");
-
-                                sender.input(AppMsg::Toast {
-                                    title: tr!("wish-url-search-failed"),
-                                    description: None
-                                });
-                            }
                         }
 
-                        Err(err) => {
-                            tracing::error!("Couldn't find wishes URL: failed to open cache file: {err}");
+                        else {
+                            tracing::error!("Couldn't find wishes URL: cache file doesn't exist");
 
                             sender.input(AppMsg::Toast {
                                 title: tr!("wish-url-search-failed"),
-                                description: Some(err.to_string())
+                                description: None
                             });
                         }
                     }
-                }
-
-                else {
-                    tracing::error!("Couldn't find wishes URL: cache file doesn't exist");
-
-                    sender.input(AppMsg::Toast {
-                        title: tr!("wish-url-search-failed"),
-                        description: None
-                    });
-                }
-            }));
-        })));
+                ));
+            }
+        )));
 
         group.add_action::<About>(RelmAction::new_stateless(move |_| {
             about_dialog_broker.send(AboutDialogMsg::Show);
@@ -824,87 +859,102 @@ impl SimpleComponent for App {
             // Download background picture if needed
 
             if download_picture {
-                tasks.push(std::thread::spawn(clone!(@strong sender => move || {
-                    if let Err(err) = crate::background::download_background() {
-                        tracing::error!("Failed to download background picture: {err}");
+                tasks.push(std::thread::spawn(clone!(
+                    #[strong]
+                    sender,
 
-                        sender.input(AppMsg::Toast {
-                            title: tr!("background-downloading-failed"),
-                            description: Some(err.to_string())
-                        });
+                    move || {
+                        if let Err(err) = crate::background::download_background() {
+                            tracing::error!("Failed to download background picture: {err}");
+
+                            sender.input(AppMsg::Toast {
+                                title: tr!("background-downloading-failed"),
+                                description: Some(err.to_string())
+                            });
+                        }
                     }
-                })));
+                )));
             }
 
             // Update components index
 
-            tasks.push(std::thread::spawn(clone!(@strong sender => move || {
-                let components = ComponentsLoader::new(&CONFIG.components.path);
+            tasks.push(std::thread::spawn(clone!(
+                #[strong]
+                sender,
 
-                match components.is_sync(&CONFIG.components.servers) {
-                    Ok(Some(_)) => (),
+                move || {
+                    let components = ComponentsLoader::new(&CONFIG.components.path);
 
-                    Ok(None) => {
-                        for host in &CONFIG.components.servers {
-                            match components.sync(host) {
-                                Ok(changes) => {
-                                    sender.input(AppMsg::Toast {
-                                        title: tr!("components-index-updated"),
-                                        description: if changes.is_empty() {
-                                            None
-                                        } else {
-                                            Some(changes.into_iter()
-                                                .map(|line| format!("- {line}"))
-                                                .collect::<Vec<_>>()
-                                                .join("\n"))
-                                        }
-                                    });
+                    match components.is_sync(&CONFIG.components.servers) {
+                        Ok(Some(_)) => (),
 
-                                    break;
-                                }
+                        Ok(None) => {
+                            for host in &CONFIG.components.servers {
+                                match components.sync(host) {
+                                    Ok(changes) => {
+                                        sender.input(AppMsg::Toast {
+                                            title: tr!("components-index-updated"),
+                                            description: if changes.is_empty() {
+                                                None
+                                            } else {
+                                                Some(changes.into_iter()
+                                                    .map(|line| format!("- {line}"))
+                                                    .collect::<Vec<_>>()
+                                                    .join("\n"))
+                                            }
+                                        });
 
-                                Err(err) => {
-                                    tracing::error!("Failed to sync components index");
+                                        break;
+                                    }
 
-                                    sender.input(AppMsg::Toast {
-                                        title: tr!("components-index-sync-failed"),
-                                        description: Some(err.to_string())
-                                    });
+                                    Err(err) => {
+                                        tracing::error!("Failed to sync components index");
+
+                                        sender.input(AppMsg::Toast {
+                                            title: tr!("components-index-sync-failed"),
+                                            description: Some(err.to_string())
+                                        });
+                                    }
                                 }
                             }
                         }
-                    }
 
-                    Err(err) => {
-                        tracing::error!("Failed to verify that components index synced");
+                        Err(err) => {
+                            tracing::error!("Failed to verify that components index synced");
 
-                        sender.input(AppMsg::Toast {
-                            title: tr!("components-index-verify-failed"),
-                            description: Some(err.to_string())
-                        });
+                            sender.input(AppMsg::Toast {
+                                title: tr!("components-index-verify-failed"),
+                                description: Some(err.to_string())
+                            });
+                        }
                     }
                 }
-            })));
+            )));
 
             // Update initial game version status
 
-            tasks.push(std::thread::spawn(clone!(@strong sender => move || {
-                sender.input(AppMsg::SetGameDiff(match GAME.try_get_diff() {
-                    Ok(diff) => Some(diff),
-                    Err(err) => {
-                        tracing::error!("Failed to find game diff: {err}");
+            tasks.push(std::thread::spawn(clone!(
+                #[strong]
+                sender,
 
-                        sender.input(AppMsg::Toast {
-                            title: tr!("game-diff-finding-error"),
-                            description: Some(err.to_string())
-                        });
+                move || {
+                    sender.input(AppMsg::SetGameDiff(match GAME.try_get_diff() {
+                        Ok(diff) => Some(diff),
+                        Err(err) => {
+                            tracing::error!("Failed to find game diff: {err}");
 
-                        None
-                    }
-                }));
+                            sender.input(AppMsg::Toast {
+                                title: tr!("game-diff-finding-error"),
+                                description: Some(err.to_string())
+                            });
 
-                tracing::info!("Updated game version status");
-            })));
+                            None
+                        }
+                    }));
+
+                    tracing::info!("Updated game version status");
+                }
+            )));
 
             // Await for tasks to finish execution
             for task in tasks {
@@ -938,21 +988,26 @@ impl SimpleComponent for App {
                     self.disabled_buttons = true;
                 }
 
-                let updater = clone!(@strong sender => move |state| {
-                    if show_status_page {
-                        match state {
-                            StateUpdating::Game => {
-                                sender.input(AppMsg::SetLoadingStatus(Some(Some(tr!("loading-launcher-state--game")))));
-                            }
+                let updater = clone!(
+                    #[strong]
+                    sender,
 
-                            StateUpdating::Voice(locale) => {
-                                sender.input(AppMsg::SetLoadingStatus(Some(Some(tr!("loading-launcher-state--voice", {
-                                    "locale" = locale.to_name()
-                                })))));
+                    move |state| {
+                        if show_status_page {
+                            match state {
+                                StateUpdating::Game => {
+                                    sender.input(AppMsg::SetLoadingStatus(Some(Some(tr!("loading-launcher-state--game")))));
+                                }
+
+                                StateUpdating::Voice(locale) => {
+                                    sender.input(AppMsg::SetLoadingStatus(Some(Some(tr!("loading-launcher-state--voice", {
+                                        "locale" = locale.to_name()
+                                    })))));
+                                }
                             }
                         }
                     }
-                });
+                );
 
                 let state = match LauncherState::get_from_config(updater) {
                     Ok(state) => Some(state),
@@ -1043,9 +1098,14 @@ impl SimpleComponent for App {
 
                     std::thread::spawn(move || {
                         for mut diff in diffs {
-                            let result = diff.download_to(&tmp, clone!(@strong progress_bar_input => move |curr, total| {
-                                progress_bar_input.send(ProgressBarMsg::UpdateProgress(curr, total));
-                            }));
+                            let result = diff.download_to(&tmp, clone!(
+                                #[strong]
+                                progress_bar_input,
+
+                                move |curr, total| {
+                                    progress_bar_input.send(ProgressBarMsg::UpdateProgress(curr, total));
+                                }
+                            ));
 
                             if let Err(err) = result {
                                 sender.input(AppMsg::Toast {
diff --git a/src/ui/main/repair_game.rs b/src/ui/main/repair_game.rs
index c3db008..0296374 100644
--- a/src/ui/main/repair_game.rs
+++ b/src/ui/main/repair_game.rs
@@ -62,17 +62,22 @@ pub fn repair_game(sender: ComponentSender<App>, progress_bar_input: Sender<Prog
 
                     let thread_sender = verify_sender.clone();
 
-                    std::thread::spawn(clone!(@strong game_path => move || {
-                        for file in thread_files {
-                            let status = if config.launcher.repairer.fast {
-                                file.fast_verify(&game_path)
-                            } else {
-                                file.verify(&game_path)
-                            };
+                    std::thread::spawn(clone!(
+                        #[strong]
+                        game_path, 
 
-                            thread_sender.send((file, status)).unwrap();
+                        move || {
+                            for file in thread_files {
+                                let status = if config.launcher.repairer.fast {
+                                    file.fast_verify(&game_path)
+                                } else {
+                                    file.verify(&game_path)
+                                };
+
+                                thread_sender.send((file, status)).unwrap();
+                            }
                         }
-                    }));
+                    ));
                 }
 
                 // We have [config.launcher.repairer.threads] copies of this sender + the original one