bless
This commit is contained in:
parent
d8fd0dfb73
commit
d9339d5c92
1
lib/.gitignore
vendored
1
lib/.gitignore
vendored
@ -1 +0,0 @@
|
||||
/target
|
||||
5166
native_client/Cargo.lock
generated
5166
native_client/Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@ -2,7 +2,18 @@
|
||||
name = "native_client"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
build = "build.rs"
|
||||
|
||||
[dependencies]
|
||||
tauri = "=2.0.0-alpha.4"
|
||||
tauri-egui = "0.3.0"
|
||||
egui = "0.32.2"
|
||||
eframe = { version = "0.32.2", default-features = false, features = [
|
||||
"default_fonts", # Embed the default egui fonts.
|
||||
"glow", # Use the glow rendering backend. Alternative: "wgpu".
|
||||
"persistence", # Enable restoring app state when restarting the app.
|
||||
"wayland", # To support Linux (and CI)
|
||||
"x11", # To support older Linux distributions (restores one of the default features)
|
||||
] }
|
||||
serde = { version = "1.0.219", features = ["derive"] }
|
||||
watchlet = { path = "../watchlet" }
|
||||
hex = "0.4.3"
|
||||
vlogger = { path = "../vlogger" }
|
||||
|
||||
6
native_client/build.rs
Normal file
6
native_client/build.rs
Normal file
@ -0,0 +1,6 @@
|
||||
fn main() {
|
||||
let dir = env!("CARGO_MANIFEST_DIR");
|
||||
let data_path = format!("{dir}/data");
|
||||
|
||||
println!("cargo:rustc-env=DATA_DIR_PATH={}", data_path);
|
||||
}
|
||||
91
native_client/src/backend/wallet.rs
Normal file
91
native_client/src/backend/wallet.rs
Normal file
@ -0,0 +1,91 @@
|
||||
use std::sync::mpsc::{Receiver, RecvTimeoutError, Sender};
|
||||
use std::time::Duration;
|
||||
|
||||
use vlogger::*;
|
||||
use watchlet::{FileStorage, Wallet, WalletManager};
|
||||
|
||||
use crate::messages::error::SystemError;
|
||||
use crate::messages::frontend::FrontendMessage;
|
||||
use crate::messages::backend::BackendMessage;
|
||||
use crate::constants::DATA_DIR_PATH;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct WalletState {
|
||||
pub address: String,
|
||||
pub balance: u64,
|
||||
}
|
||||
|
||||
pub struct WalletService {
|
||||
exit: bool,
|
||||
receiver: Receiver<BackendMessage>,
|
||||
sender: Sender<FrontendMessage>,
|
||||
wallet_manager: WalletManager<FileStorage>,
|
||||
}
|
||||
|
||||
impl WalletService {
|
||||
pub fn new(
|
||||
receiver: Receiver<BackendMessage>,
|
||||
sender: Sender<FrontendMessage>,
|
||||
) -> Self {
|
||||
let wallet_path = format!("{}/wallet/wallet.age", DATA_DIR_PATH);
|
||||
let storage = FileStorage::new(wallet_path.into());
|
||||
let mut wallet_manager = WalletManager::new(storage);
|
||||
wallet_manager.load_wallet("password".into()).unwrap();
|
||||
Self {
|
||||
exit: false,
|
||||
receiver,
|
||||
sender,
|
||||
wallet_manager
|
||||
}
|
||||
}
|
||||
|
||||
pub fn wallet(&self) -> Option<&Wallet> {
|
||||
self.wallet_manager.wallet()
|
||||
}
|
||||
|
||||
pub fn update(&mut self) {
|
||||
log!(DEBUG, "Dummy Update");
|
||||
}
|
||||
|
||||
pub fn get_state(&self) -> Option<WalletState> {
|
||||
if let Some(wallet) = self.wallet() {
|
||||
let address = hex::encode(wallet.address());
|
||||
let balance = wallet.balance();
|
||||
Some(WalletState { address, balance })
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn handle_cmd(&mut self, cmd: BackendMessage) {
|
||||
log!(DEBUG, "Received Command: {:#?}", cmd);
|
||||
match cmd {
|
||||
BackendMessage::StateRequest => {
|
||||
let msg = match self.get_state() {
|
||||
Some(state) => FrontendMessage::StateResponse(state),
|
||||
None => FrontendMessage::Error(SystemError::WalletNotLoaded),
|
||||
};
|
||||
if let Err(e) = self.sender.send(msg) {
|
||||
log!(ERROR, "Failed to send FrontendMessage: {e}");
|
||||
}
|
||||
},
|
||||
BackendMessage::Transaction(_transaction) => {
|
||||
},
|
||||
BackendMessage::Shutdown => self.exit = true,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn run(&mut self) {
|
||||
while !self.exit {
|
||||
match self.receiver.recv_timeout(Duration::from_secs(30)) {
|
||||
Ok(cmd) => self.handle_cmd(cmd),
|
||||
Err(e) => match e {
|
||||
RecvTimeoutError::Timeout => {
|
||||
self.update()
|
||||
},
|
||||
RecvTimeoutError::Disconnected => {},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
1
native_client/src/constants.rs
Normal file
1
native_client/src/constants.rs
Normal file
@ -0,0 +1 @@
|
||||
pub const DATA_DIR_PATH: &str = env!("DATA_DIR_PATH");
|
||||
6
native_client/src/frontend/display.rs
Normal file
6
native_client/src/frontend/display.rs
Normal file
@ -0,0 +1,6 @@
|
||||
use std::sync::mpsc::Sender;
|
||||
use crate::messages::backend::BackendMessage;
|
||||
|
||||
pub trait DisplayPage {
|
||||
fn show(&mut self, rx: &Sender<BackendMessage>, ctx: &egui::Context);
|
||||
}
|
||||
0
native_client/src/frontend/message.rs
Normal file
0
native_client/src/frontend/message.rs
Normal file
41
native_client/src/frontend/pages/home.rs
Normal file
41
native_client/src/frontend/pages/home.rs
Normal file
@ -0,0 +1,41 @@
|
||||
use std::sync::mpsc::Sender;
|
||||
|
||||
use crate::{frontend::DisplayPage, messages::backend::BackendMessage};
|
||||
|
||||
#[derive(Default)]
|
||||
#[derive(serde::Deserialize, serde::Serialize)]
|
||||
pub struct HomePage {
|
||||
label: String,
|
||||
|
||||
#[serde(skip)]
|
||||
value: f32,
|
||||
}
|
||||
|
||||
impl DisplayPage for HomePage {
|
||||
fn show(&mut self, _rx: &Sender<BackendMessage>, ctx: &egui::Context) {
|
||||
egui::CentralPanel::default().show(ctx, |ui| {
|
||||
ui.heading("eframe template");
|
||||
|
||||
ui.horizontal(|ui| {
|
||||
ui.label("Write something: ");
|
||||
ui.text_edit_singleline(&mut self.label).request_focus();
|
||||
});
|
||||
|
||||
ui.add(egui::Slider::new(&mut self.value, 0.0..=10.0).text("value"));
|
||||
if ui.button("Increment").clicked() {
|
||||
self.value += 1.0;
|
||||
}
|
||||
|
||||
ui.separator();
|
||||
|
||||
ui.add(egui::github_link_file!(
|
||||
"https://github.com/emilk/eframe_template/blob/main/",
|
||||
"Source code."
|
||||
));
|
||||
|
||||
ui.with_layout(egui::Layout::bottom_up(egui::Align::LEFT), |ui| {
|
||||
egui::warn_if_debug_build(ui);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
52
native_client/src/frontend/pages/tx_page.rs
Normal file
52
native_client/src/frontend/pages/tx_page.rs
Normal file
@ -0,0 +1,52 @@
|
||||
use std::sync::mpsc::Sender;
|
||||
|
||||
use egui::{Align, Layout, RichText};
|
||||
|
||||
use crate::{frontend::DisplayPage, messages::backend::BackendMessage};
|
||||
|
||||
#[derive(Default)]
|
||||
#[derive(serde::Serialize, serde::Deserialize)]
|
||||
pub struct TransactionPage {
|
||||
from_to_amount: [String; 3],
|
||||
focused: usize,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct TransactionRequest {
|
||||
|
||||
}
|
||||
|
||||
const TX_FIELDS: &[&str] = &[
|
||||
"From:",
|
||||
"To:",
|
||||
"Amount:",
|
||||
];
|
||||
|
||||
|
||||
impl DisplayPage for TransactionPage {
|
||||
fn show(&mut self, tx: &Sender<BackendMessage>, ctx: &egui::Context) {
|
||||
ctx.input(|input| {
|
||||
if input.key_pressed(egui::Key::Tab) {
|
||||
self.focused = (self.focused + 1) % TX_FIELDS.len()
|
||||
}
|
||||
});
|
||||
egui::CentralPanel::default().show(ctx, |ui| {
|
||||
ui.heading("Transaction Page");
|
||||
ui.separator();
|
||||
ui.with_layout(Layout::top_down_justified(Align::LEFT), |ui| {
|
||||
for (i, f) in TX_FIELDS.iter().enumerate() {
|
||||
ui.add_space(8.0);
|
||||
ui.label(RichText::new(*f).size(14.0));
|
||||
if i == self.focused {
|
||||
ui.text_edit_singleline(&mut self.from_to_amount[i]).request_focus();
|
||||
} else {
|
||||
ui.text_edit_singleline(&mut self.from_to_amount[i]);
|
||||
}
|
||||
}
|
||||
});
|
||||
if ui.button("GetState").clicked() {
|
||||
tx.send(BackendMessage::StateRequest).unwrap();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
119
native_client/src/frontend/state.rs
Normal file
119
native_client/src/frontend/state.rs
Normal file
@ -0,0 +1,119 @@
|
||||
use egui::{Button, Color32, Key, RichText};
|
||||
use crate::messages::{backend::BackendMessage, frontend::FrontendMessage};
|
||||
use vlogger::*;
|
||||
|
||||
use super::{pages::HomePage, DisplayPage, TransactionPage};
|
||||
use std::sync::mpsc::{self, Receiver, Sender};
|
||||
|
||||
#[derive(serde::Deserialize, serde::Serialize)]
|
||||
#[serde(default)]
|
||||
pub struct State {
|
||||
page: Page,
|
||||
#[serde(skip)]
|
||||
receiver: Receiver<FrontendMessage>,
|
||||
#[serde(skip)]
|
||||
sender: Sender<BackendMessage>,
|
||||
}
|
||||
|
||||
#[derive(serde::Deserialize, serde::Serialize)]
|
||||
pub enum Page {
|
||||
Home(HomePage),
|
||||
Transaction(TransactionPage),
|
||||
}
|
||||
|
||||
impl DisplayPage for Page {
|
||||
fn show(&mut self, rx: &Sender<BackendMessage>, ctx: &egui::Context) {
|
||||
match self {
|
||||
Page::Home(home) => home.show(rx, ctx),
|
||||
Page::Transaction(tx) => tx.show(rx, ctx),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for State {
|
||||
fn default() -> Self {
|
||||
let (_, front_rx) = mpsc::channel::<FrontendMessage>();
|
||||
let (back_tx, _) = mpsc::channel::<BackendMessage>();
|
||||
Self {
|
||||
// Example stuff:
|
||||
// page: Page::Home(HomePage::default()),
|
||||
page: Page::Transaction(TransactionPage::default()),
|
||||
receiver: front_rx,
|
||||
sender: back_tx,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl State {
|
||||
pub fn new(
|
||||
cc: &eframe::CreationContext<'_>,
|
||||
receiver: Receiver<FrontendMessage>,
|
||||
sender: Sender<BackendMessage>,
|
||||
) -> Self {
|
||||
if let Some(storage) = cc.storage {
|
||||
let mut ret = eframe::get_value::<Self>(storage, eframe::APP_KEY).unwrap_or_default();
|
||||
ret.receiver = receiver;
|
||||
ret.sender = sender;
|
||||
ret
|
||||
} else {
|
||||
Self {
|
||||
receiver,
|
||||
sender,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl eframe::App for State {
|
||||
fn save(&mut self, storage: &mut dyn eframe::Storage) {
|
||||
eframe::set_value(storage, eframe::APP_KEY, self);
|
||||
}
|
||||
|
||||
fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
|
||||
if let Ok(msg) = self.receiver.try_recv() {
|
||||
match msg {
|
||||
FrontendMessage::StateResponse(state) => {
|
||||
log!(DEBUG, "Recived: {:#?}", state);
|
||||
}
|
||||
FrontendMessage::Error(e) => {
|
||||
log!(ERROR, "Received Error {e:?}")
|
||||
}
|
||||
}
|
||||
}
|
||||
egui::TopBottomPanel::top("top_panel").show(ctx, |ui| {
|
||||
ctx.input(|input| {
|
||||
if input.key_pressed(Key::Escape) {
|
||||
self.sender.send(BackendMessage::Shutdown).unwrap();
|
||||
std::process::exit(1)
|
||||
};
|
||||
});
|
||||
egui::MenuBar::new().ui(ui, |ui| {
|
||||
egui::widgets::global_theme_preference_buttons(ui);
|
||||
});
|
||||
});
|
||||
egui::SidePanel::left("menu panel").show(ctx, |ui| {
|
||||
ui.heading("Navigation");
|
||||
let available_width = ui.available_width();
|
||||
|
||||
if ui.add_sized(
|
||||
[available_width, 24.0],
|
||||
Button::new(RichText::new("Home").size(14.0))
|
||||
.right_text("")
|
||||
.fill(Color32::TRANSPARENT)
|
||||
).clicked() {
|
||||
self.page = Page::Home(HomePage::default())
|
||||
}
|
||||
|
||||
if ui.add_sized(
|
||||
[available_width, 24.0],
|
||||
Button::new(RichText::new("Transaction").size(14.0))
|
||||
.right_text("")
|
||||
.fill(Color32::TRANSPARENT)
|
||||
).clicked() {
|
||||
self.page = Page::Transaction(TransactionPage::default())
|
||||
}
|
||||
});
|
||||
self.page.show(&self.sender, ctx)
|
||||
}
|
||||
}
|
||||
31
native_client/src/lib.rs
Normal file
31
native_client/src/lib.rs
Normal file
@ -0,0 +1,31 @@
|
||||
pub mod constants;
|
||||
|
||||
pub mod frontend {
|
||||
pub mod state;
|
||||
pub use state::*;
|
||||
|
||||
pub mod display;
|
||||
pub use display::*;
|
||||
|
||||
pub mod message;
|
||||
|
||||
pub use pages::*;
|
||||
pub mod pages {
|
||||
|
||||
pub mod tx_page;
|
||||
pub use tx_page::*;
|
||||
|
||||
pub mod home;
|
||||
pub use home::*;
|
||||
}
|
||||
}
|
||||
|
||||
pub mod messages {
|
||||
pub mod frontend;
|
||||
pub mod backend;
|
||||
pub mod error;
|
||||
}
|
||||
|
||||
pub mod backend {
|
||||
pub mod wallet;
|
||||
}
|
||||
@ -1,5 +1,37 @@
|
||||
use tauri_egui;
|
||||
#![warn(clippy::all, rust_2018_idioms)]
|
||||
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
|
||||
|
||||
fn main() {
|
||||
println!("Hello, world!");
|
||||
use native_client::{backend::wallet::WalletService, frontend, messages::{backend::BackendMessage, frontend::FrontendMessage}};
|
||||
use std::sync::mpsc::{self, Receiver, Sender};
|
||||
|
||||
fn dispatch_frontend(
|
||||
receiver: Receiver<FrontendMessage>,
|
||||
sender: Sender<BackendMessage>,
|
||||
) -> eframe::Result {
|
||||
let native_options = eframe::NativeOptions {
|
||||
viewport: egui::ViewportBuilder::default()
|
||||
.with_inner_size([400.0, 300.0])
|
||||
.with_min_inner_size([300.0, 220.0]),
|
||||
..Default::default()
|
||||
};
|
||||
eframe::run_native(
|
||||
"eframe template",
|
||||
native_options,
|
||||
Box::new(|cc| Ok(Box::new(frontend::State::new(cc, receiver, sender)))),
|
||||
)
|
||||
}
|
||||
|
||||
fn main() -> Result<(), std::io::Error> {
|
||||
let (front_tx, front_rx) = mpsc::channel::<FrontendMessage>();
|
||||
let (back_tx, back_rx) = mpsc::channel::<BackendMessage>();
|
||||
|
||||
let backend_handle = std::thread::spawn( || {
|
||||
let mut wallet_service = WalletService::new(back_rx, front_tx);
|
||||
wallet_service.run();
|
||||
});
|
||||
|
||||
let _ = dispatch_frontend(front_rx, back_tx);
|
||||
|
||||
backend_handle.join().unwrap();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
8
native_client/src/messages/backend.rs
Normal file
8
native_client/src/messages/backend.rs
Normal file
@ -0,0 +1,8 @@
|
||||
use crate::frontend::TransactionRequest;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum BackendMessage {
|
||||
StateRequest,
|
||||
Transaction(TransactionRequest),
|
||||
Shutdown,
|
||||
}
|
||||
4
native_client/src/messages/error.rs
Normal file
4
native_client/src/messages/error.rs
Normal file
@ -0,0 +1,4 @@
|
||||
#[derive(Debug)]
|
||||
pub enum SystemError {
|
||||
WalletNotLoaded
|
||||
}
|
||||
8
native_client/src/messages/frontend.rs
Normal file
8
native_client/src/messages/frontend.rs
Normal file
@ -0,0 +1,8 @@
|
||||
use crate::backend::wallet::WalletState;
|
||||
|
||||
use super::error::SystemError;
|
||||
|
||||
pub enum FrontendMessage {
|
||||
StateResponse(WalletState),
|
||||
Error(SystemError)
|
||||
}
|
||||
@ -14,7 +14,7 @@ thiserror = "2.0.16"
|
||||
tokio = { version = "1.47.1", features = ["rt-multi-thread", "net", "sync", "time", "macros"] }
|
||||
tokio-tungstenite = "0.27.0"
|
||||
uuid = { version = "1.18.0", features = ["v4", "serde"] }
|
||||
vlogger = { path = "../lib/logger-rs" }
|
||||
vlogger = { path = "../vlogger" }
|
||||
ratatui = "0.29.0"
|
||||
crossterm = { version = "0.29.0", features = ["event-stream"] }
|
||||
once_cell = "1.21.3"
|
||||
|
||||
0
wallet/.gitignore → watchlet/.gitignore
vendored
0
wallet/.gitignore → watchlet/.gitignore
vendored
38
wallet/Cargo.lock → watchlet/Cargo.lock
generated
38
wallet/Cargo.lock → watchlet/Cargo.lock
generated
@ -1610,25 +1610,6 @@ dependencies = [
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wallet"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"age",
|
||||
"bincode",
|
||||
"getrandom",
|
||||
"hex",
|
||||
"js-sys",
|
||||
"k256",
|
||||
"serde",
|
||||
"sha3",
|
||||
"shared",
|
||||
"thiserror 2.0.16",
|
||||
"wasm-bindgen",
|
||||
"wasm-bindgen-test",
|
||||
"web-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.11.1+wasi-snapshot-preview1"
|
||||
@ -1733,6 +1714,25 @@ dependencies = [
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "watchlet"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"age",
|
||||
"bincode",
|
||||
"getrandom",
|
||||
"hex",
|
||||
"js-sys",
|
||||
"k256",
|
||||
"serde",
|
||||
"sha3",
|
||||
"shared",
|
||||
"thiserror 2.0.16",
|
||||
"wasm-bindgen",
|
||||
"wasm-bindgen-test",
|
||||
"web-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "web-sys"
|
||||
version = "0.3.78"
|
||||
@ -1,5 +1,5 @@
|
||||
[package]
|
||||
name = "wallet"
|
||||
name = "watchlet"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
@ -134,6 +134,10 @@ impl Wallet {
|
||||
self.nonce
|
||||
}
|
||||
|
||||
pub fn balance(&self) -> u64 {
|
||||
self.balance
|
||||
}
|
||||
|
||||
pub fn public_key(&self) -> Result<VerifyingKey, WalletError> {
|
||||
let pk = self.private_key()?;
|
||||
Ok(*pk.verifying_key())
|
||||
BIN
watchlet/wallet.age
Normal file
BIN
watchlet/wallet.age
Normal file
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user