From df7726101d7fa7adcbf8b2a4a64377f497b71217 Mon Sep 17 00:00:00 2001 From: Observer KRypt0n_ Date: Fri, 9 Jun 2023 22:14:40 +0200 Subject: [PATCH] feat(ui): added discord rpc icon selection --- src/main.rs | 9 +- src/ui/preferences/enhancements/mod.rs | 142 ++++++++++++++++++++++++- 2 files changed, 147 insertions(+), 4 deletions(-) diff --git a/src/main.rs b/src/main.rs index 22083d2..f695f15 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4,7 +4,7 @@ use anime_launcher_sdk::config::ConfigExt; use anime_launcher_sdk::genshin::config::{Config, Schema}; use anime_launcher_sdk::genshin::states::LauncherState; -use anime_launcher_sdk::genshin::consts::launcher_dir; +use anime_launcher_sdk::genshin::consts::*; use anime_launcher_sdk::anime_game_core::prelude::*; use anime_launcher_sdk::anime_game_core::genshin::prelude::*; @@ -46,11 +46,14 @@ lazy_static::lazy_static! { /// Path to launcher folder. Standard is `$HOME/.local/share/anime-game-launcher` pub static ref LAUNCHER_FOLDER: PathBuf = launcher_dir().expect("Failed to get launcher folder"); + /// Path to launcher's cache folder. Standard is `$HOME/.cache/anime-game-launcher` + pub static ref CACHE_FOLDER: PathBuf = cache_dir().expect("Failed to get launcher's cache folder"); + /// Path to `debug.log` file. Standard is `$HOME/.local/share/anime-game-launcher/debug.log` pub static ref DEBUG_FILE: PathBuf = LAUNCHER_FOLDER.join("debug.log"); - /// Path to `background` file. Standard is `$HOME/.local/share/anime-game-launcher/background` - pub static ref BACKGROUND_FILE: PathBuf = LAUNCHER_FOLDER.join("background"); + /// Path to `background` file. Standard is `$HOME/.cache/anime-game-launcher/background` + pub static ref BACKGROUND_FILE: PathBuf = CACHE_FOLDER.join("background"); /// Path to `.keep-background` file. Used to mark launcher that it shouldn't update background picture /// diff --git a/src/ui/preferences/enhancements/mod.rs b/src/ui/preferences/enhancements/mod.rs index 5c9a372..34aae76 100644 --- a/src/ui/preferences/enhancements/mod.rs +++ b/src/ui/preferences/enhancements/mod.rs @@ -1,5 +1,6 @@ use relm4::prelude::*; use relm4::component::*; +use relm4::factory::*; use adw::prelude::*; @@ -7,6 +8,9 @@ use anime_launcher_sdk::config::ConfigExt; use anime_launcher_sdk::genshin::config::Config; use anime_launcher_sdk::config::schema_blanks::prelude::*; +use anime_launcher_sdk::anime_game_core::installer::downloader::Downloader; + +use anime_launcher_sdk::discord_rpc::DiscordRpc; use anime_launcher_sdk::is_available; pub mod game; @@ -23,7 +27,69 @@ use crate::*; use super::gamescope::*; use super::main::PreferencesAppMsg; +#[derive(Debug)] +struct DiscordRpcIcon { + pub check_button: gtk::CheckButton, + + pub name: String, + pub path: PathBuf +} + +#[relm4::factory(async)] +impl AsyncFactoryComponent for DiscordRpcIcon { + type Init = Self; + type Input = EnhancementsAppMsg; + type Output = EnhancementsAppMsg; + type CommandOutput = (); + type ParentInput = EnhancementsAppMsg; + type ParentWidget = adw::ExpanderRow; + + view! { + root = adw::ActionRow { + set_title: &self.name, + // set_subtitle: &self.name, + + // Don't even try to understand + add_prefix = &self.check_button.clone(), + + add_suffix = >k::Picture { + set_margin_start: 4, + set_margin_top: 4, + set_margin_end: 4, + set_margin_bottom: 4, + + add_css_class: "round-bin", + + set_filename: Some(&self.path) + }, + + set_activatable: true, + + connect_activated[sender, index] => move |_| { + sender.output(EnhancementsAppMsg::SetDiscordRpcIcon(index.clone())); + } + } + } + + #[inline] + async fn init_model( + init: Self::Init, + _index: &DynamicIndex, + _sender: AsyncFactorySender, + ) -> Self { + init + } + + #[inline] + fn forward_to_parent(output: Self::Output) -> Option { + Some(output) + } +} + pub struct EnhancementsApp { + discord_rpc_icons: AsyncFactoryVecDeque, + discord_rpc_root_check_button: gtk::CheckButton, + gamescope: AsyncController, game_page: AsyncController, sandbox_page: AsyncController, @@ -34,6 +100,8 @@ pub struct EnhancementsApp { pub enum EnhancementsAppMsg { SetGamescopeParent(adw::PreferencesWindow), + SetDiscordRpcIcon(DynamicIndex), + OpenGamescope, OpenMainPage, OpenGameSettingsPage, @@ -372,6 +440,12 @@ impl SimpleAsyncComponent for EnhancementsApp { } }, + #[local_ref] + discord_rpc_icons -> adw::ExpanderRow { + // TODO: add localization + set_title: "Icon" + }, + adw::EntryRow { set_title: &tr("title"), set_text: &CONFIG.launcher.discord_rpc.title, @@ -573,7 +647,10 @@ impl SimpleAsyncComponent for EnhancementsApp { ) -> AsyncComponentParts { tracing::info!("Initializing enhancements settings"); - let model = Self { + let mut model = Self { + discord_rpc_icons: AsyncFactoryVecDeque::new(adw::ExpanderRow::new(), sender.input_sender()), + discord_rpc_root_check_button: gtk::CheckButton::new(), + gamescope: GamescopeApp::builder() .launch(()) .detach(), @@ -591,6 +668,57 @@ impl SimpleAsyncComponent for EnhancementsApp { .forward(sender.input_sender(), std::convert::identity) }; + match DiscordRpc::get_assets(CONFIG.launcher.discord_rpc.app_id) { + Ok(icons) => { + for icon in icons { + let cache_file = CACHE_FOLDER.join("discord-rpc").join(&icon.name); + // let sender = sender.clone(); + + if !cache_file.exists() { + std::thread::spawn(move || { + Downloader::new(icon.get_uri()) + .expect("Failed to init Discord RPC icon downloader") + .with_continue_downloading(false) + .with_free_space_check(false) + .download(cache_file, |_, _| {}) + .expect("Failed to download Discord RPC icon"); + + /*if let Err(err) = result { + sender.input(EnhancementsAppMsg::Toast { + title: String::from("Failed to download Discord RPC icon"), // TODO: add localizations + description: Some(err.to_string()) + }); + }*/ + }); + } + + // TODO: add icons after thread above finishes its work as well + else { + let check_button = gtk::CheckButton::new(); + + check_button.set_group(Some(&model.discord_rpc_root_check_button)); + + if CONFIG.launcher.discord_rpc.icon == icon.name { + check_button.set_active(true); + } + + model.discord_rpc_icons.guard().push_back(DiscordRpcIcon { + check_button, + name: icon.name.clone(), + path: cache_file.clone() + }); + } + } + } + + Err(err) => sender.input(EnhancementsAppMsg::Toast { + title: String::from("Failed to fetch Discord RPC icons"), // TODO: add localizations + description: Some(err.to_string()) + }) + } + + let discord_rpc_icons = model.discord_rpc_icons.widget(); + let game_page = model.game_page.widget(); let sandbox_page = model.sandbox_page.widget(); let environment_page = model.environment_page.widget(); @@ -606,6 +734,18 @@ impl SimpleAsyncComponent for EnhancementsApp { self.gamescope.widget().set_transient_for(Some(&parent)); } + EnhancementsAppMsg::SetDiscordRpcIcon(index) => { + if let Some(icon) = self.discord_rpc_icons.guard().get(index.current_index()) { + if let Ok(mut config) = Config::get() { + config.launcher.discord_rpc.icon = icon.name.clone(); + + Config::update(config); + + icon.check_button.set_active(true); + } + } + } + EnhancementsAppMsg::OpenGamescope => { self.gamescope.widget().present(); }