use super::{
confirm::{confirm_collisions_then, count_pending_collisions},
folder_memory::{
last_import_directory, remember_import_directory, reset_import_progress,
spawn_save_folder_rescan,
},
pipeline::{ImportOutcome, import_path},
};
use crate::{
LibraryModel, MainWindow, bridge::library::source::LibrarySource, gui::show_toast,
settings::SettingsPersister,
};
use indicatrix_vault::db::sqlite::Database;
use slint::{ComponentHandle, ModelRc, VecModel, Weak};
use std::{
path::PathBuf,
sync::{Arc, Mutex},
thread,
};
fn spawn_import(
ui_weak: Weak<MainWindow>,
db: Arc<Mutex<Database>>,
source: Arc<Mutex<LibrarySource>>,
path: PathBuf,
recurse: bool,
) {
thread::spawn(move || {
struct BusyGuard(Weak<MainWindow>);
impl Drop for BusyGuard {
fn drop(&mut self) {
let ui_weak = self.0.clone();
let _ = ui_weak.upgrade_in_event_loop(move |ui| {
ui.global::<LibraryModel>().set_is_busy(false);
});
}
}
let _busy_guard = BusyGuard(ui_weak.clone());
let progress_ui_weak = ui_weak.clone();
let ImportOutcome {
summary: message,
imported_ids,
had_failures,
had_collision,
had_notes,
} = import_path(&db, &path, recurse, move |done, total| {
let _ = progress_ui_weak.upgrade_in_event_loop(move |ui| {
ui.global::<LibraryModel>().set_import_done(done as i32);
ui.global::<LibraryModel>().set_import_total(total as i32);
ui.global::<LibraryModel>()
.set_import_progress(done as f32 / total as f32);
ui.global::<LibraryModel>()
.set_status_message(format!("Importing {done} / {total}...").into());
});
});
let _ = ui_weak.upgrade_in_event_loop(move |ui| {
ui.global::<LibraryModel>()
.set_import_result_text(message.clone().into());
ui.global::<LibraryModel>()
.set_status_message(message.clone().into());
let toast_kind = if had_failures { "error" } else { "success" };
show_toast(&ui, &message, toast_kind);
ui.global::<LibraryModel>()
.set_import_should_stay_open(had_failures || had_collision || had_notes);
let imported_id_items: Vec<i32> = imported_ids
.iter()
.filter_map(|&id| i32::try_from(id).ok())
.collect();
ui.global::<LibraryModel>().set_id_filter_label("".into());
if imported_id_items.len() > 1 {
ui.global::<LibraryModel>()
.set_recent_import_filter(ModelRc::new(VecModel::from(imported_id_items)));
} else {
ui.global::<LibraryModel>()
.set_recent_import_filter(ModelRc::new(VecModel::from(Vec::<i32>::new())));
}
super::super::helpers::refresh_after_library_change(&ui, &db, &source);
if let [only_id] = imported_ids.as_slice()
&& let Ok(id) = i32::try_from(*only_id)
{
ui.global::<LibraryModel>().invoke_select_diagram(id);
}
crate::gui::batch::preview::offer_import_previews(&ui, &imported_ids);
});
});
}
pub fn setup_import_callback(
ui: &MainWindow,
db: &Arc<Mutex<Database>>,
source: &Arc<Mutex<LibrarySource>>,
settings_store: &Arc<SettingsPersister>,
) {
let db_file = Arc::clone(db);
let source_file = Arc::clone(source);
let settings_file = Arc::clone(settings_store);
let ui_weak_file = ui.as_weak();
ui.global::<LibraryModel>().on_pick_asc_file(move || {
let Some(ui) = ui_weak_file.upgrade() else {
return;
};
let db_file = Arc::clone(&db_file);
let source_file = Arc::clone(&source_file);
let settings_file = Arc::clone(&settings_file);
let ui_weak_file = ui_weak_file.clone();
crate::gui::pickers::pick(
&ui,
crate::gui::pickers::PickerRequest {
kind: crate::gui::pickers::PickerKind::OpenFile,
title: None,
filters: vec![crate::gui::pickers::PickerFilter {
label: "Faceting design (.asc, .gem, .gcs)".to_string(),
extensions: vec!["asc".to_string(), "gem".to_string(), "gcs".to_string()],
}],
default_file_name: None,
starting_dir: last_import_directory(&settings_file),
},
move |ui, path| {
let Some(path) = path else {
return;
};
remember_import_directory(&settings_file, path.parent());
let (collisions, total) = count_pending_collisions(&db_file, &path, false);
let db_file = Arc::clone(&db_file);
let source_file = Arc::clone(&source_file);
confirm_collisions_then(ui, collisions, total, move |ui| {
begin_import(ui, &db_file, &source_file, ui_weak_file, path, false);
});
},
);
});
let db_folder = Arc::clone(db);
let source_folder = Arc::clone(source);
let settings_folder = Arc::clone(settings_store);
let ui_weak_folder = ui.as_weak();
ui.global::<LibraryModel>()
.on_pick_asc_folder(move |recurse: bool| {
let Some(ui) = ui_weak_folder.upgrade() else {
return;
};
let db_folder = Arc::clone(&db_folder);
let source_folder = Arc::clone(&source_folder);
let settings_folder = Arc::clone(&settings_folder);
let ui_weak_folder = ui_weak_folder.clone();
crate::gui::pickers::pick(
&ui,
crate::gui::pickers::PickerRequest {
kind: crate::gui::pickers::PickerKind::PickFolder,
title: None,
filters: Vec::new(),
default_file_name: None,
starting_dir: last_import_directory(&settings_folder),
},
move |ui, path| {
let Some(path) = path else {
return;
};
remember_import_directory(&settings_folder, Some(path.as_path()));
let (collisions, total) = count_pending_collisions(&db_folder, &path, recurse);
let db_folder = Arc::clone(&db_folder);
let source_folder = Arc::clone(&source_folder);
confirm_collisions_then(ui, collisions, total, move |ui| {
begin_import(
ui,
&db_folder,
&source_folder,
ui_weak_folder,
path,
recurse,
);
});
},
);
});
spawn_save_folder_rescan(ui.as_weak(), Arc::clone(db), Arc::clone(settings_store));
}
fn begin_import(
ui: &MainWindow,
db: &Arc<Mutex<Database>>,
source: &Arc<Mutex<LibrarySource>>,
ui_weak: Weak<MainWindow>,
path: PathBuf,
recurse: bool,
) {
ui.global::<LibraryModel>().set_is_busy(true);
ui.global::<LibraryModel>()
.set_status_message("Importing...".into());
reset_import_progress(ui);
spawn_import(ui_weak, Arc::clone(db), Arc::clone(source), path, recurse);
}