qr: parse scan result, slatepack address image
This commit is contained in:
parent
7a79b88e68
commit
0aaebd1ef2
9 changed files with 460 additions and 78 deletions
146
Cargo.lock
generated
146
Cargo.lock
generated
|
@ -1000,6 +1000,12 @@ version = "0.13.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8"
|
checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "base64"
|
||||||
|
version = "0.21.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "base64"
|
name = "base64"
|
||||||
version = "0.22.0"
|
version = "0.22.0"
|
||||||
|
@ -2168,6 +2174,12 @@ version = "2.5.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7e962a19be5cfc3f3bf6dd8f61eb50107f356ad6270fbb3ed41476571db78be5"
|
checksum = "7e962a19be5cfc3f3bf6dd8f61eb50107f356ad6270fbb3ed41476571db78be5"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "data-url"
|
||||||
|
version = "0.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5c297a1c74b71ae29df00c3e22dd9534821d60eb9af5a0192823fa2acea70c2a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "der"
|
name = "der"
|
||||||
version = "0.7.9"
|
version = "0.7.9"
|
||||||
|
@ -2659,6 +2671,7 @@ dependencies = [
|
||||||
"image 0.24.9",
|
"image 0.24.9",
|
||||||
"log",
|
"log",
|
||||||
"mime_guess2",
|
"mime_guess2",
|
||||||
|
"resvg",
|
||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -3114,6 +3127,12 @@ dependencies = [
|
||||||
"miniz_oxide",
|
"miniz_oxide",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "float-cmp"
|
||||||
|
version = "0.9.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "fluent"
|
name = "fluent"
|
||||||
version = "0.16.0"
|
version = "0.16.0"
|
||||||
|
@ -3775,6 +3794,7 @@ dependencies = [
|
||||||
"local-ip-address",
|
"local-ip-address",
|
||||||
"log",
|
"log",
|
||||||
"openssl-sys",
|
"openssl-sys",
|
||||||
|
"qrcodegen",
|
||||||
"rand 0.8.5",
|
"rand 0.8.5",
|
||||||
"rqrr",
|
"rqrr",
|
||||||
"rust-i18n",
|
"rust-i18n",
|
||||||
|
@ -4847,6 +4867,12 @@ dependencies = [
|
||||||
"thiserror",
|
"thiserror",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "imagesize"
|
||||||
|
version = "0.12.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "029d73f573d8e8d63e6d5020011d3255b28c3ba85d6cf870a07184ed23de9284"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "imgref"
|
name = "imgref"
|
||||||
version = "1.10.1"
|
version = "1.10.1"
|
||||||
|
@ -5150,6 +5176,15 @@ dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "kurbo"
|
||||||
|
version = "0.9.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bd85a5776cd9500c2e2059c8c76c3b01528566b7fcbaf8098b55a33fc298849b"
|
||||||
|
dependencies = [
|
||||||
|
"arrayvec 0.7.4",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lazy_static"
|
name = "lazy_static"
|
||||||
version = "1.4.0"
|
version = "1.4.0"
|
||||||
|
@ -6557,6 +6592,12 @@ dependencies = [
|
||||||
"siphasher",
|
"siphasher",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pico-args"
|
||||||
|
version = "0.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5be167a7af36ee22fe3115051bc51f6e6c7054c9348e28deb4f49bd6f705a315"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pin-project"
|
name = "pin-project"
|
||||||
version = "1.1.5"
|
version = "1.1.5"
|
||||||
|
@ -6892,6 +6933,12 @@ version = "1.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5520fbcd7da152a449261c5a533a1c7fad044e9e8aa9528cfec3f464786c7926"
|
checksum = "5520fbcd7da152a449261c5a533a1c7fad044e9e8aa9528cfec3f464786c7926"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "qrcodegen"
|
||||||
|
version = "1.8.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4339fc7a1021c9c1621d87f5e3505f2805c8c105420ba2f2a4df86814590c142"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "quick-error"
|
name = "quick-error"
|
||||||
version = "1.2.3"
|
version = "1.2.3"
|
||||||
|
@ -7221,6 +7268,12 @@ dependencies = [
|
||||||
"crossbeam-utils",
|
"crossbeam-utils",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rctree"
|
||||||
|
version = "0.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3b42e27ef78c35d3998403c1d26f3efd9e135d3e5121b0a4845cc5cc27547f4f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rdrand"
|
name = "rdrand"
|
||||||
version = "0.4.0"
|
version = "0.4.0"
|
||||||
|
@ -7355,6 +7408,20 @@ dependencies = [
|
||||||
"winreg",
|
"winreg",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "resvg"
|
||||||
|
version = "0.37.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cadccb3d99a9efb8e5e00c16fbb732cbe400db2ec7fc004697ee7d97d86cf1f4"
|
||||||
|
dependencies = [
|
||||||
|
"log",
|
||||||
|
"pico-args",
|
||||||
|
"rgb",
|
||||||
|
"svgtypes",
|
||||||
|
"tiny-skia",
|
||||||
|
"usvg",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "retry-error"
|
name = "retry-error"
|
||||||
version = "0.5.2"
|
version = "0.5.2"
|
||||||
|
@ -7436,6 +7503,12 @@ dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "roxmltree"
|
||||||
|
version = "0.19.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3cd14fd5e3b777a7422cca79358c57a8f6e3a703d9ac187448d0daf220c2407f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rqrr"
|
name = "rqrr"
|
||||||
version = "0.7.1"
|
version = "0.7.1"
|
||||||
|
@ -8176,6 +8249,15 @@ dependencies = [
|
||||||
"time",
|
"time",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "simplecss"
|
||||||
|
version = "0.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a11be7c62927d9427e9f40f3444d5499d868648e2edbc4e2116de69e7ec0e89d"
|
||||||
|
dependencies = [
|
||||||
|
"log",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "siphasher"
|
name = "siphasher"
|
||||||
version = "0.3.11"
|
version = "0.3.11"
|
||||||
|
@ -8368,6 +8450,9 @@ name = "strict-num"
|
||||||
version = "0.1.1"
|
version = "0.1.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6637bab7722d379c8b41ba849228d680cc12d0a45ba1fa2b48f2a30577a06731"
|
checksum = "6637bab7722d379c8b41ba849228d680cc12d0a45ba1fa2b48f2a30577a06731"
|
||||||
|
dependencies = [
|
||||||
|
"float-cmp",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "strsim"
|
name = "strsim"
|
||||||
|
@ -8461,6 +8546,16 @@ version = "0.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "171758edb47aa306a78dfa4ab9aeb5167405bd4e3dc2b64e88f6a84bbe98bd63"
|
checksum = "171758edb47aa306a78dfa4ab9aeb5167405bd4e3dc2b64e88f6a84bbe98bd63"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "svgtypes"
|
||||||
|
version = "0.13.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6e44e288cd960318917cbd540340968b90becc8bc81f171345d706e7a89d9d70"
|
||||||
|
dependencies = [
|
||||||
|
"kurbo",
|
||||||
|
"siphasher",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "0.15.44"
|
version = "0.15.44"
|
||||||
|
@ -8735,6 +8830,7 @@ dependencies = [
|
||||||
"bytemuck",
|
"bytemuck",
|
||||||
"cfg-if 1.0.0",
|
"cfg-if 1.0.0",
|
||||||
"log",
|
"log",
|
||||||
|
"png",
|
||||||
"tiny-skia-path",
|
"tiny-skia-path",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -10264,6 +10360,50 @@ dependencies = [
|
||||||
"percent-encoding",
|
"percent-encoding",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "usvg"
|
||||||
|
version = "0.37.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "38b0a51b72ab80ca511d126b77feeeb4fb1e972764653e61feac30adc161a756"
|
||||||
|
dependencies = [
|
||||||
|
"base64 0.21.7",
|
||||||
|
"log",
|
||||||
|
"pico-args",
|
||||||
|
"usvg-parser",
|
||||||
|
"usvg-tree",
|
||||||
|
"xmlwriter",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "usvg-parser"
|
||||||
|
version = "0.37.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9bd4e3c291f45d152929a31f0f6c819245e2921bfd01e7bd91201a9af39a2bdc"
|
||||||
|
dependencies = [
|
||||||
|
"data-url",
|
||||||
|
"flate2",
|
||||||
|
"imagesize",
|
||||||
|
"kurbo",
|
||||||
|
"log",
|
||||||
|
"roxmltree",
|
||||||
|
"simplecss",
|
||||||
|
"siphasher",
|
||||||
|
"svgtypes",
|
||||||
|
"usvg-tree",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "usvg-tree"
|
||||||
|
version = "0.37.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8ee3d202ebdb97a6215604b8f5b4d6ef9024efd623cf2e373a6416ba976ec7d3"
|
||||||
|
dependencies = [
|
||||||
|
"rctree",
|
||||||
|
"strict-num",
|
||||||
|
"svgtypes",
|
||||||
|
"tiny-skia-path",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "utf8parse"
|
name = "utf8parse"
|
||||||
version = "0.2.1"
|
version = "0.2.1"
|
||||||
|
@ -11336,6 +11476,12 @@ version = "0.8.20"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "791978798f0597cfc70478424c2b4fdc2b7a8024aaff78497ef00f24ef674193"
|
checksum = "791978798f0597cfc70478424c2b4fdc2b7a8024aaff78497ef00f24ef674193"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "xmlwriter"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ec7a2a501ed189703dba8b08142f057e887dfc4b2cc4db2d343ac6376ba3e0b9"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "xxhash-rust"
|
name = "xxhash-rust"
|
||||||
version = "0.8.10"
|
version = "0.8.10"
|
||||||
|
|
|
@ -34,7 +34,7 @@ grin_wallet_controller = "5.3.0"
|
||||||
|
|
||||||
## ui
|
## ui
|
||||||
egui = { version = "0.27.2", default-features = false }
|
egui = { version = "0.27.2", default-features = false }
|
||||||
egui_extras = { version = "0.27.2", features = ["image"] }
|
egui_extras = { version = "0.27.2", features = ["image", "svg"] }
|
||||||
rust-i18n = "2.3.1"
|
rust-i18n = "2.3.1"
|
||||||
|
|
||||||
## other
|
## other
|
||||||
|
@ -53,8 +53,9 @@ rand = "0.8.5"
|
||||||
serde_derive = "1.0.197"
|
serde_derive = "1.0.197"
|
||||||
serde_json = "1.0.115"
|
serde_json = "1.0.115"
|
||||||
tokio = { version = "1.37.0", features = ["full"] }
|
tokio = { version = "1.37.0", features = ["full"] }
|
||||||
image = { version = "0.25.1" }
|
image = "0.25.1"
|
||||||
rqrr = "0.7.1"
|
rqrr = "0.7.1"
|
||||||
|
qrcodegen = "1.8.0"
|
||||||
|
|
||||||
## tor
|
## tor
|
||||||
arti = { version = "1.2.0", features = ["pt-client", "static"] }
|
arti = { version = "1.2.0", features = ["pt-client", "static"] }
|
||||||
|
|
|
@ -13,20 +13,22 @@
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
use std::sync::{Arc, RwLock};
|
use std::sync::{Arc, RwLock};
|
||||||
|
use std::thread;
|
||||||
use eframe::emath::Align;
|
use eframe::emath::Align;
|
||||||
use egui::load::SizedTexture;
|
use egui::load::SizedTexture;
|
||||||
use egui::{Layout, Pos2, Rect, TextureOptions, Widget};
|
use egui::{Layout, Pos2, Rect, TextureOptions, Widget};
|
||||||
|
use grin_wallet_libwallet::SlatepackAddress;
|
||||||
use image::{DynamicImage, EncodableLayout, ImageFormat};
|
use image::{DynamicImage, EncodableLayout, ImageFormat};
|
||||||
use crate::gui::Colors;
|
use crate::gui::Colors;
|
||||||
use crate::gui::icons::CAMERA_ROTATE;
|
use crate::gui::icons::CAMERA_ROTATE;
|
||||||
|
|
||||||
use crate::gui::platform::PlatformCallbacks;
|
use crate::gui::platform::PlatformCallbacks;
|
||||||
use crate::gui::views::types::QrScanState;
|
use crate::gui::views::types::{QrScanResult, QrScanState};
|
||||||
use crate::gui::views::View;
|
use crate::gui::views::View;
|
||||||
|
|
||||||
/// Camera scanner content.
|
/// Camera QR code scanner.
|
||||||
pub struct CameraContent {
|
pub struct CameraContent {
|
||||||
// QR code scanning progress and result.
|
/// QR code scanning progress and result.
|
||||||
qr_scan_state: Arc<RwLock<QrScanState>>
|
qr_scan_state: Arc<RwLock<QrScanState>>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -126,7 +128,7 @@ impl CameraContent {
|
||||||
|
|
||||||
/// Check if image is processing to find QR code.
|
/// Check if image is processing to find QR code.
|
||||||
fn image_processing(&self) -> bool {
|
fn image_processing(&self) -> bool {
|
||||||
let mut r_scan = self.qr_scan_state.read().unwrap();
|
let r_scan = self.qr_scan_state.read().unwrap();
|
||||||
r_scan.image_processing
|
r_scan.image_processing
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -142,39 +144,60 @@ impl CameraContent {
|
||||||
w_scan.image_processing = true;
|
w_scan.image_processing = true;
|
||||||
}
|
}
|
||||||
// Launch scanner at separate thread.
|
// Launch scanner at separate thread.
|
||||||
tokio::runtime::Builder::new_multi_thread()
|
let data = data.clone();
|
||||||
.enable_all()
|
let qr_scan_state = self.qr_scan_state.clone();
|
||||||
.build()
|
thread::spawn(move || {
|
||||||
.unwrap()
|
tokio::runtime::Builder::new_multi_thread()
|
||||||
.block_on(async {
|
.enable_all()
|
||||||
// Prepare image data.
|
.build()
|
||||||
let img = data.to_luma8();
|
.unwrap()
|
||||||
let mut img: rqrr::PreparedImage<image::GrayImage>
|
.block_on(async {
|
||||||
= rqrr::PreparedImage::prepare(img);
|
// Prepare image data.
|
||||||
// Scan and save results.
|
let img = data.to_luma8();
|
||||||
let grids = img.detect_grids();
|
let mut img: rqrr::PreparedImage<image::GrayImage>
|
||||||
for g in grids {
|
= rqrr::PreparedImage::prepare(img);
|
||||||
if let Ok((meta, text)) = g.decode() {
|
// Scan and save results.
|
||||||
println!("12345 ecc: {}, text: {}", meta.ecc_level, text.clone());
|
let grids = img.detect_grids();
|
||||||
if !text.trim().is_empty() {
|
for g in grids {
|
||||||
let mut w_scan = self.qr_scan_state.write().unwrap();
|
if let Ok((_, text)) = g.decode() {
|
||||||
w_scan.qr_scan_result = Some(text);
|
let text = text.trim();
|
||||||
|
if !text.is_empty() {
|
||||||
|
let result = Self::parse_scan_result(text);
|
||||||
|
let mut w_scan = qr_scan_state.write().unwrap();
|
||||||
|
w_scan.qr_scan_result = Some(result);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
// Setup scanning flag.
|
||||||
// Setup scanning flag.
|
{
|
||||||
{
|
let mut w_scan = qr_scan_state.write().unwrap();
|
||||||
let mut w_scan = self.qr_scan_state.write().unwrap();
|
w_scan.image_processing = false;
|
||||||
w_scan.image_processing = false;
|
}
|
||||||
}
|
});
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_scan_result(text: &str) -> QrScanResult {
|
||||||
|
// Check if string starts with Grin address prefix.
|
||||||
|
if text.starts_with("tgrin") || text.starts_with("grin") {
|
||||||
|
if SlatepackAddress::try_from(text).is_ok() {
|
||||||
|
return QrScanResult::Address(text.to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if string contains Slatepack message prefix and postfix.
|
||||||
|
if text.starts_with("BEGINSLATEPACK.") && text.ends_with("ENDSLATEPACK.") {
|
||||||
|
return QrScanResult::Slatepack(text.to_string())
|
||||||
|
}
|
||||||
|
|
||||||
|
QrScanResult::Text(text.to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get QR code scan result.
|
/// Get QR code scan result.
|
||||||
pub fn qr_scan_result(&self) -> Option<String> {
|
pub fn qr_scan_result(&self) -> Option<QrScanResult> {
|
||||||
let r_scan = self.qr_scan_state.read().unwrap();
|
let r_scan = self.qr_scan_state.read().unwrap();
|
||||||
if r_scan.qr_scan_result.is_some() {
|
if r_scan.qr_scan_result.is_some() {
|
||||||
return Some(r_scan.qr_scan_result.as_ref().unwrap().clone());
|
return Some(r_scan.qr_scan_result.clone().unwrap());
|
||||||
}
|
}
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,4 +33,7 @@ mod wallets;
|
||||||
pub use wallets::*;
|
pub use wallets::*;
|
||||||
|
|
||||||
mod camera;
|
mod camera;
|
||||||
pub use camera::*;
|
pub use camera::*;
|
||||||
|
|
||||||
|
mod qr;
|
||||||
|
pub use qr::*;
|
132
src/gui/views/qr.rs
Normal file
132
src/gui/views/qr.rs
Normal file
|
@ -0,0 +1,132 @@
|
||||||
|
// Copyright 2023 The Grim Developers
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
use std::sync::{Arc, RwLock};
|
||||||
|
use std::thread;
|
||||||
|
use egui::{SizeHint, TextureHandle, TextureOptions};
|
||||||
|
use egui::load::SizedTexture;
|
||||||
|
use egui_extras::image::{load_svg_bytes, load_svg_bytes_with_size};
|
||||||
|
|
||||||
|
use crate::gui::views::types::QrCreationState;
|
||||||
|
use crate::gui::views::View;
|
||||||
|
|
||||||
|
/// QR code image from text.
|
||||||
|
pub struct QrCodeContent {
|
||||||
|
/// Text to create QR code.
|
||||||
|
pub(crate) text: String,
|
||||||
|
|
||||||
|
/// Texture handle to show image when created.
|
||||||
|
texture_handle: Option<TextureHandle>,
|
||||||
|
/// QR code image creation progress and result.
|
||||||
|
qr_creation_state: Arc<RwLock<QrCreationState>>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl QrCodeContent {
|
||||||
|
pub fn new(text: String) -> Self {
|
||||||
|
Self {
|
||||||
|
text,
|
||||||
|
texture_handle: None,
|
||||||
|
qr_creation_state: Arc::new(RwLock::new(QrCreationState::default())),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Draw QR code.
|
||||||
|
pub fn ui(&mut self, ui: &mut egui::Ui, text: String) {
|
||||||
|
// Get saved QR code image or load new one.
|
||||||
|
if !self.has_image() {
|
||||||
|
ui.add_space(38.0);
|
||||||
|
View::small_loading_spinner(ui);
|
||||||
|
ui.add_space(38.0);
|
||||||
|
|
||||||
|
// Create image from text if not loading.
|
||||||
|
self.create_image(text);
|
||||||
|
} else {
|
||||||
|
// Create image from SVG data.
|
||||||
|
let r_create = self.qr_creation_state.read().unwrap();
|
||||||
|
let svg = r_create.svg.as_ref().unwrap();
|
||||||
|
let size = SizeHint::Size(ui.available_width() as u32, ui.available_width() as u32);
|
||||||
|
let color_img = load_svg_bytes_with_size(svg, Some(size)).unwrap();
|
||||||
|
// Create image texture.
|
||||||
|
let texture_handle = ui.ctx().load_texture("qr_code",
|
||||||
|
color_img.clone(),
|
||||||
|
TextureOptions::default());
|
||||||
|
self.texture_handle = Some(texture_handle.clone());
|
||||||
|
let img_size = egui::emath::vec2(color_img.width() as f32,
|
||||||
|
color_img.height() as f32);
|
||||||
|
let sized_img = SizedTexture::new(texture_handle.id(), img_size);
|
||||||
|
// Add image to content.
|
||||||
|
ui.add(egui::Image::from_texture(sized_img)
|
||||||
|
.max_height(ui.available_width())
|
||||||
|
.fit_to_original_size(1.0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Check if image is creating.
|
||||||
|
fn creating(&self) -> bool {
|
||||||
|
let r_create = self.qr_creation_state.read().unwrap();
|
||||||
|
r_create.creating
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Check if image was created.
|
||||||
|
fn has_image(&self) -> bool {
|
||||||
|
let r_create = self.qr_creation_state.read().unwrap();
|
||||||
|
r_create.svg.is_some()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create QR code image at separate thread.
|
||||||
|
fn create_image(&self, text: String) {
|
||||||
|
let qr_creation_state = self.qr_creation_state.clone();
|
||||||
|
if !self.creating() {
|
||||||
|
thread::spawn(move || {
|
||||||
|
let qr = qrcodegen::QrCode::encode_text(text.as_str(),
|
||||||
|
qrcodegen::QrCodeEcc::Medium).unwrap();
|
||||||
|
let svg = Self::qr_to_svg(qr, 0);
|
||||||
|
let mut w_create = qr_creation_state.write().unwrap();
|
||||||
|
w_create.creating = false;
|
||||||
|
w_create.svg = Some(svg.into_bytes());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Convert QR code to SVG string.
|
||||||
|
fn qr_to_svg(qr: qrcodegen::QrCode, border: i32) -> String {
|
||||||
|
let mut result = String::new();
|
||||||
|
let dimension = qr.size().checked_add(border.checked_mul(2).unwrap()).unwrap();
|
||||||
|
result += "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
|
||||||
|
result += "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n";
|
||||||
|
result += &format!(
|
||||||
|
"<svg xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\" viewBox=\"0 0 {0} {0}\" stroke=\"none\">\n", dimension);
|
||||||
|
result += "\t<rect width=\"100%\" height=\"100%\" fill=\"#FFFFFF\"/>\n";
|
||||||
|
result += "\t<path d=\"";
|
||||||
|
for y in 0 .. qr.size() {
|
||||||
|
for x in 0 .. qr.size() {
|
||||||
|
if qr.get_module(x, y) {
|
||||||
|
if x != 0 || y != 0 {
|
||||||
|
result += " ";
|
||||||
|
}
|
||||||
|
result += &format!("M{},{}h1v1h-1z", x + border, y + border);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result += "\" fill=\"#000000\"/>\n";
|
||||||
|
result += "</svg>\n";
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Reset QR code image content state to default.
|
||||||
|
pub fn clear_state(&mut self) {
|
||||||
|
let mut w_create = self.qr_creation_state.write().unwrap();
|
||||||
|
*w_create = QrCreationState::default();
|
||||||
|
}
|
||||||
|
}
|
|
@ -166,4 +166,21 @@ impl Default for QrScanState {
|
||||||
qr_scan_result: None,
|
qr_scan_result: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// QR code image creation state.
|
||||||
|
pub struct QrCreationState {
|
||||||
|
// Flag to check if QR code image is creating.
|
||||||
|
pub creating: bool,
|
||||||
|
// Found QR code content.
|
||||||
|
pub svg: Option<Vec<u8>>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for QrCreationState {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
creating: false,
|
||||||
|
svg: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -22,7 +22,7 @@ use crate::gui::Colors;
|
||||||
use crate::gui::icons::{BRIDGE, CHAT_CIRCLE_TEXT, CHECK, CHECK_FAT, FOLDER_USER, GEAR_FINE, GRAPH, PACKAGE, PATH, POWER, REPEAT, SCAN, USERS_THREE};
|
use crate::gui::icons::{BRIDGE, CHAT_CIRCLE_TEXT, CHECK, CHECK_FAT, FOLDER_USER, GEAR_FINE, GRAPH, PACKAGE, PATH, POWER, REPEAT, SCAN, USERS_THREE};
|
||||||
use crate::gui::platform::PlatformCallbacks;
|
use crate::gui::platform::PlatformCallbacks;
|
||||||
use crate::gui::views::{CameraContent, Modal, Root, View};
|
use crate::gui::views::{CameraContent, Modal, Root, View};
|
||||||
use crate::gui::views::types::{ModalPosition, TextEditOptions};
|
use crate::gui::views::types::{ModalPosition, QrScanResult, TextEditOptions};
|
||||||
use crate::gui::views::wallets::{WalletTransactions, WalletMessages, WalletTransport, WalletSettings};
|
use crate::gui::views::wallets::{WalletTransactions, WalletMessages, WalletTransport, WalletSettings};
|
||||||
use crate::gui::views::wallets::types::{GRIN, WalletTab, WalletTabType};
|
use crate::gui::views::wallets::types::{GRIN, WalletTab, WalletTabType};
|
||||||
use crate::node::Node;
|
use crate::node::Node;
|
||||||
|
@ -44,7 +44,7 @@ pub struct WalletContent {
|
||||||
/// Camera content for QR scan [`Modal`].
|
/// Camera content for QR scan [`Modal`].
|
||||||
camera_content: CameraContent,
|
camera_content: CameraContent,
|
||||||
/// QR code scan result
|
/// QR code scan result
|
||||||
qr_scan_result: Option<String>,
|
qr_scan_result: Option<QrScanResult>,
|
||||||
|
|
||||||
/// Current tab content to show.
|
/// Current tab content to show.
|
||||||
pub current_tab: Box<dyn WalletTab>
|
pub current_tab: Box<dyn WalletTab>
|
||||||
|
@ -367,15 +367,37 @@ impl WalletContent {
|
||||||
cb: &dyn PlatformCallbacks) {
|
cb: &dyn PlatformCallbacks) {
|
||||||
ui.add_space(6.0);
|
ui.add_space(6.0);
|
||||||
if let Some(result) = &self.qr_scan_result {
|
if let Some(result) = &self.qr_scan_result {
|
||||||
|
let result_text = match result {
|
||||||
|
QrScanResult::Slatepack(t) => t,
|
||||||
|
QrScanResult::Address(t) => t,
|
||||||
|
QrScanResult::Text(t) => t
|
||||||
|
};
|
||||||
ui.vertical_centered(|ui| {
|
ui.vertical_centered(|ui| {
|
||||||
ui.label(RichText::new(result).size(16.0).color(Colors::INACTIVE_TEXT));
|
ui.label(RichText::new(result_text).size(16.0).color(Colors::INACTIVE_TEXT));
|
||||||
});
|
});
|
||||||
} else if let Some(result) = self.camera_content.qr_scan_result() {
|
} else if let Some(result) = self.camera_content.qr_scan_result() {
|
||||||
///TODO: parse result and show
|
|
||||||
cb.stop_camera();
|
cb.stop_camera();
|
||||||
self.camera_content.clear_state();
|
self.camera_content.clear_state();
|
||||||
println!("result: {}", result);
|
match &result {
|
||||||
self.qr_scan_result = Some(result);
|
QrScanResult::Slatepack(message) => {
|
||||||
|
let mut messages =
|
||||||
|
WalletMessages::new(wallet.can_use_dandelion(), Some(message.clone()));
|
||||||
|
messages.parse_message(wallet);
|
||||||
|
self.current_tab = Box::new(messages);
|
||||||
|
modal.close();
|
||||||
|
}
|
||||||
|
QrScanResult::Address(receiver) => {
|
||||||
|
if wallet.get_data().unwrap().info.amount_currently_spendable > 0 {
|
||||||
|
let addr = wallet.slatepack_address().unwrap();
|
||||||
|
let mut transport = WalletTransport::new(addr.clone());
|
||||||
|
transport.show_send_tor_modal(cb, Some(receiver.clone()));
|
||||||
|
self.current_tab = Box::new(transport);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
QrScanResult::Text(_) => {
|
||||||
|
self.qr_scan_result = Some(result);
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
self.camera_content.ui(ui, cb);
|
self.camera_content.ui(ui, cb);
|
||||||
}
|
}
|
||||||
|
@ -409,13 +431,14 @@ impl WalletContent {
|
||||||
let is_messages = current_type == WalletTabType::Messages;
|
let is_messages = current_type == WalletTabType::Messages;
|
||||||
View::tab_button(ui, CHAT_CIRCLE_TEXT, is_messages, || {
|
View::tab_button(ui, CHAT_CIRCLE_TEXT, is_messages, || {
|
||||||
self.current_tab = Box::new(
|
self.current_tab = Box::new(
|
||||||
WalletMessages::new(wallet.can_use_dandelion())
|
WalletMessages::new(wallet.can_use_dandelion(), None)
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
columns[2].vertical_centered_justified(|ui| {
|
columns[2].vertical_centered_justified(|ui| {
|
||||||
View::tab_button(ui, BRIDGE, current_type == WalletTabType::Transport, || {
|
View::tab_button(ui, BRIDGE, current_type == WalletTabType::Transport, || {
|
||||||
self.current_tab = Box::new(WalletTransport::default());
|
let addr = wallet.slatepack_address().unwrap();
|
||||||
|
self.current_tab = Box::new(WalletTransport::new(addr));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
columns[3].vertical_centered_justified(|ui| {
|
columns[3].vertical_centered_justified(|ui| {
|
||||||
|
|
|
@ -77,22 +77,6 @@ pub struct WalletMessages {
|
||||||
/// Identifier for amount input [`Modal`].
|
/// Identifier for amount input [`Modal`].
|
||||||
const AMOUNT_MODAL: &'static str = "amount_modal";
|
const AMOUNT_MODAL: &'static str = "amount_modal";
|
||||||
|
|
||||||
impl WalletMessages {
|
|
||||||
pub fn new(dandelion: bool) -> Self {
|
|
||||||
Self {
|
|
||||||
send_request: false,
|
|
||||||
message_edit: "".to_string(),
|
|
||||||
message_slate: None,
|
|
||||||
message_error: None,
|
|
||||||
response_edit: "".to_string(),
|
|
||||||
dandelion,
|
|
||||||
amount_edit: "".to_string(),
|
|
||||||
request_edit: "".to_string(),
|
|
||||||
request_error: None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl WalletTab for WalletMessages {
|
impl WalletTab for WalletMessages {
|
||||||
fn get_type(&self) -> WalletTabType {
|
fn get_type(&self) -> WalletTabType {
|
||||||
WalletTabType::Messages
|
WalletTabType::Messages
|
||||||
|
@ -140,6 +124,21 @@ impl WalletTab for WalletMessages {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WalletMessages {
|
impl WalletMessages {
|
||||||
|
/// Create new content instance, put message into input if provided.
|
||||||
|
pub fn new(dandelion: bool, message: Option<String>) -> Self {
|
||||||
|
Self {
|
||||||
|
send_request: false,
|
||||||
|
message_edit: message.unwrap_or("".to_string()),
|
||||||
|
message_slate: None,
|
||||||
|
message_error: None,
|
||||||
|
response_edit: "".to_string(),
|
||||||
|
dandelion,
|
||||||
|
amount_edit: "".to_string(),
|
||||||
|
request_edit: "".to_string(),
|
||||||
|
request_error: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Draw manual wallet transaction interaction content.
|
/// Draw manual wallet transaction interaction content.
|
||||||
pub fn ui(&mut self,
|
pub fn ui(&mut self,
|
||||||
ui: &mut egui::Ui,
|
ui: &mut egui::Ui,
|
||||||
|
@ -452,7 +451,7 @@ impl WalletMessages {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse message input into [`Slate`] updating slate and response input.
|
/// Parse message input into [`Slate`] updating slate and response input.
|
||||||
fn parse_message(&mut self, wallet: &mut Wallet) {
|
pub fn parse_message(&mut self, wallet: &mut Wallet) {
|
||||||
self.message_error = None;
|
self.message_error = None;
|
||||||
if self.message_edit.is_empty() {
|
if self.message_edit.is_empty() {
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -22,7 +22,7 @@ use grin_wallet_libwallet::SlatepackAddress;
|
||||||
use crate::gui::Colors;
|
use crate::gui::Colors;
|
||||||
use crate::gui::icons::{CHECK_CIRCLE, COMPUTER_TOWER, COPY, DOTS_THREE_CIRCLE, EXPORT, GEAR_SIX, GLOBE_SIMPLE, POWER, QR_CODE, WARNING_CIRCLE, X_CIRCLE};
|
use crate::gui::icons::{CHECK_CIRCLE, COMPUTER_TOWER, COPY, DOTS_THREE_CIRCLE, EXPORT, GEAR_SIX, GLOBE_SIMPLE, POWER, QR_CODE, WARNING_CIRCLE, X_CIRCLE};
|
||||||
use crate::gui::platform::PlatformCallbacks;
|
use crate::gui::platform::PlatformCallbacks;
|
||||||
use crate::gui::views::{Modal, Root, View};
|
use crate::gui::views::{Modal, QrCodeContent, Root, View};
|
||||||
use crate::gui::views::types::{ModalPosition, TextEditOptions};
|
use crate::gui::views::types::{ModalPosition, TextEditOptions};
|
||||||
use crate::gui::views::wallets::wallet::types::{WalletTab, WalletTabType};
|
use crate::gui::views::wallets::wallet::types::{WalletTab, WalletTabType};
|
||||||
use crate::gui::views::wallets::wallet::WalletContent;
|
use crate::gui::views::wallets::wallet::WalletContent;
|
||||||
|
@ -46,20 +46,9 @@ pub struct WalletTransport {
|
||||||
address_error: bool,
|
address_error: bool,
|
||||||
/// Flag to check if [`Modal`] was just opened to focus on first field.
|
/// Flag to check if [`Modal`] was just opened to focus on first field.
|
||||||
modal_just_opened: bool,
|
modal_just_opened: bool,
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for WalletTransport {
|
/// QR code address image [`Modal`] content.
|
||||||
fn default() -> Self {
|
qr_code_content: QrCodeContent,
|
||||||
Self {
|
|
||||||
tor_sending: Arc::new(RwLock::new(false)),
|
|
||||||
tor_send_error: Arc::new(RwLock::new(false)),
|
|
||||||
tor_success: Arc::new(RwLock::new(false)),
|
|
||||||
amount_edit: "".to_string(),
|
|
||||||
address_edit: "".to_string(),
|
|
||||||
address_error: false,
|
|
||||||
modal_just_opened: false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WalletTab for WalletTransport {
|
impl WalletTab for WalletTransport {
|
||||||
|
@ -114,7 +103,24 @@ const SEND_TOR_MODAL: &'static str = "send_tor_modal";
|
||||||
/// Identifier for [`Modal`] to setup Tor service.
|
/// Identifier for [`Modal`] to setup Tor service.
|
||||||
const TOR_SETTINGS_MODAL: &'static str = "tor_settings_modal";
|
const TOR_SETTINGS_MODAL: &'static str = "tor_settings_modal";
|
||||||
|
|
||||||
|
/// Identifier for [`Modal`] to show QR code address image.
|
||||||
|
const QR_ADDRESS_MODAL: &'static str = "qr_address_modal";
|
||||||
|
|
||||||
impl WalletTransport {
|
impl WalletTransport {
|
||||||
|
/// Create new content instance from provided Slatepack address text.
|
||||||
|
pub fn new(addr: String) -> Self {
|
||||||
|
Self {
|
||||||
|
tor_sending: Arc::new(RwLock::new(false)),
|
||||||
|
tor_send_error: Arc::new(RwLock::new(false)),
|
||||||
|
tor_success: Arc::new(RwLock::new(false)),
|
||||||
|
amount_edit: "".to_string(),
|
||||||
|
address_edit: "".to_string(),
|
||||||
|
address_error: false,
|
||||||
|
modal_just_opened: false,
|
||||||
|
qr_code_content: QrCodeContent::new(addr),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Draw wallet transport content.
|
/// Draw wallet transport content.
|
||||||
pub fn ui(&mut self, ui: &mut egui::Ui, wallet: &mut Wallet, cb: &dyn PlatformCallbacks) {
|
pub fn ui(&mut self, ui: &mut egui::Ui, wallet: &mut Wallet, cb: &dyn PlatformCallbacks) {
|
||||||
ui.add_space(3.0);
|
ui.add_space(3.0);
|
||||||
|
@ -146,6 +152,11 @@ impl WalletTransport {
|
||||||
self.tor_settings_modal_ui(ui, wallet, modal);
|
self.tor_settings_modal_ui(ui, wallet, modal);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
QR_ADDRESS_MODAL => {
|
||||||
|
Modal::ui(ui.ctx(), |ui, modal| {
|
||||||
|
self.qr_address_modal_ui(ui, modal);
|
||||||
|
});
|
||||||
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -186,7 +197,7 @@ impl WalletTransport {
|
||||||
// Draw button to setup Tor transport.
|
// Draw button to setup Tor transport.
|
||||||
let button_rounding = View::item_rounding(0, 2, true);
|
let button_rounding = View::item_rounding(0, 2, true);
|
||||||
View::item_button(ui, button_rounding, GEAR_SIX, None, || {
|
View::item_button(ui, button_rounding, GEAR_SIX, None, || {
|
||||||
// Show modal.
|
// Show Tor settings modal.
|
||||||
Modal::new(TOR_SETTINGS_MODAL)
|
Modal::new(TOR_SETTINGS_MODAL)
|
||||||
.position(ModalPosition::CenterTop)
|
.position(ModalPosition::CenterTop)
|
||||||
.title(t!("transport.tor_settings"))
|
.title(t!("transport.tor_settings"))
|
||||||
|
@ -273,7 +284,7 @@ impl WalletTransport {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Draw Tor send content.
|
/// Draw Tor send content.
|
||||||
fn tor_receive_ui(&self,
|
fn tor_receive_ui(&mut self,
|
||||||
ui: &mut egui::Ui,
|
ui: &mut egui::Ui,
|
||||||
wallet: &Wallet,
|
wallet: &Wallet,
|
||||||
data: &WalletData,
|
data: &WalletData,
|
||||||
|
@ -304,7 +315,12 @@ impl WalletTransport {
|
||||||
View::item_rounding(1, 2, true)
|
View::item_rounding(1, 2, true)
|
||||||
};
|
};
|
||||||
View::item_button(ui, button_rounding, QR_CODE, None, || {
|
View::item_button(ui, button_rounding, QR_CODE, None, || {
|
||||||
//TODO: qr for address
|
// Show QR code image address modal.
|
||||||
|
self.qr_code_content.clear_state();
|
||||||
|
Modal::new(QR_ADDRESS_MODAL)
|
||||||
|
.position(ModalPosition::CenterTop)
|
||||||
|
.title(t!("network_mining.address"))
|
||||||
|
.show();
|
||||||
});
|
});
|
||||||
|
|
||||||
// Show button to enable/disable Tor listener for current wallet.
|
// Show button to enable/disable Tor listener for current wallet.
|
||||||
|
@ -338,6 +354,28 @@ impl WalletTransport {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Draw QR code image address [`Modal`] content.
|
||||||
|
fn qr_address_modal_ui(&mut self, ui: &mut egui::Ui, modal: &Modal) {
|
||||||
|
ui.add_space(6.0);
|
||||||
|
|
||||||
|
// Draw QR code content.
|
||||||
|
let text = self.qr_code_content.text.clone();
|
||||||
|
self.qr_code_content.ui(ui, text.clone());
|
||||||
|
ui.add_space(6.0);
|
||||||
|
|
||||||
|
// Show address.
|
||||||
|
View::ellipsize_text(ui, text, 15.0, Colors::TEXT);
|
||||||
|
ui.add_space(6.0);
|
||||||
|
|
||||||
|
ui.vertical_centered_justified(|ui| {
|
||||||
|
View::button(ui, t!("close"), Colors::WHITE, || {
|
||||||
|
self.qr_code_content.clear_state();
|
||||||
|
modal.close();
|
||||||
|
});
|
||||||
|
ui.add_space(6.0);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/// Draw Tor receive content.
|
/// Draw Tor receive content.
|
||||||
fn tor_send_ui(&mut self, ui: &mut egui::Ui, cb: &dyn PlatformCallbacks) {
|
fn tor_send_ui(&mut self, ui: &mut egui::Ui, cb: &dyn PlatformCallbacks) {
|
||||||
// Setup layout size.
|
// Setup layout size.
|
||||||
|
@ -355,14 +393,14 @@ impl WalletTransport {
|
||||||
// Draw button to open sending modal.
|
// Draw button to open sending modal.
|
||||||
let send_text = format!("{} {}", EXPORT, t!("wallets.send"));
|
let send_text = format!("{} {}", EXPORT, t!("wallets.send"));
|
||||||
View::button(ui, send_text, Colors::WHITE, || {
|
View::button(ui, send_text, Colors::WHITE, || {
|
||||||
self.show_send_tor_modal(cb);
|
self.show_send_tor_modal(cb, None);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Show [`Modal`] to send over Tor.
|
/// Show [`Modal`] to send over Tor.
|
||||||
fn show_send_tor_modal(&mut self, cb: &dyn PlatformCallbacks) {
|
pub fn show_send_tor_modal(&mut self, cb: &dyn PlatformCallbacks, address: Option<String>) {
|
||||||
// Setup modal values.
|
// Setup modal values.
|
||||||
let mut w_send_err = self.tor_send_error.write().unwrap();
|
let mut w_send_err = self.tor_send_error.write().unwrap();
|
||||||
*w_send_err = false;
|
*w_send_err = false;
|
||||||
|
@ -372,7 +410,7 @@ impl WalletTransport {
|
||||||
*w_success = false;
|
*w_success = false;
|
||||||
self.modal_just_opened = true;
|
self.modal_just_opened = true;
|
||||||
self.amount_edit = "".to_string();
|
self.amount_edit = "".to_string();
|
||||||
self.address_edit = "".to_string();
|
self.address_edit = address.unwrap_or("".to_string());
|
||||||
self.address_error = false;
|
self.address_error = false;
|
||||||
// Show modal.
|
// Show modal.
|
||||||
Modal::new(SEND_TOR_MODAL)
|
Modal::new(SEND_TOR_MODAL)
|
||||||
|
|
Loading…
Reference in a new issue