use crate::reply::queue::QueueRow;
use crate::ui::{Model, theme};
pub const OPEN: &str = "waiting on you…";
pub const CLOSE: &str = "done";
pub const HEADING: &str = "waiting on you";
pub const NOT_ANSWERED: &str = "waiting to hear what is asking for you";
pub const NOTHING: &str = "nothing is waiting on you — a conversation that asks will appear here";
pub const SEEN: &str = "seen";
pub const GO: &str = "go to it";
pub const WIDER: &str = "…and every call like it:";
pub fn wider(verdict: &str, scope: &str) -> String {
format!("{verdict} for this {scope}")
}
pub fn render(ui: &mut egui::Ui, model: &mut Model) -> bool {
if !model.showing(crate::ui::Listing::Queue) {
return false;
}
ui.label(egui::RichText::new(HEADING).heading().color(theme::INK));
ui.horizontal(|ui| {
ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| {
if ui.button(CLOSE).clicked() {
model.close_queue();
}
});
});
ui.add_space(theme::space::S);
let waiting = model.waiting.clone();
if waiting.is_empty() {
theme::paint::empty(ui, NOT_ANSWERED);
return true;
}
if waiting.iter().all(|section| section.rows.is_empty()) {
theme::paint::empty(ui, NOTHING);
return true;
}
egui::ScrollArea::vertical()
.id_salt(HEADING)
.auto_shrink(false)
.show(ui, |ui| {
for section in &waiting {
if section.rows.is_empty() {
continue;
}
ui.add_space(theme::space::S);
ui.colored_label(theme::INK_WEAK, crate::ui::roster::header(§ion.channel));
for row in §ion.rows {
waiting_row(ui, model, row);
}
}
});
true
}
fn waiting_row(ui: &mut egui::Ui, model: &mut Model, row: &QueueRow) {
theme::paint::row(
ui,
&headline(row),
theme::INK,
Some(theme::accent(theme::State::Attention)),
false,
0.0,
);
if let Some(said) = flagged(row) {
ui.colored_label(theme::accent(theme::State::Annotation), said);
}
if let Some(said) = row.failure.clone() {
ui.colored_label(theme::accent(theme::State::Error), said);
}
if let Some(said) = parked(row) {
ui.colored_label(theme::accent(theme::State::Annotation), said);
}
if let Some(said) = signalled(row) {
ui.colored_label(theme::INK_WEAK, said);
}
if !row.preview.is_empty() {
ui.colored_label(theme::INK_WEAK, row.preview.clone());
}
acts(ui, model, row);
}
fn acts(ui: &mut egui::Ui, model: &mut Model, row: &QueueRow) {
if model.wall(&row.workspace).is_none() {
ui.colored_label(
theme::accent(theme::State::Annotation),
crate::ui::roster::NO_NAME_HERE,
);
return;
}
let mut fired = None;
let mut verdict = None;
ui.horizontal_wrapped(|ui| {
ui.add_space(theme::space::L);
let answer = ui.button(SEEN);
crate::ui::act::tag(&answer, &[crate::verbs::SEEN.word]);
if answer.clicked() {
fired = Some(row.clone());
}
if row.held.is_some() {
for word in crate::verbs::VERDICTS {
let seat = ui.button(word);
crate::ui::act::tag(&seat, &[crate::verbs::ANSWER.word]);
if seat.clicked() {
verdict = Some((word, crate::verbs::capability::CALL));
}
}
}
if ui.button(GO).clicked() {
model.go_to(row);
}
});
if row.held.is_some() {
ui.colored_label(theme::tone_ink(&crate::reply::convs::Tone::Weak), WIDER);
for scope in crate::verbs::SCOPES.into_iter().skip(1) {
ui.horizontal_wrapped(|ui| {
for word in crate::verbs::VERDICTS {
let seat = ui.button(wider(word, scope));
crate::ui::act::tag(&seat, &[crate::verbs::ANSWER.word]);
if seat.clicked() {
verdict = Some((word, scope));
}
}
});
}
}
if let Some(row) = fired {
model.post_seen(&row);
}
if let Some((word, scope)) = verdict {
model.post_answer(row, word, scope);
}
}
pub fn headline(row: &QueueRow) -> String {
let mut said = vec![format!(
"{} on {} [{}{}] {}",
row.display,
row.workspace,
row.state.label(),
if row.uncertain {
crate::ui::convs::UNCERTAIN
} else {
""
},
crate::ui::convs::age(row.age_secs)
)];
if row.pending > 0 {
said.push(format!("{} under it", row.pending));
}
said.join(" ")
}
pub fn flagged(row: &QueueRow) -> Option<String> {
let flag = row.flag.as_ref()?;
Some(format!("flagged {} — {}", flag.at, flag.reason))
}
pub fn parked(row: &QueueRow) -> Option<String> {
let held = row.held.as_ref()?;
Some(format!(
"held at the boundary: {} ({}) — {}",
held.tool, held.tool_use, held.reason
))
}
pub fn signalled(row: &QueueRow) -> Option<String> {
(!row.signals.is_empty()).then(|| row.signals.join(" "))
}
#[cfg(test)]
mod tests;