Overhaul card-data ingest: filter art cards, segmented cancellable progress
- Exclude Scryfall Art Series cards (layout "art_series") from ingest — they aren't playable cards and were polluting search results. - Replace the coarse per-stage progress text with a byte-accurate, weighted multi-segment progress bar (Index / Card data / Printings / Indexing) that fills as one continuous bar instead of swapping per stage; segment weights are computed from the real download sizes reported by Scryfall's bulk-data index (the download responses themselves are chunked with no Content-Length to rely on). - Add a Cancel button, wired through a new CancelToken checked once per parsed record so cancellation lands almost instantly. - Fix a related correctness bug this surfaced: ingest used to delete the existing reference.db before writing its replacement, so a cancelled or crashed update left the app with no card database at all. It now builds into a temp file and only atomically replaces reference.db on full success. - Split the topbar's single "Choose library…" action into separate "Open Library…" (existing file) and "New Library…" (save-dialog for a new file) buttons. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -50,22 +50,39 @@ pub fn set_library_path(db: Db, path: String) -> Res<Status> {
|
||||
#[tauri::command]
|
||||
pub async fn update_card_data(app: AppHandle, db: Db<'_>) -> Res<Status> {
|
||||
let reference_path = db.lock().unwrap().reference_path.clone();
|
||||
let cancel = mtgc_core::ingest::CancelToken::new();
|
||||
db.lock().unwrap().ingest_cancel = Some(cancel.clone());
|
||||
let app_for_progress = app.clone();
|
||||
|
||||
tauri::async_runtime::spawn_blocking(move || {
|
||||
mtgc_core::ingest::build_reference_db(&reference_path, |msg| {
|
||||
let _ = app_for_progress.emit("ingest-progress", msg.to_string());
|
||||
})
|
||||
let result = tauri::async_runtime::spawn_blocking({
|
||||
let cancel = cancel.clone();
|
||||
move || {
|
||||
mtgc_core::ingest::build_reference_db(&reference_path, &cancel, |p| {
|
||||
let _ = app_for_progress.emit("ingest-progress", p);
|
||||
})
|
||||
}
|
||||
})
|
||||
.await
|
||||
.map_err(|e| e.to_string())?
|
||||
.map_err(|e| e.to_string())?;
|
||||
|
||||
db.lock().unwrap().ingest_cancel = None;
|
||||
result.map_err(|e| e.to_string())?;
|
||||
|
||||
let mut s = db.lock().unwrap();
|
||||
s.reopen()?;
|
||||
Ok(status_of(&s))
|
||||
}
|
||||
|
||||
/// Cancel an in-progress `update_card_data`, if one is running. No-op otherwise.
|
||||
#[tauri::command]
|
||||
pub fn cancel_update_card_data(db: Db) -> Res<()> {
|
||||
let s = db.lock().unwrap();
|
||||
if let Some(c) = &s.ingest_cancel {
|
||||
c.cancel();
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub fn search_cards(db: Db, query: String, limit: usize) -> Res<Vec<CardRef>> {
|
||||
let s = db.lock().unwrap();
|
||||
|
||||
@@ -24,6 +24,7 @@ pub fn run() {
|
||||
commands::get_status,
|
||||
commands::set_library_path,
|
||||
commands::update_card_data,
|
||||
commands::cancel_update_card_data,
|
||||
commands::search_cards,
|
||||
commands::add_owned,
|
||||
commands::set_owned,
|
||||
|
||||
@@ -10,6 +10,9 @@ pub struct AppState {
|
||||
pub reference_path: PathBuf,
|
||||
pub catalog: Option<Catalog>,
|
||||
pub index: Option<NameIndex>,
|
||||
/// Set while `update_card_data` is running; `cancel_update_card_data`
|
||||
/// flips it to abort the in-progress ingest.
|
||||
pub ingest_cancel: Option<mtgc_core::ingest::CancelToken>,
|
||||
}
|
||||
|
||||
impl AppState {
|
||||
@@ -21,6 +24,7 @@ impl AppState {
|
||||
reference_path: data_dir.join("reference.db"),
|
||||
catalog: None,
|
||||
index: None,
|
||||
ingest_cancel: None,
|
||||
};
|
||||
// Best-effort open; failures just leave the catalog unloaded.
|
||||
let _ = s.reopen();
|
||||
|
||||
Reference in New Issue
Block a user