Compare commits

..

2 commits

Author SHA1 Message Date
Joe Ardent
29034e09d1 rename to jocalsend 2025-08-06 14:09:37 -07:00
Joe Ardent
39e21b83b1 better logging, add 'main()' method to app and binding to get to main screen 2025-08-06 14:07:10 -07:00
10 changed files with 44 additions and 37 deletions

2
Cargo.lock generated
View file

@ -1150,7 +1150,7 @@ dependencies = [
]
[[package]]
name = "joecalsend"
name = "jocalsend"
version = "0.1.0"
dependencies = [
"axum",

View file

@ -1,5 +1,5 @@
[package]
name = "joecalsend"
name = "jocalsend"
version = "0.1.0"
edition = "2024"

View file

@ -2,7 +2,7 @@ use std::{collections::BTreeMap, net::SocketAddr, time::Duration};
use crossterm::event::{Event, EventStream, KeyCode, KeyEvent, KeyEventKind};
use futures::{FutureExt, StreamExt};
use joecalsend::{JoecalService, ReceiveDialog, ReceiveRequest, TransferEvent, error::Result};
use jocalsend::{JocalService, ReceiveDialog, ReceiveRequest, TransferEvent, error::Result};
use julid::Julid;
use log::{LevelFilter, debug, error, warn};
use ratatui::{
@ -23,7 +23,7 @@ pub struct Peer {
}
pub struct App {
pub service: JoecalService,
pub service: JocalService,
pub events: EventStream,
// addr -> (alias, fingerprint)
pub peers: Vec<Peer>,
@ -56,7 +56,7 @@ pub enum SendingScreen {
}
impl App {
pub fn new(service: JoecalService, event_listener: UnboundedReceiver<TransferEvent>) -> Self {
pub fn new(service: JocalService, event_listener: UnboundedReceiver<TransferEvent>) -> Self {
App {
service,
event_listener,
@ -126,6 +126,7 @@ impl App {
KeyCode::Char('s') => self.send(),
KeyCode::Char('r') => self.recv(),
KeyCode::Char('l') => self.logs(),
KeyCode::Char('m') => self.main(),
_ => match mode {
CurrentScreen::Logging => match code {
KeyCode::Left => change_log_level(-1),
@ -230,6 +231,14 @@ impl App {
}
}
pub fn main(&mut self) {
let last = self.screen.last();
match last {
Some(CurrentScreen::Main) => {}
_ => self.screen.push(CurrentScreen::Main),
}
}
// accept a content receive request
fn accept(&mut self) {
let Some(idx) = self.receiving_state.selected() else {

View file

@ -1,6 +1,6 @@
use std::sync::LazyLock;
use joecalsend::ReceiveRequest;
use jocalsend::ReceiveRequest;
use log::LevelFilter;
use ratatui::{
buffer::Buffer,
@ -217,7 +217,7 @@ impl Widget for &mut App {
}
fn outer_frame(screen: &CurrentScreen, menu: &Line, area: Rect, buf: &mut Buffer) {
let title = Line::from(" Joecalsend ".bold());
let title = Line::from(" Jocalsend ".bold());
let block = Block::bordered()
.title(title.centered())
.title_bottom(menu.clone().centered())
@ -240,14 +240,14 @@ fn text_popup(text: &str, title: &str, area: Rect, buf: &mut Buffer) {
block.render(area, buf);
let (_, len) = unicode_segmentation::UnicodeSegmentation::graphemes(text, true).size_hint();
let len = len.unwrap_or(text.len()) as u16;
let len = len.unwrap_or(text.len()) as u16 + 2;
let area = centered_rect(area, Constraint::Length(len), Constraint::Length(1));
Paragraph::new(text).centered().yellow().render(area, buf);
}
fn logger(area: Rect, buf: &mut Buffer) {
let title = Line::from(log::max_level().as_str());
let title = Line::from(format!(" {} logs ", log::max_level().as_str()));
let logger = TuiLoggerWidget::default()
.output_separator('|')
.output_timestamp(Some("%H:%M:%S%.3f".to_string()))
@ -257,7 +257,7 @@ fn logger(area: Rect, buf: &mut Buffer) {
.output_line(false)
.block(Block::bordered().title(title.centered()))
.style(Style::default())
.state(&TuiWidgetState::new().set_default_display_level(LevelFilter::Debug));
.state(&TuiWidgetState::new().set_default_display_level(LevelFilter::Trace));
logger.render(area, buf);
}
@ -267,7 +267,7 @@ fn receive_requests(
area: Rect,
buf: &mut Buffer,
) {
let title = Line::from(" Upload Requests ").bold();
let title = Line::from(" Incoming Transfer Requests ").bold();
let block = Block::bordered().title(title.centered());
let mut rows = Vec::new();
@ -311,9 +311,7 @@ fn receive_requests(
ratatui::widgets::StatefulWidget::render(table, area, buf, state);
if let Some(idx) = state.selected()
//&& let Some(area) = preview_area
{
if let Some(idx) = state.selected() {
let area = centered_rect(area, Constraint::Percentage(80), Constraint::Max(7));
let request = requests[idx];
if let Some(md) = request.files.values().next()
@ -366,13 +364,13 @@ impl Widget for NetworkInfoWidget {
{
let udp = "UDP socket";
let udp = udp.to_line().left_aligned();
let uaddr = format!("{:?}", joecalsend::LISTENING_SOCKET_ADDR);
let uaddr = format!("{:?}", jocalsend::LISTENING_SOCKET_ADDR);
let udp = Row::new(vec![udp, uaddr.to_line().right_aligned()]).yellow();
let mip = format!(
"{:?}:{:?}",
joecalsend::MULTICAST_IP,
joecalsend::DEFAULT_PORT
jocalsend::MULTICAST_IP,
jocalsend::DEFAULT_PORT
);
let multicast = "Multicast address";
let multicast = Row::new(vec![
@ -381,7 +379,7 @@ impl Widget for NetworkInfoWidget {
])
.yellow();
let haddr = format!("{:?}", joecalsend::LISTENING_SOCKET_ADDR);
let haddr = format!("{:?}", jocalsend::LISTENING_SOCKET_ADDR);
let http = "HTTP address";
let http = Row::new(vec![
http.to_line().left_aligned(),

View file

@ -11,9 +11,9 @@ use axum::{
use log::{debug, error, trace, warn};
use tokio::net::UdpSocket;
use crate::{Config, JoecalService, RunningState, models::Device};
use crate::{Config, JocalService, RunningState, models::Device};
impl JoecalService {
impl JocalService {
pub async fn announce(&self, socket: Option<SocketAddr>) -> crate::error::Result<()> {
trace!("announcing");
announce_http(&self.device, socket, self.client.clone()).await?;
@ -97,7 +97,7 @@ impl JoecalService {
/// Axum request handler for receiving other devices' registration requests.
pub async fn register_device(
State(service): State<JoecalService>,
State(service): State<JocalService>,
ConnectInfo(addr): ConnectInfo<SocketAddr>,
Json(device): Json<Device>,
) -> Json<Device> {

View file

@ -9,12 +9,12 @@ use tokio::{net::TcpListener, sync::mpsc};
use tower_http::limit::RequestBodyLimitLayer;
use crate::{
JoecalService,
JocalService,
discovery::register_device,
transfer::{prepare_upload, receive_upload},
};
impl JoecalService {
impl JocalService {
pub async fn start_http_server(&self, stop_rx: mpsc::Receiver<()>) -> crate::error::Result<()> {
let app = self.create_router();
// TODO: make addr config

View file

@ -60,7 +60,7 @@ pub struct ReceiveRequest {
/// Contains the main network and backend state for an application session.
#[derive(Clone)]
pub struct JoecalService {
pub struct JocalService {
pub device: Device,
pub peers: Arc<Mutex<BTreeMap<String, (SocketAddr, Device)>>>,
pub sessions: Arc<Mutex<BTreeMap<String, Session>>>, // Session ID to Session
@ -74,7 +74,7 @@ pub struct JoecalService {
transfer_event_tx: UnboundedSender<TransferEvent>,
}
impl JoecalService {
impl JocalService {
pub async fn new(
device: Device,
config: Config,
@ -190,7 +190,7 @@ pub struct Config {
impl Default for Config {
fn default() -> Self {
let home = std::env::home_dir().unwrap_or("/tmp".into());
let dd = home.join("joecalsend-downloads");
let dd = home.join("jocalsend-downloads");
Self {
multicast_addr: SocketAddrV4::new(MULTICAST_IP, DEFAULT_PORT),
port: DEFAULT_PORT,

View file

@ -1,4 +1,4 @@
use joecalsend::{Config, JoecalService, Listeners, error, models::Device};
use jocalsend::{Config, JocalService, Listeners, error, models::Device};
use log::{error, info};
use ratatui::DefaultTerminal;
use tokio::{sync::mpsc::unbounded_channel, task::JoinSet};
@ -12,10 +12,10 @@ fn main() -> error::Result<()> {
if std::env::var("RUST_LOG").is_err() {
unsafe {
std::env::set_var("RUST_LOG", "joecalsend");
std::env::set_var("RUST_LOG", "jocalsend");
}
}
init_logger(LevelFilter::Debug).map_err(|e| std::io::Error::other(format!("{e}")))?;
init_logger(LevelFilter::Info).map_err(|e| std::io::Error::other(format!("{e}")))?;
set_env_filter_from_env(None);
let config = Config::default();
@ -35,9 +35,9 @@ async fn start_and_run(
) -> error::Result<()> {
let (event_tx, event_listener) = unbounded_channel();
let service = JoecalService::new(device, config.clone(), event_tx)
let service = JocalService::new(device, config.clone(), event_tx)
.await
.expect("Could not create JoecalService");
.expect("Could not create JocalService");
let mut app = App::new(service, event_listener);

View file

@ -120,7 +120,7 @@ pub enum Protocol {
impl Default for Device {
fn default() -> Self {
Self {
alias: "Joecalsend".to_string(),
alias: "Jocalsend".to_string(),
version: "2.1".to_string(),
device_model: None,
device_type: Some(DeviceType::Headless),

View file

@ -13,7 +13,7 @@ use serde::{Deserialize, Serialize};
use tokio::sync::mpsc::unbounded_channel;
use crate::{
JoecalService, ReceiveDialog, ReceiveRequest, TransferEvent,
JocalService, ReceiveDialog, ReceiveRequest, TransferEvent,
error::{LocalSendError, Result},
models::{Device, FileMetadata},
};
@ -52,7 +52,7 @@ pub struct PrepareUploadRequest {
pub files: BTreeMap<String, FileMetadata>,
}
impl JoecalService {
impl JocalService {
pub async fn prepare_upload(
&self,
peer: &str,
@ -231,7 +231,7 @@ impl JoecalService {
}
pub async fn prepare_upload(
State(service): State<JoecalService>,
State(service): State<JocalService>,
ConnectInfo(addr): ConnectInfo<SocketAddr>,
Json(req): Json<PrepareUploadRequest>,
) -> impl IntoResponse {
@ -302,7 +302,7 @@ pub async fn prepare_upload(
pub async fn receive_upload(
Query(params): Query<UploadParams>,
State(service): State<JoecalService>,
State(service): State<JocalService>,
body: Bytes,
) -> impl IntoResponse {
// Extract query parameters
@ -379,7 +379,7 @@ pub struct UploadParams {
pub async fn register_cancel(
Query(params): Query<CancelParams>,
State(service): State<JoecalService>,
State(service): State<JocalService>,
) -> impl IntoResponse {
let mut sessions_lock = service.sessions.lock().await;
let session = match sessions_lock.get_mut(&params.session_id) {