refactor(nc-desktop): add privacy level user settings (#3664)
This commit is contained in:
@@ -1,104 +0,0 @@
|
||||
name: Publish Nym Connect - desktop (MacOS)
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
release_tag:
|
||||
required: true
|
||||
description: Release tag (nym-connect-s-v*)
|
||||
type: string
|
||||
default: 'nym-connect-s-v1.0.0'
|
||||
push:
|
||||
tags:
|
||||
- nym-connect-s-v*
|
||||
|
||||
defaults:
|
||||
run:
|
||||
working-directory: nym-connect/desktop
|
||||
|
||||
jobs:
|
||||
publish-tauri:
|
||||
if: ${{ (startsWith(github.ref, 'refs/tags/nym-connect-s-') && github.event_name == 'release') || github.event_name == 'workflow_dispatch' }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
platform: [macos-latest]
|
||||
runs-on: ${{ matrix.platform }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Node
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 18
|
||||
- name: Install Rust stable
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: stable
|
||||
- name: Install the Apple developer certificate for code signing
|
||||
env:
|
||||
APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }}
|
||||
APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
|
||||
KEYCHAIN_PASSWORD: ${{ secrets.KEYCHAIN_PASSWORD }}
|
||||
run: |
|
||||
# create variables
|
||||
CERTIFICATE_PATH=$RUNNER_TEMP/build_certificate.p12
|
||||
KEYCHAIN_PATH=$RUNNER_TEMP/app-signing.keychain-db
|
||||
|
||||
# import certificate and provisioning profile from secrets
|
||||
echo -n "$APPLE_CERTIFICATE" | base64 --decode --output $CERTIFICATE_PATH
|
||||
|
||||
# create temporary keychain
|
||||
security create-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH
|
||||
security set-keychain-settings -lut 21600 $KEYCHAIN_PATH
|
||||
security unlock-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH
|
||||
|
||||
# import certificate to keychain
|
||||
security import $CERTIFICATE_PATH -P "$APPLE_CERTIFICATE_PASSWORD" -A -t cert -f pkcs12 -k $KEYCHAIN_PATH
|
||||
security list-keychain -d user -s $KEYCHAIN_PATH
|
||||
|
||||
- name: Create env file
|
||||
uses: timheuer/base64-to-file@v1.2
|
||||
with:
|
||||
fileName: '.env'
|
||||
encodedString: ${{ secrets.WALLET_ADMIN_ADDRESS }}
|
||||
|
||||
- name: Prepare for build
|
||||
env:
|
||||
RELEASE_TAG: ${{ github.ref_name || inputs.release_tag }}
|
||||
run: ./scripts/pre_medium_build.sh
|
||||
|
||||
- name: Install app dependencies and build it
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
ENABLE_CODE_SIGNING: ${{ secrets.APPLE_CERTIFICATE }}
|
||||
APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }}
|
||||
APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
|
||||
APPLE_SIGNING_IDENTITY: ${{ secrets.APPLE_IDENTITY_ID }}
|
||||
APPLE_ID: ${{ secrets.APPLE_ID }}
|
||||
APPLE_PASSWORD: ${{ secrets.APPLE_PASSWORD }}
|
||||
TAURI_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }}
|
||||
TAURI_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }}
|
||||
NYM_CONNECT_ENABLE_MEDIUM: 1
|
||||
run: yarn && yarn build
|
||||
|
||||
- name: Upload Artifact
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: nym-connect-s_1.0.0_x64.dmg
|
||||
path: nym-connect/desktop/target/release/bundle/dmg/nym-connect-s_1*_x64.dmg
|
||||
retention-days: 30
|
||||
|
||||
- name: Clean up keychain
|
||||
if: ${{ always() }}
|
||||
run: |
|
||||
security delete-keychain $RUNNER_TEMP/app-signing.keychain-db
|
||||
|
||||
- id: create-release
|
||||
name: Upload to release based on tag name
|
||||
uses: softprops/action-gh-release@v1
|
||||
if: github.event_name == 'release'
|
||||
with:
|
||||
files: |
|
||||
nym-connect/desktop/target/release/bundle/dmg/*.dmg
|
||||
nym-connect/desktop/target/release/bundle/macos/*.app.tar.gz*
|
||||
@@ -1,85 +0,0 @@
|
||||
name: Publish NymConnect S - desktop (Ubuntu)
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
release_tag:
|
||||
required: true
|
||||
description: Release tag (nym-connect-s-v*)
|
||||
type: string
|
||||
default: 'nym-connect-s-v1.0.0'
|
||||
push:
|
||||
tags:
|
||||
- nym-connect-s-v*
|
||||
|
||||
defaults:
|
||||
run:
|
||||
working-directory: nym-connect/desktop
|
||||
|
||||
jobs:
|
||||
publish-tauri:
|
||||
if: ${{ (startsWith(github.ref, 'refs/tags/nym-connect-s-') && github.event_name == 'release') || github.event_name == 'workflow_dispatch' }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
platform: [custom-runner-linux]
|
||||
runs-on: ${{ matrix.platform }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Tauri dependencies
|
||||
run: >
|
||||
sudo apt-get update &&
|
||||
sudo apt-get install -y webkit2gtk-4.0 libayatana-appindicator3-dev
|
||||
continue-on-error: true
|
||||
|
||||
- name: Node
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 18
|
||||
|
||||
- name: Install Rust stable
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: stable
|
||||
|
||||
- name: Install project dependencies
|
||||
shell: bash
|
||||
run: cd .. && yarn --network-timeout 100000
|
||||
|
||||
- name: Install app dependencies
|
||||
run: yarn
|
||||
|
||||
- name: Create env file
|
||||
uses: timheuer/base64-to-file@v1.2
|
||||
with:
|
||||
fileName: '.env'
|
||||
encodedString: ${{ secrets.WALLET_ADMIN_ADDRESS }}
|
||||
|
||||
- name: Prepare for build
|
||||
env:
|
||||
RELEASE_TAG: ${{ github.ref_name || inputs.release_tag }}
|
||||
run: ./scripts/pre_medium_build.sh
|
||||
|
||||
- name: Build app
|
||||
run: yarn build
|
||||
env:
|
||||
TAURI_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }}
|
||||
TAURI_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }}
|
||||
NYM_CONNECT_ENABLE_MEDIUM: 1
|
||||
|
||||
- name: Upload Artifact
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: nym-connect-s.AppImage.tar.gz
|
||||
path: nym-connect/desktop/target/release/bundle/appimage/nym-connect-s_1*_amd64.AppImage
|
||||
retention-days: 30
|
||||
|
||||
- id: create-release
|
||||
name: Upload to release based on tag name
|
||||
uses: softprops/action-gh-release@v1
|
||||
if: github.event_name == 'release'
|
||||
with:
|
||||
files: |
|
||||
nym-connect/desktop/target/release/bundle/appimage/*.AppImage
|
||||
nym-connect/desktop/target/release/bundle/appimage/*.AppImage.tar.gz*
|
||||
@@ -1,104 +0,0 @@
|
||||
name: Publish Nym Connect - desktop (Windows 10)
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
release_tag:
|
||||
required: true
|
||||
description: Release tag (nym-connect-s-v*)
|
||||
type: string
|
||||
default: 'nym-connect-s-v1.0.0'
|
||||
push:
|
||||
tags:
|
||||
- nym-connect-s-v*
|
||||
|
||||
defaults:
|
||||
run:
|
||||
working-directory: nym-connect/desktop
|
||||
|
||||
jobs:
|
||||
publish-tauri:
|
||||
if: ${{ (startsWith(github.ref, 'refs/tags/nym-connect-s-') && github.event_name == 'release') || github.event_name == 'workflow_dispatch' }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
platform: [windows10]
|
||||
runs-on: ${{ matrix.platform }}
|
||||
|
||||
steps:
|
||||
- name: Clean up first
|
||||
continue-on-error: true
|
||||
working-directory: .
|
||||
run: |
|
||||
cd ..
|
||||
del /s /q /A:H nym
|
||||
rmdir /s /q nym
|
||||
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Import signing certificate
|
||||
env:
|
||||
WINDOWS_CERTIFICATE: ${{ secrets.WINDOWS_CERTIFICATE }}
|
||||
WINDOWS_CERTIFICATE_PASSWORD: ${{ secrets.WINDOWS_CERTIFICATE_PASSWORD }}
|
||||
run: |
|
||||
New-Item -ItemType directory -Path certificate
|
||||
Set-Content -Path certificate/tempCert.txt -Value $env:WINDOWS_CERTIFICATE
|
||||
certutil -decode certificate/tempCert.txt certificate/certificate.pfx
|
||||
Remove-Item -path certificate -include tempCert.txt
|
||||
Import-PfxCertificate -FilePath certificate/certificate.pfx -CertStoreLocation Cert:\CurrentUser\My -Password (ConvertTo-SecureString -String $env:WINDOWS_CERTIFICATE_PASSWORD -Force -AsPlainText)
|
||||
|
||||
- name: Node
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 18
|
||||
|
||||
- name: Install Rust stable
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: stable
|
||||
|
||||
- name: Create env file
|
||||
uses: timheuer/base64-to-file@v1.2
|
||||
with:
|
||||
fileName: '.env'
|
||||
encodedString: ${{ secrets.WALLET_ADMIN_ADDRESS }}
|
||||
|
||||
- name: Install project dependencies
|
||||
shell: bash
|
||||
run: cd .. && yarn --network-timeout 100000
|
||||
|
||||
- name: Install app dependencies
|
||||
shell: bash
|
||||
run: yarn --network-timeout 100000
|
||||
|
||||
- name: Prepare for build
|
||||
env:
|
||||
RELEASE_TAG: ${{ github.ref_name || inputs.release_tag }}
|
||||
run: ./scripts/pre_medium_build.sh
|
||||
|
||||
- name: Build and sign it
|
||||
shell: bash
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
ENABLE_CODE_SIGNING: ${{ secrets.WINDOWS_CERTIFICATE }}
|
||||
WINDOWS_CERTIFICATE: ${{ secrets.WINDOWS_CERTIFICATE }}
|
||||
WINDOWS_CERTIFICATE_PASSWORD: ${{ secrets.WINDOWS_CERTIFICATE_PASSWORD }}
|
||||
TAURI_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }}
|
||||
TAURI_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }}
|
||||
NYM_CONNECT_ENABLE_MEDIUM: 1
|
||||
run: yarn build
|
||||
|
||||
- name: Upload Artifact
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: nym-connect-s_1.0.0_x64_en-US.msi
|
||||
path: nym-connect/desktop/target/release/bundle/msi/nym-connect-s_1*_x64_en-US.msi
|
||||
retention-days: 30
|
||||
|
||||
- id: create-release
|
||||
name: Upload to release based on tag name
|
||||
uses: softprops/action-gh-release@v1
|
||||
if: github.event_name == 'release'
|
||||
with:
|
||||
files: |
|
||||
nym-connect/desktop/target/release/bundle/msi/*.msi
|
||||
nym-connect/desktop/target/release/bundle/msi/*.msi.zip*
|
||||
Generated
+35
-12
@@ -1532,6 +1532,12 @@ dependencies = [
|
||||
"termcolor",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "equivalent"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "88bffebc5d80432c9b140ee17875ff173a8ab62faad5b257da912bd2f6c1c0a1"
|
||||
|
||||
[[package]]
|
||||
name = "errno"
|
||||
version = "0.2.8"
|
||||
@@ -2217,7 +2223,7 @@ dependencies = [
|
||||
"futures-sink",
|
||||
"futures-util",
|
||||
"http",
|
||||
"indexmap",
|
||||
"indexmap 1.9.2",
|
||||
"slab",
|
||||
"tokio",
|
||||
"tokio-util",
|
||||
@@ -2256,6 +2262,12 @@ dependencies = [
|
||||
"ahash",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.14.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a"
|
||||
|
||||
[[package]]
|
||||
name = "hashlink"
|
||||
version = "0.7.0"
|
||||
@@ -2618,6 +2630,16 @@ dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d"
|
||||
dependencies = [
|
||||
"equivalent",
|
||||
"hashbrown 0.14.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "infer"
|
||||
version = "0.7.0"
|
||||
@@ -3462,6 +3484,7 @@ dependencies = [
|
||||
"thiserror",
|
||||
"time",
|
||||
"tokio",
|
||||
"toml 0.7.4",
|
||||
"ts-rs",
|
||||
"url",
|
||||
"yaml-rust",
|
||||
@@ -4530,7 +4553,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9bd9647b268a3d3e14ff09c23201133a62589c658db02bb7388c7246aafe0590"
|
||||
dependencies = [
|
||||
"base64 0.21.2",
|
||||
"indexmap",
|
||||
"indexmap 1.9.2",
|
||||
"line-wrap",
|
||||
"quick-xml 0.28.1",
|
||||
"serde",
|
||||
@@ -5129,7 +5152,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "02c613288622e5f0c3fdc5dbd4db1c5fbe752746b1d1a56a0630b78fd00de44f"
|
||||
dependencies = [
|
||||
"dyn-clone",
|
||||
"indexmap",
|
||||
"indexmap 1.9.2",
|
||||
"schemars_derive",
|
||||
"serde",
|
||||
"serde_json",
|
||||
@@ -5345,9 +5368,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_spanned"
|
||||
version = "0.6.2"
|
||||
version = "0.6.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "93107647184f6027e3b7dcb2e11034cf95ffa1e3a682c67951963ac69c1c007d"
|
||||
checksum = "96426c9936fd7a0124915f9185ea1d20aa9445cc9821142f0a73bc9207a2e186"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
@@ -5677,7 +5700,7 @@ dependencies = [
|
||||
"futures-util",
|
||||
"hashlink 0.7.0",
|
||||
"hex",
|
||||
"indexmap",
|
||||
"indexmap 1.9.2",
|
||||
"itoa 1.0.6",
|
||||
"libc",
|
||||
"libsqlite3-sys",
|
||||
@@ -5723,7 +5746,7 @@ dependencies = [
|
||||
"futures-util",
|
||||
"hashlink 0.8.1",
|
||||
"hex",
|
||||
"indexmap",
|
||||
"indexmap 1.9.2",
|
||||
"itoa 1.0.6",
|
||||
"libc",
|
||||
"libsqlite3-sys",
|
||||
@@ -6580,20 +6603,20 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "toml_datetime"
|
||||
version = "0.6.2"
|
||||
version = "0.6.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5a76a9312f5ba4c2dec6b9161fdf25d87ad8a09256ccea5a556fef03c706a10f"
|
||||
checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml_edit"
|
||||
version = "0.19.10"
|
||||
version = "0.19.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2380d56e8670370eee6566b0bfd4265f65b3f432e8c6d85623f728d4fa31f739"
|
||||
checksum = "c500344a19072298cd05a7224b3c0c629348b78692bf48466c5238656e315a78"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
"indexmap 2.0.0",
|
||||
"serde",
|
||||
"serde_spanned",
|
||||
"toml_datetime",
|
||||
|
||||
@@ -43,7 +43,7 @@ time = { version = "0.3.17", features = ["local-offset"] }
|
||||
tokio = { version = "1.24.1", features = ["sync", "time"] }
|
||||
url = "2.2"
|
||||
yaml-rust = "0.4"
|
||||
|
||||
toml = "0.7"
|
||||
|
||||
nym-client-core = { path = "../../../common/client-core" }
|
||||
nym-api-requests = { path = "../../../nym-api/nym-api-requests" }
|
||||
|
||||
@@ -1,10 +1,3 @@
|
||||
use std::env;
|
||||
|
||||
fn main() {
|
||||
if env::var_os("NYM_CONNECT_ENABLE_MEDIUM").is_some() {
|
||||
println!("cargo:rustc-cfg=medium_enabled");
|
||||
}
|
||||
println!("cargo:rerun-if-changed=build.rs");
|
||||
|
||||
tauri_build::build();
|
||||
}
|
||||
|
||||
@@ -26,6 +26,10 @@ mod old_config_v1_1_20_2;
|
||||
mod persistence;
|
||||
mod template;
|
||||
mod upgrade;
|
||||
mod user_data;
|
||||
|
||||
pub use user_data::PrivacyLevel;
|
||||
pub use user_data::UserData;
|
||||
|
||||
static SOCKS5_CONFIG_ID: &str = "nym-connect";
|
||||
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
use eyre::{eyre, Context, Result};
|
||||
use log::error;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{fs, str};
|
||||
use tauri::api::path::data_dir;
|
||||
|
||||
const DATA_DIR: &str = "nym-connect";
|
||||
const DATA_FILE: &str = "user-data.toml";
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Copy, Clone, Default)]
|
||||
pub enum PrivacyLevel {
|
||||
#[default]
|
||||
High,
|
||||
Medium,
|
||||
}
|
||||
|
||||
// User data is read from and write on disk
|
||||
// Linux: $XDG_DATA_HOME or $HOME/.local/share/
|
||||
// macOS: $HOME/Library/Application Support
|
||||
// Windows: {FOLDERID_RoamingAppData}
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, Default)]
|
||||
pub struct UserData {
|
||||
pub monitoring: Option<bool>,
|
||||
pub privacy_level: Option<PrivacyLevel>,
|
||||
}
|
||||
|
||||
fn create_directory_path() -> Result<()> {
|
||||
let mut data_dir = data_dir().ok_or(eyre!("Failed to retrieve data directory"))?;
|
||||
data_dir.push(DATA_DIR);
|
||||
|
||||
fs::create_dir_all(&data_dir).context(format!(
|
||||
"Failed to create user data directory path {}",
|
||||
data_dir.display()
|
||||
))
|
||||
}
|
||||
|
||||
impl UserData {
|
||||
pub fn read() -> Result<Self> {
|
||||
// create the full directory path if it is missing
|
||||
create_directory_path()?;
|
||||
|
||||
let mut data_path = data_dir().ok_or(eyre!("Failed to retrieve data directory"))?;
|
||||
|
||||
data_path.push(DATA_DIR);
|
||||
data_path.push(DATA_FILE);
|
||||
let content = fs::read(&data_path)
|
||||
.context(format!("Failed to read user data {}", data_path.display()))?;
|
||||
|
||||
toml::from_str::<UserData>(str::from_utf8(&content)?).map_err(|e| {
|
||||
error!("{}", e);
|
||||
eyre!("{e}")
|
||||
})
|
||||
}
|
||||
|
||||
pub fn write(&self) -> Result<()> {
|
||||
// create the full directory path if it is missing
|
||||
create_directory_path()?;
|
||||
|
||||
let mut data_path = data_dir().ok_or(eyre!("Failed to retrieve data directory"))?;
|
||||
|
||||
data_path.push(DATA_DIR);
|
||||
data_path.push(DATA_FILE);
|
||||
let toml = toml::to_string(&self)?;
|
||||
fs::write(data_path, toml)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -70,6 +70,8 @@ pub enum BackendError {
|
||||
NewWindowError,
|
||||
#[error("unable to parse the specified gateway")]
|
||||
UnableToParseGateway,
|
||||
#[error("unable to write user data to disk")]
|
||||
UserDataWriteError,
|
||||
|
||||
#[error("unable to load keys: {source}")]
|
||||
UnableToLoadKeys {
|
||||
|
||||
@@ -9,8 +9,9 @@ use nym_config::defaults::setup_env;
|
||||
use tauri::Manager;
|
||||
use tokio::sync::RwLock;
|
||||
|
||||
use crate::config::UserData;
|
||||
use crate::menu::{create_tray_menu, tray_menu_event_handler};
|
||||
use crate::state::{is_medium_enabled, State};
|
||||
use crate::state::State;
|
||||
use crate::window::window_toggle;
|
||||
|
||||
mod config;
|
||||
@@ -25,12 +26,6 @@ mod tasks;
|
||||
mod window;
|
||||
|
||||
fn main() {
|
||||
if is_medium_enabled() {
|
||||
println!("medium mode enabled");
|
||||
std::env::set_var("NYM_CONNECT_DISABLE_COVER", "1");
|
||||
std::env::set_var("NYM_CONNECT_ENABLE_MIXED_SIZE_PACKETS", "1");
|
||||
std::env::set_var("NYM_CONNECT_DISABLE_PER_HOP_DELAYS", "1");
|
||||
}
|
||||
setup_env(None);
|
||||
println!("Starting up...");
|
||||
|
||||
@@ -40,13 +35,22 @@ fn main() {
|
||||
log::warn!("Failed to fix PATH: {error}");
|
||||
}
|
||||
|
||||
let user_data = UserData::read().unwrap_or_else(|e| {
|
||||
println!("{}", e);
|
||||
println!("Fallback to default");
|
||||
UserData::default()
|
||||
});
|
||||
|
||||
let context = tauri::generate_context!();
|
||||
tauri::Builder::default()
|
||||
.manage(Arc::new(RwLock::new(State::new())))
|
||||
.manage(Arc::new(RwLock::new(State::new(user_data))))
|
||||
.invoke_handler(tauri::generate_handler![
|
||||
crate::operations::config::get_config_file_location,
|
||||
crate::operations::config::get_config_id,
|
||||
crate::operations::common::get_env,
|
||||
crate::operations::common::get_user_data,
|
||||
crate::operations::common::set_monitoring,
|
||||
crate::operations::common::set_privacy_level,
|
||||
crate::operations::connection::connect::get_gateway,
|
||||
crate::operations::connection::connect::get_service_provider,
|
||||
crate::operations::connection::connect::set_gateway,
|
||||
@@ -57,7 +61,6 @@ fn main() {
|
||||
crate::operations::connection::status::get_connection_status,
|
||||
crate::operations::connection::status::get_gateway_connection_status,
|
||||
crate::operations::connection::status::start_connection_health_check_task,
|
||||
crate::operations::connection::status::is_medium_mode_enabled,
|
||||
crate::operations::directory::get_services,
|
||||
crate::operations::directory::get_gateways_detailed,
|
||||
crate::operations::export::export_keys,
|
||||
|
||||
@@ -1,4 +1,9 @@
|
||||
use crate::config::PrivacyLevel;
|
||||
use crate::error::Result;
|
||||
use crate::{config::UserData, state::State};
|
||||
use std::env;
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::RwLock;
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn get_env(variable: String) -> Option<String> {
|
||||
@@ -7,3 +12,27 @@ pub async fn get_env(variable: String) -> Option<String> {
|
||||
|
||||
var
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn get_user_data(state: tauri::State<'_, Arc<RwLock<State>>>) -> Result<UserData> {
|
||||
let guard = state.read().await;
|
||||
Ok(guard.get_user_data().clone())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn set_monitoring(
|
||||
enabled: bool,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
) -> Result<()> {
|
||||
let mut guard = state.write().await;
|
||||
guard.set_monitoring(enabled)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn set_privacy_level(
|
||||
privacy_level: PrivacyLevel,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
) -> Result<()> {
|
||||
let mut guard = state.write().await;
|
||||
guard.set_privacy_level(privacy_level)
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ pub async fn start_connecting(
|
||||
window: tauri::Window<tauri::Wry>,
|
||||
) -> Result<ConnectResult> {
|
||||
log::trace!("Start connecting");
|
||||
|
||||
let (msg_receiver, exit_status_receiver) = {
|
||||
let mut state_w = state.write().await;
|
||||
state_w.start_connecting(&window).await?
|
||||
|
||||
@@ -5,7 +5,7 @@ use std::sync::Arc;
|
||||
use tokio::sync::RwLock;
|
||||
|
||||
use crate::models::{ConnectionStatusKind, ConnectivityTestResult, GatewayConnectionStatusKind};
|
||||
use crate::state::{is_medium_enabled, State};
|
||||
use crate::state::State;
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn get_connection_status(
|
||||
@@ -42,8 +42,3 @@ pub fn start_connection_health_check_task(
|
||||
) {
|
||||
tasks::start_connection_check(state.inner().clone(), window);
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn is_medium_mode_enabled(_state: tauri::State<'_, Arc<RwLock<State>>>) -> Result<bool> {
|
||||
Ok(is_medium_enabled())
|
||||
}
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
use itertools::Itertools;
|
||||
|
||||
use crate::config::PrivacyLevel;
|
||||
use crate::error::Result;
|
||||
use crate::models::{
|
||||
DirectoryService, DirectoryServiceProvider, HarbourMasterService, PagedResult,
|
||||
};
|
||||
use crate::state::is_medium_enabled;
|
||||
use crate::state::State;
|
||||
use nym_api_requests::models::GatewayBondAnnotated;
|
||||
use nym_contracts_common::types::Percent;
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::RwLock;
|
||||
|
||||
static SERVICE_PROVIDER_WELLKNOWN_URL: &str =
|
||||
"https://nymtech.net/.wellknown/connect/service-providers.json";
|
||||
@@ -20,21 +23,20 @@ static HARBOUR_MASTER_URL: &str = "https://harbourmaster.nymtech.net/v1/services
|
||||
static GATEWAYS_DETAILED_URL: &str =
|
||||
"https://validator.nymtech.net/api/v1/status/gateways/detailed";
|
||||
|
||||
fn get_services_url() -> &'static str {
|
||||
if is_medium_enabled() {
|
||||
return SERVICE_PROVIDER_WELLKNOWN_URL_MEDIUM;
|
||||
}
|
||||
SERVICE_PROVIDER_WELLKNOWN_URL
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn get_services() -> Result<Vec<DirectoryServiceProvider>> {
|
||||
pub async fn get_services(
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
) -> Result<Vec<DirectoryServiceProvider>> {
|
||||
log::trace!("Fetching services");
|
||||
let all_services = fetch_services().await?;
|
||||
|
||||
let guard = state.read().await;
|
||||
let privacy_level = guard.get_user_data().privacy_level.unwrap_or_default();
|
||||
|
||||
let all_services = fetch_services(&privacy_level).await?;
|
||||
log::trace!("Received: {:#?}", all_services);
|
||||
|
||||
// Early return if we're running with medium toggle enabled
|
||||
if is_medium_enabled() {
|
||||
if let PrivacyLevel::Medium = privacy_level {
|
||||
return Ok(all_services.into_iter().flat_map(|sp| sp.items).collect());
|
||||
}
|
||||
|
||||
@@ -100,8 +102,12 @@ fn filter_out_poor_gateways(
|
||||
.collect()
|
||||
}
|
||||
|
||||
async fn fetch_services() -> Result<Vec<DirectoryService>> {
|
||||
let services_url = get_services_url();
|
||||
async fn fetch_services(privacy_level: &PrivacyLevel) -> Result<Vec<DirectoryService>> {
|
||||
let services_url = match privacy_level {
|
||||
PrivacyLevel::Medium => SERVICE_PROVIDER_WELLKNOWN_URL_MEDIUM,
|
||||
_ => SERVICE_PROVIDER_WELLKNOWN_URL,
|
||||
};
|
||||
|
||||
let services_res = reqwest::get(services_url)
|
||||
.await?
|
||||
.json::<Vec<DirectoryService>>()
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use futures::SinkExt;
|
||||
use log::error;
|
||||
use nym_client_core::error::ClientCoreStatusMessage;
|
||||
use nym_socks5_client_core::{Socks5ControlMessage, Socks5ControlMessageSender};
|
||||
use std::time::Duration;
|
||||
@@ -7,6 +8,8 @@ use tauri::Manager;
|
||||
use tokio::time::Instant;
|
||||
|
||||
use crate::config::Config;
|
||||
use crate::config::PrivacyLevel;
|
||||
use crate::config::UserData;
|
||||
use crate::{
|
||||
config::{self, socks5_config_id_appended_with},
|
||||
error::{BackendError, Result},
|
||||
@@ -65,10 +68,13 @@ pub struct State {
|
||||
/// The latest end-to-end connectivity test result. The first test is initiated on connection
|
||||
/// established. Additional tests can be triggered.
|
||||
connectivity_test_result: ConnectivityTestResult,
|
||||
|
||||
/// User data saved on disk, like user settings
|
||||
user_data: UserData,
|
||||
}
|
||||
|
||||
impl State {
|
||||
pub fn new() -> Self {
|
||||
pub fn new(user_data: UserData) -> Self {
|
||||
State {
|
||||
status: ConnectionStatusKind::Disconnected,
|
||||
service_provider: None,
|
||||
@@ -76,6 +82,7 @@ impl State {
|
||||
socks5_client_sender: None,
|
||||
gateway_connectivity: GatewayConnectivity::Good,
|
||||
connectivity_test_result: ConnectivityTestResult::NotAvailable,
|
||||
user_data,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -93,6 +100,26 @@ impl State {
|
||||
self.gateway_connectivity
|
||||
}
|
||||
|
||||
pub fn get_user_data(&self) -> &UserData {
|
||||
&self.user_data
|
||||
}
|
||||
|
||||
pub fn set_monitoring(&mut self, enabled: bool) -> Result<()> {
|
||||
self.user_data.monitoring = Some(enabled);
|
||||
self.user_data.write().map_err(|e| {
|
||||
error!("Failed to write user data to disk {e}");
|
||||
BackendError::UserDataWriteError
|
||||
})
|
||||
}
|
||||
|
||||
pub fn set_privacy_level(&mut self, privacy_level: PrivacyLevel) -> Result<()> {
|
||||
self.user_data.privacy_level = Some(privacy_level);
|
||||
self.user_data.write().map_err(|e| {
|
||||
error!("Failed to write user data to disk {e}");
|
||||
BackendError::UserDataWriteError
|
||||
})
|
||||
}
|
||||
|
||||
pub fn set_gateway_connectivity(&mut self, gateway_connectivity: GatewayConnectivity) {
|
||||
self.gateway_connectivity = gateway_connectivity
|
||||
}
|
||||
@@ -200,8 +227,9 @@ impl State {
|
||||
&mut self,
|
||||
) -> Result<(nym_task::StatusReceiver, ExitStatusReceiver)> {
|
||||
let id = self.get_config_id()?;
|
||||
let privacy_level = self.user_data.privacy_level.unwrap_or_default();
|
||||
let (control_tx, msg_rx, exit_status_rx, used_gateway) =
|
||||
tasks::start_nym_socks5_client(&id).await?;
|
||||
tasks::start_nym_socks5_client(&id, &privacy_level).await?;
|
||||
self.socks5_client_sender = Some(control_tx);
|
||||
self.gateway = Some(used_gateway.gateway_id);
|
||||
Ok((msg_rx, exit_status_rx))
|
||||
@@ -244,7 +272,3 @@ impl State {
|
||||
self.set_state(ConnectionStatusKind::Disconnected, window);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_medium_enabled() -> bool {
|
||||
cfg!(medium_enabled) || std::env::var("NYM_CONNECT_ENABLE_MEDIUM").is_ok()
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ use std::sync::Arc;
|
||||
use tap::TapFallible;
|
||||
use tokio::sync::RwLock;
|
||||
|
||||
use crate::config::Config;
|
||||
use crate::config::{Config, PrivacyLevel};
|
||||
use crate::{
|
||||
error::Result,
|
||||
events::{self, emit_event, emit_status_event},
|
||||
@@ -30,23 +30,20 @@ pub enum Socks5ExitStatusMessage {
|
||||
Failed(Box<dyn std::error::Error + Send>),
|
||||
}
|
||||
|
||||
fn override_config_from_env(config: &mut Config) {
|
||||
fn override_config_from_env(config: &mut Config, privacy_level: &PrivacyLevel) {
|
||||
// Disable both the loop cover traffic that runs in the background as well as the Poisson
|
||||
// process that injects cover traffic into the traffic stream.
|
||||
if std::env::var("NYM_CONNECT_DISABLE_COVER").is_ok() {
|
||||
if let PrivacyLevel::Medium = privacy_level {
|
||||
log::info!("Running in Medium privacy level");
|
||||
log::warn!("Disabling cover traffic");
|
||||
config.core.base.set_no_cover_traffic_with_keepalive();
|
||||
}
|
||||
|
||||
if std::env::var("NYM_CONNECT_ENABLE_MIXED_SIZE_PACKETS").is_ok() {
|
||||
log::warn!("Enabling mixed size packets");
|
||||
config
|
||||
.core
|
||||
.base
|
||||
.set_secondary_packet_size(Some(PacketSize::ExtendedPacket16));
|
||||
}
|
||||
|
||||
if std::env::var("NYM_CONNECT_DISABLE_PER_HOP_DELAYS").is_ok() {
|
||||
log::warn!("Disabling per-hop delay");
|
||||
config.core.base.set_no_per_hop_delays();
|
||||
}
|
||||
@@ -55,6 +52,7 @@ fn override_config_from_env(config: &mut Config) {
|
||||
/// The main SOCKS5 client task. It loads the configuration from file determined by the `id`.
|
||||
pub async fn start_nym_socks5_client(
|
||||
id: &str,
|
||||
privacy_level: &PrivacyLevel,
|
||||
) -> Result<(
|
||||
Socks5ControlMessageSender,
|
||||
nym_task::StatusReceiver,
|
||||
@@ -65,7 +63,7 @@ pub async fn start_nym_socks5_client(
|
||||
let mut config = Config::read_from_default_path(id)
|
||||
.tap_err(|_| log::warn!("Failed to load configuration file"))?;
|
||||
|
||||
override_config_from_env(&mut config);
|
||||
override_config_from_env(&mut config, privacy_level);
|
||||
|
||||
log::trace!("Configuration used: {:#?}", config);
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ const ConnectionStatusContent: FCWithChildren<{
|
||||
serviceProvider?: ServiceProvider;
|
||||
gatewayError: boolean;
|
||||
}> = ({ status, serviceProvider, gatewayError }) => {
|
||||
const { speedMode } = useClientContext();
|
||||
const { userData } = useClientContext();
|
||||
|
||||
if (gatewayError) {
|
||||
return (
|
||||
@@ -49,10 +49,10 @@ const ConnectionStatusContent: FCWithChildren<{
|
||||
</Typography>
|
||||
</Box>
|
||||
</Tooltip>
|
||||
{speedMode === 'medium' && (
|
||||
{userData?.privacy_level === 'Medium' && (
|
||||
<Stack alignItems="center" color="warning.main">
|
||||
<Typography variant="caption" color="grey.400">
|
||||
Speedy mode activated
|
||||
Speed boost activated
|
||||
</Typography>
|
||||
</Stack>
|
||||
)}
|
||||
|
||||
@@ -3,23 +3,24 @@ import { DateTime } from 'luxon';
|
||||
import { invoke } from '@tauri-apps/api';
|
||||
import { Error } from 'src/types/error';
|
||||
import { getVersion } from '@tauri-apps/api/app';
|
||||
import * as Sentry from '@sentry/react';
|
||||
import { useEvents } from 'src/hooks/events';
|
||||
import { UserDefinedGateway, UserDefinedSPAddress } from 'src/types/service-provider';
|
||||
import { getItemFromStorage, setItemInStorage } from 'src/utils';
|
||||
import { ConnectionStatusKind, GatewayPerformance, SpeedMode } from '../types';
|
||||
import { ConnectionStatusKind, GatewayPerformance, PrivacyLevel, UserData } from '../types';
|
||||
import { ConnectionStatsItem } from '../components/ConnectionStats';
|
||||
import { ServiceProvider } from '../types/directory';
|
||||
import initSentry from '../sentry';
|
||||
|
||||
const FORAGE_GATEWAY_KEY = 'nym-connect-user-gateway';
|
||||
const FORAGE_SP_KEY = 'nym-connect-user-sp';
|
||||
const FORAGE_MONITORING_ENABLED = 'nym-connect-monitoring-enabled';
|
||||
|
||||
type ModeType = 'light' | 'dark';
|
||||
|
||||
export type TClientContext = {
|
||||
mode: ModeType;
|
||||
appVersion?: string;
|
||||
userData?: UserData;
|
||||
connectionStatus: ConnectionStatusKind;
|
||||
connectionStats?: ConnectionStatsItem[];
|
||||
connectedSince?: DateTime;
|
||||
@@ -32,7 +33,6 @@ export type TClientContext = {
|
||||
serviceProviders?: ServiceProvider[];
|
||||
setMode: (mode: ModeType) => void;
|
||||
clearError: () => void;
|
||||
monitoringEnabled: boolean;
|
||||
setConnectionStatus: (connectionStatus: ConnectionStatusKind) => void;
|
||||
setConnectionStats: (connectionStats: ConnectionStatsItem[] | undefined) => void;
|
||||
setConnectedSince: (connectedSince: DateTime | undefined) => void;
|
||||
@@ -43,7 +43,7 @@ export type TClientContext = {
|
||||
setUserDefinedGateway: React.Dispatch<React.SetStateAction<UserDefinedGateway>>;
|
||||
setUserDefinedSPAddress: React.Dispatch<React.SetStateAction<UserDefinedSPAddress>>;
|
||||
setMonitoring: (value: boolean) => Promise<void>;
|
||||
speedMode: SpeedMode;
|
||||
setPrivacyLevel: (value: PrivacyLevel) => Promise<void>;
|
||||
};
|
||||
|
||||
export const ClientContext = createContext({} as TClientContext);
|
||||
@@ -67,19 +67,26 @@ export const ClientContextProvider: FCWithChildren = ({ children }) => {
|
||||
isActive: false,
|
||||
address: undefined,
|
||||
});
|
||||
const [monitoringEnabled, setMonitoringEnabled] = useState(false);
|
||||
const [speedMode, setspeedMode] = useState<SpeedMode>('slow');
|
||||
const [userData, setUserData] = useState<UserData>();
|
||||
|
||||
const getAppVersion = async () => {
|
||||
const version = await getVersion();
|
||||
return version;
|
||||
};
|
||||
|
||||
const getUserData = async () => {
|
||||
const data = await invoke<UserData>('get_user_data');
|
||||
if (!data.privacy_level) {
|
||||
data.privacy_level = 'High';
|
||||
}
|
||||
setUserData(data);
|
||||
return data;
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const initSentryClient = async () => {
|
||||
const monitoring = await getItemFromStorage({ key: FORAGE_MONITORING_ENABLED });
|
||||
setMonitoringEnabled(Boolean(monitoring));
|
||||
if (monitoring === true) {
|
||||
const data = await getUserData();
|
||||
if (data.monitoring) {
|
||||
await initSentry();
|
||||
}
|
||||
};
|
||||
@@ -87,17 +94,6 @@ export const ClientContextProvider: FCWithChildren = ({ children }) => {
|
||||
initSentryClient();
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const initSpeedMode = async () => {
|
||||
const isEnabled = await invoke<boolean>('is_medium_mode_enabled');
|
||||
if (isEnabled) {
|
||||
setspeedMode('medium');
|
||||
}
|
||||
};
|
||||
|
||||
initSpeedMode();
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
setItemInStorage({ key: FORAGE_GATEWAY_KEY, value: userDefinedGateway });
|
||||
}, [userDefinedGateway]);
|
||||
@@ -124,7 +120,10 @@ export const ClientContextProvider: FCWithChildren = ({ children }) => {
|
||||
};
|
||||
|
||||
useEvents({
|
||||
onError: (e) => setError(e),
|
||||
onError: (e) => {
|
||||
setError(e);
|
||||
Sentry.captureException(e);
|
||||
},
|
||||
onGatewayPerformanceChange: (performance) => setGatewayPerformance(performance),
|
||||
onStatusChange: (status) => setConnectionStatus(status),
|
||||
});
|
||||
@@ -147,6 +146,7 @@ export const ClientContextProvider: FCWithChildren = ({ children }) => {
|
||||
} catch (e) {
|
||||
setError({ title: 'Could not connect', message: e as string });
|
||||
console.log(e);
|
||||
Sentry.captureException(e);
|
||||
}
|
||||
}, []);
|
||||
|
||||
@@ -155,6 +155,7 @@ export const ClientContextProvider: FCWithChildren = ({ children }) => {
|
||||
await invoke('start_disconnecting');
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
Sentry.captureException(e);
|
||||
}
|
||||
}, []);
|
||||
|
||||
@@ -197,8 +198,15 @@ export const ClientContextProvider: FCWithChildren = ({ children }) => {
|
||||
const clearError = () => setError(undefined);
|
||||
|
||||
const setMonitoring = async (value: boolean) => {
|
||||
setMonitoringEnabled(value);
|
||||
await setItemInStorage({ key: FORAGE_MONITORING_ENABLED, value });
|
||||
await invoke('set_monitoring', { enabled: value });
|
||||
// refresh user data
|
||||
await getUserData();
|
||||
};
|
||||
|
||||
const setPrivacyLevel = async (value: PrivacyLevel) => {
|
||||
await invoke('set_privacy_level', { privacyLevel: value });
|
||||
// refresh user data
|
||||
await getUserData();
|
||||
};
|
||||
|
||||
const contextValue = useMemo(
|
||||
@@ -216,7 +224,7 @@ export const ClientContextProvider: FCWithChildren = ({ children }) => {
|
||||
selectedProvider,
|
||||
serviceProviders,
|
||||
connectedSince,
|
||||
monitoringEnabled,
|
||||
userData,
|
||||
setConnectedSince,
|
||||
setSerivceProvider,
|
||||
startConnecting,
|
||||
@@ -228,7 +236,7 @@ export const ClientContextProvider: FCWithChildren = ({ children }) => {
|
||||
setUserDefinedGateway,
|
||||
setUserDefinedSPAddress,
|
||||
setMonitoring,
|
||||
speedMode,
|
||||
setPrivacyLevel,
|
||||
}),
|
||||
[
|
||||
mode,
|
||||
@@ -244,8 +252,7 @@ export const ClientContextProvider: FCWithChildren = ({ children }) => {
|
||||
selectedProvider,
|
||||
userDefinedGateway,
|
||||
userDefinedSPAddress,
|
||||
monitoringEnabled,
|
||||
speedMode,
|
||||
userData,
|
||||
],
|
||||
);
|
||||
|
||||
|
||||
@@ -11,8 +11,7 @@ const mockValues: TClientContext = {
|
||||
showInfoModal: false,
|
||||
userDefinedGateway: { isActive: false, gateway: '' },
|
||||
userDefinedSPAddress: { isActive: false, address: '' },
|
||||
monitoringEnabled: false,
|
||||
speedMode: 'slow',
|
||||
userData: { monitoring: false, privacy_level: 'High' },
|
||||
setShowInfoModal: () => {},
|
||||
setMode: () => {},
|
||||
clearError: () => {},
|
||||
@@ -25,6 +24,7 @@ const mockValues: TClientContext = {
|
||||
setUserDefinedGateway: () => {},
|
||||
setUserDefinedSPAddress: () => {},
|
||||
setMonitoring: async () => {},
|
||||
setPrivacyLevel: async () => {},
|
||||
};
|
||||
|
||||
export const MockProvider: FCWithChildren<{
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { useEffect, useRef } from 'react';
|
||||
import { listen, UnlistenFn } from '@tauri-apps/api/event';
|
||||
import * as Sentry from '@sentry/react';
|
||||
import { ConnectionStatusKind, GatewayPerformance } from 'src/types';
|
||||
import { Error } from 'src/types/error';
|
||||
import { TauriEvent } from 'src/types/event';
|
||||
@@ -29,7 +30,10 @@ export const useEvents = ({
|
||||
.then((result) => {
|
||||
unlisten.push(result);
|
||||
})
|
||||
.catch((e) => console.log(e));
|
||||
.catch((e) => {
|
||||
console.log(e);
|
||||
Sentry.captureException(e);
|
||||
});
|
||||
|
||||
listen('socks5-event', (e: TauriEvent) => {
|
||||
console.log(e);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import React from 'react';
|
||||
import { forage } from '@tauri-apps/tauri-forage';
|
||||
import * as Sentry from '@sentry/react';
|
||||
import { DateTime } from 'luxon';
|
||||
import { useClientContext } from 'src/context/main';
|
||||
import { useTauriEvents } from 'src/utils';
|
||||
@@ -28,12 +29,14 @@ export const ConnectionPage = () => {
|
||||
// eslint-disable-next-line default-case
|
||||
switch (currentStatus) {
|
||||
case 'disconnected':
|
||||
Sentry.captureMessage('start connect', 'info');
|
||||
await context.setSerivceProvider();
|
||||
await context.startConnecting();
|
||||
context.setConnectedSince(DateTime.now());
|
||||
context.setShowInfoModal(true);
|
||||
break;
|
||||
case 'connected':
|
||||
Sentry.captureMessage('start disconnect', 'info');
|
||||
await context.startDisconnecting();
|
||||
context.setConnectedSince(undefined);
|
||||
break;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React from 'react';
|
||||
import { Apps, HelpOutline, Settings, BugReport } from '@mui/icons-material';
|
||||
import { Apps, HelpOutline, Settings, BugReport, PrivacyTip } from '@mui/icons-material';
|
||||
import { Stack, Link, List, ListItem, ListItemButton, ListItemIcon, ListItemText } from '@mui/material';
|
||||
import { Link as RouterLink } from 'react-router-dom';
|
||||
import { AppVersion } from 'src/components/AppVersion';
|
||||
@@ -7,6 +7,7 @@ import { AppVersion } from 'src/components/AppVersion';
|
||||
const menuSchema = [
|
||||
{ title: 'Supported apps', icon: Apps, path: 'apps' },
|
||||
{ title: 'How to connect guide', icon: HelpOutline, path: 'guide' },
|
||||
{ title: 'Privacy level', icon: PrivacyTip, path: 'privacy-level' },
|
||||
{ title: 'Please help us improve the app', icon: BugReport, path: 'monitoring' },
|
||||
{ title: 'Settings', icon: Settings, path: 'settings' },
|
||||
];
|
||||
|
||||
@@ -4,8 +4,8 @@ import { Box, FormControl, FormControlLabel, FormHelperText, Stack, Switch, Typo
|
||||
import { useClientContext } from 'src/context/main';
|
||||
|
||||
export const MonitoringSettings = () => {
|
||||
const { monitoringEnabled, setMonitoring } = useClientContext();
|
||||
const [enabled, setEnabled] = useState(monitoringEnabled);
|
||||
const { userData, setMonitoring } = useClientContext();
|
||||
const [enabled, setEnabled] = useState(userData?.monitoring || false);
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
const handleChange = async (e: ChangeEvent<HTMLInputElement>) => {
|
||||
@@ -25,7 +25,13 @@ export const MonitoringSettings = () => {
|
||||
<FormControl fullWidth>
|
||||
<FormControlLabel
|
||||
control={
|
||||
<Switch checked={enabled} onChange={handleChange} disabled={loading} size="small" sx={{ ml: 1 }} />
|
||||
<Switch
|
||||
checked={enabled}
|
||||
onChange={handleChange}
|
||||
disabled={loading}
|
||||
size="small"
|
||||
sx={{ ml: 1, mr: 1 }}
|
||||
/>
|
||||
}
|
||||
label="Enable"
|
||||
/>
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
import React, { ChangeEvent, useState } from 'react';
|
||||
import * as Sentry from '@sentry/react';
|
||||
import { Box, FormControl, FormControlLabel, FormHelperText, Stack, Switch, Typography } from '@mui/material';
|
||||
import { useClientContext } from 'src/context/main';
|
||||
|
||||
export const PrivacyLevelSettings = () => {
|
||||
const { userData, setPrivacyLevel } = useClientContext();
|
||||
const [speedBoost, setSpeedBoost] = useState(userData?.privacy_level !== 'High');
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
const handleChange = async (e: ChangeEvent<HTMLInputElement>) => {
|
||||
setLoading(true);
|
||||
setSpeedBoost(e.target.checked);
|
||||
Sentry.captureMessage(`privacy level switched to ${e.target.checked ? 'Medium' : 'High'}`, 'info');
|
||||
await setPrivacyLevel(e.target.checked ? 'Medium' : 'High');
|
||||
setLoading(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<Box height="100%">
|
||||
<Stack justifyContent="space-between" height="100%">
|
||||
<Box>
|
||||
<Typography fontWeight="bold" variant="body2" mb={2}>
|
||||
Speed boost
|
||||
</Typography>
|
||||
<FormControl fullWidth>
|
||||
<FormControlLabel
|
||||
control={
|
||||
<Switch
|
||||
checked={speedBoost}
|
||||
onChange={handleChange}
|
||||
disabled={loading}
|
||||
size="small"
|
||||
sx={{ ml: 1, mr: 1 }}
|
||||
/>
|
||||
}
|
||||
label="Enable"
|
||||
/>
|
||||
<FormHelperText sx={{ m: 0, my: 2 }}>
|
||||
By activating this option, the connection speed will be relatively faster in exchange of relaxing some
|
||||
protections
|
||||
</FormHelperText>
|
||||
</FormControl>
|
||||
</Box>
|
||||
</Stack>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
@@ -9,14 +9,15 @@ import { SettingsMenu } from 'src/pages/menu/settings';
|
||||
import { GatewaySettings } from 'src/pages/menu/settings/GatewaySettings';
|
||||
import { ServiceProviderSettings } from 'src/pages/menu/settings/ServiceProviderSettings';
|
||||
import { MonitoringSettings } from '../pages/menu/settings/MonitoringSettings';
|
||||
import { PrivacyLevelSettings } from '../pages/menu/settings/PrivacyLevelSettings';
|
||||
import { useClientContext } from '../context/main';
|
||||
|
||||
const SentryRoutes = Sentry.withSentryReactRouterV6Routing(Routes);
|
||||
|
||||
export const AppRoutes = () => {
|
||||
const { monitoringEnabled } = useClientContext();
|
||||
const { userData } = useClientContext();
|
||||
|
||||
const RoutesContainer = monitoringEnabled ? SentryRoutes : Routes;
|
||||
const RoutesContainer = userData?.monitoring ? SentryRoutes : Routes;
|
||||
|
||||
return (
|
||||
<RoutesContainer>
|
||||
@@ -25,6 +26,7 @@ export const AppRoutes = () => {
|
||||
<Route index element={<Menu />} />
|
||||
<Route path="apps" element={<CompatibleApps />} />
|
||||
<Route path="guide" element={<HelpGuide />} />
|
||||
<Route path="privacy-level" element={<PrivacyLevelSettings />} />
|
||||
<Route path="monitoring" element={<MonitoringSettings />} />
|
||||
<Route path="settings">
|
||||
<Route index element={<SettingsMenu />} />
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
export type SpeedMode = 'fast' | 'medium' | 'slow';
|
||||
@@ -1,3 +1,3 @@
|
||||
export * from './rust';
|
||||
export * from './connection';
|
||||
export * from './common';
|
||||
export * from './user-data';
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
export type PrivacyLevel = 'High' | 'Medium';
|
||||
|
||||
export type UserData = {
|
||||
monitoring?: boolean;
|
||||
privacy_level?: PrivacyLevel;
|
||||
};
|
||||
Reference in New Issue
Block a user