use super::*;
use crate::core::store::{List, ListOf};
use crate::core::usecase::project::Projects;
#[derive(Debug, Clone, Copy)]
enum Field {
Projects,
Cycles,
}
impl App {
pub fn handle_message(&mut self, msg: Message) {
self.fold_message(msg);
self.advance_restore();
}
fn fold_message(&mut self, msg: Message) {
match msg {
Message::Teams(teams) => {
let remembered = self.restored_team_id().cloned();
let preferred = usecase::team::pick_initial(
&teams,
remembered.as_ref(),
self.default_team.as_deref(),
);
if let Some(idx) = preferred {
self.nav.team = idx;
}
self.store.teams = teams;
let Some(team_id) = self.team_id() else {
return;
};
let request = usecase::team::ensure_context(&mut self.store, team_id.clone());
self.send(request);
if self.restoring_destination() {
return;
}
self.nav.dest = Nav::Team(self.nav.team, TeamSection::Issues);
let preset = self.view.lists[IssueSource::Team].preset;
let open = usecase::issue::open_team_issues(&mut self.store, team_id, preset);
self.open_issues(IssueSource::Team, open);
}
Message::Viewer { id, organization } => {
usecase::user::take_viewer(&mut self.store, id, organization);
if self.nav.dest == Nav::MyIssues {
self.reload_current_tab();
}
}
Message::TeamContext {
team_id,
states,
members,
} => {
let waiting = self.popup_team_id() == Some(&team_id);
usecase::team::context_arrived(&mut self.store, team_id, states, members);
if waiting {
self.view.popup_index = self.popup_initial_index();
}
}
Message::Issues {
team_id,
preset,
page,
} => {
let of = ListOf::TeamIssues { team_id, preset };
if self.accept_issue_page(IssueSource::Team, &of, page) {
self.clear_status();
}
}
Message::MyIssues(page) => {
let Some(of) = self.store.issues[IssueSource::My].of.clone() else {
return;
};
if self.accept_issue_page(IssueSource::My, &of, page) {
self.clear_status();
}
}
Message::PaletteResults { seq, issues } => self.accept_palette_results(seq, issues),
Message::SearchResults {
term,
team_id,
issues,
} => {
if team_id.is_some() && team_id != self.team_id() {
return;
}
let Some(team) = self.team_id() else {
return;
};
usecase::issue::take_search_results(&mut self.store, team, issues);
self.nav.dest = Nav::Team(self.nav.team, TeamSection::Issues);
self.nav.screen = Screen::IssueList;
let list = &mut self.view.lists[IssueSource::Team];
list.filters.clear();
list.preset = Preset::All;
list.search.clear();
list.selected = 0;
self.nav.global_search = Some(term);
}
Message::ViewProjects { view_id, page } => {
let append = page.append;
let of = ListOf::ViewProjects(view_id);
if !usecase::project::take_page(&mut self.store, Projects::View, &of, page) {
return;
}
if !append {
self.view.selected_view_project_index = 0;
}
self.clear_status();
}
Message::Projects { team_id, page } => {
let of = ListOf::TeamProjects(team_id);
if !usecase::project::take_page(&mut self.store, Projects::Team, &of, page) {
return;
}
self.clamp(Field::Projects);
self.clear_status();
}
Message::Cycles { team_id, page } => {
let of = ListOf::TeamCycles(team_id);
if !usecase::cycle::take_page(&mut self.store, &of, page) {
return;
}
self.clamp(Field::Cycles);
self.clear_status();
}
Message::CustomViews(views) => {
let open = match self.nav.dest {
Nav::View(i) => self.store.custom_views.get(i).map(|v| v.id.clone()),
_ => None,
};
usecase::view::take_views(&mut self.store, views);
if let Some(id) = open {
match self.store.custom_views.iter().position(|v| v.id == id) {
Some(i) => self.nav.dest = Nav::View(i),
None => self.activate(Nav::Views),
}
}
self.view.selected_view_index = self
.view
.selected_view_index
.min(self.listed_views().len().saturating_sub(1));
}
Message::Favorites(favorites) => {
usecase::favorite::take_favorites(&mut self.store, favorites)
}
Message::ViewIssues { view_id, page } => {
let of = ListOf::ViewIssues(view_id);
if self.accept_issue_page(IssueSource::View, &of, page) {
self.clear_status();
}
}
Message::IssueDetail(issue) => {
let id = issue.id.clone();
let adopted = usecase::issue::take_detail(&mut self.store, *issue);
self.restored_issue_loaded(&id, adopted);
}
Message::ProjectIssues { project_id, page } => {
let of = ListOf::ProjectIssues(project_id);
self.accept_issue_page(IssueSource::Project, &of, page);
}
Message::CycleIssues { cycle_id, page } => {
let of = ListOf::CycleIssues(cycle_id);
self.accept_issue_page(IssueSource::Cycle, &of, page);
}
Message::IssueCreated { team_id, issue } => {
self.set_status(format!("Created {}", issue.identifier));
let id = issue.id.clone();
if usecase::issue::created(&mut self.store, &team_id, *issue)
&& self.issue_source() == IssueSource::Team
&& self.nav.screen == Screen::IssueList
{
self.restore_issue_selection(Some(&id));
}
}
Message::Labels { .. } => {}
Message::ProjectsFound { .. }
| Message::ProjectDetail(_)
| Message::ProjectStatuses(_)
| Message::ProjectCreated(_)
| Message::MilestoneCreated { .. } => {}
Message::CommentPosted { issue_id, .. } => {
self.set_status("Comment posted");
if self.store.current_issue.as_ref().map(|i| &i.id) == Some(&issue_id) {
self.queue_detail_fetches();
}
}
Message::Mutated(what) => {
self.set_status(what);
self.queue_detail_fetches();
}
Message::Failed { request, error } => self.request_failed(&request, &error),
}
}
fn request_failed(&mut self, request: &Request, error: &str) {
if let Request::Issue(usecase::issue::Request::Detail { issue_id }) = request
&& self.restored_issue_failed(issue_id)
{
return;
}
self.restore_lost(request);
match request {
Request::Notes(usecase::notes::Request::Deliver(handoff)) => {
return self.handoff_failed(handoff, error);
}
Request::Agent(_) => {
return self.set_error(format!("Could not reach herdr: {error}"));
}
Request::Issue(usecase::issue::Request::QuickSearch { seq, .. }) => {
if *seq == self.view.palette.seq {
self.view.palette.searching = false;
}
return self.set_status(format!("Search failed: {error}"));
}
Request::Team(usecase::team::Request::Context { team_id }) => {
usecase::team::context_failed(&mut self.store, team_id);
}
Request::Issue(change) => {
if let Some(issue_id) = change.changed_issue() {
self.request(usecase::issue::change_refused(issue_id));
}
}
_ => {}
}
if let Some(cursor) = request.cursor() {
self.store.page_failed(cursor);
}
self.set_error(format!(
"{}: {error}",
crate::core::message::failure(request)
));
}
fn accept_issue_page(&mut self, source: IssueSource, of: &ListOf, page: Page<Issue>) -> bool {
if !self.store.wants_page(List::Issues(source), of) {
return false;
}
let on_screen = source == self.issue_source();
let keep = if on_screen {
let restored = (!page.append)
.then(|| self.take_restored_selection(source))
.flatten();
restored.or_else(|| self.selected_issue_id())
} else {
None
};
let append = page.append;
usecase::issue::take_page(&mut self.store, source, of, page);
if on_screen {
self.restore_issue_selection(keep.as_ref());
} else if !append {
*self.selected_index_of(source) = 0;
}
true
}
fn clamp(&mut self, field: Field) {
let (len, index) = match field {
Field::Projects => (
self.store.projects.items.len(),
&mut self.view.selected_project_index,
),
Field::Cycles => (
self.store.cycles.items.len(),
&mut self.view.selected_cycle_index,
),
};
*index = (*index).min(len.saturating_sub(1));
}
fn selected_issue_id(&self) -> Option<IssueId> {
self.visible_issues()
.get(self.selected_index())
.map(|i| i.id.clone())
}
fn restore_issue_selection(&mut self, id: Option<&IssueId>) {
let index = id
.and_then(|id| self.visible_issues().iter().position(|i| &i.id == id))
.unwrap_or(0);
*self.selected_index_mut() = index;
}
}