feat: Add Discord RPC #23
							
								
								
									
										32
									
								
								src-tauri/Cargo.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										32
									
								
								src-tauri/Cargo.lock
									
									
									
										generated
									
									
									
								
							| @@ -489,7 +489,7 @@ checksum = "d38f2da7a0a2c4ccf0065be06397cc26a81f4e528be095826eee9d4adbb8c60f" | ||||
| dependencies = [ | ||||
|  "byteorder", | ||||
|  "fnv", | ||||
|  "uuid", | ||||
|  "uuid 1.17.0", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| @@ -843,6 +843,19 @@ dependencies = [ | ||||
|  "windows-sys 0.60.2", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "discord-rich-presence" | ||||
| version = "0.2.5" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "a75db747ecd252c01bfecaf709b07fcb4c634adf0edb5fed47bc9c3052e7076b" | ||||
| dependencies = [ | ||||
|  "serde", | ||||
|  "serde_derive", | ||||
|  "serde_json", | ||||
|  "serde_repr", | ||||
|  "uuid 0.8.2", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "dispatch" | ||||
| version = "0.2.0" | ||||
| @@ -1083,8 +1096,10 @@ dependencies = [ | ||||
| name = "ezpplauncher" | ||||
| version = "3.0.0-beta.2" | ||||
| dependencies = [ | ||||
|  "discord-rich-presence", | ||||
|  "hardware-id", | ||||
|  "md5", | ||||
|  "once_cell", | ||||
|  "open", | ||||
|  "reqwest", | ||||
|  "serde", | ||||
| @@ -3802,7 +3817,7 @@ dependencies = [ | ||||
|  "serde", | ||||
|  "serde_json", | ||||
|  "url", | ||||
|  "uuid", | ||||
|  "uuid 1.17.0", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| @@ -4737,7 +4752,7 @@ dependencies = [ | ||||
|  "thiserror 2.0.12", | ||||
|  "time", | ||||
|  "url", | ||||
|  "uuid", | ||||
|  "uuid 1.17.0", | ||||
|  "walkdir", | ||||
| ] | ||||
|  | ||||
| @@ -4968,7 +4983,7 @@ dependencies = [ | ||||
|  "toml", | ||||
|  "url", | ||||
|  "urlpattern", | ||||
|  "uuid", | ||||
|  "uuid 1.17.0", | ||||
|  "walkdir", | ||||
| ] | ||||
|  | ||||
| @@ -5475,6 +5490,15 @@ version = "1.0.4" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" | ||||
|  | ||||
| [[package]] | ||||
| name = "uuid" | ||||
| version = "0.8.2" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7" | ||||
| dependencies = [ | ||||
|  "getrandom 0.2.16", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "uuid" | ||||
| version = "1.17.0" | ||||
|   | ||||
| @@ -34,6 +34,8 @@ md5 = "0.8.0" | ||||
| tokio = { version = "1.46.1", features = ["full"] } | ||||
| open = "5.3.2" | ||||
| windows-sys = "0.60.2" | ||||
| discord-rich-presence = "0.2.5" | ||||
| once_cell = "1.21.3" | ||||
|  | ||||
| [target.'cfg(windows)'.dependencies] | ||||
| winreg = "0.55.0" | ||||
|   | ||||
| @@ -11,6 +11,7 @@ use tokio::io::AsyncWriteExt; | ||||
| use tokio::process::Command; | ||||
| use tokio::time::{Duration, sleep}; | ||||
|  | ||||
| use crate::presence; | ||||
| use crate::utils::{ | ||||
|     check_folder_completeness, get_osu_config, get_osu_user_config, get_window_title_by_pid, | ||||
|     set_osu_config_vals, set_osu_user_config_vals, | ||||
| @@ -668,3 +669,47 @@ pub async fn check_for_corruption(folder: String) -> Result<bool, String> { | ||||
|  | ||||
|     Ok(false) | ||||
| } | ||||
|  | ||||
| #[tauri::command] | ||||
| pub async fn presence_connect() -> bool { | ||||
|     presence::connect().await | ||||
| } | ||||
|  | ||||
| #[tauri::command] | ||||
| pub async fn presence_disconnect() { | ||||
|     presence::disconnect().await | ||||
| } | ||||
|  | ||||
| #[derive(Deserialize)] | ||||
| #[serde(rename_all = "camelCase")] | ||||
| pub struct PresenceStatus { | ||||
|     state: Option<String>, | ||||
|     details: Option<String>, | ||||
|     large_image_key: Option<String>, | ||||
| } | ||||
|  | ||||
| #[tauri::command] | ||||
| pub async fn presence_update_status(status: PresenceStatus) { | ||||
|     presence::update_status( | ||||
|         status.state.as_deref(), | ||||
|         status.details.as_deref(), | ||||
|         status.large_image_key.as_deref(), | ||||
|     ); | ||||
| } | ||||
|  | ||||
| #[derive(Deserialize)] | ||||
| #[serde(rename_all = "camelCase")] | ||||
| pub struct PresenceUser { | ||||
|     username: Option<String>, | ||||
|     id: Option<String>, | ||||
| } | ||||
|  | ||||
| #[tauri::command] | ||||
| pub async fn presence_update_user(user: PresenceUser) { | ||||
|     presence::update_user(user.username.as_deref(), user.id.as_deref()); | ||||
| } | ||||
|  | ||||
| #[tauri::command] | ||||
| pub async fn presence_is_connected() -> bool { | ||||
|     presence::has_presence().await | ||||
| } | ||||
|   | ||||
| @@ -2,13 +2,15 @@ | ||||
| use tauri::Manager; | ||||
|  | ||||
| pub mod commands; | ||||
| pub mod presence; | ||||
| pub mod utils; | ||||
| use crate::commands::{ | ||||
|     check_for_corruption, download_ezpp_launcher_update_files, exit, find_osu_installation, | ||||
|     get_beatmapsets_count, get_ezpp_launcher_update_files, get_hwid, get_launcher_version, | ||||
|     get_osu_release_stream, get_osu_skin, get_osu_version, get_platform, get_skins_count, | ||||
|     is_osu_running, open_url_in_browser, replace_ui_files, run_osu, run_osu_updater, | ||||
|     set_osu_config_values, set_osu_user_config_values, valid_osu_folder, | ||||
|     is_osu_running, open_url_in_browser, presence_connect, presence_disconnect, | ||||
|     presence_is_connected, presence_update_status, presence_update_user, replace_ui_files, run_osu, | ||||
|     run_osu_updater, set_osu_config_values, set_osu_user_config_values, valid_osu_folder, | ||||
| }; | ||||
|  | ||||
| #[cfg_attr(mobile, tauri::mobile_entry_point)] | ||||
| @@ -25,7 +27,7 @@ pub fn run() { | ||||
|         })); | ||||
|     } | ||||
|  | ||||
|     builder | ||||
|     let app = builder | ||||
|         .invoke_handler(tauri::generate_handler![ | ||||
|             get_hwid, | ||||
|             find_osu_installation, | ||||
| @@ -47,13 +49,27 @@ pub fn run() { | ||||
|             get_launcher_version, | ||||
|             exit, | ||||
|             get_platform, | ||||
|             check_for_corruption | ||||
|             check_for_corruption, | ||||
|             presence_connect, | ||||
|             presence_disconnect, | ||||
|             presence_update_status, | ||||
|             presence_update_user, | ||||
|             presence_is_connected | ||||
|         ]) | ||||
|         .plugin(tauri_plugin_fs::init()) | ||||
|         .plugin(tauri_plugin_dialog::init()) | ||||
|         .plugin(tauri_plugin_shell::init()) | ||||
|         .plugin(tauri_plugin_cors_fetch::init()) | ||||
|         .plugin(tauri_plugin_sql::Builder::default().build()) | ||||
|         .run(tauri::generate_context!()) | ||||
|         .expect("error while running tauri application"); | ||||
|         .build(tauri::generate_context!()) | ||||
|         .expect("error while building tauri application"); | ||||
|  | ||||
|     app.run(|_app_handle, event| { | ||||
|         if let tauri::RunEvent::ExitRequested { api, .. } = event { | ||||
|             api.prevent_exit(); | ||||
|  | ||||
|             tauri::async_runtime::block_on(presence::disconnect()); | ||||
|             std::process::exit(0); | ||||
|         } | ||||
|     }); | ||||
| } | ||||
|   | ||||
							
								
								
									
										245
									
								
								src-tauri/src/presence.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										245
									
								
								src-tauri/src/presence.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,245 @@ | ||||
| use discord_rich_presence::{ | ||||
|     activity::{Activity, Assets, Button, Timestamps}, | ||||
|     DiscordIpc, DiscordIpcClient, | ||||
| }; | ||||
| use once_cell::sync::Lazy; | ||||
| use std::sync::Mutex as StdMutex; | ||||
| use std::time::{SystemTime, UNIX_EPOCH}; | ||||
| use tokio::sync::{mpsc, oneshot}; | ||||
| use tokio::time::{interval, Duration}; | ||||
|  | ||||
| // --- Datenstrukturen und Befehle --- | ||||
|  | ||||
| #[derive(Clone, Debug)] | ||||
| pub struct PresenceData { | ||||
|     pub state: String, | ||||
|     pub details: String, | ||||
|     pub large_image_key: String, | ||||
|     pub large_image_text: String, | ||||
|     pub small_image_key: Option<String>, | ||||
|     pub small_image_text: Option<String>, | ||||
| } | ||||
|  | ||||
| // Befehle, die an den Presence-Actor gesendet werden können | ||||
| #[derive(Debug)] | ||||
| enum PresenceCommand { | ||||
|     Connect(oneshot::Sender<bool>), | ||||
|     // Geändert: Nimmt einen Sender, um den Abschluss zu signalisieren | ||||
|     Disconnect(oneshot::Sender<()>), | ||||
|     UpdateData(PresenceData), | ||||
|     IsConnected(oneshot::Sender<bool>), | ||||
| } | ||||
|  | ||||
| // --- Der Actor --- | ||||
|  | ||||
| struct PresenceActor { | ||||
|     receiver: mpsc::Receiver<PresenceCommand>, | ||||
|     client: Option<DiscordIpcClient>, | ||||
|     data: PresenceData, | ||||
|     start_timestamp: i64, | ||||
| } | ||||
|  | ||||
| impl PresenceActor { | ||||
|     fn new(receiver: mpsc::Receiver<PresenceCommand>) -> Self { | ||||
|         let start = SystemTime::now() | ||||
|             .duration_since(UNIX_EPOCH) | ||||
|             .unwrap() | ||||
|             .as_secs() as i64; | ||||
|  | ||||
|         let data = PresenceData { | ||||
|             state: "Idle in Launcher...".to_string(), | ||||
|             details: "  ".to_string(), | ||||
|             large_image_key: "ezppfarm".to_string(), | ||||
|             large_image_text: "EZPPFarm v1.0.0".to_string(), | ||||
|             small_image_key: None, | ||||
|             small_image_text: None, | ||||
|         }; | ||||
|  | ||||
|         PresenceActor { | ||||
|             receiver, | ||||
|             client: None, | ||||
|             data, | ||||
|             start_timestamp: start, | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     async fn run(&mut self) { | ||||
|         let mut update_interval = interval(Duration::from_millis(2500)); | ||||
|  | ||||
|         loop { | ||||
|             tokio::select! { | ||||
|                 Some(cmd) = self.receiver.recv() => { | ||||
|                     match cmd { | ||||
|                         PresenceCommand::Connect(responder) => self.handle_connect(responder).await, | ||||
|                         // Geändert: Leitet den Responder weiter | ||||
|                         PresenceCommand::Disconnect(responder) => { | ||||
|                             self.handle_disconnect(responder).await; | ||||
|                         }, | ||||
|                         PresenceCommand::UpdateData(new_data) => { | ||||
|                             self.data = new_data; | ||||
|                         }, | ||||
|                         PresenceCommand::IsConnected(responder) => { | ||||
|                             let _ = responder.send(self.client.is_some()); | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|                 _ = update_interval.tick() => { | ||||
|                     if self.client.is_some() { | ||||
|                         self.handle_update().await; | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     async fn handle_connect(&mut self, responder: oneshot::Sender<bool>) { | ||||
|         if self.client.is_some() { | ||||
|             let _ = responder.send(true); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         println!("Actor: Connecting to Discord..."); | ||||
|         match DiscordIpcClient::new("1032772293220384808").map_err(|e| e.to_string()) { | ||||
|             Ok(mut new_client) => { | ||||
|                 if let Err(e) = new_client.connect().map_err(|e| e.to_string()) { | ||||
|                     eprintln!("Failed to connect to Discord: {:?}", e); | ||||
|                     let _ = responder.send(false); | ||||
|                     return; | ||||
|                 } | ||||
|                 self.client = Some(new_client); | ||||
|                 println!("Actor: Connected successfully."); | ||||
|                 self.handle_update().await; | ||||
|                 let _ = responder.send(true); | ||||
|             } | ||||
|             Err(e) => { | ||||
|                 eprintln!("Failed to create Discord client: {:?}", e); | ||||
|                 let _ = responder.send(false); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     // Geändert: Nimmt einen Responder als Parameter | ||||
|     async fn handle_disconnect(&mut self, responder: oneshot::Sender<()>) { | ||||
|         if let Some(mut client) = self.client.take() { | ||||
|             println!("Actor: Disconnecting..."); | ||||
|             let _ = client.clear_activity().map_err(|e| e.to_string()); | ||||
|             let _ = client.close().map_err(|e| e.to_string()); | ||||
|             println!("Actor: Disconnected successfully."); | ||||
|         } | ||||
|         // Signalisiere, dass der Vorgang abgeschlossen ist | ||||
|         let _ = responder.send(()); | ||||
|     } | ||||
|  | ||||
|     async fn handle_update(&mut self) { | ||||
|         if let Some(client) = self.client.as_mut() { | ||||
|             let mut assets = Assets::new() | ||||
|                 .large_image(&self.data.large_image_key) | ||||
|                 .large_text(&self.data.large_image_text); | ||||
|  | ||||
|             if let Some(key) = &self.data.small_image_key { | ||||
|                 assets = assets.small_image(key); | ||||
|             } | ||||
|             if let Some(text) = &self.data.small_image_text { | ||||
|                 assets = assets.small_text(text); | ||||
|             } | ||||
|  | ||||
|             let activity = Activity::new() | ||||
|                 .state(&self.data.state) | ||||
|                 .details(&self.data.details) | ||||
|                 .timestamps(Timestamps::new().start(self.start_timestamp)) | ||||
|                 .assets(assets) | ||||
|                 .buttons(vec![ | ||||
|                     Button::new( | ||||
|                         "Download the Launcher", | ||||
|                         "https://git.ez-pp.farm/EZPPFarm/EZPPLauncher/releases/latest", | ||||
|                     ), | ||||
|                     Button::new("Join EZPZFarm", "https://ez-pp.farm/discord"), | ||||
|                 ]); | ||||
|  | ||||
|             if let Err(e) = client.set_activity(activity).map_err(|e| e.to_string()) { | ||||
|                 eprintln!("Failed to set activity, disconnecting: {:?}", e); | ||||
|                 // Rufe die interne handle_disconnect auf, ohne auf eine Antwort zu warten | ||||
|                 if let Some(mut client) = self.client.take() { | ||||
|                     let _ = client.clear_activity(); | ||||
|                     let _ = client.close(); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| // --- Öffentliche API --- | ||||
|  | ||||
| static PRESENCE_TX: Lazy<mpsc::Sender<PresenceCommand>> = Lazy::new(|| { | ||||
|     let (tx, rx) = mpsc::channel(10); | ||||
|     let mut actor = PresenceActor::new(rx); | ||||
|     tokio::spawn(async move { actor.run().await }); | ||||
|     tx | ||||
| }); | ||||
|  | ||||
| pub static PRESENCE_DATA: Lazy<StdMutex<PresenceData>> = Lazy::new(|| { | ||||
|     StdMutex::new(PresenceData { | ||||
|         state: "Idle in Launcher...".to_string(), | ||||
|         details: "  ".to_string(), | ||||
|         large_image_key: "ezppfarm".to_string(), | ||||
|         large_image_text: "EZPPFarm v1.0.0".to_string(), | ||||
|         small_image_key: None, | ||||
|         small_image_text: None, | ||||
|     }) | ||||
| }); | ||||
|  | ||||
| pub async fn connect() -> bool { | ||||
|     let (tx, rx) = oneshot::channel(); | ||||
|     if PRESENCE_TX.send(PresenceCommand::Connect(tx)).await.is_ok() { | ||||
|         return rx.await.unwrap_or(false); | ||||
|     } | ||||
|     false | ||||
| } | ||||
|  | ||||
| // Geändert: Wartet jetzt auf den Abschluss des Disconnects | ||||
| pub async fn disconnect() { | ||||
|     let (tx, rx) = oneshot::channel(); | ||||
|     if PRESENCE_TX.send(PresenceCommand::Disconnect(tx)).await.is_ok() { | ||||
|         // Warte, bis der Actor den Abschluss signalisiert | ||||
|         let _ = rx.await; | ||||
|     } else { | ||||
|         println!("Could not send disconnect command; actor may not be running."); | ||||
|     } | ||||
| } | ||||
|  | ||||
| pub async fn has_presence() -> bool { | ||||
|     let (tx, rx) = oneshot::channel(); | ||||
|     if PRESENCE_TX.send(PresenceCommand::IsConnected(tx)).await.is_ok() { | ||||
|         return rx.await.unwrap_or(false); | ||||
|     } | ||||
|     false | ||||
| } | ||||
|  | ||||
| pub fn update_status(state: Option<&str>, details: Option<&str>, large_image_key: Option<&str>) { | ||||
|     let mut data = PRESENCE_DATA.lock().unwrap(); | ||||
|     if let Some(s) = state { | ||||
|         data.state = s.to_string(); | ||||
|     } | ||||
|     if let Some(d) = details { | ||||
|         data.details = d.to_string(); | ||||
|     } | ||||
|     if let Some(img) = large_image_key { | ||||
|         data.large_image_key = img.to_string(); | ||||
|     } | ||||
|     let data_clone = data.clone(); | ||||
|     let tx = PRESENCE_TX.clone(); | ||||
|     tokio::spawn(async move { | ||||
|         let _ = tx.send(PresenceCommand::UpdateData(data_clone)).await; | ||||
|     }); | ||||
| } | ||||
|  | ||||
| pub fn update_user(username: Option<&str>, id: Option<&str>) { | ||||
|     let mut data = PRESENCE_DATA.lock().unwrap(); | ||||
|     data.small_image_key = id.map(|id_str| format!("https://a.ez-pp.farm/{}", id_str)); | ||||
|     data.small_image_text = username.map(|s| s.to_string()); | ||||
|     let data_clone = data.clone(); | ||||
|     let tx = PRESENCE_TX.clone(); | ||||
|     tokio::spawn(async move { | ||||
|         let _ = tx.send(PresenceCommand::UpdateData(data_clone)).await; | ||||
|     }); | ||||
| } | ||||
| @@ -9,6 +9,9 @@ export const currentView = writable<Component>(Loading); | ||||
| export const launcherVersion = writable<string>(''); | ||||
| export const newVersion = writable<Release | undefined>(undefined); | ||||
|  | ||||
| export const discordPresence = writable<boolean>(false); | ||||
| export const presenceLoading = writable<boolean>(false); | ||||
|  | ||||
| export const currentLoadingInfo = writable<string>('Initializing...'); | ||||
|  | ||||
| export const firstStartup = writable<boolean>(false); | ||||
|   | ||||
							
								
								
									
										17
									
								
								src/lib/presence.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								src/lib/presence.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,17 @@ | ||||
| import { invoke } from '@tauri-apps/api/core'; | ||||
|  | ||||
| export const connect = async () => await invoke('presence_connect'); | ||||
| export const disconnect = async () => await invoke('presence_disconnect'); | ||||
| export const updateStatus = async (status: { | ||||
|   state?: string; | ||||
|   details?: string; | ||||
|   large_image_key?: string; | ||||
| }) => | ||||
|   await invoke('presence_update_status', { | ||||
|     state: status.state, | ||||
|     details: status.details, | ||||
|     largeImageKey: status.large_image_key, | ||||
|   }); | ||||
| export const updateUser = async (user: { username: string; id: string }) => | ||||
|   await invoke('presence_update_user', { username: user.username, id: user.id }); | ||||
| export const isConnected = async () => await invoke<boolean>('presence_is_connected'); | ||||
| @@ -8,11 +8,13 @@ | ||||
|     beatmapSets, | ||||
|     currentSkin, | ||||
|     currentView, | ||||
|     discordPresence, | ||||
|     launcherVersion, | ||||
|     launching, | ||||
|     newVersion, | ||||
|     osuBuild, | ||||
|     osuStream, | ||||
|     presenceLoading, | ||||
|     serverConnectionFails, | ||||
|     serverPing, | ||||
|     skins, | ||||
| @@ -953,13 +955,13 @@ | ||||
|             ></Checkbox> | ||||
|  | ||||
|             <div class="flex flex-col"> | ||||
|               <Label class="text-sm" for="setting-cursor-smoothening">Reduce Animations</Label> | ||||
|               <Label class="text-sm" for="setting-reduce-animations">Reduce Animations</Label> | ||||
|               <div class="text-muted-foreground text-xs"> | ||||
|                 Disables some animations in the Launcher to improve performance on low-end devices. | ||||
|               </div> | ||||
|             </div> | ||||
|             <Checkbox | ||||
|               id="setting-cursor-smoothening" | ||||
|               id="setting-reduce-animations" | ||||
|               checked={$reduceAnimations} | ||||
|               onCheckedChange={async (e) => { | ||||
|                 reduceAnimations.set(e); | ||||
| @@ -967,6 +969,26 @@ | ||||
|               }} | ||||
|               class="flex items-center justify-center w-5 h-5" | ||||
|             ></Checkbox> | ||||
|  | ||||
|             <div class="flex flex-col"> | ||||
|               <Label class="text-sm" for="setting-rich-presence">Discord Rich Presence</Label> | ||||
|               <div class="text-muted-foreground text-xs"> | ||||
|                 Let other discord users show what you are doing right now 👀 | ||||
|               </div> | ||||
|             </div> | ||||
|             <div class="relative"> | ||||
|               {#if $presenceLoading} | ||||
|                 <div class="-left-8 absolute" transition:fade> | ||||
|                   <LoaderCircle class="animate-spin" /> | ||||
|                 </div> | ||||
|               {/if} | ||||
|               <Checkbox | ||||
|                 id="setting-rich-presence" | ||||
|                 bind:checked={$discordPresence} | ||||
|                 disabled={$presenceLoading} | ||||
|                 class="flex items-center justify-center w-5 h-5" | ||||
|               ></Checkbox> | ||||
|             </div> | ||||
|           </div> | ||||
|           <div | ||||
|             class="grid grid-cols-[0.7fr_auto] gap-y-5 items-center border-theme-800 pl-6 pr-5 pb-4" | ||||
|   | ||||
| @@ -4,7 +4,14 @@ | ||||
|  | ||||
|   import Titlebar from '@/components/ui/titlebar/titlebar.svelte'; | ||||
|   import * as AlertDialog from '@/components/ui/alert-dialog'; | ||||
|   import { currentLoadingInfo, firstStartup, launcherVersion, setupValues } from '@/global'; | ||||
|   import { | ||||
|     currentLoadingInfo, | ||||
|     discordPresence, | ||||
|     firstStartup, | ||||
|     launcherVersion, | ||||
|     presenceLoading, | ||||
|     setupValues, | ||||
|   } from '@/global'; | ||||
|   import { onMount } from 'svelte'; | ||||
|   import OsuCursor from '@/components/ui/osu-cursor/OsuCursor.svelte'; | ||||
|   import { | ||||
| @@ -20,6 +27,7 @@ | ||||
|   import { userAuth } from '@/userAuthentication'; | ||||
|   import { exit, getLauncherVersion, getPlatform } from '@/osuUtil'; | ||||
|   import Button from '@/components/ui/button/button.svelte'; | ||||
|   import * as presence from '@/presence'; | ||||
|  | ||||
|   import '@fontsource/sora'; | ||||
|   import '@fontsource/space-mono'; | ||||
| @@ -92,17 +100,39 @@ | ||||
|     const config_cursor_smoothening = $userSettings.value('cursor_smoothening'); | ||||
|     const config_reduce_animations = $userSettings.value('reduce_animations'); | ||||
|     const config_osu_installation_path = $userSettings.value('osu_installation_path'); | ||||
|     const config_discord_presence = $userSettings.value('discord_presence'); | ||||
|  | ||||
|     patch.set(config_patching.get(true)); | ||||
|     customCursor.set(config_custom_cursor.get(true)); | ||||
|     cursorSmoothening.set(config_cursor_smoothening.get(true)); | ||||
|     reduceAnimations.set(config_reduce_animations.get(false)); | ||||
|     osuInstallationPath.set(config_osu_installation_path.get('')); | ||||
|     discordPresence.set(config_discord_presence.get(true)); | ||||
|  | ||||
|     try { | ||||
|       if ($discordPresence) { | ||||
|         presenceLoading.set(true); | ||||
|         await presence.connect(); | ||||
|         presenceLoading.set(false); | ||||
|       } | ||||
|     } catch {} | ||||
|  | ||||
|     patch.subscribe((val) => config_patching.set(val)); | ||||
|     customCursor.subscribe((val) => config_custom_cursor.set(val)); | ||||
|     cursorSmoothening.subscribe((val) => config_cursor_smoothening.set(val)); | ||||
|     reduceAnimations.subscribe((val) => config_reduce_animations.set(val)); | ||||
|     discordPresence.subscribe(async (val) => { | ||||
|       config_discord_presence.set(val); | ||||
|       try { | ||||
|         presenceLoading.set(true); | ||||
|         if (val) { | ||||
|           await presence.connect(); | ||||
|         } else { | ||||
|           await presence.disconnect(); | ||||
|         } | ||||
|         presenceLoading.set(false); | ||||
|       } catch {} | ||||
|     }); | ||||
|  | ||||
|     firstStartup.set(isFirstStartup); | ||||
|   }); | ||||
|   | ||||
		Reference in New Issue
	
	Block a user