Created
July 1, 2020 07:53
-
-
Save JorgeCastilloPrz/dca5fe92016ea4cce80a3c5cf6a1a2e3 to your computer and use it in GitHub Desktop.
Functional Android workshop exercise solutions
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
// Persistence.kt will end up looking like this after exercises 1 and 2. | |
fun stubPersistence(defaultAccounts: List<User>? = null, pool: CoroutineContext = IOPool) = | |
object : AccountPersistence { | |
private var accounts = defaultAccounts ?: emptyList() | |
private lateinit var updateTrigger: () -> Unit | |
override fun loadAccountsFromDatabase(): Stream<List<User>> = Stream( | |
Stream.async { | |
updateTrigger = { emit(accounts); } | |
}, | |
Stream.effect { | |
evalOn(pool) { | |
accounts // this would be an effect to connect to the db and access accounts irl. | |
} | |
}.delayBy(700.milliseconds) | |
).parJoinUnbounded() | |
override fun dbPool() = pool | |
override suspend fun updateAccounts(accounts: List<User>) { | |
this.accounts = accounts | |
} | |
override suspend fun invalidateDatabase() { | |
accounts = emptyList() | |
updateTrigger() | |
} | |
override suspend fun toggleFavInDatabase(user: User): Unit { | |
accounts = accounts.map { | |
if (it.id == user.id) user.copy(isFavorite = !user.isFavorite) else it | |
} | |
updateTrigger() | |
} | |
} | |
// loadAccountsRepo() will ultimately look like this after exercise 3 | |
/** | |
* This needs to load accounts from DB fist, and in case there's an error or the DB accounts list | |
* is empty, fallback to loading accounts from network. | |
*/ | |
fun HomeDependencies.loadAccountsRepo(): Stream<List<User>> = | |
loadAccountsFromDatabase() | |
.handleErrorWith { fetchAccountsFromNetwork() } | |
.flatMap { | |
if (it.isEmpty()) { | |
fetchAccountsFromNetwork() | |
} else { | |
Stream(it) | |
} | |
}.effectTap { updateAccounts(it) } | |
// home.kt (program()) will end up looking like this after exercises 4, 5 and 6 | |
/* | |
Our home program exist out of refreshing items on P2R & loading initial data. | |
*/ | |
fun HomeDependencies.program(render: Stream<Unit>): Stream<Unit> = Stream( | |
loadAccounts(), | |
pullToRefresh().effectMap { invalidateDatabase() }, | |
favClicks().effectMap { toggleFavInDatabase(it) }, | |
render | |
).parJoinUnbounded() | |
fun HomeDependencies.loadAccounts(): Stream<Unit> = | |
post(HomeViewState.Loading) | |
.flatMap { loadAccountsRepo() } | |
.map(HomeViewState::Content).flatMap { post(it) } | |
.handleErrorWith { post(HomeViewState.Error(it)) } | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Hi! and thanks for the workshop!
Just a detail that could help someone :)
I think that the favClicks Stream should be passed as a parameter at least for the version of the code that we use.