Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 38 additions & 32 deletions AnkiDroid/src/main/java/com/ichi2/anki/NoteEditorFragment.kt
Original file line number Diff line number Diff line change
Expand Up @@ -328,40 +328,46 @@ class NoteEditorFragment :
private val requestTemplateEditLauncher =
registerForActivityResult(
ActivityResultContracts.StartActivityForResult(),
NoteEditorActivityResultCallback {
// Note type can change regardless of exit type - update ourselves and CardBrowser
reloadRequired = true
editorNote!!.notetype = getColUnsafe.notetypes.get(editorNote!!.noteTypeId)!!
if (currentEditedCard == null ||
!editorNote!!
.cardIds(getColUnsafe)
.contains(currentEditedCard!!.id)
) {
if (!addNote) {
/* This can occur, for example, if the
* card type was deleted or if the note
* type was changed without moving this
* card to another type. */
Timber.d("onActivityResult() template edit return - current card is gone, close note editor")
showSnackbar(getString(R.string.template_for_current_card_deleted))
closeNoteEditor()
} else {
Timber.d("onActivityResult() template edit return, in add mode, just re-display")
updateCards(editorNote!!.notetype)
}
} else {
Timber.d("onActivityResult() template edit return - current card exists")
// reload current card - the template ordinals are possibly different post-edit
currentEditedCard = getColUnsafe.getCard(currentEditedCard!!.id)
@NeedsTest("#17282 returning from template editor saves further made changes")
// make sure the card's note is available going forward
currentEditedCard!!.note(getColUnsafe)
editorNote = currentEditedCard!!.note // update the NoteEditor's working note reference
updateCards(editorNote!!.notetype)
}
},
NoteEditorActivityResultCallback { onTemplateEditorResult() },
)

/** Handles a return from the [CardTemplateEditor] */
@VisibleForTesting
internal fun onTemplateEditorResult() {
// Note type can change regardless of exit type - update ourselves and CardBrowser
reloadRequired = true
editorNote!!.notetype = getColUnsafe.notetypes.get(editorNote!!.noteTypeId)!!
if (currentEditedCard == null ||
!editorNote!!
.cardIds(getColUnsafe)
.contains(currentEditedCard!!.id)
) {
if (!addNote) {
/* This can occur, for example, if the
* card type was deleted or if the note
* type was changed without moving this
* card to another type. */
Timber.d("onActivityResult() template edit return - current card is gone, close note editor")
showSnackbar(getString(R.string.template_for_current_card_deleted))
closeNoteEditor()
} else {
Timber.d("onActivityResult() template edit return, in add mode, just re-display")
// the fields may have changed, so rebuild them
refreshNoteData(FieldChangeType.changeFieldCount(shouldReplaceNewlines()))
}
} else {
Timber.d("onActivityResult() template edit return - current card exists")
// reload current card - the template ordinals are possibly different post-edit
currentEditedCard = getColUnsafe.getCard(currentEditedCard!!.id)
@NeedsTest("#17282 returning from template editor saves further made changes")
// make sure the card's note is available going forward
currentEditedCard!!.note(getColUnsafe)
editorNote = currentEditedCard!!.note // update the NoteEditor's working note reference
// the fields may have changed, so rebuild them
setNote(editorNote, FieldChangeType.changeFieldCount(shouldReplaceNewlines()))
}
}

private val ioEditorLauncher =
registerForActivityResult(
ActivityResultContracts.GetContent(),
Expand Down
46 changes: 46 additions & 0 deletions AnkiDroid/src/test/java/com/ichi2/anki/NoteEditorTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

package com.ichi2.anki

import android.annotation.SuppressLint
import android.app.Activity
import android.content.ClipData
import android.content.Intent
Expand All @@ -19,6 +20,8 @@ import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
import androidx.test.espresso.matcher.ViewMatchers.withId
import androidx.test.ext.junit.runners.AndroidJUnit4
import anki.config.ConfigKey
import anki.notetypes.StockNotetype
import anki.notetypes.notetypeId
import com.ichi2.anki.NoteEditorTest.FromScreen.DECK_LIST
import com.ichi2.anki.NoteEditorTest.FromScreen.REVIEWER
import com.ichi2.anki.api.AddContentApi.Companion.DEFAULT_DECK_ID
Expand Down Expand Up @@ -65,6 +68,49 @@ class NoteEditorTest : RobolectricTest() {
)
}

