Updated on 2026-08-14
This commit is contained in:
parent
bc24aeb9f3
commit
c77fcafdc2
11 changed files with 380 additions and 1 deletions
4
.idea/inspectionProfiles/Project_Default.xml
generated
4
.idea/inspectionProfiles/Project_Default.xml
generated
|
|
@ -75,6 +75,10 @@
|
||||||
<option name="composableFile" value="true" />
|
<option name="composableFile" value="true" />
|
||||||
<option name="previewFile" value="true" />
|
<option name="previewFile" value="true" />
|
||||||
</inspection_tool>
|
</inspection_tool>
|
||||||
|
<inspection_tool class="PreviewParameterProviderOnFirstParameter" enabled="true" level="ERROR" enabled_by_default="true">
|
||||||
|
<option name="composableFile" value="true" />
|
||||||
|
<option name="previewFile" value="true" />
|
||||||
|
</inspection_tool>
|
||||||
<inspection_tool class="PreviewPickerAnnotation" enabled="true" level="ERROR" enabled_by_default="true">
|
<inspection_tool class="PreviewPickerAnnotation" enabled="true" level="ERROR" enabled_by_default="true">
|
||||||
<option name="composableFile" value="true" />
|
<option name="composableFile" value="true" />
|
||||||
<option name="previewFile" value="true" />
|
<option name="previewFile" value="true" />
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ import com.tangem.domain.visa.model.VisaTxDetails
|
||||||
*
|
*
|
||||||
[REDACTED_AUTHOR]
|
[REDACTED_AUTHOR]
|
||||||
*/
|
*/
|
||||||
internal class EmailMessageBodyResolver(
|
class EmailMessageBodyResolver(
|
||||||
private val feedbackRepository: FeedbackRepository,
|
private val feedbackRepository: FeedbackRepository,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ plugins {
|
||||||
alias(deps.plugins.kotlin.android)
|
alias(deps.plugins.kotlin.android)
|
||||||
alias(deps.plugins.kotlin.kapt)
|
alias(deps.plugins.kotlin.kapt)
|
||||||
alias(deps.plugins.hilt.android)
|
alias(deps.plugins.hilt.android)
|
||||||
|
alias(deps.plugins.kotlin.serialization)
|
||||||
id("configuration")
|
id("configuration")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -40,6 +41,7 @@ dependencies {
|
||||||
implementation(projects.domain.manageTokens)
|
implementation(projects.domain.manageTokens)
|
||||||
implementation(projects.domain.wallets.models)
|
implementation(projects.domain.wallets.models)
|
||||||
implementation(projects.domain.wallets)
|
implementation(projects.domain.wallets)
|
||||||
|
implementation(projects.domain.feedback.models)
|
||||||
|
|
||||||
implementation(projects.data.common)
|
implementation(projects.data.common)
|
||||||
|
|
||||||
|
|
@ -68,4 +70,5 @@ dependencies {
|
||||||
implementation(projects.common.ui)
|
implementation(projects.common.ui)
|
||||||
implementation(projects.libs.blockchainSdk)
|
implementation(projects.libs.blockchainSdk)
|
||||||
implementation(projects.libs.crypto)
|
implementation(projects.libs.crypto)
|
||||||
|
implementation(deps.kotlin.serialization)
|
||||||
}
|
}
|
||||||
|
|
@ -2,6 +2,7 @@ package com.tangem.feature.tester.presentation
|
||||||
|
|
||||||
import androidx.compose.foundation.layout.systemBarsPadding
|
import androidx.compose.foundation.layout.systemBarsPadding
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.collectAsState
|
import androidx.compose.runtime.collectAsState
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
|
@ -20,6 +21,8 @@ import com.tangem.feature.tester.presentation.accounts.ui.AccountsScreen
|
||||||
import com.tangem.feature.tester.presentation.accounts.viewmodel.TesterAccountsViewModel
|
import com.tangem.feature.tester.presentation.accounts.viewmodel.TesterAccountsViewModel
|
||||||
import com.tangem.feature.tester.presentation.actions.TesterActionsScreen
|
import com.tangem.feature.tester.presentation.actions.TesterActionsScreen
|
||||||
import com.tangem.feature.tester.presentation.actions.TesterActionsViewModel
|
import com.tangem.feature.tester.presentation.actions.TesterActionsViewModel
|
||||||
|
import com.tangem.feature.tester.presentation.addresses.ui.AddressesInfoScreen
|
||||||
|
import com.tangem.feature.tester.presentation.addresses.viewmodels.AddressesInfoViewModel
|
||||||
import com.tangem.feature.tester.presentation.environments.ui.EnvironmentTogglesScreen
|
import com.tangem.feature.tester.presentation.environments.ui.EnvironmentTogglesScreen
|
||||||
import com.tangem.feature.tester.presentation.environments.viewmodels.EnvironmentsTogglesViewModel
|
import com.tangem.feature.tester.presentation.environments.viewmodels.EnvironmentsTogglesViewModel
|
||||||
import com.tangem.feature.tester.presentation.excludedblockchains.ExcludedBlockchainsScreen
|
import com.tangem.feature.tester.presentation.excludedblockchains.ExcludedBlockchainsScreen
|
||||||
|
|
@ -82,6 +85,7 @@ internal class TesterActivity : ComposeActivity() {
|
||||||
ButtonUM.TESTER_ACTIONS,
|
ButtonUM.TESTER_ACTIONS,
|
||||||
ButtonUM.TEST_PUSHES,
|
ButtonUM.TEST_PUSHES,
|
||||||
ButtonUM.ACCOUNTS,
|
ButtonUM.ACCOUNTS,
|
||||||
|
ButtonUM.ADDRESSES_INFO,
|
||||||
),
|
),
|
||||||
onButtonClick = { buttonUM ->
|
onButtonClick = { buttonUM ->
|
||||||
val route = when (buttonUM) {
|
val route = when (buttonUM) {
|
||||||
|
|
@ -92,6 +96,7 @@ internal class TesterActivity : ComposeActivity() {
|
||||||
ButtonUM.TESTER_ACTIONS -> TesterScreen.TESTER_ACTIONS
|
ButtonUM.TESTER_ACTIONS -> TesterScreen.TESTER_ACTIONS
|
||||||
ButtonUM.TEST_PUSHES -> TesterScreen.TEST_PUSHES
|
ButtonUM.TEST_PUSHES -> TesterScreen.TEST_PUSHES
|
||||||
ButtonUM.ACCOUNTS -> TesterScreen.ACCOUNTS
|
ButtonUM.ACCOUNTS -> TesterScreen.ACCOUNTS
|
||||||
|
ButtonUM.ADDRESSES_INFO -> TesterScreen.ADDRESSES_INFO
|
||||||
}
|
}
|
||||||
|
|
||||||
innerTesterRouter.open(route)
|
innerTesterRouter.open(route)
|
||||||
|
|
@ -162,6 +167,18 @@ internal class TesterActivity : ComposeActivity() {
|
||||||
|
|
||||||
AccountsScreen(state)
|
AccountsScreen(state)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
composable(route = TesterScreen.ADDRESSES_INFO.name) {
|
||||||
|
val viewModel = hiltViewModel<AddressesInfoViewModel>()
|
||||||
|
|
||||||
|
LaunchedEffect(viewModel) {
|
||||||
|
viewModel.setupNavigation(innerTesterRouter)
|
||||||
|
}
|
||||||
|
|
||||||
|
val state by viewModel.uiState.collectAsStateWithLifecycle()
|
||||||
|
|
||||||
|
AddressesInfoScreen(state)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
package com.tangem.feature.tester.presentation.addresses.model
|
||||||
|
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class TesterAddressJson(
|
||||||
|
val addresses: List<String>,
|
||||||
|
val blockchain: String,
|
||||||
|
val derivationPath: String,
|
||||||
|
val token: String? = null,
|
||||||
|
)
|
||||||
|
|
@ -0,0 +1,24 @@
|
||||||
|
package com.tangem.feature.tester.presentation.addresses.state
|
||||||
|
|
||||||
|
import androidx.annotation.StringRes
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Content state of addresses info screen
|
||||||
|
*
|
||||||
|
* @property title title
|
||||||
|
* @property addressesText addresses in text format
|
||||||
|
* @property addressesJson addresses in JSON format
|
||||||
|
* @property onBackClick the lambda to be invoked when back button is pressed
|
||||||
|
*/
|
||||||
|
internal data class AddressesInfoScreenUM(
|
||||||
|
@StringRes val title: Int,
|
||||||
|
val addressesText: String,
|
||||||
|
val addressesJson: String,
|
||||||
|
val onBackClick: () -> Unit,
|
||||||
|
val onCopyClick: (CopyType) -> Unit,
|
||||||
|
)
|
||||||
|
|
||||||
|
internal enum class CopyType {
|
||||||
|
TEXT,
|
||||||
|
JSON,
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,142 @@
|
||||||
|
package com.tangem.feature.tester.presentation.addresses.ui
|
||||||
|
|
||||||
|
import androidx.compose.foundation.layout.*
|
||||||
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
|
import androidx.compose.material3.*
|
||||||
|
import androidx.compose.material3.TabRowDefaults.tabIndicatorOffset
|
||||||
|
import androidx.compose.runtime.*
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.graphics.vector.ImageVector
|
||||||
|
import androidx.compose.ui.res.vectorResource
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import com.tangem.core.ui.R
|
||||||
|
import com.tangem.core.ui.components.appbar.AppBarWithBackButton
|
||||||
|
import com.tangem.core.ui.extensions.stringResourceSafe
|
||||||
|
import com.tangem.core.ui.res.TangemTheme
|
||||||
|
import com.tangem.feature.tester.presentation.addresses.state.AddressesInfoScreenUM
|
||||||
|
import com.tangem.feature.tester.presentation.addresses.state.CopyType
|
||||||
|
import androidx.compose.foundation.text.selection.SelectionContainer
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Screen with addresses info in text and JSON format
|
||||||
|
*
|
||||||
|
* @param uiModel screen state
|
||||||
|
*/
|
||||||
|
@Suppress("LongMethod")
|
||||||
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
|
@Composable
|
||||||
|
internal fun AddressesInfoScreen(uiModel: AddressesInfoScreenUM) {
|
||||||
|
var selectedTab by remember { mutableIntStateOf(0) }
|
||||||
|
val tabs = listOf("Text", "JSON")
|
||||||
|
|
||||||
|
Scaffold(
|
||||||
|
topBar = {
|
||||||
|
AppBarWithBackButton(
|
||||||
|
onBackClick = uiModel.onBackClick,
|
||||||
|
text = stringResourceSafe(id = uiModel.title),
|
||||||
|
modifier = Modifier.statusBarsPadding(),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
containerColor = TangemTheme.colors.background.secondary,
|
||||||
|
) { paddingValues ->
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.padding(paddingValues),
|
||||||
|
) {
|
||||||
|
TabRow(
|
||||||
|
selectedTabIndex = selectedTab,
|
||||||
|
containerColor = TangemTheme.colors.background.secondary,
|
||||||
|
contentColor = TangemTheme.colors.text.primary1,
|
||||||
|
indicator = { tabPositions ->
|
||||||
|
TabRowDefaults.Indicator(
|
||||||
|
modifier = Modifier.tabIndicatorOffset(tabPositions[selectedTab]),
|
||||||
|
color = TangemTheme.colors.text.accent,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
) {
|
||||||
|
tabs.forEachIndexed { index, title ->
|
||||||
|
Tab(
|
||||||
|
selected = selectedTab == index,
|
||||||
|
onClick = { selectedTab = index },
|
||||||
|
text = {
|
||||||
|
Text(
|
||||||
|
text = title,
|
||||||
|
style = TangemTheme.typography.subtitle2,
|
||||||
|
color = if (selectedTab == index) {
|
||||||
|
TangemTheme.colors.text.primary1
|
||||||
|
} else {
|
||||||
|
TangemTheme.colors.text.secondary
|
||||||
|
},
|
||||||
|
)
|
||||||
|
},
|
||||||
|
selectedContentColor = TangemTheme.colors.text.primary1,
|
||||||
|
unselectedContentColor = TangemTheme.colors.text.secondary,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Row(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(horizontal = 16.dp, vertical = 8.dp),
|
||||||
|
horizontalArrangement = Arrangement.End,
|
||||||
|
) {
|
||||||
|
IconButton(
|
||||||
|
onClick = {
|
||||||
|
uiModel.onCopyClick(
|
||||||
|
if (selectedTab == 0) CopyType.TEXT else CopyType.JSON,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
imageVector = ImageVector.vectorResource(R.drawable.ic_copy_24),
|
||||||
|
contentDescription = "Copy",
|
||||||
|
tint = TangemTheme.colors.icon.primary1,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tab content
|
||||||
|
when (selectedTab) {
|
||||||
|
0 -> {
|
||||||
|
LazyColumn(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.padding(16.dp),
|
||||||
|
verticalArrangement = Arrangement.spacedBy(8.dp),
|
||||||
|
) {
|
||||||
|
item {
|
||||||
|
SelectionContainer {
|
||||||
|
Text(
|
||||||
|
text = uiModel.addressesText,
|
||||||
|
style = TangemTheme.typography.body1,
|
||||||
|
color = TangemTheme.colors.text.primary1,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
1 -> {
|
||||||
|
LazyColumn(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.padding(16.dp),
|
||||||
|
verticalArrangement = Arrangement.spacedBy(8.dp),
|
||||||
|
) {
|
||||||
|
item {
|
||||||
|
SelectionContainer {
|
||||||
|
Text(
|
||||||
|
text = uiModel.addressesJson,
|
||||||
|
style = TangemTheme.typography.body1,
|
||||||
|
color = TangemTheme.colors.text.primary1,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,175 @@
|
||||||
|
package com.tangem.feature.tester.presentation.addresses.viewmodels
|
||||||
|
|
||||||
|
import androidx.lifecycle.ViewModel
|
||||||
|
import androidx.lifecycle.viewModelScope
|
||||||
|
import com.tangem.core.ui.clipboard.ClipboardManager
|
||||||
|
import com.tangem.domain.feedback.models.BlockchainInfo
|
||||||
|
import com.tangem.domain.feedback.models.FeedbackEmailType
|
||||||
|
import com.tangem.domain.feedback.models.WalletMetaInfo
|
||||||
|
import com.tangem.domain.feedback.repository.FeedbackRepository
|
||||||
|
import com.tangem.domain.feedback.utils.EmailMessageBodyResolver
|
||||||
|
import com.tangem.domain.wallets.usecase.GetSelectedWalletSyncUseCase
|
||||||
|
import com.tangem.feature.tester.impl.R
|
||||||
|
import com.tangem.feature.tester.presentation.addresses.model.TesterAddressJson
|
||||||
|
import com.tangem.feature.tester.presentation.addresses.state.AddressesInfoScreenUM
|
||||||
|
import com.tangem.feature.tester.presentation.addresses.state.CopyType
|
||||||
|
import com.tangem.feature.tester.presentation.navigation.InnerTesterRouter
|
||||||
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
|
import kotlinx.coroutines.flow.update
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.serialization.json.Json
|
||||||
|
import javax.inject.Inject
|
||||||
|
import kotlin.coroutines.cancellation.CancellationException
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tester screen for displaying addresses and tokens.
|
||||||
|
* Uses the same data source as feedback emails to ensure identical output.
|
||||||
|
*/
|
||||||
|
|
||||||
|
@HiltViewModel
|
||||||
|
internal class AddressesInfoViewModel @Inject constructor(
|
||||||
|
private val feedbackRepository: FeedbackRepository,
|
||||||
|
private val getSelectedWalletSyncUseCase: GetSelectedWalletSyncUseCase,
|
||||||
|
private val clipboardManager: ClipboardManager,
|
||||||
|
) : ViewModel() {
|
||||||
|
|
||||||
|
private val emailMessageBodyResolver = EmailMessageBodyResolver(feedbackRepository)
|
||||||
|
|
||||||
|
private val _uiState = MutableStateFlow(getInitialState())
|
||||||
|
val uiState: StateFlow<AddressesInfoScreenUM> = _uiState.asStateFlow()
|
||||||
|
|
||||||
|
fun setupNavigation(router: InnerTesterRouter) {
|
||||||
|
_uiState.update { state ->
|
||||||
|
state.copy(
|
||||||
|
onBackClick = router::back,
|
||||||
|
onCopyClick = ::onCopyClicked,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
loadAddresses()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getInitialState(): AddressesInfoScreenUM = AddressesInfoScreenUM(
|
||||||
|
title = R.string.addresses_info,
|
||||||
|
addressesText = "Loading...",
|
||||||
|
addressesJson = "[]",
|
||||||
|
onBackClick = {},
|
||||||
|
onCopyClick = {},
|
||||||
|
)
|
||||||
|
|
||||||
|
private fun loadAddresses() {
|
||||||
|
viewModelScope.launch {
|
||||||
|
try {
|
||||||
|
val wallet = getSelectedWalletSyncUseCase()
|
||||||
|
.getOrNull() ?: run {
|
||||||
|
setEmptyState()
|
||||||
|
return@launch
|
||||||
|
}
|
||||||
|
|
||||||
|
val walletMetaInfo = WalletMetaInfo(
|
||||||
|
userWalletId = wallet.walletId,
|
||||||
|
)
|
||||||
|
|
||||||
|
val emailType = FeedbackEmailType.DirectUserRequest(
|
||||||
|
walletMetaInfo = walletMetaInfo,
|
||||||
|
)
|
||||||
|
|
||||||
|
val emailBody = emailMessageBodyResolver.resolve(emailType)
|
||||||
|
|
||||||
|
val blockchainInfos = feedbackRepository.getBlockchainInfoList(wallet.walletId)
|
||||||
|
|
||||||
|
val json = formatAddressesJson(blockchainInfos)
|
||||||
|
|
||||||
|
_uiState.update { state ->
|
||||||
|
state.copy(
|
||||||
|
addressesText = emailBody,
|
||||||
|
addressesJson = json,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} catch (e: CancellationException) {
|
||||||
|
throw e
|
||||||
|
} catch (e: Throwable) {
|
||||||
|
_uiState.update { state ->
|
||||||
|
state.copy(
|
||||||
|
addressesText = "Error loading addresses",
|
||||||
|
addressesJson = errorJson(e),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setEmptyState() {
|
||||||
|
_uiState.update { state ->
|
||||||
|
state.copy(
|
||||||
|
addressesText = "No active addresses found",
|
||||||
|
addressesJson = "[]",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Suppress("JSON_FORMAT_REDUNDANT")
|
||||||
|
private fun formatAddressesJson(list: List<BlockchainInfo>): String {
|
||||||
|
val jsonList = list
|
||||||
|
.flatMap { info ->
|
||||||
|
val addresses = info
|
||||||
|
.extractAddresses()
|
||||||
|
.sortedDescending()
|
||||||
|
|
||||||
|
val tokens = if (info.tokens.isNotEmpty()) {
|
||||||
|
info.tokens.map { it.name }
|
||||||
|
} else {
|
||||||
|
listOf(info.blockchain)
|
||||||
|
}
|
||||||
|
|
||||||
|
tokens.map { token ->
|
||||||
|
TesterAddressJson(
|
||||||
|
addresses = addresses,
|
||||||
|
blockchain = info.blockchain,
|
||||||
|
derivationPath = info.derivationPath,
|
||||||
|
token = token,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.sortedWith(
|
||||||
|
compareBy<TesterAddressJson> { it.blockchain }
|
||||||
|
.thenBy { it.derivationPath }
|
||||||
|
.thenBy { it.token },
|
||||||
|
)
|
||||||
|
|
||||||
|
return Json { prettyPrint = true }
|
||||||
|
.encodeToString(jsonList)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun BlockchainInfo.extractAddresses(): List<String> = when (val a = addresses) {
|
||||||
|
is BlockchainInfo.Addresses.Single ->
|
||||||
|
listOf(a.value)
|
||||||
|
|
||||||
|
is BlockchainInfo.Addresses.Multiple ->
|
||||||
|
a.values.map { it.value }
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun errorJson(error: Throwable): String = Json.encodeToString(
|
||||||
|
mapOf("error" to (error.message ?: "Unknown error")),
|
||||||
|
)
|
||||||
|
|
||||||
|
private fun onCopyClicked(type: CopyType) {
|
||||||
|
val state = _uiState.value
|
||||||
|
|
||||||
|
val text = when (type) {
|
||||||
|
CopyType.TEXT -> state.addressesText
|
||||||
|
CopyType.JSON -> state.addressesJson
|
||||||
|
}
|
||||||
|
|
||||||
|
if (text.isNotBlank()) {
|
||||||
|
clipboardManager.setText(
|
||||||
|
text = text,
|
||||||
|
isSensitive = false,
|
||||||
|
label = type.name,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -25,5 +25,6 @@ data class TesterMenuUM(
|
||||||
TESTER_ACTIONS(R.string.tester_actions),
|
TESTER_ACTIONS(R.string.tester_actions),
|
||||||
TEST_PUSHES(R.string.test_push),
|
TEST_PUSHES(R.string.test_push),
|
||||||
ACCOUNTS(R.string.accounts),
|
ACCOUNTS(R.string.accounts),
|
||||||
|
ADDRESSES_INFO(R.string.addresses_info),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -14,4 +14,5 @@ internal enum class TesterScreen {
|
||||||
BLOCKCHAIN_PROVIDERS,
|
BLOCKCHAIN_PROVIDERS,
|
||||||
TEST_PUSHES,
|
TEST_PUSHES,
|
||||||
ACCOUNTS,
|
ACCOUNTS,
|
||||||
|
ADDRESSES_INFO,
|
||||||
}
|
}
|
||||||
|
|
@ -21,4 +21,5 @@
|
||||||
<string name="news" translatable="false">News</string>
|
<string name="news" translatable="false">News</string>
|
||||||
<string name="news_details" translatable="false">News details</string>
|
<string name="news_details" translatable="false">News details</string>
|
||||||
<string name="news_details_bottom_sheet" translatable="false">News details (Bottom Sheet)</string>
|
<string name="news_details_bottom_sheet" translatable="false">News details (Bottom Sheet)</string>
|
||||||
|
<string name="addresses_info" translatable="false">Addresses info</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue