Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: show system notifications #510

Open
wants to merge 20 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 12 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
400 changes: 382 additions & 18 deletions src-tauri/Cargo.lock

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions src-tauri/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ log4rs = "1.3.0"
minotari_node_grpc_client = {git = "https://github.com/tari-project/tari.git", branch = "development"}
minotari_wallet_grpc_client = {git = "https://github.com/tari-project/tari.git", branch = "development"}
nix = {version = "0.29.0", features = ["signal"]}
notify-rust = "4.11.3"
nvml-wrapper = "0.10.0"
open = "5"
phraze = "0.3.15"
Expand Down Expand Up @@ -100,6 +101,7 @@ nvml-wrapper = "0.10.0"
rand = "0.8.5"
sentry-tauri = "0.3.0"
sys-locale = "0.3.1"
notify-rust = "4.11.3"
# tonic = "0.12.0"

[features]
Expand Down
24 changes: 4 additions & 20 deletions src-tauri/src/auto_launcher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,12 @@ use log::{info, warn};
use tauri::utils::platform::current_exe;
use tokio::sync::RwLock;

use crate::utils::platform_utils::{CurrentOperatingSystem, PlatformUtils};

const LOG_TARGET: &str = "tari::universe::auto_launcher";

static INSTANCE: LazyLock<AutoLauncher> = LazyLock::new(AutoLauncher::new);

pub enum CurrentOperatingSystem {
Windows,
Linux,
MacOS,
}

pub struct AutoLauncher {
auto_launcher: RwLock<Option<AutoLaunch>>,
}
Expand All @@ -28,22 +24,10 @@ impl AutoLauncher {
}
}

fn detect_current_os() -> CurrentOperatingSystem {
if cfg!(target_os = "windows") {
CurrentOperatingSystem::Windows
} else if cfg!(target_os = "linux") {
CurrentOperatingSystem::Linux
} else if cfg!(target_os = "macos") {
CurrentOperatingSystem::MacOS
} else {
panic!("Unsupported OS");
}
}

