Updated on 2026-08-14
This commit is contained in:
parent
9a8ff1830d
commit
a0798cfef2
252 changed files with 247 additions and 5857 deletions
|
|
@ -20,7 +20,7 @@ class GlobalLayoutStateHandler<T : View>(
|
|||
if (attachImmediately) attach()
|
||||
}
|
||||
|
||||
fun attach() {
|
||||
private fun attach() {
|
||||
if (isAttached) {
|
||||
Timber.d("Already attached")
|
||||
return
|
||||
|
|
|
|||
|
|
@ -11,20 +11,6 @@ fun postUi(ms: Long = 0, func: Runnable) {
|
|||
if (ms == 0L) uiHandler.post { func.run() } else uiHandler.postDelayed(func, ms)
|
||||
}
|
||||
|
||||
fun postBackground(ms: Long = 0, func: Runnable) {
|
||||
if (ms == 0L) backgroundHandler.post { func.run() } else backgroundHandler.postDelayed(func, ms)
|
||||
}
|
||||
|
||||
fun postUiDelayBg(ms: Long, func: Runnable) {
|
||||
backgroundHandler.postDelayed({ uiHandler.post(func) }, ms)
|
||||
}
|
||||
|
||||
fun post(ms: Long = 0, func: Runnable) {
|
||||
if (ms == 0L) {
|
||||
func.run()
|
||||
} else {
|
||||
val currentLooper = Looper.myLooper() ?: return
|
||||
|
||||
Handler(currentLooper).postDelayed(func, ms)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,34 +0,0 @@
|
|||
package com.tangem.tap.common
|
||||
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.DiffUtil
|
||||
import androidx.recyclerview.widget.ListAdapter
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
open class ShimmerRecyclerAdapter(
|
||||
private val viewHolderViewFactory: (ViewGroup) -> ViewGroup,
|
||||
) : ListAdapter<ShimmerData, ShimmerVH>(DiffUtilCallback) {
|
||||
|
||||
override fun getItemId(position: Int): Long {
|
||||
return if (currentList.isEmpty()) 0 else currentList[position].hashCode().toLong()
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ShimmerVH {
|
||||
return ShimmerVH(viewHolderViewFactory.invoke(parent))
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: ShimmerVH, position: Int) {}
|
||||
|
||||
object DiffUtilCallback : DiffUtil.ItemCallback<ShimmerData>() {
|
||||
override fun areContentsTheSame(oldItem: ShimmerData, newItem: ShimmerData) = oldItem == newItem
|
||||
override fun areItemsTheSame(oldItem: ShimmerData, newItem: ShimmerData) = oldItem == newItem
|
||||
}
|
||||
}
|
||||
|
||||
class ShimmerVH(viewGroup: ViewGroup) : RecyclerView.ViewHolder(viewGroup)
|
||||
|
||||
@Suppress("UnusedPrivateMember")
|
||||
data class ShimmerData(private val any: String = "")
|
||||
|
|
@ -8,11 +8,8 @@ import androidx.appcompat.widget.LinearLayoutCompat
|
|||
import com.google.android.material.bottomsheet.BottomSheetBehavior
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialog
|
||||
import com.tangem.tap.common.extensions.dispatchDialogHide
|
||||
import com.tangem.tap.common.extensions.dispatchDialogShow
|
||||
import com.tangem.tap.common.extensions.show
|
||||
import com.tangem.tap.common.redux.AppDialog
|
||||
import com.tangem.tap.store
|
||||
import com.tangem.wallet.BuildConfig
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
|
|
@ -22,18 +19,6 @@ object TestActions {
|
|||
|
||||
// It used only for the test actions in debug or debug_beta builds
|
||||
var testAmountInjectionForWalletManagerEnabled = false
|
||||
|
||||
/**
|
||||
* @param isTestView - true must be used if you want to show or hide your view depends on BuildConfig
|
||||
*/
|
||||
fun initFor(view: View, actions: List<TestAction>, isTestView: Boolean = false) {
|
||||
if (!BuildConfig.TEST_ACTION_ENABLED) return
|
||||
if (isTestView) view.show(BuildConfig.TEST_ACTION_ENABLED)
|
||||
|
||||
view.setOnClickListener {
|
||||
store.dispatchDialogShow(AppDialog.TestActionsDialog(actions))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
typealias TestAction = Pair<String, () -> Unit>
|
||||
|
|
|
|||
|
|
@ -22,10 +22,6 @@ class AnalyticsFactory {
|
|||
filters.add(filter)
|
||||
}
|
||||
|
||||
fun addParamsInterceptor(interceptor: ParamsInterceptor) {
|
||||
interceptors.add(interceptor)
|
||||
}
|
||||
|
||||
fun build(analytics: Analytics, data: AnalyticsHandlerBuilder.Data) {
|
||||
builders.mapNotNull { it.build(data) }.forEach { analytics.addHandler(it.id(), it) }
|
||||
filters.forEach { analytics.addFilter(it) }
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
package com.tangem.tap.common.analytics.converters
|
||||
|
||||
import com.tangem.blockchain.common.BlockchainSdkError
|
||||
import com.tangem.common.Converter
|
||||
import com.tangem.common.core.TangemSdkError
|
||||
import com.tangem.tap.common.analytics.events.AnalyticsParam
|
||||
import com.tangem.tap.features.demo.DemoTransactionSender
|
||||
import com.tangem.utils.converter.Converter
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
package com.tangem.tap.common.analytics.converters
|
||||
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.common.Converter
|
||||
import com.tangem.domain.common.CardTypesResolver
|
||||
import com.tangem.tap.common.analytics.events.AnalyticsParam
|
||||
import com.tangem.utils.converter.Converter
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
package com.tangem.tap.common.analytics.converters
|
||||
|
||||
import com.shopify.buy3.Storefront
|
||||
import com.tangem.common.Converter
|
||||
import com.tangem.tap.common.analytics.events.Shop
|
||||
import com.tangem.tap.features.shop.domain.models.ProductType
|
||||
import com.tangem.utils.converter.Converter
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
|
|
|
|||
|
|
@ -1,19 +0,0 @@
|
|||
package com.tangem.tap.common.analytics.converters
|
||||
|
||||
import com.tangem.common.Converter
|
||||
import com.tangem.domain.common.CardTypesResolver
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import com.tangem.tap.common.analytics.events.Basic
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
class TopUpEventConverter : Converter<Pair<UserWalletId, CardTypesResolver>, Basic.ToppedUp?> {
|
||||
|
||||
override fun convert(value: Pair<UserWalletId, CardTypesResolver>): Basic.ToppedUp? {
|
||||
val (userWalletId, resolver) = value
|
||||
val paramCardCurrency = ParamCardCurrencyConverter().convert(resolver) ?: return null
|
||||
|
||||
return Basic.ToppedUp(userWalletId, paramCardCurrency)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,16 +1,14 @@
|
|||
package com.tangem.tap.common.analytics.events
|
||||
|
||||
import com.tangem.domain.apptheme.model.AppThemeMode
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.tap.features.details.redux.SecurityOption
|
||||
|
||||
sealed class AnalyticsParam {
|
||||
|
||||
sealed class CurrencyType(val value: String) {
|
||||
class Currency(currency: com.tangem.tap.features.wallet.models.Currency) : CurrencyType(currency.currencySymbol)
|
||||
class Currency(currency: com.tangem.tap.domain.model.Currency) : CurrencyType(currency.currencySymbol)
|
||||
class Blockchain(blockchain: com.tangem.blockchain.common.Blockchain) : CurrencyType(blockchain.currency)
|
||||
class Token(token: com.tangem.blockchain.common.Token) : CurrencyType(token.symbol)
|
||||
class FiatCurrency(fiatCurrency: AppCurrency) : CurrencyType(fiatCurrency.code)
|
||||
class Amount(amount: com.tangem.blockchain.common.Amount) : CurrencyType(amount.currencySymbol)
|
||||
}
|
||||
|
||||
|
|
@ -23,15 +21,11 @@ sealed class AnalyticsParam {
|
|||
sealed class CardBalanceState(val value: String) {
|
||||
object Empty : CardBalanceState("Empty")
|
||||
object Full : CardBalanceState("Full")
|
||||
object CustomToken : CardBalanceState("Custom Token")
|
||||
object BlockchainError : CardBalanceState("Blockchain Error")
|
||||
object NoRate : CardBalanceState("No Rate")
|
||||
companion object
|
||||
}
|
||||
|
||||
sealed class RateApp(val value: String) {
|
||||
object Liked : RateApp("Liked")
|
||||
object Disliked : RateApp("Disliked")
|
||||
object Closed : RateApp("Close")
|
||||
}
|
||||
|
||||
|
|
@ -42,7 +36,6 @@ sealed class AnalyticsParam {
|
|||
|
||||
sealed class UserCode(val value: String) {
|
||||
object AccessCode : UserCode("Access Code")
|
||||
object Passcode : UserCode("Passcode")
|
||||
}
|
||||
|
||||
sealed class SecurityMode(val value: String) {
|
||||
|
|
|
|||
|
|
@ -13,13 +13,6 @@ sealed class Basic(
|
|||
error: Throwable? = null,
|
||||
) : AnalyticsEvent("Basic", event, params, error) {
|
||||
|
||||
class BalanceLoaded(balance: AnalyticsParam.CardBalanceState) : Basic(
|
||||
event = "Balance Loaded",
|
||||
params = mapOf(
|
||||
AnalyticsParam.BALANCE to balance.value,
|
||||
),
|
||||
)
|
||||
|
||||
class CardWasScanned(
|
||||
source: AnalyticsParam.ScannedFrom,
|
||||
) : Basic(
|
||||
|
|
|
|||
|
|
@ -12,23 +12,14 @@ sealed class MainScreen(
|
|||
|
||||
class ScreenOpened : MainScreen("Screen opened")
|
||||
class ButtonScanCard : MainScreen("Button - Scan Card")
|
||||
class ButtonMyWallets : MainScreen("Button - My Wallets")
|
||||
|
||||
class EnableBiometrics(state: AnalyticsParam.OnOffState) : MainScreen(
|
||||
event = "Enable Biometric",
|
||||
params = mapOf("State" to state.value),
|
||||
)
|
||||
|
||||
class MainCurrencyChanged(currencyType: AnalyticsParam.CurrencyType) : MainScreen(
|
||||
event = "Main Currency Changed",
|
||||
params = mapOf("Currency Type" to currencyType.value),
|
||||
)
|
||||
|
||||
class NoticeRateAppButton(result: AnalyticsParam.RateApp) : MainScreen(
|
||||
event = "Notice - Rate The App Button Tapped",
|
||||
params = mapOf("Result" to result.value),
|
||||
)
|
||||
|
||||
class NoticeBackupYourWalletTapped : MainScreen("Notice - Backup Your Wallet Tapped")
|
||||
class NoticeScanYourCardTapped : MainScreen("Notice - Scan Your Card Tapped")
|
||||
}
|
||||
|
|
@ -1,19 +0,0 @@
|
|||
package com.tangem.tap.common.analytics.events
|
||||
|
||||
import com.tangem.core.analytics.models.AnalyticsEvent
|
||||
|
||||
sealed class MyWallets(
|
||||
event: String,
|
||||
params: Map<String, String> = mapOf(),
|
||||
) : AnalyticsEvent("My Wallets", event, params) {
|
||||
|
||||
class MyWalletsScreenOpened : MyWallets(event = "My Wallets Screen Opened")
|
||||
|
||||
sealed class Button {
|
||||
class ScanNewCard : MyWallets(event = "Button - Scan New Card")
|
||||
class UnlockWithBiometrics : MyWallets(event = "Button - Unlock all with Face ID")
|
||||
class EditWalletTapped : MyWallets(event = "Button - Edit Wallet Tapped")
|
||||
class DeleteWalletTapped : MyWallets(event = "Button - Delete Wallet Tapped")
|
||||
class WalletUnlockTapped : MyWallets(event = "Button - Wallet Unlock Tapped")
|
||||
}
|
||||
}
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
package com.tangem.tap.common.analytics.events
|
||||
|
||||
import com.tangem.core.analytics.models.AnalyticsEvent
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
|
||||
sealed class Portfolio(
|
||||
event: String,
|
||||
params: Map<String, String> = mapOf(),
|
||||
) : AnalyticsEvent("Portfolio", event, params) {
|
||||
|
||||
class Refreshed : Portfolio("Refreshed")
|
||||
|
||||
class ButtonManageTokens : Portfolio("Button - Manage Tokens")
|
||||
|
||||
class TokenTapped : Portfolio("Token is Tapped")
|
||||
|
||||
class OrganizeTokens : Portfolio("Button - Organize Tokens")
|
||||
}
|
||||
|
|
@ -1,7 +1,6 @@
|
|||
package com.tangem.tap.common.analytics.events
|
||||
|
||||
import com.tangem.core.analytics.models.AnalyticsEvent
|
||||
import com.tangem.tap.common.analytics.events.AnalyticsParam.CurrencyType
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
|
|
@ -13,50 +12,6 @@ sealed class Token(
|
|||
error: Throwable? = null,
|
||||
) : AnalyticsEvent(category, event, params, error) {
|
||||
|
||||
class Refreshed : Token("Token", "Refreshed")
|
||||
class ButtonExplore : Token("Token", "Button - Explore")
|
||||
|
||||
class ButtonRemoveToken(type: CurrencyType) : Token(
|
||||
"Token",
|
||||
"Button - Remove Token",
|
||||
params = mapOf("Token" to type.value),
|
||||
)
|
||||
|
||||
class ButtonBuy(type: CurrencyType) : Token(
|
||||
category = "Token",
|
||||
event = "Button - Buy",
|
||||
params = mapOf("Token" to type.value),
|
||||
)
|
||||
|
||||
class ButtonSell(type: CurrencyType) : Token(
|
||||
category = "Token",
|
||||
event = "Button - Sell",
|
||||
params = mapOf("Token" to type.value),
|
||||
)
|
||||
|
||||
class ButtonExchange(type: CurrencyType) : Token(
|
||||
category = "Token",
|
||||
event = "Button - Exchange",
|
||||
params = mapOf("Token" to type.value),
|
||||
)
|
||||
|
||||
class ButtonSend(type: CurrencyType) : Token(
|
||||
category = "Token",
|
||||
event = "Button - Send",
|
||||
params = mapOf("Token" to type.value),
|
||||
)
|
||||
|
||||
class Bought(type: CurrencyType) : Token(
|
||||
category = "Token",
|
||||
event = "Token Bought",
|
||||
params = mapOf("Token" to type.value),
|
||||
)
|
||||
|
||||
object ShowWalletAddress : Token(
|
||||
category = "Token",
|
||||
event = "Button - Show the Wallet Address",
|
||||
)
|
||||
|
||||
sealed class Receive(
|
||||
event: String,
|
||||
params: Map<String, String> = mapOf(),
|
||||
|
|
|
|||
|
|
@ -3,8 +3,8 @@ package com.tangem.tap.common.analytics.handlers.amplitude
|
|||
import android.app.Application
|
||||
import com.amplitude.api.Amplitude
|
||||
import com.amplitude.api.AmplitudeClient
|
||||
import com.tangem.common.Converter
|
||||
import com.tangem.core.analytics.api.EventLogger
|
||||
import com.tangem.utils.converter.Converter
|
||||
import org.json.JSONObject
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ package com.tangem.tap.common.analytics.handlers.firebase
|
|||
|
||||
import android.os.Bundle
|
||||
import androidx.core.os.bundleOf
|
||||
import com.google.firebase.analytics.FirebaseAnalytics
|
||||
import com.google.firebase.analytics.ktx.analytics
|
||||
import com.google.firebase.crashlytics.ktx.crashlytics
|
||||
import com.google.firebase.ktx.Firebase
|
||||
|
|
@ -29,8 +28,4 @@ internal class FirebaseClient : FirebaseAnalyticsClient {
|
|||
}
|
||||
|
||||
private fun Map<String, String>.toBundle(): Bundle = bundleOf(*this.toList().toTypedArray())
|
||||
|
||||
companion object {
|
||||
const val ORDER_EVENT = FirebaseAnalytics.Event.PURCHASE
|
||||
}
|
||||
}
|
||||
|
|
@ -1,67 +0,0 @@
|
|||
package com.tangem.tap.common.compose
|
||||
|
||||
import androidx.compose.material.LocalTextStyle
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.drawWithContent
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.unit.TextUnit
|
||||
import androidx.compose.ui.unit.sp
|
||||
|
||||
@Suppress("MagicNumber")
|
||||
@Composable
|
||||
fun TextAutoSize(
|
||||
text: String,
|
||||
fontSizeRange: FontSizeRange,
|
||||
modifier: Modifier = Modifier,
|
||||
textStyle: TextStyle = LocalTextStyle.current,
|
||||
) {
|
||||
val fontSizeValue = remember { mutableStateOf(fontSizeRange.max.value) }
|
||||
val readyToDraw = remember { mutableStateOf(false) }
|
||||
|
||||
val textState = remember { mutableStateOf(text) }
|
||||
if (textState.value != text) {
|
||||
readyToDraw.value = false
|
||||
fontSizeValue.value = fontSizeRange.max.value
|
||||
textState.value = text
|
||||
}
|
||||
|
||||
Text(
|
||||
modifier = modifier.drawWithContent { if (readyToDraw.value) drawContent() },
|
||||
text = text,
|
||||
softWrap = false,
|
||||
style = textStyle,
|
||||
fontSize = fontSizeValue.value.sp,
|
||||
onTextLayout = {
|
||||
if (it.hasVisualOverflow) {
|
||||
val nextFontSizeValue = fontSizeValue.value - fontSizeRange.step.value
|
||||
if (nextFontSizeValue <= fontSizeRange.min.value) {
|
||||
fontSizeValue.value = fontSizeRange.min.value
|
||||
readyToDraw.value = true
|
||||
} else {
|
||||
fontSizeValue.value = nextFontSizeValue * 0.8f
|
||||
}
|
||||
} else {
|
||||
readyToDraw.value = true
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
data class FontSizeRange(
|
||||
val min: TextUnit,
|
||||
val max: TextUnit,
|
||||
val step: TextUnit = DEFAULT_TEXT_STEP,
|
||||
) {
|
||||
init {
|
||||
require(min < max) { "min should be less than max, $this" }
|
||||
require(step.value > 0) { "step should be greater than 0, $this" }
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val DEFAULT_TEXT_STEP = 1.sp
|
||||
}
|
||||
}
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
package com.tangem.tap.common.compose
|
||||
|
||||
import androidx.compose.material.ripple.LocalRippleTheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
|
||||
/**
|
||||
* Used for disable ripple if button is enable = false
|
||||
*/
|
||||
@Composable
|
||||
fun ToggledRippleTheme(isEnabled: Boolean, content: @Composable () -> Unit) {
|
||||
val theme = LocalRippleTheme provides if (isEnabled) LocalRippleTheme.current else NoRippleTheme()
|
||||
CompositionLocalProvider(theme) { content() }
|
||||
}
|
||||
|
|
@ -1,47 +0,0 @@
|
|||
package com.tangem.tap.common.compose
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import com.tangem.domain.common.util.ValueDebouncer
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
* This is an empty compose view. It just remember the ValueDebouncer inside of itself.
|
||||
*/
|
||||
@Composable
|
||||
fun <T> valueDebouncerAsState(
|
||||
initialValue: T,
|
||||
onValueChange: (T) -> Unit,
|
||||
debounce: Long = 600,
|
||||
onEmitValueReceive: (T) -> Unit = {},
|
||||
): ValueDebouncer<T> {
|
||||
return remember {
|
||||
ValueDebouncer(
|
||||
initialValue = initialValue,
|
||||
debounceDuration = debounce,
|
||||
onEmitValueReceived = { emitValue ->
|
||||
emitValue?.let { onEmitValueReceive(it) }
|
||||
},
|
||||
onValueChanged = { changedValue ->
|
||||
changedValue?.let { onValueChange(it) }
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun <T> valueDebouncerNullableAsState(
|
||||
initialValue: T?,
|
||||
onValueChange: (T?) -> Unit,
|
||||
debounce: Long = 400,
|
||||
onEmitValueReceive: (T?) -> Unit = {},
|
||||
): ValueDebouncer<T?> {
|
||||
return remember {
|
||||
ValueDebouncer(
|
||||
initialValue = initialValue,
|
||||
debounceDuration = debounce,
|
||||
onEmitValueReceived = onEmitValueReceive,
|
||||
onValueChanged = onValueChange,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,33 +0,0 @@
|
|||
package com.tangem.tap.common.compose
|
||||
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material.LocalTextStyle
|
||||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
@Composable
|
||||
fun ErrorView(text: String, modifier: Modifier = Modifier, style: TextStyle = LocalTextStyle.current) {
|
||||
Text(
|
||||
text,
|
||||
color = MaterialTheme.colors.error,
|
||||
modifier = modifier,
|
||||
style = style,
|
||||
)
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
private fun ErrorViewTest() {
|
||||
Box(Modifier.padding(16.dp)) {
|
||||
ErrorView(text = "Some error description")
|
||||
}
|
||||
}
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
package com.tangem.tap.common.compose
|
||||
|
||||
import androidx.compose.material.ripple.RippleAlpha
|
||||
import androidx.compose.material.ripple.RippleTheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.graphics.Color
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
class NoRippleTheme : RippleTheme {
|
||||
@Composable
|
||||
override fun defaultColor() = Color.Unspecified
|
||||
|
||||
@Composable
|
||||
override fun rippleAlpha(): RippleAlpha = RippleAlpha(0.0f, 0.0f, 0.0f, 0.0f)
|
||||
}
|
||||
|
|
@ -1,165 +0,0 @@
|
|||
package com.tangem.tap.common.compose
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.gestures.detectTapGestures
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.layout.wrapContentSize
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.foundation.text.KeyboardActions
|
||||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
import androidx.compose.material.LocalTextStyle
|
||||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.material.Surface
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.material.TextField
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.ExperimentalComposeUiApi
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.alpha
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.focus.FocusRequester
|
||||
import androidx.compose.ui.focus.focusRequester
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.input.pointer.pointerInput
|
||||
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.input.ImeAction
|
||||
import androidx.compose.ui.text.input.KeyboardType
|
||||
import androidx.compose.ui.text.input.TextFieldValue
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.core.text.isDigitsOnly
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
@OptIn(ExperimentalComposeUiApi::class)
|
||||
@Composable
|
||||
fun PinCodeWidget(
|
||||
config: PinViewConfig = tangemPinConfig,
|
||||
onPinChange: (String, Boolean) -> Unit = { pin, isLastSymbolEntered -> },
|
||||
) {
|
||||
val focusRequester = remember { FocusRequester() }
|
||||
val keyboardController = LocalSoftwareKeyboardController.current
|
||||
|
||||
val rTextFieldValue = remember { mutableStateOf(TextFieldValue("")) }
|
||||
val indexedSymbols: List<String?> = createPinSymbolsList(config.pinsCount, rTextFieldValue.value.text)
|
||||
|
||||
fun isLastSymbolEntered(): Boolean = rTextFieldValue.value.text.length == config.pinsCount
|
||||
|
||||
fun handleOnTextFieldValueChanged(value: TextFieldValue) {
|
||||
if (!value.text.isDigitsOnly()) return
|
||||
|
||||
if (value.text.length <= config.pinsCount) {
|
||||
rTextFieldValue.value = value
|
||||
onPinChange(value.text, isLastSymbolEntered())
|
||||
}
|
||||
}
|
||||
|
||||
Box(
|
||||
modifier = config.modifier
|
||||
.pointerInput(Unit) {
|
||||
detectTapGestures {
|
||||
focusRequester.requestFocus()
|
||||
keyboardController?.show()
|
||||
}
|
||||
},
|
||||
) {
|
||||
Row {
|
||||
for (index in 0 until config.pinsCount) {
|
||||
PinElement(
|
||||
config = config,
|
||||
pinSymbol = indexedSymbols[index] ?: "",
|
||||
)
|
||||
}
|
||||
}
|
||||
TextField(
|
||||
modifier = Modifier
|
||||
.alpha(0f)
|
||||
.size(1.dp)
|
||||
.align(Alignment.Center)
|
||||
.focusRequester(focusRequester),
|
||||
keyboardOptions = KeyboardOptions(
|
||||
keyboardType = KeyboardType.Decimal,
|
||||
imeAction = if (isLastSymbolEntered()) ImeAction.Done else ImeAction.Next,
|
||||
),
|
||||
keyboardActions = KeyboardActions(
|
||||
onDone = { keyboardController?.hide() },
|
||||
),
|
||||
value = rTextFieldValue.value,
|
||||
onValueChange = ::handleOnTextFieldValueChanged,
|
||||
)
|
||||
}
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
focusRequester.requestFocus()
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun PinElement(config: PinViewConfig, pinSymbol: String) {
|
||||
Box(Modifier.padding(config.pinBoxPadding)) {
|
||||
Box(config.pinBoxModifier) {
|
||||
Text(
|
||||
text = pinSymbol,
|
||||
modifier = config.pinTextModifier.align(Alignment.Center),
|
||||
style = config.pinsTextStyle ?: LocalTextStyle.current,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun createPinSymbolsList(size: Int, text: String): List<String?> = List(size) {
|
||||
try {
|
||||
text[it].toString()
|
||||
} catch (ex: IndexOutOfBoundsException) {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
data class PinViewConfig(
|
||||
val modifier: Modifier = Modifier,
|
||||
val pinBoxModifier: Modifier = Modifier,
|
||||
val pinBoxPadding: Dp = 0.dp,
|
||||
val pinTextModifier: Modifier = Modifier,
|
||||
val pinsCount: Int = 4,
|
||||
val pinsTextStyle: TextStyle? = null,
|
||||
)
|
||||
|
||||
private val tangemPinConfig = PinViewConfig(
|
||||
modifier = Modifier
|
||||
.wrapContentSize(),
|
||||
pinBoxModifier = Modifier
|
||||
.width(42.dp)
|
||||
.height(56.dp)
|
||||
.clip(RoundedCornerShape(8.dp))
|
||||
.background(Color(0xFFF0F0F0)),
|
||||
pinBoxPadding = 6.dp,
|
||||
pinTextModifier = Modifier,
|
||||
pinsCount = 4,
|
||||
pinsTextStyle = TextStyle(
|
||||
fontWeight = FontWeight(500),
|
||||
fontSize = 24.sp,
|
||||
),
|
||||
)
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
private fun PinCodeWidgetPreview() {
|
||||
Surface(modifier = Modifier.fillMaxSize(), color = MaterialTheme.colors.background) {
|
||||
PinCodeWidget(tangemPinConfig)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,49 +0,0 @@
|
|||
package com.tangem.tap.common.compose
|
||||
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.material.Surface
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.colorResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import com.tangem.common.module.ModuleMessage
|
||||
import com.tangem.core.ui.components.SpacerH8
|
||||
import com.tangem.tap.domain.moduleMessage.ModuleMessageConverter
|
||||
import com.tangem.wallet.R
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
@Composable
|
||||
fun AddCustomTokenWarning(warning: ModuleMessage, converter: ModuleMessageConverter, modifier: Modifier = Modifier) {
|
||||
Surface(
|
||||
modifier = modifier,
|
||||
shape = MaterialTheme.shapes.small,
|
||||
color = colorResource(id = R.color.warning_warning),
|
||||
elevation = 4.dp,
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier.padding(16.dp),
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(id = R.string.common_warning),
|
||||
color = colorResource(id = R.color.white),
|
||||
fontSize = 14.sp,
|
||||
fontWeight = FontWeight.Bold,
|
||||
)
|
||||
SpacerH8()
|
||||
Text(
|
||||
text = converter.convert(warning).message,
|
||||
color = colorResource(id = R.color.white),
|
||||
fontSize = 13.sp,
|
||||
lineHeight = 18.sp,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
package com.tangem.tap.common.compose.extensions
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import com.tangem.tap.common.extensions.copyToClipboard
|
||||
import com.tangem.tap.common.extensions.getFromClipboard
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
@Suppress("ComposableFunctionName")
|
||||
@Composable
|
||||
fun copyToClipboard(value: Any, label: String = "") {
|
||||
LocalContext.current.copyToClipboard(value, label)
|
||||
}
|
||||
|
||||
@Suppress("ComposableFunctionName")
|
||||
@Composable
|
||||
fun getFromClipboard(default: CharSequence? = null): CharSequence? {
|
||||
return LocalContext.current.getFromClipboard(default)
|
||||
}
|
||||
|
|
@ -14,6 +14,4 @@ fun Dp.toPx(): Float {
|
|||
return with(LocalDensity.current) { currentDp.toPx() }
|
||||
}
|
||||
|
||||
fun DpSize.halfWidth(): Dp = this.width / 2
|
||||
|
||||
fun DpSize.halfHeight(): Dp = this.height / 2
|
||||
|
|
@ -1,33 +0,0 @@
|
|||
package com.tangem.tap.common.compose.extensions
|
||||
|
||||
import androidx.compose.foundation.lazy.LazyListState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.derivedStateOf
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.platform.LocalView
|
||||
import com.tangem.tap.common.extensions.hideKeyboard
|
||||
|
||||
@Composable
|
||||
fun LazyListState.OnBottomReached(loadMoreThreshold: Int, onLoadMore: () -> Unit) {
|
||||
require(loadMoreThreshold >= 0)
|
||||
val shouldLoadMore by remember {
|
||||
derivedStateOf {
|
||||
val lastVisibleItem = layoutInfo.visibleItemsInfo.lastOrNull()
|
||||
?: return@derivedStateOf false
|
||||
lastVisibleItem.index >= layoutInfo.totalItemsCount - 1 - loadMoreThreshold
|
||||
}
|
||||
}
|
||||
|
||||
LaunchedEffect(shouldLoadMore) {
|
||||
if (shouldLoadMore) onLoadMore()
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun LazyListState.HideKeyboardOnScroll() {
|
||||
if (isScrollInProgress) {
|
||||
LocalView.current.hideKeyboard()
|
||||
}
|
||||
}
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
package com.tangem.tap.common.compose.extensions
|
||||
|
||||
import androidx.compose.runtime.MutableState
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
fun <T> MutableState<List<T>>.addAndNotify(value: T) {
|
||||
this.value = this.value.toMutableList().apply { add(value) }
|
||||
}
|
||||
|
||||
fun <T> MutableState<List<T>>.removeAndNotify(value: T) {
|
||||
this.value = this.value.toMutableList().apply { remove(value) }
|
||||
}
|
||||
|
|
@ -5,7 +5,6 @@ import androidx.compose.ui.graphics.painter.Painter
|
|||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.unit.DpSize
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.tangem.sdk.extensions.dpToPx
|
||||
import com.tangem.sdk.extensions.pxToDp
|
||||
|
||||
/**
|
||||
|
|
@ -17,8 +16,5 @@ fun Painter.dpSize(): DpSize = DpSize(
|
|||
intrinsicSize.height.pxToDp().dp,
|
||||
)
|
||||
|
||||
@Composable
|
||||
private fun Float.dpToPx(): Float = LocalContext.current.dpToPx(this)
|
||||
|
||||
@Composable
|
||||
private fun Float.pxToDp(): Float = LocalContext.current.pxToDp(this)
|
||||
|
|
@ -1,6 +0,0 @@
|
|||
package com.tangem.tap.common.extensions
|
||||
|
||||
import android.content.res.AssetManager
|
||||
|
||||
fun AssetManager.readJsonFileToString(fileName: String): String =
|
||||
this.open("$fileName.json").bufferedReader().readText()
|
||||
|
|
@ -1,7 +1,6 @@
|
|||
package com.tangem.tap.common.extensions
|
||||
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.BitmapFactory
|
||||
import java.io.ByteArrayOutputStream
|
||||
|
||||
@Suppress("MagicNumber")
|
||||
|
|
@ -9,9 +8,4 @@ fun Bitmap.toByteArray(): ByteArray {
|
|||
val stream = ByteArrayOutputStream()
|
||||
this.compress(Bitmap.CompressFormat.JPEG, 20, stream)
|
||||
return stream.toByteArray()
|
||||
}
|
||||
|
||||
@Suppress("MagicNumber")
|
||||
fun ByteArray.toBitmap(): Bitmap {
|
||||
return BitmapFactory.decodeByteArray(this, 0, this.size)
|
||||
}
|
||||
|
|
@ -7,10 +7,6 @@ import android.net.*
|
|||
import androidx.annotation.*
|
||||
import androidx.core.content.*
|
||||
|
||||
fun Context.isPermissionGranted(permission: String): Boolean {
|
||||
return ContextCompat.checkSelfPermission(this, permission) == PackageManager.PERMISSION_GRANTED
|
||||
}
|
||||
|
||||
/**
|
||||
* Get uri to any resource type via given Resource Instance
|
||||
* @param resId - resource id
|
||||
|
|
|
|||
|
|
@ -1,10 +0,0 @@
|
|||
package com.tangem.tap.common.extensions
|
||||
|
||||
import coil.request.ImageRequest
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
fun ImageRequest.Builder.cardImageData(any: Any?): ImageRequest.Builder = apply {
|
||||
data(any)
|
||||
}
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
package com.tangem.tap.common.extensions
|
||||
|
||||
import android.widget.ImageView
|
||||
import androidx.annotation.DrawableRes
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
fun ImageView.setDrawable(@DrawableRes resId: Int) {
|
||||
setImageDrawable(context.getDrawableCompat(resId))
|
||||
}
|
||||
|
|
@ -1,17 +1,11 @@
|
|||
package com.tangem.tap.common.extensions
|
||||
|
||||
import android.text.Spanned
|
||||
import android.text.SpannedString
|
||||
import android.text.style.RelativeSizeSpan
|
||||
import androidx.core.text.buildSpannedString
|
||||
import com.tangem.common.extensions.isZero
|
||||
import timber.log.Timber
|
||||
import java.math.BigDecimal
|
||||
import java.math.RoundingMode
|
||||
import java.text.DecimalFormat
|
||||
import java.text.DecimalFormatSymbols
|
||||
import java.text.NumberFormat
|
||||
import java.util.*
|
||||
import java.util.Locale
|
||||
|
||||
// TODO: move extensions to utils
|
||||
fun BigDecimal.toFormattedString(
|
||||
|
|
@ -29,105 +23,6 @@ fun BigDecimal.toFormattedString(
|
|||
return df.format(this)
|
||||
}
|
||||
|
||||
/**
|
||||
* To formatted crypto currency string
|
||||
* Specific method because there is no crypto currency codes in Locale
|
||||
*/
|
||||
@Suppress("MagicNumber")
|
||||
fun BigDecimal.toFormattedCryptoCurrencyString(
|
||||
decimals: Int,
|
||||
currency: String,
|
||||
roundingMode: RoundingMode = RoundingMode.DOWN,
|
||||
limitNumberOfDecimals: Boolean = true,
|
||||
): String {
|
||||
val decimalsForRounding = if (limitNumberOfDecimals) {
|
||||
if (decimals > 8) 8 else decimals
|
||||
} else {
|
||||
decimals
|
||||
}
|
||||
try {
|
||||
val locale = Locale.getDefault()
|
||||
val formatter = NumberFormat.getCurrencyInstance(locale)
|
||||
// first create currency instance for "USD" with Locale default to replace currency to crypto later
|
||||
Currency.getInstance("USD")?.let { currencyTmp ->
|
||||
formatter.currency = currencyTmp
|
||||
formatter.maximumFractionDigits = decimalsForRounding
|
||||
formatter.minimumFractionDigits = 0
|
||||
formatter.isGroupingUsed = true
|
||||
formatter.roundingMode = roundingMode
|
||||
// cause formatter created for USD, replace Currency with Crypto symbol on right by Locale place
|
||||
return formatter.format(this).replace(currencyTmp.getSymbol(locale), "$currency ")
|
||||
}
|
||||
} catch (e: IllegalArgumentException) {
|
||||
Timber.e(e, "can't parse currency")
|
||||
}
|
||||
// if something went wrong - use old way to format
|
||||
val formattedAmount = this.toFormattedString(
|
||||
decimals = decimalsForRounding,
|
||||
roundingMode = roundingMode,
|
||||
locale = Locale.getDefault(),
|
||||
)
|
||||
return "$formattedAmount $currency "
|
||||
}
|
||||
|
||||
fun BigDecimal.toFiatRateString(fiatCurrencyName: String, fiatCode: String): String {
|
||||
try {
|
||||
val formatter = NumberFormat.getCurrencyInstance(Locale.getDefault())
|
||||
Currency.getInstance(fiatCode)?.let { currency ->
|
||||
formatter.currency = currency
|
||||
formatter.maximumFractionDigits = 2
|
||||
formatter.roundingMode = RoundingMode.HALF_UP
|
||||
return formatter.format(this).replace(currency.symbol, "$fiatCurrencyName ")
|
||||
}
|
||||
} catch (e: IllegalArgumentException) {
|
||||
Timber.e(e, "can't parse currency")
|
||||
}
|
||||
val value = this
|
||||
.setScale(2, RoundingMode.HALF_UP)
|
||||
.formatWithSpaces()
|
||||
return "$value $fiatCurrencyName"
|
||||
}
|
||||
|
||||
fun BigDecimal.toFiatString(
|
||||
rateValue: BigDecimal,
|
||||
fiatCurrencyName: String,
|
||||
fiatCode: String,
|
||||
formatWithSpaces: Boolean = false,
|
||||
): String {
|
||||
val fiatValue = rateValue.multiply(this)
|
||||
return fiatValue.toFormattedFiatValue(
|
||||
fiatCurrencyName = fiatCurrencyName,
|
||||
fiatCode = fiatCode,
|
||||
formatWithSpaces = formatWithSpaces,
|
||||
)
|
||||
}
|
||||
|
||||
fun BigDecimal.toFiatValue(rateValue: BigDecimal): BigDecimal {
|
||||
val fiatValue = rateValue.multiply(this)
|
||||
return fiatValue.setScale(2, RoundingMode.HALF_UP)
|
||||
}
|
||||
|
||||
fun BigDecimal.toFormattedFiatValue(
|
||||
fiatCurrencyName: String,
|
||||
fiatCode: String,
|
||||
formatWithSpaces: Boolean = false,
|
||||
): String {
|
||||
try {
|
||||
val formatter = NumberFormat.getCurrencyInstance(Locale.getDefault())
|
||||
Currency.getInstance(fiatCode)?.let { currency ->
|
||||
formatter.currency = currency
|
||||
formatter.maximumFractionDigits = 2
|
||||
formatter.roundingMode = RoundingMode.HALF_UP
|
||||
return formatter.format(this).replace(currency.symbol, "$fiatCurrencyName ")
|
||||
}
|
||||
} catch (e: IllegalArgumentException) {
|
||||
Timber.e(e, "can't parse currency")
|
||||
}
|
||||
val fiatValue = this.setScale(2, RoundingMode.HALF_UP)
|
||||
.let { if (formatWithSpaces) it.formatWithSpaces() else it }
|
||||
return " $fiatValue $fiatCurrencyName"
|
||||
}
|
||||
|
||||
fun BigDecimal.stripZeroPlainString(): String = this.stripTrailingZeros().toPlainString()
|
||||
|
||||
// 0.00 -> 0.00
|
||||
|
|
@ -146,70 +41,4 @@ fun BigDecimal.setPrecision(precision: Int, roundingMode: RoundingMode = Roundin
|
|||
return this.setScale(scale() - precision() + precision, roundingMode)
|
||||
}
|
||||
|
||||
fun BigDecimal.isPositive(): Boolean = this.compareTo(BigDecimal.ZERO) == 1
|
||||
fun BigDecimal.isNegative(): Boolean = this.compareTo(BigDecimal.ZERO) == -1
|
||||
fun BigDecimal.isGreaterThan(value: BigDecimal): Boolean = this.compareTo(value) == 1
|
||||
fun BigDecimal.isLessThan(value: BigDecimal): Boolean = this.compareTo(value) == -1
|
||||
|
||||
fun BigDecimal.isGreaterThanOrEqual(value: BigDecimal): Boolean {
|
||||
val compareResult = this.compareTo(value)
|
||||
return compareResult == 1 || compareResult == 0
|
||||
}
|
||||
|
||||
fun BigDecimal.isLessThanOrEqual(value: BigDecimal): Boolean {
|
||||
val compareResult = this.compareTo(value)
|
||||
return compareResult == -1 || compareResult == 0
|
||||
}
|
||||
|
||||
fun BigDecimal.formatAmountAsSpannedString(
|
||||
currencySymbol: String,
|
||||
reminderPartSizeProportion: Float = 0.7f,
|
||||
): SpannedString {
|
||||
val amount = this
|
||||
.setScale(2, RoundingMode.HALF_UP)
|
||||
.formatWithSpaces()
|
||||
val integer = amount.substringBefore('.')
|
||||
val reminder = amount.substringAfter('.')
|
||||
|
||||
// test formatter log Log.e("TEST ", BigDecimal("1234567890987654321.1234567890987654321").formatWithSpaces())
|
||||
|
||||
return buildSpannedString {
|
||||
append(integer)
|
||||
append('.')
|
||||
append(
|
||||
"$reminder $currencySymbol",
|
||||
RelativeSizeSpan(reminderPartSizeProportion),
|
||||
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("MagicNumber")
|
||||
fun BigDecimal.formatWithSpaces(): String {
|
||||
val str = this.toString()
|
||||
var integerStr = str.substringBefore('.')
|
||||
val reminderStr = str.substringAfter('.')
|
||||
val packets = arrayListOf<String>()
|
||||
|
||||
var index: Int = integerStr.length
|
||||
while (0 < index) {
|
||||
if (index <= 3) {
|
||||
packets.add(integerStr)
|
||||
break
|
||||
}
|
||||
index -= 3
|
||||
packets.add(integerStr.substring(startIndex = index))
|
||||
integerStr = integerStr.substring(startIndex = 0, endIndex = index)
|
||||
}
|
||||
|
||||
return buildString {
|
||||
packets.reversed().forEachIndexed { index, packet ->
|
||||
append(packet)
|
||||
if (index != packets.lastIndex) append(' ')
|
||||
}
|
||||
if (reminderStr.isNotBlank()) {
|
||||
append('.')
|
||||
append(reminderStr)
|
||||
}
|
||||
}
|
||||
}
|
||||
fun BigDecimal.isPositive(): Boolean = this.signum() == 1
|
||||
|
|
@ -46,10 +46,6 @@ suspend fun Store<AppState>.onUserWalletSelected(userWallet: UserWallet, sendAna
|
|||
state.globalState.tapWalletManager.onWalletSelected(userWallet, sendAnalyticsEvent)
|
||||
}
|
||||
|
||||
fun Store<*>.dispatchToastNotification(resId: Int) {
|
||||
dispatchOnMain(GlobalAction.ShowToastNotification(resId))
|
||||
}
|
||||
|
||||
fun Store<*>.dispatchErrorNotification(error: TapError) {
|
||||
dispatchOnMain(GlobalAction.ShowErrorNotification(error))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,41 +1,12 @@
|
|||
package com.tangem.tap.common.extensions
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.Color
|
||||
import android.net.Uri
|
||||
import android.text.Spannable
|
||||
import android.text.style.ForegroundColorSpan
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.text.toSpannable
|
||||
import com.google.zxing.BarcodeFormat
|
||||
import com.google.zxing.EncodeHintType
|
||||
import com.google.zxing.qrcode.QRCodeWriter
|
||||
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel
|
||||
import java.util.*
|
||||
|
||||
fun String?.ellipsizeBeforeSpace(allowedSize: Int): String {
|
||||
if (this.isNullOrBlank()) return ""
|
||||
val size = this.length
|
||||
val sizeDifference = size - allowedSize
|
||||
val endIndex = this.indexOf(" ")
|
||||
val startIndex = endIndex - sizeDifference
|
||||
val newString = this.removeRange(startIndex, endIndex)
|
||||
return newString.substring(0 until startIndex) + "..." +
|
||||
newString.substring(startIndex until newString.length)
|
||||
}
|
||||
|
||||
fun String.colorSegment(context: Context, color: Int, startIndex: Int = 0, endIndex: Int = this.length): Spannable {
|
||||
return this.toSpannable()
|
||||
.also { spannable ->
|
||||
spannable.setSpan(
|
||||
ForegroundColorSpan(ContextCompat.getColor(context, color)),
|
||||
startIndex,
|
||||
endIndex,
|
||||
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE,
|
||||
)
|
||||
}
|
||||
}
|
||||
import java.util.Hashtable
|
||||
|
||||
@Suppress("MagicNumber")
|
||||
fun String.toQrCode(): Bitmap {
|
||||
|
|
@ -58,8 +29,6 @@ fun String.toQrCode(): Bitmap {
|
|||
return bmp
|
||||
}
|
||||
|
||||
fun String.urlEncode(): String = Uri.encode(this)
|
||||
|
||||
fun String.removePrefixOrNull(prefix: String): String? = when {
|
||||
startsWith(prefix) -> substring(prefix.length)
|
||||
else -> null
|
||||
|
|
|
|||
|
|
@ -1,24 +1,3 @@
|
|||
package com.tangem.tap.common.extensions
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
fun StringBuilder.appendIf(value: String, predicate: (String) -> Boolean): StringBuilder {
|
||||
if (predicate(value)) this.append(value)
|
||||
return this
|
||||
}
|
||||
|
||||
fun StringBuilder.appendIfNotNull(value: String?, prefix: String? = null, postfix: String? = null): StringBuilder {
|
||||
if (value == null) return this
|
||||
|
||||
prefix?.let { append(it) }
|
||||
append(value)
|
||||
postfix?.let { append(it) }
|
||||
return this
|
||||
}
|
||||
|
||||
fun String.appendIfNotNull(value: String?, prefix: String? = null, postfix: String? = null): String {
|
||||
return StringBuilder(this).apply { appendIfNotNull(value, prefix, postfix) }.toString()
|
||||
}
|
||||
|
||||
fun StringBuilder.breakLine(count: Int = 1): StringBuilder = append("\n".repeat(count))
|
||||
|
|
@ -1,31 +0,0 @@
|
|||
package com.tangem.tap.common.extensions
|
||||
|
||||
import android.content.Context
|
||||
import android.view.View
|
||||
import android.widget.Toast
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.fragment.app.Fragment
|
||||
|
||||
fun Context.toast(message: String, length: Int = Toast.LENGTH_LONG) {
|
||||
Toast.makeText(this, message, length).show()
|
||||
}
|
||||
|
||||
fun Context.toast(@StringRes messageRes: Int, length: Int = Toast.LENGTH_LONG) {
|
||||
Toast.makeText(this, this.getString(messageRes), length).show()
|
||||
}
|
||||
|
||||
fun View.toast(message: String, length: Int = Toast.LENGTH_LONG) {
|
||||
context.toast(message, length)
|
||||
}
|
||||
|
||||
fun View.toast(@StringRes messageRes: Int, length: Int = Toast.LENGTH_LONG) {
|
||||
context.toast(context.getString(messageRes), length)
|
||||
}
|
||||
|
||||
fun Fragment.toast(message: String, length: Int = Toast.LENGTH_LONG) {
|
||||
context?.toast(message, length)
|
||||
}
|
||||
|
||||
fun Fragment.toast(@StringRes messageRes: Int, length: Int = Toast.LENGTH_LONG) {
|
||||
context?.let { it.toast(it.getString(messageRes), length) }
|
||||
}
|
||||
|
|
@ -1,31 +0,0 @@
|
|||
package com.tangem.tap.common.extensions
|
||||
|
||||
import android.graphics.Color
|
||||
import androidx.annotation.ColorInt
|
||||
import androidx.core.graphics.luminance
|
||||
import androidx.core.graphics.toColorInt
|
||||
import com.tangem.blockchain.common.Token
|
||||
|
||||
@Suppress("MagicNumber")
|
||||
@ColorInt
|
||||
fun Token.getColor(isTestnet: Boolean = false): Int {
|
||||
val defaultColor = "#C7C7CC".toColorInt() // equivalent to R.color.lightGray4
|
||||
|
||||
return if (isTestnet) {
|
||||
defaultColor
|
||||
} else {
|
||||
try {
|
||||
("#" + this.contractAddress.subSequence(2..7).toString()).toColorInt()
|
||||
} catch (exception: Exception) {
|
||||
defaultColor
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("MagicNumber")
|
||||
@ColorInt
|
||||
fun Token.getTextColor(isTestnet: Boolean = false): Int = when {
|
||||
isTestnet -> Color.WHITE
|
||||
this.getColor().luminance > 0.5 -> Color.BLACK
|
||||
else -> Color.WHITE
|
||||
}
|
||||
|
|
@ -1,24 +0,0 @@
|
|||
package com.tangem.tap.common.extensions
|
||||
|
||||
import androidx.transition.Transition
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
inline fun Transition.addListener(
|
||||
crossinline onStart: (animator: Transition) -> Unit = {},
|
||||
crossinline onEnd: (animator: Transition) -> Unit = {},
|
||||
crossinline onCancel: (animator: Transition) -> Unit = {},
|
||||
crossinline onPause: (animator: Transition) -> Unit = {},
|
||||
crossinline onRepeat: (animator: Transition) -> Unit = {},
|
||||
): Transition.TransitionListener {
|
||||
val listener = object : Transition.TransitionListener {
|
||||
override fun onTransitionStart(transition: Transition) = onStart(transition)
|
||||
override fun onTransitionEnd(transition: Transition) = onEnd(transition)
|
||||
override fun onTransitionCancel(transition: Transition) = onCancel(transition)
|
||||
override fun onTransitionPause(transition: Transition) = onPause(transition)
|
||||
override fun onTransitionResume(transition: Transition) = onRepeat(transition)
|
||||
}
|
||||
addListener(listener)
|
||||
return listener
|
||||
}
|
||||
|
|
@ -3,25 +3,12 @@
|
|||
package com.tangem.tap.common.extensions
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.ClipData
|
||||
import android.content.ClipboardManager
|
||||
import android.content.Context
|
||||
import android.content.ContextWrapper
|
||||
import android.content.Intent
|
||||
import android.content.*
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.util.TypedValue
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.inputmethod.InputMethodManager
|
||||
import androidx.annotation.ColorInt
|
||||
import androidx.annotation.ColorRes
|
||||
import androidx.annotation.DrawableRes
|
||||
import androidx.annotation.PluralsRes
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.annotation.*
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.fragment.app.Fragment
|
||||
import com.google.android.material.card.MaterialCardView
|
||||
|
||||
fun Context.getDrawableCompat(@DrawableRes drawableResId: Int): Drawable? {
|
||||
return ContextCompat.getDrawable(this, drawableResId)
|
||||
|
|
@ -72,48 +59,9 @@ fun View.hide(invokeBeforeStateChanged: (() -> Unit)? = null) {
|
|||
this.visibility = View.GONE
|
||||
}
|
||||
|
||||
fun View.invisible(invisible: Boolean = true, invokeBeforeStateChanged: (() -> Unit)? = null) {
|
||||
if (invisible) {
|
||||
if (this.visibility == View.INVISIBLE) return
|
||||
|
||||
invokeBeforeStateChanged?.invoke()
|
||||
this.visibility = View.INVISIBLE
|
||||
} else {
|
||||
this.show(invokeBeforeStateChanged)
|
||||
}
|
||||
}
|
||||
|
||||
fun Context.dpToPixels(dp: Int): Int = TypedValue.applyDimension(
|
||||
TypedValue.COMPLEX_UNIT_DIP,
|
||||
dp.toFloat(),
|
||||
this.resources.displayMetrics,
|
||||
).toInt()
|
||||
|
||||
tailrec fun Context?.getActivity(): Activity? = this as? Activity
|
||||
?: (this as? ContextWrapper)?.baseContext?.getActivity()
|
||||
|
||||
fun MaterialCardView.setMargins(
|
||||
marginLeftDp: Int = 16,
|
||||
marginTopDp: Int = 8,
|
||||
marginRightDp: Int = 16,
|
||||
marginBottomDp: Int = 8,
|
||||
) {
|
||||
val params = this.layoutParams
|
||||
(params as ViewGroup.MarginLayoutParams).setMargins(
|
||||
context.dpToPixels(marginLeftDp),
|
||||
context.dpToPixels(marginTopDp),
|
||||
context.dpToPixels(marginRightDp),
|
||||
context.dpToPixels(marginBottomDp),
|
||||
)
|
||||
this.layoutParams = params
|
||||
}
|
||||
|
||||
fun View.hideKeyboard() {
|
||||
val inputMethodManager =
|
||||
context.getSystemService(Context.INPUT_METHOD_SERVICE) as? InputMethodManager
|
||||
inputMethodManager?.hideSoftInputFromWindow(this.windowToken, 0)
|
||||
}
|
||||
|
||||
fun Context.copyToClipboard(value: Any, label: String = "") {
|
||||
val clipboard = getSystemService(Context.CLIPBOARD_SERVICE) as? ClipboardManager ?: return
|
||||
|
||||
|
|
@ -140,37 +88,6 @@ fun Context.shareText(text: String) {
|
|||
startActivity(shareIntent)
|
||||
}
|
||||
|
||||
fun Fragment.shareText(text: String) {
|
||||
requireContext().shareText(text)
|
||||
}
|
||||
|
||||
fun View.getString(resId: Int, vararg formatArgs: Any?): String {
|
||||
return context.getString(resId, *formatArgs)
|
||||
}
|
||||
|
||||
fun View.animateVisibility(
|
||||
show: Boolean,
|
||||
durationMillis: Long = SHORT_ANIMATION_DURATION,
|
||||
hiddenVisibility: Int = View.GONE,
|
||||
) {
|
||||
if (show) {
|
||||
if (this.visibility == View.VISIBLE) return
|
||||
this.animate()
|
||||
.alpha(1f)
|
||||
.setDuration(durationMillis)
|
||||
.withStartAction {
|
||||
this.alpha = 0f
|
||||
this.isVisible = true
|
||||
}
|
||||
} else {
|
||||
if (this.visibility == hiddenVisibility) return
|
||||
this.animate()
|
||||
.alpha(0f)
|
||||
.setDuration(durationMillis)
|
||||
.withStartAction {
|
||||
this.visibility = hiddenVisibility
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private const val SHORT_ANIMATION_DURATION = 80L
|
||||
}
|
||||
|
|
@ -3,15 +3,9 @@ package com.tangem.tap.common.extensions
|
|||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.ViewParent
|
||||
import androidx.core.view.forEach
|
||||
import androidx.transition.AutoTransition
|
||||
import androidx.transition.Transition
|
||||
import androidx.transition.TransitionManager
|
||||
import com.google.android.material.chip.Chip
|
||||
import com.google.android.material.chip.ChipGroup
|
||||
import com.tangem.tap.common.GlobalLayoutStateHandler
|
||||
import timber.log.Timber
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
|
|
@ -20,30 +14,6 @@ fun ViewGroup.inflate(viewToInflate: Int, attachToRoot: Boolean = false): View {
|
|||
return LayoutInflater.from(context).inflate(viewToInflate, this, attachToRoot)
|
||||
}
|
||||
|
||||
fun ViewParent?.beginDelayedTransition(transition: Transition = AutoTransition()) {
|
||||
if (this == null) Timber.e("Can't invoke beginDelayedTransition, because parent is NULL")
|
||||
(this as? ViewGroup)?.beginDelayedTransition(transition)
|
||||
}
|
||||
|
||||
fun ViewGroup.beginDelayedTransition(transition: Transition = AutoTransition()) {
|
||||
TransitionManager.beginDelayedTransition(this, transition)
|
||||
}
|
||||
|
||||
fun View.beginDelayedTransition(transition: Transition = AutoTransition()) {
|
||||
(this as? ViewGroup)?.beginDelayedTransition(transition)
|
||||
}
|
||||
|
||||
fun ChipGroup.fitChipsByGroupWidth() {
|
||||
val layoutStateHandler = GlobalLayoutStateHandler(this)
|
||||
layoutStateHandler.onStateChanged = stateHandler@{
|
||||
if (it.childCount < 2) {
|
||||
layoutStateHandler.detach()
|
||||
return@stateHandler
|
||||
}
|
||||
|
||||
val spacingBetweenViews = it.chipSpacingHorizontal * (it.childCount - 1)
|
||||
val width = (it.width - spacingBetweenViews) / it.childCount
|
||||
it.forEach { chip -> (chip as? Chip)?.width = width }
|
||||
layoutStateHandler.detach()
|
||||
}
|
||||
}
|
||||
|
|
@ -2,17 +2,6 @@ package com.tangem.tap.common.extensions
|
|||
|
||||
import android.webkit.WebView
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
fun WebView.configureSettings() {
|
||||
resumeTimers()
|
||||
settings.apply {
|
||||
javaScriptEnabled = true
|
||||
domStorageEnabled = true
|
||||
}
|
||||
}
|
||||
|
||||
fun WebView.stop() {
|
||||
stopLoading()
|
||||
pauseTimers()
|
||||
|
|
|
|||
|
|
@ -63,6 +63,7 @@ class ScanQrCodeActivity : AppCompatActivity() {
|
|||
}
|
||||
|
||||
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
|
||||
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
|
||||
if (requestCode != PERMISSION_REQUEST_CODE) return
|
||||
|
||||
if (grantResults.isEmpty() || grantResults[0] != PackageManager.PERMISSION_GRANTED) {
|
||||
|
|
|
|||
|
|
@ -3,8 +3,8 @@ package com.tangem.tap.common.redux
|
|||
import com.tangem.common.extensions.VoidCallback
|
||||
import com.tangem.core.navigation.StateDialog
|
||||
import com.tangem.tap.common.TestAction
|
||||
import com.tangem.tap.domain.model.Currency
|
||||
import com.tangem.tap.domain.model.WalletAddressData
|
||||
import com.tangem.tap.features.wallet.models.Currency
|
||||
import com.tangem.wallet.R
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -66,7 +66,7 @@ data class AppState(
|
|||
private val domainState: DomainState
|
||||
get() = domainStore.state
|
||||
|
||||
val domainNetworks: NetworkServices
|
||||
private val domainNetworks: NetworkServices
|
||||
get() = domainState.globalState.networkServices
|
||||
|
||||
val featureRepositoryProvider: FeatureRepositoryProvider
|
||||
|
|
|
|||
|
|
@ -20,22 +20,14 @@ class NotificationsHandler(coordinatorLayout: CoordinatorLayout) {
|
|||
private val basicCoordinatorLayout = WeakReference(coordinatorLayout)
|
||||
private var baseLayout = basicCoordinatorLayout
|
||||
|
||||
fun replaceBaseLayout(coordinatorLayout: CoordinatorLayout) {
|
||||
baseLayout = WeakReference(coordinatorLayout)
|
||||
}
|
||||
|
||||
fun returnBaseLayout() {
|
||||
baseLayout = basicCoordinatorLayout
|
||||
}
|
||||
|
||||
fun showNotification(message: String) {
|
||||
private fun showNotification(message: String) {
|
||||
baseLayout.get()?.let { layout ->
|
||||
Snackbar.make(layout, message, Snackbar.LENGTH_LONG)
|
||||
.also { snackbar -> snackbar.show() }
|
||||
}
|
||||
}
|
||||
|
||||
fun showDebugNotification(message: String) {
|
||||
private fun showDebugNotification(message: String) {
|
||||
baseLayout.get()?.let { layout ->
|
||||
Snackbar.make(layout, message, Snackbar.LENGTH_LONG)
|
||||
.also { snackbar ->
|
||||
|
|
|
|||
|
|
@ -1,7 +0,0 @@
|
|||
package com.tangem.tap.common.redux
|
||||
|
||||
import org.rekotlin.Action
|
||||
|
||||
abstract class Request : Action {
|
||||
abstract suspend fun execute()
|
||||
}
|
||||
|
|
@ -14,7 +14,6 @@ import com.tangem.tap.common.feedback.FeedbackManager
|
|||
import com.tangem.tap.common.redux.DebugErrorAction
|
||||
import com.tangem.tap.common.redux.ErrorAction
|
||||
import com.tangem.tap.common.redux.NotificationAction
|
||||
import com.tangem.tap.common.redux.ToastNotificationAction
|
||||
import com.tangem.tap.domain.TapError
|
||||
import com.tangem.tap.domain.configurable.warningMessage.WarningMessage
|
||||
import com.tangem.tap.domain.configurable.warningMessage.WarningMessagesManager
|
||||
|
|
@ -25,7 +24,6 @@ sealed class GlobalAction : Action {
|
|||
|
||||
// notifications
|
||||
data class ShowNotification(override val messageResource: Int) : GlobalAction(), NotificationAction
|
||||
data class ShowToastNotification(override val messageResource: Int) : GlobalAction(), ToastNotificationAction
|
||||
data class ShowErrorNotification(override val error: TapError) : GlobalAction(), ErrorAction
|
||||
data class DebugShowErrorNotification(override val error: TapError) : GlobalAction(), DebugErrorAction
|
||||
|
||||
|
|
|
|||
|
|
@ -169,7 +169,7 @@ class TangemShopService(application: Application, shopifyShop: ShopifyShop) {
|
|||
return Result.success(products)
|
||||
}
|
||||
|
||||
suspend fun applyPromoCode(promoCode: String, productType: ProductType): Result<TangemProduct> {
|
||||
private suspend fun applyPromoCode(promoCode: String, productType: ProductType): Result<TangemProduct> {
|
||||
val checkout = checkouts[productType] ?: return Result.failure(Exception("No checkout"))
|
||||
|
||||
val result = if (promoCode.isBlank()) {
|
||||
|
|
|
|||
|
|
@ -19,26 +19,7 @@ import kotlin.coroutines.suspendCoroutine
|
|||
|
||||
@Suppress("LargeClass")
|
||||
class ShopifyService(private val application: Application, val shop: ShopifyShop) {
|
||||
val client: GraphClient by lazy { initClient() }
|
||||
|
||||
suspend fun getShopName(): Result<String> {
|
||||
val query = query { rootQuery: QueryRootQuery ->
|
||||
rootQuery
|
||||
.shop { shopQuery: ShopQuery ->
|
||||
shopQuery
|
||||
.name()
|
||||
}
|
||||
}
|
||||
return when (val result = queryAsync(query)) {
|
||||
is GraphCallResult.Success -> {
|
||||
val name = result.response.data!!.shop.name
|
||||
Result.success(name)
|
||||
}
|
||||
is GraphCallResult.Failure -> {
|
||||
Result.failure(result.error)
|
||||
}
|
||||
}
|
||||
}
|
||||
private val client: GraphClient by lazy { initClient() }
|
||||
|
||||
@Suppress("MagicNumber")
|
||||
suspend fun getProducts(collectionTitleFilter: String? = null): Result<List<Product>> {
|
||||
|
|
|
|||
|
|
@ -28,12 +28,12 @@ abstract class BaseTruncate : Truncate {
|
|||
protected var hasBeenTruncated = false
|
||||
|
||||
override fun apply(tv: TextView, text: String, with: String): String {
|
||||
val roughLength = getRoughFitLength(tv, text, with)
|
||||
val roughLength = getRoughFitLength(tv, text)
|
||||
val fittedText = preciseFitting(tv, roughTruncate(text, roughLength), with)
|
||||
return if (hasBeenTruncated) attachWith(fittedText, with) else fittedText
|
||||
}
|
||||
|
||||
protected fun getRoughFitLength(tv: TextView, text: String, with: String): Int {
|
||||
private fun getRoughFitLength(tv: TextView, text: String): Int {
|
||||
val existingSpace = tv.measuredWidth - (tv.paddingStart + tv.paddingEnd)
|
||||
val textWillTakeSpace = tv.paint.measureText(text)
|
||||
val overSizeRatio: Float = textWillTakeSpace / existingSpace
|
||||
|
|
@ -44,7 +44,7 @@ abstract class BaseTruncate : Truncate {
|
|||
return maxLengthOfText.toInt()
|
||||
}
|
||||
|
||||
protected fun preciseFitting(tv: TextView, text: String, with: String): String {
|
||||
private fun preciseFitting(tv: TextView, text: String, with: String): String {
|
||||
if (!hasBeenTruncated) return text
|
||||
|
||||
val spaceForText = tv.measuredWidth - (tv.paddingStart + tv.paddingEnd)
|
||||
|
|
@ -126,11 +126,5 @@ fun TextView.truncateWith(text: String, type: TruncateType, with: String = "..."
|
|||
return truncate.apply(this, text, with)
|
||||
}
|
||||
|
||||
fun TextView.truncateStartWith(text: String, with: String = "..."): String =
|
||||
this.truncateWith(text, TruncateType.START, with)
|
||||
|
||||
fun TextView.truncateMiddleWith(text: String, with: String = "..."): String =
|
||||
this.truncateWith(text, TruncateType.MIDDLE, with)
|
||||
|
||||
fun TextView.truncateEndWith(text: String, with: String = "..."): String =
|
||||
this.truncateWith(text, TruncateType.END, with)
|
||||
this.truncateWith(text, TruncateType.MIDDLE, with)
|
||||
|
|
@ -1,36 +0,0 @@
|
|||
package com.tangem.tap.common.utils
|
||||
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import org.rekotlin.StoreSubscriber
|
||||
|
||||
private val mainLooper by lazy { Looper.getMainLooper() }
|
||||
private val mainHandler by lazy { Handler(mainLooper) }
|
||||
|
||||
/**
|
||||
* A subscriber interface for safely subscribing to state changes in a Store.
|
||||
*
|
||||
* @param State the type of the state in the Store
|
||||
*/
|
||||
interface SafeStoreSubscriber<State> : StoreSubscriber<State> {
|
||||
|
||||
/**
|
||||
* A function that will be called when the state in the Store changes.
|
||||
*
|
||||
* @param state the new state in the Store
|
||||
*/
|
||||
override fun newState(state: State) {
|
||||
if (Thread.currentThread() != mainLooper.thread) {
|
||||
mainHandler.post { newStateOnMain(state) }
|
||||
} else {
|
||||
newStateOnMain(state)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A function that will be called on the main thread when the state in the Store changes.
|
||||
*
|
||||
* @param state the new state in the Store
|
||||
*/
|
||||
fun newStateOnMain(state: State)
|
||||
}
|
||||
|
|
@ -27,7 +27,6 @@ import com.tangem.operations.pins.SetUserCodeCommand
|
|||
import com.tangem.operations.usersetttings.SetUserCodeRecoveryAllowedTask
|
||||
import com.tangem.tap.common.analytics.events.Basic
|
||||
import com.tangem.tap.derivationsFinder
|
||||
import com.tangem.tap.domain.tasks.CreateWalletAndRescanTask
|
||||
import com.tangem.tap.domain.tasks.product.CreateProductWalletTask
|
||||
import com.tangem.tap.domain.tasks.product.CreateProductWalletTaskResponse
|
||||
import com.tangem.tap.domain.tasks.product.ResetToFactorySettingsTask
|
||||
|
|
@ -126,15 +125,6 @@ class TangemSdkManager(
|
|||
}
|
||||
}
|
||||
|
||||
suspend fun createWallet(cardId: String?): CompletionResult<CardDTO> {
|
||||
return runTaskAsyncReturnOnMain(
|
||||
CreateWalletAndRescanTask(),
|
||||
cardId,
|
||||
initialMessage = Message(resources.getString(R.string.initial_message_create_wallet_body)),
|
||||
)
|
||||
.map { CardDTO(it) }
|
||||
}
|
||||
|
||||
suspend fun derivePublicKeys(
|
||||
cardId: String?,
|
||||
derivations: Map<ByteArrayKey, List<DerivationPath>>,
|
||||
|
|
|
|||
|
|
@ -22,11 +22,7 @@ sealed class TapError(
|
|||
|
||||
object UnknownError : TapError(R.string.send_error_unknown)
|
||||
open class CustomError(val customMessage: String) : TapError(R.string.common_custom_string, listOf(customMessage))
|
||||
data class UnsupportedState(
|
||||
val stateError: String,
|
||||
) : TapError(R.string.common_custom_string, listOf("Unsupported state: $stateError"))
|
||||
|
||||
object UnknownBlockchain : TapError(R.string.wallet_error_unsupported_blockchain_subtitle)
|
||||
object NoInternetConnection : TapError(R.string.wallet_notification_no_internet)
|
||||
object AmountExceedsBalance : TapError(R.string.send_validation_amount_exceeds_balance)
|
||||
data class AmountLowerExistentialDeposit(
|
||||
|
|
@ -41,7 +37,6 @@ sealed class TapError(
|
|||
object DustChange : TapError(R.string.send_error_dust_change)
|
||||
|
||||
sealed class WalletManager {
|
||||
object CreationError : CustomError(customMessage = "Can't create wallet manager")
|
||||
class NoAccountError(amountToCreateAccount: String) : CustomError(amountToCreateAccount)
|
||||
class InternalError(message: String) : CustomError(message)
|
||||
object BlockchainIsUnreachableTryLater : TapError(R.string.wallet_balance_blockchain_unreachable_try_later)
|
||||
|
|
|
|||
|
|
@ -51,14 +51,12 @@ data class WarningMessage(
|
|||
}
|
||||
|
||||
enum class Location {
|
||||
@Json(name = "main")
|
||||
MainScreen,
|
||||
|
||||
@Json(name = "send")
|
||||
SendScreen,
|
||||
}
|
||||
|
||||
enum class Origin {
|
||||
Local, Remote
|
||||
Remote,
|
||||
}
|
||||
}
|
||||
|
|
@ -1,8 +1,6 @@
|
|||
package com.tangem.tap.domain.configurable.warningMessage
|
||||
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.utils.extensions.removeBy
|
||||
import com.tangem.wallet.R
|
||||
import java.util.concurrent.CopyOnWriteArrayList
|
||||
|
||||
/**
|
||||
|
|
@ -14,13 +12,6 @@ class WarningMessagesManager {
|
|||
|
||||
private val warningsList = CopyOnWriteArrayList<WarningMessage>()
|
||||
|
||||
fun addWarning(warning: WarningMessage) {
|
||||
if (findWarning(warning) == null) {
|
||||
warningsList.add(warning)
|
||||
sortByPriority()
|
||||
}
|
||||
}
|
||||
|
||||
fun getWarnings(location: WarningMessage.Location, blockchains: List<Blockchain>): List<WarningMessage> {
|
||||
return warningsList.filter { message ->
|
||||
val messageBlockchains = message.blockchainList
|
||||
|
|
@ -45,131 +36,7 @@ class WarningMessagesManager {
|
|||
}
|
||||
}
|
||||
|
||||
fun removeWarnings(origin: WarningMessage.Origin) {
|
||||
warningsList.removeBy { it.origin == origin }
|
||||
sortByPriority()
|
||||
}
|
||||
|
||||
fun removeWarnings(messageRes: Int) {
|
||||
warningsList.removeBy { it.messageResId == messageRes }
|
||||
}
|
||||
|
||||
fun containsWarning(warning: WarningMessage) = warning in warningsList
|
||||
|
||||
private fun sortByPriority() {
|
||||
warningsList.sortBy { it.priority.ordinal }
|
||||
}
|
||||
|
||||
private fun findWarning(warning: WarningMessage): WarningMessage? {
|
||||
return warningsList.firstOrNull { it == warning }
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val REMAINING_SIGNATURES_WARNING = 10
|
||||
|
||||
val devCardWarning = WarningMessage(
|
||||
title = "",
|
||||
message = "",
|
||||
type = WarningMessage.Type.Permanent,
|
||||
priority = WarningMessage.Priority.Critical,
|
||||
location = listOf(WarningMessage.Location.MainScreen),
|
||||
blockchains = null,
|
||||
titleResId = R.string.common_warning,
|
||||
// messageResId = R.string.alert_developer_card,
|
||||
origin = WarningMessage.Origin.Local,
|
||||
)
|
||||
|
||||
val alreadySignedHashesWarning = WarningMessage(
|
||||
title = "",
|
||||
message = "",
|
||||
type = WarningMessage.Type.Temporary,
|
||||
priority = WarningMessage.Priority.Info,
|
||||
location = listOf(WarningMessage.Location.MainScreen),
|
||||
blockchains = null,
|
||||
titleResId = R.string.common_warning,
|
||||
// messageResId = R.string.alert_card_signed_transactions,
|
||||
origin = WarningMessage.Origin.Local,
|
||||
)
|
||||
|
||||
val signedHashesMultiWalletWarning = WarningMessage(
|
||||
title = "",
|
||||
message = "",
|
||||
type = WarningMessage.Type.Temporary,
|
||||
priority = WarningMessage.Priority.Info,
|
||||
location = listOf(WarningMessage.Location.MainScreen),
|
||||
blockchains = null,
|
||||
// titleResId = R.string.warning_important_security_info,
|
||||
// messageResId = R.string.warning_signed_tx_previously,
|
||||
origin = WarningMessage.Origin.Local,
|
||||
buttonTextId = R.string.warning_button_learn_more,
|
||||
titleFormatArg = "\u26A0",
|
||||
)
|
||||
|
||||
val appRatingWarning = WarningMessage(
|
||||
title = "",
|
||||
message = "",
|
||||
type = WarningMessage.Type.AppRating,
|
||||
priority = WarningMessage.Priority.Info,
|
||||
location = listOf(WarningMessage.Location.MainScreen),
|
||||
blockchains = null,
|
||||
titleResId = R.string.warning_rate_app_title,
|
||||
messageResId = R.string.warning_rate_app_message,
|
||||
origin = WarningMessage.Origin.Local,
|
||||
)
|
||||
|
||||
val onlineVerificationFailed = WarningMessage(
|
||||
title = "",
|
||||
message = "",
|
||||
type = WarningMessage.Type.Permanent,
|
||||
priority = WarningMessage.Priority.Critical,
|
||||
location = listOf(WarningMessage.Location.MainScreen),
|
||||
blockchains = null,
|
||||
titleResId = R.string.warning_failed_to_verify_card_title,
|
||||
messageResId = R.string.warning_failed_to_verify_card_message,
|
||||
origin = WarningMessage.Origin.Local,
|
||||
)
|
||||
|
||||
val testCardWarning = WarningMessage(
|
||||
title = "",
|
||||
message = "",
|
||||
type = WarningMessage.Type.TestCard,
|
||||
priority = WarningMessage.Priority.Critical,
|
||||
location = listOf(WarningMessage.Location.MainScreen, WarningMessage.Location.SendScreen),
|
||||
blockchains = null,
|
||||
titleResId = R.string.common_warning,
|
||||
messageResId = R.string.warning_testnet_card_message,
|
||||
origin = WarningMessage.Origin.Local,
|
||||
)
|
||||
|
||||
val demoCardWarning = WarningMessage(
|
||||
title = "",
|
||||
message = "",
|
||||
type = WarningMessage.Type.Permanent,
|
||||
priority = WarningMessage.Priority.Critical,
|
||||
location = listOf(WarningMessage.Location.MainScreen),
|
||||
blockchains = null,
|
||||
titleResId = R.string.common_warning,
|
||||
// messageResId = R.string.alert_demo_message,
|
||||
origin = WarningMessage.Origin.Local,
|
||||
)
|
||||
|
||||
fun remainingSignaturesNotEnough(remainingSignatures: Int): WarningMessage {
|
||||
return WarningMessage(
|
||||
title = "",
|
||||
message = "",
|
||||
type = WarningMessage.Type.Permanent,
|
||||
priority = WarningMessage.Priority.Critical,
|
||||
location = listOf(WarningMessage.Location.MainScreen),
|
||||
blockchains = null,
|
||||
titleResId = R.string.common_warning,
|
||||
// messageResId = R.string.warning_low_signatures_format,
|
||||
origin = WarningMessage.Origin.Local,
|
||||
messageFormatArg = remainingSignatures.toString(),
|
||||
)
|
||||
}
|
||||
|
||||
// fun isAlreadySignedHashesWarning(warning: WarningMessage): Boolean {
|
||||
// return warning.messageResId == R.string.alert_card_signed_transactions
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
|
@ -9,17 +9,6 @@ import com.tangem.domain.userwallets.Artwork
|
|||
import com.tangem.operations.attestation.CardVerifyAndGetInfo
|
||||
import com.tangem.operations.attestation.OnlineCardVerifier
|
||||
|
||||
val CardDTO.remainingSignatures: Int?
|
||||
get() = this.wallets.firstOrNull()?.remainingSignatures
|
||||
|
||||
val CardDTO.isHdWalletAllowedByApp: Boolean
|
||||
get() = settings.isHDWalletAllowed
|
||||
|
||||
@Suppress("UnnecessaryParentheses")
|
||||
fun CardDTO.hasSignedHashes(): Boolean {
|
||||
return wallets.any { (it.totalSignedHashes ?: 0) > 0 }
|
||||
}
|
||||
|
||||
fun CardDTO.signedHashesCount(): Int {
|
||||
return wallets.sumOf { it.totalSignedHashes ?: 0 }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,17 +0,0 @@
|
|||
package com.tangem.tap.domain.extensions
|
||||
|
||||
import com.tangem.blockchain.common.Token
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
|
||||
private val tokenCustomIconUrls = mutableMapOf<String, String>()
|
||||
|
||||
fun Token.getCustomIconUrl(): String? {
|
||||
return tokenCustomIconUrls[this.contractAddress]
|
||||
}
|
||||
|
||||
fun Token.setCustomIconUrl(url: String) {
|
||||
tokenCustomIconUrls[this.contractAddress] = url
|
||||
}
|
||||
64
app/src/main/java/com/tangem/tap/domain/model/Currency.kt
Normal file
64
app/src/main/java/com/tangem/tap/domain/model/Currency.kt
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
package com.tangem.tap.domain.model
|
||||
|
||||
import com.tangem.domain.common.BlockchainNetwork
|
||||
import com.tangem.domain.features.addCustomToken.CustomCurrency
|
||||
import com.tangem.tap.common.redux.global.CryptoCurrencyName
|
||||
import com.tangem.blockchain.common.Blockchain as SdkBlockchain
|
||||
import com.tangem.blockchain.common.Token as SdkToken
|
||||
|
||||
sealed interface Currency {
|
||||
val blockchain: SdkBlockchain
|
||||
val currencySymbol: CryptoCurrencyName
|
||||
val derivationPath: String?
|
||||
val decimals
|
||||
get() = when (this) {
|
||||
is Blockchain -> blockchain.decimals()
|
||||
is Token -> token.decimals
|
||||
}
|
||||
|
||||
data class Token(
|
||||
val token: SdkToken,
|
||||
override val blockchain: SdkBlockchain,
|
||||
override val derivationPath: String?,
|
||||
) : Currency {
|
||||
override val currencySymbol = token.symbol
|
||||
}
|
||||
|
||||
data class Blockchain(
|
||||
override val blockchain: SdkBlockchain,
|
||||
override val derivationPath: String?,
|
||||
) : Currency {
|
||||
override val currencySymbol: CryptoCurrencyName = blockchain.currency
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun fromBlockchainNetwork(blockchainNetwork: BlockchainNetwork, token: SdkToken? = null): Currency {
|
||||
return if (token != null) {
|
||||
Token(
|
||||
token = token,
|
||||
blockchain = blockchainNetwork.blockchain,
|
||||
derivationPath = blockchainNetwork.derivationPath,
|
||||
)
|
||||
} else {
|
||||
Blockchain(
|
||||
blockchain = blockchainNetwork.blockchain,
|
||||
derivationPath = blockchainNetwork.derivationPath,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun fromCustomCurrency(customCurrency: CustomCurrency): Currency {
|
||||
return when (customCurrency) {
|
||||
is CustomCurrency.CustomBlockchain -> Blockchain(
|
||||
blockchain = customCurrency.network,
|
||||
derivationPath = customCurrency.derivationPath?.rawPath,
|
||||
)
|
||||
is CustomCurrency.CustomToken -> Token(
|
||||
token = customCurrency.token,
|
||||
blockchain = customCurrency.network,
|
||||
derivationPath = customCurrency.derivationPath?.rawPath,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,14 +1,8 @@
|
|||
package com.tangem.tap.features.wallet.models
|
||||
package com.tangem.tap.domain.model
|
||||
|
||||
import com.tangem.blockchain.common.Amount
|
||||
import com.tangem.blockchain.common.AmountType
|
||||
import com.tangem.blockchain.common.Token
|
||||
import com.tangem.blockchain.common.TransactionData
|
||||
import com.tangem.blockchain.common.TransactionStatus
|
||||
import com.tangem.blockchain.common.Wallet
|
||||
import com.tangem.blockchain.extensions.isAboveZero
|
||||
import com.tangem.tap.common.extensions.toFormattedString
|
||||
import java.math.BigDecimal
|
||||
|
||||
data class PendingTransaction(
|
||||
val transactionData: TransactionData,
|
||||
|
|
@ -20,10 +14,6 @@ data class PendingTransaction(
|
|||
PendingTransactionType.Unknown -> null
|
||||
}
|
||||
|
||||
val amountValue: BigDecimal? = transactionData.amount.value
|
||||
|
||||
val amountValueUi: String? = amountValue?.toFormattedString(transactionData.amount.decimals)
|
||||
|
||||
val currency: String = transactionData.amount.currencySymbol
|
||||
|
||||
private fun nullIfUnknown(address: String): String? = if (address == "unknown") null else address
|
||||
|
|
@ -46,15 +36,6 @@ fun List<TransactionData>.toPendingTransactions(walletAddress: String): List<Pen
|
|||
return this.mapNotNull { it.toPendingTransaction(walletAddress) }
|
||||
}
|
||||
|
||||
fun List<PendingTransaction>.filterByCoin(): List<PendingTransaction> {
|
||||
return this.filter { it.transactionData.amount.type == AmountType.Coin }
|
||||
}
|
||||
|
||||
fun TransactionData.toPendingTransactionForToken(token: Token, walletAddress: String): PendingTransaction? {
|
||||
if (this.amount.currencySymbol != token.symbol) return null
|
||||
return this.toPendingTransaction(walletAddress)
|
||||
}
|
||||
|
||||
fun Wallet.getPendingTransactions(type: PendingTransactionType? = null): List<PendingTransaction> {
|
||||
val txs = recentTransactions.toPendingTransactions(address)
|
||||
return when (type) {
|
||||
|
|
@ -63,24 +44,6 @@ fun Wallet.getPendingTransactions(type: PendingTransactionType? = null): List<Pe
|
|||
}
|
||||
}
|
||||
|
||||
fun Wallet.getPendingTransactions(token: Token): List<PendingTransaction> {
|
||||
return recentTransactions.mapNotNull { it.toPendingTransactionForToken(token, address) }
|
||||
}
|
||||
|
||||
fun Wallet.hasPendingTransactions(): Boolean {
|
||||
return getPendingTransactions().isNotEmpty()
|
||||
}
|
||||
|
||||
fun Wallet.getSendableAmounts(): List<Amount> {
|
||||
return amounts.values
|
||||
.filter { it.type != AmountType.Reserve }
|
||||
.filter { it.isAboveZero() }
|
||||
}
|
||||
|
||||
fun Wallet.hasSendableAmounts(): Boolean {
|
||||
return getSendableAmounts().isNotEmpty()
|
||||
}
|
||||
|
||||
fun Wallet.isSendableAmount(type: AmountType): Boolean {
|
||||
return amounts[type]?.isAboveZero() == true
|
||||
}
|
||||
|
|
@ -1,32 +0,0 @@
|
|||
package com.tangem.tap.domain.moduleMessage
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
* Base interface for conversion outputs from ModuleMessageConverter
|
||||
*/
|
||||
interface ConvertedMessage {
|
||||
val message: String
|
||||
}
|
||||
|
||||
/**
|
||||
* Dialog message used to construct a android.Dialog
|
||||
*/
|
||||
interface DialogMessage : ConvertedMessage {
|
||||
val title: String
|
||||
override val message: String
|
||||
val onPositive: String?
|
||||
val onNegative: String?
|
||||
val onNeutral: String?
|
||||
}
|
||||
|
||||
data class ConvertedStringMessage(
|
||||
override val message: String,
|
||||
) : ConvertedMessage
|
||||
|
||||
data class ConvertedDialogMessage(
|
||||
override val title: String,
|
||||
override val message: String,
|
||||
override val onPositive: String? = null,
|
||||
override val onNegative: String? = null,
|
||||
override val onNeutral: String? = null,
|
||||
) : DialogMessage
|
||||
|
|
@ -1,24 +0,0 @@
|
|||
package com.tangem.tap.domain.moduleMessage
|
||||
|
||||
import android.content.Context
|
||||
import com.tangem.common.module.ModuleMessage
|
||||
import com.tangem.common.module.ModuleMessageConverter
|
||||
import com.tangem.domain.DomainModuleMessage
|
||||
import com.tangem.tap.domain.moduleMessage.domain.DomainMessageConverter
|
||||
|
||||
class ModuleMessageConverter(
|
||||
private val context: Context,
|
||||
) : ModuleMessageConverter<ModuleMessage, ConvertedMessage> {
|
||||
|
||||
override fun convert(message: ModuleMessage): ConvertedMessage {
|
||||
val convertedMessage = when (message) {
|
||||
is DomainModuleMessage -> DomainMessageConverter(context).convert(message)
|
||||
else -> null
|
||||
}
|
||||
return convertedMessage ?: convertUnknownMessage(message)
|
||||
}
|
||||
|
||||
private fun convertUnknownMessage(message: ModuleMessage): ConvertedMessage {
|
||||
return ConvertedStringMessage("Unknown message: ${message::class.java.simpleName}")
|
||||
}
|
||||
}
|
||||
|
|
@ -1,34 +0,0 @@
|
|||
package com.tangem.tap.domain.moduleMessage.domain
|
||||
|
||||
import android.content.Context
|
||||
import com.tangem.common.module.ModuleMessageConverter
|
||||
import com.tangem.domain.AddCustomTokenError
|
||||
import com.tangem.domain.DomainModuleError
|
||||
import com.tangem.domain.DomainModuleMessage
|
||||
import com.tangem.tap.domain.moduleMessage.ConvertedMessage
|
||||
import com.tangem.tap.domain.moduleMessage.domain.converter.AddCustomTokenErrorConverter
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
class DomainMessageConverter(
|
||||
private val context: Context,
|
||||
) : ModuleMessageConverter<DomainModuleMessage, ConvertedMessage?> {
|
||||
|
||||
override fun convert(message: DomainModuleMessage): ConvertedMessage? {
|
||||
return when (message) {
|
||||
is DomainModuleError -> DomainErrorConverter(context).convert(message)
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class DomainErrorConverter(
|
||||
private val context: Context,
|
||||
) : ModuleMessageConverter<DomainModuleError, ConvertedMessage?> {
|
||||
|
||||
override fun convert(message: DomainModuleError): ConvertedMessage? = when (message) {
|
||||
is AddCustomTokenError -> AddCustomTokenErrorConverter(context).convert(message)
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
|
@ -1,42 +0,0 @@
|
|||
package com.tangem.tap.domain.moduleMessage.domain.converter
|
||||
|
||||
import android.content.Context
|
||||
import com.tangem.common.module.ModuleMessageConverter
|
||||
import com.tangem.domain.AddCustomTokenError
|
||||
import com.tangem.domain.DomainModuleError
|
||||
import com.tangem.tap.domain.moduleMessage.ConvertedMessage
|
||||
import com.tangem.tap.domain.moduleMessage.ConvertedStringMessage
|
||||
import com.tangem.wallet.R
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
internal class AddCustomTokenErrorConverter(
|
||||
private val context: Context,
|
||||
) : ModuleMessageConverter<DomainModuleError, ConvertedMessage?> {
|
||||
|
||||
@Suppress("MagicNumber")
|
||||
override fun convert(message: DomainModuleError): ConvertedMessage? {
|
||||
val customTokenError = message as? AddCustomTokenError ?: throw UnsupportedOperationException()
|
||||
|
||||
val rawMessage = when (customTokenError) {
|
||||
AddCustomTokenError.Warning.PotentialScamToken -> R.string.custom_token_validation_error_not_found
|
||||
AddCustomTokenError.Warning.TokenAlreadyAdded -> R.string.custom_token_validation_error_already_added
|
||||
AddCustomTokenError.Warning.UnsupportedSolanaToken -> R.string.alert_manage_tokens_unsupported_message
|
||||
AddCustomTokenError.InvalidContractAddress -> R.string.custom_token_creation_error_invalid_contract_address
|
||||
AddCustomTokenError.NetworkIsNotSelected -> R.string.custom_token_creation_error_network_not_selected
|
||||
AddCustomTokenError.InvalidDerivationPath -> R.string.custom_token_creation_error_invalid_derivation_path
|
||||
AddCustomTokenError.InvalidDecimalsCount -> {
|
||||
context.getString(R.string.custom_token_creation_error_wrong_decimals, 30)
|
||||
}
|
||||
AddCustomTokenError.FieldIsEmpty -> R.string.custom_token_creation_error_required_field
|
||||
else -> null
|
||||
}
|
||||
|
||||
return when (rawMessage) {
|
||||
is Int -> ConvertedStringMessage(context.getString(rawMessage))
|
||||
is String -> ConvertedStringMessage(rawMessage)
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,89 +0,0 @@
|
|||
package com.tangem.tap.domain.statePrinter
|
||||
|
||||
import com.squareup.moshi.FromJson
|
||||
import com.squareup.moshi.ToJson
|
||||
import com.tangem.common.json.MoshiJsonConverter
|
||||
import com.tangem.domain.redux.state.StringStateConverter
|
||||
import com.tangem.tap.common.redux.AppState
|
||||
import com.tangem.tap.features.onboarding.products.wallet.redux.BackupStep
|
||||
import com.tangem.tap.store
|
||||
import timber.log.Timber
|
||||
import java.math.BigInteger
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
class OnboardingWalletStateConverter : StringStateConverter<AppState> {
|
||||
private val converter = MoshiJsonConverter(
|
||||
MoshiJsonConverter.getTangemSdkAdapters() + internalAdapters(),
|
||||
MoshiJsonConverter.getTangemSdkTypedAdapters(),
|
||||
)
|
||||
|
||||
private fun internalAdapters(): List<Any> {
|
||||
return listOf(
|
||||
BackupStepAdapter(),
|
||||
BigIntegerAdapter(),
|
||||
|
||||
)
|
||||
}
|
||||
|
||||
override fun convert(stateHolder: AppState): String {
|
||||
val model = stateHolder.onboardingWalletState
|
||||
val json = converter.prettyPrint(model)
|
||||
|
||||
return json
|
||||
}
|
||||
}
|
||||
|
||||
fun printOnboardingWalletState() {
|
||||
val stringState = OnboardingWalletStateConverter().convert(store.state)
|
||||
Timber.d(stringState)
|
||||
}
|
||||
|
||||
class BackupStepAdapter {
|
||||
@ToJson
|
||||
fun toJson(src: BackupStep): String {
|
||||
return when (src) {
|
||||
BackupStep.AddBackupCards -> "AddBackupCards"
|
||||
BackupStep.EnterAccessCode -> "EnterAccessCode"
|
||||
BackupStep.Finished -> "Finished"
|
||||
BackupStep.InitBackup -> "InitBackup"
|
||||
BackupStep.ReenterAccessCode -> "ReenterAccessCode"
|
||||
BackupStep.ScanOriginCard -> "ScanOriginCard"
|
||||
BackupStep.SetAccessCode -> "SetAccessCode"
|
||||
is BackupStep.WriteBackupCard -> "WriteBackupCard[${src.cardNumber}]"
|
||||
BackupStep.WritePrimaryCard -> "WritePrimaryCard"
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("MagicNumber")
|
||||
@FromJson
|
||||
fun fromJson(json: String): BackupStep {
|
||||
return when (json) {
|
||||
"AddBackupCards" -> BackupStep.AddBackupCards
|
||||
"EnterAccessCode" -> BackupStep.EnterAccessCode
|
||||
"Finished" -> BackupStep.Finished
|
||||
"InitBackup" -> BackupStep.InitBackup
|
||||
"ReenterAccessCode" -> BackupStep.ReenterAccessCode
|
||||
"ScanOriginCard" -> BackupStep.ScanOriginCard
|
||||
"SetAccessCode" -> BackupStep.SetAccessCode
|
||||
"WriteBackupCard[1]" -> BackupStep.WriteBackupCard(1)
|
||||
"WriteBackupCard[2]" -> BackupStep.WriteBackupCard(2)
|
||||
"WriteBackupCard[3]" -> BackupStep.WriteBackupCard(3)
|
||||
"WritePrimaryCard" -> BackupStep.WritePrimaryCard
|
||||
else -> throw UnsupportedOperationException()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class BigIntegerAdapter {
|
||||
@ToJson
|
||||
fun toJson(src: BigInteger): String {
|
||||
return src.toString()
|
||||
}
|
||||
|
||||
@FromJson
|
||||
fun fromJson(json: String): BigInteger {
|
||||
return BigInteger(json)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,44 +0,0 @@
|
|||
package com.tangem.tap.domain.statePrinter
|
||||
|
||||
import com.tangem.common.extensions.toHexString
|
||||
import com.tangem.common.json.MoshiJsonConverter
|
||||
import com.tangem.domain.redux.state.StringStateConverter
|
||||
import com.tangem.tap.common.redux.AppState
|
||||
import com.tangem.tap.store
|
||||
import timber.log.Timber
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
class ScanResponseConverter : StringStateConverter<AppState> {
|
||||
private val converter = MoshiJsonConverter.INSTANCE
|
||||
|
||||
override fun convert(stateHolder: AppState): String {
|
||||
val scanResponse = stateHolder.globalState.scanResponse ?: return "NULL"
|
||||
|
||||
val scanResponseMap = mutableMapOf<String, Any?>()
|
||||
val derivedKeysMap = mutableMapOf<String, Any?>()
|
||||
scanResponse.derivedKeys.forEach { (keyWalletPubKey, mapExPubKeys) ->
|
||||
val exPubKeysMap = mapExPubKeys.entries.map { (derivationPath, exPubKey) ->
|
||||
mapOf(
|
||||
"derivationPath" to derivationPath.rawPath,
|
||||
"extendedPublicKey" to mapOf(
|
||||
"publicKey" to exPubKey.publicKey.toHexString(),
|
||||
"chainCode" to exPubKey.chainCode.toHexString(),
|
||||
),
|
||||
)
|
||||
}
|
||||
derivedKeysMap[keyWalletPubKey.bytes.toHexString()] = exPubKeysMap
|
||||
}
|
||||
scanResponseMap["derivedKeys"] = derivedKeysMap
|
||||
|
||||
val json = converter.prettyPrint(scanResponseMap)
|
||||
|
||||
return json
|
||||
}
|
||||
}
|
||||
|
||||
fun printScanResponseState() {
|
||||
val stringState = ScanResponseConverter().convert(store.state)
|
||||
Timber.d(stringState)
|
||||
}
|
||||
|
|
@ -1,41 +0,0 @@
|
|||
package com.tangem.tap.domain.tasks
|
||||
|
||||
import com.tangem.blockchain.common.Wallet
|
||||
import com.tangem.common.CompletionResult
|
||||
import com.tangem.common.core.CardSession
|
||||
import com.tangem.common.core.CardSessionRunnable
|
||||
import com.tangem.common.core.CompletionCallback
|
||||
import com.tangem.operations.CommandResponse
|
||||
import com.tangem.operations.sign.SignHashCommand
|
||||
|
||||
class TangemSignHashResponse(
|
||||
val signature: ByteArray,
|
||||
val totalSignedHashes: Int?,
|
||||
val remainingSignatures: Int?,
|
||||
) : CommandResponse
|
||||
|
||||
class SignHashTask(
|
||||
private val hash: ByteArray,
|
||||
private val publicKey: Wallet.PublicKey,
|
||||
) : CardSessionRunnable<TangemSignHashResponse> {
|
||||
override fun run(session: CardSession, callback: CompletionCallback<TangemSignHashResponse>) {
|
||||
SignHashCommand(hash, publicKey.seedKey, publicKey.derivationPath).run(session) { response ->
|
||||
|
||||
when (response) {
|
||||
is CompletionResult.Success -> {
|
||||
callback(
|
||||
CompletionResult.Success(
|
||||
TangemSignHashResponse(
|
||||
response.data.signature,
|
||||
response.data.totalSignedHashes,
|
||||
session.environment.card?.wallet(publicKey.seedKey)?.remainingSignatures,
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
is CompletionResult.Failure ->
|
||||
callback(CompletionResult.Failure(response.error))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -309,7 +309,5 @@ private class CreateWalletTangemWallet(
|
|||
}
|
||||
}
|
||||
|
||||
private companion object {
|
||||
val CURVES_FOR_WALLETS = listOf(EllipticCurve.Secp256k1, EllipticCurve.Ed25519)
|
||||
}
|
||||
private companion object
|
||||
}
|
||||
|
|
@ -13,9 +13,7 @@ internal data class UserWalletEncryptionKey(
|
|||
if (other !is UserWalletEncryptionKey) return false
|
||||
|
||||
if (walletId != other.walletId) return false
|
||||
if (!encryptionKey.contentEquals(other.encryptionKey)) return false
|
||||
|
||||
return true
|
||||
return encryptionKey.contentEquals(other.encryptionKey)
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
|
|
|
|||
|
|
@ -1,9 +1 @@
|
|||
package com.tangem.tap.domain.userWalletList.utils
|
||||
|
||||
internal fun List<ByteArray>.containsBA(element: ByteArray?): Boolean {
|
||||
this.forEach {
|
||||
if (it.contentEquals(element)) return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
package com.tangem.tap.domain.userWalletList.utils
|
||||
|
|
@ -1,14 +1,9 @@
|
|||
package com.tangem.tap.domain.walletconnect
|
||||
|
||||
import com.github.salomonbrys.kotson.fromJson
|
||||
import com.github.salomonbrys.kotson.toMap
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.GsonBuilder
|
||||
import com.google.gson.JsonArray
|
||||
import com.google.gson.JsonParser
|
||||
import com.google.gson.annotations.SerializedName
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.trustwallet.walletconnect.JSONRPC_VERSION
|
||||
|
||||
object EthSignHelper {
|
||||
private val gson: Gson by lazy {
|
||||
|
|
@ -33,68 +28,4 @@ object EthSignHelper {
|
|||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data class CustomJsonRpcRequest(
|
||||
val id: Long,
|
||||
val jsonrpc: String = JSONRPC_VERSION,
|
||||
val method: WCMethodExtended?,
|
||||
val params: JsonArray,
|
||||
) {
|
||||
|
||||
fun blockchainFromChainId(): Blockchain? {
|
||||
return try {
|
||||
val hex = params[0].asJsonObject.toMap()[CHAIN_ID_KEY]?.asString ?: ""
|
||||
Blockchain.fromChainId(Integer.decode(hex))
|
||||
} catch (exception: Exception) {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val CHAIN_ID_KEY = "chainId"
|
||||
}
|
||||
}
|
||||
|
||||
enum class WCMethodExtended {
|
||||
@SerializedName("wc_sessionRequest")
|
||||
SESSION_REQUEST,
|
||||
|
||||
@SerializedName("wc_sessionUpdate")
|
||||
SESSION_UPDATE,
|
||||
|
||||
@SerializedName("eth_sign")
|
||||
ETH_SIGN,
|
||||
|
||||
@SerializedName("personal_sign")
|
||||
ETH_PERSONAL_SIGN,
|
||||
|
||||
@SerializedName("eth_signTypedData")
|
||||
ETH_SIGN_TYPE_DATA,
|
||||
|
||||
@SerializedName("eth_signTypedData_v4")
|
||||
ETH_SIGN_TYPE_DATA_V4,
|
||||
|
||||
@SerializedName("eth_signTransaction")
|
||||
ETH_SIGN_TRANSACTION,
|
||||
|
||||
@SerializedName("eth_sendTransaction")
|
||||
ETH_SEND_TRANSACTION,
|
||||
|
||||
@SerializedName("bnb_sign")
|
||||
BNB_SIGN,
|
||||
|
||||
@SerializedName("bnb_tx_confirmation")
|
||||
BNB_TRANSACTION_CONFIRM,
|
||||
|
||||
@SerializedName("get_accounts")
|
||||
GET_ACCOUNTS,
|
||||
|
||||
@SerializedName("trust_signTransaction")
|
||||
SIGN_TRANSACTION,
|
||||
|
||||
@SerializedName("wallet_switchEthereumChain")
|
||||
SWITCH_CHAIN,
|
||||
|
||||
;
|
||||
}
|
||||
|
|
@ -36,7 +36,7 @@ import okhttp3.OkHttpClient
|
|||
import okhttp3.Request
|
||||
import okhttp3.Response
|
||||
import timber.log.Timber
|
||||
import java.util.*
|
||||
import java.util.UUID
|
||||
import java.util.concurrent.TimeUnit
|
||||
import kotlin.collections.set
|
||||
|
||||
|
|
@ -191,7 +191,7 @@ class WalletConnectManager {
|
|||
}
|
||||
}
|
||||
|
||||
fun removeSimilarSessions(activeData: WalletConnectActiveData) {
|
||||
private fun removeSimilarSessions(activeData: WalletConnectActiveData) {
|
||||
val sessionsToRemove = sessions.filter {
|
||||
it.value.wallet.walletPublicKey?.equals(activeData.wallet.walletPublicKey) == true &&
|
||||
it.value.peerMeta?.url == activeData.peerMeta?.url &&
|
||||
|
|
@ -206,7 +206,7 @@ class WalletConnectManager {
|
|||
activeData.client.rejectRequest(id)
|
||||
}
|
||||
|
||||
fun acceptRequest(topic: String, id: Long, data: String) {
|
||||
private fun acceptRequest(topic: String, id: Long, data: String) {
|
||||
val activeData = sessions[topic] ?: return
|
||||
activeData.client.approveRequest(id, data)
|
||||
}
|
||||
|
|
@ -368,7 +368,7 @@ class WalletConnectManager {
|
|||
}
|
||||
|
||||
@Suppress("LongMethod", "ComplexMethod")
|
||||
fun setListeners(client: WCClient) {
|
||||
private fun setListeners(client: WCClient) {
|
||||
client.onSessionRequest = { id: Long, peer: WCPeerMeta ->
|
||||
Timber.d("OnSessionRequest: $peer")
|
||||
val session = client.session
|
||||
|
|
|
|||
|
|
@ -25,11 +25,6 @@ class WalletConnectRepository(val context: Application) {
|
|||
saveSessions(sessions)
|
||||
}
|
||||
|
||||
fun removeSession(session: WalletConnectSession) {
|
||||
val sessions = loadSavedSessions().filterNot { it == session }
|
||||
saveSessions(sessions)
|
||||
}
|
||||
|
||||
fun removeSession(session: WCSession) {
|
||||
val sessions = loadSavedSessions().filterNot { it.session == session }
|
||||
saveSessions(sessions)
|
||||
|
|
|
|||
|
|
@ -31,14 +31,16 @@ import com.tangem.tap.domain.walletconnect2.domain.models.EthTransactionData
|
|||
import com.tangem.tap.domain.walletconnect2.domain.models.binance.WcBinanceTradeOrder
|
||||
import com.tangem.tap.domain.walletconnect2.domain.models.binance.WcBinanceTransferOrder
|
||||
import com.tangem.tap.features.demo.isDemoCard
|
||||
import com.tangem.tap.features.details.redux.walletconnect.*
|
||||
import com.tangem.tap.features.details.redux.walletconnect.BinanceMessageData
|
||||
import com.tangem.tap.features.details.redux.walletconnect.WcEthTransactionType
|
||||
import com.tangem.tap.features.details.redux.walletconnect.WcPersonalSignData
|
||||
import com.tangem.tap.features.details.redux.walletconnect.WcTransactionData
|
||||
import com.tangem.tap.features.details.ui.walletconnect.dialogs.PersonalSignDialogData
|
||||
import com.tangem.tap.features.details.ui.walletconnect.dialogs.TransactionRequestDialogData
|
||||
import com.tangem.tap.proxy.redux.DaggerGraphState
|
||||
import com.tangem.tap.store
|
||||
import com.tangem.tap.tangemSdkManager
|
||||
import com.tangem.tap.userWalletsListManager
|
||||
import com.trustwallet.walletconnect.models.ethereum.WCEthereumSignMessage.WCSignType.*
|
||||
import timber.log.Timber
|
||||
import java.math.BigDecimal
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +0,0 @@
|
|||
package com.tangem.tap.domain.walletconnect2.domain.models
|
||||
|
||||
data class ChainWithDerivation(
|
||||
val chain: String,
|
||||
val derivationPath: String?,
|
||||
)
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
package com.tangem.tap.domain.walletconnect2.domain.models.binance
|
||||
|
||||
import com.squareup.moshi.*
|
||||
import com.squareup.moshi.Json
|
||||
import com.squareup.moshi.JsonClass
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
@JsonClass(generateAdapter = true)
|
||||
|
|
@ -21,12 +22,6 @@ class WcBinanceCancelOrder(
|
|||
msgs: List<Message>,
|
||||
) : WcBinanceOrder<WcBinanceCancelOrder.Message>(accountNumber, chainId, data, memo, sequence, source, msgs) {
|
||||
|
||||
enum class MessageKey(val key: String) {
|
||||
REFID("refid"),
|
||||
SENDER("sender"),
|
||||
SYMBOL("symbol"),
|
||||
}
|
||||
|
||||
data class Message(
|
||||
val refid: String,
|
||||
val sender: String,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
package com.tangem.tap.domain.walletconnect2.domain.models.binance
|
||||
|
||||
import com.github.salomonbrys.kotson.*
|
||||
import com.github.salomonbrys.kotson.jsonSerializer
|
||||
import com.google.gson.JsonObject
|
||||
import com.squareup.moshi.Json
|
||||
import com.squareup.moshi.JsonClass
|
||||
|
|
@ -42,19 +42,6 @@ class WcBinanceTradeOrder(
|
|||
)
|
||||
}
|
||||
|
||||
val tradeOrderDeserializer = jsonDeserializer {
|
||||
WcBinanceTradeOrder.Message(
|
||||
id = it.json[WcBinanceTradeOrder.MessageKey.ID.key].string,
|
||||
orderType = it.json[WcBinanceTradeOrder.MessageKey.ORDER_TYPE.key].int,
|
||||
price = it.json[WcBinanceTradeOrder.MessageKey.PRICE.key].long,
|
||||
quantity = it.json[WcBinanceTradeOrder.MessageKey.QUANTITY.key].long,
|
||||
sender = it.json[WcBinanceTradeOrder.MessageKey.SENDER.key].string,
|
||||
side = it.json[WcBinanceTradeOrder.MessageKey.SIDE.key].int,
|
||||
symbol = it.json[WcBinanceTradeOrder.MessageKey.SYMBOL.key].string,
|
||||
timeInforce = it.json[WcBinanceTradeOrder.MessageKey.TIME_INFORCE.key].int,
|
||||
)
|
||||
}
|
||||
|
||||
val tradeOrderSerializer = jsonSerializer<WcBinanceTradeOrder.Message> {
|
||||
val jsonObject = JsonObject()
|
||||
jsonObject.addProperty(WcBinanceTradeOrder.MessageKey.ID.key, it.src.id)
|
||||
|
|
|
|||
|
|
@ -1,17 +0,0 @@
|
|||
package com.tangem.tap.domain.walletconnect2.domain.models.binance
|
||||
|
||||
data class WcBinanceTradePair(val from: String, val to: String) {
|
||||
companion object {
|
||||
fun from(symbol: String): WcBinanceTradePair? {
|
||||
val pair = symbol.split("_")
|
||||
|
||||
return if (pair.size > 1) {
|
||||
val firstParts = pair[0].split("-")
|
||||
val secondParts = pair[1].split("-")
|
||||
WcBinanceTradePair(firstParts[0], secondParts[0])
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -17,11 +17,6 @@ class WcBinanceTransferOrder(
|
|||
msgs: List<Message>,
|
||||
) : WcBinanceOrder<WcBinanceTransferOrder.Message>(accountNumber, chainId, data, memo, sequence, source, msgs) {
|
||||
|
||||
enum class MessageKey(val key: String) {
|
||||
INPUTS("inputs"),
|
||||
OUTPUTS("outputs"),
|
||||
}
|
||||
|
||||
data class Message(
|
||||
val inputs: List<Item>,
|
||||
val outputs: List<Item>,
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ import com.tangem.wallet.R
|
|||
*/
|
||||
abstract class BaseFragment(layoutId: Int) : Fragment(layoutId), FragmentOnBackPressedHandler {
|
||||
|
||||
protected lateinit var mainView: View
|
||||
private lateinit var mainView: View
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
|
|
|||
|
|
@ -23,9 +23,9 @@ import com.tangem.tap.common.extensions.dispatchDebugErrorNotification
|
|||
import com.tangem.tap.common.extensions.dispatchOnMain
|
||||
import com.tangem.tap.common.redux.global.GlobalAction
|
||||
import com.tangem.tap.domain.TapError
|
||||
import com.tangem.tap.domain.model.Currency
|
||||
import com.tangem.tap.features.customtoken.impl.domain.models.FoundToken
|
||||
import com.tangem.tap.features.tokens.legacy.redux.TokensMiddleware
|
||||
import com.tangem.tap.features.wallet.models.Currency
|
||||
import com.tangem.tap.proxy.redux.DaggerGraphState
|
||||
import com.tangem.tap.store
|
||||
import com.tangem.tap.tangemSdkManager
|
||||
|
|
|
|||
|
|
@ -615,10 +615,6 @@ internal class AddCustomTokenViewModel @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
private fun DerivationPath?.isSameDerivationPath(rawDerivationPath: String?): Boolean {
|
||||
return this == rawDerivationPath?.let { DerivationPath(it) }
|
||||
}
|
||||
|
||||
private fun handleContractAddressErrorValidation(type: AddCustomTokenError) {
|
||||
when {
|
||||
isNetworkSelected() && type == AddCustomTokenError.InvalidContractAddress -> {
|
||||
|
|
|
|||
|
|
@ -6,8 +6,8 @@ import com.tangem.domain.common.extensions.withMainContext
|
|||
import com.tangem.domain.demo.DemoConfig
|
||||
import com.tangem.domain.models.scan.ScanResponse
|
||||
import com.tangem.tap.common.entities.ProgressState
|
||||
import com.tangem.tap.domain.model.Currency
|
||||
import com.tangem.tap.features.onboarding.products.note.redux.OnboardingNoteAction
|
||||
import com.tangem.tap.features.wallet.models.Currency
|
||||
import com.tangem.tap.scope
|
||||
import com.tangem.tap.store
|
||||
import kotlinx.coroutines.launch
|
||||
|
|
|
|||
|
|
@ -74,8 +74,6 @@ sealed class WalletConnectAction : Action {
|
|||
) :
|
||||
WalletConnectAction()
|
||||
|
||||
data class SetDataToSend(val transactionData: WcTransactionData) : WalletConnectAction()
|
||||
|
||||
data class HandlePersonalSignRequest(
|
||||
val message: WCEthereumSignMessage,
|
||||
val session: WalletConnectSession,
|
||||
|
|
|
|||
|
|
@ -73,9 +73,7 @@ data class WalletForSession(
|
|||
} else if (other.derivedPublicKey != null) return false
|
||||
if (derivationPath != other.derivationPath) return false
|
||||
if (isTestNet != other.isTestNet) return false
|
||||
if (blockchain != other.blockchain) return false
|
||||
|
||||
return true
|
||||
return blockchain == other.blockchain
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
|
|
|
|||
|
|
@ -122,7 +122,6 @@ internal data class SocialNetworkLink(
|
|||
|
||||
internal sealed class EventError {
|
||||
object Empty : EventError()
|
||||
data class DemoReferralNotAvailable(val onErrorShow: () -> Unit) : EventError()
|
||||
}
|
||||
|
||||
sealed class SocialNetwork(val id: String, val iconRes: Int) {
|
||||
|
|
|
|||
|
|
@ -1,10 +1,6 @@
|
|||
package com.tangem.tap.features.home
|
||||
|
||||
import android.content.Context
|
||||
import android.telephony.TelephonyManager
|
||||
import android.telephony.TelephonyManager.PHONE_TYPE_CDMA
|
||||
import androidx.compose.ui.text.intl.Locale
|
||||
import java.lang.ref.WeakReference
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
|
|
@ -13,39 +9,8 @@ interface RegionProvider {
|
|||
fun getRegion(): String?
|
||||
}
|
||||
|
||||
class RegionService(
|
||||
private val providers: List<RegionProvider>,
|
||||
) : RegionProvider {
|
||||
override fun getRegion(): String? {
|
||||
for (provider in providers) {
|
||||
val region = provider.getRegion()
|
||||
if (region != null) return region
|
||||
}
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
class TelephonyManagerRegionProvider(context: Context) : RegionProvider {
|
||||
|
||||
private val wContext: WeakReference<Context> = WeakReference(context)
|
||||
|
||||
override fun getRegion(): String? {
|
||||
val tm = wContext.get()?.getSystemService(Context.TELEPHONY_SERVICE) as? TelephonyManager ?: return null
|
||||
|
||||
val region = when (tm.phoneType) {
|
||||
PHONE_TYPE_CDMA -> {
|
||||
// Result may be unreliable
|
||||
tm.networkCountryIso
|
||||
}
|
||||
else -> tm.networkCountryIso
|
||||
}
|
||||
return region.ifEmpty { return null }
|
||||
}
|
||||
}
|
||||
|
||||
class LocaleRegionProvider : RegionProvider {
|
||||
override fun getRegion(): String = Locale.current.region
|
||||
}
|
||||
|
||||
const val RUSSIA_COUNTRY_CODE = "ru"
|
||||
const val BELARUS_COUNTRY_CODE = "by"
|
||||
const val RUSSIA_COUNTRY_CODE = "ru"
|
||||
|
|
@ -37,7 +37,6 @@ import timber.log.Timber
|
|||
object HomeMiddleware {
|
||||
val handler = homeMiddleware
|
||||
|
||||
const val BUY_WALLET_URL = "https://tangem.com/ru/resellers/"
|
||||
const val NEW_BUY_WALLET_URL = "https://buy.tangem.com/"
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@ package com.tangem.tap.features.home.redux
|
|||
import androidx.compose.runtime.MutableState
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import com.tangem.tap.common.entities.IndeterminateProgressButton
|
||||
import com.tangem.tap.common.entities.ProgressState
|
||||
import com.tangem.tap.features.send.redux.states.ButtonState
|
||||
import org.rekotlin.StateType
|
||||
import java.util.Locale
|
||||
|
|
@ -17,9 +16,6 @@ data class HomeState(
|
|||
val firstStory: Stories
|
||||
get() = stories[0]
|
||||
|
||||
val btnScanStateInProgress: Boolean
|
||||
get() = btnScanState.progressState == ProgressState.Loading
|
||||
|
||||
fun stepOf(story: Stories): Int = stories.indexOf(story)
|
||||
|
||||
fun onCountryCodeUpdate(homeState: HomeState, countryCode: String) {
|
||||
|
|
|
|||
|
|
@ -15,15 +15,11 @@ class IntentProcessor {
|
|||
intentHandlers.add(handler)
|
||||
}
|
||||
|
||||
fun removeIntentHandler(handler: IntentHandler) {
|
||||
intentHandlers.remove(handler)
|
||||
}
|
||||
|
||||
fun removeAll() {
|
||||
intentHandlers.clear()
|
||||
}
|
||||
|
||||
suspend fun handleIntent(intent: Intent?) {
|
||||
fun handleIntent(intent: Intent?) {
|
||||
intentHandlers.forEach {
|
||||
it.handleIntent(intent)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,9 +14,9 @@ import com.tangem.tap.common.extensions.isPositive
|
|||
import com.tangem.tap.common.extensions.safeUpdate
|
||||
import com.tangem.tap.domain.TapError
|
||||
import com.tangem.tap.domain.extensions.getOrLoadCardArtworkUrl
|
||||
import com.tangem.tap.domain.model.Currency
|
||||
import com.tangem.tap.domain.model.hasPendingTransactions
|
||||
import com.tangem.tap.features.demo.isDemoCard
|
||||
import com.tangem.tap.features.wallet.models.Currency
|
||||
import com.tangem.tap.features.wallet.models.hasPendingTransactions
|
||||
import timber.log.Timber
|
||||
import java.math.BigDecimal
|
||||
|
||||
|
|
@ -25,11 +25,10 @@ import java.math.BigDecimal
|
|||
*/
|
||||
class OnboardingManager(
|
||||
var scanResponse: ScanResponse,
|
||||
val usedCardsPrefStorage: UsedCardsPrefStorage,
|
||||
private val usedCardsPrefStorage: UsedCardsPrefStorage,
|
||||
) {
|
||||
|
||||
var cardInfo: Result<CardVerifyAndGetInfo.Response.Item>? = null
|
||||
private set
|
||||
private var cardInfo: Result<CardVerifyAndGetInfo.Response.Item>? = null
|
||||
|
||||
suspend fun loadArtworkUrl(): String {
|
||||
val cardInfo = cardInfo
|
||||
|
|
@ -85,10 +84,6 @@ class OnboardingManager(
|
|||
usedCardsPrefStorage.activationFinished(cardId)
|
||||
}
|
||||
|
||||
fun isActivationFinished(cardId: String): Boolean {
|
||||
return usedCardsPrefStorage.isActivationFinished(cardId)
|
||||
}
|
||||
|
||||
fun isActivationStarted(cardId: String): Boolean {
|
||||
return usedCardsPrefStorage.isActivationStarted(cardId)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -96,69 +96,69 @@ class OnboardingNoteFragment : BaseOnboardingFragment<OnboardingNoteState>() {
|
|||
}
|
||||
|
||||
private fun setupNoneState() = with(mainBinding.onboardingActionContainer) {
|
||||
btnMainAction.isVisible = false
|
||||
btnAlternativeAction.isVisible = false
|
||||
btnMainAction?.isVisible = false
|
||||
btnAlternativeAction?.isVisible = false
|
||||
btnRefreshBalanceWidget.show(false)
|
||||
tvHeader.isVisible = false
|
||||
tvBody.isVisible = false
|
||||
tvBody?.isVisible = false
|
||||
|
||||
btnMainAction.text = ""
|
||||
btnMainAction.icon = null
|
||||
btnAlternativeAction.text = ""
|
||||
btnAlternativeAction.icon = null
|
||||
btnMainAction?.text = ""
|
||||
btnMainAction?.icon = null
|
||||
btnAlternativeAction?.text = ""
|
||||
btnAlternativeAction?.icon = null
|
||||
tvHeader.text = ""
|
||||
tvBody.text = ""
|
||||
tvBody?.text = ""
|
||||
}
|
||||
|
||||
private fun setupCreateWalletState(state: OnboardingNoteState) = with(mainBinding.onboardingActionContainer) {
|
||||
btnMainAction.setText(R.string.onboarding_create_wallet_button_create_wallet)
|
||||
btnMainAction.setIconResource(R.drawable.ic_tangem_24)
|
||||
btnMainAction.setOnClickListener {
|
||||
btnMainAction?.setText(R.string.onboarding_create_wallet_button_create_wallet)
|
||||
btnMainAction?.setIconResource(R.drawable.ic_tangem_24)
|
||||
btnMainAction?.setOnClickListener {
|
||||
Analytics.send(Onboarding.CreateWallet.ButtonCreateWallet())
|
||||
store.dispatch(OnboardingNoteAction.CreateWallet)
|
||||
}
|
||||
btnAlternativeAction.setText(R.string.onboarding_button_what_does_it_mean)
|
||||
btnAlternativeAction.setOnClickListener { }
|
||||
btnAlternativeAction?.setText(R.string.onboarding_button_what_does_it_mean)
|
||||
btnAlternativeAction?.setOnClickListener { }
|
||||
|
||||
btnRefreshBalanceWidget.mainView.setOnClickListener(null)
|
||||
|
||||
tvHeader.setText(R.string.onboarding_create_wallet_header)
|
||||
tvBody.setText(R.string.onboarding_create_wallet_body)
|
||||
tvBody?.setText(R.string.onboarding_create_wallet_body)
|
||||
|
||||
mainBinding.onboardingTopContainer.imvCardBackground.setBackgroundDrawable(
|
||||
requireContext().getDrawableCompat(R.drawable.shape_circle),
|
||||
)
|
||||
updateConstraints(state.currentStep, R.layout.lp_onboarding_create_wallet)
|
||||
|
||||
btnAlternativeAction.isVisible = false // temporary
|
||||
btnAlternativeAction?.isVisible = false // temporary
|
||||
}
|
||||
|
||||
private fun setupTopUpWalletState(state: OnboardingNoteState) = with(mainBinding.onboardingActionContainer) {
|
||||
if (state.isBuyAllowed) {
|
||||
btnMainAction.setText(R.string.onboarding_top_up_button_but_crypto)
|
||||
btnMainAction.icon = null
|
||||
btnMainAction.setOnClickListener {
|
||||
btnMainAction?.setText(R.string.onboarding_top_up_button_but_crypto)
|
||||
btnMainAction?.icon = null
|
||||
btnMainAction?.setOnClickListener {
|
||||
store.dispatch(OnboardingNoteAction.TopUp)
|
||||
}
|
||||
|
||||
btnAlternativeAction.isVisible = true
|
||||
btnAlternativeAction.setText(R.string.onboarding_top_up_button_show_wallet_address)
|
||||
btnAlternativeAction.setOnClickListener {
|
||||
btnAlternativeAction?.isVisible = true
|
||||
btnAlternativeAction?.setText(R.string.onboarding_top_up_button_show_wallet_address)
|
||||
btnAlternativeAction?.setOnClickListener {
|
||||
store.dispatch(OnboardingNoteAction.ShowAddressInfoDialog)
|
||||
}
|
||||
} else {
|
||||
btnMainAction.setText(R.string.onboarding_button_receive_crypto)
|
||||
btnMainAction.icon = null
|
||||
btnMainAction.setOnClickListener {
|
||||
btnMainAction?.setText(R.string.onboarding_button_receive_crypto)
|
||||
btnMainAction?.icon = null
|
||||
btnMainAction?.setOnClickListener {
|
||||
store.dispatch(OnboardingNoteAction.ShowAddressInfoDialog)
|
||||
}
|
||||
|
||||
btnAlternativeAction.isVisible = false
|
||||
btnAlternativeAction?.isVisible = false
|
||||
}
|
||||
|
||||
tvHeader.setText(R.string.onboarding_topup_title)
|
||||
if (state.balanceNonCriticalError == null) {
|
||||
tvBody.setText(R.string.onboarding_top_up_body)
|
||||
tvBody?.setText(R.string.onboarding_top_up_body)
|
||||
} else {
|
||||
state.walletBalance.amountToCreateAccount?.let { amount ->
|
||||
val tvBodyMessage = getString(
|
||||
|
|
@ -166,7 +166,7 @@ class OnboardingNoteFragment : BaseOnboardingFragment<OnboardingNoteState>() {
|
|||
amount,
|
||||
state.walletBalance.currency.currencySymbol,
|
||||
)
|
||||
tvBody.text = tvBodyMessage
|
||||
tvBody?.text = tvBodyMessage
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -183,20 +183,20 @@ class OnboardingNoteFragment : BaseOnboardingFragment<OnboardingNoteState>() {
|
|||
}
|
||||
|
||||
private fun setupDoneState(state: OnboardingNoteState) = with(mainBinding.onboardingActionContainer) {
|
||||
btnMainAction.setText(R.string.common_continue)
|
||||
btnMainAction.icon = null
|
||||
btnMainAction.setOnClickListener {
|
||||
btnMainAction?.setText(R.string.common_continue)
|
||||
btnMainAction?.icon = null
|
||||
btnMainAction?.setOnClickListener {
|
||||
showConfetti(false)
|
||||
store.dispatch(OnboardingNoteAction.Done)
|
||||
}
|
||||
|
||||
btnAlternativeAction.isVisible = false
|
||||
btnAlternativeAction.text = ""
|
||||
btnAlternativeAction.setOnClickListener { }
|
||||
btnAlternativeAction?.isVisible = false
|
||||
btnAlternativeAction?.text = ""
|
||||
btnAlternativeAction?.setOnClickListener { }
|
||||
btnRefreshBalanceWidget.mainView.setOnClickListener(null)
|
||||
|
||||
tvHeader.setText(R.string.onboarding_done_header)
|
||||
tvBody.setText(R.string.onboarding_done_body)
|
||||
tvBody?.setText(R.string.onboarding_done_body)
|
||||
|
||||
mainBinding.onboardingTopContainer.imvCardBackground.setBackgroundDrawable(
|
||||
requireContext().getDrawableCompat(R.drawable.shape_rectangle_rounded_8),
|
||||
|
|
|
|||
|
|
@ -15,11 +15,11 @@ import com.tangem.tap.common.redux.AppDialog
|
|||
import com.tangem.tap.common.redux.AppState
|
||||
import com.tangem.tap.common.redux.global.GlobalAction
|
||||
import com.tangem.tap.domain.TapError
|
||||
import com.tangem.tap.domain.model.Currency
|
||||
import com.tangem.tap.features.demo.DemoHelper
|
||||
import com.tangem.tap.features.home.RUSSIA_COUNTRY_CODE
|
||||
import com.tangem.tap.features.onboarding.OnboardingDialog
|
||||
import com.tangem.tap.features.onboarding.OnboardingHelper
|
||||
import com.tangem.tap.features.wallet.models.Currency
|
||||
import com.tangem.tap.scope
|
||||
import com.tangem.tap.store
|
||||
import com.tangem.tap.tangemSdkManager
|
||||
|
|
|
|||
|
|
@ -71,19 +71,19 @@ class OnboardingOtherCardsFragment : BaseOnboardingFragment<OnboardingOtherCards
|
|||
}
|
||||
|
||||
private fun setupCreateWalletState() = with(mainBinding.onboardingActionContainer) {
|
||||
btnMainAction.setText(R.string.onboarding_create_wallet_button_create_wallet)
|
||||
btnMainAction.setIconResource(R.drawable.ic_tangem_24)
|
||||
btnMainAction.setOnClickListener {
|
||||
btnMainAction?.setText(R.string.onboarding_create_wallet_button_create_wallet)
|
||||
btnMainAction?.setIconResource(R.drawable.ic_tangem_24)
|
||||
btnMainAction?.setOnClickListener {
|
||||
Analytics.send(Onboarding.CreateWallet.ButtonCreateWallet())
|
||||
store.dispatch(OnboardingOtherCardsAction.CreateWallet)
|
||||
}
|
||||
btnAlternativeAction.setText(R.string.onboarding_button_what_does_it_mean)
|
||||
btnAlternativeAction.setOnClickListener { }
|
||||
btnAlternativeAction?.setText(R.string.onboarding_button_what_does_it_mean)
|
||||
btnAlternativeAction?.setOnClickListener { }
|
||||
|
||||
tvHeader.setText(R.string.onboarding_create_wallet_header)
|
||||
tvBody.setText(R.string.onboarding_create_wallet_body)
|
||||
tvBody?.setText(R.string.onboarding_create_wallet_body)
|
||||
|
||||
btnAlternativeAction.isVisible = false // temporary
|
||||
btnAlternativeAction?.isVisible = false // temporary
|
||||
|
||||
mainBinding.onboardingTopContainer.imvCardBackground.setBackgroundDrawable(
|
||||
requireContext().getDrawableCompat(R.drawable.shape_circle),
|
||||
|
|
@ -92,19 +92,19 @@ class OnboardingOtherCardsFragment : BaseOnboardingFragment<OnboardingOtherCards
|
|||
}
|
||||
|
||||
private fun setupDoneState() = with(mainBinding.onboardingActionContainer) {
|
||||
btnMainAction.setText(R.string.common_continue)
|
||||
btnMainAction.icon = null
|
||||
btnMainAction.setOnClickListener {
|
||||
btnMainAction?.setText(R.string.common_continue)
|
||||
btnMainAction?.icon = null
|
||||
btnMainAction?.setOnClickListener {
|
||||
showConfetti(false)
|
||||
store.dispatch(OnboardingOtherCardsAction.Done)
|
||||
}
|
||||
|
||||
btnAlternativeAction.isVisible = false
|
||||
btnAlternativeAction.text = ""
|
||||
btnAlternativeAction.setOnClickListener { }
|
||||
btnAlternativeAction?.isVisible = false
|
||||
btnAlternativeAction?.text = ""
|
||||
btnAlternativeAction?.setOnClickListener { }
|
||||
|
||||
tvHeader.setText(R.string.onboarding_done_header)
|
||||
tvBody.setText(R.string.onboarding_done_body)
|
||||
tvBody?.setText(R.string.onboarding_done_body)
|
||||
|
||||
mainBinding.onboardingTopContainer.imvCardBackground.setBackgroundDrawable(
|
||||
requireContext().getDrawableCompat(R.drawable.shape_rectangle_rounded_8),
|
||||
|
|
|
|||
|
|
@ -21,11 +21,11 @@ import com.tangem.tap.common.redux.AppDialog
|
|||
import com.tangem.tap.common.redux.AppState
|
||||
import com.tangem.tap.common.redux.global.GlobalAction
|
||||
import com.tangem.tap.domain.TapError
|
||||
import com.tangem.tap.domain.model.Currency
|
||||
import com.tangem.tap.domain.twins.TwinCardsManager
|
||||
import com.tangem.tap.features.home.RUSSIA_COUNTRY_CODE
|
||||
import com.tangem.tap.features.onboarding.OnboardingDialog
|
||||
import com.tangem.tap.features.onboarding.OnboardingHelper
|
||||
import com.tangem.tap.features.wallet.models.Currency
|
||||
import com.tangem.tap.preferencesStorage
|
||||
import com.tangem.tap.proxy.redux.DaggerGraphState
|
||||
import com.tangem.tap.scope
|
||||
|
|
|
|||
|
|
@ -187,14 +187,14 @@ class TwinsCardsFragment : BaseOnboardingFragment<TwinCardsState>() {
|
|||
pbBinding.pbState.hide()
|
||||
|
||||
tvHeader.setText(R.string.twins_onboarding_subtitle)
|
||||
tvBody.text = getString(
|
||||
tvBody?.text = getString(
|
||||
R.string.twins_onboarding_description_format,
|
||||
state.cardNumber?.pairIndexNumber(),
|
||||
)
|
||||
|
||||
btnMainAction.setText(R.string.common_continue)
|
||||
btnMainAction.icon = null
|
||||
btnMainAction.setOnClickListener { onMainButtonClick() }
|
||||
btnMainAction?.setText(R.string.common_continue)
|
||||
btnMainAction?.icon = null
|
||||
btnMainAction?.setOnClickListener { onMainButtonClick() }
|
||||
}
|
||||
|
||||
private fun setupWarningState(state: TwinCardsState) = with(mainBinding.onboardingActionContainer) {
|
||||
|
|
@ -202,18 +202,18 @@ class TwinsCardsFragment : BaseOnboardingFragment<TwinCardsState>() {
|
|||
|
||||
mainBinding.onboardingTopContainer.onboardingTwinsWelcomeBg.root.hide()
|
||||
pbBinding.pbState.hide()
|
||||
chbUnderstand.show()
|
||||
chbUnderstand?.show()
|
||||
|
||||
tvHeader.setText(R.string.common_warning)
|
||||
tvBody.setText(R.string.twins_recreate_warning)
|
||||
tvBody?.setText(R.string.twins_recreate_warning)
|
||||
|
||||
chbUnderstand.setOnCheckedChangeListener { buttonView, isChecked ->
|
||||
chbUnderstand?.setOnCheckedChangeListener { buttonView, isChecked ->
|
||||
store.dispatch(TwinCardsAction.SetUserUnderstand(isChecked))
|
||||
}
|
||||
btnMainAction.isEnabled = state.userWasUnderstandIfWalletRecreate
|
||||
btnMainAction.setText(R.string.common_continue)
|
||||
btnMainAction.icon = null
|
||||
btnMainAction.setOnClickListener {
|
||||
btnMainAction?.isEnabled = state.userWasUnderstandIfWalletRecreate
|
||||
btnMainAction?.setText(R.string.common_continue)
|
||||
btnMainAction?.icon = null
|
||||
btnMainAction?.setOnClickListener {
|
||||
store.dispatch(TwinCardsAction.SetStepOfScreen(TwinCardsStep.CreateFirstWallet))
|
||||
}
|
||||
}
|
||||
|
|
@ -223,7 +223,7 @@ class TwinsCardsFragment : BaseOnboardingFragment<TwinCardsState>() {
|
|||
mainBinding.onboardingTopContainer.onboardingTwinsWelcomeBg.bgCircleLarge.hide()
|
||||
mainBinding.onboardingTopContainer.onboardingTwinsWelcomeBg.bgCircleMedium.hide()
|
||||
mainBinding.onboardingTopContainer.onboardingTwinsWelcomeBg.bgCircleMin.hide()
|
||||
chbUnderstand.hide()
|
||||
chbUnderstand?.hide()
|
||||
pbBinding.pbState.show()
|
||||
|
||||
mainBinding.onboardingTopContainer.onboardingWalletContainer.beginDelayedTransition()
|
||||
|
|
@ -251,11 +251,11 @@ class TwinsCardsFragment : BaseOnboardingFragment<TwinCardsState>() {
|
|||
|
||||
val twinIndexNumber = state.cardNumber?.indexNumber()
|
||||
tvHeader.text = getString(R.string.twins_recreate_title_format, twinIndexNumber)
|
||||
tvBody.setText(R.string.onboarding_twins_interrupt_warning)
|
||||
tvBody?.setText(R.string.onboarding_twins_interrupt_warning)
|
||||
|
||||
btnMainAction.text = getString(R.string.twins_recreate_button_format, twinIndexNumber)
|
||||
btnMainAction.setIconResource(R.drawable.ic_tangem_24)
|
||||
btnMainAction.setOnClickListener {
|
||||
btnMainAction?.text = getString(R.string.twins_recreate_button_format, twinIndexNumber)
|
||||
btnMainAction?.setIconResource(R.drawable.ic_tangem_24)
|
||||
btnMainAction?.setOnClickListener {
|
||||
Analytics.send(Onboarding.CreateWallet.ButtonCreateWallet())
|
||||
store.dispatch(
|
||||
TwinCardsAction.Wallet.LaunchFirstStep(
|
||||
|
|
@ -265,8 +265,8 @@ class TwinsCardsFragment : BaseOnboardingFragment<TwinCardsState>() {
|
|||
)
|
||||
}
|
||||
|
||||
btnAlternativeAction.isVisible = false
|
||||
btnAlternativeAction.isClickable = false
|
||||
btnAlternativeAction?.isVisible = false
|
||||
btnAlternativeAction?.isClickable = false
|
||||
}
|
||||
|
||||
private fun setupCreateSecondWalletState(state: TwinCardsState) = with(mainBinding.onboardingActionContainer) {
|
||||
|
|
@ -274,12 +274,12 @@ class TwinsCardsFragment : BaseOnboardingFragment<TwinCardsState>() {
|
|||
|
||||
val twinPairIndexNumber = state.cardNumber?.pairIndexNumber()
|
||||
tvHeader.text = getString(R.string.twins_recreate_title_format, twinPairIndexNumber)
|
||||
tvBody.setText(R.string.onboarding_twins_interrupt_warning)
|
||||
tvBody?.setText(R.string.onboarding_twins_interrupt_warning)
|
||||
|
||||
btnMainAction.text =
|
||||
btnMainAction?.text =
|
||||
getString(R.string.twins_recreate_button_format, twinPairIndexNumber)
|
||||
btnMainAction.setIconResource(R.drawable.ic_tangem_24)
|
||||
btnMainAction.setOnClickListener {
|
||||
btnMainAction?.setIconResource(R.drawable.ic_tangem_24)
|
||||
btnMainAction?.setOnClickListener {
|
||||
store.dispatch(
|
||||
TwinCardsAction.Wallet.LaunchSecondStep(
|
||||
Message(
|
||||
|
|
@ -300,11 +300,11 @@ class TwinsCardsFragment : BaseOnboardingFragment<TwinCardsState>() {
|
|||
|
||||
val twinIndexNumber = state.cardNumber?.indexNumber()
|
||||
tvHeader.text = getString(R.string.twins_recreate_title_format, twinIndexNumber)
|
||||
tvBody.setText(R.string.onboarding_twins_interrupt_warning)
|
||||
tvBody?.setText(R.string.onboarding_twins_interrupt_warning)
|
||||
|
||||
btnMainAction.text = getString(R.string.twins_recreate_button_format, twinIndexNumber)
|
||||
btnMainAction.setIconResource(R.drawable.ic_tangem_24)
|
||||
btnMainAction.setOnClickListener {
|
||||
btnMainAction?.text = getString(R.string.twins_recreate_button_format, twinIndexNumber)
|
||||
btnMainAction?.setIconResource(R.drawable.ic_tangem_24)
|
||||
btnMainAction?.setOnClickListener {
|
||||
store.dispatch(
|
||||
TwinCardsAction.Wallet.LaunchThirdStep(
|
||||
Message(getString(R.string.twins_recreate_title_format, twinIndexNumber)),
|
||||
|
|
@ -337,27 +337,27 @@ class TwinsCardsFragment : BaseOnboardingFragment<TwinCardsState>() {
|
|||
}
|
||||
|
||||
if (state.isBuyAllowed) {
|
||||
btnMainAction.setText(R.string.onboarding_top_up_button_but_crypto)
|
||||
btnMainAction.setOnClickListener {
|
||||
btnMainAction?.setText(R.string.onboarding_top_up_button_but_crypto)
|
||||
btnMainAction?.setOnClickListener {
|
||||
store.dispatch(TwinCardsAction.TopUp)
|
||||
}
|
||||
btnAlternativeAction.isVisible = true
|
||||
btnAlternativeAction.setText(R.string.onboarding_top_up_button_show_wallet_address)
|
||||
btnAlternativeAction.setOnClickListener {
|
||||
btnAlternativeAction?.isVisible = true
|
||||
btnAlternativeAction?.setText(R.string.onboarding_top_up_button_show_wallet_address)
|
||||
btnAlternativeAction?.setOnClickListener {
|
||||
store.dispatch(TwinCardsAction.ShowAddressInfoDialog)
|
||||
}
|
||||
} else {
|
||||
btnMainAction.setText(R.string.onboarding_button_receive_crypto)
|
||||
btnMainAction.setOnClickListener {
|
||||
btnMainAction?.setText(R.string.onboarding_button_receive_crypto)
|
||||
btnMainAction?.setOnClickListener {
|
||||
store.dispatch(TwinCardsAction.ShowAddressInfoDialog)
|
||||
}
|
||||
|
||||
btnAlternativeAction.isVisible = false
|
||||
btnAlternativeAction?.isVisible = false
|
||||
}
|
||||
|
||||
tvHeader.setText(R.string.onboarding_topup_title)
|
||||
tvBody.setText(R.string.onboarding_top_up_body)
|
||||
btnMainAction.icon = null
|
||||
tvBody?.setText(R.string.onboarding_top_up_body)
|
||||
btnMainAction?.icon = null
|
||||
|
||||
btnRefreshBalanceWidget.changeState(state.walletBalance.state)
|
||||
if (btnRefreshBalanceWidget.isShowing != true) {
|
||||
|
|
@ -375,18 +375,18 @@ class TwinsCardsFragment : BaseOnboardingFragment<TwinCardsState>() {
|
|||
}
|
||||
|
||||
private fun setupDoneState(state: TwinCardsState) = with(mainBinding.onboardingActionContainer) {
|
||||
btnMainAction.setText(R.string.common_continue)
|
||||
btnMainAction.icon = null
|
||||
btnMainAction.setOnClickListener {
|
||||
btnMainAction?.setText(R.string.common_continue)
|
||||
btnMainAction?.icon = null
|
||||
btnMainAction?.setOnClickListener {
|
||||
store.dispatch(TwinCardsAction.Confetti.Hide)
|
||||
store.dispatch(TwinCardsAction.Done)
|
||||
}
|
||||
|
||||
btnAlternativeAction.isVisible = false
|
||||
btnAlternativeAction.isClickable = false
|
||||
btnAlternativeAction?.isVisible = false
|
||||
btnAlternativeAction?.isClickable = false
|
||||
|
||||
tvHeader.setText(R.string.onboarding_done_header)
|
||||
tvBody.setText(R.string.onboarding_done_body)
|
||||
tvBody?.setText(R.string.onboarding_done_body)
|
||||
|
||||
val layout = when (state.mode) {
|
||||
CreateTwinWalletMode.CreateWallet -> R.layout.lp_onboarding_done_activation_twins
|
||||
|
|
|
|||
|
|
@ -250,7 +250,7 @@ class TestBackupAnimation(
|
|||
}
|
||||
|
||||
@Suppress("MagicNumber")
|
||||
fun setStep(step: Int, onStepUpdate: (Int) -> Unit = {}) {
|
||||
private fun setStep(step: Int, onStepUpdate: (Int) -> Unit = {}) {
|
||||
steps = step
|
||||
when (steps) {
|
||||
0 -> setupCreateWalletState()
|
||||
|
|
|
|||
|
|
@ -140,7 +140,7 @@ class OnboardingWalletFragment :
|
|||
}
|
||||
|
||||
private fun initCardsWidget(leapfrogWidget: LeapfrogWidget, deviceScaleFactor: Float, isTest: Boolean = false) {
|
||||
cardsWidget = WalletCardsWidget(leapfrogWidget, deviceScaleFactor) { 200f * deviceScaleFactor }
|
||||
cardsWidget = WalletCardsWidget(leapfrogWidget, deviceScaleFactor)
|
||||
animator = if (isTest) {
|
||||
TestBackupAnimation(WalletBackupAnimator(cardsWidget), binding)
|
||||
} else {
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue