feat(wallet): NFT thumbnails and metadata display in Assets tab
- Add NFT metadata fetching via Koios asset_info endpoint - Parse CIP-25 onchain_metadata for image, name, description - Convert IPFS URLs to ipfs.io gateway URLs - Display 64dp thumbnails with 8dp rounded corners using Coil AsyncImage - Add bottom sheet detail view for NFT expansion (larger image + metadata) - Graceful fallback with placeholder icons on image load failure - Load metadata in presenter, cache results for 30 minutes - Parallel metadata fetching for better performance
This commit is contained in:
parent
a57fd79098
commit
2d8df4f23f
8 changed files with 572 additions and 11 deletions
|
|
@ -9,6 +9,7 @@ package io.element.android.features.wallet.test
|
|||
import io.element.android.features.wallet.api.CardanoClient
|
||||
import io.element.android.features.wallet.api.CardanoException
|
||||
import io.element.android.features.wallet.api.NativeAsset
|
||||
import io.element.android.features.wallet.api.NftMetadata
|
||||
import io.element.android.features.wallet.api.ProtocolParameters
|
||||
import io.element.android.features.wallet.api.TxStatus
|
||||
import io.element.android.features.wallet.api.TxSummary
|
||||
|
|
@ -24,6 +25,7 @@ import io.element.android.features.wallet.api.UtxoAsset
|
|||
* - Rate limiting
|
||||
* - Transaction lifecycle (pending → confirmed)
|
||||
* - ADA Handle resolution
|
||||
* - NFT metadata
|
||||
*/
|
||||
class FakeCardanoClient : CardanoClient {
|
||||
// Configurable responses
|
||||
|
|
@ -34,6 +36,7 @@ class FakeCardanoClient : CardanoClient {
|
|||
var assets = mutableMapOf<String, List<NativeAsset>>()
|
||||
var transactions = mutableMapOf<String, List<TxSummary>>()
|
||||
var handles = mutableMapOf<String, String>() // handle name (without $) -> address
|
||||
var nftMetadata = mutableMapOf<String, NftMetadata>() // policyId+assetName -> metadata
|
||||
|
||||
// Error simulation
|
||||
var shouldFailWithNetworkError = false
|
||||
|
|
@ -67,6 +70,8 @@ class FakeCardanoClient : CardanoClient {
|
|||
private set
|
||||
var resolveHandleCallCount = 0
|
||||
private set
|
||||
var getNftMetadataCallCount = 0
|
||||
private set
|
||||
|
||||
/**
|
||||
* Represents a submitted transaction for testing.
|
||||
|
|
@ -204,6 +209,20 @@ class FakeCardanoClient : CardanoClient {
|
|||
return Result.success(address)
|
||||
}
|
||||
|
||||
override suspend fun getNftMetadata(policyId: String, assetName: String): Result<NftMetadata?> {
|
||||
getNftMetadataCallCount++
|
||||
|
||||
if (shouldFailWithNetworkError) {
|
||||
return Result.failure(CardanoException.NetworkException("Simulated network error"))
|
||||
}
|
||||
if (shouldFailWithRateLimit) {
|
||||
return Result.failure(CardanoException.RateLimitException(retryAfterMs = 1000L))
|
||||
}
|
||||
|
||||
val key = "$policyId$assetName"
|
||||
return Result.success(nftMetadata[key])
|
||||
}
|
||||
|
||||
// Helper methods for test setup
|
||||
|
||||
/**
|
||||
|
|
@ -270,6 +289,13 @@ class FakeCardanoClient : CardanoClient {
|
|||
handles[handle.lowercase().trim()] = address
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures NFT metadata for an asset.
|
||||
*/
|
||||
fun givenNftMetadata(policyId: String, assetName: String, metadata: NftMetadata) {
|
||||
nftMetadata["$policyId$assetName"] = metadata
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets all state and counters.
|
||||
*/
|
||||
|
|
@ -281,6 +307,7 @@ class FakeCardanoClient : CardanoClient {
|
|||
assets.clear()
|
||||
transactions.clear()
|
||||
handles.clear()
|
||||
nftMetadata.clear()
|
||||
shouldFailWithNetworkError = false
|
||||
shouldFailWithRateLimit = false
|
||||
submitShouldFail = false
|
||||
|
|
@ -294,6 +321,7 @@ class FakeCardanoClient : CardanoClient {
|
|||
getAddressAssetsCallCount = 0
|
||||
getAddressTransactionsCallCount = 0
|
||||
resolveHandleCallCount = 0
|
||||
getNftMetadataCallCount = 0
|
||||
protocolParameters = ProtocolParameters(
|
||||
minFeeA = 44L,
|
||||
minFeeB = 155381L,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue