use crate::AppModel;
use crate::boundary::Action;
use crate::delete::Confirmation;
use crate::theme;
use crate::wire::post::Ticket;
use std::path::PathBuf;
use super::ShellState;
#[derive(Default)]
pub struct DeleteState {
pub target: Option<PathBuf>,
pub typed: String,
pub error: String,
pub ticket: Option<Ticket>,
}
pub(super) fn open(model: &AppModel, state: &mut ShellState, name: &str) {
state.delete = DeleteState {
target: model.workspace_path(name),
..DeleteState::default()
};
}
pub(super) fn danger_row(ui: &mut egui::Ui, model: &mut AppModel, state: &mut ShellState) {
let Some(name) = model.focused_ws_name() else {
return;
};
if !crate::nav::tabs::is_named(&super::chrome::ws_rows(model), &name) {
return;
}
if ui
.button(egui::RichText::new("delete this workspace…").color(theme::ICHOR))
.on_hover_text(
"takes the sphere wall down — irrecoverable. Typed, it is \
`/delete-workspace <typed name>`.",
)
.clicked()
{
open(model, state, &name);
}
}
pub(super) fn dialog(ctx: &egui::Context, model: &mut AppModel, state: &mut ShellState) {
if state.delete.target.is_none() {
return;
}
settle(model, state);
window(ctx, model, state);
if state.delete.target.is_none() {
super::focus::request(state);
}
}
fn window(ctx: &egui::Context, model: &mut AppModel, state: &mut ShellState) {
let Some(ws) = state.delete.target.clone() else {
return;
};
let name = model.snap.ws_name(&ws);
if !crate::nav::tabs::is_named(&super::chrome::ws_rows(model), &name) {
state.delete = DeleteState::default();
return;
}
let rows = super::convs::of(model, name.clone())
.value
.unwrap_or_default();
let balls = super::chrome::balls(model, &name).value.unwrap_or_default();
let confirm = crate::delete::confirmation_of_rows(&name, &rows, &balls);
let mut shown = true;
egui::Window::new(format!("delete workspace {}", confirm.name))
.collapsible(false)
.resizable(false)
.anchor(egui::Align2::CENTER_CENTER, egui::Vec2::ZERO)
.open(&mut shown)
.show(ctx, |ui| body(ui, model, state, &confirm));
if !shown {
state.delete = DeleteState::default();
}
}
fn body(ui: &mut egui::Ui, model: &mut AppModel, state: &mut ShellState, confirm: &Confirmation) {
ui.colored_label(
theme::ICHOR,
"this destroys the workspace and everything inside it — irrecoverably",
);
census_room(ui, "delete-workspace-census", &mut |ui| {
ui.label("conversations that die:");
enumerate(ui, &confirm.conversations, "(none)");
ui.label("balls released:");
enumerate(ui, &confirm.ball_ids(), "(none)");
});
ui.separator();
if confirm.refused() {
ui.colored_label(
theme::ICHOR,
format!("live: {} — stop them first", confirm.live.join(", ")),
);
return;
}
ui.horizontal(|ui| {
ui.label(format!("type “{}” to confirm:", confirm.name));
ui.text_edit_singleline(&mut state.delete.typed)
.on_hover_text(
"Type the workspace's name exactly as shown to arm the delete — the \
same name `/delete-workspace <typed name>` carries.",
);
});
let armed = confirm.armed(&state.delete.typed) && state.delete.ticket.is_none();
if ui
.add_enabled(armed, egui::Button::new("Delete workspace"))
.on_hover_text(
"Destroy this workspace and every conversation listed above, and release \
the balls it holds. There is no undo and no archive. Typed, it is \
`/delete-workspace <typed name>`.",
)
.on_disabled_hover_text("type the workspace's name above to arm this")
.clicked()
{
fire(model, state, &confirm.name);
}
if state.delete.ticket.is_some() {
ui.weak("taking the wall down …");
}
if !state.delete.error.is_empty() {
ui.colored_label(theme::ICHOR, &state.delete.error);
}
}
pub(super) fn census_room(ui: &mut egui::Ui, salt: &str, rows: &mut dyn FnMut(&mut egui::Ui)) {
let cap = crate::layout::panel_ceiling(ui.ctx().screen_rect().height());
ui.scope(|ui| {
ui.set_max_height(cap);
egui::ScrollArea::vertical()
.id_salt(salt)
.max_height(cap)
.show(ui, rows);
});
}
fn enumerate(ui: &mut egui::Ui, items: &[String], empty: &str) {
if items.is_empty() {
ui.weak(empty);
}
for item in items {
ui.weak(item);
}
}
fn fire(model: &mut AppModel, state: &mut ShellState, name: &str) {
let action = Action::DeleteWorkspace {
workspace: name.to_owned(),
typed: state.delete.typed.clone(),
};
state.delete.ticket = Some(model.post_act(&action));
}
fn settle(model: &mut AppModel, state: &mut ShellState) {
let (Some(ticket), Some(ws)) = (state.delete.ticket, state.delete.target.clone()) else {
return;
};
let Some(landed) = model.act_receipt(ticket) else {
return;
};
state.delete.ticket = None;
model.deleted_workspace(&ws);
if let Some(reason) = super::act::trouble(&landed) {
state.delete.error = reason;
return;
}
state.forget_wall(&ws);
state.delete = DeleteState::default();
}