Skip to content

Commit

Permalink
Send funds/Wallet details filter tokens by type (Desktop) #171
Browse files Browse the repository at this point in the history
  • Loading branch information
MrStahlfelge committed Jun 6, 2023
1 parent 33f389c commit d83d5fc
Show file tree
Hide file tree
Showing 11 changed files with 174 additions and 43 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,7 @@ class WalletDetailsFragment : Fragment(), AddressChooserCallback {

// tokens
val tokensList = walletDetailsViewModel.uiLogic.tokensList
binding.cardviewTokens.visibility = if (tokensList.isNotEmpty()) View.VISIBLE else View.GONE
binding.cardviewTokens.visibility = if (walletDetailsViewModel.uiLogic.hasTokens) View.VISIBLE else View.GONE
binding.walletTokenNum.text = tokensList.size.toString()

binding.walletTokenEntries.apply {
Expand Down
4 changes: 4 additions & 0 deletions android/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -334,6 +334,10 @@
Please be aware that this content is created by third parties and might be unwanted.</string>
<string name="button_download_content_on">Activate</string>
<string name="button_download_content_off">Deactivate</string>
<string name="label_token_generic">Generic</string>
<string name="label_token_image">Images</string>
<string name="label_token_audio">Audio</string>
<string name="label_token_video">Video</string>

<!-- Mosaik view -->
<string name="title_mosaik">dApps</string>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -316,9 +316,13 @@ const val STRING_LABEL_TIME_SPAN_JUST_NOW = "label_time_span_just_now"
const val STRING_LABEL_TIME_SPAN_MINUTES_AGO = "label_time_span_minutes_ago"
const val STRING_LABEL_TIME_SPAN_MOMENTS_AGO = "label_time_span_moments_ago"
const val STRING_LABEL_TO = "label_to"
const val STRING_LABEL_TOKEN_AUDIO = "label_token_audio"
const val STRING_LABEL_TOKEN_DESCRIPTION = "label_token_description"
const val STRING_LABEL_TOKEN_GENERIC = "label_token_generic"
const val STRING_LABEL_TOKEN_IMAGE = "label_token_image"
const val STRING_LABEL_TOKEN_SUPPLY = "label_token_supply"
const val STRING_LABEL_TOKEN_VERIFICATION_URL = "label_token_verification_url"
const val STRING_LABEL_TOKEN_VIDEO = "label_token_video"
const val STRING_LABEL_TOKENS = "label_tokens"
const val STRING_LABEL_UNCONFIRMED = "label_unconfirmed"
const val STRING_LABEL_UNNAMED_TOKEN = "label_unnamed_token"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package org.ergoplatform.uilogic.tokens

import org.ergoplatform.persistance.TokenInformation

/**
* Helper methods for filterable token lists
*/
interface FilterTokenListUiLogic {
val tokenFilterMap: MutableMap<Int, Boolean>

fun toggleTokenFilter(filterType: Int) {
tokenFilterMap[filterType] = !(tokenFilterMap[filterType] ?: false)
onFilterChanged()
}

fun hasTokenFilter(filterType: Int): Boolean =
tokenFilterMap[filterType] ?: false

fun isTokenInFilter(ti: TokenInformation?): Boolean {
println(tokenFilterMap.values)
return tokenFilterMap.values.none { it }
|| ti?.thumbnailType?.let { tokenFilterMap[it] == true } ?: false
}

fun onFilterChanged() {}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,13 @@ import org.ergoplatform.tokens.TokenInfoManager
import org.ergoplatform.tokens.isSingularToken
import org.ergoplatform.transactions.*
import org.ergoplatform.uilogic.*
import org.ergoplatform.uilogic.tokens.FilterTokenListUiLogic
import org.ergoplatform.utils.LogUtils
import org.ergoplatform.utils.formatFiatToString
import org.ergoplatform.wallet.*
import kotlin.math.max

abstract class SendFundsUiLogic : SubmitTransactionUiLogic() {
abstract class SendFundsUiLogic : SubmitTransactionUiLogic(), FilterTokenListUiLogic {

var receiverAddress: String = ""
set(value) {
Expand Down Expand Up @@ -59,6 +60,7 @@ abstract class SendFundsUiLogic : SubmitTransactionUiLogic() {
val tokensAvail: HashMap<String, WalletToken> = HashMap()
val tokensChosen: HashMap<String, ErgoToken> = HashMap()
val tokensInfo: HashMap<String, TokenInformation> = HashMap()
override val tokenFilterMap: MutableMap<Int, Boolean> = HashMap()

private val paymentRequestWarnings = ArrayList<PaymentRequestWarning>()

Expand Down Expand Up @@ -399,7 +401,7 @@ abstract class SendFundsUiLogic : SubmitTransactionUiLogic() {
*/
fun getTokensToChooseFrom(): List<WalletToken> {
return tokensAvail.values.filter {
!tokensChosen.containsKey(it.tokenId)
!tokensChosen.containsKey(it.tokenId) && isTokenInFilter(tokensInfo[it.tokenId])
}.sortedBy { it.name?.lowercase() }
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch
import org.ergoplatform.ErgoAmount
import org.ergoplatform.ApiServiceManager
import org.ergoplatform.ErgoAmount
import org.ergoplatform.WalletStateSyncManager
import org.ergoplatform.ergoauth.isErgoAuthRequestUri
import org.ergoplatform.parsePaymentRequest
Expand All @@ -19,11 +19,12 @@ import org.ergoplatform.uilogic.STRING_ERROR_QR_CODE_CONTENT_UNKNOWN
import org.ergoplatform.uilogic.STRING_HINT_READONLY_SIGNING_REQUEST
import org.ergoplatform.uilogic.STRING_LABEL_ALL_ADDRESSES
import org.ergoplatform.uilogic.StringProvider
import org.ergoplatform.uilogic.tokens.FilterTokenListUiLogic
import org.ergoplatform.uilogic.transactions.AddressTransactionWithTokens
import org.ergoplatform.wallet.*
import org.ergoplatform.wallet.addresses.getAddressLabel

abstract class WalletDetailsUiLogic {
abstract class WalletDetailsUiLogic: FilterTokenListUiLogic {
val maxTransactionsToShow = 5

var wallet: Wallet? = null
Expand All @@ -32,8 +33,11 @@ abstract class WalletDetailsUiLogic {
private set
var walletAddress: WalletAddress? = null
private set
var tokensList: List<WalletToken> = emptyList()
private set
private var fullTokensList: List<WalletToken> = emptyList()
val hasTokens get() = fullTokensList.isNotEmpty()
val tokensList
get() = fullTokensList.filter { isTokenInFilter(tokenInformation[it.tokenId]) }
override val tokenFilterMap: MutableMap<Int, Boolean> = HashMap()

private var tokenInformationJob: Job? = null
val tokenInformation: HashMap<String, TokenInformation> = HashMap()
Expand Down Expand Up @@ -100,7 +104,7 @@ abstract class WalletDetailsUiLogic {

private fun refreshAddress() {
walletAddress = addressIdx?.let { wallet?.getDerivedAddressEntity(it) }
tokensList = (walletAddress?.let { wallet?.getTokensForAddress(it.publicAddress) }
fullTokensList = (walletAddress?.let { wallet?.getTokensForAddress(it.publicAddress) }
?: wallet?.getTokensForAllAddresses() ?: emptyList()).sortedBy { it.name?.lowercase() }

onDataChanged()
Expand Down Expand Up @@ -148,7 +152,7 @@ abstract class WalletDetailsUiLogic {
tokenInformationJob?.cancel()

// copy to an own list to prevent race conditions
val tokensList = this.tokensList.toMutableList()
val tokensList = this.fullTokensList.toMutableList()

// start gathering token information
if (tokensList.isNotEmpty()) {
Expand Down Expand Up @@ -259,6 +263,10 @@ abstract class WalletDetailsUiLogic {
return walletAddress?.let { listOf(it) } ?: wallet?.getSortedDerivedAddressesList()
}

override fun onFilterChanged() {
onDataChanged()
}

abstract fun onDataChanged()

abstract fun onNewTokenInfoGathered(tokenInformation: TokenInformation)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package org.ergoplatform.desktop.tokens

import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.widthIn
import androidx.compose.material.*
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Check
import androidx.compose.material.icons.filled.FilterAlt
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
import androidx.compose.ui.unit.dp
import org.ergoplatform.Application
import org.ergoplatform.compose.settings.defaultPadding
import org.ergoplatform.persistance.THUMBNAIL_TYPE_NFT_AUDIO
import org.ergoplatform.persistance.THUMBNAIL_TYPE_NFT_IMG
import org.ergoplatform.persistance.THUMBNAIL_TYPE_NFT_VID
import org.ergoplatform.persistance.THUMBNAIL_TYPE_NONE
import org.ergoplatform.uilogic.STRING_LABEL_TOKEN_AUDIO
import org.ergoplatform.uilogic.STRING_LABEL_TOKEN_GENERIC
import org.ergoplatform.uilogic.STRING_LABEL_TOKEN_IMAGE
import org.ergoplatform.uilogic.STRING_LABEL_TOKEN_VIDEO
import org.ergoplatform.uilogic.tokens.FilterTokenListUiLogic

@Composable
fun TokenFilterMenu(uiLogic: FilterTokenListUiLogic) {
var showActionMenu by remember { mutableStateOf(false) }
IconButton(onClick = { showActionMenu = !showActionMenu }) {
Icon(Icons.Default.FilterAlt, null)
DropdownMenu(
showActionMenu,
onDismissRequest = { showActionMenu = false },
modifier = Modifier.widthIn(min = 150.dp)
) {
val modifierForFilter: (Int) -> Modifier = {
Modifier.alpha(if (uiLogic.hasTokenFilter(it)) 1f else 0f)
}

DropdownMenuItem(onClick = {
uiLogic.toggleTokenFilter(THUMBNAIL_TYPE_NONE)
showActionMenu = false
}) {
Icon(Icons.Default.Check, null, modifierForFilter(THUMBNAIL_TYPE_NONE))
Spacer(Modifier.size(defaultPadding))
Text(Application.texts.getString(STRING_LABEL_TOKEN_GENERIC))
}
DropdownMenuItem(onClick = {
uiLogic.toggleTokenFilter(THUMBNAIL_TYPE_NFT_IMG)
showActionMenu = false
}) {
Icon(Icons.Default.Check, null, modifierForFilter(THUMBNAIL_TYPE_NFT_IMG))
Spacer(Modifier.size(defaultPadding))
Text(Application.texts.getString(STRING_LABEL_TOKEN_IMAGE))
}
DropdownMenuItem(onClick = {
uiLogic.toggleTokenFilter(THUMBNAIL_TYPE_NFT_AUDIO)
showActionMenu = false
}) {
Icon(Icons.Default.Check, null, modifierForFilter(THUMBNAIL_TYPE_NFT_AUDIO))
Spacer(Modifier.size(defaultPadding))
Text(Application.texts.getString(STRING_LABEL_TOKEN_AUDIO))
}
DropdownMenuItem(onClick = {
uiLogic.toggleTokenFilter(THUMBNAIL_TYPE_NFT_VID)
showActionMenu = false
}) {
Icon(Icons.Default.Check, null, modifierForFilter(THUMBNAIL_TYPE_NFT_VID))
Spacer(Modifier.size(defaultPadding))
Text(Application.texts.getString(STRING_LABEL_TOKEN_VIDEO))
}
}
}
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
package org.ergoplatform.desktop.transactions

import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.Text
Expand All @@ -16,26 +13,29 @@ import androidx.compose.ui.text.style.TextAlign
import org.ergoplatform.Application
import org.ergoplatform.compose.tokens.TokenEntryViewData
import org.ergoplatform.compose.tokens.TokenLabel
import org.ergoplatform.desktop.tokens.TokenFilterMenu
import org.ergoplatform.desktop.ui.AppDialog
import org.ergoplatform.desktop.ui.AppScrollbar
import org.ergoplatform.desktop.ui.defaultPadding
import org.ergoplatform.mosaik.MiddleEllipsisText
import org.ergoplatform.mosaik.MosaikStyleConfig
import org.ergoplatform.mosaik.labelStyle
import org.ergoplatform.mosaik.model.ui.text.LabelStyle
import org.ergoplatform.persistance.TokenInformation
import org.ergoplatform.persistance.WalletToken
import org.ergoplatform.uilogic.STRING_TITLE_ADD_TOKEN
import org.ergoplatform.uilogic.transactions.SendFundsUiLogic

@Composable
fun ChooseTokenListDialog(
tokenToChooseFrom: List<WalletToken>,
tokenInfoMap: HashMap<String, TokenInformation>,
onTokenChosen: (WalletToken) -> Unit,
uiLogic: SendFundsUiLogic,
refreshCount: Int,
onDismissRequest: () -> Unit,
) {
val preparedList = remember {
tokenToChooseFrom.map { token ->
val tokenInfoMap = uiLogic.tokensInfo
val onTokenChosen: (WalletToken) -> Unit = { uiLogic.newTokenChosen(it.tokenId!!) }

val preparedList = remember(refreshCount) {
uiLogic.getTokensToChooseFrom().map { token ->
TokenEntryViewData(
token, false, Application.texts
).apply { bind(tokenInfoMap[token.tokenId]) }
Expand All @@ -49,12 +49,19 @@ fun ChooseTokenListDialog(
Column(
Modifier.fillMaxWidth().verticalScroll(scrollState)
) {
Text(
remember { Application.texts.getString(STRING_TITLE_ADD_TOKEN) },
Row(
Modifier.fillMaxWidth().padding(defaultPadding),
textAlign = TextAlign.Center,
style = labelStyle(LabelStyle.BODY1)
)
verticalAlignment = Alignment.CenterVertically,
) {
Text(
remember { Application.texts.getString(STRING_TITLE_ADD_TOKEN) },
Modifier.weight(1f),
textAlign = TextAlign.Center,
style = labelStyle(LabelStyle.BODY1)
)

TokenFilterMenu(uiLogic)
}

preparedList.forEach { token ->
Box(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,7 @@ import androidx.compose.material.IconButton
import androidx.compose.material.ScaffoldState
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.QrCodeScanner
import androidx.compose.runtime.Composable
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.*
import androidx.compose.ui.text.input.TextFieldValue
import com.arkivanov.decompose.ComponentContext
import com.arkivanov.decompose.router.pop
Expand All @@ -25,7 +22,10 @@ import org.ergoplatform.persistance.WalletAddress
import org.ergoplatform.persistance.WalletConfig
import org.ergoplatform.transactions.TransactionInfo
import org.ergoplatform.transactions.TransactionResult
import org.ergoplatform.uilogic.*
import org.ergoplatform.uilogic.STRING_BUTTON_SEND
import org.ergoplatform.uilogic.STRING_INFO_PURPOSE_MESSAGE
import org.ergoplatform.uilogic.STRING_INFO_PURPOSE_MESSAGE_ACCEPT
import org.ergoplatform.uilogic.STRING_INFO_PURPOSE_MESSAGE_DECLINE
import org.ergoplatform.uilogic.transactions.SendFundsUiLogic
import org.ergoplatform.uilogic.transactions.SuggestedFee
import org.ergoplatform.wallet.isReadOnly
Expand Down Expand Up @@ -65,6 +65,7 @@ class SendFundsComponent(
private val editFeeDialogState = mutableStateOf(false)
private val addressBookDialogState = AddressBookDialogStateHandler()
private val preparedTransactionInfoState = mutableStateOf<TransactionInfo?>(null)
private var tokenFilterRefresh by mutableStateOf(0)

@Composable
override fun renderScreenContents(scaffoldState: ScaffoldState?) {
Expand Down Expand Up @@ -98,9 +99,8 @@ class SendFundsComponent(

if (addTokenDialogState.value) {
ChooseTokenListDialog(
remember { uiLogic.getTokensToChooseFrom() },
uiLogic.tokensInfo,
onTokenChosen = { uiLogic.newTokenChosen(it.tokenId!!) },
uiLogic,
tokenFilterRefresh,
onDismissRequest = { addTokenDialogState.value = false }
)
}
Expand Down Expand Up @@ -242,6 +242,10 @@ class SendFundsComponent(
showSigningPrompt(signingPrompt)
}

override fun onFilterChanged() {
tokenFilterRefresh++
}

override fun notifyHasPreparedTx(preparedTx: TransactionInfo) {
preparedTransactionInfoState.value = preparedTx
}
Expand Down
Loading

0 comments on commit d83d5fc

Please sign in to comment.