Compare commits

...

15 Commits

Author SHA1 Message Date
Tommy Verrall fd516d4633 Merge branch 'feature/mixnode-sandbox-cycles' of https://github.com/nymtech/nym into feature/mixnode-sandbox-cycles 2023-12-20 17:04:20 +01:00
Tommy Verrall a12ade1ecf warnings on ci 2023-12-20 16:56:12 +01:00
Tommy Verrall 458a2c5e42 Update build-upload-binaries.yml
remove noise
2023-12-20 16:40:02 +01:00
Tommy Verrall ecaa970380 Update build-upload-binaries.yml
update build binary for this branch
2023-12-20 16:38:54 +01:00
Tommy Verrall 42e3064cbb Merge pull request #4266 from nymtech/mixnode-tokio-console
Add tokio-console feature
2023-12-20 15:20:56 +00:00
durch 0d5f594a69 Fix feature name 2023-12-20 16:16:45 +01:00
durch 826db30d2b Add tokio-console feature 2023-12-20 16:15:18 +01:00
Zane Schepke 2c731cf048 Merge pull request #4265 from nymtech/fix/vpn_desktop_style
refactor(vpn-desktop): minor style changes
2023-12-20 09:37:51 -05:00
pierre 9a1f28bd43 fix spacing issue 2023-12-20 15:27:56 +01:00
Zane Schepke b9493004aa Merge pull request #4264 from nymtech/refactor/vpn_desktop_style
refactor(vpn-desktop): fix style
2023-12-20 08:49:19 -05:00
pierre b27f806c40 fix spacing issue 2023-12-20 14:47:24 +01:00
Pierre Dommerc 08aaa8813e refactor(vpn-desktop): clean ui (#4263)
* set cursor default on disabled state

* fix long error messages overflow

* clean code

* fix hop select component style

* fix exit select button

* add nym vpn icon

* style refactoring

* style refactoring

* fix hover bg country list in light mode

* fix hover bg country list in light mode

---------

Co-authored-by: Zane Schepke <zanecschepke@gmail.com>
2023-12-20 14:13:48 +01:00
Pierre Dommerc dd9f4f24f1 doc(vpn-desktop): update readme (#4260) 2023-12-19 11:33:21 +01:00
Zane Schepke f6c2cab531 feat(vpn-desktop): add exit node location (#4234)
* add entry node selection

* update quick prefix to fastest

* add entry node selection

* update quick prefix to fastest

* add country selection

* add country selection

* remove config properties

* disable entry location for now

* refactor: rename entry location selector command

* remove un-related comment

* refactor: renamed entry location selector

* use translation key for copy

* fix on connect error handling

* use a default entry location for now

* refactor(backend): move country list into module

* refactor(backend): init app state based on restored app data

* fix(backend): connect command

* add comments

* fix country select bug

* add entry_node_location to app config file

* add proper default location handling

* clean code

---------

Co-authored-by: pierre <dommerc.pierre@gmail.com>
2023-12-19 11:15:11 +01:00
Jon Häggblad f4dd9a915d Add some license entries to Cargo.toml files (#4250)
* Add Apache 2.0 to bunch of common crates

* Allow some basic licenses

* Add more licenses
2023-12-19 09:21:33 +01:00
107 changed files with 539 additions and 261 deletions
+36 -33
View File
@@ -1,16 +1,10 @@
name: build-upload-binaries
name: ci-build-upload-binaries
on:
workflow_dispatch:
inputs:
add_tokio_unstable:
description: 'True to add RUSTFLAGS="--cfg tokio_unstable"'
required: true
default: false
type: boolean
env:
NETWORK: mainnet
pull_request:
paths:
- 'mixnode/**'
jobs:
publish-nym:
@@ -22,40 +16,49 @@ jobs:
runs-on: ${{ matrix.platform }}
env:
CARGO_TERM_COLOR: always
RUSTFLAGS: "--cfg tokio_unstable"
steps:
- uses: actions/checkout@v3
- name: Install Dependencies (Linux)
run: sudo apt-get update && sudo apt-get -y install libwebkit2gtk-4.0-dev build-essential curl wget libssl-dev libgtk-3-dev libudev-dev squashfs-tools
continue-on-error: true
- name: Sets env vars for tokio if set in manual dispatch inputs
- name: Prepare build output directory
shell: bash
env:
OUTPUT_DIR: ci-builds/${{ github.ref_name }}
run: |
echo 'RUSTFLAGS="--cfg tokio_unstable"' >> $GITHUB_ENV
if: github.event_name == 'workflow_dispatch' && inputs.add_tokio_unstable == true
rm -rf ci-builds || true
mkdir -p $OUTPUT_DIR
echo $OUTPUT_DIR
- name: Install Dependencies (Linux)
run: sudo apt update && sudo apt install libudev-dev
- name: Install Rust stable
uses: actions-rs/toolchain@v1
with:
toolchain: stable
- name: Build all binaries
- name: Build mixnode binary
uses: actions-rs/cargo@v1
with:
command: build
args: --workspace --release
args: --release --features cpucycles --package nym-mixnode
- name: Upload Artifact
uses: actions/upload-artifact@v3
with:
name: nym-binaries-artifacts
path: |
target/release/nym-client
target/release/nym-gateway
target/release/nym-mixnode
target/release/nym-socks5-client
target/release/nym-api
target/release/nym-network-requester
target/release/nym-network-statistics
target/release/nym-cli
retention-days: 30
- name: Prepare build output
shell: bash
env:
OUTPUT_DIR: ci-builds/${{ github.ref_name }}
run: |
cp target/release/nym-mixnode $OUTPUT_DIR
- name: Deploy branch to CI www
continue-on-error: true
uses: easingthemes/ssh-deploy@main
env:
SSH_PRIVATE_KEY: ${{ secrets.CI_WWW_SSH_PRIVATE_KEY }}
ARGS: "-avzr"
SOURCE: "ci-builds/"
REMOTE_HOST: ${{ secrets.CI_WWW_REMOTE_HOST }}
REMOTE_USER: ${{ secrets.CI_WWW_REMOTE_USER }}
TARGET: ${{ secrets.CI_WWW_REMOTE_TARGET }}/builds/
EXCLUDE: "/dist/, /node_modules/"
+1
View File
@@ -5,6 +5,7 @@ authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>", "Jędrzej St
description = "Implementation of the Nym Client"
edition = "2021"
rust-version = "1.65"
license.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@@ -3,6 +3,7 @@ name = "nym-client-websocket-requests"
version = "0.1.0"
authors = ["Jędrzej Stuczyński <andrew@nymtech.net>"]
edition = "2021"
license.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+1
View File
@@ -5,6 +5,7 @@ authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>"]
description = "A SOCKS5 localhost proxy that converts incoming messages to Sphinx and sends them to a Nym address"
edition = "2021"
rust-version = "1.56"
license.workspace = true
[dependencies]
clap = { workspace = true, features = ["cargo", "derive"] }
+1
View File
@@ -2,6 +2,7 @@
name = "async-file-watcher"
version = "0.1.0"
edition = "2021"
license.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+1
View File
@@ -2,6 +2,7 @@
name = "nym-bandwidth-controller"
version = "0.1.0"
edition = "2021"
license.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+1
View File
@@ -4,6 +4,7 @@ version = "1.1.15"
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>"]
edition = "2021"
rust-version = "1.66"
license.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@@ -3,6 +3,7 @@ name = "nym-gateway-client"
version = "0.1.0"
authors = ["Jędrzej Stuczyński <andrew@nymtech.net>"]
edition = "2021"
license.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@@ -3,6 +3,7 @@ name = "nym-mixnet-client"
version = "0.1.0"
authors = ["Jedrzej Stuczynski <andrew@nymtech.net>"]
edition = "2021"
license.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@@ -4,6 +4,7 @@ version = "0.1.0"
authors = ["Jędrzej Stuczyński <andrew@nymtech.net>"]
edition = "2021"
rust-version = "1.56"
license.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+1
View File
@@ -3,6 +3,7 @@ name = "nym-coconut-interface"
version = "0.1.0"
edition = "2021"
description = "Crutch library until there is proper SerDe support for coconut structs"
license.workspace = true
[dependencies]
bs58 = "0.4.0"
+1
View File
@@ -3,6 +3,7 @@ name = "nym-cli-commands"
version = "1.0.0"
authors.workspace = true
edition = "2021"
license.workspace = true
[dependencies]
anyhow = { workspace = true }
+2 -1
View File
@@ -3,6 +3,7 @@ name = "nym-config"
version = "0.1.0"
authors = ["Jedrzej Stuczynski <andrew@nymtech.net>"]
edition = "2021"
license.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@@ -17,4 +18,4 @@ url = { workspace = true }
nym-network-defaults = { path = "../network-defaults" }
[features]
default = ["dirs"]
default = ["dirs"]
@@ -2,6 +2,7 @@
name = "nym-coconut-bandwidth-contract-common"
version = "0.1.0"
edition = "2021"
license.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@@ -12,4 +13,4 @@ cw2 = { workspace = true, optional = true }
nym-multisig-contract-common = { path = "../multisig-contract" }
[features]
schema = ["cw2"]
schema = ["cw2"]
@@ -2,6 +2,7 @@
name = "nym-coconut-dkg-common"
version = "0.1.0"
edition = "2021"
license.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@@ -14,4 +15,4 @@ contracts-common = { path = "../contracts-common", package = "nym-contracts-comm
nym-multisig-contract-common = { path = "../multisig-contract" }
[features]
schema = []
schema = []
@@ -2,6 +2,7 @@
name = "nym-ephemera-common"
version = "0.1.0"
edition = "2021"
license.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@@ -13,4 +14,4 @@ cw-utils = { workspace = true }
contracts-common = { path = "../contracts-common", package = "nym-contracts-common" }
[features]
schema = []
schema = []
@@ -2,6 +2,7 @@
name = "nym-group-contract-common"
version = "0.1.0"
edition = "2021"
license.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@@ -2,6 +2,7 @@
name = "nym-multisig-contract-common"
version = "0.1.0"
edition = "2021"
license.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@@ -2,6 +2,7 @@
name = "nym-name-service-common"
version = "0.1.0"
edition = "2021"
license.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@@ -16,4 +17,4 @@ serde = { workspace = true, features = ["derive"] }
thiserror = { workspace = true }
[features]
schema = ["cw2"]
schema = ["cw2"]
@@ -2,6 +2,7 @@
name = "nym-service-provider-directory-common"
version = "0.1.0"
edition = "2021"
license.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@@ -15,4 +16,4 @@ nym-contracts-common = { path = "../contracts-common", version = "0.5.0" }
thiserror = { workspace = true }
[features]
schema = ["cw2"]
schema = ["cw2"]
+1
View File
@@ -2,6 +2,7 @@
name = "nym-credential-storage"
version = "0.1.0"
edition = "2021"
license.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+1
View File
@@ -2,6 +2,7 @@
name = "nym-credential-utils"
version = "0.1.0"
edition = "2021"
license.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+1
View File
@@ -2,6 +2,7 @@
name = "nym-credentials"
version = "0.1.0"
edition = "2021"
license.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+1
View File
@@ -3,6 +3,7 @@ name = "nym-dkg"
version = "0.1.0"
edition = "2021"
resolver = "2"
license.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+1
View File
@@ -2,6 +2,7 @@
name = "nym-execute"
version = "0.1.0"
edition = "2021"
license.workspace = true
[lib]
proc-macro = true
+2 -1
View File
@@ -2,6 +2,7 @@
name = "ledger"
version = "0.1.0"
edition = "2021"
license.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@@ -10,4 +11,4 @@ bip32 = "0.5.1"
k256 = { workspace = true }
ledger-transport = "0.10.0"
ledger-transport-hid = "0.10.0"
thiserror = { workspace = true }
thiserror = { workspace = true }
+1
View File
@@ -3,6 +3,7 @@ name = "nym-mixnode-common"
version = "0.1.0"
authors = ["Jędrzej Stuczyński <andrew@nymtech.net>"]
edition = "2021"
license.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+1
View File
@@ -2,6 +2,7 @@
name = "nym-node-tester-utils"
version = "0.1.0"
edition = "2021"
license.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@@ -3,6 +3,7 @@ name = "nym-nonexhaustive-delayqueue"
version = "0.1.0"
authors = ["Jędrzej Stuczyński <andrew@nymtech.net>"]
edition = "2021"
license.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+1
View File
@@ -3,6 +3,7 @@ name = "nym-coconut"
version = "0.5.0"
authors = ["Jedrzej Stuczynski <andrew@nymtech.net>", "Ania Piotrowska <ania@nymtech.net>"]
edition = "2021"
license.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+1
View File
@@ -2,6 +2,7 @@
name = "nym-socks5-client-core"
version = "0.1.0"
edition = "2021"
license.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+1
View File
@@ -3,6 +3,7 @@ name = "nym-ordered-buffer"
version = "0.1.0"
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>"]
edition = "2021"
license.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+1
View File
@@ -3,6 +3,7 @@ name = "nym-socks5-proxy-helpers"
version = "0.1.0"
authors = ["Jedrzej Stuczynski <andrew@nymtech.net>"]
edition = "2021"
license.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+1
View File
@@ -3,6 +3,7 @@ name = "nym-socks5-requests"
version = "0.1.0"
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>"]
edition = "2021"
license.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+1
View File
@@ -5,6 +5,7 @@
name = "nym-statistics-common"
version = "1.0.1"
edition = "2021"
license.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+2 -1
View File
@@ -2,6 +2,7 @@
name = "nym-store-cipher"
version = "0.1.0"
edition = "2021"
license.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@@ -20,4 +21,4 @@ getrandom = { version = "0.2", features = ["js"] }
[features]
default = []
json = ["serde_json"]
json = ["serde_json"]
+1
View File
@@ -5,6 +5,7 @@ description = "Nym common types"
authors.workspace = true
edition = "2021"
rust-version = "1.58"
license.workspace = true
[dependencies]
base64 = "0.21.4"
+2 -1
View File
@@ -3,6 +3,7 @@ name = "wasm-storage"
version = "0.1.0"
authors = ["Jedrzej Stuczynski <andrew@nymtech.net>"]
edition = "2021"
license.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@@ -17,4 +18,4 @@ indexed_db_futures = { version = " 0.3.0"}
thiserror = { workspace = true }
nym-store-cipher = { path = "../../store-cipher", features = ["json"] }
wasm-utils = { path = "../utils", default-features = false }
wasm-utils = { path = "../utils", default-features = false }
+1
View File
@@ -3,6 +3,7 @@ name = "wasm-utils"
version = "0.1.0"
authors = ["Jedrzej Stuczynski <andrew@nymtech.net>"]
edition = "2021"
license.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+17 -9
View File
@@ -104,8 +104,16 @@ unlicensed = "deny"
# See https://spdx.org/licenses/ for list of possible licenses
# [possible values: any SPDX 3.11 short identifier (+ optional exception)].
allow = [
#"MIT",
#"Apache-2.0",
"MIT",
"Apache-2.0",
"BSD-2-Clause",
"BSD-3-Clause",
"ISC",
"0BSD",
"MPL-2.0",
"CC0-1.0",
"Unicode-DFS-2016",
"OpenSSL",
]
# List of explicitly disallowed licenses
# See https://spdx.org/licenses/ for list of possible licenses
@@ -143,22 +151,22 @@ exceptions = [
# Some crates don't have (easily) machine readable licensing information,
# adding a clarification entry for it allows you to manually specify the
# licensing information
#[[licenses.clarify]]
[[licenses.clarify]]
# The name of the crate the clarification applies to
#name = "ring"
name = "ring"
# The optional version constraint for the crate
#version = "*"
version = "*"
# The SPDX expression for the license requirements of the crate
#expression = "MIT AND ISC AND OpenSSL"
expression = "MIT AND ISC AND OpenSSL"
# One or more files in the crate's source used as the "source of truth" for
# the license expression. If the contents match, the clarification will be used
# when running the license check, otherwise the clarification will be ignored
# and the crate will be checked normally, which may produce warnings or errors
# depending on the rest of your configuration
#license-files = [
license-files = [
# Each entry is a crate relative path, and the (opaque) hash of its contents
#{ path = "LICENSE", hash = 0xbd0eed23 }
#]
{ path = "LICENSE", hash = 0xbd0eed23 }
]
[licenses.private]
# If true, ignores workspace crates that aren't published, or are only
+1
View File
@@ -2,6 +2,7 @@
name = "explorer-api"
version = "1.1.32"
edition = "2021"
license.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@@ -2,6 +2,7 @@
name = "nym-explorer-api-requests"
version = "0.1.0"
edition = "2021"
license.workspace = true
[dependencies]
nym-contracts-common = { path = "../../common/cosmwasm-smart-contracts/contracts-common" }
+1
View File
@@ -2,6 +2,7 @@
name = "nym-explorer-client"
version = "0.1.0"
edition = "2021"
license.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+1 -1
View File
@@ -3,7 +3,7 @@
[package]
name = "nym-gateway"
license = "GPL-3"
license = "GPL-3.0"
version = "1.1.32"
authors = [
"Dave Hrycyszyn <futurechimp@users.noreply.github.com>",
+1
View File
@@ -6,6 +6,7 @@ name = "nym-gateway-requests"
version = "0.1.0"
authors = ["Jedrzej Stuczynski <andrew@nymtech.net>"]
edition = "2021"
license.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+5 -1
View File
@@ -3,7 +3,7 @@
[package]
name = "nym-mixnode"
license = "GPL-3"
license = "GPL-3.0"
version = "1.1.34"
authors = [
"Dave Hrycyszyn <futurechimp@users.noreply.github.com>",
@@ -35,6 +35,7 @@ serde_json = { workspace = true }
sysinfo = "0.27.7"
tokio = { workspace = true, features = ["rt-multi-thread", "net", "signal"] }
tokio-util = { workspace = true, features = ["codec"] }
console-subscriber = { version = "0.2", optional = true }
toml = "0.5.8"
url = { workspace = true, features = ["serde"] }
cfg-if = "1.0.0"
@@ -83,6 +84,9 @@ cpucycles = [
"nym-bin-common/tracing",
]
# build with RUSTFLAGS="--cfg tokio_unstable"
tokio-console = ["console-subscriber", "tokio/tracing"]
[package.metadata.deb]
name = "nym-mixnode"
maintainer-scripts = "debian"
+7 -1
View File
@@ -4,7 +4,7 @@
use ::nym_config::defaults::setup_env;
use clap::{crate_name, crate_version, Parser};
use lazy_static::lazy_static;
use log::info;
use log::{error, info};
use nym_bin_common::bin_info;
#[allow(unused_imports)]
@@ -53,6 +53,12 @@ fn test_function() {
#[tokio::main]
async fn main() -> anyhow::Result<()> {
cfg_if::cfg_if! {
if #[cfg(feature = "tokio-console")] {
console_subscriber::init();
}
}
let args = Cli::parse();
setup_env(args.config_env_file.as_ref());
+1 -1
View File
@@ -3,7 +3,7 @@
[package]
name = "nym-api"
license = "GPL-3"
license = "GPL-3.0"
version = "1.1.34"
authors = [
"Dave Hrycyszyn <futurechimp@users.noreply.github.com>",
+1
View File
@@ -2,6 +2,7 @@
name = "nym-api-requests"
version = "0.1.0"
edition = "2021"
license.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+1 -1
View File
@@ -9,7 +9,7 @@ repository.workspace = true
homepage.workspace = true
documentation.workspace = true
edition.workspace = true
license = "GPL-3"
license = "GPL-3.0"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+10 -3
View File
@@ -39,10 +39,17 @@ For example on Linux the path would be `~/.config/nym-vpn/config.toml`
# path to the env config file if you provide one
env_config_file = "$HOME/.config/nym-vpn/qa.env"
```
# pick a gateway and exit router accordingly to the selected env
entry_gateway = "xxx"
exit_router = "xxx"
## Optional config
In the config file `nym-vpn/config.toml`
```toml
env_config_file = "$HOME/.config/nym-vpn/qa.env"
# set the entry node location to use, as two letter country code
entry_node_location = "FR"
```
## Dev
+1 -1
View File
@@ -1,5 +1,5 @@
<!doctype html>
<html lang="en" class="text-[12px]">
<html lang="en" class="text-[14px]">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
+1
View File
@@ -5267,6 +5267,7 @@ dependencies = [
"nym-task",
"nym-validator-client",
"nym-wireguard-types",
"rand 0.7.3",
"serde",
"serde_json",
"signature 1.4.0",
Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

After

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.8 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 974 B

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

After

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 KiB

After

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.6 KiB

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 903 B

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.4 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.
Binary file not shown.

Before

Width:  |  Height:  |  Size: 85 KiB

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 25 KiB

+25 -23
View File
@@ -1,6 +1,7 @@
use tauri::State;
use tracing::{debug, instrument};
use crate::country::COUNTRIES;
use crate::states::app::Country;
use crate::{
error::{CmdError, CmdErrorSource},
@@ -12,29 +13,8 @@ use crate::{
#[tauri::command]
pub fn get_node_countries() -> Result<Vec<Country>, CmdError> {
debug!("get_node_countries");
let countries: Vec<Country> = vec![
Country {
name: "United States".to_string(),
code: "US".to_string(),
},
Country {
name: "France".to_string(),
code: "FR".to_string(),
},
Country {
name: "Switzerland".to_string(),
code: "CH".to_string(),
},
Country {
name: "Sweden".to_string(),
code: "SE".to_string(),
},
Country {
name: "Germany".to_string(),
code: "DE".to_string(),
},
];
Ok(countries)
// TODO fetch the list of countries from some API
Ok(COUNTRIES.clone())
}
#[instrument(skip(state))]
@@ -97,3 +77,25 @@ pub async fn set_ui_theme(
.map_err(|e| CmdError::new(CmdErrorSource::InternalError, e.to_string()))?;
Ok(())
}
#[instrument(skip(data_state))]
#[tauri::command]
pub async fn set_entry_location_selector(
data_state: State<'_, SharedAppData>,
entry_selector: bool,
) -> Result<(), CmdError> {
debug!("set_entry_location_selector");
let mut app_data_store = data_state.lock().await;
let mut app_data = app_data_store
.read()
.await
.map_err(|e| CmdError::new(CmdErrorSource::InternalError, e.to_string()))?;
app_data.entry_location_selector = Some(entry_selector);
app_data_store.data = app_data;
app_data_store
.write()
.await
.map_err(|e| CmdError::new(CmdErrorSource::InternalError, e.to_string()))?;
Ok(())
}
+42 -14
View File
@@ -1,4 +1,5 @@
use futures::SinkExt;
use nym_vpn_lib::gateway_client::{EntryPoint, ExitPoint};
use nym_vpn_lib::{NymVpnCtrlMessage, NymVpnHandle};
use tauri::{Manager, State};
use tracing::{debug, error, info, instrument, trace};
@@ -16,6 +17,8 @@ use crate::{
},
};
const DEFAULT_NODE_LOCATION: &str = "DE";
#[instrument(skip_all)]
#[tauri::command]
pub async fn get_connection_state(
@@ -67,23 +70,48 @@ pub async fn connect(
)
.ok();
let app_config = config_store.lock().await.read().await.map_err(|e| {
CmdError::new(
CmdErrorSource::InternalError,
format!("failed to read app config: {}", e),
)
})?;
let mut vpn_config = create_vpn_config(&app_config);
{
let app_state = state.lock().await;
if let VpnMode::TwoHop = app_state.vpn_mode {
info!("2-hop mode enabled");
vpn_config.enable_two_hop = true;
} else {
info!("5-hop mode enabled");
let app_state = state.lock().await;
// get entry node location from app config file
let app_config = config_store
.lock()
.await
.read()
.await
.map_err(|e| CmdError::new(CmdErrorSource::InternalError, e.to_string()))?;
let entry_location = app_config
.entry_node_location
.clone()
.unwrap_or_else(|| DEFAULT_NODE_LOCATION.to_string());
// !! release config_store mutex
drop(app_config);
debug!("using entry node location: {}", entry_location);
let entry_point = EntryPoint::Location(entry_location);
let exit_point = match app_state.exit_node_location {
Some(ref exit_node_location) => {
debug!("exit node location set, using: {}", exit_node_location.code);
ExitPoint::Location(exit_node_location.code.clone())
}
_ => {
debug!(
"exit node location not set, using default: {}",
DEFAULT_NODE_LOCATION
);
ExitPoint::Location(DEFAULT_NODE_LOCATION.into())
}
};
let mut vpn_config = create_vpn_config(entry_point, exit_point);
if let VpnMode::TwoHop = app_state.vpn_mode {
info!("2-hop mode enabled");
vpn_config.enable_two_hop = true;
} else {
info!("5-hop mode enabled");
}
// vpn_config.disable_routing = true;
// !! release app_state mutex
drop(app_state);
// spawn the VPN client and start a new connection
let NymVpnHandle {
@@ -1,3 +1,4 @@
use once_cell::sync::Lazy;
use serde::{Deserialize, Serialize};
use tauri::State;
use tracing::{debug, instrument};
@@ -14,6 +15,11 @@ pub enum NodeType {
Exit,
}
static DEFAULT_NODE_LOCATION: Lazy<Country> = Lazy::new(|| Country {
code: "DE".to_string(),
name: "Germany".to_string(),
});
#[instrument(skip(app_state, data_state))]
#[tauri::command]
pub async fn set_node_location(
@@ -57,3 +63,10 @@ pub async fn set_node_location(
Ok(())
}
#[instrument]
#[tauri::command]
pub async fn get_default_node_location() -> Result<Country, CmdError> {
debug!("get_default_node_location");
Ok(DEFAULT_NODE_LOCATION.clone())
}
+25
View File
@@ -0,0 +1,25 @@
use once_cell::sync::Lazy;
use crate::states::app::Country;
// TODO use hardcoded country list for now
pub static COUNTRIES: Lazy<Vec<Country>> = Lazy::new(|| {
vec![
Country {
name: "Ireland".to_string(),
code: "IE".to_string(),
},
Country {
name: "Germany".to_string(),
code: "DE".to_string(),
},
Country {
name: "Japan".to_string(),
code: "JP".to_string(),
},
Country {
name: "Great Britain".to_string(),
code: "GB".to_string(),
},
]
});
+2 -4
View File
@@ -6,8 +6,6 @@ use serde::{Deserialize, Serialize};
pub struct AppConfig {
/// Path pointing to an env configuration file describing the network
pub env_config_file: Option<PathBuf>,
/// Mixnet public ID of the entry gateway
pub entry_gateway: String,
/// Mixnet recipient address
pub exit_router: String,
/// Country code (two letters format, eg. FR)
pub entry_node_location: Option<String>,
}
+1
View File
@@ -17,6 +17,7 @@ pub struct AppData {
pub monitoring: Option<bool>,
pub autoconnect: Option<bool>,
pub killswitch: Option<bool>,
pub entry_location_selector: Option<bool>,
pub ui_theme: Option<UiTheme>,
pub vpn_mode: Option<VpnMode>,
pub entry_node: Option<NodeConfig>,
+6 -1
View File
@@ -16,6 +16,7 @@ use nym_vpn_lib::nym_config;
use crate::fs::{config::AppConfig, data::AppData, storage::AppStorage};
mod commands;
mod country;
mod error;
mod fs;
mod states;
@@ -63,6 +64,8 @@ async fn main() -> Result<()> {
&app_config_store.full_path.display()
);
let app_data = app_data_store.read().await?;
debug!("app_data: {app_data:?}");
let app_config = app_config_store.read().await?;
debug!("app_config: {app_config:?}");
@@ -88,7 +91,7 @@ async fn main() -> Result<()> {
info!("Starting tauri app");
tauri::Builder::default()
.manage(Arc::new(Mutex::new(AppState::default())))
.manage(Arc::new(Mutex::new(AppState::from(&app_data))))
.manage(Arc::new(Mutex::new(app_data_store)))
.manage(Arc::new(Mutex::new(app_config_store)))
.setup(|_app| {
@@ -104,8 +107,10 @@ async fn main() -> Result<()> {
app_data::get_app_data,
app_data::set_app_data,
app_data::set_ui_theme,
app_data::set_entry_location_selector,
app_data::get_node_countries,
node_location::set_node_location,
node_location::get_default_node_location,
])
.run(tauri::generate_context!())
.expect("error while running tauri application");
+13
View File
@@ -4,6 +4,8 @@ use serde::{Deserialize, Serialize};
use time::OffsetDateTime;
use ts_rs::TS;
use crate::fs::data::AppData;
#[derive(Debug, Serialize, Deserialize, Clone, TS)]
#[ts(export)]
pub struct NodeConfig {
@@ -51,6 +53,17 @@ pub struct AppState {
pub vpn_ctrl_tx: Option<UnboundedSender<NymVpnCtrlMessage>>,
}
impl From<&AppData> for AppState {
fn from(app_data: &AppData) -> Self {
AppState {
entry_node_location: app_data.entry_node_location.clone(),
exit_node_location: app_data.exit_node_location.clone(),
vpn_mode: app_data.vpn_mode.clone().unwrap_or_default(),
..Default::default()
}
}
}
#[derive(Default, Serialize, Deserialize, Debug, Clone, TS)]
#[ts(export)]
pub struct Country {
+3 -4
View File
@@ -1,9 +1,8 @@
use crate::fs::config::AppConfig;
use crate::states::{app::ConnectionState, SharedAppState};
use anyhow::Result;
use futures::channel::oneshot::Receiver as OneshotReceiver;
use futures::StreamExt;
use nym_vpn_lib::gateway_client::Config as GatewayClientConfig;
use nym_vpn_lib::gateway_client::{Config as GatewayClientConfig, EntryPoint, ExitPoint};
use nym_vpn_lib::nym_config::OptionalSet;
use nym_vpn_lib::{NymVpn, NymVpnExitError, NymVpnExitStatusMessage, StatusReceiver};
use tauri::Manager;
@@ -166,8 +165,8 @@ fn setup_gateway_client_config(private_key: Option<&str>) -> GatewayClientConfig
}
#[instrument(skip_all)]
pub fn create_vpn_config(app_config: &AppConfig) -> NymVpn {
let mut nym_vpn = NymVpn::new(&app_config.entry_gateway, &app_config.exit_router);
pub fn create_vpn_config(entry_point: EntryPoint, exit_point: ExitPoint) -> NymVpn {
let mut nym_vpn = NymVpn::new(entry_point, exit_point);
nym_vpn.gateway_config = setup_gateway_client_config(None);
nym_vpn
}
+1 -1
View File
@@ -7,7 +7,7 @@
"withGlobalTauri": false
},
"package": {
"productName": "nym-vpn-ui",
"productName": "nym-vpn",
"version": "0.0.0"
},
"tauri": {
+1 -4
View File
@@ -1,5 +1,3 @@
import { Country } from './types';
export const routes = {
root: '/',
settings: '/settings',
@@ -10,5 +8,4 @@ export const routes = {
export const AppName = 'NymVPN';
export const ConnectionEvent = 'connection-state';
export const ProgressEvent = 'connection-progress';
//putting this here for now until decided how default country is determined
export const QuickConnectCountry: Country = { name: 'Germany', code: 'DE' };
export const QuickConnectPrefix = 'Fastest';
+21 -5
View File
@@ -1,7 +1,7 @@
import { mockIPC, mockWindows } from '@tauri-apps/api/mocks';
import { emit } from '@tauri-apps/api/event';
import { AppDataFromBackend, ConnectionState, Country } from '../types';
import { ConnectionEvent, QuickConnectCountry } from '../constants';
import { ConnectionEvent } from '../constants';
export function mockTauriIPC() {
mockWindows('main');
@@ -56,21 +56,37 @@ export function mockTauriIPC() {
);
}
if (cmd === 'get_default_node_location') {
return new Promise<Country>((resolve) =>
resolve({
name: 'France',
code: 'FR',
}),
);
}
if (cmd === 'get_app_data') {
return new Promise<AppDataFromBackend>((resolve) =>
resolve({
monitoring: false,
autoconnect: false,
killswitch: false,
entry_location_selector: false,
ui_theme: 'Dark',
vpn_mode: 'TwoHop',
entry_node: {
country: QuickConnectCountry,
id: QuickConnectCountry.code,
country: {
name: 'France',
code: 'FR',
},
id: 'nodeOne',
},
exit_node: {
country: QuickConnectCountry,
id: QuickConnectCountry.code,
country: {
name: 'France',
code: 'FR',
},
id: 'nodeTwo',
},
entry_node_location: null,
exit_node_location: null,
+2 -3
View File
@@ -1,7 +1,6 @@
{
"loading": "Loading..",
"none-found": "No results found!",
"none-found": "No results found. Please try another search",
"selected": "Selected",
"search-country": "Search country",
"quick-connect": "Quick connect"
"search-country": "Search country"
}
+3 -1
View File
@@ -1 +1,3 @@
{}
{
"entry-selector": "Entry location selector"
}
+18 -14
View File
@@ -55,7 +55,7 @@ function ConnectionStatus() {
}
};
return (
<div className="h-64 flex flex-col justify-center items-center gap-y-2">
<div className="h-72 flex flex-col justify-center items-center gap-y-2">
<div className="flex flex-1 items-end">
<div
className={clsx([
@@ -66,22 +66,26 @@ function ConnectionStatus() {
{getStatusText(state.state)}
</div>
</div>
<div className="flex flex-1">
{state.loading && state.progressMessages.length > 0 && (
<p className="text-dim-gray dark:text-mercury-mist font-bold">
{t(
`connection-progress.${
state.progressMessages[state.progressMessages.length - 1]
}`,
{
ns: 'backendMessages',
},
)}
</p>
<div className="w-full flex flex-col flex-1 items-center overflow-hidden">
{state.loading && state.progressMessages.length > 0 && !state.error && (
<div className="w-4/5 h-2/3 overflow-scroll break-words text-center">
<p className="text-dim-gray dark:text-mercury-mist font-bold">
{t(
`connection-progress.${
state.progressMessages[state.progressMessages.length - 1]
}`,
{
ns: 'backendMessages',
},
)}
</p>
</div>
)}
{state.state === 'Connected' && <ConnectionTimer />}
{state.error && (
<p className="text-teaberry font-bold">{state.error}</p>
<div className="w-4/5 h-2/3 overflow-scroll break-words text-center">
<p className="text-teaberry font-bold">{state.error}</p>
</div>
)}
</div>
</div>
+25 -16
View File
@@ -5,14 +5,15 @@ import clsx from 'clsx';
import { Button } from '@mui/base';
import { useNavigate } from 'react-router-dom';
import { useMainDispatch, useMainState } from '../../contexts';
import { StateDispatch } from '../../types';
import { QuickConnectCountry, routes } from '../../constants';
import { CmdError, StateDispatch } from '../../types';
import { routes } from '../../constants';
import NetworkModeSelect from './NetworkModeSelect';
import ConnectionStatus from './ConnectionStatus';
import HopSelect from './HopSelect';
function Home() {
const { state, loading, exitNodeLocation } = useMainState();
const { state, loading, exitNodeLocation, defaultNodeLocation } =
useMainState();
const dispatch = useMainDispatch() as StateDispatch;
const navigate = useNavigate();
const { t } = useTranslation('home');
@@ -25,9 +26,9 @@ function Home() {
console.log('disconnect result');
console.log(result);
})
.catch((e) => {
console.log(e);
dispatch({ type: 'set-error', error: e });
.catch((e: CmdError) => {
console.warn(`backend error: ${e.source} - ${e.message}`);
dispatch({ type: 'set-error', error: e.message });
});
} else if (state === 'Disconnected') {
dispatch({ type: 'connect' });
@@ -36,9 +37,9 @@ function Home() {
console.log('connect result');
console.log(result);
})
.catch((e) => {
console.log(e);
dispatch({ type: 'set-error', error: e });
.catch((e: CmdError) => {
console.warn(`backend error: ${e.source} - ${e.message}`);
dispatch({ type: 'set-error', error: e.message });
});
}
};
@@ -70,14 +71,22 @@ function Home() {
<div className="h-full flex flex-col p-4">
<ConnectionStatus />
<div className="flex grow flex-col justify-between gap-y-2">
<div className="flex flex-col justify-between">
<div className="flex flex-col justify-between gap-y-4">
<NetworkModeSelect />
<div className="py-2"></div>
<HopSelect
country={exitNodeLocation ?? QuickConnectCountry}
onClick={() => navigate(routes.exitNodeLocation)}
nodeHop="exit"
/>
<div className="flex flex-col gap-4">
<div className="mt-3 font-semibold text-lg">
{t('select-node-title')}
</div>
<HopSelect
country={exitNodeLocation || defaultNodeLocation}
onClick={() => {
if (state === 'Disconnected') {
navigate(routes.exitNodeLocation);
}
}}
nodeHop="exit"
/>
</div>
</div>
<Button
className={clsx([
+29 -24
View File
@@ -1,5 +1,7 @@
import { useTranslation } from 'react-i18next';
import clsx from 'clsx';
import { Country, NodeHop } from '../../types';
import { useMainState } from '../../contexts';
interface HopSelectProps {
country: Country;
@@ -12,38 +14,41 @@ export default function HopSelect({
country,
onClick,
}: HopSelectProps) {
const { state } = useMainState();
const { t } = useTranslation('home');
return (
<>
<div className="my-3 font-semibold text-lg">Connect to</div>
<div
className={clsx([
state === 'Disconnected' ? 'cursor-pointer' : 'cursor-not-allowed',
'w-full flex flex-row justify-between items-center py-3 px-4',
'text-baltic-sea dark:text-mercury-pinkish',
'border-cement-feet dark:border-gun-powder border-2 rounded-lg',
'relative',
])}
onKeyDown={onClick}
role="presentation"
onClick={onClick}
>
<div
className="relative w-full flex flex-row justify-center cursor-pointer"
onKeyDown={onClick}
role="presentation"
onClick={onClick}
className={clsx([
'absolute left-3 -top-3 px-1',
'bg-blanc-nacre dark:bg-baltic-sea text-sm',
])}
>
<input
readOnly={true}
type="text"
id="floating_outlined"
value={country.name}
className="dark:bg-baltic-sea cursor-pointer pl-11 dark:placeholder-white border border-gun-powder block px-2.5 pb-4 pt-4 w-full text-sm text-gray-900 bg-transparent rounded-lg border-1 border-gray-300 appearance-none dark:text-white dark:border-gray-600 dark:focus:border-blue-500 focus:outline-none focus:ring-0 focus:border-blue-600 peer"
/>
{nodeHop === 'entry' ? t('first-hop') : t('last-hop')}
</div>
<div className="flex flex-row items-center gap-3">
<img
src={`./flags/${country.code.toLowerCase()}.svg`}
className="h-8 scale-75 pointer-events-none absolute fill-current top-1/2 transform -translate-y-1/2 left-2"
className="h-8 scale-90 pointer-events-none fill-current"
alt={country.code}
/>
<span className="font-icon scale-125 pointer-events-none absolute fill-current top-1/4 transform -translate-x-1/2 right-2">
arrow_right
</span>
<label
htmlFor="floating_outlined"
className="dark:text-white bg-blanc-nacre dark:bg-baltic-sea absolute text-sm text-gray-500 dark:text-gray-400 ml-4 duration-300 transform -translate-y-4 scale-75 top-2 z-10 origin-[0] dark:bg-gray-900 px-2 peer-placeholder-shown:px-2 peer-placeholder-shown:text-blue-600 peer-placeholder-shown:dark:text-blue-500 peer-placeholder-shown:top-2 peer-placeholder-shown:scale-75 peer-placeholder-shown:-translate-y-4 rtl:peer-placeholder-shown:translate-x-1/4 rtl:peer-placeholder-shown:left-auto start-1"
>
{nodeHop === 'entry' ? t('first-hop') : t('last-hop')}
</label>
<div className="text-base">{country.name}</div>
</div>
</>
<span className="font-icon scale-150 pointer-events-none">
arrow_right
</span>
</div>
);
}
@@ -55,11 +55,11 @@ function NetworkModeSelect() {
};
return (
<div className="">
<div>
<RadioGroup value={selected} onChange={handleSelect}>
<RadioGroup.Label
as="div"
className="font-semibold text-lg text-baltic-sea dark:text-white mb-4"
className="font-semibold text-lg text-baltic-sea dark:text-white mb-5"
>
{t('select-network-label')}
</RadioGroup.Label>
@@ -102,7 +102,7 @@ function NetworkModeSelect() {
</RadioGroup.Label>
<RadioGroup.Description
as="span"
className="text-base text-ciment-feet dark:text-mercury-mist"
className="text-base text-cement-feet dark:text-mercury-mist"
>
<span>{mode.desc}</span>
</RadioGroup.Description>
@@ -12,14 +12,14 @@ export default function CountryList({
}: CountryListProps) {
const { t } = useTranslation('nodeLocation');
return (
<ul className="flex flex-col w-full items-stretch p-1">
<ul className="flex flex-col w-full items-stretch gap-4">
{countries && countries.length > 0 ? (
countries.map((country) => (
<li key={country.code} className="list-none w-full">
<div
role="presentation"
onKeyDown={() => onClick(country.name, country.code)}
className="flex flex-row justify-between dark:hover:bg-baltic-sea-jaguar hover:bg-coal-mine-light rounded-lg cursor-pointer"
className="flex flex-row justify-between dark:hover:bg-gun-powder hover:dark:bg-opacity-80 hover:bg-cement-feet hover:bg-opacity-20 rounded-lg cursor-pointer px-3"
onClick={() => onClick(country.name, country.code)}
>
<div className="flex flex-row items-center m-1 gap-3 p-1 cursor-pointer">
@@ -32,14 +32,14 @@ export default function CountryList({
{country.name}
</div>
</div>
<div className="p-4 flex items-center text-mercury-mist text-xs cursor-pointer">
<div className="p-4 flex items-center text-mercury-mist text-sm cursor-pointer">
{isSelected(country.code) ? t('selected') : ''}
</div>
</div>
</li>
))
) : (
<p>{t('none-found')}</p>
<p className="flex justify-center">{t('none-found')}</p>
)}
</ul>
);
+19 -31
View File
@@ -11,28 +11,22 @@ import QuickConnect from './QuickConnect';
function NodeLocation({ node }: { node: NodeHop }) {
const { t } = useTranslation('nodeLocation');
const [countries, setCountries] = useState<Country[]>([]);
const { entryNodeLocation, exitNodeLocation, countries } = useMainState();
const [search, setSearch] = useState('');
const [loading, setLoading] = useState(false);
const [foundCountries, setFoundCountries] = useState<Country[]>([]);
const [foundCountries, setFoundCountries] = useState<Country[]>(countries);
const { entryNodeLocation, exitNodeLocation } = useMainState();
const dispatch = useMainDispatch() as StateDispatch;
const navigate = useNavigate();
//request backend to update countries cache
useEffect(() => {
setLoading(true);
const getNodeCountries = async () => {
const countries = await invoke<Country[]>('get_node_countries');
setTimeout(() => {
setCountries(countries);
setFoundCountries(countries);
setLoading(false);
}, 1000);
dispatch({ type: 'set-countries', countries });
};
getNodeCountries().catch(console.error);
}, []);
}, [dispatch]);
const filter = (e: InputEvent) => {
const keyword = e.target.value;
@@ -72,26 +66,20 @@ function NodeLocation({ node }: { node: NodeHop }) {
};
return (
<div>
<div className="h-full flex flex-col">
<div className="h-70 flex flex-col justify-center items-center gap-y-2 p-1">
<QuickConnect onClick={handleCountrySelection} />
<SearchBox
value={search}
onChange={filter}
placeholder={t('search-country')}
/>
<span className="mt-3" />
{!loading ? (
<CountryList
countries={foundCountries}
onClick={handleCountrySelection}
isSelected={isCountrySelected}
/>
) : (
<div>{t('loading')}</div>
)}
</div>
<div className="h-full flex flex-col p-4">
<div className="h-70 flex flex-col justify-center items-center gap-y-2">
<QuickConnect onClick={handleCountrySelection} />
<SearchBox
value={search}
onChange={filter}
placeholder={t('search-country')}
/>
<span className="mt-3" />
<CountryList
countries={foundCountries}
onClick={handleCountrySelection}
isSelected={isCountrySelected}
/>
</div>
</div>
);
+11 -10
View File
@@ -1,25 +1,26 @@
import { useTranslation } from 'react-i18next';
import { QuickConnectCountry } from '../../constants';
import { useMainState } from '../../contexts';
import { QuickConnectPrefix } from '../../constants';
interface QuickConnectProps {
onClick: (name: string, code: string) => void;
}
export default function QuickConnect({ onClick }: QuickConnectProps) {
const { t } = useTranslation('nodeLocation');
const { defaultNodeLocation } = useMainState();
return (
<div
role="presentation"
onKeyDown={() =>
onClick(QuickConnectCountry.name, QuickConnectCountry.code)
onClick(defaultNodeLocation.name, defaultNodeLocation.code)
}
className="flex flex-row w-full py-8 cursor-pointer"
className="flex px-1 flex-row items-center w-full py-8 cursor-pointer"
onClick={() =>
onClick(QuickConnectCountry.name, QuickConnectCountry.code)
onClick(defaultNodeLocation.name, defaultNodeLocation.code)
}
>
<span className="font-icon px-4 cursor-pointer">bolt</span>
<div className="cursor-pointer">{`${t('quick-connect')} (${
QuickConnectCountry.name
})`}</div>
<span className="font-icon text-2xl px-4 cursor-pointer">bolt</span>
<div className="cursor-pointer">{`${QuickConnectPrefix} ${defaultNodeLocation.name}`}</div>
</div>
);
}
+17 -10
View File
@@ -1,3 +1,4 @@
import clsx from 'clsx';
import { InputEvent } from '../../types';
interface SearchProps {
@@ -12,24 +13,30 @@ export default function SearchBox({
placeholder,
}: SearchProps) {
return (
<div className="relative w-full flex flex-row justify-center px-1.5">
<div className="w-full relative flex flex-row items-center px-4 mb-2">
<input
type="text"
id="floating_outlined"
id="country_search"
value={value}
className="dark:bg-baltic-sea pl-9 dark:placeholder-white border border-gun-powder block px-2.5 pb-4 pt-4 w-full text-sm text-gray-900 bg-transparent rounded-lg border-1 border-gray-300 appearance-none dark:text-white dark:border-gray-600 dark:focus:border-blue-500 focus:outline-none focus:ring-0 focus:border-blue-600 peer"
className={clsx([
'bg-blanc-nacre dark:bg-baltic-sea focus:outline-none focus:ring-0',
'w-full flex flex-row justify-between items-center py-3 px-4 pl-11',
'text-baltic-sea dark:text-mercury-pinkish',
'placeholder:text-cement-feet placeholder:dark:text-mercury-mist',
'border-cement-feet dark:border-gun-powder border-2 rounded-lg',
'relative text-base',
])}
placeholder={placeholder}
onChange={onChange}
/>
<span className="font-icon scale-125 pointer-events-none absolute fill-current top-1/2 transform -translate-y-1/2 left-4">
<span
className={clsx([
'font-icon text-2xl absolute left-7',
'text-baltic-sea dark:text-laughing-jack',
])}
>
search
</span>
<label
htmlFor="floating_outlined"
className="dark:text-white dark:bg-baltic-sea absolute text-sm text-gray-500 dark:text-gray-400 ml-4 duration-300 transform -translate-y-4 scale-75 top-2 z-10 origin-[0] bg-blanc-nacre dark:bg-gray-900 px-2 peer-placeholder-shown:px-2 peer-placeholder-shown:text-blue-600 peer-placeholder-shown:dark:text-blue-500 peer-placeholder-shown:top-2 peer-placeholder-shown:scale-75 peer-placeholder-shown:-translate-y-4 rtl:peer-placeholder-shown:translate-x-1/4 rtl:peer-placeholder-shown:left-auto start-1"
>
Search
</label>
</div>
);
}
+12 -8
View File
@@ -9,11 +9,13 @@ function Settings() {
const state = useMainState();
const dispatch = useMainDispatch() as StateDispatch;
const [enabled, setEnabled] = useState(state.uiTheme === 'Dark');
const [darkModeEnabled, setDarkModeEnabled] = useState(
state.uiTheme === 'Dark',
);
useEffect(() => {
setEnabled(state.uiTheme === 'Dark');
}, [state.uiTheme]);
setDarkModeEnabled(state.uiTheme === 'Dark');
}, [state]);
const handleThemeChange = async (darkMode: boolean) => {
if (darkMode && state.uiTheme === 'Light') {
@@ -29,24 +31,26 @@ function Settings() {
};
return (
<div className="h-full flex flex-col p-4">
<div className="h-full flex flex-col p-4 gap-4">
<div className="flex flex-row justify-between items-center">
<p className="text-lg text-baltic-sea dark:text-mercury-pinkish">
Dark Mode
</p>
<Switch
checked={enabled}
checked={darkModeEnabled}
onChange={handleThemeChange}
className={clsx([
enabled ? 'bg-melon' : 'bg-mercury-pinkish dark:bg-gun-powder',
darkModeEnabled
? 'bg-melon'
: 'bg-mercury-pinkish dark:bg-gun-powder',
'relative inline-flex h-6 w-11 items-center rounded-full',
])}
>
<span className="sr-only">Dark mode</span>
<span
className={clsx([
enabled ? 'translate-x-6' : 'translate-x-1',
'inline-block h-4 w-4 transform rounded-full bg-ciment-feet dark:bg-white transition',
darkModeEnabled ? 'translate-x-6' : 'translate-x-1',
'inline-block h-4 w-4 transform rounded-full bg-cement-feet dark:bg-white transition',
])}
/>
</Switch>
+26 -2
View File
@@ -13,6 +13,7 @@ export type StateAction =
| { type: 'set-partial-state'; partialState: Partial<AppState> }
| { type: 'change-connection-state'; state: ConnectionState }
| { type: 'set-vpn-mode'; mode: VpnMode }
| { type: 'set-entry-selector'; entrySelector: boolean }
| { type: 'set-error'; error: string }
| { type: 'reset-error' }
| { type: 'new-progress-message'; message: ConnectProgressMsg }
@@ -23,17 +24,25 @@ export type StateAction =
| { type: 'set-disconnected' }
| { type: 'reset' }
| { type: 'set-ui-theme'; theme: UiTheme }
| { type: 'set-node-location'; payload: { hop: NodeHop; country: Country } };
| { type: 'set-countries'; countries: Country[] }
| { type: 'set-node-location'; payload: { hop: NodeHop; country: Country } }
| { type: 'set-default-node-location'; country: Country };
export const initialState: AppState = {
state: 'Disconnected',
loading: false,
vpnMode: 'Mixnet',
vpnMode: 'TwoHop',
entrySelector: false,
tunnel: { name: 'nym', id: 'nym' },
uiTheme: 'Light',
progressMessages: [],
entryNodeLocation: null,
exitNodeLocation: null,
defaultNodeLocation: {
name: 'France',
code: 'FR',
},
countries: [],
};
export function reducer(state: AppState, action: StateAction): AppState {
@@ -54,6 +63,16 @@ export function reducer(state: AppState, action: StateAction): AppState {
...state,
vpnMode: action.mode,
};
case 'set-entry-selector':
return {
...state,
entrySelector: action.entrySelector,
};
case 'set-countries':
return {
...state,
countries: action.countries,
};
case 'set-partial-state': {
return { ...state, ...action.partialState };
}
@@ -121,6 +140,11 @@ export function reducer(state: AppState, action: StateAction): AppState {
...state,
uiTheme: action.theme,
};
case 'set-default-node-location':
return {
...state,
defaultNodeLocation: action.country,
};
case 'reset':
return initialState;
}
+70 -17
View File
@@ -1,8 +1,12 @@
import React, { useEffect, useReducer } from 'react';
import { invoke } from '@tauri-apps/api';
import { MainDispatchContext, MainStateContext } from '../contexts';
import { AppDataFromBackend, CmdError, ConnectionState } from '../types';
import { QuickConnectCountry } from '../constants';
import {
AppDataFromBackend,
CmdError,
ConnectionState,
Country,
} from '../types';
import { initialState, reducer } from './main';
import { useTauriEvents } from './useTauriEvents';
@@ -26,12 +30,53 @@ export function MainStateProvider({ children }: Props) {
return await invoke<number | undefined>('get_connection_start_time');
};
getInitialConnectionState().then((state) =>
dispatch({ type: 'change-connection-state', state }),
);
getSessionStartTime().then((startTime) =>
dispatch({ type: 'set-connection-start-time', startTime }),
);
// init country list
const getCountries = async () => {
return await invoke<Country[]>('get_node_countries');
};
// init default node location
const getDefaultNodeLocation = async () => {
return await invoke<Country>('get_default_node_location');
};
getInitialConnectionState()
.then((state) => dispatch({ type: 'change-connection-state', state }))
.catch((e: CmdError) => {
console.warn(
`command [get_connection_state] returned an error: ${e.source} - ${e.message}`,
);
});
getSessionStartTime()
.then((startTime) =>
dispatch({ type: 'set-connection-start-time', startTime }),
)
.catch((e: CmdError) => {
console.warn(
`command [get_connection_start_time] returned an error: ${e.source} - ${e.message}`,
);
});
getCountries()
.then((countries) => {
dispatch({ type: 'set-countries', countries });
})
.catch((e: CmdError) => {
console.warn(
`command [get_node_countries] returned an error: ${e.source} - ${e.message}`,
);
});
getDefaultNodeLocation()
.then((country) => {
dispatch({ type: 'set-default-node-location', country });
})
.catch((e: CmdError) => {
console.warn(
`command [get_default_node_location] returned an error: ${e.source} - ${e.message}`,
);
});
}, []);
// get saved on disk app data and restore state from it
@@ -42,20 +87,28 @@ export function MainStateProvider({ children }: Props) {
getAppData()
.then((data) => {
console.log('app data read from disk:');
console.log(data);
const partialState: Partial<typeof initialState> = {
entrySelector: data.entry_location_selector || false,
uiTheme: data.ui_theme || 'Light',
vpnMode: data.vpn_mode || 'TwoHop',
};
if (data.entry_node_location) {
partialState.entryNodeLocation = data.entry_node_location;
}
if (data.exit_node_location) {
partialState.exitNodeLocation = data.exit_node_location;
}
dispatch({
type: 'set-partial-state',
partialState: {
uiTheme: data.ui_theme || 'Light',
vpnMode: data.vpn_mode || 'TwoHop',
entryNodeLocation: data.entry_node_location || QuickConnectCountry,
exitNodeLocation: data.exit_node_location || QuickConnectCountry,
},
partialState,
});
})
.catch((err: CmdError) => {
// TODO handle error properly
console.log(err);
.catch((e: CmdError) => {
console.warn(
`command [get_app_data] returned an error: ${e.source} - ${e.message}`,
);
});
}, []);
+1
View File
@@ -17,6 +17,7 @@ export interface AppDataFromBackend {
monitoring: boolean | null;
autoconnect: boolean | null;
killswitch: boolean | null;
entry_location_selector: boolean | null;
ui_theme: UiTheme | null;
vpn_mode: VpnMode | null;
entry_node: NodeConfig | null;
+3
View File
@@ -26,8 +26,11 @@ export type AppState = {
vpnMode: VpnMode;
tunnel: TunnelConfig;
uiTheme: 'Light' | 'Dark';
entrySelector: boolean;
entryNodeLocation: Country | null;
exitNodeLocation: Country | null;
defaultNodeLocation: Country;
countries: Country[];
};
export type ConnectionEventPayload = {
+1 -1
View File
@@ -62,7 +62,7 @@ export default {
'dim-gray': '#696571',
// [L] network mode desc text
// [L] node location select outline
'ciment-feet': '#79747E',
'cement-feet': '#79747E',
// [D] node location select outline
'gun-powder': '#49454F',
// [D] top-bar icon
+1
View File
@@ -3,6 +3,7 @@ name = "nym-wallet-types"
version = "1.0.0"
edition = "2021"
rust-version = "1.58"
license = "Apache-2.0"
[dependencies]
hex-literal = "0.3.3"
+1
View File
@@ -2,6 +2,7 @@
name = "nym-socks5-listener"
version = "0.1.0"
edition = "2021"
license.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+1
View File
@@ -2,6 +2,7 @@
name = "nym-sdk"
version = "0.1.0"
edition = "2021"
license.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

Some files were not shown because too many files have changed in this diff Show More