diff --git a/proto/anki/import_export.proto b/proto/anki/import_export.proto index c2d475564..e418c5fe2 100644 --- a/proto/anki/import_export.proto +++ b/proto/anki/import_export.proto @@ -114,7 +114,8 @@ message CsvMetadataRequest { string path = 1; optional CsvMetadata.Delimiter delimiter = 2; optional int64 notetype_id = 3; - optional bool is_html = 4; + optional int64 deck_id = 4; + optional bool is_html = 5; } // Column indices are 1-based to make working with them in TS easier, where diff --git a/rslib/src/adding.rs b/rslib/src/adding.rs index fba879e15..92ce7bfb7 100644 --- a/rslib/src/adding.rs +++ b/rslib/src/adding.rs @@ -50,8 +50,8 @@ impl Collection { }) } - /// The currently selected deck, the home deck of the provided card, or the - /// default deck. + /// The currently selected deck, the home deck of the provided card if + /// current deck is filtered, or the default deck. fn get_current_deck_for_adding( &mut self, home_deck_of_reviewer_card: DeckId, diff --git a/rslib/src/backend/import_export.rs b/rslib/src/backend/import_export.rs index d4dd2e2b3..5ec32f0b4 100644 --- a/rslib/src/backend/import_export.rs +++ b/rslib/src/backend/import_export.rs @@ -88,6 +88,7 @@ impl ImportExportService for Backend { &input.path, delimiter, input.notetype_id.map(Into::into), + input.deck_id.map(Into::into), input.is_html, ) }) diff --git a/rslib/src/import_export/text/csv/metadata.rs b/rslib/src/import_export/text/csv/metadata.rs index ac2c8b219..13c4ef261 100644 --- a/rslib/src/import_export/text/csv/metadata.rs +++ b/rslib/src/import_export/text/csv/metadata.rs @@ -42,10 +42,11 @@ impl Collection { path: &str, delimiter: Option, notetype_id: Option, + deck_id: Option, is_html: Option, ) -> Result { let mut reader = open_file(path)?; - self.get_reader_metadata(&mut reader, delimiter, notetype_id, is_html) + self.get_reader_metadata(&mut reader, delimiter, notetype_id, deck_id, is_html) } fn get_reader_metadata( @@ -53,6 +54,7 @@ impl Collection { mut reader: impl Read + Seek, delimiter: Option, notetype_id: Option, + deck_id: Option, is_html: Option, ) -> Result { let mut metadata = CsvMetadata::from_config(self); @@ -62,9 +64,8 @@ impl Collection { maybe_set_fallback_is_html(&mut metadata, &records, is_html)?; set_preview(&mut metadata, &records)?; maybe_set_fallback_columns(&mut metadata)?; - self.maybe_set_fallback_notetype(&mut metadata, notetype_id)?; + self.maybe_set_notetype_and_deck(&mut metadata, notetype_id, deck_id)?; self.maybe_init_notetype_map(&mut metadata)?; - self.maybe_set_fallback_deck(&mut metadata)?; Ok(metadata) } @@ -176,37 +177,33 @@ impl Collection { } } - fn maybe_set_fallback_notetype( + /// Ensure notetype and deck are set. + /// + /// - When the UI is first loaded, both notetype and deck arguments will be + /// None. + /// - When the UI refreshes due to user changes, the currently-selected deck + /// and notetype will be provided. + /// - Metadata may already have deck and notetype set, if those directives + /// were present in the file to import. In the UI refresh case, we + /// override them with the current UI values, so that the user can adjust + /// the deck/notetype if they wish. + /// - In the initial load case, if notetype/deck were not specified in file, + /// we apply the defaults from defaults_for_adding(). + pub(crate) fn maybe_set_notetype_and_deck( &mut self, - metadata: &mut CsvMetadata, + metadata: &mut crate::pb::import_export::CsvMetadata, notetype_id: Option, + deck_id: Option, ) -> Result<()> { - if let Some(ntid) = notetype_id { - metadata.notetype = Some(CsvNotetype::new_global(ntid)); - } else if metadata.notetype.is_none() { - metadata.notetype = Some(CsvNotetype::new_global(self.fallback_notetype_id()?)); - } - Ok(()) - } - - fn maybe_set_fallback_deck(&mut self, metadata: &mut CsvMetadata) -> Result<()> { - if metadata.deck.is_none() { - metadata.deck = Some(CsvDeck::DeckId( - metadata - .notetype_id() - .and_then(|ntid| self.default_deck_for_notetype(ntid).transpose()) - .unwrap_or_else(|| { - self.get_current_deck().map(|deck| { - if deck.is_filtered() { - DeckId(1) - } else { - deck.id - } - }) - })? - .0, + let defaults = self.defaults_for_adding(DeckId(0))?; + if metadata.notetype.is_none() || notetype_id.is_some() { + metadata.notetype = Some(CsvNotetype::new_global( + notetype_id.unwrap_or(defaults.notetype_id), )); } + if metadata.deck.is_none() || deck_id.is_some() { + metadata.deck = Some(CsvDeck::DeckId(deck_id.unwrap_or(defaults.deck_id).0)); + } Ok(()) } @@ -234,18 +231,6 @@ impl Collection { } Ok(()) } - - fn fallback_notetype_id(&mut self) -> Result { - Ok(if let Some(notetype_id) = self.get_current_notetype_id() { - notetype_id - } else { - self.storage - .get_all_notetype_names()? - .first() - .or_invalid("collection has no notetypes")? - .0 - }) - } } impl CsvMetadata { @@ -524,14 +509,6 @@ impl CsvNotetype { } impl CsvMetadata { - fn notetype_id(&self) -> Option { - if let Some(CsvNotetype::GlobalNotetype(ref global)) = self.notetype { - Some(NotetypeId(global.id)) - } else { - None - } - } - pub(super) fn meta_columns(&self) -> HashSet { let mut columns = HashSet::new(); if let Some(CsvDeck::DeckColumn(deck_column)) = self.deck { @@ -579,7 +556,7 @@ mod test { metadata!($col, $csv, None) }; ($col:expr,$csv:expr, $delim:expr) => { - $col.get_reader_metadata(Cursor::new($csv.as_bytes()), $delim, None, None) + $col.get_reader_metadata(Cursor::new($csv.as_bytes()), $delim, None, None, None) .unwrap() }; } diff --git a/ts/import-csv/ImportCsvPage.svelte b/ts/import-csv/ImportCsvPage.svelte index bc6fff47f..bb786f62d 100644 --- a/ts/import-csv/ImportCsvPage.svelte +++ b/ts/import-csv/ImportCsvPage.svelte @@ -56,17 +56,20 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html deckColumn, guidColumn, ); - $: getCsvMetadata(path, delimiter, undefined, isHtml).then((meta) => { + $: getCsvMetadata(path, delimiter, undefined, undefined, isHtml).then((meta) => { columnLabels = meta.columnLabels; preview = meta.preview; }); $: if (globalNotetype?.id !== lastNotetypeId || delimiter !== lastDelimeter) { lastNotetypeId = globalNotetype?.id; lastDelimeter = delimiter; - getCsvMetadata(path, delimiter, globalNotetype?.id).then((meta) => { - globalNotetype = meta.globalNotetype ?? null; - tagsColumn = meta.tagsColumn; - }); + getCsvMetadata(path, delimiter, globalNotetype?.id, deckId || undefined).then( + (meta) => { + globalNotetype = meta.globalNotetype ?? null; + deckId = meta.deckId ?? null; + tagsColumn = meta.tagsColumn; + }, + ); } async function onImport(): Promise { diff --git a/ts/import-csv/lib.ts b/ts/import-csv/lib.ts index ea1ac9b41..9d01a561d 100644 --- a/ts/import-csv/lib.ts +++ b/ts/import-csv/lib.ts @@ -60,6 +60,7 @@ export async function getCsvMetadata( path: string, delimiter?: ImportExport.CsvMetadata.Delimiter, notetypeId?: number, + deckId?: number, isHtml?: boolean, ): Promise { return importExport.getCsvMetadata( @@ -67,6 +68,7 @@ export async function getCsvMetadata( path, delimiter, notetypeId, + deckId, isHtml, }), );