use super::AppModel;
use crate::opslog::{self, OpRow, SurfaceFailure};
use crate::projects;
use crate::projects::balls::parse_list;
use crate::projects::join::{self, JoinRow};
use crate::projects::runner;
use std::path::{Path, PathBuf};
const OPS_TAIL: usize = 256;
impl AppModel {
pub(super) fn refresh_balls(&mut self) {
let all = projects::enumerate(&self.roots.balls_clones);
let visible: Vec<PathBuf> = projects::visible(&all, self.show_internal())
.into_iter()
.map(|p| p.path.clone())
.collect();
self.balls_by_project = visible
.iter()
.filter_map(|p| Some((p.clone(), parse_list(&self.balls.list(p).ok()?))))
.collect();
self.rebuild_join(&visible);
}
pub fn show_internal(&self) -> bool {
self.ui.show_internal()
}
pub fn set_show_internal(&mut self, show: bool) {
self.ui.set_show_internal(show);
self.refresh_balls();
}
pub(super) fn rebuild_join(&mut self, cloned: &[PathBuf]) {
self.join_rows = join::join(
cloned,
&self.balls_by_project,
&self.closed_by_project,
&self.workspaces,
);
}
pub fn identity(&self) -> String {
runner::identity(self.ui.identity_last_used(), self.identity_user.clone())
}
pub(super) fn refresh_ops(&mut self) {
let root = self.roots.yog_state.clone();
self.ops = opslog::tail(&root, OPS_TAIL)
.iter()
.map(|entry| OpRow::from(&opslog::detached::fold(&root, entry)))
.collect();
}
pub(crate) fn ops_rows(&self) -> &[OpRow] {
&self.ops
}
pub fn last_failure(&self) -> Option<SurfaceFailure> {
self.ops
.last()
.filter(|r| r.failed())
.map(SurfaceFailure::from)
}
pub(crate) fn state_root(&self) -> &Path {
&self.roots.yog_state
}
pub fn ws_balls(&self, ws: &Path) -> Vec<crate::nav::BoundBall> {
self.join_rows
.iter()
.filter(|r| r.workspace.as_deref() == Some(ws) && !r.ball_id.is_empty())
.map(|r| crate::nav::BoundBall {
id: r.ball_id.clone(),
badge: join::badge(r.state, r.claimant.as_deref()),
})
.collect()
}
pub fn conversation_groups(&self, now_unix: i64) -> Vec<crate::nav::convs::group::ConvGroup> {
crate::nav::convs::group::group_by_ball(self.conversations(now_unix))
}
pub fn conversation_ball(&self, root_id: &str) -> Option<crate::nav::convs::ConvBall> {
let ws = self.focus.ws.as_deref()?;
let tree = self.trees.get(ws)?;
let id = tree
.agents
.iter()
.find(|a| a.agent_id == root_id)?
.goal_ball
.as_deref()?;
Some(self.resolve_conv_ball(id))
}
pub(crate) fn resolve_conv_ball(&self, id: &str) -> crate::nav::convs::ConvBall {
match self.join_rows.iter().find(|r| r.ball_id == id) {
Some(r) => crate::nav::convs::ConvBall {
id: id.to_owned(),
state: Some(r.state),
title: r.title.clone(),
badge: join::badge(r.state, r.claimant.as_deref()),
},
None => crate::nav::convs::ConvBall {
id: id.to_owned(),
state: None,
title: None,
badge: None,
},
}
}
pub fn focused_join(&self) -> Option<&JoinRow> {
self.row_for(self.focus.ws.as_deref()?)
}
fn row_for(&self, ws: &Path) -> Option<&JoinRow> {
self.join_rows
.iter()
.find(|r| r.workspace.as_deref() == Some(ws))
}
pub fn focused_ws_name(&self) -> Option<String> {
self.focus
.ws
.as_deref()
.and_then(Path::file_name)
.map(|s| s.to_string_lossy().into_owned())
}
pub fn workspace_names(&self) -> Vec<String> {
self.workspaces
.iter()
.filter_map(|w| match &w.kind {
crate::binding::WorkspaceKind::Named { name } => Some(name.clone()),
_ => None,
})
.collect()
}
pub fn empty_project_hint(&self) -> Option<String> {
let has_project = !self.balls_by_project.is_empty()
|| self
.join_rows
.iter()
.any(|r| r.state == join::JoinState::OrphanedProject);
(!has_project).then(|| "yog exec bl prime".to_owned())
}
pub fn after_bl_verb(&mut self, project: &Path) {
self.balls_by_project
.insert(project.to_path_buf(), self.balls.live(project));
self.closed_by_project
.insert(project.to_path_buf(), self.balls.closed(project));
let cloned: Vec<PathBuf> = self.balls_by_project.keys().cloned().collect();
self.rebuild_join(&cloned);
self.refresh_ops();
}
pub fn after_lernie_verb(&mut self) {
self.refresh_ops();
}
#[cfg(test)]
pub(crate) fn yog_data_root(&self) -> &Path {
&self.roots.yog_data
}
pub fn balls_state_root(&self) -> PathBuf {
let clones = &self.roots.balls_clones;
clones.parent().unwrap_or(clones).to_path_buf()
}
}
mod starts;
#[cfg(test)]
mod tests;