Skip to content

Commit

Permalink
Misc chores for first alpha release
Browse files Browse the repository at this point in the history
  • Loading branch information
Peter Laggner committed Oct 12, 2021
1 parent c8187a0 commit 659985e
Show file tree
Hide file tree
Showing 10 changed files with 114 additions and 93 deletions.
3 changes: 2 additions & 1 deletion .idea/misc.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
package com.greyhairredbear.racooin.apiclient

import arrow.core.Either
import com.greyhairredbear.racooin.core.interfaces.ApiCallFailed
import com.greyhairredbear.racooin.core.interfaces.ApiClient
import com.greyhairredbear.racooin.core.interfaces.ApiClientError
import com.greyhairredbear.racooin.core.model.ApiCallFailed
import com.greyhairredbear.racooin.core.model.ApiClientError
import com.greyhairredbear.racooin.core.model.CryptoCurrency
import com.greyhairredbear.racooin.core.model.CryptoCurrencyRate
import com.greyhairredbear.racooin.core.model.FiatBalance
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import androidx.datastore.core.DataStore
import androidx.datastore.dataStore
import arrow.core.Either
import arrow.core.Either.Companion.catch
import com.google.protobuf.Timestamp
import com.greyhairredbear.racooin.core.interfaces.Persistence
import com.greyhairredbear.racooin.core.model.CryptoBalance
import com.greyhairredbear.racooin.core.model.CryptoCurrency
Expand Down
75 changes: 6 additions & 69 deletions app/src/main/java/com/greyhairredbear/racooin/ui/MainActivity.kt
Original file line number Diff line number Diff line change
@@ -1,47 +1,25 @@
package com.greyhairredbear.racooin

import android.content.Context
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.datastore.core.DataStore
import androidx.datastore.dataStore
import androidx.lifecycle.ViewModel
import androidx.compose.foundation.layout.Column
import androidx.compose.ui.Alignment
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.viewModelScope
import androidx.lifecycle.viewmodel.compose.viewModel
import arrow.core.computations.either
import com.greyhairredbear.racooin.core.interfaces.ApiClient
import com.greyhairredbear.racooin.core.interfaces.Persistence
import com.greyhairredbear.racooin.core.interfaces.Resource
import com.greyhairredbear.racooin.core.model.ApiClientError
import com.greyhairredbear.racooin.core.model.CryptoCurrencyRate
import com.greyhairredbear.racooin.persistence.CryptoBalances
import com.greyhairredbear.racooin.persistence.CryptoCurrencyRates
import com.greyhairredbear.racooin.persistence.Invests
import com.greyhairredbear.racooin.persistence.serializer.CryptoBalanceSerializer
import com.greyhairredbear.racooin.persistence.serializer.CryptoCurrencyRateSerializer
import com.greyhairredbear.racooin.persistence.serializer.InvestSerializer
import com.greyhairredbear.racooin.ui.MainScreen
import dagger.hilt.android.AndroidEntryPoint
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import javax.inject.Inject

@AndroidEntryPoint
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

setContent {
MainScreen()
Column(horizontalAlignment = Alignment.CenterHorizontally) {
MainScreen()
}
}

