Skip to content

Commit

Permalink
RPC in UI with toggle
Browse files Browse the repository at this point in the history
  • Loading branch information
Rigidity committed Feb 28, 2025
1 parent b269dd5 commit a628e7d
Show file tree
Hide file tree
Showing 7 changed files with 233 additions and 86 deletions.
3 changes: 3 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions src-tauri/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ sage-api = { workspace = true, features = ["tauri"] }
sage-api-macro = { workspace = true }
sage-config = { workspace = true }
sage-wallet = { workspace = true }
sage-rpc = { workspace = true }
serde = { workspace = true, features = ["derive"] }
tauri = { workspace = true, features = [] }
tauri-specta = { workspace = true, features = ["derive", "typescript"] }
Expand All @@ -32,6 +33,8 @@ tauri-plugin-shell = { workspace = true }
tauri-plugin-os = { workspace = true }
serde_json = { workspace = true, features = ["arbitrary_precision"] }
tracing = { workspace = true }
anyhow = { workspace = true }
rustls = { workspace = true }

# This is to ensure that the bindgen feature is enabled for the aws-lc-rs crate.
# https://aws.github.io/aws-lc-rs/platform_support.html#tested-platforms
Expand Down
111 changes: 36 additions & 75 deletions src-tauri/src/app_state.rs
Original file line number Diff line number Diff line change
@@ -1,83 +1,44 @@
use std::{
ops::{Deref, DerefMut},
path::Path,
sync::Arc,
};
use std::sync::Arc;

use sage::Sage;
use sage::{Result, Sage};
use sage_api::SyncEvent as ApiEvent;
use sage_wallet::SyncEvent;
use tauri::{AppHandle, Emitter};
use tokio::sync::Mutex;

use crate::error::Result;

pub type AppState = Arc<Mutex<AppStateInner>>;

pub struct AppStateInner {
pub app_handle: AppHandle,
pub initialized: bool,
pub sage: Sage,
}

impl Deref for AppStateInner {
type Target = Sage;

fn deref(&self) -> &Self::Target {
&self.sage
}
}

impl DerefMut for AppStateInner {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.sage
}
}

