From 11bbbb3179e466204d998487dcff991a14abb1cc Mon Sep 17 00:00:00 2001 From: Fouad Date: Tue, 7 Feb 2023 06:32:02 +0000 Subject: [PATCH] Feature/nym connect health status frontend (#2969) * filter services on rust side by gateway performance * format rust code * create events hook * use events hook * remove unused component remove unused component update prop names in svg * display errors in connected screen when needed update failed health check message --- nym-connect/src/App.tsx | 74 ------------------- .../components/PowerButton/PowerButton.tsx | 6 +- nym-connect/src/context/main.tsx | 53 ++----------- nym-connect/src/hooks/events.ts | 68 +++++++++++++++++ nym-connect/src/index.tsx | 1 - .../src/pages/connection/Connected.tsx | 7 ++ nym-connect/src/pages/connection/index.tsx | 2 + nym-connect/src/stories/AppFlow.stories.tsx | 1 + .../src/stories/ConnectedLayout.stories.tsx | 1 + 9 files changed, 89 insertions(+), 124 deletions(-) delete mode 100644 nym-connect/src/App.tsx create mode 100644 nym-connect/src/hooks/events.ts diff --git a/nym-connect/src/App.tsx b/nym-connect/src/App.tsx deleted file mode 100644 index 9d57dcf4d8..0000000000 --- a/nym-connect/src/App.tsx +++ /dev/null @@ -1,74 +0,0 @@ -import React, { useEffect } from 'react'; -import { DateTime } from 'luxon'; -import { forage } from '@tauri-apps/tauri-forage'; -import { useClientContext } from './context/main'; -import { useTauriEvents } from './utils'; -import { AppRoutes } from './routes'; -import { Connected } from './pages/connection/Connected'; - -export const App: FCWithChildren = () => { - const context = useClientContext(); - const [busy, setBusy] = React.useState(); - - useTauriEvents('help://clear-storage', (_event) => { - console.log('About to clear local storage...'); - // clear local storage - try { - forage.clear()(); - console.log('Local storage cleared'); - } catch (e) { - console.error('Failed to clear local storage', e); - } - }); - - const handleConnectClick = React.useCallback(async () => { - const currentStatus = context.connectionStatus; - if (currentStatus === 'connected' || currentStatus === 'disconnected') { - setBusy(true); - - // eslint-disable-next-line default-case - switch (currentStatus) { - case 'disconnected': - await context.startConnecting(); - context.setConnectedSince(DateTime.now()); - break; - case 'connected': - await context.startDisconnecting(); - context.setConnectedSince(undefined); - break; - } - setBusy(false); - } - }, [context.connectionStatus]); - - if (context.connectionStatus === 'disconnected' || context.connectionStatus === 'connecting') { - return ; - } - - return ( - context.setShowInfoModal(false)} - busy={busy} - onConnectClick={handleConnectClick} - ipAddress="127.0.0.1" - port={1080} - gatewayPerformance={context.gatewayPerformance} - connectedSince={context.connectedSince} - serviceProvider={context.selectedProvider} - stats={[ - { - label: 'in:', - totalBytes: 1024, - rateBytesPerSecond: 1024 * 1024 * 1024 + 10, - }, - { - label: 'out:', - totalBytes: 1024 * 1024 * 1024 * 1024 * 20, - rateBytesPerSecond: 1024 * 1024 + 10, - }, - ]} - /> - ); -}; diff --git a/nym-connect/src/components/PowerButton/PowerButton.tsx b/nym-connect/src/components/PowerButton/PowerButton.tsx index a0c6dc757a..4e0c690967 100644 --- a/nym-connect/src/components/PowerButton/PowerButton.tsx +++ b/nym-connect/src/components/PowerButton/PowerButton.tsx @@ -89,18 +89,18 @@ export const PowerButton: FCWithChildren<{ - + - + - + { setServiceProviders(services as ServiceProvider[]); }; + useEvents({ + onError: (error) => setError(error), + onGatewayPerformanceChange: (performance) => setGatewayPerformance(performance), + onStatusChange: (status) => setConnectionStatus(status), + }); + useEffect(() => { initialiseApp(); }, []); @@ -78,49 +82,6 @@ export const ClientContextProvider: FCWithChildren = ({ children }) => { })(); }, []); - useEffect(() => { - const unlisten: UnlistenFn[] = []; - - // TODO: fix typings - listen(TAURI_EVENT_STATUS_CHANGED, (event) => { - const { status } = event.payload as any; - console.log(TAURI_EVENT_STATUS_CHANGED, { status, event }); - setConnectionStatus(status); - }) - .then((result) => { - unlisten.push(result); - }) - .catch((e) => console.log(e)); - - listen('socks5-event', (e: TauriEvent) => { - console.log(e); - - setError(e.payload); - }).then((result) => { - unlisten.push(result); - }); - - listen('socks5-status-event', (e: TauriEvent) => { - if (e.payload.message.includes('slow')) { - setGatewayPerformance('Poor'); - - if (timerId.current) { - clearTimeout(timerId.current); - } - - timerId.current = setTimeout(() => { - setGatewayPerformance('Good'); - }, 10000); - } - }).then((result) => { - unlisten.push(result); - }); - - return () => { - unlisten.forEach((unsubscribe) => unsubscribe()); - }; - }, []); - const startConnecting = useCallback(async () => { try { await invoke('start_connecting'); diff --git a/nym-connect/src/hooks/events.ts b/nym-connect/src/hooks/events.ts new file mode 100644 index 0000000000..4b2c4fcc55 --- /dev/null +++ b/nym-connect/src/hooks/events.ts @@ -0,0 +1,68 @@ +import { useEffect, useRef } from 'react'; +import { listen, UnlistenFn } from '@tauri-apps/api/event'; +import { ConnectionStatusKind, GatewayPerformance } from 'src/types'; +import { Error } from 'src/types/error'; +import { TauriEvent } from 'src/types/event'; + +const TAURI_EVENT_STATUS_CHANGED = 'app:connection-status-changed'; + +export const useEvents = ({ + onError, + onStatusChange, + onGatewayPerformanceChange, +}: { + onError: (error: Error) => void; + onStatusChange: (status: ConnectionStatusKind) => void; + onGatewayPerformanceChange: (status: GatewayPerformance) => void; +}) => { + const timerId = useRef(); + + useEffect(() => { + const unlisten: UnlistenFn[] = []; + + // TODO: fix typings + listen(TAURI_EVENT_STATUS_CHANGED, (event) => { + const { status } = event.payload as any; + console.log(TAURI_EVENT_STATUS_CHANGED, { status, event }); + onStatusChange(status); + }) + .then((result) => { + unlisten.push(result); + }) + .catch((e) => console.log(e)); + + listen('socks5-event', (e: TauriEvent) => { + console.log(e); + onError(e.payload); + }).then((result) => { + unlisten.push(result); + }); + + listen('socks5-status-event', (e: TauriEvent) => { + if (e.payload.message.includes('slow')) { + onGatewayPerformanceChange('Poor'); + + if (timerId?.current) { + clearTimeout(timerId.current); + } + + timerId.current = setTimeout(() => { + onGatewayPerformanceChange('Good'); + }, 10000); + } + }).then((result) => { + unlisten.push(result); + }); + + listen('socks5-connection-fail-event', (e: TauriEvent) => { + onError({ title: 'Connection failed', message: `${e.payload.message} - Please disconnect and reconnect.` }); + onGatewayPerformanceChange('Poor'); + }).then((result) => { + unlisten.push(result); + }); + + return () => { + unlisten.forEach((unsubscribe) => unsubscribe()); + }; + }, []); +}; diff --git a/nym-connect/src/index.tsx b/nym-connect/src/index.tsx index 7041b86787..2498330910 100644 --- a/nym-connect/src/index.tsx +++ b/nym-connect/src/index.tsx @@ -4,7 +4,6 @@ import { ErrorBoundary } from 'react-error-boundary'; import { ClientContextProvider } from './context/main'; import { ErrorFallback } from './components/Error'; import { NymMixnetTheme } from './theme'; -import { App } from './App'; import { AppWindowFrame } from './components/AppWindowFrame'; import { TestAndEarnContextProvider } from './components/Growth/context/TestAndEarnContext'; import { BrowserRouter as Router } from 'react-router-dom'; diff --git a/nym-connect/src/pages/connection/Connected.tsx b/nym-connect/src/pages/connection/Connected.tsx index a896380fd4..5071349ec3 100644 --- a/nym-connect/src/pages/connection/Connected.tsx +++ b/nym-connect/src/pages/connection/Connected.tsx @@ -11,8 +11,11 @@ import { ServiceProvider } from 'src/types/directory'; import { ExperimentalWarning } from 'src/components/ExperimentalWarning'; import { ConnectionLayout } from 'src/layouts/ConnectionLayout'; import { PowerButton } from 'src/components/PowerButton/PowerButton'; +import { Error } from 'src/types/error'; +import { InfoModal } from 'src/components/InfoModal'; export const Connected: FCWithChildren<{ + error?: Error; status: ConnectionStatusKind; showInfoModal: boolean; gatewayPerformance: GatewayPerformance; @@ -23,9 +26,11 @@ export const Connected: FCWithChildren<{ busy?: boolean; isError?: boolean; serviceProvider?: ServiceProvider; + clearError: () => void; onConnectClick: (status: ConnectionStatusKind) => void; closeInfoModal: () => void; }> = ({ + error, status, showInfoModal, gatewayPerformance, @@ -35,11 +40,13 @@ export const Connected: FCWithChildren<{ busy, isError, serviceProvider, + clearError, onConnectClick, closeInfoModal, }) => { return ( <> + {error && } { if (context.connectionStatus === 'connected') return ( = () => { return ( {}} gatewayPerformance="Good" showInfoModal={false} closeInfoModal={() => undefined} diff --git a/nym-connect/src/stories/ConnectedLayout.stories.tsx b/nym-connect/src/stories/ConnectedLayout.stories.tsx index b572d58490..f6d744f68c 100644 --- a/nym-connect/src/stories/ConnectedLayout.stories.tsx +++ b/nym-connect/src/stories/ConnectedLayout.stories.tsx @@ -15,6 +15,7 @@ export default { export const Default: ComponentStory = () => ( {}} gatewayPerformance="Good" showInfoModal={false} closeInfoModal={() => {