diff --git a/src-tauri/capabilities/default.json b/src-tauri/capabilities/default.json index 5d63dd035..e79b0898b 100644 --- a/src-tauri/capabilities/default.json +++ b/src-tauri/capabilities/default.json @@ -12,6 +12,8 @@ "core:window:allow-close", "core:window:allow-destroy", "core:window:allow-minimize", - "core:window:allow-unminimize" + "core:window:allow-unminimize", + "shell:allow-open", + "process:default" ] } \ No newline at end of file diff --git a/src-tauri/src/commands.rs b/src-tauri/src/commands.rs index 1a6e0e69c..1dafe8842 100644 --- a/src-tauri/src/commands.rs +++ b/src-tauri/src/commands.rs @@ -17,10 +17,11 @@ use crate::tor_adapter::TorConfig; use crate::utils::shutdown_utils::stop_all_processes; use crate::wallet_adapter::TransactionInfo; use crate::wallet_manager::WalletManagerError; +#[allow(unused_imports)] use crate::{ - setup_inner, ApplicationsVersions, BaseNodeStatus, CpuMinerMetrics, GpuMinerMetrics, - MaxUsageLevels, MinerMetrics, TariWalletDetails, UniverseAppState, APPLICATION_FOLDER_ID, - MAX_ACCEPTABLE_COMMAND_TIME, + external_dependencies, setup_inner, ApplicationsVersions, BaseNodeStatus, CpuMinerMetrics, + GpuMinerMetrics, MaxUsageLevels, MinerMetrics, TariWalletDetails, UniverseAppState, + APPLICATION_FOLDER_ID, }; use keyring::Entry; use log::{debug, error, info, warn}; @@ -38,6 +39,8 @@ use tauri::Manager; const LOG_TARGET: &str = "tari::universe::commands"; const LOG_TARGET_WEB: &str = "tari::universe::web"; +const MAX_ACCEPTABLE_COMMAND_TIME: Duration = Duration::from_secs(1); + #[tauri::command] pub fn close_splashscreen(app: tauri::AppHandle) { let splash_window = app @@ -243,13 +246,12 @@ pub async fn get_max_consumption_levels( #[tauri::command] pub async fn get_miner_metrics( state: tauri::State<'_, UniverseAppState>, - _app: tauri::AppHandle, ) -> Result { let timer = Instant::now(); if state.is_getting_miner_metrics.load(Ordering::SeqCst) { let read = state.cached_miner_metrics.read().await; if let Some(metrics) = &*read { - warn!(target: LOG_TARGET, "Already getting miner metrics, returning cached value"); + debug!(target: LOG_TARGET, "Already getting miner metrics, returning cached value"); return Ok(metrics.clone()); } warn!(target: LOG_TARGET, "Already getting miner metrics"); @@ -257,7 +259,6 @@ pub async fn get_miner_metrics( } state.is_getting_miner_metrics.store(true, Ordering::SeqCst); - // info!(target: LOG_TARGET, "1 elapsed {:?}", timer.elapsed()); let (sha_hash_rate, randomx_hash_rate, block_reward, block_height, block_time, is_synced) = state .node_manager @@ -269,7 +270,6 @@ pub async fn get_miner_metrics( } (0, 0, MicroMinotari(0), 0, 0, false) }); - // info!(target: LOG_TARGET, "2 elapsed {:?}", timer.elapsed()); let cpu_miner = state.cpu_miner.read().await; let cpu_mining_status = match cpu_miner @@ -288,8 +288,6 @@ pub async fn get_miner_metrics( }; drop(cpu_miner); - // info!(target: LOG_TARGET, "3 elapsed {:?}", timer.elapsed()); - let gpu_miner = state.gpu_miner.read().await; let gpu_mining_status = match gpu_miner.status(sha_hash_rate, block_reward).await { Ok(gpu) => gpu, @@ -318,7 +316,7 @@ pub async fn get_miner_metrics( warn!(target: LOG_TARGET, "get_miner_metrics took too long: {:?}", timer.elapsed()); } - let ret = MinerMetrics { + let metrics_ret = MinerMetrics { sha_network_hash_rate: sha_hash_rate, randomx_network_hash_rate: randomx_hash_rate, cpu: CpuMinerMetrics { @@ -337,13 +335,14 @@ pub async fn get_miner_metrics( connected_peers, }, }; + let mut lock = state.cached_miner_metrics.write().await; - *lock = Some(ret.clone()); + *lock = Some(metrics_ret.clone()); state .is_getting_miner_metrics .store(false, Ordering::SeqCst); - Ok(ret) + Ok(metrics_ret) } #[tauri::command] @@ -1390,7 +1389,7 @@ pub async fn update_applications( ) .map_err(|e| e.to_string())?; - let progress_tracker = ProgressTracker::new(&app.clone()); + let progress_tracker = ProgressTracker::new(app.clone()); binary_resolver .update_binary(Binaries::Xmrig, progress_tracker.clone()) .await diff --git a/src-tauri/src/hardware/hardware_status_monitor.rs b/src-tauri/src/hardware/hardware_status_monitor.rs index 5b055bb68..e7c75ce7c 100644 --- a/src-tauri/src/hardware/hardware_status_monitor.rs +++ b/src-tauri/src/hardware/hardware_status_monitor.rs @@ -18,7 +18,6 @@ use anyhow::Error; use log::{error, info, warn}; use serde::{Deserialize, Serialize}; use sysinfo::{CpuRefreshKind, RefreshKind, System}; -use tauri::{AppHandle, Manager}; use tokio::sync::RwLock; const LOG_TARGET: &str = "tari::universe::auto_launcher"; @@ -145,13 +144,8 @@ impl HardwareStatusMonitor { async fn load_gpu_devices_from_status_file( &self, - app_handle: &AppHandle, + config_dir: PathBuf, ) -> Result { - let config_dir = app_handle - .path() - .app_config_dir() - .expect("Could not find app config dir"); - let file: PathBuf = config_dir.join("gpuminer").join("gpu_status.json"); if file.exists() { info!(target: LOG_TARGET, "Loading gpu status from file: {:?}", file); @@ -182,9 +176,9 @@ impl HardwareStatusMonitor { async fn initialize_gpu_devices( &self, - app_handle: &AppHandle, + config_dir: PathBuf, ) -> Result, Error> { - let gpu_status_file_content = self.load_gpu_devices_from_status_file(app_handle).await?; + let gpu_status_file_content = self.load_gpu_devices_from_status_file(config_dir).await?; let mut platform_devices = Vec::new(); for gpu_device in &gpu_status_file_content.gpu_devices { @@ -272,8 +266,8 @@ impl HardwareStatusMonitor { Ok(cpu_devices) } - pub async fn initialize(&self, app_handle: &AppHandle) -> Result<(), Error> { - let gpu_devices = self.initialize_gpu_devices(app_handle).await?; + pub async fn initialize(&self, config_dir: PathBuf) -> Result<(), Error> { + let gpu_devices = self.initialize_gpu_devices(config_dir).await?; let cpu_devices = self.initialize_cpu_devices().await?; let mut gpu_devices_lock = self.gpu_devices.write().await; diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index fc4a8d74a..7acb72e80 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -91,8 +91,6 @@ mod wallet_manager; mod xmrig; mod xmrig_adapter; -const MAX_ACCEPTABLE_COMMAND_TIME: Duration = Duration::from_secs(1); - const LOG_TARGET: &str = "tari::universe::main"; #[cfg(not(any(feature = "release-ci", feature = "release-ci-beta")))] const APPLICATION_FOLDER_ID: &str = "com.tari.universe.alpha"; @@ -169,9 +167,7 @@ async fn setup_inner( .initialize_auto_launcher(is_auto_launcher_enabled) .await?; - let app_handle_clone: tauri::AppHandle = app.clone(); - - let progress = ProgressTracker::new(&app_handle_clone); + let progress = ProgressTracker::new(app.clone()); let last_binaries_update_timestamp = state.config.read().await.last_binaries_update_timestamp(); let now = SystemTime::now(); @@ -288,7 +284,7 @@ async fn setup_inner( .inspect_err(|e| error!(target: LOG_TARGET, "Could not detect gpu miner: {:?}", e)); HardwareStatusMonitor::current() - .initialize(&app_handle_clone) + .initialize(config_dir.clone()) .await?; let mut tor_control_port = None; @@ -429,17 +425,19 @@ async fn setup_inner( .inspect_err(|e| error!(target: LOG_TARGET, "Could not emit event 'message': {:?}", e)), ); - let mvhandle = app.clone(); + let move_handle = app.clone(); tauri::async_runtime::spawn(async move { + let mut interval: time::Interval = time::interval(Duration::from_secs(1)); loop { - let app_state = mvhandle.state::().clone(); - if let Ok(ret) = commands::get_miner_metrics(app_state, mvhandle.clone()).await { - drop(mvhandle.emit("miner_metrics", ret)); - }; - tokio::time::sleep(Duration::from_secs(1)).await; + interval.tick().await; + let app_state = move_handle.state::().clone(); + if let Ok(metrics_ret) = commands::get_miner_metrics(app_state).await { + drop(move_handle.clone().emit("miner_metrics", metrics_ret)); + } } }); + let app_handle_clone: tauri::AppHandle = app.clone(); tauri::async_runtime::spawn(async move { let mut interval: time::Interval = time::interval(Duration::from_secs(30)); let mut has_send_error = false; diff --git a/src-tauri/src/progress_tracker.rs b/src-tauri/src/progress_tracker.rs index 4cea26c20..1be7ef059 100644 --- a/src-tauri/src/progress_tracker.rs +++ b/src-tauri/src/progress_tracker.rs @@ -21,7 +21,7 @@ impl Clone for ProgressTracker { } impl ProgressTracker { - pub fn new(app_handle: &AppHandle) -> Self { + pub fn new(app_handle: AppHandle) -> Self { Self { inner: Arc::new(RwLock::new(ProgressTrackerInner::new(app_handle))), } @@ -51,7 +51,7 @@ pub struct ProgressTrackerInner { } impl ProgressTrackerInner { - pub fn new(app_handle: &AppHandle) -> Self { + pub fn new(app_handle: AppHandle) -> Self { Self { app_handle: app_handle.clone(), min: 0, diff --git a/src/hooks/app/useSystemTray.ts b/src/hooks/app/useSystemTray.ts index 3d17d728e..038ae8b35 100644 --- a/src/hooks/app/useSystemTray.ts +++ b/src/hooks/app/useSystemTray.ts @@ -1,9 +1,20 @@ import { MinerMetrics } from '@app/types/app-status'; -import { menu, CPU_HASH_ITEM_ID, GPU_HASH_ITEM_ID, EARNINGS_ITEM_ID } from '@app/utils'; +import { + menu, + CPU_HASH_ITEM_ID, + GPU_HASH_ITEM_ID, + EARNINGS_ITEM_ID, + UNMINIMIZE_ITEM_ID, + MINIMIZE_ITEM_ID, +} from '@app/utils'; import { listen } from '@tauri-apps/api/event'; +import { getCurrentWindow } from '@tauri-apps/api/window'; import { useCallback, useEffect, useMemo, useState } from 'react'; import { formatHashrate, formatNumber, FormatPreset } from '@app/utils'; +import { MenuItem } from '@tauri-apps/api/menu/menuItem'; + +const currentWindow = getCurrentWindow(); export function useUpdateSystemTray() { const [metrics, setMetrics] = useState(); @@ -11,12 +22,24 @@ export function useUpdateSystemTray() { const totalEarningsFormatted = useMemo(() => { const cpu_est = metrics?.cpu?.mining?.estimated_earnings || 0; const gpu_est = metrics?.gpu?.mining?.estimated_earnings || 0; - return formatNumber(cpu_est + gpu_est, FormatPreset.TXTM_COMPACT); + const total = cpu_est + gpu_est; + return total > 0 ? formatNumber(total, FormatPreset.TXTM_COMPACT) : '0'; }, [metrics]); - const updateMenuItem = useCallback(async ({ itemId, itemText }: { itemId: string; itemText: string }) => { + const updateMenuItemEnabled = useCallback(async (itemId: string, enabled: boolean) => { const item = await menu.get(itemId); + if (item) { + const menuItem = item as MenuItem; + const currentEnabled = await menuItem?.isEnabled(); + if (currentEnabled !== enabled) { + await menuItem.setEnabled(enabled); + } + } + }, []); + const updateMenuItem = useCallback(async ({ itemId, itemText }: { itemId: string; itemText?: string }) => { + const item = await menu.get(itemId); + if (item && itemText) { await item.setText(itemText); } }, []); @@ -44,13 +67,18 @@ export function useUpdateSystemTray() { }, [items, updateMenuItem]); useEffect(() => { - const ul = listen('miner_metrics', ({ payload }) => { + const ul = listen('miner_metrics', async ({ payload }) => { + const minimized = await currentWindow.isMinimized(); + if (payload) { setMetrics(payload as MinerMetrics); } + + await updateMenuItemEnabled(UNMINIMIZE_ITEM_ID, minimized); + await updateMenuItemEnabled(MINIMIZE_ITEM_ID, !minimized); }); return () => { ul.then((unlisten) => unlisten()); }; - }, []); + }, [updateMenuItemEnabled]); } diff --git a/src/hooks/mining/useEarningsRecap.ts b/src/hooks/mining/useEarningsRecap.ts index 49b9227b8..96aae9d13 100644 --- a/src/hooks/mining/useEarningsRecap.ts +++ b/src/hooks/mining/useEarningsRecap.ts @@ -23,15 +23,17 @@ export default function useEarningsRecap() { }, [handleWinRecap, recapIds, transactions]); useEffect(() => { - const listener = listen('tauri://focus', async () => { - const minimized = await appWindow?.isMinimized(); - const documentIsVisible = document?.visibilityState === 'visible' || false; - if (documentIsVisible && !minimized) { + const listener = listen( + 'tauri://focus', + async () => { + const minimized = await appWindow?.isMinimized(); + const documentIsVisible = document?.visibilityState === 'visible' || false; if (documentIsVisible && !minimized) { getMissedEarnings(); } - } - }); + }, + { target: { kind: 'WebviewWindow', label: 'main' } } + ); return () => { listener.then((unlisten) => unlisten()); diff --git a/src/store/useBlockchainVisualisationStore.ts b/src/store/useBlockchainVisualisationStore.ts index eb8fed8cc..c682c4cab 100644 --- a/src/store/useBlockchainVisualisationStore.ts +++ b/src/store/useBlockchainVisualisationStore.ts @@ -6,7 +6,7 @@ import { getCurrentWebviewWindow } from '@tauri-apps/api/webviewWindow'; import { BlockTimeData } from '@app/types/mining.ts'; import { setAnimationState } from '@app/visuals.ts'; import { TransactionInfo } from '@app/types/app-status.ts'; -import { useWalletStore } from '@app/store/useWalletStore.ts'; +import { useWalletStore } from './useWalletStore.ts'; const appWindow = getCurrentWebviewWindow(); interface Recap { diff --git a/src/utils/systray.ts b/src/utils/systray.ts index 4d5179354..7a2e0a317 100644 --- a/src/utils/systray.ts +++ b/src/utils/systray.ts @@ -2,6 +2,7 @@ import { Menu } from '@tauri-apps/api/menu'; import { TrayIcon } from '@tauri-apps/api/tray'; import { MenuItemOptions } from '@tauri-apps/api/menu/menuItem'; import { PredefinedMenuItemOptions } from '@tauri-apps/api/menu/predefinedMenuItem'; +import { getCurrentWindow } from '@tauri-apps/api/window'; const TRAY_ID = 'universe-tray-id'; const TRAY_MENU_ID = 'universe-tray-menu-id'; @@ -9,6 +10,8 @@ const TRAY_MENU_ID = 'universe-tray-menu-id'; export const CPU_HASH_ITEM_ID = 'cpu_hashrate'; export const GPU_HASH_ITEM_ID = 'gpu_hashrate'; export const EARNINGS_ITEM_ID = 'estimated_earning'; +export const UNMINIMIZE_ITEM_ID = 'unminimize'; +export const MINIMIZE_ITEM_ID = 'minimize'; const about = { item: { About: null }, @@ -17,10 +20,17 @@ const separator = { item: 'Separator', } as PredefinedMenuItemOptions; -const minimize = { - item: 'Minimize', - text: 'Minimize', -} as PredefinedMenuItemOptions; +const currentWindow = getCurrentWindow(); + +async function handleMinimize(itemId: string): Promise { + if (itemId === UNMINIMIZE_ITEM_ID) { + await currentWindow.unminimize(); + } + + if (itemId === MINIMIZE_ITEM_ID) { + await currentWindow.minimize(); + } +} // TODO use translations const dynamicItems = [ @@ -40,6 +50,19 @@ const dynamicItems = [ text: `Est earning: -`, enabled: false, }, + separator, + { + id: UNMINIMIZE_ITEM_ID, + text: 'Uninimize', + enabled: false, + action: handleMinimize, + }, + { + id: MINIMIZE_ITEM_ID, + text: 'Minimize', + enabled: true, + action: handleMinimize, + }, ] as MenuItemOptions[]; let tray: TrayIcon | null; @@ -49,7 +72,7 @@ export async function initSystray() { try { menu = await Menu.new({ id: TRAY_MENU_ID, - items: [about, separator, ...dynamicItems, separator, minimize], + items: [about, separator, ...dynamicItems], }); } catch (e) { console.error('Menu error: ', e);