send.rs 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173
  1. /* This file is part of DarkFi (https://dark.fi)
  2. *
  3. * Copyright (C) 2020-2026 Dyne.org foundation
  4. *
  5. * This program is free software: you can redistribute it and/or modify
  6. * it under the terms of the GNU Affero General Public License as
  7. * published by the Free Software Foundation, either version 3 of the
  8. * License, or (at your option) any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU Affero General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU Affero General Public License
  16. * along with this program. If not, see <https://www.gnu.org/licenses/>.
  17. */
  18. use darkfi::{tx::Transaction, util::parse::encode_base10};
  19. use darkfi_serial::Decodable;
  20. use crate::{
  21. app::App,
  22. gfx::gfxtag,
  23. prop::{PropertyBool, PropertyFloat32, Role},
  24. scene::SceneNodePtr,
  25. util::i18n::I18nBabelFish,
  26. };
  27. use super::{
  28. data::{SendTxData, BALANCE_BASE10_DECIMALS},
  29. send_step1, send_step2, send_step3, send_step4, tx_status,
  30. };
  31. pub async fn make(
  32. app: &App,
  33. wallet_layer: SceneNodePtr,
  34. i18n_fish: &I18nBabelFish,
  35. window_scale: PropertyFloat32,
  36. ) -> SceneNodePtr {
  37. let send_tx_data = std::sync::Arc::new(std::sync::Mutex::new(SendTxData::new()));
  38. let send_step1_layer = send_step1::make(
  39. app,
  40. wallet_layer.clone(),
  41. i18n_fish,
  42. window_scale.clone(),
  43. send_tx_data.clone(),
  44. )
  45. .await;
  46. let send_step2_layer = send_step2::make(
  47. app,
  48. wallet_layer.clone(),
  49. i18n_fish,
  50. window_scale.clone(),
  51. send_tx_data.clone(),
  52. )
  53. .await;
  54. let step2_is_visible =
  55. PropertyBool::wrap(&send_step2_layer, Role::App, "is_visible", 0).unwrap();
  56. let _send_step3_layer = send_step3::make(
  57. app,
  58. wallet_layer.clone(),
  59. i18n_fish,
  60. window_scale.clone(),
  61. send_tx_data.clone(),
  62. )
  63. .await;
  64. let _send_step4_layer = send_step4::make(
  65. app,
  66. wallet_layer.clone(),
  67. i18n_fish,
  68. window_scale.clone(),
  69. send_tx_data.clone(),
  70. )
  71. .await;
  72. let tx_status_layer =
  73. tx_status::make(app, wallet_layer.clone(), i18n_fish, window_scale.clone(), send_tx_data)
  74. .await;
  75. // Add listener for tx built signal to update send button label and show fee
  76. let set_built_tx_sub = tx_status_layer.subscribe_method_call("set_built_tx").unwrap();
  77. let redraw_for_built = app.redraw_trigger.clone();
  78. let sg_root_for_built = app.sg_root.clone();
  79. app.tasks.lock().push(app.ex.spawn(async move {
  80. while let Ok(mcall) = set_built_tx_sub.receive().await {
  81. let mut cur = std::io::Cursor::new(mcall.data);
  82. let tx = Transaction::decode(&mut cur).unwrap();
  83. let mut fees: u64 = 0;
  84. for call in tx.calls.iter() {
  85. if call.data.is_money_fee() {
  86. if let Ok(fee) = darkfi_serial::deserialize(&call.data.data[1..9]) {
  87. fees = fees.saturating_add(fee);
  88. }
  89. }
  90. }
  91. let atom = &mut redraw_for_built.make_guard(gfxtag!("tx built - update send button"));
  92. // Make send button active
  93. if let Some(send_layer_node) = sg_root_for_built
  94. .lookup_node("/window/content/wallet/send_step4_layer/send_btn_layer")
  95. {
  96. send_layer_node.set_property_bool(atom, Role::App, "is_visible", true).unwrap();
  97. }
  98. if let Some(send_layer_grey_node) = sg_root_for_built
  99. .lookup_node("/window/content/wallet/send_step4_layer/send_btn_layer_grey")
  100. {
  101. send_layer_grey_node
  102. .set_property_bool(atom, Role::App, "is_visible", false)
  103. .unwrap();
  104. }
  105. // Show transaction fee
  106. if let Some(tx_fee_label) = sg_root_for_built
  107. .lookup_node("/window/content/wallet/send_step4_layer/send_fee_label")
  108. {
  109. let prop = tx_fee_label.get_property("text_color").unwrap();
  110. prop.set_f32(atom, Role::App, 3, 1.).unwrap();
  111. }
  112. if let Some(tx_fee_value) = sg_root_for_built
  113. .lookup_node("/window/content/wallet/send_step4_layer/send_fee_value")
  114. {
  115. tx_fee_value
  116. .set_property_str(
  117. atom,
  118. Role::App,
  119. "text",
  120. format!("{} DRK", encode_base10(fees, BALANCE_BASE10_DECIMALS)),
  121. )
  122. .unwrap();
  123. let prop = tx_fee_value.get_property("text_color").unwrap();
  124. prop.set_f32(atom, Role::App, 3, 1.).unwrap();
  125. }
  126. }
  127. }));
  128. // Setup step2 visibility listener for auto-focusing recipient input
  129. let step2_is_visible_for_focus = step2_is_visible.clone();
  130. let sg_root_for_focus = app.sg_root.clone();
  131. let step2_is_visible_sub = step2_is_visible.prop().subscribe_modify();
  132. let listen_step2_visible = app.ex.spawn(async move {
  133. while let Ok(_) = step2_is_visible_sub.receive().await {
  134. let recipient_input_node = sg_root_for_focus
  135. .lookup_node("/window/content/wallet/send_step2_layer/send_recipient_input")
  136. .unwrap();
  137. if step2_is_visible_for_focus.get() {
  138. // Focus when becoming visible
  139. loop {
  140. darkfi::system::msleep(16).await;
  141. let input_rect =
  142. crate::prop::PropertyRect::wrap(&recipient_input_node, Role::App, "rect")
  143. .unwrap();
  144. if input_rect.has_cached() {
  145. break;
  146. }
  147. }
  148. recipient_input_node.call_method("focus", vec![]).await.unwrap();
  149. } else {
  150. // Unfocus when becoming hidden
  151. recipient_input_node.call_method("unfocus", vec![]).await.unwrap();
  152. }
  153. }
  154. });
  155. app.tasks.lock().push(listen_step2_visible);
  156. send_step1_layer
  157. }