Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 2c88ca137a | |||
| ca3bfc859a |
+21
-8
@@ -4,18 +4,31 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
|
|||||||
|
|
||||||
# [Unreleased]
|
# [Unreleased]
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- remove coconut feature and unify builds ([#2890])
|
||||||
|
|
||||||
|
[#2890]: https://github.com/nymtech/nym/pull/2890
|
||||||
|
|
||||||
|
# [v1.1.8] (2023-01-31)
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
- dkg rerun from scratch and dkg-specific epochs ([#2839])
|
- Rust SDK - Support SURBS (anonymous send + storage) ([#2754])
|
||||||
- nym-sdk: add support for surb storage ([#2870])
|
- dkg rerun from scratch and dkg-specific epochs ([#2810])
|
||||||
- nym-sdk: enable reply-SURBs by default ([#2874])
|
- Rename `'initial_supply'` field to `'total_supply'` in the circulating supply endpoint ([#2931])
|
||||||
- remove coconut feature and unify builds ([#2890])
|
- Circulating supply api endpoint (read the note inside before testing/deploying) ([#1902])
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- nym-api: an `--id` flag is now always explicitly required ([#2873])
|
||||||
|
|
||||||
|
[#2754]: https://github.com/nymtech/nym/issues/2754
|
||||||
|
[#2839]: https://github.com/nymtech/nym/issues/2810
|
||||||
|
[#2931]: https://github.com/nymtech/nym/issues/2931
|
||||||
|
[#1902]: https://github.com/nymtech/nym/issues/1902
|
||||||
|
[#2873]: https://github.com/nymtech/nym/issues/2873
|
||||||
|
|
||||||
[#2839]: https://github.com/nymtech/nym/pull/2839
|
|
||||||
[#2870]: https://github.com/nymtech/nym/pull/2870
|
|
||||||
[#2874]: https://github.com/nymtech/nym/pull/2874
|
|
||||||
[#2890]: https://github.com/nymtech/nym/pull/2890
|
|
||||||
|
|
||||||
# [v1.1.7] (2023-01-24)
|
# [v1.1.7] (2023-01-24)
|
||||||
|
|
||||||
|
|||||||
Generated
+11
-10
@@ -701,7 +701,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "client-core"
|
name = "client-core"
|
||||||
version = "1.1.7"
|
version = "1.1.8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-trait",
|
"async-trait",
|
||||||
"client-connections",
|
"client-connections",
|
||||||
@@ -1891,7 +1891,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "explorer-api"
|
name = "explorer-api"
|
||||||
version = "1.1.7"
|
version = "1.1.8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"chrono",
|
"chrono",
|
||||||
"clap 4.1.3",
|
"clap 4.1.3",
|
||||||
@@ -3314,6 +3314,7 @@ dependencies = [
|
|||||||
"cw4",
|
"cw4",
|
||||||
"schemars",
|
"schemars",
|
||||||
"serde",
|
"serde",
|
||||||
|
"thiserror",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -3437,7 +3438,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nym-api"
|
name = "nym-api"
|
||||||
version = "1.1.7"
|
version = "1.1.8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
@@ -3535,7 +3536,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nym-cli"
|
name = "nym-cli"
|
||||||
version = "1.1.7"
|
version = "1.1.8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"base64 0.13.1",
|
"base64 0.13.1",
|
||||||
@@ -3593,7 +3594,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nym-client"
|
name = "nym-client"
|
||||||
version = "1.1.7"
|
version = "1.1.8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"build-information",
|
"build-information",
|
||||||
"clap 4.1.3",
|
"clap 4.1.3",
|
||||||
@@ -3633,7 +3634,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nym-gateway"
|
name = "nym-gateway"
|
||||||
version = "1.1.7"
|
version = "1.1.8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
@@ -3685,7 +3686,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nym-mixnode"
|
name = "nym-mixnode"
|
||||||
version = "1.1.8"
|
version = "1.1.9"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"atty",
|
"atty",
|
||||||
@@ -3730,7 +3731,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nym-network-requester"
|
name = "nym-network-requester"
|
||||||
version = "1.1.7"
|
version = "1.1.8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-trait",
|
"async-trait",
|
||||||
"clap 4.1.3",
|
"clap 4.1.3",
|
||||||
@@ -3762,7 +3763,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nym-network-statistics"
|
name = "nym-network-statistics"
|
||||||
version = "1.1.7"
|
version = "1.1.8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"dirs",
|
"dirs",
|
||||||
"log",
|
"log",
|
||||||
@@ -3819,7 +3820,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nym-socks5-client"
|
name = "nym-socks5-client"
|
||||||
version = "1.1.7"
|
version = "1.1.8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"build-information",
|
"build-information",
|
||||||
"clap 4.1.3",
|
"clap 4.1.3",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "client-core"
|
name = "client-core"
|
||||||
version = "1.1.7"
|
version = "1.1.8"
|
||||||
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>"]
|
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>"]
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
rust-version = "1.66"
|
rust-version = "1.66"
|
||||||
|
|||||||
@@ -546,35 +546,35 @@ impl<T: NymConfig> Default for Client<T> {
|
|||||||
|
|
||||||
impl<T: NymConfig> Client<T> {
|
impl<T: NymConfig> Client<T> {
|
||||||
fn default_private_identity_key_file(id: &str) -> PathBuf {
|
fn default_private_identity_key_file(id: &str) -> PathBuf {
|
||||||
T::default_data_directory(Some(id)).join("private_identity.pem")
|
T::default_data_directory(id).join("private_identity.pem")
|
||||||
}
|
}
|
||||||
|
|
||||||
fn default_public_identity_key_file(id: &str) -> PathBuf {
|
fn default_public_identity_key_file(id: &str) -> PathBuf {
|
||||||
T::default_data_directory(Some(id)).join("public_identity.pem")
|
T::default_data_directory(id).join("public_identity.pem")
|
||||||
}
|
}
|
||||||
|
|
||||||
fn default_private_encryption_key_file(id: &str) -> PathBuf {
|
fn default_private_encryption_key_file(id: &str) -> PathBuf {
|
||||||
T::default_data_directory(Some(id)).join("private_encryption.pem")
|
T::default_data_directory(id).join("private_encryption.pem")
|
||||||
}
|
}
|
||||||
|
|
||||||
fn default_public_encryption_key_file(id: &str) -> PathBuf {
|
fn default_public_encryption_key_file(id: &str) -> PathBuf {
|
||||||
T::default_data_directory(Some(id)).join("public_encryption.pem")
|
T::default_data_directory(id).join("public_encryption.pem")
|
||||||
}
|
}
|
||||||
|
|
||||||
fn default_gateway_shared_key_file(id: &str) -> PathBuf {
|
fn default_gateway_shared_key_file(id: &str) -> PathBuf {
|
||||||
T::default_data_directory(Some(id)).join("gateway_shared.pem")
|
T::default_data_directory(id).join("gateway_shared.pem")
|
||||||
}
|
}
|
||||||
|
|
||||||
fn default_ack_key_file(id: &str) -> PathBuf {
|
fn default_ack_key_file(id: &str) -> PathBuf {
|
||||||
T::default_data_directory(Some(id)).join("ack_key.pem")
|
T::default_data_directory(id).join("ack_key.pem")
|
||||||
}
|
}
|
||||||
|
|
||||||
fn default_reply_surb_database_path(id: &str) -> PathBuf {
|
fn default_reply_surb_database_path(id: &str) -> PathBuf {
|
||||||
T::default_data_directory(Some(id)).join("persistent_reply_store.sqlite")
|
T::default_data_directory(id).join("persistent_reply_store.sqlite")
|
||||||
}
|
}
|
||||||
|
|
||||||
fn default_database_path(id: &str) -> PathBuf {
|
fn default_database_path(id: &str) -> PathBuf {
|
||||||
T::default_data_directory(Some(id)).join(DB_FILE_NAME)
|
T::default_data_directory(id).join(DB_FILE_NAME)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -149,7 +149,7 @@ pub fn load_existing_gateway_config<T>(id: &str) -> Result<GatewayEndpointConfig
|
|||||||
where
|
where
|
||||||
T: NymConfig + ClientCoreConfigTrait,
|
T: NymConfig + ClientCoreConfigTrait,
|
||||||
{
|
{
|
||||||
T::load_from_file(Some(id))
|
T::load_from_file(id)
|
||||||
.map(|existing_config| existing_config.get_gateway_endpoint().clone())
|
.map(|existing_config| existing_config.get_gateway_endpoint().clone())
|
||||||
.map_err(|err| {
|
.map_err(|err| {
|
||||||
log::error!(
|
log::error!(
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "nym-client"
|
name = "nym-client"
|
||||||
version = "1.1.7"
|
version = "1.1.8"
|
||||||
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>", "Jędrzej Stuczyński <andrew@nymtech.net>"]
|
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>", "Jędrzej Stuczyński <andrew@nymtech.net>"]
|
||||||
description = "Implementation of the Nym Client"
|
description = "Implementation of the Nym Client"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|||||||
@@ -108,7 +108,7 @@ pub(crate) async fn execute(args: &Init) -> Result<(), ClientError> {
|
|||||||
|
|
||||||
let id = &args.id;
|
let id = &args.id;
|
||||||
|
|
||||||
let already_init = Config::default_config_file_path(Some(id)).exists();
|
let already_init = Config::default_config_file_path(id).exists();
|
||||||
if already_init {
|
if already_init {
|
||||||
println!("Client \"{id}\" was already initialised before");
|
println!("Client \"{id}\" was already initialised before");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -94,7 +94,7 @@ fn version_check(cfg: &Config) -> bool {
|
|||||||
pub(crate) async fn execute(args: &Run) -> Result<(), Box<dyn Error + Send + Sync>> {
|
pub(crate) async fn execute(args: &Run) -> Result<(), Box<dyn Error + Send + Sync>> {
|
||||||
let id = &args.id;
|
let id = &args.id;
|
||||||
|
|
||||||
let mut config = match Config::load_from_file(Some(id)) {
|
let mut config = match Config::load_from_file(id) {
|
||||||
Ok(cfg) => cfg,
|
Ok(cfg) => cfg,
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
error!("Failed to load config for {}. Are you sure you have run `init` before? (Error was: {err})", id);
|
error!("Failed to load config for {}. Are you sure you have run `init` before? (Error was: {err})", id);
|
||||||
|
|||||||
@@ -131,7 +131,7 @@ pub(crate) fn execute(args: &Upgrade) {
|
|||||||
|
|
||||||
let id = &args.id;
|
let id = &args.id;
|
||||||
|
|
||||||
let existing_config = Config::load_from_file(Some(id)).unwrap_or_else(|err| {
|
let existing_config = Config::load_from_file(id).unwrap_or_else(|err| {
|
||||||
eprintln!("failed to load existing config file! - {err}");
|
eprintln!("failed to load existing config file! - {err}");
|
||||||
process::exit(1)
|
process::exit(1)
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "nym-socks5-client"
|
name = "nym-socks5-client"
|
||||||
version = "1.1.7"
|
version = "1.1.8"
|
||||||
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>"]
|
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"
|
description = "A SOCKS5 localhost proxy that converts incoming messages to Sphinx and sends them to a Nym address"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|||||||
@@ -117,7 +117,7 @@ pub(crate) async fn execute(args: &Init) -> Result<(), Socks5ClientError> {
|
|||||||
let id = &args.id;
|
let id = &args.id;
|
||||||
let provider_address = &args.provider;
|
let provider_address = &args.provider;
|
||||||
|
|
||||||
let already_init = Config::default_config_file_path(Some(id)).exists();
|
let already_init = Config::default_config_file_path(id).exists();
|
||||||
if already_init {
|
if already_init {
|
||||||
println!("SOCKS5 client \"{id}\" was already initialised before");
|
println!("SOCKS5 client \"{id}\" was already initialised before");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -108,7 +108,7 @@ fn version_check(cfg: &Config) -> bool {
|
|||||||
pub(crate) async fn execute(args: &Run) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
pub(crate) async fn execute(args: &Run) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||||
let id = &args.id;
|
let id = &args.id;
|
||||||
|
|
||||||
let mut config = match Config::load_from_file(Some(id)) {
|
let mut config = match Config::load_from_file(id) {
|
||||||
Ok(cfg) => cfg,
|
Ok(cfg) => cfg,
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
error!("Failed to load config for {}. Are you sure you have run `init` before? (Error was: {err})", id);
|
error!("Failed to load config for {}. Are you sure you have run `init` before? (Error was: {err})", id);
|
||||||
|
|||||||
@@ -144,7 +144,7 @@ pub(crate) fn execute(args: &Upgrade) {
|
|||||||
|
|
||||||
let id = &args.id;
|
let id = &args.id;
|
||||||
|
|
||||||
let existing_config = Config::load_from_file(Some(id)).unwrap_or_else(|err| {
|
let existing_config = Config::load_from_file(id).unwrap_or_else(|err| {
|
||||||
eprintln!("failed to load existing config file! - {err}");
|
eprintln!("failed to load existing config file! - {err}");
|
||||||
process::exit(1)
|
process::exit(1)
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -30,23 +30,15 @@ pub trait NymConfig: Default + Serialize + DeserializeOwned {
|
|||||||
fn default_root_directory() -> PathBuf;
|
fn default_root_directory() -> PathBuf;
|
||||||
|
|
||||||
// default, most probable, implementations; can be easily overridden where required
|
// default, most probable, implementations; can be easily overridden where required
|
||||||
fn default_config_directory(id: Option<&str>) -> PathBuf {
|
fn default_config_directory(id: &str) -> PathBuf {
|
||||||
if let Some(id) = id {
|
|
||||||
Self::default_root_directory().join(id).join(CONFIG_DIR)
|
Self::default_root_directory().join(id).join(CONFIG_DIR)
|
||||||
} else {
|
|
||||||
Self::default_root_directory().join(CONFIG_DIR)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn default_data_directory(id: Option<&str>) -> PathBuf {
|
fn default_data_directory(id: &str) -> PathBuf {
|
||||||
if let Some(id) = id {
|
|
||||||
Self::default_root_directory().join(id).join(DATA_DIR)
|
Self::default_root_directory().join(id).join(DATA_DIR)
|
||||||
} else {
|
|
||||||
Self::default_root_directory().join(DATA_DIR)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn default_config_file_path(id: Option<&str>) -> PathBuf {
|
fn default_config_file_path(id: &str) -> PathBuf {
|
||||||
Self::default_config_directory(id).join(Self::config_file_name())
|
Self::default_config_directory(id).join(Self::config_file_name())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -54,23 +46,15 @@ pub trait NymConfig: Default + Serialize + DeserializeOwned {
|
|||||||
|
|
||||||
fn try_default_root_directory() -> Option<PathBuf>;
|
fn try_default_root_directory() -> Option<PathBuf>;
|
||||||
|
|
||||||
fn try_default_config_directory(id: Option<&str>) -> Option<PathBuf> {
|
fn try_default_config_directory(id: &str) -> Option<PathBuf> {
|
||||||
if let Some(id) = id {
|
|
||||||
Self::try_default_root_directory().map(|d| d.join(id).join(CONFIG_DIR))
|
Self::try_default_root_directory().map(|d| d.join(id).join(CONFIG_DIR))
|
||||||
} else {
|
|
||||||
Self::try_default_root_directory().map(|d| d.join(CONFIG_DIR))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn try_default_data_directory(id: Option<&str>) -> Option<PathBuf> {
|
fn try_default_data_directory(id: &str) -> Option<PathBuf> {
|
||||||
if let Some(id) = id {
|
|
||||||
Self::try_default_root_directory().map(|d| d.join(id).join(DATA_DIR))
|
Self::try_default_root_directory().map(|d| d.join(id).join(DATA_DIR))
|
||||||
} else {
|
|
||||||
Self::try_default_root_directory().map(|d| d.join(DATA_DIR))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn try_default_config_file_path(id: Option<&str>) -> Option<PathBuf> {
|
fn try_default_config_file_path(id: &str) -> Option<PathBuf> {
|
||||||
Self::try_default_config_directory(id).map(|d| d.join(Self::config_file_name()))
|
Self::try_default_config_directory(id).map(|d| d.join(Self::config_file_name()))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -113,7 +97,7 @@ pub trait NymConfig: Default + Serialize + DeserializeOwned {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load_from_file(id: Option<&str>) -> io::Result<Self> {
|
fn load_from_file(id: &str) -> io::Result<Self> {
|
||||||
let file = Self::default_config_file_path(id);
|
let file = Self::default_config_file_path(id);
|
||||||
log::trace!("Loading from file: {:#?}", file);
|
log::trace!("Loading from file: {:#?}", file);
|
||||||
let config_contents = fs::read_to_string(file)?;
|
let config_contents = fs::read_to_string(file)?;
|
||||||
|
|||||||
@@ -13,3 +13,4 @@ cw4 = { version = "0.13.4" }
|
|||||||
cosmwasm-std = "1.0.0"
|
cosmwasm-std = "1.0.0"
|
||||||
schemars = "0.8"
|
schemars = "0.8"
|
||||||
serde = { version = "1.0.103", default-features = false, features = ["derive"] }
|
serde = { version = "1.0.103", default-features = false, features = ["derive"] }
|
||||||
|
thiserror = { version = "1.0.23" }
|
||||||
|
|||||||
+3
@@ -1,3 +1,6 @@
|
|||||||
|
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
use cosmwasm_std::StdError;
|
use cosmwasm_std::StdError;
|
||||||
use cw_utils::ThresholdError;
|
use cw_utils::ThresholdError;
|
||||||
|
|
||||||
@@ -1 +1,2 @@
|
|||||||
|
pub mod error;
|
||||||
pub mod msg;
|
pub mod msg;
|
||||||
|
|||||||
@@ -2,8 +2,9 @@
|
|||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
use cosmwasm_std::{entry_point, Addr, Coin, DepsMut, Empty, Env, Response};
|
use cosmwasm_std::{entry_point, Addr, Coin, DepsMut, Empty, Env, Response};
|
||||||
use cw3_flex_multisig::{state::CONFIG, ContractError};
|
use cw3_flex_multisig::state::CONFIG;
|
||||||
use cw_multi_test::{App, AppBuilder, Contract, ContractWrapper};
|
use cw_multi_test::{App, AppBuilder, Contract, ContractWrapper};
|
||||||
|
use multisig_contract_common::error::ContractError;
|
||||||
use schemars::JsonSchema;
|
use schemars::JsonSchema;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
|||||||
@@ -26,7 +26,6 @@ cw-storage-plus = { version = "0.13.4" }
|
|||||||
cosmwasm-std = { version = "1.0.0" }
|
cosmwasm-std = { version = "1.0.0" }
|
||||||
schemars = "0.8.1"
|
schemars = "0.8.1"
|
||||||
serde = { version = "1.0.103", default-features = false, features = ["derive"] }
|
serde = { version = "1.0.103", default-features = false, features = ["derive"] }
|
||||||
thiserror = { version = "1.0.23" }
|
|
||||||
|
|
||||||
group-contract-common = { path = "../../../common/cosmwasm-smart-contracts/group-contract" }
|
group-contract-common = { path = "../../../common/cosmwasm-smart-contracts/group-contract" }
|
||||||
multisig-contract-common = { path= "../../../common/cosmwasm-smart-contracts/multisig-contract" }
|
multisig-contract-common = { path= "../../../common/cosmwasm-smart-contracts/multisig-contract" }
|
||||||
|
|||||||
@@ -17,8 +17,8 @@ use cw4::{Cw4Contract, MemberChangedHookMsg, MemberDiff};
|
|||||||
use cw_storage_plus::Bound;
|
use cw_storage_plus::Bound;
|
||||||
use cw_utils::{maybe_addr, Expiration, ThresholdResponse};
|
use cw_utils::{maybe_addr, Expiration, ThresholdResponse};
|
||||||
|
|
||||||
use crate::error::ContractError;
|
|
||||||
use crate::state::{Config, CONFIG};
|
use crate::state::{Config, CONFIG};
|
||||||
|
use multisig_contract_common::error::ContractError;
|
||||||
use multisig_contract_common::msg::{ExecuteMsg, InstantiateMsg, MigrateMsg, QueryMsg};
|
use multisig_contract_common::msg::{ExecuteMsg, InstantiateMsg, MigrateMsg, QueryMsg};
|
||||||
|
|
||||||
// version info for migration info
|
// version info for migration info
|
||||||
|
|||||||
@@ -1,5 +1,2 @@
|
|||||||
pub mod contract;
|
pub mod contract;
|
||||||
pub mod error;
|
|
||||||
pub mod state;
|
pub mod state;
|
||||||
|
|
||||||
pub use crate::error::ContractError;
|
|
||||||
|
|||||||
+4
-4
@@ -12,10 +12,10 @@ DENOMS_EXPONENT=6
|
|||||||
MIXNET_CONTRACT_ADDRESS=n14hj2tavq8fpesdwxxcu44rty3hh90vhujrvcmstl4zr3txmfvw9sjyvg3g
|
MIXNET_CONTRACT_ADDRESS=n14hj2tavq8fpesdwxxcu44rty3hh90vhujrvcmstl4zr3txmfvw9sjyvg3g
|
||||||
VESTING_CONTRACT_ADDRESS=n1nc5tatafv6eyq7llkr2gv50ff9e22mnf70qgjlv737ktmt4eswrq73f2nw
|
VESTING_CONTRACT_ADDRESS=n1nc5tatafv6eyq7llkr2gv50ff9e22mnf70qgjlv737ktmt4eswrq73f2nw
|
||||||
BANDWIDTH_CLAIM_CONTRACT_ADDRESS=n19lc9u84cz0yz3fww5283nucc9yvr8gsjmgeul0
|
BANDWIDTH_CLAIM_CONTRACT_ADDRESS=n19lc9u84cz0yz3fww5283nucc9yvr8gsjmgeul0
|
||||||
COCONUT_BANDWIDTH_CONTRACT_ADDRESS=n12ckdkm3q7eytefs7rwu4ue3t9hxgvl9v08jddmtwgct2ve0pv50q0t8dlt
|
COCONUT_BANDWIDTH_CONTRACT_ADDRESS=n19d2nwj7fdhxqmyvgy8lf3ad49a6vmww4shryhrkj2mqk36att66s6xzszw
|
||||||
GROUP_CONTRACT_ADDRESS=n1rw8fw2mpcpzzq3jpa4e52ufawnmj5a4u68p35umvgskewuw0nlzsaa5w4m
|
GROUP_CONTRACT_ADDRESS=n1fqquzw4mk0pkamgr2ywt2v7h2j9nuyjjn4gvpy8zlpp6xn0uyuzqfm28l5
|
||||||
MULTISIG_CONTRACT_ADDRESS=n14krxe8ukzagwhvec0rmteexu62w8k9kp9sra9ww6em2hnmzcukqsa0utc8
|
MULTISIG_CONTRACT_ADDRESS=n1gaq3666chd5348apj8cka8t2mckv7azp9espyr7wgpxyuzur5d0sazpysy
|
||||||
COCONUT_DKG_CONTRACT_ADDRESS=n1rl5n6cxuz2hdy3f7d9hsnw8zn0zwwwr0r4dxfz7tktgpgkcnz9zshmvksc
|
COCONUT_DKG_CONTRACT_ADDRESS=n18yadscxw8v35dds7ksv3j0svmjh3h6e7tmxpadk96mvgz27zygkshuf4vs
|
||||||
REWARDING_VALIDATOR_ADDRESS=n10yyd98e2tuwu0f7ypz9dy3hhjw7v772q6287gy
|
REWARDING_VALIDATOR_ADDRESS=n10yyd98e2tuwu0f7ypz9dy3hhjw7v772q6287gy
|
||||||
STATISTICS_SERVICE_DOMAIN_ADDRESS="https://mainnet-stats.nymte.ch:8090"
|
STATISTICS_SERVICE_DOMAIN_ADDRESS="https://mainnet-stats.nymte.ch:8090"
|
||||||
NYXD="https://qwerty-validator.qa.nymte.ch/"
|
NYXD="https://qwerty-validator.qa.nymte.ch/"
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "explorer-api"
|
name = "explorer-api"
|
||||||
version = "1.1.7"
|
version = "1.1.8"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|||||||
@@ -1,5 +1,15 @@
|
|||||||
## UNRELEASED
|
## UNRELEASED
|
||||||
|
|
||||||
|
## [nym-explorer-v1.0.4](https://github.com/nymtech/nym/tree/nym-explorer-v1.0.4) (2023-01-31)
|
||||||
|
|
||||||
|
- Add routing score on gateway list ([#2913])
|
||||||
|
- Add gateway's last Routing Score to the gateways list page ([#2186])
|
||||||
|
- Upgrade Sandbox and make below changes: ([#2332])
|
||||||
|
|
||||||
|
[#2913]: https://github.com/nymtech/nym/pull/2913
|
||||||
|
[#2186]: https://github.com/nymtech/nym/issues/2186
|
||||||
|
[#2332]: https://github.com/nymtech/nym/issues/2332
|
||||||
|
|
||||||
## [nym-explorer-v1.0.3](https://github.com/nymtech/nym/tree/nym-explorer-v1.0.3) (2023-01-24)
|
## [nym-explorer-v1.0.3](https://github.com/nymtech/nym/tree/nym-explorer-v1.0.3) (2023-01-24)
|
||||||
|
|
||||||
- Stake Saturation tooltip on node list and node pages updated ([#2877])
|
- Stake Saturation tooltip on node list and node pages updated ([#2877])
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@nym/network-explorer",
|
"name": "@nym/network-explorer",
|
||||||
"version": "1.0.3",
|
"version": "1.0.4",
|
||||||
"private": true,
|
"private": true,
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ export const OVERVIEW_API = `${API_BASE_URL}/overview`;
|
|||||||
export const MIXNODE_PING = `${API_BASE_URL}/ping`;
|
export const MIXNODE_PING = `${API_BASE_URL}/ping`;
|
||||||
export const MIXNODES_API = `${API_BASE_URL}/mix-nodes`;
|
export const MIXNODES_API = `${API_BASE_URL}/mix-nodes`;
|
||||||
export const MIXNODE_API = `${API_BASE_URL}/mix-node`;
|
export const MIXNODE_API = `${API_BASE_URL}/mix-node`;
|
||||||
export const GATEWAYS_API = `${NYM_API_BASE_URL}/api/v1/gateways`;
|
export const GATEWAYS_API = `${NYM_API_BASE_URL}/api/v1/status/gateways/detailed`;
|
||||||
export const VALIDATORS_API = `${VALIDATOR_BASE_URL}/validators`;
|
export const VALIDATORS_API = `${VALIDATOR_BASE_URL}/validators`;
|
||||||
export const BLOCK_API = `${NYM_API_BASE_URL}/block`;
|
export const BLOCK_API = `${NYM_API_BASE_URL}/block`;
|
||||||
export const COUNTRY_DATA_API = `${API_BASE_URL}/countries`;
|
export const COUNTRY_DATA_API = `${API_BASE_URL}/countries`;
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ import {
|
|||||||
CountryDataResponse,
|
CountryDataResponse,
|
||||||
DelegationsResponse,
|
DelegationsResponse,
|
||||||
UniqDelegationsResponse,
|
UniqDelegationsResponse,
|
||||||
GatewayResponse,
|
|
||||||
GatewayReportResponse,
|
GatewayReportResponse,
|
||||||
UptimeStoryResponse,
|
UptimeStoryResponse,
|
||||||
MixNodeDescriptionResponse,
|
MixNodeDescriptionResponse,
|
||||||
@@ -27,7 +26,10 @@ import {
|
|||||||
StatusResponse,
|
StatusResponse,
|
||||||
SummaryOverviewResponse,
|
SummaryOverviewResponse,
|
||||||
ValidatorsResponse,
|
ValidatorsResponse,
|
||||||
|
GatewayBondAnnotated,
|
||||||
|
GatewayBond,
|
||||||
} from '../typeDefs/explorer-api';
|
} from '../typeDefs/explorer-api';
|
||||||
|
import { toPercentIntegerString } from '../utils';
|
||||||
|
|
||||||
function getFromCache(key: string) {
|
function getFromCache(key: string) {
|
||||||
const ts = Number(localStorage.getItem('ts'));
|
const ts = Number(localStorage.getItem('ts'));
|
||||||
@@ -89,9 +91,13 @@ export class Api {
|
|||||||
return response.json();
|
return response.json();
|
||||||
};
|
};
|
||||||
|
|
||||||
static fetchGateways = async (): Promise<GatewayResponse> => {
|
static fetchGateways = async (): Promise<GatewayBond[]> => {
|
||||||
const res = await fetch(GATEWAYS_API);
|
const res = await fetch(GATEWAYS_API);
|
||||||
return res.json();
|
const gatewaysAnnotated: GatewayBondAnnotated[] = await res.json();
|
||||||
|
return gatewaysAnnotated.map(({ gateway_bond, performance }) => ({
|
||||||
|
...gateway_bond,
|
||||||
|
performance: toPercentIntegerString(performance),
|
||||||
|
}));
|
||||||
};
|
};
|
||||||
|
|
||||||
static fetchGatewayUptimeStoryById = async (id: string): Promise<UptimeStoryResponse> =>
|
static fetchGatewayUptimeStoryById = async (id: string): Promise<UptimeStoryResponse> =>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { GatewayResponse, GatewayResponseItem, GatewayReportResponse } from '../typeDefs/explorer-api';
|
import { GatewayResponse, GatewayBond, GatewayReportResponse } from '../typeDefs/explorer-api';
|
||||||
|
|
||||||
export type GatewayRowType = {
|
export type GatewayRowType = {
|
||||||
id: string;
|
id: string;
|
||||||
@@ -8,6 +8,7 @@ export type GatewayRowType = {
|
|||||||
host: string;
|
host: string;
|
||||||
location: string;
|
location: string;
|
||||||
version: string;
|
version: string;
|
||||||
|
performance: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type GatewayEnrichedRowType = GatewayRowType & {
|
export type GatewayEnrichedRowType = GatewayRowType & {
|
||||||
@@ -28,13 +29,11 @@ export function gatewayToGridRow(arrayOfGateways: GatewayResponse): GatewayRowTy
|
|||||||
bond: gw.pledge_amount.amount || 0,
|
bond: gw.pledge_amount.amount || 0,
|
||||||
host: gw.gateway.host || '',
|
host: gw.gateway.host || '',
|
||||||
version: gw.gateway.version || '',
|
version: gw.gateway.version || '',
|
||||||
|
performance: gw.performance,
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
export function gatewayEnrichedToGridRow(
|
export function gatewayEnrichedToGridRow(gateway: GatewayBond, report: GatewayReportResponse): GatewayEnrichedRowType {
|
||||||
gateway: GatewayResponseItem,
|
|
||||||
report: GatewayReportResponse,
|
|
||||||
): GatewayEnrichedRowType {
|
|
||||||
return {
|
return {
|
||||||
id: gateway.owner,
|
id: gateway.owner,
|
||||||
owner: gateway.owner,
|
owner: gateway.owner,
|
||||||
@@ -47,5 +46,6 @@ export function gatewayEnrichedToGridRow(
|
|||||||
mixPort: gateway.gateway.mix_port || 0,
|
mixPort: gateway.gateway.mix_port || 0,
|
||||||
routingScore: `${report.most_recent}%`,
|
routingScore: `${report.most_recent}%`,
|
||||||
avgUptime: `${report.last_day || report.last_hour}%`,
|
avgUptime: `${report.last_day || report.last_hour}%`,
|
||||||
|
performance: gateway.performance,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { Alert, AlertTitle, Box, CircularProgress, Grid } from '@mui/material';
|
import { Alert, AlertTitle, Box, CircularProgress, Grid } from '@mui/material';
|
||||||
import { useParams } from 'react-router-dom';
|
import { useParams } from 'react-router-dom';
|
||||||
import { GatewayResponseItem } from '../../typeDefs/explorer-api';
|
import { GatewayBond } from '../../typeDefs/explorer-api';
|
||||||
import { ColumnsType, DetailTable } from '../../components/DetailTable';
|
import { ColumnsType, DetailTable } from '../../components/DetailTable';
|
||||||
import { gatewayEnrichedToGridRow, GatewayEnrichedRowType } from '../../components/Gateways';
|
import { gatewayEnrichedToGridRow, GatewayEnrichedRowType } from '../../components/Gateways';
|
||||||
import { ComponentError } from '../../components/ComponentError';
|
import { ComponentError } from '../../components/ComponentError';
|
||||||
@@ -69,7 +69,7 @@ const columns: ColumnsType[] = [
|
|||||||
/**
|
/**
|
||||||
* Shows gateway details
|
* Shows gateway details
|
||||||
*/
|
*/
|
||||||
const PageGatewayDetailsWithState = ({ selectedGateway }: { selectedGateway: GatewayResponseItem | undefined }) => {
|
const PageGatewayDetailsWithState = ({ selectedGateway }: { selectedGateway: GatewayBond | undefined }) => {
|
||||||
const [enrichGateway, setEnrichGateway] = React.useState<GatewayEnrichedRowType>();
|
const [enrichGateway, setEnrichGateway] = React.useState<GatewayEnrichedRowType>();
|
||||||
const [status, setStatus] = React.useState<number[] | undefined>();
|
const [status, setStatus] = React.useState<number[] | undefined>();
|
||||||
const { uptimeReport, uptimeStory } = useGatewayContext();
|
const { uptimeReport, uptimeStory } = useGatewayContext();
|
||||||
@@ -130,7 +130,7 @@ const PageGatewayDetailsWithState = ({ selectedGateway }: { selectedGateway: Gat
|
|||||||
* Guard component to handle loading and not found states
|
* Guard component to handle loading and not found states
|
||||||
*/
|
*/
|
||||||
const PageGatewayDetailGuard: FCWithChildren = () => {
|
const PageGatewayDetailGuard: FCWithChildren = () => {
|
||||||
const [selectedGateway, setSelectedGateway] = React.useState<GatewayResponseItem | undefined>();
|
const [selectedGateway, setSelectedGateway] = React.useState<GatewayBond | undefined>();
|
||||||
const { gateways } = useMainContext();
|
const { gateways } = useMainContext();
|
||||||
const { id } = useParams<{ id: string | undefined }>();
|
const { id } = useParams<{ id: string | undefined }>();
|
||||||
|
|
||||||
|
|||||||
@@ -81,6 +81,24 @@ export const PageGateways: FCWithChildren = () => {
|
|||||||
</MuiLink>
|
</MuiLink>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
field: 'performance',
|
||||||
|
headerName: 'Routing Score',
|
||||||
|
renderHeader: () => <CustomColumnHeading headingTitle="Routing Score" />,
|
||||||
|
width: 150,
|
||||||
|
headerAlign: 'left',
|
||||||
|
headerClassName: 'MuiDataGrid-header-override',
|
||||||
|
renderCell: (params: GridRenderCellParams) => (
|
||||||
|
<MuiLink
|
||||||
|
sx={{ ...cellStyles }}
|
||||||
|
component={RRDLink}
|
||||||
|
to={`/network-components/gateway/${params.row.identityKey}`}
|
||||||
|
data-testid="pledge-amount"
|
||||||
|
>
|
||||||
|
{`${params.value}%`}
|
||||||
|
</MuiLink>
|
||||||
|
),
|
||||||
|
},
|
||||||
{
|
{
|
||||||
field: 'host',
|
field: 'host',
|
||||||
renderHeader: () => <CustomColumnHeading headingTitle="IP:Port" />,
|
renderHeader: () => <CustomColumnHeading headingTitle="IP:Port" />,
|
||||||
|
|||||||
@@ -116,15 +116,21 @@ export interface StatsResponse {
|
|||||||
|
|
||||||
export type MixNodeHistoryResponse = StatsResponse;
|
export type MixNodeHistoryResponse = StatsResponse;
|
||||||
|
|
||||||
export interface GatewayResponseItem {
|
export interface GatewayBond {
|
||||||
block_height: number;
|
block_height: number;
|
||||||
pledge_amount: Amount;
|
pledge_amount: Amount;
|
||||||
total_delegation: Amount;
|
total_delegation: Amount;
|
||||||
owner: string;
|
owner: string;
|
||||||
gateway: Gateway;
|
gateway: Gateway;
|
||||||
|
performance: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type GatewayResponse = GatewayResponseItem[];
|
export interface GatewayBondAnnotated {
|
||||||
|
gateway_bond: GatewayBond;
|
||||||
|
performance: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type GatewayResponse = GatewayBond[];
|
||||||
|
|
||||||
export interface GatewayReportResponse {
|
export interface GatewayReportResponse {
|
||||||
identity: string;
|
identity: string;
|
||||||
|
|||||||
+1
-1
@@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
[package]
|
[package]
|
||||||
name = "nym-gateway"
|
name = "nym-gateway"
|
||||||
version = "1.1.7"
|
version = "1.1.8"
|
||||||
authors = [
|
authors = [
|
||||||
"Dave Hrycyszyn <futurechimp@users.noreply.github.com>",
|
"Dave Hrycyszyn <futurechimp@users.noreply.github.com>",
|
||||||
"Jędrzej Stuczyński <andrew@nymtech.net>",
|
"Jędrzej Stuczyński <andrew@nymtech.net>",
|
||||||
|
|||||||
@@ -102,7 +102,7 @@ impl From<Init> for OverrideConfig {
|
|||||||
pub async fn execute(args: Init, output: OutputFormat) -> Result<(), Box<dyn Error + Send + Sync>> {
|
pub async fn execute(args: Init, output: OutputFormat) -> Result<(), Box<dyn Error + Send + Sync>> {
|
||||||
println!("Initialising gateway {}...", args.id);
|
println!("Initialising gateway {}...", args.id);
|
||||||
|
|
||||||
let already_init = if Config::default_config_file_path(Some(&args.id)).exists() {
|
let already_init = if Config::default_config_file_path(&args.id).exists() {
|
||||||
eprintln!(
|
eprintln!(
|
||||||
"Gateway \"{}\" was already initialised before! Config information will be \
|
"Gateway \"{}\" was already initialised before! Config information will be \
|
||||||
overwritten (but keys will be kept)!",
|
overwritten (but keys will be kept)!",
|
||||||
|
|||||||
@@ -139,7 +139,7 @@ fn do_upgrade(mut config: Config, args: &Upgrade, package_version: Version) {
|
|||||||
pub async fn execute(args: &Upgrade) {
|
pub async fn execute(args: &Upgrade) {
|
||||||
let package_version = parse_package_version();
|
let package_version = parse_package_version();
|
||||||
|
|
||||||
let existing_config = Config::load_from_file(Some(&args.id)).unwrap_or_else(|err| {
|
let existing_config = Config::load_from_file(&args.id).unwrap_or_else(|err| {
|
||||||
eprintln!("failed to load existing config file! - {err}");
|
eprintln!("failed to load existing config file! - {err}");
|
||||||
process::exit(1)
|
process::exit(1)
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -374,23 +374,23 @@ pub struct Gateway {
|
|||||||
|
|
||||||
impl Gateway {
|
impl Gateway {
|
||||||
fn default_private_sphinx_key_file(id: &str) -> PathBuf {
|
fn default_private_sphinx_key_file(id: &str) -> PathBuf {
|
||||||
Config::default_data_directory(Some(id)).join("private_sphinx.pem")
|
Config::default_data_directory(id).join("private_sphinx.pem")
|
||||||
}
|
}
|
||||||
|
|
||||||
fn default_public_sphinx_key_file(id: &str) -> PathBuf {
|
fn default_public_sphinx_key_file(id: &str) -> PathBuf {
|
||||||
Config::default_data_directory(Some(id)).join("public_sphinx.pem")
|
Config::default_data_directory(id).join("public_sphinx.pem")
|
||||||
}
|
}
|
||||||
|
|
||||||
fn default_private_identity_key_file(id: &str) -> PathBuf {
|
fn default_private_identity_key_file(id: &str) -> PathBuf {
|
||||||
Config::default_data_directory(Some(id)).join("private_identity.pem")
|
Config::default_data_directory(id).join("private_identity.pem")
|
||||||
}
|
}
|
||||||
|
|
||||||
fn default_public_identity_key_file(id: &str) -> PathBuf {
|
fn default_public_identity_key_file(id: &str) -> PathBuf {
|
||||||
Config::default_data_directory(Some(id)).join("public_identity.pem")
|
Config::default_data_directory(id).join("public_identity.pem")
|
||||||
}
|
}
|
||||||
|
|
||||||
fn default_database_path(id: &str) -> PathBuf {
|
fn default_database_path(id: &str) -> PathBuf {
|
||||||
Config::default_data_directory(Some(id)).join("db.sqlite")
|
Config::default_data_directory(id).join("db.sqlite")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -11,14 +11,14 @@ pub(crate) fn build_config<O: Into<OverrideConfig>>(
|
|||||||
id: String,
|
id: String,
|
||||||
override_args: O,
|
override_args: O,
|
||||||
) -> Result<Config, GatewayError> {
|
) -> Result<Config, GatewayError> {
|
||||||
let config = match Config::load_from_file(Some(&id)) {
|
let config = match Config::load_from_file(&id) {
|
||||||
Ok(cfg) => cfg,
|
Ok(cfg) => cfg,
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
error!(
|
error!(
|
||||||
"Failed to load config for {id}. Are you sure you have run `init` before? (Error was: {err})",
|
"Failed to load config for {id}. Are you sure you have run `init` before? (Error was: {err})",
|
||||||
);
|
);
|
||||||
return Err(GatewayError::ConfigLoadFailure {
|
return Err(GatewayError::ConfigLoadFailure {
|
||||||
path: Config::default_config_file_path(Some(&id)),
|
path: Config::default_config_file_path(&id),
|
||||||
id,
|
id,
|
||||||
source: err,
|
source: err,
|
||||||
});
|
});
|
||||||
|
|||||||
+1
-1
@@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
[package]
|
[package]
|
||||||
name = "nym-mixnode"
|
name = "nym-mixnode"
|
||||||
version = "1.1.8"
|
version = "1.1.9"
|
||||||
authors = [
|
authors = [
|
||||||
"Dave Hrycyszyn <futurechimp@users.noreply.github.com>",
|
"Dave Hrycyszyn <futurechimp@users.noreply.github.com>",
|
||||||
"Jędrzej Stuczyński <andrew@nymtech.net>",
|
"Jędrzej Stuczyński <andrew@nymtech.net>",
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ fn read_user_input() -> String {
|
|||||||
|
|
||||||
pub(crate) fn execute(args: Describe) {
|
pub(crate) fn execute(args: Describe) {
|
||||||
// ensure that the mixnode has in fact been initialized
|
// ensure that the mixnode has in fact been initialized
|
||||||
match Config::load_from_file(Some(&args.id)) {
|
match Config::load_from_file(&args.id) {
|
||||||
Ok(cfg) => cfg,
|
Ok(cfg) => cfg,
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
error!("Failed to load config for {}. Are you sure you have run `init` before? (Error was: {err})", &args.id);
|
error!("Failed to load config for {}. Are you sure you have run `init` before? (Error was: {err})", &args.id);
|
||||||
@@ -83,7 +83,7 @@ pub(crate) fn execute(args: Describe) {
|
|||||||
// save the struct
|
// save the struct
|
||||||
NodeDescription::save_to_file(
|
NodeDescription::save_to_file(
|
||||||
&node_description,
|
&node_description,
|
||||||
Config::default_config_directory(Some(&args.id)),
|
Config::default_config_directory(&args.id),
|
||||||
)
|
)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ pub(crate) fn execute(args: &Init, output: OutputFormat) {
|
|||||||
let id = &override_config_fields.id;
|
let id = &override_config_fields.id;
|
||||||
eprintln!("Initialising mixnode {id}...");
|
eprintln!("Initialising mixnode {id}...");
|
||||||
|
|
||||||
let already_init = if Config::default_config_file_path(Some(id)).exists() {
|
let already_init = if Config::default_config_file_path(id).exists() {
|
||||||
eprintln!("Mixnode \"{id}\" was already initialised before! Config information will be overwritten (but keys will be kept)!");
|
eprintln!("Mixnode \"{id}\" was already initialised before! Config information will be overwritten (but keys will be kept)!");
|
||||||
true
|
true
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ pub(crate) struct NodeDetails {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn execute(args: &NodeDetails, output: OutputFormat) {
|
pub(crate) fn execute(args: &NodeDetails, output: OutputFormat) {
|
||||||
let config = match Config::load_from_file(Some(&args.id)) {
|
let config = match Config::load_from_file(&args.id) {
|
||||||
Ok(cfg) => cfg,
|
Ok(cfg) => cfg,
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
error!(
|
error!(
|
||||||
|
|||||||
@@ -79,7 +79,7 @@ fn special_addresses() -> Vec<&'static str> {
|
|||||||
pub(crate) async fn execute(args: &Run, output: OutputFormat) {
|
pub(crate) async fn execute(args: &Run, output: OutputFormat) {
|
||||||
eprintln!("Starting mixnode {}...", args.id);
|
eprintln!("Starting mixnode {}...", args.id);
|
||||||
|
|
||||||
let mut config = match Config::load_from_file(Some(&args.id)) {
|
let mut config = match Config::load_from_file(&args.id) {
|
||||||
Ok(cfg) => cfg,
|
Ok(cfg) => cfg,
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
error!(
|
error!(
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ fn print_signed_text(private_key: &identity::PrivateKey, text: &str) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn execute(args: &Sign) {
|
pub(crate) fn execute(args: &Sign) {
|
||||||
let config = match Config::load_from_file(Some(&args.id)) {
|
let config = match Config::load_from_file(&args.id) {
|
||||||
Ok(cfg) => cfg,
|
Ok(cfg) => cfg,
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
error!(
|
error!(
|
||||||
|
|||||||
@@ -125,7 +125,7 @@ fn do_upgrade(mut config: Config, args: &Upgrade, package_version: Version) {
|
|||||||
pub(crate) fn execute(args: &Upgrade) {
|
pub(crate) fn execute(args: &Upgrade) {
|
||||||
let package_version = parse_package_version();
|
let package_version = parse_package_version();
|
||||||
|
|
||||||
let existing_config = Config::load_from_file(Some(&args.id)).unwrap_or_else(|err| {
|
let existing_config = Config::load_from_file(&args.id).unwrap_or_else(|err| {
|
||||||
eprintln!("failed to load existing config file! - {err}");
|
eprintln!("failed to load existing config file! - {err}");
|
||||||
process::exit(1)
|
process::exit(1)
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -376,19 +376,19 @@ struct MixNode {
|
|||||||
|
|
||||||
impl MixNode {
|
impl MixNode {
|
||||||
fn default_private_identity_key_file(id: &str) -> PathBuf {
|
fn default_private_identity_key_file(id: &str) -> PathBuf {
|
||||||
Config::default_data_directory(Some(id)).join("private_identity.pem")
|
Config::default_data_directory(id).join("private_identity.pem")
|
||||||
}
|
}
|
||||||
|
|
||||||
fn default_public_identity_key_file(id: &str) -> PathBuf {
|
fn default_public_identity_key_file(id: &str) -> PathBuf {
|
||||||
Config::default_data_directory(Some(id)).join("public_identity.pem")
|
Config::default_data_directory(id).join("public_identity.pem")
|
||||||
}
|
}
|
||||||
|
|
||||||
fn default_private_sphinx_key_file(id: &str) -> PathBuf {
|
fn default_private_sphinx_key_file(id: &str) -> PathBuf {
|
||||||
Config::default_data_directory(Some(id)).join("private_sphinx.pem")
|
Config::default_data_directory(id).join("private_sphinx.pem")
|
||||||
}
|
}
|
||||||
|
|
||||||
fn default_public_sphinx_key_file(id: &str) -> PathBuf {
|
fn default_public_sphinx_key_file(id: &str) -> PathBuf {
|
||||||
Config::default_data_directory(Some(id)).join("public_sphinx.pem")
|
Config::default_data_directory(id).join("public_sphinx.pem")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ impl MixNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn load_node_description(config: &Config) -> NodeDescription {
|
fn load_node_description(config: &Config) -> NodeDescription {
|
||||||
NodeDescription::load_from_file(Config::default_config_directory(Some(&config.get_id())))
|
NodeDescription::load_from_file(Config::default_config_directory(&config.get_id()))
|
||||||
.unwrap_or_default()
|
.unwrap_or_default()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
[package]
|
[package]
|
||||||
name = "nym-api"
|
name = "nym-api"
|
||||||
version = "1.1.7"
|
version = "1.1.8"
|
||||||
authors = [
|
authors = [
|
||||||
"Dave Hrycyszyn <futurechimp@users.noreply.github.com>",
|
"Dave Hrycyszyn <futurechimp@users.noreply.github.com>",
|
||||||
"Jędrzej Stuczyński <andrew@nymtech.net>",
|
"Jędrzej Stuczyński <andrew@nymtech.net>",
|
||||||
|
|||||||
@@ -291,7 +291,7 @@ pub struct GatewayUptimeHistoryResponse {
|
|||||||
|
|
||||||
#[derive(Clone, Serialize, Deserialize, schemars::JsonSchema)]
|
#[derive(Clone, Serialize, Deserialize, schemars::JsonSchema)]
|
||||||
pub struct CirculatingSupplyResponse {
|
pub struct CirculatingSupplyResponse {
|
||||||
pub initial_supply: Coin,
|
pub total_supply: Coin,
|
||||||
pub mixmining_reserve: Coin,
|
pub mixmining_reserve: Coin,
|
||||||
pub vesting_tokens: Coin,
|
pub vesting_tokens: Coin,
|
||||||
pub circulating_supply: Coin,
|
pub circulating_supply: Coin,
|
||||||
|
|||||||
+3
-3
@@ -7,7 +7,7 @@ use validator_client::nyxd::Coin;
|
|||||||
|
|
||||||
pub(crate) struct CirculatingSupplyCacheData {
|
pub(crate) struct CirculatingSupplyCacheData {
|
||||||
// no need to cache that one as it's constant, but let's put it here for consistency sake
|
// no need to cache that one as it's constant, but let's put it here for consistency sake
|
||||||
pub(crate) initial_supply: Coin,
|
pub(crate) total_supply: Coin,
|
||||||
pub(crate) mixmining_reserve: Cache<Coin>,
|
pub(crate) mixmining_reserve: Cache<Coin>,
|
||||||
pub(crate) vesting_tokens: Cache<Coin>,
|
pub(crate) vesting_tokens: Cache<Coin>,
|
||||||
pub(crate) circulating_supply: Cache<Coin>,
|
pub(crate) circulating_supply: Cache<Coin>,
|
||||||
@@ -18,7 +18,7 @@ impl CirculatingSupplyCacheData {
|
|||||||
let zero_coin = Coin::new(0, &mix_denom);
|
let zero_coin = Coin::new(0, &mix_denom);
|
||||||
|
|
||||||
CirculatingSupplyCacheData {
|
CirculatingSupplyCacheData {
|
||||||
initial_supply: Coin::new(1_000_000_000_000_000, mix_denom),
|
total_supply: Coin::new(1_000_000_000_000_000, mix_denom),
|
||||||
mixmining_reserve: Cache::new(zero_coin.clone()),
|
mixmining_reserve: Cache::new(zero_coin.clone()),
|
||||||
vesting_tokens: Cache::new(zero_coin.clone()),
|
vesting_tokens: Cache::new(zero_coin.clone()),
|
||||||
circulating_supply: Cache::new(zero_coin),
|
circulating_supply: Cache::new(zero_coin),
|
||||||
@@ -29,7 +29,7 @@ impl CirculatingSupplyCacheData {
|
|||||||
impl<'a> From<&'a CirculatingSupplyCacheData> for CirculatingSupplyResponse {
|
impl<'a> From<&'a CirculatingSupplyCacheData> for CirculatingSupplyResponse {
|
||||||
fn from(value: &'a CirculatingSupplyCacheData) -> Self {
|
fn from(value: &'a CirculatingSupplyCacheData) -> Self {
|
||||||
CirculatingSupplyResponse {
|
CirculatingSupplyResponse {
|
||||||
initial_supply: value.initial_supply.clone().into(),
|
total_supply: value.total_supply.clone().into(),
|
||||||
mixmining_reserve: value.mixmining_reserve.clone().into_inner().into(),
|
mixmining_reserve: value.mixmining_reserve.clone().into_inner().into(),
|
||||||
vesting_tokens: value.vesting_tokens.clone().into_inner().into(),
|
vesting_tokens: value.vesting_tokens.clone().into_inner().into(),
|
||||||
circulating_supply: value.circulating_supply.clone().into_inner().into(),
|
circulating_supply: value.circulating_supply.clone().into_inner().into(),
|
||||||
|
|||||||
+1
-1
@@ -76,7 +76,7 @@ impl CirculatingSupplyCache {
|
|||||||
pub(crate) async fn update(&self, mixmining_reserve: Coin, vesting_tokens: Coin) {
|
pub(crate) async fn update(&self, mixmining_reserve: Coin, vesting_tokens: Coin) {
|
||||||
let mut cache = self.data.write().await;
|
let mut cache = self.data.write().await;
|
||||||
|
|
||||||
let mut circulating_supply = cache.initial_supply.clone();
|
let mut circulating_supply = cache.total_supply.clone();
|
||||||
circulating_supply.amount -= mixmining_reserve.amount;
|
circulating_supply.amount -= mixmining_reserve.amount;
|
||||||
circulating_supply.amount -= vesting_tokens.amount;
|
circulating_supply.amount -= vesting_tokens.amount;
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ use crate::coconut::dkg::client::DkgClient;
|
|||||||
use crate::coconut::dkg::complaints::ComplaintReason;
|
use crate::coconut::dkg::complaints::ComplaintReason;
|
||||||
use crate::coconut::dkg::state::{ConsistentState, State};
|
use crate::coconut::dkg::state::{ConsistentState, State};
|
||||||
use crate::coconut::error::CoconutError;
|
use crate::coconut::error::CoconutError;
|
||||||
|
use crate::coconut::helpers::accepted_vote_err;
|
||||||
use coconut_dkg_common::event_attributes::DKG_PROPOSAL_ID;
|
use coconut_dkg_common::event_attributes::DKG_PROPOSAL_ID;
|
||||||
use coconut_dkg_common::types::{NodeIndex, TOTAL_DEALINGS};
|
use coconut_dkg_common::types::{NodeIndex, TOTAL_DEALINGS};
|
||||||
use coconut_dkg_common::verification_key::owner_from_cosmos_msgs;
|
use coconut_dkg_common::verification_key::owner_from_cosmos_msgs;
|
||||||
@@ -203,21 +204,23 @@ pub(crate) async fn verification_key_validation(
|
|||||||
.iter()
|
.iter()
|
||||||
.position(|node_index| contract_share.node_index == *node_index)
|
.position(|node_index| contract_share.node_index == *node_index)
|
||||||
{
|
{
|
||||||
if !check_vk_pairing(¶ms, &recovered_partials[idx], &vk) {
|
let ret = if !check_vk_pairing(¶ms, &recovered_partials[idx], &vk) {
|
||||||
dkg_client
|
dkg_client
|
||||||
.vote_verification_key_share(proposal_id, false)
|
.vote_verification_key_share(proposal_id, false)
|
||||||
.await?;
|
.await
|
||||||
} else {
|
} else {
|
||||||
dkg_client
|
dkg_client
|
||||||
.vote_verification_key_share(proposal_id, true)
|
.vote_verification_key_share(proposal_id, true)
|
||||||
.await?;
|
.await
|
||||||
}
|
};
|
||||||
|
accepted_vote_err(ret)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
dkg_client
|
let ret = dkg_client
|
||||||
.vote_verification_key_share(proposal_id, false)
|
.vote_verification_key_share(proposal_id, false)
|
||||||
.await?
|
.await;
|
||||||
|
accepted_vote_err(ret)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
use crate::coconut::error::CoconutError;
|
||||||
|
use validator_client::nyxd::error::NyxdError::AbciError;
|
||||||
|
|
||||||
|
// If the result is already established, the vote might be redundant and
|
||||||
|
// thus the transaction might fail
|
||||||
|
pub(crate) fn accepted_vote_err(ret: Result<(), CoconutError>) -> Result<(), CoconutError> {
|
||||||
|
if let Err(CoconutError::NyxdError(AbciError { ref log, .. })) = ret {
|
||||||
|
let accepted_err = multisig_contract_common::error::ContractError::NotOpen {}.to_string();
|
||||||
|
// If redundant voting is not the case, error out on all other error variants
|
||||||
|
if !log.value().contains(&accepted_err) {
|
||||||
|
ret?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
@@ -5,6 +5,7 @@ use self::comm::APICommunicationChannel;
|
|||||||
use crate::coconut::client::Client as LocalClient;
|
use crate::coconut::client::Client as LocalClient;
|
||||||
use crate::coconut::deposit::extract_encryption_key;
|
use crate::coconut::deposit::extract_encryption_key;
|
||||||
use crate::coconut::error::{CoconutError, Result};
|
use crate::coconut::error::{CoconutError, Result};
|
||||||
|
use crate::coconut::helpers::accepted_vote_err;
|
||||||
use crate::support::storage::NymApiStorage;
|
use crate::support::storage::NymApiStorage;
|
||||||
use coconut_bandwidth_contract_common::spend_credential::{
|
use coconut_bandwidth_contract_common::spend_credential::{
|
||||||
funds_from_cosmos_msgs, SpendCredentialStatus,
|
funds_from_cosmos_msgs, SpendCredentialStatus,
|
||||||
@@ -40,6 +41,7 @@ pub(crate) mod comm;
|
|||||||
mod deposit;
|
mod deposit;
|
||||||
pub(crate) mod dkg;
|
pub(crate) mod dkg;
|
||||||
pub(crate) mod error;
|
pub(crate) mod error;
|
||||||
|
pub(crate) mod helpers;
|
||||||
pub(crate) mod keypair;
|
pub(crate) mod keypair;
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub(crate) mod tests;
|
pub(crate) mod tests;
|
||||||
@@ -301,7 +303,7 @@ pub async fn verify_bandwidth_credential(
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Vote yes or no on the proposal based on the verification result
|
// Vote yes or no on the proposal based on the verification result
|
||||||
state
|
let ret = state
|
||||||
.client
|
.client
|
||||||
.vote_proposal(
|
.vote_proposal(
|
||||||
proposal_id,
|
proposal_id,
|
||||||
@@ -312,7 +314,8 @@ pub async fn verify_bandwidth_credential(
|
|||||||
Some(verify_credential_body.gateway_cosmos_addr().to_owned()),
|
Some(verify_credential_body.gateway_cosmos_addr().to_owned()),
|
||||||
)),
|
)),
|
||||||
)
|
)
|
||||||
.await?;
|
.await;
|
||||||
|
accepted_vote_err(ret)?;
|
||||||
|
|
||||||
Ok(Json(VerifyCredentialResponse::new(vote_yes)))
|
Ok(Json(VerifyCredentialResponse::new(vote_yes)))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ pub(crate) struct CliArgs {
|
|||||||
|
|
||||||
/// Id of the nym-api we want to run
|
/// Id of the nym-api we want to run
|
||||||
#[clap(long)]
|
#[clap(long)]
|
||||||
pub(crate) id: Option<String>,
|
pub(crate) id: String,
|
||||||
|
|
||||||
/// Specifies whether network monitoring is enabled on this API
|
/// Specifies whether network monitoring is enabled on this API
|
||||||
#[clap(short = 'm', long)]
|
#[clap(short = 'm', long)]
|
||||||
@@ -102,12 +102,13 @@ pub(crate) struct CliArgs {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn build_config(args: CliArgs) -> Result<Config> {
|
pub(crate) fn build_config(args: CliArgs) -> Result<Config> {
|
||||||
|
let id = args.id.clone();
|
||||||
|
|
||||||
// try to load config from the file, if it doesn't exist, use default values
|
// try to load config from the file, if it doesn't exist, use default values
|
||||||
let id = args.id.as_deref();
|
let (config_from_file, already_initialized) = match Config::load_from_file(&id) {
|
||||||
let (config_from_file, _already_initialized) = match Config::load_from_file(id) {
|
|
||||||
Ok(cfg) => (cfg, true),
|
Ok(cfg) => (cfg, true),
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
let config_path = Config::default_config_file_path(id)
|
let config_path = Config::default_config_file_path(&id)
|
||||||
.into_os_string()
|
.into_os_string()
|
||||||
.into_string()
|
.into_string()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
@@ -121,23 +122,20 @@ pub(crate) fn build_config(args: CliArgs) -> Result<Config> {
|
|||||||
|
|
||||||
let config = override_config(config_from_file, args);
|
let config = override_config(config_from_file, args);
|
||||||
|
|
||||||
if !_already_initialized {
|
if already_initialized {
|
||||||
|
fs::create_dir_all(Config::default_config_directory(&id))
|
||||||
|
.expect("Could not create config directory");
|
||||||
|
fs::create_dir_all(Config::default_data_directory(&id))
|
||||||
|
.expect("Could not create data directory");
|
||||||
crate::coconut::dkg::controller::init_keypair(&config)?;
|
crate::coconut::dkg::controller::init_keypair(&config)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(config)
|
Ok(config)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn override_config(mut config: Config, args: CliArgs) -> Config {
|
pub(crate) fn override_config(config: Config, args: CliArgs) -> Config {
|
||||||
if let Some(id) = args.id {
|
|
||||||
fs::create_dir_all(Config::default_config_directory(Some(&id)))
|
|
||||||
.expect("Could not create config directory");
|
|
||||||
fs::create_dir_all(Config::default_data_directory(Some(&id)))
|
|
||||||
.expect("Could not create data directory");
|
|
||||||
config = config.with_id(&id);
|
|
||||||
}
|
|
||||||
|
|
||||||
config
|
config
|
||||||
|
.with_id(&args.id)
|
||||||
.with_optional(Config::with_custom_nyxd_validator, args.nyxd_validator)
|
.with_optional(Config::with_custom_nyxd_validator, args.nyxd_validator)
|
||||||
.with_optional_env(
|
.with_optional_env(
|
||||||
Config::with_custom_mixnet_contract,
|
Config::with_custom_mixnet_contract,
|
||||||
|
|||||||
@@ -201,8 +201,8 @@ pub struct NetworkMonitor {
|
|||||||
impl NetworkMonitor {
|
impl NetworkMonitor {
|
||||||
pub const DB_FILE: &'static str = "credentials_database.db";
|
pub const DB_FILE: &'static str = "credentials_database.db";
|
||||||
|
|
||||||
fn default_credentials_database_path() -> PathBuf {
|
fn default_credentials_database_path(id: &str) -> PathBuf {
|
||||||
Config::default_data_directory(None).join(Self::DB_FILE)
|
Config::default_data_directory(id).join(Self::DB_FILE)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -220,7 +220,7 @@ impl Default for NetworkMonitor {
|
|||||||
gateway_response_timeout: DEFAULT_GATEWAY_RESPONSE_TIMEOUT,
|
gateway_response_timeout: DEFAULT_GATEWAY_RESPONSE_TIMEOUT,
|
||||||
gateway_connection_timeout: DEFAULT_GATEWAY_CONNECTION_TIMEOUT,
|
gateway_connection_timeout: DEFAULT_GATEWAY_CONNECTION_TIMEOUT,
|
||||||
packet_delivery_timeout: DEFAULT_PACKET_DELIVERY_TIMEOUT,
|
packet_delivery_timeout: DEFAULT_PACKET_DELIVERY_TIMEOUT,
|
||||||
credentials_database_path: Self::default_credentials_database_path(),
|
credentials_database_path: Default::default(),
|
||||||
test_routes: DEFAULT_TEST_ROUTES,
|
test_routes: DEFAULT_TEST_ROUTES,
|
||||||
minimum_test_routes: DEFAULT_MINIMUM_TEST_ROUTES,
|
minimum_test_routes: DEFAULT_MINIMUM_TEST_ROUTES,
|
||||||
route_test_packets: DEFAULT_ROUTE_TEST_PACKETS,
|
route_test_packets: DEFAULT_ROUTE_TEST_PACKETS,
|
||||||
@@ -242,15 +242,15 @@ pub struct NodeStatusAPI {
|
|||||||
impl NodeStatusAPI {
|
impl NodeStatusAPI {
|
||||||
pub const DB_FILE: &'static str = "db.sqlite";
|
pub const DB_FILE: &'static str = "db.sqlite";
|
||||||
|
|
||||||
fn default_database_path() -> PathBuf {
|
fn default_database_path(id: &str) -> PathBuf {
|
||||||
Config::default_data_directory(None).join(Self::DB_FILE)
|
Config::default_data_directory(id).join(Self::DB_FILE)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for NodeStatusAPI {
|
impl Default for NodeStatusAPI {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
NodeStatusAPI {
|
NodeStatusAPI {
|
||||||
database_path: Self::default_database_path(),
|
database_path: Default::default(),
|
||||||
caching_interval: DEFAULT_NODE_STATUS_CACHE_INTERVAL,
|
caching_interval: DEFAULT_NODE_STATUS_CACHE_INTERVAL,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -342,24 +342,24 @@ impl CoconutSigner {
|
|||||||
pub const COCONUT_VERIFICATION_KEY_FILE: &'static str = "coconut_verification_key.pem";
|
pub const COCONUT_VERIFICATION_KEY_FILE: &'static str = "coconut_verification_key.pem";
|
||||||
pub const COCONUT_SECRET_KEY_FILE: &'static str = "coconut_secret_key.pem";
|
pub const COCONUT_SECRET_KEY_FILE: &'static str = "coconut_secret_key.pem";
|
||||||
|
|
||||||
fn default_coconut_verification_key_path() -> PathBuf {
|
fn default_coconut_verification_key_path(id: &str) -> PathBuf {
|
||||||
Config::default_data_directory(None).join(Self::COCONUT_VERIFICATION_KEY_FILE)
|
Config::default_data_directory(id).join(Self::COCONUT_VERIFICATION_KEY_FILE)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn default_coconut_secret_key_path() -> PathBuf {
|
fn default_coconut_secret_key_path(id: &str) -> PathBuf {
|
||||||
Config::default_data_directory(None).join(Self::COCONUT_SECRET_KEY_FILE)
|
Config::default_data_directory(id).join(Self::COCONUT_SECRET_KEY_FILE)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn default_dkg_persistent_state_path() -> PathBuf {
|
fn default_dkg_persistent_state_path(id: &str) -> PathBuf {
|
||||||
Config::default_data_directory(None).join(Self::DKG_PERSISTENT_STATE_FILE)
|
Config::default_data_directory(id).join(Self::DKG_PERSISTENT_STATE_FILE)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn default_dkg_decryption_key_path() -> PathBuf {
|
fn default_dkg_decryption_key_path(id: &str) -> PathBuf {
|
||||||
Config::default_data_directory(None).join(Self::DKG_DECRYPTION_KEY_FILE)
|
Config::default_data_directory(id).join(Self::DKG_DECRYPTION_KEY_FILE)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn default_dkg_public_key_with_proof_path() -> PathBuf {
|
fn default_dkg_public_key_with_proof_path(id: &str) -> PathBuf {
|
||||||
Config::default_data_directory(None).join(Self::DKG_PUBLIC_KEY_WITH_PROOF_FILE)
|
Config::default_data_directory(id).join(Self::DKG_PUBLIC_KEY_WITH_PROOF_FILE)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -367,11 +367,11 @@ impl Default for CoconutSigner {
|
|||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
enabled: Default::default(),
|
enabled: Default::default(),
|
||||||
dkg_persistent_state_path: CoconutSigner::default_dkg_persistent_state_path(),
|
dkg_persistent_state_path: Default::default(),
|
||||||
verification_key_path: CoconutSigner::default_coconut_verification_key_path(),
|
verification_key_path: Default::default(),
|
||||||
secret_key_path: CoconutSigner::default_coconut_secret_key_path(),
|
secret_key_path: Default::default(),
|
||||||
decryption_key_path: CoconutSigner::default_dkg_decryption_key_path(),
|
decryption_key_path: Default::default(),
|
||||||
public_key_with_proof_path: CoconutSigner::default_dkg_public_key_with_proof_path(),
|
public_key_with_proof_path: Default::default(),
|
||||||
dkg_contract_polling_rate: DEFAULT_DKG_CONTRACT_POLLING_RATE,
|
dkg_contract_polling_rate: DEFAULT_DKG_CONTRACT_POLLING_RATE,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -384,20 +384,18 @@ impl Config {
|
|||||||
|
|
||||||
pub fn with_id(mut self, id: &str) -> Self {
|
pub fn with_id(mut self, id: &str) -> Self {
|
||||||
self.base.id = id.to_string();
|
self.base.id = id.to_string();
|
||||||
self.node_status_api.database_path =
|
self.node_status_api.database_path = NodeStatusAPI::default_database_path(id);
|
||||||
Config::default_data_directory(Some(id)).join(NodeStatusAPI::DB_FILE);
|
|
||||||
self.network_monitor.credentials_database_path =
|
self.network_monitor.credentials_database_path =
|
||||||
Config::default_data_directory(Some(id)).join(NetworkMonitor::DB_FILE);
|
NetworkMonitor::default_credentials_database_path(id);
|
||||||
self.coconut_signer.dkg_persistent_state_path =
|
self.coconut_signer.dkg_persistent_state_path =
|
||||||
Config::default_data_directory(Some(id)).join(CoconutSigner::DKG_PERSISTENT_STATE_FILE);
|
CoconutSigner::default_dkg_persistent_state_path(id);
|
||||||
self.coconut_signer.verification_key_path = Config::default_data_directory(Some(id))
|
self.coconut_signer.verification_key_path =
|
||||||
.join(CoconutSigner::COCONUT_VERIFICATION_KEY_FILE);
|
CoconutSigner::default_coconut_verification_key_path(id);
|
||||||
self.coconut_signer.secret_key_path =
|
self.coconut_signer.secret_key_path = CoconutSigner::default_coconut_secret_key_path(id);
|
||||||
Config::default_data_directory(Some(id)).join(CoconutSigner::COCONUT_SECRET_KEY_FILE);
|
|
||||||
self.coconut_signer.decryption_key_path =
|
self.coconut_signer.decryption_key_path =
|
||||||
Config::default_data_directory(Some(id)).join(CoconutSigner::DKG_DECRYPTION_KEY_FILE);
|
CoconutSigner::default_dkg_decryption_key_path(id);
|
||||||
self.coconut_signer.public_key_with_proof_path = Config::default_data_directory(Some(id))
|
self.coconut_signer.public_key_with_proof_path =
|
||||||
.join(CoconutSigner::DKG_PUBLIC_KEY_WITH_PROOF_FILE);
|
CoconutSigner::default_dkg_public_key_with_proof_path(id);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,14 @@
|
|||||||
|
|
||||||
## UNRELEASED
|
## UNRELEASED
|
||||||
|
|
||||||
|
## [nym-connect-v1.1.8](https://github.com/nymtech/nym/tree/nym-connect-v1.1.8) (2023-01-31)
|
||||||
|
|
||||||
|
- Add supported apps in the menu + update guide ([#2868])
|
||||||
|
- Copy changes to remove the dropdown: ([#2777])
|
||||||
|
|
||||||
|
[#2868]: https://github.com/nymtech/nym/issues/2868
|
||||||
|
[#2777]: https://github.com/nymtech/nym/issues/2777
|
||||||
|
|
||||||
## [nym-connect-v1.1.7](https://github.com/nymtech/nym/tree/nym-connect-v1.1.7) (2023-01-24)
|
## [nym-connect-v1.1.7](https://github.com/nymtech/nym/tree/nym-connect-v1.1.7) (2023-01-24)
|
||||||
|
|
||||||
- Remove test and earn ([#2865])
|
- Remove test and earn ([#2865])
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@nym/nym-connect",
|
"name": "@nym/nym-connect",
|
||||||
"version": "1.1.7",
|
"version": "1.1.8",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
@@ -43,7 +43,7 @@
|
|||||||
"react-error-boundary": "^3.1.3",
|
"react-error-boundary": "^3.1.3",
|
||||||
"react-hook-form": "^7.14.2",
|
"react-hook-form": "^7.14.2",
|
||||||
"react-markdown": "^8.0.4",
|
"react-markdown": "^8.0.4",
|
||||||
"react-router-dom": "^5.2.0",
|
"react-router-dom": "^6.7.0",
|
||||||
"semver": "^6.3.0",
|
"semver": "^6.3.0",
|
||||||
"yup": "^0.32.9"
|
"yup": "^0.32.9"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "nym-connect"
|
name = "nym-connect"
|
||||||
version = "1.1.7"
|
version = "1.1.8"
|
||||||
description = "nym-connect"
|
description = "nym-connect"
|
||||||
authors = ["Nym Technologies SA"]
|
authors = ["Nym Technologies SA"]
|
||||||
license = ""
|
license = ""
|
||||||
|
|||||||
@@ -93,7 +93,7 @@ impl Config {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn config_file_location(id: &str) -> Result<PathBuf> {
|
pub fn config_file_location(id: &str) -> Result<PathBuf> {
|
||||||
Socks5Config::try_default_config_file_path(Some(id))
|
Socks5Config::try_default_config_file_path(id)
|
||||||
.ok_or(BackendError::CouldNotGetConfigFilename)
|
.ok_or(BackendError::CouldNotGetConfigFilename)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -56,9 +56,9 @@ pub enum BackendError {
|
|||||||
InitializationPanic,
|
InitializationPanic,
|
||||||
#[error("could not get config id before gateway is set")]
|
#[error("could not get config id before gateway is set")]
|
||||||
CouldNotGetIdWithoutGateway,
|
CouldNotGetIdWithoutGateway,
|
||||||
#[error("could initialize without gateway set")]
|
#[error("could not initialize without gateway set")]
|
||||||
CouldNotInitWithoutGateway,
|
CouldNotInitWithoutGateway,
|
||||||
#[error("could initialize without service provider set")]
|
#[error("could not initialize without service provider set")]
|
||||||
CouldNotInitWithoutServiceProvider,
|
CouldNotInitWithoutServiceProvider,
|
||||||
#[error("could not get file name")]
|
#[error("could not get file name")]
|
||||||
CouldNotGetFilename,
|
CouldNotGetFilename,
|
||||||
|
|||||||
@@ -154,7 +154,7 @@ impl State {
|
|||||||
|
|
||||||
pub fn load_socks5_config(&self) -> Result<Socks5Config> {
|
pub fn load_socks5_config(&self) -> Result<Socks5Config> {
|
||||||
let id = self.get_config_id()?;
|
let id = self.get_config_id()?;
|
||||||
let config = Socks5Config::load_from_file(Some(&id))
|
let config = Socks5Config::load_from_file(&id)
|
||||||
.tap_err(|_| log::warn!("Failed to load configuration file"))?;
|
.tap_err(|_| log::warn!("Failed to load configuration file"))?;
|
||||||
Ok(config)
|
Ok(config)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ pub fn start_nym_socks5_client(
|
|||||||
GatewayEndpointConfig,
|
GatewayEndpointConfig,
|
||||||
)> {
|
)> {
|
||||||
log::info!("Loading config from file: {id}");
|
log::info!("Loading config from file: {id}");
|
||||||
let config = Socks5Config::load_from_file(Some(id))
|
let config = Socks5Config::load_from_file(id)
|
||||||
.tap_err(|_| log::warn!("Failed to load configuration file"))?;
|
.tap_err(|_| log::warn!("Failed to load configuration file"))?;
|
||||||
let used_gateway = config.get_base().get_gateway_endpoint().clone();
|
let used_gateway = config.get_base().get_gateway_endpoint().clone();
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"package": {
|
"package": {
|
||||||
"productName": "nym-connect",
|
"productName": "nym-connect",
|
||||||
"version": "1.1.7"
|
"version": "1.1.8"
|
||||||
},
|
},
|
||||||
"build": {
|
"build": {
|
||||||
"distDir": "../dist",
|
"distDir": "../dist",
|
||||||
@@ -19,7 +19,13 @@
|
|||||||
"active": true,
|
"active": true,
|
||||||
"targets": "all",
|
"targets": "all",
|
||||||
"identifier": "net.nymtech.connect",
|
"identifier": "net.nymtech.connect",
|
||||||
"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": [],
|
"resources": [],
|
||||||
"externalBin": [],
|
"externalBin": [],
|
||||||
"copyright": "Copyright © 2021-2022 Nym Technologies SA",
|
"copyright": "Copyright © 2021-2022 Nym Technologies SA",
|
||||||
@@ -44,7 +50,9 @@
|
|||||||
},
|
},
|
||||||
"updater": {
|
"updater": {
|
||||||
"active": true,
|
"active": true,
|
||||||
"endpoints": ["https://nymtech.net/.wellknown/connect/updater.json"],
|
"endpoints": [
|
||||||
|
"https://nymtech.net/.wellknown/connect/updater.json"
|
||||||
|
],
|
||||||
"dialog": true,
|
"dialog": true,
|
||||||
"pubkey": "dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IENCNzQ2M0E5N0VFODE2NApSV1JrZ2U2WE9rYTNETTg1OTBKdE5uWUEra0hML2syOVUvQ2lxZmFZRzZ1T3NWbGM0eVRzUTVhVwo="
|
"pubkey": "dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IENCNzQ2M0E5N0VFODE2NApSV1JrZ2U2WE9rYTNETTg1OTBKdE5uWUEra0hML2syOVUvQ2lxZmFZRzZ1T3NWbGM0eVRzUTVhVwo="
|
||||||
},
|
},
|
||||||
@@ -68,7 +76,7 @@
|
|||||||
{
|
{
|
||||||
"title": "NymConnect",
|
"title": "NymConnect",
|
||||||
"width": 240,
|
"width": 240,
|
||||||
"height": 635,
|
"height": 480,
|
||||||
"resizable": false,
|
"resizable": false,
|
||||||
"decorations": false,
|
"decorations": false,
|
||||||
"transparent": true
|
"transparent": true
|
||||||
|
|||||||
+12
-32
@@ -1,17 +1,15 @@
|
|||||||
import React, { useEffect } from 'react';
|
import React, { useEffect } from 'react';
|
||||||
import { DateTime } from 'luxon';
|
import { DateTime } from 'luxon';
|
||||||
import { forage } from '@tauri-apps/tauri-forage';
|
import { forage } from '@tauri-apps/tauri-forage';
|
||||||
import { ConnectionStatusKind } from './types';
|
|
||||||
import { useClientContext } from './context/main';
|
import { useClientContext } from './context/main';
|
||||||
import { DefaultLayout } from './layouts/DefaultLayout';
|
|
||||||
import { ConnectedLayout } from './layouts/ConnectedLayout';
|
|
||||||
import { HelpGuideLayout } from './layouts/HelpGuideLayout';
|
|
||||||
import { useTauriEvents } from './utils';
|
import { useTauriEvents } from './utils';
|
||||||
|
import { AppRoutes } from './routes';
|
||||||
|
import { Connected } from './pages/connection/Connected';
|
||||||
|
|
||||||
export const App: FCWithChildren = () => {
|
export const App: FCWithChildren = () => {
|
||||||
const context = useClientContext();
|
const context = useClientContext();
|
||||||
const [busy, setBusy] = React.useState<boolean>();
|
const [busy, setBusy] = React.useState<boolean>();
|
||||||
const [showInfoModal, setShowInfoModal] = React.useState(false);
|
|
||||||
useTauriEvents('help://clear-storage', (_event) => {
|
useTauriEvents('help://clear-storage', (_event) => {
|
||||||
console.log('About to clear local storage...');
|
console.log('About to clear local storage...');
|
||||||
// clear local storage
|
// clear local storage
|
||||||
@@ -25,16 +23,16 @@ export const App: FCWithChildren = () => {
|
|||||||
|
|
||||||
const handleConnectClick = React.useCallback(async () => {
|
const handleConnectClick = React.useCallback(async () => {
|
||||||
const currentStatus = context.connectionStatus;
|
const currentStatus = context.connectionStatus;
|
||||||
if (currentStatus === ConnectionStatusKind.connected || currentStatus === ConnectionStatusKind.disconnected) {
|
if (currentStatus === 'connected' || currentStatus === 'disconnected') {
|
||||||
setBusy(true);
|
setBusy(true);
|
||||||
|
|
||||||
// eslint-disable-next-line default-case
|
// eslint-disable-next-line default-case
|
||||||
switch (currentStatus) {
|
switch (currentStatus) {
|
||||||
case ConnectionStatusKind.disconnected:
|
case 'disconnected':
|
||||||
await context.startConnecting();
|
await context.startConnecting();
|
||||||
context.setConnectedSince(DateTime.now());
|
context.setConnectedSince(DateTime.now());
|
||||||
break;
|
break;
|
||||||
case ConnectionStatusKind.connected:
|
case 'connected':
|
||||||
await context.startDisconnecting();
|
await context.startDisconnecting();
|
||||||
context.setConnectedSince(undefined);
|
context.setConnectedSince(undefined);
|
||||||
break;
|
break;
|
||||||
@@ -43,40 +41,22 @@ export const App: FCWithChildren = () => {
|
|||||||
}
|
}
|
||||||
}, [context.connectionStatus]);
|
}, [context.connectionStatus]);
|
||||||
|
|
||||||
useEffect(() => {
|
if (context.connectionStatus === 'disconnected' || context.connectionStatus === 'connecting') {
|
||||||
if (context.connectionStatus === ConnectionStatusKind.connected) setShowInfoModal(true);
|
return <AppRoutes />;
|
||||||
}, [context.connectionStatus]);
|
|
||||||
|
|
||||||
if (context.showHelp) return <HelpGuideLayout />;
|
|
||||||
|
|
||||||
if (
|
|
||||||
context.connectionStatus === ConnectionStatusKind.disconnected ||
|
|
||||||
context.connectionStatus === ConnectionStatusKind.connecting
|
|
||||||
) {
|
|
||||||
return (
|
|
||||||
<DefaultLayout
|
|
||||||
error={context.error}
|
|
||||||
clearError={context.clearError}
|
|
||||||
status={context.connectionStatus}
|
|
||||||
busy={busy}
|
|
||||||
onConnectClick={handleConnectClick}
|
|
||||||
services={context.services}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ConnectedLayout
|
<Connected
|
||||||
showInfoModal={showInfoModal}
|
|
||||||
handleCloseInfoModal={() => setShowInfoModal(false)}
|
|
||||||
status={context.connectionStatus}
|
status={context.connectionStatus}
|
||||||
|
showInfoModal={context.showInfoModal}
|
||||||
|
closeInfoModal={() => context.setShowInfoModal(false)}
|
||||||
busy={busy}
|
busy={busy}
|
||||||
onConnectClick={handleConnectClick}
|
onConnectClick={handleConnectClick}
|
||||||
ipAddress="127.0.0.1"
|
ipAddress="127.0.0.1"
|
||||||
port={1080}
|
port={1080}
|
||||||
gatewayPerformance={context.gatewayPerformance}
|
gatewayPerformance={context.gatewayPerformance}
|
||||||
connectedSince={context.connectedSince}
|
connectedSince={context.connectedSince}
|
||||||
serviceProvider={context.serviceProvider}
|
serviceProvider={context.selectedProvider}
|
||||||
stats={[
|
stats={[
|
||||||
{
|
{
|
||||||
label: 'in:',
|
label: 'in:',
|
||||||
|
|||||||
Binary file not shown.
|
After Width: | Height: | Size: 14 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 36 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 35 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 35 KiB After Width: | Height: | Size: 18 KiB |
@@ -6,8 +6,8 @@ export const AppVersion = () => {
|
|||||||
const { appVersion } = useClientContext();
|
const { appVersion } = useClientContext();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box sx={{ display: 'grid', width: '100%', justifyContent: 'center' }}>
|
<Box sx={{ display: 'flex', width: '100%', justifyContent: 'center' }}>
|
||||||
<Box fontSize="small" sx={{ mb: 4, color: 'grey.600' }}>
|
<Box fontSize="small" sx={{ color: 'grey.600' }}>
|
||||||
Version {appVersion}
|
Version {appVersion}
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
|
|||||||
@@ -1,20 +1,21 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Box } from '@mui/material';
|
import { Box } from '@mui/material';
|
||||||
import { CustomTitleBar } from './CustomTitleBar';
|
import { CustomTitleBar } from './CustomTitleBar';
|
||||||
import { AppVersion } from './AppVersion';
|
import { useLocation, useParams } from 'react-router-dom';
|
||||||
|
|
||||||
export const AppWindowFrame: FCWithChildren = ({ children }) => (
|
export const AppWindowFrame: FCWithChildren = ({ children }) => {
|
||||||
|
const location = useLocation();
|
||||||
|
|
||||||
|
return (
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
display: 'grid',
|
display: 'grid',
|
||||||
borderRadius: '12px',
|
gridTemplateRows: '40px 1fr',
|
||||||
gridTemplateRows: '40px 1fr 30px',
|
|
||||||
height: '100vh',
|
height: '100vh',
|
||||||
overflowY: 'hidden',
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<CustomTitleBar />
|
<CustomTitleBar path={location.pathname} />
|
||||||
<Box style={{ padding: '16px' }}>{children}</Box>
|
<Box style={{ padding: '16px' }}>{children}</Box>
|
||||||
<AppVersion />
|
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
|
};
|
||||||
|
|||||||
@@ -10,13 +10,13 @@ const getStatusFillColor = (status: ConnectionStatusKind, hover: boolean, isErro
|
|||||||
}
|
}
|
||||||
|
|
||||||
switch (status) {
|
switch (status) {
|
||||||
case ConnectionStatusKind.disconnected:
|
case 'disconnected':
|
||||||
if (hover) {
|
if (hover) {
|
||||||
return '#FFFF33';
|
return '#FFFF33';
|
||||||
}
|
}
|
||||||
return '#FFE600';
|
return '#FFE600';
|
||||||
case ConnectionStatusKind.connecting:
|
case 'connecting':
|
||||||
case ConnectionStatusKind.disconnecting:
|
case 'disconnecting':
|
||||||
return '#FFE600';
|
return '#FFE600';
|
||||||
default:
|
default:
|
||||||
// connected
|
// connected
|
||||||
@@ -29,11 +29,11 @@ const getStatusFillColor = (status: ConnectionStatusKind, hover: boolean, isErro
|
|||||||
|
|
||||||
const getStatusText = (status: ConnectionStatusKind, hover: boolean): string => {
|
const getStatusText = (status: ConnectionStatusKind, hover: boolean): string => {
|
||||||
switch (status) {
|
switch (status) {
|
||||||
case ConnectionStatusKind.disconnected:
|
case 'disconnected':
|
||||||
return 'Connect';
|
return 'Connect';
|
||||||
case ConnectionStatusKind.connecting:
|
case 'connecting':
|
||||||
return 'Connecting';
|
return 'Connecting';
|
||||||
case ConnectionStatusKind.disconnecting:
|
case 'disconnecting':
|
||||||
return 'Connected';
|
return 'Connected';
|
||||||
default:
|
default:
|
||||||
// connected
|
// connected
|
||||||
@@ -89,7 +89,7 @@ export const ConnectionButton: FCWithChildren<{
|
|||||||
<circle cx="131" cy="131" r="64" stroke={statusFillColor} strokeWidth="2" />
|
<circle cx="131" cy="131" r="64" stroke={statusFillColor} strokeWidth="2" />
|
||||||
</g>
|
</g>
|
||||||
<circle cx="131" cy="131" r="73.5" stroke={statusFillColor} strokeOpacity="0.5" />
|
<circle cx="131" cy="131" r="73.5" stroke={statusFillColor} strokeOpacity="0.5" />
|
||||||
{status === ConnectionStatusKind.connected && hover ? (
|
{status === 'connected' && hover ? (
|
||||||
<path
|
<path
|
||||||
d="M120.217 119.833C120.217 117.838 121.838 116.217 123.833 116.217H128.5V114H123.833C120.613 114 118 116.613 118 119.833C118 123.053 120.613 125.667 123.833 125.667H128.5V123.45H123.833C121.838 123.45 120.217 121.828 120.217 119.833ZM127 121H136.333V118.667H127V121ZM139.5 114H134.833V116.217H139.505C141.5 116.217 143.117 117.838 143.117 119.833C143.117 121.828 141.495 123.45 139.5 123.45H134.833V125.667H139.5C142.72 125.667 145.333 123.053 145.333 119.833C145.333 116.613 142.72 114 139.5 114Z"
|
d="M120.217 119.833C120.217 117.838 121.838 116.217 123.833 116.217H128.5V114H123.833C120.613 114 118 116.613 118 119.833C118 123.053 120.613 125.667 123.833 125.667H128.5V123.45H123.833C121.838 123.45 120.217 121.828 120.217 119.833ZM127 121H136.333V118.667H127V121ZM139.5 114H134.833V116.217H139.505C141.5 116.217 143.117 117.838 143.117 119.833C143.117 121.828 141.495 123.45 139.5 123.45H134.833V125.667H139.5C142.72 125.667 145.333 123.053 145.333 119.833C145.333 116.613 142.72 114 139.5 114Z"
|
||||||
fill="white"
|
fill="white"
|
||||||
|
|||||||
@@ -3,32 +3,65 @@ import { Box, CircularProgress, Tooltip, Typography } from '@mui/material';
|
|||||||
import { DateTime } from 'luxon';
|
import { DateTime } from 'luxon';
|
||||||
import { ConnectionStatusKind, GatewayPerformance } from '../types';
|
import { ConnectionStatusKind, GatewayPerformance } from '../types';
|
||||||
import { ServiceProvider } from '../types/directory';
|
import { ServiceProvider } from '../types/directory';
|
||||||
import { ServiceProviderInfo } from './ServiceProviderInfo';
|
import { GatwayWarningInfo, ServiceProviderInfo } from './TooltipInfo';
|
||||||
|
import { ErrorOutline, InfoOutlined } from '@mui/icons-material';
|
||||||
|
|
||||||
const FONT_SIZE = '10px';
|
const FONT_SIZE = '14px';
|
||||||
const FONT_WEIGHT = '600';
|
const FONT_WEIGHT = '600';
|
||||||
const FONT_STYLE = 'normal';
|
const FONT_STYLE = 'normal';
|
||||||
|
|
||||||
const ConnectionStatusContent: FCWithChildren<{
|
const ConnectionStatusContent: FCWithChildren<{
|
||||||
status: ConnectionStatusKind;
|
status: ConnectionStatusKind;
|
||||||
}> = ({ status }) => {
|
serviceProvider?: ServiceProvider;
|
||||||
switch (status) {
|
gatewayError: boolean;
|
||||||
case ConnectionStatusKind.connected:
|
}> = ({ status, serviceProvider, gatewayError }) => {
|
||||||
|
if (gatewayError) {
|
||||||
return (
|
return (
|
||||||
<Typography fontWeight={FONT_WEIGHT} fontStyle={FONT_STYLE} fontSize="14px">
|
<Tooltip title={serviceProvider ? <GatwayWarningInfo /> : undefined}>
|
||||||
Connected to
|
<Box
|
||||||
|
display="flex"
|
||||||
|
alignItems="center"
|
||||||
|
gap={0.5}
|
||||||
|
justifyContent="center"
|
||||||
|
sx={{ cursor: 'pointer' }}
|
||||||
|
color="warning.main"
|
||||||
|
>
|
||||||
|
<ErrorOutline sx={{ fontSize: 14 }} />
|
||||||
|
<Typography fontWeight={FONT_WEIGHT} fontStyle={FONT_STYLE} fontSize={FONT_SIZE} textAlign="center">
|
||||||
|
Gateway has issues
|
||||||
|
</Typography>
|
||||||
|
</Box>
|
||||||
|
</Tooltip>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
switch (status) {
|
||||||
|
case 'connected':
|
||||||
|
return (
|
||||||
|
<Tooltip title={serviceProvider ? <ServiceProviderInfo serviceProvider={serviceProvider} /> : undefined}>
|
||||||
|
<Box display="flex" alignItems="center" gap={0.5} justifyContent="center" sx={{ cursor: 'pointer' }}>
|
||||||
|
<InfoOutlined sx={{ fontSize: 14 }} />
|
||||||
|
<Typography fontWeight={FONT_WEIGHT} fontStyle={FONT_STYLE} fontSize={FONT_SIZE} textAlign="center">
|
||||||
|
Connected to Nym Mixnet
|
||||||
|
</Typography>
|
||||||
|
</Box>
|
||||||
|
</Tooltip>
|
||||||
|
);
|
||||||
|
case 'disconnected':
|
||||||
|
return (
|
||||||
|
<Typography fontWeight={FONT_WEIGHT} fontStyle={FONT_STYLE} textAlign="center" fontSize={FONT_SIZE}>
|
||||||
|
Connect to the mixnet
|
||||||
</Typography>
|
</Typography>
|
||||||
);
|
);
|
||||||
case ConnectionStatusKind.disconnecting:
|
case 'disconnecting':
|
||||||
return (
|
return (
|
||||||
<Box display="flex" alignItems="center" justifyContent="center">
|
<Box display="flex" alignItems="center" justifyContent="center">
|
||||||
<CircularProgress size={FONT_SIZE} color="inherit" />
|
<CircularProgress size={FONT_SIZE} color="inherit" />
|
||||||
<Typography fontWeight={FONT_WEIGHT} fontStyle={FONT_STYLE} ml={1}>
|
<Typography fontWeight={FONT_WEIGHT} fontStyle={FONT_STYLE}>
|
||||||
Disconnecting...
|
Disconnecting...
|
||||||
</Typography>
|
</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
case ConnectionStatusKind.connecting:
|
case 'connecting':
|
||||||
return (
|
return (
|
||||||
<Box display="flex" alignItems="center" justifyContent="center">
|
<Box display="flex" alignItems="center" justifyContent="center">
|
||||||
<CircularProgress size={FONT_SIZE} color="inherit" />
|
<CircularProgress size={FONT_SIZE} color="inherit" />
|
||||||
@@ -37,20 +70,7 @@ const ConnectionStatusContent: FCWithChildren<{
|
|||||||
</Typography>
|
</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
case ConnectionStatusKind.disconnected:
|
|
||||||
return (
|
|
||||||
<Typography
|
|
||||||
fontWeight={FONT_WEIGHT}
|
|
||||||
fontStyle={FONT_STYLE}
|
|
||||||
ml={1}
|
|
||||||
textTransform="uppercase"
|
|
||||||
textAlign="center"
|
|
||||||
fontSize={FONT_SIZE}
|
|
||||||
sx={{ wordSpacing: 3, letterSpacing: 2 }}
|
|
||||||
>
|
|
||||||
You are not protected
|
|
||||||
</Typography>
|
|
||||||
);
|
|
||||||
default:
|
default:
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -62,29 +82,18 @@ export const ConnectionStatus: FCWithChildren<{
|
|||||||
connectedSince?: DateTime;
|
connectedSince?: DateTime;
|
||||||
serviceProvider?: ServiceProvider;
|
serviceProvider?: ServiceProvider;
|
||||||
}> = ({ status, serviceProvider, gatewayPerformance }) => {
|
}> = ({ status, serviceProvider, gatewayPerformance }) => {
|
||||||
const color =
|
const color = status === 'connected' || status === 'disconnecting' ? '#21D072' : 'white';
|
||||||
status === ConnectionStatusKind.connected || status === ConnectionStatusKind.disconnecting
|
console.log(gatewayPerformance);
|
||||||
? '#21D072'
|
|
||||||
: 'warning.main';
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Box color={color} fontSize={FONT_SIZE} sx={{ mb: 1 }}>
|
<Box color={color} sx={{ mb: 2 }}>
|
||||||
{status === ConnectionStatusKind.connected && gatewayPerformance !== 'Good' ? (
|
<ConnectionStatusContent
|
||||||
<Typography fontWeight={FONT_WEIGHT} fontStyle={FONT_STYLE} textAlign="left" color="primary">
|
status={status}
|
||||||
Gateway has issues
|
serviceProvider={serviceProvider}
|
||||||
</Typography>
|
gatewayError={gatewayPerformance !== 'Good'}
|
||||||
) : (
|
/>
|
||||||
<ConnectionStatusContent status={status} />
|
|
||||||
)}
|
|
||||||
</Box>
|
</Box>
|
||||||
{serviceProvider ? (
|
|
||||||
<Tooltip title={<ServiceProviderInfo serviceProvider={serviceProvider} />}>
|
|
||||||
<Box sx={{ cursor: 'pointer' }}>
|
|
||||||
{serviceProvider && <Typography>{serviceProvider.description}</Typography>}
|
|
||||||
</Box>
|
|
||||||
</Tooltip>
|
|
||||||
) : null}
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { ArrowBack, Close, HelpOutline, Minimize } from '@mui/icons-material';
|
import { ArrowBack, Close, Menu, Minimize } from '@mui/icons-material';
|
||||||
import { Box, IconButton } from '@mui/material';
|
import { Box, IconButton, Typography } from '@mui/material';
|
||||||
import { NymWordmark } from '@nymproject/react/logo/NymWordmark';
|
import { NymWordmark } from '@nymproject/react/logo/NymWordmark';
|
||||||
import { appWindow } from '@tauri-apps/api/window';
|
import { appWindow } from '@tauri-apps/api/window';
|
||||||
import { useClientContext } from 'src/context/main';
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
|
||||||
const customTitleBarStyles = {
|
const customTitleBarStyles = {
|
||||||
titlebar: {
|
titlebar: {
|
||||||
@@ -24,22 +24,34 @@ const CustomButton = ({ Icon, onClick }: { Icon: React.JSXElementConstructor<any
|
|||||||
</IconButton>
|
</IconButton>
|
||||||
);
|
);
|
||||||
|
|
||||||
export const CustomTitleBar = () => {
|
const MenuIcon = () => {
|
||||||
const { showHelp, handleShowHelp } = useClientContext();
|
const navigate = useNavigate();
|
||||||
|
return <CustomButton Icon={Menu} onClick={() => navigate('/menu')} />;
|
||||||
|
};
|
||||||
|
|
||||||
|
const ArrowBackIcon = () => {
|
||||||
|
const navigate = useNavigate();
|
||||||
|
return <CustomButton Icon={ArrowBack} onClick={() => navigate(-1)} />;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getTitleIcon = (path: string) => {
|
||||||
|
if (path !== '/') {
|
||||||
|
const title = path.split('/').slice(-1);
|
||||||
|
return (
|
||||||
|
<Typography textTransform="capitalize" fontWeight={700}>
|
||||||
|
{title}
|
||||||
|
</Typography>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return <NymWordmark width={36} />;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const CustomTitleBar = ({ path = '/' }: { path?: string }) => {
|
||||||
return (
|
return (
|
||||||
<Box data-tauri-drag-region style={customTitleBarStyles.titlebar}>
|
<Box data-tauri-drag-region style={customTitleBarStyles.titlebar}>
|
||||||
{/* set width to keep logo centered */}
|
{/* set width to keep logo centered */}
|
||||||
<Box sx={{ width: '40px' }}>
|
<Box sx={{ width: '40px' }}>{path === '/' ? <MenuIcon /> : <ArrowBackIcon />}</Box>
|
||||||
<CustomButton
|
{getTitleIcon(path)}
|
||||||
Icon={!showHelp ? HelpOutline : ArrowBack}
|
|
||||||
onClick={() => {
|
|
||||||
handleShowHelp();
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</Box>
|
|
||||||
|
|
||||||
<NymWordmark width={36} />
|
|
||||||
|
|
||||||
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
|
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
|
||||||
<CustomButton Icon={Minimize} onClick={() => appWindow.minimize()} />
|
<CustomButton Icon={Minimize} onClick={() => appWindow.minimize()} />
|
||||||
<CustomButton Icon={Close} onClick={() => appWindow.close()} />
|
<CustomButton Icon={Close} onClick={() => appWindow.close()} />
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { Box, Typography } from '@mui/material';
|
||||||
|
|
||||||
|
export const ExperimentalWarning = () => (
|
||||||
|
<Box sx={{ color: 'grey.600' }}>
|
||||||
|
<Typography fontSize="10px" textAlign="center">
|
||||||
|
This is experimental software.
|
||||||
|
</Typography>
|
||||||
|
<Typography fontSize="10px" textAlign="center">
|
||||||
|
Do not rely on it for strong anonymity (yet).
|
||||||
|
</Typography>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
@@ -25,7 +25,7 @@ export const Wrapper: FCWithChildren<{ disabled: boolean }> = ({ disabled, child
|
|||||||
export const TestAndEarnButtonArea: FCWithChildren = () => {
|
export const TestAndEarnButtonArea: FCWithChildren = () => {
|
||||||
const clientContext = useClientContext();
|
const clientContext = useClientContext();
|
||||||
const context = useTestAndEarnContext();
|
const context = useTestAndEarnContext();
|
||||||
const disabled = clientContext.connectionStatus !== ConnectionStatusKind.connected;
|
const disabled = clientContext.connectionStatus !== 'connected';
|
||||||
const pinger = React.useRef<NodeJS.Timer | null>();
|
const pinger = React.useRef<NodeJS.Timer | null>();
|
||||||
|
|
||||||
const doPing = async () => {
|
const doPing = async () => {
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ export const TestAndEarnPopupContent: FCWithChildren<{
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!connectionStatus || connectionStatus === ConnectionStatusKind.disconnected) {
|
if (!connectionStatus || connectionStatus === 'disconnected') {
|
||||||
return (
|
return (
|
||||||
<Box p={4}>
|
<Box p={4}>
|
||||||
<ContentNotAvailable />
|
<ContentNotAvailable />
|
||||||
@@ -36,7 +36,7 @@ export const TestAndEarnPopupContent: FCWithChildren<{
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (connectionStatus === ConnectionStatusKind.connecting || connectionStatus === ConnectionStatusKind.disconnecting) {
|
if (connectionStatus === 'connecting' || connectionStatus === 'disconnecting') {
|
||||||
return (
|
return (
|
||||||
<Box p={4} justifyContent="center" alignItems="center" display="flex">
|
<Box p={4} justifyContent="center" alignItems="center" display="flex">
|
||||||
<CircularProgress />
|
<CircularProgress />
|
||||||
@@ -77,7 +77,7 @@ export const TestAndEarnPopup: FCWithChildren = () => {
|
|||||||
const context = useTestAndEarnContext();
|
const context = useTestAndEarnContext();
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
if (clientContext.connectionStatus === ConnectionStatusKind.connected) {
|
if (clientContext.connectionStatus === 'connected') {
|
||||||
context.refresh();
|
context.refresh();
|
||||||
}
|
}
|
||||||
}, [clientContext.connectionStatus]);
|
}, [clientContext.connectionStatus]);
|
||||||
@@ -94,7 +94,7 @@ export const TestAndEarnPopup: FCWithChildren = () => {
|
|||||||
return () => clearInterval(interval);
|
return () => clearInterval(interval);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
if (!context.loadedOnce && clientContext.connectionStatus === ConnectionStatusKind.connected) {
|
if (!context.loadedOnce && clientContext.connectionStatus === 'connected') {
|
||||||
const message = 'Waiting for data to be transferred over the mixnet...';
|
const message = 'Waiting for data to be transferred over the mixnet...';
|
||||||
return (
|
return (
|
||||||
<Box p={4}>
|
<Box p={4}>
|
||||||
|
|||||||
@@ -151,7 +151,7 @@ export const TestAndEarnContextProvider: FCWithChildren = ({ children }) => {
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
if (registration && clientContext.connectionStatus === ConnectionStatusKind.connected) {
|
if (registration && clientContext.connectionStatus === 'connected') {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
loadDraws().catch(console.error);
|
loadDraws().catch(console.error);
|
||||||
}, 1000 * 3);
|
}, 1000 * 3);
|
||||||
|
|||||||
@@ -6,24 +6,26 @@ import { StepIndicator } from './HelpPageStepIndicator';
|
|||||||
|
|
||||||
export const HelpPage = ({
|
export const HelpPage = ({
|
||||||
step,
|
step,
|
||||||
|
totalSteps,
|
||||||
description,
|
description,
|
||||||
img,
|
img,
|
||||||
onNext,
|
onNext,
|
||||||
onPrev,
|
onPrev,
|
||||||
}: {
|
}: {
|
||||||
step: number;
|
step: number;
|
||||||
|
totalSteps: number;
|
||||||
description: string;
|
description: string;
|
||||||
img: any;
|
img: any;
|
||||||
onNext?: () => void;
|
onNext?: () => void;
|
||||||
onPrev?: () => void;
|
onPrev?: () => void;
|
||||||
}) => (
|
}) => (
|
||||||
<Stack justifyContent="space-between" sx={{ height: '100%' }}>
|
<Stack justifyContent="space-between" sx={{ height: '100%' }}>
|
||||||
<Stack gap={3}>
|
<Stack gap={2}>
|
||||||
<StepIndicator step={step} />
|
<StepIndicator step={step} />
|
||||||
<Typography variant="body2" color="white" fontWeight="bold">
|
<Typography variant="body2" color="white" fontWeight="bold">
|
||||||
How to connect guide {step}/4
|
How to connect guide {step}/{totalSteps}
|
||||||
</Typography>
|
</Typography>
|
||||||
<Typography variant="body2" sx={{ color: 'grey.400' }}>
|
<Typography variant="body2" sx={{ color: 'grey.400' }} textAlign="left">
|
||||||
{description}
|
{description}
|
||||||
</Typography>
|
</Typography>
|
||||||
<HelpImage img={img} imageDescription="select a provider" />
|
<HelpImage img={img} imageDescription="select a provider" />
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import { Box } from '@mui/material';
|
||||||
|
|
||||||
export const HelpImage = ({ img, imageDescription }: { img: any; imageDescription: string }) => (
|
export const HelpImage = ({ img, imageDescription }: { img: string; imageDescription: string }) => (
|
||||||
<img src={img} alt={imageDescription} />
|
<img src={img} alt={imageDescription} width="100%" />
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,15 +1,14 @@
|
|||||||
import { Box } from '@mui/material';
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import { Box } from '@mui/material';
|
||||||
|
|
||||||
const Step = ({ highlight }: { highlight: boolean }) => (
|
const Step = ({ highlight }: { highlight: boolean }) => (
|
||||||
<Box sx={{ width: '48px', height: '1px', bgcolor: highlight ? 'nym.highlight' : 'grey.600' }} />
|
<Box sx={{ width: '65px', height: '1px', bgcolor: highlight ? 'nym.highlight' : 'grey.600' }} />
|
||||||
);
|
);
|
||||||
|
|
||||||
export const StepIndicator = ({ step }: { step: number }) => (
|
export const StepIndicator = ({ step }: { step: number }) => (
|
||||||
<Box sx={{ display: 'flex', alignItems: 'center', justifyContent: 'space-evenly' }}>
|
<Box sx={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
|
||||||
<Step highlight />
|
<Step highlight />
|
||||||
<Step highlight={step >= 2} />
|
<Step highlight={step >= 2} />
|
||||||
<Step highlight={step >= 3} />
|
<Step highlight={step >= 3} />
|
||||||
<Step highlight={step >= 4} />
|
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ const styles = {
|
|||||||
const ModalTitle = ({ title, withCloseIcon }: { title: string; withCloseIcon: boolean }) => (
|
const ModalTitle = ({ title, withCloseIcon }: { title: string; withCloseIcon: boolean }) => (
|
||||||
<Box textAlign="center" mt={withCloseIcon ? -2 : 0}>
|
<Box textAlign="center" mt={withCloseIcon ? -2 : 0}>
|
||||||
<ErrorOutline sx={{ color: 'warning.main' }} />
|
<ErrorOutline sx={{ color: 'warning.main' }} />
|
||||||
<Typography variant="body2" textAlign="center" sx={{ color: 'warning.main' }}>
|
<Typography variant="body2" textAlign="center" sx={{ color: 'warning.main', my: 1 }}>
|
||||||
{title}
|
{title}
|
||||||
</Typography>
|
</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
@@ -27,7 +27,7 @@ const ModalTitle = ({ title, withCloseIcon }: { title: string; withCloseIcon: bo
|
|||||||
const ModalBody = ({ description, children }: { description: string; children?: React.ReactElement }) => (
|
const ModalBody = ({ description, children }: { description: string; children?: React.ReactElement }) => (
|
||||||
<Box textAlign="center" mt={1}>
|
<Box textAlign="center" mt={1}>
|
||||||
{children}
|
{children}
|
||||||
<Typography fontSize="small" sx={{ mt: 1, overflowWrap: 'anywhere', color: 'grey.300' }}>
|
<Typography fontSize="small" sx={{ mt: 1, overflowWrap: 'anywhere', color: 'grey.400' }}>
|
||||||
{description}
|
{description}
|
||||||
</Typography>
|
</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
|
|||||||
@@ -3,6 +3,9 @@ import { Box, Button, Typography } from '@mui/material';
|
|||||||
import { InfoModal } from './InfoModal';
|
import { InfoModal } from './InfoModal';
|
||||||
import { CopyToClipboard } from './CopyToClipboard';
|
import { CopyToClipboard } from './CopyToClipboard';
|
||||||
|
|
||||||
|
const FONT_SIZE = '12px';
|
||||||
|
const FONT_COLOR = 'grey.400';
|
||||||
|
|
||||||
export const IpAddressAndPortModal = ({
|
export const IpAddressAndPortModal = ({
|
||||||
show,
|
show,
|
||||||
ipAddress,
|
ipAddress,
|
||||||
@@ -16,12 +19,15 @@ export const IpAddressAndPortModal = ({
|
|||||||
}) => (
|
}) => (
|
||||||
<InfoModal
|
<InfoModal
|
||||||
show={show}
|
show={show}
|
||||||
title="Almost there"
|
title="You are half way there"
|
||||||
description="Copy these values to the proxy settings in your application"
|
description="Check NymConnect menu for supported apps"
|
||||||
Action={<Button onClick={onClose}>Done</Button>}
|
onClose={onClose}
|
||||||
>
|
>
|
||||||
<Box sx={{ mt: 1 }}>
|
<Box sx={{ mt: 1 }}>
|
||||||
<Typography fontSize="14px" sx={{ color: 'grey.600' }}>
|
<Typography fontSize={FONT_SIZE} color={FONT_COLOR} sx={{ my: 2 }}>
|
||||||
|
Paste below values in the proxy settings of your app
|
||||||
|
</Typography>
|
||||||
|
<Typography fontSize={FONT_SIZE} sx={{ color: 'grey.600' }}>
|
||||||
Socks5 address
|
Socks5 address
|
||||||
</Typography>
|
</Typography>
|
||||||
<Box sx={{ display: 'flex', justifyContent: 'center', alignItems: 'center' }}>
|
<Box sx={{ display: 'flex', justifyContent: 'center', alignItems: 'center' }}>
|
||||||
@@ -29,7 +35,7 @@ export const IpAddressAndPortModal = ({
|
|||||||
<CopyToClipboard text={ipAddress} iconButton light />
|
<CopyToClipboard text={ipAddress} iconButton light />
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<Typography fontSize="14px" sx={{ color: 'grey.600', mt: 2 }}>
|
<Typography fontSize={FONT_SIZE} sx={{ color: 'grey.600', mt: 2 }}>
|
||||||
Port
|
Port
|
||||||
</Typography>
|
</Typography>
|
||||||
<Box sx={{ display: 'flex', justifyContent: 'center', alignItems: 'center' }}>
|
<Box sx={{ display: 'flex', justifyContent: 'center', alignItems: 'center' }}>
|
||||||
|
|||||||
@@ -0,0 +1,132 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { ConnectionStatusKind } from 'src/types';
|
||||||
|
|
||||||
|
const getStatusFillColor = (status: ConnectionStatusKind, hover: boolean, isError: boolean): string => {
|
||||||
|
if (isError && hover) {
|
||||||
|
return '#21D072';
|
||||||
|
}
|
||||||
|
if (isError) {
|
||||||
|
return '#40475C';
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (status) {
|
||||||
|
case 'disconnected':
|
||||||
|
if (hover) {
|
||||||
|
return '#FFF';
|
||||||
|
}
|
||||||
|
return '#BBB';
|
||||||
|
case 'connecting':
|
||||||
|
return '#FFF';
|
||||||
|
case 'disconnecting':
|
||||||
|
return '#FFF';
|
||||||
|
default:
|
||||||
|
// connected
|
||||||
|
if (hover) {
|
||||||
|
return '#E43E3E';
|
||||||
|
}
|
||||||
|
return '#21D072';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const PowerButton: FCWithChildren<{
|
||||||
|
onClick?: (status: ConnectionStatusKind) => void;
|
||||||
|
isError?: boolean;
|
||||||
|
disabled?: boolean;
|
||||||
|
status: ConnectionStatusKind;
|
||||||
|
busy?: boolean;
|
||||||
|
}> = ({ onClick, disabled, status, isError }) => {
|
||||||
|
const [hover, setHover] = React.useState<boolean>(false);
|
||||||
|
|
||||||
|
const handleClick = () => {
|
||||||
|
if (disabled === true) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (onClick) {
|
||||||
|
onClick(status);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const statusFillColor = getStatusFillColor(status, hover, Boolean(isError));
|
||||||
|
|
||||||
|
return (
|
||||||
|
<svg
|
||||||
|
width="190"
|
||||||
|
height="190"
|
||||||
|
viewBox="0 0 200 200"
|
||||||
|
fill="none"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
onClick={handleClick}
|
||||||
|
style={{ cursor: disabled ? 'not-allowed' : 'pointer' }}
|
||||||
|
onMouseEnter={() => !disabled && setHover(true)}
|
||||||
|
onMouseLeave={() => !disabled && setHover(false)}
|
||||||
|
>
|
||||||
|
<g transform="translate(-30, -25) ">
|
||||||
|
<circle cx={131} cy={131} r={70} strokeWidth={2} stroke={statusFillColor} filter="url(#blur)" opacity="0.6" />
|
||||||
|
<circle cx={131} cy={131} r={22} strokeWidth={1} stroke={statusFillColor} filter="url(#blur)" opacity="0.3" />
|
||||||
|
<circle opacity={0.6} cx={131} cy={131} r={68.5} stroke={statusFillColor} />
|
||||||
|
<g filter="url(#filter1_d_944_9033)">
|
||||||
|
<circle cx={131} cy={131} r={64} fill="url(#paint1_radial_944_9033)" />
|
||||||
|
<circle cx={131} cy={131} r={63} stroke={statusFillColor} strokeWidth={2} />
|
||||||
|
</g>
|
||||||
|
<g opacity={0.5} filter="url(#filter2_f_944_9033)">
|
||||||
|
<g clipPath="url(#clip0_944_9033)">
|
||||||
|
<path
|
||||||
|
d="M131 113C129.9 113 129 113.9 129 115V131C129 132.1 129.9 133 131 133C132.1 133 133 132.1 133 131V115C133 113.9 132.1 113 131 113ZM141.28 118.72C140.5 119.5 140.52 120.72 141.26 121.5C143.52 123.9 144.92 127.1 145 130.64C145.18 138.3 138.84 144.9 131.18 144.98C123.36 145.1 117 138.8 117 131C117 127.32 118.42 123.98 120.74 121.48C121.48 120.7 121.48 119.48 120.72 118.72C119.92 117.92 118.62 117.94 117.86 118.76C114.96 121.84 113.14 125.94 113 130.48C112.72 140.24 120.66 148.68 130.42 148.98C140.62 149.3 149 141.12 149 130.98C149 126.24 147.16 121.96 144.16 118.76C143.4 117.94 142.08 117.92 141.28 118.72Z"
|
||||||
|
stroke={statusFillColor}
|
||||||
|
/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g clipPath="url(#clip1_944_9033)">
|
||||||
|
<path
|
||||||
|
d="M131 113C129.9 113 129 113.9 129 115V131C129 132.1 129.9 133 131 133C132.1 133 133 132.1 133 131V115C133 113.9 132.1 113 131 113ZM141.28 118.72C140.5 119.5 140.52 120.72 141.26 121.5C143.52 123.9 144.92 127.1 145 130.64C145.18 138.3 138.84 144.9 131.18 144.98C123.36 145.1 117 138.8 117 131C117 127.32 118.42 123.98 120.74 121.48C121.48 120.7 121.48 119.48 120.72 118.72C119.92 117.92 118.62 117.94 117.86 118.76C114.96 121.84 113.14 125.94 113 130.48C112.72 140.24 120.66 148.68 130.42 148.98C140.62 149.3 149 141.12 149 130.98C149 126.24 147.16 121.96 144.16 118.76C143.4 117.94 142.08 117.92 141.28 118.72Z"
|
||||||
|
fill={statusFillColor}
|
||||||
|
/>
|
||||||
|
</g>
|
||||||
|
<defs>
|
||||||
|
<filter
|
||||||
|
id="filter0_f_944_9033"
|
||||||
|
x={0}
|
||||||
|
y={0}
|
||||||
|
width={240}
|
||||||
|
height={240}
|
||||||
|
filterUnits="userSpaceOnUse"
|
||||||
|
colorInterpolationFilters="sRGB"
|
||||||
|
>
|
||||||
|
<feFlood floodOpacity={0} result="BackgroundImageFix" />
|
||||||
|
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape" />
|
||||||
|
<feGaussianBlur stdDeviation={40} result="effect1_foregroundBlur_944_9033" />
|
||||||
|
</filter>
|
||||||
|
<filter
|
||||||
|
id="filter1_d_944_9033"
|
||||||
|
x={52}
|
||||||
|
y={58}
|
||||||
|
width={158}
|
||||||
|
height={158}
|
||||||
|
filterUnits="userSpaceOnUse"
|
||||||
|
colorInterpolationFilters="sRGB"
|
||||||
|
>
|
||||||
|
<feFlood floodOpacity={0} result="BackgroundImageFix" />
|
||||||
|
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_944_9033" />
|
||||||
|
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_944_9033" result="shape" />
|
||||||
|
</filter>
|
||||||
|
<filter
|
||||||
|
id="filter2_f_944_9033"
|
||||||
|
x={97}
|
||||||
|
y={97}
|
||||||
|
width={68}
|
||||||
|
height={68}
|
||||||
|
filterUnits="userSpaceOnUse"
|
||||||
|
colorInterpolationFilters="sRGB"
|
||||||
|
>
|
||||||
|
<feFlood floodOpacity={0} result="BackgroundImageFix" />
|
||||||
|
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape" />
|
||||||
|
<feGaussianBlur stdDeviation={5} result="effect1_foregroundBlur_944_9033" />
|
||||||
|
</filter>
|
||||||
|
<filter id="blur">
|
||||||
|
<feGaussianBlur stdDeviation="5" />
|
||||||
|
</filter>
|
||||||
|
</defs>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
};
|
||||||
+11
-2
@@ -7,14 +7,23 @@ export const ServiceProviderInfo = ({ serviceProvider }: { serviceProvider: Serv
|
|||||||
<Typography variant="body2" fontWeight="bold">
|
<Typography variant="body2" fontWeight="bold">
|
||||||
Connection info
|
Connection info
|
||||||
</Typography>
|
</Typography>
|
||||||
<Typography variant="caption">{serviceProvider.description}</Typography>
|
|
||||||
<Divider />
|
<Divider />
|
||||||
<Typography variant="caption" fontWeight="bold">
|
<Typography variant="caption" fontWeight="bold">
|
||||||
Gateway <Typography variant="caption">{serviceProvider.gateway}</Typography>
|
Gateway <Typography variant="caption">{serviceProvider.gateway}</Typography>
|
||||||
</Typography>
|
</Typography>
|
||||||
<Divider />
|
<Divider />
|
||||||
<Typography variant="caption" fontWeight="bold">
|
<Typography variant="caption" fontWeight="bold">
|
||||||
Provider <Typography variant="caption">{serviceProvider.address.slice(0, 35)}...</Typography>
|
Service provider <Typography variant="caption">{serviceProvider.address.slice(0, 35)}...</Typography>
|
||||||
</Typography>
|
</Typography>
|
||||||
</Stack>
|
</Stack>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
export const GatwayWarningInfo = () => (
|
||||||
|
<Stack gap={1} sx={{ wordWrap: 'break-word', maxWidth: 150, p: 1 }}>
|
||||||
|
<Typography variant="body2" fontWeight="bold" color="warning.main">
|
||||||
|
Connection issue
|
||||||
|
</Typography>
|
||||||
|
<Divider />
|
||||||
|
<Typography variant="caption">Try disconnecting and connecting again</Typography>
|
||||||
|
</Stack>
|
||||||
|
);
|
||||||
@@ -21,19 +21,17 @@ export type TClientContext = {
|
|||||||
connectionStatus: ConnectionStatusKind;
|
connectionStatus: ConnectionStatusKind;
|
||||||
connectionStats?: ConnectionStatsItem[];
|
connectionStats?: ConnectionStatsItem[];
|
||||||
connectedSince?: DateTime;
|
connectedSince?: DateTime;
|
||||||
services?: Services;
|
|
||||||
serviceProvider?: ServiceProvider;
|
|
||||||
showHelp: boolean;
|
|
||||||
error?: Error;
|
error?: Error;
|
||||||
gatewayPerformance: GatewayPerformance;
|
gatewayPerformance: GatewayPerformance;
|
||||||
|
selectedProvider?: ServiceProvider;
|
||||||
|
showInfoModal: boolean;
|
||||||
setMode: (mode: ModeType) => void;
|
setMode: (mode: ModeType) => void;
|
||||||
clearError: () => void;
|
clearError: () => void;
|
||||||
handleShowHelp: () => void;
|
|
||||||
setConnectionStatus: (connectionStatus: ConnectionStatusKind) => void;
|
setConnectionStatus: (connectionStatus: ConnectionStatusKind) => void;
|
||||||
setConnectionStats: (connectionStats: ConnectionStatsItem[] | undefined) => void;
|
setConnectionStats: (connectionStats: ConnectionStatsItem[] | undefined) => void;
|
||||||
setConnectedSince: (connectedSince: DateTime | undefined) => void;
|
setConnectedSince: (connectedSince: DateTime | undefined) => void;
|
||||||
setServiceProvider: (serviceProvider?: ServiceProvider) => void;
|
setShowInfoModal: (show: boolean) => void;
|
||||||
|
setRandomSerivceProvider: () => void;
|
||||||
startConnecting: () => Promise<void>;
|
startConnecting: () => Promise<void>;
|
||||||
startDisconnecting: () => Promise<void>;
|
startDisconnecting: () => Promise<void>;
|
||||||
};
|
};
|
||||||
@@ -42,28 +40,40 @@ export const ClientContext = createContext({} as TClientContext);
|
|||||||
|
|
||||||
export const ClientContextProvider: FCWithChildren = ({ children }) => {
|
export const ClientContextProvider: FCWithChildren = ({ children }) => {
|
||||||
const [mode, setMode] = useState<ModeType>('dark');
|
const [mode, setMode] = useState<ModeType>('dark');
|
||||||
const [connectionStatus, setConnectionStatus] = useState<ConnectionStatusKind>(ConnectionStatusKind.disconnected);
|
const [connectionStatus, setConnectionStatus] = useState<ConnectionStatusKind>(ConnectionStatusKind.connected);
|
||||||
const [connectionStats, setConnectionStats] = useState<ConnectionStatsItem[]>();
|
const [connectionStats, setConnectionStats] = useState<ConnectionStatsItem[]>();
|
||||||
const [connectedSince, setConnectedSince] = useState<DateTime>();
|
const [connectedSince, setConnectedSince] = useState<DateTime>();
|
||||||
const [services, setServices] = React.useState<Services>([]);
|
const [selectedProvider, setSelectedProvider] = React.useState<ServiceProvider>();
|
||||||
const [serviceProvider, setRawServiceProvider] = React.useState<ServiceProvider>();
|
const [serviceProviders, setServiceProviders] = React.useState<ServiceProvider[]>();
|
||||||
const [showHelp, setShowHelp] = useState(false);
|
|
||||||
const [error, setError] = useState<Error>();
|
const [error, setError] = useState<Error>();
|
||||||
const [appVersion, setAppVersion] = useState<string>();
|
const [appVersion, setAppVersion] = useState<string>();
|
||||||
const [gatewayPerformance, setGatewayPerformance] = useState<GatewayPerformance>('Good');
|
const [gatewayPerformance, setGatewayPerformance] = useState<GatewayPerformance>('Good');
|
||||||
|
const [showInfoModal, setShowInfoModal] = useState(false);
|
||||||
|
|
||||||
const getAppVersion = async () => {
|
const getAppVersion = async () => {
|
||||||
const version = await getVersion();
|
const version = await getVersion();
|
||||||
setAppVersion(version);
|
return version;
|
||||||
};
|
};
|
||||||
|
|
||||||
const timerId = useRef<NodeJS.Timeout>();
|
const timerId = useRef<NodeJS.Timeout>();
|
||||||
|
|
||||||
|
const flattenProviders = (services: Services) => {
|
||||||
|
return services.reduce((a: ServiceProvider[], b) => {
|
||||||
|
return [...a, ...b.items];
|
||||||
|
}, []);
|
||||||
|
};
|
||||||
|
|
||||||
|
const initialiseApp = async () => {
|
||||||
|
const services = await invoke('get_services');
|
||||||
|
const allServiceProviders = flattenProviders(services as Services);
|
||||||
|
const AppVersion = await getAppVersion();
|
||||||
|
|
||||||
|
setAppVersion(AppVersion);
|
||||||
|
setServiceProviders(allServiceProviders);
|
||||||
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
invoke('get_services').then((result) => {
|
initialiseApp();
|
||||||
setServices(result as Services);
|
|
||||||
});
|
|
||||||
getAppVersion();
|
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -129,12 +139,18 @@ export const ClientContextProvider: FCWithChildren = ({ children }) => {
|
|||||||
const startDisconnecting = useCallback(async () => {
|
const startDisconnecting = useCallback(async () => {
|
||||||
try {
|
try {
|
||||||
await invoke('start_disconnecting');
|
await invoke('start_disconnecting');
|
||||||
setGatewayPerformance('Good');
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log(e);
|
console.log(e);
|
||||||
}
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
const setServiceProvider = async (newServiceProvider?: ServiceProvider) => {
|
||||||
|
if (newServiceProvider) {
|
||||||
|
await invoke('set_gateway', { gateway: newServiceProvider.gateway });
|
||||||
|
await invoke('set_service_provider', { serviceProvider: newServiceProvider.address });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const setSpInStorage = async (sp: ServiceProvider) => {
|
const setSpInStorage = async (sp: ServiceProvider) => {
|
||||||
await forage.setItem({
|
await forage.setItem({
|
||||||
key: 'nym-connect-sp',
|
key: 'nym-connect-sp',
|
||||||
@@ -142,50 +158,35 @@ export const ClientContextProvider: FCWithChildren = ({ children }) => {
|
|||||||
} as any)();
|
} as any)();
|
||||||
};
|
};
|
||||||
|
|
||||||
const setServiceProvider = useCallback(async (newServiceProvider?: ServiceProvider) => {
|
const removeSpFromStorage = async () => {
|
||||||
await invoke('set_gateway', { gateway: newServiceProvider?.gateway });
|
await forage.removeItem({
|
||||||
await invoke('set_service_provider', { serviceProvider: newServiceProvider?.address });
|
key: 'nym-connect-sp',
|
||||||
if (newServiceProvider) {
|
})();
|
||||||
await setSpInStorage(newServiceProvider);
|
};
|
||||||
}
|
|
||||||
setRawServiceProvider(newServiceProvider);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const getSpFromStorage = async () => {
|
const getSpFromStorage = async (): Promise<ServiceProvider | undefined> => {
|
||||||
try {
|
try {
|
||||||
const spFromStorage = await forage.getItem({ key: 'nym-connect-sp' })();
|
const spFromStorage = await forage.getItem({ key: 'nym-connect-sp' })();
|
||||||
if (spFromStorage) {
|
return spFromStorage;
|
||||||
setRawServiceProvider(spFromStorage);
|
|
||||||
setServiceProvider(spFromStorage);
|
|
||||||
}
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.warn(e);
|
console.warn(e);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleShowHelp = () => setShowHelp((show) => !show);
|
const getRandomSPFromList = (serviceProviders: ServiceProvider[]) => {
|
||||||
|
const randomSelection = serviceProviders[Math.floor(Math.random() * serviceProviders.length)];
|
||||||
|
return randomSelection;
|
||||||
|
};
|
||||||
|
|
||||||
const clearError = () => setError(undefined);
|
const setRandomSerivceProvider = async () => {
|
||||||
|
if (serviceProviders) {
|
||||||
useEffect(() => {
|
const randomServiceProvider = getRandomSPFromList(serviceProviders);
|
||||||
const validityCheck = async () => {
|
await setServiceProvider(randomServiceProvider);
|
||||||
if (services.length > 0 && serviceProvider) {
|
setSelectedProvider(randomServiceProvider);
|
||||||
const isValid = services.some(({ items }) => items.some(({ id }) => id === serviceProvider.id));
|
|
||||||
if (!isValid) {
|
|
||||||
console.warn('invalid SP, cleaning local storage');
|
|
||||||
await forage.removeItem({
|
|
||||||
key: 'nym-connect-sp',
|
|
||||||
})();
|
|
||||||
setRawServiceProvider(undefined);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
validityCheck();
|
|
||||||
}, [services, serviceProvider]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
const clearError = () => setError(undefined);
|
||||||
getSpFromStorage();
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const contextValue = useMemo(
|
const contextValue = useMemo(
|
||||||
() => ({
|
() => ({
|
||||||
@@ -197,31 +198,28 @@ export const ClientContextProvider: FCWithChildren = ({ children }) => {
|
|||||||
connectionStatus,
|
connectionStatus,
|
||||||
setConnectionStatus,
|
setConnectionStatus,
|
||||||
connectionStats,
|
connectionStats,
|
||||||
|
showInfoModal,
|
||||||
setConnectionStats,
|
setConnectionStats,
|
||||||
|
selectedProvider,
|
||||||
connectedSince,
|
connectedSince,
|
||||||
setConnectedSince,
|
setConnectedSince,
|
||||||
|
setRandomSerivceProvider,
|
||||||
startConnecting,
|
startConnecting,
|
||||||
startDisconnecting,
|
startDisconnecting,
|
||||||
services,
|
|
||||||
serviceProvider,
|
|
||||||
setServiceProvider,
|
|
||||||
showHelp,
|
|
||||||
handleShowHelp,
|
|
||||||
gatewayPerformance,
|
gatewayPerformance,
|
||||||
|
setShowInfoModal,
|
||||||
}),
|
}),
|
||||||
[
|
[
|
||||||
appVersion,
|
|
||||||
mode,
|
mode,
|
||||||
appVersion,
|
appVersion,
|
||||||
error,
|
error,
|
||||||
|
showInfoModal,
|
||||||
connectedSince,
|
connectedSince,
|
||||||
showHelp,
|
|
||||||
connectionStatus,
|
connectionStatus,
|
||||||
connectionStats,
|
connectionStats,
|
||||||
connectedSince,
|
connectedSince,
|
||||||
services,
|
|
||||||
serviceProvider,
|
|
||||||
gatewayPerformance,
|
gatewayPerformance,
|
||||||
|
selectedProvider,
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -6,19 +6,18 @@ const mockValues: TClientContext = {
|
|||||||
appVersion: 'v1.x.x',
|
appVersion: 'v1.x.x',
|
||||||
mode: 'dark',
|
mode: 'dark',
|
||||||
connectionStatus: ConnectionStatusKind.disconnected,
|
connectionStatus: ConnectionStatusKind.disconnected,
|
||||||
services: [],
|
selectedProvider: { id: '1', description: 'Keybase service provider', gateway: 'abc123', address: '123abc' },
|
||||||
showHelp: false,
|
|
||||||
serviceProvider: { id: '1', description: 'Keybase service provider', gateway: 'abc123', address: '123abc' },
|
|
||||||
gatewayPerformance: 'Good',
|
gatewayPerformance: 'Good',
|
||||||
|
showInfoModal: false,
|
||||||
|
setShowInfoModal: () => {},
|
||||||
setMode: () => {},
|
setMode: () => {},
|
||||||
clearError: () => {},
|
clearError: () => {},
|
||||||
handleShowHelp: () => {},
|
|
||||||
setConnectedSince: () => {},
|
setConnectedSince: () => {},
|
||||||
setConnectionStats: () => {},
|
setConnectionStats: () => {},
|
||||||
setConnectionStatus: () => {},
|
setConnectionStatus: () => {},
|
||||||
setServiceProvider: () => {},
|
|
||||||
startConnecting: async () => {},
|
startConnecting: async () => {},
|
||||||
startDisconnecting: async () => {},
|
startDisconnecting: async () => {},
|
||||||
|
setRandomSerivceProvider: () => {},
|
||||||
};
|
};
|
||||||
|
|
||||||
export const MockProvider: FCWithChildren<{
|
export const MockProvider: FCWithChildren<{
|
||||||
|
|||||||
@@ -7,6 +7,9 @@ import { NymMixnetTheme } from './theme';
|
|||||||
import { App } from './App';
|
import { App } from './App';
|
||||||
import { AppWindowFrame } from './components/AppWindowFrame';
|
import { AppWindowFrame } from './components/AppWindowFrame';
|
||||||
import { TestAndEarnContextProvider } from './components/Growth/context/TestAndEarnContext';
|
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');
|
const elem = document.getElementById('root');
|
||||||
|
|
||||||
@@ -14,15 +17,18 @@ if (elem) {
|
|||||||
const root = createRoot(elem);
|
const root = createRoot(elem);
|
||||||
root.render(
|
root.render(
|
||||||
<ErrorBoundary FallbackComponent={ErrorFallback}>
|
<ErrorBoundary FallbackComponent={ErrorFallback}>
|
||||||
|
<Router>
|
||||||
<ClientContextProvider>
|
<ClientContextProvider>
|
||||||
<TestAndEarnContextProvider>
|
<TestAndEarnContextProvider>
|
||||||
|
<GlobalStyles styles={{ html: { borderRadius: 10 } }} />
|
||||||
<NymMixnetTheme mode="dark">
|
<NymMixnetTheme mode="dark">
|
||||||
<AppWindowFrame>
|
<AppWindowFrame>
|
||||||
<App />
|
<AppRoutes />
|
||||||
</AppWindowFrame>
|
</AppWindowFrame>
|
||||||
</NymMixnetTheme>
|
</NymMixnetTheme>
|
||||||
</TestAndEarnContextProvider>
|
</TestAndEarnContextProvider>
|
||||||
</ClientContextProvider>
|
</ClientContextProvider>
|
||||||
|
</Router>
|
||||||
</ErrorBoundary>,
|
</ErrorBoundary>,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,57 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import { Box, Divider } from '@mui/material';
|
|
||||||
import { DateTime } from 'luxon';
|
|
||||||
import { IpAddressAndPortModal } from 'src/components/IpAddressAndPortModal';
|
|
||||||
import { ConnectionTimer } from 'src/components/ConntectionTimer';
|
|
||||||
import { ConnectionStatus } from '../components/ConnectionStatus';
|
|
||||||
import { ConnectionStatusKind, GatewayPerformance } from '../types';
|
|
||||||
import { ConnectionStatsItem } from '../components/ConnectionStats';
|
|
||||||
import { ConnectionButton } from '../components/ConnectionButton';
|
|
||||||
import { IpAddressAndPort } from '../components/IpAddressAndPort';
|
|
||||||
import { ServiceProvider } from '../types/directory';
|
|
||||||
import { TestAndEarnButtonArea } from '../components/Growth/TestAndEarnButtonArea';
|
|
||||||
|
|
||||||
export const ConnectedLayout: FCWithChildren<{
|
|
||||||
status: ConnectionStatusKind;
|
|
||||||
gatewayPerformance: GatewayPerformance;
|
|
||||||
stats: ConnectionStatsItem[];
|
|
||||||
ipAddress: string;
|
|
||||||
port: number;
|
|
||||||
connectedSince?: DateTime;
|
|
||||||
busy?: boolean;
|
|
||||||
showInfoModal: boolean;
|
|
||||||
isError?: boolean;
|
|
||||||
handleCloseInfoModal: () => void;
|
|
||||||
onConnectClick?: (status: ConnectionStatusKind) => void;
|
|
||||||
serviceProvider?: ServiceProvider;
|
|
||||||
}> = ({
|
|
||||||
status,
|
|
||||||
gatewayPerformance,
|
|
||||||
showInfoModal,
|
|
||||||
handleCloseInfoModal,
|
|
||||||
ipAddress,
|
|
||||||
port,
|
|
||||||
connectedSince,
|
|
||||||
busy,
|
|
||||||
isError,
|
|
||||||
serviceProvider,
|
|
||||||
onConnectClick,
|
|
||||||
}) => (
|
|
||||||
<>
|
|
||||||
<IpAddressAndPortModal show={showInfoModal} onClose={handleCloseInfoModal} ipAddress={ipAddress} port={port} />
|
|
||||||
<Box pb={1}>
|
|
||||||
<ConnectionStatus
|
|
||||||
status={ConnectionStatusKind.connected}
|
|
||||||
serviceProvider={serviceProvider}
|
|
||||||
gatewayPerformance={gatewayPerformance}
|
|
||||||
/>
|
|
||||||
</Box>
|
|
||||||
<Divider sx={{ my: 2 }} />
|
|
||||||
<Box sx={{ mb: 3 }}>
|
|
||||||
<IpAddressAndPort label="Socks5 address" ipAddress={ipAddress} port={port} />
|
|
||||||
</Box>
|
|
||||||
{/* <ConnectionStats stats={stats} /> */}
|
|
||||||
<ConnectionTimer connectedSince={connectedSince} />
|
|
||||||
<ConnectionButton status={status} busy={busy} onClick={onConnectClick} isError={isError} />
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
import { Box } from '@mui/material';
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
const layout = {
|
||||||
|
display: 'grid',
|
||||||
|
gridTemplateColumns: '1fr',
|
||||||
|
gridTemplateRows: '80px 180px 1fr',
|
||||||
|
gridColumnGap: '0px',
|
||||||
|
gridRowGap: '4px',
|
||||||
|
overflow: 'hidden',
|
||||||
|
height: '100%',
|
||||||
|
};
|
||||||
|
|
||||||
|
export const ConnectionLayout = ({
|
||||||
|
TopContent,
|
||||||
|
ConnectButton,
|
||||||
|
BottomContent,
|
||||||
|
}: {
|
||||||
|
TopContent: React.ReactNode;
|
||||||
|
ConnectButton: React.ReactNode;
|
||||||
|
BottomContent: React.ReactNode;
|
||||||
|
}) => (
|
||||||
|
<Box sx={layout}>
|
||||||
|
{TopContent}
|
||||||
|
<Box display="flex" justifyContent="center" alignItems="center">
|
||||||
|
{ConnectButton}
|
||||||
|
</Box>
|
||||||
|
{BottomContent}
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
@@ -1,49 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import { Box, Typography } from '@mui/material';
|
|
||||||
import { ConnectionStatus } from 'src/components/ConnectionStatus';
|
|
||||||
import { ConnectionTimer } from 'src/components/ConntectionTimer';
|
|
||||||
import { InfoModal } from 'src/components/InfoModal';
|
|
||||||
import { Error } from 'src/types/error';
|
|
||||||
import { ConnectionButton } from '../components/ConnectionButton';
|
|
||||||
import { ServiceSelector } from '../components/ServiceSelector';
|
|
||||||
import { useClientContext } from '../context/main';
|
|
||||||
import { ConnectionStatusKind } from '../types';
|
|
||||||
import { Services } from '../types/directory';
|
|
||||||
import { TestAndEarnButtonArea } from '../components/Growth/TestAndEarnButtonArea';
|
|
||||||
import { AppVersion } from '../components/AppVersion';
|
|
||||||
|
|
||||||
export const DefaultLayout: FCWithChildren<{
|
|
||||||
error?: Error;
|
|
||||||
status: ConnectionStatusKind;
|
|
||||||
services?: Services;
|
|
||||||
busy?: boolean;
|
|
||||||
isError?: boolean;
|
|
||||||
clearError: () => void;
|
|
||||||
onConnectClick?: (status: ConnectionStatusKind) => void;
|
|
||||||
}> = ({ status, error, services, busy, isError, onConnectClick, clearError }) => {
|
|
||||||
const context = useClientContext();
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Box pt={1}>
|
|
||||||
{error && <InfoModal show title={error.title} description={error.message} onClose={clearError} />}
|
|
||||||
<ConnectionStatus status={ConnectionStatusKind.disconnected} />
|
|
||||||
<Box px={2}>
|
|
||||||
<Typography fontWeight="400" fontSize="16px" textAlign="center" pt={2}>
|
|
||||||
Connect to the Nym <br /> mixnet for privacy.
|
|
||||||
</Typography>
|
|
||||||
<Typography textAlign="center" fontSize="small" sx={{ color: 'grey.500' }}>
|
|
||||||
This is experimental software. Do not rely on it for strong anonymity (yet).
|
|
||||||
</Typography>
|
|
||||||
</Box>
|
|
||||||
<ServiceSelector services={services} onChange={context.setServiceProvider} currentSp={context.serviceProvider} />
|
|
||||||
<ConnectionTimer />
|
|
||||||
<ConnectionButton
|
|
||||||
status={status}
|
|
||||||
disabled={context.serviceProvider === undefined}
|
|
||||||
busy={busy}
|
|
||||||
isError={isError}
|
|
||||||
onClick={onConnectClick}
|
|
||||||
/>
|
|
||||||
</Box>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
@@ -1,70 +0,0 @@
|
|||||||
import React, { useState } from 'react';
|
|
||||||
import { HelpPage } from 'src/components/HelpPage';
|
|
||||||
import { Button, Link, Stack } from '@mui/material';
|
|
||||||
import Image1 from '../assets/help-step-one.png';
|
|
||||||
import Image2 from '../assets/help-step-two.png';
|
|
||||||
import Image3 from '../assets/help-step-three.png';
|
|
||||||
import Image4 from '../assets/help-step-four.png';
|
|
||||||
|
|
||||||
export const HelpGuideLayout = () => {
|
|
||||||
const [step, setStep] = useState(0);
|
|
||||||
|
|
||||||
if (step === 1)
|
|
||||||
return (
|
|
||||||
<HelpPage
|
|
||||||
step={step}
|
|
||||||
description="Select your service provider from the dropdown menu."
|
|
||||||
img={Image1}
|
|
||||||
onNext={() => setStep(2)}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
|
|
||||||
if (step === 2)
|
|
||||||
return (
|
|
||||||
<HelpPage
|
|
||||||
step={step}
|
|
||||||
description="Click yellow button and connect to a service provider."
|
|
||||||
img={Image2}
|
|
||||||
onPrev={() => setStep(1)}
|
|
||||||
onNext={() => setStep(3)}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
|
|
||||||
if (step === 3)
|
|
||||||
return (
|
|
||||||
<HelpPage
|
|
||||||
step={step}
|
|
||||||
description="Click on IP and Port to copy their values to the clipboard."
|
|
||||||
img={Image3}
|
|
||||||
onPrev={() => setStep(2)}
|
|
||||||
onNext={() => setStep(4)}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
|
|
||||||
if (step === 4)
|
|
||||||
return (
|
|
||||||
<HelpPage
|
|
||||||
step={step}
|
|
||||||
description="Go to the settings of your selected app, under Proxy select run via SOCKS5 proxy then paste the IP and Port values given by NymConnect."
|
|
||||||
img={Image4}
|
|
||||||
onPrev={() => setStep(3)}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Stack gap={1}>
|
|
||||||
<Button variant="text" color="inherit" onClick={() => setStep(1)}>
|
|
||||||
How to connect guide
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
LinkComponent={Link}
|
|
||||||
variant="text"
|
|
||||||
color="inherit"
|
|
||||||
href="https://nymtech.net/docs/stable/quickstart/nym-connect/"
|
|
||||||
target="_blank"
|
|
||||||
>
|
|
||||||
Docs
|
|
||||||
</Button>
|
|
||||||
</Stack>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
@@ -0,0 +1,75 @@
|
|||||||
|
import React, { useState } from 'react';
|
||||||
|
import { Box, Stack } from '@mui/material';
|
||||||
|
import { DateTime } from 'luxon';
|
||||||
|
import { IpAddressAndPortModal } from 'src/components/IpAddressAndPortModal';
|
||||||
|
import { ConnectionTimer } from 'src/components/ConntectionTimer';
|
||||||
|
import { ConnectionStatus } from 'src/components/ConnectionStatus';
|
||||||
|
import { ConnectionStatusKind, GatewayPerformance } from 'src/types';
|
||||||
|
import { ConnectionStatsItem } from 'src/components/ConnectionStats';
|
||||||
|
import { IpAddressAndPort } from 'src/components/IpAddressAndPort';
|
||||||
|
import { ServiceProvider } from 'src/types/directory';
|
||||||
|
import { ExperimentalWarning } from 'src/components/ExperimentalWarning';
|
||||||
|
import { ConnectionLayout } from 'src/layouts/ConnectionLayout';
|
||||||
|
import { PowerButton } from 'src/components/PowerButton';
|
||||||
|
|
||||||
|
export const Connected: FCWithChildren<{
|
||||||
|
status: ConnectionStatusKind;
|
||||||
|
showInfoModal: boolean;
|
||||||
|
gatewayPerformance: GatewayPerformance;
|
||||||
|
stats: ConnectionStatsItem[];
|
||||||
|
ipAddress: string;
|
||||||
|
port: number;
|
||||||
|
connectedSince?: DateTime;
|
||||||
|
busy?: boolean;
|
||||||
|
isError?: boolean;
|
||||||
|
serviceProvider?: ServiceProvider;
|
||||||
|
onConnectClick: (status: ConnectionStatusKind) => void;
|
||||||
|
closeInfoModal: () => void;
|
||||||
|
}> = ({
|
||||||
|
status,
|
||||||
|
showInfoModal,
|
||||||
|
gatewayPerformance,
|
||||||
|
ipAddress,
|
||||||
|
port,
|
||||||
|
connectedSince,
|
||||||
|
busy,
|
||||||
|
isError,
|
||||||
|
serviceProvider,
|
||||||
|
onConnectClick,
|
||||||
|
closeInfoModal,
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<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 === 'connecting' || status === 'disconnecting'}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
BottomContent={
|
||||||
|
<Stack justifyContent="space-between">
|
||||||
|
<Box sx={{ mb: 2 }}>
|
||||||
|
<IpAddressAndPort label="Socks5 address" ipAddress={ipAddress} port={port} />
|
||||||
|
</Box>
|
||||||
|
<ExperimentalWarning />
|
||||||
|
</Stack>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { Stack, Typography } from '@mui/material';
|
||||||
|
import { ConnectionStatus } from 'src/components/ConnectionStatus';
|
||||||
|
import { ConnectionTimer } from 'src/components/ConntectionTimer';
|
||||||
|
import { useClientContext } from 'src/context/main';
|
||||||
|
import { InfoModal } from 'src/components/InfoModal';
|
||||||
|
import { Error } from 'src/types/error';
|
||||||
|
import { ExperimentalWarning } from 'src/components/ExperimentalWarning';
|
||||||
|
import { ServiceProvider, Services } from 'src/types/directory';
|
||||||
|
import { ConnectionStatusKind } from 'src/types';
|
||||||
|
import { ConnectionButton } from 'src/components/ConnectionButton';
|
||||||
|
import { PowerButton } from 'src/components/PowerButton';
|
||||||
|
import { Box } from '@mui/system';
|
||||||
|
import { ConnectionLayout } from 'src/layouts/ConnectionLayout';
|
||||||
|
|
||||||
|
export const Disconnected: FCWithChildren<{
|
||||||
|
error?: Error;
|
||||||
|
status: ConnectionStatusKind;
|
||||||
|
services?: Services;
|
||||||
|
busy?: boolean;
|
||||||
|
isError?: boolean;
|
||||||
|
serviceProvider?: ServiceProvider;
|
||||||
|
clearError: () => void;
|
||||||
|
onConnectClick: (status: ConnectionStatusKind) => void;
|
||||||
|
}> = ({ status, error, onConnectClick, clearError, serviceProvider }) => {
|
||||||
|
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={false} />}
|
||||||
|
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>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -0,0 +1,85 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { forage } from '@tauri-apps/tauri-forage';
|
||||||
|
import { DateTime } from 'luxon';
|
||||||
|
import { useClientContext } from 'src/context/main';
|
||||||
|
import { useTauriEvents } from 'src/utils';
|
||||||
|
import { Connected } from './Connected';
|
||||||
|
import { Disconnected } from './Disconnected';
|
||||||
|
|
||||||
|
export const ConnectionPage = () => {
|
||||||
|
const context = useClientContext();
|
||||||
|
const [busy, setBusy] = React.useState<boolean>();
|
||||||
|
|
||||||
|
useTauriEvents('help://clear-storage', (_event) => {
|
||||||
|
console.log('About to clear local storage...');
|
||||||
|
// clear local storage
|
||||||
|
try {
|
||||||
|
forage.clear()();
|
||||||
|
console.log('Local storage cleared');
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Failed to clear local storage', e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const handleConnectClick = async () => {
|
||||||
|
const currentStatus = context.connectionStatus;
|
||||||
|
if (currentStatus === 'connected' || currentStatus === 'disconnected') {
|
||||||
|
setBusy(true);
|
||||||
|
// eslint-disable-next-line default-case
|
||||||
|
switch (currentStatus) {
|
||||||
|
case 'disconnected':
|
||||||
|
await context.setRandomSerivceProvider();
|
||||||
|
await context.startConnecting();
|
||||||
|
context.setConnectedSince(DateTime.now());
|
||||||
|
context.setShowInfoModal(true);
|
||||||
|
break;
|
||||||
|
case 'connected':
|
||||||
|
await context.startDisconnecting();
|
||||||
|
context.setConnectedSince(undefined);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
setBusy(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const closeInfoModal = () => context.setShowInfoModal(false);
|
||||||
|
|
||||||
|
if (context.connectionStatus === 'connected')
|
||||||
|
return (
|
||||||
|
<Connected
|
||||||
|
status={context.connectionStatus}
|
||||||
|
showInfoModal={context.showInfoModal}
|
||||||
|
busy={busy}
|
||||||
|
onConnectClick={handleConnectClick}
|
||||||
|
ipAddress="127.0.0.1"
|
||||||
|
port={1080}
|
||||||
|
gatewayPerformance={context.gatewayPerformance}
|
||||||
|
connectedSince={context.connectedSince}
|
||||||
|
serviceProvider={context.selectedProvider}
|
||||||
|
closeInfoModal={closeInfoModal}
|
||||||
|
stats={[
|
||||||
|
{
|
||||||
|
label: 'in:',
|
||||||
|
totalBytes: 1024,
|
||||||
|
rateBytesPerSecond: 1024 * 1024 * 1024 + 10,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'out:',
|
||||||
|
totalBytes: 1024 * 1024 * 1024 * 1024 * 20,
|
||||||
|
rateBytesPerSecond: 1024 * 1024 + 10,
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Disconnected
|
||||||
|
busy={busy}
|
||||||
|
error={context.error}
|
||||||
|
onConnectClick={handleConnectClick}
|
||||||
|
clearError={context.clearError}
|
||||||
|
status={context.connectionStatus}
|
||||||
|
serviceProvider={context.selectedProvider}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { Divider, Typography } from '@mui/material';
|
||||||
|
import { Box } from '@mui/system';
|
||||||
|
|
||||||
|
const appsSchema = {
|
||||||
|
messagingApps: ['Telegram', 'Keybase'],
|
||||||
|
wallets: ['Blockstream', 'Electum'],
|
||||||
|
};
|
||||||
|
|
||||||
|
export const CompatibleApps = () => (
|
||||||
|
<Box>
|
||||||
|
<Typography fontSize="small" color="grey.600" sx={{ mb: 2 }}>
|
||||||
|
Supported apps
|
||||||
|
</Typography>
|
||||||
|
<Typography color="nym.highlight" sx={{ mb: 2 }}>
|
||||||
|
Messaging apps
|
||||||
|
</Typography>
|
||||||
|
|
||||||
|
<Divider sx={{ mb: 2 }} />
|
||||||
|
|
||||||
|
<Box sx={{ mb: 4 }}>
|
||||||
|
{appsSchema.messagingApps.map((app, i) => (
|
||||||
|
<Typography variant="body2" color="grey.400" sx={{ mb: 2 }} key={i}>
|
||||||
|
{app}
|
||||||
|
</Typography>
|
||||||
|
))}
|
||||||
|
</Box>
|
||||||
|
<Typography color="nym.highlight" sx={{ mb: 2 }}>
|
||||||
|
Wallets
|
||||||
|
</Typography>
|
||||||
|
|
||||||
|
<Divider sx={{ mb: 2 }} />
|
||||||
|
|
||||||
|
<Box sx={{ mb: 4 }}>
|
||||||
|
{appsSchema.wallets.map((wallet, i) => (
|
||||||
|
<Typography variant="body2" color="grey.400" sx={{ mb: 2 }} key={i}>
|
||||||
|
{wallet}
|
||||||
|
</Typography>
|
||||||
|
))}
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
import React, { useState } from 'react';
|
||||||
|
import { HelpPage } from 'src/components/HelpPage';
|
||||||
|
import Image1 from '../../assets/help-step-one.png';
|
||||||
|
import Image2 from '../../assets/help-step-two.png';
|
||||||
|
import Image4 from '../../assets/help-step-four.png';
|
||||||
|
|
||||||
|
export const HelpGuide = () => {
|
||||||
|
const [step, setStep] = useState(1);
|
||||||
|
const TOTAL_STEPS = 3;
|
||||||
|
|
||||||
|
if (step === 1)
|
||||||
|
return (
|
||||||
|
<HelpPage
|
||||||
|
step={step}
|
||||||
|
totalSteps={TOTAL_STEPS}
|
||||||
|
description="Click button and connect
|
||||||
|
to a Nym mixnet."
|
||||||
|
img={Image1}
|
||||||
|
onNext={() => setStep(2)}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
if (step === 2)
|
||||||
|
return (
|
||||||
|
<HelpPage
|
||||||
|
step={step}
|
||||||
|
totalSteps={TOTAL_STEPS}
|
||||||
|
description="Click on IP and Port to copy their values to the clipboard."
|
||||||
|
img={Image2}
|
||||||
|
onPrev={() => setStep(1)}
|
||||||
|
onNext={() => setStep(3)}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
if (step === 3)
|
||||||
|
return (
|
||||||
|
<HelpPage
|
||||||
|
step={step}
|
||||||
|
totalSteps={TOTAL_STEPS}
|
||||||
|
description="Go to settings in your app, select run via SOCKS5 proxy and paste the IP and Port values given by NymConnect."
|
||||||
|
img={Image4}
|
||||||
|
onPrev={() => setStep(2)}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
return null;
|
||||||
|
};
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
import React from 'react';
|
||||||
|
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';
|
||||||
|
|
||||||
|
const menuSchema = [
|
||||||
|
{ title: 'Supported apps', icon: Apps, path: 'apps' },
|
||||||
|
{ title: 'How to connect guide', icon: HelpOutline, path: 'guide' },
|
||||||
|
];
|
||||||
|
|
||||||
|
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>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
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';
|
||||||
|
|
||||||
|
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>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -4,10 +4,10 @@ import { Box } from '@mui/material';
|
|||||||
import { DateTime } from 'luxon';
|
import { DateTime } from 'luxon';
|
||||||
import { AppWindowFrame } from '../components/AppWindowFrame';
|
import { AppWindowFrame } from '../components/AppWindowFrame';
|
||||||
import { useClientContext } from '../context/main';
|
import { useClientContext } from '../context/main';
|
||||||
import { ConnectionStatusKind } from '../types';
|
|
||||||
import { DefaultLayout } from '../layouts/DefaultLayout';
|
|
||||||
import { ConnectedLayout } from '../layouts/ConnectedLayout';
|
|
||||||
import { Services } from '../types/directory';
|
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 {
|
export default {
|
||||||
title: 'App/Flow',
|
title: 'App/Flow',
|
||||||
@@ -36,15 +36,15 @@ export const Mock: ComponentStory<typeof AppWindowFrame> = () => {
|
|||||||
];
|
];
|
||||||
const handleConnectClick = React.useCallback(() => {
|
const handleConnectClick = React.useCallback(() => {
|
||||||
const oldStatus = context.connectionStatus;
|
const oldStatus = context.connectionStatus;
|
||||||
if (oldStatus === ConnectionStatusKind.connected || oldStatus === ConnectionStatusKind.disconnected) {
|
if (oldStatus === 'connected' || oldStatus === 'disconnected') {
|
||||||
setBusy(true);
|
setBusy(true);
|
||||||
|
|
||||||
// eslint-disable-next-line default-case
|
// eslint-disable-next-line default-case
|
||||||
switch (oldStatus) {
|
switch (oldStatus) {
|
||||||
case ConnectionStatusKind.disconnected:
|
case 'disconnected':
|
||||||
context.setConnectionStatus(ConnectionStatusKind.connecting);
|
context.setConnectionStatus(ConnectionStatusKind.connecting);
|
||||||
break;
|
break;
|
||||||
case ConnectionStatusKind.connected:
|
case 'connected':
|
||||||
context.setConnectionStatus(ConnectionStatusKind.disconnecting);
|
context.setConnectionStatus(ConnectionStatusKind.disconnecting);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -52,11 +52,11 @@ export const Mock: ComponentStory<typeof AppWindowFrame> = () => {
|
|||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
// eslint-disable-next-line default-case
|
// eslint-disable-next-line default-case
|
||||||
switch (oldStatus) {
|
switch (oldStatus) {
|
||||||
case ConnectionStatusKind.disconnected:
|
case 'disconnected':
|
||||||
context.setConnectedSince(DateTime.now());
|
context.setConnectedSince(DateTime.now());
|
||||||
context.setConnectionStatus(ConnectionStatusKind.connected);
|
context.setConnectionStatus(ConnectionStatusKind.connected);
|
||||||
break;
|
break;
|
||||||
case ConnectionStatusKind.connected:
|
case 'connected':
|
||||||
context.setConnectionStatus(ConnectionStatusKind.disconnected);
|
context.setConnectionStatus(ConnectionStatusKind.disconnected);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -72,7 +72,7 @@ export const Mock: ComponentStory<typeof AppWindowFrame> = () => {
|
|||||||
return (
|
return (
|
||||||
<Box width={width} height={height}>
|
<Box width={width} height={height}>
|
||||||
<AppWindowFrame>
|
<AppWindowFrame>
|
||||||
<DefaultLayout
|
<Disconnected
|
||||||
status={context.connectionStatus}
|
status={context.connectionStatus}
|
||||||
busy={busy}
|
busy={busy}
|
||||||
onConnectClick={handleConnectClick}
|
onConnectClick={handleConnectClick}
|
||||||
@@ -86,10 +86,10 @@ export const Mock: ComponentStory<typeof AppWindowFrame> = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<AppWindowFrame>
|
<AppWindowFrame>
|
||||||
<ConnectedLayout
|
<Connected
|
||||||
gatewayPerformance="Good"
|
gatewayPerformance="Good"
|
||||||
showInfoModal={false}
|
showInfoModal={false}
|
||||||
handleCloseInfoModal={() => undefined}
|
closeInfoModal={() => undefined}
|
||||||
status={context.connectionStatus}
|
status={context.connectionStatus}
|
||||||
busy={busy}
|
busy={busy}
|
||||||
onConnectClick={handleConnectClick}
|
onConnectClick={handleConnectClick}
|
||||||
|
|||||||
@@ -2,23 +2,28 @@ import React from 'react';
|
|||||||
import { ComponentMeta, ComponentStory } from '@storybook/react';
|
import { ComponentMeta, ComponentStory } from '@storybook/react';
|
||||||
import { Box } from '@mui/material';
|
import { Box } from '@mui/material';
|
||||||
import { DateTime } from 'luxon';
|
import { DateTime } from 'luxon';
|
||||||
import { ConnectedLayout } from '../layouts/ConnectedLayout';
|
import { Connected } from 'src/pages/connection/Connected';
|
||||||
import { ConnectionStatusKind } from '../types';
|
import { ConnectionStatusKind } from 'src/types';
|
||||||
|
|
||||||
|
const onClick = () => undefined;
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
title: 'Layouts/ConnectedLayout',
|
title: 'Layouts/ConnectedLayout',
|
||||||
component: ConnectedLayout,
|
component: Connected,
|
||||||
} as ComponentMeta<typeof ConnectedLayout>;
|
} as ComponentMeta<typeof Connected>;
|
||||||
|
|
||||||
export const Default: ComponentStory<typeof ConnectedLayout> = () => (
|
export const Default: ComponentStory<typeof Connected> = () => (
|
||||||
<Box p={2} width={242} sx={{ bgcolor: 'nym.background.dark' }}>
|
<Box p={2} width={242} sx={{ bgcolor: 'nym.background.dark' }}>
|
||||||
<ConnectedLayout
|
<Connected
|
||||||
showInfoModal={false}
|
|
||||||
gatewayPerformance="Good"
|
gatewayPerformance="Good"
|
||||||
handleCloseInfoModal={() => undefined}
|
showInfoModal={false}
|
||||||
|
closeInfoModal={() => {
|
||||||
|
return undefined;
|
||||||
|
}}
|
||||||
status={ConnectionStatusKind.connected}
|
status={ConnectionStatusKind.connected}
|
||||||
connectedSince={DateTime.now()}
|
connectedSince={DateTime.now()}
|
||||||
ipAddress="127.0.0.1"
|
ipAddress="127.0.0.1"
|
||||||
|
serviceProvider={{ id: 'service 1', description: 'good services', address: 'abc123', gateway: '8910xyz' }}
|
||||||
port={1080}
|
port={1080}
|
||||||
stats={[
|
stats={[
|
||||||
{
|
{
|
||||||
@@ -32,6 +37,7 @@ export const Default: ComponentStory<typeof ConnectedLayout> = () => (
|
|||||||
rateBytesPerSecond: 1024 * 1024 + 10,
|
rateBytesPerSecond: 1024 * 1024 + 10,
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
|
onConnectClick={onClick}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { ComponentMeta, ComponentStory } from '@storybook/react';
|
import { ComponentMeta, ComponentStory } from '@storybook/react';
|
||||||
import { ConnectionButton } from '../components/ConnectionButton';
|
import { ConnectionButton } from '../components/ConnectionButton';
|
||||||
import { ConnectionStatusKind } from '../types';
|
import { ConnectionStatusKind } from 'src/types';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
title: 'Components/ConnectionButton',
|
title: 'Components/ConnectionButton',
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user