add default duration

This commit is contained in:
Joe Ardent 2025-08-13 17:48:55 -07:00
parent 471fc5d73c
commit 8791468c17
5 changed files with 66 additions and 46 deletions

View file

@ -7,7 +7,7 @@ use std::{
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 jocalsend::{JocalService, ReceiveDialog, ReceiveRequest, TransferEvent, error::Result}; use jocalsend::{JocalEvent, JocalService, ReceiveDialog, ReceiveRequest, error::Result};
use julid::Julid; use julid::Julid;
use log::{LevelFilter, debug, error, warn}; use log::{LevelFilter, debug, error, warn};
use ratatui::{ use ratatui::{
@ -54,7 +54,7 @@ pub struct App {
receiving_state: TableState, receiving_state: TableState,
// for getting messages back from the web server or web client about things we've done; the // for getting messages back from the web server or web client about things we've done; the
// other end is held by the service // other end is held by the service
event_listener: UnboundedReceiver<TransferEvent>, event_listener: UnboundedReceiver<JocalEvent>,
file_finder: FileFinder, file_finder: FileFinder,
text: Option<String>, text: Option<String>,
input: Input, input: Input,
@ -83,7 +83,7 @@ pub enum FileMode {
} }
impl App { impl App {
pub fn new(service: JocalService, event_listener: UnboundedReceiver<TransferEvent>) -> Self { pub fn new(service: JocalService, event_listener: UnboundedReceiver<JocalEvent>) -> Self {
App { App {
service, service,
event_listener, event_listener,
@ -117,12 +117,15 @@ impl App {
if let Some(event) = transfer_event { if let Some(event) = transfer_event {
debug!("got transferr event {event:?}"); debug!("got transferr event {event:?}");
match event { match event {
TransferEvent::ReceiveRequest { id, request } => { JocalEvent::ReceiveRequest { id, request } => {
self.receive_requests.insert(id, request); self.receive_requests.insert(id, request);
} }
TransferEvent::Cancelled(id) | TransferEvent::Received(id) => { JocalEvent::Cancelled(id) | JocalEvent::ReceivedInbound(id) => {
self.receive_requests.remove(&id); self.receive_requests.remove(&id);
} }
JocalEvent::SendApproved(_id) => todo!(),
JocalEvent::SendDenied(_id) => todo!(),
JocalEvent::Tick => {}
} }
} }
} }

View file

@ -1,7 +1,6 @@
use std::{ use std::{
net::{SocketAddr, SocketAddrV4}, net::{SocketAddr, SocketAddrV4},
sync::Arc, sync::Arc,
time::Duration,
}; };
use axum::{ use axum::{
@ -11,7 +10,7 @@ use axum::{
use log::{debug, error, trace, warn}; use log::{debug, error, trace, warn};
use tokio::net::UdpSocket; use tokio::net::UdpSocket;
use crate::{Config, JocalService, RunningState, models::Device}; use crate::{Config, DEFAULT_INTERVAL, JocalService, RunningState, models::Device};
impl JocalService { impl JocalService {
pub async fn announce(&self, socket: Option<SocketAddr>) -> crate::error::Result<()> { pub async fn announce(&self, socket: Option<SocketAddr>) -> crate::error::Result<()> {
@ -29,7 +28,7 @@ impl JocalService {
pub async fn listen_multicast(&self) -> crate::error::Result<()> { pub async fn listen_multicast(&self) -> crate::error::Result<()> {
let mut buf = [0; 65536]; let mut buf = [0; 65536];
let mut timeout = tokio::time::interval(Duration::from_secs(5)); let mut timeout = tokio::time::interval(DEFAULT_INTERVAL);
timeout.tick().await; timeout.tick().await;
loop { loop {
@ -45,7 +44,6 @@ impl JocalService {
} }
}, },
r = self.socket.recv_from(&mut buf) => { r = self.socket.recv_from(&mut buf) => {
trace!("received multicast datagram");
match r { match r {
Ok((size, src)) => { Ok((size, src)) => {
let received_msg = String::from_utf8_lossy(&buf[..size]); let received_msg = String::from_utf8_lossy(&buf[..size]);

View file

@ -32,11 +32,14 @@ pub const MULTICAST_IP: Ipv4Addr = Ipv4Addr::new(224, 0, 0, 167);
pub const LISTENING_SOCKET_ADDR: SocketAddrV4 = pub const LISTENING_SOCKET_ADDR: SocketAddrV4 =
SocketAddrV4::new(Ipv4Addr::from_bits(0), DEFAULT_PORT); SocketAddrV4::new(Ipv4Addr::from_bits(0), DEFAULT_PORT);
pub const DEFAULT_INTERVAL: Duration = Duration::from_millis(200);
#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Listeners { pub enum JocalTasks {
Udp, Udp,
Http, Http,
Multicast, Multicast,
Tick,
} }
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq)]
@ -46,10 +49,13 @@ pub enum ReceiveDialog {
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub enum TransferEvent { pub enum JocalEvent {
Received(Julid), ReceivedInbound(Julid),
SendApproved(Julid),
SendDenied(Julid),
Cancelled(Julid), Cancelled(Julid),
ReceiveRequest { id: Julid, request: ReceiveRequest }, ReceiveRequest { id: Julid, request: ReceiveRequest },
Tick,
} }
#[derive(Clone)] #[derive(Clone)]
@ -80,13 +86,13 @@ pub struct JocalService {
http_handle: OnceLock<axum_server::Handle>, http_handle: OnceLock<axum_server::Handle>,
// the receiving end will be held by the application so it can update the UI based on backend // the receiving end will be held by the application so it can update the UI based on backend
// events // events
transfer_event_tx: UnboundedSender<TransferEvent>, transfer_event_tx: UnboundedSender<JocalEvent>,
} }
impl JocalService { impl JocalService {
pub async fn new( pub async fn new(
config: Config, config: Config,
) -> crate::error::Result<(Self, UnboundedReceiver<TransferEvent>)> { ) -> crate::error::Result<(Self, UnboundedReceiver<JocalEvent>)> {
let (tx, rx) = mpsc::unbounded_channel(); let (tx, rx) = mpsc::unbounded_channel();
let socket = UdpSocket::bind(LISTENING_SOCKET_ADDR).await?; let socket = UdpSocket::bind(LISTENING_SOCKET_ADDR).await?;
socket.set_multicast_loop_v4(true)?; socket.set_multicast_loop_v4(true)?;
@ -113,43 +119,56 @@ impl JocalService {
)) ))
} }
pub async fn start(&self, handles: &mut JoinSet<Listeners>) { pub async fn start(&self, handles: &mut JoinSet<JocalTasks>) {
let service = self.clone(); let service = self.clone();
handles.spawn({ handles.spawn(async move {
async move { if let Err(e) = service.start_http_server().await {
if let Err(e) = service.start_http_server().await { error!("HTTP server error: {e}");
error!("HTTP server error: {e}");
}
Listeners::Http
} }
JocalTasks::Http
}); });
let service = self.clone(); let service = self.clone();
handles.spawn({ handles.spawn(async move {
async move { if let Err(e) = service.listen_multicast().await {
if let Err(e) = service.listen_multicast().await { error!("UDP listener error: {e}");
error!("UDP listener error: {e}");
}
Listeners::Multicast
} }
JocalTasks::Multicast
}); });
let service = self.clone(); let service = self.clone();
handles.spawn({ handles.spawn(async move {
async move { let service = &service;
loop { let mut tick = tokio::time::interval(DEFAULT_INTERVAL);
let rstate = service.running_state.lock().await;
if *rstate == RunningState::Stopping { loop {
break; let rstate = service.running_state.lock().await;
} if *rstate == RunningState::Stopping {
if let Err(e) = service.announce(None).await { break;
error!("Announcement error: {e}");
}
tokio::time::sleep(Duration::from_secs(5)).await;
} }
Listeners::Udp tick.tick().await;
service
.transfer_event_tx
.send(JocalEvent::Tick)
.unwrap_or_else(|e| log::warn!("could not send tick event: {e:?}"));
} }
JocalTasks::Tick
});
let service = self.clone();
handles.spawn(async move {
loop {
let rstate = service.running_state.lock().await;
if *rstate == RunningState::Stopping {
break;
}
if let Err(e) = service.announce(None).await {
error!("Announcement error: {e}");
}
tokio::time::sleep(Duration::from_secs(2)).await;
}
JocalTasks::Udp
}); });
} }
@ -168,7 +187,7 @@ impl JocalService {
peers.clear(); peers.clear();
} }
pub fn send_event(&self, event: TransferEvent) { pub fn send_event(&self, event: JocalEvent) {
if let Err(e) = self.transfer_event_tx.send(event.clone()) { if let Err(e) = self.transfer_event_tx.send(event.clone()) {
error!("got error sending transfer event '{event:?}': {e:?}"); error!("got error sending transfer event '{event:?}': {e:?}");
} }

View file

@ -1,7 +1,7 @@
use std::{path::Path, str::FromStr, time::Duration}; use std::{path::Path, str::FromStr, time::Duration};
use clap::Parser; use clap::Parser;
use jocalsend::{Config, JocalService, Listeners, error::Result}; use jocalsend::{Config, JocalService, JocalTasks, error::Result};
use log::{error, info}; use log::{error, info};
use ratatui::DefaultTerminal; use ratatui::DefaultTerminal;
use ratatui_explorer::FileExplorer; use ratatui_explorer::FileExplorer;
@ -105,7 +105,7 @@ async fn start_and_run(terminal: &mut DefaultTerminal, config: Config) -> Result
Ok(()) Ok(())
} }
async fn shutdown(handles: &mut JoinSet<Listeners>) { async fn shutdown(handles: &mut JoinSet<JocalTasks>) {
let mut timeout = tokio::time::interval(Duration::from_secs(5)); let mut timeout = tokio::time::interval(Duration::from_secs(5));
timeout.tick().await; timeout.tick().await;
loop { loop {

View file

@ -13,7 +13,7 @@ use serde::{Deserialize, Serialize};
use tokio::sync::mpsc::unbounded_channel; use tokio::sync::mpsc::unbounded_channel;
use crate::{ use crate::{
JocalService, ReceiveDialog, ReceiveRequest, TransferEvent, JocalEvent, JocalService, ReceiveDialog, ReceiveRequest,
error::{LocalSendError, Result}, error::{LocalSendError, Result},
models::{Device, FileMetadata}, models::{Device, FileMetadata},
}; };
@ -239,7 +239,7 @@ pub async fn handle_prepare_upload(
match service match service
.transfer_event_tx .transfer_event_tx
.send(TransferEvent::ReceiveRequest { id, request }) .send(JocalEvent::ReceiveRequest { id, request })
{ {
Ok(_) => {} Ok(_) => {}
Err(e) => { Err(e) => {
@ -354,7 +354,7 @@ pub async fn handle_receive_upload(
} }
if let Ok(id) = Julid::from_str(session_id) { if let Ok(id) = Julid::from_str(session_id) {
service.send_event(TransferEvent::Received(id)); service.send_event(JocalEvent::ReceivedInbound(id));
}; };
StatusCode::OK.into_response() StatusCode::OK.into_response()
@ -384,7 +384,7 @@ pub async fn handle_cancel(
session.status = SessionStatus::Cancelled; session.status = SessionStatus::Cancelled;
if let Ok(id) = Julid::from_str(&params.session_id) { if let Ok(id) = Julid::from_str(&params.session_id) {
service.send_event(TransferEvent::Cancelled(id)); service.send_event(JocalEvent::Cancelled(id));
}; };
StatusCode::OK.into_response() StatusCode::OK.into_response()