diff --git a/nym-connect-android/src-tauri/src/state.rs b/nym-connect-android/src-tauri/src/state.rs
index 2012406358..0af63ce1e0 100644
--- a/nym-connect-android/src-tauri/src/state.rs
+++ b/nym-connect-android/src-tauri/src/state.rs
@@ -175,10 +175,16 @@ impl State {
self.set_state(ConnectionStatusKind::Connecting, window);
let res = self.init_config().await;
- match &res {
+ match res {
Ok(_) => {}
Err(e) => {
- dbg!(e);
+ log::error!("Failed to initialize: {e}");
+
+ // Wait a little to give the user some rudimentary feedback that the click actually
+ // registered.
+ tokio::time::sleep(Duration::from_secs(1)).await;
+ self.set_state(ConnectionStatusKind::Disconnected, window);
+ return Err(e);
}
};
let (config, keys) = res.unwrap();
diff --git a/nym-connect-android/src/components/AppWindowFrame.tsx b/nym-connect-android/src/components/AppWindowFrame.tsx
index 4b407c3e7b..88a423d15e 100644
--- a/nym-connect-android/src/components/AppWindowFrame.tsx
+++ b/nym-connect-android/src/components/AppWindowFrame.tsx
@@ -1,10 +1,28 @@
import React from 'react';
import { Box } from '@mui/material';
import { useLocation } from 'react-router-dom';
+import { useClientContext } from 'src/context/main';
import { CustomTitleBar } from './CustomTitleBar';
export const AppWindowFrame: FCWithChildren = ({ children }) => {
const location = useLocation();
+ const { userDefinedGateway, setUserDefinedGateway } = useClientContext();
+
+ // defined functions to be used when moving away from pages
+ const onBack = () => {
+ switch (location.pathname) {
+ case '/menu/settings':
+ return () => {
+ // when the user moves away from the settings page and the gateway is not valid
+ // set isActive to false
+ if (!userDefinedGateway?.gateway) {
+ setUserDefinedGateway((current) => ({ ...current, isActive: false }));
+ }
+ };
+ default:
+ return undefined;
+ }
+ };
return (
{
height: '100vh',
}}
>
-
- {children}
+
+ {children}
);
};
diff --git a/nym-connect-android/src/components/CustomTitleBar.tsx b/nym-connect-android/src/components/CustomTitleBar.tsx
index 15c3def1ad..43540d743b 100644
--- a/nym-connect-android/src/components/CustomTitleBar.tsx
+++ b/nym-connect-android/src/components/CustomTitleBar.tsx
@@ -28,16 +28,20 @@ const MenuIcon = () => {
return navigate('/menu')} />;
};
-const ArrowBackIcon = () => {
+const ArrowBackIcon = ({ onBack }: { onBack?: () => void }) => {
const navigate = useNavigate();
- return navigate(-1)} />;
+ const handleBack = () => {
+ onBack?.();
+ navigate(-1);
+ };
+ return ;
};
const getTitleIcon = (path: string) => {
if (path !== '/') {
const title = path.split('/').slice(-1);
return (
-
+
{title}
);
@@ -45,10 +49,11 @@ const getTitleIcon = (path: string) => {
return ;
};
-export const CustomTitleBar = ({ path = '/' }: { path?: string }) => (
-
+export const CustomTitleBar = ({ path = '/', onBack }: { path?: string; onBack?: () => void }) => (
+
{/* set width to keep logo centered */}
- {path === '/' ? : }
+ {path === '/' ? : }
{getTitleIcon(path)}
+ {path !== '/' && }
);
diff --git a/nym-connect-android/src/context/main.tsx b/nym-connect-android/src/context/main.tsx
index 8c02df4e41..a933a8c36f 100644
--- a/nym-connect-android/src/context/main.tsx
+++ b/nym-connect-android/src/context/main.tsx
@@ -4,10 +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 { ConnectionStatusKind, GatewayPerformance } from '../types';
import { ConnectionStatsItem } from '../components/ConnectionStats';
import { ServiceProvider } from '../types/directory';
+const FORAGE_KEY = 'nym-connect-user-gateway';
+
type ModeType = 'light' | 'dark';
export type TClientContext = {
@@ -20,6 +24,7 @@ export type TClientContext = {
gatewayPerformance: GatewayPerformance;
selectedProvider?: ServiceProvider;
showInfoModal: boolean;
+ userDefinedGateway?: UserDefinedGateway;
setMode: (mode: ModeType) => void;
clearError: () => void;
setConnectionStatus: (connectionStatus: ConnectionStatusKind) => void;
@@ -29,6 +34,7 @@ export type TClientContext = {
setRandomSerivceProvider: () => void;
startConnecting: () => Promise;
startDisconnecting: () => Promise;
+ setUserDefinedGateway: React.Dispatch>;
};
export const ClientContext = createContext({} as TClientContext);
@@ -44,19 +50,43 @@ 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 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;
+ };
+
+ const getUserGatewayFromStorage = async (): Promise => {
+ try {
+ const gatewayFromStorage = await forage.getItem({ key: FORAGE_KEY })();
+ return gatewayFromStorage;
+ } catch (e) {
+ console.warn(e);
+ }
+ return undefined;
+ };
+
const initialiseApp = async () => {
const services = await invoke('get_services');
const AppVersion = await getAppVersion();
- console.log(services);
+ const storedUserDefinedGateway = await getUserGatewayFromStorage();
setAppVersion(AppVersion);
setServiceProviders(services as ServiceProvider[]);
+ if (storedUserDefinedGateway) setUserDefinedGateway(storedUserDefinedGateway);
};
useEvents({
@@ -94,15 +124,19 @@ export const ClientContextProvider: FCWithChildren = ({ children }) => {
}
}, []);
- const setServiceProvider = async (newServiceProvider?: ServiceProvider) => {
- if (newServiceProvider) {
- await invoke('set_gateway', { gateway: newServiceProvider.gateway });
- await invoke('set_service_provider', { serviceProvider: newServiceProvider.address });
- }
+ const shouldUseUserGateway = !!userDefinedGateway.gateway && userDefinedGateway.isActive;
+
+ const setServiceProvider = async (newServiceProvider: ServiceProvider) => {
+ await invoke('set_gateway', {
+ gateway: newServiceProvider.gateway,
+ });
+ 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;
};
@@ -110,8 +144,10 @@ export const ClientContextProvider: FCWithChildren = ({ children }) => {
if (serviceProviders) {
const randomServiceProvider = getRandomSPFromList(serviceProviders);
await setServiceProvider(randomServiceProvider);
+ await setUserGatewayInStorage(userDefinedGateway);
setSelectedProvider(randomServiceProvider);
}
+ return undefined;
};
const clearError = () => setError(undefined);
@@ -136,6 +172,8 @@ export const ClientContextProvider: FCWithChildren = ({ children }) => {
startDisconnecting,
gatewayPerformance,
setShowInfoModal,
+ userDefinedGateway,
+ setUserDefinedGateway,
}),
[
mode,
@@ -148,6 +186,7 @@ export const ClientContextProvider: FCWithChildren = ({ children }) => {
connectedSince,
gatewayPerformance,
selectedProvider,
+ userDefinedGateway,
],
);
diff --git a/nym-connect-android/src/context/mocks/main.tsx b/nym-connect-android/src/context/mocks/main.tsx
index ea86bb365d..6d0f7d9a82 100644
--- a/nym-connect-android/src/context/mocks/main.tsx
+++ b/nym-connect-android/src/context/mocks/main.tsx
@@ -9,6 +9,7 @@ const mockValues: TClientContext = {
selectedProvider: { id: '1', description: 'Keybase service provider', gateway: 'abc123', address: '123abc' },
gatewayPerformance: 'Good',
showInfoModal: false,
+ userDefinedGateway: { isActive: false, gateway: '' },
setShowInfoModal: () => {},
setMode: () => {},
clearError: () => {},
@@ -18,6 +19,7 @@ const mockValues: TClientContext = {
startConnecting: async () => {},
startDisconnecting: async () => {},
setRandomSerivceProvider: () => {},
+ setUserDefinedGateway: () => {},
};
export const MockProvider: FCWithChildren<{
diff --git a/nym-connect-android/src/pages/menu/Apps.tsx b/nym-connect-android/src/pages/menu/Apps.tsx
index ebe31deb44..d75a7a5536 100644
--- a/nym-connect-android/src/pages/menu/Apps.tsx
+++ b/nym-connect-android/src/pages/menu/Apps.tsx
@@ -9,28 +9,25 @@ const appsSchema = {
export const CompatibleApps = () => (
-
+
Supported apps
-
+
Messaging apps
-
-
-
+
{appsSchema.messagingApps.map((app) => (
{app}
))}
-
+
+
Wallets
-
-
{appsSchema.wallets.map((wallet) => (
diff --git a/nym-connect-android/src/pages/menu/Settings.tsx b/nym-connect-android/src/pages/menu/Settings.tsx
new file mode 100644
index 0000000000..f200c2c605
--- /dev/null
+++ b/nym-connect-android/src/pages/menu/Settings.tsx
@@ -0,0 +1,80 @@
+import React, { ChangeEvent, useState } from 'react';
+import { IdentityKeyFormField } from '@nymproject/react/mixnodes/IdentityKeyFormField';
+import { Box, FormControl, FormControlLabel, FormHelperText, Stack, Switch, Typography } from '@mui/material';
+import { useClientContext } from 'src/context/main';
+import { ConnectionStatusKind } from 'src/types';
+import { AppVersion } from 'src/components/AppVersion';
+
+export const Settings = () => {
+ const { userDefinedGateway, setUserDefinedGateway } = useClientContext();
+ const [gatewayKey, setGatewayKey] = useState(userDefinedGateway?.gateway);
+
+ const handleIsValidGatewayKey = (isValid: boolean) => {
+ let gateway: string | undefined;
+
+ if (isValid) {
+ gateway = gatewayKey;
+ }
+
+ setUserDefinedGateway((current) => ({ ...current, gateway }));
+ };
+
+ const handleChange = (e: ChangeEvent) => {
+ console.warn('HANERE***');
+ setUserDefinedGateway((current) => ({ ...current, isActive: e.target.checked }));
+ };
+
+ const { connectionStatus } = useClientContext();
+
+ return (
+
+
+
+
+ Select your Gateway
+
+
+ Use a gateway of your choice
+
+
+
+ }
+ label={userDefinedGateway?.isActive ? 'On' : 'Off'}
+ />
+ {connectionStatus === ConnectionStatusKind.connected && (
+ This setting is disabled during an active connection
+ )}
+ {userDefinedGateway?.isActive && (
+
+ )}
+
+
+
+
+ To find a gateway go to{' '}
+
+ explorer.nymtech.net/network-components/gateways
+
+
+
+
+
+
+ );
+};
diff --git a/nym-connect-android/src/pages/menu/index.tsx b/nym-connect-android/src/pages/menu/index.tsx
index d58a08c25f..47840d6668 100644
--- a/nym-connect-android/src/pages/menu/index.tsx
+++ b/nym-connect-android/src/pages/menu/index.tsx
@@ -1,5 +1,5 @@
import React from 'react';
-import { Apps, HelpOutline } from '@mui/icons-material';
+import { Apps, HelpOutline, Settings } from '@mui/icons-material';
import { Stack, Link, List, ListItem, ListItemButton, ListItemIcon, ListItemText, Typography } from '@mui/material';
import { Link as RouterLink } from 'react-router-dom';
import { AppVersion } from 'src/components/AppVersion';
@@ -7,15 +7,16 @@ import { AppVersion } from 'src/components/AppVersion';
const menuSchema = [
{ title: 'Supported apps', icon: Apps, path: 'apps' },
{ title: 'How to connect guide', icon: HelpOutline, path: 'guide' },
+ { title: 'Settings', icon: Settings, path: 'settings' },
];
export const Menu = () => (
-
+
{menuSchema.map((item) => (
-
+
-
+
{' '}
diff --git a/nym-connect-android/src/routes/index.tsx b/nym-connect-android/src/routes/index.tsx
index ffb0881876..ec24080c6c 100644
--- a/nym-connect-android/src/routes/index.tsx
+++ b/nym-connect-android/src/routes/index.tsx
@@ -4,6 +4,7 @@ import { ConnectionPage } from 'src/pages/connection';
import { Menu } from 'src/pages/menu';
import { CompatibleApps } from 'src/pages/menu/Apps';
import { HelpGuide } from 'src/pages/menu/Guide';
+import { Settings } from 'src/pages/menu/Settings';
export const AppRoutes = () => (
@@ -12,6 +13,7 @@ export const AppRoutes = () => (
} />
} />
} />
+ } />
);
diff --git a/nym-connect-android/src/theme/mui-theme.d.ts b/nym-connect-android/src/theme/mui-theme.d.ts
index 6c4d88fe39..76db4b1a99 100644
--- a/nym-connect-android/src/theme/mui-theme.d.ts
+++ b/nym-connect-android/src/theme/mui-theme.d.ts
@@ -29,6 +29,7 @@ declare module '@mui/material/styles' {
*/
interface NymPalette {
highlight: string;
+ cta: string;
success: string;
warning: string;
info: string;
diff --git a/nym-connect-android/src/theme/theme.tsx b/nym-connect-android/src/theme/theme.tsx
index 58a1becba1..23c8ff8b57 100644
--- a/nym-connect-android/src/theme/theme.tsx
+++ b/nym-connect-android/src/theme/theme.tsx
@@ -21,6 +21,7 @@ import {
const nymPalette: NymPalette = {
/** emphasises important elements */
highlight: '#21D072',
+ cta: '#FB6E4E',
success: '#21D073',
info: '#60D7EF',
warning: '#FFE600',
diff --git a/nym-connect-android/src/types/gateway.ts b/nym-connect-android/src/types/gateway.ts
new file mode 100644
index 0000000000..735a3cdb03
--- /dev/null
+++ b/nym-connect-android/src/types/gateway.ts
@@ -0,0 +1,4 @@
+export interface UserDefinedGateway {
+ isActive: boolean;
+ gateway?: string;
+}