-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
UserViewState, UserModel, AccountModel #3 With Undo for remove
- Loading branch information
Showing
10 changed files
with
775 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
45 changes: 45 additions & 0 deletions
45
account/src/main/java/io/github/novacrypto/account/AccountModel.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
/* | ||
* NovaWallet, Cryptocurrency Wallet for Android | ||
* Copyright (C) 2018 Alan Evans, NovaCrypto | ||
* | ||
* This program is free software: you can redistribute it and/or modify | ||
* it under the terms of the GNU General Public License as published by | ||
* the Free Software Foundation, either version 3 of the License, or | ||
* (at your option) any later version. | ||
* | ||
* This program is distributed in the hope that it will be useful, | ||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
* GNU General Public License for more details. | ||
* | ||
* You should have received a copy of the GNU General Public License | ||
* along with this program. If not, see <https://www.gnu.org/licenses/>. | ||
* | ||
* Original source: https://github.com/NovaCrypto/NovaWallet | ||
* You can contact the authors via github issues. | ||
*/ | ||
|
||
package io.github.novacrypto.account | ||
|
||
import io.reactivex.Observable | ||
import java.util.* | ||
|
||
data class AccountId(val uuid: UUID = UUID.randomUUID()) | ||
|
||
data class AccountModel( | ||
val id: AccountId = AccountId(), | ||
val name: String = "" | ||
) | ||
|
||
fun <T : AccountModelIntent> Observable<T>.toAccountModelStream(): Observable<AccountModel> { | ||
return this.scan(AccountModel(), | ||
{ model: AccountModel, intent: AccountModelIntent -> | ||
accountModelReducer(intent, model) | ||
}) | ||
} | ||
|
||
fun accountModelReducer(intent: AccountModelIntent, model: AccountModel): AccountModel { | ||
return when (intent) { | ||
is AccountModelIntent.Rename -> model.copy(name = intent.name) | ||
} | ||
} |
44 changes: 44 additions & 0 deletions
44
account/src/main/java/io/github/novacrypto/account/AccountModelIntents.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
/* | ||
* NovaWallet, Cryptocurrency Wallet for Android | ||
* Copyright (C) 2018 Alan Evans, NovaCrypto | ||
* | ||
* This program is free software: you can redistribute it and/or modify | ||
* it under the terms of the GNU General Public License as published by | ||
* the Free Software Foundation, either version 3 of the License, or | ||
* (at your option) any later version. | ||
* | ||
* This program is distributed in the hope that it will be useful, | ||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
* GNU General Public License for more details. | ||
* | ||
* You should have received a copy of the GNU General Public License | ||
* along with this program. If not, see <https://www.gnu.org/licenses/>. | ||
* | ||
* Original source: https://github.com/NovaCrypto/NovaWallet | ||
* You can contact the authors via github issues. | ||
*/ | ||
|
||
package io.github.novacrypto.account | ||
|
||
sealed class AccountModelIntent { | ||
class Rename(val name: String) : 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() | ||
|
||
fun AccountModel.toRenameIntent(newName: String) = | ||
renameAccount(newName).toUserModelIntent(this).forward() | ||
|
||
fun renameAccount(newName: String) = | ||
AccountModelIntent.Rename(newName) | ||
|
||
fun AccountModelIntent.toUserModelIntent(account: AccountModel) = | ||
UserModelIntent.ForwardAccountModelIntent(account.id, this) |
28 changes: 28 additions & 0 deletions
28
account/src/main/java/io/github/novacrypto/account/ErrorModel.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
/* | ||
* NovaWallet, Cryptocurrency Wallet for Android | ||
* Copyright (C) 2018 Alan Evans, NovaCrypto | ||
* | ||
* This program is free software: you can redistribute it and/or modify | ||
* it under the terms of the GNU General Public License as published by | ||
* the Free Software Foundation, either version 3 of the License, or | ||
* (at your option) any later version. | ||
* | ||
* This program is distributed in the hope that it will be useful, | ||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
* GNU General Public License for more details. | ||
* | ||
* You should have received a copy of the GNU General Public License | ||
* along with this program. If not, see <https://www.gnu.org/licenses/>. | ||
* | ||
* Original source: https://github.com/NovaCrypto/NovaWallet | ||
* You can contact the authors via github issues. | ||
*/ | ||
|
||
package io.github.novacrypto.account | ||
|
||
data class ErrorModel( | ||
val errorMessage: String | ||
) | ||
|
||
class UserReportableException(message: String) : Exception(message) |
37 changes: 37 additions & 0 deletions
37
account/src/main/java/io/github/novacrypto/account/UserModel.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
/* | ||
* NovaWallet, Cryptocurrency Wallet for Android | ||
* Copyright (C) 2018 Alan Evans, NovaCrypto | ||
* | ||
* This program is free software: you can redistribute it and/or modify | ||
* it under the terms of the GNU General Public License as published by | ||
* the Free Software Foundation, either version 3 of the License, or | ||
* (at your option) any later version. | ||
* | ||
* This program is distributed in the hope that it will be useful, | ||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
* GNU General Public License for more details. | ||
* | ||
* You should have received a copy of the GNU General Public License | ||
* along with this program. If not, see <https://www.gnu.org/licenses/>. | ||
* | ||
* Original source: https://github.com/NovaCrypto/NovaWallet | ||
* You can contact the authors via github issues. | ||
*/ | ||
|
||
package io.github.novacrypto.account | ||
|
||
data class UserModel( | ||
val accounts: List<AccountModel> = emptyList() | ||
) | ||
|
||
sealed class UserModelIntent { | ||
class AddAccountIntent(val account: AccountModel, val index: Int? = null) : UserModelIntent() | ||
class RemoveAccountIntent(val accountId: AccountId) : UserModelIntent() | ||
class ForwardAccountModelIntent( | ||
val accountId: AccountId, | ||
val intent: AccountModelIntent | ||
) : UserModelIntent() | ||
} | ||
|
||
fun UserModelIntent.forward() = UserModelViewStateIntent.ForwardUserModelIntent(this) |
110 changes: 110 additions & 0 deletions
110
account/src/main/java/io/github/novacrypto/account/UserModelViewState.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
/* | ||
* NovaWallet, Cryptocurrency Wallet for Android | ||
* Copyright (C) 2018 Alan Evans, NovaCrypto | ||
* | ||
* This program is free software: you can redistribute it and/or modify | ||
* it under the terms of the GNU General Public License as published by | ||
* the Free Software Foundation, either version 3 of the License, or | ||
* (at your option) any later version. | ||
* | ||
* This program is distributed in the hope that it will be useful, | ||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
* GNU General Public License for more details. | ||
* | ||
* You should have received a copy of the GNU General Public License | ||
* along with this program. If not, see <https://www.gnu.org/licenses/>. | ||
* | ||
* Original source: https://github.com/NovaCrypto/NovaWallet | ||
* You can contact the authors via github issues. | ||
*/ | ||
|
||
package io.github.novacrypto.account | ||
|
||
import io.reactivex.Observable | ||
|
||
data class UserModelViewState( | ||
val userModel: UserModel = UserModel(), | ||
val error: ErrorModel? = null, | ||
val undoModel: UserModelViewStateUndoModel? = null | ||
) | ||
|
||
data class UserModelViewStateUndoModel( | ||
val message: String, | ||
val undoIntent: UserModelViewStateIntent | ||
) | ||
|
||
fun <T : UserModelViewStateIntent> Observable<T>.toUserModelStream(): Observable<UserModelViewState> { | ||
return this.scan(UserModelViewState(), | ||
{ model: UserModelViewState, intent: UserModelViewStateIntent -> | ||
model.transformWithIntent(intent) | ||
}) | ||
} | ||
|
||
private fun UserModelViewState.transformWithIntent(intent: UserModelViewStateIntent): UserModelViewState { | ||
return when (intent) { | ||
is UserModelViewStateIntent.DismissError -> copy(error = null) | ||
is UserModelViewStateIntent.ForwardUserModelIntent -> | ||
try { | ||
val (newModel, undoModel) = userModel.transformWithIntent(intent.userModelIntent) | ||
copy(userModel = newModel, undoModel = undoModel) | ||
} catch (e: UserReportableException) { | ||
copy(error = ErrorModel(e.message ?: "")) | ||
} | ||
is UserModelViewStateIntent.Undo -> | ||
undoModel?.undoIntent?.let { | ||
transformWithIntent(it | ||
) | ||
} ?: this | ||
} | ||
} | ||
|
||
private fun UserModel.transformWithIntent(intent: UserModelIntent) = | ||
when (intent) { | ||
is UserModelIntent.AddAccountIntent -> { | ||
copy(accounts = if (intent.index != null) { | ||
accounts.insert(intent.account, intent.index) | ||
} else { | ||
accounts + intent.account | ||
}).withNoUndo() | ||
} | ||
is UserModelIntent.RemoveAccountIntent -> { | ||
withAccountAndIndexForId(intent.accountId) { account, index -> | ||
Pair(copy(accounts = accounts - account), | ||
UserModelViewStateUndoModel("Removed account '${account.name}'", | ||
account.toInsertIntent(index))) | ||
} | ||
} | ||
is UserModelIntent.ForwardAccountModelIntent -> { | ||
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) = | ||
copyAndModify { | ||
this[index] = replacement | ||
} | ||
|
||
private fun cannotFind(accountId: AccountId) = | ||
UserReportableException("Cannot find account $accountId") |
32 changes: 32 additions & 0 deletions
32
account/src/main/java/io/github/novacrypto/account/UserModelViewStateIntents.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
/* | ||
* NovaWallet, Cryptocurrency Wallet for Android | ||
* Copyright (C) 2018 Alan Evans, NovaCrypto | ||
* | ||
* This program is free software: you can redistribute it and/or modify | ||
* it under the terms of the GNU General Public License as published by | ||
* the Free Software Foundation, either version 3 of the License, or | ||
* (at your option) any later version. | ||
* | ||
* This program is distributed in the hope that it will be useful, | ||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
* GNU General Public License for more details. | ||
* | ||
* You should have received a copy of the GNU General Public License | ||
* along with this program. If not, see <https://www.gnu.org/licenses/>. | ||
* | ||
* Original source: https://github.com/NovaCrypto/NovaWallet | ||
* You can contact the authors via github issues. | ||
*/ | ||
|
||
package io.github.novacrypto.account | ||
|
||
sealed class UserModelViewStateIntent { | ||
class DismissError : UserModelViewStateIntent() | ||
class Undo : UserModelViewStateIntent() | ||
class ForwardUserModelIntent( | ||
val userModelIntent: UserModelIntent | ||
) : UserModelViewStateIntent() | ||
} | ||
|
||
fun dismissError() = UserModelViewStateIntent.DismissError() |
67 changes: 67 additions & 0 deletions
67
account/src/test/java/io/github/novacrypto/AccountModelTests.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
/* | ||
* NovaWallet, Cryptocurrency Wallet for Android | ||
* Copyright (C) 2018 Alan Evans, NovaCrypto | ||
* | ||
* This program is free software: you can redistribute it and/or modify | ||
* it under the terms of the GNU General Public License as published by | ||
* the Free Software Foundation, either version 3 of the License, or | ||
* (at your option) any later version. | ||
* | ||
* This program is distributed in the hope that it will be useful, | ||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
* GNU General Public License for more details. | ||
* | ||
* You should have received a copy of the GNU General Public License | ||
* along with this program. If not, see <https://www.gnu.org/licenses/>. | ||
* | ||
* Original source: https://github.com/NovaCrypto/NovaWallet | ||
* You can contact the authors via github issues. | ||
*/ | ||
|
||
package io.github.novacrypto | ||
|
||
import io.github.novacrypto.account.AccountModel | ||
import io.github.novacrypto.account.renameAccount | ||
import io.github.novacrypto.account.toAccountModelStream | ||
import io.reactivex.Observable | ||
import org.amshove.kluent.`should be` | ||
import org.amshove.kluent.`should equal` | ||
import org.amshove.kluent.`should not equal` | ||
import org.junit.Test | ||
|
||
class AccountModelTests { | ||
|
||
@Test | ||
fun `new account gets new id`() { | ||
AccountModel().id `should not equal` AccountModel().id | ||
} | ||
|
||
} | ||
|
||
class AccountModelRenameIntentTests { | ||
|
||
@Test | ||
fun `user can rename account`() { | ||
Observable.just( | ||
renameAccount("My Account") | ||
).toAccountModelStream() | ||
.assertWithLastElement { | ||
it.name `should equal` "My Account" | ||
} | ||
} | ||
|
||
@Test | ||
fun `user can rename account twice`() { | ||
Observable.just( | ||
renameAccount("My Account"), | ||
renameAccount("My Account 2") | ||
).toAccountModelStream() | ||
.assertWithLastElement { | ||
it.name `should equal` "My Account 2" | ||
} | ||
.assertWithLastTwoElements { penultimate, ultimate -> | ||
penultimate.id `should be` ultimate.id | ||
} | ||
} | ||
} |
Oops, something went wrong.