//! keys-screen key-input handler (split out of tui/input.rs). use super::{ App, AppEvent, ConfirmAction, DataPayload, KeyCode, KeyEvent, MnwApiClient, Screen, load_item_detail, load_license_keys, mpsc, }; pub(crate) async fn handle_keys_input( key: KeyEvent, app: &mut App, screen: &mut Screen, api: &MnwApiClient, tx: &mpsc::Sender, ) { // Cancel pending confirmation on any key other than the confirmation key if app.confirm_action.is_some() && !matches!(key.code, KeyCode::Char('x' | 'X')) { app.confirm_action = None; app.keys_status = None; } match key.code { KeyCode::Char('j') | KeyCode::Down => app.move_down(screen), KeyCode::Char('k') | KeyCode::Up => app.move_up(screen), KeyCode::Esc => { if let Screen::Keys(pidx, item_id) = &*screen { let pidx = *pidx; let iid = item_id.clone(); *screen = Screen::Item(pidx, iid.clone()); app.license_keys.clear(); app.keys_status = None; app.selected_index = 0; app.loading = true; let api = api.clone(); let user_id = app.user.user_id.clone(); let tx = tx.clone(); tokio::spawn(async move { load_item_detail(&api, &user_id, &iid, &tx).await; }); } } KeyCode::Char('g' | 'G') => { if let Screen::Keys(_, item_id) = &*screen { let item_id = item_id.clone(); let api = api.clone(); let user_id = app.user.user_id.clone(); let tx = tx.clone(); app.keys_status = Some("Generating...".to_string()); let iid = item_id.clone(); tokio::spawn(async move { match api.generate_license_key(&user_id, &item_id).await { Ok(key) => { let _ = tx .send(AppEvent::DataLoaded(DataPayload::GenericSuccess { message: format!("Generated: {}", key.key_code), })) .await; load_license_keys(&api, &user_id, &iid, &tx).await; } Err(e) => { let _ = tx .send(AppEvent::DataLoaded(DataPayload::GenericError { error: e.to_string(), })) .await; } } }); } } KeyCode::Char('x' | 'X') if !app.license_keys.is_empty() => { let idx = app.selected_index; if matches!(app.confirm_action, Some(ConfirmAction::RevokeLicenseKey { key_idx }) if key_idx == idx) { // Confirmed — execute revoke app.confirm_action = None; let key_id = app.license_keys[idx].id.clone(); let key_code = app.license_keys[idx].key_code.clone(); let user_id = app.user.user_id.clone(); tracing::info!( user_id = %user_id, key_id = %key_id, key_code = %key_code, "revoke license key confirmed" ); if let Screen::Keys(_, item_id) = &*screen { let item_id = item_id.clone(); let api = api.clone(); let user_id = app.user.user_id.clone(); let tx = tx.clone(); app.keys_status = Some("Revoking...".to_string()); tokio::spawn(async move { match api.revoke_license_key(&user_id, &key_id).await { Ok(()) => { load_license_keys(&api, &user_id, &item_id, &tx).await; } Err(e) => { let _ = tx .send(AppEvent::DataLoaded(DataPayload::GenericError { error: e.to_string(), })) .await; } } }); } } else { // First press — ask for confirmation app.confirm_action = Some(ConfirmAction::RevokeLicenseKey { key_idx: idx }); app.keys_status = Some(format!( "Revoke '{}'? Press x again to confirm", app.license_keys[idx].key_code )); } } KeyCode::Char('r' | 'R') => { if let Screen::Keys(_, item_id) = &*screen { app.loading = true; let item_id = item_id.clone(); let api = api.clone(); let user_id = app.user.user_id.clone(); let tx = tx.clone(); tokio::spawn(async move { load_license_keys(&api, &user_id, &item_id, &tx).await; }); } } _ => {} } }