Skip to content

Commit

Permalink
Add fallback behavior when unable to search coins on CoinGecko.
Browse files Browse the repository at this point in the history
  • Loading branch information
hwki committed Oct 30, 2022
1 parent fbcb57a commit 87ec758
Show file tree
Hide file tree
Showing 6 changed files with 68 additions and 20 deletions.
4 changes: 2 additions & 2 deletions bitcoin/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ android {
applicationId "com.brentpanther.bitcoinwidget"
minSdk 23
targetSdk 33
versionCode 313
versionName "8.3.3"
versionCode 314
versionName "8.3.4"

javaCompileOptions {
annotationProcessorOptions {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
import androidx.compose.ui.input.key.onKeyEvent
import androidx.compose.ui.input.key.onPreviewKeyEvent
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
import androidx.compose.ui.res.painterResource
Expand All @@ -34,6 +34,7 @@ import com.brentpanther.bitcoinwidget.Coin
import com.brentpanther.bitcoinwidget.R
import com.brentpanther.bitcoinwidget.Theme
import com.brentpanther.bitcoinwidget.ui.theme.HighlightRippleTheme
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch

@OptIn(ExperimentalComposeUiApi::class)
Expand All @@ -55,7 +56,18 @@ fun CoinSelectionScreen(
navController.navigateUp()
}
}
val scaffoldState: ScaffoldState = rememberScaffoldState()
LaunchedEffect(Unit) {
viewModel.error.collectLatest {
it?.let {
scaffoldState.snackbarHostState.showSnackbar(
message = context.getString(it)
)
}
}
}
Scaffold(
scaffoldState = scaffoldState,
topBar = {
TopAppBar(
title = {
Expand Down Expand Up @@ -99,11 +111,11 @@ fun CoinSelectionScreen(
modifier = Modifier
.fillMaxWidth()
.padding(8.dp)
.onKeyEvent {
.onPreviewKeyEvent {
if (it.nativeKeyEvent.keyCode == KEYCODE_ENTER) {
viewModel.search(searchText)
keyboardController?.hide()
return@onKeyEvent true
return@onPreviewKeyEvent true
}
false
}
Expand All @@ -119,7 +131,7 @@ fun CoinSelectionScreen(
}
result.coins.isEmpty() -> {
Box(contentAlignment = Alignment.Center, modifier = Modifier.fillMaxSize()) {
Text("No coins found.")
Text(stringResource(R.string.search_empty))
}
}
else -> {
Expand All @@ -138,7 +150,9 @@ fun CoinSelectionScreen(
@Composable
fun CoinList(coins: List<CoinResponse>, paddingValues: PaddingValues, onClick: (CoinResponse) -> Unit) {
val isNightMode = isSystemInDarkTheme()
val iconModifier = Modifier.padding(start = 4.dp, end = 8.dp).size(28.dp)
val iconModifier = Modifier
.padding(start = 4.dp, end = 8.dp)
.size(28.dp)
val customStartIndex = coins.indexOfFirst { it.coin == Coin.CUSTOM }
Column {
LazyColumn(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
package com.brentpanther.bitcoinwidget.ui.selection

import android.app.Application
import android.content.Context
import android.os.Build
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.brentpanther.bitcoinwidget.*
import com.brentpanther.bitcoinwidget.db.Widget
Expand All @@ -17,12 +16,14 @@ import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import okhttp3.OkHttpClient
import okhttp3.Request
import java.io.IOException

class CoinSelectionViewModel(application: Application) : AndroidViewModel(application) {
class CoinSelectionViewModel : ViewModel() {

private var allCoins = Coin.values().filterNot { it == Coin.CUSTOM }.associateBy { it.coinGeckoId }

val coins = MutableStateFlow<SearchResponse?>(null)
val error = MutableStateFlow<Int?>(null)

suspend fun createWidget(context: Context, widgetId: Int, coinEntry: CoinResponse) = withContext(Dispatchers.IO) {
val widgetType = WidgetApplication.instance.getWidgetType(widgetId)
Expand Down Expand Up @@ -63,26 +64,53 @@ class CoinSelectionViewModel(application: Application) : AndroidViewModel(applic
fun search(query: String) {
searchJob?.cancel()
searchJob = viewModelScope.launch(Dispatchers.IO) {
error.emit(null)
coins.emit(SearchResponse(emptyList(), true))
val responses = searchCoinGecko(query)
var coinResults = when {
responses != null -> {
responses.coins.map {
allCoins[it.id]?.let { coin ->
CoinResponse(coin.name, coin.coinName, coin.getSymbol(), null, null, coin)
} ?: it.apply { coin = Coin.CUSTOM }
}
}
else -> {
allCoins.values.filter {
it.coinName.contains(query, ignoreCase = true) || it.getSymbol().contains(query, ignoreCase = true)
}.map { coin ->
CoinResponse(coin.name, coin.coinName, coin.getSymbol(), null, null, coin)
}
}
}
coinResults = coinResults.sortedWith(
compareBy(
{ it.coin == Coin.CUSTOM },
{ it.name.uppercase() }
))
coins.emit(SearchResponse(coinResults, false))
}
}

private fun searchCoinGecko(query: String): SearchResponse? {
try {
val client = OkHttpClient()
val request = Request.Builder()
.url("https://api.coingecko.com/api/v3/search?query=$query")
.addHeader("Accept", "application/json")
.build()
client.newCall(request).execute().use { response ->
if (response.isSuccessful) {
val responses = Gson().fromJson(response.body!!.charStream(), SearchResponse::class.java)
val coinResults = responses.coins.map {
allCoins[it.id]?.let { coin ->
CoinResponse(coin.name, coin.coinName, coin.getSymbol(), null, null, coin)
} ?: it.apply { coin = Coin.CUSTOM }
}.sortedWith( compareBy(
{ it.coin == Coin.CUSTOM },
{ it.name.uppercase() }
))
coins.emit(SearchResponse(coinResults, false))
error.tryEmit(null)
return Gson().fromJson(response.body!!.charStream(), SearchResponse::class.java)
} else {
error.tryEmit(R.string.search_error)
return null
}
}
} catch (e: IOException) {
error.tryEmit(R.string.search_error)
return null
}
}

Expand Down
2 changes: 2 additions & 0 deletions bitcoin/src/main/res/values-pt-rBR/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -92,5 +92,7 @@
<string name="widget_price_summary">Mostra o preço de negociação atual do %s.</string>
<string name="widget_value_summary">Mostra o valor atual de sua carteira %s.</string>
<string name="dismiss">Liberar</string>
<string name="search_error">Erro ao obter moedas do CoinGecko.</string>
<string name="search_empty">Nenhuma moeda encontrada.</string>

</resources>
2 changes: 2 additions & 0 deletions bitcoin/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -130,5 +130,7 @@
<string name="widget_price_summary">Shows the current %s trading price.</string>
<string name="widget_value_summary">Shows the current value of your %s wallet.</string>
<string name="dismiss">Dismiss</string>
<string name="search_error">Error retrieving coins from CoinGecko.</string>
<string name="search_empty">No coins found.</string>

</resources>
2 changes: 2 additions & 0 deletions fastlane/metadata/android/en-US/changelogs/314.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Fixed Bitcoin Cash not being available.
Better error handling on search screen.

0 comments on commit 87ec758

Please sign in to comment.