Skip to content

Commit 0ad2eae

Browse files
committed
chore(currency): use rates/currency through Exchange
Signed-off-by: Brandon McAnsh <[email protected]>
1 parent 7e53605 commit 0ad2eae

File tree

21 files changed

+196
-211
lines changed

21 files changed

+196
-211
lines changed

api/src/main/java/com/getcode/model/CurrencyCode.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -316,9 +316,9 @@ enum class CurrencyCode {
316316
}
317317

318318
companion object {
319-
fun tryValueOf(value: String): CurrencyCode? {
319+
fun tryValueOf(value: String?): CurrencyCode? {
320320
return try {
321-
valueOf(value.uppercase())
321+
valueOf(value?.uppercase().orEmpty())
322322
} catch (e: Exception) {
323323
null
324324
}

api/src/main/java/com/getcode/model/PrefString.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ data class PrefString(
1212
enum class PrefsString(val value: String) {
1313
KEY_USER_ID("user_id"),
1414
KEY_DATA_CONTAINER_ID("data_container_id"),
15-
KEY_GIVE_CURRENCY_SELECTED("currency_selected"),//keep
15+
KEY_ENTRY_CURRENCY("currency_selected"),//keep
1616
KEY_CURRENCIES_RECENT("currencies_recent"),//keep
1717
KEY_TIP_ACCOUNT("tip_account"),
18-
KEY_PREFERRED_APP_CURRENCY("balance_currency_selected")
18+
KEY_LOCAL_CURRENCY("balance_currency_selected")
1919
}

api/src/main/java/com/getcode/network/BalanceController.kt

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,19 @@
11
package com.getcode.network
22

3-
import android.content.Context
43
import com.getcode.manager.SessionManager
54
import com.getcode.model.Currency
65
import com.getcode.model.CurrencyCode
7-
import com.getcode.model.PrefsString
86
import com.getcode.model.Rate
97
import com.getcode.network.client.TransactionReceiver
108
import com.getcode.network.exchange.Exchange
119
import com.getcode.network.repository.AccountRepository
1210
import com.getcode.network.repository.BalanceRepository
13-
import com.getcode.network.repository.PrefRepository
1411
import com.getcode.network.repository.TransactionRepository
1512
import com.getcode.solana.organizer.Organizer
1613
import com.getcode.solana.organizer.Tray
1714
import com.getcode.utils.FormatUtils
1815
import com.getcode.utils.network.NetworkConnectivityListener
1916
import com.getcode.utils.trace
20-
import dagger.hilt.android.qualifiers.ApplicationContext
2117
import io.reactivex.rxjava3.core.Completable
2218
import kotlinx.coroutines.CoroutineScope
2319
import kotlinx.coroutines.Dispatchers
@@ -27,12 +23,9 @@ import kotlinx.coroutines.flow.SharingStarted
2723
import kotlinx.coroutines.flow.StateFlow
2824
import kotlinx.coroutines.flow.combine
2925
import kotlinx.coroutines.flow.distinctUntilChanged
30-
import kotlinx.coroutines.flow.flatMapLatest
31-
import kotlinx.coroutines.flow.flowOf
3226
import kotlinx.coroutines.flow.flowOn
3327
import kotlinx.coroutines.flow.launchIn
3428
import kotlinx.coroutines.flow.map
35-
import kotlinx.coroutines.flow.mapNotNull
3629
import kotlinx.coroutines.flow.onEach
3730
import kotlinx.coroutines.flow.stateIn
3831
import kotlinx.coroutines.launch

api/src/main/java/com/getcode/network/exchange/Exchange.kt

Lines changed: 42 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,7 @@ import kotlinx.coroutines.CoroutineScope
1616
import kotlinx.coroutines.Dispatchers
1717
import kotlinx.coroutines.flow.Flow
1818
import kotlinx.coroutines.flow.MutableStateFlow
19-
import kotlinx.coroutines.flow.combine
20-
import kotlinx.coroutines.flow.distinctUntilChanged
2119
import kotlinx.coroutines.flow.emptyFlow
22-
import kotlinx.coroutines.flow.filterNotNull
23-
import kotlinx.coroutines.flow.flatMapLatest
24-
import kotlinx.coroutines.flow.flowOf
2520
import kotlinx.coroutines.flow.launchIn
2621
import kotlinx.coroutines.flow.map
2722
import kotlinx.coroutines.flow.mapNotNull
@@ -42,6 +37,7 @@ interface Exchange {
4237
fun observeLocalRate(): Flow<Rate>
4338

4439
val entryRate: Rate
40+
fun observeEntryRate(): Flow<Rate>
4541

4642
fun rates(): Map<CurrencyCode, Rate>
4743
fun observeRates(): Flow<Map<CurrencyCode, Rate>>
@@ -60,10 +56,15 @@ class ExchangeNull : Exchange {
6056
override val entryRate: Rate
6157
get() = Rate.oneToOne
6258

59+
6360
override fun observeLocalRate(): Flow<Rate> {
6461
return emptyFlow()
6562
}
6663

64+
override fun observeEntryRate(): Flow<Rate> {
65+
return emptyFlow()
66+
}
67+
6768
override fun rates(): Map<CurrencyCode, Rate> {
6869
return emptyMap()
6970
}
@@ -88,14 +89,17 @@ class CodeExchange @Inject constructor(
8889
private val currencyApi: CurrencyApi,
8990
private val networkOracle: NetworkOracle,
9091
prefs: PrefRepository,
92+
private val preferredCurrency: suspend () -> Currency?,
9193
private val defaultCurrency: suspend () -> Currency?,
9294
) : Exchange, CoroutineScope by CoroutineScope(Dispatchers.IO) {
9395

9496
private val db = Database.getInstance()
9597

96-
private var _entryRate: Rate = Rate.oneToOne
98+
private var _entryRate = MutableStateFlow(Rate.oneToOne)
9799
override val entryRate: Rate
98-
get() = _entryRate
100+
get() = _entryRate.value
101+
102+
override fun observeEntryRate(): Flow<Rate> = _entryRate
99103

100104
private val _localRate = MutableStateFlow(Rate.oneToOne)
101105
override val localRate
@@ -105,6 +109,7 @@ class CodeExchange @Inject constructor(
105109

106110
private var rateDate: Long = System.currentTimeMillis()
107111

112+
private var localCurrency: CurrencyCode? = null
108113
private var entryCurrency: CurrencyCode? = null
109114

110115
private val _rates = MutableStateFlow(emptyMap<CurrencyCode, Rate>())
@@ -128,6 +133,19 @@ class CodeExchange @Inject constructor(
128133
}
129134

130135
init {
136+
launch {
137+
localCurrency = CurrencyCode.tryValueOf(preferredCurrency()?.code.orEmpty())
138+
entryCurrency = CurrencyCode.tryValueOf(defaultCurrency()?.code.orEmpty())
139+
140+
prefs.observeOrDefault(PrefsString.KEY_ENTRY_CURRENCY, "")
141+
.map { it.takeIf { it.isNotEmpty() } }
142+
.map { CurrencyCode.tryValueOf(it.orEmpty()) }
143+
.mapNotNull { preferred ->
144+
preferred ?: CurrencyCode.tryValueOf(defaultCurrency()?.code.orEmpty())
145+
}.onEach { setEntryCurrency(it) }
146+
.launchIn(this)
147+
}
148+
131149
launch {
132150
db?.exchangeDao()?.query()?.let { exchangeData ->
133151
val rates = exchangeData.map { Rate(it.fx, it.currency) }
@@ -138,18 +156,13 @@ class CodeExchange @Inject constructor(
138156
fetchRatesIfNeeded()
139157
}
140158

141-
prefs.observeOrDefault(PrefsString.KEY_PREFERRED_APP_CURRENCY, "")
159+
prefs.observeOrDefault(PrefsString.KEY_LOCAL_CURRENCY, "")
142160
.map { it.takeIf { it.isNotEmpty() } }
143-
.filterNotNull()
144-
.mapNotNull { CurrencyCode.tryValueOf(it) }
145-
.flatMapLatest {
146-
combine(observeRates(), flowOf(it)) { rates, currencyCode -> rates[currencyCode] }
147-
}.filterNotNull()
148-
.distinctUntilChanged()
149-
.onEach {
150-
trace("Local rate updated=${it.currency}", type = TraceType.Log)
151-
_localRate.value = it
152-
}.launchIn(this)
161+
.map { CurrencyCode.tryValueOf(it.orEmpty()) }
162+
.mapNotNull { preferred ->
163+
preferred ?: CurrencyCode.tryValueOf(defaultCurrency()?.code.orEmpty())
164+
}.onEach { setLocalCurrency(it) }
165+
.launchIn(this)
153166
}
154167

155168
override suspend fun fetchRatesIfNeeded() {
@@ -164,11 +177,16 @@ class CodeExchange @Inject constructor(
164177
}
165178
}
166179

167-
private suspend fun set(currency: CurrencyCode) {
180+
private suspend fun setEntryCurrency(currency: CurrencyCode) {
168181
entryCurrency = currency
169182
updateRates()
170183
}
171184

185+
private suspend fun setLocalCurrency(currency: CurrencyCode) {
186+
localCurrency = currency
187+
updateRates()
188+
}
189+
172190
private suspend fun set(ratesBox: RatesBox) {
173191
rates = ratesBox
174192
rateDate = ratesBox.dateMillis
@@ -182,7 +200,7 @@ class CodeExchange @Inject constructor(
182200
return
183201
}
184202

185-
val localRegionCurrency = defaultCurrency() ?: return
203+
val localRegionCurrency = preferredCurrency() ?: return
186204
val currency = CurrencyCode.tryValueOf(localRegionCurrency.code)
187205
entryCurrency = currency
188206
}
@@ -196,16 +214,15 @@ class CodeExchange @Inject constructor(
196214
return
197215
}
198216

199-
val localCurrency = CurrencyCode.tryValueOf(defaultCurrency()?.code.orEmpty())
200-
val rate = localCurrency?.let { rates.rateFor(it) }
201-
_localRate.value = if (rate != null) {
217+
val localRate = localCurrency?.let { rates.rateFor(it) }
218+
_localRate.value = if (localRate != null) {
202219
trace(
203220
message = "Updated the local currency: $localCurrency, " +
204221
"Staleness ${System.currentTimeMillis() - rates.dateMillis} ms, " +
205222
"Date: ${Date(rates.dateMillis)}",
206223
type = TraceType.Silent
207224
)
208-
rate
225+
localRate
209226
} else {
210227
trace(
211228
message = "local:: Rate for $localCurrency not found. Defaulting to USD.",
@@ -216,7 +233,7 @@ class CodeExchange @Inject constructor(
216233

217234

218235
val entryRate = entryCurrency?.let { rates.rateFor(it) }
219-
this._entryRate = if (entryRate != null) {
236+
_entryRate.value = if (entryRate != null) {
220237
trace(
221238
message = "Updated the entry currency: $entryCurrency, " +
222239
"Staleness ${System.currentTimeMillis() - rates.dateMillis} ms, " +

api/src/main/java/com/getcode/utils/Logging.kt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,11 @@ sealed interface TraceType {
1313

1414
data object Silent : TraceType
1515

16+
/**
17+
* An error event
18+
*/
19+
data object Error: TraceType
20+
1621
/**
1722
* A log message
1823
*/
@@ -47,6 +52,7 @@ sealed interface TraceType {
4752
private fun TraceType.toBugsnagBreadcrumbType(): BreadcrumbType? {
4853
return when (this) {
4954
TraceType.Silent -> null
55+
TraceType.Error -> BreadcrumbType.ERROR
5056
TraceType.Log -> BreadcrumbType.LOG
5157
TraceType.Navigation -> BreadcrumbType.NAVIGATION
5258
TraceType.Network -> BreadcrumbType.REQUEST

app/src/main/java/com/getcode/inject/ApiModule.kt

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -147,15 +147,21 @@ object ApiModule {
147147
locale: LocaleHelper,
148148
currencyUtils: CurrencyUtils,
149149
prefRepository: PrefRepository,
150-
): Exchange = CodeExchange(currencyApi, networkOracle, prefRepository) {
151-
val preferredCurrencyCode = prefRepository.get(
152-
PrefsString.KEY_PREFERRED_APP_CURRENCY,
153-
""
154-
).takeIf { it.isNotEmpty() }
155-
156-
val preferredCurrency = preferredCurrencyCode?.let { currencyUtils.getCurrency(it) }
157-
preferredCurrency ?: locale.getDefaultCurrency()
158-
}
150+
): Exchange = CodeExchange(
151+
currencyApi = currencyApi,
152+
networkOracle = networkOracle,
153+
prefs = prefRepository,
154+
preferredCurrency = {
155+
val preferredCurrencyCode = prefRepository.get(
156+
PrefsString.KEY_LOCAL_CURRENCY,
157+
""
158+
).takeIf { it.isNotEmpty() }
159+
160+
val preferredCurrency = preferredCurrencyCode?.let { currencyUtils.getCurrency(it) }
161+
preferredCurrency ?: locale.getDefaultCurrency()
162+
},
163+
defaultCurrency = { locale.getDefaultCurrency() }
164+
)
159165

160166
@Singleton
161167
@Provides

app/src/main/java/com/getcode/navigation/screens/ModalScreens.kt

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import com.getcode.view.main.account.BackupKey
2525
import com.getcode.view.main.account.BetaFlagsScreen
2626
import com.getcode.view.main.account.ConfirmDeleteAccount
2727
import com.getcode.view.main.account.DeleteCodeAccount
28+
import com.getcode.view.main.currency.CurrencySelectKind
2829
import com.getcode.view.main.currency.CurrencySelectionSheet
2930
import com.getcode.view.main.currency.CurrencyViewModel
3031
import com.getcode.view.main.getKin.BuyAndSellKin
@@ -280,7 +281,7 @@ data object ReferFriendScreen : MainGraph, ModalContent {
280281
}
281282

282283
@Parcelize
283-
data class CurrencySelectionModal(val forBalance: Boolean = false) : MainGraph, ModalContent {
284+
data class CurrencySelectionModal(val kind: CurrencySelectKind = CurrencySelectKind.Entry) : MainGraph, ModalContent {
284285
@IgnoredOnParcel
285286
override val key: ScreenKey = uniqueScreenKey
286287

@@ -304,13 +305,8 @@ data class CurrencySelectionModal(val forBalance: Boolean = false) : MainGraph,
304305
CurrencySelectionSheet(viewModel = viewModel)
305306
}
306307

307-
LaunchedEffect(viewModel, forBalance) {
308-
val key = if (forBalance) {
309-
PrefsString.KEY_PREFERRED_APP_CURRENCY
310-
} else {
311-
PrefsString.KEY_GIVE_CURRENCY_SELECTED
312-
}
313-
viewModel.dispatchEvent(CurrencyViewModel.Event.OnSelectedKeyChanged(key))
308+
LaunchedEffect(viewModel, kind) {
309+
viewModel.dispatchEvent(CurrencyViewModel.Event.OnKindChanged(kind))
314310
}
315311
}
316312
}

app/src/main/java/com/getcode/util/AccountUtils.kt

Lines changed: 3 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -43,35 +43,6 @@ object AccountUtils {
4343
}
4444

4545
private suspend fun getAccount(context: Activity): @NonNull Single<Pair<String?, Account?>> {
46-
trace("getAccount")
47-
fun getAuthTokenByFeatures(cb: (k: String?, a: Account?) -> Unit) {
48-
val am: AccountManager = AccountManager.get(context)
49-
val start = Clock.System.now()
50-
am.getAuthTokenByFeatures(
51-
acctType, acctType, null, context, null, null,
52-
{ future ->
53-
try {
54-
val bundle = future?.result
55-
val authToken = bundle?.getString(AccountManager.KEY_AUTHTOKEN)
56-
val accountName = bundle?.getString(AccountManager.KEY_ACCOUNT_NAME)
57-
val account: Account? = getAccount(context, accountName)
58-
59-
val end = Clock.System.now()
60-
trace("auth token feature fetch took ${end.toEpochMilliseconds() - start.toEpochMilliseconds()} ms")
61-
trace("token=$authToken, $accountName, ${account?.name}", type = TraceType.Silent)
62-
63-
cb(authToken.orEmpty(), account)
64-
65-
if (null == account && authToken != null) {
66-
addAccount(context, accountName.orEmpty(), "", authToken)
67-
}
68-
} catch (e: AuthenticatorException) {
69-
cb(null, null)
70-
}
71-
}, null
72-
)
73-
}
74-
7546
val subject = SingleSubject.create<Pair<String?, Account?>>()
7647
return subject.doOnSubscribe {
7748
CoroutineScope(Dispatchers.IO).launch {
@@ -91,11 +62,11 @@ object AccountUtils {
9162
}
9263

9364
private suspend fun getAccountNoActivity(context: Context) : Pair<String?, Account?>? = suspendCancellableCoroutine {cont ->
94-
trace("getAuthToken")
65+
trace("getAuthToken", type = TraceType.Silent)
9566
val am: AccountManager = AccountManager.get(context)
9667
val accountthing = am.accounts.getOrNull(0)
9768
if (accountthing == null) {
98-
trace("no associated account found")
69+
trace("no associated account found", type = TraceType.Error)
9970
cont.resume(null to null)
10071
return@suspendCancellableCoroutine
10172
}
@@ -118,7 +89,7 @@ object AccountUtils {
11889
addAccount(context, accountName.orEmpty(), "", authToken)
11990
}
12091
} catch (e: AuthenticatorException) {
121-
trace(message = "failed to read account", error = e)
92+
trace(message = "failed to read account", error = e, type = TraceType.Error)
12293
cont.resume(null to null)
12394
}
12495
}, handler
@@ -130,11 +101,6 @@ object AccountUtils {
130101
return getAccountNoActivity(context)?.first
131102
}
132103

133-
suspend fun getToken(context: Activity): @NonNull Single<String> {
134-
return getAccount(context)
135-
.map { it.first.orEmpty() }
136-
}
137-
138104
private fun getAccount(context: Context?, accountName: String?): Account? {
139105
val accountManager = AccountManager.get(context)
140106
val accounts = accountManager.getAccountsByType(acctType)

app/src/main/java/com/getcode/view/main/account/withdraw/AccountWithdrawAmount.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ fun AccountWithdrawAmount(
5959
captionText = dataState.amountModel.captionText,
6060
isAltCaption = dataState.amountModel.isCaptionConversion,
6161
altCaptionColor = color,
62-
currencyResId = dataState.currencyModel.selectedCurrencyResId,
62+
currencyResId = dataState.currencyModel.selectedCurrency?.resId,
6363
uiModel = dataState.amountAnimatedModel,
6464
isAnimated = true,
6565
textStyle = CodeTheme.typography.displayLarge,

0 commit comments

Comments
 (0)