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
6 changes: 4 additions & 2 deletions AnkiDroid/src/main/java/com/ichi2/anki/NoteEditorFragment.kt
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,7 @@ import com.ichi2.anki.snackbar.BaseSnackbarBuilderProvider
import com.ichi2.anki.snackbar.SnackbarBuilder
import com.ichi2.anki.snackbar.showSnackbar
import com.ichi2.anki.ui.setupNoteTypeSpinner
import com.ichi2.anki.utils.RunOnlyOnce
import com.ichi2.anki.utils.ext.sharedPrefs
import com.ichi2.anki.utils.ext.showDialogFragment
import com.ichi2.anki.utils.ext.window
Expand Down Expand Up @@ -224,7 +225,7 @@ class NoteEditorFragment :
private var changed = false
private var isTagsEdited = false
private var isFieldEdited = false

private var addNoteJob = RunOnlyOnce(scope = lifecycleScope)
private var multimediaActionJob: Job? = null

private val getColUnsafe: Collection
Expand Down Expand Up @@ -1338,7 +1339,8 @@ class NoteEditorFragment :

reloadRequired = true

lifecycleScope.launch {
// Use addNoteJob to ignore any requests made after the initial add note request, this prevents multiple clicks from crashing the app
addNoteJob.launch {
val noteFieldsCheck = checkNoteFieldsResponse(editorNote!!)
if (noteFieldsCheck is NoteFieldsCheckResult.Failure) {
addNoteErrorMessage = noteFieldsCheck.localizedMessage ?: getString(R.string.something_wrong)
Expand Down
21 changes: 21 additions & 0 deletions AnkiDroid/src/main/java/com/ichi2/anki/utils/OnlyOnce.kt
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@

package com.ichi2.anki.utils

import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
import timber.log.Timber

/**
Expand Down Expand Up @@ -51,3 +53,22 @@ object OnlyOnce {
}
}
}

/**
* Enforces single execution of a coroutine task
* if a task is running, ignore any other requests till it finishes
* @param scope The coroutine scope where the task is launched
*/
class RunOnlyOnce(
private val scope: CoroutineScope,
) {
private var job: Job? = null

fun launch(block: suspend CoroutineScope.() -> Unit) {
if (job?.isActive == true) {
Timber.d("skipped multiple executions of job")
return
}
job = scope.launch(block = block)
}
}