lifecycleScope.launchWhenResumed {
Expand All @@ -65,44 +43,3 @@ class MainActivity : ComponentActivity() {
// - calculate balances
// onChangeCryptoCurrency
}

@Composable
private fun MainScreen(
screenViewModel: MainViewModel = viewModel()
) {
val uiState by screenViewModel.uiState.collectAsState()
when (uiState) {
is Resource.Success -> {
Text(text = "Success")
}
is Resource.Error -> {
Text(text = "Error")
}
is Resource.Loading -> {
Text(text = "Loading")
}
}
}

@HiltViewModel
class MainViewModel @Inject constructor(
private val apiClient: ApiClient,
private val persistence: Persistence,
) : ViewModel() {
private val _uiState = MutableStateFlow<Resource<List<CryptoCurrencyRate>>>(Resource.Loading)
val uiState: StateFlow<Resource<List<CryptoCurrencyRate>>> = _uiState

init {
viewModelScope.launch(Dispatchers.IO) {
val test = either<ApiClientError, List<CryptoCurrencyRate>> {
val currencyRateResult = apiClient.fetchCurrencyRates().bind()
currencyRateResult
}

test.fold(
ifRight = { _uiState.value = Resource.Success(it) },
ifLeft = { _uiState.value = Resource.Error("failed api call") },
)
}
}
}
33 changes: 33 additions & 0 deletions app/src/main/java/com/greyhairredbear/racooin/ui/MainScreen.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package com.greyhairredbear.racooin.ui

import androidx.compose.foundation.layout.Column
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.lifecycle.viewmodel.compose.viewModel
import com.greyhairredbear.racooin.core.interfaces.Resource
import com.greyhairredbear.racooin.core.model.CryptoCurrencyRate

@Composable
fun MainScreen(
screenViewModel: MainViewModel = viewModel()
) {
val uiState by screenViewModel.uiState.collectAsState()
Column(horizontalAlignment = Alignment.CenterHorizontally) {
when (uiState) {
is Resource.Success -> {
(uiState as Resource.Success).data.forEach {
Text(text = "1 ${it.cryptoCurrency.name} = ${it.fiatBalance.balance} ${it.fiatBalance.fiatCurrency}")
}
}
is Resource.Error -> {
Text(text = "Error")
}
is Resource.Loading -> {
Text(text = "Loading")
}
}
}
}
40 changes: 40 additions & 0 deletions app/src/main/java/com/greyhairredbear/racooin/ui/MainViewModel.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package com.greyhairredbear.racooin.ui

import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import arrow.core.computations.either
import com.greyhairredbear.racooin.core.interfaces.ApiClient
import com.greyhairredbear.racooin.core.interfaces.Persistence
import com.greyhairredbear.racooin.core.interfaces.Resource
import com.greyhairredbear.racooin.core.model.ApiClientError
import com.greyhairredbear.racooin.core.model.CryptoCurrencyRate
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.launch
import javax.inject.Inject


@HiltViewModel
class MainViewModel @Inject constructor(
private val apiClient: ApiClient,
private val persistence: Persistence,
) : ViewModel() {
private val _uiState = MutableStateFlow<Resource<List<CryptoCurrencyRate>>>(Resource.Loading)
val uiState: StateFlow<Resource<List<CryptoCurrencyRate>>> = _uiState

init {
viewModelScope.launch(Dispatchers.IO) {
val test = either<ApiClientError, List<CryptoCurrencyRate>> {
val currencyRateResult = apiClient.fetchCurrencyRates().bind()
currencyRateResult
}

test.fold(
ifRight = { _uiState.value = Resource.Success(it) },
ifLeft = { _uiState.value = Resource.Error("failed api call") },
)
}
}
}
4 changes: 2 additions & 2 deletions buildSrc/src/main/kotlin/com/greyhairredbear/AppInfo.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ val PUBLISHING_GROUP = "com.greyhairredbear.racooin"
object AppInfo {
const val APP_ID = "com.greyhairredbear.racooin.app"

const val APP_VERSION_NAME = "1.0.0"
const val APP_VERSION_CODE = 1
const val APP_VERSION_NAME = "0.0.1"
const val APP_VERSION_CODE = 2
}

Original file line number Diff line number Diff line change
Expand Up @@ -6,25 +6,27 @@ import arrow.core.computations.either
import arrow.core.flatMap
import arrow.core.left
import arrow.core.right
import arrow.core.traverseEither
import com.greyhairredbear.racooin.core.model.ComputationFailedError
import com.greyhairredbear.racooin.core.model.CryptoBalance
import com.greyhairredbear.racooin.core.model.CryptoCurrencyRate
import com.greyhairredbear.racooin.core.model.FiatBalance

// TODO
// test
// error vs none
fun CryptoBalance.toFiatBalance(rate: CryptoCurrencyRate): Either<None, FiatBalance> {
// error modelling
fun CryptoBalance.toFiatBalance(rate: CryptoCurrencyRate): Either<ComputationFailedError, FiatBalance> {
return when {
cryptoCurrency != rate.cryptoCurrency -> None.left()
cryptoCurrency != rate.cryptoCurrency -> ComputationFailedError.left()
else -> rate.fiatBalance.copy(balance = balance * rate.fiatBalance.balance).right()
}
}

fun List<CryptoBalance>.toFiatBalances(rates: List<CryptoCurrencyRate>): Either<ComputationFailedError, List<FiatBalance>> {
return listOf<FiatBalance>().right() // TODO
// return map { cryptoBalance ->
// cryptoBalance.toFiatBalance(rates.first { it.cryptoCurrency == cryptoBalance.cryptoCurrency }) // TODO catch first exception
// .mapLeft { ComputationFailedError }
// }.right()
}
// TODO: support multiple rates
fun List<CryptoBalance>.toFiatBalances(rates: List<CryptoCurrencyRate>): Either<ComputationFailedError, List<FiatBalance>> =
traverseEither { balance ->
balance.toFiatBalance(
rates.firstOrNull { it.cryptoCurrency == balance.cryptoCurrency }
?: return ComputationFailedError.left()
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,11 @@ suspend fun calculateBalances(
persistence: Persistence,
): Either<UsecaseError, List<FiatBalance>> =
either {
val rates = getCurrencyRates(apiClient, persistence).bind()
val rates = getCurrencyRates(
{ persistence.fetchCurrencyRates() },
{ apiClient.fetchCurrencyRates() },
{ persistence.persistCurrencyRates(it) }
).bind()
val cryptoBalances = persistence.fetchAllCryptoBalances().bind()
val result = cryptoBalances.toFiatBalances(rates).bind()
result
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,21 @@ package com.greyhairredbear.racooin.core.usecase

import arrow.core.Either
import arrow.core.computations.either
import com.greyhairredbear.racooin.core.interfaces.ApiClient
import com.greyhairredbear.racooin.core.interfaces.Persistence
import com.greyhairredbear.racooin.core.model.ApiClientError
import com.greyhairredbear.racooin.core.model.CryptoCurrencyRate
import com.greyhairredbear.racooin.core.model.PersistenceError
import com.greyhairredbear.racooin.core.model.UsecaseError

suspend fun getCurrencyRates(
apiClient: ApiClient,
persistence: Persistence,
): Either<UsecaseError, List<CryptoCurrencyRate>> = either {
fetchRatesFromDb: suspend () -> Either<PersistenceError, List<CryptoCurrencyRate>>,
fetchRatesFromApi: suspend () -> Either<ApiClientError, List<CryptoCurrencyRate>>,
persistRates: suspend (List<CryptoCurrencyRate>) -> Either<PersistenceError, Unit>,
): Either<UsecaseError, List<CryptoCurrencyRate>> = either {
val rates = fetchRatesFromApi().bind()
val persistRatesResultIgnored = persistRates(rates).bind()
val result = fetchRatesFromDb().bind()
// TODO
// fetch from persistence, check timestamp
// fetch from persistence before api call, check timestamp
// timestamp longer than 1h ago -> reloadCurrenciesUsecase
// return
listOf()
result
}

0 comments on commit 659985e

Please # to comment.