From 5f19ddfa4a157f899abf27f2c4598fccca1f0d8b Mon Sep 17 00:00:00 2001 From: Garrett Mills Date: Fri, 23 Oct 2026 22:01:33 -0500 Subject: [PATCH] Read picked import files through the fs plugin so Android content URIs work On Android the open dialog returns a content:// URI, which std::fs can't open, so ManaBox/library and deck imports failed with "no such file or directory". Read them via tauri-plugin-fs, which resolves content URIs and falls through to plain paths on desktop. Closes #24 --- Cargo.lock | 1 + src-tauri/Cargo.toml | 3 +++ src-tauri/src/commands.rs | 14 +++++++++++--- src-tauri/src/lib.rs | 1 + 4 files changed, 16 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4df09d8..0c02db1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2448,6 +2448,7 @@ dependencies = [ "tauri-build", "tauri-plugin-deep-link", "tauri-plugin-dialog", + "tauri-plugin-fs", "tauri-plugin-opener", "url", ] diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index dde0ee8..5613a8c 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -16,6 +16,9 @@ tauri-build = { version = "2", features = [] } [dependencies] tauri = { version = "2", features = [] } tauri-plugin-dialog = "2" +# Reads the files the dialog picks: on Android those are content:// URIs, which +# std::fs can't open. +tauri-plugin-fs = "2" serde = { version = "1", features = ["derive"] } serde_json = "1" mtgc-core = { path = "../crates/mtgc-core" } diff --git a/src-tauri/src/commands.rs b/src-tauri/src/commands.rs index 0398b19..16a1827 100644 --- a/src-tauri/src/commands.rs +++ b/src-tauri/src/commands.rs @@ -11,6 +11,7 @@ use mtgc_core::{ }; use serde::Serialize; use tauri::{AppHandle, Emitter, State}; +use tauri_plugin_fs::{FilePath, FsExt}; use crate::state::AppState; @@ -416,10 +417,17 @@ pub fn export_deck(db: Db, deck_id: i64, path: String) -> Res<()> { std::fs::write(&path, text).map_err(|e| e.to_string()) } +/// Read a text file the open dialog picked. On Android that's a content:// URI +/// rather than a filesystem path, so it has to go through the fs plugin. +fn read_picked_text(app: &AppHandle, path: &str) -> Res { + let path: FilePath = path.parse().unwrap(); + app.fs().read_to_string(path).map_err(|e| e.to_string()) +} + #[tauri::command] -pub fn import_deck(db: Db, path: String, name: String) -> Res { +pub fn import_deck(app: AppHandle, db: Db, path: String, name: String) -> Res { + let text = read_picked_text(&app, &path)?; let s = db.lock().unwrap(); - let text = std::fs::read_to_string(&path).map_err(|e| e.to_string())?; let (deck, unresolved) = s.catalog()?.import_deck(&name, &text).map_err(|e| e.to_string())?; Ok(DeckImportResult { deck, unresolved }) } @@ -452,7 +460,7 @@ pub async fn import_library_cards( location: Option, ) -> Res { crate::sync::blocking(move || { - let text = std::fs::read_to_string(&path).map_err(|e| e.to_string())?; + let text = read_picked_text(&app, &path)?; let s = crate::sync::lock(&app); // At most one event per percent, so a big file doesn't flood the UI. let mut last_percent = None; diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index c5e3109..b0f5ae5 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -37,6 +37,7 @@ pub(crate) fn logged(what: &str, result: Result) -> Result