diff --git a/nym-wallet/src-tauri/src/log.rs b/nym-wallet/src-tauri/src/log.rs index 5a18a79b3e..63077b9217 100644 --- a/nym-wallet/src-tauri/src/log.rs +++ b/nym-wallet/src-tauri/src/log.rs @@ -1,8 +1,13 @@ use std::str::FromStr; +// Log webview emit chain: keep `cfg(not(windows))` here aligned with +// `platform_constants::SECONDARY_LOG_WEBVIEW_SUPPORTED` (compile-time strip on Windows). use fern::colors::{Color, ColoredLevelConfig}; +#[cfg(not(target_os = "windows"))] use serde::Serialize; +#[cfg(not(target_os = "windows"))] use serde_repr::{Deserialize_repr, Serialize_repr}; +#[cfg(not(target_os = "windows"))] use tauri::{Emitter, Manager}; use time::{format_description, OffsetDateTime}; @@ -23,7 +28,9 @@ fn formatted_time() -> String { _now.format(&format).unwrap() } +#[cfg_attr(target_os = "windows", allow(unused_variables))] pub fn setup_logging(app_handle: tauri::AppHandle) -> Result<(), log::SetLoggerError> { + #[cfg(not(target_os = "windows"))] let log_window_app = app_handle.clone(); let colors = ColoredLevelConfig::new() .trace(Color::Magenta) @@ -48,6 +55,7 @@ pub fn setup_logging(app_handle: tauri::AppHandle) -> Result<(), log::SetLoggerE }) .chain(std::io::stdout()); + #[cfg(not(target_os = "windows"))] let tauri_event_config = fern::Dispatch::new() .format(move |out, message, record| { out.finish(format_args!( @@ -76,10 +84,12 @@ pub fn setup_logging(app_handle: tauri::AppHandle) -> Result<(), log::SetLoggerE } })); - base_config - .chain(stdout_config) - .chain(tauri_event_config) - .apply() + #[cfg(not(target_os = "windows"))] + let dispatch = base_config.chain(stdout_config).chain(tauri_event_config); + #[cfg(target_os = "windows")] + let dispatch = base_config.chain(stdout_config); + + dispatch.apply() } trait FernExt { @@ -118,13 +128,14 @@ fn global_level() -> log::LevelFilter { } } +#[cfg(not(target_os = "windows"))] #[derive(Debug, Serialize, Clone)] struct LogMessage { message: String, level: LogLevel, } -// Serialize to u16 instead of strings. +#[cfg(not(target_os = "windows"))] #[derive(Debug, Clone, Deserialize_repr, Serialize_repr)] #[repr(u16)] enum LogLevel { @@ -135,6 +146,7 @@ enum LogLevel { Error, } +#[cfg(not(target_os = "windows"))] impl From for LogLevel { fn from(level: log::Level) -> Self { match level { diff --git a/nym-wallet/src-tauri/src/main.rs b/nym-wallet/src-tauri/src/main.rs index e03c36765a..74c76f1fa4 100644 --- a/nym-wallet/src-tauri/src/main.rs +++ b/nym-wallet/src-tauri/src/main.rs @@ -213,6 +213,7 @@ fn main() { signatures::ed25519_signing_payload::generate_nym_node_bonding_msg_payload, signatures::ed25519_signing_payload::vesting_generate_gateway_bonding_msg_payload, help::log::help_log_toggle_window, + help::log::log_viewer_window_supported, app::window::create_main_window, app::window::create_auth_window, app::react::set_react_state, @@ -221,7 +222,9 @@ fn main() { .menu(menu::build_app_menu) .on_menu_event(|app, event| { if event.id() == SHOW_LOG_WINDOW { - let _r = help::log::help_log_toggle_window(app.app_handle().clone()); + if let Err(err) = help::log::help_log_toggle_window(app.app_handle().clone()) { + ::log::warn!("Show logs menu action failed: {err}"); + } } }) .setup(|app| Ok(log::setup_logging(app.app_handle().clone())?)) diff --git a/nym-wallet/src-tauri/src/menu.rs b/nym-wallet/src-tauri/src/menu.rs index e4cd3393b0..e22acee339 100644 --- a/nym-wallet/src-tauri/src/menu.rs +++ b/nym-wallet/src-tauri/src/menu.rs @@ -1,6 +1,8 @@ use tauri::menu::{Menu, MenuBuilder, MenuItemBuilder, SubmenuBuilder}; use tauri::{AppHandle, Runtime}; +use crate::platform_constants::SECONDARY_LOG_WEBVIEW_SUPPORTED; + pub const SHOW_LOG_WINDOW: &str = "show_log_window"; pub fn build_app_menu(app: &AppHandle) -> tauri::Result> { @@ -13,7 +15,7 @@ pub fn build_app_menu(app: &AppHandle) -> tauri::Result> let mut menu_builder = MenuBuilder::new(app).item(&edit_submenu); - if std::env::var("NYM_WALLET_ENABLE_LOG").is_ok() { + if std::env::var("NYM_WALLET_ENABLE_LOG").is_ok() && SECONDARY_LOG_WEBVIEW_SUPPORTED { let help_text = MenuItemBuilder::with_id(SHOW_LOG_WINDOW, "Show logs").build(app)?; let help_submenu = SubmenuBuilder::new(app, "Help") diff --git a/nym-wallet/src-tauri/src/operations/help/log.rs b/nym-wallet/src-tauri/src/operations/help/log.rs index 81a1d7fc0a..11b34ebe11 100644 --- a/nym-wallet/src-tauri/src/operations/help/log.rs +++ b/nym-wallet/src-tauri/src/operations/help/log.rs @@ -1,8 +1,16 @@ use crate::error::BackendError; +use crate::platform_constants::SECONDARY_LOG_WEBVIEW_SUPPORTED; use crate::webview_theme::NYM_WALLET_WEBVIEW_BG; use tauri::webview::PageLoadEvent; use tauri::Manager; +/// Separate log viewer window is disabled on Windows due to WebView2 freezes / "(Not Responding)". +#[tauri::command] +#[must_use] +pub fn log_viewer_window_supported() -> bool { + SECONDARY_LOG_WEBVIEW_SUPPORTED +} + #[tauri::command] pub fn help_log_toggle_window(app_handle: tauri::AppHandle) -> Result<(), BackendError> { if let Some(current_log_window) = app_handle.get_webview_window("log") { @@ -13,41 +21,50 @@ pub fn help_log_toggle_window(app_handle: tauri::AppHandle) -> Result<(), Backen return Ok(()); } - log::info!("Creating log window..."); - match tauri::WebviewWindowBuilder::new( - &app_handle, - "log", - tauri::WebviewUrl::App("log.html".into()), - ) - .title("Nym Wallet Logs") - .background_color(NYM_WALLET_WEBVIEW_BG) - .use_https_scheme(true) - .on_page_load(|window, payload| match payload.event() { - PageLoadEvent::Started => { - log::debug!("Log webview load started: {}", payload.url()); - } - PageLoadEvent::Finished => { - log::info!("Log webview load finished: {}", payload.url()); - if std::env::var("NYM_WALLET_LOG_WEBVIEW_DEVTOOLS") - .ok() - .as_deref() - == Some("1") - { - window.open_devtools(); - } - } - }) - .build() + #[cfg(target_os = "windows")] { - Ok(window) => { - if let Err(err) = window.set_focus() { - log::error!("Unable to focus log window: {err}"); + log::info!("Log viewer window is disabled on Windows; use stdout or RUST_LOG."); + return Ok(()); + } + + #[cfg(not(target_os = "windows"))] + { + log::info!("Creating log window..."); + match tauri::WebviewWindowBuilder::new( + &app_handle, + "log", + tauri::WebviewUrl::App("log.html".into()), + ) + .title("Nym Wallet Logs") + .background_color(NYM_WALLET_WEBVIEW_BG) + .use_https_scheme(true) + .on_page_load(|window, payload| match payload.event() { + PageLoadEvent::Started => { + log::debug!("Log webview load started: {}", payload.url()); + } + PageLoadEvent::Finished => { + log::info!("Log webview load finished: {}", payload.url()); + if std::env::var("NYM_WALLET_LOG_WEBVIEW_DEVTOOLS") + .ok() + .as_deref() + == Some("1") + { + window.open_devtools(); + } + } + }) + .build() + { + Ok(window) => { + if let Err(err) = window.set_focus() { + log::error!("Unable to focus log window: {err}"); + } + Ok(()) + } + Err(err) => { + log::error!("Unable to create log window: {err}"); + Err(BackendError::NewWindowError) } - Ok(()) - } - Err(err) => { - log::error!("Unable to create log window: {err}"); - Err(BackendError::NewWindowError) } } } diff --git a/nym-wallet/src-tauri/src/platform_constants.rs b/nym-wallet/src-tauri/src/platform_constants.rs index eaf87250ca..db49d021da 100644 --- a/nym-wallet/src-tauri/src/platform_constants.rs +++ b/nym-wallet/src-tauri/src/platform_constants.rs @@ -1,6 +1,9 @@ // Copyright 2022 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 +/// Secondary log viewer `WebviewWindow` is disabled on Windows due to WebView2 freezes. +pub const SECONDARY_LOG_WEBVIEW_SUPPORTED: bool = cfg!(not(target_os = "windows")); + pub const CONFIG_DIR_NAME: &str = "nym-wallet"; pub const CONFIG_FILENAME: &str = "config.toml"; pub const STORAGE_DIR_NAME: &str = "nym-wallet"; diff --git a/nym-wallet/src/pages/settings/AdvancedSettings.tsx b/nym-wallet/src/pages/settings/AdvancedSettings.tsx index e9684d43e4..353a2b6190 100644 --- a/nym-wallet/src/pages/settings/AdvancedSettings.tsx +++ b/nym-wallet/src/pages/settings/AdvancedSettings.tsx @@ -1,12 +1,27 @@ -import React, { useContext } from 'react'; +import React, { useContext, useEffect, useState } from 'react'; import { Box, Button, Divider, Stack, Typography } from '@mui/material'; -import { helpLogToggleWindow } from '../../requests'; +import { helpLogToggleWindow, logViewerWindowSupported } from '../../requests'; import { AppContext } from '../../context'; import { config } from '../../config'; import SelectValidator from '../../components/Settings/SelectValidator'; const AdvancedSettings = () => { const { handleShowTerminal, appEnv } = useContext(AppContext); + const [logViewerOk, setLogViewerOk] = useState(null); + + useEffect(() => { + let cancelled = false; + logViewerWindowSupported() + .then((ok) => { + if (!cancelled) setLogViewerOk(ok); + }) + .catch(() => { + if (!cancelled) setLogViewerOk(false); + }); + return () => { + cancelled = true; + }; + }, []); return ( @@ -28,20 +43,28 @@ const AdvancedSettings = () => { )} - - - Logs - - Open logs to monitor all actions in the wallet - - - - - - - + {logViewerOk !== null && ( + <> + + + Logs + + {logViewerOk + ? 'Open logs to monitor all actions in the wallet' + : 'The in-app log viewer window is not available on Windows. Run the wallet from a terminal or set RUST_LOG to capture logs on stdout.'} + + + {logViewerOk ? ( + + + + ) : null} + + + + )} ); diff --git a/nym-wallet/src/requests/logging.ts b/nym-wallet/src/requests/logging.ts index 307f65d672..194073257e 100644 --- a/nym-wallet/src/requests/logging.ts +++ b/nym-wallet/src/requests/logging.ts @@ -1,3 +1,6 @@ import { invokeWrapper } from './wrapper'; export const helpLogToggleWindow = async () => invokeWrapper('help_log_toggle_window', {}); + +export const logViewerWindowSupported = async () => + invokeWrapper('log_viewer_window_supported', {});