From 330a300710ede778af2e5e75f3150cb162e56274 Mon Sep 17 00:00:00 2001 From: Tommy Verrall Date: Wed, 6 May 2026 14:55:56 +0200 Subject: [PATCH] Fixing the logging to ouput debug window - Route-scoped refresh - Mock TDelegationContext - notistack for log copy errors - main-thread Tauri emit for log viewer events. --- nym-wallet/src-tauri/src/log.rs | 12 +++-- nym-wallet/src/components/LogViewer/index.tsx | 4 ++ nym-wallet/src/context/delegations.tsx | 28 +++++------- nym-wallet/src/context/mocks/delegations.tsx | 45 +++++++++++++++---- nym-wallet/src/context/rewards.tsx | 6 +-- nym-wallet/src/log.tsx | 9 ++-- nym-wallet/src/pages/delegation/index.tsx | 4 +- 7 files changed, 69 insertions(+), 39 deletions(-) diff --git a/nym-wallet/src-tauri/src/log.rs b/nym-wallet/src-tauri/src/log.rs index f9b61aaff2..83d2cc8636 100644 --- a/nym-wallet/src-tauri/src/log.rs +++ b/nym-wallet/src-tauri/src/log.rs @@ -62,10 +62,14 @@ pub fn setup_logging(app_handle: tauri::AppHandle) -> Result<(), log::SetLoggerE message: record.args().to_string(), level: record.level().into(), }; - // Tauri 2: target the log webview explicitly so the dedicated window receives events. - if let Some(log_win) = log_window_app.get_webview_window("log") { - let _ = log_win.emit("log://log", msg); - } + let app = log_window_app.clone(); + let app_for_emit = app.clone(); + // Logging callbacks often run off the UI thread; WebView event delivery must run on main (macOS). + let _ = app.run_on_main_thread(move || { + if let Some(log_win) = app_for_emit.get_webview_window("log") { + let _ = log_win.emit("log://log", msg); + } + }); })); base_config diff --git a/nym-wallet/src/components/LogViewer/index.tsx b/nym-wallet/src/components/LogViewer/index.tsx index ae76a09351..f968471723 100644 --- a/nym-wallet/src/components/LogViewer/index.tsx +++ b/nym-wallet/src/components/LogViewer/index.tsx @@ -17,6 +17,7 @@ import { useTheme, } from '@mui/material'; import { writeText } from '@tauri-apps/plugin-clipboard-manager'; +import { useSnackbar } from 'notistack'; import { helpLogToggleWindow } from 'src/requests/logging'; import { Console } from 'src/utils/console'; @@ -96,6 +97,7 @@ interface RecordPayload { export const LogViewer: FC = () => { const theme = useTheme(); + const { enqueueSnackbar } = useSnackbar(); const unlisten = useRef(null); const [messages, setMessages] = useState([]); const [messageCount, setMessageCount] = useState(0); @@ -154,6 +156,7 @@ export const LogViewer: FC = () => { await writeText(chronological.map(formatLine).join('\n')); } catch (e) { Console.error(e); + enqueueSnackbar('Could not copy logs to the clipboard', { variant: 'error' }); } }; @@ -164,6 +167,7 @@ export const LogViewer: FC = () => { await writeText(formatLine(latest)); } catch (e) { Console.error(e); + enqueueSnackbar('Could not copy to the clipboard', { variant: 'error' }); } }; diff --git a/nym-wallet/src/context/delegations.tsx b/nym-wallet/src/context/delegations.tsx index 3d4f21b788..bf31d4aa17 100644 --- a/nym-wallet/src/context/delegations.tsx +++ b/nym-wallet/src/context/delegations.tsx @@ -17,11 +17,6 @@ import { Console } from 'src/utils/console'; import { AppContext } from 'src/context/main'; import { delegationQueryKeys, fetchDelegationSummaryQuery } from './delegationQuery'; -export type TDelegationRefreshOptions = { - /** When true, do not flip the global loading state (keeps cached list visible during refetch). */ - background?: boolean; -}; - export type TDelegationContext = { delegationItemErrors?: { nodeId: string; errors: string }; isLoading: boolean; @@ -33,7 +28,7 @@ export type TDelegationContext = { totalDelegations?: string; totalRewards?: string; totalDelegationsAndRewards?: string; - refresh: (opts?: TDelegationRefreshOptions) => Promise; + refresh: () => Promise; addDelegation: ( data: { mix_id: number; amount: DecCoin }, tokenPool: TPoolOption, @@ -62,7 +57,7 @@ export const DelegationContext = createContext({ isFetching: false, isError: false, lastUpdatedAtMs: 0, - refresh: async (_opts?: TDelegationRefreshOptions) => undefined, + refresh: async () => undefined, addDelegation: async () => { throw new Error('Not implemented'); }, @@ -108,17 +103,14 @@ export const DelegationContextProvider: FC<{ const bundle = clientAddress && onDelegationRoute ? query.data : undefined; - const refresh = useCallback( - async (_opts?: TDelegationRefreshOptions) => { - if (!clientAddress) { - return; - } - await queryClient.invalidateQueries({ - queryKey: delegationQueryKeys.summary(clientAddress), - }); - }, - [clientAddress, queryClient], - ); + const refresh = useCallback(async () => { + if (!clientAddress || !onDelegationRoute) { + return; + } + await queryClient.invalidateQueries({ + queryKey: delegationQueryKeys.summary(clientAddress), + }); + }, [clientAddress, onDelegationRoute, queryClient]); const addDelegation = async (data: { mix_id: number; amount: DecCoin }, tokenPool: TPoolOption, fee?: FeeDetails) => { try { diff --git a/nym-wallet/src/context/mocks/delegations.tsx b/nym-wallet/src/context/mocks/delegations.tsx index 783ed2b4dd..0c5943c9d8 100644 --- a/nym-wallet/src/context/mocks/delegations.tsx +++ b/nym-wallet/src/context/mocks/delegations.tsx @@ -7,7 +7,7 @@ import { FeeDetails, TransactionExecuteResult, } from '@nymproject/types'; -import { DelegationContext, TDelegationRefreshOptions, TDelegationTransaction } from '../delegations'; +import { DelegationContext, TDelegationTransaction } from '../delegations'; import { mockSleep } from './utils'; import { TPoolOption } from '../../components'; @@ -71,6 +71,9 @@ export const MockDelegationContextProvider: FCWithChildren = ({ children }) => { const [error, setError] = useState(); const [delegations, setDelegations] = useState(); const [totalDelegations, setTotalDelegations] = useState(); + const [totalRewards, setTotalRewards] = useState(); + const [totalDelegationsAndRewards, setTotalDelegationsAndRewards] = useState(); + const [lastUpdatedAtMs, setLastUpdatedAtMs] = useState(0); const [delegationItemErrors, setDelegationItemErrors] = useState<{ nodeId: string; errors: string }>(); const triggerStateUpdate = () => setTrigger(new Date()); @@ -81,8 +84,16 @@ export const MockDelegationContextProvider: FCWithChildren = ({ children }) => { const recalculate = async () => { const newDelegations = await getDelegations(); const newTotalDelegations = `${newDelegations.length * 100} NYM`; + const rewardsSum = newDelegations.reduce((acc, d) => { + const n = parseFloat(d.unclaimed_rewards?.amount ?? '0'); + return acc + (Number.isFinite(n) ? n : 0); + }, 0); + const newTotalRewards = `${rewardsSum} nym`; setDelegations(newDelegations); setTotalDelegations(newTotalDelegations); + setTotalRewards(newTotalRewards); + setTotalDelegationsAndRewards(`${newTotalDelegations} + ${newTotalRewards}`); + setLastUpdatedAtMs(Date.now()); }; const addDelegation = async ( @@ -193,8 +204,7 @@ export const MockDelegationContextProvider: FCWithChildren = ({ children }) => { }; }; - // eslint-disable-next-line @typescript-eslint/no-unused-vars - const undelegateVesting = async (mix_id: number, _fee?: FeeDetails) => ({ + const undelegateVesting = async (_mix_id: number) => ({ msg_responses_json: '', data_json: '', logs_json: '', @@ -210,10 +220,13 @@ export const MockDelegationContextProvider: FCWithChildren = ({ children }) => { setIsLoading(true); setError(undefined); setTotalDelegations(undefined); + setTotalRewards(undefined); + setTotalDelegationsAndRewards(undefined); + setLastUpdatedAtMs(0); setDelegations([]); }; - const refresh = useCallback(async (_opts?: TDelegationRefreshOptions) => { + const refresh = useCallback(async () => { resetState(); setTimeout(async () => { try { @@ -239,18 +252,32 @@ export const MockDelegationContextProvider: FCWithChildren = ({ children }) => { isLoading, isFetching: isLoading, isError: Boolean(error), - lastUpdatedAtMs: 0, - error, + lastUpdatedAtMs, delegations, + pendingDelegations: [], totalDelegations, + totalRewards, + totalDelegationsAndRewards, refresh, - getDelegations, addDelegation, - updateDelegation, undelegate, undelegateVesting, }), - [isLoading, error, delegations, totalDelegations, trigger], + [ + isLoading, + error, + delegations, + totalDelegations, + totalRewards, + totalDelegationsAndRewards, + lastUpdatedAtMs, + refresh, + addDelegation, + undelegate, + undelegateVesting, + delegationItemErrors, + trigger, + ], ); return {children}; diff --git a/nym-wallet/src/context/rewards.tsx b/nym-wallet/src/context/rewards.tsx index f0d91a8bba..dce17f8b3a 100644 --- a/nym-wallet/src/context/rewards.tsx +++ b/nym-wallet/src/context/rewards.tsx @@ -1,13 +1,13 @@ import React, { createContext, useContext, useEffect, useMemo, useState } from 'react'; import { FeeDetails, TransactionExecuteResult } from '@nymproject/types'; -import { type TDelegationRefreshOptions, useDelegationContext } from './delegations'; +import { useDelegationContext } from './delegations'; import { claimDelegatorRewards } from '../requests'; type TRewardsContext = { isLoading: boolean; error?: string; totalRewards?: string; - refresh: (opts?: TDelegationRefreshOptions) => Promise; + refresh: () => Promise; claimRewards: (mixId: number, fee?: FeeDetails) => Promise; }; @@ -18,7 +18,7 @@ export type TRewardsTransaction = { export const RewardsContext = createContext({ isLoading: false, - refresh: async (_opts?: TDelegationRefreshOptions) => undefined, + refresh: async () => undefined, claimRewards: async () => { throw new Error('Not implemented'); }, diff --git a/nym-wallet/src/log.tsx b/nym-wallet/src/log.tsx index 8fc9f166c1..24330d7f26 100644 --- a/nym-wallet/src/log.tsx +++ b/nym-wallet/src/log.tsx @@ -1,15 +1,18 @@ import React from 'react'; import { createRoot } from 'react-dom/client'; import { ErrorBoundary } from 'react-error-boundary'; +import { SnackbarProvider } from 'notistack'; import { LogViewer } from './components/LogViewer'; import { ErrorFallback } from './components'; import { NymWalletTheme } from './theme'; const Log = () => ( - - - + + + + + ); diff --git a/nym-wallet/src/pages/delegation/index.tsx b/nym-wallet/src/pages/delegation/index.tsx index 07e96aa7fa..4971014083 100644 --- a/nym-wallet/src/pages/delegation/index.tsx +++ b/nym-wallet/src/pages/delegation/index.tsx @@ -114,7 +114,7 @@ export const DelegationPage: FC = () => { const doMigrateNow = async () => { setShowVestingMigrationProgressModal(true); await migrateVestedDelegations(); - await refreshDelegations(undefined); + await refreshDelegations(); setShowVestingMigrationProgressModal(false); }; @@ -132,7 +132,7 @@ export const DelegationPage: FC = () => { }); } prevConfirmationModalProps.current = confirmationModalProps; - }, [confirmationModalProps]); + }, [confirmationModalProps, refreshWithIntervalUpdate]); const handleDelegationItemActionClick = (item: DelegationWithEverything, action: DelegationListItemActions) => { if (