Skip to content

Commit

Permalink
Undo puts account back where it was
Browse files Browse the repository at this point in the history
  • Loading branch information
westonal committed Feb 12, 2018
1 parent 409ce82 commit 6415aeb
Show file tree
Hide file tree
Showing 4 changed files with 53 additions and 20 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ sealed class AccountModelIntent {
fun AccountModel.toAddIntent() =
UserModelIntent.AddAccountIntent(this).forward()

fun AccountModel.toInsertIntent(index: Int) =
UserModelIntent.AddAccountIntent(this, index).forward()

fun AccountModel.toRemoveIntent() =
UserModelIntent.RemoveAccountIntent(this.id).forward()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ data class UserModel(
)

sealed class UserModelIntent {
class AddAccountIntent(val account: AccountModel) : UserModelIntent()
class AddAccountIntent(val account: AccountModel, val index: Int? = null) : UserModelIntent()
class RemoveAccountIntent(val accountId: AccountId) : UserModelIntent()
class ForwardAccountModelIntent(
val accountId: AccountId,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,35 +61,50 @@ private fun UserModelViewState.transformWithIntent(intent: UserModelViewStateInt

private fun UserModel.transformWithIntent(intent: UserModelIntent) =
when (intent) {
is UserModelIntent.AddAccountIntent -> Pair(copy(accounts = accounts + intent.account), null)
is UserModelIntent.AddAccountIntent -> {
copy(accounts = if (intent.index != null) {
accounts.insert(intent.account, intent.index)
} else {
accounts + intent.account
}).withNoUndo()
}
is UserModelIntent.RemoveAccountIntent -> {
val existingAccount = findAccount(intent.accountId)
?: throw cannotFind(intent.accountId)
Pair(copy(accounts = accounts - existingAccount),
UserModelViewStateUndoModel("Removed account '${existingAccount.name}'",
existingAccount.toAddIntent()))
withAccountAndIndexForId(intent.accountId) { account, index ->
Pair(copy(accounts = accounts - account),
UserModelViewStateUndoModel("Removed account '${account.name}'",
account.toInsertIntent(index)))
}
}
is UserModelIntent.ForwardAccountModelIntent -> {
val accountIdx = findAccountIndex(intent.accountId)
if (accountIdx != -1) {
val newAccounts = accounts.replace(accountIdx, accountModelReducer(intent.intent, accounts[accountIdx]))
Pair(copy(accounts = newAccounts), null)
} else {
throw cannotFind(intent.accountId)
withAccountAndIndexForId(intent.accountId) { account, index ->
val newAccounts = accounts.replace(index, accountModelReducer(intent.intent, account))
copy(accounts = newAccounts).withNoUndo()
}
}
}

private fun <T> UserModel.withAccountAndIndexForId(accountId: AccountId, transform: (account: AccountModel, index: Int) -> T): T {
val accountIdx = accounts.indexOfFirst { it.id == accountId }.also {
if (it == -1) throw cannotFind(accountId)
}
val existingAccount = accounts[accountIdx]
return transform(existingAccount, accountIdx)
}

private fun UserModel.withNoUndo() = Pair(this, null)

private inline fun <E> List<E>.copyAndModify(modify: MutableList<E>.() -> Unit): List<E> =
toMutableList().apply(modify)

private fun <E> List<E>.insert(newItem: E, index: Int) =
copyAndModify {
this.add(index, newItem)
}

private fun <E> List<E>.replace(index: Int, replacement: E) =
toMutableList().apply {
copyAndModify {
this[index] = replacement
}

private fun UserModel.findAccountIndex(accountId: AccountId) =
accounts.indexOfFirst { it.id == accountId }

private fun cannotFind(accountId: AccountId) =
UserReportableException("Cannot find account $accountId")

private fun UserModel.findAccount(accountId: AccountId) =
accounts.getOrNull(findAccountIndex(accountId))
15 changes: 15 additions & 0 deletions account/src/test/java/io/github/novacrypto/UserModelIntentTests.kt
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,21 @@ class UserModelUndo {
}
}

@Test
fun `an undone remove is in the same position it was before`() {
val account1 = AccountModel()
val account2 = AccountModel()
Observable.just(
account1.toAddIntent(),
account2.toAddIntent(),
account1.toRemoveIntent(),
undo()
).toUserModelStream()
.assertWithLastAndThirdToLastElements { lastMinusTwo, last ->
lastMinusTwo.userModel.accounts `should equal` last.userModel.accounts
}
}

@Test
fun `if there is nothing to undo, undo does nothing`() {
val account = AccountModel()
Expand Down

0 comments on commit 6415aeb

Please sign in to comment.