Skip to content

Commit 99f3dca

Browse files
committed
Merge branch 'change-currency' into main
2 parents 810bf29 + 8b224be commit 99f3dca

27 files changed

+514
-186
lines changed

app/src/main/java/dev/shorthouse/coinwatch/data/datastore/UserPreferences.kt

+4-6
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
package dev.shorthouse.coinwatch.data.datastore
22

3-
import androidx.annotation.StringRes
4-
import dev.shorthouse.coinwatch.R
53
import kotlinx.serialization.Serializable
64

75
@Serializable
@@ -10,10 +8,10 @@ data class UserPreferences(
108
val coinSort: CoinSort = CoinSort.MarketCap
119
)
1210

13-
enum class Currency(@StringRes val nameStringId: Int) {
14-
USD(R.string.currency_usd),
15-
GBP(R.string.currency_gbp),
16-
EUR(R.string.currency_eur)
11+
enum class Currency(val symbol: String) {
12+
USD("$"),
13+
GBP("£"),
14+
EUR("")
1715
}
1816

1917
enum class CoinSort {

app/src/main/java/dev/shorthouse/coinwatch/data/mapper/CoinChartMapper.kt

+5-5
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
package dev.shorthouse.coinwatch.data.mapper
22

3-
import dev.shorthouse.coinwatch.common.Mapper
43
import dev.shorthouse.coinwatch.common.toSanitisedBigDecimalOrNull
4+
import dev.shorthouse.coinwatch.data.datastore.Currency
55
import dev.shorthouse.coinwatch.data.source.remote.model.CoinChartApiModel
66
import dev.shorthouse.coinwatch.model.CoinChart
77
import dev.shorthouse.coinwatch.model.Percentage
@@ -10,8 +10,8 @@ import java.math.BigDecimal
1010
import javax.inject.Inject
1111
import kotlinx.collections.immutable.toPersistentList
1212

13-
class CoinChartMapper @Inject constructor() : Mapper<CoinChartApiModel, CoinChart> {
14-
override fun mapApiModelToModel(from: CoinChartApiModel): CoinChart {
13+
class CoinChartMapper @Inject constructor() {
14+
fun mapApiModelToModel(from: CoinChartApiModel, currency: Currency): CoinChart {
1515
val validPrices = from.coinChartData?.pastPrices
1616
.orEmpty()
1717
.mapNotNull { pastPrice ->
@@ -32,8 +32,8 @@ class CoinChartMapper @Inject constructor() : Mapper<CoinChartApiModel, CoinChar
3232

3333
return CoinChart(
3434
prices = validPrices.toPersistentList(),
35-
minPrice = Price(minPrice),
36-
maxPrice = Price(maxPrice),
35+
minPrice = Price(minPrice, currency = currency),
36+
maxPrice = Price(maxPrice, currency = currency),
3737
periodPriceChangePercentage = Percentage(from.coinChartData?.priceChangePercentage)
3838
)
3939
}

app/src/main/java/dev/shorthouse/coinwatch/data/mapper/CoinDetailsMapper.kt

+6-6
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
package dev.shorthouse.coinwatch.data.mapper
22

3-
import dev.shorthouse.coinwatch.common.Mapper
3+
import dev.shorthouse.coinwatch.data.datastore.Currency
44
import dev.shorthouse.coinwatch.data.source.remote.model.CoinDetailsApiModel
55
import dev.shorthouse.coinwatch.model.CoinDetails
66
import dev.shorthouse.coinwatch.model.Price
@@ -12,7 +12,7 @@ import java.time.format.DateTimeFormatter
1212
import java.util.Locale
1313
import javax.inject.Inject
1414

15-
class CoinDetailsMapper @Inject constructor() : Mapper<CoinDetailsApiModel, CoinDetails> {
15+
class CoinDetailsMapper @Inject constructor() {
1616
companion object {
1717
private val dateFormatter = DateTimeFormatter.ofPattern("d MMM yyyy", Locale.US)
1818

@@ -21,20 +21,20 @@ class CoinDetailsMapper @Inject constructor() : Mapper<CoinDetailsApiModel, Coin
2121
}
2222
}
2323

24-
override fun mapApiModelToModel(from: CoinDetailsApiModel): CoinDetails {
24+
fun mapApiModelToModel(from: CoinDetailsApiModel, currency: Currency): CoinDetails {
2525
val coinDetails = from.coinDetailsDataHolder?.coinDetailsData
2626

2727
return CoinDetails(
2828
id = coinDetails?.id.orEmpty(),
2929
name = coinDetails?.name.orEmpty(),
3030
symbol = coinDetails?.symbol.orEmpty(),
3131
imageUrl = coinDetails?.imageUrl.orEmpty(),
32-
currentPrice = Price(coinDetails?.currentPrice),
33-
marketCap = Price(coinDetails?.marketCap),
32+
currentPrice = Price(coinDetails?.currentPrice, currency = currency),
33+
marketCap = Price(coinDetails?.marketCap, currency = currency),
3434
marketCapRank = coinDetails?.marketCapRank.orEmpty(),
3535
volume24h = formatNumberOrEmpty(coinDetails?.volume24h),
3636
circulatingSupply = formatNumberOrEmpty(coinDetails?.supply?.circulatingSupply),
37-
allTimeHigh = Price(coinDetails?.allTimeHigh?.price),
37+
allTimeHigh = Price(coinDetails?.allTimeHigh?.price, currency = currency),
3838
allTimeHighDate = epochToDateOrEmpty(coinDetails?.allTimeHigh?.timestamp),
3939
listedDate = epochToDateOrEmpty(coinDetails?.listedAt)
4040
)

app/src/main/java/dev/shorthouse/coinwatch/data/mapper/CoinMapper.kt

+4-4
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
package dev.shorthouse.coinwatch.data.mapper
22

3-
import dev.shorthouse.coinwatch.common.Mapper
3+
import dev.shorthouse.coinwatch.data.datastore.Currency
44
import dev.shorthouse.coinwatch.data.source.remote.model.CoinsApiModel
55
import dev.shorthouse.coinwatch.model.Coin
66
import dev.shorthouse.coinwatch.model.Percentage
@@ -9,8 +9,8 @@ import java.math.BigDecimal
99
import javax.inject.Inject
1010
import kotlinx.collections.immutable.toPersistentList
1111

12-
class CoinMapper @Inject constructor() : Mapper<CoinsApiModel, List<Coin>> {
13-
override fun mapApiModelToModel(from: CoinsApiModel): List<Coin> {
12+
class CoinMapper @Inject constructor() {
13+
fun mapApiModelToModel(from: CoinsApiModel, currency: Currency): List<Coin> {
1414
val validCoins = from.coinsData?.coins
1515
.orEmpty()
1616
.filterNotNull()
@@ -22,7 +22,7 @@ class CoinMapper @Inject constructor() : Mapper<CoinsApiModel, List<Coin>> {
2222
name = coinApiModel.name.orEmpty(),
2323
symbol = coinApiModel.symbol.orEmpty(),
2424
imageUrl = coinApiModel.imageUrl.orEmpty(),
25-
currentPrice = Price(coinApiModel.currentPrice),
25+
currentPrice = Price(coinApiModel.currentPrice, currency = currency),
2626
priceChangePercentage24h = Percentage(coinApiModel.priceChangePercentage24h),
2727
prices24h = coinApiModel.prices24h
2828
.orEmpty()
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,14 @@
11
package dev.shorthouse.coinwatch.data.repository.chart
22

33
import dev.shorthouse.coinwatch.common.Result
4+
import dev.shorthouse.coinwatch.data.datastore.Currency
45
import dev.shorthouse.coinwatch.model.CoinChart
56
import kotlinx.coroutines.flow.Flow
67

78
interface CoinChartRepository {
8-
fun getCoinChart(coinId: String, chartPeriod: String): Flow<Result<CoinChart>>
9+
fun getCoinChart(
10+
coinId: String,
11+
chartPeriod: String,
12+
currency: Currency
13+
): Flow<Result<CoinChart>>
914
}

app/src/main/java/dev/shorthouse/coinwatch/data/repository/chart/CoinChartRepositoryImpl.kt

+9-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package dev.shorthouse.coinwatch.data.repository.chart
22

33
import dev.shorthouse.coinwatch.common.Result
4+
import dev.shorthouse.coinwatch.data.datastore.Currency
45
import dev.shorthouse.coinwatch.data.mapper.CoinChartMapper
56
import dev.shorthouse.coinwatch.data.source.remote.CoinNetworkDataSource
67
import dev.shorthouse.coinwatch.di.IoDispatcher
@@ -18,16 +19,21 @@ class CoinChartRepositoryImpl @Inject constructor(
1819
private val coinChartMapper: CoinChartMapper,
1920
@IoDispatcher private val ioDispatcher: CoroutineDispatcher
2021
) : CoinChartRepository {
21-
override fun getCoinChart(coinId: String, chartPeriod: String): Flow<Result<CoinChart>> = flow {
22+
override fun getCoinChart(
23+
coinId: String,
24+
chartPeriod: String,
25+
currency: Currency
26+
): Flow<Result<CoinChart>> = flow {
2227
val response = coinNetworkDataSource.getCoinChart(
2328
coinId = coinId,
24-
chartPeriod = chartPeriod
29+
chartPeriod = chartPeriod,
30+
currency = currency
2531
)
2632

2733
val body = response.body()
2834

2935
if (response.isSuccessful && body?.coinChartData != null) {
30-
val coinChart = coinChartMapper.mapApiModelToModel(body)
36+
val coinChart = coinChartMapper.mapApiModelToModel(body, currency = currency)
3137
emit(Result.Success(coinChart))
3238
} else {
3339
Timber.e("getCoinChart unsuccessful retrofit response ${response.message()}")

app/src/main/java/dev/shorthouse/coinwatch/data/repository/coin/CoinRepository.kt

+3-1
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,14 @@ package dev.shorthouse.coinwatch.data.repository.coin
22

33
import dev.shorthouse.coinwatch.common.Result
44
import dev.shorthouse.coinwatch.data.datastore.CoinSort
5+
import dev.shorthouse.coinwatch.data.datastore.Currency
56
import dev.shorthouse.coinwatch.model.Coin
67
import kotlinx.coroutines.flow.Flow
78

89
interface CoinRepository {
910
fun getCoins(
1011
coinIds: List<String> = emptyList(),
11-
coinSort: CoinSort = CoinSort.MarketCap
12+
coinSort: CoinSort = CoinSort.MarketCap,
13+
currency: Currency = Currency.USD
1214
): Flow<Result<List<Coin>>>
1315
}

app/src/main/java/dev/shorthouse/coinwatch/data/repository/coin/CoinRepositoryImpl.kt

+9-3
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package dev.shorthouse.coinwatch.data.repository.coin
22

33
import dev.shorthouse.coinwatch.common.Result
44
import dev.shorthouse.coinwatch.data.datastore.CoinSort
5+
import dev.shorthouse.coinwatch.data.datastore.Currency
56
import dev.shorthouse.coinwatch.data.mapper.CoinMapper
67
import dev.shorthouse.coinwatch.data.source.remote.CoinNetworkDataSource
78
import dev.shorthouse.coinwatch.di.IoDispatcher
@@ -19,17 +20,22 @@ class CoinRepositoryImpl @Inject constructor(
1920
private val coinMapper: CoinMapper,
2021
@IoDispatcher private val ioDispatcher: CoroutineDispatcher
2122
) : CoinRepository {
22-
override fun getCoins(coinIds: List<String>, coinSort: CoinSort): Flow<Result<List<Coin>>> =
23+
override fun getCoins(
24+
coinIds: List<String>,
25+
coinSort: CoinSort,
26+
currency: Currency
27+
): Flow<Result<List<Coin>>> =
2328
flow {
2429
val response = coinNetworkDataSource.getCoins(
2530
coinIds = coinIds,
26-
coinSort = coinSort
31+
coinSort = coinSort,
32+
currency = currency
2733
)
2834

2935
val body = response.body()
3036

3137
if (response.isSuccessful && body?.coinsData != null) {
32-
val coins = coinMapper.mapApiModelToModel(body)
38+
val coins = coinMapper.mapApiModelToModel(body, currency = currency)
3339
emit(Result.Success(coins))
3440
} else {
3541
Timber.e("getCoins unsuccessful retrofit response ${response.message()}")
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
package dev.shorthouse.coinwatch.data.repository.details
22

33
import dev.shorthouse.coinwatch.common.Result
4+
import dev.shorthouse.coinwatch.data.datastore.Currency
45
import dev.shorthouse.coinwatch.model.CoinDetails
56
import kotlinx.coroutines.flow.Flow
67

78
interface CoinDetailsRepository {
8-
fun getCoinDetails(coinId: String): Flow<Result<CoinDetails>>
9+
fun getCoinDetails(coinId: String, currency: Currency): Flow<Result<CoinDetails>>
910
}
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package dev.shorthouse.coinwatch.data.repository.details
22

33
import dev.shorthouse.coinwatch.common.Result
4+
import dev.shorthouse.coinwatch.data.datastore.Currency
45
import dev.shorthouse.coinwatch.data.mapper.CoinDetailsMapper
56
import dev.shorthouse.coinwatch.data.source.remote.CoinNetworkDataSourceImpl
67
import dev.shorthouse.coinwatch.di.IoDispatcher
@@ -18,19 +19,24 @@ class CoinDetailsRepositoryImpl @Inject constructor(
1819
private val coinDetailsMapper: CoinDetailsMapper,
1920
@IoDispatcher private val ioDispatcher: CoroutineDispatcher
2021
) : CoinDetailsRepository {
21-
override fun getCoinDetails(coinId: String): Flow<Result<CoinDetails>> = flow {
22-
val response = coinNetworkDataSource.getCoinDetails(coinId = coinId)
23-
val body = response.body()
22+
override fun getCoinDetails(coinId: String, currency: Currency): Flow<Result<CoinDetails>> =
23+
flow {
24+
val response = coinNetworkDataSource.getCoinDetails(
25+
coinId = coinId,
26+
currency = currency
27+
)
2428

25-
if (response.isSuccessful && body?.coinDetailsDataHolder?.coinDetailsData != null) {
26-
val coinDetails = coinDetailsMapper.mapApiModelToModel(body)
27-
emit(Result.Success(coinDetails))
28-
} else {
29-
Timber.e("getCoinDetails unsuccessful retrofit response ${response.message()}")
29+
val body = response.body()
30+
31+
if (response.isSuccessful && body?.coinDetailsDataHolder?.coinDetailsData != null) {
32+
val coinDetails = coinDetailsMapper.mapApiModelToModel(body, currency = currency)
33+
emit(Result.Success(coinDetails))
34+
} else {
35+
Timber.e("getCoinDetails unsuccessful retrofit response ${response.message()}")
36+
emit(Result.Error("Unable to fetch coin details"))
37+
}
38+
}.catch { e ->
39+
Timber.e("getCoinDetails exception ${e.message}")
3040
emit(Result.Error("Unable to fetch coin details"))
31-
}
32-
}.catch { e ->
33-
Timber.e("getCoinDetails exception ${e.message}")
34-
emit(Result.Error("Unable to fetch coin details"))
35-
}.flowOn(ioDispatcher)
41+
}.flowOn(ioDispatcher)
3642
}

app/src/main/java/dev/shorthouse/coinwatch/data/source/remote/CoinNetworkDataSource.kt

+12-3
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,29 @@
11
package dev.shorthouse.coinwatch.data.source.remote
22

33
import dev.shorthouse.coinwatch.data.datastore.CoinSort
4+
import dev.shorthouse.coinwatch.data.datastore.Currency
45
import dev.shorthouse.coinwatch.data.source.remote.model.CoinChartApiModel
56
import dev.shorthouse.coinwatch.data.source.remote.model.CoinDetailsApiModel
67
import dev.shorthouse.coinwatch.data.source.remote.model.CoinSearchResultsApiModel
78
import dev.shorthouse.coinwatch.data.source.remote.model.CoinsApiModel
89
import retrofit2.Response
910

1011
interface CoinNetworkDataSource {
11-
suspend fun getCoins(coinIds: List<String>, coinSort: CoinSort): Response<CoinsApiModel>
12+
suspend fun getCoins(
13+
coinIds: List<String>,
14+
coinSort: CoinSort,
15+
currency: Currency
16+
): Response<CoinsApiModel>
1217

13-
suspend fun getCoinDetails(coinId: String): Response<CoinDetailsApiModel>
18+
suspend fun getCoinDetails(
19+
coinId: String,
20+
currency: Currency
21+
): Response<CoinDetailsApiModel>
1422

1523
suspend fun getCoinChart(
1624
coinId: String,
17-
chartPeriod: String
25+
chartPeriod: String,
26+
currency: Currency
1827
): Response<CoinChartApiModel>
1928

2029
suspend fun getCoinSearchResults(searchQuery: String): Response<CoinSearchResultsApiModel>
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package dev.shorthouse.coinwatch.data.source.remote
22

33
import dev.shorthouse.coinwatch.data.datastore.CoinSort
4+
import dev.shorthouse.coinwatch.data.datastore.Currency
45
import dev.shorthouse.coinwatch.data.source.remote.model.CoinChartApiModel
56
import dev.shorthouse.coinwatch.data.source.remote.model.CoinDetailsApiModel
67
import dev.shorthouse.coinwatch.data.source.remote.model.CoinSearchResultsApiModel
@@ -13,27 +14,36 @@ class CoinNetworkDataSourceImpl @Inject constructor(
1314
) : CoinNetworkDataSource {
1415
override suspend fun getCoins(
1516
coinIds: List<String>,
16-
coinSort: CoinSort
17+
coinSort: CoinSort,
18+
currency: Currency
1719
): Response<CoinsApiModel> {
18-
val orderBy = when (coinSort) {
19-
CoinSort.MarketCap -> "marketCap"
20-
CoinSort.Price -> "price"
21-
CoinSort.PriceChange24h -> "change"
22-
CoinSort.Volume24h -> "24hVolume"
23-
}
24-
25-
return coinApi.getCoins(coinIds = coinIds, orderBy = orderBy)
20+
return coinApi.getCoins(
21+
coinIds = coinIds,
22+
orderBy = coinSort.toOrderByString(),
23+
currencyUUID = currency.toCurrencyUUID()
24+
)
2625
}
2726

28-
override suspend fun getCoinDetails(coinId: String): Response<CoinDetailsApiModel> {
29-
return coinApi.getCoinDetails(coinId = coinId)
27+
override suspend fun getCoinDetails(
28+
coinId: String,
29+
currency: Currency
30+
): Response<CoinDetailsApiModel> {
31+
return coinApi.getCoinDetails(
32+
coinId = coinId,
33+
currencyUUID = currency.toCurrencyUUID()
34+
)
3035
}
3136

3237
override suspend fun getCoinChart(
3338
coinId: String,
34-
chartPeriod: String
39+
chartPeriod: String,
40+
currency: Currency
3541
): Response<CoinChartApiModel> {
36-
return coinApi.getCoinChart(coinId = coinId, chartPeriod = chartPeriod)
42+
return coinApi.getCoinChart(
43+
coinId = coinId,
44+
chartPeriod = chartPeriod,
45+
currencyUUID = currency.toCurrencyUUID()
46+
)
3747
}
3848

3949
override suspend fun getCoinSearchResults(
@@ -42,3 +52,20 @@ class CoinNetworkDataSourceImpl @Inject constructor(
4252
return coinApi.getCoinSearchResults(searchQuery = searchQuery)
4353
}
4454
}
55+
56+
private fun CoinSort.toOrderByString(): String {
57+
return when (this) {
58+
CoinSort.MarketCap -> "marketCap"
59+
CoinSort.Price -> "price"
60+
CoinSort.PriceChange24h -> "change"
61+
CoinSort.Volume24h -> "24hVolume"
62+
}
63+
}
64+
65+
private fun Currency.toCurrencyUUID(): String {
66+
return when (this) {
67+
Currency.USD -> "yhjMzLPhuIDl"
68+
Currency.GBP -> "Hokyui45Z38f"
69+
Currency.EUR -> "5k-_VTxqtCEI"
70+
}
71+
}

0 commit comments

Comments
 (0)