ur: scanning and creating qr codes for slatepack messages

This commit is contained in:
ardocrat 2024-05-27 00:20:16 +03:00
parent 4b46e5a997
commit 3f03d145e8
10 changed files with 373 additions and 110 deletions

73
Cargo.lock generated
View file

@ -1059,6 +1059,21 @@ version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc827186963e592360843fb5ba4b973e145841266c1357f7180c43526f2e5b61" checksum = "dc827186963e592360843fb5ba4b973e145841266c1357f7180c43526f2e5b61"
[[package]]
name = "bitcoin-private"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73290177011694f38ec25e165d0387ab7ea749a4b81cd4c80dae5988229f7a57"
[[package]]
name = "bitcoin_hashes"
version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5d7066118b13d4b20b23645932dfb3a81ce7e29f95726c2036fa33cd7b092501"
dependencies = [
"bitcoin-private",
]
[[package]] [[package]]
name = "bitflags" name = "bitflags"
version = "0.9.1" version = "0.9.1"
@ -1800,6 +1815,21 @@ dependencies = [
"libc", "libc",
] ]
[[package]]
name = "crc"
version = "3.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "69e6e4d7b33a94f0991c26729976b10ebde1d34c3ee82408fb536164fa10d636"
dependencies = [
"crc-catalog",
]
[[package]]
name = "crc-catalog"
version = "2.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5"
[[package]] [[package]]
name = "crc32fast" name = "crc32fast"
version = "1.4.0" version = "1.4.0"
@ -3705,6 +3735,7 @@ dependencies = [
"tor-keymgr", "tor-keymgr",
"tor-llcrypto", "tor-llcrypto",
"tor-rtcompat", "tor-rtcompat",
"ur",
"url", "url",
"wgpu", "wgpu",
"winit", "winit",
@ -5492,6 +5523,26 @@ dependencies = [
"unicase", "unicase",
] ]
[[package]]
name = "minicbor"
version = "0.19.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d7005aaf257a59ff4de471a9d5538ec868a21586534fff7f85dd97d4043a6139"
dependencies = [
"minicbor-derive",
]
[[package]]
name = "minicbor-derive"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1154809406efdb7982841adb6311b3d095b46f78342dd646736122fe6b19e267"
dependencies = [
"proc-macro2 1.0.81",
"quote 1.0.36",
"syn 1.0.109",
]
[[package]] [[package]]
name = "minimal-lexical" name = "minimal-lexical"
version = "0.2.1" version = "0.2.1"
@ -7102,6 +7153,15 @@ dependencies = [
"rand_core 0.3.1", "rand_core 0.3.1",
] ]
[[package]]
name = "rand_xoshiro"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6f97cdb2a36ed4183de61b2f824cc45c9f1037f28afe0a322e9fff4c108b5aaa"
dependencies = [
"rand_core 0.6.4",
]
[[package]] [[package]]
name = "range-alloc" name = "range-alloc"
version = "0.1.3" version = "0.1.3"
@ -10258,6 +10318,19 @@ version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
[[package]]
name = "ur"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "010f24a953db5d22d0010969ca3bbf40b3857b89f47c0f7be0da4c2d7ded0760"
dependencies = [
"bitcoin_hashes",
"crc",
"minicbor",
"phf",
"rand_xoshiro",
]
[[package]] [[package]]
name = "url" name = "url"
version = "2.5.0" version = "2.5.0"

View file

@ -57,6 +57,7 @@ tokio = { version = "1.37.0", features = ["full"] }
image = "0.25.1" image = "0.25.1"
rqrr = "0.7.1" rqrr = "0.7.1"
qrcodegen = "1.8.0" qrcodegen = "1.8.0"
ur = "0.4.1"
## tor ## tor
arti = { version = "1.2.0", features = ["pt-client", "static"] } arti = { version = "1.2.0", features = ["pt-client", "static"] }

View file