fn build_auto_launcher(app_name: &str, app_path: &str) -> Result<AutoLaunch, anyhow::Error> {
info!(target: LOG_TARGET, "Building auto-launcher with app_name: {} and app_path: {}", app_name, app_path);

match AutoLauncher::detect_current_os() {
match PlatformUtils::detect_current_os() {
CurrentOperatingSystem::Windows => {
return AutoLaunchBuilder::new()
.set_app_name(app_name)
Expand Down Expand Up @@ -79,7 +63,7 @@ impl AutoLauncher {

if config_is_auto_launcher_enabled && !is_auto_launcher_enabled {
info!(target: LOG_TARGET, "Enabling auto-launcher");
match AutoLauncher::detect_current_os() {
match PlatformUtils::detect_current_os() {
CurrentOperatingSystem::MacOS => {
// This for some reason fixes the issue where macOS starts two instances of the app
// when auto-launcher is enabled and when during shutdown user selects to reopen the apps after restart
Expand Down
24 changes: 4 additions & 20 deletions src-tauri/src/hardware_monitor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,12 @@ use serde::{Deserialize, Serialize};
use sysinfo::{Component, Components, CpuRefreshKind, RefreshKind, System};
use tokio::sync::RwLock;

use crate::utils::platform_utils::{CurrentOperatingSystem, PlatformUtils};

const LOG_TARGET: &str = "tari::universe::hardware_monitor";
static INSTANCE: LazyLock<RwLock<HardwareMonitor>> =
LazyLock::new(|| RwLock::new(HardwareMonitor::new()));

enum CurrentOperatingSystem {
Windows,
Linux,
MacOS,
}

#[derive(Clone, Debug, Serialize)]
pub struct HardwareParameters {
pub label: String,
Expand Down Expand Up @@ -80,8 +76,8 @@ pub struct HardwareMonitor {
impl HardwareMonitor {
pub fn new() -> Self {
HardwareMonitor {
current_os: HardwareMonitor::detect_current_os(),
current_implementation: match HardwareMonitor::detect_current_os() {
current_os: PlatformUtils::detect_current_os(),
current_implementation: match PlatformUtils::detect_current_os() {
CurrentOperatingSystem::Windows => Box::new(WindowsHardwareMonitor {
nvml: HardwareMonitor::initialize_nvml(),
gpu_status_file: None,
Expand Down Expand Up @@ -118,18 +114,6 @@ impl HardwareMonitor {
}
}

fn detect_current_os() -> CurrentOperatingSystem {
if cfg!(target_os = "windows") {
CurrentOperatingSystem::Windows
} else if cfg!(target_os = "linux") {
CurrentOperatingSystem::Linux
} else if cfg!(target_os = "macos") {
CurrentOperatingSystem::MacOS
} else {
panic!("Unsupported OS");
}
}

pub fn read_hardware_parameters(&mut self) -> HardwareStatus {
// USED FOR DEBUGGING
// println!("Reading hardware parameters for {}", self.current_implementation.get_implementation_name());
Expand Down
11 changes: 10 additions & 1 deletion src-tauri/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@
use ::sentry::integrations::anyhow::capture_anyhow;
use auto_launcher::AutoLauncher;
use external_dependencies::{ExternalDependencies, ExternalDependency, RequiredExternalDependency};
use hardware_monitor::{HardwareMonitor, HardwareParameters};
use log::trace;
use log::{debug, error, info, warn};
use notification_manager::NotificationManager;
use regex::Regex;
use serde::Serialize;
use std::fs::{read_dir, remove_dir_all, remove_file};
Expand All @@ -27,7 +29,6 @@ use app_config::AppConfig;
use app_in_memory_config::{AirdropInMemoryConfig, AppInMemoryConfig};
use binaries::{binaries_list::Binaries, binaries_resolver::BinaryResolver};
use gpu_miner_adapter::{GpuMinerStatus, GpuNodeSource};
use hardware_monitor::{HardwareMonitor, HardwareParameters};
use node_manager::NodeManagerError;
use progress_tracker::ProgressTracker;
use setup_status_event::SetupStatusEvent;
Expand Down Expand Up @@ -67,6 +68,7 @@ mod mm_proxy_manager;
mod network_utils;
mod node_adapter;
mod node_manager;
mod notification_manager;
mod p2pool;
mod p2pool_adapter;
mod p2pool_manager;
Expand Down Expand Up @@ -1066,6 +1068,12 @@ async fn get_seed_words(
Ok(res)
}

#[tauri::command]
async fn trigger_notification(summary: &str, body: &str) -> Result<(), String> {
NotificationManager::current().trigger_notification(summary, body);
Ok(())
}

#[allow(clippy::too_many_lines)]
#[tauri::command]
async fn start_mining<'r>(
Expand Down Expand Up @@ -2038,6 +2046,7 @@ fn main() {
restart_application,
resolve_application_language,
set_application_language,
trigger_notification,
set_mine_on_app_start,
get_miner_metrics,
get_app_config,
Expand Down
55 changes: 55 additions & 0 deletions src-tauri/src/notification_manager.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
use notify_rust::Notification;
use std::sync::LazyLock;

use log::info;

use crate::utils::platform_utils::{CurrentOperatingSystem, PlatformUtils};

const LOG_TARGET: &str = "tari::universe::notification_manager";
static INSTANCE: LazyLock<NotificationManager> = LazyLock::new(NotificationManager::new);

pub struct NotificationManager {}

impl NotificationManager {
pub fn new() -> Self {
Self {}
}

pub fn trigger_notification(&self, summary: &str, body: &str) {
info!(target: LOG_TARGET, "Triggering notification with summary: {} and body: {}", summary, body);
let notification = self.build_notification(summary, body);

match PlatformUtils::detect_current_os() {
CurrentOperatingSystem::Linux => {
#[cfg(target_os = "linux")]
notification.show().unwrap().on_close(|notification| {
info!(target: LOG_TARGET, "Notification closed: {:?}", notification);
});
}
CurrentOperatingSystem::MacOS => {
#[cfg(target_os = "macos")]
notification.show().unwrap();
}
CurrentOperatingSystem::Windows => {
#[cfg(target_os = "windows")]
notification.show().unwrap();
}
}
}
Misieq01 marked this conversation as resolved.
Show resolved Hide resolved

fn build_notification(&self, summary: &str, body: &str) -> Notification {
let mut notification = Notification::new().summary(summary).body(body).finalize();

match PlatformUtils::detect_current_os() {
CurrentOperatingSystem::Linux => {
notification.auto_icon().appname("Tari Universe").finalize()
}
CurrentOperatingSystem::MacOS => notification.finalize(),
CurrentOperatingSystem::Windows => notification.finalize(),
}
}

pub fn current() -> &'static NotificationManager {
&INSTANCE
}
}
31 changes: 8 additions & 23 deletions src-tauri/src/systemtray_manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@ use tauri::{
SystemTrayMenuItem,
};

use crate::hardware_monitor::HardwareStatus;
use crate::{
hardware_monitor::HardwareStatus,
utils::platform_utils::{CurrentOperatingSystem, PlatformUtils},
};

const LOG_TARGET: &str = "tari::universe::systemtray_manager";
static INSTANCE: LazyLock<SystemtrayManager> = LazyLock::new(SystemtrayManager::new);
Expand Down Expand Up @@ -64,12 +67,6 @@ impl SystrayItemId {
}
}

pub enum CurrentOperatingSystem {
Windows,
Linux,
MacOS,
}

#[derive(Debug, Clone)]
pub struct SystrayData {
pub cpu_hashrate: f64,
Expand Down Expand Up @@ -135,7 +132,7 @@ impl SystemtrayManager {

fn initialize_systray() -> SystemTray {
info!(target: LOG_TARGET, "Initializing system tray");
let current_os = SystemtrayManager::detect_current_os();
let current_os = PlatformUtils::detect_current_os();
let systray = SystemTray::new();

let empty_data = SystrayData {
Expand All @@ -162,7 +159,7 @@ impl SystemtrayManager {
}

fn internal_create_tooltip_from_data(data: SystrayData) -> String {
let current_os = SystemtrayManager::detect_current_os();
let current_os = PlatformUtils::detect_current_os();

match current_os {
CurrentOperatingSystem::Windows => {
Expand Down Expand Up @@ -223,20 +220,8 @@ impl SystemtrayManager {
.set_enabled(window.is_minimized().expect("Could not get is_minimized"));
}

fn detect_current_os() -> CurrentOperatingSystem {
if cfg!(target_os = "windows") {
CurrentOperatingSystem::Windows
} else if cfg!(target_os = "linux") {
CurrentOperatingSystem::Linux
} else if cfg!(target_os = "macos") {
CurrentOperatingSystem::MacOS
} else {
panic!("Unsupported OS");
}
}

pub fn update_systray(&self, app: AppHandle, data: SystrayData) {
let current_os = SystemtrayManager::detect_current_os();
let current_os = PlatformUtils::detect_current_os();
let tooltip = SystemtrayManager::internal_create_tooltip_from_data(data.clone());

match current_os {
Expand Down Expand Up @@ -284,7 +269,7 @@ impl SystemtrayManager {
match id.as_str() {
"unminimize" => {
info!(target: LOG_TARGET, "Unminimizing window");
match SystemtrayManager::detect_current_os() {
match PlatformUtils::detect_current_os() {
CurrentOperatingSystem::Linux => {
let is_minimized = window.is_minimized().unwrap_or(false);
let is_visible = window.is_visible().unwrap_or(false);
Expand Down
1 change: 1 addition & 0 deletions src-tauri/src/utils/mod.rs
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
pub mod auto_rollback;
pub mod file_utils;
pub mod platform_utils;
20 changes: 20 additions & 0 deletions src-tauri/src/utils/platform_utils.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
pub enum CurrentOperatingSystem {
Windows,
Linux,
MacOS,
}

pub struct PlatformUtils {}
impl PlatformUtils {
pub fn detect_current_os() -> CurrentOperatingSystem {
if cfg!(target_os = "windows") {
CurrentOperatingSystem::Windows
} else if cfg!(target_os = "linux") {
CurrentOperatingSystem::Linux
} else if cfg!(target_os = "macos") {
CurrentOperatingSystem::MacOS
} else {
panic!("Unsupported OS");
}
}
}
20 changes: 20 additions & 0 deletions src/hooks/useNotifications.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { invoke } from '@tauri-apps/api/tauri';
import { useCallback } from 'react';

export const useNotifcations = () => {
const winNotification = useCallback(async (winAmount: string) => {
await invoke('trigger_notification', {
summary: 'Congratulations !',
body: `You won a block! We are sending you rewards of ${winAmount} tXTM!`,
});
}, []);

const testNotification = useCallback(async () => {
await invoke('trigger_notification', {
summary: 'Test Notification',
body: 'This is a test notification.',
});
}, []);

return { winNotification, testNotification };
};
12 changes: 12 additions & 0 deletions src/store/appStateStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ interface AppState {
fetchApplicationsVersions: () => Promise<void>;
fetchApplicationsVersionsWithRetry: () => Promise<void>;
updateApplicationsVersions: () => Promise<void>;
triggerNotification: (summary: string, body: string) => Promise<void>;
}

export const useAppStateStore = create<AppState>()((set, getState) => ({
Expand Down Expand Up @@ -108,4 +109,15 @@ export const useAppStateStore = create<AppState>()((set, getState) => ({
},
missingExternalDependencies: [],
loadExternalDependencies: (externalDependencies: ExternalDependency[]) => set({ externalDependencies }),
triggerNotification: async (summary: string, body: string) => {
try {
await invoke('trigger_notification', {
summary,
body,
});
} catch (error) {
Sentry.captureException(error);
console.error('Error triggering notification', error);
}
},
}));
8 changes: 8 additions & 0 deletions src/store/useBlockchainVisualisationStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ 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 { useAppStateStore } from './appStateStore.ts';

interface Recap {
count: number;
Expand Down Expand Up @@ -58,6 +59,13 @@ export const useBlockchainVisualisationStore = create<BlockchainVisualisationSto
const earnings = latestTx.amount;
console.info(`Block #${blockHeight} mined! Earnings: ${earnings}`);

useAppStateStore
.getState()
.triggerNotification(
'Congratulations!',
`You won a block! We are sending you rewards of ${earnings} tXTM!`
);

if (canAnimate) {
useMiningStore.getState().setMiningControlsEnabled(false);
setAnimationState('success');
Expand Down
1 change: 1 addition & 0 deletions src/types/invoke.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ declare module '@tauri-apps/api/tauri' {
function invoke(param: 'get_external_dependencies'): Promise<ExternalDependency[]>;
function invoke(param: 'get_paper_wallet_details'): Promise<PaperWalletDetails>;
function invoke(param: 'resolve_application_language'): Promise<Language>;
function invoke(param: 'trigger_notification', payload: { summary: string; body: string }): Promise<void>;
function invoke(param: 'set_mine_on_app_start', payload: { mineOnAppStart: boolean }): Promise<void>;
function invoke(param: 'setup_application'): Promise<boolean>;
function invoke(param: 'open_log_dir'): Promise<void>;
Expand Down
Loading