merge resolve
This commit is contained in:
+6
-8
@@ -14,19 +14,17 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
|
||||
# [v1.1.9] (2023-02-07)
|
||||
|
||||
### Added
|
||||
|
||||
- Separate `nym-api` endpoints with values of "total-supply" and "circulating-supply" in `nym` ([#2964])
|
||||
- Add `host` option to client init ([#2912])
|
||||
- Remove Coconut feature flag ([#2793])
|
||||
- Don't drop in mixnet connection handler ([#2963])
|
||||
- Separate `nym-api` endpoints with values of "total-supply" and "circulating-supply" in `nym` ([#2964])
|
||||
|
||||
### Changed
|
||||
- native-client: is now capable of listening for requests on sockets different than `127.0.0.1` ([#2939]). This can be specified via `--host` flag during `init` or `run`. Alternatively a custom `host` can be set in `config.toml` file under `socket` section.
|
||||
- native-client: is now capable of listening for requests on sockets different than `127.0.0.1` ([#2912]). This can be specified via `--host` flag during `init` or `run`. Alternatively a custom `host` can be set in `config.toml` file under `socket` section.
|
||||
- mixnode, gateway: fix unexpected shutdown on corrupted connection ([#2963])
|
||||
|
||||
[#2939]: https://github.com/nymtech/nym/pull/2939
|
||||
[#2963]: https://github.com/nymtech/nym/pull/2963
|
||||
|
||||
[#2793]: https://github.com/nymtech/nym/issues/2793
|
||||
[#2912]: https://github.com/nymtech/nym/issues/2912
|
||||
[#2964]: https://github.com/nymtech/nym/issues/2964
|
||||
[#2963]: https://github.com/nymtech/nym/issues/3017
|
||||
|
||||
# [v1.1.8] (2023-01-31)
|
||||
|
||||
|
||||
Generated
+10
-10
@@ -693,7 +693,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "client-core"
|
||||
version = "1.1.8"
|
||||
version = "1.1.9"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"client-connections",
|
||||
@@ -1885,7 +1885,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "explorer-api"
|
||||
version = "1.1.8"
|
||||
version = "1.1.9"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"clap 4.1.4",
|
||||
@@ -3425,7 +3425,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-api"
|
||||
version = "1.1.9"
|
||||
version = "1.1.10"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@@ -3523,7 +3523,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-cli"
|
||||
version = "1.1.8"
|
||||
version = "1.1.9"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"base64 0.13.1",
|
||||
@@ -3581,7 +3581,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-client"
|
||||
version = "1.1.8"
|
||||
version = "1.1.9"
|
||||
dependencies = [
|
||||
"build-information",
|
||||
"clap 4.1.4",
|
||||
@@ -3621,7 +3621,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-gateway"
|
||||
version = "1.1.8"
|
||||
version = "1.1.9"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@@ -3673,7 +3673,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-mixnode"
|
||||
version = "1.1.9"
|
||||
version = "1.1.10"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"atty",
|
||||
@@ -3718,7 +3718,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-network-requester"
|
||||
version = "1.1.8"
|
||||
version = "1.1.9"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"build-information",
|
||||
@@ -3752,7 +3752,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-network-statistics"
|
||||
version = "1.1.8"
|
||||
version = "1.1.9"
|
||||
dependencies = [
|
||||
"dirs",
|
||||
"log",
|
||||
@@ -3809,7 +3809,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-socks5-client"
|
||||
version = "1.1.8"
|
||||
version = "1.1.9"
|
||||
dependencies = [
|
||||
"build-information",
|
||||
"clap 4.1.4",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "client-core"
|
||||
version = "1.1.8"
|
||||
version = "1.1.9"
|
||||
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>"]
|
||||
edition = "2021"
|
||||
rust-version = "1.66"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "nym-client"
|
||||
version = "1.1.8"
|
||||
version = "1.1.9"
|
||||
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>", "Jędrzej Stuczyński <andrew@nymtech.net>"]
|
||||
description = "Implementation of the Nym Client"
|
||||
edition = "2021"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "nym-socks5-client"
|
||||
version = "1.1.8"
|
||||
version = "1.1.9"
|
||||
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>"]
|
||||
description = "A SOCKS5 localhost proxy that converts incoming messages to Sphinx and sends them to a Nym address"
|
||||
edition = "2021"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "explorer-api"
|
||||
version = "1.1.8"
|
||||
version = "1.1.9"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
- nothing yet
|
||||
|
||||
## [nym-explorer-v1.0.5](https://github.com/nymtech/nym/tree/nym-explorer-v1.0.5) (2023-02-07)
|
||||
## [nym-explorer-v1.0.5](https://github.com/nymtech/nym/tree/nym-explorer-v1.0.5) (2023-02-14)
|
||||
|
||||
- NE - link `Owner` field on the node detail page to the account details on NG explorer ([#2923])
|
||||
- NE - Upgrade Sandbox and make below changes: ([#2332])
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@nym/network-explorer",
|
||||
"version": "1.0.4",
|
||||
"version": "1.0.5",
|
||||
"private": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
|
||||
+1
-1
@@ -3,7 +3,7 @@
|
||||
|
||||
[package]
|
||||
name = "nym-gateway"
|
||||
version = "1.1.8"
|
||||
version = "1.1.9"
|
||||
authors = [
|
||||
"Dave Hrycyszyn <futurechimp@users.noreply.github.com>",
|
||||
"Jędrzej Stuczyński <andrew@nymtech.net>",
|
||||
|
||||
+1
-1
@@ -3,7 +3,7 @@
|
||||
|
||||
[package]
|
||||
name = "nym-mixnode"
|
||||
version = "1.1.9"
|
||||
version = "1.1.10"
|
||||
authors = [
|
||||
"Dave Hrycyszyn <futurechimp@users.noreply.github.com>",
|
||||
"Jędrzej Stuczyński <andrew@nymtech.net>",
|
||||
|
||||
+1
-1
@@ -3,7 +3,7 @@
|
||||
|
||||
[package]
|
||||
name = "nym-api"
|
||||
version = "1.1.9"
|
||||
version = "1.1.10"
|
||||
authors = [
|
||||
"Dave Hrycyszyn <futurechimp@users.noreply.github.com>",
|
||||
"Jędrzej Stuczyński <andrew@nymtech.net>",
|
||||
|
||||
@@ -1,12 +1,16 @@
|
||||
# Changelog
|
||||
|
||||
## [nym-connect-v1.1.9](https://github.com/nymtech/nym/tree/nym-connect-v1.1.9) (2023-02-07)
|
||||
## UNRELEASED
|
||||
|
||||
- NC - Button animations ([#2949])
|
||||
- NC - add effect when the button is clicked ([#2947])
|
||||
- NC - UI to select gateways based on some performance criteria by checking gateways' routing score from nym-api ([#2942])
|
||||
- NC - client health check when connecting ([#2859])
|
||||
## [nym-connect-v1.1.9](https://github.com/nymtech/nym/tree/nym-connect-v1.1.9) (2023-02-14)
|
||||
|
||||
- Button animations ([#2949])
|
||||
- add effect when the button is clicked ([#2947])
|
||||
- UI to select gateways based on some performance criteria by checking gateways' routing score from nym-api ([#2942])
|
||||
- client health check when connecting ([#2859])
|
||||
- allow user to select own gateway ([#2952])
|
||||
|
||||
[#2952]: https://github.com/nymtech/nym/issues/2952
|
||||
[#2949]: https://github.com/nymtech/nym/issues/2949
|
||||
[#2947]: https://github.com/nymtech/nym/issues/2947
|
||||
[#2942]: https://github.com/nymtech/nym/issues/2942
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@nym/nym-connect",
|
||||
"version": "1.1.8",
|
||||
"version": "1.1.9",
|
||||
"main": "index.js",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "nym-connect"
|
||||
version = "1.1.8"
|
||||
version = "1.1.9"
|
||||
description = "nym-connect"
|
||||
authors = ["Nym Technologies SA"]
|
||||
license = ""
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"package": {
|
||||
"productName": "nym-connect",
|
||||
"version": "1.1.8"
|
||||
"version": "1.1.9"
|
||||
},
|
||||
"build": {
|
||||
"distDir": "../dist",
|
||||
|
||||
@@ -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 (
|
||||
<Box
|
||||
@@ -14,7 +32,7 @@ export const AppWindowFrame: FCWithChildren = ({ children }) => {
|
||||
height: '100vh',
|
||||
}}
|
||||
>
|
||||
<CustomTitleBar path={location.pathname} />
|
||||
<CustomTitleBar path={location.pathname} onBack={onBack()} />
|
||||
<Box style={{ padding: '16px' }}>{children}</Box>
|
||||
</Box>
|
||||
);
|
||||
|
||||
@@ -29,9 +29,13 @@ const MenuIcon = () => {
|
||||
return <CustomButton Icon={Menu} onClick={() => navigate('/menu')} />;
|
||||
};
|
||||
|
||||
const ArrowBackIcon = () => {
|
||||
const ArrowBackIcon = ({ onBack }: { onBack?: () => void }) => {
|
||||
const navigate = useNavigate();
|
||||
return <CustomButton Icon={ArrowBack} onClick={() => navigate(-1)} />;
|
||||
const handleBack = () => {
|
||||
onBack?.();
|
||||
navigate(-1);
|
||||
};
|
||||
return <CustomButton Icon={ArrowBack} onClick={handleBack} />;
|
||||
};
|
||||
|
||||
const getTitleIcon = (path: string) => {
|
||||
@@ -46,10 +50,10 @@ const getTitleIcon = (path: string) => {
|
||||
return <NymWordmark width={36} />;
|
||||
};
|
||||
|
||||
export const CustomTitleBar = ({ path = '/' }: { path?: string }) => (
|
||||
export const CustomTitleBar = ({ path = '/', onBack }: { path?: string; onBack?: () => void }) => (
|
||||
<Box data-tauri-drag-region style={customTitleBarStyles.titlebar}>
|
||||
{/* set width to keep logo centered */}
|
||||
<Box sx={{ width: '40px' }}>{path === '/' ? <MenuIcon /> : <ArrowBackIcon />}</Box>
|
||||
<Box sx={{ width: '40px' }}>{path === '/' ? <MenuIcon /> : <ArrowBackIcon onBack={onBack} />}</Box>
|
||||
{getTitleIcon(path)}
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
|
||||
<CustomButton Icon={Minimize} onClick={() => appWindow.minimize()} />
|
||||
|
||||
@@ -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<void>;
|
||||
startDisconnecting: () => Promise<void>;
|
||||
setUserDefinedGateway: React.Dispatch<React.SetStateAction<UserDefinedGateway>>;
|
||||
};
|
||||
|
||||
export const ClientContext = createContext({} as TClientContext);
|
||||
@@ -44,19 +50,43 @@ export const ClientContextProvider: FCWithChildren = ({ children }) => {
|
||||
const [appVersion, setAppVersion] = useState<string>();
|
||||
const [gatewayPerformance, setGatewayPerformance] = useState<GatewayPerformance>('Good');
|
||||
const [showInfoModal, setShowInfoModal] = useState(false);
|
||||
const [userDefinedGateway, setUserDefinedGateway] = useState<UserDefinedGateway>({ 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<UserDefinedGateway | undefined> => {
|
||||
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,
|
||||
],
|
||||
);
|
||||
|
||||
|
||||
@@ -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<{
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
import React, { ChangeEvent, useState } from 'react';
|
||||
import { IdentityKeyFormField } from '@nymproject/react/mixnodes/IdentityKeyFormField';
|
||||
import { Box, FormControl, FormControlLabel, FormHelperText, Link, 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<string | undefined>(userDefinedGateway?.gateway);
|
||||
|
||||
const handleIsValidGatewayKey = (isValid: boolean) => {
|
||||
let gateway: string | undefined;
|
||||
|
||||
if (isValid) {
|
||||
gateway = gatewayKey;
|
||||
}
|
||||
|
||||
setUserDefinedGateway((current) => ({ ...current, gateway }));
|
||||
};
|
||||
|
||||
const handleChange = (e: ChangeEvent<HTMLInputElement>) => {
|
||||
setUserDefinedGateway((current) => ({ ...current, isActive: e.target.checked }));
|
||||
};
|
||||
|
||||
const { connectionStatus } = useClientContext();
|
||||
|
||||
return (
|
||||
<Box height="100%">
|
||||
<Stack justifyContent="space-between" height="100%">
|
||||
<Box>
|
||||
<Typography fontWeight="bold" variant="body2" mb={1}>
|
||||
Select your Gateway
|
||||
</Typography>
|
||||
<Typography color="grey.300" variant="body2" mb={2}>
|
||||
Use a gateway of your choice
|
||||
</Typography>
|
||||
<FormControl fullWidth>
|
||||
<FormControlLabel
|
||||
control={
|
||||
<Switch
|
||||
checked={userDefinedGateway?.isActive}
|
||||
onChange={handleChange}
|
||||
disabled={connectionStatus === ConnectionStatusKind.connected}
|
||||
size="small"
|
||||
sx={{ ml: 1 }}
|
||||
/>
|
||||
}
|
||||
label={userDefinedGateway?.isActive ? 'On' : 'Off'}
|
||||
/>
|
||||
{connectionStatus === ConnectionStatusKind.connected && (
|
||||
<FormHelperText sx={{ m: 0, my: 1 }}>This setting is disabled during an active connection</FormHelperText>
|
||||
)}
|
||||
{userDefinedGateway?.isActive && (
|
||||
<IdentityKeyFormField
|
||||
size="small"
|
||||
placeholder="Gateway identity key"
|
||||
onChanged={setGatewayKey}
|
||||
initialValue={gatewayKey}
|
||||
onValidate={handleIsValidGatewayKey}
|
||||
sx={{ mt: 1 }}
|
||||
disabled={connectionStatus === 'connected' || !userDefinedGateway?.isActive}
|
||||
/>
|
||||
)}
|
||||
</FormControl>
|
||||
</Box>
|
||||
<Box>
|
||||
<Typography variant="body2" mb={3}>
|
||||
To find a gateway go to the{' '}
|
||||
<Link
|
||||
underline="none"
|
||||
target="_blank"
|
||||
href="https://explorer.nymtech.net/network-components/gateways"
|
||||
sx={{ cursor: 'pointer' }}
|
||||
color="nym.cta"
|
||||
>
|
||||
Network Explorer
|
||||
</Link>
|
||||
</Typography>
|
||||
<AppVersion />
|
||||
</Box>
|
||||
</Stack>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
@@ -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 } from '@mui/material';
|
||||
import { Link as RouterLink } from 'react-router-dom';
|
||||
import { AppVersion } from 'src/components/AppVersion';
|
||||
@@ -7,13 +7,14 @@ 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 = () => (
|
||||
<Stack justifyContent="space-between" height="100%">
|
||||
<List dense disablePadding>
|
||||
{menuSchema.map((item) => (
|
||||
<Link component={RouterLink} to={item.path} underline="none" color="white">
|
||||
<Link component={RouterLink} to={item.path} underline="none" color="white" key={item.title}>
|
||||
<ListItem disablePadding>
|
||||
<ListItemButton>
|
||||
<ListItemIcon sx={{ minWidth: 25 }}>
|
||||
|
||||
@@ -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 = () => (
|
||||
<Routes>
|
||||
@@ -12,6 +13,7 @@ export const AppRoutes = () => (
|
||||
<Route index element={<Menu />} />
|
||||
<Route path="apps" element={<CompatibleApps />} />
|
||||
<Route path="guide" element={<HelpGuide />} />
|
||||
<Route path="settings" element={<Settings />} />
|
||||
</Route>
|
||||
</Routes>
|
||||
);
|
||||
|
||||
Vendored
+1
@@ -29,6 +29,7 @@ declare module '@mui/material/styles' {
|
||||
*/
|
||||
interface NymPalette {
|
||||
highlight: string;
|
||||
cta: string;
|
||||
success: string;
|
||||
warning: string;
|
||||
info: string;
|
||||
|
||||
@@ -21,6 +21,7 @@ import {
|
||||
const nymPalette: NymPalette = {
|
||||
/** emphasises important elements */
|
||||
highlight: '#21D072',
|
||||
cta: '#FB6E4E',
|
||||
success: '#21D073',
|
||||
info: '#60D7EF',
|
||||
warning: '#FFE600',
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
export interface UserDefinedGateway {
|
||||
isActive: boolean;
|
||||
gateway?: string;
|
||||
}
|
||||
@@ -2,6 +2,17 @@
|
||||
|
||||
## UNRELEASED
|
||||
|
||||
## [nym-wallet-v1.1.9](https://github.com/nymtech/nym/releases/tag/nym-wallet-v1.1.9) (2023-02-14)
|
||||
|
||||
- Allow more flexibility for user when setting passwords ([#2993])
|
||||
- User feedback on weak passwords ([#2993])
|
||||
- User no longer has to copy mnemonic to continune account creation ([#2948])
|
||||
- Updated instructional steps for creating accounts with a password ([#2962])
|
||||
|
||||
[#2948]: https://github.com/nymtech/nym/issues/2948
|
||||
[#2993]: https://github.com/nymtech/nym/issues/2993
|
||||
[#2962]: https://github.com/nymtech/nym/issues/2962
|
||||
|
||||
## [nym-wallet-v1.1.8](https://github.com/nymtech/nym/releases/tag/nym-wallet-v1.1.8) (2023-01-24)
|
||||
|
||||
- Fix delegations sorting ([#2885])
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@nymproject/nym-wallet-app",
|
||||
"version": "1.1.8",
|
||||
"version": "1.1.9",
|
||||
"main": "index.js",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
@@ -52,7 +52,8 @@
|
||||
"string-to-color": "^2.2.2",
|
||||
"use-clipboard-copy": "^0.2.0",
|
||||
"uuid": "^8.3.2",
|
||||
"yup": "^0.32.9"
|
||||
"yup": "^0.32.9",
|
||||
"zxcvbn": "^4.4.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.15.0",
|
||||
@@ -76,6 +77,7 @@
|
||||
"@types/react-dom": "^18.0.10",
|
||||
"@types/semver": "^7.3.8",
|
||||
"@types/uuid": "^8.3.4",
|
||||
"@types/zxcvbn": "^4.4.1",
|
||||
"@typescript-eslint/eslint-plugin": "^5.13.0",
|
||||
"@typescript-eslint/parser": "^5.13.0",
|
||||
"babel-loader": "^8.3.0",
|
||||
@@ -120,4 +122,4 @@
|
||||
"webpack-favicons": "^1.3.8",
|
||||
"webpack-merge": "^5.8.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "nym_wallet"
|
||||
version = "1.1.8"
|
||||
version = "1.1.9"
|
||||
description = "Nym Native Wallet"
|
||||
authors = ["Nym Technologies SA"]
|
||||
license = ""
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"package": {
|
||||
"productName": "nym-wallet",
|
||||
"version": "1.1.8"
|
||||
"version": "1.1.9"
|
||||
},
|
||||
"build": {
|
||||
"distDir": "../dist",
|
||||
@@ -14,7 +14,13 @@
|
||||
"active": true,
|
||||
"targets": "all",
|
||||
"identifier": "net.nymtech.wallet",
|
||||
"icon": ["icons/32x32.png", "icons/128x128.png", "icons/128x128@2x.png", "icons/icon.icns", "icons/icon.ico"],
|
||||
"icon": [
|
||||
"icons/32x32.png",
|
||||
"icons/128x128.png",
|
||||
"icons/128x128@2x.png",
|
||||
"icons/icon.icns",
|
||||
"icons/icon.ico"
|
||||
],
|
||||
"resources": [],
|
||||
"externalBin": [],
|
||||
"copyright": "Copyright © 2021-2022 Nym Technologies SA",
|
||||
@@ -27,7 +33,6 @@
|
||||
"macOS": {
|
||||
"frameworks": [],
|
||||
"minimumSystemVersion": "",
|
||||
|
||||
"exceptionDomain": "",
|
||||
"signingIdentity": "Developer ID Application: Nym Technologies SA (VW5DZLFHM5)",
|
||||
"entitlements": null
|
||||
@@ -40,7 +45,9 @@
|
||||
},
|
||||
"updater": {
|
||||
"active": true,
|
||||
"endpoints": ["https://nymtech.net/.wellknown/wallet/updater.json"],
|
||||
"endpoints": [
|
||||
"https://nymtech.net/.wellknown/wallet/updater.json"
|
||||
],
|
||||
"dialog": true,
|
||||
"pubkey": "dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IENCNzQ2M0E5N0VFODE2NApSV1JrZ2U2WE9rYTNETTg1OTBKdE5uWUEra0hML2syOVUvQ2lxZmFZRzZ1T3NWbGM0eVRzUTVhVwo="
|
||||
},
|
||||
@@ -67,4 +74,4 @@
|
||||
"csp": "default-src blob: data: filesystem: ws: wss: http: https: tauri: 'unsafe-eval' 'unsafe-inline' 'self' img-src: 'self'"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,11 +5,12 @@ import { SimpleModal } from '../Modals/SimpleModal';
|
||||
import { Warning } from '../Warning';
|
||||
|
||||
const passwordCreationSteps = [
|
||||
'Log out of your wallet',
|
||||
'Log out from the wallet',
|
||||
'Sign in using “Sign in with mnemonic” button',
|
||||
'On the next screen select “Create a password for your account”',
|
||||
'Sign in to the wallet with your new password',
|
||||
'Then come back here to import or create new accounts',
|
||||
'On the next screen select “Create a password"',
|
||||
'Type in the mnemonic you want to create a password for and follow the next steps',
|
||||
'Sign back in the wallet using your new password',
|
||||
'Come back to this page to import or create new accounts',
|
||||
];
|
||||
|
||||
// TODO add the link href value
|
||||
|
||||
@@ -10,7 +10,6 @@ import {
|
||||
TextField,
|
||||
Typography,
|
||||
} from '@mui/material';
|
||||
import { useClipboard } from 'use-clipboard-copy';
|
||||
import { createMnemonic, validateMnemonic } from 'src/requests';
|
||||
import { Console } from 'src/utils/console';
|
||||
import { AccountsContext } from 'src/context';
|
||||
@@ -30,16 +29,16 @@ const importAccountSteps = [
|
||||
];
|
||||
|
||||
const MnemonicStep = ({ mnemonic, onNext, onBack }: { mnemonic: string; onNext: () => void; onBack: () => void }) => {
|
||||
const { copy, copied } = useClipboard({ copiedTimeout: 5000 });
|
||||
const [confirmed, setConfirmed] = useState(false);
|
||||
return (
|
||||
<Box sx={{ mt: 1 }}>
|
||||
<DialogContent>
|
||||
<Mnemonic mnemonic={mnemonic} handleCopy={copy} copied={copied} />
|
||||
<Mnemonic mnemonic={mnemonic} handleConfirmed={setConfirmed} confirmed={confirmed} />
|
||||
</DialogContent>
|
||||
<DialogActions sx={{ p: 3, pt: 0, gap: 2 }}>
|
||||
<StyledBackButton onBack={onBack} />
|
||||
<Button disabled={!copied} fullWidth disableElevation variant="contained" size="large" onClick={onNext}>
|
||||
I saved my mnemonic
|
||||
<Button disabled={!confirmed} fullWidth disableElevation variant="contained" size="large" onClick={onNext}>
|
||||
Continue
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</Box>
|
||||
|
||||
@@ -11,15 +11,12 @@ import {
|
||||
Typography,
|
||||
} from '@mui/material';
|
||||
import { AccountsContext } from 'src/context';
|
||||
import { useClipboard } from 'use-clipboard-copy';
|
||||
import { Mnemonic, PasswordInput } from 'src/components';
|
||||
import { StyledBackButton } from 'src/components/StyledBackButton';
|
||||
|
||||
export const MnemonicModal = () => {
|
||||
const [password, setPassword] = useState('');
|
||||
|
||||
const { copy, copied } = useClipboard({ copiedTimeout: 5000 });
|
||||
|
||||
const {
|
||||
dialogToDisplay,
|
||||
setDialogToDisplay,
|
||||
@@ -72,7 +69,7 @@ export const MnemonicModal = () => {
|
||||
/>
|
||||
</>
|
||||
) : (
|
||||
<Mnemonic mnemonic={accountMnemonic.value} handleCopy={copy} copied={copied} />
|
||||
<Mnemonic mnemonic={accountMnemonic.value} />
|
||||
)}
|
||||
</Box>
|
||||
</DialogContent>
|
||||
|
||||
@@ -1,23 +1,32 @@
|
||||
import React from 'react';
|
||||
import { Button, Stack, TextField, Typography } from '@mui/material';
|
||||
import { Check, ContentCopySharp } from '@mui/icons-material';
|
||||
import { Box, Checkbox, FormControlLabel, Stack, TextField, Typography } from '@mui/material';
|
||||
import { Title } from 'src/pages/auth/components/heading';
|
||||
import { Warning } from './Warning';
|
||||
|
||||
export const Mnemonic = ({
|
||||
mnemonic,
|
||||
copied,
|
||||
handleCopy,
|
||||
confirmed,
|
||||
withTitle,
|
||||
handleConfirmed,
|
||||
}: {
|
||||
mnemonic: string;
|
||||
copied: boolean;
|
||||
handleCopy: (text?: string) => void;
|
||||
confirmed?: boolean;
|
||||
withTitle?: boolean;
|
||||
handleConfirmed?: (confirmed: boolean) => void;
|
||||
}) => (
|
||||
<Stack spacing={2} alignItems="center">
|
||||
<Warning>
|
||||
<Typography sx={{ textAlign: 'center' }}>
|
||||
Below is your 24 word mnemonic, make sure to store it in a safe place for accessing your wallet in the future
|
||||
</Typography>
|
||||
</Warning>
|
||||
<Stack spacing={2}>
|
||||
{withTitle && (
|
||||
<Box sx={{ pb: 2, textAlign: 'center' }}>
|
||||
<Title title="Copy and save or write down your mnemonic" />
|
||||
</Box>
|
||||
)}
|
||||
<Box sx={{ pb: 2 }}>
|
||||
<Warning>
|
||||
<Typography sx={{ textAlign: 'center' }}>
|
||||
Below is your 24 word mnemonic, make sure to store it in a safe place for accessing your wallet in the future
|
||||
</Typography>
|
||||
</Warning>
|
||||
</Box>
|
||||
<TextField
|
||||
label="Mnemonic"
|
||||
type="input"
|
||||
@@ -38,19 +47,11 @@ export const Mnemonic = ({
|
||||
}}
|
||||
/>
|
||||
|
||||
<Button
|
||||
color="inherit"
|
||||
disableElevation
|
||||
size="large"
|
||||
onClick={() => {
|
||||
handleCopy(mnemonic);
|
||||
}}
|
||||
sx={{
|
||||
width: 250,
|
||||
}}
|
||||
endIcon={!copied ? <ContentCopySharp /> : <Check color="success" />}
|
||||
>
|
||||
Copy mnemonic
|
||||
</Button>
|
||||
{handleConfirmed && (
|
||||
<FormControlLabel
|
||||
label="I saved my mnemonic"
|
||||
control={<Checkbox checked={confirmed} onChange={(_, checked) => handleConfirmed(checked)} />}
|
||||
/>
|
||||
)}
|
||||
</Stack>
|
||||
);
|
||||
|
||||
@@ -35,7 +35,9 @@ export const inputValidationSchema = Yup.object().shape({
|
||||
.test('Is valid operator cost value', (value, ctx) => {
|
||||
const stringValueToNumber = Math.round(Number(value));
|
||||
|
||||
if (isGreaterThan(stringValueToNumber, -1) && isLessThan(stringValueToNumber, 101)) return true;
|
||||
return ctx.createError({ message: 'Operator cost must be a valid number' });
|
||||
if (isLessThan(stringValueToNumber, 0))
|
||||
return ctx.createError({ message: 'Operator cost must be a valid number' });
|
||||
|
||||
return true;
|
||||
}),
|
||||
});
|
||||
|
||||
@@ -1,52 +1,61 @@
|
||||
/* eslint-disable no-nested-ternary */
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import React from 'react';
|
||||
import zxcvbn, { ZXCVBNScore } from 'zxcvbn';
|
||||
import { LockOutlined } from '@mui/icons-material';
|
||||
import { LinearProgress, Stack, Typography, Box } from '@mui/material';
|
||||
|
||||
type TStrength = 'weak' | 'medium' | 'strong' | 'init';
|
||||
|
||||
const strong = /^(?=.*[a-z])(?=.*[0-9])(?=.*[!@#$%^&*])(?=.{8,})/;
|
||||
const medium = /^(((?=.*[a-z]))|((?=.*[a-z])(?=.*[0-9]))|((?=.*[0-9])))(?=.{6,})/;
|
||||
|
||||
const colorMap = {
|
||||
init: 'inherit' as 'inherit',
|
||||
weak: 'error' as 'error',
|
||||
medium: 'warning' as 'warning',
|
||||
strong: 'success' as 'success',
|
||||
4: 'success' as 'success',
|
||||
3: 'success' as 'success',
|
||||
2: 'warning' as 'warning',
|
||||
1: 'error' as 'error',
|
||||
0: 'error' as 'error',
|
||||
};
|
||||
|
||||
const getText = (strength: TStrength) => {
|
||||
switch (strength) {
|
||||
case 'strong':
|
||||
const getText = (score: ZXCVBNScore) => {
|
||||
switch (score) {
|
||||
case 4:
|
||||
return 'Very strong password';
|
||||
case 3:
|
||||
return 'Strong password';
|
||||
case 'medium':
|
||||
return 'Medium strength password';
|
||||
case 'weak':
|
||||
case 2:
|
||||
return 'Average password';
|
||||
case 1:
|
||||
return 'Weak password';
|
||||
case 0:
|
||||
return 'Very weak password';
|
||||
default:
|
||||
return 'Password strength';
|
||||
return '';
|
||||
}
|
||||
};
|
||||
|
||||
const getTextColor = (strength: TStrength) => {
|
||||
switch (strength) {
|
||||
case 'strong':
|
||||
const getColor = (score: ZXCVBNScore) => {
|
||||
switch (score) {
|
||||
case 4:
|
||||
return 'success.main';
|
||||
case 'medium':
|
||||
case 3:
|
||||
return 'success.main';
|
||||
case 2:
|
||||
return 'warning.main';
|
||||
case 'weak':
|
||||
case 1:
|
||||
return 'error.main';
|
||||
case 0:
|
||||
return 'error.main';
|
||||
default:
|
||||
return 'grey.500';
|
||||
}
|
||||
};
|
||||
|
||||
const getPasswordStrength = (strength: TStrength) => {
|
||||
switch (strength) {
|
||||
case 'strong':
|
||||
const getPasswordStrength = (score: ZXCVBNScore) => {
|
||||
switch (score) {
|
||||
case 4:
|
||||
return 100;
|
||||
case 'medium':
|
||||
case 3:
|
||||
return 75;
|
||||
case 2:
|
||||
return 50;
|
||||
case 1:
|
||||
return 25;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
@@ -54,47 +63,32 @@ const getPasswordStrength = (strength: TStrength) => {
|
||||
|
||||
export const PasswordStrength = ({
|
||||
password,
|
||||
onChange,
|
||||
withWarnings,
|
||||
handleIsSafePassword,
|
||||
}: {
|
||||
password: string;
|
||||
onChange: (isStrong: boolean) => void;
|
||||
withWarnings?: boolean;
|
||||
handleIsSafePassword: (isSafe: boolean) => void;
|
||||
}) => {
|
||||
const [strength, setStrength] = useState<TStrength>('init');
|
||||
const result = zxcvbn(password);
|
||||
|
||||
useEffect(() => {
|
||||
if (password.length === 0) {
|
||||
setStrength('init');
|
||||
return;
|
||||
}
|
||||
handleIsSafePassword(result.score > 1);
|
||||
|
||||
if (password.match(strong)) {
|
||||
setStrength('strong');
|
||||
return;
|
||||
}
|
||||
|
||||
if (password.match(medium)) {
|
||||
setStrength('medium');
|
||||
return;
|
||||
}
|
||||
setStrength('weak');
|
||||
}, [password]);
|
||||
|
||||
useEffect(() => {
|
||||
if (strength === 'strong') {
|
||||
onChange(true);
|
||||
} else {
|
||||
onChange(false);
|
||||
}
|
||||
}, [strength]);
|
||||
if (!password.length) return null;
|
||||
|
||||
return (
|
||||
<Stack spacing={0.5}>
|
||||
<LinearProgress variant="determinate" color={colorMap[strength]} value={getPasswordStrength(strength)} />
|
||||
<Box display="flex" alignItems="center">
|
||||
<LockOutlined sx={{ fontSize: 15, color: getTextColor(strength) }} />
|
||||
<Typography variant="caption" sx={{ ml: 0.5, color: getTextColor(strength) }}>
|
||||
{getText(strength)}
|
||||
</Typography>
|
||||
<LinearProgress variant="determinate" color={colorMap[result.score]} value={getPasswordStrength(result.score)} />
|
||||
<Box display="flex" alignItems="center" justifyContent="space-between">
|
||||
<Box display="flex" alignItems="center">
|
||||
<LockOutlined sx={{ fontSize: 15, color: getColor(result.score) }} />
|
||||
<Typography variant="caption" sx={{ ml: 0.5, color: getColor(result.score) }}>
|
||||
{getText(result.score)}
|
||||
</Typography>
|
||||
</Box>
|
||||
{withWarnings && result.feedback.warning && (
|
||||
<Typography variant="caption">{result.feedback.warning}</Typography>
|
||||
)}
|
||||
</Box>
|
||||
</Stack>
|
||||
);
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
import * as React from 'react';
|
||||
import { ComponentMeta, ComponentStory } from '@storybook/react';
|
||||
import { Stack, TextField } from '@mui/material';
|
||||
import { PasswordStrength } from './password-strength';
|
||||
|
||||
export default {
|
||||
title: 'Wallet / Password Strength',
|
||||
component: PasswordStrength,
|
||||
} as ComponentMeta<typeof PasswordStrength>;
|
||||
|
||||
const Template: ComponentStory<typeof PasswordStrength> = ({ password, withWarnings, handleIsSafePassword }) => {
|
||||
const [value, setValue] = React.useState(password);
|
||||
return (
|
||||
<Stack alignContent="center">
|
||||
<TextField value={value} onChange={(e) => setValue(e.target.value)} sx={{ mb: 0.5 }} />
|
||||
{!!password.length && (
|
||||
<PasswordStrength handleIsSafePassword={handleIsSafePassword} withWarnings={withWarnings} password={password} />
|
||||
)}
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
|
||||
export const VeryStrong = Template.bind({});
|
||||
VeryStrong.args = { password: 'fedgklnrf34£', withWarnings: true, handleIsSafePassword: () => undefined };
|
||||
|
||||
export const Strong = Template.bind({});
|
||||
Strong.args = { password: '"56%abc123?@', withWarnings: true, handleIsSafePassword: () => undefined };
|
||||
|
||||
export const Average = Template.bind({});
|
||||
Average.args = { password: '"abc123?', withWarnings: true, handleIsSafePassword: () => undefined };
|
||||
|
||||
export const Weak = Template.bind({});
|
||||
Weak.args = { password: 'abc123?', withWarnings: true, handleIsSafePassword: () => undefined };
|
||||
|
||||
export const VeryWeak = Template.bind({});
|
||||
VeryWeak.args = {
|
||||
password: 'abc123',
|
||||
withWarnings: true,
|
||||
handleIsSafePassword: () => undefined,
|
||||
};
|
||||
|
||||
export const WithName = Template.bind({});
|
||||
WithName.args = {
|
||||
password: 'fred',
|
||||
withWarnings: true,
|
||||
handleIsSafePassword: () => undefined,
|
||||
};
|
||||
|
||||
export const WithSequence = Template.bind({});
|
||||
WithSequence.args = {
|
||||
password: '121212',
|
||||
withWarnings: true,
|
||||
handleIsSafePassword: () => undefined,
|
||||
};
|
||||
|
||||
export const Default = Template.bind({});
|
||||
Default.args = {
|
||||
password: 'abc123',
|
||||
withWarnings: true,
|
||||
handleIsSafePassword: () => undefined,
|
||||
};
|
||||
@@ -1,9 +0,0 @@
|
||||
import React from 'react';
|
||||
import { AuthProvider } from 'src/context';
|
||||
import { AuthRoutes } from 'src/routes/auth';
|
||||
|
||||
export const Auth = () => (
|
||||
<AuthProvider>
|
||||
<AuthRoutes />
|
||||
</AuthProvider>
|
||||
);
|
||||
@@ -9,9 +9,8 @@ import { Subtitle, Title, PasswordStrength } from '../components';
|
||||
|
||||
export const ConnectPassword = () => {
|
||||
const [confirmedPassword, setConfirmedPassword] = useState<string>('');
|
||||
const [isStrongPassword, setIsStrongPassword] = useState(false);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
|
||||
const [isSafePassword, setIsSafePassword] = useState(false);
|
||||
const { mnemonic, password, setPassword, resetState } = useContext(AuthContext);
|
||||
const navigate = useNavigate();
|
||||
|
||||
@@ -49,7 +48,7 @@ export const ConnectPassword = () => {
|
||||
label="Password"
|
||||
autoFocus
|
||||
/>
|
||||
<PasswordStrength password={password} onChange={(isStrong) => setIsStrongPassword(isStrong)} />
|
||||
<PasswordStrength password={password} handleIsSafePassword={setIsSafePassword} withWarnings />
|
||||
</>
|
||||
<PasswordInput
|
||||
password={confirmedPassword}
|
||||
@@ -59,7 +58,7 @@ export const ConnectPassword = () => {
|
||||
<Button
|
||||
size="large"
|
||||
variant="contained"
|
||||
disabled={password !== confirmedPassword || password.length === 0 || !isStrongPassword || isLoading}
|
||||
disabled={password !== confirmedPassword || password.length === 0 || isLoading || !isSafePassword}
|
||||
onClick={storePassword}
|
||||
>
|
||||
{isLoading ? <CircularProgress size={25} /> : 'Create password'}
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import React, { useContext, useEffect } from 'react';
|
||||
import React, { useContext, useEffect, useState } from 'react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { Container, Button, Stack } from '@mui/material';
|
||||
import { AuthContext } from 'src/context/auth';
|
||||
import { useClipboard } from 'use-clipboard-copy';
|
||||
import { Mnemonic } from '../../../components';
|
||||
|
||||
export const CreateMnemonic = () => {
|
||||
const { mnemonic, mnemonicWords, generateMnemonic, resetState } = useContext(AuthContext);
|
||||
const navigate = useNavigate();
|
||||
const [confirmed, setConfirmed] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (mnemonicWords.length === 0) {
|
||||
@@ -15,12 +15,10 @@ export const CreateMnemonic = () => {
|
||||
}
|
||||
}, []);
|
||||
|
||||
const { copy, copied } = useClipboard({ copiedTimeout: 5000 });
|
||||
return (
|
||||
<Container maxWidth="xs">
|
||||
<Container maxWidth="sm">
|
||||
<Stack alignItems="center" spacing={3} maxWidth="xs">
|
||||
<Mnemonic mnemonic={mnemonic} handleCopy={copy} copied={copied} />
|
||||
|
||||
<Mnemonic mnemonic={mnemonic} handleConfirmed={setConfirmed} confirmed={confirmed} withTitle />
|
||||
<Button
|
||||
variant="contained"
|
||||
color="primary"
|
||||
@@ -28,9 +26,9 @@ export const CreateMnemonic = () => {
|
||||
size="large"
|
||||
onClick={() => navigate('/verify-mnemonic')}
|
||||
sx={{ width: '100%', fontSize: 15 }}
|
||||
disabled={!copied}
|
||||
disabled={!confirmed}
|
||||
>
|
||||
I saved my mnemonic
|
||||
Continue
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => {
|
||||
|
||||
@@ -10,8 +10,8 @@ import { Subtitle, Title, PasswordStrength } from '../components';
|
||||
export const CreatePassword = () => {
|
||||
const { password, setPassword, resetState, mnemonic } = useContext(AuthContext);
|
||||
const [confirmedPassword, setConfirmedPassword] = useState<string>('');
|
||||
const [isStrongPassword, setIsStrongPassword] = useState(false);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [isSafePassword, setIsSafePassword] = useState(false);
|
||||
|
||||
const navigate = useNavigate();
|
||||
|
||||
@@ -48,7 +48,7 @@ export const CreatePassword = () => {
|
||||
label="Password"
|
||||
autoFocus
|
||||
/>
|
||||
<PasswordStrength password={password} onChange={(isStrong) => setIsStrongPassword(isStrong)} />
|
||||
<PasswordStrength password={password} handleIsSafePassword={setIsSafePassword} withWarnings />
|
||||
</>
|
||||
<PasswordInput
|
||||
password={confirmedPassword}
|
||||
@@ -58,7 +58,7 @@ export const CreatePassword = () => {
|
||||
<Button
|
||||
size="large"
|
||||
variant="contained"
|
||||
disabled={password !== confirmedPassword || password.length === 0 || !isStrongPassword || isLoading}
|
||||
disabled={password !== confirmedPassword || password.length === 0 || isLoading || !isSafePassword}
|
||||
onClick={storePassword}
|
||||
>
|
||||
Next
|
||||
|
||||
@@ -17,6 +17,7 @@ import { AppContext, urls } from 'src/context/main';
|
||||
import { isGateway, isMixnode, TBondGatewayArgs, TBondMixNodeArgs, TBondMoreArgs } from 'src/types';
|
||||
import { BondedGateway } from 'src/components/Bonding/BondedGateway';
|
||||
import { RedeemRewardsModal } from 'src/components/Bonding/modals/RedeemRewardsModal';
|
||||
import { Console } from 'src/utils/console';
|
||||
import { BondingContextProvider, useBondingContext } from '../../context';
|
||||
import { getMixnodeStakeSaturation } from '../../requests';
|
||||
|
||||
@@ -104,7 +105,7 @@ const Bonding = () => {
|
||||
}
|
||||
return { isOverSaturated: false, saturationPercentage: undefined };
|
||||
} catch (e) {
|
||||
console.error('Error fetching the saturation, error:', e);
|
||||
Console.error('Error fetching the saturation, error:', e);
|
||||
return { isOverSaturated: false, saturationPercentage: undefined };
|
||||
}
|
||||
};
|
||||
|
||||
@@ -7,6 +7,7 @@ import { CalculateArgs, Inputs } from 'src/components/RewardsPlayground/Inputs';
|
||||
import { TBondedMixnode } from 'src/context';
|
||||
import { useSnackbar } from 'notistack';
|
||||
import { LoadingModal } from 'src/components/Modals/LoadingModal';
|
||||
import { Console } from 'src/utils/console';
|
||||
import { computeEstimate, computeStakeSaturation, handleCalculatePeriodRewards } from './utils';
|
||||
|
||||
export type DefaultInputValues = {
|
||||
@@ -99,7 +100,7 @@ export const ApyPlayground = ({ bondedNode }: { bondedNode: TBondedMixnode }) =>
|
||||
setStakeSaturation(computedStakeSaturation);
|
||||
setResults(estimationResult);
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
Console.log(e);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
+1
-1
@@ -56,7 +56,7 @@ export const ParametersSettings = ({ bondedNode }: { bondedNode: TBondedMixnode
|
||||
const { nextInterval } = await getIntervalAsDate();
|
||||
setIntervalTime(nextInterval);
|
||||
} catch {
|
||||
console.log('cant retrieve next interval');
|
||||
Console.log('cant retrieve next interval');
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
export * from './auth';
|
||||
export * from './Admin';
|
||||
export * from './balance';
|
||||
export * from './bonding';
|
||||
|
||||
@@ -59,9 +59,5 @@ export const computeMixnodeRewardEstimation = async (args: {
|
||||
totalDelegation: number;
|
||||
profitMarginPercent: string;
|
||||
intervalOperatingCost: { denom: 'unym'; amount: string };
|
||||
}) => {
|
||||
console.log(args);
|
||||
|
||||
return invokeWrapper<RewardEstimationResponse>('compute_mixnode_reward_estimation', args);
|
||||
};
|
||||
}) => invokeWrapper<RewardEstimationResponse>('compute_mixnode_reward_estimation', args);
|
||||
export const getMixnodeUptime = async (mixId: number) => invokeWrapper<number>('get_mixnode_uptime', { mixId });
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
[package]
|
||||
name = "nym-network-requester"
|
||||
version = "1.1.8"
|
||||
version = "1.1.9"
|
||||
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>", "Jędrzej Stuczyński <andrew@nymtech.net>"]
|
||||
edition = "2021"
|
||||
rust-version = "1.65"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "nym-network-statistics"
|
||||
version = "1.1.8"
|
||||
version = "1.1.9"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "nym-cli"
|
||||
version = "1.1.8"
|
||||
version = "1.1.9"
|
||||
authors.workspace = true
|
||||
edition = "2021"
|
||||
|
||||
|
||||
@@ -19,7 +19,9 @@ export const IdentityKeyFormField: FCWithChildren<{
|
||||
onValidate?: (isValid: boolean, error?: string) => void;
|
||||
textFieldProps?: TextFieldProps;
|
||||
errorText?: string;
|
||||
size?: 'small' | 'medium';
|
||||
sx?: SxProps;
|
||||
disabled?: boolean;
|
||||
}> = ({
|
||||
required,
|
||||
fullWidth,
|
||||
@@ -33,6 +35,8 @@ export const IdentityKeyFormField: FCWithChildren<{
|
||||
onValidate,
|
||||
textFieldProps,
|
||||
showTickOnValid = true,
|
||||
size,
|
||||
disabled,
|
||||
}) => {
|
||||
const [value, setValue] = React.useState<string | undefined>(initialValue);
|
||||
const [validationError, setValidationError] = React.useState<string | undefined>();
|
||||
@@ -100,6 +104,8 @@ export const IdentityKeyFormField: FCWithChildren<{
|
||||
defaultValue={initialValue}
|
||||
onChange={handleChange}
|
||||
InputLabelProps={{ shrink: true }}
|
||||
size={size}
|
||||
disabled={disabled}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -5338,6 +5338,11 @@
|
||||
dependencies:
|
||||
"@types/yargs-parser" "*"
|
||||
|
||||
"@types/zxcvbn@^4.4.1":
|
||||
version "4.4.1"
|
||||
resolved "https://registry.yarnpkg.com/@types/zxcvbn/-/zxcvbn-4.4.1.tgz#46e42cbdcee681b22181478feaf4af2bc4c1abd2"
|
||||
integrity sha512-3NoqvZC2W5gAC5DZbTpCeJ251vGQmgcWIHQJGq2J240HY6ErQ9aWKkwfoKJlHLx+A83WPNTZ9+3cd2ILxbvr1w==
|
||||
|
||||
"@typescript-eslint/eslint-plugin@^5.13.0", "@typescript-eslint/eslint-plugin@^5.7.0":
|
||||
version "5.49.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.49.0.tgz#d0b4556f0792194bf0c2fb297897efa321492389"
|
||||
@@ -19683,3 +19688,8 @@ zwitch@^2.0.0:
|
||||
version "2.0.4"
|
||||
resolved "https://registry.yarnpkg.com/zwitch/-/zwitch-2.0.4.tgz#c827d4b0acb76fc3e685a4c6ec2902d51070e9d7"
|
||||
integrity sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==
|
||||
|
||||
zxcvbn@^4.4.2:
|
||||
version "4.4.2"
|
||||
resolved "https://registry.yarnpkg.com/zxcvbn/-/zxcvbn-4.4.2.tgz#28ec17cf09743edcab056ddd8b1b06262cc73c30"
|
||||
integrity sha512-Bq0B+ixT/DMyG8kgX2xWcI5jUvCwqrMxSFam7m0lAf78nf04hv6lNCsyLYdyYTrCVMqNDY/206K7eExYCeSyUQ==
|
||||
|
||||
Reference in New Issue
Block a user