Skip to content

Commit 8a9f4eb

Browse files
committed
Always visible .ini preview for assemble from url
1 parent 3934461 commit 8a9f4eb

2 files changed

Lines changed: 26 additions & 193 deletions

File tree

src/dialogs/create_distrobox_dialog.rs

Lines changed: 22 additions & 192 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ use adw::prelude::*;
22
use adw::subclass::prelude::*;
33
use gtk::gio::File;
44
use gtk::{gio, glib};
5-
use std::cell::Cell;
65
use std::time::Duration;
76
use tracing::error;
87

@@ -39,16 +38,11 @@ mod imp {
3938
pub toolbar_view: adw::ToolbarView,
4039
pub toast_overlay: adw::ToastOverlay,
4140
pub name_row: adw::EntryRow,
42-
#[property(get, set)]
43-
pub url_validated: Cell<bool>,
4441
pub image_row: adw::ActionRow,
4542
pub images_model: gtk::StringList,
4643
pub selected_image: RefCell<String>,
4744
pub prefill_query: RefCell<Option<Query<Option<String>>>>,
48-
pub url_validation_query: RefCell<Option<Query<bool>>>,
4945
pub ini_content_query: RefCell<Option<Query<String>>>,
50-
#[property(get, set)]
51-
pub ini_approved: Cell<bool>,
5246
pub home_row_expander: adw::ExpanderRow,
5347
#[property(get, set, nullable)]
5448
pub home_folder: RefCell<Option<String>>,
@@ -479,28 +473,21 @@ impl CreateDistroboxDialog {
479473
let url_row = adw::EntryRow::new();
480474
url_row.set_title(&gettext("URL"));
481475
url_row.set_text("https://example.com/container.ini");
482-
url_row.set_show_apply_button(true);
483476

484477
url_group.add(&url_row);
485478
content.append(&url_group);
486479

487-
// Create preview expandable row
488-
let preview_expander = adw::ExpanderRow::new();
489-
preview_expander.set_title(&gettext("File Preview"));
490-
preview_expander.set_subtitle(&gettext("Loading..."));
491-
preview_expander.set_enable_expansion(false);
492-
preview_expander.set_visible(false);
480+
// Create preview section with always-visible text view
481+
let preview_label = gtk::Label::new(Some(&gettext("Configuration Preview")));
482+
preview_label.set_halign(gtk::Align::Start);
483+
preview_label.add_css_class("heading");
493484

494485
// Create TextView for content display
495486
let text_view = gtk::TextView::new();
496487
text_view.set_editable(false);
497488
text_view.set_cursor_visible(false);
498489
text_view.set_monospace(true);
499490
text_view.set_wrap_mode(gtk::WrapMode::None);
500-
text_view.set_margin_start(12);
501-
text_view.set_margin_end(12);
502-
text_view.set_margin_top(6);
503-
text_view.set_margin_bottom(6);
504491

505492
// Wrap TextView in ScrolledWindow
506493
let scrolled_window = gtk::ScrolledWindow::new();
@@ -509,28 +496,11 @@ impl CreateDistroboxDialog {
509496
scrolled_window.set_max_content_height(400);
510497
scrolled_window.set_vexpand(true);
511498

512-
// Create a box to hold the scrolled window
513-
let preview_box = gtk::Box::new(gtk::Orientation::Vertical, 0);
499+
let preview_box = gtk::Box::new(gtk::Orientation::Vertical, 6);
500+
preview_box.append(&preview_label);
514501
preview_box.append(&scrolled_window);
515-
preview_expander.add_row(&preview_box);
516-
517-
// Create security warning InfoBar
518-
let warning_infobar = adw::Banner::new(&gettext("Review the file contents carefully before proceeding. Only create containers from trusted sources."));
519-
warning_infobar.set_revealed(false);
520-
warning_infobar.set_button_label(Some(&gettext("Re-download")));
521-
522-
// Create approval checkbox
523-
let approval_checkbox = gtk::CheckButton::new();
524-
approval_checkbox.set_label(Some(&gettext("I have reviewed the file and trust this source")));
525-
approval_checkbox.set_margin_start(12);
526-
approval_checkbox.set_margin_end(12);
527-
approval_checkbox.set_margin_top(6);
528-
approval_checkbox.set_margin_bottom(6);
529-
approval_checkbox.set_visible(false);
530-
531-
content.append(&preview_expander);
532-
content.append(&warning_infobar);
533-
content.append(&approval_checkbox);
502+
503+
content.append(&preview_box);
534504

535505
// Add create button for URL
536506
let create_btn = self.build_create_btn();
@@ -559,45 +529,26 @@ impl CreateDistroboxDialog {
559529

560530
// Wire ini_content_query success handler
561531
ini_content_query.connect_success(clone!(
562-
#[weak(rename_to=this)]
563-
self,
564-
#[weak]
565-
preview_expander,
566532
#[weak]
567533
text_view,
568534
#[weak]
569-
warning_infobar,
570-
#[weak]
571-
approval_checkbox,
535+
create_btn,
572536
move |content| {
573-
// Show preview section
574-
preview_expander.set_visible(true);
575-
preview_expander.set_enable_expansion(true);
576-
preview_expander.set_subtitle(&gettext("Downloaded successfully"));
577-
preview_expander.set_expanded(true); // Auto-expand on first successful download
578-
579537
// Set content in TextView
580538
text_view.buffer().set_text(content);
581539

582-
// Show warning and approval checkbox
583-
warning_infobar.set_revealed(true);
584-
approval_checkbox.set_visible(true);
585-
586-
// Show success toast
587-
let toast = adw::Toast::new(&gettext("File downloaded successfully"));
588-
this.imp().toast_overlay.add_toast(toast);
540+
// Enable create button
541+
create_btn.set_sensitive(true);
589542
}
590543
));
591544

592545
// Wire ini_content_query loading handler
593546
ini_content_query.connect_loading(clone!(
594547
#[weak]
595-
preview_expander,
548+
create_btn,
596549
move |is_loading| {
597550
if is_loading {
598-
preview_expander.set_visible(true);
599-
preview_expander.set_subtitle(&gettext("Downloading..."));
600-
preview_expander.set_enable_expansion(false);
551+
create_btn.set_sensitive(false);
601552
}
602553
}
603554
));
@@ -607,17 +558,12 @@ impl CreateDistroboxDialog {
607558
#[weak(rename_to=this)]
608559
self,
609560
#[weak]
610-
preview_expander,
611-
#[weak]
612-
warning_infobar,
561+
create_btn,
613562
#[weak]
614-
approval_checkbox,
563+
url_row,
615564
move |error| {
616-
preview_expander.set_visible(true);
617-
preview_expander.set_subtitle(&gettext("Failed to download"));
618-
preview_expander.set_enable_expansion(false);
619-
warning_infobar.set_revealed(false);
620-
approval_checkbox.set_visible(false);
565+
create_btn.set_sensitive(false);
566+
url_row.add_css_class("error");
621567

622568
let toast = adw::Toast::new(&format!("{}: {}", gettext("Download failed"), error));
623569
this.imp().toast_overlay.add_toast(toast);
@@ -626,124 +572,24 @@ impl CreateDistroboxDialog {
626572

627573
*self.imp().ini_content_query.borrow_mut() = Some(ini_content_query.clone());
628574

629-
// Create URL validation query with debouncing
630-
let url_validation_query: Query<bool> = Query::new(
631-
"url-validation".to_string(),
632-
clone!(
633-
#[weak(rename_to=this)]
634-
self,
635-
#[upgrade_or_panic]
636-
move || async move {
637-
if let Some(url_text) = this.assemble_url() {
638-
if url_text.is_empty() {
639-
return Ok(false);
640-
}
641-
this.validate_url(&url_text).await
642-
} else {
643-
Ok(false)
644-
}
645-
}
646-
),
647-
).with_timeout(Duration::from_secs(10));
648-
649-
url_validation_query.connect_success(clone!(
650-
#[weak(rename_to=this)]
651-
self,
652-
#[weak]
653-
url_row,
654-
#[strong]
655-
ini_content_query,
656-
move |is_valid| {
657-
this.set_url_validated(*is_valid);
658-
659-
if !is_valid {
660-
let toast = adw::Toast::new(&gettext("Could not connect to URL"));
661-
this.imp().toast_overlay.add_toast(toast);
662-
url_row.add_css_class("error");
663-
} else {
664-
url_row.remove_css_class("error");
665-
// Trigger download when URL is valid
666-
ini_content_query.refetch();
667-
}
668-
}
669-
));
670-
671-
url_validation_query.connect_error(clone!(
672-
#[weak]
673-
url_row,
674-
move |_| {
675-
url_row.add_css_class("error");
676-
}
677-
));
678-
679-
*self.imp().url_validation_query.borrow_mut() = Some(url_validation_query.clone());
680-
681575
url_row.connect_changed(clone!(
682576
#[weak(rename_to=this)]
683577
self,
684578
#[weak]
685579
create_btn,
686580
#[weak]
687-
preview_expander,
688-
#[weak]
689-
warning_infobar,
690-
#[weak]
691-
approval_checkbox,
581+
text_view,
692582
#[strong]
693-
url_validation_query,
583+
ini_content_query,
694584
move |entry| {
695585
this.set_assemble_url(Some(entry.text()));
696-
this.set_url_validated(false);
697-
this.set_ini_approved(false);
698586
create_btn.set_sensitive(false);
699-
// Clear error CSS when user types
700587
entry.remove_css_class("error");
701588

702-
// Reset UI state on URL change
703-
preview_expander.set_visible(false);
704-
warning_infobar.set_revealed(false);
705-
approval_checkbox.set_visible(false);
706-
approval_checkbox.set_active(false);
589+
text_view.buffer().set_text("");
707590

708-
// Debounced validation
709-
url_validation_query.refetch_with(Query::debounce(Duration::from_millis(500)));
710-
}
711-
));
712-
713-
// Also validate immediately on Apply button press
714-
url_row.connect_apply(clone!(
715-
#[strong]
716-
url_validation_query,
717-
move |_| {
718-
url_validation_query.refetch_with(Query::immediate());
719-
}
720-
));
721-
722-
// Handle approval checkbox changes
723-
approval_checkbox.connect_toggled(clone!(
724-
#[weak(rename_to=this)]
725-
self,
726-
#[weak]
727-
create_btn,
728-
move |checkbox| {
729-
let is_approved = checkbox.is_active();
730-
this.set_ini_approved(is_approved);
731-
// Update create button sensitivity
732-
let is_valid = this.url_validated();
733-
create_btn.set_sensitive(is_valid && is_approved);
734-
}
735-
));
736-
737-
// Handle re-download button in warning banner
738-
warning_infobar.connect_button_clicked(clone!(
739-
#[weak]
740-
approval_checkbox,
741-
#[strong]
742-
ini_content_query,
743-
move |_| {
744-
// Reset approval when re-downloading
745-
approval_checkbox.set_active(false);
746-
ini_content_query.refetch();
591+
// Debounced download (validation happens implicitly)
592+
ini_content_query.refetch_with(Query::debounce(Duration::from_millis(500)));
747593
}
748594
));
749595

@@ -1167,22 +1013,6 @@ impl CreateDistroboxDialog {
11671013
}
11681014
}
11691015

1170-
async fn validate_url(&self, url: &str) -> anyhow::Result<bool> {
1171-
// Use curl with HEAD request to validate URL
1172-
// CRITICAL: Use self.root_store().command_runner() for Flatpak compatibility
1173-
let command_runner = self.root_store().command_runner();
1174-
let mut cmd = Command::new("curl");
1175-
cmd.arg("-s"); // Silent
1176-
cmd.arg("-f"); // Fail on HTTP errors
1177-
cmd.arg("-I"); // HEAD request only
1178-
cmd.arg("--connect-timeout");
1179-
cmd.arg("5"); // 5 second connection timeout
1180-
cmd.arg(url);
1181-
1182-
let output = command_runner.output(cmd).await?;
1183-
Ok(output.status.success())
1184-
}
1185-
11861016
async fn download_ini_file(&self, url: &str) -> anyhow::Result<String> {
11871017
// Download the .ini file content using curl
11881018
// CRITICAL: Use self.root_store().command_runner() for Flatpak compatibility

src/fakers/command_runner.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,8 +99,11 @@ impl CommandRunner {
9999

100100
pub fn output(
101101
&self,
102-
command: Command,
102+
mut command: Command,
103103
) -> Pin<Box<dyn Future<Output = io::Result<std::process::Output>>>> {
104+
command.stdout = FdMode::Pipe;
105+
command.stderr = FdMode::Pipe;
106+
104107
let event_id = self.event_id();
105108
self.output_tracker
106109
.push(CommandRunnerEvent::Started(event_id, command.clone()));

0 commit comments

Comments
 (0)