diff --git a/android/src/main/java/org/ergoplatform/android/wallet/WalletDetailsFragment.kt b/android/src/main/java/org/ergoplatform/android/wallet/WalletDetailsFragment.kt
index b97c57769..11a8de825 100644
--- a/android/src/main/java/org/ergoplatform/android/wallet/WalletDetailsFragment.kt
+++ b/android/src/main/java/org/ergoplatform/android/wallet/WalletDetailsFragment.kt
@@ -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 {
diff --git a/android/src/main/res/values/strings.xml b/android/src/main/res/values/strings.xml
index f12492f80..4dcf60ab3 100644
--- a/android/src/main/res/values/strings.xml
+++ b/android/src/main/res/values/strings.xml
@@ -334,6 +334,10 @@
Please be aware that this content is created by third parties and might be unwanted.
Activate
Deactivate
+ Generic
+ Images
+ Audio
+ Video
dApps
diff --git a/common-jvm/src/main/java/org/ergoplatform/uilogic/StringResources.kt b/common-jvm/src/main/java/org/ergoplatform/uilogic/StringResources.kt
index 844a3f2f0..45cd612fa 100644
--- a/common-jvm/src/main/java/org/ergoplatform/uilogic/StringResources.kt
+++ b/common-jvm/src/main/java/org/ergoplatform/uilogic/StringResources.kt
@@ -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"
diff --git a/common-jvm/src/main/java/org/ergoplatform/uilogic/tokens/FilterTokenListUiLogic.kt b/common-jvm/src/main/java/org/ergoplatform/uilogic/tokens/FilterTokenListUiLogic.kt
new file mode 100644
index 000000000..9ac20e6a4
--- /dev/null
+++ b/common-jvm/src/main/java/org/ergoplatform/uilogic/tokens/FilterTokenListUiLogic.kt
@@ -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
+
+ 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() {}
+}
\ No newline at end of file
diff --git a/common-jvm/src/main/java/org/ergoplatform/uilogic/transactions/SendFundsUiLogic.kt b/common-jvm/src/main/java/org/ergoplatform/uilogic/transactions/SendFundsUiLogic.kt
index 8118394ac..180d93bff 100644
--- a/common-jvm/src/main/java/org/ergoplatform/uilogic/transactions/SendFundsUiLogic.kt
+++ b/common-jvm/src/main/java/org/ergoplatform/uilogic/transactions/SendFundsUiLogic.kt
@@ -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) {
@@ -59,6 +60,7 @@ abstract class SendFundsUiLogic : SubmitTransactionUiLogic() {
val tokensAvail: HashMap = HashMap()
val tokensChosen: HashMap = HashMap()
val tokensInfo: HashMap = HashMap()
+ override val tokenFilterMap: MutableMap = HashMap()
private val paymentRequestWarnings = ArrayList()
@@ -399,7 +401,7 @@ abstract class SendFundsUiLogic : SubmitTransactionUiLogic() {
*/
fun getTokensToChooseFrom(): List {
return tokensAvail.values.filter {
- !tokensChosen.containsKey(it.tokenId)
+ !tokensChosen.containsKey(it.tokenId) && isTokenInFilter(tokensInfo[it.tokenId])
}.sortedBy { it.name?.lowercase() }
}
diff --git a/common-jvm/src/main/java/org/ergoplatform/uilogic/wallet/WalletDetailsUiLogic.kt b/common-jvm/src/main/java/org/ergoplatform/uilogic/wallet/WalletDetailsUiLogic.kt
index 70099e34f..ae0ea3461 100644
--- a/common-jvm/src/main/java/org/ergoplatform/uilogic/wallet/WalletDetailsUiLogic.kt
+++ b/common-jvm/src/main/java/org/ergoplatform/uilogic/wallet/WalletDetailsUiLogic.kt
@@ -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
@@ -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
@@ -32,8 +33,11 @@ abstract class WalletDetailsUiLogic {
private set
var walletAddress: WalletAddress? = null
private set
- var tokensList: List = emptyList()
- private set
+ private var fullTokensList: List = emptyList()
+ val hasTokens get() = fullTokensList.isNotEmpty()
+ val tokensList
+ get() = fullTokensList.filter { isTokenInFilter(tokenInformation[it.tokenId]) }
+ override val tokenFilterMap: MutableMap = HashMap()
private var tokenInformationJob: Job? = null
val tokenInformation: HashMap = HashMap()
@@ -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()
@@ -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()) {
@@ -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)
diff --git a/desktop/src/main/java/org/ergoplatform/desktop/tokens/TokenFilterMenu.kt b/desktop/src/main/java/org/ergoplatform/desktop/tokens/TokenFilterMenu.kt
new file mode 100644
index 000000000..21851d5b4
--- /dev/null
+++ b/desktop/src/main/java/org/ergoplatform/desktop/tokens/TokenFilterMenu.kt
@@ -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))
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/desktop/src/main/java/org/ergoplatform/desktop/transactions/ChooseTokenListDialog.kt b/desktop/src/main/java/org/ergoplatform/desktop/transactions/ChooseTokenListDialog.kt
index 084a7c48a..5e80e8e81 100644
--- a/desktop/src/main/java/org/ergoplatform/desktop/transactions/ChooseTokenListDialog.kt
+++ b/desktop/src/main/java/org/ergoplatform/desktop/transactions/ChooseTokenListDialog.kt
@@ -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
@@ -16,6 +13,7 @@ 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
@@ -23,19 +21,21 @@ 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,
- tokenInfoMap: HashMap,
- 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]) }
@@ -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(
diff --git a/desktop/src/main/java/org/ergoplatform/desktop/transactions/SendFundsComponent.kt b/desktop/src/main/java/org/ergoplatform/desktop/transactions/SendFundsComponent.kt
index ea4eed506..8a88b0810 100644
--- a/desktop/src/main/java/org/ergoplatform/desktop/transactions/SendFundsComponent.kt
+++ b/desktop/src/main/java/org/ergoplatform/desktop/transactions/SendFundsComponent.kt
@@ -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
@@ -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
@@ -65,6 +65,7 @@ class SendFundsComponent(
private val editFeeDialogState = mutableStateOf(false)
private val addressBookDialogState = AddressBookDialogStateHandler()
private val preparedTransactionInfoState = mutableStateOf(null)
+ private var tokenFilterRefresh by mutableStateOf(0)
@Composable
override fun renderScreenContents(scaffoldState: ScaffoldState?) {
@@ -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 }
)
}
@@ -242,6 +242,10 @@ class SendFundsComponent(
showSigningPrompt(signingPrompt)
}
+ override fun onFilterChanged() {
+ tokenFilterRefresh++
+ }
+
override fun notifyHasPreparedTx(preparedTx: TransactionInfo) {
preparedTransactionInfoState.value = preparedTx
}
diff --git a/desktop/src/main/java/org/ergoplatform/desktop/wallet/WalletDetailsScreen.kt b/desktop/src/main/java/org/ergoplatform/desktop/wallet/WalletDetailsScreen.kt
index dd4881b11..d753aaee2 100644
--- a/desktop/src/main/java/org/ergoplatform/desktop/wallet/WalletDetailsScreen.kt
+++ b/desktop/src/main/java/org/ergoplatform/desktop/wallet/WalletDetailsScreen.kt
@@ -2,10 +2,7 @@ package org.ergoplatform.desktop.wallet
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
-import androidx.compose.material.Divider
-import androidx.compose.material.Icon
-import androidx.compose.material.IconButton
-import androidx.compose.material.Text
+import androidx.compose.material.*
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.*
import androidx.compose.runtime.*
@@ -22,6 +19,7 @@ import org.ergoplatform.compose.settings.smallIconSize
import org.ergoplatform.compose.tokens.TokenEntryViewData
import org.ergoplatform.compose.tokens.TokenLabel
import org.ergoplatform.desktop.tokens.TokenEntryView
+import org.ergoplatform.desktop.tokens.TokenFilterMenu
import org.ergoplatform.desktop.ui.*
import org.ergoplatform.desktop.wallet.addresses.ChooseAddressButton
import org.ergoplatform.mosaik.MiddleEllipsisText
@@ -64,7 +62,7 @@ fun WalletDetailsScreen(
ErgoBalanceLayout(uiLogic)
- if (uiLogic.tokensList.isNotEmpty()) {
+ if (uiLogic.hasTokens) {
WalletTokensLayout(uiLogic, onTokenClicked)
}
}
@@ -225,7 +223,7 @@ private fun WalletTokensLayout(
Column(Modifier.padding(defaultPadding)) {
// HEADER
- Row {
+ Row(verticalAlignment = Alignment.CenterVertically) {
Icon(
painterResource("ic_tokenlogo.xml"), null,
Modifier.requiredSize(mediumIconSize)
@@ -233,15 +231,13 @@ private fun WalletTokensLayout(
Text(
uiLogic.tokensList.size.toString(),
- Modifier.padding(start = defaultPadding)
- .align(Alignment.CenterVertically),
+ Modifier.padding(start = defaultPadding),
style = labelStyle(LabelStyle.HEADLINE2),
)
Text(
Application.texts.getString(STRING_LABEL_TOKENS),
- Modifier.padding(start = defaultPadding / 2).weight(1f)
- .align(Alignment.CenterVertically),
+ Modifier.padding(start = defaultPadding / 2).weight(1f),
style = labelStyle(LabelStyle.BODY1BOLD),
color = uiErgoColor
)
@@ -261,9 +257,11 @@ private fun WalletTokensLayout(
style = labelStyle(LabelStyle.BODY1),
color = MosaikStyleConfig.secondaryLabelColor,
modifier = Modifier.align(Alignment.CenterVertically)
- .padding(start = defaultPadding / 2),
+ .padding(horizontal = defaultPadding / 2),
)
}
+
+ TokenFilterMenu(uiLogic)
}
// TOKENS LIST
diff --git a/ios/resources/i18n/strings.properties b/ios/resources/i18n/strings.properties
index d519e56e5..8a76a9d8a 100644
--- a/ios/resources/i18n/strings.properties
+++ b/ios/resources/i18n/strings.properties
@@ -359,9 +359,13 @@ label_time_span_just_now=just now
label_time_span_minutes_ago={0} minutes ago
label_time_span_moments_ago=a few moments ago
label_to=to
+label_token_audio=Audio
label_token_description=Token description
+label_token_generic=Generic
+label_token_image=Images
label_token_supply=Full supply amount
label_token_verification_url=Token verification service URL
+label_token_video=Video
label_tokens=tokens
label_unconfirmed=unconfirmed
label_unnamed_token=(unnamed token)