Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding some communication between UI and backend to ensure sync at connection start #164

Merged
merged 12 commits into from
Aug 24, 2022
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
### Removed

### Fixed
- [\#164](https://github.com/Manta-Network/manta-signer/pull/164) Adding some communication between UI and backend to ensure sync at connection start

### Security

Expand Down
1 change: 0 additions & 1 deletion ui/src-tauri/rust-toolchain

This file was deleted.

91 changes: 84 additions & 7 deletions ui/src-tauri/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,10 @@

extern crate alloc;

use core::time::Duration;
use core::{
sync::atomic::{AtomicBool, Ordering},
time::Duration,
};
use manta_signer::{
config::{Config, Setup},
secret::{
Expand All @@ -36,13 +39,64 @@ use manta_signer::{
serde::Serialize,
service::Server,
storage::Store,
tokio::time::sleep,
};
use std::time::Instant;
use tauri::{
async_runtime::spawn, CustomMenuItem, Manager, RunEvent, Runtime, State, SystemTray,
SystemTrayEvent, SystemTrayMenu, Window, WindowEvent,
};

/// App State
///
/// Keeps track of global state flags that we need for specific behaviors.
#[derive(Debug)]
pub struct AppState {
/// UI is Connected
pub ui_connected: AtomicBool,
}

impl AppState {
/// Builds a new [`AppState`].
#[inline]
pub const fn new() -> Self {
Self {
ui_connected: AtomicBool::new(false),
}
}

/// Returns the UI connection status.
#[inline]
pub fn get_ui_connected(&self) -> bool {
self.ui_connected.load(Ordering::Relaxed)
}

/// Sets the UI connection status.
#[inline]
pub fn set_ui_connected(&self, ui_connected: bool) {
self.ui_connected.store(ui_connected, Ordering::Relaxed)
}
}

/// Application State
pub static APP_STATE: AppState = AppState::new();

/// Repeatedly executes `f` until the `timeout` is reached calling `exit` to return from the
/// function.
#[inline]
pub fn while_timeout<F, E, T>(timeout: Duration, mut f: F, exit: E) -> T
where
F: FnMut(),
E: FnOnce(Instant, Duration) -> T,
{
let time_start = Instant::now();
loop {
f();
if time_start.elapsed() >= timeout {
return exit(time_start, timeout);
}
}
}

/// User
pub struct User {
/// Main Window
Expand Down Expand Up @@ -107,11 +161,23 @@ impl Authorizer for User {
fn setup<'s>(&'s mut self, setup: &'s Setup) -> UnitFuture<'s> {
let window = self.window.clone();
Box::pin(async move {
// NOTE: We have to wait here until the UI listener is registered.
sleep(Duration::from_millis(500)).await;
window
.emit("connect", setup)
.expect("The `connect` command failed to be emitted to the window.");
while_timeout(
Duration::from_millis(5000),
move || {
if APP_STATE.get_ui_connected() {
return;
}
window
.emit("connect", setup)
.expect("The `connect` command failed to be emitted to the window.");
},
move |time_start, timeout| {
panic!(
"Connection attempt timed-out! Started: {:?} with {:?} timeout.",
time_start, timeout
);
},
)
})
}

Expand All @@ -136,6 +202,16 @@ pub type PasswordStore = Store<PasswordSender>;
/// Server Store
pub type ServerStore = Store<Server<User>>;

/// Called from the UI after it recieves a `connect` event.
///
/// To ensure proper connection you should emit `connect` continuously until the
/// [`AppState::ui_connected`] flag is `true` then stop. This is the only way for now to ensure they
/// are synchronized. Tauri is working on a better way.
#[tauri::command]
fn ui_connected() {
APP_STATE.set_ui_connected(true);
}

/// Sends the current `password` into storage from the UI.
#[tauri::command]
async fn send_password(
Expand Down Expand Up @@ -220,6 +296,7 @@ fn main() {
.invoke_handler(tauri::generate_handler![
send_password,
stop_password_prompt,
ui_connected,
])
.build(tauri::generate_context!())
.expect("Error while building UI.");
Expand Down
1 change: 1 addition & 0 deletions ui/src/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ function App() {
if (isConnected) return;
const beginInitialConnectionPhase = async () => {
await once('connect', (event) => {
invoke('ui_connected');
console.log("[INFO]: Connect Event: ", event);
let payload = event.payload;
switch (payload.type) {
Expand Down