@ -35,18 +35,26 @@ use crate::wallet::WalletUtils;
/// Camera QR code scanner. /// 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>>,
/// Uniform Resources URIs collected from QR code scanning.
ur_data: Arc<RwLock<Option<Vec<String>>>>,
start: i64,
} }
impl Default for CameraContent { impl Default for CameraContent {
fn default() -> Self { fn default() -> Self {
Self { Self {
qr_scan_state: Arc::new(RwLock::new(QrScanState::default())), qr_scan_state: Arc::new(RwLock::new(QrScanState::default())),
ur_data: Arc::new(RwLock::new(None)),
start: 0,
} }
} }
} }
impl CameraContent { impl CameraContent {
/// Draw camera content.
pub fn ui(&mut self, ui: &mut egui::Ui, cb: &dyn PlatformCallbacks) { pub fn ui(&mut self, ui: &mut egui::Ui, cb: &dyn PlatformCallbacks) {
// Draw last image from camera or loader. // Draw last image from camera or loader.
if let Some(img_data) = cb.camera_image() { if let Some(img_data) = cb.camera_image() {
@ -138,8 +146,13 @@ impl CameraContent {
r_scan.image_processing r_scan.image_processing
} }
/// Get UR scanning progress in percents.
fn ur_progress(&self) -> i32 {
0
}
/// Parse QR code from provided image data. /// Parse QR code from provided image data.
fn scan_qr(&self, data: &DynamicImage) { fn scan_qr(&self, image_data: &DynamicImage) {
// Do not scan when another image is processing. // Do not scan when another image is processing.
if self.image_processing() { if self.image_processing() {
return; return;
@ -149,55 +162,117 @@ impl CameraContent {
let mut w_scan = self.qr_scan_state.write(); let mut w_scan = self.qr_scan_state.write();
w_scan.image_processing = true; w_scan.image_processing = true;
} }
// Launch scanner at separate thread.
let data = data.clone(); let image_data = image_data.clone();
let qr_scan_state = self.qr_scan_state.clone(); let qr_scan_state = self.qr_scan_state.clone();
let ur_data = self.ur_data.clone();
let on_scan = async move {
// Prepare image data.
let img = image_data.to_luma8();
let mut img: rqrr::PreparedImage<image::GrayImage>
= rqrr::PreparedImage::prepare(img);
// Scan and save results.
let grids = img.detect_grids();
if let Some(g) = grids.get(0) {
let mut qr_data = vec![];
if let Ok(_) = g.decode_to(&mut qr_data) {
// Setup scanned data into text.
let text = String::from_utf8(qr_data.clone()).unwrap_or("".to_string());
// Setup current text.
let cur_text = {
let r_scan = qr_scan_state.read();
let text = if let Some(res) = r_scan.qr_scan_result.clone() {
res.text()
} else {
"".to_string()
};
text
};
// Parse non-empty data if parsed text is different from saved.
if !qr_data.is_empty() && (cur_text.is_empty() || text != cur_text) {
let res = Self::parse_qr_code(qr_data);
match res {
QrScanResult::URPart(uri, index, total) => {
// Setup current UR data.
let mut cur_data = {
let r_data = ur_data.read();
let mut cur_data = vec!["".to_string(); total];
if let Some(d) = r_data.clone() {
cur_data = d;
}
cur_data
};
if !cur_data.contains(&uri) {
// Save part of UR data.
{
cur_data.insert(index, uri);
let mut w_data = ur_data.write();
*w_data = Some(cur_data.clone());
}
// Setup UR decoder.
let mut decoder = ur::Decoder::default();
for m in cur_data {
if !m.is_empty() {
if let Ok(_) = decoder.receive(m.as_str()) {
continue;
} else {
break;
}
}
}
// Check if UR data is complete.
if decoder.complete() {
if let Ok(data) = decoder.message() {
// Parse complete data.
let res = Self::parse_qr_code(data.unwrap_or(vec![]));
// Clean UR data.
let mut w_data = ur_data.write();
*w_data = None;
// Save scan result.
let mut w_scan = qr_scan_state.write();
w_scan.qr_scan_result = Some(res);
return;
}
}
}
}
_ => {
// Clean UR data.
let mut w_data = ur_data.write();
*w_data = None;
// Save scan result.
let mut w_scan = qr_scan_state.write();
w_scan.qr_scan_result = Some(res);
return;
}
}
}
}
}
// Reset scanning flag to process again.
{
let mut w_scan = qr_scan_state.write();
w_scan.image_processing = false;
}
};
// Launch scanner at separate thread.
thread::spawn(move || { thread::spawn(move || {
tokio::runtime::Builder::new_multi_thread() tokio::runtime::Builder::new_multi_thread()
.enable_all() .enable_all()
.build() .build()
.unwrap() .unwrap()
.block_on(async { .block_on(on_scan);
// Prepare image data.
let img = data.to_luma8();
let mut img: rqrr::PreparedImage<image::GrayImage>
= rqrr::PreparedImage::prepare(img);
// Scan and save results.
let grids = img.detect_grids();
if let Some(g) = grids.get(0) {
let mut data = vec![];
if let Ok(_) = g.decode_to(&mut data) {
let cur_text = {
let r_scan = qr_scan_state.read();
let text = if let Some(res) = r_scan.qr_scan_result.clone() {
res.value()
} else {
"".to_string()
};
text
};
let text = String::from_utf8(data.clone()).unwrap_or("".to_string());
if !data.is_empty() && (cur_text.is_empty() || text != cur_text) {
let result = Self::parse_scan_result(&data);
let mut w_scan = qr_scan_state.write();
w_scan.qr_scan_result = Some(result);
return;
}
}
}
// Reset scanning flag to process again.
{
let mut w_scan = qr_scan_state.write();
w_scan.image_processing = false;
}
});
}); });
} }
fn parse_scan_result(data: &Vec<u8>) -> QrScanResult { /// Parse QR code scan result.
fn parse_qr_code(data: Vec<u8>) -> QrScanResult {
// Check if string starts with Grin address prefix. // Check if string starts with Grin address prefix.
let text_string = String::from_utf8(data.clone()).unwrap_or("".to_string()); let text_string = String::from_utf8(data.clone()).unwrap_or("".to_string());
let text = text_string.as_str(); println!("data: {}", text_string);
let text = text_string.trim();
if text.starts_with("tgrin") || text.starts_with("grin") { if text.starts_with("tgrin") || text.starts_with("grin") {
if SlatepackAddress::try_from(text).is_ok() { if SlatepackAddress::try_from(text).is_ok() {
return QrScanResult::Address(ZeroingString::from(text)); return QrScanResult::Address(ZeroingString::from(text));
@ -209,7 +284,25 @@ impl CameraContent {
return QrScanResult::Slatepack(ZeroingString::from(text)); return QrScanResult::Slatepack(ZeroingString::from(text));
} }
// Check Compact SeedQR format (https://github.com/SeedSigner/seedsigner/blob/dev/docs/seed_qr/README.md#compactseedqr-specification). // Check Uniform Resource data.
// https://github.com/BlockchainCommons/Research/blob/master/papers/bcr-2020-005-ur.md
if text.starts_with("ur:bytes/") {
let split = text.split("/").collect::<Vec<_>>();
if let Some(index_total) = split.get(1) {
if let Some((index, total)) = index_total.split_once("-") {
let index = index.parse::<usize>();
let total = total.parse::<usize>();
if index.is_ok() && total.is_ok() {
let index = index.unwrap() - 1;
let total = total.unwrap();
return QrScanResult::URPart(text_string, index, total);
}
}
}
}
// Check Compact SeedQR format.
// https://github.com/SeedSigner/seedsigner/blob/dev/docs/seed_qr/README.md#compactseedqr-specification
if data.len() <= 32 && 16 <= data.len() && data.len() % 4 == 0 { if data.len() <= 32 && 16 <= data.len() && data.len() % 4 == 0 {
// Setup words amount. // Setup words amount.
let total_bits = data.len() * 8; let total_bits = data.len() * 8;
@ -259,7 +352,8 @@ impl CameraContent {
} }
} }
// Check Standard SeedQR format (https://github.com/SeedSigner/seedsigner/blob/dev/docs/seed_qr/README.md#standard-seedqr-specification). // Check Standard SeedQR format.
// https://github.com/SeedSigner/seedsigner/blob/dev/docs/seed_qr/README.md#standard-seedqr-specification
let only_numbers = || { let only_numbers = || {
for c in text.chars() { for c in text.chars() {
if !c.is_numeric() { if !c.is_numeric() {
@ -316,7 +410,11 @@ impl CameraContent {
/// Reset camera content state to default. /// Reset camera content state to default.
pub fn clear_state(&mut self) { pub fn clear_state(&mut self) {
// Clear QR code scanning state.
let mut w_scan = self.qr_scan_state.write(); let mut w_scan = self.qr_scan_state.write();
*w_scan = QrScanState::default(); *w_scan = QrScanState::default();
// Clear UR data.
let mut w_data = self.ur_data.write();
*w_data = None;
} }
} }

View file

@ -28,16 +28,27 @@ pub struct QrCodeContent {
/// Text to create QR code. /// Text to create QR code.
pub(crate) text: String, pub(crate) text: String,
/// Flag to draw animated QR with Uniform Resources
/// https://github.com/BlockchainCommons/Research/blob/master/papers/bcr-2020-005-ur.md
animated: bool,
/// Index of current image at animation.
animated_index: Option<usize>,
/// Time of last image draw.
animation_time: Option<i64>,
/// Texture handle to show image when created. /// Texture handle to show image when created.
texture_handle: Option<TextureHandle>, texture_handle: Option<TextureHandle>,
/// QR code image creation progress and result. /// QR code image creation progress and result.
qr_creation_state: Arc<RwLock<QrCreationState>> qr_creation_state: Arc<RwLock<QrCreationState>>,
} }
impl QrCodeContent { impl QrCodeContent {
pub fn new(text: String) -> Self { pub fn new(text: String, animated: bool) -> Self {
Self { Self {
text, text,
animated,
animated_index: None,
animation_time: None,
texture_handle: None, texture_handle: None,
qr_creation_state: Arc::new(RwLock::new(QrCreationState::default())), qr_creation_state: Arc::new(RwLock::new(QrCreationState::default())),
} }
@ -45,67 +56,141 @@ impl QrCodeContent {
/// Draw QR code. /// Draw QR code.
pub fn ui(&mut self, ui: &mut egui::Ui, text: String) { pub fn ui(&mut self, ui: &mut egui::Ui, text: String) {
// Get saved QR code image or load new one. if self.animated {
if !self.has_image() { // Create animated QR code image if not created.
let space = (ui.available_width() - View::BIG_SPINNER_SIZE) / 2.0; if !self.has_image() {
ui.vertical_centered(|ui| { let space = (ui.available_width() - View::BIG_SPINNER_SIZE) / 2.0;
ui.add_space(space); ui.vertical_centered(|ui| {
View::big_loading_spinner(ui); ui.add_space(space);
ui.add_space(space); View::big_loading_spinner(ui);
}); ui.add_space(space);
});
// Create image from text if not loading. // Create multiple vector images from text if not creating.
self.create_image(text); if !self.creating() {
self.create_svg_list(text);
}
} else {
let svg_list = {
let r_create = self.qr_creation_state.read();
r_create.svg_list.clone().unwrap()
};
// Setup animated index.
let now = chrono::Utc::now().timestamp_millis();
if now - *self.animation_time.get_or_insert(now) > 100 {
if let Some(i) = self.animated_index {
self.animated_index = Some(i + 1);
}
if *self.animated_index.get_or_insert(0) == svg_list.len() {
self.animated_index = Some(0);
}
self.animation_time = Some(now);
}
let svg = svg_list[self.animated_index.unwrap_or(0)].clone();
// Create images from SVG data.
let size = SizeHint::Size(ui.available_width() as u32, ui.available_width() as u32);
let color_img = load_svg_bytes_with_size(svg.as_slice(), 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));
ui.ctx().request_repaint();
}
} else { } else {
// Create image from SVG data. // Create vector QR code image if not created.
let r_create = self.qr_creation_state.read(); if !self.has_image() {
let svg = r_create.svg.as_ref().unwrap(); let space = (ui.available_width() - View::BIG_SPINNER_SIZE) / 2.0;
let size = SizeHint::Size(ui.available_width() as u32, ui.available_width() as u32); ui.vertical_centered(|ui| {
let color_img = load_svg_bytes_with_size(svg, Some(size)).unwrap(); ui.add_space(space);
// Create image texture. View::big_loading_spinner(ui);
let texture_handle = ui.ctx().load_texture("qr_code", ui.add_space(space);
color_img.clone(), });
TextureOptions::default());
self.texture_handle = Some(texture_handle.clone()); // Create vector image from text if not creating.
let img_size = egui::emath::vec2(color_img.width() as f32, if !self.creating() {
color_img.height() as f32); self.create_svg(text);
let sized_img = SizedTexture::new(texture_handle.id(), img_size); }
// Add image to content. } else {
ui.add(egui::Image::from_texture(sized_img) // Create image from SVG data.
.max_height(ui.available_width()) let r_create = self.qr_creation_state.read();
.fit_to_original_size(1.0)); 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. /// Check if QR code is creating.
fn creating(&self) -> bool { fn creating(&self) -> bool {
let r_create = self.qr_creation_state.read(); let r_create = self.qr_creation_state.read();
r_create.creating r_create.creating
} }
/// Create multiple vector QR code images at separate thread.
fn create_svg_list(&self, text: String) {
let qr_creation_state = self.qr_creation_state.clone();
thread::spawn(move || {
let mut encoder = ur::Encoder::bytes(text.as_bytes(), 100).unwrap();
let mut data = Vec::with_capacity(encoder.fragment_count());
for _ in 0..encoder.fragment_count() {
let ur = encoder.next_part().unwrap();
if let Ok(qr) = QrCode::encode_text(ur.as_str(), qrcodegen::QrCodeEcc::Low) {
let svg = Self::qr_to_svg(qr, 0);
data.push(svg.into_bytes());
}
}
let mut w_create = qr_creation_state.write();
if !data.is_empty() {
w_create.svg_list = Some(data);
}
w_create.creating = false;
});
}
/// Check if image was created. /// Check if image was created.
fn has_image(&self) -> bool { fn has_image(&self) -> bool {
let r_create = self.qr_creation_state.read(); let r_create = self.qr_creation_state.read();
r_create.svg.is_some() r_create.svg.is_some() || r_create.svg_list.is_some()
} }
/// Create QR code image at separate thread. /// Create vector QR code image at separate thread.
fn create_image(&self, text: String) { fn create_svg(&self, text: String) {
let qr_creation_state = self.qr_creation_state.clone(); let qr_creation_state = self.qr_creation_state.clone();
if !self.creating() { thread::spawn(move || {
thread::spawn(move || { if let Ok(qr) = QrCode::encode_text(text.as_str(), qrcodegen::QrCodeEcc::Low) {
if let Ok(qr) = QrCode::encode_text(text.as_str(), qrcodegen::QrCodeEcc::Medium) { let svg = Self::qr_to_svg(qr, 0);
let svg = Self::qr_to_svg(qr, 0); let mut w_create = qr_creation_state.write();
let mut w_create = qr_creation_state.write(); w_create.creating = false;
w_create.creating = false; w_create.svg = Some(svg.into_bytes());
w_create.svg = Some(svg.into_bytes()); }
} });
});
}
} }
/// Convert QR code to SVG string. /// Convert QR code to SVG string.
fn qr_to_svg(qr: qrcodegen::QrCode, border: i32) -> String { fn qr_to_svg(qr: QrCode, border: i32) -> String {
let mut result = String::new(); let mut result = String::new();
let dimension = qr.size().checked_add(border.checked_mul(2).unwrap()).unwrap(); let dimension = qr.size().checked_add(border.checked_mul(2).unwrap()).unwrap();
result += "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"; result += "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";

View file

@ -154,19 +154,24 @@ pub enum QrScanResult {
Address(ZeroingString), Address(ZeroingString),
/// Parsed text. /// Parsed text.
Text(ZeroingString), Text(ZeroingString),
/// Parsed SeedQR https://github.com/SeedSigner/seedsigner/blob/dev/docs/seed_qr/README.md. /// Recovery phrase in standard or compact SeedQR format.
SeedQR(ZeroingString) /// https://github.com/SeedSigner/seedsigner/blob/dev/docs/seed_qr/README.md
SeedQR(ZeroingString),
/// Part of Uniform Resources as URI with current index and total messages amount.
/// https://github.com/BlockchainCommons/Research/blob/master/papers/bcr-2020-005-ur.md
URPart(String, usize, usize),
} }
impl QrScanResult { impl QrScanResult {
/// Get scan result value. /// Get text scanning result.
pub fn value(&self) -> String { pub fn text(&self) -> String {
match self { match self {
QrScanResult::Slatepack(text) => text, QrScanResult::Slatepack(text) => text.to_string(),
QrScanResult::Address(text) => text, QrScanResult::Address(text) => text.to_string(),
QrScanResult::Text(text) => text, QrScanResult::Text(text) => text.to_string(),
QrScanResult::SeedQR(text) => text QrScanResult::SeedQR(text) => text.to_string(),
}.to_string() QrScanResult::URPart(uri, _, _) => uri.to_string(),
}
} }
} }
@ -191,8 +196,10 @@ impl Default for QrScanState {
pub struct QrCreationState { pub struct QrCreationState {
// Flag to check if QR code image is creating. // Flag to check if QR code image is creating.
pub creating: bool, pub creating: bool,
// Found QR code content. // Vector image data.
pub svg: Option<Vec<u8>> pub svg: Option<Vec<u8>>,
// Multiple vector image data.
pub svg_list: Option<Vec<Vec<u8>>>
} }
impl Default for QrCreationState { impl Default for QrCreationState {
@ -200,6 +207,7 @@ impl Default for QrCreationState {
Self { Self {
creating: false, creating: false,
svg: None, svg: None,
svg_list: None,
} }
} }
} }

View file

@ -371,7 +371,7 @@ impl WalletContent {
cb: &dyn PlatformCallbacks) { cb: &dyn PlatformCallbacks) {
// Show scan result if exists or show camera content while scanning. // Show scan result if exists or show camera content while scanning.
if let Some(result) = &self.qr_scan_result { if let Some(result) = &self.qr_scan_result {
let mut result_text = result.value(); let mut result_text = result.text();
View::horizontal_line(ui, Colors::ITEM_STROKE); View::horizontal_line(ui, Colors::ITEM_STROKE);
ui.add_space(3.0); ui.add_space(3.0);
ScrollArea::vertical() ScrollArea::vertical()

View file

@ -173,13 +173,13 @@ impl WalletMessages {
request_edit: "".to_string(), request_edit: "".to_string(),
request_error: None, request_error: None,
request_qr: false, request_qr: false,
request_qr_content: QrCodeContent::new("".to_string()), request_qr_content: QrCodeContent::new("".to_string(), true),
request_loading: false, request_loading: false,
request_result: Arc::new(RwLock::new(None)), request_result: Arc::new(RwLock::new(None)),
message_camera_content: CameraContent::default(), message_camera_content: CameraContent::default(),
message_scan_error: false, message_scan_error: false,
qr_message_text: None, qr_message_text: None,
qr_message_content: QrCodeContent::new("".to_string()), qr_message_content: QrCodeContent::new("".to_string(), true),
} }
} }
@ -472,7 +472,7 @@ impl WalletMessages {
} else { } else {
t!("wallets.send_request_desc","amount" => amount_format) t!("wallets.send_request_desc","amount" => amount_format)
}; };
ui.label(RichText::new(desc_text).size(16.0).color(Colors::INACTIVE_TEXT)); ui.label(RichText::new(desc_text).size(16.0).color(Colors::GRAY));
}); });
ui.add_space(6.0); ui.add_space(6.0);

View file

@ -145,7 +145,7 @@ impl WalletTransport {
show_address_scan: false, show_address_scan: false,
address_scan_content: CameraContent::default(), address_scan_content: CameraContent::default(),
modal_just_opened: false, modal_just_opened: false,
qr_address_content: QrCodeContent::new(addr), qr_address_content: QrCodeContent::new(addr, false),
tor_settings_changed: false, tor_settings_changed: false,
bridge_bin_path_edit: bin_path, bridge_bin_path_edit: bin_path,
bridge_conn_line_edit: conn_line, bridge_conn_line_edit: conn_line,
@ -327,7 +327,7 @@ impl WalletTransport {
}; };
if let Some(result) = self.bridge_qr_scan_content.qr_scan_result() { if let Some(result) = self.bridge_qr_scan_content.qr_scan_result() {
self.bridge_conn_line_edit = result.value(); self.bridge_conn_line_edit = result.text();
on_stop(&mut self.bridge_qr_scan_content); on_stop(&mut self.bridge_qr_scan_content);
cb.show_keyboard(); cb.show_keyboard();
} else { } else {
@ -688,7 +688,7 @@ impl WalletTransport {
}; };
if let Some(result) = self.address_scan_content.qr_scan_result() { if let Some(result) = self.address_scan_content.qr_scan_result() {
self.address_edit = result.value(); self.address_edit = result.text();
self.modal_just_opened = true; self.modal_just_opened = true;
on_stop(&mut self.address_scan_content); on_stop(&mut self.address_scan_content);
cb.show_keyboard(); cb.show_keyboard();

View file

@ -80,7 +80,7 @@ impl Default for WalletTransactions {
tx_info_finalizing: false, tx_info_finalizing: false,
tx_info_final_result: Arc::new(RwLock::new(None)), tx_info_final_result: Arc::new(RwLock::new(None)),
tx_info_show_qr: false, tx_info_show_qr: false,
tx_info_qr_code_content: QrCodeContent::new("".to_string()), tx_info_qr_code_content: QrCodeContent::new("".to_string(), true),
tx_info_show_scanner: false, tx_info_show_scanner: false,
tx_info_scanner_content: CameraContent::default(), tx_info_scanner_content: CameraContent::default(),
confirm_cancel_tx_id: None, confirm_cancel_tx_id: None,
@ -708,7 +708,7 @@ impl WalletTransactions {
self.tx_info_scanner_content.clear_state(); self.tx_info_scanner_content.clear_state();
// Setup value to finalization input field. // Setup value to finalization input field.
self.tx_info_finalize_edit = result.value(); self.tx_info_finalize_edit = result.text();
self.on_finalization_input_change(tx, wallet, modal, cb); self.on_finalization_input_change(tx, wallet, modal, cb);
modal.enable_closing(); modal.enable_closing();
@ -737,7 +737,7 @@ impl WalletTransactions {
let desc_color = if self.tx_info_finalize_error { let desc_color = if self.tx_info_finalize_error {
Colors::RED Colors::RED
} else { } else {
Colors::INACTIVE_TEXT Colors::GRAY
}; };
ui.label(RichText::new(desc_text).size(16.0).color(desc_color)); ui.label(RichText::new(desc_text).size(16.0).color(desc_color));
} else { } else {
@ -754,7 +754,7 @@ impl WalletTransactions {
t!("wallets.parse_s1_slatepack_desc", "amount" => amount) t!("wallets.parse_s1_slatepack_desc", "amount" => amount)
} }
}; };
ui.label(RichText::new(desc_text).size(16.0).color(Colors::INACTIVE_TEXT)); ui.label(RichText::new(desc_text).size(16.0).color(Colors::GRAY));
} }
}); });
ui.add_space(6.0); ui.add_space(6.0);

View file

@ -23,8 +23,6 @@ impl WalletUtils {
let mut hasher = Sha256::new(); let mut hasher = Sha256::new();
hasher.update(data.clone()); hasher.update(data.clone());
let checksum = hasher.finalize(); let checksum = hasher.finalize();
println!("BEFORE data: {}, checksum: {}", data.len(), checksum.len());
data.extend(checksum); data.extend(checksum);
println!("AFTER data: {}", data.len());
} }
} }