use crate::app::{Action, AppState, reducer};
use crate::errors::ComponentError;
use crate::git::parsers::status;
use crate::services::GitService;
use chrono::Local;
use std::sync::Arc;
pub fn refresh_status(
state: &mut AppState,
git_service: &Arc<GitService>,
) -> Result<(), ComponentError> {
let path = state.repo_path.clone();
if let Ok(branch) = git_service.current_branch(&path) {
*state = reducer(state.clone(), Action::SetCurrentBranch(branch));
}
if state.merge_notifier_enabled {
let due = state
.merge_notifier_last_check
.map(|t| t.elapsed().as_secs() >= state.merge_notifier_interval_secs)
.unwrap_or(true);
if due {
match git_service.ahead_behind_remote(&path, &state.merge_base_branch) {
Ok((ahead, _behind)) => {
let merge_base = state.merge_base_branch.clone();
*state = reducer(state.clone(), Action::SetMergeAhead(Some(ahead)));
*state = reducer(
state.clone(),
Action::AppendMergeLog(format!(
"{}: ahead {} (auto)",
merge_base, ahead
)),
);
}
Err(e) => {
tracing::warn!("merge notifier ahead/behind error: {e}");
let merge_base = state.merge_base_branch.clone();
*state = reducer(state.clone(), Action::SetMergeAhead(None));
*state = reducer(
state.clone(),
Action::AppendMergeLog(format!("{}: error {}", merge_base, e)),
);
}
}
state.merge_notifier_last_check = Some(std::time::Instant::now());
if let Ok(list) = git_service.branch_names(&path) {
*state = reducer(state.clone(), Action::SetMergeBaseCandidates(list));
}
}
} else {
*state = reducer(state.clone(), Action::SetMergeAhead(None));
state.merge_notifier_last_check = None;
}
if state.auto_fetch_enabled {
let due = state
.auto_fetch_last_check
.map(|t| t.elapsed().as_secs() >= state.auto_fetch_interval_secs)
.unwrap_or(true);
if due {
match git_service.fetch_dry_run(&path, &state.auto_fetch_remote) {
Ok(output) => {
let remote = state.auto_fetch_remote.clone();
if output.trim().is_empty() {
*state = reducer(state.clone(), Action::AppendOpLog(format!("fetch {}: no changes", remote)));
} else {
*state = reducer(state.clone(), Action::AppendOpLog(format!("fetch {}:", remote)));
for line in output.lines().take(5) {
*state = reducer(state.clone(), Action::AppendOpLog(format!(" {}", line)));
}
}
}
Err(e) => {
tracing::warn!("auto-fetch error: {e}");
*state = reducer(state.clone(), Action::AppendOpLog(format!("fetch error: {e}")));
}
}
state.auto_fetch_last_check = Some(std::time::Instant::now());
}
} else {
state.auto_fetch_last_check = None;
}
match git_service.status_porcelain(&path) {
Ok(out) => {
*state = reducer(state.clone(), Action::SetStatusError(None));
let now = Local::now().format("%H:%M:%S").to_string();
*state = reducer(state.clone(), Action::SetLastRefreshed(Some(now)));
let mut lines: Vec<String> = Vec::new();
for line in out.lines().take(200) {
lines.push(line.to_string());
}
if out.lines().count() > 200 {
lines.push(format!("… (+{} more)", out.lines().count() - 200));
}
if let Ok(summary) = status::parse_status(&out) {
*state = reducer(state.clone(), Action::SetStatusSummary(summary));
}
let out_clone = out.clone();
*state = reducer(state.clone(), Action::SetStatus(out));
let mut entries = status::parse_status_entries(&state.last_status);
let total_entries = entries.len();
if total_entries > 200 {
entries.truncate(200);
entries.push(status::StatusEntry {
staged: false,
unstaged: false,
conflict: false,
path: format!("… (+{} more)", total_entries - 200),
});
}
*state = reducer(state.clone(), Action::SetStatusEntries(entries));
*state = reducer(state.clone(), Action::SetStatusLines(lines));
let has_conflicts = state.status_entries.iter().any(|e| e.conflict);
if has_conflicts {
*state = reducer(state.clone(), Action::FilterConflictsOnly);
} else {
*state = reducer(state.clone(), Action::ClearConflictFilter);
}
}
Err(e) => {
tracing::warn!("status error: {e}");
*state = reducer(state.clone(), Action::SetStatusError(Some(e.to_string())));
let now = Local::now().format("%H:%M:%S").to_string();
*state = reducer(state.clone(), Action::SetLastRefreshed(Some(now)));
}
}
*state = reducer(state.clone(), Action::SetRefreshing(false));
Ok(())
}