mirror of
https://github.com/storytold/lightcraft.git
synced 2026-11-01 14:07:34 -06:00
Merge PR #3403: LRC-LIB-CATALOG-SWITCH: files waiting for a library wait for a running import instead of being dropped
crates/ui-egui/src/dialogs/settings/libraries.rs: can_switch_library keeps both refusals (folder.move pick pending, files waiting to import). docs/whats-new.md: the PR's entry added under the 2026-10-30 heading.
This commit is contained in:
@@ -2410,6 +2410,7 @@
|
||||
"Dropped Files": "Přetažené soubory",
|
||||
"Import dropped files without review": "Importovat přetažené soubory bez kontroly",
|
||||
"An import is running": "Import už běží",
|
||||
"The photos are imported once it ends": "Fotky se naimportují potom",
|
||||
"An import is running. Drop photos when it has finished": "Import už běží. Fotky přetáhněte, až skončí",
|
||||
"Finish Lightroom catalog import before adding photos": "Dokončete import katalogu Lightroom, než přidáte fotky",
|
||||
"Import Thumbnail Size": "Velikost miniatur při importu"
|
||||
|
||||
@@ -3442,6 +3442,7 @@
|
||||
"Dropped Files": "Abgelegte Dateien",
|
||||
"Import dropped files without review": "Abgelegte Dateien ohne Prüfung importieren",
|
||||
"An import is running": "Ein Import läuft",
|
||||
"The photos are imported once it ends": "Die Fotos werden danach importiert",
|
||||
"An import is running. Drop photos when it has finished": "Ein Import läuft. Lege die Fotos ab, wenn er fertig ist",
|
||||
"Finish Lightroom catalog import before adding photos": "Schließe den Import des Lightroom-Katalogs ab, bevor du Fotos hinzufügst",
|
||||
"Import Thumbnail Size": "Vorschaubildgröße beim Import"
|
||||
|
||||
@@ -2360,6 +2360,7 @@
|
||||
"Dropped Files": "Archivos soltados",
|
||||
"Import dropped files without review": "Importar los archivos soltados sin revisión",
|
||||
"An import is running": "Hay una importación en curso",
|
||||
"The photos are imported once it ends": "Las fotos se importarán después",
|
||||
"An import is running. Drop photos when it has finished": "Hay una importación en curso. Suelta las fotos cuando termine",
|
||||
"Finish Lightroom catalog import before adding photos": "Termina la importación del catálogo de Lightroom antes de añadir fotos",
|
||||
"Thumbnail size": "Tamaño de las miniaturas",
|
||||
|
||||
@@ -2323,6 +2323,7 @@
|
||||
"Mask Pins": "Maskin nastat",
|
||||
"Photo Counts": "Kuvamäärät",
|
||||
"An import is running": "Tuonti on käynnissä",
|
||||
"The photos are imported once it ends": "Kuvat tuodaan sen jälkeen",
|
||||
"An import is running. Drop photos when it has finished": "Tuonti on käynnissä. Pudota kuvat, kun se on valmis",
|
||||
"Close the open dialog to drop files here": "Sulje avoin valintaikkuna pudottaaksesi tiedostot tähän",
|
||||
"Close the open dialog, then drop the files again": "Sulje avoin valintaikkuna ja pudota tiedostot sitten uudelleen",
|
||||
|
||||
@@ -2355,6 +2355,7 @@
|
||||
"Dropped Files": "Fichiers déposés",
|
||||
"Import dropped files without review": "Importer les fichiers déposés sans vérification",
|
||||
"An import is running": "Une importation est en cours",
|
||||
"The photos are imported once it ends": "Les photos seront importées ensuite",
|
||||
"An import is running. Drop photos when it has finished": "Une importation est en cours. Déposez les photos quand elle sera terminée",
|
||||
"Finish Lightroom catalog import before adding photos": "Terminez l'importation du catalogue Lightroom avant d'ajouter des photos",
|
||||
"The library is empty: the catalog's photos, collections and edits become its first.": "La bibliothèque est vide : les photos, collections et retouches du catalogue seront les premières.",
|
||||
|
||||
@@ -2335,6 +2335,7 @@
|
||||
"Mask Pins": "Pin masker",
|
||||
"Photo Counts": "Jumlah foto",
|
||||
"An import is running": "Impor sedang berjalan",
|
||||
"The photos are imported once it ends": "Foto diimpor setelah selesai",
|
||||
"An import is running. Drop photos when it has finished": "Impor sedang berjalan. Jatuhkan foto setelah selesai",
|
||||
"Close the open dialog to drop files here": "Tutup dialog yang terbuka untuk menjatuhkan file di sini",
|
||||
"Close the open dialog, then drop the files again": "Tutup dialog yang terbuka, lalu jatuhkan file lagi",
|
||||
|
||||
@@ -2363,6 +2363,7 @@
|
||||
"Dropped Files": "File rilasciati",
|
||||
"Import dropped files without review": "Importa i file rilasciati senza revisione",
|
||||
"An import is running": "Un'importazione è in corso",
|
||||
"The photos are imported once it ends": "Le foto verranno importate dopo",
|
||||
"An import is running. Drop photos when it has finished": "Un'importazione è in corso. Trascina le foto quando è finita",
|
||||
"Finish Lightroom catalog import before adding photos": "Termina l'importazione del catalogo Lightroom prima di aggiungere foto",
|
||||
"Thumbnail size": "Dimensione delle miniature",
|
||||
|
||||
@@ -2358,6 +2358,7 @@
|
||||
"Dropped Files": "ドロップしたファイル",
|
||||
"Import dropped files without review": "ドロップしたファイルを確認せずに読み込む",
|
||||
"An import is running": "読み込み中です",
|
||||
"The photos are imported once it ends": "終わりしだい写真を読み込みます",
|
||||
"An import is running. Drop photos when it has finished": "読み込み中です。完了してから写真をドロップしてください",
|
||||
"Finish Lightroom catalog import before adding photos": "写真を追加する前に Lightroom カタログの読み込みを完了してください",
|
||||
"Thumbnail size": "サムネイルのサイズ",
|
||||
|
||||
@@ -2736,6 +2736,7 @@
|
||||
"Dropped Files": "ჩაგდებული ფაილები",
|
||||
"Import dropped files without review": "ჩაგდებული ფაილების იმპორტი გადახედვის გარეშე",
|
||||
"An import is running": "იმპორტი მიმდინარეობს",
|
||||
"The photos are imported once it ends": "ფოტოები ამის შემდეგ შემოიტანება",
|
||||
"An import is running. Drop photos when it has finished": "იმპორტი მიმდინარეობს. ფოტოები ჩააგდეთ, როცა დასრულდება",
|
||||
"Finish Lightroom catalog import before adding photos": "ფოტოების დამატებამდე დაასრულეთ Lightroom-ის კატალოგის იმპორტი",
|
||||
"Thumbnail size": "მინიატურების ზომა",
|
||||
|
||||
@@ -2285,6 +2285,7 @@
|
||||
"Dropped Files": "Upuszczone pliki",
|
||||
"Import dropped files without review": "Importuj upuszczone pliki bez przeglądu",
|
||||
"An import is running": "Import jest w toku",
|
||||
"The photos are imported once it ends": "Zdjęcia zostaną zaimportowane potem",
|
||||
"An import is running. Drop photos when it has finished": "Import jest w toku. Upuść zdjęcia, gdy się zakończy",
|
||||
"Finish Lightroom catalog import before adding photos": "Zakończ import katalogu Lightrooma, zanim dodasz zdjęcia",
|
||||
"Thumbnail size": "Rozmiar miniatur",
|
||||
|
||||
@@ -2204,6 +2204,7 @@
|
||||
"Dropped Files": "Arquivos soltos",
|
||||
"Import dropped files without review": "Importar arquivos soltos sem revisão",
|
||||
"An import is running": "Uma importação está em andamento",
|
||||
"The photos are imported once it ends": "As fotos serão importadas depois",
|
||||
"An import is running. Drop photos when it has finished": "Uma importação está em andamento. Solte as fotos quando ela terminar",
|
||||
"Finish Lightroom catalog import before adding photos": "Conclua a importação do catálogo do Lightroom antes de adicionar fotos",
|
||||
"Thumbnail size": "Tamanho das miniaturas",
|
||||
|
||||
@@ -2567,6 +2567,7 @@
|
||||
"Dropped Files": "Перетащенные файлы",
|
||||
"Import dropped files without review": "Импортировать перетащенные файлы без проверки",
|
||||
"An import is running": "Импорт уже выполняется",
|
||||
"The photos are imported once it ends": "Фото будут импортированы после этого",
|
||||
"An import is running. Drop photos when it has finished": "Импорт уже выполняется. Перетащите фото, когда он закончится",
|
||||
"Finish Lightroom catalog import before adding photos": "Завершите импорт каталога Lightroom, прежде чем добавлять фото",
|
||||
"★ Favorites": "★ Избранное",
|
||||
|
||||
@@ -2425,6 +2425,7 @@
|
||||
"Mask Pins": "Masknålar",
|
||||
"Photo Counts": "Antal foton",
|
||||
"An import is running": "En import pågår",
|
||||
"The photos are imported once it ends": "Bilderna importeras efteråt",
|
||||
"An import is running. Drop photos when it has finished": "En import pågår. Släpp fotona när den är klar",
|
||||
"Close the open dialog to drop files here": "Stäng den öppna dialogrutan för att släppa filer här",
|
||||
"Close the open dialog, then drop the files again": "Stäng den öppna dialogrutan och släpp sedan filerna igen",
|
||||
|
||||
@@ -2356,6 +2356,7 @@
|
||||
"Dropped Files": "Перетягнуті файли",
|
||||
"Import dropped files without review": "Імпортувати перетягнуті файли без перегляду",
|
||||
"An import is running": "Імпорт уже виконується",
|
||||
"The photos are imported once it ends": "Фото буде імпортовано після цього",
|
||||
"An import is running. Drop photos when it has finished": "Імпорт уже виконується. Перетягніть фото, коли він завершиться",
|
||||
"Finish Lightroom catalog import before adding photos": "Завершіть імпорт каталогу Lightroom, перш ніж додавати фото",
|
||||
"Thumbnail size": "Розмір мініатюр",
|
||||
|
||||
@@ -2231,6 +2231,7 @@
|
||||
"Mask Pins": "Ghim mặt nạ",
|
||||
"Photo Counts": "Số lượng ảnh",
|
||||
"An import is running": "Đang có một lần nhập chạy",
|
||||
"The photos are imported once it ends": "Ảnh sẽ được nhập khi việc đó kết thúc",
|
||||
"An import is running. Drop photos when it has finished": "Đang có một lần nhập chạy. Hãy thả ảnh khi nó hoàn tất",
|
||||
"Close the open dialog to drop files here": "Hãy đóng hộp thoại đang mở để thả tệp vào đây",
|
||||
"Close the open dialog, then drop the files again": "Hãy đóng hộp thoại đang mở, rồi thả lại các tệp",
|
||||
|
||||
@@ -2202,6 +2202,7 @@
|
||||
"Dropped Files": "拖入的文件",
|
||||
"Import dropped files without review": "不经检查直接导入拖入的文件",
|
||||
"An import is running": "正在导入",
|
||||
"The photos are imported once it ends": "结束后即导入这些照片",
|
||||
"An import is running. Drop photos when it has finished": "正在导入。导入完成后再拖放照片",
|
||||
"Finish Lightroom catalog import before adding photos": "添加照片前,请先完成 Lightroom 目录的导入",
|
||||
"Thumbnail size": "缩略图大小",
|
||||
|
||||
@@ -2159,6 +2159,7 @@
|
||||
"Dropped Files": "拖放的檔案",
|
||||
"Import dropped files without review": "不經檢查直接匯入拖放的檔案",
|
||||
"An import is running": "正在匯入",
|
||||
"The photos are imported once it ends": "結束後即匯入這些照片",
|
||||
"An import is running. Drop photos when it has finished": "正在匯入。匯入完成後再拖放照片",
|
||||
"Finish Lightroom catalog import before adding photos": "新增照片前,請先完成 Lightroom 目錄的匯入",
|
||||
"Thumbnail size": "縮圖大小",
|
||||
|
||||
@@ -102,6 +102,7 @@ impl LightcraftApp {
|
||||
merge: merge::MergeState::default(),
|
||||
import: None,
|
||||
scan: None,
|
||||
waiting_import: Default::default(),
|
||||
sync: None,
|
||||
sync_owns_changes: false,
|
||||
sync_run: None,
|
||||
|
||||
@@ -66,6 +66,7 @@ impl LightcraftApp {
|
||||
if import_tick_allowed(ctx) {
|
||||
import::tick(self, ctx);
|
||||
}
|
||||
panels::library_problem::import_waiting(self, ctx);
|
||||
lightroom_import::tick(self, ctx);
|
||||
recheck::tick(self);
|
||||
tasks::poll(self, ctx);
|
||||
|
||||
@@ -51,6 +51,11 @@ pub struct ImportTask {
|
||||
}
|
||||
|
||||
impl ImportTask {
|
||||
/// Whether it has its row in the activity list yet (it gets one on its first tick).
|
||||
pub fn listed(&self) -> bool {
|
||||
self.guard.is_some()
|
||||
}
|
||||
|
||||
/// An import of `paths` (files or folders) with `library.import` params (without `paths`).
|
||||
pub fn new(paths: Vec<String>, params: Value, undo0: usize, browse: bool) -> Self {
|
||||
ImportTask { total: paths.len(), queue: paths, params, undo0, browse, ..Default::default() }
|
||||
|
||||
@@ -17,6 +17,10 @@ pub(crate) fn can_switch_library(app: &LightcraftApp) -> Result<(), String> {
|
||||
if app.pending_picks.iter().any(|p| p.command == "folder.move") {
|
||||
return Err("wait for Move Folder To… to finish before switching libraries".into());
|
||||
}
|
||||
// they were meant for the library open now (issue #2779)
|
||||
if !app.waiting_import.files.is_empty() {
|
||||
return Err("wait for the photos waiting to be imported before switching libraries".into());
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
@@ -298,6 +298,8 @@ pub struct LightcraftApp {
|
||||
pub import: Option<import::ImportTask>,
|
||||
/// A folder scan in progress (feeds the import review).
|
||||
pub scan: Option<import::ScanTask>,
|
||||
/// Files that waited for a library and wait for the running import or Synchronize Folder too.
|
||||
pub waiting_import: panels::library_problem::WaitingImport,
|
||||
/// A Synchronize Folder scan in progress (feeds its dialog).
|
||||
pub sync: Option<sync::SyncTask>,
|
||||
/// `session.folder_changes` came from the Synchronize Folder dialog (and goes with it).
|
||||
|
||||
@@ -331,6 +331,16 @@ pub struct Chooser {
|
||||
pub over: Option<u64>,
|
||||
}
|
||||
|
||||
/// Files that waited for a library (the command line, drops on the launch list) and found another
|
||||
/// import or a Synchronize Folder running when one opened: kept, and imported once it ends instead
|
||||
/// of being lost (issue #2779).
|
||||
#[derive(Clone, Debug, Default, PartialEq)]
|
||||
pub struct WaitingImport {
|
||||
pub files: Vec<String>,
|
||||
/// The toast already said why they wait.
|
||||
told: bool,
|
||||
}
|
||||
|
||||
/// A library that failed to open (set by the host at launch).
|
||||
#[derive(Clone, Debug, Default, PartialEq)]
|
||||
pub struct LibraryProblem {
|
||||
@@ -823,12 +833,7 @@ fn open(app: &mut LightcraftApp, path: Option<String>) {
|
||||
Ok(serde_json::Value::Null) => {} // folder dialog cancelled
|
||||
Ok(_) => {
|
||||
let files = app.library_problem.take().map(|p| p.pending_import).unwrap_or_default();
|
||||
// in the background, like files dropped on the window (issue #374)
|
||||
if !files.is_empty()
|
||||
&& let Err(e) = crate::import::start_paths(app, files)
|
||||
{
|
||||
log::warn!("import: {e}");
|
||||
}
|
||||
app.waiting_import.files.extend(files);
|
||||
}
|
||||
Err(e) => {
|
||||
// a failed switch reopens the library that was open behind the launch list
|
||||
@@ -860,6 +865,27 @@ fn open(app: &mut LightcraftApp, path: Option<String>) {
|
||||
}
|
||||
}
|
||||
|
||||
/// Import the files that waited for a library, in the background like files dropped on the window
|
||||
/// (issue #374). While another import or a Synchronize Folder runs they keep waiting, and a toast
|
||||
/// says why once (issue #2779). Called from the background logic, so they go in as soon as that
|
||||
/// ends even while the window is hidden.
|
||||
pub(crate) fn import_waiting(app: &mut LightcraftApp, ctx: &egui::Context) {
|
||||
if app.waiting_import.files.is_empty() {
|
||||
return;
|
||||
}
|
||||
let files = std::mem::take(&mut app.waiting_import.files);
|
||||
match crate::import::start_paths(app, files.clone()) {
|
||||
Ok(_) => app.waiting_import = WaitingImport::default(),
|
||||
Err(e) => {
|
||||
if !app.waiting_import.told {
|
||||
app.toast(ctx, format!("{e}. {}", crate::i18n::tr("The photos are imported once it ends")));
|
||||
}
|
||||
app.waiting_import = WaitingImport { files, told: true };
|
||||
ctx.request_repaint_after(std::time::Duration::from_millis(250));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Clear the problem once a library is open (e.g. through Settings → Open Library…) — another
|
||||
/// one than the launch list was opened over; offer the list when Alt/Option is held as the window
|
||||
/// appears; and the list's keys.
|
||||
@@ -871,11 +897,7 @@ pub fn logic(app: &mut LightcraftApp, ctx: &egui::Context) {
|
||||
// files from the command line go into the library opened another way (New Library…,
|
||||
// File ▸ Open Library…), as through the window's own buttons
|
||||
let files = app.library_problem.take().map(|p| p.pending_import).unwrap_or_default();
|
||||
if !files.is_empty()
|
||||
&& let Err(e) = crate::import::start_paths(app, files)
|
||||
{
|
||||
log::warn!("import: {e}");
|
||||
}
|
||||
app.waiting_import.files.extend(files);
|
||||
}
|
||||
let batch_in_flight = app.synthetic_in_flight;
|
||||
alt_at_launch(app, ctx);
|
||||
@@ -1155,12 +1177,7 @@ fn act(app: &mut LightcraftApp, choice: Choice) {
|
||||
p.dismissed = true;
|
||||
}
|
||||
let files = app.library_problem.as_mut().map(|p| std::mem::take(&mut p.pending_import)).unwrap_or_default();
|
||||
// in the background, like files dropped on the window (issue #374)
|
||||
if !files.is_empty()
|
||||
&& let Err(e) = crate::import::start_paths(app, files)
|
||||
{
|
||||
log::warn!("import: {e}");
|
||||
}
|
||||
app.waiting_import.files.extend(files);
|
||||
}
|
||||
Choice::Quit => app.ui.quit = true,
|
||||
}
|
||||
|
||||
@@ -65,9 +65,15 @@ pub fn may_close(app: &mut LightcraftApp) -> bool {
|
||||
}
|
||||
// before the library is touched: a Cancel here keeps working with it
|
||||
let running = app.session.activity.running_cancellable();
|
||||
if !running.is_empty() {
|
||||
let names = running.iter().map(|t| crate::i18n::tr(&t.label)).collect::<Vec<_>>().join(", ");
|
||||
app.quit_prompt = Some(QuitPrompt::Tasks(tasks_message(running.len(), &names)));
|
||||
let mut names = running.iter().map(|t| crate::i18n::tr(&t.label)).collect::<Vec<_>>();
|
||||
// an import handed its files this tick (files that waited for a library, #2779) is listed only
|
||||
// from its first tick on, and files still waiting will be imported too
|
||||
let unlisted = app.import.as_ref().is_some_and(|t| !t.listed()) || !app.waiting_import.files.is_empty();
|
||||
if unlisted && !running.iter().any(|t| t.kind == "import") {
|
||||
names.push(crate::i18n::tr("Importing"));
|
||||
}
|
||||
if !names.is_empty() {
|
||||
app.quit_prompt = Some(QuitPrompt::Tasks(tasks_message(names.len(), &names.join(", "))));
|
||||
return false;
|
||||
}
|
||||
if app.session.unsaved().is_none() {
|
||||
@@ -536,6 +542,12 @@ pub fn show(app: &mut LightcraftApp, ctx: &egui::Context) {
|
||||
app.quit_prompt = None;
|
||||
if buttons.get(i).is_some_and(|(_, _, choice)| *choice == Choice::QuitAnyway) {
|
||||
app.session.activity.cancel_all();
|
||||
// files waiting for an import, and one handed its files but not started, are given up
|
||||
// too, or `may_close` would ask about them again
|
||||
app.waiting_import = Default::default();
|
||||
if app.import.as_ref().is_some_and(|t| !t.listed()) {
|
||||
app.import = None;
|
||||
}
|
||||
// unsaved changes still get their own question
|
||||
if may_close(app) {
|
||||
app.ui.quit = true;
|
||||
|
||||
@@ -172,6 +172,35 @@ fn quit_with_a_running_task_asks_and_quit_anyway_cancels() {
|
||||
assert!(g.is_cancelled() && h.quit_requested());
|
||||
}
|
||||
|
||||
/// Files waiting for a running import or Synchronize Folder (issue #2779) make quitting ask, and
|
||||
/// Quit Anyway gives them up instead of asking about them again and again.
|
||||
#[test]
|
||||
fn quit_anyway_gives_up_files_waiting_to_be_imported() {
|
||||
let dir = std::env::temp_dir().join(format!("lightcraft-quit-waiting-{}", std::process::id()));
|
||||
let _ = std::fs::remove_dir_all(&dir);
|
||||
std::fs::create_dir_all(&dir).unwrap();
|
||||
for i in 0..1000u32 {
|
||||
let img = lightcraft_raster::Rgba8 { width: 8, height: 8, data: vec![[(i % 251) as u8, (i / 251) as u8, 9, 255]; 64] };
|
||||
let png = lightcraft_codecs::encode_png(&lightcraft_codecs::EncodeImage::rgba8(&img), &lightcraft_codecs::EncodeMeta::default()).unwrap();
|
||||
std::fs::write(dir.join(format!("{i}.png")), png).unwrap();
|
||||
}
|
||||
let mut h = demo();
|
||||
// the file waits behind an import that is still running
|
||||
crate::import::start_paths(&mut h.app, vec![dir.to_string_lossy().to_string()]).unwrap();
|
||||
h.app.waiting_import.files = vec![dir.join("0.png").to_string_lossy().to_string()];
|
||||
wait_visible(&mut h);
|
||||
assert!(h.app.import.is_some() && !h.app.waiting_import.files.is_empty(), "still waiting");
|
||||
assert!(!crate::panels::notices::may_close(&mut h.app));
|
||||
assert!(matches!(h.app.quit_prompt, Some(crate::QuitPrompt::Tasks(_))), "{:?}", h.app.quit_prompt);
|
||||
h.step();
|
||||
assert!(h.app.import.is_some() && !h.app.waiting_import.files.is_empty(), "still waiting at the click");
|
||||
click(&mut h, "button:quitAnyway");
|
||||
assert!(h.app.quit_prompt.is_none(), "not asked again: {:?}", h.app.quit_prompt);
|
||||
assert!(h.quit_requested());
|
||||
assert!(h.app.waiting_import.files.is_empty());
|
||||
let _ = std::fs::remove_dir_all(&dir);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn quit_cancel_keeps_the_task_running() {
|
||||
let mut h = demo();
|
||||
|
||||
@@ -804,3 +804,78 @@ fn typing_reaches_the_launch_lists_own_new_library_prompt() {
|
||||
assert_eq!(command, "app.newLibrary");
|
||||
assert_eq!(value, "Fresh", "typing reaches the launch list's own text prompt");
|
||||
}
|
||||
|
||||
/// Issue #2779: files waiting for a library (the command line, drops on the launch list) that
|
||||
/// find another import running when a library opens are kept, with a toast saying why, and
|
||||
/// imported once that import ends instead of being dropped.
|
||||
#[test]
|
||||
fn waiting_files_wait_for_a_running_import_instead_of_being_dropped() {
|
||||
let dir = std::env::temp_dir().join(format!("lightcraft-waiting-import-{}", std::process::id()));
|
||||
let _ = std::fs::remove_dir_all(&dir);
|
||||
std::fs::create_dir_all(&dir).unwrap();
|
||||
let png = |v: u8| {
|
||||
let img = lightcraft_raster::Rgba8 { width: 8, height: 8, data: vec![[v, 3, 9, 255]; 64] };
|
||||
lightcraft_codecs::encode_png(&lightcraft_codecs::EncodeImage::rgba8(&img), &lightcraft_codecs::EncodeMeta::default()).unwrap()
|
||||
};
|
||||
// enough photos that the first import is still running two frames later
|
||||
let busy = dir.join("busy");
|
||||
std::fs::create_dir_all(&busy).unwrap();
|
||||
for i in 0..100u8 {
|
||||
std::fs::write(busy.join(format!("{i}.png")), png(i)).unwrap();
|
||||
}
|
||||
let waiting = dir.join("waiting.png");
|
||||
std::fs::write(&waiting, png(200)).unwrap();
|
||||
|
||||
let app = LightcraftApp::new(lightcraft_engine::Session::new().with_fs(), Services { png: None, ..Default::default() });
|
||||
let mut h = Headless::new(app, [1300.0, 900.0], 1.0);
|
||||
let lib = dir.join("lib");
|
||||
h.app.library_problem = Some(LibraryProblem {
|
||||
pending_import: vec![waiting.to_string_lossy().to_string()],
|
||||
..LibraryProblem::new(lib.to_string_lossy(), "a test error")
|
||||
});
|
||||
h.step();
|
||||
// a library opens another way (File ▸ Open Library…) while an import is running
|
||||
h.app.session.open_library(&lib, false).unwrap();
|
||||
crate::import::start_paths(&mut h.app, vec![busy.to_string_lossy().to_string()]).unwrap();
|
||||
// the UI pass takes the files; the next background tick tries to import them
|
||||
h.step();
|
||||
h.step();
|
||||
assert!(h.app.library_problem.is_none(), "the problem is over");
|
||||
assert_eq!(h.app.waiting_import.files, vec![waiting.to_string_lossy().to_string()], "the waiting file is kept");
|
||||
let toast = h.app.ui.toast.as_ref().map(|t| t.0.clone()).unwrap_or_default();
|
||||
assert!(toast.contains("An import is running") && toast.contains("once it ends"), "{toast}");
|
||||
// the files belong to this library: no switch while they wait, also when what they wait for
|
||||
// is a Synchronize Folder, which doesn't hold switching back by itself (the import set aside)
|
||||
let running = h.app.import.take();
|
||||
let r = h.app.run("app.openLibrary", json!({"path": dir.join("other").to_string_lossy()}));
|
||||
h.app.import = running;
|
||||
assert!(r.as_ref().is_err_and(|e| e.contains("waiting")), "{r:?}");
|
||||
assert_eq!(h.app.session.library.as_ref().map(|l| l.dir.clone()), Some(lib.clone()));
|
||||
|
||||
// quitting while the file waits asks first
|
||||
assert!(!crate::panels::notices::may_close(&mut h.app));
|
||||
h.app.quit_prompt = None;
|
||||
|
||||
// the window is hidden from here on: only the background logic runs. The tick that hands the
|
||||
// file to a new import (once the first one ends) leaves that import unlisted until its own
|
||||
// first tick, and quitting right then still asks
|
||||
let t0 = std::time::Instant::now();
|
||||
while !h.app.waiting_import.files.is_empty() && t0.elapsed() < T {
|
||||
let ctx = h.view.ctx.clone();
|
||||
h.app.logic(&ctx);
|
||||
std::thread::sleep(Duration::from_millis(2));
|
||||
}
|
||||
assert!(h.app.waiting_import.files.is_empty(), "the file was handed to a new import");
|
||||
assert!(h.app.import.as_ref().is_some_and(|t| !t.listed()), "not listed before its first tick");
|
||||
assert!(!crate::panels::notices::may_close(&mut h.app), "quitting asks about the import just handed over");
|
||||
h.app.quit_prompt = None;
|
||||
while !(h.app.import.is_none() && h.app.session.catalog.len() == 101) && t0.elapsed() < T {
|
||||
let ctx = h.view.ctx.clone();
|
||||
h.app.logic(&ctx);
|
||||
std::thread::sleep(Duration::from_millis(2));
|
||||
}
|
||||
assert_eq!(h.app.session.catalog.len(), 101, "both imports went in while hidden");
|
||||
assert!(h.app.waiting_import.files.is_empty());
|
||||
let _ = h.app.session.close_library();
|
||||
let _ = std::fs::remove_dir_all(&dir);
|
||||
}
|
||||
|
||||
@@ -9,6 +9,11 @@
|
||||
ends where that block begins, so these photos import, show and build previews. A JPEG that is really cut short is
|
||||
still refused.
|
||||
|
||||
### Photos waiting for a library wait for a running import too
|
||||
- Photos given on the command line or dropped on the launch list are imported once a library opens. If another import
|
||||
or a Synchronize Folder is still running then, they now wait for it to end instead of being dropped, and a message
|
||||
says so (issue #2779).
|
||||
|
||||
## 2026-10-29
|
||||
|
||||
### Move Folder To… no longer freezes the window
|
||||
|
||||
Reference in New Issue
Block a user