feat: even more M4 closing
This commit is contained in:
@@ -137,8 +137,10 @@ pub enum Message {
|
||||
GenerateSite,
|
||||
RunMetadataDiff,
|
||||
RunSiteValidation,
|
||||
ApplySiteValidation,
|
||||
EngineTaskDone { task_id: TaskId, label: String, result: Result<String, String> },
|
||||
SiteValidationLoaded(Result<engine::validate_site::SiteValidationReport, String>),
|
||||
SiteValidationApplied(Result<String, String>),
|
||||
|
||||
// Editor views
|
||||
PostEditor(PostEditorMsg),
|
||||
@@ -2351,6 +2353,7 @@ impl BdsApp {
|
||||
Task::none()
|
||||
}
|
||||
Message::RunSiteValidation => self.start_site_validation(),
|
||||
Message::ApplySiteValidation => self.apply_site_validation(),
|
||||
Message::EngineTaskDone { task_id, label, result } => {
|
||||
match &result {
|
||||
Ok(detail) => {
|
||||
@@ -2396,6 +2399,21 @@ impl BdsApp {
|
||||
}
|
||||
Task::none()
|
||||
}
|
||||
Message::SiteValidationApplied(result) => {
|
||||
self.site_validation_state.is_applying = false;
|
||||
match result {
|
||||
Ok(detail) => {
|
||||
self.site_validation_state.error_message = None;
|
||||
self.notify(ToastLevel::Success, &detail);
|
||||
self.start_site_validation()
|
||||
}
|
||||
Err(error) => {
|
||||
self.site_validation_state.error_message = Some(error.clone());
|
||||
self.notify(ToastLevel::Error, &error);
|
||||
Task::none()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ── Toast ──
|
||||
Message::ShowToast(level, msg) => {
|
||||
@@ -3703,6 +3721,70 @@ impl BdsApp {
|
||||
)
|
||||
}
|
||||
|
||||
fn apply_site_validation(&mut self) -> Task<Message> {
|
||||
if self.site_validation_state.is_running || self.site_validation_state.is_applying {
|
||||
return Task::none();
|
||||
}
|
||||
|
||||
let report = engine::validate_site::SiteValidationReport {
|
||||
missing_pages: self.site_validation_state.missing_files.clone(),
|
||||
extra_pages: self.site_validation_state.extra_files.clone(),
|
||||
stale_pages: self.site_validation_state.stale_files.clone(),
|
||||
};
|
||||
let sections = engine::generation::sections_from_validation_report(&report);
|
||||
if sections.is_empty() {
|
||||
return Task::none();
|
||||
}
|
||||
|
||||
let Some(project_id) = self.active_project.as_ref().map(|project| project.id.clone()) else {
|
||||
self.site_validation_state.error_message = Some(t(self.ui_locale, "engine.generateSiteNoProject"));
|
||||
return Task::none();
|
||||
};
|
||||
let Some(data_dir) = self.data_dir.clone() else {
|
||||
self.site_validation_state.error_message = Some(t(self.ui_locale, "engine.previewDataDirUnavailable"));
|
||||
return Task::none();
|
||||
};
|
||||
|
||||
self.site_validation_state.is_applying = true;
|
||||
self.site_validation_state.error_message = None;
|
||||
let db_path = self.db_path.clone();
|
||||
let applied_label = t(self.ui_locale, "siteValidation.apply");
|
||||
|
||||
Task::perform(
|
||||
async move {
|
||||
let db = Database::open(&db_path).map_err(|error| error.to_string())?;
|
||||
let metadata = engine::meta::read_project_json(&data_dir).map_err(|error| error.to_string())?;
|
||||
let all_posts = bds_core::db::queries::post::list_posts_by_project(db.conn(), &project_id)
|
||||
.map_err(|error| error.to_string())?;
|
||||
let mut sources = Vec::new();
|
||||
for post in all_posts.into_iter().filter(|post| post.status == PostStatus::Published) {
|
||||
let body_markdown = load_generation_post_body(&data_dir, &post)?;
|
||||
sources.push(engine::generation::PublishedPostSource { post, body_markdown });
|
||||
}
|
||||
let output_dir = data_dir.join("html");
|
||||
std::fs::create_dir_all(&output_dir).map_err(|error| error.to_string())?;
|
||||
let apply_report = engine::generation::apply_validation_sections(
|
||||
db.conn(),
|
||||
&output_dir,
|
||||
&project_id,
|
||||
&metadata,
|
||||
&sources,
|
||||
§ions,
|
||||
)
|
||||
.map_err(|error| error.to_string())?;
|
||||
Ok(format!(
|
||||
"{}: written={}, skipped={}, deleted={}, output={}",
|
||||
applied_label,
|
||||
apply_report.written_paths.len(),
|
||||
apply_report.skipped_paths.len(),
|
||||
apply_report.deleted_paths.len(),
|
||||
output_dir.display(),
|
||||
))
|
||||
},
|
||||
Message::SiteValidationApplied,
|
||||
)
|
||||
}
|
||||
|
||||
/// Spawn a blocking engine operation on a background thread via TaskManager.
|
||||
///
|
||||
/// Returns `Task::none()` if no active project/db/data_dir.
|
||||
|
||||
@@ -11,6 +11,7 @@ use crate::i18n::t;
|
||||
pub struct SiteValidationState {
|
||||
pub has_run: bool,
|
||||
pub is_running: bool,
|
||||
pub is_applying: bool,
|
||||
pub missing_files: Vec<String>,
|
||||
pub extra_files: Vec<String>,
|
||||
pub stale_files: Vec<String>,
|
||||
@@ -24,6 +25,15 @@ pub fn view<'a>(state: &'a SiteValidationState, locale: UiLocale) -> Element<'a,
|
||||
button(text(t(locale, "siteValidation.run")).size(13).shaping(Shaping::Advanced))
|
||||
.on_press(Message::RunSiteValidation)
|
||||
};
|
||||
let has_issues = !state.missing_files.is_empty() || !state.extra_files.is_empty() || !state.stale_files.is_empty();
|
||||
let apply_button = if state.is_applying {
|
||||
button(text(t(locale, "siteValidation.applying")).size(13).shaping(Shaping::Advanced))
|
||||
} else if !state.is_running && state.error_message.is_none() && has_issues {
|
||||
button(text(t(locale, "siteValidation.apply")).size(13).shaping(Shaping::Advanced))
|
||||
.on_press(Message::ApplySiteValidation)
|
||||
} else {
|
||||
button(text(t(locale, "siteValidation.apply")).size(13).shaping(Shaping::Advanced))
|
||||
};
|
||||
|
||||
let mut content = column![
|
||||
row![
|
||||
@@ -31,7 +41,7 @@ pub fn view<'a>(state: &'a SiteValidationState, locale: UiLocale) -> Element<'a,
|
||||
.size(24)
|
||||
.shaping(Shaping::Advanced)
|
||||
.color(Color::from_rgb(0.88, 0.88, 0.92)),
|
||||
run_button,
|
||||
row![run_button, apply_button].spacing(12),
|
||||
]
|
||||
.spacing(16),
|
||||
]
|
||||
|
||||
Reference in New Issue
Block a user