From d5ad504104a98e55b77dccf470d840c6436c8540 Mon Sep 17 00:00:00 2001 From: fmtabbara Date: Mon, 27 Feb 2023 10:58:19 +0000 Subject: [PATCH 1/6] reuseable storage functions --- nym-connect/desktop/src/utils/index.ts | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/nym-connect/desktop/src/utils/index.ts b/nym-connect/desktop/src/utils/index.ts index fefef166d7..04b415c026 100644 --- a/nym-connect/desktop/src/utils/index.ts +++ b/nym-connect/desktop/src/utils/index.ts @@ -1,6 +1,8 @@ import { useEffect, useRef } from 'react'; import { EventName, listen, UnlistenFn, EventCallback } from '@tauri-apps/api/event'; import { invoke } from '@tauri-apps/api'; +import { forage } from '@tauri-apps/tauri-forage'; +import { StorageKeyValue } from 'src/types/storage'; export const useTauriEvents = (event: EventName, handler: EventCallback) => { const unlisten = useRef(); @@ -22,3 +24,25 @@ export const useTauriEvents = (event: EventName, handler: EventCallback) = export const toggleLogViewer = async () => { await invoke('help_log_toggle_window'); }; + +export async function setItemInStorage({ key, value }: StorageKeyValue) { + try { + await forage.setItem({ + key, + value, + } as any)(); + } catch (e) { + console.warn(e); + } + return undefined; +} + +export const getItemFromStorage = async ({ key }: Pick, 'key'>) => { + try { + const gatewayFromStorage = await forage.getItem({ key })(); + return gatewayFromStorage; + } catch (e) { + console.warn(e); + } + return undefined; +}; From 7a4c6e4ed46e592430410a9b0591c17ee162d96f Mon Sep 17 00:00:00 2001 From: fmtabbara Date: Mon, 27 Feb 2023 10:58:31 +0000 Subject: [PATCH 2/6] storage type update types --- .../menu/settings/ServiceProviderSettings.tsx | 37 +++++++++++-------- .../desktop/src/types/service-provider.ts | 9 +++++ nym-connect/desktop/src/types/storage.ts | 4 ++ 3 files changed, 35 insertions(+), 15 deletions(-) create mode 100644 nym-connect/desktop/src/types/service-provider.ts create mode 100644 nym-connect/desktop/src/types/storage.ts diff --git a/nym-connect/desktop/src/pages/menu/settings/ServiceProviderSettings.tsx b/nym-connect/desktop/src/pages/menu/settings/ServiceProviderSettings.tsx index 8906c67129..1d7fe62f13 100644 --- a/nym-connect/desktop/src/pages/menu/settings/ServiceProviderSettings.tsx +++ b/nym-connect/desktop/src/pages/menu/settings/ServiceProviderSettings.tsx @@ -1,4 +1,4 @@ -import React, { ChangeEvent, useState } from 'react'; +import React, { ChangeEvent, useEffect, useState } from 'react'; import { Autocomplete, Box, @@ -15,18 +15,22 @@ import { ConnectionStatusKind } from 'src/types'; import { useClientContext } from 'src/context/main'; export const ServiceProviderSettings = () => { - const [spAddress, setSPAddress] = useState(); - const [isOn, setIsOn] = useState(false); - - const { connectionStatus, serviceProviders } = useClientContext(); + const { connectionStatus, serviceProviders, userDefinedSPAddress, setUserDefinedSPAddress } = useClientContext(); const toggleOnOff = (e: ChangeEvent) => { - if (!isOn) setSPAddress(undefined); - setIsOn(e.target.checked); + setUserDefinedSPAddress((current) => ({ ...current, isActive: e.target.checked })); }; const handleSelectFromList = (value: string | null) => { - setSPAddress(value ?? undefined); + setUserDefinedSPAddress((current) => ({ ...current, address: value ?? undefined })); + }; + + const getSPDescription = (spAddress: string) => { + const match = serviceProviders?.find((sp) => sp.address === spAddress); + + if (match) return match.description; + + return 'The service provider specified is not in our known list.'; }; return ( @@ -43,40 +47,43 @@ export const ServiceProviderSettings = () => { } - label={isOn ? 'On' : 'Off'} + label={userDefinedSPAddress.isActive ? 'On' : 'Off'} /> {connectionStatus === ConnectionStatusKind.connected && ( This setting is disabled during an active connection )} - {isOn && serviceProviders && ( + {userDefinedSPAddress.isActive && serviceProviders && ( `${sp.address.substring(0, 20)}...`)} + options={serviceProviders.map((sp) => sp.address)} + getOptionLabel={(address) => `${address.substring(0, 18)}...`} freeSolo onChange={(e, value) => handleSelectFromList(value)} - value={spAddress} size="small" renderInput={(params) => ( console.log(e.target.value)} placeholder="Service provider" + onChange={(e) => setUserDefinedSPAddress((current) => ({ ...current, address: e.target.value }))} /> )} ListboxProps={{ style: { background: 'unset', fontSize: '14px' } }} /> )} + {userDefinedSPAddress.address && userDefinedSPAddress.isActive && ( + {getSPDescription(userDefinedSPAddress.address)} + )} diff --git a/nym-connect/desktop/src/types/service-provider.ts b/nym-connect/desktop/src/types/service-provider.ts new file mode 100644 index 0000000000..ed370e2755 --- /dev/null +++ b/nym-connect/desktop/src/types/service-provider.ts @@ -0,0 +1,9 @@ +export interface UserDefinedGateway { + isActive: boolean; + gateway?: string; +} + +export interface UserDefinedSPAddress { + isActive: boolean; + address?: string; +} diff --git a/nym-connect/desktop/src/types/storage.ts b/nym-connect/desktop/src/types/storage.ts new file mode 100644 index 0000000000..ca792b65d9 --- /dev/null +++ b/nym-connect/desktop/src/types/storage.ts @@ -0,0 +1,4 @@ +export interface StorageKeyValue { + key: string; + value: T; +} From 9644eb43298dd655af73b312685c3d933d763d55 Mon Sep 17 00:00:00 2001 From: fmtabbara Date: Mon, 27 Feb 2023 10:59:28 +0000 Subject: [PATCH 3/6] pick service provider --- .../desktop/src/components/AppWindowFrame.tsx | 2 +- nym-connect/desktop/src/context/main.tsx | 82 +++++++++++-------- .../desktop/src/context/mocks/main.tsx | 4 +- .../desktop/src/pages/connection/index.tsx | 2 +- nym-connect/desktop/src/types/gateway.ts | 4 - 5 files changed, 52 insertions(+), 42 deletions(-) delete mode 100644 nym-connect/desktop/src/types/gateway.ts diff --git a/nym-connect/desktop/src/components/AppWindowFrame.tsx b/nym-connect/desktop/src/components/AppWindowFrame.tsx index 929b0f269f..01aa390286 100644 --- a/nym-connect/desktop/src/components/AppWindowFrame.tsx +++ b/nym-connect/desktop/src/components/AppWindowFrame.tsx @@ -11,7 +11,7 @@ export const AppWindowFrame: FCWithChildren = ({ children }) => { // defined functions to be used when moving away from pages const onBack = () => { switch (location.pathname) { - case '/menu/settings': + case '/menu/settings/gateway': return () => { // when the user moves away from the settings page and the gateway is not valid // set isActive to false diff --git a/nym-connect/desktop/src/context/main.tsx b/nym-connect/desktop/src/context/main.tsx index 2efdc49289..5de2ae1bc5 100644 --- a/nym-connect/desktop/src/context/main.tsx +++ b/nym-connect/desktop/src/context/main.tsx @@ -4,13 +4,14 @@ import { invoke } from '@tauri-apps/api'; import { Error } from 'src/types/error'; import { getVersion } from '@tauri-apps/api/app'; import { useEvents } from 'src/hooks/events'; -import { UserDefinedGateway } from 'src/types/gateway'; -import { forage } from '@tauri-apps/tauri-forage'; +import { UserDefinedGateway, UserDefinedSPAddress } from 'src/types/service-provider'; import { ConnectionStatusKind, GatewayPerformance } from '../types'; import { ConnectionStatsItem } from '../components/ConnectionStats'; import { ServiceProvider } from '../types/directory'; +import { getItemFromStorage, setItemInStorage } from 'src/utils'; -const FORAGE_KEY = 'nym-connect-user-gateway'; +const FORAGE_GATEWAY_KEY = 'nym-connect-user-gateway'; +const FORAGE_SP_KEY = 'nym-connect-user-sp'; type ModeType = 'light' | 'dark'; @@ -25,6 +26,7 @@ export type TClientContext = { selectedProvider?: ServiceProvider; showInfoModal: boolean; userDefinedGateway?: UserDefinedGateway; + userDefinedSPAddress: UserDefinedSPAddress; serviceProviders?: ServiceProvider[]; setMode: (mode: ModeType) => void; clearError: () => void; @@ -32,10 +34,11 @@ export type TClientContext = { setConnectionStats: (connectionStats: ConnectionStatsItem[] | undefined) => void; setConnectedSince: (connectedSince: DateTime | undefined) => void; setShowInfoModal: (show: boolean) => void; - setRandomSerivceProvider: () => void; + setSerivceProvider: () => void; startConnecting: () => Promise; startDisconnecting: () => Promise; setUserDefinedGateway: React.Dispatch>; + setUserDefinedSPAddress: React.Dispatch>; }; export const ClientContext = createContext({} as TClientContext); @@ -51,43 +54,39 @@ export const ClientContextProvider: FCWithChildren = ({ children }) => { const [appVersion, setAppVersion] = useState(); const [gatewayPerformance, setGatewayPerformance] = useState('Good'); const [showInfoModal, setShowInfoModal] = useState(false); - const [userDefinedGateway, setUserDefinedGateway] = useState({ isActive: false, gateway: '' }); + const [userDefinedGateway, setUserDefinedGateway] = useState({ + isActive: false, + gateway: undefined, + }); + const [userDefinedSPAddress, setUserDefinedSPAddress] = useState({ + isActive: false, + address: undefined, + }); const getAppVersion = async () => { const version = await getVersion(); return version; }; - const setUserGatewayInStorage = async (gateway: UserDefinedGateway) => { - try { - await forage.setItem({ - key: FORAGE_KEY, - value: gateway, - } as any)(); - } catch (e) { - console.warn(e); - } - return undefined; - }; + useEffect(() => { + setItemInStorage({ key: FORAGE_GATEWAY_KEY, value: userDefinedGateway }); + }, [userDefinedGateway]); - const getUserGatewayFromStorage = async (): Promise => { - try { - const gatewayFromStorage = await forage.getItem({ key: FORAGE_KEY })(); - return gatewayFromStorage; - } catch (e) { - console.warn(e); - } - return undefined; - }; + useEffect(() => { + setItemInStorage({ key: FORAGE_SP_KEY, value: userDefinedSPAddress }); + }, [userDefinedGateway]); const initialiseApp = async () => { const services = await invoke('get_services'); const AppVersion = await getAppVersion(); - const storedUserDefinedGateway = await getUserGatewayFromStorage(); + const storedUserDefinedGateway = await getItemFromStorage({ key: FORAGE_GATEWAY_KEY }); + const storedUserDefinedSP = await getItemFromStorage({ key: FORAGE_SP_KEY }); setAppVersion(AppVersion); setServiceProviders(services as ServiceProvider[]); + if (storedUserDefinedGateway) setUserDefinedGateway(storedUserDefinedGateway); + if (storedUserDefinedSP) setUserDefinedSPAddress(storedUserDefinedSP); }; useEvents({ @@ -126,27 +125,37 @@ export const ClientContextProvider: FCWithChildren = ({ children }) => { }, []); const shouldUseUserGateway = !!userDefinedGateway.gateway && userDefinedGateway.isActive; + const shouldUseUserSP = !!userDefinedSPAddress.address && userDefinedSPAddress.isActive; const setServiceProvider = async (newServiceProvider: ServiceProvider) => { await invoke('set_gateway', { - gateway: newServiceProvider.gateway, + gateway: shouldUseUserGateway ? userDefinedGateway.gateway : newServiceProvider.gateway, + }); + await invoke('set_service_provider', { + serviceProvider: shouldUseUserSP ? userDefinedSPAddress.address : newServiceProvider.address, }); - await invoke('set_service_provider', { serviceProvider: newServiceProvider.address }); }; const getRandomSPFromList = (services: ServiceProvider[]) => { const randomSelection = services[Math.floor(Math.random() * services.length)]; - - if (shouldUseUserGateway) return { ...randomSelection, gateway: userDefinedGateway.gateway } as ServiceProvider; return randomSelection; }; - const setRandomSerivceProvider = async () => { + const buildServiceProvider = async (serviceProvider: ServiceProvider) => { + let sp = { ...serviceProvider }; + + if (shouldUseUserGateway) sp.gateway = userDefinedGateway.gateway as string; + if (shouldUseUserSP) sp.address = userDefinedSPAddress.address as string; + + return sp; + }; + + const setSerivceProvider = async () => { if (serviceProviders) { const randomServiceProvider = getRandomSPFromList(serviceProviders); - await setServiceProvider(randomServiceProvider); - await setUserGatewayInStorage(userDefinedGateway); - setSelectedProvider(randomServiceProvider); + const withUserDefinitions = await buildServiceProvider(randomServiceProvider); + await setServiceProvider(withUserDefinitions); + setSelectedProvider(withUserDefinitions); } return undefined; }; @@ -169,13 +178,15 @@ export const ClientContextProvider: FCWithChildren = ({ children }) => { serviceProviders, connectedSince, setConnectedSince, - setRandomSerivceProvider, + setSerivceProvider, startConnecting, startDisconnecting, gatewayPerformance, setShowInfoModal, + userDefinedSPAddress, userDefinedGateway, setUserDefinedGateway, + setUserDefinedSPAddress, }), [ mode, @@ -190,6 +201,7 @@ export const ClientContextProvider: FCWithChildren = ({ children }) => { gatewayPerformance, selectedProvider, userDefinedGateway, + userDefinedSPAddress, ], ); diff --git a/nym-connect/desktop/src/context/mocks/main.tsx b/nym-connect/desktop/src/context/mocks/main.tsx index 6d0f7d9a82..78028b03f8 100644 --- a/nym-connect/desktop/src/context/mocks/main.tsx +++ b/nym-connect/desktop/src/context/mocks/main.tsx @@ -10,6 +10,7 @@ const mockValues: TClientContext = { gatewayPerformance: 'Good', showInfoModal: false, userDefinedGateway: { isActive: false, gateway: '' }, + userDefinedSPAddress: { isActive: false, address: '' }, setShowInfoModal: () => {}, setMode: () => {}, clearError: () => {}, @@ -18,8 +19,9 @@ const mockValues: TClientContext = { setConnectionStatus: () => {}, startConnecting: async () => {}, startDisconnecting: async () => {}, - setRandomSerivceProvider: () => {}, + setSerivceProvider: () => {}, setUserDefinedGateway: () => {}, + setUserDefinedSPAddress: () => {}, }; export const MockProvider: FCWithChildren<{ diff --git a/nym-connect/desktop/src/pages/connection/index.tsx b/nym-connect/desktop/src/pages/connection/index.tsx index 028ddcf21d..2ba081331f 100644 --- a/nym-connect/desktop/src/pages/connection/index.tsx +++ b/nym-connect/desktop/src/pages/connection/index.tsx @@ -28,7 +28,7 @@ export const ConnectionPage = () => { // eslint-disable-next-line default-case switch (currentStatus) { case 'disconnected': - await context.setRandomSerivceProvider(); + await context.setSerivceProvider(); await context.startConnecting(); context.setConnectedSince(DateTime.now()); context.setShowInfoModal(true); diff --git a/nym-connect/desktop/src/types/gateway.ts b/nym-connect/desktop/src/types/gateway.ts deleted file mode 100644 index 735a3cdb03..0000000000 --- a/nym-connect/desktop/src/types/gateway.ts +++ /dev/null @@ -1,4 +0,0 @@ -export interface UserDefinedGateway { - isActive: boolean; - gateway?: string; -} From ec23f3dcb7e1b665e7a00e0858fbe505160af625 Mon Sep 17 00:00:00 2001 From: fmtabbara Date: Mon, 27 Feb 2023 11:13:25 +0000 Subject: [PATCH 4/6] disable input when connected fix lint errors --- nym-connect/desktop/src/context/main.tsx | 4 ++-- .../src/pages/menu/settings/ServiceProviderSettings.tsx | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/nym-connect/desktop/src/context/main.tsx b/nym-connect/desktop/src/context/main.tsx index 5de2ae1bc5..c861eb9bf0 100644 --- a/nym-connect/desktop/src/context/main.tsx +++ b/nym-connect/desktop/src/context/main.tsx @@ -5,10 +5,10 @@ import { Error } from 'src/types/error'; import { getVersion } from '@tauri-apps/api/app'; import { useEvents } from 'src/hooks/events'; import { UserDefinedGateway, UserDefinedSPAddress } from 'src/types/service-provider'; +import { getItemFromStorage, setItemInStorage } from 'src/utils'; import { ConnectionStatusKind, GatewayPerformance } from '../types'; import { ConnectionStatsItem } from '../components/ConnectionStats'; import { ServiceProvider } from '../types/directory'; -import { getItemFromStorage, setItemInStorage } from 'src/utils'; const FORAGE_GATEWAY_KEY = 'nym-connect-user-gateway'; const FORAGE_SP_KEY = 'nym-connect-user-sp'; @@ -142,7 +142,7 @@ export const ClientContextProvider: FCWithChildren = ({ children }) => { }; const buildServiceProvider = async (serviceProvider: ServiceProvider) => { - let sp = { ...serviceProvider }; + const sp = { ...serviceProvider }; if (shouldUseUserGateway) sp.gateway = userDefinedGateway.gateway as string; if (shouldUseUserSP) sp.address = userDefinedSPAddress.address as string; diff --git a/nym-connect/desktop/src/pages/menu/settings/ServiceProviderSettings.tsx b/nym-connect/desktop/src/pages/menu/settings/ServiceProviderSettings.tsx index 1d7fe62f13..580d36e9d6 100644 --- a/nym-connect/desktop/src/pages/menu/settings/ServiceProviderSettings.tsx +++ b/nym-connect/desktop/src/pages/menu/settings/ServiceProviderSettings.tsx @@ -1,4 +1,4 @@ -import React, { ChangeEvent, useEffect, useState } from 'react'; +import React, { ChangeEvent } from 'react'; import { Autocomplete, Box, @@ -62,6 +62,7 @@ export const ServiceProviderSettings = () => { {userDefinedSPAddress.isActive && serviceProviders && ( sp.address)} From 7929bac6855cdf2a1abd22c3618bb36e253eba9f Mon Sep 17 00:00:00 2001 From: fmtabbara Date: Mon, 27 Feb 2023 14:20:16 +0000 Subject: [PATCH 5/6] turn off user defined SP address is input it empty --- nym-connect/desktop/src/components/AppWindowFrame.tsx | 11 ++++++++++- nym-connect/desktop/src/context/main.tsx | 2 +- .../pages/menu/settings/ServiceProviderSettings.tsx | 9 ++++++--- 3 files changed, 17 insertions(+), 5 deletions(-) diff --git a/nym-connect/desktop/src/components/AppWindowFrame.tsx b/nym-connect/desktop/src/components/AppWindowFrame.tsx index 01aa390286..f8304ce00b 100644 --- a/nym-connect/desktop/src/components/AppWindowFrame.tsx +++ b/nym-connect/desktop/src/components/AppWindowFrame.tsx @@ -6,7 +6,8 @@ import { CustomTitleBar } from './CustomTitleBar'; export const AppWindowFrame: FCWithChildren = ({ children }) => { const location = useLocation(); - const { userDefinedGateway, setUserDefinedGateway } = useClientContext(); + const { userDefinedGateway, setUserDefinedGateway, userDefinedSPAddress, setUserDefinedSPAddress } = + useClientContext(); // defined functions to be used when moving away from pages const onBack = () => { @@ -19,6 +20,14 @@ export const AppWindowFrame: FCWithChildren = ({ children }) => { setUserDefinedGateway((current) => ({ ...current, isActive: false })); } }; + case '/menu/settings/service-provider': + return () => { + // when the user moves away from the settings page and the gateway is not valid + // set isActive to false + if (!userDefinedSPAddress?.address) { + setUserDefinedSPAddress((current) => ({ ...current, isActive: false })); + } + }; default: return undefined; } diff --git a/nym-connect/desktop/src/context/main.tsx b/nym-connect/desktop/src/context/main.tsx index c861eb9bf0..b999b957a2 100644 --- a/nym-connect/desktop/src/context/main.tsx +++ b/nym-connect/desktop/src/context/main.tsx @@ -74,7 +74,7 @@ export const ClientContextProvider: FCWithChildren = ({ children }) => { useEffect(() => { setItemInStorage({ key: FORAGE_SP_KEY, value: userDefinedSPAddress }); - }, [userDefinedGateway]); + }, [userDefinedSPAddress]); const initialiseApp = async () => { const services = await invoke('get_services'); diff --git a/nym-connect/desktop/src/pages/menu/settings/ServiceProviderSettings.tsx b/nym-connect/desktop/src/pages/menu/settings/ServiceProviderSettings.tsx index 580d36e9d6..8f18caaacd 100644 --- a/nym-connect/desktop/src/pages/menu/settings/ServiceProviderSettings.tsx +++ b/nym-connect/desktop/src/pages/menu/settings/ServiceProviderSettings.tsx @@ -33,6 +33,10 @@ export const ServiceProviderSettings = () => { return 'The service provider specified is not in our known list.'; }; + const validateInput = (value: string) => { + setUserDefinedSPAddress((current) => ({ ...current, address: !value.length ? undefined : value })); + }; + return ( @@ -63,11 +67,10 @@ export const ServiceProviderSettings = () => { sp.address)} - getOptionLabel={(address) => `${address.substring(0, 18)}...`} freeSolo + value={userDefinedSPAddress.address || ''} onChange={(e, value) => handleSelectFromList(value)} size="small" renderInput={(params) => ( @@ -75,7 +78,7 @@ export const ServiceProviderSettings = () => { autoFocus {...params} placeholder="Service provider" - onChange={(e) => setUserDefinedSPAddress((current) => ({ ...current, address: e.target.value }))} + onChange={(e) => validateInput(e.target.value)} /> )} ListboxProps={{ style: { background: 'unset', fontSize: '14px' } }} From bdcc19e86a2329bde33ee7a20f07b1a642cf0828 Mon Sep 17 00:00:00 2001 From: fmtabbara Date: Mon, 27 Feb 2023 15:33:54 +0000 Subject: [PATCH 6/6] PR update --- nym-connect/desktop/src/components/AppWindowFrame.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nym-connect/desktop/src/components/AppWindowFrame.tsx b/nym-connect/desktop/src/components/AppWindowFrame.tsx index f8304ce00b..90e01fffa7 100644 --- a/nym-connect/desktop/src/components/AppWindowFrame.tsx +++ b/nym-connect/desktop/src/components/AppWindowFrame.tsx @@ -22,7 +22,7 @@ export const AppWindowFrame: FCWithChildren = ({ children }) => { }; case '/menu/settings/service-provider': return () => { - // when the user moves away from the settings page and the gateway is not valid + // when the user moves away from the settings page and the sp is not valid // set isActive to false if (!userDefinedSPAddress?.address) { setUserDefinedSPAddress((current) => ({ ...current, isActive: false }));