use crossterm::event::{KeyCode, KeyEvent};
use crate::events::distro;
use crate::state::AppState;
#[allow(clippy::too_many_arguments)]
pub(super) fn handle_system_update(
ke: KeyEvent,
app: &mut AppState,
do_mirrors: &mut bool,
do_pacman: &mut bool,
force_sync: &mut bool,
do_aur: &mut bool,
do_cache: &mut bool,
country_idx: &mut usize,
countries: &[String],
mirror_count: &mut u16,
cursor: &mut usize,
) -> Option<bool> {
match ke.code {
KeyCode::Esc | KeyCode::Char('q') => {
app.modal = crate::state::Modal::None;
Some(false)
}
KeyCode::Up | KeyCode::Char('k') => {
if *cursor > 0 {
*cursor -= 1;
}
Some(false)
}
KeyCode::Down | KeyCode::Char('j') => {
let max = 4; if *cursor < max {
*cursor += 1;
}
Some(false)
}
KeyCode::Left => {
match *cursor {
1 => *force_sync = !*force_sync,
4 if !countries.is_empty() => {
if *country_idx == 0 {
*country_idx = countries.len() - 1;
} else {
*country_idx -= 1;
}
}
_ => {}
}
Some(false)
}
KeyCode::Right | KeyCode::Tab => {
match *cursor {
1 => *force_sync = !*force_sync,
4 if !countries.is_empty() => {
*country_idx = (*country_idx + 1) % countries.len();
}
_ => {}
}
Some(false)
}
KeyCode::Char(' ') => {
match *cursor {
0 => *do_mirrors = !*do_mirrors,
1 => *do_pacman = !*do_pacman,
2 => *do_aur = !*do_aur,
3 => *do_cache = !*do_cache,
_ => {}
}
Some(false)
}
KeyCode::Char('-') => {
if *cursor == 4 && *mirror_count > 1 {
*mirror_count -= 1;
crate::theme::save_mirror_count(*mirror_count);
}
Some(false)
}
KeyCode::Char('+') => {
if *cursor == 4 && *mirror_count < 200 {
*mirror_count += 1;
crate::theme::save_mirror_count(*mirror_count);
}
Some(false)
}
KeyCode::Enter | KeyCode::Char('\n' | '\r') => {
handle_system_update_enter(
app,
*do_mirrors,
*do_pacman,
*force_sync,
*do_aur,
*do_cache,
*country_idx,
countries,
*mirror_count,
);
Some(true)
}
_ => None,
}
}
#[cfg(test)]
mod tests;
#[allow(clippy::too_many_arguments, clippy::fn_params_excessive_bools)]
fn handle_system_update_enter(
app: &mut AppState,
do_mirrors: bool,
do_pacman: bool,
force_sync: bool,
do_aur: bool,
do_cache: bool,
country_idx: usize,
countries: &[String],
mirror_count: u16,
) {
maybe_show_long_run_auth_preflight_warning(app);
let mut cmds: Vec<String> = Vec::new();
if do_mirrors {
let sel = if country_idx < countries.len() {
countries[country_idx].as_str()
} else {
"Worldwide"
};
let prefs = crate::theme::settings();
let countries_arg = if sel == "Worldwide" {
prefs.selected_countries.as_str()
} else {
sel
};
crate::theme::save_selected_countries(countries_arg);
crate::theme::save_mirror_count(mirror_count);
match distro::mirror_update_command(countries_arg, mirror_count) {
Ok(cmd) => cmds.push(cmd),
Err(msg) => {
app.modal = crate::state::Modal::Alert { message: msg };
return;
}
}
}
if do_pacman {
let sync_flag = if force_sync { "-Syyu" } else { "-Syu" };
let tool = match crate::logic::privilege::active_tool() {
Ok(t) => t,
Err(msg) => {
app.modal = crate::state::Modal::Alert { message: msg };
return;
}
};
cmds.push(crate::logic::privilege::build_privilege_command(
tool,
&format!("pacman {sync_flag} --noconfirm"),
));
}
let aur_command = if do_aur {
let sync_flag = "-Sua";
Some(format!(
"if command -v paru >/dev/null 2>&1; then \
paru {sync_flag} --noconfirm; \
elif command -v yay >/dev/null 2>&1; then \
yay {sync_flag} --noconfirm; \
else \
echo 'No AUR helper (paru/yay) found.'; \
fi"
))
} else {
None
};
if let Some(aur_cmd) = aur_command {
if do_pacman {
app.pending_aur_update_command = Some(aur_cmd);
} else {
cmds.push(aur_cmd);
}
}
if do_cache {
let tool = match crate::logic::privilege::active_tool() {
Ok(t) => t,
Err(msg) => {
app.modal = crate::state::Modal::Alert { message: msg };
return;
}
};
let bin = tool.binary_name();
cmds.push(crate::logic::privilege::build_privilege_command(
tool,
"pacman -Sc --noconfirm",
));
cmds.push(format!("((command -v paru >/dev/null 2>&1 || {bin} pacman -Qi paru >/dev/null 2>&1) && paru -Sc --noconfirm) || ((command -v yay >/dev/null 2>&1 || {bin} pacman -Qi yay >/dev/null 2>&1) && yay -Sc --noconfirm) || true"));
}
if cmds.is_empty() {
app.modal = crate::state::Modal::Alert {
message: "No actions selected".to_string(),
};
return;
}
if std::env::var("PACSEA_TEST_OUT").is_ok() {
crate::install::spawn_shell_commands_in_terminal(&cmds);
app.modal = crate::state::Modal::None;
return;
}
let settings = crate::theme::settings();
if crate::logic::password::should_use_interactive_auth_handoff(&settings) {
match crate::events::try_interactive_auth_handoff() {
Ok(true) => {
app.modal = crate::state::Modal::PreflightExec {
items: Vec::new(),
action: crate::state::PreflightAction::Install,
tab: crate::state::PreflightTab::Summary,
verbose: false,
log_lines: Vec::new(),
abortable: false,
header_chips: crate::state::modal::PreflightHeaderChips::default(),
success: None,
};
app.pending_executor_request = Some(crate::install::ExecutorRequest::Update {
commands: cmds,
password: None,
dry_run: app.dry_run,
});
}
Ok(false) => {
app.modal = crate::state::Modal::Alert {
message: crate::i18n::t(app, "app.errors.authentication_failed"),
};
}
Err(e) => {
app.modal = crate::state::Modal::Alert { message: e };
}
}
return;
}
if crate::logic::password::resolve_auth_mode(&settings)
== crate::logic::privilege::AuthMode::PasswordlessOnly
&& crate::logic::password::should_use_passwordless_sudo(&settings)
{
app.modal = crate::state::Modal::PreflightExec {
items: Vec::new(),
action: crate::state::PreflightAction::Install,
tab: crate::state::PreflightTab::Summary,
verbose: false,
log_lines: Vec::new(),
abortable: false,
header_chips: crate::state::modal::PreflightHeaderChips::default(),
success: None,
};
app.pending_executor_request = Some(crate::install::ExecutorRequest::Update {
commands: cmds,
password: None,
dry_run: app.dry_run,
});
return;
}
app.pending_update_commands = Some(cmds);
app.modal = crate::state::Modal::PasswordPrompt {
purpose: crate::state::modal::PasswordPurpose::Update,
items: Vec::new(), input: crate::state::SecureString::default(),
cursor: 0,
error: None,
};
}
fn maybe_show_long_run_auth_preflight_warning(app: &mut AppState) {
let settings = crate::theme::settings();
let readiness = crate::logic::long_run_auth::evaluate_long_run_auth_readiness(&settings);
if readiness.should_warn && !app.long_run_auth_preflight_warned {
app.long_run_auth_preflight_warned = true;
app.toast_message = Some(crate::logic::long_run_auth::build_long_run_warning_message(
app,
));
app.toast_expires_at = Some(std::time::Instant::now() + std::time::Duration::from_secs(8));
}
}