impl AppStateInner {
pub fn new(app_handle: AppHandle, path: &Path) -> Self {
Self {
app_handle,
initialized: false,
sage: Sage::new(path),
}
}

pub async fn initialize(&mut self) -> Result<bool> {
if self.initialized {
return Ok(true);
}

self.initialized = true;

let mut receiver = self.sage.initialize().await?;
let app_handle = self.app_handle.clone();

tokio::spawn(async move {
while let Some(event) = receiver.recv().await {
let event = match event {
SyncEvent::Start(ip) => ApiEvent::Start { ip: ip.to_string() },
SyncEvent::Stop => ApiEvent::Stop,
SyncEvent::Subscribed => ApiEvent::Subscribed,
SyncEvent::DerivationIndex { .. } => ApiEvent::Derivation,
// TODO: New event?
SyncEvent::CoinsUpdated { .. }
| SyncEvent::TransactionUpdated { .. }
| SyncEvent::TransactionEnded { .. }
| SyncEvent::OfferUpdated { .. } => ApiEvent::CoinState,
SyncEvent::PuzzleBatchSynced => ApiEvent::PuzzleBatchSynced,
SyncEvent::CatInfo => ApiEvent::CatInfo,
SyncEvent::DidInfo => ApiEvent::DidInfo,
SyncEvent::NftData => ApiEvent::NftData,
};
if app_handle.emit("sync-event", event).is_err() {
break;
}
use tokio::{sync::Mutex, task::JoinHandle};

pub struct Initialized(pub Mutex<bool>);

pub struct RpcTask(pub Mutex<Option<JoinHandle<anyhow::Result<()>>>>);

pub type AppState = Arc<Mutex<Sage>>;

pub async fn initialize(app_handle: AppHandle, sage: &mut Sage) -> Result<()> {
let mut receiver = sage.initialize().await?;

tokio::spawn(async move {
while let Some(event) = receiver.recv().await {
let event = match event {
SyncEvent::Start(ip) => ApiEvent::Start { ip: ip.to_string() },
SyncEvent::Stop => ApiEvent::Stop,
SyncEvent::Subscribed => ApiEvent::Subscribed,
SyncEvent::DerivationIndex { .. } => ApiEvent::Derivation,
// TODO: New event?
SyncEvent::CoinsUpdated { .. }
| SyncEvent::TransactionUpdated { .. }
| SyncEvent::TransactionEnded { .. }
| SyncEvent::OfferUpdated { .. } => ApiEvent::CoinState,
SyncEvent::PuzzleBatchSynced => ApiEvent::PuzzleBatchSynced,
SyncEvent::CatInfo => ApiEvent::CatInfo,
SyncEvent::DidInfo => ApiEvent::DidInfo,
SyncEvent::NftData => ApiEvent::NftData,
};
if app_handle.emit("sync-event", event).is_err() {
break;
}
}

Result::Ok(())
});
Result::Ok(())
});

Ok(false)
}
Ok(())
}
77 changes: 72 additions & 5 deletions src-tauri/src/commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,37 @@ use chia_wallet_sdk::decode_address;
use sage_api::{wallet_connect::*, *};
use sage_api_macro::impl_endpoints_tauri;
use sage_config::{NetworkConfig, WalletConfig};
use sage_rpc::start_rpc;
use specta::specta;
use tauri::{command, State};
use tauri::{command, AppHandle, State};
use tokio::time::sleep;
use tracing::error;

use crate::{app_state::AppState, error::Result};
use crate::{
app_state::{self, AppState, Initialized, RpcTask},
error::Result,
};

#[command]
#[specta]
pub async fn initialize(state: State<'_, AppState>) -> Result<()> {
if state.lock().await.initialize().await? {
pub async fn initialize(
app_handle: AppHandle,
state: State<'_, AppState>,
initialized: State<'_, Initialized>,
rpc_task: State<'_, RpcTask>,
) -> Result<()> {
let mut initialized = initialized.0.lock().await;

if *initialized {
return Ok(());
}

*initialized = true;

let mut sage = state.lock().await;
app_state::initialize(app_handle, &mut sage).await?;
drop(sage);

let app_state = (*state).clone();

tokio::spawn(async move {
Expand All @@ -26,14 +43,20 @@ pub async fn initialize(state: State<'_, AppState>) -> Result<()> {

let app_state = app_state.lock().await;

if let Err(error) = app_state.sage.save_peers().await {
if let Err(error) = app_state.save_peers().await {
error!("Error while saving peers: {error:?}");
}

drop(app_state);
}
});

let app_state = state.lock().await;

if app_state.config.rpc.run_on_startup {
*rpc_task.0.lock().await = Some(tokio::spawn(start_rpc((*state).clone())));
}

Ok(())
}

Expand Down Expand Up @@ -68,3 +91,47 @@ pub async fn network_config(state: State<'_, AppState>) -> Result<NetworkConfig>
pub async fn wallet_config(state: State<'_, AppState>, fingerprint: u32) -> Result<WalletConfig> {
Ok(state.lock().await.try_wallet_config(fingerprint).clone())
}

#[command]
#[specta]
pub async fn is_rpc_running(rpc_task: State<'_, RpcTask>) -> Result<bool> {
Ok(rpc_task.0.lock().await.is_some())
}

#[command]
#[specta]
pub async fn start_rpc_server(
state: State<'_, AppState>,
rpc_task: State<'_, RpcTask>,
) -> Result<()> {
let mut rpc_task = rpc_task.0.lock().await;
*rpc_task = Some(tokio::spawn(start_rpc((*state).clone())));
Ok(())
}

#[command]
#[specta]
pub async fn stop_rpc_server(rpc_task: State<'_, RpcTask>) -> Result<()> {
let mut rpc_task = rpc_task.0.lock().await;
if let Some(handle) = rpc_task.take() {
handle.abort();
}
Ok(())
}

#[command]
#[specta]
pub async fn get_rpc_run_on_startup(state: State<'_, AppState>) -> Result<bool> {
Ok(state.lock().await.config.rpc.run_on_startup)
}

#[command]
#[specta]
pub async fn set_rpc_run_on_startup(
state: State<'_, AppState>,
run_on_startup: bool,
) -> Result<()> {
state.lock().await.config.rpc.run_on_startup = run_on_startup;
state.lock().await.save_config()?;
Ok(())
}
19 changes: 15 additions & 4 deletions src-tauri/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
use app_state::{AppState, AppStateInner};
use app_state::{AppState, Initialized, RpcTask};
use rustls::crypto::aws_lc_rs::default_provider;
use sage::Sage;
use sage_api::SyncEvent;
use tauri::Manager;
use tauri_specta::{collect_commands, collect_events, Builder, ErrorHandlingMode};
Expand All @@ -13,6 +15,10 @@ use specta_typescript::{BigIntExportBehavior, Typescript};

#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
default_provider()
.install_default()
.expect("could not install AWS LC provider");

let builder = Builder::<tauri::Wry>::new()
.error_handling(ErrorHandlingMode::Throw)
// Then register them (separated by a comma)
Expand Down Expand Up @@ -99,6 +105,11 @@ pub fn run() {
commands::sign_message_with_public_key,
commands::sign_message_by_address,
commands::send_transaction_immediately,
commands::is_rpc_running,
commands::start_rpc_server,
commands::stop_rpc_server,
commands::get_rpc_run_on_startup,
commands::set_rpc_run_on_startup,
])
.events(collect_events![SyncEvent]);

Expand Down Expand Up @@ -134,10 +145,10 @@ pub fn run() {
app.handle().plugin(tauri_plugin_safe_area_insets::init())?;
}
builder.mount_events(app);
let app_handle = app.handle().clone();
let path = app.path().app_data_dir()?;
let inner = AppStateInner::new(app_handle, &path);
let app_state = AppState::new(Mutex::new(inner));
let app_state = AppState::new(Mutex::new(Sage::new(&path)));
app.manage(Initialized(Mutex::new(false)));
app.manage(RpcTask(Mutex::new(None)));
app.manage(app_state);
Ok(())
})
Expand Down
15 changes: 15 additions & 0 deletions src/bindings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,21 @@ async signMessageByAddress(req: SignMessageByAddress) : Promise<SignMessageByAdd
},
async sendTransactionImmediately(req: SendTransactionImmediately) : Promise<SendTransactionImmediatelyResponse> {
return await TAURI_INVOKE("send_transaction_immediately", { req });
},
async isRpcRunning() : Promise<boolean> {
return await TAURI_INVOKE("is_rpc_running");
},
async startRpcServer() : Promise<null> {
return await TAURI_INVOKE("start_rpc_server");
},
async stopRpcServer() : Promise<null> {
return await TAURI_INVOKE("stop_rpc_server");
},
async getRpcRunOnStartup() : Promise<boolean> {
return await TAURI_INVOKE("get_rpc_run_on_startup");
},
async setRpcRunOnStartup(runOnStartup: boolean) : Promise<null> {
return await TAURI_INVOKE("set_rpc_run_on_startup", { runOnStartup });
}
}

Expand Down
Loading

0 comments on commit a628e7d

Please sign in to comment.