Split the 2495-line mod.rs into 10 files by concern: - playback.rs: playback controls and track management - cava.rs: cava process management and VT100 parsing - input.rs: event dispatch and global keybindings - input_artists.rs: artists page keyboard handling - input_queue.rs: queue page keyboard handling - input_playlists.rs: playlists page keyboard handling - input_server.rs: server page keyboard handling - input_settings.rs: settings page keyboard handling - mouse.rs: all mouse click and scroll handling - mod.rs: App struct, new(), run(), event_loop(), load_initial_data() Pure code reorganization — no behavioral changes. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
137 lines
5.8 KiB
Rust
137 lines
5.8 KiB
Rust
use crossterm::event::{self, KeyCode};
|
|
use tracing::info;
|
|
|
|
use crate::error::Error;
|
|
use crate::subsonic::SubsonicClient;
|
|
|
|
use super::*;
|
|
|
|
impl App {
|
|
/// Handle server page keys
|
|
pub(super) async fn handle_server_key(&mut self, key: event::KeyEvent) -> Result<(), Error> {
|
|
let mut state = self.state.write().await;
|
|
|
|
let field = state.server_state.selected_field;
|
|
let is_text_field = field <= 2;
|
|
|
|
match key.code {
|
|
// Navigation - always works
|
|
KeyCode::Up => {
|
|
if field > 0 {
|
|
state.server_state.selected_field -= 1;
|
|
}
|
|
}
|
|
KeyCode::Down => {
|
|
if field < 4 {
|
|
state.server_state.selected_field += 1;
|
|
}
|
|
}
|
|
KeyCode::Tab => {
|
|
// Tab moves to next field, wrapping around
|
|
state.server_state.selected_field = (field + 1) % 5;
|
|
}
|
|
// Text input for text fields (0=URL, 1=Username, 2=Password)
|
|
KeyCode::Char(c) if is_text_field => match field {
|
|
0 => state.server_state.base_url.push(c),
|
|
1 => state.server_state.username.push(c),
|
|
2 => state.server_state.password.push(c),
|
|
_ => {}
|
|
},
|
|
KeyCode::Backspace if is_text_field => match field {
|
|
0 => {
|
|
state.server_state.base_url.pop();
|
|
}
|
|
1 => {
|
|
state.server_state.username.pop();
|
|
}
|
|
2 => {
|
|
state.server_state.password.pop();
|
|
}
|
|
_ => {}
|
|
},
|
|
// Enter activates buttons, ignored on text fields
|
|
KeyCode::Enter => {
|
|
match field {
|
|
3 => {
|
|
// Test connection
|
|
let url = state.server_state.base_url.clone();
|
|
let user = state.server_state.username.clone();
|
|
let pass = state.server_state.password.clone();
|
|
state.server_state.status = Some("Testing connection...".to_string());
|
|
drop(state);
|
|
|
|
match SubsonicClient::new(&url, &user, &pass) {
|
|
Ok(client) => match client.ping().await {
|
|
Ok(_) => {
|
|
let mut state = self.state.write().await;
|
|
state.server_state.status =
|
|
Some("Connection successful!".to_string());
|
|
}
|
|
Err(e) => {
|
|
let mut state = self.state.write().await;
|
|
state.server_state.status =
|
|
Some(format!("Connection failed: {}", e));
|
|
}
|
|
},
|
|
Err(e) => {
|
|
let mut state = self.state.write().await;
|
|
state.server_state.status = Some(format!("Invalid URL: {}", e));
|
|
}
|
|
}
|
|
return Ok(());
|
|
}
|
|
4 => {
|
|
// Save config and reconnect
|
|
info!(
|
|
"Saving config: url='{}', user='{}'",
|
|
state.server_state.base_url, state.server_state.username
|
|
);
|
|
state.config.base_url = state.server_state.base_url.clone();
|
|
state.config.username = state.server_state.username.clone();
|
|
state.config.password = state.server_state.password.clone();
|
|
|
|
let url = state.config.base_url.clone();
|
|
let user = state.config.username.clone();
|
|
let pass = state.config.password.clone();
|
|
|
|
match state.config.save_default() {
|
|
Ok(_) => {
|
|
info!("Config saved successfully");
|
|
state.server_state.status =
|
|
Some("Saved! Connecting...".to_string());
|
|
}
|
|
Err(e) => {
|
|
info!("Config save failed: {}", e);
|
|
state.server_state.status = Some(format!("Save failed: {}", e));
|
|
return Ok(());
|
|
}
|
|
}
|
|
drop(state);
|
|
|
|
// Create new client and load data
|
|
match SubsonicClient::new(&url, &user, &pass) {
|
|
Ok(client) => {
|
|
self.subsonic = Some(client);
|
|
self.load_initial_data().await;
|
|
let mut state = self.state.write().await;
|
|
state.server_state.status =
|
|
Some("Connected and loaded data!".to_string());
|
|
}
|
|
Err(e) => {
|
|
let mut state = self.state.write().await;
|
|
state.server_state.status =
|
|
Some(format!("Saved but connection failed: {}", e));
|
|
}
|
|
}
|
|
return Ok(());
|
|
}
|
|
_ => {} // Ignore Enter on text fields (handles paste with newlines)
|
|
}
|
|
}
|
|
_ => {}
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
}
|