@Test // #20510
fun `editing - fields are refreshed after the template editor restores the note type to default`() {
val basic = col.notetypes.byName("Basic")!!
col.notetypes.addFieldLegacy(basic, col.notetypes.newField("ThirdField"))

val editor = getNoteEditorEditingExistingBasicNote("Hello", "World", REVIEWER)
assertThat("field count before restore", editor.editFields!!.size, equalTo(3))

// simulate 'Restore to Default' being performed inside the CardTemplateEditor
@SuppressLint("CheckResult")
col.notetypes.restoreNotetypeToStock(
notetypeId { ntid = basic.id },
StockNotetype.Kind.KIND_BASIC,
)
editor.onTemplateEditorResult()

assertThat("field count after restore", editor.editFields!!.size, equalTo(2))
assertThat(editor.currentFieldStrings.toList(), contains("Hello", "World"))
}

@Test // #20510
fun `adding - fields are refreshed after the template editor restores the note type to default`() {
val basic = col.notetypes.byName("Basic")!!
col.notetypes.addFieldLegacy(basic, col.notetypes.newField("ThirdField"))

val editor =
getNoteEditorAdding(NoteType.BASIC)
.withFirstField("Hello")
.build()
assertThat("field count before restore", editor.editFields!!.size, equalTo(3))

// simulate 'Restore to Default' being performed inside the CardTemplateEditor
@SuppressLint("CheckResult")
col.notetypes.restoreNotetypeToStock(
notetypeId { ntid = basic.id },
StockNotetype.Kind.KIND_BASIC,
)
editor.onTemplateEditorResult()

assertThat("field count after restore", editor.editFields!!.size, equalTo(2))
assertThat("typed text is preserved", editor.currentFieldStrings[0], equalTo("Hello"))
}

@Test
fun errorSavingNoteWithNoFirstFieldDisplaysNoFirstField() =
runTest {
Expand Down
16 changes: 14 additions & 2 deletions libanki/src/main/java/com/ichi2/anki/libanki/Collection.kt
Original file line number Diff line number Diff line change
Expand Up @@ -1185,14 +1185,26 @@ class Collection(
* directly mutating the database).
*/
@LibAnkiAlias("undo")
fun undo(): OpChangesAfterUndo = backend.undo()
fun undo(): OpChangesAfterUndo {
val out = backend.undo()
if (out.changes.notetype) {
notetypes.clearCache()
}
return out
}

/**
* Returns result of backend redo operation, or throws UndoEmpty.
*/
@RustCleanup("document exception")
@LibAnkiAlias("redo")
fun redo(): OpChangesAfterUndo = backend.redo()
fun redo(): OpChangesAfterUndo {
val out = backend.redo()
if (out.changes.notetype) {
notetypes.clearCache()
}
return out
}

@Deprecated("Not implemented")
@LibAnkiAlias("op_made_changes")
Expand Down
29 changes: 29 additions & 0 deletions libanki/src/test/java/com/ichi2/anki/libanki/NotetypesTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@

package com.ichi2.anki.libanki

import android.annotation.SuppressLint
import anki.notetypes.StockNotetype
import anki.notetypes.notetypeId
import com.ichi2.anki.libanki.testutils.InMemoryAnkiTest
import net.ankiweb.rsdroid.exceptions.BackendInvalidInputException
import net.ankiweb.rsdroid.exceptions.BackendNotFoundException
Expand All @@ -25,6 +28,32 @@ import org.junit.Test
import kotlin.test.assertFailsWith

class NotetypesTest : InMemoryAnkiTest() {
@Test // #20510
fun `notetype cache is invalidated on undo and redo`() {
val notetype = col.notetypes.basic

fun fieldCount() =
col.notetypes
.get(notetype.id)!!
.fields.size

col.notetypes.addFieldLegacy(notetype, col.notetypes.newField("NewField"))
assertThat("field added", fieldCount(), equalTo(3))

@SuppressLint("CheckResult")
col.notetypes.restoreNotetypeToStock(
notetypeId { ntid = notetype.id },
forceKind = StockNotetype.Kind.KIND_BASIC,
)
assertThat("fields after restore", fieldCount(), equalTo(2))

col.undo()
assertThat("fields after undo", fieldCount(), equalTo(3))

col.redo()
assertThat("fields after redo", fieldCount(), equalTo(2))
}

@Test
fun `getSingleNotetypeOfNotes - multiple`() {
val notes = addNotes(2)
Expand Down
Loading