mirror of
https://github.com/ankitects/anki.git
synced 2026-01-07 02:53:54 -05:00
Merge branch 'main' into rust
This commit is contained in:
commit
f873e68e31
14 changed files with 75 additions and 64 deletions
|
|
@ -255,6 +255,7 @@ Ranjit Odedra <ranjitodedra.dev@gmail.com>
|
||||||
Eltaurus <https://github.com/Eltaurus-Lt>
|
Eltaurus <https://github.com/Eltaurus-Lt>
|
||||||
jariji
|
jariji
|
||||||
Francisco Esteva <fr.esteva@duocuc.cl>
|
Francisco Esteva <fr.esteva@duocuc.cl>
|
||||||
|
SelfishPig <https://github.com/SelfishPig>
|
||||||
|
|
||||||
********************
|
********************
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -234,7 +234,7 @@ class DeckBrowser:
|
||||||
if node.collapsed:
|
if node.collapsed:
|
||||||
prefix = "+"
|
prefix = "+"
|
||||||
else:
|
else:
|
||||||
prefix = "-"
|
prefix = "−"
|
||||||
|
|
||||||
def indent() -> str:
|
def indent() -> str:
|
||||||
return " " * 6 * (node.level - 1)
|
return " " * 6 * (node.level - 1)
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ from markdown import markdown
|
||||||
|
|
||||||
import aqt
|
import aqt
|
||||||
from anki.collection import HelpPage
|
from anki.collection import HelpPage
|
||||||
from anki.errors import BackendError, Interrupted
|
from anki.errors import BackendError, CardTypeError, Interrupted
|
||||||
from anki.utils import is_win
|
from anki.utils import is_win
|
||||||
from aqt.addons import AddonManager, AddonMeta
|
from aqt.addons import AddonManager, AddonMeta
|
||||||
from aqt.qt import *
|
from aqt.qt import *
|
||||||
|
|
@ -36,6 +36,14 @@ def show_exception(*, parent: QWidget, exception: Exception) -> None:
|
||||||
global _mbox
|
global _mbox
|
||||||
error_lines = []
|
error_lines = []
|
||||||
help_page = HelpPage.TROUBLESHOOTING
|
help_page = HelpPage.TROUBLESHOOTING
|
||||||
|
|
||||||
|
# default to PlainText
|
||||||
|
text_format = Qt.TextFormat.PlainText
|
||||||
|
|
||||||
|
# set CardTypeError messages as rich text to allow HTML formatting
|
||||||
|
if isinstance(exception, CardTypeError):
|
||||||
|
text_format = Qt.TextFormat.RichText
|
||||||
|
|
||||||
if isinstance(exception, BackendError):
|
if isinstance(exception, BackendError):
|
||||||
if exception.context:
|
if exception.context:
|
||||||
error_lines.append(exception.context)
|
error_lines.append(exception.context)
|
||||||
|
|
@ -51,7 +59,7 @@ def show_exception(*, parent: QWidget, exception: Exception) -> None:
|
||||||
)
|
)
|
||||||
error_text = "\n".join(error_lines)
|
error_text = "\n".join(error_lines)
|
||||||
print(error_lines)
|
print(error_lines)
|
||||||
_mbox = _init_message_box(str(exception), error_text, help_page)
|
_mbox = _init_message_box(str(exception), error_text, help_page, text_format)
|
||||||
_mbox.show()
|
_mbox.show()
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -171,7 +179,10 @@ if not os.environ.get("DEBUG"):
|
||||||
|
|
||||||
|
|
||||||
def _init_message_box(
|
def _init_message_box(
|
||||||
user_text: str, debug_text: str, help_page=HelpPage.TROUBLESHOOTING
|
user_text: str,
|
||||||
|
debug_text: str,
|
||||||
|
help_page=HelpPage.TROUBLESHOOTING,
|
||||||
|
text_format=Qt.TextFormat.PlainText,
|
||||||
):
|
):
|
||||||
global _mbox
|
global _mbox
|
||||||
|
|
||||||
|
|
@ -179,7 +190,7 @@ def _init_message_box(
|
||||||
_mbox.setWindowTitle("Anki")
|
_mbox.setWindowTitle("Anki")
|
||||||
_mbox.setText(user_text)
|
_mbox.setText(user_text)
|
||||||
_mbox.setIcon(QMessageBox.Icon.Warning)
|
_mbox.setIcon(QMessageBox.Icon.Warning)
|
||||||
_mbox.setTextFormat(Qt.TextFormat.PlainText)
|
_mbox.setTextFormat(text_format)
|
||||||
|
|
||||||
def show_help():
|
def show_help():
|
||||||
openHelp(help_page)
|
openHelp(help_page)
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,6 @@ mod python;
|
||||||
mod typescript;
|
mod typescript;
|
||||||
mod write_strings;
|
mod write_strings;
|
||||||
|
|
||||||
use std::env;
|
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use anki_io::create_dir_all;
|
use anki_io::create_dir_all;
|
||||||
|
|
@ -32,8 +31,7 @@ fn main() -> Result<()> {
|
||||||
python::write_py_interface(&modules)?;
|
python::write_py_interface(&modules)?;
|
||||||
|
|
||||||
// write strings.json file to requested path
|
// write strings.json file to requested path
|
||||||
println!("cargo:rerun-if-env-changed=STRINGS_JSON");
|
if let Some(path) = option_env!("STRINGS_JSON") {
|
||||||
if let Ok(path) = env::var("STRINGS_JSON") {
|
|
||||||
if !path.is_empty() {
|
if !path.is_empty() {
|
||||||
let path = PathBuf::from(path);
|
let path = PathBuf::from(path);
|
||||||
let meta_json = serde_json::to_string_pretty(&modules).unwrap();
|
let meta_json = serde_json::to_string_pretty(&modules).unwrap();
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
// Copyright: Ankitects Pty Ltd and contributors
|
// Copyright: Ankitects Pty Ltd and contributors
|
||||||
// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
|
|
||||||
use std::env;
|
|
||||||
use std::fmt::Write;
|
use std::fmt::Write;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
|
@ -21,7 +20,7 @@ pub fn write_py_interface(modules: &[Module]) -> Result<()> {
|
||||||
render_methods(modules, &mut out);
|
render_methods(modules, &mut out);
|
||||||
render_legacy_enum(modules, &mut out);
|
render_legacy_enum(modules, &mut out);
|
||||||
|
|
||||||
if let Ok(path) = env::var("STRINGS_PY") {
|
if let Some(path) = option_env!("STRINGS_PY") {
|
||||||
let path = PathBuf::from(path);
|
let path = PathBuf::from(path);
|
||||||
create_dir_all(path.parent().unwrap())?;
|
create_dir_all(path.parent().unwrap())?;
|
||||||
write_file_if_changed(path, out)?;
|
write_file_if_changed(path, out)?;
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
// Copyright: Ankitects Pty Ltd and contributors
|
// Copyright: Ankitects Pty Ltd and contributors
|
||||||
// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
|
|
||||||
use std::env;
|
|
||||||
use std::fmt::Write;
|
use std::fmt::Write;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
|
@ -22,7 +21,7 @@ pub fn write_ts_interface(modules: &[Module]) -> Result<()> {
|
||||||
render_module_map(modules, &mut ts_out);
|
render_module_map(modules, &mut ts_out);
|
||||||
render_methods(modules, &mut ts_out);
|
render_methods(modules, &mut ts_out);
|
||||||
|
|
||||||
if let Ok(path) = env::var("STRINGS_TS") {
|
if let Some(path) = option_env!("STRINGS_TS") {
|
||||||
let path = PathBuf::from(path);
|
let path = PathBuf::from(path);
|
||||||
create_dir_all(path.parent().unwrap())?;
|
create_dir_all(path.parent().unwrap())?;
|
||||||
write_file_if_changed(path, ts_out)?;
|
write_file_if_changed(path, ts_out)?;
|
||||||
|
|
|
||||||
|
|
@ -335,6 +335,15 @@ pub fn write_file_if_changed(path: impl AsRef<Path>, contents: impl AsRef<[u8]>)
|
||||||
.map(|existing| existing != contents)
|
.map(|existing| existing != contents)
|
||||||
.unwrap_or(true)
|
.unwrap_or(true)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
match std::env::var("CARGO_PKG_NAME") {
|
||||||
|
Ok(pkg) if pkg == "anki_proto" || pkg == "anki_i18n" => {
|
||||||
|
// at comptime for the proto/i18n crates, register implicit output as input
|
||||||
|
println!("cargo:rerun-if-changed={}", path.to_str().unwrap());
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
|
||||||
if changed {
|
if changed {
|
||||||
write_file(path, contents)?;
|
write_file(path, contents)?;
|
||||||
Ok(true)
|
Ok(true)
|
||||||
|
|
|
||||||
|
|
@ -434,9 +434,10 @@ impl SqlWriter<'_> {
|
||||||
let timing = self.col.timing_today()?;
|
let timing = self.col.timing_today()?;
|
||||||
(timing.days_elapsed, timing.next_day_at, timing.now)
|
(timing.days_elapsed, timing.next_day_at, timing.now)
|
||||||
};
|
};
|
||||||
|
const NEW_TYPE: i8 = CardType::New as i8;
|
||||||
write!(
|
write!(
|
||||||
self.sql,
|
self.sql,
|
||||||
"extract_fsrs_retrievability(c.data, case when c.odue !=0 then c.odue else c.due end, c.ivl, {elap}, {next_day_at}, {now}) {op} {r}"
|
"case when c.type = {NEW_TYPE} then false else (extract_fsrs_retrievability(c.data, case when c.odue !=0 then c.odue else c.due end, c.ivl, {elap}, {next_day_at}, {now}) {op} {r}) end"
|
||||||
)
|
)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -54,7 +54,7 @@ fn build_retrievability_query(
|
||||||
) -> String {
|
) -> String {
|
||||||
if fsrs {
|
if fsrs {
|
||||||
format!(
|
format!(
|
||||||
"extract_fsrs_relative_retrievability(c.data, case when c.odue !=0 then c.odue else c.due end, {today}, ivl, {next_day_at}, {now}) {order}"
|
"extract_fsrs_relative_retrievability(c.data, case when c.odue !=0 then c.odue else c.due end, ivl, {today}, {next_day_at}, {now}) {order}"
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
format!(
|
format!(
|
||||||
|
|
|
||||||
|
|
@ -837,7 +837,7 @@ impl fmt::Display for ReviewOrderSubclause {
|
||||||
let next_day_at = timing.next_day_at.0;
|
let next_day_at = timing.next_day_at.0;
|
||||||
let now = timing.now.0;
|
let now = timing.now.0;
|
||||||
temp_string =
|
temp_string =
|
||||||
format!("extract_fsrs_relative_retrievability(data, case when odue !=0 then odue else due end, {today}, ivl, {next_day_at}, {now}) {order}");
|
format!("extract_fsrs_relative_retrievability(data, case when odue !=0 then odue else due end, ivl, {today}, {next_day_at}, {now}) {order}");
|
||||||
&temp_string
|
&temp_string
|
||||||
}
|
}
|
||||||
ReviewOrderSubclause::Added => "nid asc, ord asc",
|
ReviewOrderSubclause::Added => "nid asc, ord asc",
|
||||||
|
|
|
||||||
|
|
@ -332,23 +332,30 @@ fn add_extract_fsrs_retrievability(db: &Connection) -> rusqlite::Result<()> {
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
};
|
};
|
||||||
let seconds_elapsed = if let Some(last_review_time) = card_data.last_review_time {
|
let seconds_elapsed = if let Some(last_review_time) = card_data.last_review_time {
|
||||||
now.saturating_sub(last_review_time.0) as u32
|
// This and any following
|
||||||
|
// (x as u32).saturating_sub(y as u32)
|
||||||
|
// must not be changed to
|
||||||
|
// x.saturating_sub(y) as u32
|
||||||
|
// as x and y are i64's and saturating_sub will therfore allow negative numbers
|
||||||
|
// before converting to u32 in the latter example.
|
||||||
|
(now as u32).saturating_sub(last_review_time.0 as u32)
|
||||||
} else if due > 365_000 {
|
} else if due > 365_000 {
|
||||||
// (re)learning card in seconds
|
// (re)learning card in seconds
|
||||||
let Ok(ivl) = ctx.get_raw(2).as_i64() else {
|
let Ok(ivl) = ctx.get_raw(2).as_i64() else {
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
};
|
};
|
||||||
let last_review_time = due.saturating_sub(ivl);
|
let last_review_time = (due as u32).saturating_sub(ivl as u32);
|
||||||
now.saturating_sub(last_review_time) as u32
|
(now as u32).saturating_sub(last_review_time)
|
||||||
} else {
|
} else {
|
||||||
let Ok(ivl) = ctx.get_raw(2).as_i64() else {
|
let Ok(ivl) = ctx.get_raw(2).as_i64() else {
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
};
|
};
|
||||||
let Ok(days_elapsed) = ctx.get_raw(3).as_i64() else {
|
// timing.days_elapsed
|
||||||
|
let Ok(today) = ctx.get_raw(3).as_i64() else {
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
};
|
};
|
||||||
let review_day = due.saturating_sub(ivl);
|
let review_day = (due as u32).saturating_sub(ivl as u32);
|
||||||
days_elapsed.saturating_sub(review_day) as u32 * 86_400
|
(today as u32).saturating_sub(review_day) * 86_400
|
||||||
};
|
};
|
||||||
let decay = card_data.decay.unwrap_or(FSRS5_DEFAULT_DECAY);
|
let decay = card_data.decay.unwrap_or(FSRS5_DEFAULT_DECAY);
|
||||||
let retrievability = card_data.memory_state().map(|state| {
|
let retrievability = card_data.memory_state().map(|state| {
|
||||||
|
|
@ -364,7 +371,7 @@ fn add_extract_fsrs_retrievability(db: &Connection) -> rusqlite::Result<()> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// eg. extract_fsrs_relative_retrievability(card.data, card.due,
|
/// eg. extract_fsrs_relative_retrievability(card.data, card.due,
|
||||||
/// timing.days_elapsed, card.ivl, timing.next_day_at, timing.now) -> float |
|
/// card.ivl, timing.days_elapsed, timing.next_day_at, timing.now) -> float |
|
||||||
/// null. The higher the number, the higher the card's retrievability relative
|
/// null. The higher the number, the higher the card's retrievability relative
|
||||||
/// to the configured desired retention.
|
/// to the configured desired retention.
|
||||||
fn add_extract_fsrs_relative_retrievability(db: &Connection) -> rusqlite::Result<()> {
|
fn add_extract_fsrs_relative_retrievability(db: &Connection) -> rusqlite::Result<()> {
|
||||||
|
|
@ -378,25 +385,32 @@ fn add_extract_fsrs_relative_retrievability(db: &Connection) -> rusqlite::Result
|
||||||
let Ok(due) = ctx.get_raw(1).as_i64() else {
|
let Ok(due) = ctx.get_raw(1).as_i64() else {
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
};
|
};
|
||||||
let Ok(interval) = ctx.get_raw(3).as_i64() else {
|
let Ok(interval) = ctx.get_raw(2).as_i64() else {
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
};
|
};
|
||||||
|
/*
|
||||||
|
// Unused
|
||||||
let Ok(next_day_at) = ctx.get_raw(4).as_i64() else {
|
let Ok(next_day_at) = ctx.get_raw(4).as_i64() else {
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
};
|
};
|
||||||
|
*/
|
||||||
let Ok(now) = ctx.get_raw(5).as_i64() else {
|
let Ok(now) = ctx.get_raw(5).as_i64() else {
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
};
|
};
|
||||||
let days_elapsed = if due > 365_000 {
|
let secs_elapsed = if due > 365_000 {
|
||||||
// (re)learning
|
// (re)learning card with due in seconds
|
||||||
(next_day_at as u32).saturating_sub(due as u32) / 86_400
|
|
||||||
|
// Don't change this to now.subtracting_sub(due) as u32
|
||||||
|
// for the same reasons listed in the comment
|
||||||
|
// in add_extract_fsrs_retrievability
|
||||||
|
(now as u32).saturating_sub(due as u32)
|
||||||
} else {
|
} else {
|
||||||
let Ok(days_elapsed) = ctx.get_raw(2).as_i64() else {
|
// timing.days_elapsed
|
||||||
|
let Ok(today) = ctx.get_raw(3).as_i64() else {
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
};
|
};
|
||||||
let review_day = due.saturating_sub(interval);
|
let review_day = due.saturating_sub(interval);
|
||||||
|
(today as u32).saturating_sub(review_day as u32) * 86_400
|
||||||
(days_elapsed as u32).saturating_sub(review_day as u32)
|
|
||||||
};
|
};
|
||||||
if let Ok(card_data) = ctx.get_raw(0).as_str() {
|
if let Ok(card_data) = ctx.get_raw(0).as_str() {
|
||||||
if !card_data.is_empty() {
|
if !card_data.is_empty() {
|
||||||
|
|
@ -410,23 +424,12 @@ fn add_extract_fsrs_relative_retrievability(db: &Connection) -> rusqlite::Result
|
||||||
|
|
||||||
let seconds_elapsed =
|
let seconds_elapsed =
|
||||||
if let Some(last_review_time) = card_data.last_review_time {
|
if let Some(last_review_time) = card_data.last_review_time {
|
||||||
now.saturating_sub(last_review_time.0) as u32
|
// Don't change this to now.subtracting_sub(due) as u32
|
||||||
} else if due > 365_000 {
|
// for the same reasons listed in the comment
|
||||||
// (re)learning card in seconds
|
// in add_extract_fsrs_retrievability
|
||||||
let Ok(ivl) = ctx.get_raw(2).as_i64() else {
|
(now as u32).saturating_sub(last_review_time.0 as u32)
|
||||||
return Ok(None);
|
|
||||||
};
|
|
||||||
let last_review_time = due.saturating_sub(ivl);
|
|
||||||
now.saturating_sub(last_review_time) as u32
|
|
||||||
} else {
|
} else {
|
||||||
let Ok(ivl) = ctx.get_raw(2).as_i64() else {
|
secs_elapsed
|
||||||
return Ok(None);
|
|
||||||
};
|
|
||||||
let Ok(days_elapsed) = ctx.get_raw(3).as_i64() else {
|
|
||||||
return Ok(None);
|
|
||||||
};
|
|
||||||
let review_day = due.saturating_sub(ivl);
|
|
||||||
days_elapsed.saturating_sub(review_day) as u32 * 86_400
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let current_retrievability = FSRS::new(None)
|
let current_retrievability = FSRS::new(None)
|
||||||
|
|
@ -441,7 +444,7 @@ fn add_extract_fsrs_relative_retrievability(db: &Connection) -> rusqlite::Result
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
let days_elapsed = secs_elapsed / 86_400;
|
||||||
// FSRS data missing; fall back to SM2 ordering
|
// FSRS data missing; fall back to SM2 ordering
|
||||||
Ok(Some(
|
Ok(Some(
|
||||||
-((days_elapsed as f32) + 0.001) / (interval as f32).max(1.0),
|
-((days_elapsed as f32) + 0.001) / (interval as f32).max(1.0),
|
||||||
|
|
|
||||||
|
|
@ -202,7 +202,7 @@ fn sveltekit_temp_file(path: &str) -> bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_cargo_deny() -> Result<()> {
|
fn check_cargo_deny() -> Result<()> {
|
||||||
Command::run("cargo install cargo-deny@0.18.3")?;
|
Command::run("cargo install cargo-deny@0.18.6")?;
|
||||||
Command::run("cargo deny check")?;
|
Command::run("cargo deny check")?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,9 +10,6 @@ export function allImagesLoaded(): Promise<void[]> {
|
||||||
}
|
}
|
||||||
|
|
||||||
function imageLoaded(img: HTMLImageElement): Promise<void> {
|
function imageLoaded(img: HTMLImageElement): Promise<void> {
|
||||||
if (!img.getAttribute("decoding")) {
|
|
||||||
img.decoding = "async";
|
|
||||||
}
|
|
||||||
return img.complete
|
return img.complete
|
||||||
? Promise.resolve()
|
? Promise.resolve()
|
||||||
: new Promise((resolve) => {
|
: new Promise((resolve) => {
|
||||||
|
|
@ -31,6 +28,8 @@ function extractImageSrcs(fragment: DocumentFragment): string[] {
|
||||||
function createImage(src: string): HTMLImageElement {
|
function createImage(src: string): HTMLImageElement {
|
||||||
const img = new Image();
|
const img = new Image();
|
||||||
img.src = src;
|
img.src = src;
|
||||||
|
img.decoding = "async";
|
||||||
|
img.decode();
|
||||||
return img;
|
return img;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -37,7 +37,9 @@ export const addOrUpdateNote = async function(
|
||||||
backExtra,
|
backExtra,
|
||||||
tags,
|
tags,
|
||||||
});
|
});
|
||||||
showResult(mode.noteId, result, noteCount);
|
if (result.note) {
|
||||||
|
showResult(mode.noteId, result, noteCount);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
const result = await addImageOcclusionNote({
|
const result = await addImageOcclusionNote({
|
||||||
// IOCloningMode is not used on mobile
|
// IOCloningMode is not used on mobile
|
||||||
|
|
@ -55,23 +57,12 @@ export const addOrUpdateNote = async function(
|
||||||
// show toast message
|
// show toast message
|
||||||
const showResult = (noteId: number | null, result: OpChanges, count: number) => {
|
const showResult = (noteId: number | null, result: OpChanges, count: number) => {
|
||||||
const props = $state({
|
const props = $state({
|
||||||
message: "",
|
message: noteId ? tr.browsingCardsUpdated({ count: count }) : tr.importingCardsAdded({ count: count }),
|
||||||
type: "error" as "error" | "success",
|
type: "success" as "error" | "success",
|
||||||
showToast: true,
|
showToast: true,
|
||||||
});
|
});
|
||||||
mount(Toast, {
|
mount(Toast, {
|
||||||
target: document.body,
|
target: document.body,
|
||||||
props,
|
props,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (result.note) {
|
|
||||||
const msg = noteId ? tr.browsingCardsUpdated({ count: count }) : tr.importingCardsAdded({ count: count });
|
|
||||||
props.message = msg;
|
|
||||||
props.type = "success";
|
|
||||||
props.showToast = true;
|
|
||||||
} else {
|
|
||||||
const msg = tr.notetypesErrorGeneratingCloze();
|
|
||||||
props.message = msg;
|
|
||||||
props.showToast = true;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue