Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ import one.mixin.android.ui.wallet.Web3FilterParams.Companion.FILTER_MASK
import one.mixin.android.ui.wallet.adapter.Web3TransactionPagedAdapter
import one.mixin.android.util.viewBinding
import one.mixin.android.web3.details.Web3TransactionFragment
import one.mixin.android.web3.details.navigateToWeb3Transaction
import one.mixin.android.web3.details.openWeb3AssetTransactions
import one.mixin.android.web3.details.tokenNavigationTarget
import one.mixin.android.widget.BottomSheet
Expand All @@ -67,7 +68,7 @@ class AllWeb3TransactionsFragment : BaseTransactionsFragment(R.layout.fragment_a
lifecycleScope.launch {
val token = web3ViewModel.web3TokenItemById(filterParams.walletId,transaction.getMainAssetId()) ?: return@launch
val wallet = web3ViewModel.findWalletById(filterParams.walletId) ?: return@launch
this@AllWeb3TransactionsFragment.view?.findNavController()?.navigate(
this@AllWeb3TransactionsFragment.view?.findNavController()?.navigateToWeb3Transaction(
R.id.action_all_web3_transactions_fragment_to_web3_transaction_fragment,
Bundle().apply {
putParcelable("args_transaction", transaction)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,14 @@ internal fun String?.parseWalletHomeCache(): WalletHomeCache? =
}
}
}
root.get("importKeyAction")?.let { action ->
val kind = if (action.isJsonObject) action.asJsonObject.get("kind") else null
if (kind == null || !kind.isJsonPrimitive || !kind.asJsonPrimitive.isString ||
WalletHomeImportKeyKind.entries.none { it.name == kind.asString }
) {
root.remove("importKeyAction")
}
}
GsonHelper.customGson.fromJson(root, WalletHomeCache::class.java)
}.getOrNull()

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package one.mixin.android.ui.wallet.home

import com.google.gson.annotations.SerializedName
import one.mixin.android.api.response.perps.PerpsPositionItem
import one.mixin.android.api.response.perps.PerpsMarket
import one.mixin.android.R
Expand Down Expand Up @@ -129,15 +130,21 @@ enum class WalletHomeWatchKind {
}

data class WalletHomeImportKeyAction(
@SerializedName("kind")
val kind: WalletHomeImportKeyKind,
) {
val buttonTextRes: Int get() = kind.buttonTextRes
val descriptionRes: Int get() = kind.descriptionRes
val learnMoreUrlRes: Int get() = kind.learnMoreUrlRes
}

enum class WalletHomeImportKeyKind(
val buttonTextRes: Int,
val descriptionRes: Int,
val learnMoreUrlRes: Int,
)

enum class WalletHomeImportKeyKind {
MNEMONIC_PHRASE,
PRIVATE_KEY,
) {
MNEMONIC_PHRASE(R.string.import_mnemonic_phrase, R.string.Import_Mnemonic_Phrase_Desc, R.string.import_mnemonic_phrase_url),
PRIVATE_KEY(R.string.import_private_key, R.string.Import_Private_Key_Desc, R.string.import_private_key_url),
}

fun List<PendingDisplay>.toWalletHomePendingIndicator(): WalletHomePendingIndicator? {
Expand Down Expand Up @@ -208,18 +215,8 @@ fun walletHomeImportKeyAction(
): WalletHomeImportKeyAction? {
if (hasLocalPrivateKey) return null
return when (category) {
WalletCategory.IMPORTED_MNEMONIC.value -> WalletHomeImportKeyAction(
kind = WalletHomeImportKeyKind.MNEMONIC_PHRASE,
buttonTextRes = R.string.import_mnemonic_phrase,
descriptionRes = R.string.Import_Mnemonic_Phrase_Desc,
learnMoreUrlRes = R.string.import_mnemonic_phrase_url,
)
WalletCategory.IMPORTED_PRIVATE_KEY.value -> WalletHomeImportKeyAction(
kind = WalletHomeImportKeyKind.PRIVATE_KEY,
buttonTextRes = R.string.import_private_key,
descriptionRes = R.string.Import_Private_Key_Desc,
learnMoreUrlRes = R.string.import_private_key_url,
)
WalletCategory.IMPORTED_MNEMONIC.value -> WalletHomeImportKeyAction(WalletHomeImportKeyKind.MNEMONIC_PHRASE)
WalletCategory.IMPORTED_PRIVATE_KEY.value -> WalletHomeImportKeyAction(WalletHomeImportKeyKind.PRIVATE_KEY)
else -> null
}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
package one.mixin.android.web3.details

import android.os.Bundle
import androidx.annotation.IdRes
import androidx.fragment.app.Fragment
import androidx.navigation.NavBackStackEntry
import androidx.navigation.NavController
import androidx.navigation.NavOptions
import androidx.navigation.fragment.findNavController
import one.mixin.android.R
import one.mixin.android.db.web3.vo.Web3TokenItem
Expand Down Expand Up @@ -62,16 +65,9 @@ private fun Fragment.navigateToWeb3Asset(
address: String?,
): Boolean {
val nav = runCatching { findNavController() }.getOrNull() ?: return false
if (nav.currentDestination?.id == R.id.web3_transactions_fragment &&
nav.currentBackStackEntry?.tokenArgument()?.assetId == token.assetId
) {
return true
}
val previous = nav.previousBackStackEntry
if (previous?.destination?.id == R.id.web3_transactions_fragment &&
previous.tokenArgument()?.assetId == token.assetId
) {
activity?.onBackPressedDispatcher?.onBackPressed()
val existingToken = nav.findBackStackEntry(R.id.web3_transactions_fragment)?.tokenArgument()
if (existingToken?.assetId == token.assetId && existingToken.walletId == token.walletId) {
nav.popBackStack(R.id.web3_transactions_fragment, false)
return true
}
val action = when (nav.currentDestination?.id) {
Expand All @@ -84,8 +80,39 @@ private fun Fragment.navigateToWeb3Asset(
putParcelable(Web3TransactionsFragment.ARGS_TOKEN, token)
if (!address.isNullOrEmpty()) putString(Web3TransactionsFragment.ARGS_ADDRESS, address)
}
return runCatching { nav.navigate(action, args) }.isSuccess
return runCatching { nav.navigateReplacingExisting(action, args, R.id.web3_transactions_fragment) }.isSuccess
}

internal fun NavController.navigateToWeb3Transaction(
@IdRes resId: Int,
args: Bundle,
) {
navigateReplacingExisting(resId, args, R.id.web3_transaction_fragment)
}

// Keep at most one entry of destinationId in the back stack: pop the existing one (and everything above it) before navigating.
private fun NavController.navigateReplacingExisting(
@IdRes resId: Int,
args: Bundle,
@IdRes destinationId: Int,
) {
if (findBackStackEntry(destinationId) == null) {
navigate(resId, args)
return
}
val actionOptions = currentDestination?.getAction(resId)?.navOptions
val options = NavOptions.Builder()
.setEnterAnim(actionOptions?.enterAnim ?: -1)
.setExitAnim(actionOptions?.exitAnim ?: -1)
.setPopEnterAnim(actionOptions?.popEnterAnim ?: -1)
.setPopExitAnim(actionOptions?.popExitAnim ?: -1)
.setPopUpTo(destinationId, true)
.build()
navigate(resId, args, options)
}

private fun NavController.findBackStackEntry(@IdRes destinationId: Int): NavBackStackEntry? =
runCatching { getBackStackEntry(destinationId) }.getOrNull()

private fun NavBackStackEntry.tokenArgument(): Web3TokenItem? =
arguments?.getParcelableCompat(Web3TransactionsFragment.ARGS_TOKEN, Web3TokenItem::class.java)
Original file line number Diff line number Diff line change
Expand Up @@ -613,7 +613,7 @@ class Web3TransactionsFragment : BaseFragment(R.layout.fragment_web3_transaction
putParcelable(ARGS_TOKEN, token)
putParcelable(Web3TransactionFragment.ARGS_WALLET, wallet?.toWeb3Wallet())
}
findNavController().navigate(
findNavController().navigateToWeb3Transaction(
R.id.action_web3_transactions_to_web3_transaction,
bundle
)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package one.mixin.android.ui.wallet.home

import com.google.gson.Gson
import one.mixin.android.R
import one.mixin.android.api.response.CashAccount
import one.mixin.android.api.response.WalletHomeBanner
import one.mixin.android.util.GsonHelper
Expand Down Expand Up @@ -182,6 +183,89 @@ class WalletHomeCashAccountTest {
assertNull(cache?.toState()?.earnAccounts?.single()?.maxApyText)
}

@Test
fun walletHomeCacheIgnoresStaleImportKeyResourceIds() {
val json = """
{
"walletType":"CLASSIC",
"fiatTotal":"0.00",
"btcTotal":"0.00",
"fiatSymbol":"USD",
"totalTokenCount":0,
"totalTransactionCount":0,
"importKeyAction":{"kind":"MNEMONIC_PHRASE","buttonTextRes":1,"descriptionRes":2,"learnMoreUrlRes":3}
}
""".trimIndent()

val action = json.parseWalletHomeCache()?.toState()?.importKeyAction

assertEquals(R.string.import_mnemonic_phrase, action?.buttonTextRes)
assertEquals(R.string.Import_Mnemonic_Phrase_Desc, action?.descriptionRes)
assertEquals(R.string.import_mnemonic_phrase_url, action?.learnMoreUrlRes)
assertEquals("""{"kind":"MNEMONIC_PHRASE"}""", GsonHelper.customGson.toJson(action))
}

@Test
fun walletHomeCacheDropsInvalidImportKeyActions() {
val actions = listOf(
"""{"a":"MNEMONIC_PHRASE","b":1,"c":2,"d":3}""",
"""{}""",
"""{"kind":null}""",
"""{"kind":"UNKNOWN"}""",
"""{"kind":1}""",
"""{"kind":true}""",
"""{"kind":[]}""",
"""{"kind":{}}""",
"""null""",
"""[]""",
""""MNEMONIC_PHRASE"""",
)

for (action in actions) {
val json = """
{
"walletType":"CLASSIC",
"fiatTotal":"12.34",
"btcTotal":"0.00",
"fiatSymbol":"USD",
"totalTokenCount":0,
"totalTransactionCount":0,
"importKeyAction":$action
}
""".trimIndent()

val cache = json.parseWalletHomeCache()

assertNotNull(action, cache)
assertNull(action, cache?.importKeyAction)
val state = cache?.toState()
assertNull(action, state?.importKeyAction)
assertEquals(action, "12.34", state?.fiatTotal)
}
}

@Test
fun walletHomeCacheRestoresPrivateKeyAction() {
val cache = WalletHomeCache(
walletType = WalletHomeType.CLASSIC,
fiatTotal = "0.00",
btcTotal = "0.00",
fiatSymbol = "USD",
totalTokenCount = 0,
totalTransactionCount = 0,
importKeyAction = WalletHomeImportKeyAction(WalletHomeImportKeyKind.PRIVATE_KEY),
)
val json = GsonHelper.customGson.toJson(cache)

val action = json.parseWalletHomeCache()?.toState()?.importKeyAction

assertEquals(WalletHomeImportKeyKind.PRIVATE_KEY, action?.kind)
assertEquals(R.string.import_private_key, action?.buttonTextRes)
assertEquals(R.string.Import_Private_Key_Desc, action?.descriptionRes)
assertEquals(R.string.import_private_key_url, action?.learnMoreUrlRes)
assertEquals("""{"kind":"PRIVATE_KEY"}""", GsonHelper.customGson.toJson(action))
}

@Test
fun walletHomeStateShowsDynamicBannerWhenStillLoading() {
val banner = WalletHomeBanner(
Expand Down
Loading