- Replace featured section Stack with AdwCarousel + indicator dots - Convert category grid to horizontal scrollable filter chips - Add grid/list view toggle for catalog with compact row layout - Add quick launch button on library list rows - Add stale catalog banner when data is older than 7 days - Add command palette (Ctrl+K) for quick app search and launch - Show specific app names in update notifications - Add per-app auto-update toggle (skip updates switch) - Add keyboard shortcut hints to button tooltips - Add source trust badges (AppImageHub/Community) on catalog tiles - Add undo-based uninstall with toast and record restoration - Add type-to-search in library view - Use human-readable catalog source labels - Show Launch button for installed apps in catalog detail - Replace external browser link with inline AppImage explainer dialog
98 lines
3.0 KiB
Rust
98 lines
3.0 KiB
Rust
use adw::prelude::*;
|
|
use std::rc::Rc;
|
|
|
|
use crate::core::database::Database;
|
|
use crate::core::launcher;
|
|
use crate::i18n::i18n;
|
|
|
|
/// Show a first-run permission summary before launching an AppImage.
|
|
/// Returns a dialog that the caller should present. The `on_proceed` callback
|
|
/// is called if the user chooses to continue.
|
|
pub fn show_permission_dialog(
|
|
parent: &impl IsA<gtk::Widget>,
|
|
app_name: &str,
|
|
record_id: i64,
|
|
db: &Rc<Database>,
|
|
on_proceed: impl Fn() + 'static,
|
|
) {
|
|
let dialog = adw::AlertDialog::builder()
|
|
.heading(&format!("Launch {}?", app_name))
|
|
.body(&i18n(
|
|
"This AppImage will run with your full user permissions. \
|
|
It can access your files, network, and desktop.",
|
|
))
|
|
.build();
|
|
|
|
let extra = gtk::Box::builder()
|
|
.orientation(gtk::Orientation::Vertical)
|
|
.spacing(8)
|
|
.margin_top(12)
|
|
.build();
|
|
|
|
let access_label = gtk::Label::builder()
|
|
.label(&i18n("This app will have access to:"))
|
|
.xalign(0.0)
|
|
.build();
|
|
access_label.add_css_class("heading");
|
|
extra.append(&access_label);
|
|
|
|
let items = [
|
|
"Your files and folders",
|
|
"Internet and network access",
|
|
"Your screen and windows",
|
|
"Background system services",
|
|
];
|
|
for item in &items {
|
|
let label = gtk::Label::builder()
|
|
.label(&format!(" - {}", i18n(item)))
|
|
.xalign(0.0)
|
|
.build();
|
|
extra.append(&label);
|
|
}
|
|
|
|
// Explanation paragraph
|
|
let explain = gtk::Label::builder()
|
|
.label(&i18n(
|
|
"AppImages run like regular programs on your computer. Unlike phone apps, \
|
|
desktop apps typically have full access to your files and system. This is normal.",
|
|
))
|
|
.wrap(true)
|
|
.xalign(0.0)
|
|
.margin_top(8)
|
|
.build();
|
|
explain.add_css_class("caption");
|
|
explain.add_css_class("dim-label");
|
|
extra.append(&explain);
|
|
|
|
// Show firejail option if available
|
|
if launcher::has_firejail() {
|
|
let sandbox_note = gtk::Label::builder()
|
|
.label(&i18n(
|
|
"You can restrict this app's access later in Details > Security Restrictions.",
|
|
))
|
|
.wrap(true)
|
|
.xalign(0.0)
|
|
.margin_top(4)
|
|
.build();
|
|
sandbox_note.add_css_class("dim-label");
|
|
extra.append(&sandbox_note);
|
|
}
|
|
|
|
dialog.set_extra_child(Some(&extra));
|
|
|
|
dialog.add_response("cancel", &i18n("Cancel"));
|
|
dialog.add_response("proceed", &i18n("Launch"));
|
|
dialog.set_response_appearance("proceed", adw::ResponseAppearance::Suggested);
|
|
dialog.set_default_response(Some("proceed"));
|
|
dialog.set_close_response("cancel");
|
|
|
|
let db_ref = db.clone();
|
|
dialog.connect_response(Some("proceed"), move |_dlg, _response| {
|
|
// Mark as prompted so we don't show again
|
|
db_ref.set_first_run_prompted(record_id, true).ok();
|
|
on_proceed();
|
|
});
|
|
|
|
dialog.present(Some(parent));
|
|
}
|