Skip to content

Commit

Permalink
feat(new reviewer): undo and redo
Browse files Browse the repository at this point in the history
  • Loading branch information
BrayanDSO committed Jun 20, 2024
1 parent 136ee26 commit 3fe04cb
Show file tree
Hide file tree
Showing 3 changed files with 86 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import androidx.lifecycle.lifecycleScope
import com.google.android.material.appbar.MaterialToolbar
import com.google.android.material.button.MaterialButton
import com.ichi2.anki.AbstractFlashcardViewer.Companion.RESULT_NO_MORE_CARDS
import com.ichi2.anki.CollectionManager
import com.ichi2.anki.NoteEditor
import com.ichi2.anki.R
import com.ichi2.anki.cardviewer.CardMediaPlayer
Expand Down Expand Up @@ -118,8 +119,10 @@ class ReviewerFragment :
R.id.action_edit -> launchEditNote()
R.id.action_mark -> viewModel.toggleMark()
R.id.action_open_deck_options -> launchDeckOptions()
R.id.action_redo -> viewModel.redo()
R.id.action_suspend_card -> viewModel.suspendCard()
R.id.action_suspend_note -> viewModel.suspendNote()
R.id.action_undo -> viewModel.undo()
R.id.user_action_1 -> viewModel.userAction(1)
R.id.user_action_2 -> viewModel.userAction(2)
R.id.user_action_3 -> viewModel.userAction(3)
Expand Down Expand Up @@ -205,6 +208,20 @@ class ReviewerFragment :
suspendItem.isVisible = true
}
}

val undoItem = menu.findItem(R.id.action_undo)
viewModel.undoLabelFlow.flowWithLifecycle(lifecycle)
.collectLatestIn(lifecycleScope) { label ->
undoItem.title = label ?: CollectionManager.TR.undoUndo()
undoItem.isEnabled = label != null
}

val redoItem = menu.findItem(R.id.action_redo)
viewModel.redoLabelFlow.flowWithLifecycle(lifecycle)
.collectLatestIn(lifecycleScope) { label ->
redoItem.title = label ?: CollectionManager.TR.undoRedo()
redoItem.isEnabled = label != null
}
}

private val noteEditorLauncher =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import androidx.activity.result.ActivityResult
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewmodel.initializer
import androidx.lifecycle.viewmodel.viewModelFactory
import anki.collection.OpChanges
import anki.frontend.SetSchedulingStatesRequest
import com.ichi2.anki.CollectionManager
import com.ichi2.anki.CollectionManager.withCol
Expand All @@ -38,9 +39,12 @@ import com.ichi2.anki.servicelayer.MARKED_TAG
import com.ichi2.anki.servicelayer.NoteService
import com.ichi2.anki.servicelayer.isBuryNoteAvailable
import com.ichi2.anki.servicelayer.isSuspendNoteAvailable
import com.ichi2.libanki.ChangeManager
import com.ichi2.libanki.hasTag
import com.ichi2.libanki.note
import com.ichi2.libanki.redo
import com.ichi2.libanki.sched.CurrentQueueState
import com.ichi2.libanki.undo
import com.ichi2.libanki.undoableOp
import com.ichi2.libanki.utils.TimeManager
import kotlinx.coroutines.CompletableDeferred
Expand All @@ -49,7 +53,9 @@ import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow

class ReviewerViewModel(cardMediaPlayer: CardMediaPlayer) : CardViewerViewModel(cardMediaPlayer) {
class ReviewerViewModel(cardMediaPlayer: CardMediaPlayer) :
CardViewerViewModel(cardMediaPlayer),
ChangeManager.Subscriber {

private var queueState: Deferred<CurrentQueueState?> = asyncIO {
// this assumes that the Reviewer won't be launched if there isn't a queueState
Expand All @@ -63,6 +69,8 @@ class ReviewerViewModel(cardMediaPlayer: CardMediaPlayer) : CardViewerViewModel(
val actionFeedbackFlow = MutableSharedFlow<String>()
val canBuryNoteFlow = MutableStateFlow(true)
val canSuspendNoteFlow = MutableStateFlow(true)
val undoLabelFlow = MutableStateFlow<String?>(null)
val redoLabelFlow = MutableStateFlow<String?>(null)

override val server = AnkiServer(this).also { it.start() }
private val stateMutationKey = TimeManager.time.intTimeMS().toString()
Expand All @@ -83,6 +91,13 @@ class ReviewerViewModel(cardMediaPlayer: CardMediaPlayer) : CardViewerViewModel(
*/
private var statesMutated = true

init {
ChangeManager.subscribe(this)
launchCatchingIO {
updateUndoAndRedoLabels()
}
}

/* *********************************************************************************************
************************ Public methods: meant to be used by the View **************************
********************************************************************************************* */
Expand Down Expand Up @@ -213,6 +228,36 @@ class ReviewerViewModel(cardMediaPlayer: CardMediaPlayer) : CardViewerViewModel(
}
}

fun undo() {
launchCatchingIO {
val changes = undoableOp {
undo()
}
val message = if (changes.operation.isEmpty()) {
CollectionManager.TR.actionsNothingToUndo()
} else {
CollectionManager.TR.undoActionUndone(changes.operation)
}
actionFeedbackFlow.emit(message)
updateCurrentCard()
}
}

fun redo() {
launchCatchingIO {
val changes = undoableOp {
redo()
}
val message = if (changes.operation.isEmpty()) {
CollectionManager.TR.actionsNothingToRedo()
} else {
CollectionManager.TR.undoRedoAction(changes.operation)
}
actionFeedbackFlow.emit(message)
updateCurrentCard()
}
}

fun userAction(@Reviewer.UserAction number: Int) {
launchCatchingIO {
eval.emit("javascript: ankidroid.userAction($number);")
Expand Down Expand Up @@ -321,6 +366,15 @@ class ReviewerViewModel(cardMediaPlayer: CardMediaPlayer) : CardViewerViewModel(
return text
}

private suspend fun updateUndoAndRedoLabels() {
undoLabelFlow.emit(withCol { undoLabel() })
redoLabelFlow.emit(withCol { redoLabel() })
}

override fun opExecuted(changes: OpChanges, handler: Any?) {
launchCatchingIO { updateUndoAndRedoLabels() }
}

companion object {
fun factory(soundPlayer: CardMediaPlayer): ViewModelProvider.Factory {
return viewModelFactory {
Expand Down
15 changes: 14 additions & 1 deletion AnkiDroid/src/main/res/menu/reviewer2.xml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,20 @@
~ this program. If not, see <http://www.gnu.org/licenses/>.
-->
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<item
android:id="@+id/action_undo"
android:enabled="false"
android:icon="@drawable/ic_undo_white"
tools:title="Undo"
app:showAsAction="never"/>
<item
android:id="@+id/action_redo"
android:enabled="false"
android:icon="@drawable/ic_redo"
tools:title="Redo"
app:showAsAction="never"/>
<item
android:id="@+id/action_mark"
android:icon="@drawable/ic_star_border_white"
Expand Down

0 comments on commit 3fe04cb

Please sign in to comment.