update to ratatui 0.30

This commit is contained in:
Joe Ardent 2025-07-15 16:27:19 -07:00
parent cac0e4f6e3
commit a2c6f7f8e7
4 changed files with 781 additions and 112 deletions

787
Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -16,7 +16,7 @@ mime = "0.3"
mime_guess = "2" mime_guess = "2"
native-dialog = "0.9" native-dialog = "0.9"
network-interface = { version = "2", features = ["serde"] } network-interface = { version = "2", features = ["serde"] }
ratatui = "0.29" ratatui = "0.30.0-alpha.5"
reqwest = { version = "0.12", features = ["json"] } reqwest = { version = "0.12", features = ["json"] }
serde = { version = "1", features = ["derive"] } serde = { version = "1", features = ["derive"] }
serde_json = "1" serde_json = "1"

View file

@ -1,8 +1,8 @@
use std::{collections::BTreeMap, io, net::SocketAddr, time::Duration}; use std::{collections::BTreeMap, io, net::SocketAddr, sync::OnceLock, time::Duration};
use crossterm::event::{Event, EventStream, KeyCode, KeyEvent, KeyEventKind}; use crossterm::event::{Event, EventStream, KeyCode, KeyEvent, KeyEventKind};
use futures::{FutureExt, StreamExt}; use futures::{FutureExt, StreamExt};
use joecalsend::JoecalState; use joecalsend::{Config, JoecalState, Listeners, models::Device};
use ratatui::{ use ratatui::{
DefaultTerminal, DefaultTerminal,
buffer::Buffer, buffer::Buffer,
@ -12,13 +12,14 @@ use ratatui::{
text::{Line, Text}, text::{Line, Text},
widgets::{Block, Paragraph, Widget}, widgets::{Block, Paragraph, Widget},
}; };
use tokio::task::JoinSet;
pub mod ui; pub mod ui;
pub type Peers = BTreeMap<SocketAddr, (String, String)>; pub type Peers = BTreeMap<SocketAddr, (String, String)>;
pub struct App { pub struct App {
pub state: JoecalState, pub state: OnceLock<JoecalState>,
pub screen: Vec<CurrentScreen>, pub screen: Vec<CurrentScreen>,
pub events: EventStream, pub events: EventStream,
// addr -> (alias, fingerprint) // addr -> (alias, fingerprint)
@ -34,16 +35,29 @@ pub enum CurrentScreen {
} }
impl App { impl App {
pub fn new(state: JoecalState) -> Self { pub fn new() -> Self {
App { App {
state, state: Default::default(),
screen: vec![CurrentScreen::Main], screen: vec![CurrentScreen::Main],
peers: Default::default(), peers: Default::default(),
events: Default::default(), events: Default::default(),
} }
} }
pub async fn run(&mut self, terminal: &mut DefaultTerminal) -> io::Result<()> { #[tokio::main]
pub async fn run(
&mut self,
terminal: &mut DefaultTerminal,
config: Config,
device: Device,
) -> io::Result<()> {
let state = JoecalState::new(device)
.await
.expect("Could not create JoecalState");
let mut handles = JoinSet::new();
state.start(&config, &mut handles).await;
self.state.get_or_init(|| state);
loop { loop {
terminal.draw(|frame| self.draw(frame))?; terminal.draw(|frame| self.draw(frame))?;
self.handle_events().await?; self.handle_events().await?;
@ -51,20 +65,21 @@ impl App {
if let Some(&top) = self.screen.last() if let Some(&top) = self.screen.last()
&& top == CurrentScreen::Stopping && top == CurrentScreen::Stopping
{ {
self.state.stop().await; self.state.get().unwrap().stop().await;
break; break;
} }
let peers = self.state.peers.lock().await; let peers = self.state.get().unwrap().peers.lock().await;
self.peers.clear(); self.peers.clear();
peers.iter().for_each(|(k, v)| { peers.iter().for_each(|(fingerprint, (addr, device))| {
// k is fingerprint, v is addr, device let alias = device.alias.clone();
let addr = v.0; self.peers
let alias = v.1.alias.clone(); .insert(addr.to_owned(), (alias, fingerprint.to_owned()));
let fingerprint = k.clone();
self.peers.insert(addr, (alias, fingerprint));
}); });
} }
shutdown(&mut handles).await;
Ok(()) Ok(())
} }
@ -126,6 +141,28 @@ impl App {
} }
} }
async fn shutdown(handles: &mut JoinSet<Listeners>) {
let mut alarm = tokio::time::interval(tokio::time::Duration::from_secs(5));
loop {
tokio::select! {
join_result = handles.join_next() => {
match join_result {
Some(handle) => match handle {
Ok(h) => println!("Stopped {h:?}"),
Err(e) => println!("Got error {e:?}"),
}
None => break,
}
}
_ = alarm.tick() => {
println!("Exit timeout reached, aborting all unjoined tasks");
handles.abort_all();
break;
},
}
}
}
impl Widget for &App { impl Widget for &App {
fn render(self, area: Rect, buf: &mut Buffer) { fn render(self, area: Rect, buf: &mut Buffer) {
let title = Line::from(" Joecalsend ".bold()); let title = Line::from(" Joecalsend ".bold());

View file

@ -1,15 +1,13 @@
#![feature(slice_as_array)] #![feature(slice_as_array)]
use frontend::App; use frontend::App;
use joecalsend::{Config, JoecalState, error, models::Device}; use joecalsend::{Config, error, models::Device};
use local_ip_address::local_ip; use local_ip_address::local_ip;
use network_interface::{Addr, NetworkInterface, NetworkInterfaceConfig, V4IfAddr}; use network_interface::{Addr, NetworkInterface, NetworkInterfaceConfig, V4IfAddr};
use tokio::task::JoinSet;
mod frontend; mod frontend;
#[tokio::main] fn main() -> error::Result<()> {
async fn main() -> error::Result<()> {
let device = Device::default(); let device = Device::default();
dbg!(&device); dbg!(&device);
@ -33,38 +31,9 @@ async fn main() -> error::Result<()> {
} }
} }
let state = JoecalState::new(device)
.await
.expect("Could not create application session");
let config = Config::default(); let config = Config::default();
let mut handles = JoinSet::new();
state.start(&config, &mut handles).await;
let mut app = App::new(state.clone());
let mut terminal = ratatui::init();
let result = app.run(&mut terminal).await;
ratatui::restore();
let mut alarm = tokio::time::interval(tokio::time::Duration::from_secs(5)); let mut app = App::new();
alarm.tick().await;
loop { Ok(ratatui::run(|terminal| app.run(terminal, config, device))?)
tokio::select! {
handle = handles.join_next() => {
match handle {
Some(handle) => match handle {
Ok(h) => println!("Stopped {h:?}"),
Err(e) => println!("Got error {e:?}"),
}
None => break,
}
}
_ = alarm.tick() => {
println!("Exit timeout reached, aborting all unjoined tasks");
handles.abort_all();
break;
},
}
}
Ok(result?)
} }