onClick(country.name, country.code)}
diff --git a/nym-vpn/ui/src/pages/location/NodeLocation.tsx b/nym-vpn/ui/src/pages/location/NodeLocation.tsx
index c010525e72..bb873fb350 100644
--- a/nym-vpn/ui/src/pages/location/NodeLocation.tsx
+++ b/nym-vpn/ui/src/pages/location/NodeLocation.tsx
@@ -3,24 +3,20 @@ import { useEffect, useState } from 'react';
import { useNavigate } from 'react-router-dom';
import { invoke } from '@tauri-apps/api';
import { useMainDispatch, useMainState } from '../../contexts';
-import { InputEvent, NodeHop } from '../../types/general';
-import { Country, StateDispatch } from '../../types';
+import { Country, InputEvent, NodeHop, StateDispatch } from '../../types';
import { routes } from '../../constants';
import SearchBox from './SearchBox';
import CountryList from './CountryList';
import QuickConnect from './QuickConnect';
-function NodeLocation({ type }: NodeHop) {
- const isEntryNodeSelectionScreen = type === 'entry';
+function NodeLocation({ node }: { node: NodeHop }) {
const { t } = useTranslation('nodeLocation');
const [countries, setCountries] = useState
([]);
const [search, setSearch] = useState('');
const [loading, setLoading] = useState(false);
const [foundCountries, setFoundCountries] = useState([]);
- const {
- localAppData: { entryNode, exitNode },
- } = useMainState();
+ const { entryNodeLocation, exitNodeLocation } = useMainState();
const dispatch = useMainDispatch() as StateDispatch;
const navigate = useNavigate();
@@ -54,19 +50,24 @@ function NodeLocation({ type }: NodeHop) {
};
const isCountrySelected = (code: string): boolean => {
- return isEntryNodeSelectionScreen
- ? entryNode?.id === code
- : exitNode?.id === code;
+ return node === 'entry'
+ ? entryNodeLocation?.code === code
+ : exitNodeLocation?.code === code;
};
- const setNodeSelection = (name: string, code: string) => {
- const nodeType = isEntryNodeSelectionScreen
- ? 'set-entry-node'
- : 'set-exit-node';
- dispatch({ type: nodeType, data: { country: name, id: code } });
- };
- const handleCountrySelection = (name: string, code: string) => {
- setNodeSelection(name, code);
+ const handleCountrySelection = async (name: string, code: string) => {
+ try {
+ await invoke('set_node_location', {
+ nodeType: node === 'entry' ? 'Entry' : 'Exit',
+ country: { name, code },
+ });
+ dispatch({
+ type: 'set-node-location',
+ payload: { hop: node, country: { name, code } },
+ });
+ } catch (e) {
+ console.log(e);
+ }
navigate(routes.root);
};
diff --git a/nym-vpn/ui/src/pages/location/SearchBox.tsx b/nym-vpn/ui/src/pages/location/SearchBox.tsx
index 43191c61e0..60b98ad98a 100644
--- a/nym-vpn/ui/src/pages/location/SearchBox.tsx
+++ b/nym-vpn/ui/src/pages/location/SearchBox.tsx
@@ -1,9 +1,11 @@
-import { InputEvent } from '../../types/general';
+import { InputEvent } from '../../types';
+
interface SearchProps {
value: string;
onChange: (e: InputEvent) => void;
placeholder: string;
}
+
export default function SearchBox({
value,
onChange,
diff --git a/nym-vpn/ui/src/router.tsx b/nym-vpn/ui/src/router.tsx
index 42992e681e..a7b8dc5d39 100644
--- a/nym-vpn/ui/src/router.tsx
+++ b/nym-vpn/ui/src/router.tsx
@@ -20,12 +20,12 @@ const router = createBrowserRouter([
{
path: routes.entryNodeLocation,
// eslint-disable-next-line react/jsx-no-undef
- element: ,
+ element: ,
errorElement: ,
},
{
path: routes.exitNodeLocation,
- element: ,
+ element: ,
errorElement: ,
},
],
diff --git a/nym-vpn/ui/src/state/main.ts b/nym-vpn/ui/src/state/main.ts
index 23f59c08a2..5e74b2aebd 100644
--- a/nym-vpn/ui/src/state/main.ts
+++ b/nym-vpn/ui/src/state/main.ts
@@ -1,13 +1,12 @@
import dayjs from 'dayjs';
import {
- AppData,
AppState,
ConnectionState,
- NodeConfig,
+ Country,
+ NodeHop,
UiTheme,
VpnMode,
} from '../types';
-import { QuickConnectCountry } from '../constants';
export type StateAction =
| { type: 'set-partial-state'; partialState: Partial }
@@ -22,10 +21,8 @@ export type StateAction =
| { type: 'set-connection-start-time'; startTime?: number | null }
| { type: 'set-disconnected' }
| { type: 'reset' }
- | { type: 'set-app-data'; data: AppData }
| { type: 'set-ui-theme'; theme: UiTheme }
- | { type: 'set-exit-node'; data: NodeConfig }
- | { type: 'set-entry-node'; data: NodeConfig };
+ | { type: 'set-node-location'; payload: { hop: NodeHop; country: Country } };
export const initialState: AppState = {
state: 'Disconnected',
@@ -34,30 +31,27 @@ export const initialState: AppState = {
tunnel: { name: 'nym', id: 'nym' },
uiTheme: 'Light',
progressMessages: [],
- localAppData: {
- monitoring: false,
- autoconnect: false,
- killswitch: false,
- uiTheme: 'Light',
- vpnMode: 'TwoHop',
- entryNode: {
- country: QuickConnectCountry.name,
- id: QuickConnectCountry.code,
- },
- exitNode: {
- country: QuickConnectCountry.name,
- id: QuickConnectCountry.code,
- },
- },
+ entryNodeLocation: null,
+ exitNodeLocation: null,
};
export function reducer(state: AppState, action: StateAction): AppState {
switch (action.type) {
+ case 'set-node-location':
+ if (action.payload.hop === 'entry') {
+ return {
+ ...state,
+ entryNodeLocation: action.payload.country,
+ };
+ }
+ return {
+ ...state,
+ exitNodeLocation: action.payload.country,
+ };
case 'set-vpn-mode':
return {
...state,
vpnMode: action.mode,
- localAppData: { ...state.localAppData, vpnMode: action.mode },
};
case 'set-partial-state': {
return { ...state, ...action.partialState };
@@ -79,18 +73,6 @@ export function reducer(state: AppState, action: StateAction): AppState {
case 'disconnect': {
return { ...state, state: 'Disconnecting', loading: true };
}
- case 'set-exit-node': {
- return {
- ...state,
- localAppData: { ...state.localAppData, exitNode: action.data },
- };
- }
- case 'set-entry-node': {
- return {
- ...state,
- localAppData: { ...state.localAppData, entryNode: action.data },
- };
- }
case 'set-connected': {
return {
...state,
@@ -115,9 +97,6 @@ export function reducer(state: AppState, action: StateAction): AppState {
sessionStartDate:
(action.startTime && dayjs.unix(action.startTime)) || null,
};
- case 'set-app-data': {
- return { ...state, localAppData: action.data };
- }
case 'set-error':
return { ...state, error: action.error };
case 'reset-error':
@@ -131,7 +110,6 @@ export function reducer(state: AppState, action: StateAction): AppState {
return {
...state,
uiTheme: action.theme,
- localAppData: { ...state.localAppData, uiTheme: action.theme },
};
case 'reset':
return initialState;
diff --git a/nym-vpn/ui/src/state/provider.tsx b/nym-vpn/ui/src/state/provider.tsx
index 9bc0f86c3d..d2a281ff5e 100644
--- a/nym-vpn/ui/src/state/provider.tsx
+++ b/nym-vpn/ui/src/state/provider.tsx
@@ -43,29 +43,13 @@ export function MainStateProvider({ children }: Props) {
getAppData()
.then((data) => {
console.log(data);
- dispatch({
- type: 'set-app-data',
- data: {
- autoconnect: data.autoconnect || false,
- monitoring: data.monitoring || false,
- killswitch: data.killswitch || false,
- uiTheme: data.ui_theme || 'Light',
- vpnMode: data.vpn_mode || 'TwoHop',
- entryNode: data.entry_node || {
- country: QuickConnectCountry.name,
- id: QuickConnectCountry.code,
- },
- exitNode: data.exit_node || {
- country: QuickConnectCountry.name,
- id: QuickConnectCountry.code,
- },
- },
- });
dispatch({
type: 'set-partial-state',
partialState: {
uiTheme: data.ui_theme || 'Light',
vpnMode: data.vpn_mode || 'TwoHop',
+ entryNodeLocation: data.entry_node_location || QuickConnectCountry,
+ exitNodeLocation: data.exit_node_location || QuickConnectCountry,
},
});
})
diff --git a/nym-vpn/ui/src/types/app-data.ts b/nym-vpn/ui/src/types/app-data.ts
index 75a6e016e9..623904fe11 100644
--- a/nym-vpn/ui/src/types/app-data.ts
+++ b/nym-vpn/ui/src/types/app-data.ts
@@ -4,7 +4,7 @@ export type UiTheme = 'Dark' | 'Light';
export interface NodeConfig {
id: string;
- country: string;
+ country: Country;
}
export type Country = {
@@ -12,23 +12,15 @@ export type Country = {
code: string;
};
-export interface AppData {
- monitoring: boolean;
- autoconnect: boolean;
- killswitch: boolean;
- uiTheme: UiTheme;
- vpnMode: VpnMode;
- entryNode?: NodeConfig | null;
- exitNode?: NodeConfig | null;
-}
-
// tauri type, hence the use of snake_case
export interface AppDataFromBackend {
monitoring: boolean | null;
autoconnect: boolean | null;
killswitch: boolean | null;
ui_theme: UiTheme | null;
- vpn_mode: VpnMode;
+ vpn_mode: VpnMode | null;
entry_node: NodeConfig | null;
exit_node: NodeConfig | null;
+ entry_node_location: Country | null;
+ exit_node_location: Country | null;
}
diff --git a/nym-vpn/ui/src/types/app-state.ts b/nym-vpn/ui/src/types/app-state.ts
index c30f0aa5e9..4caea2a137 100644
--- a/nym-vpn/ui/src/types/app-state.ts
+++ b/nym-vpn/ui/src/types/app-state.ts
@@ -1,7 +1,7 @@
import { Dispatch } from 'react';
import { Dayjs } from 'dayjs';
import { StateAction } from '../state';
-import { AppData } from './app-data';
+import { Country } from './app-data';
export type ConnectionState =
| 'Connected'
@@ -26,7 +26,8 @@ export type AppState = {
vpnMode: VpnMode;
tunnel: TunnelConfig;
uiTheme: 'Light' | 'Dark';
- localAppData: AppData;
+ entryNodeLocation: Country | null;
+ exitNodeLocation: Country | null;
};
export type ConnectionEventPayload = {
diff --git a/nym-vpn/ui/src/types/general.ts b/nym-vpn/ui/src/types/general.ts
index cf17b734d7..a7b89bc235 100644
--- a/nym-vpn/ui/src/types/general.ts
+++ b/nym-vpn/ui/src/types/general.ts
@@ -2,6 +2,4 @@ import React from 'react';
export type InputEvent = React.ChangeEvent;
-export type NodeHop = {
- type: 'entry' | 'exit';
-};
+export type NodeHop = 'entry' | 'exit';
diff --git a/nym-vpn/ui/src/types/index.ts b/nym-vpn/ui/src/types/index.ts
index f9d47f3c04..7bd3444e71 100644
--- a/nym-vpn/ui/src/types/index.ts
+++ b/nym-vpn/ui/src/types/index.ts
@@ -2,3 +2,4 @@ export * from './app-state';
export * from './app-data';
export * from './tauri-ipc';
export * from './routes';
+export * from './general';