use super::*;
pub struct HomeSection<'a> {
pub title: &'a str,
pub search: Result<SectionSearch<'a>, &'a String>,
}
pub struct SectionSearch<'a> {
pub kind: SearchKind,
pub query: &'a str,
pub rows: usize,
pub results: Fetched<'a, SearchResults>,
}
pub(super) struct Start<'a, T> {
pub(super) list: &'a Results<T>,
pub(super) rows: usize,
pub(super) url: String,
}
impl<T> Rows<T> for Start<'_, T> {
fn rows(&self) -> &[T] {
self.list.items.get(..self.rows).unwrap_or(&self.list.items)
}
fn rest(&self) -> Rest {
let shown = self.rows().len() as u64;
let total = self.list.total.max(self.list.items.len() as u64);
match total - shown {
0 => Rest::All,
n => Rest::At(n, self.url.clone()),
}
}
}
pub fn home(page: &mut Page, sections: &[HomeSection<'_>], icons: Icons, now: u64) {
let inbox = button(
page,
"Notifications",
format!("{}/notifications", url::base()),
);
page.line(vec![inbox]);
page.blank();
if sections.is_empty() {
page.box_top(vec![Seg::new("Home", Role::Strong)], Vec::new());
empty_row(
page,
"No sections. Add a search from its list (in the Space menu), or as [[home]] in config.toml.",
);
page.box_bottom();
}
for (i, section) in sections.iter().enumerate() {
if i > 0 {
page.blank();
}
page.anchor(format!("section-{i}"));
let title = |count: Option<String>| {
let mut t = vec![Seg::new(section.title.to_owned(), Role::Strong)];
t.extend(count.map(|c| Seg::new(format!(" {c}"), Role::Meta)));
t
};
let failed = |page: &mut Page, why: &str| {
page.box_top(title(None), Vec::new());
section_note(page, i, why, Role::Error);
page.box_bottom();
};
let search = match §ion.search {
Ok(search) => search,
Err(why) => {
failed(page, &format!("This section is written wrong: {why}."));
continue;
}
};
let loading = |page: &mut Page| loading_box(page, title(None));
let what = section.title.to_lowercase();
let Some(results) = search.results.show_with(page, &what, loading, failed) else {
continue;
};
let url = url::search(search.kind, search.query);
let noun = match search.kind {
SearchKind::Repos => "repositories",
SearchKind::Pulls => "pull requests",
_ => "issues",
};
let empty = format!("No {noun} match “{}”.", search.query);
let rows = search.rows;
match results {
SearchResults::Issues(list) => {
let start = Start { list, rows, url };
let title = title(Some(count_of(&start)));
let review = search.query.contains("review-requested:");
section_box(page, title, &start, (i, &empty), |page, it| {
let shown = (true, by_update(search.query));
issue_row_to(page, it, shown, icons, now, review);
});
}
SearchResults::Repos(list) => {
let start = Start { list, rows, url };
let title = title(Some(count_of(&start)));
section_box(page, title, &start, (i, &empty), |page, r| {
repo_row(page, r, now, true);
});
}
_ => failed(page, "GitHub sent the wrong kind of results"),
}
}
}
pub(super) fn section_box<T>(
page: &mut Page,
title: Vec<Seg>,
start: &Start<'_, T>,
(i, empty): (usize, &str),
row: impl FnMut(&mut Page, &T),
) {
page.box_top(title, Vec::new());
if start.rows().is_empty() {
section_note(page, i, empty, Role::Meta);
}
box_rows(page, start, row);
page.box_bottom();
}
pub(super) fn section_note(page: &mut Page, i: usize, text: &str, role: Role) {
item(page, Link::Section(i), |page, link| {
page.wrapped(
vec![Seg::linked(text.to_owned(), role, link)],
0,
Frame::Body,
);
});
}