Compare commits

..

22 Commits

Author SHA1 Message Date
Dave Hrycyszyn 1b5cc1bb54 Changelog tweaks 2023-02-08 08:43:20 +01:00
joeiacono2021 e980441e5f Merge branch 'release/v1.1.9' of https://github.com/nymtech/nym into release/v1.1.9 2023-02-08 08:43:20 +01:00
joeiacono2021 f1214e29f9 changelog changes for release 1.1.9 2023-02-08 08:43:20 +01:00
Jon Häggblad 604742bddb changelog: add note about fix for unexpected shutdown 2023-02-08 08:43:20 +01:00
Fouad e935b2c60f Feature/nym connect health status frontend (#2969)
* filter services on rust side by gateway performance

* format rust code

* create events hook

* use events hook

* remove unused component

remove unused component

update prop names in svg

* display errors in connected screen when needed

update failed health check message
2023-02-08 08:43:20 +01:00
Fouad 4cb04e9192 link owner field to ng explorer (#2970)
* link owner field to ng explorer
2023-02-08 08:43:20 +01:00
Fouad 4fa5c7f2d3 filter services on rust side by gateway performance (#2966)
* filter services on rust side by gateway performance

* update changelog for NC
2023-02-08 08:43:20 +01:00
Mark Sinclair 3321331cb0 GitHub Actions: fix up build-and-upload-binaries-ci.yml 2023-02-08 08:43:20 +01:00
Jędrzej Stuczyński 1e031391fa introduced '/circulating-supply/total-supply-value' and '/circulating-supply/circulating-supply-value' endpoints (#2965) 2023-02-08 08:43:20 +01:00
Mark Sinclair e9f5f0223f Update build-and-upload-binaries-ci.yml 2023-02-08 08:43:20 +01:00
Mark Sinclair 8e8adc45f3 Update build-and-upload-binaries-ci.yml 2023-02-08 08:43:20 +01:00
Mark Sinclair 158ad15d37 Update build-and-upload-binaries-ci.yml 2023-02-08 08:43:20 +01:00
Mark Sinclair 9954334b30 GitHub Actions: add action to build and upload binaries to CI server 2023-02-08 08:43:20 +01:00
Jon Häggblad 8ddd9e37e0 Don't drop in mixnet connection handlers (#2963) 2023-02-08 08:43:20 +01:00
Fouad d7deb9819b NymConnect - Add button animations (#2950)
* add button animations

* pulse and disable button on connecting/disconnecting status

* update button component story

* disabled hover on connecting/disconnecting

* add transition delay

fix up overflow
2023-02-08 08:43:20 +01:00
Jędrzej Stuczyński 3ec0fcb0a4 Added an option to set custom 'host' for the native client (#2939)
* Added an option to set custom 'host' for the native client

* Changelog entry
2023-02-08 08:43:20 +01:00
Pierre Dommerc ba6584e4b6 build(nc-android): prepare for apk release (#2943)
* chore(nc-android): prepare for production build

* refactor(nc-android): remove dead code

* feat(nc-android): update native color theme

* feat(nc-android): update native color theme

* build(nc-android): fix rfd version issue

* build(nc-android): fix dist dir no such file error

* fix(nc-android): post rebase changes
2023-02-08 08:43:20 +01:00
Bogdan-Ștefan Neacşu 44bec774c3 Fix typo during merge back to develop (#2956) 2023-02-08 08:43:20 +01:00
cgi-bin/ a1f15cda16 typo: electrum (#2954) 2023-02-08 08:43:20 +01:00
farbanas 2a42090eb7 fix: formatting 2023-02-08 08:43:20 +01:00
farbanas d31d4630b7 fix: wrong parameter type for addresses in generator commands (should be AccountId instead of String) 2023-02-08 08:43:20 +01:00
Fran Arbanas a1890e49cf Update CHANGELOG.md 2023-02-08 08:43:20 +01:00
72 changed files with 367 additions and 568 deletions
+8 -6
View File
@@ -7,17 +7,19 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
# [v1.1.9] (2023-02-07)
### Added
- Remove Coconut feature flag ([#2793])
- 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])
### Changed
- 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.
- 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.
- mixnode, gateway: fix unexpected shutdown on corrupted connection ([#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
[#2939]: https://github.com/nymtech/nym/pull/2939
[#2963]: https://github.com/nymtech/nym/pull/2963
# [v1.1.8] (2023-01-31)
Generated
+1 -1
View File
@@ -3438,7 +3438,7 @@ dependencies = [
[[package]]
name = "nym-api"
version = "1.1.9"
version = "1.1.8"
dependencies = [
"anyhow",
"async-trait",
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "client-core"
version = "1.1.9"
version = "1.1.8"
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>"]
edition = "2021"
rust-version = "1.66"
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "nym-client"
version = "1.1.9"
version = "1.1.8"
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>", "Jędrzej Stuczyński <andrew@nymtech.net>"]
description = "Implementation of the Nym Client"
edition = "2021"
+2 -2
View File
@@ -31,7 +31,7 @@ pub(crate) struct Init {
force_register_gateway: bool,
/// Comma separated list of rest endpoints of the nyxd validators
#[clap(long, alias = "nymd_validators", value_delimiter = ',', hide = true)]
#[clap(long, alias = "nymd_validators", value_delimiter = ',')]
nyxd_urls: Option<Vec<url::Url>>,
/// Comma separated list of rest endpoints of the API validators
@@ -62,7 +62,7 @@ pub(crate) struct Init {
/// Set this client to work in a enabled credentials mode that would attempt to use gateway
/// with bandwidth credential requirement.
#[clap(long, hide = true)]
#[clap(long)]
enabled_credentials_mode: Option<bool>,
/// Save a summary of the initialization to a json file
+2 -2
View File
@@ -23,7 +23,7 @@ pub(crate) struct Run {
id: String,
/// Comma separated list of rest endpoints of the nyxd validators
#[clap(long, alias = "nymd_validators", value_delimiter = ',', hide = true)]
#[clap(long, alias = "nymd_validators", value_delimiter = ',')]
nyxd_urls: Option<Vec<url::Url>>,
/// Comma separated list of rest endpoints of the API validators
@@ -59,7 +59,7 @@ pub(crate) struct Run {
/// Set this client to work in a enabled credentials mode that would attempt to use gateway
/// with bandwidth credential requirement.
#[clap(long, hide = true)]
#[clap(long)]
enabled_credentials_mode: Option<bool>,
}
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "nym-socks5-client"
version = "1.1.9"
version = "1.1.8"
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"
+2 -2
View File
@@ -43,7 +43,7 @@ pub(crate) struct Init {
force_register_gateway: bool,
/// Comma separated list of rest endpoints of the nyxd validators
#[clap(long, alias = "nymd_validators", value_delimiter = ',', hide = true)]
#[clap(long, alias = "nymd_validators", value_delimiter = ',')]
nyxd_urls: Option<Vec<url::Url>>,
/// Comma separated list of rest endpoints of the API validators
@@ -66,7 +66,7 @@ pub(crate) struct Init {
/// Set this client to work in a enabled credentials mode that would attempt to use gateway
/// with bandwidth credential requirement.
#[clap(long, hide = true)]
#[clap(long)]
enabled_credentials_mode: Option<bool>,
/// Save a summary of the initialization to a json file
+2 -2
View File
@@ -43,7 +43,7 @@ pub(crate) struct Run {
gateway: Option<identity::PublicKey>,
/// Comma separated list of rest endpoints of the nyxd validators
#[clap(long, alias = "nymd_validators", value_delimiter = ',', hide = true)]
#[clap(long, alias = "nymd_validators", value_delimiter = ',')]
nyxd_urls: Option<Vec<url::Url>>,
/// Comma separated list of rest endpoints of the Nym APIs
@@ -65,7 +65,7 @@ pub(crate) struct Run {
/// Set this client to work in a enabled credentials mode that would attempt to use gateway
/// with bandwidth credential requirement.
#[clap(long, hide = true)]
#[clap(long)]
enabled_credentials_mode: Option<bool>,
}
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "explorer-api"
version = "1.1.9"
version = "1.1.8"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+1 -1
View File
@@ -2,7 +2,7 @@
- nothing yet
## [nym-explorer-v1.0.5](https://github.com/nymtech/nym/tree/nym-explorer-v1.0.5) (2023-02-14)
## [nym-explorer-v1.0.5](https://github.com/nymtech/nym/tree/nym-explorer-v1.0.5) (2023-02-07)
- 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 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@nym/network-explorer",
"version": "1.0.5",
"version": "1.0.4",
"private": true,
"license": "Apache-2.0",
"dependencies": {
+1 -1
View File
@@ -3,7 +3,7 @@
[package]
name = "nym-gateway"
version = "1.1.9"
version = "1.1.8"
authors = [
"Dave Hrycyszyn <futurechimp@users.noreply.github.com>",
"Jędrzej Stuczyński <andrew@nymtech.net>",
+2 -3
View File
@@ -55,8 +55,7 @@ pub struct Init {
long,
alias = "validators",
alias = "nymd_validators",
value_delimiter = ',',
hide = true
value_delimiter = ','
)]
// the alias here is included for backwards compatibility (1.1.4 and before)
nyxd_urls: Option<Vec<url::Url>>,
@@ -67,7 +66,7 @@ pub struct Init {
/// Set this gateway to work only with coconut credentials; that would disallow clients to
/// bypass bandwidth credential requirement
#[clap(long, hide = true)]
#[clap(long)]
only_coconut_credentials: Option<bool>,
/// Enable/disable gateway anonymized statistics that get sent to a statistics aggregator server
+2 -3
View File
@@ -53,8 +53,7 @@ pub struct Run {
long,
alias = "validators",
alias = "nymd_validators",
value_delimiter = ',',
hide = true
value_delimiter = ','
)]
// the alias here is included for backwards compatibility (1.1.4 and before)
nyxd_urls: Option<Vec<url::Url>>,
@@ -65,7 +64,7 @@ pub struct Run {
/// Set this gateway to work only with coconut credentials; that would disallow clients to
/// bypass bandwidth credential requirement
#[clap(long, hide = true)]
#[clap(long)]
only_coconut_credentials: Option<bool>,
/// Enable/disable gateway anonymized statistics that get sent to a statistics aggregator server
+2 -3
View File
@@ -4,8 +4,7 @@
"nym-wallet",
"nym-connect",
"nym-connect-android",
"sdk/typescript/examples/docs",
"sdk/typescript/packages/**"
"sdk/typescript/**"
],
"version": "0.0.0"
}
}
+1 -1
View File
@@ -3,7 +3,7 @@
[package]
name = "nym-mixnode"
version = "1.1.10"
version = "1.1.9"
authors = [
"Dave Hrycyszyn <futurechimp@users.noreply.github.com>",
"Jędrzej Stuczyński <andrew@nymtech.net>",
+1 -1
View File
@@ -3,7 +3,7 @@
[package]
name = "nym-api"
version = "1.1.10"
version = "1.1.8"
authors = [
"Dave Hrycyszyn <futurechimp@users.noreply.github.com>",
"Jędrzej Stuczyński <andrew@nymtech.net>",
+2 -7
View File
@@ -93,16 +93,11 @@ pub(crate) struct CliArgs {
pub(crate) enabled_credentials_mode: Option<bool>,
/// Announced address where coconut clients will connect.
#[clap(long, hide = true)]
#[clap(long)]
pub(crate) announce_address: Option<url::Url>,
/// Flag to indicate whether coconut signer authority is enabled on this API
#[clap(
long,
requires = "mnemonic",
requires = "announce_address",
hide = true
)]
#[clap(long, requires = "mnemonic", requires = "announce_address")]
pub(crate) enable_coconut: Option<bool>,
}
+5 -7
View File
@@ -2,15 +2,13 @@
## UNRELEASED
## [nym-connect-v1.1.9](https://github.com/nymtech/nym/tree/nym-connect-v1.1.9) (2023-02-14)
## [nym-connect-v1.1.9](https://github.com/nymtech/nym/tree/nym-connect-v1.1.9) (2023-02-07)
- 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])
- 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])
[#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 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@nym/nym-connect",
"version": "1.1.9",
"version": "1.1.8",
"main": "index.js",
"license": "MIT",
"scripts": {
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "nym-connect"
version = "1.1.9"
version = "1.1.8"
description = "nym-connect"
authors = ["Nym Technologies SA"]
license = ""
+1 -1
View File
@@ -1,7 +1,7 @@
{
"package": {
"productName": "nym-connect",
"version": "1.1.9"
"version": "1.1.8"
},
"build": {
"distDir": "../dist",
+2 -20
View File
@@ -1,28 +1,10 @@
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';
import { useLocation, useParams } from 'react-router-dom';
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
@@ -32,7 +14,7 @@ export const AppWindowFrame: FCWithChildren = ({ children }) => {
height: '100vh',
}}
>
<CustomTitleBar path={location.pathname} onBack={onBack()} />
<CustomTitleBar path={location.pathname} />
<Box style={{ padding: '16px' }}>{children}</Box>
</Box>
);
@@ -1,10 +1,10 @@
import React from 'react';
import { Box, CircularProgress, Tooltip, Typography } from '@mui/material';
import { DateTime } from 'luxon';
import { ErrorOutline, InfoOutlined } from '@mui/icons-material';
import { ConnectionStatusKind, GatewayPerformance } from '../types';
import { ServiceProvider } from '../types/directory';
import { GatwayWarningInfo, ServiceProviderInfo } from './TooltipInfo';
import { ErrorOutline, InfoOutlined } from '@mui/icons-material';
const FONT_SIZE = '14px';
const FONT_WEIGHT = '600';
@@ -85,12 +85,14 @@ export const ConnectionStatus: FCWithChildren<{
const color = status === 'connected' || status === 'disconnecting' ? '#21D072' : 'white';
return (
<Box color={color} sx={{ mb: 2 }}>
<ConnectionStatusContent
status={status}
serviceProvider={serviceProvider}
gatewayError={gatewayPerformance !== 'Good'}
/>
</Box>
<>
<Box color={color} sx={{ mb: 2 }}>
<ConnectionStatusContent
status={status}
serviceProvider={serviceProvider}
gatewayError={gatewayPerformance !== 'Good'}
/>
</Box>
</>
);
};
+14 -16
View File
@@ -29,13 +29,9 @@ const MenuIcon = () => {
return <CustomButton Icon={Menu} onClick={() => navigate('/menu')} />;
};
const ArrowBackIcon = ({ onBack }: { onBack?: () => void }) => {
const ArrowBackIcon = () => {
const navigate = useNavigate();
const handleBack = () => {
onBack?.();
navigate(-1);
};
return <CustomButton Icon={ArrowBack} onClick={handleBack} />;
return <CustomButton Icon={ArrowBack} onClick={() => navigate(-1)} />;
};
const getTitleIcon = (path: string) => {
@@ -50,14 +46,16 @@ const getTitleIcon = (path: string) => {
return <NymWordmark width={36} />;
};
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 onBack={onBack} />}</Box>
{getTitleIcon(path)}
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
<CustomButton Icon={Minimize} onClick={() => appWindow.minimize()} />
<CustomButton Icon={Close} onClick={() => appWindow.close()} />
export const CustomTitleBar = ({ path = '/' }: { path?: string }) => {
return (
<Box data-tauri-drag-region style={customTitleBarStyles.titlebar}>
{/* set width to keep logo centered */}
<Box sx={{ width: '40px' }}>{path === '/' ? <MenuIcon /> : <ArrowBackIcon />}</Box>
{getTitleIcon(path)}
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
<CustomButton Icon={Minimize} onClick={() => appWindow.minimize()} />
<CustomButton Icon={Close} onClick={() => appWindow.close()} />
</Box>
</Box>
</Box>
);
);
};
@@ -6,6 +6,7 @@ import Content from './content/en.yaml';
import { useClientContext } from '../../context/main';
import { useTestAndEarnContext } from './context/TestAndEarnContext';
import { NymShipyardTheme } from '../../theme';
import { ConnectionStatusKind } from '../../types';
export const Wrapper: FCWithChildren<{ disabled: boolean }> = ({ disabled, children }) => {
if (disabled) {
@@ -4,6 +4,7 @@ import { forage } from '@tauri-apps/tauri-forage';
import { invoke } from '@tauri-apps/api';
import { ClientId, DrawEntry, Draws, Registration } from './types';
import { useClientContext } from '../../../context/main';
import { ConnectionStatusKind } from '../../../types';
export type TTestAndEarnContext = {
loadedOnce: boolean;
@@ -1,4 +1,5 @@
import React from 'react';
import { Box } from '@mui/material';
export const HelpImage = ({ img, imageDescription }: { img: string; imageDescription: string }) => (
<img src={img} alt={imageDescription} width="100%" />
@@ -1,5 +1,5 @@
import React from 'react';
import { Box, Typography } from '@mui/material';
import { Box, Button, Typography } from '@mui/material';
import { InfoModal } from './InfoModal';
import { CopyToClipboard } from './CopyToClipboard';
@@ -66,7 +66,6 @@ export const PowerButton: FCWithChildren<{
return 'contract';
}
}
return 'contract';
}, [status, hover]);
const buttonPulse = () => {
+36 -48
View File
@@ -1,16 +1,15 @@
import React, { createContext, useCallback, useContext, useEffect, useMemo, useState } from 'react';
import React, { createContext, useCallback, useContext, useEffect, useMemo, useState, useRef } from 'react';
import { DateTime } from 'luxon';
import { invoke } from '@tauri-apps/api';
import { forage } from '@tauri-apps/tauri-forage';
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';
import { ServiceProvider, Services } from '../types/directory';
import { useEvents } from 'src/hooks/events';
const FORAGE_KEY = 'nym-connect-user-gateway';
const TAURI_EVENT_STATUS_CHANGED = 'app:connection-status-changed';
type ModeType = 'light' | 'dark';
@@ -24,7 +23,6 @@ export type TClientContext = {
gatewayPerformance: GatewayPerformance;
selectedProvider?: ServiceProvider;
showInfoModal: boolean;
userDefinedGateway?: UserDefinedGateway;
setMode: (mode: ModeType) => void;
clearError: () => void;
setConnectionStatus: (connectionStatus: ConnectionStatusKind) => void;
@@ -34,7 +32,6 @@ export type TClientContext = {
setRandomSerivceProvider: () => void;
startConnecting: () => Promise<void>;
startDisconnecting: () => Promise<void>;
setUserDefinedGateway: React.Dispatch<React.SetStateAction<UserDefinedGateway>>;
};
export const ClientContext = createContext({} as TClientContext);
@@ -50,47 +47,25 @@ 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 timerId = useRef<NodeJS.Timeout>();
const initialiseApp = async () => {
const services = await invoke('get_services');
const AppVersion = await getAppVersion();
const storedUserDefinedGateway = await getUserGatewayFromStorage();
console.log(services);
setAppVersion(AppVersion);
setServiceProviders(services as ServiceProvider[]);
if (storedUserDefinedGateway) setUserDefinedGateway(storedUserDefinedGateway);
};
useEvents({
onError: (e) => setError(e),
onError: (error) => setError(error),
onGatewayPerformanceChange: (performance) => setGatewayPerformance(performance),
onStatusChange: (status) => setConnectionStatus(status),
});
@@ -124,19 +99,37 @@ export const ClientContextProvider: FCWithChildren = ({ children }) => {
}
}, []);
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 setServiceProvider = async (newServiceProvider?: ServiceProvider) => {
if (newServiceProvider) {
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)];
const setSpInStorage = async (sp: ServiceProvider) => {
await forage.setItem({
key: 'nym-connect-sp',
value: sp,
} as any)();
};
if (shouldUseUserGateway) return { ...randomSelection, gateway: userDefinedGateway.gateway } as ServiceProvider;
const removeSpFromStorage = async () => {
await forage.removeItem({
key: 'nym-connect-sp',
})();
};
const getSpFromStorage = async (): Promise<ServiceProvider | undefined> => {
try {
const spFromStorage = await forage.getItem({ key: 'nym-connect-sp' })();
return spFromStorage;
} catch (e) {
console.warn(e);
}
};
const getRandomSPFromList = (serviceProviders: ServiceProvider[]) => {
const randomSelection = serviceProviders[Math.floor(Math.random() * serviceProviders.length)];
return randomSelection;
};
@@ -144,10 +137,8 @@ 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);
@@ -172,8 +163,6 @@ export const ClientContextProvider: FCWithChildren = ({ children }) => {
startDisconnecting,
gatewayPerformance,
setShowInfoModal,
userDefinedGateway,
setUserDefinedGateway,
}),
[
mode,
@@ -186,7 +175,6 @@ export const ClientContextProvider: FCWithChildren = ({ children }) => {
connectedSince,
gatewayPerformance,
selectedProvider,
userDefinedGateway,
],
);
-2
View File
@@ -9,7 +9,6 @@ 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: () => {},
@@ -19,7 +18,6 @@ const mockValues: TClientContext = {
startConnecting: async () => {},
startDisconnecting: async () => {},
setRandomSerivceProvider: () => {},
setUserDefinedGateway: () => {},
};
export const MockProvider: FCWithChildren<{
+2 -2
View File
@@ -1,14 +1,14 @@
import React from 'react';
import { createRoot } from 'react-dom/client';
import { ErrorBoundary } from 'react-error-boundary';
import { BrowserRouter as Router } from 'react-router-dom';
import { GlobalStyles } from '@mui/material';
import { ClientContextProvider } from './context/main';
import { ErrorFallback } from './components/Error';
import { NymMixnetTheme } from './theme';
import { AppWindowFrame } from './components/AppWindowFrame';
import { TestAndEarnContextProvider } from './components/Growth/context/TestAndEarnContext';
import { BrowserRouter as Router } from 'react-router-dom';
import { AppRoutes } from './routes';
import { GlobalStyles } from '@mui/material';
const elem = document.getElementById('root');
+37 -35
View File
@@ -1,4 +1,4 @@
import React from 'react';
import React, { useState } from 'react';
import { Box, Stack } from '@mui/material';
import { DateTime } from 'luxon';
import { IpAddressAndPortModal } from 'src/components/IpAddressAndPortModal';
@@ -43,38 +43,40 @@ export const Connected: FCWithChildren<{
clearError,
onConnectClick,
closeInfoModal,
}) => (
<>
{error && <InfoModal show title={error.title} description={error.message} onClose={clearError} />}
<IpAddressAndPortModal show={showInfoModal} onClose={closeInfoModal} ipAddress={ipAddress} port={port} />
<ConnectionLayout
TopContent={
<Box>
<ConnectionStatus
status={ConnectionStatusKind.connected}
gatewayPerformance={gatewayPerformance}
serviceProvider={serviceProvider}
/>
<ConnectionTimer connectedSince={connectedSince} />
</Box>
}
ConnectButton={
<PowerButton
status={status}
busy={busy}
onClick={onConnectClick}
isError={isError}
disabled={status === 'disconnecting'}
/>
}
BottomContent={
<Stack justifyContent="space-between">
<Box sx={{ mb: 2 }}>
<IpAddressAndPort label="Socks5 address" ipAddress={ipAddress} port={port} />
}) => {
return (
<>
{error && <InfoModal show title={error.title} description={error.message} onClose={clearError} />}
<IpAddressAndPortModal show={showInfoModal} onClose={closeInfoModal} ipAddress={ipAddress} port={port} />
<ConnectionLayout
TopContent={
<Box>
<ConnectionStatus
status={ConnectionStatusKind.connected}
gatewayPerformance={gatewayPerformance}
serviceProvider={serviceProvider}
/>
<ConnectionTimer connectedSince={connectedSince} />
</Box>
<ExperimentalWarning />
</Stack>
}
/>
</>
);
}
ConnectButton={
<PowerButton
status={status}
busy={busy}
onClick={onConnectClick}
isError={isError}
disabled={status === 'disconnecting'}
/>
}
BottomContent={
<Stack justifyContent="space-between">
<Box sx={{ mb: 2 }}>
<IpAddressAndPort label="Socks5 address" ipAddress={ipAddress} port={port} />
</Box>
<ExperimentalWarning />
</Stack>
}
/>
</>
);
};
@@ -20,32 +20,34 @@ export const Disconnected: FCWithChildren<{
serviceProvider?: ServiceProvider;
clearError: () => void;
onConnectClick: (status: ConnectionStatusKind) => void;
}> = ({ status, error, onConnectClick, clearError }) => (
<>
{error && <InfoModal show title={error.title} description={error.message} onClose={clearError} />}
<ConnectionLayout
TopContent={
<Box>
<ConnectionStatus status={ConnectionStatusKind.disconnected} gatewayPerformance="Good" />
<ConnectionTimer />
</Box>
}
ConnectButton={<PowerButton onClick={onConnectClick} status={status} disabled={status === 'connecting'} />}
BottomContent={
<Stack justifyContent="space-between" pt={1}>
<Typography
fontWeight={600}
textTransform="uppercase"
textAlign="center"
fontSize="12px"
sx={{ wordSpacing: 1.5, letterSpacing: 1.5 }}
color="warning.main"
>
You are not protected
</Typography>
<ExperimentalWarning />
</Stack>
}
/>
</>
);
}> = ({ status, error, onConnectClick, clearError }) => {
return (
<>
{error && <InfoModal show title={error.title} description={error.message} onClose={clearError} />}
<ConnectionLayout
TopContent={
<Box>
<ConnectionStatus status={ConnectionStatusKind.disconnected} gatewayPerformance="Good" />
<ConnectionTimer />
</Box>
}
ConnectButton={<PowerButton onClick={onConnectClick} status={status} disabled={status === 'connecting'} />}
BottomContent={
<Stack justifyContent="space-between" pt={1}>
<Typography
fontWeight={600}
textTransform="uppercase"
textAlign="center"
fontSize="12px"
sx={{ wordSpacing: 1.5, letterSpacing: 1.5 }}
color="warning.main"
>
You are not protected
</Typography>
<ExperimentalWarning />
</Stack>
}
/>
</>
);
};
+4 -4
View File
@@ -19,8 +19,8 @@ export const CompatibleApps = () => (
<Divider sx={{ mb: 2 }} />
<Box sx={{ mb: 4 }}>
{appsSchema.messagingApps.map((app) => (
<Typography variant="body2" color="grey.400" sx={{ mb: 2 }} key={app}>
{appsSchema.messagingApps.map((app, i) => (
<Typography variant="body2" color="grey.400" sx={{ mb: 2 }} key={i}>
{app}
</Typography>
))}
@@ -32,8 +32,8 @@ export const CompatibleApps = () => (
<Divider sx={{ mb: 2 }} />
<Box sx={{ mb: 4 }}>
{appsSchema.wallets.map((wallet) => (
<Typography variant="body2" color="grey.400" sx={{ mb: 2 }} key={wallet}>
{appsSchema.wallets.map((wallet, i) => (
<Typography variant="body2" color="grey.400" sx={{ mb: 2 }} key={i}>
{wallet}
</Typography>
))}
-85
View File
@@ -1,85 +0,0 @@
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>
);
};
+20 -21
View File
@@ -1,5 +1,5 @@
import React from 'react';
import { Apps, HelpOutline, Settings } from '@mui/icons-material';
import { Apps, HelpOutline } 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,25 +7,24 @@ 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" key={item.title}>
<ListItem disablePadding>
<ListItemButton>
<ListItemIcon sx={{ minWidth: 25 }}>
<item.icon sx={{ fontSize: '12px' }} />
</ListItemIcon>{' '}
<ListItemText>{item.title}</ListItemText>
</ListItemButton>
</ListItem>
</Link>
))}
</List>
<AppVersion />
</Stack>
);
export const Menu = () => {
return (
<Stack justifyContent="space-between" height="100%">
<List dense disablePadding>
{menuSchema.map((item) => (
<Link component={RouterLink} to={item.path} underline="none" color="white">
<ListItem disablePadding>
<ListItemButton>
<ListItemIcon sx={{ minWidth: 25 }}>{<item.icon sx={{ fontSize: '12px' }} />}</ListItemIcon>{' '}
<ListItemText>{item.title}</ListItemText>
</ListItemButton>
</ListItem>
</Link>
))}
</List>
<AppVersion />
</Stack>
);
};
+12 -13
View File
@@ -1,19 +1,18 @@
import React from 'react';
import { Routes, Route } from 'react-router-dom';
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>
<Route index path="/" element={<ConnectionPage />} />
<Route path="menu">
<Route index element={<Menu />} />
<Route path="apps" element={<CompatibleApps />} />
<Route path="guide" element={<HelpGuide />} />
<Route path="settings" element={<Settings />} />
</Route>
</Routes>
);
export const AppRoutes = () => {
return (
<Routes>
<Route index path="/" element={<ConnectionPage />} />
<Route path="menu">
<Route index element={<Menu />} />
<Route path="apps" element={<CompatibleApps />} />
<Route path="guide" element={<HelpGuide />} />
</Route>
</Routes>
);
};
+3 -3
View File
@@ -2,12 +2,12 @@ import React from 'react';
import { ComponentMeta, ComponentStory } from '@storybook/react';
import { Box } from '@mui/material';
import { DateTime } from 'luxon';
import { Disconnected } from 'src/pages/connection/Disconnected';
import { Connected } from 'src/pages/connection/Connected';
import { ConnectionStatusKind } from 'src/types';
import { AppWindowFrame } from '../components/AppWindowFrame';
import { useClientContext } from '../context/main';
import { Services } from '../types/directory';
import { Disconnected } from 'src/pages/connection/Disconnected';
import { Connected } from 'src/pages/connection/Connected';
import { ConnectionStatusKind } from 'src/types';
export default {
title: 'App/Flow',
@@ -18,7 +18,9 @@ export const Default: ComponentStory<typeof Connected> = () => (
clearError={() => {}}
gatewayPerformance="Good"
showInfoModal={false}
closeInfoModal={() => undefined}
closeInfoModal={() => {
return undefined;
}}
status={ConnectionStatusKind.connected}
connectedSince={DateTime.now()}
ipAddress="127.0.0.1"
@@ -1,7 +1,7 @@
import React from 'react';
import { ComponentMeta, ComponentStory } from '@storybook/react';
import { ConnectionStatusKind } from 'src/types';
import { ConnectionButton } from '../components/ConnectionButton';
import { ConnectionStatusKind } from 'src/types';
export default {
title: 'Components/ConnectionButton',
@@ -1,8 +1,8 @@
import React from 'react';
import { ComponentMeta, ComponentStory } from '@storybook/react';
import { Box } from '@mui/material';
import { Disconnected } from 'src/pages/connection/Disconnected';
import { ConnectionStatusKind } from '../types';
import { Disconnected } from 'src/pages/connection/Disconnected';
export default {
title: 'Layouts/DefaultLayout',
-1
View File
@@ -29,7 +29,6 @@ declare module '@mui/material/styles' {
*/
interface NymPalette {
highlight: string;
cta: string;
success: string;
warning: string;
info: string;
-1
View File
@@ -21,7 +21,6 @@ import {
const nymPalette: NymPalette = {
/** emphasises important elements */
highlight: '#21D072',
cta: '#FB6E4E',
success: '#21D073',
info: '#60D7EF',
warning: '#FFE600',
-4
View File
@@ -1,4 +0,0 @@
export interface UserDefinedGateway {
isActive: boolean;
gateway?: string;
}
-11
View File
@@ -2,17 +2,6 @@
## 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])
+3 -5
View File
@@ -1,6 +1,6 @@
{
"name": "@nymproject/nym-wallet-app",
"version": "1.1.9",
"version": "1.1.8",
"main": "index.js",
"license": "MIT",
"scripts": {
@@ -52,8 +52,7 @@
"string-to-color": "^2.2.2",
"use-clipboard-copy": "^0.2.0",
"uuid": "^8.3.2",
"yup": "^0.32.9",
"zxcvbn": "^4.4.2"
"yup": "^0.32.9"
},
"devDependencies": {
"@babel/core": "^7.15.0",
@@ -77,7 +76,6 @@
"@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",
@@ -122,4 +120,4 @@
"webpack-favicons": "^1.3.8",
"webpack-merge": "^5.8.0"
}
}
}
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "nym_wallet"
version = "1.1.9"
version = "1.1.8"
description = "Nym Native Wallet"
authors = ["Nym Technologies SA"]
license = ""
+5 -12
View File
@@ -1,7 +1,7 @@
{
"package": {
"productName": "nym-wallet",
"version": "1.1.9"
"version": "1.1.8"
},
"build": {
"distDir": "../dist",
@@ -14,13 +14,7 @@
"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",
@@ -33,6 +27,7 @@
"macOS": {
"frameworks": [],
"minimumSystemVersion": "",
"exceptionDomain": "",
"signingIdentity": "Developer ID Application: Nym Technologies SA (VW5DZLFHM5)",
"entitlements": null
@@ -45,9 +40,7 @@
},
"updater": {
"active": true,
"endpoints": [
"https://nymtech.net/.wellknown/wallet/updater.json"
],
"endpoints": ["https://nymtech.net/.wellknown/wallet/updater.json"],
"dialog": true,
"pubkey": "dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IENCNzQ2M0E5N0VFODE2NApSV1JrZ2U2WE9rYTNETTg1OTBKdE5uWUEra0hML2syOVUvQ2lxZmFZRzZ1T3NWbGM0eVRzUTVhVwo="
},
@@ -74,4 +67,4 @@
"csp": "default-src blob: data: filesystem: ws: wss: http: https: tauri: 'unsafe-eval' 'unsafe-inline' 'self' img-src: 'self'"
}
}
}
}
@@ -5,12 +5,11 @@ import { SimpleModal } from '../Modals/SimpleModal';
import { Warning } from '../Warning';
const passwordCreationSteps = [
'Log out from the wallet',
'Log out of your wallet',
'Sign in using “Sign in with mnemonic” button',
'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',
'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',
];
// TODO add the link href value
@@ -10,6 +10,7 @@ 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';
@@ -29,16 +30,16 @@ const importAccountSteps = [
];
const MnemonicStep = ({ mnemonic, onNext, onBack }: { mnemonic: string; onNext: () => void; onBack: () => void }) => {
const [confirmed, setConfirmed] = useState(false);
const { copy, copied } = useClipboard({ copiedTimeout: 5000 });
return (
<Box sx={{ mt: 1 }}>
<DialogContent>
<Mnemonic mnemonic={mnemonic} handleConfirmed={setConfirmed} confirmed={confirmed} />
<Mnemonic mnemonic={mnemonic} handleCopy={copy} copied={copied} />
</DialogContent>
<DialogActions sx={{ p: 3, pt: 0, gap: 2 }}>
<StyledBackButton onBack={onBack} />
<Button disabled={!confirmed} fullWidth disableElevation variant="contained" size="large" onClick={onNext}>
Continue
<Button disabled={!copied} fullWidth disableElevation variant="contained" size="large" onClick={onNext}>
I saved my mnemonic
</Button>
</DialogActions>
</Box>
@@ -11,12 +11,15 @@ 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,
@@ -69,7 +72,7 @@ export const MnemonicModal = () => {
/>
</>
) : (
<Mnemonic mnemonic={accountMnemonic.value} />
<Mnemonic mnemonic={accountMnemonic.value} handleCopy={copy} copied={copied} />
)}
</Box>
</DialogContent>
+26 -27
View File
@@ -1,32 +1,23 @@
import React from 'react';
import { Box, Checkbox, FormControlLabel, Stack, TextField, Typography } from '@mui/material';
import { Title } from 'src/pages/auth/components/heading';
import { Button, Stack, TextField, Typography } from '@mui/material';
import { Check, ContentCopySharp } from '@mui/icons-material';
import { Warning } from './Warning';
export const Mnemonic = ({
mnemonic,
confirmed,
withTitle,
handleConfirmed,
copied,
handleCopy,
}: {
mnemonic: string;
confirmed?: boolean;
withTitle?: boolean;
handleConfirmed?: (confirmed: boolean) => void;
copied: boolean;
handleCopy: (text?: string) => void;
}) => (
<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>
<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>
<TextField
label="Mnemonic"
type="input"
@@ -47,11 +38,19 @@ export const Mnemonic = ({
}}
/>
{handleConfirmed && (
<FormControlLabel
label="I saved my mnemonic"
control={<Checkbox checked={confirmed} onChange={(_, checked) => handleConfirmed(checked)} />}
/>
)}
<Button
color="inherit"
disableElevation
size="large"
onClick={() => {
handleCopy(mnemonic);
}}
sx={{
width: 250,
}}
endIcon={!copied ? <ContentCopySharp /> : <Check color="success" />}
>
Copy mnemonic
</Button>
</Stack>
);
@@ -35,9 +35,7 @@ export const inputValidationSchema = Yup.object().shape({
.test('Is valid operator cost value', (value, ctx) => {
const stringValueToNumber = Math.round(Number(value));
if (isLessThan(stringValueToNumber, 0))
return ctx.createError({ message: 'Operator cost must be a valid number' });
return true;
if (isGreaterThan(stringValueToNumber, -1) && isLessThan(stringValueToNumber, 101)) return true;
return ctx.createError({ message: 'Operator cost must be a valid number' });
}),
});
@@ -1,61 +1,52 @@
/* eslint-disable no-nested-ternary */
import React from 'react';
import zxcvbn, { ZXCVBNScore } from 'zxcvbn';
import React, { useEffect, useState } from 'react';
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 = {
4: 'success' as 'success',
3: 'success' as 'success',
2: 'warning' as 'warning',
1: 'error' as 'error',
0: 'error' as 'error',
init: 'inherit' as 'inherit',
weak: 'error' as 'error',
medium: 'warning' as 'warning',
strong: 'success' as 'success',
};
const getText = (score: ZXCVBNScore) => {
switch (score) {
case 4:
return 'Very strong password';
case 3:
const getText = (strength: TStrength) => {
switch (strength) {
case 'strong':
return 'Strong password';
case 2:
return 'Average password';
case 1:
case 'medium':
return 'Medium strength password';
case 'weak':
return 'Weak password';
case 0:
return 'Very weak password';
default:
return '';
return 'Password strength';
}
};
const getColor = (score: ZXCVBNScore) => {
switch (score) {
case 4:
const getTextColor = (strength: TStrength) => {
switch (strength) {
case 'strong':
return 'success.main';
case 3:
return 'success.main';
case 2:
case 'medium':
return 'warning.main';
case 1:
return 'error.main';
case 0:
case 'weak':
return 'error.main';
default:
return 'grey.500';
}
};
const getPasswordStrength = (score: ZXCVBNScore) => {
switch (score) {
case 4:
const getPasswordStrength = (strength: TStrength) => {
switch (strength) {
case 'strong':
return 100;
case 3:
return 75;
case 2:
case 'medium':
return 50;
case 1:
return 25;
default:
return 0;
}
@@ -63,32 +54,47 @@ const getPasswordStrength = (score: ZXCVBNScore) => {
export const PasswordStrength = ({
password,
withWarnings,
handleIsSafePassword,
onChange,
}: {
password: string;
withWarnings?: boolean;
handleIsSafePassword: (isSafe: boolean) => void;
onChange: (isStrong: boolean) => void;
}) => {
const result = zxcvbn(password);
const [strength, setStrength] = useState<TStrength>('init');
handleIsSafePassword(result.score > 1);
useEffect(() => {
if (password.length === 0) {
setStrength('init');
return;
}
if (!password.length) return null;
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]);
return (
<Stack spacing={0.5}>
<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>
)}
<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>
</Box>
</Stack>
);
@@ -1,61 +0,0 @@
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,
};
+9
View File
@@ -0,0 +1,9 @@
import React from 'react';
import { AuthProvider } from 'src/context';
import { AuthRoutes } from 'src/routes/auth';
export const Auth = () => (
<AuthProvider>
<AuthRoutes />
</AuthProvider>
);
@@ -9,8 +9,9 @@ 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();
@@ -48,7 +49,7 @@ export const ConnectPassword = () => {
label="Password"
autoFocus
/>
<PasswordStrength password={password} handleIsSafePassword={setIsSafePassword} withWarnings />
<PasswordStrength password={password} onChange={(isStrong) => setIsStrongPassword(isStrong)} />
</>
<PasswordInput
password={confirmedPassword}
@@ -58,7 +59,7 @@ export const ConnectPassword = () => {
<Button
size="large"
variant="contained"
disabled={password !== confirmedPassword || password.length === 0 || isLoading || !isSafePassword}
disabled={password !== confirmedPassword || password.length === 0 || !isStrongPassword || isLoading}
onClick={storePassword}
>
{isLoading ? <CircularProgress size={25} /> : 'Create password'}
@@ -1,13 +1,13 @@
import React, { useContext, useEffect, useState } from 'react';
import React, { useContext, useEffect } 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,10 +15,12 @@ export const CreateMnemonic = () => {
}
}, []);
const { copy, copied } = useClipboard({ copiedTimeout: 5000 });
return (
<Container maxWidth="sm">
<Container maxWidth="xs">
<Stack alignItems="center" spacing={3} maxWidth="xs">
<Mnemonic mnemonic={mnemonic} handleConfirmed={setConfirmed} confirmed={confirmed} withTitle />
<Mnemonic mnemonic={mnemonic} handleCopy={copy} copied={copied} />
<Button
variant="contained"
color="primary"
@@ -26,9 +28,9 @@ export const CreateMnemonic = () => {
size="large"
onClick={() => navigate('/verify-mnemonic')}
sx={{ width: '100%', fontSize: 15 }}
disabled={!confirmed}
disabled={!copied}
>
Continue
I saved my mnemonic
</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} handleIsSafePassword={setIsSafePassword} withWarnings />
<PasswordStrength password={password} onChange={(isStrong) => setIsStrongPassword(isStrong)} />
</>
<PasswordInput
password={confirmedPassword}
@@ -58,7 +58,7 @@ export const CreatePassword = () => {
<Button
size="large"
variant="contained"
disabled={password !== confirmedPassword || password.length === 0 || isLoading || !isSafePassword}
disabled={password !== confirmedPassword || password.length === 0 || !isStrongPassword || isLoading}
onClick={storePassword}
>
Next
+1 -2
View File
@@ -17,7 +17,6 @@ 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';
@@ -105,7 +104,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,7 +7,6 @@ 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 = {
@@ -100,7 +99,7 @@ export const ApyPlayground = ({ bondedNode }: { bondedNode: TBondedMixnode }) =>
setStakeSaturation(computedStakeSaturation);
setResults(estimationResult);
} catch (e) {
Console.log(e);
console.log(e);
}
};
@@ -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
View File
@@ -1,3 +1,4 @@
export * from './auth';
export * from './Admin';
export * from './balance';
export * from './bonding';
+5 -1
View File
@@ -59,5 +59,9 @@ export const computeMixnodeRewardEstimation = async (args: {
totalDelegation: number;
profitMarginPercent: string;
intervalOperatingCost: { denom: 'unym'; amount: string };
}) => invokeWrapper<RewardEstimationResponse>('compute_mixnode_reward_estimation', args);
}) => {
console.log(args);
return 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.9"
version = "1.1.8"
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.9"
version = "1.1.8"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "nym-cli"
version = "1.1.9"
version = "1.1.8"
authors.workspace = true
edition = "2021"
@@ -19,9 +19,7 @@ export const IdentityKeyFormField: FCWithChildren<{
onValidate?: (isValid: boolean, error?: string) => void;
textFieldProps?: TextFieldProps;
errorText?: string;
size?: 'small' | 'medium';
sx?: SxProps;
disabled?: boolean;
}> = ({
required,
fullWidth,
@@ -35,8 +33,6 @@ 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>();
@@ -104,8 +100,6 @@ export const IdentityKeyFormField: FCWithChildren<{
defaultValue={initialValue}
onChange={handleChange}
InputLabelProps={{ shrink: true }}
size={size}
disabled={disabled}
/>
);
};
-10
View File
@@ -5338,11 +5338,6 @@
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"
@@ -19755,8 +19750,3 @@ 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==