mirror of
https://github.com/jwinterm/grim.git
synced 2026-10-19 14:58:33 +02:00
qr: parse scan result, slatepack address image
This commit is contained in:
+53
-30
@@ -13,20 +13,22 @@
|
||||
// limitations under the License.
|
||||
|
||||
use std::sync::{Arc, RwLock};
|
||||
use std::thread;
|
||||
use eframe::emath::Align;
|
||||
use egui::load::SizedTexture;
|
||||
use egui::{Layout, Pos2, Rect, TextureOptions, Widget};
|
||||
use grin_wallet_libwallet::SlatepackAddress;
|
||||
use image::{DynamicImage, EncodableLayout, ImageFormat};
|
||||
use crate::gui::Colors;
|
||||
use crate::gui::icons::CAMERA_ROTATE;
|
||||
|
||||
use crate::gui::platform::PlatformCallbacks;
|
||||
use crate::gui::views::types::QrScanState;
|
||||
use crate::gui::views::types::{QrScanResult, QrScanState};
|
||||
use crate::gui::views::View;
|
||||
|
||||
/// Camera scanner content.
|
||||
/// Camera QR code scanner.
|
||||
pub struct CameraContent {
|
||||
// QR code scanning progress and result.
|
||||
/// QR code scanning progress and result.
|
||||
qr_scan_state: Arc<RwLock<QrScanState>>
|
||||
}
|
||||
|
||||
@@ -126,7 +128,7 @@ impl CameraContent {
|
||||
|
||||
/// Check if image is processing to find QR code.
|
||||
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
|
||||
}
|
||||
|
||||
@@ -142,39 +144,60 @@ impl CameraContent {
|
||||
w_scan.image_processing = true;
|
||||
}
|
||||
// Launch scanner at separate thread.
|
||||
tokio::runtime::Builder::new_multi_thread()
|
||||
.enable_all()
|
||||
.build()
|
||||
.unwrap()
|
||||
.block_on(async {
|
||||
// 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();
|
||||
for g in grids {
|
||||
if let Ok((meta, text)) = g.decode() {
|
||||
println!("12345 ecc: {}, text: {}", meta.ecc_level, text.clone());
|
||||
if !text.trim().is_empty() {
|
||||
let mut w_scan = self.qr_scan_state.write().unwrap();
|
||||
w_scan.qr_scan_result = Some(text);
|
||||
let data = data.clone();
|
||||
let qr_scan_state = self.qr_scan_state.clone();
|
||||
thread::spawn(move || {
|
||||
tokio::runtime::Builder::new_multi_thread()
|
||||
.enable_all()
|
||||
.build()
|
||||
.unwrap()
|
||||
.block_on(async {
|
||||
// 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();
|
||||
for g in grids {
|
||||
if let Ok((_, text)) = g.decode() {
|
||||
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.
|
||||
{
|
||||
let mut w_scan = self.qr_scan_state.write().unwrap();
|
||||
w_scan.image_processing = false;
|
||||
}
|
||||
});
|
||||
// Setup scanning flag.
|
||||
{
|
||||
let mut w_scan = qr_scan_state.write().unwrap();
|
||||
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.
|
||||
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();
|
||||
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
|
||||
}
|
||||
|
||||
@@ -33,4 +33,7 @@ mod wallets;
|
||||
pub use wallets::*;
|
||||
|
||||
mod camera;
|
||||
pub use camera::*;
|
||||
pub use camera::*;
|
||||
|
||||
mod qr;
|
||||
pub use qr::*;
|
||||
@@ -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 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::platform::PlatformCallbacks;
|
||||
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::types::{GRIN, WalletTab, WalletTabType};
|
||||
use crate::node::Node;
|
||||
@@ -44,7 +44,7 @@ pub struct WalletContent {
|
||||
/// Camera content for QR scan [`Modal`].
|
||||
camera_content: CameraContent,
|
||||
/// QR code scan result
|
||||
qr_scan_result: Option<String>,
|
||||
qr_scan_result: Option<QrScanResult>,
|
||||
|
||||
/// Current tab content to show.
|
||||
pub current_tab: Box<dyn WalletTab>
|
||||
@@ -367,15 +367,37 @@ impl WalletContent {
|
||||
cb: &dyn PlatformCallbacks) {
|
||||
ui.add_space(6.0);
|
||||
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.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() {
|
||||
///TODO: parse result and show
|
||||
cb.stop_camera();
|
||||
self.camera_content.clear_state();
|
||||
println!("result: {}", result);
|
||||
self.qr_scan_result = Some(result);
|
||||
match &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 {
|
||||
self.camera_content.ui(ui, cb);
|
||||
}
|
||||
@@ -409,13 +431,14 @@ impl WalletContent {
|
||||
let is_messages = current_type == WalletTabType::Messages;
|
||||
View::tab_button(ui, CHAT_CIRCLE_TEXT, is_messages, || {
|
||||
self.current_tab = Box::new(
|
||||
WalletMessages::new(wallet.can_use_dandelion())
|
||||
WalletMessages::new(wallet.can_use_dandelion(), None)
|
||||
);
|
||||
});
|
||||
});
|
||||
columns[2].vertical_centered_justified(|ui| {
|
||||
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| {
|
||||
|
||||
@@ -77,22 +77,6 @@ pub struct WalletMessages {
|
||||
/// Identifier for amount input [`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 {
|
||||
fn get_type(&self) -> WalletTabType {
|
||||
WalletTabType::Messages
|
||||
@@ -140,6 +124,21 @@ impl WalletTab for 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.
|
||||
pub fn ui(&mut self,
|
||||
ui: &mut egui::Ui,
|
||||
@@ -452,7 +451,7 @@ impl WalletMessages {
|
||||
}
|
||||
|
||||
/// 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;
|
||||
if self.message_edit.is_empty() {
|
||||
return;
|
||||
|
||||
@@ -22,7 +22,7 @@ use grin_wallet_libwallet::SlatepackAddress;
|
||||
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::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::wallets::wallet::types::{WalletTab, WalletTabType};
|
||||
use crate::gui::views::wallets::wallet::WalletContent;
|
||||
@@ -46,20 +46,9 @@ pub struct WalletTransport {
|
||||
address_error: bool,
|
||||
/// Flag to check if [`Modal`] was just opened to focus on first field.
|
||||
modal_just_opened: bool,
|
||||
}
|
||||
|
||||
impl Default for WalletTransport {
|
||||
fn default() -> 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 address image [`Modal`] content.
|
||||
qr_code_content: QrCodeContent,
|
||||
}
|
||||
|
||||
impl WalletTab for WalletTransport {
|
||||
@@ -114,7 +103,24 @@ const SEND_TOR_MODAL: &'static str = "send_tor_modal";
|
||||
/// Identifier for [`Modal`] to setup Tor service.
|
||||
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 {
|
||||
/// 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.
|
||||
pub fn ui(&mut self, ui: &mut egui::Ui, wallet: &mut Wallet, cb: &dyn PlatformCallbacks) {
|
||||
ui.add_space(3.0);
|
||||
@@ -146,6 +152,11 @@ impl WalletTransport {
|
||||
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.
|
||||
let button_rounding = View::item_rounding(0, 2, true);
|
||||
View::item_button(ui, button_rounding, GEAR_SIX, None, || {
|
||||
// Show modal.
|
||||
// Show Tor settings modal.
|
||||
Modal::new(TOR_SETTINGS_MODAL)
|
||||
.position(ModalPosition::CenterTop)
|
||||
.title(t!("transport.tor_settings"))
|
||||
@@ -273,7 +284,7 @@ impl WalletTransport {
|
||||
}
|
||||
|
||||
/// Draw Tor send content.
|
||||
fn tor_receive_ui(&self,
|
||||
fn tor_receive_ui(&mut self,
|
||||
ui: &mut egui::Ui,
|
||||
wallet: &Wallet,
|
||||
data: &WalletData,
|
||||
@@ -304,7 +315,12 @@ impl WalletTransport {
|
||||
View::item_rounding(1, 2, true)
|
||||
};
|
||||
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.
|
||||
@@ -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.
|
||||
fn tor_send_ui(&mut self, ui: &mut egui::Ui, cb: &dyn PlatformCallbacks) {
|
||||
// Setup layout size.
|
||||
@@ -355,14 +393,14 @@ impl WalletTransport {
|
||||
// Draw button to open sending modal.
|
||||
let send_text = format!("{} {}", EXPORT, t!("wallets.send"));
|
||||
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.
|
||||
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.
|
||||
let mut w_send_err = self.tor_send_error.write().unwrap();
|
||||
*w_send_err = false;
|
||||
@@ -372,7 +410,7 @@ impl WalletTransport {
|
||||
*w_success = false;
|
||||
self.modal_just_opened = true;
|
||||
self.amount_edit = "".to_string();
|
||||
self.address_edit = "".to_string();
|
||||
self.address_edit = address.unwrap_or("".to_string());
|
||||
self.address_error = false;
|
||||
// Show modal.
|
||||
Modal::new(SEND_TOR_MODAL)
|
||||
|
||||
Reference in New Issue
Block a user